diff --git a/packages/opencode/src/cli/cmd/run/runtime.boot.ts b/packages/opencode/src/cli/cmd/run/runtime.boot.ts index f56ec4cf8b..280664f91f 100644 --- a/packages/opencode/src/cli/cmd/run/runtime.boot.ts +++ b/packages/opencode/src/cli/cmd/run/runtime.boot.ts @@ -20,6 +20,30 @@ const DEFAULT_KEYBINDS: FooterKeybinds = { inputNewline: "shift+return,ctrl+return,alt+return,ctrl+j", } +let configTask: Promise>> | undefined + +function loadConfig() { + if (configTask) { + return configTask + } + + const task = TuiConfig.get() + configTask = task + task.then( + () => { + if (configTask === task) { + configTask = undefined + } + }, + () => { + if (configTask === task) { + configTask = undefined + } + }, + ) + return task +} + export type ModelInfo = { variants: string[] limits: Record @@ -98,7 +122,7 @@ export async function resolveSessionInfo( // Always ensures t is present in the variant cycle binding. export async function resolveFooterKeybinds(): Promise { try { - const config = await TuiConfig.get() + const config = await loadConfig() const configuredLeader = config.keybinds?.leader?.trim() || DEFAULT_KEYBINDS.leader const configuredVariantCycle = config.keybinds?.variant_cycle?.trim() || "ctrl+t" const configuredInterrupt = config.keybinds?.session_interrupt?.trim() || DEFAULT_KEYBINDS.interrupt @@ -132,7 +156,7 @@ export async function resolveFooterKeybinds(): Promise { export async function resolveDiffStyle(): Promise { try { - const config = await TuiConfig.get() + const config = await loadConfig() return config.diff_style ?? "auto" } catch { return "auto" diff --git a/packages/opencode/src/cli/cmd/run/runtime.lifecycle.ts b/packages/opencode/src/cli/cmd/run/runtime.lifecycle.ts index 6fc6bf28ce..4127612cba 100644 --- a/packages/opencode/src/cli/cmd/run/runtime.lifecycle.ts +++ b/packages/opencode/src/cli/cmd/run/runtime.lifecycle.ts @@ -176,6 +176,7 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise {}) - const { RunFooter } = await import("./footer") + const { RunFooter } = await footerTask const labels = footerLabels({ agent: input.agent, diff --git a/packages/opencode/src/cli/cmd/run/runtime.ts b/packages/opencode/src/cli/cmd/run/runtime.ts index 34e501a7fd..6adcce464a 100644 --- a/packages/opencode/src/cli/cmd/run/runtime.ts +++ b/packages/opencode/src/cli/cmd/run/runtime.ts @@ -83,14 +83,6 @@ async function runInteractiveRuntime(input: RunRuntimeInput): Promise { variant: undefined, }) const savedTask = resolveSavedVariant(ctx.model) - const agentsTask = ctx.sdk.app - .agents({ directory: ctx.directory }) - .then((x) => x.data ?? []) - .catch(() => []) - const resourcesTask = ctx.sdk.experimental.resource - .list({ directory: ctx.directory }) - .then((x) => Object.values(x.data ?? {})) - .catch(() => []) let variants: string[] = [] let limits: Record = {} let aborting = false @@ -210,17 +202,48 @@ async function runInteractiveRuntime(input: RunRuntimeInput): Promise { }) const footer = shell.footer - void Promise.all([agentsTask, resourcesTask]).then(([agents, resources]) => { - if (footer.isClosed) { - return + let catalogTask: Promise | undefined + const loadCatalog = () => { + if (catalogTask) { + return catalogTask } - footer.event({ - type: "catalog", - agents, - resources, + catalogTask = Promise.all([ + ctx.sdk.app + .agents({ directory: ctx.directory }) + .then((x) => x.data ?? []) + .catch(() => []), + ctx.sdk.experimental.resource + .list({ directory: ctx.directory }) + .then((x) => Object.values(x.data ?? {})) + .catch(() => []), + ]) + .then(([agents, resources]) => { + if (footer.isClosed) { + return + } + + footer.event({ + type: "catalog", + agents, + resources, + }) + }) + .catch(() => {}) + + return catalogTask + } + + void footer + .idle() + .then(() => { + if (footer.isClosed) { + return + } + + void loadCatalog() }) - }) + .catch(() => {}) if (Flag.OPENCODE_SHOW_TTFD) { footer.append({ @@ -435,12 +458,12 @@ export async function runInteractiveLocalMode(input: RunLocalInput): Promise { + pending = Promise.all([input.resolveAgent(), input.session(sdk)]).then(([agent, session]) => { if (!session?.id) { throw new Error("Session not found") } - await input.share(sdk, session.id) + void input.share(sdk, session.id).catch(() => {}) return { sessionID: session.id, sessionTitle: session.title,