goalbuddy 0.3.7 → 0.3.9

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.
Files changed (36) hide show
  1. package/.claude-plugin/marketplace.json +16 -0
  2. package/CHANGELOG.md +70 -0
  3. package/CONTRIBUTING.md +2 -2
  4. package/README.md +11 -3
  5. package/{RELEASE-0.3.7.md → docs/releases/0.3.7.md} +2 -0
  6. package/docs/releases/0.3.8.md +40 -0
  7. package/docs/releases/0.3.9.md +46 -0
  8. package/docs/releases/README.md +84 -0
  9. package/goalbuddy/SKILL.md +26 -8
  10. package/goalbuddy/scripts/check-goal-state.mjs +22 -4
  11. package/goalbuddy/scripts/check-update.mjs +18 -1
  12. package/goalbuddy/scripts/render-task-prompt.mjs +17 -3
  13. package/goalbuddy/surfaces/local-goal-board/scripts/lib/goal-board.mjs +16 -15
  14. package/goalbuddy/surfaces/local-goal-board/scripts/local-goal-board.mjs +25 -3
  15. package/goalbuddy/surfaces/local-goal-board/test/local-goal-board.test.mjs +189 -4
  16. package/goalbuddy/templates/goal.md +12 -0
  17. package/goalbuddy/templates/state.yaml +2 -1
  18. package/internal/cli/goal-maker.mjs +186 -7
  19. package/package.json +6 -6
  20. package/plugins/goalbuddy/.claude-plugin/plugin.json +1 -1
  21. package/plugins/goalbuddy/.codex-plugin/plugin.json +1 -1
  22. package/plugins/goalbuddy/README.md +1 -1
  23. package/plugins/goalbuddy/skills/goalbuddy/SKILL.md +26 -8
  24. package/plugins/goalbuddy/skills/goalbuddy/scripts/check-goal-state.mjs +22 -4
  25. package/plugins/goalbuddy/skills/goalbuddy/scripts/check-update.mjs +18 -1
  26. package/plugins/goalbuddy/skills/goalbuddy/scripts/render-task-prompt.mjs +17 -3
  27. package/plugins/goalbuddy/skills/goalbuddy/surfaces/local-goal-board/scripts/lib/goal-board.mjs +1 -4
  28. package/plugins/goalbuddy/skills/goalbuddy/surfaces/local-goal-board/scripts/local-goal-board.mjs +25 -3
  29. package/plugins/goalbuddy/skills/goalbuddy/surfaces/local-goal-board/test/local-goal-board.test.mjs +27 -0
  30. package/plugins/goalbuddy/skills/goalbuddy/templates/goal.md +12 -0
  31. package/plugins/goalbuddy/skills/goalbuddy/templates/state.yaml +2 -1
  32. package/examples/improve-goal-maker/goal.md +0 -51
  33. package/examples/improve-goal-maker/notes/T001-repo-map.md +0 -59
  34. package/examples/improve-goal-maker/notes/T002-risk-map.md +0 -37
  35. package/examples/improve-goal-maker/state.yaml +0 -224
  36. /package/{RELEASE-0.3.5.md → docs/releases/0.3.5.md} +0 -0
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/claude-code-marketplace.json",
3
+ "name": "goalbuddy",
4
+ "owner": {
5
+ "name": "tolibear",
6
+ "email": "support@tolibear.com"
7
+ },
8
+ "description": "GoalBuddy /goal-prep skill plus Scout/Judge/Worker subagents for pressured /goal runs.",
9
+ "plugins": [
10
+ {
11
+ "name": "goalbuddy",
12
+ "source": "./plugins/goalbuddy",
13
+ "description": "GoalBuddy: turn broad work into pressured /goal runs with oracles, local boards, receipts, and verification."
14
+ }
15
+ ]
16
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,70 @@
1
+ # Changelog
2
+
3
+ ## 0.3.9 — Marketplace and Board Runtime Polish (2026-06-23)
4
+
5
+ - **Made Claude marketplace install discoverable.** The repo now ships a root `.claude-plugin/marketplace.json`, keeps it in the npm package allowlist, and validates marketplace install flow alongside the existing plugin manifest checks.
6
+ - **Made `/goal-prep` install-channel agnostic.** Model-invoked board, prompt, and parallel-plan commands now use bundled skill scripts instead of assuming a global `goalbuddy` or `npx goalbuddy` binary. Update and agent guidance now points users back to their actual install channel.
7
+ - **Stopped local-board flicker during task transitions.** The board watcher now coalesces rapid `state.yaml` writes before streaming updates, avoiding transient “more than one active task” errors during normal multi-step transitions.
8
+ - **Let the board render valid parallel work.** The local board now renders multiple active tasks in the In Progress column instead of refusing to parse the whole board, while the stricter `check-goal-state` invariant remains available for board validation.
9
+ - **Added exact-approval wait guidance.** GoalBuddy now has a terminal waiting shape for exact human approval gates: ask once, preserve the required reply, set `waiting_for_user_approval: true`, and stop until the user replies.
10
+ - **Added PM-owned board health stewardship.** Goal Prep now explains the safe steward model: use the bundled checker and live board API to repair GoalBuddy control files only, without introducing an always-on implementation actor.
11
+
12
+ ## 0.3.8 — Board Hub Guardrails (2026-05-29)
13
+
14
+ - **Clarified multi-board hub recovery.** Unregistered board URLs now explain that a `/slug/` 404 does not mean the `41737` process is stale; agents should verify `/api/boards` and register the new goal on the same hub before stopping any process. Release checks now include the local board surface tests.
15
+ - **Prefer the largest safe useful slice.** GoalBuddy now teaches Judge to pick whole useful slices, Worker to complete the assigned slice, and PM to reorient boards when tasks are safe-looking but outcome-light. `goalbuddy prompt` and the state checker emit non-fatal micro-slicing warnings without breaking old boards.
16
+ - **Hardened Codex plugin-only installs.** Codex install/update now use the native plugin path, refresh the bundled Scout/Judge/Worker agents, and leave stale personal `~/.codex/skills/goalbuddy` / `goal-maker` folders out of the expected clean state.
17
+ - **Fixed Codex doctor for plugin-only installs.** `goalbuddy doctor --target codex --goal-ready` now validates the plugin cache, bundled `$goal-prep` skill, enabled plugin config, and GoalBuddy agents instead of failing only because standalone personal skill folders are absent. The report also distinguishes native OpenAI-gated Codex `/goal` from GoalBuddy `$goal-prep` and local boards.
18
+ - **Made mutating command help safe.** `goalbuddy plugin install --help` and `goalbuddy update --help` print help without installing, updating, or touching global Codex/Claude files.
19
+
20
+ ## 0.3.5 — Subgoals, Parallel Agents, and Dark Mode (2026-05-12)
21
+
22
+ - **Subgoals for bounded branching work.** Parent tasks can link to depth-1 child `state.yaml` boards under `subgoals/`, the checker validates child shape and containment, and the local board renders the child board inside the parent task detail.
23
+ - **Parallel-agent-ready boards.** `goalbuddy parallel-plan` reports safe read-only Scout/Judge handoffs and Worker handoffs only when write scopes are known and disjoint. It does not mutate state or spawn agents.
24
+ - **Dark mode and a sharper live board.** The local board now has readable dark mode, global viewer settings, compact mode, completed-task collapse, a site-aligned header, GitHub stars, and active-card motion with reduced-motion handling.
25
+ - **Multi-board local hub navigation.** Multiple local boards share one readable `goalbuddy.localhost` hub with an in-header board selector, and parent boards stream updates when linked child subgoal state changes.
26
+ - **More durable execution plumbing.** Scout/Judge/Worker contracts are stricter, `goalbuddy prompt` emits compact task prompts, Worker write-scope checks fail closed for ambiguous overlap, and source/plugin tests cover the new branching and parallel-safety surfaces.
27
+
28
+ ## 0.3.2 — Harden Codex plugin cache updates (2026-05-11)
29
+
30
+ - **Fixed Codex plugin updates when stale preserved-extension folders exist.** The updater now ignores non-version cache directories like `.goalbuddy-preserved-extend-*` while selecting the active plugin skill, so a leftover temporary folder cannot make `npx goalbuddy update` fail with `Unsupported version`.
31
+ - **Stopped leaving empty preserved-extension folders during plugin reinstalls.** The updater only creates the temporary preservation directory when there is a custom extension to copy.
32
+
33
+ ## 0.3.1 — Fix duplicate /goal-prep slash entry (2026-05-11)
34
+
35
+ - **Fixed duplicate `/goal-prep` in the Claude Code slash menu.** Previous installs shipped both a `name: goal-prep` skill and a `commands/goal-prep.md` slash command, so Claude Code listed `/goal-prep` twice with different descriptions. The skill is now the single canonical surface for `/goal-prep`. Existing installs with `~/.claude/commands/goal-prep.md` are migrated automatically: `npx goalbuddy` (and `install` / `update`) removes the legacy file. `goalbuddy doctor --target claude` reports `legacy_command_present` and fails until the legacy file is gone.
36
+
37
+ ## 0.3.0 — Claude Code and Codex targets
38
+
39
+ GoalBuddy now installs into both **Codex** and **Claude Code** with a single `npx goalbuddy` run. The shared skill payload and `/goal` workflow are unchanged — this release adds a Claude Code target alongside the existing Codex one and reframes the project as "a /goal operating system for Codex and Claude Code."
40
+
41
+ ### Highlights
42
+
43
+ - **One command installs both targets.** `npx goalbuddy` installs and enables the native Codex plugin in `~/.codex/`, then installs the GoalBuddy skill, three Scout/Judge/Worker subagents, and the `/goal-prep` slash command into `~/.claude/`.
44
+ - **Target-specific installs remain available.** Use `npx goalbuddy --target codex` or `npx goalbuddy --target claude` when you only want one side.
45
+ - **Claude Code plugin scaffold** at `plugins/goalbuddy/.claude-plugin/plugin.json` with markdown subagents (`agents/goal-scout.md`, `agents/goal-judge.md`, `agents/goal-worker.md`) and a `/goal-prep` command (`commands/goal-prep.md`).
46
+ - **`$goal-prep` (Codex) and `/goal-prep` (Claude Code)** are documented as sibling entry points throughout the skill, README, site, and CLI.
47
+ - **Reframed README, site, plugin docs, package.json, and SKILL.md** to position the workflow as "a /goal operating system for Codex and Claude Code."
48
+ - **CLI is target-aware.** New flags: `--target codex|claude`, `--claude-home <path>`. Existing `--codex-home` and `CODEX_HOME` continue to work unchanged.
49
+ - **Update supports both targets.** `goalbuddy update` refreshes the Codex plugin and Claude Code skill/agents/command together unless `--target` narrows it.
50
+ - **Doctor checks both targets.** Default is Codex; `goalbuddy doctor --target claude` runs the Claude Code skill/agent/command check.
51
+
52
+ ### Compatibility
53
+
54
+ - `npx goalbuddy` with no flag now prepares Codex and Claude Code together. Existing Codex-only automation can keep using `--target codex` or `--codex-home`.
55
+ - `npx goal-maker` continues to work as a temporary alias and prints the new command.
56
+ - The shared `goalbuddy/SKILL.md` payload is unchanged in shape; the framing is now bilingual.
57
+
58
+ ### Tests
59
+
60
+ - All 46 tests pass.
61
+ - Help-text and version-arithmetic tests updated for the bilingual usage and the 0.3.0 bump.
62
+
63
+ ### Adding Or Updating Both
64
+
65
+ Install or refresh both supported agent environments:
66
+
67
+ ```bash
68
+ npx goalbuddy
69
+ npx goalbuddy update
70
+ ```
package/CONTRIBUTING.md CHANGED
@@ -39,11 +39,11 @@ Before opening a PR, verify the npm package contents:
39
39
  npm pack --dry-run
