opencode-goal-mode 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.4.4
4
+
5
+ ### Sidebar todos and gates stay correct and up to date
6
+
7
+ - **Acceptance-criterion todos now check off.** They were matched against the
8
+ *display-clipped* criterion text, so any criterion longer than the sidebar width
9
+ never showed as done even with exact matching evidence. Matching now uses the full
10
+ criterion text (clipping is display-only). Verified live in the OpenCode TUI.
11
+ - **The Goal section refreshes promptly.** It now updates on OpenCode activity
12
+ events (`message.part.updated`, …) — the same mechanism the reference TUI plugin
13
+ uses — in addition to the polling fallback, and forces a repaint on each refresh,
14
+ so gates and todos track the goal's real state as reviewers pass and evidence is
15
+ recorded instead of going stale.
16
+
17
+ ### Docs
18
+
19
+ - The README preview is now a real TUI screenshot (`docs/sidebar-preview.png`).
20
+
3
21
  ## v0.4.3
4
22
 
5
23
  ### Build mode no longer behaves like a goal
package/README.md CHANGED
@@ -60,7 +60,7 @@ config, including `.opencode/tui.json`. See [Installer options](#installer-optio
60
60
  [![license](https://img.shields.io/npm/l/opencode-goal-mode?color=2da44e)](LICENSE)
61
61
  [![node](https://img.shields.io/node/v/opencode-goal-mode?color=2da44e)](package.json)
62
62
 
63
- ![OpenCode Goal Mode sidebar todo section](docs/sidebar-demo.svg)
63
+ ![OpenCode Goal Mode sidebar preview](docs/sidebar-preview.png)
64
64
 
65
65
  <sub>↑ In goal mode, the Goal plugin takes over the sidebar todo section with a
66
66
  structured, evidence-aware Goal todo list — a bold `GOAL` label, then the goal
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-goal-mode",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Strict Goal Mode agents, commands, and guard plugin for OpenCode.",
5
5
  "type": "module",
6
6
  "main": "plugins/goal-sidebar.tsx",
@@ -54,9 +54,12 @@ function sidebarTodos(state, required, missing) {
54
54
  const criteria = Array.isArray(state?.contract?.acceptanceCriteria) ? state.contract.acceptanceCriteria : [];
55
55
  const items = [];
56
56
  for (const criterion of criteria.slice(0, 4)) {
57
- const text = clip(criterion, 52);
58
- if (!text) continue;
59
- items.push({ status: criterionEvidenceFresh(state, text) ? "done" : "todo", text });
57
+ // Match evidence against the FULL criterion text; clip only for display. (Clipping
58
+ // before matching meant any criterion longer than the display width never checked
59
+ // off the recorded evidence carries the full text.)
60
+ const full = String(criterion || "").replace(/\s+/g, " ").trim();
61
+ if (!full) continue;
62
+ items.push({ status: criterionEvidenceFresh(state, full) ? "done" : "todo", text: clip(full, 52) });
60
63
  }
61
64
  if (state?.dirty) items.push({ status: "todo", text: "Re-verify & re-review after recent edits" });
62
65
  // One row per missing/stale review gate, by friendly name — more scannable than a
@@ -167,9 +167,42 @@ const tui = async (api, options) => {
167
167
  // Returning undefined at mount (the old behavior) meant the poll never
168
168
  // ran and the Goal section never showed even once a goal existed.
169
169
  const first = read();
170
- const [model, setModel] = createSignal(first);
171
- const timer = setInterval(() => setModel(read()), POLL_MS);
170
+ // equals:false → every refresh re-notifies, so a changed snapshot always
171
+ // repaints (gates/todos stay live even when the new object compares equal).
172
+ const [model, setModel] = createSignal(first, { equals: false });
173
+ const refresh = () => setModel(read());
174
+ // Refresh on OpenCode activity. Tool calls (verdicts, evidence, edits)
175
+ // change the gates/todos and emit message-part events, so subscribing here
176
+ // keeps the section up to date promptly — this is the mechanism the
177
+ // reference OpenCode TUI plugin uses. The interval is a fallback for any
178
+ // quiet period or runtime where the event bus is unavailable.
179
+ const offs = [];
180
+ try {
181
+ const bus = api && api.event;
182
+ if (bus && typeof bus.on === "function") {
183
+ for (const ev of ["message.part.updated", "message.updated", "session.idle"]) {
184
+ try {
185
+ const off = bus.on(ev, refresh);
186
+ if (typeof off === "function") offs.push(off);
187
+ } catch {
188
+ /* unknown event type on this OpenCode build — skip it */
189
+ }
190
+ }
191
+ }
192
+ } catch {
193
+ /* no event bus — rely on the interval */
194
+ }
195
+ const timer = setInterval(refresh, POLL_MS);
172
196
  onCleanup(() => clearInterval(timer));
197
+ onCleanup(() => {
198
+ for (const off of offs) {
199
+ try {
200
+ off();
201
+ } catch {
202
+ /* ignore */
203
+ }
204
+ }
205
+ });
173
206
  // First-display rainbow: starts the moment a goal FIRST appears. If a goal
174
207
  // is already present at mount it starts immediately; otherwise the effect
175
208
  // fires when the goal later appears (the common case — the goal is set