From f7c9d4f3c74802f474e9253419798ecba3f8e480 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 7 Apr 2026 10:19:36 -0400 Subject: [PATCH 1/6] fix(tui): simplify console org display --- .../cli/cmd/tui/component/dialog-model.tsx | 13 +++-------- .../cli/cmd/tui/component/dialog-provider.tsx | 8 ++----- .../cli/cmd/tui/component/prompt/index.tsx | 23 ++----------------- .../opencode/src/cli/cmd/tui/routes/home.tsx | 14 ++++++++++- .../src/cli/cmd/tui/util/provider-origin.ts | 13 ----------- 5 files changed, 20 insertions(+), 51 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx index 1fd1c130c6d3..fb6849d72d16 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx @@ -8,7 +8,6 @@ import { createDialogProviderOptions, DialogProvider } from "./dialog-provider" import { DialogVariant } from "./dialog-variant" import { useKeybind } from "../context/keybind" import * as fuzzysort from "fuzzysort" -import { consoleManagedProviderLabel } from "@tui/util/provider-origin" export function useConnected() { const sync = useSync() @@ -47,11 +46,7 @@ export function DialogModel(props: { providerID?: string }) { key: item, value: { providerID: provider.id, modelID: model.id }, title: model.name ?? item.modelID, - description: consoleManagedProviderLabel( - sync.data.console_state.consoleManagedProviders, - provider.id, - provider.name, - ), + description: provider.name, category, disabled: provider.id === "opencode" && model.id.includes("-nano"), footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined, @@ -89,9 +84,7 @@ export function DialogModel(props: { providerID?: string }) { description: favorites.some((item) => item.providerID === provider.id && item.modelID === model) ? "(Favorite)" : undefined, - category: connected() - ? consoleManagedProviderLabel(sync.data.console_state.consoleManagedProviders, provider.id, provider.name) - : undefined, + category: connected() ? provider.name : undefined, disabled: provider.id === "opencode" && model.includes("-nano"), footer: info.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined, onSelect() { @@ -142,7 +135,7 @@ export function DialogModel(props: { providerID?: string }) { const title = createMemo(() => { const value = provider() if (!value) return "Select model" - return consoleManagedProviderLabel(sync.data.console_state.consoleManagedProviders, value.id, value.name) + return value.name }) function onSelect(providerID: string, modelID: string) { diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx index cb7abb8227ed..c0e39e0e2100 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx @@ -13,7 +13,7 @@ import { DialogModel } from "./dialog-model" import { useKeyboard } from "@opentui/solid" import { Clipboard } from "@tui/util/clipboard" import { useToast } from "../ui/toast" -import { CONSOLE_MANAGED_ICON, isConsoleManagedProvider } from "@tui/util/provider-origin" +import { isConsoleManagedProvider } from "@tui/util/provider-origin" const PROVIDER_PRIORITY: Record = { opencode: 0, @@ -49,11 +49,7 @@ export function createDialogProviderOptions() { }[provider.id], footer: consoleManaged ? sync.data.console_state.activeOrgName : undefined, category: provider.id in PROVIDER_PRIORITY ? "Popular" : "Other", - gutter: consoleManaged ? ( - {CONSOLE_MANAGED_ICON} - ) : connected ? ( - - ) : undefined, + gutter: connected ? : undefined, async onSelect() { if (consoleManaged) return diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 045d730c9ef4..ee2aca7c8598 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -36,7 +36,6 @@ import { useToast } from "../../ui/toast" import { useKV } from "../../context/kv" import { useTextareaKeybindings } from "../textarea-keybindings" import { DialogSkill } from "../dialog-skill" -import { CONSOLE_MANAGED_ICON, consoleManagedProviderLabel } from "@tui/util/provider-origin" export type PromptProps = { sessionID?: string @@ -96,15 +95,8 @@ export function Prompt(props: PromptProps) { const list = createMemo(() => props.placeholders?.normal ?? []) const shell = createMemo(() => props.placeholders?.shell ?? []) const [auto, setAuto] = createSignal() - const activeOrgName = createMemo(() => sync.data.console_state.activeOrgName) - const canSwitchOrgs = createMemo(() => sync.data.console_state.switchableOrgCount > 1) - const currentProviderLabel = createMemo(() => { - const current = local.model.current() - const provider = local.model.parsed().provider - if (!current) return provider - return consoleManagedProviderLabel(sync.data.console_state.consoleManagedProviders, current.providerID, provider) - }) - const hasRightContent = createMemo(() => Boolean(props.right || activeOrgName())) + const currentProviderLabel = createMemo(() => local.model.parsed().provider) + const hasRightContent = createMemo(() => Boolean(props.right)) function promptModelWarning() { toast.show({ @@ -1120,17 +1112,6 @@ export function Prompt(props: PromptProps) { {props.right} - - { - if (!canSwitchOrgs()) return - command.trigger("console.org.switch") - }} - > - {`${CONSOLE_MANAGED_ICON} ${activeOrgName()}`} - - diff --git a/packages/opencode/src/cli/cmd/tui/routes/home.tsx b/packages/opencode/src/cli/cmd/tui/routes/home.tsx index 79b5c4d7ab96..70a71e5504f5 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/home.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/home.tsx @@ -1,5 +1,5 @@ import { Prompt, type PromptRef } from "@tui/component/prompt" -import { createEffect, createSignal } from "solid-js" +import { createEffect, createMemo, createSignal, Show } from "solid-js" import { Logo } from "../component/logo" import { useSync } from "../context/sync" import { Toast } from "../ui/toast" @@ -8,6 +8,8 @@ import { useRouteData } from "@tui/context/route" import { usePromptRef } from "../context/prompt" import { useLocal } from "../context/local" import { TuiPluginRuntime } from "../plugin" +import { useTheme } from "../context/theme" +import { isConsoleManagedProvider } from "../util/provider-origin" // TODO: what is the best way to do this? let once = false @@ -23,8 +25,17 @@ export function Home() { const [ref, setRef] = createSignal() const args = useArgs() const local = useLocal() + const { theme } = useTheme() let sent = false + const org = createMemo(() => { + const current = local.model.current() + const name = sync.data.console_state.activeOrgName + if (!current || !name) return + if (!isConsoleManagedProvider(sync.data.console_state.consoleManagedProviders, current.providerID)) return + return name + }) + const bind = (r: PromptRef | undefined) => { setRef(r) promptRef.set(r) @@ -67,6 +78,7 @@ export function Home() { {(item) => {item()}}} right={} placeholders={placeholder} /> diff --git a/packages/opencode/src/cli/cmd/tui/util/provider-origin.ts b/packages/opencode/src/cli/cmd/tui/util/provider-origin.ts index 7ec345ff5212..48d1f852de15 100644 --- a/packages/opencode/src/cli/cmd/tui/util/provider-origin.ts +++ b/packages/opencode/src/cli/cmd/tui/util/provider-origin.ts @@ -1,5 +1,3 @@ -export const CONSOLE_MANAGED_ICON = "⌂" - const contains = (consoleManagedProviders: string[] | ReadonlySet, providerID: string) => Array.isArray(consoleManagedProviders) ? consoleManagedProviders.includes(providerID) @@ -7,14 +5,3 @@ const contains = (consoleManagedProviders: string[] | ReadonlySet, provi export const isConsoleManagedProvider = (consoleManagedProviders: string[] | ReadonlySet, providerID: string) => contains(consoleManagedProviders, providerID) - -export const consoleManagedProviderSuffix = ( - consoleManagedProviders: string[] | ReadonlySet, - providerID: string, -) => (contains(consoleManagedProviders, providerID) ? ` ${CONSOLE_MANAGED_ICON}` : "") - -export const consoleManagedProviderLabel = ( - consoleManagedProviders: string[] | ReadonlySet, - providerID: string, - providerName: string, -) => `${providerName}${consoleManagedProviderSuffix(consoleManagedProviders, providerID)}` From 234863844d3900b9cfdacf3f9f5c59ddd7bb9ebf Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 7 Apr 2026 10:21:14 -0400 Subject: [PATCH 2/6] fix(tui): keep home org hint stable --- .../opencode/src/cli/cmd/tui/component/prompt/index.tsx | 2 +- packages/opencode/src/cli/cmd/tui/routes/home.tsx | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index ee2aca7c8598..747c61fd0bf9 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -1143,7 +1143,7 @@ export function Prompt(props: PromptProps) { } /> - + }> { - const current = local.model.current() - const name = sync.data.console_state.activeOrgName - if (!current || !name) return - if (!isConsoleManagedProvider(sync.data.console_state.consoleManagedProviders, current.providerID)) return - return name - }) + const org = createMemo(() => sync.data.console_state.activeOrgName) const bind = (r: PromptRef | undefined) => { setRef(r) From 90e1e50adaebd76ee06c0f3fb5459afd5b30888b Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 7 Apr 2026 10:27:52 -0400 Subject: [PATCH 3/6] fix(tui): remove home org hint --- packages/opencode/src/cli/cmd/tui/routes/home.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/routes/home.tsx b/packages/opencode/src/cli/cmd/tui/routes/home.tsx index bf7eb312078b..79b5c4d7ab96 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/home.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/home.tsx @@ -1,5 +1,5 @@ import { Prompt, type PromptRef } from "@tui/component/prompt" -import { createEffect, createMemo, createSignal, Show } from "solid-js" +import { createEffect, createSignal } from "solid-js" import { Logo } from "../component/logo" import { useSync } from "../context/sync" import { Toast } from "../ui/toast" @@ -8,7 +8,6 @@ import { useRouteData } from "@tui/context/route" import { usePromptRef } from "../context/prompt" import { useLocal } from "../context/local" import { TuiPluginRuntime } from "../plugin" -import { useTheme } from "../context/theme" // TODO: what is the best way to do this? let once = false @@ -24,11 +23,8 @@ export function Home() { const [ref, setRef] = createSignal() const args = useArgs() const local = useLocal() - const { theme } = useTheme() let sent = false - const org = createMemo(() => sync.data.console_state.activeOrgName) - const bind = (r: PromptRef | undefined) => { setRef(r) promptRef.set(r) @@ -71,7 +67,6 @@ export function Home() { {(item) => {item()}}} right={} placeholders={placeholder} /> From 9b4b419b1cec70076c9072258e01bbb8ed24653d Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 7 Apr 2026 17:02:56 -0400 Subject: [PATCH 4/6] test(tool): avoid network install in registry dependency test --- packages/opencode/test/tool/registry.test.ts | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/opencode/test/tool/registry.test.ts b/packages/opencode/test/tool/registry.test.ts index c9951ef198ce..e3a274bb211a 100644 --- a/packages/opencode/test/tool/registry.test.ts +++ b/packages/opencode/test/tool/registry.test.ts @@ -98,6 +98,37 @@ describe("tool.registry", () => { }), ) + await Bun.write( + path.join(opencodeDir, "package-lock.json"), + JSON.stringify({ + name: "custom-tools", + lockfileVersion: 3, + packages: { + "": { + dependencies: { + "@opencode-ai/plugin": "^0.0.0", + cowsay: "^1.6.0", + }, + }, + }, + }), + ) + + const cowsayDir = path.join(opencodeDir, "node_modules", "cowsay") + await fs.mkdir(cowsayDir, { recursive: true }) + await Bun.write( + path.join(cowsayDir, "package.json"), + JSON.stringify({ + name: "cowsay", + type: "module", + exports: "./index.js", + }), + ) + await Bun.write( + path.join(cowsayDir, "index.js"), + ["export function say({ text }) {", " return `moo ${text}`", "}", ""].join("\n"), + ) + await Bun.write( path.join(toolsDir, "cowsay.ts"), [ From 56d9fb0c4a6e2afb60e03154e755601f4f725828 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 7 Apr 2026 19:04:21 -0400 Subject: [PATCH 5/6] test(webfetch): avoid brittle global timer count assertion --- packages/opencode/test/tool/webfetch.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opencode/test/tool/webfetch.test.ts b/packages/opencode/test/tool/webfetch.test.ts index c37ba3e08a25..5233f10816b5 100644 --- a/packages/opencode/test/tool/webfetch.test.ts +++ b/packages/opencode/test/tool/webfetch.test.ts @@ -147,8 +147,7 @@ describe("tool.webfetch", () => { ) expect(ids).toHaveLength(1) - expect(cleared).toHaveLength(1) - expect(cleared[0]).toBe(ids[0]) + expect(cleared).toContain(ids[0]) }) }) }) From 84d741d33962187f9d3a8c701938ee71a8e03363 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Wed, 8 Apr 2026 00:27:20 +0000 Subject: [PATCH 6/6] chore: update nix node_modules hashes --- nix/hashes.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nix/hashes.json b/nix/hashes.json index 0b8e34e78646..f592339c2df1 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-r1+AehuOGIOaaxfXkQGracT/6OdFRn5Ub8s7H+MeKFY=", - "aarch64-linux": "sha256-WkMSRF/ZJLyzxNBjpiMR459C9G0NVOEw31tm8roPneA=", - "aarch64-darwin": "sha256-Z127cxFpTl8Ml7PB3CG9TcCU08oYCPuk0FECK2MQ2CI=", - "x86_64-darwin": "sha256-pkRoFtnVjyl+5fm+rrFyRnEwvptxylnFxPAcEv4ZOCg=" + "x86_64-linux": "sha256-85wpU1oCWbthPleNIOj5d5AOuuYZ6rM7gMLZR6YJ2WU=", + "aarch64-linux": "sha256-C3A56SDQGJquCpIRj2JhIzr4A7N4cc9lxtEjl8bXDeM=", + "aarch64-darwin": "sha256-/Ij3qhGRrcLlMfl9uEacDNnGK5URxhctuQFBW4Njrog=", + "x86_64-darwin": "sha256-10sOPuN4eZ75orw4FI8ztCq1+AKS2e8aAfg3Z6Yn56w=" } }