claws-code 0.8.0

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 (180) hide show
  1. package/.claude/commands/claws-auto.md +90 -0
  2. package/.claude/commands/claws-bin.md +28 -0
  3. package/.claude/commands/claws-cleanup.md +28 -0
  4. package/.claude/commands/claws-do.md +82 -0
  5. package/.claude/commands/claws-fix.md +40 -0
  6. package/.claude/commands/claws-goal.md +111 -0
  7. package/.claude/commands/claws-help.md +54 -0
  8. package/.claude/commands/claws-plan.md +103 -0
  9. package/.claude/commands/claws-report.md +29 -0
  10. package/.claude/commands/claws-status.md +37 -0
  11. package/.claude/commands/claws-update.md +32 -0
  12. package/.claude/commands/claws.md +64 -0
  13. package/.claude/rules/claws-default-behavior.md +76 -0
  14. package/.claude/settings.json +112 -0
  15. package/.claude/settings.local.json +19 -0
  16. package/.claude/skills/claws-auto-engine/SKILL.md +97 -0
  17. package/.claude/skills/claws-goal-tracker/SKILL.md +106 -0
  18. package/.claude/skills/claws-prompt-templates/SKILL.md +203 -0
  19. package/.claude/skills/claws-wave-lead/SKILL.md +126 -0
  20. package/.claude/skills/claws-wave-subworker/SKILL.md +60 -0
  21. package/CHANGELOG.md +1949 -0
  22. package/LICENSE +21 -0
  23. package/README.md +420 -0
  24. package/bin/cli.js +84 -0
  25. package/cli.js +223 -0
  26. package/docs/ARCHITECTURE.md +511 -0
  27. package/docs/event-protocol.md +588 -0
  28. package/docs/features.md +562 -0
  29. package/docs/guide.md +891 -0
  30. package/docs/index.html +716 -0
  31. package/docs/protocol.md +323 -0
  32. package/extension/.vscodeignore +15 -0
  33. package/extension/CHANGELOG.md +1906 -0
  34. package/extension/LICENSE +21 -0
  35. package/extension/README.md +137 -0
  36. package/extension/docs/features.md +424 -0
  37. package/extension/docs/protocol.md +197 -0
  38. package/extension/esbuild.mjs +25 -0
  39. package/extension/icon.png +0 -0
  40. package/extension/native/.metadata.json +10 -0
  41. package/extension/native/node-pty/LICENSE +69 -0
  42. package/extension/native/node-pty/README.md +165 -0
  43. package/extension/native/node-pty/lib/conpty_console_list_agent.js +16 -0
  44. package/extension/native/node-pty/lib/conpty_console_list_agent.js.map +1 -0
  45. package/extension/native/node-pty/lib/eventEmitter2.js +47 -0
  46. package/extension/native/node-pty/lib/eventEmitter2.js.map +1 -0
  47. package/extension/native/node-pty/lib/index.js +52 -0
  48. package/extension/native/node-pty/lib/index.js.map +1 -0
  49. package/extension/native/node-pty/lib/interfaces.js +7 -0
  50. package/extension/native/node-pty/lib/interfaces.js.map +1 -0
  51. package/extension/native/node-pty/lib/shared/conout.js +11 -0
  52. package/extension/native/node-pty/lib/shared/conout.js.map +1 -0
  53. package/extension/native/node-pty/lib/terminal.js +190 -0
  54. package/extension/native/node-pty/lib/terminal.js.map +1 -0
  55. package/extension/native/node-pty/lib/types.js +7 -0
  56. package/extension/native/node-pty/lib/types.js.map +1 -0
  57. package/extension/native/node-pty/lib/unixTerminal.js +346 -0
  58. package/extension/native/node-pty/lib/unixTerminal.js.map +1 -0
  59. package/extension/native/node-pty/lib/utils.js +39 -0
  60. package/extension/native/node-pty/lib/utils.js.map +1 -0
  61. package/extension/native/node-pty/lib/windowsConoutConnection.js +125 -0
  62. package/extension/native/node-pty/lib/windowsConoutConnection.js.map +1 -0
  63. package/extension/native/node-pty/lib/windowsPtyAgent.js +320 -0
  64. package/extension/native/node-pty/lib/windowsPtyAgent.js.map +1 -0
  65. package/extension/native/node-pty/lib/windowsTerminal.js +199 -0
  66. package/extension/native/node-pty/lib/windowsTerminal.js.map +1 -0
  67. package/extension/native/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  68. package/extension/native/node-pty/lib/worker/conoutSocketWorker.js.map +1 -0
  69. package/extension/native/node-pty/package.json +64 -0
  70. package/extension/native/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  71. package/extension/native/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  72. package/extension/native/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  73. package/extension/native/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  74. package/extension/native/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  75. package/extension/native/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  76. package/extension/native/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
  77. package/extension/native/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  78. package/extension/native/node-pty/prebuilds/win32-arm64/pty.node +0 -0
  79. package/extension/native/node-pty/prebuilds/win32-arm64/winpty-agent.exe +0 -0
  80. package/extension/native/node-pty/prebuilds/win32-arm64/winpty.dll +0 -0
  81. package/extension/native/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  82. package/extension/native/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  83. package/extension/native/node-pty/prebuilds/win32-x64/conpty.node +0 -0
  84. package/extension/native/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
  85. package/extension/native/node-pty/prebuilds/win32-x64/pty.node +0 -0
  86. package/extension/native/node-pty/prebuilds/win32-x64/winpty-agent.exe +0 -0
  87. package/extension/native/node-pty/prebuilds/win32-x64/winpty.dll +0 -0
  88. package/extension/package-lock.json +605 -0
  89. package/extension/package.json +343 -0
  90. package/extension/scripts/bundle-native.mjs +104 -0
  91. package/extension/scripts/deploy-dev.mjs +60 -0
  92. package/extension/src/ansi-strip.ts +52 -0
  93. package/extension/src/backends/vscode/claws-pty.ts +483 -0
  94. package/extension/src/backends/vscode/status-bar.ts +99 -0
  95. package/extension/src/backends/vscode/vscode-backend.ts +282 -0
  96. package/extension/src/capture-store.ts +125 -0
  97. package/extension/src/event-log.ts +629 -0
  98. package/extension/src/event-schemas.ts +478 -0
  99. package/extension/src/extension.js +492 -0
  100. package/extension/src/extension.ts +873 -0
  101. package/extension/src/lifecycle-engine.ts +60 -0
  102. package/extension/src/lifecycle-rules.ts +171 -0
  103. package/extension/src/lifecycle-store.ts +506 -0
  104. package/extension/src/peer-registry.ts +176 -0
  105. package/extension/src/pipeline-registry.ts +82 -0
  106. package/extension/src/platform.ts +64 -0
  107. package/extension/src/protocol.ts +532 -0
  108. package/extension/src/server-config.ts +98 -0
  109. package/extension/src/server.ts +2210 -0
  110. package/extension/src/task-registry.ts +51 -0
  111. package/extension/src/terminal-backend.ts +211 -0
  112. package/extension/src/terminal-manager.ts +395 -0
  113. package/extension/src/topic-registry.ts +70 -0
  114. package/extension/src/topic-utils.ts +46 -0
  115. package/extension/src/transport.ts +45 -0
  116. package/extension/src/uninstall-cleanup.ts +232 -0
  117. package/extension/src/wave-registry.ts +314 -0
  118. package/extension/src/websocket-transport.ts +153 -0
  119. package/extension/tsconfig.json +23 -0
  120. package/lib/capabilities.js +145 -0
  121. package/lib/dry-run.js +43 -0
  122. package/lib/install.js +1018 -0
  123. package/lib/mcp-setup.js +92 -0
  124. package/lib/platform.js +240 -0
  125. package/lib/preflight.js +152 -0
  126. package/lib/shell-hook.js +343 -0
  127. package/lib/uninstall.js +162 -0
  128. package/lib/verify.js +166 -0
  129. package/mcp_server.js +3529 -0
  130. package/package.json +48 -0
  131. package/rules/claws-default-behavior.md +72 -0
  132. package/scripts/_helpers/atomic-file.mjs +137 -0
  133. package/scripts/_helpers/fix-repair.js +64 -0
  134. package/scripts/_helpers/json-safe.mjs +218 -0
  135. package/scripts/bump-version.sh +84 -0
  136. package/scripts/codegen/gen-docs.mjs +61 -0
  137. package/scripts/codegen/gen-json-schema.mjs +62 -0
  138. package/scripts/codegen/gen-mcp-tools.mjs +358 -0
  139. package/scripts/codegen/gen-types.mjs +172 -0
  140. package/scripts/codegen/index.mjs +42 -0
  141. package/scripts/dev-hooks/check-extension-dirs.js +77 -0
  142. package/scripts/dev-hooks/check-open-claws-terminals.js +70 -0
  143. package/scripts/dev-hooks/check-stale-main.js +55 -0
  144. package/scripts/dev-hooks/check-tag-pushed.js +51 -0
  145. package/scripts/dev-hooks/check-tag-vs-main.js +56 -0
  146. package/scripts/dev-vsix-install.sh +60 -0
  147. package/scripts/fix.sh +702 -0
  148. package/scripts/gen-client-types.mjs +81 -0
  149. package/scripts/git-hooks/pre-commit +31 -0
  150. package/scripts/hooks/lifecycle-state.js +61 -0
  151. package/scripts/hooks/package.json +4 -0
  152. package/scripts/hooks/post-tool-use-claws.js +292 -0
  153. package/scripts/hooks/pre-bash-no-verify-block.js +72 -0
  154. package/scripts/hooks/pre-tool-use-claws.js +206 -0
  155. package/scripts/hooks/session-start-claws.js +97 -0
  156. package/scripts/hooks/stop-claws.js +88 -0
  157. package/scripts/inject-claude-md.js +205 -0
  158. package/scripts/inject-dev-hooks.js +96 -0
  159. package/scripts/inject-global-claude-md.js +140 -0
  160. package/scripts/inject-settings-hooks.js +370 -0
  161. package/scripts/install.ps1 +146 -0
  162. package/scripts/install.sh +1729 -0
  163. package/scripts/monitor-arm-watch.js +155 -0
  164. package/scripts/rebuild-node-pty.sh +245 -0
  165. package/scripts/report.sh +232 -0
  166. package/scripts/shell-hook.fish +164 -0
  167. package/scripts/shell-hook.ps1 +33 -0
  168. package/scripts/shell-hook.sh +232 -0
  169. package/scripts/stream-events.js +399 -0
  170. package/scripts/terminal-wrapper.sh +36 -0
  171. package/scripts/test-enforcement.sh +132 -0
  172. package/scripts/test-install.sh +174 -0
  173. package/scripts/test-installer-parity.sh +135 -0
  174. package/scripts/test-template-enforcement.sh +76 -0
  175. package/scripts/uninstall.sh +143 -0
  176. package/scripts/update.sh +337 -0
  177. package/scripts/verify-release.sh +323 -0
  178. package/scripts/verify-wrapped.sh +194 -0
  179. package/templates/CLAUDE.global.md +135 -0
  180. package/templates/CLAUDE.project.md +37 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,1949 @@
