From b89e6a4c6b44cbbb0b35718bf83155a510e92c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20TEKTA=C5=9E?= Date: Tue, 27 Oct 2026 13:45:04 +0300 Subject: [PATCH 1/5] fix(goal): allow native OpenTUI 0.6/0.7 optional peers for OpenCode 2.1 host (#284) --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index cc7019cb..90036d59 100644 --- a/package.json +++ b/package.json @@ -127,8 +127,8 @@ "solid-js": "1.9.12" }, "peerDependencies": { - "@opentui/core": ">=0.5.14 <0.6", - "@opentui/solid": ">=0.5.14 <0.6", + "@opentui/core": ">=0.5.14 <0.8", + "@opentui/solid": ">=0.5.14 <0.8", "solid-js": ">=1.9.0 <2" }, "peerDependenciesMeta": { From d1fbd3ade82f0b21a3d63d4f6fdaf63cdc49cf2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20TEKTA=C5=9E?= Date: Tue, 27 Oct 2026 13:46:02 +0300 Subject: [PATCH 2/5] test(goal): native packed TUI rendering in strict standalone npm consumer (#284) --- scripts/native-tui-clean-consumer-smoke.mjs | 74 +++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 scripts/native-tui-clean-consumer-smoke.mjs diff --git a/scripts/native-tui-clean-consumer-smoke.mjs b/scripts/native-tui-clean-consumer-smoke.mjs new file mode 100644 index 00000000..3dd95ec1 --- /dev/null +++ b/scripts/native-tui-clean-consumer-smoke.mjs @@ -0,0 +1,74 @@ +import assert from "node:assert/strict" +import { testRender } from "@opentui/solid" +import { createSignal } from "solid-js" +import plugin from "@bybrawe/opencode-goal/tui" + +// This file runs only after a fresh strict npm installation of the packed +// candidate into a *different* directory. None of the checkout's dev +// dependencies should resolve from this consumer location. +let slot +let listener +let unregistered = 0 +let unsubscribed = 0 +const requests = [] +const [selected, select] = createSignal("ses_first") +const project = (id) => "/remote/project/" + id + +const ctx = { + client: { rpc: (definition) => { + assert.equal(definition.id, "opencode-goal-status") + return { read: async ({ sessionID }, options) => { + requests.push({ sessionID, location: options.location }) + return { + schemaVersion: 1, sessionID, + directory: options.location.directory, + text: "OpenCode Goals\nACTIVE " + sessionID, + } + } } + } }, + theme: { text: { base: "#ffffff" } }, + data: { + session: { get: (id) => ({ id, location: { directory: project(id), workspaceID: "w1" } }) }, + listen: (callback) => { listener = callback; return () => { unsubscribed++ } }, + }, + ui: { slot: (contribution) => { + assert.equal(contribution.append, "sidebar.content") + slot = contribution + return () => { unregistered++ } + } }, +} + +assert.equal(plugin.id, "@bybrawe/opencode-goal") +const close = await plugin.setup(ctx) +assert.ok(slot) +const rendered = await testRender( + () => slot.render({ get sessionID() { return selected() } }), + { width: 60, height: 12 }, +) + +try { + await new Promise((r) => setTimeout(r, 120)) + await rendered.renderOnce() + assert.match(rendered.captureCharFrame(), /OpenCode Goals/) + assert.match(rendered.captureCharFrame(), /ACTIVE ses_first/) + select("ses_second") + await new Promise((r) => setTimeout(r, 120)) + await rendered.renderOnce() + assert.match(rendered.captureCharFrame(), /ACTIVE ses_second/) + assert.doesNotMatch(rendered.captureCharFrame(), /ACTIVE ses_first/) + assert.equal(requests.at(-1).location.directory, project("ses_second")) + assert.equal(requests.at(-1).location.workspaceID, "w1") + + close() + close() + const previous = requests.length + listener({ details: { type: "server.connected" } }) + await new Promise((r) => setTimeout(r, 135)) + assert.equal(requests.length, previous, "disposed sidebar must not keep polling") + assert.equal(unregistered, 1, "slot unregisters exactly once") + assert.equal(unsubscribed, 1, "listener unsubscribes exactly once") + console.log("Packed Goal strict npm consumer: OpenTUI visible sidebar, session switch and teardown PASS") +} finally { + close() + rendered.renderer.destroy() +} From f7f11b05d707de88c15dcb39f946f60a9c1dea42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20TEKTA=C5=9E?= Date: Tue, 27 Oct 2026 13:46:57 +0300 Subject: [PATCH 3/5] ci(goal): strict Windows/Linux clean packed TUI install with pinned OpenCode 2.0/2.1 hosts (#284) --- .github/workflows/native-sidebar.yml | 52 ++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/.github/workflows/native-sidebar.yml b/.github/workflows/native-sidebar.yml index 2bfc43be..029275cc 100644 --- a/.github/workflows/native-sidebar.yml +++ b/.github/workflows/native-sidebar.yml @@ -39,3 +39,55 @@ jobs: run: | npm install --prefix "$RUNNER_TEMP/native-host" --package-lock=false @opencode/cli@2.0.18 OPENCODE2_BINARY="$RUNNER_TEMP/native-host/node_modules/.bin/opencode2" node scripts/native-status-host-canary.mjs + + strict-consumer: + name: Strict npm Goal TUI + OpenCode ${{ matrix.host }} / OpenTUI ${{ matrix.opentui }} (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 25 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + include: + - opentui: '0.5.14' + host: '2.0.18' + - opentui: '0.7.0' + host: '2.1.4' + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: '24' + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + with: + bun-version: '1.4.2' + - name: Build candidate without changing published version + run: | + npm install + npm run check + npm run build + - name: Clean strictly resolved package install + real TUI render + shell: bash + run: | + set -euo pipefail + mkdir -p "$RUNNER_TEMP/goal-packages" + npm pack --json --ignore-scripts --pack-destination "$RUNNER_TEMP/goal-packages" > "$RUNNER_TEMP/goal-pack.json" + filename="$(node -e 'const fs=require("node:fs");const p=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));if(p.length!==1)process.exit(1);process.stdout.write(p[0].filename)' "$RUNNER_TEMP/goal-pack.json")" + consumer="$RUNNER_TEMP/goal-clean-consumer" + mkdir -p "$consumer" + printf '{"private":true,"type":"module"}\n' > "$consumer/package.json" + # Strict npm resolution: NEVER --legacy-peer-deps, --force, + # --omit=peer or a developer-checkout node_modules link. + npm install --prefix "$consumer" --no-audit --no-fund --package-lock=false \ + "$RUNNER_TEMP/goal-packages/$filename" \ + "@opencode/cli@${{ matrix.host }}" \ + "@opentui/core@${{ matrix.opentui }}" \ + "@opentui/solid@${{ matrix.opentui }}" \ + solid-js@1.9.12 + npm ls --prefix "$consumer" @opencode/cli @opencode/plugin @bybrawe/opencode-goal @opentui/core @opentui/solid solid-js + node -e 'const p=require(process.argv[1]);if(p.version!==process.argv[2])process.exit(1);console.log("Host OpenCode CLI",p.version)' "$consumer/node_modules/@opencode/cli/package.json" "${{ matrix.host }}" + node -e 'const p=require(process.argv[1]);if(p.version!==process.argv[2])process.exit(1);console.log("OpenTUI Solid",p.version)' "$consumer/node_modules/@opentui/solid/package.json" "${{ matrix.opentui }}" + cp scripts/native-tui-clean-consumer-smoke.mjs "$consumer/native-tui-smoke.mjs" + (cd "$consumer" && bun --conditions=browser native-tui-smoke.mjs) From 2d698949c721a8f52946c73dcb0c60a8f25c2c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20TEKTA=C5=9E?= Date: Tue, 27 Oct 2026 13:48:15 +0300 Subject: [PATCH 4/5] ci(goal): expand strict native TUI matrix to all four host/OS combinations (#284) --- .github/workflows/native-sidebar.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/native-sidebar.yml b/.github/workflows/native-sidebar.yml index 029275cc..ca9c35a4 100644 --- a/.github/workflows/native-sidebar.yml +++ b/.github/workflows/native-sidebar.yml @@ -47,11 +47,18 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] include: - - opentui: '0.5.14' + - os: ubuntu-latest + opentui: '0.5.14' + host: '2.0.18' + - os: windows-latest + opentui: '0.5.14' host: '2.0.18' - - opentui: '0.7.0' + - os: ubuntu-latest + opentui: '0.7.0' + host: '2.1.4' + - os: windows-latest + opentui: '0.7.0' host: '2.1.4' steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 From f263fdb92caae25132f5af52a152b29f81e4f081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20TEKTA=C5=9E?= Date: Tue, 27 Oct 2026 13:50:50 +0300 Subject: [PATCH 5/5] test(goal): verify established native TUI plugin id, not npm scope (#284) --- scripts/native-tui-clean-consumer-smoke.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/native-tui-clean-consumer-smoke.mjs b/scripts/native-tui-clean-consumer-smoke.mjs index 3dd95ec1..25e63525 100644 --- a/scripts/native-tui-clean-consumer-smoke.mjs +++ b/scripts/native-tui-clean-consumer-smoke.mjs @@ -38,7 +38,7 @@ const ctx = { } }, } -assert.equal(plugin.id, "@bybrawe/opencode-goal") +assert.equal(plugin.id, "opencode-goal", "native TUI plugin identity must match its established public registration") const close = await plugin.setup(ctx) assert.ok(slot) const rendered = await testRender(