40
40
  ```
41
41
 
42
- The package should include `README.md`, `internal/assets/`, `package.json`, `internal/cli/`, the canonical `goalbuddy/` skill directory, and `plugins/goalbuddy/` (with both `.codex-plugin/` and `.claude-plugin/` manifests). The temporary `$goal-maker` compatibility skill is generated by the installer; do not add a second tracked skill payload.
42
+ The package should include `README.md`, `CHANGELOG.md`, `docs/releases/`, `internal/assets/`, `package.json`, `internal/cli/`, the canonical `goalbuddy/` skill directory, and `plugins/goalbuddy/` (with both `.codex-plugin/` and `.claude-plugin/` manifests). The temporary `$goal-maker` compatibility skill is generated by the installer; do not add a second tracked skill payload.
43
43
 
44
44
  ## Releases
45
45
 
46
- GoalBuddy publishes from GitHub Actions with npm trusted publishing. See [RELEASE.md](RELEASE.md) before creating a release.
46
+ GoalBuddy publishes from GitHub Actions with npm trusted publishing. See [docs/releases](docs/releases/README.md) before creating a release.
47
47
 
48
48
  ## Contribution Guidelines
49
49
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <p align="center">
4
4
  <a href="https://goalbuddy.dev">
5
- <img src="internal/assets/goalbuddy-v0.3.5-release.png" alt="GoalBuddy v0.3.5 release: Subgoals, parallel agents, and dark mode." width="100%">
5
+ <img src="internal/assets/goalbuddy-readme-hero.png" alt="GoalBuddy local board and agent workflow." width="100%">
6
6
  </a>
7
7
  </p>
8
8
 
@@ -63,6 +63,14 @@ To verify a Codex install:
63
63
  npx goalbuddy doctor --target codex --goal-ready
64
64
  ```