1
+ # Changelog
2
+
3
+ All notable changes to Claws will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+
9
+ ## [0.8.0] - 2026-05-17
10
+
11
+ ### Tooling
12
+
13
+ - Added `scripts/verify-release.sh`: 12-check pre-publish gate (clean tree, branch, pushed, package.json sanity, bin executable, LICENSE, AF-AC marker, npm pack size/exclusions, vsce round-trip, CHANGELOG entry, tag-pending).
14
+ - `bin/cli.js`: `--no-hooks` flag now OR-combines with `CLAWS_NO_GLOBAL_HOOKS=1` env var; `lib/install.js` Phase 8 shell hook injection skipped under either condition (darwin smoke: `~/.zshrc` byte-identical pre/post install).
15
+ - `README.md`: clarified linux node-pty source-compile fallback vs darwin/win32 prebuilds; added platform-aware callout above Option 1 npm block.
16
+
17
+
18
+
19
+ v0.8 is the public launch release: deterministic event-driven mission submit on all three platforms, a new autoclose lifecycle architecture, three new orchestration slash commands, and the full public-launch infrastructure stack.
20
+
21
+ ### Cross-platform submit reliability
22
+
23
+ v0.8 replaces the fixed-cadence polling loop for mission submit with a pure event-driven architecture that works identically on darwin, linux, and win32.
24
+
25
+ **Event-driven submit** (AE-7, AE-6.b): `_sendAndSubmitMission` now awaits `_waitForSubmitEvent`, which blocks up to `RETRY_INTERVAL_MS` on one of three bus signals — pty content growth (`vehicle.<termId>.content`), a worker MCP tool invocation (`tool.+.invoked`), or lifecycle-end — before escalating through `SUBMIT_STRATEGIES`. The boot gate (`_waitForWorkerReady`) uses a 120 s safety ceiling uniform across all platforms, with early-abort listeners keyed by `correlation_id` for `system.terminal.closed` and `system.worker.terminated`. No `process.platform` branches in either path.
26
+
27
+ **Windows 4-layer submit defense** (AF-3): ConPTY's `vehicle.<N>.content` event fires before the submit listener is installed. Four coordinated layers ensure reliable delivery regardless of timing: (1) eager `handleContent()` immediately after listener registration; (2) extension growth watcher emitting `system.terminal.paste_complete` after `sendText(paste:true)`; (3) `_preLen` measured in raw bytes rather than ANSI-stripped chars; (4) `correlation_id` carried in the `tool.invoked` publish payload, eliminating a `_peerIdToCorrId` race on Windows.
28
+
29
+ The event-driven boot substrate (AE-1 through AE-1.2) underpins both paths: `mcp_server.js` eager-hellos at startup when `CLAWS_TERMINAL_CORR_ID` is set; `hasRootOrchestrator()` excludes `correlationId`-carrying nested orchestrators; the paste gate drops regex/pty-log fallbacks in favor of clean `event_driven_boot_timeout` aborts.
30
+
31
+ ### Autoclose lifecycle architecture — Phase 1 (AF-AC)
32
+
33
+ `ClawsPtyOptions` gains an `onProcessExitHook` callback, called as the first line of `ClawsPty.handleExit`. The hook propagates through `CreateOptions.onProcessExit` → `TerminalManager.createWrapped` → `VsCodeBackend.createTerminal`. The extension emits a `terminal:process_exited` backend event; `server.ts` translates it to a `system.worker.process_exited` bus event (`terminal_id`, `correlation_id`, `exit_code`, `exited_at`). The MCP detach watcher (`_setupDetachWatcher`) subscribes to the new topic as the highest-priority completion signal — before the existing `detectCompletion` call — resolving with `completion_signal:'process_exited'`.
34
+
35
+ This is Phase 1 — additive only. All five existing completion layers (F3 `claws_done()`, F4 `printf`, F5 chat narration, Wave-D `system.worker.terminated`, Phase-4a bus-complete) remain active and unchanged. Phases 2 and 3 (draining the fallback tower) land in v0.8.x.
36
+
37
+ New test suite: `process-exited-hook.test.js` (11 checks). Zero regression: paste-gate 26/26, mac-event-driven-boot 21/21.
38
+
39
+ ### Three new slash commands
40
+
41
+ - **`/claws-plan`** — Context engineering: reads the user task, audits relevant code, identifies dependencies and risks, and produces a structured plan document before any work begins.
42
+ - **`/claws-auto`** — Autonomous orchestration loop: runs `claws_plan` → dispatches waves → audits between waves → self-corrects on failures → stops when the goal is met or the budget is exhausted.
43
+ - **`/claws-goal`** — Goal tracker (Codex/Claude Code agent loop pattern): breaks a high-level goal into subtasks, tracks progress, and surfaces clarifying questions when blocked.
44
+
45
+ Companion skills: `claws-auto-engine` (wave dispatch, inter-wave audit, failure recovery, time-budget tracking) and `claws-goal-tracker` (breakdown heuristics, clarifying-question pattern, task-status protocol).
46
+
47
+ ### Public-launch infrastructure
48
+
49
+ - **npm package `claws-code`** — mirrors `claude-code` branding. Install command: `npx claws-code install`. Binary: `claws-code`; backward-compat alias `claws` kept for one minor (removed in v0.9). VS Code extension publisher ID, slash commands, and MCP tool names are unchanged.
50
+ - **GitHub Actions CI/CD** — `ci.yml` runs lint + full test matrix on every PR. `release.yml` is tag-triggered: on `v*` tags it builds the VSIX, publishes to the VS Code Marketplace, and publishes to npm.
51
+ - **`install.ps1`**: `$ProgressPreference = 'SilentlyContinue'` added before the first `Invoke-WebRequest` (~50× IWR speedup on PowerShell 5).
52
+ - **`install.sh`**: platform/arch detection block (`CLAWS_PLATFORM_ARCH`) maps `uname -s`/`uname -m` to `darwin-x64 | darwin-arm64 | linux-x64 | linux-arm64`; `EXPECTED_MIN_VERSION` bumped to `0.8.0`.
53
+ - **`bin/cli.js`**: executable bit (`chmod +x`) committed via `git update-index --chmod=+x` so `npx claws-code` works without a post-install script.
54
+ - **Prebuilt `pty.node`**: darwin-x64 and darwin-arm64 ship pre-built native binaries in `extension/native/node-pty/prebuilds/`. linux falls back to local compile; win32 uses ConPTY (no spawn-helper). `bundle-native.mjs` harvests from `node_modules/node-pty/prebuilds/` at build time and `chmod 0755` the spawn-helper binaries; source-tree binaries are committed `+x` via `git update-index --chmod=+x`.
55
+ - **`scripts/verify-release.sh`** — 12-check pre-publish gate (working tree clean, branch + push state, package.json sanity, `bin/cli.js` executable, `extension/LICENSE` present, AF-AC marker in dist bundle, `npm pack --dry-run` size + exclusions, `vsce package` round-trip, CHANGELOG section present, tag location). Exits non-zero on any blocker; runs in ~90 s.
56
+
57
+ ### Bus topic catalog additions
58
+
59
+ | Topic | Added in | Payload |
60
+ |-------|----------|---------|
61
+ | `system.terminal.paste_complete` | AF-3 Layer 2 | `terminal_id`, `pre_size`, `post_size`, `ts` |
62
+ | `system.worker.process_exited` | AF-AC Phase 1 | `terminal_id`, `correlation_id`, `exit_code`, `exited_at` |
63
+
64
+ ### MCP tools
65
+
66
+ - **`claws_reload_window`** — triggers `workbench.action.reloadWindow` via the extension. Eliminates the manual Cmd+Shift+P → Reload Window step after extension changes.
67
+ - **`claws_reload_mcp`** — exits cleanly so the MCP supervisor auto-respawns. Eliminates the manual MCP reconnect step after `mcp_server.js` changes.
68
+
69
+ ## [0.7.14] - 2026-05-12
70
+
71
+ ### Internal
72
+ - **Pre-release polish** — `mcp_server.js` `initialize` response `serverInfo.version` bumped from stale `'0.7.10'` to `'0.7.14'` (W21 F5; eliminates spurious client-version-drift warnings). One-line stale comment in `extension/test/claws-ws-transport.test.js:242` updated from `orchestratorPresent` → `rootOrchestratorPresent` to match the assertion two lines below.
73
+
74
+ ### Fixed (runtime)
75
+ - **Bug 14 — scope orchestrator singleton guard by waveId** — Wave LEAD sub-workers calling `claws_hello({role:'orchestrator', waveId:'...', subWorkerRole:'lead'})` were unconditionally rejected with `'orchestrator already registered'` because `hasOrchestrator()` scanned all peers globally without waveId scoping. Root cause: the flat single-session model made it impossible for a LEAD to register as orchestrator alongside the main session, blocking all privileged commands (`broadcast`, `task.assign`, `deliver-cmd`). Fix: replaced `hasOrchestrator()` with `hasRootOrchestrator()` which filters peers without a `waveId`; guard updated to `!r.waveId && hasRootOrchestrator()`. Error message updated to `'root orchestrator already registered'`. `mcp_server.js` `claws_hello` handler now forwards `waveId`/`subWorkerRole` to the server and returns `rootOrchestratorPresent` (renamed from `orchestratorPresent`). `claws_hello` schema in `gen-mcp-tools.mjs` adds `waveId`/`subWorkerRole` fields. All consumers (`mcp_server.js`, ws-transport test, pconn-identity mock) updated to new field name. 7 checks in `claws-v2-hello.test.js` including new waveId-scoped LEAD hello. Refs: `.local/plans/v0714/investigations/bug14-orchestrator-claim-race.md`.
76
+ - **Bug 15 — emit `system.terminal.<id>.alive` in heartbeat loop** — `stream-events.js --keep-alive-on <termId>` exited with code 2 ("no events for terminal X within 120000ms") even while the LEAD was visibly alive. Root cause: no periodic bus event was keyed by `terminal_id`. `vehicle.<id>.state/content` fire only on startup transitions; `system.heartbeat` carries no `terminal_id`; worker heartbeats (`WorkerHeartbeatV1`) have no `terminal_id` field. Fix: inside the extension's heartbeat timer body (`server.ts`), after existing `system.heartbeat` / `system.metrics` emissions, iterate `terminalManager.liveTerminalIds()` and emit `system.terminal.<id>.alive` with `{ terminal_id, ts }` for every live terminal. Fires every 60 s (heartbeat cadence); the 120 s stale threshold gives a 2× safety margin. `liveTerminalIds()` already existed on `TerminalManager` (added in LH-9). Zero changes to `stream-events.js` or `mcp_server.js`. Independently fixes both the Hello-failure cascade (Bug 14) and the latent gap for any correctly-helloed worker in a long quiet-computation phase (H15.A + H15.B). Regression test: `claws-terminal-alive.test.js` (7 checks) with a 200ms heartbeat interval for sub-second wall-clock time.
77
+ - **Bug 12 — residual race: retry on "orchestrator already registered"** — Sim pass 6 confirmed W30's Bug 12 fix correctly surfaces the hello failure but has no recovery: extension returns `{ok:false, error:'orchestrator already registered'}` when a concurrent `_pconnEnsureRegistered` caller reconnects before the old socket's FIN is processed, leaving the old peer in the extension's Map. No retry existed, so every subsequent L2 publish failed for the rest of the session. Deep fix: exponential-backoff retry in `_pconnEnsureRegistered` — up to 3 attempts (100ms → 200ms → 400ms delays) specifically on "already registered" errors. The delay gives the extension's event loop time to delete the stale peer. On exhaustion throws with retry count for diagnostics. Retry uses `_helloInFlight` dedup to prevent concurrent callers from sending multiple simultaneous hellos. 6 new unit checks in `pconn-identity.test.js`: positive (2 failures then success, `orchestratorHelloCount=3`) and negative (all fail, 12 total hello attempts, 0 publishes). Refs: `.local/plans/v0714/investigations/bug12-pconn-peer-loss.md` (Fix 4), `.local/audits/v0714-validation-report-pass6.md` (Bug 12 race section).
78
+ - **Bug 12 — _pconn socket-identity guard + ok-checks** — W26 investigation confirmed `_pconn` undergoes disconnect/reconnect cycles. Three intersecting defects masked the failures: (1) `_pconnEnsureRegistered` didn't verify hello succeeded, silently proceeding with `peerId=null` on `ok:false`; (2) no socket-identity guard — `_pconn.peerId` could belong to a closed socket while `_pconn.socket` pointed to a new one; (3) no ok-check at publish call sites, causing extension rejections to be logged as "publish-succeeded". Fix: `_pconn` gains `socketId` (incremented per connect) and `helloSocketId` (set on hello success). Guard in `_pconnEnsureRegistered` skips hello only when `peerId` is set AND `helloSocketId === socketId`; mismatch forces re-hello. `_pconnEnsureRegistered` now throws on `ok:false` instead of silent return. `_pconnWriteOrThrow` helper added; 28 call sites migrated. Unit tests in `extension/test/pconn-identity.test.js` (15 checks).
79
+ - **Bug 13 — split arm-intent from arm-execution (two-stage L2 state machine)** — W27 investigation confirmed the L2 30s timer races Claude's response composition latency. The timer measured from MCP tool invocation (server-side); the arm-execution signal arrived after Claude composed its next message and called `Monitor()`, which under load exceeds 30s. Deep fix: two-state machine in `PeerRegistry`. `pendingArms` set tracks corrIds whose intent is registered but `stream-events.js` hello has not yet completed. `monitors.register-intent` RPC is called at every spawn site in `mcp_server.js` immediately after corrId allocation. `recordMonitorClaim` now graduates corrId from `pendingArms` → `armedCorrelations`. `monitors.is-corr-armed` RPC is extended with `claimed` / `pending` fields (backward-compatible `armed` field preserved). L2 `setTimeout` at all 4 sites now distinguishes: no intent at 30s → true orphan, publish immediately; pending at 30s → arm in flight, re-check at +60s (90s total); claimed → silent. 5 new state-machine checks added to `monitor-corr-arm.test.js` (15 total).
80
+ - **Bug 11 — `claws_dispatch_subworker` `_dswCwd` undeclared + watcher consolidation** — `ReferenceError: _dswCwd is not defined` thrown inside the `setImmediate` boot block, silently caught by the surrounding `try/catch`, preventing Claude from launching in every dispatched sub-worker. Symptom: LEAD spawns sub-worker via `claws_dispatch_subworker`, terminal appears in `claws_list`, Claude never boots, terminal stays open forever, `claws_workers_wait` times out. Phase 1: add `const _dswCwd = …` before `setImmediate`. Phase 2 (structural): `_fpTick` (`claws_worker` fast-path) and `_dswTick` (`claws_dispatch_subworker`) were near-identical ~150-line watcher implementations that had drifted — BUG-26 was applied asymmetrically, `_dswCwd` (Bug 11) lived undetected for the same copy-paste reason. Extracted `_setupDetachWatcher` helper (HB L4/L5/L6, `detectCompletion`, pub scan, auto-close); applied at both sites. `tick` in `runBlockingWorker` (structurally different: no HB, dead `_msState` tracking, different timeout behavior) is left in place. Future BUG-26-class regressions in the LEAD/wave path are now structurally impossible. Root cause in `.local/plans/v0714/investigations/army-shell-autoclose.md` (Divergence 2).
81
+
82
+ ### Changed
83
+ - **Bug 12 PCONN/peer-registry debug instrumentation** — `PeerRegistry` gains `notifyRegister()` / `notifyUnregister()` methods that write structured JSON to `.claws/peer-registry-trace.log` on every peer lifecycle event. `ClawsServer` wires these at hello and `handleDisconnect`. `mcp_server.js` gains per-socket `_pconnSocketSeq` IDs and `PCONN-DEBUG:` log lines at every connect attempt, hello send/ack, write start/response, and reconnect cycle, plus a `write-failed-silently` log when `resp.ok === false`. Investigation-only; no logic changes.
84
+ - **Bug 13 arm-race observability** — `stream-events.js --wait` now logs a stderr line when the `monitorCorrelationId` hello is dispatched (`hello sent | corrId=… | t=…`). Permanent observability trace; no logic change. Enables comparing arm-dispatch time against the L2 30s window during future arm-race investigations. Root cause documented in `.local/plans/v0714/investigations/bug13-arm-race.md`.
85
+
86
+ ### Fixed (runtime)
87
+ - **Bug 6 L1 — `unwrapMcpResponse` forward-compat normalizer** — PostToolUse hook silently exited at the `if (!resp.ok)` guard because `unwrapMcpResponse` didn't handle the bare-array `tool_response` shape Claude Code currently emits (`[{type:'text', text:'<JSON>'}]`). Affected root-session AND nested-TUI calls equally; unit tests passed because they used pre-unwrapped synthetic data. Rewrote as a proper forward-compat normalizer: handles three observed shapes (bare array, wrapped object, plain object) plus null/undefined. Unknown shapes return `null` AND emit a structured diagnostic to `/tmp/claws-hook-diag.log` so future format changes never silently break the hook. Caller updated to check for `null` return before checking `resp.ok`. Fixture-driven unit tests in `extension/test/unwrap-mcp-response.test.js` replay real captured stdin from `/tmp/claws-hook-stdin-*.json` (W15 forensic trace, pids 80621 + 81977). E2E validated: watcher log `/tmp/claws-monitor-arm-watch-<corrId>.log` created on first spawned worker after fix. Root cause documented in `.local/plans/v0714/investigations/bug6-hook-nested-context.md`.
88
+
89
+ ## [0.7.14] - 2026-05-11
90
+
91
+ ### Changed
92
+ - **Layer 2 debug instrumentation** — `mcp_server.js` all 4 Layer 2 `setTimeout` callbacks now emit `L2-DEBUG:` lines at every decision branch (callback-entered, rpc-result, skip-publish, about-to-publish, pconn-registered, publish-succeeded/failed, outer-error). A new `_logL2File()` helper writes in parallel to `.claws/l2-debug.log` for file-based capture. Investigation-only; no logic changes. Covered sites: `runBlockingWorker`, `claws_create`, `claws_worker-fast-path`, `claws_dispatch_subworker`.
93
+
94
+ ## [0.7.14] - 2026-05-08
95
+
96
+ ### Fixed
97
+ - **Bug 1** — `install.sh` now sweeps stale `claws-*.md` / `claws.md` command files from `$TARGET/.claude/commands` before copying. Prior installs accumulated every renamed/deleted command (PEDI had 27; installer ships 8). User-added commands (no `claws-` prefix) are untouched.
98
+ - **Bug 2** — `install.sh` now sweeps stale `claws-*` skill directories from `$TARGET/.claude/skills` before copying. Retired skill `claws-orchestration-engine` (renamed to `claws-prompt-templates` in v0.7.13) no longer lingers after upgrade.
99
+ - **Rules idempotence** — `claws-default-behavior.md` is now removed before re-copy so a stale version can never survive an upgrade.
100
+
101
+ ### Added
102
+ - **`extension/test/install-sweep.test.sh`** (8 checks) — verifies command sweep, skill sweep, user-content preservation, and source markers; registered in `npm test` chain.
103
+ - **Bug 3 — cold-start preamble in entry-point slash commands** — models deliberated over plan mode / TodoWrite / pre-verification on first `/claws-do` invocation, stalling or falling back to Bash. MANDATORY ordered preamble added to all entry-point commands (`claws.md`, `claws-do.md`, `claws-fix.md`, `claws-help.md`, `claws-status.md`): acknowledge → skip plan mode → skip TodoWrite → trust sidecar → classify → spawn immediately → arm Monitor verbatim from `monitor_arm_command` → wait. Also covers Bug 6 Layer 0: Monitor arming imperative explicitly says "MUST be the very next tool call after spawn."
104
+ - **Bug 8 — fast-path BUG-26 analog for shell workers** — `_dispatchTool` (claws_worker, detach=true) was running the 15s TUI-specific submit-verification loop for shell workers (`launch_claude=false`), causing `_fpMarkerScanFrom` to land past the already-printed `__CLAWS_DONE__` marker; detach watcher scanned an empty slice forever and auto-close never fired. Applied the BUG-26 guard (present in `runBlockingWorker` since LH-15) to the fast-path: verification loop and post-mission offset snapshot are now wrapped in `if (launchClaude)`; shell workers keep `_fpMarkerScanFrom=0` so the watcher scans the full log.
105
+ - **Bug 4 — worker binary alias UX** — two new MCP tools (`claws_set_bin`, `claws_get_bin`) surface the existing `.claws/claude-bin` / `CLAWS_CLAUDE_BIN` plumbing. New `/claws-bin` slash command (set/reset/get). Natural-language imperative added to `CLAUDE.global.md` so "use claude-neu for workers" resolves automatically. Schema regenerated (39 → 41 tools). `install.sh` post-install banner now lists the three override mechanisms.
106
+ - **Bug 5 — Monitor in-process rearm** — `stream-events.js --wait` now supports `--keep-alive-on <termId>`. When the inner timer fires, a 3-check decision runs: (1) `system.worker.completed` in events.log → exit 0; (2) `system.terminal.closed` / `system.worker.terminated` in events.log → exit 0; (3) `eventsSeen(termId, staleMs)` — terminal active on bus within threshold → rearm in place. Otherwise exit 2 (truly stuck). New flags: `--stale-threshold <ms>` (default 120 s) and `--rearm-cycle <ms>` (default = `--timeout-ms`). All 5 `monitor_arm_command` emission sites in `mcp_server.js` updated: `--keep-alive-on` appended, `timeout_ms` raised from 600 000 → 7 200 000 (2 h). `extension/test/monitor-rearm.test.js` (6 unit branches) registered in `npm test` chain.
107
+ - **Bug 7 — `system.worker.terminated` identity fix** — `server.ts` now attaches `correlation_id` (already in scope from the `system.terminal.closed` lookup) to the `system.worker.terminated` payload when lifecycle state has it. `terminal_id` is a session-local integer VS Code recycles on extension reload; `correlation_id` is a UUID globally unique across sessions. `stream-events.js` rearmDecisionLoop Check 2 simultaneously drops its `terminal_id` fallback (`system.worker.terminated` matched by `termId`) and replaces it with corrId-only matching for both `system.terminal.closed` and `system.worker.terminated`, eliminating false-positive Monitor exits caused by prior-session log entries with the same numeric terminal ID.
108
+ - **Bug 9 — `claws-wave-lead/SKILL.md` instruction quality** — step 0 ("Arm Monitor on .claws/events.log via Bash run_in_background") replaced with a note that the sidecar is already running. Explicit Monitor()-only prohibition added (no Bash backgrounding for event observation). Sub-worker dispatch section expanded with a worked `Monitor(command=r.monitor_arm_command)` example. Obsolete BUG-03 capabilities comment removed from step 1 (auto-granted since v0.7.13).
109
+ - **Bug 6 Layer 1 — per-worker Monitor pgrep watcher** — `lifecycle.monitors[]` is pre-populated by `mcp_server.js` atomically at spawn time, so the PostToolUse hook's `monitors.some()` check is vacuously satisfied even when no OS-level `stream-events.js` process is arming. New `scripts/monitor-arm-watch.js` sleeps `grace_ms` (10 s default), runs `pgrep -f 'stream-events.js.*--wait <corrId>'`, and publishes `system.monitor.unarmed` to the bus if no match. `post-tool-use-claws.js` wired with `extractWorkerEntries()` + `spawnWatchers()` — spawns one detached watcher per worker for every spawn-class tool call (`claws_worker` / `claws_fleet` / `claws_dispatch_subworker` / `claws_create`); hook stays under 100 ms. Unit tests in `extension/test/monitor-arm-watch.test.js` (2 branches). Layer 2 (`hello`-with-`monitorCorrelationId`) closes the loophole structurally.
110
+ - **Bug 6 Layer 2 — `hello`-with-`monitorCorrelationId`** — closes the structural loophole where `lifecycle.monitors[]` pre-population makes per-worker Monitor verification vacuously satisfied. `stream-events.js --wait <corrId>` now includes `monitorCorrelationId: corrId` in its `hello` frame. Server (`peer-registry.ts`: new `PeerRegistry` class with `armedCorrelations Map<corrId,peerId>`; `server.ts`: `hello` handler records the claim, `handleDisconnect` removes it on peer close) tracks which corrIds have an active Monitor process. New `monitors.is-corr-armed` RPC (`{ correlation_id }` → `{ ok, armed, peerId }`) exposes the state. Spawn handlers (`claws_worker`, `runBlockingWorker`/fleet, `claws_dispatch_subworker`, `claws_create`) each fire a parallel 30 s grace `setTimeout` calling `monitors.is-corr-armed`; if the corrId is not claimed, logs a Layer-2-specific warning and publishes `system.monitor.unarmed` (same topic as Layer 1; consumers dedupe on `corrId`). `extension/test/monitor-corr-arm.test.js` (9 checks) covers claim, no-claim, disconnect-cleanup, RPC before/after hello, and error case.
111
+
112
+ ### Internal
113
+ - **Bug 6 forensic trace** — `post-tool-use-claws.js` now appends a one-line entry to `/tmp/claws-hook-trace.log` at the very top (before any logic) and dumps the raw PostToolUse stdin to `/tmp/claws-hook-stdin-<pid>.json` at the top of `run()`. Both writes are in try/catch and never throw. Added as permanent observability to detect future `tool_response` format regressions; see `.local/plans/v0714/investigations/bug6-hook-nested-context.md`.
114
+
115
+ ### Fixed (runtime)
116
+ - **Bug 6 Layer 1 — watcher subprocess publish reliability** — sim pass 3 (`.local/audits/v0714-validation-report-pass3-recovered.md`, Sim 4) found 0 `system.monitor.unarmed` events for real workers. Root cause: `.claude/settings.json` has a `hooks.PostToolUse` array (Bash dev-hooks), and Claude Code treats project settings as authoritative for hook types present there, overriding the global `~/.claude/settings.json` PostToolUse entries entirely. The `mcp__claws__claws_worker` (and sibling) matchers existed only in the global settings, so the PostToolUse hook process was never spawned and `monitor-arm-watch.js` was never launched. Fix: mirror the four claws MCP PostToolUse entries from global settings into `.claude/settings.json`. Also hardened `monitor-arm-watch.js` (absolute `/usr/bin/pgrep` path; startup + per-exit stderr lines with corrId/socket/cwd/reason — never exits silently) and `post-tool-use-claws.js` (watcher stdio redirected from `'ignore'` to per-corrId `/tmp/claws-monitor-arm-watch-<corrId>.log`; explicit `cwd=project-root` on spawn). Validated via manual E2E trigger: watcher log shows `exit 1 | reason=unarmed-published`; `system.monitor.unarmed` event appears in `events.log` for the real worker corrId.
117
+ - **Bug 6 Layer 1 — missing `.claws-bin/monitor-arm-watch.js` deploy** — `scripts/monitor-arm-watch.js` (added in commit 1849ee9) was never deployed to `.claws-bin/`. The `post-tool-use-claws.js` hook resolves the watcher path as `path.join(__dirname, '..', 'monitor-arm-watch.js')` → `.claws-bin/monitor-arm-watch.js`; the file did not exist → spawn silently failed → 0 Layer 1 events fired at runtime. Fix: created `.claws-bin/monitor-arm-watch.js` as a symlink to `../scripts/monitor-arm-watch.js`, mirroring the existing `mcp_server.js` / `stream-events.js` pattern. `install.sh` updated to create the same symlink (and `cp` equivalent for non-dev installs) on every fresh install.
118
+ - **Bug 6 enforcement runtime — Layer 1 + Layer 2 publish reliability** — sim pass 2 (`.local/audits/v0714-validation-report-pass2.md`) found both enforcement layers silently failing in live MCP fast-path context. Layer 1 (`post-tool-use-claws.js`): Claude Code passes MCP tool responses as `{ content: [{ type: 'text', text: '<JSON>' }] }`; the hook checked `resp.ok` directly on the wrapper (undefined), short-circuiting before spawning `monitor-arm-watch.js`. Fixed with `unwrapMcpResponse()` helper that parses the text content first. Layer 2 (`mcp_server.js`): the 30 s `setTimeout` callbacks called `_pconnWrite()` without `_pconnEnsureRegistered()` — if `_pconn` was not registered at fire time (e.g., after a 25 s Claude boot), the write threw and was silently swallowed. Fixed by adding `_pconnEnsureRegistered(sock)` before every Layer 2 `_pconnWrite` at all four sites (`claws_worker` fast-path, `runBlockingWorker`, `claws_create`, `claws_dispatch_subworker`). Silent `catch` replaced with logged `catch` at all sites. Smoke test: `extension/test/bug6-enforcement-runtime.test.js` (4 checks).
119
+
120
+ ### Tests
121
+ - **Stale assertion alignment** — three pre-existing test assertions encoding old pre-v0.7.14 behavior updated: `monitor-rearm.test.js` (c) now injects `correlation_id` in the synthetic `system.worker.terminated` event (matching Bug 7 corrId-only Check 2 path); `lh-stack-regression.test.js` D8 updated from `timeout_ms=600000` → `7200000` with `--keep-alive-on` presence check (Bug 5 cascade); E4 rewritten to verify Option A (stream-events.js references `system.worker.terminated` and `system.terminal.closed`, both matched by corrId) instead of the rejected Option B. lh-stack: 51/51 PASS.
122
+
123
+ ## [0.7.13] - 2026-05-05
124
+
125
+ ### Added
126
+ - **`claws_done()`** (LH-18) — zero-arg MCP tool, primary worker-completion primitive. Reads `CLAWS_TERMINAL_ID` from worker env, publishes `system.worker.completed`, closes terminal. Mission preambles trimmed to single line.
127
+ - **LH-stack regression suite** (`lh-stack-regression.test.js`, 51 checks across 7 sections) — locks LH-9/10/11/12 + LH-18/18.1 invariants. No overlap with existing lifecycle-store (58), lh9-state-bulletproof (40), or stream-events-wait (8) suites.
128
+ - **LH-12 `--wait` mode** in `scripts/stream-events.js` — native `--wait <uuid>` replaces 5-layer awk/grep Monitor pipeline; direct `correlation_id` equality match, no regex, no shell quoting. Exit codes: 0 match, 1 connect failure, 2 socket close, 3 timeout.
129
+ - **LH-10 `correlation_id`** on `system.terminal.closed` + `monitors[]` reconcile-on-boot in lifecycle-store.
130
+ - **LH-9 bulletproof state management** — single-writer TTL watchdog (10 min idle / 4 h max), reconcile-on-boot, Stop-hook defang; `markActivity` + `findExpiredWorkers` + slot reuse for stale CLOSED entries.
131
+ - **LH-1 wave violation auto-close** — silent sub-workers auto-closed at 25s threshold via `wave_violation` close origin.
132
+ - **Phase 4a bus-based completion** — workers publish `worker.<id>.complete`; orchestrator subscribes via `_pconn` wildcard; pty marker remains fallback.
133
+ - **`claws_marketing/`** workshop folder + 6 AI-generated README images (fal.ai Nano Banana Pro, 2K 16:9): social-preview, before-after, install-flow, architecture, wrapped-terminal, claws-done-completion.
134
+ - **`scripts/test-template-enforcement.sh`** — 10-assertion CI test for CLAUDE.md injection chain (tool list, phases, idempotence, sentinel migration).
135
+ - **`scripts/dev-vsix-install.sh`** + `npm run install:vsix` — full VSIX reinstall for VS Code extension refresh (~25s); complements fast `deploy:dev` path.
136
+
137
+ ### Changed
138
+ - **LH-13 consolidation** — 27→8 commands, 6→3 skills; `claws_worker`/`claws_fleet`/`claws_dispatch_subworker` replace manual 7-step boot; 5 daily + 3 system command mental model.
139
+ - **LH-14 completion convention** — `__CLAWS_DONE__` canonical marker replaces per-worker variants; 5-layer F1–F5 convention; `claws_publish` elevated to PRIMARY in mission preambles.
140
+ - **LH-14.1 mode-aware `detach`** — mission-mode `claws_worker(mission=...)` → `detach:true`; command-mode `claws_worker(command=...)` → `detach:false`.
141
+ - **LH-11 silent Monitor template** — awk filter flipped to silent-until-pattern; one notification per worker at terminal state; `system.worker.terminated` added to alternation.
142
+ - **Worker boot detection** — `❯` + `cost:$` stable for 3 polls replaces legacy `bypass permissions` string match (v0.7.9).
143
+ - **CLAUDE.md injection** now parametric: tool list from `mcp_server.js` dispatch handlers, phases from `lifecycle-store.ts`, version from `package.json`; versioned sentinels for clean cross-version upgrades.
144
+ - **Templates** (`CLAUDE.global.md`, `CLAUDE.project.md`) rewritten new-user-first with `claws_done()` primary, per-worker Monitor pattern, full 11-phase lifecycle.
145
+ - **Extension README** rewritten marketplace-focused (137 lines from 295); **Root README** refreshed with 39-tool grouped table, Wave Army section, behavioral injection enforcement section.
146
+ - **Extension `package.json`** v0.7.13, categories `[Other,AI,Machine Learning]`, +keywords `mcp`/`wave-army`/`workers`, galleryBanner `#111318`, `screenshots[]` added.
147
+ - **Skill rename** `claws-orchestration-engine` → `claws-prompt-templates` in cli.js + 4 doc files; F3 updated from `claws_publish(...)` to `claws_done()` across all mission templates.
148
+
149
+ ### Fixed
150
+ - **LH-18.1** — `claws_done()` publish path: replaced broken `clawsRpcStateful` with `_pconnEnsureRegistered` + `_pconnWrite` (`protocol: claws/2`); `system.worker.completed` now lands on bus with `completion_signal:'claws_done'`.
151
+ - **LH-15** — shell-worker marker: regex tolerates zsh `\` line-wrap artifact; server-side auto-wrap adds `[CLAWS_PUB]` bus event + `__CLAWS_DONE__` marker after every `claws_worker(command=...)`.
152
+ - **LH-11.1** — awk `\\.` → `[.]` fix; LH-11 silent Monitors were matching nothing due to regex literal misparse across all 5 spawn sites.
153
+ - **LH-3** — `claws_dispatch_subworker` paste-collapse submit parity with `runBlockingWorker` (signal-based predicates, 15s deadline, 5-nudge retry).
154
+ - **Worker boot paste-collapse** (latent v0.7.11 bug, 9fd97ac) — submit verification polls buffer growth or placeholder removal rather than byte-count comparison.
155
+ - **T9** — FAILED lifecycle phase now recoverable: `plan()` from FAILED resets arrays, increments `mission_n`, preserves `failure_cause`.
156
+ - **T8** — `claws_workers_wait` checks all 4 completion signals (marker, error_marker, pub_complete, terminated); adds `min_complete` parameter and per-worker `signal` field.
157
+ - **`uninstall-cleanup.ts`** skill array expanded: `claws-wave-lead` + `claws-wave-subworker` added; `claws-orchestration-engine` retained for backward-compat cleanup.
158
+ - **install.sh** `EXPECTED_MIN_VERSION` + **fix.sh** `EXT_VERSION` fallbacks bumped to 0.7.13.
159
+
160
+ ## [0.7.12] - 2026-05-03 — Install UX hardening + heartbeat parser foundation
161
+
162
+ ### In Progress (heartbeat v0.7.12)
163
+
164
+ - HB-L1 (in progress for v0.7.12): heartbeat parser primitives added — pure functions in mcp_server.js for TUI state detection. No runtime change yet; foundation for state machine in HB-L3+. Functions: parseToolIndicators, parseCostFooter, parseSpinnerActivity, parsePromptIdle, parseTodoWrite, parseErrorIndicators. Anchor: docs/heartbeat-architecture.md §V.E.
165
+ - HB-L2 (in progress for v0.7.12): WorkerHeartbeatV1 extended with optional kind/summary/cost/etc fields. Backward-compat additive. No runtime change yet.
166
+ - HB-L3 (in progress for v0.7.12): WorkerHeartbeatStateMachine class added to mcp_server.js. Tracks BOOTING/READY/WORKING/POST_WORK/COMPLETE transitions from pty observation. Not yet wired (L4 next).
167
+ - HB-L4 fix: heartbeat wiring moved from runBlockingWorker → fast-path watcher (the default for claws_worker). L4 verification now works. Reload VS Code + /mcp + spawn a claws_worker to see kind=heartbeat events every 30s.
168
+ - HB-L4 polish: dropped cost_usd from heartbeat (was bogus — showed orchestrator's overall session cost ~$2376, not per-worker cost). Tokens (in/out) remain. Fixed BOOTING→READY transition to fire on bypass-permissions detection alone (was waiting for prompt-idle which never happens during active work).
169
+ - HB-L4 cascade fix: READY→WORKING now uses cumulative toolCount (idempotent across ticks), no longer stuck at READY when boot+tool land in the same observe() tick.
170
+ - FIX-MON: canonical monitor pattern streams heartbeats + auto-exits on completion — orchestrator's `monitor_arm_command` now subscribes to `worker.<termId>.heartbeat,system.worker.*` and uses `awk '{print; fflush()} /system\.worker\.completed/{exit}'` instead of a completion-only `grep -m1`. Eliminates the blind window between spawn and completion; single awk wrapper emits each heartbeat line immediately AND exits cleanly on completion via SIGPIPE cascade. Five sites updated; new test: `extension/test/monitor-pattern.test.js`.
171
+ - FIX-MON-V2: monitor pattern v2 — switched CLAWS_TOPIC from comma-separated `worker.<id>.heartbeat,system.worker.*` to `'**'` wildcard. `scripts/stream-events.js:43` reads CLAWS_TOPIC as one literal string and passes it directly to the server's subscribe call; the comma-separated form was treated as a single invalid topic name, silently dropping all heartbeat notifications. Fix: subscribe to `'**'` (everything) and let the existing `grep --line-buffered correlation_id` filter narrow to the target worker. Verified live. Test: `extension/test/monitor-pattern.test.js` updated to 4/4 assertions.
172
+ - FIX-CLOSE-CALLBACK: `TerminalManager.close()` now invokes the close callback synchronously before deleting from the `byTerminal` map. Root cause of the lifecycle silent-mutation bug (audit 6e68a76): `close()` called `terminal.dispose()` then immediately deleted the `byTerminal` entry; VS Code's async `onDidCloseTerminal` fired after the entry was gone, so `onTerminalClosed()` bailed at its early-return guard and `system.worker.terminated` was never emitted. Every programmatic close (claws_close, fast-path watcher) was silently skipping the bus event. Monitors waiting on `system.worker.completed/terminated` hung indefinitely. Fix: call `this.onTerminalClose?.(key, rec.wrapped)` BEFORE `dispose()` and `byTerminal.delete()`. The existing `if (!id) return` guard in `onTerminalClosed()` is now idempotent — it means "already cleaned up". Test: `extension/test/terminal-manager.test.js` (4 checks).
173
+ - FIX-PARSER: `parseToolIndicators` regex `\s+` → `\s*` — Claude TUI renders `⏺Bash(args)` with zero whitespace between `⏺` and the tool name (hex: `e2 8f ba 42 61 73 68`). The `\s+` requirement caused 0/124 ticker matches in real pty samples; `this.toolCount` stayed at 0 forever; state machine never transitioned READY→WORKING. Fix is backward-compatible: `\s*` still accepts the documented `⏺ Bash(args)` form. Verified against 62KB captured pty fixture. Test: `extension/test/parse-tool-indicators.test.js` (5 checks, audit 6c1bd43).
174
+ - HB-L7: POST_WORK detection now publishes `kind=mission_complete` heartbeats. When the state machine fires POST_WORK→COMPLETE (spinner gone + prompt idle + bytes idle ≥20s), the fast-path watcher emits a one-shot heartbeat with `kind=mission_complete`, rich summary (`Xm Ys · N tool calls`), duration_ms, total_tool_calls, and tokens. Guard `_fpMissionCompletePublished` prevents double-fire. Test: `extension/test/mission-complete-heartbeat.test.js` (9 checks).
175
+ - HB-L8 (BIG FIX): when state machine detects sustained TUI idle (POST_WORK→COMPLETE), watcher now publishes `system.worker.completed` with `completion_signal:"tui_idle"`, clears interval, marks lifecycle completed, and auto-closes the terminal (per `close_on_complete: true`, default). Solves M15 marker-skip pattern: Claude finishes work + reaches `❯` prompt without running F3 printf → previously hung until 180s timeout, now completes in ~25-30s. Guard `_fpTuiIdleCompleted` gates `detectCompletion` path to prevent double-publish. Existing completion paths (marker, error_marker, pub_complete, Wave D terminated) remain primary. Test: `extension/test/tui-idle-completion.test.js` (8 checks).
176
+ - v0.7.12 prompt-idle fix: `parsePromptIdle` now scans the last 10 lines for the `❯` prompt instead of just the last non-empty line. Required for L7/L8 to actually fire — Claude TUI renders the prompt above the bypass-permissions footer, not as the last line. Test: `extension/test/parse-prompt-idle.test.js` (4 checks).
177
+ - v0.7.12 prompt-idle v2: `parsePromptIdle` now detects the `"⏵⏵ bypass permissions on"` footer instead of the `❯` char. ANSI strip collapses the `❯` prompt onto a multi-component single line (box-drawing border + ❯ + border), defeating the previous regex even with 10-line scan. Scans last 30 lines for the bypass-permissions substring — plain text that survives strip cleanly. Test: `parse-prompt-idle.test.js` rewritten with 4 cases (real TUI layout, working state, empty, stale-history).
178
+ - v0.7.12 post-work gate fix: dropped `bytesIdle` requirement from WORKING→POST_WORK transition. Claude Code's prompt suggestion feature emits pty bytes when idle, blocking the previous `bytesIdle` gate. Now: spinner-stopped + prompt-visible is sufficient. Test: new `WORKING→POST_WORK fires even with recent pty bytes` check in `heartbeat-state-machine.test.js`.
179
+ - HB-L5: kind=progress heartbeats with 5s burst aggregation. Fast-path watcher publishes activity bursts in real-time, collapsed to one summary per window. Richer orchestrator visibility between 30s backstop ticks.
180
+ - HB-L6: kind=approach + kind=error heartbeats. Fast-path watcher publishes TodoWrite plans (deduped by JSON.stringify compare) and Bash errors (deduped by errorsCount). Richer orchestrator observability.
181
+ - v0.7.12 hotfix: DISARMED L8 tui_idle auto-close. Destructive false-positives killed long-thinking workers (audit, deep edits). L7 mission_complete heartbeat publish stays for observability. Marker/error_marker/timeout/Wave-D paths unchanged.
182
+
183
+ ---
184
+
185
+ ### Fixed
186
+
187
+ #### Fix #1 (P0): ESM project crash — `mcp_server.js` fails to start in projects with `"type":"module"`
188
+ - **Impact**: ~50% of modern Node projects (Next.js, Vite, ESM-default tooling) hit `ReferenceError: require is not defined in ES module scope` on MCP server startup. Install appeared to succeed but Claws never functioned.
189
+ - **Root cause**: Node inherits `"type":"module"` from the nearest parent `package.json`. `.claws-bin/` had no own `package.json`, so `mcp_server.js` (CommonJS) was loaded as ESM.
190
+ - **Fix**: `scripts/install.sh` now writes `<project>/.claws-bin/package.json` with `{"type":"commonjs"}` to scope the override to `.claws-bin/` only. User's project files are unaffected. `update.sh` and `fix.sh` auto-restore the file so existing installs without it get fixed automatically.
191
+
192
+ #### Fix #2 (P1): dev-hooks leak into every user project — SessionStart hook spam
193
+ - **Impact**: every Claude Code session in a user project fired `SessionStart:startup hook error` for contributor diagnostic scripts (`check-stale-main.js`, `check-tag-pushed.js`, etc.) that have no relevance outside a Claws contributor environment.
194
+ - **Root cause**: `scripts/install.sh` unconditionally copied `scripts/dev-hooks/*.js` and registered them in `<project>/.claude/settings.json` for every install.
195
+ - **Fix**: dev-hooks install is now gated behind `CLAWS_INSTALL_DEV_HOOKS=1` env var. Default install path skips them entirely. Contributors installing into the Claws source tree (`scripts/install.sh` + `extension/src/` detected) get dev-hooks automatically without the flag.
196
+
197
+ #### Fix #3 (P1): `monitor_arm_command` pointed at wrong `stream-events.js` path in user installs
198
+ - **Impact**: orchestrators received a `monitor_arm_command` referencing `<project>/scripts/stream-events.js`, which doesn't exist in user installs (the file is at `<project>/.claws-bin/stream-events.js`). Per-worker Monitor commands failed immediately, forcing fallback to deprecated `tail -F events.log` (anti-pattern A1).
199
+ - **Root cause**: 5 occurrences in `mcp_server.js` computed the path from the socket location (`path.dirname(path.resolve(sock))`) instead of from `__dirname`, producing the wrong result outside the dev source tree.
200
+ - **Fix**: extracted a `STREAM_EVENTS_JS` constant at module load time using a multi-candidate resolver anchored to `__dirname` (same logic as the existing `_ensureSidecarOrThrow` resolver). All 5 `monitor_arm_command` generators now use it.
201
+
202
+ #### Fix #5 (P2): `uninstall.sh` — clarify that `code --uninstall-extension` is machine-wide
203
+ - **Impact**: users running `uninstall.sh` from one project folder followed the printed `code --uninstall-extension neunaha.claws` instruction, not realising it removes Claws from their editor globally — breaking Claws in any other project they had it installed in.
204
+ - **Fix**: added two-line warning above the printed command explaining the machine-wide scope and advising against running it if Claws is active in other projects.
205
+
206
+ ### Deferred
207
+ - **Bug #4** (shell-mode `claws_worker` command-path pipe-mode anomaly) deferred to v0.7.13 — fix scope unknown, needs investigation.
208
+
209
+ ## [0.7.11] - 2026-05-03 — Install Just Works™ (UX-first install)
210
+
211
+ ### Changed
212
+ - **`scripts/install.sh`: removed the v0.7.9 dirty-tree guard from default install path.** `~/.claws-src` is install.sh's working directory — not a user dev clone — and treating it as protected was wrong UX. The script now hard-resets `~/.claws-src` to `origin/main` on every run, so install.sh just works in any folder for any user (new install or upgrading from v0.7.9/v0.7.10) with no manual stash/force-reset/CLAWS_FORCE_RESET dance.
213
+ - **Contributor escape hatch unchanged**: contributors who want a protected dev clone use `CLAWS_DIR=/path/to/dev/claws bash <(curl ...)` to point install at a different working dir. The default `~/.claws-src` is now treated as throwaway by design.
214
+ - **Opt-in protection**: `CLAWS_DEV_PROTECT=1 bash <(curl ...)` re-enables the old v0.7.9 dirty-tree guard for the rare case someone wants safety on the default `~/.claws-src` path. Most users should use `CLAWS_DIR=` instead.
215
+ - **Net effect for users**: zero friction. Run `bash <(curl -fsSL https://raw.githubusercontent.com/neunaha/claws/main/scripts/install.sh)` in any folder, get Claws.
216
+
217
+ ### Why this changed
218
+ v0.7.9 added a guard meant to protect contributors from accidentally losing in-flight work via `git reset --hard`. But the guard fired on auto-generated tracked files (e.g., `extension/native/.metadata.json` bundledAt timestamp), blocking every user's second install. v0.7.10's hotfix gitignored the metadata file, but existing v0.7.9 users still hit the guard during upgrade because their old `~/.claws-src` was at v0.7.9 commit (pre-gitignore). v0.7.11 removes the guard entirely — the architectural insight is that install.sh OWNS its working directory and should hard-reset freely.
219
+
220
+ ## [0.7.10] - 2026-05-03 — 10-phase lifecycle (Wave A+B+C+D) + auto-advance engine + event-driven completion
221
+
222
+ ### Fixed (post-release hotfix, force-tagged)
223
+ - **Install blocker**: `extension/native/.metadata.json` was tracked in git but rewritten with a fresh `bundledAt` timestamp on every native rebuild. Every user's second install hit the P3 hygiene guard ("uncommitted changes — refusing to git reset --hard"). Now gitignored and untracked. `git rm --cached` removes it from the index; existing installs that pull this fix will silently drop the tracked copy. Per ARCHITECTURE.md P4 (build artifacts shouldn't be tracked).
224
+
225
+ ### Added
226
+ - **`docs/ARCHITECTURE.md`** (NEW): comprehensive canonical architecture anchor — 10 principles, system map (extension ↔ MCP server ↔ lifecycle engine), anti-patterns catalog (A1–A5), known-gaps roadmap §IX. Commit d831820. All architectural changes must pass §X anchoring protocol before landing.
227
+ - **Wave C closure — PostToolUse hook fail-closes spawn → monitor race** (`scripts/hooks/post-tool-use-claws.js`, NEW): after every spawn-class MCP tool call returns, waits up to ~5 s for `lifecycle.monitors[terminal_id]` to be registered. If missing, publishes `wave.violation` event (kind=monitor-missing) and auto-closes the orphaned terminal with a stderr warning. `inject-settings-hooks.js` registers four explicit PostToolUse matchers (claws_create/worker/fleet/dispatch_subworker), tagged `_source:'claws'`, dual-form canonical+fallback. Closes the spawn → monitor race window flagged in ARCHITECTURE.md §VI. New `extension/test/post-tool-use-monitor-gate.test.js` (15 checks): no-socket bail, non-spawn-class no-op, no-terminal-id no-op, monitor registered → pass, monitor missing → violate+close, 5 s self-kill.
228
+ - **Task #58 — multi-signal completion detection in detach watchers.** Adds three backup signals beyond the printf marker: explicit `[CLAWS_PUB] topic=worker.<id>.complete` line, idle-timeout (no pty activity for `idle_timeout_ms` after `min_runtime_ms` warm-up), and existing error-marker remains. ANY signal fires → publish system.worker.completed → auto-close → mark-worker-status('closed') → engine cascade. Payload now includes `completion_signal`. Defaults: idle_timeout_ms=30000, min_runtime_ms=30000.
229
+ - **Wave D — LifecycleEngine** (`extension/src/lifecycle-engine.ts`, NEW): in-process auto-advance state machine. Wired into the three lifecycle mutation handlers (`register-spawn`, `register-monitor`, `mark-worker-status`). On each worker state change, calls `nextAutoPhase()` from `lifecycle-rules.ts`; if a transition is recommended and passes `canTransition` + gate checks, calls `store.setPhase(next)` and emits `lifecycle.phase-changed` on the bus. Cascades safely (safety loop of 10) for multi-step transitions (DEPLOY→OBSERVE→HARVEST in a single call). Eliminates orchestrator camping in SPAWN — phases self-progress as work happens.
230
+ - **Lifecycle schema v3** (`extension/src/lifecycle-store.ts`): SESSION-BOOT through SESSION-END phases (10), `worker_mode` (single|fleet|army), `expected_workers`, `spawned_workers` map, `monitors` map, `registerSpawn`/`registerMonitor`/`markWorkerStatus` methods.
231
+ - **`extension/src/lifecycle-rules.ts`** (NEW): pure validators for transitions, gates, auto-advance decisions.
232
+ - **New lifecycle commands**: `lifecycle.register-spawn`, `lifecycle.register-monitor`, `lifecycle.mark-worker-status`.
233
+ - **D+F integration (Wave A — all spawn-class tools)**: `claws_create`, `claws_worker` fast-path, `runBlockingWorker` (+ `claws_fleet` which routes through it), and `claws_dispatch_subworker` all generate `correlation_id` + atomically call `lifecycle.register-spawn` + `lifecycle.register-monitor`. All detach watchers include `correlation_id` in `system.worker.spawned` / `system.worker.completed` event payloads and call `lifecycle.mark-worker-status` on every terminal transition (completed / failed / timeout). `lifecycle.spawned_workers` + `lifecycle.monitors` maps now populate for every worker regardless of which spawn-class tool was used.
234
+ - feat(v0.7.10) — Wave D part 1: extension now publishes `system.worker.terminated` on `onDidCloseTerminal` for any Claws-tracked wrapped terminal. `TerminalManager.setTerminalCloseCallback` wired from `ClawsServer` constructor; fires only for `wrapped=true` terminals. New test `test:ondidclose-publish` (8 checks) verifies publish + payload + unwrapped suppression. Per ARCHITECTURE.md P1 + Wave D roadmap. Adds `'terminated'` to `WorkerStatus` type + `TERMINAL_WORKER_STATUSES` set.
235
+ - feat(v0.7.10) — Wave D part 2: detach watchers in `mcp_server.js` recognize `system.worker.terminated` as 4th completion signal (alongside marker/error/pub_complete). `detectCompletion` extended with `terminatedSet` param; `_pconn` subscribes to `system.worker.terminated` on first registration; `_workerTerminatedSet` updated on every push frame. When worker's terminal closes, watcher fires `system.worker.completed` with `completion_signal:'terminated'` without waiting for marker. Closes the M15 marker-skip gap (`.local/audits/m15-marker-skip-gap.md`). `multisignal-completion.test.js` extended with 3 `terminated` cases. Also fixes pre-existing bug: detach-watcher `detectCompletion` callsite was passing `_msState` object as `termId` arg, breaking `pub_complete` signal in that path; corrected to `String(termId)`.
236
+ - docs(v0.7.10) — `templates/CLAUDE.global.md`: codified F1/F2/F3 numbered-final-actions worker convention (per `.local/audits/m15-marker-skip-gap.md`). Reduces marker-skip rate by framing final Bash calls as a numbered checklist with rationale.
237
+ - scripts(v0.7.10) — `scripts/install.sh`: dev-mode symlinks, `scripts/uninstall.sh` (NEW), Linux ABI support for bundled node-pty, install backup pruning (keeps last 3 only).
238
+ - 60 unit tests covering lifecycle-store + lifecycle-rules.
239
+
240
+ ### Changed
241
+ - **Wave B — bus-stream Monitor primitive in all spawn-class tool responses**: `monitor_arm_command` strings returned by `claws_create`, `claws_worker`, `runBlockingWorker`/`claws_fleet`, and `claws_dispatch_subworker` now use the canonical `Monitor + stream-events.js` pattern (CLAUDE.md principle #5). Subscribes directly to the pub/sub bus, filters by `correlation_id`, exits on first `system.worker.completed` event (`grep -m1`). Sub-100ms latency, immune to SIGURG idle-kill, appears as 'monitor' in the Claude Code task panel. The old `Bash(until grep -qE…events.log)` passive polling pattern is fully removed.
242
+ - **Wave C enforcement** (`pre-tool-use-claws.js`, `session-start-claws.js`): SessionStart now auto-spawns the `stream-events.js` sidecar on boot; PreToolUse Monitor arm gate hard-blocks spawn-class tools until a Monitor is armed; both hooks recognize the canonical bus-stream sidecar pattern and no longer demand the deprecated `tail -F` satisfier.
243
+ - templates (`templates/CLAUDE.project.md`, `templates/CLAUDE.global.md`): updated Monitor arm command, peerId heartbeat interval, and sidecar boot instructions to match canonical Wave B/C pattern.
244
+ - `.claude/commands/` + `.claude/skills/`: all 24 slash commands and orchestration-engine skill aligned with non-blocking defaults and canonical `monitor_arm_command` shape.
245
+ - `claws_lifecycle_plan` now requires `worker_mode` + `expected_workers` args (declares mission shape upfront).
246
+
247
+ ### Fixed
248
+ - **CRITICAL: claws_worker fast-path boot detection.** The default `detach:true` code path had only `sleep(400)` before sending mission paste — too short for Claude TUI to boot. Multi-line missions landed in shell prompt before Claude was ready, sat in input box without ever submitting. Now polls for `❯` + `cost:$` stable for 3 polls, then waits 5000ms post-readiness settle. Mirrors `runBlockingWorker` pattern. Verified end-to-end with real Claude worker test.
249
+ - fix(v0.7.10) — Wave C SIGURG resolution (Task #63): `pre-tool-use-claws.js` now accepts the canonical `stream-events.js` sidecar (auto-spawned by SessionStart) as a valid Monitor satisfier. Eliminates the recurring "Hook satisfier failed exit 144" task notifications. Old `tail -F` check kept as deprecated fallback for one release. Per ARCHITECTURE.md P9 + anti-pattern A1. New regression test: `test:pre-tool-use-sidecar-recognized` (7 assertions).
250
+ - **BUG-A** — `nextAutoPhase` had cases for SPAWN, DEPLOY, OBSERVE, CLEANUP but missing HARVEST. Engine cascaded 4 transitions then stopped. Now adds HARVEST→CLEANUP transition (gated by `canCleanup`), enabling 5-transition auto-cascade through the mission cycle.
251
+ - **BUG-B** — Detach watchers in `mcp_server.js` (4 callsites: runBlockingWorker, blocking-worker happy-path, fast-path watcher, dispatch_subworker) auto-closed terminals on marker match but never notified the lifecycle store of closure. `workers[].closed` stayed false, blocking `canReflect` gate forever. Fix: after successful close, call `lifecycle.mark-worker-status` with `status='closed'` so the lifecycle store flips the flag. Now CLEANUP→REFLECT can auto-advance once all spawns are closed.
252
+ - **M20 install.sh calibration** — step 8 verification now checks PostToolUse spawn-class hooks in addition to PreToolUse, so a partial Wave C install is surfaced at install time instead of silently running without the monitor race-close gate.
253
+ - **M20 update.sh calibration** — post-update health check extended with PostToolUse spawn-class hook presence check; warns with auto-fix command if Wave C entries are absent.
254
+ - **M20 fix.sh calibration** — check 8b detects and auto-repairs missing PostToolUse entries (pre-Wave C installs); check 9 hook probe now covers `post-tool-use-claws.js` alongside session-start/pre-tool-use/stop.
255
+
256
+ ## [0.7.10-pre] - 2026-05-01 — Direct-prompt missions (revert v0.7.9 file-referrer + boot retry)
257
+
258
+ v0.7.10 deletes the two new abstractions v0.7.9 introduced (file-referrer mission delivery and boot retry) and returns to the v0.7.4 contract: **the mission text becomes Claude Code's input as if a human typed it.** No /tmp file. No "Read /tmp/.../mission.md and follow it precisely." prompt. The v0.7.9 marker false-match is still fixed, but via a much simpler mechanism: capture the pty scan offset *after* the payload + echo settle, so the user's mission text never re-matches the completion marker.
259
+
260
+ ### Fixed — claws_fleet sharedDefaults undefined-key bug
261
+
262
+ The first claws_fleet implementation wrote `sharedDefaults = { cwd: args.cwd, model: args.model, ... }` unconditionally — when the caller omitted any of those keys, the spread `{ ...sharedDefaults, ...w }` propagated `undefined` values into `runBlockingWorker`. Inside, `{ ...DEFAULTS, ...args }` let the undefined clobber the model default, producing a launch line of `claude --dangerously-skip-permissions --model undefined` that broke the spawn and left workers hanging at `pid=-1`. Fix: build `sharedDefaults` by INCLUDING ONLY keys the caller actually set. Same defensive filter for per-worker overrides via `wClean`. Regression test added.
263
+
264
+ ### Removed (rolled back from v0.7.9)
265
+
266
+ - **File-referrer pattern.** No mission file in `/tmp`. No `runToken`. No `fileNonce`. No `mission_file` / `run_token` worker return-value fields. Mission goes directly to Claude Code's input prompt.
267
+ - **Boot retry** (`boot_retries`). Sending the launch command a second time was harmful — it typed `claude ...` into an already-booted Claude Code TUI as a user prompt, which the user saw as a confusing "second claude command". Now: single launch attempt, proceed best-effort if `boot_marker` isn't seen within `boot_wait_ms`.
268
+
269
+ ### Kept (the only correctness fix that survived from v0.7.9)
270
+
271
+ - **Marker scan offset** — `markerScanFrom` is captured **after** the payload is sent and the echo lands (400ms sleep), so the poll loop only scans bytes produced by Claude *after* the mission was submitted. The user's mission text (now echoed in the pty log) cannot false-match the completion marker.
272
+ - **`boot_marker` default** — `'bypass permissions'` (matches the Claude Code v2.x bypass-mode footer; legacy `'Claude Code'` never matched the ANSI-stripped banner).
273
+ - **`scripts/install.sh` skill-loop self-collision guard** — `-ef` (same-inode) test prevents the loop from `rm`-ing the source when `TARGET == INSTALL_DIR` on dev machines.
274
+ - **`scripts/install.sh` uncommitted-work guard** — Step 1's `git reset --hard origin/main` refuses to run on a dirty tree unless `CLAWS_FORCE_RESET=1` is set, so contributor edits are no longer silently destroyed.
275
+
276
+ ### Added — claws_fleet detach mode + claws_workers_wait
277
+
278
+ `claws_fleet` now accepts `detach=true`, which returns immediately after spawning each worker terminal (no marker poll) — the default blocking behavior is completely unchanged. Detached fleets are meant to be observed by a follow-up call to the new `claws_workers_wait` tool, which polls an array of terminal ids until each emits its `complete_marker` or `error_marker` (or times out). The two together enable a fire-and-monitor pattern where the orchestrator can do other work between spawning and harvesting. `claws_fleet` also migrated internally from `Promise.all` to `Promise.allSettled` so a single failed worker no longer aborts the entire fleet — each result is individually wrapped and the summary always covers all N workers. Tool count grows 37 → 38.
279
+
280
+ ### Added — `claws_fleet` (real parallel orchestration)
281
+
282
+ `claws_fleet({ workers: [{name, mission, …}] })` is the new MCP tool for true parallel worker fan-out. Internally calls `runBlockingWorker` for each entry inside `Promise.all`, so all workers spawn and run concurrently within a **single MCP tool/call**. This bypasses Claude Code's MCP client-side serialization (which awaits each tool/call response before sending the next) — calling `claws_worker` N times from one assistant message still serializes, but `claws_fleet` returns one consolidated result with `wall_clock_ms`, `max_individual_ms`, `sum_individual_ms`, and per-worker results. Tool count grows 36 → 37.
283
+
284
+ ### Fixed — bulletproof marker scan offset
285
+
286
+ `runBlockingWorker`'s `markerScanFrom` capture replaced the timing-fragile 400ms fixed sleep with a **poll-for-settle loop** (up to 5s): waits until either the pty buffer grows by 200+ bytes past pre-send length OR a TUI indicator appears (`Pasted text`, `tokens`, `thinking`, `Synthesizing`, `Combobulating`, `Brewed`, etc.), then captures the offset. Eliminates the false-completion that hit `para-auditor` in the v0.7.10 parallel test, where Claude Code v2.x delayed the input echo past the 400ms window and the marker substring in the mission body false-matched on echo.
287
+
288
+ ### Added — `docs/mcp-tools-guide.md` (calibration matrix)
289
+
290
+ Comprehensive when-to-use-which guide for all 37 MCP tools. TL;DR decision matrix, mission-style cookbook (single / fleet / wave army), lifecycle gates table, anti-patterns, and the concurrent-dispatch caveats explaining why `claws_fleet` exists.
291
+
292
+ ### Fixed — MCP main-loop concurrent dispatch (the critical fan-out fix)
293
+
294
+ `mcp_server.js:1304` — the MCP `tools/call` branch used to `await handleTool(...)` inside the main `while (true)` loop. That made the loop **strictly serial**: every tool call blocked the next message read. When `claws_worker` sat in its poll loop for up to `timeout_ms`, the next tool call couldn't even start until the first either matched its marker or timed out. Three "parallel" `claws_worker` calls actually queued up sequentially — fan-out and wave-army patterns were broken.
295
+
296
+ Now the dispatch is fire-and-forget: `handleTool(...).then(respond).catch(respond)`. The main loop reads the next message immediately, multiple handlers interleave on the JS event loop, and N parallel `claws_worker` calls actually run concurrently with their own pty terminals. JSON-RPC responses can arrive out-of-order relative to requests (allowed by spec; each response carries the matching id). State sharing is safe — `_pconn.pending` uses unique rids, `_eventBuffer` push is single-statement-atomic, `_circuitBreaker` reads/writes are racy but benign.
297
+
298
+ ### Tests
299
+
300
+ `extension/test/worker-fixes-v079.test.js` rewritten to lock down v0.7.10's contract: 11 static-analysis checks covering the four correctness fixes plus **explicit assertions that the file-referrer pattern and boot-retry loop are NOT present, AND that `tools/call` dispatches concurrently (no `await` on `handleTool`)**. Future contributors who try to reintroduce the broken patterns will fail this test.
301
+
302
+ ### Added — single-source-of-truth version
303
+
304
+ - **`scripts/bump-version.sh <X.Y.Z>`** — the only blessed way to change the project version. Updates root `package.json`, `extension/package.json`, and `extension/package-lock.json` (root + nested `packages[""]`) atomically. Validates SemVer 2.0 strictness (rejects four-segment versions like `0.7.7.1` that VS Code's manifest validator can't parse).
305
+ - **`extension/test/version-drift.test.js`** — fails the suite if any of the four version fields disagree, or if the version isn't SemVer 2.0 compliant. Wired into `npm test`. Catches the class of bug that left `extension/package-lock.json` stale at `0.7.5` for three releases without anyone noticing.
306
+
307
+ ### Backwards compatibility
308
+
309
+ `claws_worker(name, mission)` API is unchanged. All call patterns from v0.7.4 onward continue to work as they did pre-v0.7.9. The worker return value drops `mission_file` and `run_token` (those fields were introduced in v0.7.9 and are gone with the file-referrer they belonged to).
310
+
311
+ ## [0.7.9] - 2026-04-30 — claws_worker reliability overhaul
312
+
313
+ **`claws_worker` v0.7.8 was effectively unusable** for any caller passing a multi-line `mission` to Claude Code: workers either false-completed in 1–2 seconds (marker collision on input echo) or sat forever in collapsed-paste limbo (Claude Code v2.x auto-detects multi-line bursts as paste). The only working pattern was hand-rolling a single-line file referrer with a token never present in the mission text. v0.7.9 makes that pattern automatic, and adds boot retry + a correct boot marker.
314
+
315
+ > **Follow-up patches (post-tag, force-pushed onto v0.7.9):**
316
+ > - **Fix A** — `scripts/install.sh` skill-copy loop: added `-ef` (same-inode) guard so the loop skips the `rm -rf` + `cp -r` pair when source and destination resolve to the same directory. Without this, dev machines where `~/.claws-src` symlinks to the project root had install.sh wipe the source skill before it could be copied — install.sh aborted at step 6, never running steps 7–9.
317
+ > - **Fix B** — `mcp_server.js` `runBlockingWorker` file-referrer: decoupled the mission file path nonce from the run-token. The initial v0.7.9 implementation embedded `runToken` directly in the file path; the path was in the single-line referrer payload, which was echoed into the pty log on send, where the marker scanner false-matched. New behavior: independent `fileNonce` for the path, `runToken` lives only inside the file content (where Claude's Read tool ingests it but the pty never sees it).
318
+ > - **Fix C** — `scripts/install.sh` Step 1: added an uncommitted-work guard before the `git reset --hard origin/main`. install.sh used to silently destroy local edits on dev boxes where INSTALL_DIR == project root. The guard refuses to reset on a dirty tree unless `CLAWS_FORCE_RESET=1` is set; clear error explains the three escape hatches (commit, force, or use a different `CLAWS_DIR`).
319
+ > - Regression test: `extension/test/worker-fixes-v079.test.js` grew from 14 → 19 static-analysis checks covering all three follow-ups.
320
+
321
+ **No API change.** Existing `claws_worker(name, mission)` calls just work. Existing `command:` / `launch_claude=false` / explicit `complete_marker` paths preserve v0.7.8 behavior exactly.
322
+
323
+ ### Fixed
324
+
325
+ - **Bug 1 — Marker false-match on input echo (`mcp_server.js:531`)** The poll loop did `text.includes(complete_marker)` over the entire pty buffer including the echo of the mission text the worker just sent. Any marker substring referenced in the mission triggered immediate false-completion. v0.7.9 captures `markerScanFrom = pty_log_length` after the payload is sent and only scans bytes added *after* that point.
326
+ - **Bug 2 — Bracketed-paste mission never submits in Claude Code v2.x** Claude Code auto-detects multi-line bursts as paste and collapses to `[Pasted text #N +M lines] paste again to expand`. The trailing CR `runBlockingWorker` sent did not escape that collapsed state, so the mission never ran. v0.7.9 introduces a **file-referrer pattern** for Claude Code missions: the mission body is written to a temp file with a per-spawn random run-token, and the worker sends a single-line referrer (`Read /tmp/claws-mission-…md and follow it precisely.`) that fits on one line and bypasses paste detection entirely. Mission file is cleaned up on auto-close.
327
+ - **Bug 4 — Boot marker default never matched** `boot_marker` defaulted to `'Claude Code'` (with a space), but the ANSI-stripped Claude Code v2.x banner renders as `ClaudeCodev2.1.123` — no space. Worker burned the full 8s `boot_wait_ms` on every spawn before falling through. New default: `'bypass permissions'`, which matches the bypass-mode footer reliably.
328
+
329
+ ### Added
330
+
331
+ - **Boot retry** (`boot_retries`, default 2) — if the first boot attempt times out without seeing the boot marker, `runBlockingWorker` re-sends the launch command and waits another `boot_wait_ms`. Caps at the configured retry count, then proceeds best-effort.
332
+ - **`mission_file` and `run_token` in the worker return value** — surfaces the file path and per-spawn token used by the file-referrer pattern, for debugging and inspection. Both are `null` when the legacy path is used (explicit marker, `command:` mode, or `launch_claude=false`).
333
+
334
+ ### Compatibility
335
+
336
+ The file-referrer pattern is **opt-out, not opt-in**:
337
+ - Default for `claws_worker(name, mission)` with `launch_claude=true` (the common case).
338
+ - **Skipped** when the caller passes an explicit `complete_marker` — the user signalled they want their own marker semantics; v0.7.9 just adds the scan-offset fix to make that marker bulletproof against echo-match.
339
+ - **Skipped** for `command:` mode (shell workers, no Claude Code). Scan-offset still applies.
340
+ - **Skipped** when `launch_claude=false`. Mission goes directly to whatever shell or REPL is running.
341
+ - File-write failure (e.g. `/tmp` readonly) falls back to the v0.7.8 direct-send path — degraded but matches prior contract.
342
+
343
+ ### Out-of-scope (deferred to v0.7.10)
344
+
345
+ - **Bug 3 — `claws_send` first-character duplication** (cosmetic: `cclaude`, `eecho`). Visual artifact in pty echo only; bytes arriving at the shell are correct. Needs byte-level pty trace to root-cause.
346
+ - Send-retry on stuck paste (would require activity-detection heuristic).
347
+ - `[Pasted text]` detection + auto-recovery (re-send as single-line with file referrer).
348
+
349
+ ## [0.7.8] - 2026-04-30 — Re-release of v0.7.7.1 with semver-compliant version
350
+
351
+ **Hot-fix replacing v0.7.7.1.** v0.7.7.1 used a four-segment version (`MAJOR.MINOR.PATCH.BUILD`) which is not valid per the [SemVer 2.0 spec](https://semver.org/spec/v2.0.0.html) — only `MAJOR.MINOR.PATCH` is allowed. VS Code's extension manifest validator rejected the `0.7.7.1` extension with "Extension version is not semver compatible" and disabled it for users who updated. v0.7.8 contains the same fixes as v0.7.7.1 with a properly-incremented PATCH segment so VS Code accepts and enables the extension.
352
+
353
+ ### Action required for users hit by 0.7.7.1
354
+
355
+ Run `/claws-update` again — install.sh will deploy the v0.7.8 VSIX over the disabled v0.7.7.1 entry. Reload VS Code (Developer: Reload Window) and the extension activates again. No manual cleanup needed.
356
+
357
+ ### Fixed
358
+
359
+ - **Banner suppression bug** `scripts/shell-hook.sh:24` — dropped `export` from `CLAWS_BANNER_SHOWN=1`. The variable was meant to scope to the current shell only (suppress re-paint on `source`), but `export` made it leak into every child process. When VS Code was launched from a banner-painted shell, every terminal it spawned inherited `CLAWS_BANNER_SHOWN=1` pre-set and the banner never painted. With `export` removed, each new interactive shell starts with no flag, paints once, and child processes get a clean slate. Verified: banner paints reliably with `v0.7.8` in fresh terminals.
360
+ - **Worker spawn cwd + model defaults** `mcp_server.js` `runBlockingWorker` — `claws_worker` now passes `cwd` to the create RPC (defaulting to the MCP server's `process.cwd()` so workers land in the project root, not `$HOME`) and launches Claude Code with `--model claude-sonnet-4-6` by default. Both overridable via new `cwd` and `model` args. Previously, workers landed in `$HOME`, hit the trust dialog, failed the project MCP socket walk-up, and booted whichever model the user's shell defaulted to (often Opus xhigh). Schema regenerated; codegen test still passes (36 tools).
361
+ - **GAP-3** `scripts/install.sh` — when update.sh's `--ff-only` pull diverged and exported `GIT_PULL_OK=0`, install.sh's own `git reset --hard origin/main` would succeed, leaving the source fresh but with the stale `GIT_PULL_OK=0` flag still gating CLAUDE.md re-injection. install.sh now flips `GIT_PULL_OK=1` after a successful force-reset so the new template + tool list lands.
362
+ - **GAP-1** `scripts/install.sh:559` — `EXPECTED_MIN_VERSION` bumped from `"0.7.4"` to `"0.7.7"`.
363
+ - **GAP-2** `scripts/install.sh:1182-1185` — corrected misleading comment about hook bin path. Documentation only.
364
+
365
+ ## [0.7.7.1] - 2026-04-30 — WITHDRAWN (invalid semver)
366
+
367
+ This version was published with a four-segment string (`0.7.7.1`) which is not valid per [SemVer 2.0](https://semver.org/spec/v2.0.0.html). VS Code disabled the extension. **Use [v0.7.8](#078---2026-04-30--re-release-of-v0771-with-semver-compliant-version) instead** — same fixes, valid version.
368
+
369
+ ## [0.7.7] - 2026-04-30 — Development Discipline Hooks
370
+
371
+ ### Fixed (v0.7.7-bulletproof)
372
+
373
+ - **P0-1** `scripts/fix.sh` — added explicit `~/.claude/settings.json` JSON validity check (check 7b). Malformed settings caused ALL hooks to fail silently per session. Fix.sh now detects the issue, backs up the file, and re-injects Claws hooks via `inject-settings-hooks.js`.
374
+ - **P1-2** `scripts/inject-dev-hooks.js` — replaced raw `JSON.parse` + `writeFileSync` with `json-safe.mjs` `mergeIntoFile` (JSONC-tolerant, atomic, abort-on-malformed). Previously, a comment in `.claude/settings.json` caused silent parse failure, `{}` fallback, and full overwrite — destroying all pre-existing project hooks.
375
+ - **P1-1** `scripts/inject-settings-hooks.js` — legacy array hooks (early Claude Code) now migrated to object format before remove+add. Previously, adding named properties to a JS array caused new hooks to be silently dropped by `JSON.stringify`. Both `--update` and add-only paths now migrate array→object first.
376
+ - **D-2** `.claude/settings.json` (dev-box) — migrated dev-hook command paths from `scripts/dev-hooks/` (source path) to `.claws-bin/dev-hooks/` (canonical production path). Removed legacy inner-`_source` format entries that the inject script could not update automatically due to format mismatch.
377
+ - **P3-6** `scripts/update.sh` — added `--dry-run` flag. When passed, skips the installer step and instead prints the git diff stat from `origin/main` plus pending local commits — useful for previewing what an update would change before applying it.
378
+ - **P3-5** `scripts/install.sh` — VSIX post-install stale cleanup now polls up to 1s (5×200ms) for the extracted extension directory to appear before deciding it's absent. Previously, VS Code's async VSIX extraction could cause the cleanup to skip and warn on every install.
379
+ - **P3-1/2/3** `scripts/install.sh` — three hygiene fixes: (1) deploy `schemas/client-types.d.ts` to `.claws-bin/schemas/` for typed SDK consumers; (2) replace 5 hardcoded `if [ -d .claude/skills/<name> ]` blocks with a `for _skill_src in .claude/skills/claws-* dev-protocol-*` loop so new skills are picked up automatically; (3) emit `warn` when `claws-sdk.js` is absent instead of silently skipping.
380
+ - **P2-4** `scripts/fix.sh` — when the socket is LIVE, now probes terminal list for wrapped terminals with missing `logPath` (indicates `script(1)` failed at pty allocation time). On Linux, also checks `command -v script` and suggests `bsdutils` install if absent.
381
+ - **P2-3** `scripts/fix.sh` — MCP handshake failure now triggers auto-recovery: copies `mcp_server.js` from `$INSTALL_DIR` to `.claws-bin/` and re-probes the handshake once. Previously the script reported the failure and stopped.
382
+ - **P2-1** `scripts/shell-hook.sh` — now exports `CLAWS_DIR` at source time (detected from script location, falls back to `~/.claws-src`). Updated `/claws-fix` and `/claws-update` slash commands to use `${CLAWS_DIR:-$HOME/.claws-src}` instead of hardcoded `~/.claws-src`, so a `CLAWS_DIR` override is honored end-to-end.
383
+ - **P2-2** `scripts/install.sh` and `scripts/fix.sh` — added `.claws-bin` symlink guard before `mkdir -p`. Detects and removes dangling or unexpected symlinks at `$PROJECT_ROOT/.claws-bin` before creating the directory, preventing silent deployment to the wrong target path.
384
+ - **P1-5** `scripts/fix.sh` — added shell-hook sourcing check (check 6b). Greps `.zshrc` / `.bashrc` for a `shell-hook.sh` source line; if absent, appends one automatically so `claws-ls`, `claws-new`, `claws-run`, and `claws-log` functions are available in new terminals.
385
+ - **P1-4** `scripts/fix.sh` — added unconditional `.claws-bin` integrity check (check 4c), independent of the `.mcp.json` registration gate. Detects missing directory, missing `mcp_server.js`, and dangling symlinks, then auto-repairs by copying from `$INSTALL_DIR`. Previously, a broken `.claws-bin` was only repaired when `.mcp.json` was also missing.
386
+ - **P1-3** `scripts/fix.sh` — replaced `ls … | head -1` with a full `for inst` loop over all `neunaha.claws-*` entries per editor dir. Added post-scan duplicate detection that warns when multiple copies exist in the same editor, preventing silent load-order conflicts.
387
+ - **D-1** `schemas/mcp-tools.json` — registered 5 missing MCP tools: `claws_drain_events`, `claws_pipeline_create`, `claws_pipeline_list`, `claws_pipeline_close`, `claws_dispatch_subworker`. These handlers existed in `mcp_server.js` but were invisible to AI orchestrators because `tools/list` reads from the schema file.
388
+ - **A-5** `scripts/install.sh` — `STEP_TOTAL` bumped from 8 to 9 to match actual step count (cosmetic — install used to render `9/8 Verifying`).
389
+
390
+ ### Added
391
+
392
+ - **Five dev-hook scripts** in `scripts/dev-hooks/`: `check-stale-main`, `check-tag-pushed`, `check-tag-vs-main`, `check-open-claws-terminals`, `check-extension-dirs` — each exits 0 (warn-only, never blocks), logs to `/tmp/claws-dev-hooks.log`.
393
+ - **`scripts/inject-dev-hooks.js`** — idempotent safe-merge hook registration; tags all entries with `_source:"claws-dev-hooks"` for clean removal; skips re-registration if hook already present.
394
+ - **`install.sh` and `update.sh`** — now deploy dev hooks to `<project>/.claws-bin/dev-hooks/` and register them via `inject-dev-hooks.js` on both fresh install and update paths.
395
+ - **`scripts/shell-hook.sh` runtime version** — banner version is now read at runtime from `CLAWS_VERSION` env var or nearest `package.json` (walks up from CWD); no more hardcoded `v0.x.y` that drifts across releases. Banner also detects pipe-mode (non-interactive) and suppresses the command list in that context.
396
+ - **`templates/CLAUDE.project.md`** — new "Development Discipline (enforced by hooks)" section with 7 best-practice bullets covering stale-main pulls, semver compliance, extension-dir safety, CLAUDE.md re-injection, pre-PR drift check, tag discipline, and Claws terminal policy.
397
+ - **WaveRegistry lifecycle hardening** — wave lifecycle state machine enforces valid transitions (PLANNED→RUNNING→COMPLETE/FAILED); stale wave entries GC'd after configurable TTL; `claws_wave_status` now returns `phase` + `elapsedMs`.
398
+ - **Army-style nested wave harvest** (`extension/src/wave-registry.ts`, `server.ts`) — `WaveRecord` gains `parentWave?`, `subWorkerTerminals[]`, `harvestedAt?`, `orphanedTerminals[]`; `WaveRegistry.trackTerminal()` records TIDs spawned by wave-affiliated peers; `harvestWave()` returns orphaned TIDs for auto-close on `wave.complete`; lead-silence violation timer fires `wave.<id>.violation {kind:"silent_lead_with_active_subs"}` when LEAD goes quiet with active sub-worker terminals.
399
+ - **`WaveHarvestedV1` schema** (`event-schemas.ts`) — typed schema for `wave.*.harvested` events; registered in `SCHEMA_BY_NAME` and `TOPIC_REGISTRY`.
400
+ - **`claws_wave_status` nested tree** (`mcp_server.js`) — response now includes `lead: {peerId, peerName, terminalId, status, lastSeenMs}` and `subWorkers[].terminalId`; `subWorkerTerminals` flat array exposed.
401
+ - **`PeerConnection`** (`peer-registry.ts`) — gains `waveId?` and `subWorkerRole?` to persist wave affiliation from `hello` across subsequent commands.
402
+
403
+ ### Fixed (carries all v0.7.6.1 patch fixes)
404
+
405
+ - **P0-1** `mcp_server.js` — `claws_worker` circuit breaker: skips reconnect if last failure < 30 s ago; `_scanAndPublishCLAWSPUB` trips `scanDisabled` after 3 consecutive socket errors; default `timeout_ms` reduced 1 800 000 → 300 000 ms.
406
+ - **P0-2** `extension/src/server.ts` — orchestrator peers exempt from per-peer publish rate limit; no self-deadlock during high-volume waves.
407
+ - **P1-1** `extension/src/server-config.ts` — `DEFAULT_STRICT_EVENT_VALIDATION` flipped `false` → `true`; W4 validation active by default.
408
+ - **P1-2** `mcp_server.js` — `_eventBuffer.maxWaiters=10` cap; excess `wait_ms` requests return error immediately; `system.bus.ring-overflow` event emitted once per eviction batch.
409
+ - **P1-5/P1-6** `scripts/install.sh` — deploy blocks for `claws-wave-lead`, `claws-wave-subworker`, `dev-protocol-piafeur` skills.
410
+ - **P1-7** `schemas/mcp-tools.json` + `mcp_server.js` — all 5 `claws_task_*` tools (`claws_task_assign`, `claws_task_update`, `claws_task_complete`, `claws_task_cancel`, `claws_task_list`) registered in schemas and MCP handlers.
411
+ - **P1-8** `scripts/shell-hook.sh` — banner version no longer drifts across releases (see Added above).
412
+
413
+ ### Known Issues
414
+
415
+ - `auto-subscribe-cmd.test.js` — emits `envelope:invalid` under `strictEventValidation=true`; pre-existing since v0.7.6.1 P1-1 flip. Not introduced by v0.7.7.
416
+ - `claws-v2-rate.test.js` — same `envelope:invalid` root cause. Pre-existing.
417
+ - `claws-v2-typed-rpc.test.js` — `rpc.call` timeout on test teardown; pre-existing race condition unrelated to v0.7.7 changes.
418
+
419
+ ## [0.7.6.1] - 2026-04-30 — Bug-fix patch (8 P0/P1 code + 2 hot-fixes)
420
+
421
+ ### Fixed
422
+
423
+ - **P0-1/P2-3** `mcp_server.js` — circuit breaker: `_pconnEnsureRegistered` skips reconnect if last failure < 30s ago; `_scanAndPublishCLAWSPUB` trips `scanDisabled` after 3 consecutive socket errors, resumes on explicit reconnect; default `timeout_ms` reduced 1,800,000 → 300,000 ms (5 min).
424
+ - **P0-2** `extension/src/server.ts` — orchestrator peers exempt from per-peer publish rate limit; orchestrator management commands can no longer be self-rate-limited during high-volume waves. Peer role looked up via `this.peers.get(peerId)?.role` before the bucket check.
425
+ - **P1-1** `extension/src/server-config.ts` — `DEFAULT_STRICT_EVENT_VALIDATION` flipped `false` → `true`; the W4 validation guarantee is now active by default; unregistered topics pass through unchecked (the existing `if (dataSchema !== null)` guard in server.ts handles this).
426
+ - **P1-2** `mcp_server.js` — `_eventBuffer.maxWaiters=10` cap; excess `wait_ms` requests return an error immediately; `system.bus.ring-overflow` event emitted once per eviction batch (via `setImmediate` dedup guard `_overflowPending`).
427
+ - **P1-5/P1-6** `scripts/install.sh` — copy blocks added for `claws-wave-lead`, `claws-wave-subworker`, `dev-protocol-piafeur` skills; existing `claws*.md` glob already covers `claws-wave-lead.md` and `claws-army.md` commands; `claws-update` on existing projects now picks up all three skills.
428
+ - **P1-7** `schemas/mcp-tools.json` + `mcp_server.js` — added `claws_task_assign`, `claws_task_update`, `claws_task_complete`, `claws_task_cancel`, `claws_task_list` tool definitions (schemas) and their MCP handler stubs routing to `task.*` protocol commands via the stateful socket; `claws_schema_get` no longer returns not-found for these 5 tools.
429
+ - **P1-8** `scripts/shell-hook.sh:66` — banner version updated `v0.6.1` → `v0.7.6`; was 7 releases stale.
430
+ - **HOT-FIX A** — ran `inject-claude-md.js` against `/Users/ANISH.NEUNAHA/Desktop/Claws`; `CLAWS:BEGIN` block now present.
431
+ - **HOT-FIX B** — removed stale `~/.vscode/extensions/neunaha.claws-0.7.4/` and `neunaha.claws-0.7.5/`; only `neunaha.claws-0.7.6` remains.
432
+
433
+ ## [0.7.6] - 2026-04-30 — Claws TCP — full architectural release (10 waves + embedder)
434
+
435
+ ### Fixed — Ship restoration
436
+
437
+ - `extension/scripts/deploy-dev.mjs`: deploy loop now copies `README.md`, `CHANGELOG.md`, `icon.png` alongside `dist/` and `native/` — these were previously skipped, causing blank display in the VS Code Extensions panel.
438
+ - `extension/CHANGELOG.md`: synced from root CHANGELOG (was stale at v0.5.3, now complete through v0.7.6).
439
+ - `scripts/inject-claude-md.js`: `TOOLS_V2` expanded with all v0.7.6 MCP tools — `claws_lifecycle_{plan,advance,snapshot,reflect}`, `claws_wave_{create,status,complete}`, `claws_deliver_cmd`, `claws_cmd_ack`, `claws_schema_{list,get}`, `claws_rpc_call`.
440
+
441
+ ### Added — W10/L18+L19 Token Auth + WebSocket Transport (Wave 10 — FINAL)
442
+
443
+ - `extension/src/server-config.ts` — `AuthConfig` (`enabled`, `tokenPath`), `WebSocketConfig` (`enabled`, `port`, `certPath`, `keyPath`) sub-configs added to `ServerConfig`; `defaultServerConfig` defaults to both disabled.
444
+ - `extension/src/protocol.ts` — `HelloRequest` gains `token?`, `nonce?`, `timestamp?` fields for L18 auth.
445
+ - `extension/src/server.ts` — `validateAuthToken()`: HMAC-SHA256 over `peerName:role:nonce:timestamp`; checks token present, timestamp ≤5 min stale, nonce single-use, HMAC `timingSafeEqual`; `usedNonces` Set cleared on `stop()`; auth called at start of `hello` handler before any other logic; `wsTransport.start()` invoked in `start()` chain when `webSocket.enabled`; `wsTransport.stop()` in `stop()`.
446
+ - `extension/src/websocket-transport.ts` (new) — `WebSocketTransport` class: `WsSocketAdapter` wraps `ws.WebSocket` in a `net.Socket`-compatible EventEmitter shim (adapts message→data, close→end, write strips `\n` and calls `ws.send`); `WebSocketServer` created over http/https server; TLS when `certPath`+`keyPath` provided; loaded lazily so no cost when WS disabled.
447
+ - `extension/src/extension.ts` — `getConfig()` wires `auth.*` and `webSocket.*` from VS Code settings.
448
+ - `extension/package.json` — `ws@8` + `@types/ws@8` as optional/dev deps; VS Code config contributions for all 6 new config keys; `test:auth` and `test:ws-transport` scripts; both added to `test` chain.
449
+ - `extension/test/claws-auth.test.js` (new) — 6-check auth suite: no-token, wrong-HMAC, valid-HMAC, stale-timestamp, nonce-reuse, auth-disabled.
450
+ - `extension/test/claws-ws-transport.test.js` (new) — 5-check WS suite: hello, pub/sub round-trip, shared peer registry with Unix socket, protocol tag, worker auto-subscribe.
451
+
452
+ ### Added — W8/L16+L7 Typed RPC + Schema Registry (Wave 8)
453
+
454
+ - `extension/src/server.ts` — `rpc.call` command: synchronous blocking RPC — caller's request is held open (like `exec`) until the target peer publishes to `rpc.response.<callerPeerId>.<requestId>` or the timeout fires; `rpcPending` correlation map with `clearTimeout` cleanup on resolution; `schema.list` command returns sorted keys from `SCHEMA_BY_NAME`; `schema.get` command returns a simplified JSON representation via `serializeZodSchema` (recursive Zod `_def` traversal covering object, string, number, boolean, array, record, enum, literal, optional, nullable, unknown).
455
+ - `extension/src/event-schemas.ts` — `RpcRequestV1` (requestId uuid, method, params optional, callerPeerId) and `RpcResponseV1` (requestId, ok, result optional, error optional) Zod schemas; both registered in `SCHEMA_BY_NAME` (32 → 35 with PipelineStepV1).
456
+ - `extension/src/topic-registry.ts` — `rpc.*.request` and `rpc.response.**` patterns registered; registry grows 32 → 34.
457
+ - `extension/src/protocol.ts` — `RpcCallRequest`, `SchemaListRequest`, `SchemaGetRequest` interfaces added to `ClawsRequest` union.
458
+ - `mcp_server.js` — `claws_schema_list`, `claws_schema_get`, `claws_rpc_call` handlers.
459
+ - `scripts/codegen/gen-mcp-tools.mjs` — descriptions and input schemas for the 3 new tools (`claws_schema_list`, `claws_schema_get`, `claws_rpc_call`); tool count grows 23 → 26; `schemas/mcp-tools.json` is fully generated — no hand-edits needed.
460
+ - `schemas/json/rpc-request-v1.json`, `schemas/json/rpc-response-v1.json`, `schemas/json/pipeline-step-v1.json` — generated JSON Schema files (pipeline-step-v1 was missing from prior run).
461
+ - `scripts/gen-client-types.mjs` (new) — standalone codegen script: bundles `event-schemas.ts` via esbuild, walks `SCHEMA_BY_NAME`, emits `schemas/client-types.d.ts` with TypeScript interface declarations for all 35 schemas; zero additional deps (uses esbuild already in extension devDeps).
462
+ - `schemas/client-types.d.ts` (new) — generated TypeScript client type declarations; one `export interface` per SCHEMA_BY_NAME entry; union/nullable/optional/record/array types all handled.
463
+ - `extension/test/claws-v2-typed-rpc.test.js` (new) — 40-check integration suite: round-trip RPC (<500ms), timeout (300ms), unknown-peer error, `schema.list` (checks rpc/worker/cmd names), `schema.get` (positive + negative), validation (missing method/targetPeerId).
464
+
465
+ ### Added — W9/L11+L17 Pipeline Composition + Workflow DAG Foundation (Wave 9)
466
+
467
+ - `extension/src/pipeline-registry.ts` (new) — `PipelineRegistry` with `create`, `get`, `list`, `close`, `findBySource`, `clear`; `PipelineRecord` and `PipelineStep` types; `pipe_NNNN` monotonic IDs; `findBySource` returns only active pipelines for O(n) output-wiring dispatch.
468
+ - `extension/src/server.ts` — `pipeline.create` handler (orchestrator-only, ≥2 steps with source+sink required); `pipeline.list` and `pipeline.close` handlers; output→sink wiring in `publish` handler: `output.<id>.*` topics matched by regex, active pipelines found via `findBySource`, text forwarded to sink via pty `writeInjected` or VS Code `sendText`, `pipeline.<id>.step.<stepId>` event emitted for each delivery.
469
+ - `extension/src/event-schemas.ts` — `PipelineStepV1` Zod schema (pipelineId, stepId, role, terminalId, state, ts); `SCHEMA_BY_NAME` grows from 34 → 35 (also adds previously-missing `rpc-request-v1` and `rpc-response-v1` entries).
470
+ - `extension/src/topic-registry.ts` — `pipeline.*.step.*`, `pipeline.*.created`, `pipeline.*.closed` patterns registered; registry grows 31 → 34.
471
+ - `extension/src/protocol.ts` — `PipelineCreateRequest`, `PipelineListRequest`, `PipelineCloseRequest` interfaces added to the `ClawsRequest` union.
472
+ - `mcp_server.js` — `claws_pipeline_create`, `claws_pipeline_list`, `claws_pipeline_close` handlers.
473
+ - `schemas/mcp-tools.json` — 3 new tool definitions for the pipeline MCP tools.
474
+ - `extension/test/claws-v2-pipeline.test.js` (new) — 34-check integration suite: create/list/close lifecycle (pipeline.*.created push, list active, close emits pipeline.*.closed, list shows closed state), output wiring (output.tA.line publish → step event + sink sendText), error cases (empty steps, missing source/sink, unknown pipelineId), topic subscription acceptance.
475
+
476
+ ### Added — W6/L10 Structured Control — deliver-cmd + cmd.ack (Wave 6)
477
+
478
+ - `extension/src/server.ts` — `deliver-cmd` handler: orchestrator-only; validates target peer exists, deduplicates by `idempotencyKey`, allocates monotonic `seq`, appends to event log, and pushes the command envelope to the worker's auto-subscription topic. `cmd.ack` handler: worker-only; fans out `cmd.<peerId>.ack` to all subscribed orchestrators with the `seq` and `status` fields.
479
+ - `extension/src/protocol.ts` — `DeliverCmdRequest` and `CmdAckRequest` interfaces added to the `ClawsRequest` union.
480
+ - `extension/src/event-schemas.ts` — `CmdDeliverV1` and `CmdAckV1` Zod schemas; `SCHEMA_BY_NAME` grows from 30 → 32.
481
+ - `extension/src/topic-registry.ts` — `cmd.*.ack` pattern registered with `CmdAckV1` schema; registry grows 28 → 29.
482
+ - `mcp_server.js` — `claws_deliver_cmd` and `claws_cmd_ack` MCP tool handlers.
483
+ - `schemas/mcp-tools.json` — 21 → 23 tools; `schemas/json/cmd-deliver-v1.json` and `schemas/json/cmd-ack-v1.json` generated.
484
+ - `scripts/codegen/gen-mcp-tools.mjs` — descriptions and input schemas for the two new tools.
485
+ - `extension/test/claws-v2-control.test.js` — 31-check integration suite (6 suites): basic delivery (push frame, seq number), idempotency (duplicate key returns `{ok:true, duplicate:true}` without re-push), unknown peer error, role gating (orchestrator cannot call `cmd.ack`), event-log durability, and `cmd.*.ack` registry subscription.
486
+
487
+ ### Added — W7/L13+L14 Observability and Rate Control (Wave 7)
488
+
489
+ - `extension/src/event-log.ts` — `lastSequence` getter: returns the last successfully appended sequence number (min 0); used by `system.metrics` heartbeat payload.
490
+ - `extension/src/server-config.ts` — `maxPublishRateHz` (default 10 000) and `maxQueueDepth` (default 500) added to `ServerConfig`; `DEFAULT_MAX_PUBLISH_RATE_HZ` and `DEFAULT_MAX_QUEUE_DEPTH` exported.
491
+ - `extension/src/extension.ts` — `getConfig()` wires `maxPublishRateHz` and `maxQueueDepth` from `claws.*` VS Code settings.
492
+ - `extension/src/server.ts` — L13: heartbeat timer now emits `system.metrics` (publishRate_per_sec, queueDepth, peerCount, eventLogLastSeq, uptimeMs, ts) and `system.peer.metrics.<peerId>` for peers with drops or rate-limit hits; per-heartbeat publish counter resets each tick.
493
+ - `extension/src/server.ts` — L14: per-peer sliding 1-second rate limiter; publish requests exceeding `maxPublishRateHz` return `{ok:false,error:'rate-limit-exceeded'}`; `serverInFlight` admission-control counter (incremented synchronously before any `await`) rejects beyond `maxQueueDepth` with `{ok:false,error:'admission-control:backlog'}`; rate check fires before admission so high-rate publishers get the semantically correct error code.
494
+ - `extension/src/event-schemas.ts` — `SystemMetricsV1` and `SystemPeerMetricsV1` Zod schemas added; registered in `SCHEMA_BY_NAME`.
495
+ - `extension/src/topic-registry.ts` — `system.metrics` and `system.peer.metrics.*` registered with their schemas.
496
+ - `schemas/json/system-metrics-v1.json`, `schemas/json/system-peer-metrics-v1.json` — JSON Schema representations of the two new event types.
497
+ - `extension/test/claws-v2-rate.test.js` — 19-check integration test suite: system.metrics shape and cadence, burst rate-limit rejection, admission-control:backlog, system.peer.metrics per-peer emission with rateLimitHits, 1s backoff recovery, peerCount tracking.
498
+
499
+ ### Added — W5/L8 Event Log Durability Hardening (Wave 5)
500
+
501
+ - `extension/src/event-log.ts` — `EventLogWriter.runRetention(retentionDays)`: deletes `.jsonl` segments (and companion `.idx` files) whose mtime is older than `retentionDays` days; closes the open fd before unlinking the active segment so no EBUSY on Linux; removes deleted entries from the in-memory manifest and flushes to disk.
502
+ - `extension/src/event-log.ts` — `EventLogWriter.compact()`: on startup, merges all segments smaller than 1 KB (COMPACT_SIZE_THRESHOLD) into a single merged `.jsonl` using atomic tmp-then-rename; preserves event sequence ordering; rebuilds the `.idx` for the merged segment.
503
+ - `extension/src/event-log.ts` — Per-segment `.idx` files: `topic<TAB>byte_offset` index written atomically alongside each `.jsonl` on `close()` and `rotate()`; offsets are the exact byte positions of each record's start, enabling O(1) seek for filtered replay. Written via tmp-then-rename for atomicity.
504
+ - `extension/src/server-config.ts` — `EventLogConfig` interface with `retentionDays` (default 7) and `compact` (default true); added to `ServerConfig` as `eventLog` field; `DEFAULT_EVENT_LOG_RETENTION_DAYS` and `DEFAULT_EVENT_LOG_COMPACT` constants exported.
505
+ - `extension/src/server.ts` — `start()` calls `eventLog.compact()` after `open()` when `eventLog.compact` config is true; heartbeat timer calls `eventLog.runRetention(retentionDays)` each tick.
506
+ - `extension/src/extension.ts` — `getConfig()` now populates `eventLog.retentionDays` and `eventLog.compact` from VS Code settings (`claws.eventLog.*`).
507
+ - `extension/test/claws-event-log-retention.test.js` — 10-check test suite: retention deletes old segments and keeps recent; manifest updated; fd closed before deletion; `.idx` written and parseable; `compact()` merges 3 small segments into 1; sequence ordering preserved; `scanFrom` replay works after compaction; byte offsets in `.idx` match actual line starts.
508
+
509
+ ### Added — W1/L4 Vehicle State Machine
510
+
511
+ - `extension/src/protocol.ts` — `TerminalDescriptor` now includes `vehicleState?: 'PROVISIONING' | 'BOOTING' | 'READY' | 'BUSY' | 'IDLE' | 'CLOSING' | 'CLOSED'` so `list` responses expose the current vehicle state.
512
+ - `extension/src/event-schemas.ts` — `VehicleStateV1` Zod schema (terminalId, from, to, ts); `VehicleStateEnum` with all 7 states. `SCHEMA_BY_NAME` updated to include `vehicle-state-v1`.
513
+ - `extension/src/topic-registry.ts` — three new topic patterns registered: `vehicle.*.state`, `vehicle.*.created`, `vehicle.*.closed`.
514
+ - `extension/src/claws-pty.ts` — `ClawsPtyOptions` gains two optional hooks: `onOpenHook` (fires when VS Code calls Pseudoterminal.open()) and `onFirstOutputHook` (fires on the first byte of pty output). These let TerminalManager drive state transitions without coupling to the pty internals.
515
+ - `extension/src/terminal-manager.ts` — `TerminalRecord` grows `vehicleState: VehicleStateName`; `TerminalManager` gains `setStateChangeCallback(cb)` and a private `transitionState(rec, to)` that enforces the valid-transition table (PROVISIONING→BOOTING→READY→BUSY/IDLE→CLOSING→CLOSED). `createWrapped` emits PROVISIONING then immediately BOOTING; `onOpenHook` fires BOOTING→READY when the pty opens; `close` and `onTerminalClosed` emit CLOSING→CLOSED.
516
+ - `extension/src/server.ts` — wires `setStateChangeCallback` in the constructor; the callback calls `emitSystemEvent('vehicle.<id>.state', {terminalId, from, to, ts})` so every transition is appended to the event log and fanned out to subscribers.
517
+ - `extension/test/claws-v2-vehicle-state.test.js` — 19-assertion integration test suite covering: PROVISIONING→BOOTING and BOOTING→READY push frames, close emitting CLOSING→CLOSED, vehicleState in list responses, ordering invariants, payload structure (terminalId, from, to, ts).
518
+
519
+ ### Added — Wave Army Protocol (embedder wave)
520
+
521
+ The embedder wave introduces the Wave Army Protocol — a structured multi-agent orchestration layer built on the claws/2 pub/sub bus. Every wave has a typed lifecycle (create → sub-workers boot → sub-workers complete → lead emits complete) with violation detection and disciplined per-role obligations.
522
+
523
+ **Protocol layer (shipped):**
524
+ - `extension/src/protocol.ts` — `SubWorkerRole` type (`lead | tester | reviewer | auditor | bench | doc`); `ContractedRoles` constant; `HelloRequest` extended with optional `waveId` and `subWorkerRole`; `WaveCreateRequest`, `WaveCompleteRequest`, `WaveStatusRequest` added to `ClawsRequest` union.
525
+ - `extension/src/event-schemas.ts` — 7 new Zod schemas: `WaveLeadBootV1`, `WaveLeadCompleteV1`, `WaveTesterRedCompleteV1`, `WaveReviewFindingV1`, `WaveAuditFindingV1`, `WaveBenchMetricV1`, `WaveDocCompleteV1`. `SCHEMA_BY_NAME` grows from 24 to 31 entries.
526
+ - `extension/src/topic-registry.ts` — `wave.**` catch-all pattern registered; specific wave schemas bound in `SCHEMA_BY_NAME`.
527
+ - `extension/src/wave-registry.ts` — new `WaveRegistry` class tracking active waves: per-role heartbeat timers fire `wave.<N>.violation` after 25s silence; `createWave`, `recordHeartbeat`, `markSubWorkerComplete`, `completeWave`, `handlePeerDisconnect`, `dispose`.
528
+ - `extension/src/server.ts` — `WaveRegistry` wired into `ClawsServer`; handlers for `wave.create`, `wave.status`, `wave.complete`; `hello` records sub-worker heartbeat when `waveId+subWorkerRole` present; `handleDisconnect` notifies registry.
529
+
530
+ **MCP tools (shipped):** `claws_wave_create`, `claws_wave_status`, `claws_wave_complete`, `claws_dispatch_subworker` added to `mcp_server.js` handler dispatch; `schemas/mcp-tools.json` updated with all 4 tool schemas (total grows by 4).
531
+
532
+ **Discipline contract embedded (shipped):** `templates/CLAUDE.project.md` and `templates/CLAUDE.global.md` gain "Wave Discipline Contract (mandatory)" sections listing all 8 sub-worker rules (heartbeat, boot event, phase events, error events, no --no-verify, full suite before commit, type check per .ts file, complete event). `scripts/hooks/session-start-claws.js` extended to include wave discipline summary block when Claws socket is detected.
533
+
534
+ ### Fixed — embedder wave reviewer findings (F28/F29)
535
+
536
+ - `mcp_server.js` `claws_dispatch_subworker` — F28 (MEDIUM): switched mission delivery from `newline:true` to `newline:false` + separate `\r` submit, matching the established `claws_worker` pattern; prevents spurious double-LF in Claude TUI mid-think (reviewer finding F28).
537
+ - `mcp_server.js` `claws_dispatch_subworker` — F29 (LOW): boot-poll loop now tracks `nextOffset` from each `readLog` response and passes it as `offset` on the next call; eliminates repeated full-log reads during the 25 s boot window.
538
+
539
+ **Skills (shipped):** `.claude/skills/claws-wave-lead/SKILL.md` and `.claude/skills/claws-wave-subworker/SKILL.md` — full role contracts, boot sequences, schema references.
540
+
541
+ **Commands (shipped):** `.claude/commands/claws-wave-lead.md` (LEAD activation flow) and `.claude/commands/claws-army.md` (full army deployment with monitoring and completion criteria).
542
+
543
+ ### Added — W2/L15 Event Log Replay + L9 Observation
544
+
545
+ - `extension/src/event-log.ts` — `EventLogReader` class: `scanFrom(cursor, topicPattern)` async generator reads segments from a byte-offset cursor position, filters records by topic pattern via `matchTopic()`, handles both manifest-based and directory-scan segment discovery.
546
+ - `extension/src/server.ts` — `subscribe` handler now validates `fromCursor` format (`parseCursor` → null = reject with `invalid cursor format`); registers subscription in `subscriptionIndex` **before** replay starts (atomicity — no live events missed during replay); `setImmediate` dispatches `replayFromCursor` so the subscribe ACK is sent first. `replayFromCursor` sends `{push:'message', replayed:true}` frames then a `{push:'caught-up', subscriptionId, replayedCount, resumeCursor}` terminal signal.
547
+ - `extension/src/protocol.ts` — `SubscribeResponse` interface adds optional `replayedCount?: number`.
548
+ - `extension/src/claws-pty.ts` — `getForegroundProcess()` uses `pgrep -P <shellPid>` + `ps -p <pid> -o comm=` to detect the foreground process basename under the shell; powers L9 content-type observation.
549
+ - `extension/src/peer-registry.ts` — `DisconnectedPeer` tombstone interface; `fingerprintPeer(peerName, role, nonce)` derives stable 12-hex sha256 fingerprint for `fp_`-prefixed stable peer IDs on reconnect.
550
+ - `extension/src/terminal-manager.ts` — `ContentChangeCallback`; `startContentDetection` polls foreground process every 2 s and fires `onContentChange` on basename transitions; wired via `setContentChangeCallback`.
551
+ - `extension/src/wave-registry.ts` — violation timer updates; sub-worker heartbeat tracking improvements.
552
+ - `extension/test/claws-event-log-replay.test.js` — 13-assertion integration test: publishes 10 events, subscribes with `fromCursor`, verifies all 10 replayed frames carry `replayed:true`, caught-up frame fires with correct count, live events arrive without `replayed`, invalid cursor rejected (TDD: 6 failing → 13 passing).
553
+
554
+ ### Fixed
555
+ - `extension/test/claws-v2-content.test.js` — interrupt foreground process with `\x03` before sending vim and extend wait timeout from 5s to 8s to reduce flakiness on slow machines.
556
+ - `extension/test/event-schemas.test.js` — update `SCHEMA_BY_NAME` count from 19 to 20 (added `vehicle-state-v1`).
557
+ - `extension/test/topic-registry.test.js` — update `TOPIC_REGISTRY` count from 19 to 22 (added 3 vehicle.* patterns).
558
+ - `extension/test/event-schemas.test.js` — align `SCHEMA_BY_NAME` count assertion with current registry: was 19, now 20 after v0.7.5 L1.1+L1.4 schemas added `vehicle-state-v1`; test now derives expected names explicitly and asserts the correct total.
559
+ - `scripts/inject-settings-hooks.js` — hook commands must use absolute paths. `CLAWS_BIN` was used as-is when passed as a relative argument (e.g. `"scripts"`), producing hook commands like `node "scripts/hooks/pre-tool-use-claws.js"` that broke with `ERR_MODULE_NOT_FOUND` whenever Claude Code's CWD was not the project root. Fix: wrap the arg with `path.resolve()` before computing script paths. Regression test added: `extension/test/inject-settings-absolute-paths.test.js`.
560
+
561
+ ## [0.7.5] - 2026-04-29 — Bus hardening release
562
+
563
+ This release hardens the orchestrator↔worker communication bus surfaced by W1–W4 audits. The `.claws/events/default/*.jsonl` was empty on user systems because (a) the MCP server was dropping every push frame from the persistent socket, and (b) default workers never publish.
564
+
565
+ ### L-1 Display fixes (R1, R4, R5, R7) — landed
566
+ - `claws-pty.ts` — inject `CLAWS_WRAPPED=1` (real pty) or `CLAWS_PIPE_MODE=1` (degraded) plus `CLAWS_TERMINAL_ID` so the shell hook reports truthful state
567
+ - `protocol.ts` — `TerminalDescriptor` now exposes `ptyPid` (real shell pid) and `ptyMode` (`'pty'`/`'pipe'`/`'none'`)
568
+ - `terminal-manager.ts` — `describe()` returns `pty.pid` and `pty.mode` from the live `ClawsPty` instance
569
+ - `mcp_server.js` — `claws_list` formatter trusts the `wrapped` boolean (was incorrectly keying off `logPath` which is always null in the Pseudoterminal capture model). Pid column shows the real shell pid. Wrapped state labels: `WRAPPED`, `WRAPPED-DEGRADED-pipe-mode`, `WRAPPED-pending`, `unwrapped`
570
+
571
+ Pre-fix symptoms: `claws_list` always showed `[unwrapped]` and `pid=-1` for wrapped terminals; shell-hook banner always said "unwrapped". All cosmetic-but-misleading; the underlying terminals were real ptys.
572
+
573
+ ### L0 — Push-frame capture (landed)
574
+ - `mcp_server.js` — `_pconnHandleData` buffers push frames (no rid) into a 1000-entry ring buffer instead of silently dropping them; each entry carries `absoluteIndex`, `topic`, `from`, `payload`, `sentAt`, `sequence`
575
+ - `mcp_server.js` — new `claws_drain_events` MCP tool: drains buffered push frames with `since_index` cursor, optional `wait_ms` blocking, and `max` page size; auto-subscribes to `**` on first call so no explicit subscribe is required
576
+ - `mcp_server.js` — `_pconnEnsureRegistered` helper: lazily hellos as `orchestrator / mcp-orchestrator` on the persistent socket (once per process lifetime) so publish/subscribe calls work without a prior `claws_hello`
577
+ - `schemas/mcp-tools.json` — added `claws_drain_events` tool schema
578
+
579
+ ### L1.1 — Worker lifecycle events (landed)
580
+ - `mcp_server.js` — `runBlockingWorker` publishes `system.worker.spawned` (with `terminal_id`, `name`, `wrapped`, `started_at`) immediately after the terminal is created and `system.worker.completed` (with `terminal_id`, `status`, `duration_ms`, `marker_line`, `booted`) after the poll loop exits; guaranteed for both mission-mode and command-mode workers
581
+ - `mcp_server.js` — publishes go via the persistent socket registered as `orchestrator / mcp-orchestrator`; both are best-effort — failure is logged and the worker run continues unaffected
582
+
583
+ ### L1.2 — Lazy .jsonl creation (landed)
584
+ - `extension/src/event-log.ts` — `EventLogWriter.openFreshSegment` defers `fs.openSync` until the first `doAppend` call; the segment file is only created when an event is actually written, eliminating empty `.jsonl` files at activation time
585
+ - `extension/src/event-log.ts` — `doAppend` performs a lazy open when `fdDeferred` is true; open errors set `degraded` and return gracefully
586
+ - `extension/src/event-log.ts` — `append` allows the deferred-open case through (changed guard from `fd === null` to `fd === null && !fdDeferred`)
587
+ - `extension/src/event-log.ts` — `tryRecoverFromManifest` handles missing segment files gracefully — if the file doesn't exist (lazy segment never written), it marks `fdDeferred = true` rather than falling back to a full scan
588
+ - `extension/src/event-log.ts` — `rotate` clears `fdDeferred` before `openFreshSegment` so rotation always starts with a clean deferred state
589
+
590
+ ### L1.2 rotation regression fix (landed)
591
+ - `extension/src/event-log.ts` — `rotate()` now opens the new segment fd eagerly after `openFreshSegment()`; the lazy-open guarantee (no empty `.jsonl` at activation) applied only to the first segment; rotation fires inside `doAppend` so the file is already being written — deferring left `fd=null` which the post-rotate `fd === null` guard treated as degraded mode, returning `sequence=-1` for all subsequent appends; fix: open fd immediately in `rotate()` and clear `fdDeferred`
592
+
593
+ ### L1.4 — Persist task.* + system.malformed.received events (landed)
594
+ - `extension/src/server.ts` — new `emitServerEvent(topic, payload)` private async helper: appends to the event log then fans out, mirroring the `publish` handler's persist-then-fanout contract for server-originated events
595
+ - `extension/src/server.ts` — all 6 server-side `fanOut` call-sites for `task.assigned.*`, `task.status`, `task.completed`, `task.cancel_requested.*`, and `system.malformed.received` replaced with `await this.emitServerEvent(...)` so these events are now durably persisted to `.claws/events/default/*.jsonl`
596
+ - `extension/src/server.ts` — degraded mode: if `eventLog.append` returns sequence -1 the sequence field is omitted from the push frame; on real I/O error the fanOut fires anyway (delivery preserved, persistence skipped)
597
+ - `extension/test/task-event-persist.test.js` — new regression test: boots extension, registers orchestrator + worker, drives assign → update → complete, asserts all 3 entries appear in the .jsonl with monotonically-increasing sequences
598
+
599
+ ### L3 — Reverse channel hardening (landed)
600
+
601
+ #### L3.1 — Monotonic `seq` stamp in `[CLAWS_CMD]` broadcast text
602
+ - `extension/src/server.ts` — added `private broadcastSeq = 0` class field; broadcast handler increments it and rewrites text matching `[CLAWS_CMD ` to `[CLAWS_CMD seq=N ` before `writeInjected` and `pushFrame` calls; free-form broadcast text (no `[CLAWS_CMD` prefix) passes through unchanged; makes re-delivered commands idempotent — workers can track the highest seq seen
603
+ - `extension/test/broadcast-seq.test.js` — 6 regression checks: seq=1/2/3 inserted correctly on three consecutive broadcasts; free-form text unchanged; seq counter only advances for `[CLAWS_CMD` text
604
+
605
+ #### L3.2 — Worker auto-subscribe to `cmd.<peerId>.**` on hello
606
+ - `extension/src/server.ts` — hello handler now auto-registers a `cmd.${peerId}.**` subscription on the peer's socket when `role=worker`; uses the existing subscription-index mechanism so non-Template-8 workers get the reverse channel at the transport layer without an explicit `subscribe` call
607
+ - `extension/test/auto-subscribe-cmd.test.js` — 8 regression checks: worker receives `cmd.<peerId>.approve` push without explicit subscribe; deep wildcard `cmd.<peerId>.sub.nested` also delivered; observer role is NOT auto-subscribed
608
+
609
+ #### L3.1 test fix — `reverse-channel.test.js` updated for seq= prefix
610
+ - `extension/test/reverse-channel.test.js` — two legacy assertions compared injected/pushed text against the original `CMD_TEXT` literal; after L3.1 the server rewrites `[CLAWS_CMD ` to `[CLAWS_CMD seq=N `; switched both assertions to regex `CMD_TEXT_RE = /^\[CLAWS_CMD seq=\d+ r=r1\] approve_request/` — no behavior change, test now correctly validates the seq-stamped output
611
+
612
+ #### L3.4 — Backpressure on `socket.write` in `pushFrame`
613
+ - `extension/src/server.ts` — added `private readonly pausedPeers = new Set<string>()` and `private readonly droppedFrames = new Map<string, number>()`; `pushFrame` checks `socket.write()` return value — if `false`, marks peer as paused, logs `[claws/2] backpressure on push to <peerId>; pausing`, registers a one-shot `drain` listener; while paused, frames are silently dropped with a per-peer counter; drain clears the paused state and logs dropped count (warning if ≥ 100)
614
+ - `extension/test/pushframe-backpressure.test.js` — 9 regression checks: normal push arrives before backpressure; publish after subscriber disconnect returns ok (no crash); new subscriber receives pushes normally after prior peer disconnect; no crash logs; graceful disconnection log
615
+
616
+ ### L2 — Lifecycle REFLECT → PLAN cycle reset (landed)
617
+ - `extension/src/lifecycle-store.ts` — `hasPlan()` now returns `false` when the current phase is `REFLECT`, closing the lifecycle gate after a completed cycle (was: always true once any plan was logged)
618
+ - `extension/src/lifecycle-store.ts` — `plan()` resets the cycle when called from `REFLECT` phase, starting cycle N+1 with fresh `phases_completed=['PLAN']` and the new plan text; idempotency still applies within any active (non-REFLECT) cycle
619
+ - `extension/src/server.ts` — `lifecycle.plan` handler sets `idempotent:false` when the previous phase was `REFLECT` (a cycle reset is not an idempotent no-op); `idempotent:true` only for mid-cycle duplicate calls
620
+ - `extension/test/lifecycle-reset.test.js` — 8 regression checks covering: gate-closes-at-REFLECT, plan-resets-cycle, phases_completed-reset, hasPlan-reopens, SPAWN-advances-after-reset, mid-cycle-idempotency-preserved, reflect-field-cleared
621
+
622
+ ### L1.3 — Periodic system.heartbeat from the extension (landed)
623
+ - `extension/src/server-config.ts` — added `heartbeatIntervalMs` field (default 60 000 ms, 0 = disabled) to `ServerConfig` and `defaultServerConfig`; exported `DEFAULT_HEARTBEAT_INTERVAL_MS`
624
+ - `extension/src/server.ts` — new `private async emitSystemEvent(topic, payload)` helper: appends to the event log then fans out with the returned sequence; skips entirely when `eventLog.isDegraded` is true; errors are swallowed so timer failures never crash the extension
625
+ - `extension/src/server.ts` — `start()` schedules a `setInterval` after `bind()` resolves; reads `heartbeatIntervalMs` from `getConfig()` at schedule time; 0 = no timer created; stores timer in `private heartbeatTimer`
626
+ - `extension/src/server.ts` — `stop()` clears `heartbeatTimer` before closing the event log and socket
627
+ - `extension/src/event-log.ts` — added public `get isDegraded(): boolean` accessor so the server can gate heartbeat emissions without accessing a private field
628
+ - `extension/src/terminal-manager.ts` — added public `get terminalCount(): number` accessor so heartbeat payload can report the live terminal count without exposing the private `records` Map
629
+ - `extension/src/extension.ts` — `getConfig()` now reads `claws.heartbeatIntervalMs` from VS Code settings and passes it to the server
630
+ - `extension/package.json` — added `claws.heartbeatIntervalMs` configuration property (type: number, default: 60000, minimum: 0)
631
+ - `extension/test/heartbeat.test.js` — new regression test: boots extension with `heartbeatIntervalMs=200ms`, waits 700ms, asserts ≥2 `system.heartbeat` entries in the segment file and validates payload shape (uptimeMs, peers, terminals, from, ts_server, sequence)
632
+
633
+ ### L1.5 — [CLAWS_PUB] line scanner for SDK-less worker publishing (landed)
634
+ - `mcp_server.js` — new `_scanAndPublishCLAWSPUB(newText, sockPath)` async helper: scans lines for `[CLAWS_PUB] topic=<topic> key=val ...` markers, parses key=value pairs (quoted strings, bare tokens, numeric, boolean coercion), and calls `_pconnEnsureRegistered` + `_pconnWrite` to publish on the worker's behalf; parse errors and publish failures are logged and never abort the worker run
635
+ - `mcp_server.js` — poll loop (step 6) in `runBlockingWorker` now tracks `pubScanOffset` across iterations; each tick slices `text.slice(scanStart)` (new bytes only) into `_scanAndPublishCLAWSPUB` and advances `pubScanOffset = text.length` so each pty line is scanned at most once
636
+ - Workers using Templates 1–7 can now emit bus events by printing a single line: `[CLAWS_PUB] topic=worker.<id>.phase kind=DEPLOY step=3` — no socket, SDK, peerId, or env-var injection required
637
+ - `extension/test/claws-pub-scanner.test.js` — 12 regression checks: source-level (function defined, MARKER_RE present, called in poll loop, pubScanOffset present, _pconnEnsureRegistered called) + behavioral (3 publishes from 3 markers, duplicate-scan no-re-publish, new-bytes-after-offset published, malformed lines skipped without throw, quoted values, boolean/numeric coercion, non-prefixed lines ignored)
638
+
639
+ ### L4 — Bus correctness (landed)
640
+
641
+ #### L4.1 — `_pconnWrite` id field collision fix
642
+ - `mcp_server.js` — `_pconnWrite` now explicitly destructures and drops any user-supplied `id` before stamping the RPC correlation id (`const { id: _discarded, ...reqBody } = req`). No behaviour change for current callers (none set `id`) but makes the contract auditable and prevents silent misrouting for future stateful commands that use `id` as a routing field.
643
+
644
+ #### L4.2 — Sequence counter persistence across restarts
645
+ - `extension/src/event-log.ts` — `Manifest` interface gains `sequence_counter?: number`
646
+ - `extension/src/event-log.ts` — `writeManifest()` persists `sequence_counter: this.sequenceCounter` (the next value to issue) so the counter survives server restarts
647
+ - `extension/src/event-log.ts` — `tryRecoverFromManifest()` restores the counter with `+1` offset so the last issued sequence before crash is never re-issued; cost is one detectable gap per restart (acceptable)
648
+ - `extension/test/sequence-persist.test.js` — new regression test: writes 5 events, simulates restart with a fresh writer, writes 5 more; asserts second batch is ≥5, monotonically increasing, and spans at most one gap at the restart boundary
649
+
650
+ #### L4.3 — Peer disconnect fails orphaned tasks
651
+ - `extension/src/server.ts` — `handleDisconnect()` now walks `this.tasks` after removing the peer and fails any task whose `assignee === peerId` and `status` is `pending`, `running`, or `blocked`; sets `status='failed'`, `note='assignee disconnected'`, `updatedAt=Date.now()`
652
+ - `extension/src/server.ts` — each newly-failed task emits a `task.completed` event via `emitServerEvent` (best-effort, `.catch()` guards so disconnect never throws) so subscribers see the cancellation
653
+ - `extension/test/peer-disconnect-fails-tasks.test.js` — new regression test: registers orchestrator + worker, assigns 2 tasks, destroys the worker socket, asserts both tasks are `failed` in `task.list` and `task.completed` push frames fired for both
654
+
655
+ #### L4.4 — subscribe fromCursor (structural contract, full replay P1 for v0.7.6)
656
+ - `extension/src/protocol.ts` — `SubscribeRequest` gains optional `fromCursor?: string` field with inline doc describing the cursor format and the v0.7.6 TODO
657
+ - `extension/src/server.ts` — subscribe handler accepts `fromCursor`; logs `[claws/2] fromCursor replay not yet implemented` and continues with live delivery when the field is present; full replay (read event log from cursor, push matching events before live) deferred to v0.7.6 (P1)
658
+
659
+ ### L1.4–L3 (previously landed — see entries above)
660
+ Fleet of layered fixes ordered root-up: L0 capture (push frames captured, `claws_drain_events` MCP tool), L1 production (`system.worker.spawned/completed`, lazy `.jsonl`, heartbeat, task event persistence, `[CLAWS_PUB]` line scanner), L2 lifecycle (REFLECT-reset cycle), L3 reverse-channel hardening (idempotent re-delivery, ACK protocol, backpressure), L4 bus correctness (sequence persistence, peer reconnect, replay).
661
+
662
+ ## [0.7.4] - 2026-04-29 — Bulletproof regression fix release
663
+
664
+ This release closes 50 findings surfaced by a 4-worker parallel audit of the v0.7.2 + v0.7.3 release cycle. After the user reported lifecycle breakage on `/claws-update`, we ran a full Plan→Implement→Review→Audit→Test→Fix→Repeat loop across 5 layers to deliver one bulletproof codebase that absorbs all in-flight unmerged work (γ.1 reverse channel, γ.2 event log core, MCP persistent socket fix) plus 50 regression fixes.
665
+
666
+ ### CRITICAL — confirmed data-loss prevention
667
+ - **M-01** `install.sh` awk strip — anchored to Claws-marked block + timestamped dotfile backup before any modification
668
+ - **M-02** `.mcp.json` silent reset — JSONC-tolerant safe-merge with abort-on-error; never wipes other MCP servers
669
+ - **M-03** `~/.claude/settings.json` silent reset — JSONC-tolerant safe-merge; never wipes user's Claude Code config
670
+ - **M-38** `inject-settings-hooks.js` non-atomic write — atomic write via L0 helpers
671
+ - **M-39** `cli.js` MCP fallback non-atomic write — atomic write via L0 helpers
672
+
673
+ ### HIGH — silent lifecycle breaks
674
+ - **M-04** Hook silent skip → forensic log (`/tmp/claws-hook-misfire.log` + stderr)
675
+ - **M-05** Rosetta arch silent miscompile → auto-correct to arm64 (not x64)
676
+ - **M-06** Stale-extension cleanup race → gate on `[ -d "$kept_dir" ]` before iterating
677
+ - **M-07** `spawnSync` null-status (signal-killed rebuild) → explicit `result.status === null` detection + helpful error
678
+ - **M-08** No rebuild timeout → 5-minute ceiling + SIGTERM detection
679
+ - **M-09** Hooks dir wipe-then-copy non-atomic → atomic `copyDirAtomic` via L0 helper
680
+ - **M-10** Health check 2s timeout → 8s + 3-attempt exponential backoff (8s/12s/16s)
681
+ - **M-11** `mcp_server.js` orphan → SIGKILL escalation 500ms after SIGTERM + socket-unlink verify
682
+ - **M-31** `fix.sh` `@electron/rebuild` no timeout → mirrored from M-08 (recovery path hardening)
683
+ - **M-36** `rebuild-node-pty.sh` no timeout + no TERM_PROGRAM detection → mirrored trifecta
684
+ - **M-44** `fix.sh` stale Content-Length framing → newline-delimited frames (MCP check was always false-failing)
685
+ - **M-45** `fix.sh` `.mcp.json` repair silent-reset-to-`{}` → safe-merge + atomic write + env-var path (recovery path)
686
+
687
+ ### MEDIUM/LOW (M-12 to M-50 not listed above)
688
+ 50 total findings — see `.local/audits/regression-master-issues.md` for the complete catalog.
689
+
690
+ ### Foundation utilities (Layer 0)
691
+ - `scripts/_helpers/json-safe.mjs` — JSONC parse + safe-merge + abort-on-error; used across install/update/fix/inject paths
692
+ - `scripts/_helpers/atomic-file.mjs` — rename-pattern atomic file/dir ops with fsync; used for all config writes
693
+
694
+ ### Test coverage
695
+ - 224 baseline → 501 PASS (+277 regression checks across ~40 new test files)
696
+ - Every M-XX finding has a regression test that exercises its failure mode
697
+
698
+ ### Includes (rolled forward from open PRs)
699
+ - γ.1 reverse channel (was PR #27)
700
+ - γ.2 event log core (was PR #28)
701
+ - MCP persistent socket fix (was PR #29)
702
+
703
+ ---
704
+
705
+ ## [0.7.4-bulletproof-L4-fix] - 2026-04-29 — Layer 4 fix: code-review findings + audit items (F1–F7, M-44–M-50)
706
+
707
+ ### Fixed
708
+
709
+ - **F1** `scripts/update.sh` M-10 retry loop: added `_claws_attempt` counter; emits `note "MCP handshake timeout — retry N of 3 (Nms)..."` between attempts so the operator knows progress during the silent ~38s retry window.
710
+ - **F4** `extension/src/extension.ts` M-41 `runRebuildPty()`: killTimer now sends SIGTERM first, then SIGKILL after 5s grace, matching the recipe pattern from M-11. Previously sent SIGKILL directly. Regression test: `extension-rebuild-pty-timeout.test.js` updated (SIGTERM-before-SIGKILL check added).
711
+ - **F2** `scripts/install.sh`: `inject-global-claude-md.js` now gated on `GIT_PULL_OK` — mirrors the project-level CLAUDE.md gate; avoids rewriting machine-wide policy from stale source when git pull failed. Emits skip note on GIT_PULL_OK=0.
712
+ - **F3** `extension/test/update-step6-orphan.test.sh`: added test 5 — behavioral check that a Unix socket server has no active listener after SIGTERM+SIGKILL sequence (process is gone, no orphan socket-holder).
713
+ - **F6** `extension/src/extension.ts` plutil candidate loop: added JSDoc noting 4 candidates × 3s timeout = 12s worst-case synchronous block; acceptable for explicit user-triggered rebuild command.
714
+ - **F7** `extension/src/event-log.ts` `writeManifest()`: migrated from `writeFileSync` to `openSync+writeSync+fsyncSync+closeSync+renameSync` — mirrors M-29/M-43 fsync-before-rename pattern; manifest survives power-cut or SIGKILL after write.
715
+ - **F5** `scripts/inject-settings-hooks.js`: added `withLock()` helper using `fs.openSync(lockPath, 'wx')` exclusive create with 15-attempt/100ms backoff; all three `mergeIntoFile` call sites (REMOVE, UPDATE, add-mode) wrapped — prevents concurrent `install.sh`+`update.sh` invocations from tearing settings.json. Fixed: removed stale `|| attempt === 2` early-throw that caused EEXIST on retry 2 to propagate (leftover from old 3-attempt loop). Regression test: `inject-settings-exclusive-lock.test.js` (6 checks).
716
+ - **M-44** `scripts/fix.sh` MCP handshake: replaced Content-Length framing (stale LSP protocol) with `mcp.stdin.write(req + '\n')` — matches mcp_server.js's newline-delimited JSON protocol. Added full `protocolVersion`+`clientInfo` to initialize params. Regression test: `fix-mcp-handshake.test.sh` (5 checks).
717
+ - **M-50** `mcp_server.js` `_pconnConnect()`: added `sock.setTimeout(5000)` + `on('timeout', destroy)` — prevents the persistent socket connect phase from hanging forever when VS Code is reloading and the socket is transiently unreachable. `setTimeout(0)` clears the timer once the connection succeeds. Regression test: `mcp-pconn-timeout.test.js` (5 checks).
718
+ - **M-49** `scripts/install.sh` `EXPECTED_MIN_VERSION`: bumped from `0.5.7` → `0.7.4` — was stale, causing stale-clone warnings to fire against fully-up-to-date clones at v0.7.4.
719
+ - **M-47** `scripts/update.sh` `.mcp.json` sanity check: path now passed via `CLAWS_MCP_CHECK` env var instead of string-interpolation into `node -e` — handles project roots with apostrophes/backslashes without JS syntax errors (mirrors M-20 socket-probe fix). Regression test: `update-mcp-path-quoting.test.sh` (5 checks).
720
+ - **M-45+M-46** `scripts/fix.sh` + `scripts/_helpers/fix-repair.js` (new): `.mcp.json` and `.vscode/extensions.json` repair now use `fix-repair.js` which calls `mergeIntoFile` from `json-safe.mjs` — atomic write, abort-on-malformed (never silently resets to `{}`), JSONC-tolerant, path via `CLAWS_REPAIR_TARGET` env var (no injection). Regression test: `fix-mcp-repair.test.sh` (10 checks).
721
+
722
+ ## [0.7.4-bulletproof-L4] - 2026-04-29 — Layer 4: update.sh + extension.ts hardening (M-10, M-11, M-18, M-19, M-20, M-21, M-41, M-42, M-43)
723
+
724
+ ### Fixed
725
+
726
+ - **M-10** `scripts/update.sh` Step 6 health check: bumped timeout from 2000ms to 8s; added 3-attempt retry loop with exponential timeout series (8s, 12s, 16s) so loaded machines don't see false-positive YELLOW on slow startup. YELLOW only declared after all three attempts fail.
727
+ - **M-11** `scripts/update.sh` Step 6 health check: SIGKILL escalation 500ms after SIGTERM — mcp_server.js child is force-killed if SIGTERM is not handled quickly, preventing orphaned socket fd holding the project socket open. mcp_server.js path passed via `CLAWS_MCP_PATH` env var (no embedded path injection). Regression test: `update-step6-orphan.test.sh` (4 checks, includes behavioral SIGTERM-ignore mock).
728
+ - **M-19** `scripts/update.sh`: `CLAWS_LOG` now defined and exported before `install.sh` runs, so Step 6 warning "see install log: $CLAWS_LOG" references the actual log path written by install.sh. install.sh inherits via `${CLAWS_LOG:-...}`. Regression test: `update-claws-log.test.sh` (6 checks).
729
+ - **M-20** `scripts/update.sh` socket probe: project root path passed via `CLAWS_PROBE_PATH` env var instead of string-interpolation into `node -e` — handles project paths containing apostrophes/backslashes without JS syntax errors. Regression test: `update-probe-path-quoting.test.sh` (5 checks, includes behavioral apostrophe + backslash path tests).
730
+ - **M-21** `scripts/update.sh` + `scripts/install.sh`: `GIT_PULL_OK` flag exported on git pull failure; `install.sh` skips `inject-claude-md.js` when `GIT_PULL_OK=0` — avoids rewriting CLAUDE.md tool-set from stale source. Regression test: `update-git-pull-fail.test.sh` (8 checks, behavioral GIT_PULL_OK=0 and GIT_PULL_OK=1 paths).
731
+ - **M-18** `scripts/inject-settings-hooks.js` + `scripts/install.sh`: added `--update` mode that removes old Claws hooks and adds new ones in a single atomic `mergeIntoFile` call. `install.sh` now calls `inject-settings-hooks.js --update` instead of two-pass `--remove` + add, eliminating the kill-window where settings.json has zero Claws hooks. Regression test: `update-atomic-hooks.test.sh` (7 checks, behavioral update preserves non-Claws hooks).
732
+ - **M-41** `extension/src/extension.ts` `runRebuildPty()`: added 5-minute SIGKILL timer (`setTimeout → proc.kill('SIGKILL')`) to prevent hung `@electron/rebuild` invocations from freezing VS Code indefinitely. Timer cleared on normal exit. Regression test: `extension-rebuild-pty-timeout.test.js` (6 checks).
733
+ - **M-42** `extension/src/extension.ts` `execFileSync('plutil', ...)`: added `{ timeout: 3000 }` to prevent synchronous Electron-version detection from blocking the VS Code extension host on network-mounted `/Applications`. Regression test: `extension-plutil-timeout.test.js` (5 checks).
734
+ - **M-43** `extension/src/lifecycle-store.ts` `flushToDisk()`: migrated from `writeFileSync` to `openSync+writeSync+fsyncSync+closeSync+renameSync` pattern — mirrors the M-29 hooks-side fix for parity; ensures lifecycle state survives power-cut or SIGKILL after write but before kernel flush. Regression test: `lifecycle-store-fsync.test.js` (7 checks, behavioral compile+run verification).
735
+
736
+ ## [0.7.4-bulletproof-L3-fix] - 2026-04-29 — Layer 3 fix: code-review findings F1+F2+F3
737
+
738
+ ### Fixed
739
+
740
+ - **F1** `scripts/inject-settings-hooks.js` `isCanonicalInstall()`: now checks both `CLAWS_BIN/hooks/` directory presence AND individual script file existence before emitting bare `node "<path>"`. Previously, a hooks/ dir with missing scripts would produce a `node` invocation that exits non-zero (MODULE_NOT_FOUND), breaking the SAFETY CONTRACT. Falls through to the wrapped `sh -c` misfire-log form instead. [L3.11]
741
+ - **F2** `scripts/inject-settings-hooks.js` M-14 comment: corrected to accurately state that `_source === 'claws'` already prevented non-Claws hooks from being matched before M-14; M-14's actual improvement is replacing substring `command.includes(scriptName)` with exact-command equality, making the "already current" vs "stale, upgrade in-place" distinction unambiguous. [L3.12]
742
+ - **F3** `scripts/inject-settings-hooks.js` `hookCmd()` non-canonical form: misfire message now also written to stderr (`>&2`) alongside `/tmp/claws-hook-misfire.log` (with `2>/dev/null`). When `/tmp` is unwritable, the message still reaches stderr for forensics while `exit 0` preserves the SAFETY CONTRACT. [L3.13]
743
+
744
+ ## [0.7.4-bulletproof-L3] - 2026-04-29 — Layer 3: hooks + settings.json hardening (M-03, M-04, M-12, M-13, M-14, M-15, M-16, M-24, M-38, M-39)
745
+
746
+ ### Fixed
747
+
748
+ - **M-03/M-38** `scripts/inject-settings-hooks.js`: replaced `loadSettings()` try/catch-reset-to-`{}` + `fs.writeFileSync` with async `mergeIntoFile()` from `scripts/_helpers/json-safe.mjs`. On malformed JSON: backup created, original untouched, exits non-zero. Never silently wipes user's entire Claude Code config.
749
+ - **M-39** `cli.js` MCP fallback: replaced `JSON.parse + writeFileSync` with inline ESM `mergeIntoFile()` call via `spawnSync --input-type=module`. Same atomic + JSONC-tolerant + abort-on-malformed guarantees.
750
+ - **M-04** `scripts/inject-settings-hooks.js` `hookCmd()`: missing hook path now appends to `/tmp/claws-hook-misfire.log` with timestamp + path instead of silently exiting 0 with no trace. [L3.2]
751
+ - **M-12** `scripts/inject-settings-hooks.js` `hookCmd()`: replaced `[ -f "$0" ] && exec node "$0" || (...)` with explicit `if [ -f "$0" ]; then exec node "$0"; else ...; fi` — `else` branch is reachable even if `exec` fails for unusual reasons (applies to non-canonical paths; canonical paths use direct node per M-15). [L3.3]
752
+ - **M-13** `scripts/hooks/{session-start,pre-tool-use,stop}-claws.js`: stdin 'data' and 'end' listeners now registered in a single try block; added 5-second `setTimeout(...).unref()` safety timer so hooks can never hang the parent process. [L3.4]
753
+ - **M-14** `scripts/inject-settings-hooks.js` dedup: replaced `command.includes(scriptName)` with exact-command equality + `_source === 'claws'` guard — prevents overwriting non-Claws hooks whose command happens to contain a Claws script name as substring. [L3.5]
754
+ - **M-15** `scripts/inject-settings-hooks.js` `hookCmd()`: when `CLAWS_BIN/hooks/` directory exists (canonical install), registers hooks as direct `node "<path>"` invocations (skips the `sh -c` wrapper) — reduces fork overhead on each hook invocation. [L3.6]
755
+ - **M-16** `scripts/hooks/pre-tool-use-claws.js` STRICT deny: all `process.stdout.write` calls now end with `\n` so Claude Code's hook protocol parser flushes correctly. [L3.7]
756
+ - **M-24** `scripts/hooks/{session-start,pre-tool-use,stop}-claws.js`: `process.on('uncaughtException')` and `process.on('unhandledRejection')` handlers gated on `!process.env.CLAWS_DEBUG` — when `CLAWS_DEBUG=1`, errors propagate visibly for debugging. [L3.8]
757
+
758
+ ## [0.7.4-bulletproof-L2-fix] - 2026-04-29 — Layer 2 fix: code-review findings F1+F5 (error-path + env-var path passing)
759
+
760
+ ### Fixed
761
+
762
+ - **F1** `scripts/install.sh` M-09 + M-02 heredoc blocks: wrapped each `node --input-type=module` heredoc with `set +e` / capture `_exit=$?` / `set -e` so the `die`/`warn` call fires before the shell aborts. Under `set -eo pipefail`, `if [ $? -ne 0 ]` after a heredoc is dead code — the shell terminates at the heredoc line when node exits non-zero.
763
+ - **F5** `scripts/install.sh` M-02 block: switched from shell-expanded string literals (`'${PROJECT_MCP}'`) to `process.env.X` for all user-controlled paths. Also changed static `import ... from '...'` to `await import(process.env.INSTALL_DIR + '...')` to avoid JS SyntaxError when any path component contains a single-quote or backslash.
764
+ - **F5 test** `extension/test/install-mcp-merge.test.sh`: added apostrophe path test (creates a project dir named `user's-project`, runs M-02 merge via env vars, asserts claws entry written).
765
+ - **F1 test** `extension/test/install-error-path.test.sh` (9 checks): static checks that `_hooks_exit` and `_mcp_exit` capture patterns are present; behavioral harness proving the message fires before set-e exit.
766
+ - **F2** `extension/test/install-hooks-atomic.test.sh`: replaced polling simulation (1ms interval checks) with a real SIGKILL mid-copy test. Spawns `copyDirAtomic` in a subprocess, sends SIGKILL after 5ms (during the 100-file step-1 copy phase), then asserts dest has either complete OLD content or complete NEW content — never an empty dir or a partial mix.
767
+ - **F3** `scripts/inject-claude-md.js`, `scripts/inject-global-claude-md.js`, `scripts/hooks/lifecycle-state.js`, `extension/src/uninstall-cleanup.ts`: replaced `writeFileSync(tmp)` with `openSync(tmp, 'w') → writeSync → fsyncSync → closeSync` in all four inline `writeAtomic` helpers. Adds durability for power-cut scenarios where the OS page cache hasn't been flushed — mirrors the `fd.sync()` call that `scripts/_helpers/atomic-file.mjs` (L0) already does in its async variant.
768
+ - **F4** `scripts/install.sh inject_hook`: fixed orphaned-marker edge case. Previous awk `skip { skip=0; next }` stripped the marker AND whatever line followed it, even if the user had manually removed the source line. New pattern: `skip && /source.*shell-hook\.sh/ { skip=0; next }; skip { skip=0; print }` — only strips the following line when it IS the Claws source line; preserves it otherwise. Added F4 orphaned-marker test to `install-awk-anchor.test.sh` (+4 checks, +1 static → 20 total).
769
+ - **F6** `CHANGELOG.md`: added missing `[2c99bda]` commit hash to M-28 entry.
770
+ - **M-40** `extension/scripts/bundle-native.mjs`: replaced `resetNativeDest()` (wipe-then-copy) with `setupStagingDir()` + atomic rename pattern in `copyRuntimeSlice()`. Files now copy into `NATIVE_DEST.claws-new`, then `rename(NATIVE_DEST → .claws-old)` + `rename(staging → NATIVE_DEST)` + cleanup. Kill during file copy leaves old NATIVE_DEST intact; kill after rename leaves new NATIVE_DEST intact — never an empty dir. `extension/test/bundle-native-copy-atomic.test.js` (10 checks): static pattern verification + behavioral atomic-rename simulation + kill-before-rename invariant.
771
+
772
+ ## [0.7.4-bulletproof-L2] - 2026-04-29 — Layer 2: install.sh data-loss + atomicity fixes (M-01, M-02, M-09, M-17, M-27–M-30)
773
+
774
+ ### Fixed
775
+
776
+ - **M-01** `scripts/install.sh inject_hook`: removed generic `/source .../shell-hook\.sh/` awk regex that stripped non-Claws tool hooks (oh-my-zsh, asdf, custom dotfiles). awk now strips ONLY lines inside a `# CLAWS terminal hook` marked block. Added timestamped dotfile backup (`$rcfile.claws-bak.<ISO-ts>`) before any modification. [ac1661a]
777
+ - **M-02** `scripts/install.sh` `.mcp.json` merge: replaced `try{}catch{}` reset-to-`{}` pattern with `mergeIntoFile()` from `scripts/_helpers/json-safe.mjs`. On parse failure: backup created, original untouched, install.sh exits non-zero with actionable message. Never silently wipes other MCP servers. [cbb447e]
778
+ - **M-09** `scripts/install.sh` `.claws-bin/hooks/` copy: replaced `rm -rf + cp` with atomic rename pattern via `copyDirAtomic()` from `scripts/_helpers/atomic-file.mjs`. Kill-window now leaves either full old hooks or full new hooks — never an empty dir. [df0b224]
779
+ - **M-17** `scripts/install.sh inject_hook`: fixed awk empty-file edge case. [aa488da] When `.zshrc` contains ONLY the Claws block, awk output is empty; the old `[ -s "$tmp" ]` guard prevented promotion, leaving original intact and causing duplicate blocks on next install. Now always promotes awk output when awk succeeds.
780
+ - **M-27** `scripts/inject-claude-md.js`: replaced `fs.writeFileSync` with atomic write pattern (tmp + rename) — prevents partial project `CLAUDE.md` on kill mid-write. [2c99bda]
781
+ - **M-28** `scripts/inject-global-claude-md.js`: same atomic write for `~/.claude/CLAUDE.md` — machine-wide config corruption on power-cut prevented. [2c99bda]
782
+ - **M-29** `scripts/hooks/lifecycle-state.js writeState()`: replaced `fs.writeFileSync` with atomic tmp+rename pattern — mirrors `extension/src/lifecycle-store.ts` which was already atomic. Prevents partial lifecycle-state.json on hook kill. [9696ecb]
783
+ - **M-30** `extension/src/uninstall-cleanup.ts`: replaced both `writeFileSync` calls (`.mcp.json` edit-json + CLAUDE.md edit-markdown) with inline atomic write pattern — partially-uninstalled state no longer possible on kill mid-write.
784
+
785
+ ## [0.7.4-bulletproof-L1-fix] - 2026-04-29 — Layer 1 fix: code-review + similar-bug findings (F1+F2, F3+F4, M-31–M-37)
786
+
787
+ ### Changed
788
+
789
+ - **F1** `bundle-native.mjs detectElectronVersion()` darwin sort: removed redundant `(tp === 'vscode' && c.key === 'vscode')` sub-expression — semantically identical to `c.key === tp` when `tp==='vscode'`. Now matches the simpler Linux branch form.
790
+ - **F2** `install.sh` ABI detection darwin block: replaced `eval "set -- $_claws_darwin_apps"` with bash array (`declare`-compatible `case` + `for` loop) — eliminates eval footgun that would become shell injection if `$_tp` were ever interpolated into the string literal.
791
+ - **F3** `extension/test/update-socket-probe.test.js`: replaced regular-file fixture with a real Unix domain socket (server binds, stays open but never responds, probe times out). Faithfully replicates an unresponsive Claws server; satisfies `[ -S ]` check. Server closed in `finally` block post-assertion.
792
+ - **F4** `bundle-native.mjs detectElectronVersion()`: added `cursorChannel` (`$CURSOR_CHANNEL`) secondary signal — when `TERM_PROGRAM=vscode` but `CURSOR_CHANNEL` is set (Cursor-specific env), promotes Cursor candidates over VS Code. Covers old Cursor builds that pre-date `TERM_PROGRAM=cursor`. Injected as parameter for testability; test 9 added.
793
+ - **M-31** `scripts/fix.sh` `@electron/rebuild` block: wrapped with `timeout 300` / `gtimeout 300` (5-minute ceiling). Exit code 124 → user-actionable "slow Electron headers download" message. Prevents indefinite hang on captive portals.
794
+ - **M-36** `scripts/rebuild-node-pty.sh` rebuild step: same timeout pattern as M-31. Exits 1 on timeout with network/proxy hint.
795
+ - **M-32** `scripts/fix.sh` ABI detection: TERM_PROGRAM-aware darwin loop (bash array, same F2 pattern); CURSOR_CHANNEL secondary signal for old Cursor builds.
796
+ - **M-33** `scripts/fix.sh` ABI detection: Linux Cursor (`/usr/share/cursor/electron`, `/opt/cursor/electron`) + Windsurf paths added; TERM_PROGRAM-ordered.
797
+ - **M-36 (editor detect)** `scripts/rebuild-node-pty.sh` detection: TERM_PROGRAM-aware darwin ordering + CURSOR_CHANNEL + Linux Cursor/Windsurf paths.
798
+ - **M-34** `scripts/install.sh` arch verify: when bash runs under Rosetta 2 (`uname -m=x86_64` on Apple Silicon), `sysctl.proc_translated` is checked and the expected arch is promoted to `arm64`. Prevents false "pty.node arch mismatch" warning after M-05 build.
799
+ - **M-35** `scripts/update.sh` Step 6 ABI check: TERM_PROGRAM-aware darwin ordering for editor detection (cursor/windsurf/default). CURSOR_CHANNEL secondary signal included.
800
+ - **M-37** `claws-sdk.js ClawsSDK.connect()`: `sock.setTimeout(5000)` — when the socket file exists but the server doesn't respond within 5s, `sock.destroy(err)` fires with a `/claws-fix` hint. `sock.setTimeout(0)` on connect prevents false fires during normal use.
801
+ - **M-37** `claws-sdk.js ClawsSDK._send()`: per-request `timeoutMs` (default 10s) via `setTimeout`/`clearTimeout` — rejects and cleans up the `_pending` Map entry if no response arrives. Prevents unbounded Map growth when the extension is reloading.
802
+
803
+ ## [0.7.4-bulletproof-L1] - 2026-04-29 — Layer 1: ABI/native-bundle fixes (M-05, M-06, M-07, M-08, M-22, M-23, M-25, M-26)
804
+
805
+ ### Fixed
806
+
807
+ - **M-05** `bundle-native.mjs detectTargetArch()`: Rosetta 2 detection now returns `'arm64'` instead of warning-only. Prevents x64 pty.node being shipped for arm64 VS Code/Cursor.
808
+ - **M-07** `bundle-native.mjs runElectronRebuild()`: explicit `result.status === null` check catches signal-killed rebuilds that previously silently passed. `spawnFn`/`failFn` injectable for testability.
809
+ - **M-08** `bundle-native.mjs runElectronRebuild()`: 5-minute `spawnSync` timeout (`timeout: 5*60*1000`) prevents indefinite hang on slow Electron headers fetch; SIGTERM → network/proxy hint message.
810
+ - **M-22** Editor detection prefers `$TERM_PROGRAM` env (vscode|cursor|windsurf) so the current-shell's editor wins over hardcoded path order. Applied in both `bundle-native.mjs` and `install.sh` ABI drift block.
811
+ - **M-23** When Electron version detection returns empty, emits explicit warning recommending `CLAWS_ELECTRON_VERSION` env override.
812
+ - **M-25** Linux Cursor/Windsurf install paths added to ABI detection candidates (`/usr/share/cursor/electron`, `/opt/cursor/electron`, `/usr/share/windsurf/electron`, `/opt/windsurf/electron`).
813
+ - **M-26** `update.sh` socket probe is now health-check only — never deletes socket on failed probe (races with VS Code hot-reload); defers destructive cleanup to user-explicit `/claws-fix` with actionable hint.
814
+ - **M-06** `install.sh` stale-extension cleanup loop gated on `[ -d "$kept_dir" ]`; skips with warning if just-installed directory has not yet extracted (VS Code async VSIX extraction), preventing total extension loss race.
815
+
816
+ ## [0.7.4-bulletproof] - 2026-04-29 — Layer 0: shared helpers (M-02, M-03, M-01, M-09 foundation)
817
+
818
+ ### Added
819
+
820
+ - `scripts/_helpers/json-safe.mjs` — JSONC-tolerant parse + `mergeIntoFile` that aborts on parse error (never silently resets to `{}`). Supports `//` line comments, `/* block comments */`, and trailing commas. Foundation for M-02 (`.mcp.json` wipe) and M-03 (`settings.json` wipe) fixes.
821
+ - `scripts/_helpers/atomic-file.mjs` — rename-pattern atomic write/dir-copy + `backupFile`. Foundation for M-01 (dotfile backup) and M-09 (hooks copy atomicity) fixes. Per-call nonce on tmp filenames ensures correctness under concurrent invocations. `@throws {Error}` documented for ENOENT on `backupFile` (F5).
822
+ - `json-safe.mjs` review fixes: `/* block comments */` stripped (F3), pid+nonce tmp suffix (F1), fsync before rename (F2).
823
+ - `atomic-file.test.js` test 3 strengthened: asserts all 10 concurrent writes succeed with `Promise.allSettled` + exact content match (F4).
824
+
825
+ ## [0.7.4] - 2026-04-28 — Phase γ (reverse channel + event log) + MCP socket fix
826
+
827
+ This release integrates Phase γ (γ.1 reverse channel + γ.2 persistent event log)
828
+ and fixes a high-severity architectural bug in `mcp_server.js` that made all
829
+ stateful claws/2 MCP flows fail silently.
830
+
831
+ ### Fixed (CRITICAL — issue 09)
832
+
833
+ - **`mcp_server.js` now maintains a single persistent socket** for stateful
834
+ claws/2 commands (`claws_hello`, `claws_subscribe`, `claws_publish`,
835
+ `claws_broadcast`, `claws_peers`). Previously each MCP tool call opened a
836
+ fresh socket, sent one frame, and destroyed it. The claws/2 protocol binds
837
+ peer state to a single connection; hello on socket A registered a peer and
838
+ closed; publish on socket B had no peer and returned "call hello first".
839
+ Stateless claws/1 commands (`list`, `create`, `send`, `close`, `readLog`,
840
+ `poll`, `exec`, `lifecycle.*`) continue to use per-call sockets.
841
+ Fix: module-level `_pconn` object with `_pconnEnsure()` / `_pconnWrite()` /
842
+ `clawsRpcStateful()`. Auto-reconnects on socket close; re-issues hello if a
843
+ prior identity was cached.
844
+
845
+ ### Added (Phase γ.1 — reverse channel, integrated from branch)
846
+
847
+ - **`[CLAWS_CMD]` reverse channel**: orchestrator can broadcast a command token
848
+ into every worker's terminal via `{ inject: true }` on `claws_broadcast`.
849
+ Extension writes the text directly into the pty using `writeInjected()` with
850
+ bracketed paste. Worker skill (`/claws-streaming-worker`) scans its log for
851
+ the `[CLAWS_CMD]` prefix and routes to a named handler. Slash command
852
+ `/claws-broadcast` exposes the pattern. Integration test: `reverse-channel.test.js`
853
+ (12 checks). Commits: `80893ab`, `36bfece`, `d9c883a`, `4c434f9`.
854
+
855
+ ### Added (Phase γ.2 — persistent event log, integrated from branch)
856
+
857
+ - **Append-only event log** (`EventLogWriter` in `extension/src/event-log.ts`).
858
+ Every `publish` call is durably written to `.claws/events/default/*.jsonl`
859
+ before fan-out. Segment rotation on size (10 MB) and age (1 hour). Atomic
860
+ manifest updates (`manifest.json`) for crash recovery. Sequence counter
861
+ monotonically increasing across segment boundaries. 15-check test suite:
862
+ `event-log.test.js`. Commits: `37acac1`, `0150572`, `f16f399`.
863
+
864
+ ### Tests
865
+
866
+ - New: `extension/test/mcp-publish-flow.test.js` — spawns `mcp_server.js` as
867
+ a child process, calls `claws_hello` + `claws_publish` via MCP JSON-RPC,
868
+ asserts ok:true and event record on disk. Guards issue 09 regression.
869
+ Added `test:mcp-publish-flow` script and wired into `npm test`.
870
+ - Suite total: 224 checks across 21 suites (was 219 across 20).
871
+
872
+ ### Version markers
873
+
874
+ - `extension/package.json` → 0.7.4
875
+ - `package.json` (root CLI) → 0.7.4
876
+ - `mcp_server.js` serverInfo → 0.7.4
877
+ - `claws-sdk.js` VERSION → 0.7.4
878
+
879
+ ## [0.7.3] - 2026-04-28 — Bulletproof `/claws-update`
880
+
881
+ User-reported breakage on a real upgrade: `/claws-update` ran cleanly but
882
+ left MCP unable to connect. VS Code reload didn't fix it; running the
883
+ installer again in the same project didn't fix it. Root cause was an
884
+ Electron-ABI rebuild gap that none of the existing checks covered, plus
885
+ a blunt socket-cleanup that destroyed live state.
886
+
887
+ This release reworks `update.sh`, `install.sh`, and `fix.sh` so a future
888
+ `/claws-update` can never produce the same broken state. See
889
+ `.local/audits/update-sh-deep-audit.md` for the full bug catalog.
890
+
891
+ ### Fixed (CRITICAL — caused the user-reported breakage)
892
+
893
+ - **Electron-ABI mismatch is now auto-detected** (`scripts/install.sh`).
894
+ Previously, `needs_rebuild_native` only triggered if the binary was
895
+ missing, the user passed `CLAWS_FORCE_REBUILD_NPTY=1`, or the git SHA
896
+ changed. If the user updated VS Code to a newer Electron version while
897
+ Claws was already installed, install.sh saw the binary present, the
898
+ SHA unchanged, and **skipped the rebuild** — the bundled `pty.node`
899
+ was now ABI-mismatched, the extension silently fell into pipe-mode,
900
+ and wrapped terminals (the entire MCP-driven workflow) broke. Fix:
901
+ read `electronVersion` from `extension/native/.metadata.json`,
902
+ compare against the currently-installed editor's Electron version,
903
+ force a rebuild on mismatch. macOS via `plutil` on Electron Framework
904
+ Info.plist; Linux via `electron --version` from common install paths.
905
+ Audit finding #1.
906
+
907
+ - **`/claws-fix` now propagates the rebuilt `pty.node` to every installed
908
+ extension dir** (`scripts/fix.sh`). Previously, fix.sh rebuilt
909
+ node-pty in `~/.claws-src/extension/node_modules/node-pty/` but VS Code
910
+ loads the extension from `~/.vscode/extensions/neunaha.claws-X.Y.Z/native/`
911
+ — a different copy. So the rebuild had no visible effect after reload.
912
+ Fix: copy the freshly-built pty.node into the source's `native/`
913
+ bundle AND into every `~/.{vscode,vscode-insiders,cursor,windsurf}/extensions/neunaha.claws-*/native/...`
914
+ directory, then update `native/.metadata.json` so future ABI checks
915
+ see the new version. Audit finding #3.
916
+
917
+ ### Fixed (HIGH)
918
+
919
+ - **Safe socket cleanup in `update.sh`** (`scripts/update.sh:86`).
920
+ Previously: `find -name claws.sock -mtime +1 -delete`. If the user
921
+ kept VS Code open for >24 hours, the live socket file's mtime was
922
+ stale and `update.sh` deleted it. The running extension still held
923
+ the socket fd internally, but the path was gone — every subsequent
924
+ MCP child process got `ENOENT`. Replaced with a Node-based connect
925
+ probe: only delete the socket if it fails a 800ms `list` ping.
926
+ Live sockets are preserved. Audit finding #2.
927
+
928
+ - **Visible `git pull` failure in `update.sh`**. The previous version
929
+ treated "no changes" and "git pull failed" identically (same dim
930
+ `note()` output). On network errors / dirty source / merge conflicts,
931
+ the user proceeded silently with stale source. Now: distinguishes
932
+ the two cases, prints the actual git error, and warns when running
933
+ install.sh against a stale local clone. Audit finding #5.
934
+
935
+ ### Added
936
+
937
+ - **Post-update health check in `update.sh`** (Step 6). After install.sh
938
+ returns, verifies pty.node ABI parity, `.mcp.json` JSON validity, and
939
+ MCP server `initialize` handshake. If any check fails, prints a yellow
940
+ WARNING banner with concrete recovery steps before the success line.
941
+ Stops the user from being surprised an hour later. Audit finding #4.
942
+
943
+ ### Fixed (CRITICAL — "solve hook errors forever" — three-layer fix)
944
+
945
+ User-reported recurring class of failure: `SessionStart:startup hook
946
+ error / Failed with non-blocking status code: file:///.../hooks/X.js:14`
947
+ on every Bash tool call. Three distinct trigger paths, each closed:
948
+
949
+ - **Layer 1 — Hook scripts can never crash.** All three hook scripts
950
+ (`session-start-claws.js`, `pre-tool-use-claws.js`, `stop-claws.js`)
951
+ now have `process.on('uncaughtException')` + `unhandledRejection`
952
+ handlers registered as the first executable lines, plus full
953
+ try/catch wrapping around the body, plus lazy-require for any
954
+ cross-script deps (`stop-claws.js`'s `lifecycle-state` module). Any
955
+ internal error → silent `process.exit(0)`. Garbage stdin, missing
956
+ deps, ESM-loader confusion — all become no-ops instead of visible
957
+ errors. Verified: `printf garbage | node hook.js` → exit 0 for all
958
+ three hooks.
959
+
960
+ - **Layer 2 — Missing hook paths silent-skip instead of erroring.**
961
+ `inject-settings-hooks.js` now registers each hook command as
962
+ `sh -c '[ -f "$0" ] && exec node "$0" || exit 0' "<scriptPath>"`
963
+ instead of plain `node "<scriptPath>"`. If the path 404s (install
964
+ dir moved, sandbox path leaked, prior install removed), the
965
+ shell sees no file and exits 0 silently. Claude Code never
966
+ surfaces the error. Path-existence is checked at every tool call,
967
+ zero perf cost (sh + test). The injector's `alreadyPresent`
968
+ detection now also recognises and replaces old plain-format
969
+ entries on upgrade — no duplicate accumulation.
970
+
971
+ - **Layer 3 — `/claws-fix` auto-heals stale hook registrations.**
972
+ Two new checks added to `scripts/fix.sh`:
973
+ - "Hook script paths in `~/.claude/settings.json`" — extracts the
974
+ `.js` path from each Claws hook command (whether wrapped or
975
+ plain), tests `fs.existsSync`, lists any 404s, and re-runs
976
+ `inject-settings-hooks.js --remove + add` to re-register from
977
+ the current install dir. Self-healing.
978
+ - "Hook scripts execute cleanly" — invokes each registered hook
979
+ with synthetic stdin under a Node-based 5s timeout (replaces
980
+ macOS-incompatible `timeout` cmd), and reports any non-zero
981
+ exit. Surfaces pre-v0.7.3 hook scripts that don't have the
982
+ safety wrappers.
983
+
984
+ Together: any recurrence of the hook-error class is auto-detected
985
+ and auto-repaired by the next `/claws-fix` run.
986
+
987
+ This closes a recurring failure class that has bitten users since
988
+ the Claws hook chain shipped in v0.6.x.
989
+
990
+ ## [0.7.2] - 2026-04-28 — Audit-driven hardening
991
+
992
+ User-reported regression on a real ESM project (`/Users/miles/dev/tokenomic/`)
993
+ plus the consolidated findings of the four-worker codebase audit
994
+ (`.local/audits/audit-{1,2,3,4}-*.md`). Net effect of this release:
995
+
996
+ - Hooks no longer crash in modern Node/TypeScript projects whose root
997
+ `package.json` declares `"type": "module"`.
998
+ - Stale shell-rc source lines from prior installs are reliably cleaned even
999
+ on macOS Monterey (BSD sed pre-Ventura) and on every Linux.
1000
+ - Files removed from `scripts/hooks/` between releases no longer survive in
1001
+ users' `.claws-bin/hooks/`.
1002
+ - Linux x86_64 installs no longer print a false-positive arch warning every
1003
+ time. Zsh-only syntax in `~/.zshrc` no longer triggers a misleading
1004
+ "syntax error" warning.
1005
+ - The `/claws-do` and `/claws-worker` slash commands now route one-shot shell
1006
+ commands through `claws_exec` and reserve wrapped terminals for hosting
1007
+ Claude Code workers. Closes the user-reported bug where wrapped terminals
1008
+ ran shell commands instead of booting a Claude Code instance.
1009
+
1010
+ ### Fixed (HIGH)
1011
+
1012
+ - **ESM-project hook crash** (`scripts/hooks/package.json` new + install.sh).
1013
+ In projects whose root `package.json` declares `"type":"module"`, Node
1014
+ walked up from the hook script and inherited the ESM type, so the
1015
+ CommonJS `require('fs')` at the top of every Claws hook crashed at line 14.
1016
+ The user (Miles) saw `Failed with non-blocking status code: file:///…/.claws-bin/hooks/pre-tool-use-claws.js:14` once per Bash tool call. Fix:
1017
+ ship a `package.json` shim (`{"type":"commonjs"}`) alongside the hook
1018
+ scripts so Node loads them as CJS regardless of the surrounding project's
1019
+ ESM type. Audit 4 surfaced this as a follow-up gap (no audit covered ESM
1020
+ projects directly — added to the matrix going forward).
1021
+
1022
+ - **Stale `.claws-bin/hooks/` overlay** (`scripts/install.sh`). The hooks
1023
+ copy step used to overlay new files on top of the existing directory
1024
+ without first wiping it. Files removed in a newer release (e.g.
1025
+ `post-tool-use-claws.js`, deleted in v0.6.5) survived indefinitely in
1026
+ every existing project's `.claws-bin/hooks/`, and `~/.claude/settings.json`
1027
+ still referenced them. Fix: `rm -rf "$PROJECT_ROOT/.claws-bin/hooks"`
1028
+ before the copy. Audit 4 finding I, audit 2 findings A/B.
1029
+
1030
+ - **Multi-project hook displacement** (`scripts/install.sh`,
1031
+ `scripts/inject-settings-hooks.js`). Hook registration in
1032
+ `~/.claude/settings.json` pointed at `$PROJECT_ROOT/.claws-bin/hooks/`,
1033
+ so each new project install silently displaced every prior project's hook
1034
+ registration — last install won globally. Deleting a project also orphaned
1035
+ its hook commands in settings.json, leaving broken entries that fired on
1036
+ every Bash call. Fix: pass `$INSTALL_DIR/scripts` to the hook injector so
1037
+ `hookCmd` resolves to `$INSTALL_DIR/scripts/hooks/<script>.js` — the
1038
+ committed source-of-truth. One registration now serves all projects;
1039
+ `/claws-update` from any project refreshes it; project deletion never
1040
+ orphans it. Audit 3 finding A.
1041
+
1042
+ - **BSD sed self-heal regression** (`scripts/install.sh:inject_hook`). The
1043
+ `# CLAWS terminal hook` cleanup used `sed '/pat/,+1d'` — the `,+N` range
1044
+ is a GNU sed extension. macOS ≤ Monterey ships a BSD sed that silently
1045
+ treats `+1` as the literal line number 1, so only the marker line was
1046
+ deleted and the `source ".../shell-hook.sh"` line on the next line
1047
+ survived. Subsequent installs no longer matched the marker (already
1048
+ gone) and the orphan source line stayed forever. Fix: replaced sed with
1049
+ a portable awk pass that also nukes any standalone orphaned
1050
+ `source .../shell-hook.sh` line — heals existing damage on the next
1051
+ install. Audit 4 finding G — the single highest-leverage item in that
1052
+ audit.
1053
+
1054
+ ### Fixed (MEDIUM)
1055
+
1056
+ - **Linux x86_64 false-positive arch warning** (`scripts/install.sh:431`).
1057
+ `uname -m` returns `x86_64` (underscore) but `file(1)` reports the binary
1058
+ as `x86-64` (hyphen). Every legitimate Linux x86_64 install used to print
1059
+ `pty.node architecture may not match current machine (x86_64)`. Fix:
1060
+ match both spellings via `uname -m | sed 's/_/-/g'`. Audit 1 finding H-1.
1061
+
1062
+ - **Misleading "zshrc syntax error" warning** (`scripts/install.sh:1068`).
1063
+ `bash -n ~/.zshrc` flagged any zsh-specific construct (`setopt`,
1064
+ `autoload -Uz`, `zstyle`, …) as a syntax error after every install. Fix:
1065
+ prefer `zsh -n` when zsh is installed (it almost always is when
1066
+ `~/.zshrc` exists). Audit 1 finding H-2.
1067
+
1068
+ ### Changed (slash command docs — no code change)
1069
+
1070
+ - **`/claws-do` and `/claws-worker`** now require classifying the request
1071
+ before creating a terminal:
1072
+ - One-shot shell command (`npm test`, `pytest`, `cargo build`) → use
1073
+ `claws_exec`. No terminal. No cleanup.
1074
+ - Mission-shaped task (refactor, fix bug, multi-step) → 7-step Claude
1075
+ Code boot sequence + `MISSION_COMPLETE` marker.
1076
+ Closes the user-reported regression where wrapped terminals were hosting
1077
+ bare shell commands instead of Claude Code instances. The new
1078
+ `/claws-do` doc explicitly forbids the old "send shell command into
1079
+ wrapped terminal" pattern.
1080
+
1081
+ ### Audit findings deferred to v0.7.3+
1082
+
1083
+ - Stale VS Code extension dirs after symlink fallback (audit 4 K gap 1) —
1084
+ rare, low-impact.
1085
+ - Lifecycle-state `v` field validation (audit 4 J) — future hardening.
1086
+ - Offline / corporate-firewall bypass path (audit 4 A) — needs `--no-network`
1087
+ flag and `CLAWS_DIR` semantics review; out of scope for a hotfix.
1088
+
1089
+ ## [0.7.1] - 2026-04-28 — Fresh-install fix
1090
+
1091
+ ### Fixed (CRITICAL — fresh installs were silently broken)
1092
+
1093
+ `scripts/install.sh` was producing a partially-working system on every fresh
1094
+ project install since v0.6.5+ shipped the lifecycle hook chain. Issue 11 in
1095
+ `.local/issues/` documents the four layered bugs surfaced by an end-to-end
1096
+ install test against a clean `/tmp/claws-fresh-install-test/`.
1097
+
1098
+ - **Hook source path was wrong.** `install.sh` copied lifecycle hooks from
1099
+ `$INSTALL_DIR/.claws-bin/hooks/` — a path that is gitignored and therefore
1100
+ missing on every fresh `git clone`. The committed source-of-truth is
1101
+ `$INSTALL_DIR/scripts/hooks/`. Fixed: copy from the right source.
1102
+ Result: `<project>/.claws-bin/hooks/` is now populated as designed.
1103
+ - **`inject-settings-hooks.js` got the wrong directory.** It was invoked
1104
+ with `$INSTALL_DIR/.claws-bin` (source-clone path), so registered hook
1105
+ commands in `~/.claude/settings.json` pointed at non-existent files. Fixed:
1106
+ pass `$PROJECT_ROOT/.claws-bin` so registered paths are project-local and
1107
+ match the deployed copies. Lifecycle hooks (PreToolUse, SessionStart, Stop)
1108
+ now resolve correctly on every fresh install.
1109
+ - **`schemas/` deployment was incomplete.** Only `schemas/mcp-tools.json`
1110
+ was being copied. The 20 `schemas/json/*.json` and the
1111
+ `schemas/types/event-protocol.d.ts` were NOT. Fixed: copy the whole
1112
+ `schemas/` tree (`mcp-tools.json` + `json/` + `types/`). External schema
1113
+ consumers (worker SDKs, validators, IDE hints) now find the artifacts.
1114
+ - **Verifier flagged missing hooks dir as a warning, not a failure.** With
1115
+ the source-path bug fixed, the `.claws-bin/hooks/` directory should always
1116
+ be present after install — so its absence is now a hard `_miss`, not a soft
1117
+ `warn`. Catches regressions immediately rather than letting them slip past
1118
+ the install banner.
1119
+
1120
+ ### Added
1121
+
1122
+ - `CLAWS_NO_GLOBAL_HOOKS=1` env var. When set, `install.sh` skips registering
1123
+ Claws hooks in `~/.claude/settings.json`. Useful for testing, CI, and
1124
+ sandboxed installs where the user's global Claude Code config should not
1125
+ be touched. Surfaced as a need during the issue-11 reproduction (running
1126
+ install.sh against a temp dir for testing was clobbering the dev
1127
+ environment's hook registration).
1128
+
1129
+ ## [0.7.0] - 2026-04-28 — Phase β: streaming foundation
1130
+
1131
+ ### Fixed (post-deploy)
1132
+
1133
+ - **`extension/scripts/deploy-dev.mjs`** — also copies `extension/package.json`
1134
+ into each `~/.vscode/extensions/<publisher>.<name>-*/` directory. VS Code
1135
+ reads the version label from the installed dir's `package.json`, so without
1136
+ this copy the Extensions panel keeps showing the pre-deploy version even
1137
+ after the bundle has been updated. Surfaced during the v0.7.0 integration
1138
+ test (issue 10 in `.local/issues/`).
1139
+
1140
+ ### Fixed (post-review)
1141
+
1142
+ Three issues were found in the Phase β code review and addressed in this
1143
+ release before merge:
1144
+
1145
+ - **BLOCKING-1** — `claws-sdk.js` `hello()` was overwriting `CLAWS_PEER_ID`
1146
+ with the server-assigned connection peer id, so SDK publishes were
1147
+ routing to `worker.<server-id>.*` instead of the documented
1148
+ `worker.<CLAWS_PEER_ID>.*`. The constructor now captures `CLAWS_PEER_ID`
1149
+ into an immutable `_topicPeerId` field that all publish methods use for
1150
+ topic construction; `hello()` never overwrites it.
1151
+ - **BLOCKING-2** — `publishBoot`/`publishPhase`/`publishHeartbeat` (and
1152
+ others) were constructing payloads whose field names did not match the
1153
+ corresponding Zod schemas (`reason` vs `transition_reason`, `phase` vs
1154
+ `current_phase`, missing `model`/`parent_peer_id`/`cwd`/`terminal_id`,
1155
+ etc.). Every SDK publish was triggering `system.malformed.received` even
1156
+ in normal operation. All payload field names now match the schemas
1157
+ exactly. Schema names also switched from PascalCase (`WorkerBootV1`) to
1158
+ kebab-case (`worker-boot-v1`) to match the `SCHEMA_BY_NAME` convention.
1159
+ - **MAJOR-1** — `extension/package.json` `build` and `compile` scripts now
1160
+ prepend `npm run schemas` so committed artifacts under `schemas/` cannot
1161
+ silently fall stale relative to `event-schemas.ts`.
1162
+
1163
+ ### Added
1164
+
1165
+ **Schemas-as-code (Zod → committed JSON, TypeScript, docs)**
1166
+ - `extension/src/event-schemas.ts`: Zod v3 schema definitions as the single
1167
+ source of truth for all 19 event types — `EnvelopeV1`, 5 worker schemas,
1168
+ 8 cmd schemas, 6 system schemas, enums, and `SCHEMA_BY_NAME` lookup
1169
+ - `extension/src/topic-registry.ts`: `TOPIC_REGISTRY` (19 entries) and
1170
+ `schemaForTopic()` lookup; `topic-utils.ts` extracted to avoid circular deps
1171
+ - `npm run schemas` codegen pipeline: bundles TS via esbuild, then generates
1172
+ `schemas/json/` (20 JSON Schema files), `schemas/types/event-protocol.d.ts`,
1173
+ `docs/event-protocol.md` topic table, and `schemas/mcp-tools.json`
1174
+ - All generated files committed — no runtime build step required
1175
+
1176
+ **Server-side publish validation (soft-reject mode by default)**
1177
+ - `server.ts` publish handler validates `EnvelopeV1` and per-topic data
1178
+ schema before fan-out using the Zod schemas
1179
+ - Soft-reject (default): on failure, emits `system.malformed.received` with
1180
+ `{ from, topic, error: ZodIssues }`, then still fans the event out
1181
+ - Strict mode (`claws.strictEventValidation=true`): hard-rejects with
1182
+ `{ ok:false, error:'envelope:invalid'|'payload:invalid', details }`;
1183
+ no fan-out occurs
1184
+ - Migration note: soft-reject is the default in v0.7.0; flips to strict in
1185
+ v0.8.0 to give existing callers one release cycle to adopt the envelope
1186
+
1187
+ **MCP tool descriptors generated from Zod schemas**
1188
+ - All 18 MCP tools defined as Zod schemas in `scripts/codegen/gen-mcp-tools.mjs`
1189
+ - `schemas/mcp-tools.json` committed and consumed by `mcp_server.js` at startup
1190
+ - `tools/list` response is byte-identical to the previous hand-written array
1191
+ - `mcp_server.js` startup guard exits clearly if the file is absent
1192
+
1193
+ **Claws SDK — zero-dep typed publish helpers**
1194
+ - `claws-sdk.js` (repo root, copied to `.claws-bin/` by installer): dual CLI
1195
+ + module API for workers to publish typed `EnvelopeV1` frames
1196
+ - CLI verbs: `publish boot|phase|event|heartbeat|complete`
1197
+ - Module: `ClawsSDK` class with `connect()`, `hello()`, `publishBoot()`,
1198
+ `publishPhase()`, `publishEvent()`, `publishHeartbeat()`, `publishComplete()`
1199
+ - Socket auto-discovery (walks up from `cwd`); reads env `CLAWS_PEER_ID`,
1200
+ `CLAWS_PEER_NAME`, `CLAWS_TERMINAL_ID`
1201
+ - Migration note: SDK is opt-in for Phase β — legacy `claws_publish` with raw
1202
+ payloads continues to work in soft-reject mode
1203
+
1204
+ **Streaming Worker orchestration pattern**
1205
+ - Template 8 in `.claude/skills/prompt-templates/SKILL.md`: full streaming
1206
+ worker mission boilerplate with checkpoint publish table and orchestrator
1207
+ setup guide
1208
+ - `.claude/skills/claws-orchestration-engine/SKILL.md` Phase 4 OBSERVE
1209
+ refactored to event-driven sidecar pattern with heartbeat-based stuck
1210
+ detection; legacy `claws_read_log` polling documented as fallback
1211
+ - New slash command `.claude/commands/claws-streaming-worker.md`
1212
+
1213
+ ### Tests
1214
+
1215
+ 192 checks across 18 suites (all green):
1216
+ - 34 unit checks — `event-schemas.test.js`
1217
+ - 14 unit checks — `topic-registry.test.js`
1218
+ - 7 integration checks — `server-validation.test.js`
1219
+ - 7 static + smoke checks — `mcp-tools-codegen.test.js`
1220
+ - 7 CLI + integration checks — `sdk-cli.test.js`
1221
+ - 123 pre-existing checks (suites 1–13) unchanged
1222
+
1223
+ ---
1224
+
1225
+ ## [Unreleased] - Phase β: streaming foundation
1226
+
1227
+ ### Added — β.5 Claws SDK (commit 5/7)
1228
+
1229
+ **Zero-dependency worker publish helper:**
1230
+ - `claws-sdk.js` (repo root): dual CLI + module API for workers to publish
1231
+ typed `EnvelopeV1` frames; zero deps (stdlib `net`, `crypto`, `fs` only)
1232
+ - CLI: `node .claws-bin/claws-sdk.js publish boot|phase|event|heartbeat|complete [flags]`
1233
+ - Module: `const { ClawsSDK } = require('.claws-bin/claws-sdk.js')` —
1234
+ `connect()`, `hello()`, `publishBoot()`, `publishPhase()`,
1235
+ `publishEvent()`, `publishHeartbeat()`, `publishComplete()`
1236
+ - Socket auto-discovery: walks up from `cwd` looking for `.claws/claws.sock`
1237
+ - Reads env: `CLAWS_SOCKET`, `CLAWS_PEER_ID` (required for publish),
1238
+ `CLAWS_PEER_NAME`, `CLAWS_TERMINAL_ID`
1239
+ - `--help` / `--version` (`0.7.0`) / clean error on missing `CLAWS_PEER_ID`
1240
+ - `scripts/install.sh`: copies `schemas/mcp-tools.json` → `.claws-bin/schemas/`
1241
+ (required by `mcp_server.js` at runtime) and `claws-sdk.js` → `.claws-bin/`
1242
+ - 7 checks in `extension/test/sdk-cli.test.js` (static CLI + module API +
1243
+ live server integration via built extension bundle)
1244
+ - `extension/package.json`: adds `test:sdk` script, 192 checks across 18 suites
1245
+
1246
+ ---
1247
+
1248
+ ### Added — β.4 MCP tool descriptor migration (commit 4/7)
1249
+
1250
+ **MCP tool descriptors generated from Zod schemas:**
1251
+ - `scripts/codegen/gen-mcp-tools.mjs`: defines all 18 MCP tools as Zod schemas
1252
+ with verbatim descriptions; writes `schemas/mcp-tools.json` at codegen time
1253
+ - `schemas/mcp-tools.json`: committed generated file — 18 tool descriptors
1254
+ consumed by `mcp_server.js` at startup
1255
+ - `mcp_server.js`: replaced 224-line hand-written `TOOLS` array with
1256
+ `require('./schemas/mcp-tools.json')`; adds startup guard that exits with
1257
+ a clear message when the file is absent
1258
+ - 7 checks in `extension/test/mcp-tools-codegen.test.js` (static JSON checks
1259
+ + `mcp_server.js` `tools/list` stdio smoke test)
1260
+ - `extension/package.json`: adds `test:mcp-codegen` script and appends it to
1261
+ the `test` chain (185 checks total across 17 suites)
1262
+
1263
+ ---
1264
+
1265
+ ### Added — β.1 Schemas + β.1 Server Validation (commits 1–2/7)
1266
+
1267
+ **Zod schema definitions as single source of truth for all event types:**
1268
+ - `extension/src/event-schemas.ts`: `EnvelopeV1`, 5 worker schemas
1269
+ (`WorkerBootV1`, `WorkerPhaseV1`, `WorkerEventV1`, `WorkerHeartbeatV1`,
1270
+ `WorkerCompleteV1`), 8 cmd schemas, 6 system schemas, enums
1271
+ (`PHASES`, `EventKindEnum`, `ClawsRoleEnum`, `ResultEnum`, `SeverityEnum`),
1272
+ and `SCHEMA_BY_NAME` lookup map
1273
+ - `extension/src/topic-utils.ts`: standalone `matchTopic` + `matchSegments`
1274
+ extracted from `peer-registry.ts` (§7.7 refactor — clean dep graph)
1275
+ - `extension/src/topic-registry.ts`: `TOPIC_REGISTRY` (19 entries),
1276
+ `schemaForTopic(topic)` lookup
1277
+ - `extension/src/peer-registry.ts`: now re-exports `matchTopic` from
1278
+ `topic-utils.ts`; backward compatible for all existing callers
1279
+ - 34 unit checks in `test/event-schemas.test.js`
1280
+ - 14 unit checks in `test/topic-registry.test.js`
1281
+ - `zod@^3` and `zod-to-json-schema@^3` added as devDependencies
1282
+
1283
+ **Codegen pipeline (`npm run schemas`):**
1284
+ - `scripts/codegen/index.mjs`: bundles `event-schemas.ts` via esbuild → CJS,
1285
+ then calls each generator in sequence
1286
+ - `scripts/codegen/gen-json-schema.mjs`: iterates exported Zod schemas, calls
1287
+ `zodToJsonSchema()`, writes 20 files to `schemas/json/`
1288
+ - `scripts/codegen/gen-types.mjs`: writes `schemas/types/event-protocol.d.ts`
1289
+ with hand-templated type aliases for all 19 event schemas
1290
+ - `scripts/codegen/gen-docs.mjs`: regenerates schema reference table in
1291
+ `docs/event-protocol.md` between `<!-- BEGIN/END GENERATED SCHEMAS -->` markers
1292
+ - `extension/package.json`: adds `"schemas"` script; `build` unchanged
1293
+ (codegen is an explicit separate step — run before build for full pipeline)
1294
+ - `.gitignore`: adds `extension/dist/event-schemas.bundle.cjs` (temp artifact)
1295
+ - `docs/event-protocol.md`: adds `BEGIN/END GENERATED SCHEMAS` markers with
1296
+ initial generated content
1297
+ - `schemas/` directory committed with all 20 JSON Schema files and `.d.ts`
1298
+
1299
+ **Server-side publish validation with soft-reject mode:**
1300
+ - `server.ts` publish handler now validates envelope (`EnvelopeV1`) and data
1301
+ payload (`schemaForTopic`) before fan-out
1302
+ - Soft-reject mode (default): on failure, emits `system.malformed.received`
1303
+ with `{ from, topic, error: ZodIssues }`, then still fans out the event
1304
+ - Strict mode (`claws.strictEventValidation=true`): hard-rejects with
1305
+ `{ ok:false, error:'envelope:invalid'|'payload:invalid', details }`;
1306
+ no fan-out occurs
1307
+ - `server-config.ts`: new `strictEventValidation: boolean` field
1308
+ (default `false`); `DEFAULT_STRICT_EVENT_VALIDATION` constant
1309
+ - `extension.ts`: wires `strictEventValidation` from VS Code settings
1310
+ - `extension/package.json`: adds `claws.strictEventValidation` VS Code config
1311
+ - 7 integration checks in `test/server-validation.test.js`
1312
+
1313
+ ---
1314
+
1315
+ ## [0.6.5] - 2026-04-28
1316
+
1317
+ ### Added — Phase α: server-side lifecycle gate
1318
+
1319
+ The lifecycle enforcement trust boundary moves from Claude Code hooks (which do
1320
+ not reliably fire on MCP tool calls) into the socket server itself. Every
1321
+ transport — MCP, raw Bash socket, and future WebSocket — is gated by the same
1322
+ server-side check.
1323
+
1324
+ **Server-owned lifecycle state (`LifecycleStore`):**
1325
+ A new `LifecycleStore` class holds lifecycle state in memory (authoritative) and
1326
+ persists it atomically to `.claws/lifecycle-state.json` via tmp-rename. The
1327
+ server constructs the store on startup and is the only writer. No client — not
1328
+ even the model — can write the state file to bypass the gate.
1329
+
1330
+ **Server-side gate on `create`:**
1331
+ The `create` command handler now calls `lifecycleStore.hasPlan()` as its first
1332
+ action. When no plan exists, `create` is rejected immediately with:
1333
+ ```json
1334
+ { "ok": false, "error": "lifecycle:plan-required", "message": "..." }
1335
+ ```
1336
+ This error is identical regardless of whether the caller is the MCP server, a
1337
+ raw Bash `node -e` snippet, or a future WebSocket client.
1338
+
1339
+ **Four new socket commands:**
1340
+ - `lifecycle.plan` — log the PLAN phase; idempotent; returns state + `idempotent` flag
1341
+ - `lifecycle.advance` — advance the state machine one step; enforces legal transitions
1342
+ - `lifecycle.snapshot` — read-only state query; no side effects
1343
+ - `lifecycle.reflect` — terminal REFLECT transition with persisted retrospective text
1344
+
1345
+ **Four new MCP tools:**
1346
+ `claws_lifecycle_plan`, `claws_lifecycle_advance`, `claws_lifecycle_snapshot`,
1347
+ `claws_lifecycle_reflect`. All wrap the new socket commands. The plan tool's
1348
+ description explains the server gate so the model knows to call it first.
1349
+
1350
+ **`/claws-plan` now uses MCP tool, not Write:**
1351
+ Step 2 of `/claws-plan` previously instructed the model to write
1352
+ `.claws/lifecycle-state.json` directly. It now invokes
1353
+ `mcp__claws__claws_lifecycle_plan(plan="...")` — the server writes the file
1354
+ under its own ownership.
1355
+
1356
+ **PostToolUse hook removed:**
1357
+ `scripts/hooks/post-tool-use-claws.js` is deleted. This hook never reliably
1358
+ fired on MCP tool calls (issue 06) and phase advancement now happens at the
1359
+ server dispatch layer. Keeping it was dead code.
1360
+
1361
+ **PreToolUse hook simplified:**
1362
+ `scripts/hooks/pre-tool-use-claws.js` no longer contains lifecycle gate blocks
1363
+ for `mcp__claws__*` tools. The Bash long-running pattern guard (soft nudge /
1364
+ CLAWS_STRICT hard-block) is retained — it remains useful for observability.
1365
+
1366
+ **Raw-socket bypass instructions removed from claws-do.md:**
1367
+ All `net.createConnection` / "raw socket via node" fallback instructions are
1368
+ removed from `.claude/commands/claws-do.md`. If MCP fails to load, the user is
1369
+ directed to reload VS Code — not to bypass via Bash.
1370
+
1371
+ **install.sh migration:**
1372
+ The hooks-registration step now runs `inject-settings-hooks.js --remove` before
1373
+ re-registering. This cleanly removes the stale PostToolUse entry from
1374
+ `~/.claude/settings.json` on re-install without touching non-Claws hooks.
1375
+
1376
+ > **Note for users who edited settings.json manually:** if you removed the
1377
+ > `_source: "claws"` tag from a hook entry, `inject-settings-hooks.js --remove`
1378
+ > will not find it. Verify your `~/.claude/settings.json` has no PostToolUse
1379
+ > entry for `post-tool-use-claws.js` after upgrading.
1380
+
1381
+ **Post-review fixes (M1+M2+M3 — applied as immediate follow-up):**
1382
+ Three issues found in the post-merge review have been addressed in this release.
1383
+ M1: `lifecycle.advance` (and `lifecycle.reflect`) error responses now return the
1384
+ stable machine-readable code in `error` and the human-readable detail in a
1385
+ separate `message` field, matching the §2.3 contract already implemented by the
1386
+ other lifecycle handlers. M2: `lifecycle.advance` returns `idempotent: true` when
1387
+ the requested phase equals the current phase (no-op transition), as specified in
1388
+ §2.3. M3: All remaining "or raw socket" bypass phrasing in `claws-do.md` is
1389
+ removed; the affected prohibition lines are rephrased without the term.
1390
+
1391
+ Files changed:
1392
+ - `extension/src/lifecycle-store.ts` — new `LifecycleStore` class (pure Node.js)
1393
+ - `extension/src/protocol.ts` — `LifecycleState`, `LifecyclePlanRequest`,
1394
+ `LifecycleAdvanceRequest`, `LifecycleSnapshotRequest`, `LifecycleReflectRequest`
1395
+ added; all four added to `ClawsRequest` union
1396
+ - `extension/src/server.ts` — import + field + constructor wiring; gate check in
1397
+ `create` handler; four new `lifecycle.*` command handlers
1398
+ - `mcp_server.js` — four new tool descriptors + four new `handleTool` cases
1399
+ - `.claude/commands/claws-do.md` — raw-socket bypass instructions removed
1400
+ - `.claude/commands/claws-plan.md` — step 2 now invokes `claws_lifecycle_plan`;
1401
+ lifecycle table updated to remove `(post-tool-use hook auto-advances)` reference
1402
+ - `scripts/hooks/post-tool-use-claws.js` — **deleted**
1403
+ - `scripts/hooks/pre-tool-use-claws.js` — lifecycle gate blocks removed; Bash guard kept
1404
+ - `scripts/inject-settings-hooks.js` — PostToolUse entry removed; 3 hooks remain
1405
+ - `scripts/install.sh` — hooks registration updated to `--remove` then re-register
1406
+ - `extension/test/lifecycle-store.test.js` — 25 unit tests (all pass)
1407
+ - `extension/test/lifecycle-server.test.js` — 8 integration tests (7 original + 1
1408
+ new illegal-transition test; idempotent:true assertion added to advance test)
1409
+
1410
+ ## [0.6.4] - 2026-04-28
1411
+
1412
+ ### Added — CLAWS_STRICT mode (first Hard enforcement mechanism)
1413
+
1414
+ Until v0.6.4 every Claws enforcement layer was advisory: CLAUDE.md blocks,
1415
+ SessionStart/PreToolUse/Stop hooks all *suggested* the Claws path but the
1416
+ model could still fall back to plain Bash for long-running orchestration
1417
+ work. This release ships the first hard block.
1418
+
1419
+ When `CLAWS_STRICT=1` is set in the user's environment (or in
1420
+ `~/.claude/settings.json` `env` block), the PreToolUse hook returns
1421
+ `permissionDecision: "deny"` for Bash commands that match long-running
1422
+ patterns (servers, watchers, `nohup`, `nodemon`, `pnpm/bun start|dev|serve|watch`,
1423
+ etc.). The deny reason is an actionable four-step recipe: `claws_create` →
1424
+ `claws_send` → `claws_read_log` → `claws_close`. Claude Code blocks the
1425
+ tool call and the model pivots.
1426
+
1427
+ The pattern list is conservative — only commands that are unambiguously
1428
+ long-running. Ordinary commands like `ls`, `git status`, one-shot builds,
1429
+ or short tests pass through unchanged. `CLAWS_STRICT` defaults to off; no
1430
+ behavior change for existing users.
1431
+
1432
+ The mechanism uses Claude Code's documented PreToolUse hook protocol
1433
+ (`hookSpecificOutput.permissionDecision`); no Claude Code change required.
1434
+
1435
+ Files changed:
1436
+ - `scripts/hooks/pre-tool-use-claws.js` — added `STRICT` branch with
1437
+ `hookSpecificOutput.permissionDecision: "deny"` + actionable reason.
1438
+ Pattern list expanded with `pnpm`, `bun`, `hypercorn`, `nodemon`,
1439
+ `nohup`. Word-boundary anchored to reduce false positives.
1440
+
1441
+ ### Fixed — settings.json schema URL + install.sh housekeeping
1442
+
1443
+ - `.claude/settings.json` — `$schema` URL was `json-schema.store.org` (typo);
1444
+ corrected to `json.schemastore.org`. Closes the "Found 1 settings issue"
1445
+ warning surfaced by `/doctor`.
1446
+ - `scripts/install.sh` — copies `scripts/stream-events.js` into `.claws-bin/`
1447
+ during install so the event-streaming sidecar (referenced in
1448
+ `docs/event-protocol.md`) is available out of the box. Documents
1449
+ `CLAWS_STRICT` env var in the header. Fixes the install-time MCP
1450
+ handshake probe to use newline-delimited JSON (matches the v0.6.1 server
1451
+ framing fix; the probe was still using LSP `Content-Length` framing and
1452
+ failing silently).
1453
+
1454
+ ## [0.6.3] - 2026-04-28
1455
+
1456
+ ### Fixed — claws_send submit reliability for TUI workers
1457
+
1458
+ Multi-line text sent via `claws_send` with `newline=true` was not registering as
1459
+ a discrete Enter keypress in Ink-based TUIs (Claude Code). The trailing CR
1460
+ arrived in the same write as the bracketed-paste close marker and got bundled
1461
+ into the TUI's paste-detection burst, leaving the input populated but never
1462
+ submitted. Empirical workaround: send the CR via raw socket as a separate write.
1463
+ This release encodes the workaround into the send path itself.
1464
+
1465
+ Two-part fix:
1466
+ - `extension/src/claws-pty.ts:writeInjected` — when bracketed paste is used and
1467
+ `withNewline=true`, the trailing `\r` is emitted in a separate `write()` call
1468
+ after a 30 ms delay. The pause closes the TUI's paste-detection window before
1469
+ the CR arrives, so it registers as Enter.
1470
+ - `mcp_server.js:claws_send` — auto-sets `paste: true` when text contains `\n`
1471
+ or `\r`. The tool description always promised this; the server never
1472
+ enforced it.
1473
+
1474
+ End-to-end verified: a multi-line `claws_send` with `newline=true` now submits
1475
+ on the first try in a Claude Code worker terminal — no raw-socket CR fallback
1476
+ needed.
1477
+
1478
+ ### Added — npm run deploy:dev for local extension iteration
1479
+
1480
+ `extension/scripts/deploy-dev.mjs` (called via `npm run deploy:dev`) copies the
1481
+ freshly built `dist/extension.js` and `native/` bundle into every installed
1482
+ extension directory under `~/.vscode/extensions/<publisher>.<name>-*/`. Closes
1483
+ the silent gap where `npm run build` produced a new bundle that VS Code never
1484
+ loaded because the editor only reads from its installed-extensions dir.
1485
+
1486
+ ### Fixed — install.sh now cleans stale install dirs
1487
+
1488
+ After a successful `code --install-extension <vsix>`, the installer now removes
1489
+ older `<publisher>.<name>-X.Y.Z` directories so VS Code's extension picker
1490
+ isn't confused by lingering versions. Previously, prior installs (e.g. stuck
1491
+ on a lock when a window was open) could leave multiple version dirs under
1492
+ `~/.vscode/extensions/` indefinitely.
1493
+
1494
+ ### Fixed — version manifests now track CHANGELOG
1495
+
1496
+ `extension/package.json` was stuck at `0.6.0` and root `package.json` at
1497
+ `0.5.3` despite CHANGELOG, README, CLAUDE.md, and the `v0.6.1` git tag all
1498
+ declaring `0.6.1`. Result: every reinstall packaged a VSIX labeled `0.6.0`,
1499
+ and VS Code's extension UI kept showing `0.6.0` even when the bytes had moved
1500
+ on. Both manifests now match the CHANGELOG.
1501
+
1502
+ ## [0.6.2] - 2026-04-28
1503
+
1504
+ ### Added — Lifecycle gate (PLAN→REFLECT) for orchestration
1505
+
1506
+ Multi-terminal orchestration via Claws now follows an enforced 8-phase lifecycle:
1507
+ PLAN → SPAWN → DEPLOY → OBSERVE → RECOVER → HARVEST → CLEANUP → REFLECT.
1508
+ A PreToolUse gate blocks `claws_create` (and any `claws_*` tool) until a PLAN
1509
+ file exists at `.claws/lifecycle-state.json`. The `/claws-plan` slash command
1510
+ writes this file and unlocks terminal creation.
1511
+
1512
+ Why: pre-0.6.2, orchestrators could spawn workers without stating a mission,
1513
+ which led to runaway terminals, no audit trail, and no shared memory of what
1514
+ each worker was supposed to do. The gate forces a one-paragraph plan before
1515
+ any worker is created.
1516
+
1517
+ Components:
1518
+ - `scripts/hooks/lifecycle-state.js` — shared module that read/writes the
1519
+ state machine.
1520
+ - `scripts/hooks/pre-tool-use-claws.js` — gate logic that returns a blocking
1521
+ error when no PLAN exists.
1522
+ - `scripts/hooks/post-tool-use-claws.js` — auto-advances phase after each
1523
+ `claws_*` tool call.
1524
+ - `scripts/hooks/stop-claws.js` — checks lifecycle state on Stop and reminds
1525
+ the model to close terminals + write REFLECT before session end.
1526
+ - `scripts/inject-settings-hooks.js` — registers the new PostToolUse hook
1527
+ matcher (`mcp__claws__*`).
1528
+ - `.claude/commands/claws-plan.md` — new `/claws-plan` slash command.
1529
+
1530
+ ### Added — Event-streaming sidecar protocol
1531
+
1532
+ A convention layer over the existing claws/2 pub-sub for real-time, no-polling
1533
+ orchestration. Workers emit lifecycle events on well-known topics; orchestrators
1534
+ subscribe via a long-lived sidecar process that prints each push frame as one
1535
+ JSON line on stdout — designed to be spawned via `run_in_background` and
1536
+ consumed by Monitor-style line tailing.
1537
+
1538
+ - `docs/event-protocol.md` — event shapes, command channel, state machine.
1539
+ - `scripts/stream-events.js` — sidecar implementation. Holds one persistent
1540
+ socket, registers as a peer, subscribes to a topic pattern, emits JSON-line
1541
+ events per push frame.
1542
+
1543
+ ### Housekeeping
1544
+
1545
+ - `scripts/git-hooks/pre-commit` — repo-local hook that enforces CHANGELOG
1546
+ updates for code commits. Installed by `scripts/install.sh` into
1547
+ `.git/hooks/`.
1548
+ - `.gitignore` — ignore `.claude/scheduled_tasks.lock` (runtime artifact
1549
+ from the scheduling system).
1550
+
1551
+ ## [0.6.1] - 2026-04-22
1552
+
1553
+ ### Fixed — MCP server stdio framing (CRITICAL)
1554
+
1555
+ The MCP server was implementing **LSP-style `Content-Length` framing** instead of the
1556
+ **newline-delimited JSON** the MCP spec requires for stdio transport. Result: every
1557
+ JSON-RPC request from Claude Code (`initialize`, `tools/list`, every tool call) hung
1558
+ forever — the server was waiting for `Content-Length: NNN\r\n\r\n` headers that never
1559
+ came. `/mcp` showed "claws — needs auth" / "Failed to reconnect"; `mcp__claws__*` tools
1560
+ were never actually available in any Claude Code session, regardless of install state.
1561
+
1562
+ Fix: `readMessage()` now reads line-by-line; `writeMessage()` appends `\n` instead of a
1563
+ Content-Length header. Verified end-to-end: `initialize` → response, `tools/list` →
1564
+ all 14 tools, `claws_ping` callable from a real Claude Code session.
1565
+
1566
+ The prior "GAP-2 + GAP-3 — MCP spec compliance" commit (b5c2c7c) only fixed the
1567
+ `isError` shape and stderr logging — it never tested the JSON-RPC handshake, so the
1568
+ framing bug shipped in every release through 0.6.0.
1569
+
1570
+ Also bumped `serverInfo.version` from `0.6.0` → `0.6.1`.
1571
+
1572
+ ### Added — Behavioral Injection Enforcement (Lifecycle enforcement overhaul)
1573
+
1574
+ Closes the lifecycle enforcement gap identified in `.local/audits/lifecycle-enforcement-gap.md`.
1575
+ Prior to this release, Claude Code defaulted to Bash in new sessions because the behavioral
1576
+ injection system was advisory wallpaper — the strong imperative content existed in orphaned
1577
+ files that nothing auto-loaded.
1578
+
1579
+ **Templates (Wave 1)**
1580
+ - `templates/CLAUDE.project.md` — replaces orphaned `templates/CLAUDE.claws.md`. New template uses
1581
+ imperative framing (`MUST`/`ALWAYS`/`NEVER`) and includes the full 7-step worker boot sequence,
1582
+ lifecycle phase list, and tool inventory with placeholder substitution.
1583
+ - `templates/CLAUDE.global.md` — new machine-wide policy template. Injected into `~/.claude/CLAUDE.md`
1584
+ so every Claude Code session on the machine sees the lifecycle rules, even in non-Claws projects.
1585
+ - `.claude/skills/claws-orchestration-engine/SKILL.md` — rewritten with full 8-phase lifecycle
1586
+ (PLAN→SPAWN→DEPLOY→OBSERVE→RECOVER→HARVEST→CLEANUP→REFLECT) inlined. Removed false claim
1587
+ that lifecycle auto-loads on MCP registration. Deleted dead `lifecycle.yaml`.
1588
+ - `.claude/commands/claws-boot.md` — new `/claws-boot` slash command codifying the exact 7-step
1589
+ worker boot sequence (create → activate → trust → bypass → mission → CR).
1590
+ - `rules/claws-default-behavior.md` — added ECC-only scope note; canonical rules now live in
1591
+ the injected `CLAUDE.md` block.
1592
+
1593
+ **Injector scripts (Wave 2)**
1594
+ - `scripts/inject-claude-md.js` — rewritten to read from `templates/CLAUDE.project.md` instead of
1595
+ hardcoded advisory copy. Substitutes 8 placeholders (`{PROJECT_NAME}`, `{SOCKET_PATH}`,
1596
+ `{TOOLS_V1_LIST}`, `{TOOLS_V2_LIST}`, `{CMDS_LIST}`, etc.).
1597
+ - `scripts/inject-global-claude-md.js` — new script. Writes machine-wide Claws policy to
1598
+ `~/.claude/CLAUDE.md` using `<!-- CLAWS-GLOBAL:BEGIN v1 -->` sentinel. Idempotent.
1599
+ - `scripts/inject-settings-hooks.js` — new script. Registers `SessionStart`, `PreToolUse:Bash`,
1600
+ and `Stop` hooks in `~/.claude/settings.json` with `_source:"claws"` tag for clean uninstall.
1601
+ Supports `--remove` flag to strip all Claws hooks without touching others.
1602
+ - `.claws-bin/hooks/session-start-claws.js` — fires on every Claude Code session start in a Claws
1603
+ project (socket detected). Emits lifecycle rules as a system-reminder.
1604
+ - `.claws-bin/hooks/pre-tool-use-claws.js` — nudges long-running Bash commands toward `claws_create`.
1605
+ - `.claws-bin/hooks/stop-claws.js` — reminds model to close terminals before session ends.
1606
+
1607
+ **Installer wiring (Wave 3)**
1608
+ - `scripts/install.sh` — three additive additions (zero line deletions):
1609
+ - Vendors `hooks/*.js` into project `.claws-bin/hooks/`
1610
+ - Calls `inject-global-claude-md.js` after project CLAUDE.md injection
1611
+ - Calls `inject-settings-hooks.js` to register lifecycle hooks on every install
1612
+ - Adds `.claws-bin/hooks/` to post-install verification checklist
1613
+
1614
+ **Testing**
1615
+ - `scripts/test-enforcement.sh` — integration test covering the full pipeline:
1616
+ inject-claude-md.js (idempotency + imperative content), inject-global-claude-md.js (dry-run),
1617
+ inject-settings-hooks.js (dry-run + tag verification), session-start hook (socket detection),
1618
+ hook exit codes.
1619
+
1620
+ ## [0.6.0] - 2026-04-21
1621
+
1622
+ ### Added — claws/2 Agentic SDLC Protocol (Phase A + B)
1623
+
1624
+ **New protocol version `claws/2`** — a backward-compatible extension of `claws/1` that adds a message bus, peer identity, and a task registry so an orchestrator Claude can coordinate a fleet of worker Claudes over the existing Unix socket.
1625
+
1626
+ Key additions (all new commands are additive — `claws/1` clients continue to work unchanged):
1627
+
1628
+ - **`hello` handshake** — clients register as `orchestrator`, `worker`, or `observer`. Returns a stable `peerId` for the session. Exactly one orchestrator allowed per socket; a second registration returns an error. Workers that disconnect trigger `worker.offline.<peerId>` events.
1629
+
1630
+ - **Peer registry** (`peer-registry.ts`) — in-memory map of live peers keyed by peerId. Tracks role, peerName, terminalId, subscriptions, and lastSeen. Cleared on extension reload. `WeakMap<Socket, peerId>` enables O(1) cleanup on disconnect.
1631
+
1632
+ - **`subscribe` / `unsubscribe` / `publish`** — named topic pub/sub over the existing socket. Topic patterns support `*` (one segment) and `**` (many segments). Server fans out to matching subscribers. `echo: true` delivers to the sender too.
1633
+
1634
+ - **Server-push frames** — new frame format with `push: 'message'` and no `rid` field. Clients distinguish push frames from responses by the absence of `rid`. Implemented via a dedicated `pushFrame()` helper that catches write errors without crashing the server.
1635
+
1636
+ - **`broadcast`** — orchestrator-only shorthand that fans out a text message to all workers (or all peers by role). Optional `inject: true` also sends the text into each peer's associated terminal via bracketed paste — the "kill switch" for hung workers.
1637
+
1638
+ - **`ping`** — lightweight heartbeat command. Returns `serverTime`. Any command from a peer refreshes its `lastSeen`.
1639
+
1640
+ - **Task registry** (`task-registry.ts`) — in-memory task lifecycle with five commands:
1641
+ - `task.assign` (orchestrator) — creates a task, delivers via pub/sub and/or terminal inject
1642
+ - `task.update` (worker, own tasks only) — reports progress; publishes `task.status`
1643
+ - `task.complete` (worker, own tasks only, idempotent) — finalises the task; publishes `task.completed`
1644
+ - `task.cancel` (orchestrator) — sets `cancelRequested`; publishes `task.cancel_requested.<assignee>`
1645
+ - `task.list` (any role) — filtered snapshot by assignee, status, or updatedAt cursor
1646
+
1647
+ - **MCP client tools** (`mcp_server.js`) — six new tools expose claws/2 to Claude Code: `claws_hello`, `claws_subscribe`, `claws_publish`, `claws_broadcast`, `claws_ping`, `claws_peers`.
1648
+
1649
+ ### Fixed
1650
+ - **`.mcp.json` now emits absolute paths** — `command`, `args[0]`, `cwd`, and `CLAWS_SOCKET` are pinned to absolute paths at install time. Eliminates silent failures for nvm/volta/asdf users and CWD-sensitive Claude Code launches. The file is machine-specific and gitignored; the embedded README now says so.
1651
+
1652
+ ### Tests
1653
+ - 11 suites, 90+ checks. Three new suites added: `claws-v2-hello` (hello handshake + peer registry), `claws-v2-pubsub` (subscribe/unsubscribe/publish/broadcast + wildcard matching), `claws-v2-tasks` (full task lifecycle including push-frame delivery assertions).
1654
+
1655
+ ## [0.5.11] - 2026-04-19
1656
+
1657
+ ### Milestone
1658
+ - **First successful external install verified.** End-to-end install on a fresh machine by a user outside the dev environment completed without issues — extension loaded, socket connected, MCP tools live, shell hooks active.
1659
+
1660
+ ### Fixed
1661
+ - **Network pre-check before native build (R3.5).** Before `npm run build` runs `@electron/rebuild`, the installer now probes `https://github.com` with a 5-second timeout (curl, then wget fallback). Air-gapped machines or broken network configurations get an immediate actionable warning — including `CLAWS_ELECTRON_VERSION` and `CLAWS_FORCE_REBUILD_NPTY=0` escape hatches — rather than a silent 3-minute hang waiting for Electron headers that will never arrive.
1662
+ - **Nushell hook injection added (R5.4).** After the fish hook block, the installer now checks for `~/.config/nushell/env.nu` or `config.nu`. If either exists and doesn't already contain `CLAWS_DIR`, it appends `$env.CLAWS_DIR` and `$env.CLAWS_SOCKET` assignments in native Nushell syntax. Nushell users no longer need to manually export these variables.
1663
+ - **VSIX install retried with sudo on permission failure (R4.7/B7).** The `--install-extension` loop now attempts a plain install first, then falls back to `sudo` if the first attempt fails (common when the extensions directory is owned by root on shared machines). Both success and failure paths log the outcome.
1664
+ - **Post-install extensions directory verification (R4.10).** After each editor install attempt, the installer checks `~/$HOME/.<editor>/extensions/neunaha.claws-*` to confirm the VSIX actually landed — rather than trusting the undocumented VS Code exit code alone. The verified/unverified distinction is reported in the install log.
1665
+ - **Per-editor ABI mismatch warning (R3.7).** After confirming the native `pty.node` build, the installer reads the `electronVersion` field from Cursor, Windsurf, and VS Code Insiders app bundles. If any editor's Electron version differs from the version the binary was built for, a targeted warning is emitted per editor explaining pipe-mode fallback and providing the exact `CLAWS_ELECTRON_VERSION=<version>` rebuild command.
1666
+ - **Explicit `--arch` passed to `@electron/rebuild` (R3.10).** `bundle-native.mjs` now calls `detectTargetArch()` which honours `CLAWS_ELECTRON_ARCH` env override, then falls back to `process.arch`. On macOS, if Node.js reports `x64` while actually running under Rosetta 2 (detected via `sysctl sysctl.proc_translated`), the user is warned that the binary will be x64 and given the `CLAWS_ELECTRON_ARCH=arm64` override. The detected arch is passed as `--arch` to `@electron/rebuild`.
1667
+ - **`--useCache` and `--cachePath` added to `@electron/rebuild` (R3.4).** Repeated installs no longer recompile `node-pty` from scratch when the ABI-correct binary is already cached. Cache lives at `<repo>/../.electron-rebuild-cache` and is keyed by Electron version + arch.
1668
+
1669
+ ## [0.5.10] - 2026-04-19
1670
+
1671
+ ### Fixed
1672
+ - **Fish shell hook no longer requires `bass` (R5.3/B3).** Created a standalone `scripts/shell-hook.fish` in pure fish syntax that replicates everything in `shell-hook.sh` — startup banner with socket status and terminal count, plus all four shell functions (`claws-ls`, `claws-new`, `claws-run`, `claws-log`). The `conf.d` loader now sets `CLAWS_DIR`/`CLAWS_SOCKET` and sources `shell-hook.fish` directly. Fish users without `bass` (the majority) previously got only an env var export; now they get the full experience unconditionally.
1673
+ - **Disk space pre-check added (R8.7).** Installer checks `df -k $HOME` at the start of the dependency preflight and warns if less than 500MB is free, before spending time on a clone + native build that will fail mid-way.
1674
+ - **Build failure error now targets the root cause (R8.8).** After a failed `npm run build`, the installer scans `$CLAWS_LOG` for keywords (Xcode/CLT, network/ENOTFOUND, python/gyp) and emits a specific fix command for each case instead of a generic "common causes" list.
1675
+ - **Sensitive env vars purged before `CLAWS_DEBUG` trace (R8.9).** When `CLAWS_DEBUG=1` enables `set -x`, the installer now unsets `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`, `GITHUB_TOKEN`, `NPM_TOKEN`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and similar vars before enabling the trace, so secrets never appear in debug logs.
1676
+ - **Zed editor documented as unsupported (R4.12).** If `zed` is found in PATH, the installer now prints an explicit info message explaining that Claws is VS Code/Cursor/Windsurf-only (VSIX format) and Zed is not supported.
1677
+ - **`CLAWS_SOCKET` relative-path constraint documented inline (R6.4/B9).** Added a JS comment directly inside the `.mcp.json` write block explaining that `.claws/claws.sock` is relative to Claude Code's CWD (workspace root) and how to use an absolute path for non-standard setups.
1678
+
1679
+ ## [0.5.9] - 2026-04-19
1680
+
1681
+ ### Fixed
1682
+ - **git minimum version check (R2.1).** Installer now parses the major version from `git --version` and aborts with a clear upgrade message if it's below 2.x.
1683
+ - **git clone stderr was silently discarded (R1.4).** `git clone --quiet` and `git fetch --quiet` swallowed all error output. Removed `--quiet`; errors now append to `$CLAWS_LOG` so network failures and auth errors are inspectable.
1684
+ - **CLAWS_REF pinning mechanism added (R1.5).** `CLAWS_REF="${CLAWS_REF:-main}"` lets users pin to a tag or branch (`CLAWS_REF=v0.5.9 bash install.sh`). All `origin/main` references replaced with `origin/$CLAWS_REF`. Initial clone now tries `--branch $CLAWS_REF` first (works for tags and branches), falls back to default-branch clone.
1685
+ - **git fsck integrity check after clone and update (R1.6).** After every successful clone or update, `git fsck --no-dangling` runs and warns if integrity fails, catching partial/corrupted clones before the build.
1686
+ - **Shallow clone for first-time installs (R1.7).** Initial `git clone` now uses `--depth 1`, significantly reducing download size for users who don't need full history.
1687
+ - **pty.node architecture verification (R3.9).** After the native build, `file "$NATIVE_PTY_BIN"` is checked against `uname -m`. A mismatch (e.g. x86_64 binary on arm64) emits a warning pointing to `$CLAWS_LOG`.
1688
+ - **Step 4 "Runtime check" was a no-op (B4).** The step previously just printed "No Python required — runtime ready" and did nothing. Replaced with a live `node` reachability check (verifies Node.js is still in PATH mid-install) and a pre-check that `mcp_server.js` exists in the clone before the copy step.
1689
+ - **`.gitignore` not created for new projects (R6.13).** The `.claws/` gitignore guard previously skipped projects with no `.gitignore`. Now calls `touch` first so the entry is written regardless.
1690
+ - **inject-claude-md.js error message too generic (B8).** Failure message now references `$CLAWS_LOG` for details instead of just "injector failed".
1691
+ - **No bash -n smoke test after shell hook injection (R5.8).** After each `inject_hook` call for zsh, bash, and bash_profile, the installer now runs `bash -n` on the modified rc file and warns if a syntax error is detected.
1692
+ - **Step-8 .mcp.json check was file-exists only (R7.6).** Replaced with a full `JSON.parse` validation — a truncated or corrupted `.mcp.json` now correctly fails the verification step rather than passing silently.
1693
+ - **Node.js PATH not surfaced in verification (R7.10).** Step 8 now logs the full `process.execPath` of the Node.js binary found in PATH, with a note that GUI-launched VS Code may resolve a different Node.
1694
+ - **Electron version not visible before build (R2.7/R3.2).** Installer now pre-detects the VS Code/Cursor/Windsurf Electron version from their app bundles (macOS) or binaries (Linux) before `npm run build` starts, surfacing the ABI target in the install log.
1695
+ - **@electron/rebuild not verified after npm install (R2.8).** Added `require.resolve('@electron/rebuild')` check after `npm install`; warns immediately if the package is missing rather than letting the build fail 2 minutes later with a cryptic error.
1696
+
1697
+ ## [0.5.8] - 2026-04-19
1698
+
1699
+ ### Fixed
1700
+ - **nvm/fnm hints were dead code (B10).** Worker B placed the nvm/fnm detection hints *after* the `case "$PLATFORM"` block whose every arm calls `die()`. Since `die()` exits immediately, the hints could never execute — every user with Node installed via nvm or fnm got a generic "node not found" with no actionable guidance. Moved both hints to before the `case` block so they print before the installer exits.
1701
+ - **Stale-clone detection was always-false (B11).** Worker C's dynamic-version fix (v0.5.7) read `EXPECTED_MIN_VERSION` from `extension/package.json`, then immediately set `EXT_VERSION="$EXPECTED_MIN_VERSION"` — making both variables identical. The comparison `[ "$EXT_VERSION" != "$EXPECTED_MIN_VERSION" ]` was therefore always false, silently disabling the stale-clone guard entirely. Restored the original two-variable pattern: `EXPECTED_MIN_VERSION` is hardcoded to the release baseline (`"0.5.7"`); `EXT_VERSION` is dynamically read from the clone's `package.json` at runtime. A stale clone now correctly aborts with a recovery command. EXT_VERSION fallback changed from `"0.5.6"` to `"0.0.0"` so a broken read always triggers the check rather than silently passing.
1702
+
1703
+ ## [0.5.7] - 2026-04-19
1704
+
1705
+ ### Fixed
1706
+ - **Fish shell hook broken on first install.** The previous fish config block used `source` (invalid in fish) to load the POSIX shell hook. Replaced with native fish syntax: sets `$CLAWS_DIR` as a global env var and optionally calls `bass` to source the POSIX hook if available. Fish users no longer land in a broken `claws_worker` state on first install.
1707
+ - **shell-hook.sh existence guard.** Installer now hard-fails if `shell-hook.sh` is absent from `$INSTALL_DIR/scripts/` before attempting injection, surfacing incomplete clones early.
1708
+ - **No-op source removed from installer end.** The trailing `source "$INSTALL_DIR/scripts/shell-hook.sh"` ran in a subshell and exported nothing to the user's shell. Replaced with an explicit `info` message to open a new terminal.
1709
+ - **Dead code removed from preflight.** `detect_ext_dir()` function and `EXT_DIR` variable were defined but never referenced; removed entirely.
1710
+ - **npm minimum version enforced.** Installer now requires npm 7+ and aborts with a clear upgrade command (`npm install -g npm`) if the detected version is older.
1711
+ - **nvm/fnm hints when node is missing.** If `node` is not found and `~/.nvm` or `~/.fnm` exists, installer surfaces the exact command to activate the version manager before failing.
1712
+ - **Windows guard added.** Git Bash / MSYS / Cygwin environments now get an immediate `die` with a WSL2 redirect instead of failing mid-install on Unix-specific operations.
1713
+ - **Architecture logged in preflight.** `uname -m` output (`x86_64` / `arm64`) now appears in the preflight summary — essential context for diagnosing node-pty ABI mismatches.
1714
+ - **`EXPECTED_MIN_VERSION` is now dynamic.** Previously hardcoded to a static string at script-release time; now read from `extension/package.json` at runtime so version drift between the script and the manifest is impossible.
1715
+ - **vsce output routed to `$CLAWS_LOG`.** VSIX packaging errors were silently swallowed (`>/dev/null 2>&1`). Now appended to the install log file so failures are inspectable without re-running with verbose flags.
1716
+ - **Publisher field pre-checked before VSIX packaging.** If `extension/package.json` is missing the `publisher` field, `vsce` fails with an opaque error. Installer now checks the field and warns early.
1717
+ - **VSIX size sanity check.** A packaged VSIX under 50 KB is almost certainly missing the native binary (`!native/**` absent from `.vscodeignore`). Installer now rejects suspiciously small VSIXes rather than installing a broken extension.
1718
+ - **`.mcp.json` validated after write.** The node script that writes `.mcp.json` is now followed by a `JSON.parse` check; invalid JSON emits `bad` messages pointing to the log before continuing.
1719
+ - **`.claws/` added to project `.gitignore` automatically.** If the project `.gitignore` exists but doesn't contain `.claws/`, the installer appends it.
1720
+ - **`inject-claude-md.js` existence guard.** Before invoking `inject-claude-md.js`, the installer now checks both candidate paths and skips with a `warn` if neither exists, rather than crashing.
1721
+ - **Shell hook verification before final banner.** Installer now checks `~/.zshrc`, `~/.bashrc`, and `~/.bash_profile` for the hook marker and warns if none is found, prompting the user to source it manually.
1722
+
1723
+ ## [0.5.6] - 2026-04-18
1724
+
1725
+ ### Fixed
1726
+ - **VSIX never installed (regression in v0.5.5).** The v0.5.5 rewrite of the build section forgot to set `BUILD_OK=1` after a successful `npm run build`, so the condition at line 486 (`[ "${BUILD_OK:-0}" = "1" ]`) was always false. Every user was silently getting the symlink fallback — VSIX install was completely bypassed.
1727
+ - **git pull --ff-only failed silently AND printed a green checkmark anyway.** On a dirty `~/.claws-src/` (local changes, diverged history, offline), `git pull --ff-only` would fail, `warn` would fire, and then `ok "updated"` would print unconditionally on the next line. Replaced with `git fetch origin main && git reset --hard origin/main`. On failure, installer now hard-exits with a concrete recovery command (`rm -rf ~/.claws-src && re-run`).
1728
+ - **Version stale check warned and continued.** When git fetch/reset updated `~/.claws-src/` to a newer version, the old `EXPECTED_MIN_VERSION` check would detect a mismatch and warn — but then continue with the stale code. Now dies with the recovery command instead of silently shipping v0.4.0 while the banner says v0.5.6.
1729
+ - **Linux Electron detection completely absent from bundle-native.mjs.** On Linux, `detectElectronVersion()` had no detection logic and fell straight to the hardcoded fallback (`39.8.5`). VS Code on modern Ubuntu typically ships Electron 30–35, so every Linux user got the wrong ABI → pipe-mode. Added detection via VS Code's bundled `electron` binary at known Linux install paths (`/usr/share/code/electron`, `/opt/visual-studio-code/electron`, `/snap/code/current/electron`, etc.).
1730
+
1731
+ ## [0.5.5] - 2026-04-18
1732
+
1733
+ ### Fixed
1734
+ - **Pipe-mode-after-install bug eliminated for good.** Previously, `scripts/install.sh` had two separate node-pty handling paths: `npm run build` (which runs `bundle-native.mjs` → `@electron/rebuild` → copies the ABI-correct binary into `extension/native/node-pty/`) AND a *second, redundant* `@electron/rebuild` run against `node_modules/node-pty/` whose output never made it into the VSIX. If `npm run build` failed silently, the installer fell back to legacy JS, packaged a VSIX without a working `native/node-pty/build/Release/pty.node`, and the extension landed in pipe-mode — exactly what Miles was seeing. The installer now has ONE canonical build path: `npm run build`. If it fails, the installer aborts with a concrete diagnostic (Xcode CLT missing, @electron/rebuild network failure, node-gyp error) instead of shipping a broken VSIX. After the build, it hard-verifies `native/node-pty/build/Release/pty.node` exists and reports which Electron version it was built for; if the binary is missing, install aborts rather than completing "successfully" with a VSIX that can't load node-pty.
1735
+ - **Pre-flight Xcode Command Line Tools check on macOS.** `@electron/rebuild` needs a C compiler. The installer now checks `xcode-select -p` up front and aborts with the exact recovery command (`xcode-select --install`) before wasting the user's time attempting a build that cannot succeed.
1736
+ - **`npm run build` output is no longer silent during install.** The previous `--silent` flag hid `@electron/rebuild` progress and compile errors, turning every native build failure into an invisible "falling back to legacy JS" warn. Users now see the bundle-native.mjs output on screen and in `$CLAWS_LOG`, so diagnosing a broken build is a copy-paste away.
1737
+
1738
+ ### Removed
1739
+ - Redundant second `@electron/rebuild` run against `node_modules/node-pty/` in `install.sh`. `bundle-native.mjs` is the single source of truth for the ABI-correct binary; the duplicated rebuild only rebuilt a directory the VSIX didn't ship and masked `bundle-native.mjs` failures.
1740
+
1741
+ ### Migration notes
1742
+ - Users affected by pipe-mode just need to re-run the curl install once. The v0.5.5 installer will either produce a working VSIX (binary present, Electron version matches) or abort with a concrete next step — no more invisible failures.
1743
+
1744
+ ## [0.5.4] - 2026-04-18
1745
+
1746
+ ### Fixed
1747
+ - **Shell hook injection now self-heals on every install/update.** The old `inject_hook` in `scripts/install.sh` only *added* a `source /path/to/shell-hook.sh` line when no `# CLAWS terminal hook` marker was present; it never removed stale entries. Users who ran the installer under a different `CLAWS_DIR` (e.g. pointing at a project root during testing) ended up with broken `.zshrc` lines like `source:31: no such file or directory: /Users/miles/renew/scripts/shell-hook.sh` that fired on every new shell. The function now strips any existing `# CLAWS terminal hook` marker plus the following line via `sed`, then appends a fresh entry using the current `INSTALL_DIR`. If a stale path was removed, the banner reports `refreshed in .zshrc (removed stale path)` so the fix is visible.
1748
+ - **Fish shell config is now idempotent too.** `~/.config/fish/conf.d/claws.fish` was only written when absent, leaving stale `INSTALL_DIR` references alive across reinstalls. The installer now overwrites it unconditionally so fish stays in sync with the zsh/bash hooks.
1749
+
1750
+ ### Migration notes
1751
+ - Users affected by stale `.zshrc` / `.bashrc` / `.bash_profile` lines just need to re-run the curl install once. The v0.5.4 installer deletes the broken line and installs a fresh one in the same pass — no manual editing of dotfiles required.
1752
+
1753
+ ## [0.5.3] - 2026-04-18
1754
+
1755
+ ### Changed
1756
+ - **Extension install path switched from symlink to `code --install-extension <vsix>`.** When VS Code's CLI is available, the installer now packages the extension as a `.vsix`, runs `code --install-extension --force` for every detected editor (VS Code, Cursor, Insiders, Windsurf), and VS Code itself handles extension registration and shows its standard "Reload to activate?" toast in any running window. Single-click activation vs the old "hope VS Code noticed the symlink" pattern.
1757
+ - **Install banner reports the install method** explicitly: `(method: vsix)` or `(method: symlink)` so users know which code path landed.
1758
+
1759
+ ### Added
1760
+ - `CLAWS_DEV_SYMLINK=1` env var forces symlink install (developer workflow — edit TypeScript → reload → test without re-packaging).
1761
+ - Detects editor CLIs in both `$PATH` and macOS app bundles (`/Applications/<Editor>.app/Contents/Resources/app/bin/<cli>`) so VSIX install works even when the user never ran "Shell Command: Install 'code' in PATH".
1762
+ - Symlink install remains as fallback when VSIX packaging fails, when `npx` is unavailable, or when `CLAWS_DEV_SYMLINK=1` is set. Never silent — the banner shows which path was used.
1763
+
1764
+ ### Fixed
1765
+ - The previous symlink-only install required users to manually `Developer: Reload Window` and hope VS Code picked up the new symlink. VSIX install via `code --install-extension` means VS Code proactively notices the extension and prompts the user via its own toast.
1766
+
1767
+ ### Migration notes
1768
+ - Re-run the curl install once. The new installer will package a VSIX, call `code --install-extension --force`, and VS Code will show a reload toast in any open window (or auto-load on next open). No change needed in how you invoke `/claws-update` or the install curl URL.
1769
+ - Works now because Phase 2 (v0.4.0) moved `node-pty` from `node_modules/` to `native/node-pty/` — `vsce package` used to strip `node_modules/` and break the runtime load, but `.vscodeignore` allows `!native/**` through so the VSIX now contains the ABI-correct binary.
1770
+
1771
+ ## [0.5.2] - 2026-04-18
1772
+
1773
+ ### Fixed
1774
+ - **Stale-clone bug (critical)** — The installer's `git pull --ff-only --quiet || warn` allowed a failed fetch (dirty tree, diverged history, network hiccup) to fall through to "✓ updated" without actually updating `~/.claws-src/`. Users ended up running the installer against stale source and seeing banners like "Terminal Control Bridge v0.4.0 — installed" even though main was at v0.5.1. Replaced with `git fetch origin main && git reset --hard origin/main`, with an explicit SHA-transition log line (`✓ already at origin/main (abc1234)` or `✓ updated abc1234 → def5678`).
1775
+ - **Stale-version detection in step 2b** — The installer now compares the extension's actual `package.json` version against an `EXPECTED_MIN_VERSION` pinned at script-release time. If the working tree is older than what this installer expects, the installer prints a loud warning with the recovery command (`rm -rf ~/.claws-src && re-run`).
1776
+ - **Installer failure modes made explicit** — `git fetch` failing now prints a concrete diagnostic suggesting offline/diverged causes; `git reset --hard` failing prints a clean-slate recovery command and exits rather than continuing with broken state.
1777
+
1778
+ ### Migration notes
1779
+ - This is a transparent upgrade — users on v0.5.0 / v0.5.1 just need to re-run the curl install command. The new installer force-resets their `~/.claws-src/` to match origin/main.
1780
+ - If your `~/.claws-src/` had local edits (unlikely but possible), they'll be lost by the reset. `~/.claws-src/` is not meant to be edited by hand; do development in a separate clone.
1781
+
1782
+ ## [0.5.1] - 2026-04-18
1783
+
1784
+ ### Added
1785
+ - **Extension copy in every project** — `install.sh` now copies the built VS Code extension (`dist/`, `native/`, `package.json`, `README`, `CHANGELOG`) into `<project>/.claws-bin/extension/` on every install and update. Purely for visibility — VS Code still loads the extension from the user-level install at `~/.vscode/extensions/neunaha.claws-<version>`, not from this copy. Size: ~300–400 KB per project. Opt out with `CLAWS_SKIP_EXTENSION_COPY=1`.
1786
+ - **`.claws-bin/README.md`** — auto-generated in every project. Documents what each file in `.claws-bin/` does, explains why the extension lives at user-scope (VS Code design), provides gitignore guidance, and includes install + update curl URLs for teammates.
1787
+ - **Verify step** now reports the presence of the project-local extension copy + the `README.md`.
1788
+
1789
+ ### Changed
1790
+ - **End-of-install banner rewritten** — the post-install instructions are now a single action: **Reload VS Code**. The Claude Code restart step is no longer called out as a separate required action; new `claude` sessions auto-pick-up `.mcp.json` without manual restart (only users mid-session in a pre-install Claude Code need to restart, which is their natural lifecycle anyway).
1791
+ - Banner now prints the exact extension symlink path (`~/.vscode/extensions/neunaha.claws-<version>`) AND the project-local visible copy path, so users can see both where VS Code loads from and where the files live in their project.
1792
+
1793
+ ### Migration notes from v0.5.0
1794
+ - No code changes required. Next `/claws-update` automatically gets the extension copy and README. Existing project files (`.mcp.json`, `.claude/`, `CLAUDE.md`) are untouched.
1795
+ - If you want to opt out of the extension copy (disk-sensitive projects, etc.): set `CLAWS_SKIP_EXTENSION_COPY=1` before running install/update. The extension still installs at user-scope.
1796
+ - The new files are safe to commit (~300 KB total) OR gitignore — see `<project>/.claws-bin/README.md` for guidance.
1797
+
1798
+ ## [0.5.0] - 2026-04-18
1799
+
1800
+ ### Architecture
1801
+ - **Phase 6 hardening sweep** — server, core modules, and extension polish landed in two passes (6A + 6B). Net result: 57 automated checks (up from 22 in v0.4.0), full async deactivate lifecycle, runtime-readable server config, stable UUID-based profile adoption, and a marketplace-ready command surface.
1802
+ - **`server-config.ts` provider pattern** — the socket server no longer holds hard-coded values for exec-timeout / poll-limit. Extension-level code passes a `ServerConfigProvider` closure that reads live from `vscode.workspace.getConfiguration('claws')` on every request, so `settings.json` edits take effect without a window reload.
1803
+ - **`IntrospectProvider` pattern** — the new `introspect` protocol command is powered by a provider passed into the server, keeping `server.ts` free of any direct `vscode` import. One snapshot shape is consumed by both the CLI-via-socket path and the in-UI Health Check command.
1804
+ - **UUID-based profile adoption** — wrapped terminals spawned via the `+` dropdown now embed a crypto-random UUID token in the terminal name (visible as `Claws Wrapped N · abcd1234 [full-uuid]`). Match-on-open is by UUID, not numeric id, eliminating the race where two simultaneous profile provisions could bind to each other's PTY.
1805
+
1806
+ ### Added
1807
+ - **`introspect` socket command** — returns `extensionVersion`, `nodeVersion`, `electronAbi`, `platform`, `nodePty: { loaded, loadedFrom, error }`, `servers: [{ workspace, socket }]`, `terminals`, `uptime_ms`. Feeds both the MCP client diagnostics and the in-UI Health Check.
1808
+ - **`Claws: Uninstall Cleanup` command** — scans open workspace folders, inventories Claws-installed files (`.mcp.json` claws entry, `.claws-bin/`, `.claude/commands/claws-*.md`, skill directories, `.vscode/extensions.json` recommendations, `CLAUDE.md` fenced block), shows a per-folder confirmation, removes only what was actually installed, and writes a summary to the Output channel. Reversible-by-git, destructive-outside-git — modal warning before every removal.
1809
+ - **Status bar item** — right-aligned, priority 100, shows `$(terminal) Claws (N)` where N is the live terminal count. Tooltip is a rich `MarkdownString` with socket list, node-pty status, and version. Click → Health Check. Color shifts to warning-yellow in pipe-mode, error-red when no server is running. Auto-refreshes every 30s via `unref`'d interval.
1810
+ - **Command palette `Claws:` grouping** — every contributed command now has an explicit `"category": "Claws"`, so the palette renders them as one cluster.
1811
+ - **Keybindings** (chord, non-intrusive): `ctrl+alt+c h` / `cmd+alt+c h` → Health Check; `ctrl+alt+c l` / `cmd+alt+c l` → Show Log; `ctrl+alt+c s` / `cmd+alt+c s` → Show Status.
1812
+ - **`claws.statusBar` command** — manual refresh + re-show hook for the status bar item; useful after a theme swap or a window focus cycle.
1813
+ - **Version-mismatch detection** — when a client request includes `clientVersion`, the server compares against the running extension version and logs a one-shot warning to the Output channel on drift ≥ 1 minor release. MCP server version is also displayed in the Health Check by reading `<workspace>/.claws-bin/package.json` or parsing a `version: 'x.y.z'` literal from the MCP source.
1814
+ - **`onCommand:` activationEvents** — `claws.healthCheck`, `claws.showLog`, `claws.status`, `claws.statusBar`, `claws.listTerminals`, `claws.rebuildPty`, `claws.uninstallCleanup` are all registered as activation triggers alongside `onStartupFinished`, so users can invoke diagnostic commands even if the startup activation was skipped.
1815
+ - **Two new test suites** — `test/profile-provider.test.js` (6 checks: provider registration, UUID match-on-open, concurrent provision safety, socket-visible adoption) and `test/multi-connection.test.js` (8 checks: 3 concurrent connections × 3 interleaved requests, per-connection rid correlation, introspect shape). Run via `npm run test:profile` and `npm run test:multiconn`.
1816
+ - **Phase 6A checks** (already landed, recapped here for completeness): oversized-line defense + fresh-connection-still-alive probe, capture-store ring-buffer trim + stripAnsi coverage, config hot-reload, pty lifecycle (`mode`, `hasOpened`, `ageMs`, sanitizeEnv), orphan-PTY scan timer in `TerminalManager.dispose()`, and protocol-tag rejection.
1817
+
1818
+ ### Changed
1819
+ - **`displayName`** bumped from `"Claws — Terminal Control Bridge"` to `"Claws: Programmable Terminal Bridge"`. Clearer marketplace positioning; leads with the outcome ("programmable") rather than the mechanism ("bridge").
1820
+ - **`claws.status`** emits a markdown-style status block with section headers (`# Claws Status`, `## Sockets`, `## Runtime`) instead of a single-line dump. Renders well in the Output channel and copies cleanly into bug reports.
1821
+ - **`claws.listTerminals`** now opens a VS Code QuickPick with each terminal as a selectable item (`id · name · wrapped(pty)/unwrapped · pid`). Selecting an item calls `terminal.show()` on it. Falls through to an info message when no terminals are open.
1822
+ - **`deactivate()` is now async** — returns `Promise<void>`. Stops every server in the `servers` Map, calls `TerminalManager.dispose()` to clear the orphan-PTY scan timer, disposes every pending profile PTY, disposes the status bar item and its refresh timer, disposes the Output channel, and logs a final state line (`N/M sockets closed`). Wrapped in a `Promise.race` with a 3-second ceiling so a slow dispose can't hang VS Code shutdown.
1823
+ - **Extension `version`** bumped to `0.5.0`. Root `package.json` (`claws-cli`) and `mcp_server.js` `serverInfo.version` also bumped to `0.5.0` for parity.
1824
+
1825
+ ### Fixed
1826
+ - **#6 — createWrapped vs profile-provider name collision.** Name-based match-on-open was brittle when two provisions ran concurrently (both could use "Claws Wrapped 3" before the id increment landed). Now every pending profile carries a UUID token in its name; `onDidOpenTerminal` matches by full-UUID substring. The orphan-timeout path is preserved.
1827
+ - **#13/#14 — unwired `ServerOptions.getConfig`.** Phase 6A shipped the hook but the extension never passed a value in. v0.5.0 wires it to `cfg('execTimeoutMs', …)` / `cfg('pollLimit', …)`.
1828
+ - Test mocks updated to cover the new `vscode.window.createStatusBarItem`, `MarkdownString`, `ThemeColor`, and `StatusBarAlignment` surface area. Existing tests continue to pass against both sync and async `deactivate()` call shapes.
1829
+
1830
+ ### Deprecated
1831
+ - Nothing newly deprecated in 0.5.0. The 0.4.0-era deprecations (`scripts/terminal-wrapper.sh`, `extension/src/extension.js`) remain — both are scheduled for removal once the Pseudoterminal path has been marketplace-published.
1832
+
1833
+ ### Migration notes for v0.4 users
1834
+ - The new `Claws: Uninstall Cleanup` command is OPT-IN — it never runs automatically. It's safe to ignore unless you're actually removing Claws from a project.
1835
+ - Keybindings are added; if you already have `ctrl+alt+c`-prefixed chords bound to something else, VS Code will surface the conflict in `Keyboard Shortcuts`. Override ours there; the extension will still work without them.
1836
+ - The status bar item is visible by default. To hide it, right-click the status bar and uncheck "Claws".
1837
+ - If you were consuming `deactivate()` externally (unit tests, harness scripts), it now returns a Promise. `await ext.deactivate()` is the correct invocation. Calling without `await` still works but the 100ms sleep you may have used to drain teardown is now strictly unnecessary.
1838
+ - `claws.listTerminals` used to dump to the Output channel; it now opens a QuickPick. If you had a keybinding or macro that expected Output-channel output, use the new `claws.status` which still renders a textual block.
1839
+
1840
+ ## [0.4.0] - 2026-04-18
1841
+
1842
+ ### Architecture
1843
+ - **Extension rewritten in TypeScript** — 8 modular files (`extension.ts`, `server.ts`, `terminal-manager.ts`, `claws-pty.ts`, `capture-store.ts`, `protocol.ts`, `safety.ts`, `ansi-strip.ts`), strict mode, esbuild bundle → `dist/extension.js`.
1844
+ - **Pseudoterminal replaces `script(1)` wrapping** — wrapped terminals now run under VS Code's native `vscode.Pseudoterminal` with `node-pty` (or `child_process` pipe-mode fallback). Fixes TUI rendering corruption in Claude Code, vim, htop, k9s, and other Ink/ncurses apps.
1845
+ - **In-memory ring buffer replaces file-tailing** for `readLog` on Pseudoterminal-backed terminals. No more `.claws/terminals/*.log` files for new wrapped terminals; the buffer is configurable via `claws.maxCaptureBytes` (default 1 MB per terminal).
1846
+
1847
+ ### Added
1848
+ - **Blocking `claws_worker` lifecycle** — one tool call runs the full worker flow: spawn wrapped terminal → optional Claude Code boot with `boot_marker` detection → send mission → poll capture buffer for `complete_marker` / `error_markers` → harvest last N lines → auto-close → return structured result with `status`, `duration_ms`, `marker_line`, `cleaned_up`, `harvest`. Configurable via `timeout_ms`, `boot_wait_ms`, `poll_interval_ms`, `harvest_lines`, `close_on_complete`. Legacy fire-and-forget behavior via `detach: true`.
1849
+ - **Project-local install** — `scripts/install.sh` now writes into the current project root as the primary target:
1850
+ - `<project>/.mcp.json` — registers Claws MCP server with relative path `./.claws-bin/mcp_server.js`
1851
+ - `<project>/.claws-bin/{mcp_server.js,shell-hook.sh}` — self-contained, no dependency on `~/.claws-src`
1852
+ - `<project>/.claude/commands/` — all 19 `claws-*` slash commands
1853
+ - `<project>/.claude/rules/claws-default-behavior.md`
1854
+ - `<project>/.claude/skills/{claws-orchestration-engine,claws-prompt-templates}/`
1855
+ - Global `~/.claude/*` install is now opt-in via `CLAWS_GLOBAL_CONFIG=1` and `CLAWS_GLOBAL_MCP=1`.
1856
+ - **Dynamic CLAUDE.md injection** — fenced with `<!-- CLAWS:BEGIN --> ... <!-- CLAWS:END -->`. Block content is generated at install time (lists actually-installed tools and slash commands). Re-install replaces only the fenced section, preserving every other line of the project's CLAUDE.md.
1857
+ - **Automatic legacy CLAUDE.md migration** — on upgrade, the installer strips the old `## CLAWS — Terminal Orchestration Active` section (v0.1–v0.3) before inserting the new fenced block. Original project content on either side of the old section is preserved.
1858
+ - **Extension test suite** — `extension/test/smoke.test.js` (5 checks: bundle load, socket server, protocol, cleanup) and `extension/test/worker.test.js` (6 checks: blocking lifecycle, marker detection, detach mode) — both run via `npm test`.
1859
+ - **Big end-of-install ASCII banner** with 3-step activation guidance (reload VS Code → restart Claude Code → `/claws-help`) and troubleshooting pointer (`/claws-fix`).
1860
+
1861
+ ### Changed
1862
+ - Extension entry point: `main` now points at `./dist/extension.js` (built from TypeScript). Legacy `./src/extension.js` is preserved as a fallback; the installer repoints `main` to it if the TypeScript build fails.
1863
+ - Install verification expanded from 4 to 10 checks; MCP handshake test uses a portable Node driver (no dependency on GNU `timeout`, works on macOS out of the box).
1864
+ - `extension/package.json` adds `devDependencies` (`typescript`, `esbuild`, `@types/vscode`, `@types/node`) and `optionalDependencies` (`node-pty`). Pure Node stdlib remains the only runtime requirement.
1865
+
1866
+ ### Deprecated
1867
+ - `scripts/terminal-wrapper.sh` — kept for v0.1–v0.3 compatibility but unused by new Pseudoterminal-backed wrapped terminals. Will be removed in v0.5.
1868
+ - `extension/src/extension.js` (legacy JS) — kept as fallback; will be removed once Pseudoterminal path is marketplace-published.
1869
+
1870
+ ### Migration notes for v0.3 users running `/claws-update`
1871
+ - Your project gets a new `.mcp.json`, `.claws-bin/`, and project-local `.claude/` — safe to commit or gitignore per your preference.
1872
+ - CLAUDE.md's legacy Claws section is automatically stripped and replaced with the new fenced block. Expect to see `CLAUDE.md legacy section migrated; Claws block inserted` during install.
1873
+ - The old global `~/.claude/settings.json` claws MCP entry remains but becomes inactive when the project-local `.mcp.json` takes precedence. Safe to leave or remove manually.
1874
+ - `claws_worker` return-text format changed. If you had automation parsing the old output (`worker 'X' spawned with Claude Code...`), it will need updating. The new format leads with `worker 'X' COMPLETED|FAILED|TIMEOUT` followed by structured fields and a `── harvest (last lines) ──` section.
1875
+
1876
+ ## [0.3.0] - 2026-04-14
1877
+
1878
+ ### Changed
1879
+ - MCP server rewritten from Python to Node.js — zero dependencies
1880
+ - Install no longer requires Python, pip, or brew
1881
+ - Shell hook commands rewritten from Python to Node.js
1882
+
1883
+ ### Removed
1884
+ - Python dependency from install path (Python client remains as optional)
1885
+
1886
+ ## [0.2.0] - 2026-04-18
1887
+
1888
+ ### Added
1889
+ - **MCP Server** — register once, every Claude Code session gets 8 terminal control tools natively
1890
+ - **Orchestration Engine skill** — 7 patterns (scout, single worker, parallel fleet, AI session driver, pipeline stages, watchdog, orchestrator with delegation)
1891
+ - **Lifecycle YAML protocol** — 8-phase terminal lifecycle (plan → spawn → deploy → observe → recover → harvest → cleanup → reflect)
1892
+ - **Prompt engineering guide** — `/claws-help` with 5 levels from beginner to power user
1893
+ - **Default behavior rule** — Claude prefers visible Claws terminals over silent Bash
1894
+ - **CLAUDE.md injection** — installer appends Claws orchestration context to project CLAUDE.md
1895
+ - **Shell hook** — every terminal shows CLAWS banner with bridge status + 4 shell commands (claws-ls, claws-new, claws-run, claws-log)
1896
+ - **Auto-launch Claude Code** — `claws_worker` auto-starts `claude --dangerously-skip-permissions` in worker terminals
1897
+ - **Click-to-copy install prompt** on landing page
1898
+ - **npx claws-cli** — Node.js CLI installer with `claude mcp add` support
1899
+ - **11 slash commands** — /claws-help, /claws-install, /claws-update, /claws-status, /claws-connect, /claws-create, /claws-send, /claws-exec, /claws-read, /claws-worker, /claws-fleet
1900
+ - **7 prompt templates** — single worker, analysis, multi-commit, pair programming, parallel fleet, graphify-driven, error recovery
1901
+ - **6 cinematic capability images** — terminal mgmt, pty capture, exec, safety gate, MCP, cross-device
1902
+ - **GitHub Pages landing page** — full website with carousels, stats, animations, case studies
1903
+ - **Cross-platform installer** — bash (macOS/Linux) + PowerShell (Windows), auto-detects VS Code/Cursor/Windsurf
1904
+ - **Live demo test script** — spawns 3 parallel workers to prove orchestration works
1905
+
1906
+ ### Fixed
1907
+ - Linux `script(1)` compatibility — auto-detects BSD vs GNU arg order
1908
+ - Shell injection in `claws-run` — commands passed via temp file, not interpolated
1909
+ - `nc -U` dependency removed — all shell commands use Python sockets
1910
+ - Install step numbering consistent [1/8] through [8/8]
1911
+ - MCP server tilde path warning in docs
1912
+ - Installer never exits — `set +e`, all checks are warnings not blockers
1913
+ - `pip` install uses `python -m pip` with `--break-system-packages` for macOS compatibility
1914
+
1915
+ ### Changed
1916
+ - `/claws-update` is now a full rebuild (re-runs entire installer), not just git pull
1917
+
1918
+ ## [0.1.0] - 2026-04-17
1919
+
1920
+ ### Added
1921
+ - Initial release
1922
+ - Unix socket server with newline-delimited JSON protocol
1923
+ - Terminal management: list, create, show, send, close
1924
+ - Wrapped terminals via `script(1)` for full pty capture
1925
+ - `readLog` command with ANSI stripping
1926
+ - `exec` command with file-based output capture
1927
+ - `poll` command for shell-integration event streaming
1928
+ - Safety gate: foreground process detection + warnings for non-shell TUIs
1929
+ - Bracketed paste mode for multi-line sends
1930
+ - "Claws Wrapped Terminal" dropdown profile
1931
+ - Python client library (`claws-client`)
1932
+ - Example scripts: basic orchestrator, parallel workers
1933
+
1934
+ - docs(architecture) — comprehensive ARCHITECTURE.md as the canonical anchor: charter, 10 architectural principles (event-driven only, atomic writes, hooks safety, no orchestrator-side patches, etc.), full system layer map, protocol specs, lifecycle architecture, 5-layer enforcement chain, test invariants, anti-patterns catalog (10 burned-in lessons), known gaps + roadmap, and an anchoring protocol for future PRs.
1935
+ - fix(v0.7.10) — worker-fixes-v079.test: sync scanText check to detectCompletion(scanText,opt) (replaces scanText.includes pattern removed when findStandaloneMarker was introduced); restore 17/17 pass.
1936
+ - fix(v0.7.10) — claws-v2-vehicle-state.test: add workerMode:single+expectedWorkers:1 to lifecycle.plan call and lifecycle.advance to SPAWN before create (canSpawn gate, schema v3 required fields).
1937
+ - fix(v0.7.10) — lifecycle-store.ts: add M-43 sentinel comment to flushToDisk() (fsyncSync-before-renameSync parity with M-29; fixes lifecycle-store-fsync test).
1938
+ - feat(v0.7.10) — scripts: install.sh dev-mode symlink branch (eliminates source/.claws-bin drift), F-11 .electron-abi cache, dynamic EXPECTED_MIN_VERSION, .gitignore auto-additions, backup pruning; install.ps1 stub directs Windows to WSL2; update.sh Linux Electron ABI detection + BUG-28 health check; uninstall.sh idempotent 5-step removal.
1939
+ - feat(v0.7.10) — templates: CLAUDE.global.md + CLAUDE.project.md add capabilities:[push] requirement (BUG-03), worker.<peerId>.heartbeat correction (BUG-06), Sidecar mandatory section, Monitor mandatory section.
1940
+ - docs(v0.7.10) — commands + skills aligned with non-blocking defaults: claws-army/fleet/wave-lead/worker.md document fire/poll/audit pattern; claws-orchestration-engine SKILL upgraded to 10-phase ring; wave-lead/wave-subworker SKILLs add Monitor arm step 0 + BUG-03/06/10 fixes. New /claws-install command. New claws-prompt-templates SKILL library. New .claude/rules/ dir. README tool count synced 14→38.
1941
+ - fix(v0.7.10) — test fixture sync: lifecycle-server.test now expects [SESSION-BOOT, PLAN]; reverse-channel.test calls lifecycle.advance to SPAWN; server-validation.test expects payload:invalid; worker-fixes-v079.test regex syncs for detach !== false + findStandaloneMarker. Removes unused Phase import (lifecycle-engine.ts) and dead _requireCapability method (server.ts).
1942
+ - test(v0.7.10) — non-blocking-defaults (10 checks: detach !== false default, withMaxHold 8s ceiling) and sidecar-enforcement (18 checks: _ensureSidecarOrThrow guards all 4 spawn handlers, singleton dedup, read-only handlers ungated). Both pass.
1943
+
1944
+ - **chore(v0.7.10)**: Wave C precursor work landed — pre-tool-use hook fail-closed (BUG-16 argv0 allowlist, BUG-27 mcp_server.js edit gate, BUG-28 spawn-class Monitor arm gate); session-start spawns sidecar + emits "FIRST ACTION arm Monitor" reminder; stop hook kills sidecar + orphan tails + grace file; inject-settings-hooks.js registers four explicit per-tool PreToolUse matchers (claws_create/worker/fleet/dispatch_subworker) as belt-and-suspenders.
1945
+ - **test(v0.7.10)**: Two new regression suites — non-blocking-defaults (10 checks: detach !== false default, withMaxHold 8s ceiling) and sidecar-enforcement (18 checks: _ensureSidecarOrThrow guards all 4 spawn handlers, singleton dedup, read-only handlers ungated). Both pass.
1946
+ - **fix(v0.7.10)**: Test fixture sync — lifecycle-server now expects [SESSION-BOOT, PLAN]; reverse-channel calls lifecycle.advance to SPAWN; server-validation expects payload:invalid (not envelope:invalid); worker-fixes-v079 regex updates for detach !== false + findStandaloneMarker. Removes unused Phase import from lifecycle-engine.ts and dead _requireCapability method from server.ts.
1947
+ - **docs(v0.7.10)**: Slash commands + skills aligned with non-blocking defaults — claws-army/fleet/wave-lead/worker.md document fire/poll/audit pattern; claws-orchestration-engine SKILL upgraded to 10-phase ring with mode-fork spawn table; wave-lead/wave-subworker SKILLs add Monitor arm step 0, BUG-03 capabilities:[\'push\'] workaround, BUG-06 worker.<peerId>.heartbeat fix, BUG-10 drain-and-wait LEAD harvest. New /claws-install command. New claws-prompt-templates SKILL library. README tool count synced 14→38; uninstall section rewritten; Windows section directs to WSL2.
1948
+ - **feat(v0.7.10)**: Templates updated — CLAUDE.global.md + CLAUDE.project.md add capabilities:[\'push\'] requirement (BUG-03), worker.<peerId>.heartbeat correction (BUG-06), Sidecar mandatory section, Monitor mandatory section with lifecycle-bound description pattern.
1949
+ - **feat(v0.7.10)**: Scripts — install.sh dev-mode symlink branch (eliminates source↔.claws-bin drift), F-11 .electron-abi cache, dynamic EXPECTED_MIN_VERSION from package.json, .gitignore auto-additions, backup pruning (3 most recent); install.ps1 stub directs Windows to WSL2; update.sh adds Linux Electron ABI detection + BUG-28 health check; uninstall.sh idempotent 5-step removal.