opencode-goal-mode 0.3.9 → 0.3.11
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/ARCHITECTURE.md +3 -2
- package/CHANGELOG.md +24 -0
- package/README.md +8 -2
- package/package.json +1 -1
- package/plugins/goal-guard/sidebar-data.js +5 -2
- package/plugins/goal-guard/tools.js +17 -0
package/ARCHITECTURE.md
CHANGED
|
@@ -188,8 +188,9 @@ progress is visible even without the banner.
|
|
|
188
188
|
The JSX renderer is verified headlessly with `@opentui/solid`'s `testRender` in
|
|
189
189
|
`tools/visual-test/sidebar-visual.jsx` (`npm run test:visual`, needs Bun + the
|
|
190
190
|
OpenTUI stack): it asserts the rendered text, the exact foreground colours, and
|
|
191
|
-
the bold attribute for Goal todo / done / native-todo-preserved states. That tool
|
|
192
|
-
the npm package and from `node --test
|
|
191
|
+
the bold attribute for Goal todo / done / native-todo-preserved states. That tool
|
|
192
|
+
is excluded from the npm package and from `node --test`; the GitHub CI workflow
|
|
193
|
+
runs it in a separate Bun/OpenTUI job.
|
|
193
194
|
|
|
194
195
|
## Configuration
|
|
195
196
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.3.11
|
|
4
|
+
|
|
5
|
+
- Fixed Goal sidebar/status isolation so an explicit Build or other non-Goal
|
|
6
|
+
session never falls back to another active Goal session in the same worktree.
|
|
7
|
+
- Blocked mutating `goal_*` tools from activating Goal Guard state in non-Goal
|
|
8
|
+
sessions; read-only tools remain strictly scoped to the current session.
|
|
9
|
+
- Added regression coverage for mixed Goal/Build persisted snapshots, session-scoped
|
|
10
|
+
status/evidence/memory reads, and Build-mode tool calls.
|
|
11
|
+
|
|
12
|
+
## v0.3.10
|
|
13
|
+
|
|
14
|
+
- Clarified the recommended install command to use a persistent global npm install
|
|
15
|
+
before running the installer, so OpenCode can resolve the TUI package on future
|
|
16
|
+
starts. `npx` remains documented for temporary installs/server-side checks.
|
|
17
|
+
- Added the missing historical `v0.3.8` changelog section. `v0.3.8` reached npm,
|
|
18
|
+
but its GitHub Release workflow failed while generating release notes, so it was
|
|
19
|
+
superseded by `v0.3.9`.
|
|
20
|
+
|
|
3
21
|
## v0.3.9
|
|
4
22
|
|
|
5
23
|
- Installer docs and `--help` now put the one-command `npx opencode-goal-mode --global`
|
|
@@ -12,6 +30,12 @@
|
|
|
12
30
|
- Destructive-command blocking no longer activates Goal enforcement for Build or
|
|
13
31
|
other non-Goal sessions, preventing non-Goal tasks from being classified as goals.
|
|
14
32
|
|
|
33
|
+
## v0.3.8
|
|
34
|
+
|
|
35
|
+
- Superseded release: npm publish succeeded, but the GitHub Release workflow failed
|
|
36
|
+
because the changelog section was still named `Unreleased`. The same functional
|
|
37
|
+
changes shipped correctly in `v0.3.9` with matching npm and GitHub releases.
|
|
38
|
+
|
|
15
39
|
## v0.3.7
|
|
16
40
|
|
|
17
41
|
- **FIX: the sidebar now actually loads.** OpenCode loads a TUI plugin via the
|
package/README.md
CHANGED
|
@@ -17,14 +17,15 @@ TUI sidebar.
|
|
|
17
17
|
**One command** (recommended; needs [Node](https://nodejs.org) 20.11+ and a working [OpenCode](https://opencode.ai) install):
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
|
|
20
|
+
npm install -g opencode-goal-mode && opencode-goal-mode --global
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Then **restart OpenCode**. That's the whole install: it copies the Goal agent,
|
|
24
24
|
review subagents, slash commands, and guard plugin into `~/.config/opencode`, and
|
|
25
25
|
merge-safely registers the Goal todo sidebar in `~/.config/opencode/tui.json`.
|
|
26
26
|
In the agent picker you'll see only the **`goal`** agent; reviewers are subagents
|
|
27
|
-
it drives automatically.
|
|
27
|
+
it drives automatically. The global install keeps the TUI package resolvable on
|
|
28
|
+
future OpenCode starts; Goal Mode inherits your existing OpenCode model/provider.
|
|
28
29
|
|
|
29
30
|
<details>
|
|
30
31
|
<summary>Other ways to install</summary>
|
|
@@ -34,6 +35,10 @@ it drives automatically. Goal Mode inherits your existing OpenCode model/provide
|
|
|
34
35
|
npm install -g opencode-goal-mode
|
|
35
36
|
opencode-goal-mode --global # alias of opencode-goal-mode-install
|
|
36
37
|
|
|
38
|
+
# Temporary npx install (server-side components work; for the TUI sidebar,
|
|
39
|
+
# prefer the global install above so OpenCode can resolve the package later)
|
|
40
|
+
npx opencode-goal-mode --global
|
|
41
|
+
|
|
37
42
|
# Into a single project (writes ./.opencode, including ./.opencode/tui.json)
|
|
38
43
|
npx opencode-goal-mode
|
|
39
44
|
|
|
@@ -253,6 +258,7 @@ enforcement and writes its state to disk, and an experimental TUI plugin
|
|
|
253
258
|
## Installer options
|
|
254
259
|
|
|
255
260
|
```bash
|
|
261
|
+
npm install -g opencode-goal-mode && opencode-goal-mode --global
|
|
256
262
|
npx opencode-goal-mode --global --dry-run
|
|
257
263
|
npx opencode-goal-mode --global
|
|
258
264
|
opencode-goal-mode-install --global --uninstall
|
package/package.json
CHANGED
|
@@ -30,8 +30,10 @@ function normalize(record) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* Choose which session's goal to show
|
|
34
|
-
*
|
|
33
|
+
* Choose which session's goal to show. When OpenCode gives us a concrete session
|
|
34
|
+
* id, never fall back to another session: Build/non-Goal sessions must not show a
|
|
35
|
+
* Goal from the same worktree. The most-recent active fallback is only for
|
|
36
|
+
* no-session contexts such as initial sidebar registration polling.
|
|
35
37
|
*/
|
|
36
38
|
export function pickSession(snapshot, sessionId) {
|
|
37
39
|
if (!snapshot || !Array.isArray(snapshot.sessions)) return null;
|
|
@@ -41,6 +43,7 @@ export function pickSession(snapshot, sessionId) {
|
|
|
41
43
|
if (sessionId) {
|
|
42
44
|
const direct = records.find(([key, st]) => key === sessionId && st.active);
|
|
43
45
|
if (direct) return direct[1];
|
|
46
|
+
return null;
|
|
44
47
|
}
|
|
45
48
|
const active = records.filter(([, st]) => st.active);
|
|
46
49
|
if (active.length === 0) return null;
|
|
@@ -16,6 +16,7 @@ import { evidenceMapReport, reviewerMemoryReport, statusReport } from "./summary
|
|
|
16
16
|
import { recordEvidence } from "./events.js";
|
|
17
17
|
import { refreshStickyGates } from "./gates.js";
|
|
18
18
|
import { createState } from "./state.js";
|
|
19
|
+
import { isPrimaryAgent } from "./agents.js";
|
|
19
20
|
|
|
20
21
|
const s = tool.schema;
|
|
21
22
|
|
|
@@ -28,6 +29,18 @@ const s = tool.schema;
|
|
|
28
29
|
export function createGoalTools({ store, config, persist }) {
|
|
29
30
|
const save = typeof persist === "function" ? persist : () => {};
|
|
30
31
|
|
|
32
|
+
function requireGoalMode(state) {
|
|
33
|
+
return Boolean(state?.active || isPrimaryAgent(state?.currentAgent));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function goalModeOnlyResult() {
|
|
37
|
+
return {
|
|
38
|
+
title: "Goal Mode required",
|
|
39
|
+
output: "This goal_* tool can only mutate Goal Guard state from an active Goal session. Switch to the `goal` agent or start with /goal.",
|
|
40
|
+
metadata: { blocked: true, reason: "not_goal_mode" },
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
return {
|
|
32
45
|
goal_status: tool({
|
|
33
46
|
description:
|
|
@@ -109,6 +122,7 @@ export function createGoalTools({ store, config, persist }) {
|
|
|
109
122
|
},
|
|
110
123
|
async execute(args, ctx) {
|
|
111
124
|
const state = store.stateFor(ctx.sessionID);
|
|
125
|
+
if (!requireGoalMode(state)) return goalModeOnlyResult();
|
|
112
126
|
state.active = true;
|
|
113
127
|
state.contract = {
|
|
114
128
|
title: String(args.title || "").replace(/\s+/g, " ").trim(),
|
|
@@ -145,6 +159,7 @@ export function createGoalTools({ store, config, persist }) {
|
|
|
145
159
|
},
|
|
146
160
|
async execute(args, ctx) {
|
|
147
161
|
const state = store.stateFor(ctx.sessionID);
|
|
162
|
+
if (!requireGoalMode(state)) return goalModeOnlyResult();
|
|
148
163
|
state.active = true;
|
|
149
164
|
recordEvidence(store, state, args.command, args.result, args.criteria);
|
|
150
165
|
save();
|
|
@@ -164,6 +179,8 @@ export function createGoalTools({ store, config, persist }) {
|
|
|
164
179
|
confirm: s.boolean().describe("Must be true to actually reset."),
|
|
165
180
|
},
|
|
166
181
|
async execute(args, ctx) {
|
|
182
|
+
const state = store.stateFor(ctx.sessionID);
|
|
183
|
+
if (!requireGoalMode(state)) return goalModeOnlyResult();
|
|
167
184
|
if (!args.confirm) {
|
|
168
185
|
return { title: "Reset not confirmed", output: "Pass confirm=true to reset Goal Guard state." };
|
|
169
186
|
}
|