65
65
 
66
+ To remove GoalBuddy-owned Codex runtime surfaces:
67
+
68
+ ```bash
69
+ npx goalbuddy reset --target codex
70
+ ```
71
+
72
+ Native `codex plugin remove goalbuddy@goalbuddy` only removes the native plugin surface. GoalBuddy also owns the `goal_*.toml` agent files it installed, its Codex plugin cache, its marketplace entry, and old personal skill folders from earlier installs. Use `goalbuddy reset --target codex` when you want those GoalBuddy-owned files removed too.
73
+
66
74
  ## What It Creates
67
75
 
68
76
  ```text
@@ -144,7 +152,7 @@ Multiple local boards reuse one readable `goalbuddy.localhost` hub with an in-he
144
152
 
145
153
  Custom external integrations should be built as ordinary repo work with a concrete implementation plan, not installed from a GoalBuddy catalog.
146
154
 
147
- See [GoalBuddy 0.3.7: Goalmaxxed](RELEASE-0.3.7.md) for the latest release notes.
155
+ See [GoalBuddy 0.3.9: Marketplace and Board Runtime Polish](docs/releases/0.3.9.md) for the latest release notes.
148
156
 
149
157
  <p align="center">
150
158
  <img src="internal/assets/goalbuddy-live-board.jpg" alt="GoalBuddy local live board open next to Codex while Scout, Judge, and Worker tasks populate." width="100%">
@@ -164,7 +172,7 @@ GoalBuddy is MIT licensed and published on npm.
164
172
 
165
173
  The implementation lives in this repo, but the happy path is intentionally tiny: install it, run Goal Prep, then let `/goal` work from the generated files.
166
174
 
167
- For release process details, see [RELEASE.md](RELEASE.md).
175
+ For release process details, see [docs/releases](docs/releases/README.md).
168
176
 
169
177
  ## Star History
170
178
 
@@ -1,5 +1,7 @@
1
1
  # GoalBuddy 0.3.7: Goalmaxxed
2
2
 
3
+ ![GoalBuddy v0.3.7 release: Goalmaxxed.](https://raw.githubusercontent.com/tolibear/goalbuddy/v0.3.7/internal/assets/goalbuddy-v0.3.7-release.png)
4
+
3
5
  Release date: 2026-05-19
4
6
 
5
7
  Goalmaxxed is the release where GoalBuddy stops trying to become a workflow catalog and commits to one sharper job:
@@ -0,0 +1,40 @@
1
+ # GoalBuddy 0.3.8: Board Hub Guardrails
2
+
3
+ Release date: 2026-05-29
4
+
5
+ This patch release fixes a confusing local-board failure mode.
6
+
7
+ GoalBuddy already supports multiple local boards on one shared hub:
8
+
9
+ ```text
10
+ http://goalbuddy.localhost:41737/<slug>/
11
+ ```
12
+
13
+ The problem was the unregistered-path error. If an agent opened a new board URL before registering that goal with the hub, the server returned a bare 404. That made it too easy to infer that the process on `41737` was stale, even when it was a healthy multi-board hub for another goal.
14
+
15
+ ## What Changed
16
+
17
+ - Unregistered board paths now return an explicit diagnostic explaining that a `/slug/` 404 does not mean the hub is stale.
18
+ - The diagnostic points agents to `http://127.0.0.1:41737/api/boards` and tells them to rerun `npx goalbuddy board <goal-dir>` to register the goal on the same port.
19
+ - `$goal-prep` / `/goal-prep` now says to stop a process on `41737` only when `/api/boards` proves the listener is not a current GoalBuddy multi-board hub.
20
+ - `npm run check` now includes the local board surface tests and syntax checks.
21
+
22
+ ## Release Boundaries
23
+
24
+ This release does not change the board state model. `state.yaml` remains the source of truth, the local board remains a viewer over repo files, and multiple boards still share the same local hub.
25
+
26
+ ## Package Notes
27
+
28
+ This release updates:
29
+
30
+ - npm package version: `0.3.8`
31
+ - Codex plugin version: `0.3.8`
32
+ - Claude Code plugin version: `0.3.8`
33
+ - release checks to cover `goalbuddy/surfaces/local-goal-board/`
34
+
35
+ Before publishing, verify:
36
+
37
+ ```bash
38
+ npm run check
39
+ npm run publish:check
40
+ ```
@@ -0,0 +1,46 @@
1
+ # GoalBuddy 0.3.9: Marketplace and Board Runtime Polish
2
+
3
+ GoalBuddy 0.3.9 prepares the package for cleaner Claude marketplace installs and tightens the runtime behavior around local boards, approval gates, and board-health stewardship.
4
+
5
+ ## Highlights
6
+
7
+ - **Claude marketplace wrapper.** The repo now includes a root `.claude-plugin/marketplace.json` so Claude Code can discover the existing `plugins/goalbuddy` plugin through marketplace install flow.
8
+ - **Install-channel agnostic Goal Prep.** `/goal-prep` no longer assumes a global `goalbuddy` or `npx goalbuddy` binary for model-invoked board, prompt, and parallel-plan commands. It uses bundled skill scripts from `<skill-path>` instead.
9
+ - **Better update and agent advice.** `check-update` detects common install channels and falls back to “use the install channel that installed GoalBuddy” instead of guessing wrong. Agent availability warnings now use the same channel-neutral language.
10
+ - **Less board flicker.** Local board file watching now waits for rapid `state.yaml` transition writes to settle before rebuilding the streamed payload.
11
+ - **Parallel active tasks render.** The local board can display multiple active tasks in the In Progress column, which keeps parallel/disjoint work visible instead of failing the entire board render.
12
+ - **Exact approval wait state.** When the only remaining action is an exact human approval phrase, GoalBuddy now has an explicit terminal wait shape: ask once, preserve `required_reply`, set `waiting_for_user_approval: true`, and stop until the user replies.
13
+ - **Board health stewardship.** The PM-owned stewardship contract is now documented: run the bundled state checker, compare the live board API when available, and repair only GoalBuddy control files unless an active task explicitly allows product-file edits.
14
+
15
+ ## Why This Release Matters
16
+
17
+ The Claude marketplace wrapper from 0.3.9 makes marketplace installs viable, but marketplace installs do not guarantee a shell-level `goalbuddy` binary. This release closes that gap by moving model-run commands onto the bundled scripts that ship inside the installed skill.
18
+
19
+ The board changes also make GoalBuddy more honest during real runs: transient task switches do not flash false errors, and valid parallel work stays visible.
20
+
21
+ ## Verification
22
+
23
+ - `npm run check`
24
+ - `git diff --check`
25
+ - `node --check plugins/goalbuddy/skills/goalbuddy/scripts/check-update.mjs plugins/goalbuddy/skills/goalbuddy/scripts/check-goal-state.mjs`
26
+ - `GOALBUDDY_TEST_NPM_LATEST_VERSION=99.0.0 GOALBUDDY_TEST_UPDATE_COMMAND='/plugin update goalbuddy@goalbuddy' node plugins/goalbuddy/skills/goalbuddy/scripts/check-update.mjs --json`
27
+ - `npm pack --dry-run --json`
28
+
29
+ ## Package Metadata
30
+
31
+ - npm package version: `0.3.9`
32
+ - Codex plugin version: `0.3.9`
33
+ - Claude Code plugin version: `0.3.9`
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ npx goalbuddy
39
+ ```
40
+
41
+ For Claude Code marketplace installs:
42
+
43
+ ```text
44
+ /plugin marketplace add tolibear/goalbuddy
45
+ /plugin install goalbuddy@goalbuddy
46
+ ```
@@ -0,0 +1,84 @@
1
+ # Release Process
2
+
3
+ Historical release notes live next to this process doc:
4
+
5
+ - [0.3.9: Marketplace and Board Runtime Polish](0.3.9.md)
6
+ - [0.3.8: Board Hub Guardrails](0.3.8.md)
7
+ - [0.3.7: Goalmaxxed](0.3.7.md)
8
+ - [0.3.5: Subgoals, Parallel Agents, and Dark Mode](0.3.5.md)
9
+
10
+ GoalBuddy publishes the `goalbuddy` npm package from GitHub Actions using npm trusted publishing. This avoids long-lived npm write tokens and lets npm generate provenance for future releases.
11
+
12
+ ## One-Time npm Setup
13
+
14
+ Configure this on npmjs.com for the `goalbuddy` package:
15
+
16
+ - Publisher: GitHub Actions
17
+ - GitHub owner/user: `tolibear`
18
+ - Repository: `goalbuddy`
19
+ - Workflow filename: `npm-publish.yml`
20
+ - Package: `goalbuddy`
21
+
22
+ The workflow path in this repo is:
23
+
24
+ ```text
25
+ .github/workflows/npm-publish.yml
26
+ ```
27
+
28
+ Or configure the same trust relationship from the npm CLI:
29
+
30
+ ```bash
31
+ npx --yes npm@11.13.0 trust github goalbuddy \
32
+ --repo tolibear/goalbuddy \
33
+ --file npm-publish.yml \
34
+ --yes
35
+ ```
36
+
37
+ This command requires npm owner authentication and may print an `EOTP` browser/OTP URL. Complete that npm authentication step, then rerun the same command if needed. The `npx --yes npm@11.13.0 trust ...` form is intentional; using `npx -p npm@latest npm trust ...` can resolve to an older global npm binary that does not expose the `trust` command.
38
+
39
+ After the trusted publisher works, use npm package settings to require 2FA and disallow tokens for publishing. Keep `goal-maker` published during the migration window.
40
+
41
+ Starting in `0.3.0`, the installer is target-aware: `npx goalbuddy` installs into both `~/.codex/` and `~/.claude/`, and `goalbuddy update` refreshes both by default. Use `--target codex` or `--target claude` to narrow a command. Both targets share the same `goalbuddy/` skill payload and are exercised by the test suite under `internal/test/`.
42
+
43
+ ## Release Flow
44
+
45
+ 1. Update `package.json` version.
46
+ 2. Run local checks:
47
+
48
+ ```bash
49
+ npm run check
50
+ npm run pack:dry-run
51
+ node internal/cli/check-publish-version.mjs
52
+ ```
53
+
54
+ 3. Commit and push the version change.
55
+ 4. Create and publish a GitHub release whose tag matches the package version, for example `v0.2.11`.
56
+ 5. Confirm the GitHub Actions workflow `Publish npm package` completed.
57
+ 6. Verify npm:
58
+
59
+ ```bash
60
+ npm view goalbuddy name version dist-tags repository bin --json
61
+ npx goalbuddy --help
62
+ npx goalbuddy doctor --target codex
63
+ npx goalbuddy doctor --target claude
64
+ ```
65
+
66
+ ## Provenance Expectations
67
+
68
+ npm trusted publishing requires a GitHub-hosted runner, Node `22.14.0` or newer, npm `11.5.1` or newer, and `id-token: write` workflow permission. The release workflow uses Node 24 and grants the OIDC permission required by npm.
69
+
70
+ When publishing through trusted publishing from this public repo to the public `goalbuddy` package, npm should generate provenance automatically. The workflow intentionally runs `npm publish` without `NODE_AUTH_TOKEN`; npm exchanges the GitHub OIDC identity for a short-lived publish credential.
71
+
72
+ ## Compatibility Package
73
+
74
+ Do not unpublish `goal-maker`. During the 60-90 day compatibility window, `npx goal-maker` should continue to work and point users to:
75
+
76
+ ```bash
77
+ npx goalbuddy
78
+ ```
79
+
80
+ After the compatibility window:
81
+
82
+ ```bash
83
+ npm deprecate goal-maker "Renamed to goalbuddy. Use: npx goalbuddy"
84
+ ```
@@ -58,7 +58,7 @@ node <skill-path>/scripts/check-update.mjs --json
58
58
  If the checker reports `update_available: true`, tell the user once before continuing:
59
59
 
60
60
  ```text
