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 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) and a single
115
- "completion unlocked" toast the moment the last required gate clears.
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 globally
148
+ ## Install
149
+
150
+ ### From npm (recommended)
144
151
 
145
152
  ```bash
146
- npm ci
147
- npm run validate
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
- Restart OpenCode after installation. OpenCode loads agents, commands, and
152
- plugins at startup.
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
- ## Install into one project
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
- ## npm publishing
274
+ ## Releasing
257
275
 
258
- Install from npm after the first publish:
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 install -g opencode-goal-mode
262
- opencode-goal-mode-install --global
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
- Publishing is handled by `.github/workflows/publish.yml`, which runs on Node 24
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
- Release flow for a new version:
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
- ```bash
274
- npm version patch
275
- git push --follow-tags
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
- For a version that is already bumped and reviewed, commit the current tree, tag
279
- the reviewed version (for example `v0.2.4`), push the branch and tag, then create
280
- the GitHub Release. Ensure `NPM_TOKEN` has npm publish rights before publishing
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-goal-mode",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Strict Goal Mode agents, commands, and guard plugin for OpenCode.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -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
+ }
@@ -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(`Goal Guard: ${recordedAgent} → ${recordedVerdict}`, recordedVerdict === "PASS" ? "success" : "warning");
213
+ logger.toast(`${prettyAgentName(recordedAgent)} → ${recordedVerdict}`, recordedVerdict === "PASS" ? "success" : "warning");
214
214
  if (!wasAllowed && completionAllowed(state, config)) {
215
- logger.toast("Goal Guard: all required gates passed — completion unlocked", "success");
215
+ logger.toast("All required gates passed — completion unlocked", "success");
216
216
  }
217
217
  }
218
218
  persist();