diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts index 7230dae16a..f775a32c85 100644 --- a/packages/opencode/src/cli/cmd/tui/thread.ts +++ b/packages/opencode/src/cli/cmd/tui/thread.ts @@ -189,7 +189,8 @@ export const TuiThreadCommand = cmd({ const prompt = await input(args.prompt) const config = await TuiConfig.get() - const network = resolveNetworkOptionsNoConfig(args) + const network = await resolveNetworkOptionsNoConfig(args) + const external = process.argv.includes("--port") || process.argv.includes("--hostname") || diff --git a/packages/opencode/src/effect/app-runtime.ts b/packages/opencode/src/effect/app-runtime.ts index 4c1637006c..b794fdfa48 100644 --- a/packages/opencode/src/effect/app-runtime.ts +++ b/packages/opencode/src/effect/app-runtime.ts @@ -49,6 +49,7 @@ import { Worktree } from "@/worktree" import { Pty } from "@/pty" import { PtyTicket } from "@/pty/ticket" import { Installation } from "@/installation" +import * as Effect from "effect/Effect" import { ShareNext } from "@/share/share-next" import { SessionShare } from "@/share/session" import { SyncEvent } from "@/sync" @@ -56,13 +57,30 @@ import { Npm } from "@opencode-ai/core/npm" import { memoMap } from "@opencode-ai/core/effect/memo-map" import { DataMigration } from "@/data-migration" +// Adjusts the default Config layer to ensure that plugins are always initialised before +// any other layers read the current config +const ConfigWithPluginPriority = Layer.effect( + Config.Service, + Effect.gen(function* () { + const config = yield* Config.Service + const plugin = yield* Plugin.Service + + return { + ...config, + get: () => Effect.andThen(plugin.init(), config.get), + getGlobal: () => Effect.andThen(plugin.init(), config.getGlobal), + getConsoleState: () => Effect.andThen(plugin.init(), config.getConsoleState), + } + }), +).pipe(Layer.provide(Layer.merge(Plugin.defaultLayer, Config.defaultLayer))) + export const AppLayer = Layer.mergeAll( Npm.defaultLayer, AppFileSystem.defaultLayer, Bus.defaultLayer, Auth.defaultLayer, Account.defaultLayer, - Config.defaultLayer, + ConfigWithPluginPriority, Git.defaultLayer, Ripgrep.defaultLayer, File.defaultLayer, diff --git a/packages/opencode/src/project/bootstrap.ts b/packages/opencode/src/project/bootstrap.ts index 6103a9efb4..81022bdc24 100644 --- a/packages/opencode/src/project/bootstrap.ts +++ b/packages/opencode/src/project/bootstrap.ts @@ -1,4 +1,3 @@ -import { Plugin } from "../plugin" import { Format } from "../format" import { LSP } from "@/lsp/lsp" import { File } from "../file" @@ -6,6 +5,7 @@ import { Snapshot } from "../snapshot" import * as Project from "./project" import * as Vcs from "./vcs" import { Bus } from "../bus" +import { Plugin } from "../plugin" import { InstanceState } from "@/effect/instance-state" import { FileWatcher } from "@/file/watcher" import { ShareNext } from "@/share/share-next" @@ -23,12 +23,10 @@ export const layer = Layer.effect( // Yield each bootstrap dep at layer init so `run` itself has R = never. // InstanceStore imports only the lightweight tag from bootstrap-service.ts, // so it can depend on bootstrap without importing this implementation graph. - const config = yield* Config.Service const file = yield* File.Service const fileWatcher = yield* FileWatcher.Service const format = yield* Format.Service const lsp = yield* LSP.Service - const plugin = yield* Plugin.Service const project = yield* Project.Service const reference = yield* Reference.Service const shareNext = yield* ShareNext.Service @@ -38,10 +36,6 @@ export const layer = Layer.effect( const run = Effect.gen(function* () { const ctx = yield* InstanceState.context yield* Effect.logInfo("bootstrapping", { directory: ctx.directory }) - // everything depends on config so eager load it for nice traces - yield* config.get() - // Plugin can mutate config so it has to be initialized before anything else. - yield* plugin.init() // Each service self-manages its own slow work via Effect.forkScoped against // its per-instance state scope. We just await materialization here. yield* Effect.forEach(