61
- GoalBuddy <latest_version> is available. After this turn, update with: npx goalbuddy
61
+ GoalBuddy <latest_version> is available. After this turn, update through the channel that installed GoalBuddy: `/plugin update goalbuddy@goalbuddy`, `npx goalbuddy@latest`, `npm i -g goalbuddy`, `pnpm update -g goalbuddy`, `bun update -g goalbuddy`, or `mise upgrade npm:goalbuddy`.
62
62
  ```
63
63
 
64
64
  Do not block intake or board creation on update checking. If the checker is missing, cannot find npm, or network access fails, continue silently unless the user asked about updates.
@@ -93,7 +93,9 @@ Recommended options:
93
93
  1. Local live board (Recommended) - starts immediately, requires no credentials, and lets the user watch tasks populate inside Codex or Claude Code.
94
94
  2. No visual board - best for quick or private goals where the file board is enough.
95
95
 
96
- If the user chooses the local live board, create the goal directory, `notes/`, and an initial minimal `state.yaml` as soon as the slug is known, then run `npx goalbuddy board docs/goals/<slug>` and open the printed local URL in the AI coding agent's in-app browser (the Codex in-app Browser, the Claude Code preview, or the user's regular browser). The default local hub is `http://goalbuddy.localhost:41737/`, and board URLs normally look like `http://goalbuddy.localhost:41737/<slug>/`. In short: start the local board before filling the task list so the board pops up right away and cards populate live as `state.yaml` changes. Include the printed board URL in the final prep response as an actual clickable Markdown link, for example `[Open GoalBuddy board](http://goalbuddy.localhost:41737/<slug>/)`. Do not put the board URL only in a code block, quote, HTML comment, or prose that the UI cannot click.
96
+ If the user chooses the local live board, create the goal directory, `notes/`, and an initial minimal `state.yaml` as soon as the slug is known, then run `node <skill-path>/surfaces/local-goal-board/scripts/local-goal-board.mjs --goal docs/goals/<slug>` and open the printed local URL in the AI coding agent's in-app browser (the Codex in-app Browser, the Claude Code preview, or the user's regular browser). The default local hub is `http://goalbuddy.localhost:41737/`, and board URLs normally look like `http://goalbuddy.localhost:41737/<slug>/`. In short: start the local board before filling the task list so the board pops up right away and cards populate live as `state.yaml` changes. Include the printed board URL in the final prep response as an actual clickable Markdown link, for example `[Open GoalBuddy board](http://goalbuddy.localhost:41737/<slug>/)`. Do not put the board URL only in a code block, quote, HTML comment, or prose that the UI cannot click.
97
+
98
+ If `http://goalbuddy.localhost:41737/<slug>/` returns 404, do not assume the existing process is stale and do not stop it. First check `http://127.0.0.1:41737/api/boards`. If that endpoint returns board JSON, the port is the shared multi-board hub; rerun `node <skill-path>/surfaces/local-goal-board/scripts/local-goal-board.mjs --goal <absolute-goal-path>` if needed so the new goal registers on the same port. Only stop a specific process on 41737 when `/api/boards` is missing, returns 404, or otherwise proves the listener is not a current GoalBuddy multi-board hub.
97
99
 
