opencode-goal-mode 0.3.1 → 0.3.2
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 +14 -0
- package/README.md +51 -31
- package/package.json +1 -1
- package/plugins/goal-guard/agents.js +20 -0
- package/plugins/goal-guard.js +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.3.2
|
|
4
|
+
|
|
5
|
+
- Only the `goal` agent is user-selectable. The structural validator now requires
|
|
6
|
+
every other agent to be `mode: subagent` (no `all`/extra `primary`), so the
|
|
7
|
+
specialist reviewers can only be invoked by the Goal agent via the task tool,
|
|
8
|
+
never picked by the user.
|
|
9
|
+
- Friendlier subagent names in the TUI: review-verdict toasts now read
|
|
10
|
+
"Security Reviewer → PASS" / "API Reviewer → PASS" instead of raw hyphenated ids
|
|
11
|
+
(`prettyAgentName` drops the `goal-` prefix, de-hyphenates, keeps acronyms).
|
|
12
|
+
- Release pipeline: a single `vX.Y.Z` tag push now publishes to npm AND creates
|
|
13
|
+
the matching GitHub Release (versions stay in sync). `publish:check` fails the
|
|
14
|
+
release if the tag does not match `package.json` or the version already exists.
|
|
15
|
+
- README: npm-first install instructions and a documented release flow.
|
|
16
|
+
|
|
3
17
|
## v0.3.1
|
|
4
18
|
|
|
5
19
|
- Sidebar: when a task is running but no goal is set, show a clean grey `No goal`
|
package/README.md
CHANGED
|
@@ -90,7 +90,11 @@ second) — negligible for a per-tool-call guard:
|
|
|
90
90
|
## What it adds
|
|
91
91
|
|
|
92
92
|
- A primary `goal` agent that owns implementation but delegates research,
|
|
93
|
-
discovery, verification planning, and reviews to subagents.
|
|
93
|
+
discovery, verification planning, and reviews to subagents. **`goal` is the only
|
|
94
|
+
user-selectable agent** — every specialist (security, diff, verifier, …) is a
|
|
95
|
+
`mode: subagent` that the Goal agent invokes via the task tool; the user never
|
|
96
|
+
picks one directly. They surface with friendly names (e.g. "Security Reviewer",
|
|
97
|
+
"API Reviewer") rather than raw ids.
|
|
94
98
|
- Strict review gates for prompt compliance, diff review, verification, security,
|
|
95
99
|
UX, operations, data, API, performance, tests, docs, quality, and final audit.
|
|
96
100
|
- Slash commands: `/goal`, `/goal-contract`, `/goal-review`,
|
|
@@ -111,8 +115,9 @@ second) — negligible for a per-tool-call guard:
|
|
|
111
115
|
`goal_reviewer_memory`, `goal_status`, `goal_reset`.
|
|
112
116
|
- **Live state injection** into the system prompt so the model always knows
|
|
113
117
|
what the guard requires.
|
|
114
|
-
- **TUI toasts**: a toast on each review verdict (PASS/FAIL)
|
|
115
|
-
"completion unlocked" toast the moment
|
|
118
|
+
- **TUI toasts**: a toast on each review verdict (PASS/FAIL), with the
|
|
119
|
+
reviewer's friendly name, and a single "completion unlocked" toast the moment
|
|
120
|
+
the last required gate clears.
|
|
116
121
|
- An **experimental** companion TUI plugin (`plugins/goal-sidebar.js`) that shows
|
|
117
122
|
the active goal as a shining-yellow banner in the sidebar with a compact gate
|
|
118
123
|
status line. See [TUI integration](#tui-integration).
|
|
@@ -140,27 +145,40 @@ enforcement and writes its state to disk, and an experimental TUI plugin
|
|
|
140
145
|
(`toastOnReview`), and blocked destructive commands / premature completions
|
|
141
146
|
toast as before (`toastOnBlock`).
|
|
142
147
|
|
|
143
|
-
## Install
|
|
148
|
+
## Install
|
|
149
|
+
|
|
150
|
+
### From npm (recommended)
|
|
144
151
|
|
|
145
152
|
```bash
|
|
146
|
-
npm
|
|
147
|
-
|
|
148
|
-
npm run install:global
|
|
153
|
+
npm install -g opencode-goal-mode
|
|
154
|
+
opencode-goal-mode-install --global # installs into ~/.config/opencode
|
|
149
155
|
```
|
|
150
156
|
|
|
151
|
-
|
|
152
|
-
|
|
157
|
+
Then restart OpenCode (it loads agents, commands, and plugins at startup). In the
|
|
158
|
+
agent picker you will see **only the `goal` agent** — the specialist reviewers are
|
|
159
|
+
subagents the Goal agent drives for you; they are never selectable by the user.
|
|
153
160
|
|
|
154
|
-
|
|
161
|
+
Install into a single project instead of globally:
|
|
155
162
|
|
|
156
163
|
```bash
|
|
164
|
+
npm install -D opencode-goal-mode
|
|
165
|
+
npx opencode-goal-mode-install # writes to ./.opencode
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Upgrade later by re-running the same install command after `npm install -g
|
|
169
|
+
opencode-goal-mode@latest`; the installer replaces only the files it owns and
|
|
170
|
+
leaves your local edits alone (see [Installer options](#installer-options)).
|
|
171
|
+
|
|
172
|
+
### From source
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
git clone https://github.com/devinoldenburg/opencode-goal-mode
|
|
176
|
+
cd opencode-goal-mode
|
|
157
177
|
npm ci
|
|
158
178
|
npm run validate
|
|
159
|
-
npm run install:local
|
|
179
|
+
npm run install:global # or: npm run install:local
|
|
160
180
|
```
|
|
161
181
|
|
|
162
|
-
This writes to `./.opencode` in the current project.
|
|
163
|
-
|
|
164
182
|
## Installer options
|
|
165
183
|
|
|
166
184
|
```bash
|
|
@@ -253,32 +271,34 @@ keeps read-only inspection from dirtying the session, preserves goal state durin
|
|
|
253
271
|
compaction and across restarts, and blocks premature `Goal Completed` responses
|
|
254
272
|
when review gates are missing or stale.
|
|
255
273
|
|
|
256
|
-
##
|
|
274
|
+
## Releasing
|
|
257
275
|
|
|
258
|
-
|
|
276
|
+
Releases are fully automated and **version-synced**: one pushed tag publishes to
|
|
277
|
+
npm *and* creates the matching GitHub Release. The pipeline lives in
|
|
278
|
+
[`.github/workflows/publish.yml`](.github/workflows/publish.yml) (Node 24).
|
|
259
279
|
|
|
260
280
|
```bash
|
|
261
|
-
npm
|
|
262
|
-
|
|
281
|
+
npm version patch # bumps package.json + package-lock.json and creates the vX.Y.Z tag
|
|
282
|
+
git push --follow-tags # pushes main + the tag → the Release workflow runs
|
|
263
283
|
```
|
|
264
284
|
|
|
265
|
-
|
|
266
|
-
and publishes with the `NPM_TOKEN` repository secret. The workflow validates the
|
|
267
|
-
package, checks the tag matches `package.json`, verifies the version is not
|
|
268
|
-
already on npm, then publishes. Manual workflow dispatch defaults to
|
|
269
|
-
`npm publish --dry-run`.
|
|
285
|
+
On a `vX.Y.Z` tag push the workflow:
|
|
270
286
|
|
|
271
|
-
|
|
287
|
+
1. installs and runs the full CI gate (`npm run ci` — tests, audit, structural
|
|
288
|
+
validation, `npm pack --dry-run`);
|
|
289
|
+
2. runs `npm run publish:check`, which **fails if the tag does not match
|
|
290
|
+
`package.json`** or if that version already exists on npm;
|
|
291
|
+
3. publishes with `npm publish --access public` using the `NPM_TOKEN` repository
|
|
292
|
+
secret;
|
|
293
|
+
4. creates the GitHub Release for the tag with auto-generated notes.
|
|
272
294
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
```
|
|
295
|
+
So the git tag, the `package.json` version, the npm version, and the GitHub
|
|
296
|
+
Release version are always identical. A manual `workflow_dispatch` is available
|
|
297
|
+
and defaults to a safe `npm publish --dry-run`.
|
|
277
298
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
the release.
|
|
299
|
+
**One-time setup:** add a publish-scoped npm token as the `NPM_TOKEN` repository
|
|
300
|
+
secret (`gh secret set NPM_TOKEN`). Treat that token as sensitive — never commit
|
|
301
|
+
it.
|
|
282
302
|
|
|
283
303
|
## Goal Completion Contract
|
|
284
304
|
|
package/package.json
CHANGED
|
@@ -130,3 +130,23 @@ export const CONTEXTUAL_GATES = Object.freeze({
|
|
|
130
130
|
|
|
131
131
|
/** The reviewer that, when it returns a verdict, closes one review cycle. */
|
|
132
132
|
export const CYCLE_CLOSING_AGENT = "goal-final-auditor";
|
|
133
|
+
|
|
134
|
+
/** Acronyms that should stay upper-case in display names. */
|
|
135
|
+
const ACRONYMS = new Set(["api", "ux", "ui", "sql", "ops", "qa"]);
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Human-friendly display name for an agent id: drops the `goal-` namespace
|
|
139
|
+
* prefix, turns hyphens into spaces, Title-Cases words, and keeps known acronyms
|
|
140
|
+
* upper-case. e.g. "goal-security-reviewer" → "Security Reviewer",
|
|
141
|
+
* "goal-api-reviewer" → "API Reviewer", "goal-final-auditor" → "Final Auditor".
|
|
142
|
+
*/
|
|
143
|
+
export function prettyAgentName(id) {
|
|
144
|
+
const raw = String(id || "").trim();
|
|
145
|
+
if (!raw) return "";
|
|
146
|
+
return raw
|
|
147
|
+
.replace(/^goal-/, "")
|
|
148
|
+
.split(/[-_\s]+/)
|
|
149
|
+
.filter(Boolean)
|
|
150
|
+
.map((w) => (ACRONYMS.has(w.toLowerCase()) ? w.toUpperCase() : w.charAt(0).toUpperCase() + w.slice(1)))
|
|
151
|
+
.join(" ");
|
|
152
|
+
}
|
package/plugins/goal-guard.js
CHANGED
|
@@ -23,7 +23,7 @@ import { createStore, createState } from "./goal-guard/state.js";
|
|
|
23
23
|
import { createPersistence } from "./goal-guard/persistence.js";
|
|
24
24
|
import { createLogger } from "./goal-guard/logger.js";
|
|
25
25
|
import { analyzeCommand, looksLikeDestructiveBash, looksLikeMutatingBash, isVerification } from "./goal-guard/shell.js";
|
|
26
|
-
import { isPrimaryAgent, isReviewAgent, CYCLE_CLOSING_AGENT } from "./goal-guard/agents.js";
|
|
26
|
+
import { isPrimaryAgent, isReviewAgent, CYCLE_CLOSING_AGENT, prettyAgentName } from "./goal-guard/agents.js";
|
|
27
27
|
import { textOf, parseVerdict, recordVerdict } from "./goal-guard/verdicts.js";
|
|
28
28
|
import { completionAllowed, missingGates, refreshStickyGates } from "./goal-guard/gates.js";
|
|
29
29
|
import { evaluateCompletionClaim } from "./goal-guard/completion.js";
|
|
@@ -210,9 +210,9 @@ export function createGuard(input = {}, options = {}, overrides = {}) {
|
|
|
210
210
|
// Surface review progress in the TUI: a toast per recorded verdict, and a
|
|
211
211
|
// single celebratory toast the moment the last required gate clears.
|
|
212
212
|
if (recordedAgent && recordedVerdict && config.toastOnReview) {
|
|
213
|
-
logger.toast(
|
|
213
|
+
logger.toast(`${prettyAgentName(recordedAgent)} → ${recordedVerdict}`, recordedVerdict === "PASS" ? "success" : "warning");
|
|
214
214
|
if (!wasAllowed && completionAllowed(state, config)) {
|
|
215
|
-
logger.toast("
|
|
215
|
+
logger.toast("All required gates passed — completion unlocked", "success");
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
persist();
|