98
100
  If the user wants an external board, GitHub sync, Slack digest, Linear handoff, or any other custom integration, do not install a GoalBuddy catalog item. Treat it as normal implementation work: create a concrete task that designs and verifies that integration inside the target repo or asks the operator for the required credentials and scope.
99
101
 
@@ -472,6 +474,22 @@ Blocked tasks do not necessarily block the goal. The PM should keep doing safe l
472
474
 
473
475
  Avoid setting `goal.status: blocked` for missing input, credentials, production access, destructive-operation permission, or policy decisions. Block the specific task instead, record the missing requirement, and continue with every safe local workaround or adjacent slice.
474
476
 
477
+ Exception: if an exact human approval phrase is the only remaining blocker and no safe local work remains, ask once, preserve the exact phrase, and stop. Set `goal.status: blocked`, set `active_task: null`, mark every unfinished task `blocked`, and write a receipt with `result: blocked`, `waiting_for_user_approval: true`, and `required_reply: "<exact phrase>"`. Do not rephrase, retry, spawn follow-up work, or post another approval prompt until the user replies.
478
+
479
+ ## Board Health Stewardship
480
+
481
+ The PM owns board health. Do not auto-spawn a separate always-on steward by default.
482
+
483
+ When the board looks stale, misleading, offline, Not Found, or inconsistent, run the bundled checker:
484
+
485
+ ```bash
486
+ node <skill-path>/scripts/check-goal-state.mjs docs/goals/<slug>
487
+ ```
488
+
489
+ If a local board server is running, compare `state.yaml` with `http://127.0.0.1:41737/<slug>/api/board` or `http://127.0.0.1:41737/api/boards`. Repair only GoalBuddy control files: `goal.md`, `state.yaml`, `notes/`, depth-1 `subgoals/`, and `.goalbuddy-board/`. Never edit product implementation files during board-health work unless there is an active Worker or PM task with explicit `allowed_files`.
490
+
491
+ Board-health work should verify these truths: `active_task` matches live task status, done and blocked tasks have receipts, human-blocked work is in the blocked column, future work stays queued, and the live board/API reflects `state.yaml`.
492
+
475
493
  ## Operator Escalation
476
494
 
477
495
  When Scout, Judge, Worker, or PM discovers a problem, improvement opportunity, product suggestion, follow-up repair, or tool limitation that should not be fixed inside the current active task, do not let it disappear in chat.
@@ -526,9 +544,9 @@ Use these `state.yaml` values:
526
544
  | State | Meaning | Next action |
527
545
  |---|---|---|
528
546
  | `installed` | Matching Scout/Worker/Judge agent configs were found in the expected user or project agent location. | Continue. |
529
- | `bundled_not_installed` | The bundled `goal_*.toml` template exists with the skill, but no matching installed agent config was verified. | `/goal` can proceed through PM fallback. If dedicated agents are required before `/goal`, run `npx goalbuddy agents`. |
530
- | `missing` | Neither an installed config nor the bundled template was verified. | `/goal` can proceed through PM fallback. If dedicated agents are required before `/goal`, run `npx goalbuddy install`. |
531
- | `unknown` | Agent availability could not be checked. | `/goal` can proceed through PM fallback. To check before `/goal`, run `npx goalbuddy doctor`. |
547
+ | `bundled_not_installed` | The bundled `goal_*.toml` template exists with the skill, but no matching installed agent config was verified. | `/goal` can proceed through PM fallback. If dedicated agents are required before `/goal`, run the GoalBuddy CLI through the user's install channel with `agents`. |
548
+ | `missing` | Neither an installed config nor the bundled template was verified. | `/goal` can proceed through PM fallback. If dedicated agents are required before `/goal`, run the GoalBuddy CLI through the user's install channel with `install`. |
549
+ | `unknown` | Agent availability could not be checked. | `/goal` can proceed through PM fallback. To check before `/goal`, run the GoalBuddy CLI through the user's install channel with `doctor`. |
532
550
 
533
551
  Non-`installed` states are warnings, not false failures, because the main `/goal` PM can perform Scout/Judge/Worker-shaped tasks directly when dedicated agents are unavailable.
534
552
 
@@ -568,11 +586,11 @@ Treat `reasoning_hint` as PM guidance. It does not override task scope, write pe
568
586
 
569
587
  ## Execution Quality Commands
570
588
 
571
- Use `goalbuddy prompt docs/goals/<slug>` to render a compact prompt for the active task. The prompt includes only task-specific material, safe agent metadata, continuation warnings, and the expected receipt shape. It should not include broad chat history or dump the whole state file.
589
+ Use `node <skill-path>/scripts/render-task-prompt.mjs docs/goals/<slug>` to render a compact prompt for the active task. The prompt includes only task-specific material, safe agent metadata, continuation warnings, and the expected receipt shape. It should not include broad chat history or dump the whole state file.
572
590
 
573
- When dispatching Codex subagents from a GoalBuddy prompt, the `required_spawn_agent_type` is mandatory. Use that exact `spawn_agent` `agent_type` (`goal_scout`, `goal_worker`, or `goal_judge`). Do not substitute generic `scout`, `worker`, or `judge` agents; if the required GoalBuddy agent is unavailable, stop spawning and continue as PM fallback or run `npx goalbuddy agents`/`npx goalbuddy install`. After one `wait_agent` timeout with no visible allowed-file changes, stop waiting, record the timeout, and recover deterministically instead of waiting forever.
591
+ When dispatching Codex subagents from a GoalBuddy prompt, the `required_spawn_agent_type` is mandatory. Use that exact `spawn_agent` `agent_type` (`goal_scout`, `goal_worker`, or `goal_judge`). Do not substitute generic `scout`, `worker`, or `judge` agents; if the required GoalBuddy agent is unavailable, stop spawning and continue as PM fallback or ask the operator to run the GoalBuddy CLI through their install channel with `agents` or `install`. After one `wait_agent` timeout with no visible allowed-file changes, stop waiting, record the timeout, and recover deterministically instead of waiting forever.
574
592
 
575
- Use `goalbuddy parallel-plan docs/goals/<slug>` when the user explicitly asks for parallel agent work. It is read-only: it recommends safe Scout/Judge handoffs and Worker handoffs only when write scopes are known and disjoint. It does not mutate `state.yaml`, create sub-goals, apply receipts, or spawn agents.
593
+ Use `node <skill-path>/scripts/parallel-plan.mjs docs/goals/<slug>` when the user explicitly asks for parallel agent work. It is read-only: it recommends safe Scout/Judge handoffs and Worker handoffs only when write scopes are known and disjoint. It does not mutate `state.yaml`, create sub-goals, apply receipts, or spawn agents.
576
594
 
577
595
  ## Completion
578
596
 
@@ -297,12 +297,12 @@ if (isWeakProof(completionProof)) {
297
297
  function agentStatusWarning(agent, status) {
298
298
  const agentLabel = agent[0].toUpperCase() + agent.slice(1);
299
299
  if (status === "bundled_not_installed") {
300
- return `agents.${agent} is bundled_not_installed; /goal can continue through PM fallback, but dedicated ${agentLabel} delegation is unavailable until installed. If dedicated agents are required before /goal, run: npx goalbuddy agents`;
300
+ return `agents.${agent} is bundled_not_installed; /goal can continue through PM fallback, but dedicated ${agentLabel} delegation is unavailable until installed. If dedicated agents are required before /goal, run the GoalBuddy CLI through the user's install channel with: agents`;
301
301
  }
302
302
  if (status === "missing") {
303
- return `agents.${agent} is missing; /goal can continue through PM fallback, but dedicated ${agentLabel} delegation is unavailable. If dedicated agents are required before /goal, run: npx goalbuddy install`;
303
+ return `agents.${agent} is missing; /goal can continue through PM fallback, but dedicated ${agentLabel} delegation is unavailable. If dedicated agents are required before /goal, run the GoalBuddy CLI through the user's install channel with: install`;
304
304
  }
305
- return `agents.${agent} is unknown; /goal can continue through PM fallback, but dedicated ${agentLabel} delegation was not verified. To check before /goal, run: npx goalbuddy doctor`;
305
+ return `agents.${agent} is unknown; /goal can continue through PM fallback, but dedicated ${agentLabel} delegation was not verified. To check before /goal, run the GoalBuddy CLI through the user's install channel with: doctor`;
306
306
  }
307
307
 
308
308
  for (const { agent, status } of agentStatuses) {
@@ -353,6 +353,7 @@ for (const task of tasks) {
353
353
  if (tasks.length === 0) errors.push("tasks must contain at least one task");
354
354
 
355
355
  const activeTasks = tasks.filter((task) => task.status === "active");
356
+ const terminalApprovalWait = isTerminalApprovalWait(tasks, activeTasks, activeTask);
356
357
  if (goalStatus === "done") {
357
358
  if (activeTasks.length !== 0) errors.push("done goals must not have an active task");
358
359
  if (activeTask !== null) errors.push("done goals must set active_task: null");
@@ -364,7 +365,7 @@ if (goalStatus === "done") {
364
365
  }
365
366
  } else if (goalStatus === "blocked") {
366
367
  if (activeTasks.length > 1) errors.push("blocked goals may have at most one active task");
367
- if (continuousUntilFullOutcome && missingInputOrCredentialsDoNotStopGoal) {
368
+ if (continuousUntilFullOutcome && missingInputOrCredentialsDoNotStopGoal && !terminalApprovalWait) {
368
369
  errors.push("continuous goals must keep goal.status active; missing input or credentials should block specific tasks, not the whole goal");
369
370
  }
370
371
  } else if (activeTasks.length !== 1) {
@@ -436,6 +437,23 @@ for (const task of tasks) {
436
437
 
437
438
  warnings.push(...microSliceWarnings(tasks, activeTask, goalStatus));
438
439
 
440
+ function isTerminalApprovalWait(tasks, activeTasks, activeTask) {
441
+ if (goalStatus !== "blocked") return false;
442
+ if (activeTask !== null) return false;
443
+ if (activeTasks.length !== 0) return false;
444
+
445
+ const unfinishedTasks = tasks.filter((task) => task.status !== "done");
446
+ if (unfinishedTasks.length === 0) return false;
447
+ if (unfinishedTasks.some((task) => task.status !== "blocked")) return false;
448
+
449
+ return unfinishedTasks.some((task) => {
450
+ if (!task.receipt.present || task.receipt.value === null) return false;
451
+ return task.receipt.scalar("result") === "blocked"
452
+ && task.receipt.scalar("waiting_for_user_approval") === true
453
+ && Boolean(task.receipt.scalar("required_reply"));
454
+ });
455
+ }
456
+
439
457
  function validateSubgoal(task) {
440
458
  if (isChildCheck) {
441
459
  errors.push(`child task ${task.id} must not contain a nested subgoal`);
@@ -14,7 +14,7 @@ const report = {
14
14
  latest_version: null,
15
15
  update_available: false,
16
16
  check_status: "unknown",
17
- update_command: "npx goalbuddy",
17
+ update_command: detectUpdateCommand(),
18
18
  };
19
19
 
20
20
  try {
@@ -77,6 +77,23 @@ function latestPublishedVersion() {
77
77
  return normalizeVersion(result.stdout);
78
78
  }
79
79
 
80
+ function detectUpdateCommand() {
81
+ if (process.env.GOALBUDDY_TEST_UPDATE_COMMAND) return process.env.GOALBUDDY_TEST_UPDATE_COMMAND;
82
+ if (process.env.CLAUDE_PLUGIN_ROOT || normalizedPath(scriptDir).includes("/.claude/")) return "/plugin update goalbuddy@goalbuddy";
83
+
84
+ const userAgent = process.env.npm_config_user_agent || "";
85
+ if (/^pnpm\//.test(userAgent)) return "pnpm update -g goalbuddy";
86
+ if (/^bun\//.test(userAgent)) return "bun update -g goalbuddy";
87
+ if (process.env.MISE_EXE || process.env.MISE_SHELL || process.env.MISE_PROJECT_ROOT) return "mise upgrade npm:goalbuddy";
88
+ if (/^npm\//.test(userAgent)) return "npx goalbuddy@latest";
89
+
90
+ return "use the install channel that installed GoalBuddy";
91
+ }
92
+
93
+ function normalizedPath(path) {
94
+ return String(path).replace(/\\/g, "/");
95
+ }
96
+
80
97
  function readJson(path) {
81
98
  if (!existsSync(path)) return null;
82
99
  try {
@@ -237,29 +237,43 @@ function receiptSchema(role) {
237
237
  if (role === "worker") {
238
238
  return {
239
239
  result: "done | blocked",
240
+ task_id: "<T###>",
241
+ board_path: "<path to state.yaml>",
240
242
  changed_files: [],
241
- commands: [{ cmd: "<command>", status: "pass | fail | not_run" }],
242
- summary: "<=120 words",
243
+ commands: [],
244
+ summary: "<=120 words>",
243
245
  remaining_blockers: [],
246
+ verification_attempts: 1,
247
+ stopped_because: null,
244
248
  };
245
249
  }
246
250
  if (role === "judge") {
247
251
  return {
248
252
  result: "done | blocked",
253
+ task_id: "<T###>",
254
+ board_path: "<path to state.yaml>",
249
255
  decision: "approved | rejected | approve_subgoal | reject_subgoal | not_complete | complete",
250
256
  full_outcome_complete: false,
257
+ rationale: "<=120 words>",
251
258
  evidence: [],
259
+ subgoal_contract: null,
260
+ parallel_safety: null,
252
261
  blocked_tasks: [],
262
+ missing_evidence: [],
253
263
  required_board_updates: [],
254
264
  };
255
265
  }
256
266
  return {
257
267
  result: "done | blocked",
258
- summary: "<=120 words",
268
+ task_id: "<T###>",
269
+ board_path: "<path to state.yaml>",
270
+ summary: "<=120 words>",
259
271
  evidence: [],
260
272
  facts: [],
261
273
  contradictions: [],
262
274
  ambiguity_requiring_judge: [],
275
+ commands: [],
276
+ note_needed: false,
263
277
  };
264
278
  }
265
279
 
@@ -88,9 +88,6 @@ export function normalizeGoalBoard(document, goalDir = "<memory>") {
88
88
 
89
89
  const tasks = document.tasks.map((task, index) => normalizeTask(task, index));
90
90
  const activeTasks = tasks.filter((task) => task.status === "active");
91
- if (activeTasks.length > 1) {
92
- throw new GoalBoardError("Goal state has more than one active task.");
93
- }
94
91
 
95
92
  return {
96
93
  goalDir,
@@ -142,13 +139,13 @@ export function buildColumns(tasks) {
142
139
  byColumn.get(task.column).push(task);
143
140
  }
144
141
 
145
- for (const columnTasks of byColumn.values()) {
146
- columnTasks.sort((left, right) => taskSortKey(left).localeCompare(taskSortKey(right)));
142
+ for (const [columnId, columnTasks] of byColumn.entries()) {
143
+ columnTasks.sort((left, right) => compareColumnTasks(columnId, left, right));
147
144
  }
148
145
 
149
146
  return [
150
147
  { id: "todo", title: "Todo", description: "Queued work ready to pull", tasks: byColumn.get("todo") },
151
- { id: "in-progress", title: "In Progress", description: "The active task", tasks: byColumn.get("in-progress") },
148
+ { id: "in-progress", title: "In Progress", description: "Active task work", tasks: byColumn.get("in-progress") },
152
149
  { id: "blocked", title: "Blocked", description: "Needs unblock or a smaller slice", tasks: byColumn.get("blocked") },
153
150
  { id: "completed", title: "Completed", description: "Receipted work", tasks: byColumn.get("completed") },
154
151
  ];
@@ -289,7 +286,7 @@ function titleForTask(task) {
289
286
  function compactTaskTitle(value) {
290
287
  const text = cleanText(value).replace(/\.$/, "");
291
288
  const routeMatch = text.match(/^Implement\b.*?\s(\/[A-Za-z0-9_./:-]+)\s+(route|queue slice|slice)\b/i);
292
- if (routeMatch) return truncateTitle(`Implement ${routeMatch[1]} ${routeMatch[2]}`);
289
+ if (routeMatch) return `Implement ${routeMatch[1]} ${routeMatch[2]}`;
293
290
 
294
291
  const firstClause = text
295
292
  .split(/(?<=[.!?])\s+|\s+(?:Use only|Add|Match|Render|Clearly label|Do not)\b/i)[0]
@@ -300,14 +297,7 @@ function compactTaskTitle(value) {
300
297
  .replace(/[.;:,]\s*$/, "")
301
298
  .trim();
302
299
 
303
- return truncateTitle(firstClause || text);
304
- }
305
-
306
- function truncateTitle(value, maxLength = 82) {
307
- const text = cleanText(value).replace(/\.$/, "");
308
- if (text.length <= maxLength) return text;
309
- const shortened = text.slice(0, maxLength + 1).replace(/\s+\S*$/, "").trim();
310
- return `${shortened || text.slice(0, maxLength).trim()}...`;
300
+ return firstClause || text;
311
301
  }
312
302
 
313
303
  function columnForStatus(status) {
@@ -322,6 +312,12 @@ function taskSortKey(task) {
322
312
  return `${rank}:${task.id}`;
323
313
  }
324
314
 
315
+ function compareColumnTasks(columnId, left, right) {
316
+ const order = taskSortKey(left).localeCompare(taskSortKey(right));
317
+ if (columnId === "completed") return -order;
318
+ return order;
319
+ }
320
+
325
321
  function normalizeStringList(value) {
326
322
  if (!value) return [];
327
323
  if (Array.isArray(value)) return value.map(cleanText).filter(Boolean);
@@ -1503,8 +1499,13 @@ h1 {
1503
1499
  .task-title {
1504
1500
  margin: 0;
1505
1501
  color: #2f3437;
1502
+ display: -webkit-box;
1506
1503
  font-size: 15px;
1507
1504
  line-height: 1.35;
1505
+ overflow: hidden;
1506
+ overflow-wrap: anywhere;
1507
+ -webkit-box-orient: vertical;
1508
+ -webkit-line-clamp: 5;
1508
1509
  }
1509
1510
 
1510
1511
  .card-footer {