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.
- package/.claude/commands/claws-auto.md +90 -0
- package/.claude/commands/claws-bin.md +28 -0
- package/.claude/commands/claws-cleanup.md +28 -0
- package/.claude/commands/claws-do.md +82 -0
- package/.claude/commands/claws-fix.md +40 -0
- package/.claude/commands/claws-goal.md +111 -0
- package/.claude/commands/claws-help.md +54 -0
- package/.claude/commands/claws-plan.md +103 -0
- package/.claude/commands/claws-report.md +29 -0
- package/.claude/commands/claws-status.md +37 -0
- package/.claude/commands/claws-update.md +32 -0
- package/.claude/commands/claws.md +64 -0
- package/.claude/rules/claws-default-behavior.md +76 -0
- package/.claude/settings.json +112 -0
- package/.claude/settings.local.json +19 -0
- package/.claude/skills/claws-auto-engine/SKILL.md +97 -0
- package/.claude/skills/claws-goal-tracker/SKILL.md +106 -0
- package/.claude/skills/claws-prompt-templates/SKILL.md +203 -0
- package/.claude/skills/claws-wave-lead/SKILL.md +126 -0
- package/.claude/skills/claws-wave-subworker/SKILL.md +60 -0
- package/CHANGELOG.md +1949 -0
- package/LICENSE +21 -0
- package/README.md +420 -0
- package/bin/cli.js +84 -0
- package/cli.js +223 -0
- package/docs/ARCHITECTURE.md +511 -0
- package/docs/event-protocol.md +588 -0
- package/docs/features.md +562 -0
- package/docs/guide.md +891 -0
- package/docs/index.html +716 -0
- package/docs/protocol.md +323 -0
- package/extension/.vscodeignore +15 -0
- package/extension/CHANGELOG.md +1906 -0
- package/extension/LICENSE +21 -0
- package/extension/README.md +137 -0
- package/extension/docs/features.md +424 -0
- package/extension/docs/protocol.md +197 -0
- package/extension/esbuild.mjs +25 -0
- package/extension/icon.png +0 -0
- package/extension/native/.metadata.json +10 -0
- package/extension/native/node-pty/LICENSE +69 -0
- package/extension/native/node-pty/README.md +165 -0
- package/extension/native/node-pty/lib/conpty_console_list_agent.js +16 -0
- package/extension/native/node-pty/lib/conpty_console_list_agent.js.map +1 -0
- package/extension/native/node-pty/lib/eventEmitter2.js +47 -0
- package/extension/native/node-pty/lib/eventEmitter2.js.map +1 -0
- package/extension/native/node-pty/lib/index.js +52 -0
- package/extension/native/node-pty/lib/index.js.map +1 -0
- package/extension/native/node-pty/lib/interfaces.js +7 -0
- package/extension/native/node-pty/lib/interfaces.js.map +1 -0
- package/extension/native/node-pty/lib/shared/conout.js +11 -0
- package/extension/native/node-pty/lib/shared/conout.js.map +1 -0
- package/extension/native/node-pty/lib/terminal.js +190 -0
- package/extension/native/node-pty/lib/terminal.js.map +1 -0
- package/extension/native/node-pty/lib/types.js +7 -0
- package/extension/native/node-pty/lib/types.js.map +1 -0
- package/extension/native/node-pty/lib/unixTerminal.js +346 -0
- package/extension/native/node-pty/lib/unixTerminal.js.map +1 -0
- package/extension/native/node-pty/lib/utils.js +39 -0
- package/extension/native/node-pty/lib/utils.js.map +1 -0
- package/extension/native/node-pty/lib/windowsConoutConnection.js +125 -0
- package/extension/native/node-pty/lib/windowsConoutConnection.js.map +1 -0
- package/extension/native/node-pty/lib/windowsPtyAgent.js +320 -0
- package/extension/native/node-pty/lib/windowsPtyAgent.js.map +1 -0
- package/extension/native/node-pty/lib/windowsTerminal.js +199 -0
- package/extension/native/node-pty/lib/windowsTerminal.js.map +1 -0
- package/extension/native/node-pty/lib/worker/conoutSocketWorker.js +22 -0
- package/extension/native/node-pty/lib/worker/conoutSocketWorker.js.map +1 -0
- package/extension/native/node-pty/package.json +64 -0
- package/extension/native/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
- package/extension/native/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
- package/extension/native/node-pty/prebuilds/darwin-x64/pty.node +0 -0
- package/extension/native/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/pty.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/winpty-agent.exe +0 -0
- package/extension/native/node-pty/prebuilds/win32-arm64/winpty.dll +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/conpty.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/pty.node +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/winpty-agent.exe +0 -0
- package/extension/native/node-pty/prebuilds/win32-x64/winpty.dll +0 -0
- package/extension/package-lock.json +605 -0
- package/extension/package.json +343 -0
- package/extension/scripts/bundle-native.mjs +104 -0
- package/extension/scripts/deploy-dev.mjs +60 -0
- package/extension/src/ansi-strip.ts +52 -0
- package/extension/src/backends/vscode/claws-pty.ts +483 -0
- package/extension/src/backends/vscode/status-bar.ts +99 -0
- package/extension/src/backends/vscode/vscode-backend.ts +282 -0
- package/extension/src/capture-store.ts +125 -0
- package/extension/src/event-log.ts +629 -0
- package/extension/src/event-schemas.ts +478 -0
- package/extension/src/extension.js +492 -0
- package/extension/src/extension.ts +873 -0
- package/extension/src/lifecycle-engine.ts +60 -0
- package/extension/src/lifecycle-rules.ts +171 -0
- package/extension/src/lifecycle-store.ts +506 -0
- package/extension/src/peer-registry.ts +176 -0
- package/extension/src/pipeline-registry.ts +82 -0
- package/extension/src/platform.ts +64 -0
- package/extension/src/protocol.ts +532 -0
- package/extension/src/server-config.ts +98 -0
- package/extension/src/server.ts +2210 -0
- package/extension/src/task-registry.ts +51 -0
- package/extension/src/terminal-backend.ts +211 -0
- package/extension/src/terminal-manager.ts +395 -0
- package/extension/src/topic-registry.ts +70 -0
- package/extension/src/topic-utils.ts +46 -0
- package/extension/src/transport.ts +45 -0
- package/extension/src/uninstall-cleanup.ts +232 -0
- package/extension/src/wave-registry.ts +314 -0
- package/extension/src/websocket-transport.ts +153 -0
- package/extension/tsconfig.json +23 -0
- package/lib/capabilities.js +145 -0
- package/lib/dry-run.js +43 -0
- package/lib/install.js +1018 -0
- package/lib/mcp-setup.js +92 -0
- package/lib/platform.js +240 -0
- package/lib/preflight.js +152 -0
- package/lib/shell-hook.js +343 -0
- package/lib/uninstall.js +162 -0
- package/lib/verify.js +166 -0
- package/mcp_server.js +3529 -0
- package/package.json +48 -0
- package/rules/claws-default-behavior.md +72 -0
- package/scripts/_helpers/atomic-file.mjs +137 -0
- package/scripts/_helpers/fix-repair.js +64 -0
- package/scripts/_helpers/json-safe.mjs +218 -0
- package/scripts/bump-version.sh +84 -0
- package/scripts/codegen/gen-docs.mjs +61 -0
- package/scripts/codegen/gen-json-schema.mjs +62 -0
- package/scripts/codegen/gen-mcp-tools.mjs +358 -0
- package/scripts/codegen/gen-types.mjs +172 -0
- package/scripts/codegen/index.mjs +42 -0
- package/scripts/dev-hooks/check-extension-dirs.js +77 -0
- package/scripts/dev-hooks/check-open-claws-terminals.js +70 -0
- package/scripts/dev-hooks/check-stale-main.js +55 -0
- package/scripts/dev-hooks/check-tag-pushed.js +51 -0
- package/scripts/dev-hooks/check-tag-vs-main.js +56 -0
- package/scripts/dev-vsix-install.sh +60 -0
- package/scripts/fix.sh +702 -0
- package/scripts/gen-client-types.mjs +81 -0
- package/scripts/git-hooks/pre-commit +31 -0
- package/scripts/hooks/lifecycle-state.js +61 -0
- package/scripts/hooks/package.json +4 -0
- package/scripts/hooks/post-tool-use-claws.js +292 -0
- package/scripts/hooks/pre-bash-no-verify-block.js +72 -0
- package/scripts/hooks/pre-tool-use-claws.js +206 -0
- package/scripts/hooks/session-start-claws.js +97 -0
- package/scripts/hooks/stop-claws.js +88 -0
- package/scripts/inject-claude-md.js +205 -0
- package/scripts/inject-dev-hooks.js +96 -0
- package/scripts/inject-global-claude-md.js +140 -0
- package/scripts/inject-settings-hooks.js +370 -0
- package/scripts/install.ps1 +146 -0
- package/scripts/install.sh +1729 -0
- package/scripts/monitor-arm-watch.js +155 -0
- package/scripts/rebuild-node-pty.sh +245 -0
- package/scripts/report.sh +232 -0
- package/scripts/shell-hook.fish +164 -0
- package/scripts/shell-hook.ps1 +33 -0
- package/scripts/shell-hook.sh +232 -0
- package/scripts/stream-events.js +399 -0
- package/scripts/terminal-wrapper.sh +36 -0
- package/scripts/test-enforcement.sh +132 -0
- package/scripts/test-install.sh +174 -0
- package/scripts/test-installer-parity.sh +135 -0
- package/scripts/test-template-enforcement.sh +76 -0
- package/scripts/uninstall.sh +143 -0
- package/scripts/update.sh +337 -0
- package/scripts/verify-release.sh +323 -0
- package/scripts/verify-wrapped.sh +194 -0
- package/templates/CLAUDE.global.md +135 -0
- package/templates/CLAUDE.project.md +37 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
# Claws — Architecture Reference
|
|
2
|
+
|
|
3
|
+
> **Status**: This document is the canonical architectural anchor for Claws. Every implementation choice — including bug fixes — must be checked against the principles and component contracts here. If a fix would violate something in this doc, fix the doc OR change the approach. Never silently drift.
|
|
4
|
+
>
|
|
5
|
+
> **Version**: v0.7.10 baseline (May 2026). Updated on architectural changes only.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## I. The Charter — what Claws is
|
|
10
|
+
|
|
11
|
+
Claws is a **bridge** that turns every VS Code integrated terminal into a programmable, observable, controllable endpoint. External processes (AI orchestrators, automation, CI) connect over a local socket and orchestrate terminals end-to-end.
|
|
12
|
+
|
|
13
|
+
**Claws IS:**
|
|
14
|
+
- A VS Code extension that owns terminal identity, pty capture, and a JSON-over-socket protocol.
|
|
15
|
+
- An MCP server that exposes that protocol as tools to Claude Code (and other MCP clients).
|
|
16
|
+
- A pub/sub event bus (claws/2) that lets orchestrators and workers communicate as peers.
|
|
17
|
+
- A 10-phase lifecycle state machine that orchestrates multi-terminal missions.
|
|
18
|
+
- A 5-layer enforcement chain that makes the protocol non-optional in practice.
|
|
19
|
+
|
|
20
|
+
**Claws IS NOT:**
|
|
21
|
+
- A terminal emulator (VS Code already is one).
|
|
22
|
+
- A remote shell (SSH already does that).
|
|
23
|
+
- A multiplexer (tmux already does that).
|
|
24
|
+
- A collaboration tool (Live Share already does that).
|
|
25
|
+
- A polling/heuristic completion detector. **Completion is event-driven, period.**
|
|
26
|
+
|
|
27
|
+
The smallest surface area that enables AI-driven multi-terminal orchestration with strong contracts.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## II. Architectural Principles (Non-Negotiable)
|
|
32
|
+
|
|
33
|
+
Each principle has a **rationale** (why) and an **enforcement** (where the protocol holds the line). If a code change violates a principle, the change is wrong — not the principle.
|
|
34
|
+
|
|
35
|
+
### P1 — Event-driven, never polling
|
|
36
|
+
Completion, state changes, worker progress — all surface as bus events. Never `sleep + check`, never idle-timeout heuristics, never "give up after N seconds and assume done."
|
|
37
|
+
- **Rationale**: Polling is fragile (race conditions, false positives), it burns budget, and it makes the system silent during the only periods that matter (long-running real work). The pub/sub bus exists precisely so observation is event-sourced.
|
|
38
|
+
- **Enforcement**: `[CLAWS_PUB]` line scanner publishes worker self-reported events; `system.worker.completed` emitted by the watcher only on explicit signals (marker / error / pub-complete); VS Code `onDidCloseTerminal` becomes `system.worker.terminated` (planned).
|
|
39
|
+
- **Anti-pattern**: idle-timeout in `detectCompletion` (introduced in Task #58, ripped out in v0.7.10 — see anti-patterns catalog).
|
|
40
|
+
|
|
41
|
+
### P2 — Wrapped terminals or it doesn't count
|
|
42
|
+
Anything Claws creates is `wrapped: true` (under `script(1)`) so the full pty stream is captured to a log. Unwrapped terminals are user-owned and Claws never touches them.
|
|
43
|
+
- **Rationale**: VS Code shell-integration is unreliable for TUIs (Claude, vim, REPLs). pty capture is the only reliable observability.
|
|
44
|
+
- **Enforcement**: `claws_create wrapped=true` is the default contract. `readLog` rejects unwrapped terminals.
|
|
45
|
+
|
|
46
|
+
### P3 — Non-blocking by default for spawn-class tools
|
|
47
|
+
`claws_worker` / `claws_fleet` / `claws_dispatch_subworker` spawn terminals and return `terminal_ids` within seconds. Orchestrators poll completion via `claws_workers_wait`, the bus, or `.local/audits/` files.
|
|
48
|
+
- **Rationale**: MCP stdio cannot safely hold a response open for more than a few seconds. Blocking the socket stalls the entire orchestrator session.
|
|
49
|
+
- **Enforcement**: `detach` defaults to `true` in v0.7.10. Blocking modes (`wait:true` / `detach:false`) are explicit opt-in and flagged unsafe.
|
|
50
|
+
|
|
51
|
+
### P4 — Atomic writes for every persisted file
|
|
52
|
+
`.claws/lifecycle-state.json`, `~/.claude/settings.json`, `CLAUDE.md` (CLAWS:BEGIN block), event log segments — all use temp-file + fsync + rename.
|
|
53
|
+
- **Rationale**: Partial writes during crash, reload, or concurrent install corrupt the install permanently and silently.
|
|
54
|
+
- **Enforcement**: `json-safe.mjs` + `atomic-file.mjs` helpers; fsync test (`test:lifecycle-store-fsync`); exclusive lock for settings (`test:inject-settings-exclusive-lock`).
|
|
55
|
+
|
|
56
|
+
### P5 — Hooks safety over hook completeness
|
|
57
|
+
Hooks may never crash, hang, or exit non-zero except for an intentional, documented deny. A buggy hook must degrade silently.
|
|
58
|
+
- **Rationale**: A flaky hook breaks every Claude Code session machine-wide. Worse than a missing hook.
|
|
59
|
+
- **Enforcement**: Every hook has a 5s self-kill `setTimeout(...).unref()`, every block is wrapped in try/catch, errors only print when `CLAWS_DEBUG=1`. Verified by `test:hook-stdin-safety`, `test:hook-misfire-log`, `test:hook-debug-visibility`.
|
|
60
|
+
|
|
61
|
+
### P6 — One commit, one concern; no `--no-verify`
|
|
62
|
+
Pre-commit hooks (test suite + CHANGELOG update) are mandatory. Bypassing them is not allowed even when the bypass would be "convenient."
|
|
63
|
+
- **Rationale**: Every bypass becomes load-bearing. The hook exists because something broke last time.
|
|
64
|
+
- **Enforcement**: Memory rule + worker mission preambles. Every worker that needs to commit gets reminded explicitly.
|
|
65
|
+
|
|
66
|
+
### P7 — File-referrer missions are forbidden
|
|
67
|
+
Worker missions must be inline in the `mission:` arg. Never `Read /path/to/mission.md and execute it`.
|
|
68
|
+
- **Rationale**: v0.7.9 introduced file-referrer to handle multi-line missions. v0.7.10 (commit 70ec1b1) ripped it out — direct prompts are simpler, debuggable, and don't leave temp files behind.
|
|
69
|
+
- **Enforcement**: Documented in memory + `templates/CLAUDE.project.md`. Future Wave C should add a hook that detects "Read .*mission.*md" patterns and fails.
|
|
70
|
+
|
|
71
|
+
### P8 — Direct edits to `mcp_server.js` from the orchestrator are forbidden
|
|
72
|
+
The orchestrator dispatches a worker for every change to `mcp_server.js`. Workers run with `CLAWS_WORKER=1` and bypass the gate.
|
|
73
|
+
- **Rationale**: `mcp_server.js` is the contract surface. Inline orchestrator patches accumulate untested, ungated, and untraceable churn.
|
|
74
|
+
- **Enforcement**: `pre-tool-use-claws.js` PreToolUse hook hard-blocks Edit/Write to `mcp_server.js` from the orchestrator (verified live).
|
|
75
|
+
|
|
76
|
+
### P9 — Monitor primitive: bus-stream subscription, not file polling
|
|
77
|
+
Orchestrators arm Monitors via `Monitor + scripts/stream-events.js | grep --line-buffered` — bus subscription with sub-100ms latency. Never `tail -F file | grep` (anti-pattern: dies via SIGURG within ~30s of inactivity).
|
|
78
|
+
- **Rationale**: `tail -F | grep` is a passive idle wait; Claude Code's background-process supervisor SIGURG-kills it. `stream-events.js` emits constantly (heartbeats, system metrics, every event) — it never goes idle, never gets killed, and each push frame becomes one Monitor notification.
|
|
79
|
+
- **Enforcement**: CLAUDE.md principle #5 documents canonical pattern. Spawn-class tool responses include `monitor_arm_command` in the canonical form. Wave C (TODO) will make the PreToolUse hook recognize the bus-stream pattern and stop demanding the deprecated `tail -F` satisfier.
|
|
80
|
+
|
|
81
|
+
### P10 — Lifecycle gates are server-enforced, not honor-based
|
|
82
|
+
Phase transitions, worker capacity, terminal-must-be-closed-before-REFLECT — all enforced by the lifecycle store before the relevant operation lands. Orchestrators cannot skip phases.
|
|
83
|
+
- **Rationale**: Pre-v0.7.10 was an 8-phase honor system. Orchestrators routinely skipped CLEANUP or REFLECT. The v0.7.10 store + engine + rules close that.
|
|
84
|
+
- **Enforcement**: `canSpawn` (gates `claws_create`), `canCleanup` / `canReflect` / `canEndSession` (gate transitions), `nextAutoPhase` (engine cascades). Hook-side enforcement (Wave C TODO): PostToolUse verifies monitor registered within 5s; Stop hook blocks exit until phase ∈ {REFLECT, SESSION-END}.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## III. System Layers
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
┌─ Slash commands + skills (.claude/) ─────────────────────────────┐
|
|
92
|
+
│ /claws-do, /claws-go, /claws-worker, /claws-fleet, … │
|
|
93
|
+
│ skills/claws-prompt-templates/SKILL.md │
|
|
94
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
95
|
+
↓
|
|
96
|
+
┌─ Templates + injectors (templates/, scripts/inject-*) ───────────┐
|
|
97
|
+
│ CLAUDE.global.md → ~/.claude/CLAUDE.md │
|
|
98
|
+
│ CLAUDE.project.md → <project>/CLAUDE.md (CLAWS:BEGIN block) │
|
|
99
|
+
│ inject-settings-hooks.js → ~/.claude/settings.json │
|
|
100
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
101
|
+
↓
|
|
102
|
+
┌─ Hooks (scripts/hooks/) ─────────────────────────────────────────┐
|
|
103
|
+
│ session-start-claws.js → reminder + sidecar spawn │
|
|
104
|
+
│ pre-tool-use-claws.js → spawn-gate + Bash-pattern + edit-gate │
|
|
105
|
+
│ stop-claws.js → cleanup + REFLECT reminder │
|
|
106
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
107
|
+
↓
|
|
108
|
+
┌─ MCP server (mcp_server.js) — 38 tools ──────────────────────────┐
|
|
109
|
+
│ Per-call socket (claws/1): list, create, send, exec, readLog, │
|
|
110
|
+
│ close, lifecycle.* │
|
|
111
|
+
│ Persistent socket (claws/2): hello, publish, subscribe, │
|
|
112
|
+
│ broadcast, task.*, drain_events │
|
|
113
|
+
│ Worker orchestration: claws_worker, claws_fleet, │
|
|
114
|
+
│ claws_dispatch_subworker (with detach watchers) │
|
|
115
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
116
|
+
↓
|
|
117
|
+
┌─ stream-events.js sidecar ───────────────────────────────────────┐
|
|
118
|
+
│ Single persistent claws/2 connection; subscribes to **; │
|
|
119
|
+
│ pipes every push frame to .claws/events.log (one line each). │
|
|
120
|
+
│ Spawned by SessionStart hook, killed by Stop hook. │
|
|
121
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
122
|
+
↓
|
|
123
|
+
┌─ VS Code extension (extension/src/, TypeScript) ─────────────────┐
|
|
124
|
+
│ ClawsServer (Unix socket, claws/1 + claws/2 routing) │
|
|
125
|
+
│ TerminalManager + ClawsPty (wrapped pty via node-pty) │
|
|
126
|
+
│ CaptureStore (per-terminal ring buffer with ANSI strip) │
|
|
127
|
+
│ PeerRegistry + TaskRegistry + WaveRegistry │
|
|
128
|
+
│ LifecycleStore + LifecycleRules + LifecycleEngine │
|
|
129
|
+
│ EventLog (append-only segmented JSONL with rotation) │
|
|
130
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### A. VS Code Extension (`extension/src/`)
|
|
134
|
+
|
|
135
|
+
22 TypeScript files, ~7,000 lines. Strict mode, zero npm runtime deps (only stdlib + VS Code API + bundled `node-pty`).
|
|
136
|
+
|
|
137
|
+
| File | Lines | Purpose |
|
|
138
|
+
|------|-------|---------|
|
|
139
|
+
| `extension.ts` | 862 | Activation entry; per-workspace ClawsServer; UUID-keyed terminal profile matching; 30s pending-pty cleanup; deactivate hardening (3s timeout) |
|
|
140
|
+
| `server.ts` | 2010 | Unix socket server; routes claws/1 + claws/2; peer/task/wave registries; rate limiting; event log; backpressure; lifecycle integration |
|
|
141
|
+
| `terminal-manager.ts` | 363 | Stable terminal IDs; vehicle state FSM (PROVISIONING→BOOTING→READY→BUSY/IDLE→CLOSING→CLOSED); 2s content polling; 60s unopened-pty cleanup |
|
|
142
|
+
| `claws-pty.ts` | 446 | `vscode.Pseudoterminal` impl; bundled-first node-pty load with pipe-mode fallback; bracketed-paste; env sanitization (drop VSCODE_/ELECTRON_/npm_ prefixes); foreground PID detection |
|
|
143
|
+
| `capture-store.ts` | 111 | Per-terminal circular buffer; trim-on-overflow; offset-based reads; ANSI-strip on demand |
|
|
144
|
+
| `protocol.ts` | ~200 | Wire types (claws/1 + claws/2); SubWorkerRole + ContractedRoles |
|
|
145
|
+
| `ansi-strip.ts` | 53 | CSI/OSC/DCS/single-ESC/C0-C1 stripping; preserves `\t` `\n` `\r` |
|
|
146
|
+
| `peer-registry.ts` | 94 | Peer identity (`p_NNNNNN`); fingerprinting (`fp_NNNNNNNNNNNN`) for reconnect; subscription tracking |
|
|
147
|
+
| `task-registry.ts` | 52 | Task lifecycle (pending → running → blocked → succeeded/failed/skipped); IDs `t_NNN` |
|
|
148
|
+
| `lifecycle-store.ts` | ~250 | Schema v3 state; persists to `.claws/lifecycle-state.json` atomically; `bootSession`, `plan`, `setPhase`, `registerSpawn`, `registerMonitor`, `markWorkerStatus`, `reflect` |
|
|
149
|
+
| `lifecycle-rules.ts` | ~170 | Pure validators: `canTransition`, `canSpawn`, `canCleanup`, `canReflect`, `canEndSession`, `nextAutoPhase` (auto-advance decisions) |
|
|
150
|
+
| `lifecycle-engine.ts` | 61 | `onWorkerEvent` triggers cascade through `nextAutoPhase`; safety limit 10 iterations; emits `lifecycle.phase-changed` |
|
|
151
|
+
| `server-config.ts` | 99 | Live config getter from VS Code settings; hot-reload on settings.json edits |
|
|
152
|
+
| `status-bar.ts` | ~100 | Right-aligned status item; color-coded (green/yellow/red); 30s refresh |
|
|
153
|
+
| `topic-registry.ts` | 69 | Zod schema map `pattern → schema` for all known topics |
|
|
154
|
+
| `topic-utils.ts` | 47 | Pure `matchTopic(topic, pattern)`; `*` (one segment) and `**` (one+ segments, greedy) |
|
|
155
|
+
| `event-schemas.ts` | ~150 | EnvelopeV1 + WorkerBootV1 / WorkerPhaseV1 / WorkerEventV1 / WorkerHeartbeatV1 / WorkerCompleteV1 / Cmd*V1 / VehicleStateV1 / PipelineStepV1 / RpcRequestV1 / RpcResponseV1 / WaveHarvestedV1 |
|
|
156
|
+
| `event-log.ts` | ~250 | Append-only segments (10 MB / 1h rotation); manifest tracking; cursor `NNNN:offset`; crash recovery via dir scan |
|
|
157
|
+
| `pipeline-registry.ts` | 83 | In-memory data-flow pipelines `pipe_NNNN`; source/sink steps; close marks all closed |
|
|
158
|
+
| `websocket-transport.ts` | ~150 | L19 WebSocket server; lazy `ws` require; `WsSocketAdapter` shim presents net.Socket-like interface |
|
|
159
|
+
| `wave-registry.ts` | ~150 | Wave manifest; per-sub-worker 25s violation timer; LEAD violation timer; auto-harvest |
|
|
160
|
+
| `uninstall-cleanup.ts` | 233 | Inventory + remove Claws-installed artifacts (.mcp.json entry, .claws-bin, .claude/commands/claws-*, CLAWS:BEGIN block); atomic writes; per-folder confirmation |
|
|
161
|
+
|
|
162
|
+
### B. MCP Server (`mcp_server.js`, ~2150 lines)
|
|
163
|
+
|
|
164
|
+
Pure Node.js, zero deps. Exposes 38 MCP tools to Claude Code over stdio JSON-RPC.
|
|
165
|
+
|
|
166
|
+
**Tool families:**
|
|
167
|
+
- **Terminal control** (claws/1, per-call socket): `claws_list`, `claws_create`, `claws_send`, `claws_exec`, `claws_read_log`, `claws_poll`, `claws_close`
|
|
168
|
+
- **Worker orchestration**: `claws_worker`, `claws_fleet`, `claws_workers_wait`, `claws_dispatch_subworker`
|
|
169
|
+
- **Pub/sub** (claws/2, persistent socket): `claws_hello`, `claws_subscribe`, `claws_publish`, `claws_broadcast`, `claws_ping`, `claws_drain_events`, `claws_peers`
|
|
170
|
+
- **Lifecycle**: `claws_lifecycle_plan`, `claws_lifecycle_advance`, `claws_lifecycle_snapshot`, `claws_lifecycle_reflect`
|
|
171
|
+
- **Wave**: `claws_wave_create`, `claws_wave_status`, `claws_wave_complete`
|
|
172
|
+
- **Tasks**: `claws_task_assign`, `claws_task_update`, `claws_task_complete`, `claws_task_cancel`, `claws_task_list`
|
|
173
|
+
- **RPC + commands**: `claws_deliver_cmd`, `claws_cmd_ack`, `claws_rpc_call`
|
|
174
|
+
- **Schema + pipeline**: `claws_schema_list`, `claws_schema_get`, `claws_pipeline_create`, `claws_pipeline_list`, `claws_pipeline_close`
|
|
175
|
+
|
|
176
|
+
**Three socket models inside the MCP server:**
|
|
177
|
+
1. **Per-call stateless** (claws/1): `clawsRpc(sockPath, req, timeout)` — opens, sends one frame, reads response, closes. Used by every claws/1 tool + lifecycle.
|
|
178
|
+
2. **Persistent claws/2** (`_pconn`): single TCP socket, lazy connect, auto-reconnect with 1s delay, idempotent `_pconnEnsureRegistered` re-registers on reconnect. Used by all stateful claws/2 tools.
|
|
179
|
+
3. **Sidecar stream**: `stream-events.js` holds its own claws/2 connection subscribed to `**`; pipes push frames to `events.log`.
|
|
180
|
+
|
|
181
|
+
**Detach watcher pattern** — there are 4 watchers, all sharing the same `detectCompletion` helper (Task #58, idle-timeout removed):
|
|
182
|
+
1. `runBlockingWorker` detach branch (~line 659): created when `claws_worker(detach:true)` (default). Background `setInterval` polls log, calls `detectCompletion`, on signal publishes `system.worker.completed` + auto-closes.
|
|
183
|
+
2. `runBlockingWorker` blocking poll loop (~line 726): for `wait:true` / `detach:false`. Same logic, in-line while loop.
|
|
184
|
+
3. Fast-path `_fpTick` (~line 1425): for `claws_worker` fast-path (event-driven boot detection — polls for `❯` + `cost:$` stable for 3 polls + 5000ms settle).
|
|
185
|
+
4. `dispatch_subworker` `_dswTick` (~line 1885): for wave sub-workers; same shape but tracks `waveId` + `role` in payload.
|
|
186
|
+
|
|
187
|
+
**Ring buffer + drain** (`_eventBuffer`): 1000-frame circular buffer captures every server push; dedup via `seenSequences` Set; overflow emits `system.bus.ring-overflow`; `claws_drain_events` consumers wait via `_eventBuffer.waiters`.
|
|
188
|
+
|
|
189
|
+
**Sidecar auto-spawn** (`_spawnAndVerifySidecar`): on first claws/2 push, MCP detects + spawns `stream-events.js` if not running (pgrep dedup by socket path), opens `events.log`, waits for `sidecar.subscribed` JSON. GAP-A1 fixes the dedup.
|
|
190
|
+
|
|
191
|
+
### C. Scripts Layer
|
|
192
|
+
|
|
193
|
+
**Install / update / uninstall:**
|
|
194
|
+
- `install.sh` (~380 lines, Unix), `install.ps1` (Windows): clone → install extension → build VSIX → install to editor → inject CLAUDE.md → register MCP → register hooks. 9 steps, idempotent, env-overridable.
|
|
195
|
+
- `update.sh`: pull → rebuild → reinstall → re-inject. Preserves `.claws/lifecycle-state.json`.
|
|
196
|
+
- `uninstall.sh`: deregister hooks via `inject-settings-hooks.js --remove` → strip CLAWS:BEGIN blocks → remove shell-hook sourcing → kill sidecar+tail → remove grace file.
|
|
197
|
+
|
|
198
|
+
**Injectors:**
|
|
199
|
+
- `inject-claude-md.js`: writes `<!-- CLAWS:BEGIN --> ... <!-- CLAWS:END -->` block into project `CLAUDE.md`. Atomic write. Migrates legacy v0.1–v0.3 sections.
|
|
200
|
+
- `inject-global-claude-md.js`: same for `~/.claude/CLAUDE.md`.
|
|
201
|
+
- `inject-settings-hooks.js`: registers SessionStart + PreToolUse + Stop hooks into `~/.claude/settings.json`. Tags with `_source:"claws"` for clean uninstall. Atomic + JSONC-tolerant + exclusive-lock (M-18). Auto-migrates legacy flat-array hook format.
|
|
202
|
+
|
|
203
|
+
**Hooks:**
|
|
204
|
+
- `session-start-claws.js`: detects `.claws/claws.sock`; spawns sidecar (pgrep dedup); pre-creates `events.log`; emits lifecycle reminder. 5s self-kill, silent errors unless `CLAWS_DEBUG=1`.
|
|
205
|
+
- `pre-tool-use-claws.js`: gates spawn-class MCP tools (must have Monitor armed within 5s grace); blocks long-running Bash patterns (`npm/yarn/pnpm/bun` + `serve|dev|watch`, `node/python` + `server`, `uvicorn`/`gunicorn`/`flask run`, etc.) with argv0 allowlist (BUG-16); blocks Edit/Write to `mcp_server.js` from orchestrator (worker bypass via `CLAWS_WORKER=1`).
|
|
206
|
+
- `stop-claws.js`: kills sidecar + orphan tails + grace file; warns on unclosed terminals; reminds about REFLECT.
|
|
207
|
+
|
|
208
|
+
**Stream + helpers:**
|
|
209
|
+
- `stream-events.js`: persistent claws/2 sidecar; subscribes per `CLAWS_TOPIC` env (default `**`); JSON output one frame per stdout line. Designed for Monitor consumption.
|
|
210
|
+
- `shell-hook.sh`: sourced by `~/.zshrc` / `~/.bashrc`; emits Claws banner + bridge status; provides `claws-ls`, `claws-new`, `claws-run`, `claws-log` shell functions over the socket.
|
|
211
|
+
- `terminal-wrapper.sh`: sets `CLAWS_WRAPPED=1`, `CLAWS_PIPE_MODE` conditional, `CLAWS_WORKER` env vars inside wrapped terminals.
|
|
212
|
+
- `bump-version.sh`: single source of truth for version bumps across `package.json`, extension `package.json`, tags.
|
|
213
|
+
- `_helpers/json-safe.mjs` + `atomic-file.mjs`: shared atomic-write helpers used by all injectors.
|
|
214
|
+
|
|
215
|
+
### D. Templates + Slash Commands
|
|
216
|
+
|
|
217
|
+
**Templates** (`templates/`):
|
|
218
|
+
- `CLAUDE.global.md`: machine-wide policy; loaded into `~/.claude/CLAUDE.md` by global injector. Contains the 7-step worker boot sequence, the lifecycle phase list, and the wave discipline contract.
|
|
219
|
+
- `CLAUDE.project.md`: project-level rules; loaded into `<project>/CLAUDE.md` CLAWS:BEGIN block. Contains tool inventory placeholders (`{TOOLS_V1_COUNT}`, `{TOOLS_V2_COUNT}`, `{CMDS_COUNT}`) filled at injection time.
|
|
220
|
+
|
|
221
|
+
**Slash commands** (`.claude/commands/claws-*.md`): 19 commands. The user-facing entry points (`/claws-do`, `/claws-go`, `/claws-worker`, `/claws-fleet`, `/claws-army`, `/claws-watch`, `/claws-cleanup`, etc.) and admin commands (`/claws-install`, `/claws-update`, `/claws-fix`, `/claws-status`, `/claws-introspect`, etc.).
|
|
222
|
+
|
|
223
|
+
**Skills** (`.claude/skills/`):
|
|
224
|
+
- `claws-prompt-templates/SKILL.md`: production-grade mission prompts and lifecycle patterns.
|
|
225
|
+
- `claws-wave-lead/SKILL.md` + `claws-wave-subworker/SKILL.md`: wave army role contracts.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## IV. Wire Protocols
|
|
230
|
+
|
|
231
|
+
### claws/1 (Terminal Control) — per-call stateless
|
|
232
|
+
|
|
233
|
+
Newline-delimited JSON over Unix socket (`.claws/claws.sock`). Multi-root workspaces get one socket per folder.
|
|
234
|
+
|
|
235
|
+
Request envelope:
|
|
236
|
+
```json
|
|
237
|
+
{ "id": <number|string>, "cmd": "<command>", "protocol": "claws/1", ...args }
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Response envelope (always):
|
|
241
|
+
```json
|
|
242
|
+
{ "id": <echoed>, "rid": <echoed>, "ok": true|false, "protocol": "claws/1", ... }
|
|
243
|
+
```
|
|
244
|
+
Use `rid` (not `id`) for correlation — `id` is sometimes shadowed by response-specific fields (e.g., `create` returns the new terminal's `id`).
|
|
245
|
+
|
|
246
|
+
Commands: `list`, `create`, `show`, `send`, `exec`, `readLog`, `poll`, `close`, `introspect`. See `docs/protocol.md` for full request/response shapes.
|
|
247
|
+
|
|
248
|
+
### claws/2 (Agentic SDLC) — persistent connection
|
|
249
|
+
|
|
250
|
+
Same wire format but the connection is stateful. Required first frame:
|
|
251
|
+
```json
|
|
252
|
+
{ "id": 1, "cmd": "hello", "protocol": "claws/2", "role": "orchestrator|worker|observer",
|
|
253
|
+
"peerName": "...", "terminalId": "<optional>", "capabilities": ["push", ...] }
|
|
254
|
+
```
|
|
255
|
+
Server allocates `peerId` (`p_NNNNNN` transient, or `fp_NNNNNNNNNNNN` if `instanceNonce` provided for reconnect recovery). Exactly one orchestrator per socket.
|
|
256
|
+
|
|
257
|
+
**Server-pushed frame** (no `rid`, can arrive any time after `hello`):
|
|
258
|
+
```json
|
|
259
|
+
{ "push": "message", "protocol": "claws/2", "topic": "...",
|
|
260
|
+
"from": "p_NNNNNN", "payload": {...}, "sentAt": <epoch_ms> }
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Commands**: `subscribe`, `unsubscribe`, `publish`, `broadcast`, `task.assign`, `task.update`, `task.complete`, `task.cancel`, `task.list`, `ping`.
|
|
264
|
+
|
|
265
|
+
### Topic namespace
|
|
266
|
+
|
|
267
|
+
| Prefix | Owner | Permissions | Purpose |
|
|
268
|
+
|--------|-------|-------------|---------|
|
|
269
|
+
| `worker.<peerId>.*` | worker (self) | write self only | boot / phase / heartbeat / event / complete |
|
|
270
|
+
| `cmd.<peerId>.*` | orchestrator | orchestrator-only write | direct commands |
|
|
271
|
+
| `cmd.role.<role>` | orchestrator | orchestrator-only | broadcast by role |
|
|
272
|
+
| `task.<taskId>.*` | orch + assignee | split | task lifecycle |
|
|
273
|
+
| `wave.<waveId>.<role>.*` | LEAD + sub-workers | role-scoped | wave army |
|
|
274
|
+
| `system.*` | server | server-only | peer joined/left/stale, gate fires, malformed events, worker.completed/spawned |
|
|
275
|
+
|
|
276
|
+
**Wildcards** (`subscribe` / `topic_registry`): `*` = one segment, `**` = one+ segments greedy.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## V. Lifecycle Architecture (10-Phase, v0.7.10)
|
|
281
|
+
|
|
282
|
+
Schema v3 (`extension/src/lifecycle-store.ts`):
|
|
283
|
+
```typescript
|
|
284
|
+
interface LifecycleState {
|
|
285
|
+
v: 3;
|
|
286
|
+
phase: Phase;
|
|
287
|
+
phases_completed: Phase[];
|
|
288
|
+
plan: string;
|
|
289
|
+
worker_mode: 'single' | 'fleet' | 'army';
|
|
290
|
+
expected_workers: number;
|
|
291
|
+
spawned_workers: { id, correlation_id, name, spawned_at, status, completed_at? }[];
|
|
292
|
+
monitors: { terminal_id, correlation_id, command, armed_at }[];
|
|
293
|
+
workers: { id, closed }[]; // backward-compat mirror
|
|
294
|
+
mission_n: number;
|
|
295
|
+
session_started_at: string;
|
|
296
|
+
mission_started_at: string;
|
|
297
|
+
reflect?: string;
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### The 10 phases
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
SESSION-BOOT → PLAN → SPAWN → DEPLOY → OBSERVE
|
|
305
|
+
↓ ↘
|
|
306
|
+
HARVEST RECOVER (escape — back to DEPLOY/OBSERVE/FAILED)
|
|
307
|
+
↓
|
|
308
|
+
CLEANUP → REFLECT → PLAN (next mission)
|
|
309
|
+
↓
|
|
310
|
+
SESSION-END
|
|
311
|
+
FAILED is reachable from any phase; routes to CLEANUP or SESSION-END.
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
| # | Phase | Trigger | Entry gate | Auto-advance |
|
|
315
|
+
|---|-------|---------|-----------|--------------|
|
|
316
|
+
| 0 | SESSION-BOOT | mcp_server constructor / session-start hook | sidecar verified alive | auto when sidecar verified |
|
|
317
|
+
| 1 | PLAN | `claws_lifecycle_plan(text, mode, count)` | non-empty text + valid mode + positive count | manual |
|
|
318
|
+
| 2 | SPAWN | `claws_lifecycle_advance to=SPAWN` | PLAN done | auto → DEPLOY when all `expected_workers` spawned + monitors armed |
|
|
319
|
+
| 3 | DEPLOY | engine | all workers registered + monitored | auto → OBSERVE when any worker `status !== spawned` |
|
|
320
|
+
| 4 | OBSERVE | engine | at least one worker progressed | auto → HARVEST: single=1 done; fleet=all done; army=`claws_wave_complete` |
|
|
321
|
+
| 5 | RECOVER | manual on failure | any | manual |
|
|
322
|
+
| 6 | HARVEST | engine | all workers terminal | auto → CLEANUP via `canCleanup` |
|
|
323
|
+
| 7 | CLEANUP | engine | gate via `canCleanup` | auto → REFLECT via `canReflect` (all closed) |
|
|
324
|
+
| 8 | REFLECT | `claws_lifecycle_reflect(text)` | gate via `canReflect` | manual: → PLAN (mission n+1) or → SESSION-END |
|
|
325
|
+
| 9 | SESSION-END | Stop hook or explicit | phase REFLECT/FAILED + zero open terminals | terminal |
|
|
326
|
+
|
|
327
|
+
### Auto-advance engine (`lifecycle-engine.ts`)
|
|
328
|
+
|
|
329
|
+
`onWorkerEvent(reason)` cascades transitions via while-loop until `nextAutoPhase(state)` returns null. Safety limit 10 iterations. Each transition validated by `canTransition` + appropriate gate before persisting. Emits `lifecycle.phase-changed` on the bus.
|
|
330
|
+
|
|
331
|
+
`nextAutoPhase` rules (`lifecycle-rules.ts`):
|
|
332
|
+
- SPAWN → DEPLOY: `spawned.length === expected && allWorkersHaveMonitors(state)`
|
|
333
|
+
- DEPLOY → OBSERVE: `spawned.some(w => w.status !== 'spawned')`
|
|
334
|
+
- OBSERVE → HARVEST: mode-aware (army never auto-advances; single=1 terminal status; fleet=all)
|
|
335
|
+
- HARVEST → CLEANUP: `canCleanup(state).ok` (BUG-A fix)
|
|
336
|
+
- CLEANUP → REFLECT: `canReflect(state).ok` (BUG-B fix — `mark-worker-status('closed')` after auto-close)
|
|
337
|
+
|
|
338
|
+
### D+F architecture (Declaration + Forwarding)
|
|
339
|
+
|
|
340
|
+
Race-free spawn + monitor registration:
|
|
341
|
+
1. **D (Declaration)**: orchestrator-supplied `correlation_id` (UUID) flows in mission AND in spawn-tool args.
|
|
342
|
+
2. **F (Forwarding)**: MCP spawn tool atomically calls `lifecycle.register-spawn(termId, corrId, name)` + `lifecycle.register-monitor(termId, corrId, command)` before returning.
|
|
343
|
+
3. Watcher publishes `system.worker.completed` with `correlation_id` in payload.
|
|
344
|
+
4. Per-worker Monitor subscribes via `grep '"correlation_id":"<UUID>"' | grep -m1 'system\.worker\.completed'` — exits on first completion event for THAT worker.
|
|
345
|
+
|
|
346
|
+
Result: zero race window between "terminal exists" and "monitor registered." Correlation_id is the single source of truth for matching events to workers.
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## VI. Enforcement Chain (5 + 1 layers)
|
|
351
|
+
|
|
352
|
+
The enforcement chain is *progressive* — each outer layer educates, each inner layer enforces. Failure of an outer layer surfaces in an inner layer's deny.
|
|
353
|
+
|
|
354
|
+
| # | Layer | Scope | Mechanism | Failure mode |
|
|
355
|
+
|---|-------|-------|-----------|--------------|
|
|
356
|
+
| 1 | `templates/CLAUDE.global.md` → `~/.claude/CLAUDE.md` | machine-wide | always-loaded user instructions | honor system + boot sequence |
|
|
357
|
+
| 2 | `templates/CLAUDE.project.md` → `<project>/CLAUDE.md` CLAWS:BEGIN | project | always-loaded project instructions + tool inventory | honor system + lifecycle phases |
|
|
358
|
+
| 3 | `.claude/rules/claws-default-behavior.md` (ECC plugin) | session-runtime | system-reminder via ECC SessionStart | optional supplement |
|
|
359
|
+
| 4 | `scripts/hooks/session-start-claws.js` | session start | sidecar spawn + reminder emit | sidecar dead → PreToolUse deny |
|
|
360
|
+
| 5 | `scripts/hooks/pre-tool-use-claws.js` | every MCP tool call | hard-block via exit 2 OR `permissionDecision: deny` | spawn-class denied if Monitor missing; long Bash patterns blocked; mcp_server.js edits forbidden from orchestrator |
|
|
361
|
+
| 6 | `scripts/hooks/stop-claws.js` | session end | cleanup + audit | warn on unclosed terminals + missing REFLECT |
|
|
362
|
+
|
|
363
|
+
Wave C (TODO):
|
|
364
|
+
- New `post-tool-use-claws.js`: fail-closed if `lifecycle.monitors[terminal_id]` not registered within 5s of a spawn-class tool returning.
|
|
365
|
+
- Update `stop-claws.js`: hard-block exit until phase ∈ {REFLECT, SESSION-END} + zero open terminals.
|
|
366
|
+
- Update `pre-tool-use-claws.js`: recognize the canonical bus-stream Monitor pattern (currently still demands the deprecated `tail -F` satisfier — known bug).
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## VII. Test Infrastructure
|
|
371
|
+
|
|
372
|
+
109 test files / 143 named test targets in `extension/package.json`. `npm test` runs them all sequentially. Major categories:
|
|
373
|
+
|
|
374
|
+
- **Smoke + baseline** (5): activation, native PTY, config reload, capture-store trim, oversized line.
|
|
375
|
+
- **Native bundle + build** (8): arch selection, spawn correctness, timeout, editor detect, atomic copy.
|
|
376
|
+
- **PTY lifecycle + capture** (6): per-terminal isolation, profile detection, multi-conn, event log.
|
|
377
|
+
- **Claws/1** (5): reverse-channel, SDK/CLI, socket timeout, atomic file.
|
|
378
|
+
- **Claws/2 + SDLC** (13): hello, pubsub, tasks, vehicle-state, broadcast-seq, claws-pub-scanner, content/control/identity/pipeline/typed-rpc.
|
|
379
|
+
- **Lifecycle** (10): store, server, fsync, engine, rules, reset, non-blocking-defaults, multisignal-completion, sequence-persist, task-event-persist.
|
|
380
|
+
- **Hooks** (10): safe-merge, dedup, exclusive-lock, canonical-fast-path, misfire-log, explicit-if, stdin-safety, strict-deny-newline, debug-visibility, atomic-state.
|
|
381
|
+
- **Template injection** (3): claude-md-atomic, dev-hooks, absolute-paths.
|
|
382
|
+
- **install.sh / update.sh / fix.sh** (26): every step, race conditions, error paths, dry-run, atomicity, recovery.
|
|
383
|
+
- **Wave army** (2): registration → boot → complete cycle for single + multi-role.
|
|
384
|
+
- **Schema + validation** (4): event-schemas, server-validation, topic-registry, mcp-tools-codegen (38 tools).
|
|
385
|
+
- **Worker reliability** (3): v0.7.9 marker fixes, version-drift, worker-fixes-v079 (11 assertions).
|
|
386
|
+
|
|
387
|
+
**Key invariants enforced by tests:**
|
|
388
|
+
1. Atomic file writes — no partial files, fsync verified.
|
|
389
|
+
2. Hook safety — never exit non-zero except intentional deny; settings.json never silently reset.
|
|
390
|
+
3. Terminal isolation — each terminal owns its pty; capture per-terminal.
|
|
391
|
+
4. Bus ordering — events fan-out deterministically; peer isolation enforced.
|
|
392
|
+
5. Lifecycle gates — phase transitions validated; REFLECT requires all closed.
|
|
393
|
+
6. Bash pattern detection — long-running blocked; argv0 allowlist prevents false positives.
|
|
394
|
+
7. Monitor requirement — spawn-class tools gated unless Monitor armed.
|
|
395
|
+
8. Worker protocol — boot sequence exact; mission delivery correct; markerScanFrom prevents echo false-match.
|
|
396
|
+
9. Idempotency — install/update/hooks repeatable safely.
|
|
397
|
+
10. Timeout safety — no blocking calls; configurable timeouts everywhere.
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## VIII. Anti-Patterns Catalog (burned in)
|
|
402
|
+
|
|
403
|
+
Each entry: the pattern we tried, why it broke, and the right answer. **Adding to this list is the cost of an architecture violation. Read it before adding "just a small fix."**
|
|
404
|
+
|
|
405
|
+
### A1 — `tail -F file | grep` Monitor primitive
|
|
406
|
+
- **Tried**: `Monitor(command="tail -F .claws/events.log | grep ...")` to watch the bus.
|
|
407
|
+
- **Broke**: Claude Code's background-process supervisor SIGURG-kills idle processes within ~30s. `tail -F` produces no output during quiet periods.
|
|
408
|
+
- **Right answer**: `Monitor(command="node scripts/stream-events.js | grep --line-buffered ...")`. The sidecar emits constantly (heartbeats, system.metrics, every event) — never idle, never killed.
|
|
409
|
+
|
|
410
|
+
### A2 — idle-timeout completion signal
|
|
411
|
+
- **Tried**: Task #58 added `idle_timeout_ms` to `detectCompletion` — declare worker complete after N seconds of no pty growth.
|
|
412
|
+
- **Broke**: Claude TUI is silent during long thinking. Real-world test killed a worker at 70s before any work happened. Idle is fundamentally polling; contradicts P1.
|
|
413
|
+
- **Right answer**: removed entirely (commit pending). Use only event-driven signals: `complete_marker`, `error_markers`, `[CLAWS_PUB] topic=worker.<id>.complete`. Future event-driven fallback: VS Code `onDidCloseTerminal` → `system.worker.terminated` bus event.
|
|
414
|
+
|
|
415
|
+
### A3 — File-referrer missions
|
|
416
|
+
- **Tried**: v0.7.9 — write mission to `/tmp/...md`, send `Read /tmp/.../mission.md and follow it precisely` to Claude.
|
|
417
|
+
- **Broke**: extra abstraction layer; debug nightmare; left temp files; race conditions on mission file vs. Claude readiness.
|
|
418
|
+
- **Right answer**: v0.7.10 reverted (commit 70ec1b1). Mission text is sent inline as Claude's input, as if a human typed it.
|
|
419
|
+
|
|
420
|
+
### A4 — Mission augmentation with `[CLAWS_PUB]` preamble
|
|
421
|
+
- **Tried**: prepend "First publish [CLAWS_PUB] topic=..." preamble to every mission.
|
|
422
|
+
- **Broke**: bracketed-paste with the preamble broke multi-line submission — paste landed in shell instead of Claude's input.
|
|
423
|
+
- **Right answer**: never augment. Ask the worker (in the mission body) to publish if needed. Multi-line missions use single `paste:true newline:true` send; `writeInjected` handles 30ms internal CR.
|
|
424
|
+
|
|
425
|
+
### A5 — Marker that appears literally in mission text
|
|
426
|
+
- **Tried**: `complete_marker: "MISSION_COMPLETE"` while the mission text said "print MISSION_COMPLETE when done".
|
|
427
|
+
- **Broke**: marker false-matched on Claude's mission echo before any work.
|
|
428
|
+
- **Right answer**: printf concat trick: `printf 'M%sARK%s_OK_XXXX\n' '' ''` — the marker string `MARK_OK_XXXX` never appears in the mission body.
|
|
429
|
+
|
|
430
|
+
### A6 — `script -F` flag with Ink-based TUIs
|
|
431
|
+
- **Tried**: `script -F` (per-write flush) for snappier reads.
|
|
432
|
+
- **Broke**: Splits Ink's atomic frames mid-render; visual corruption in Claude Code TUI.
|
|
433
|
+
- **Right answer**: default buffering. The ~1-2s buffering delay is acceptable.
|
|
434
|
+
|
|
435
|
+
### A7 — `parallel: true` as advisory flag
|
|
436
|
+
- **Tried**: `claws_fleet({ parallel: true, ... })` documented as parallel but actually serialized internally.
|
|
437
|
+
- **Broke**: invisible regression — orchestrators relied on parallelism that wasn't there.
|
|
438
|
+
- **Right answer**: `parallel: true` must be enforced in code, not just documented. `claws_fleet` actually dispatches concurrently in v0.7.10.
|
|
439
|
+
|
|
440
|
+
### A8 — Hard-blocking safety gate by default
|
|
441
|
+
- **Tried**: refuse to send text into TUI processes (Claude, vim, less).
|
|
442
|
+
- **Broke**: defeats Claws's primary use case (sending prompts INTO Claude).
|
|
443
|
+
- **Right answer**: warn-and-proceed by default; `strict: true` is the opt-in for hard-block.
|
|
444
|
+
|
|
445
|
+
### A9 — Inline orchestrator patches
|
|
446
|
+
- **Tried**: orchestrator edits `mcp_server.js` directly because "it's just a small fix."
|
|
447
|
+
- **Broke**: untracked, untested, ungated churn accumulates; the orchestrator becomes a developer instead of an orchestrator.
|
|
448
|
+
- **Right answer**: PreToolUse hook hard-blocks (P8). Every change goes through a worker.
|
|
449
|
+
|
|
450
|
+
### A10 — Hook-bypass via `--no-verify`
|
|
451
|
+
- **Tried**: skip pre-commit hook to commit faster.
|
|
452
|
+
- **Broke**: hook exists because something broke last time — bypass = guaranteed regression.
|
|
453
|
+
- **Right answer**: never. If the hook is wrong, fix the hook, never bypass it.
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## IX. Known Gaps + Roadmap
|
|
458
|
+
|
|
459
|
+
### Wave C — Hook fail-closed enforcement (next)
|
|
460
|
+
- New `scripts/hooks/post-tool-use-claws.js`: after spawn-class tool returns, verify `lifecycle.monitors[terminal_id]` registered within 5s; else emit `wave.violation` + auto-cancel.
|
|
461
|
+
- Update `stop-claws.js`: hard-block session exit until phase ∈ {REFLECT, SESSION-END} + zero open terminals.
|
|
462
|
+
- Update `pre-tool-use-claws.js`: recognize canonical bus-stream Monitor pattern; stop demanding deprecated `tail -F`.
|
|
463
|
+
- Update `inject-settings-hooks.js`: register PostToolUse with `matcher='*'`.
|
|
464
|
+
|
|
465
|
+
### Wave D — Event-driven completion (Task #58 v2)
|
|
466
|
+
- Replace removed idle-timeout with `onDidCloseTerminal` → `system.worker.terminated` bus event.
|
|
467
|
+
- Mission preamble (per worker class) injects: "Your final action MUST publish `[CLAWS_PUB] topic=worker.<your-id>.complete`."
|
|
468
|
+
- Add `tests/onDidCloseTerminal-publish` test.
|
|
469
|
+
|
|
470
|
+
### Wave V — Infra hardening (long-range orchestration)
|
|
471
|
+
- `events.log` rotation policy (currently grows unbounded ~1MB/hr; cap 10MB + rotate).
|
|
472
|
+
- Bus reconnect on socket drop in `stream-events.js` + sidecar manager.
|
|
473
|
+
- Periodic stale-terminal sweep (workers stuck at `status='spawned'` > N min).
|
|
474
|
+
- Auto-sidecar restart on crash detection.
|
|
475
|
+
- 1+ hour soak test verifying memory + bus stability.
|
|
476
|
+
|
|
477
|
+
### Wave E — Ship gates
|
|
478
|
+
- Full Phase A/B/C sim: single + fleet + army workers, all auto-cascade through 10 phases.
|
|
479
|
+
- CHANGELOG complete for v0.7.10.
|
|
480
|
+
- Tag + push to origin.
|
|
481
|
+
|
|
482
|
+
### Phase 3 — Cross-device (post-v0.7.10)
|
|
483
|
+
- WebSocket transport (opt-in alongside Unix socket).
|
|
484
|
+
- Token auth + TLS.
|
|
485
|
+
- mDNS/Bonjour discovery for LAN.
|
|
486
|
+
- Cross-device readLog streaming (WebSocket push, not poll).
|
|
487
|
+
|
|
488
|
+
### Phase 4 — Ecosystem (post-Phase 3)
|
|
489
|
+
- CLI tool (`npx claws list`, `npx claws send 1 "ls"`).
|
|
490
|
+
- REST API mode.
|
|
491
|
+
- Dashboard web UI.
|
|
492
|
+
- Live Share integration.
|
|
493
|
+
- GitHub Action.
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## X. Anchoring Protocol (how to use this doc)
|
|
498
|
+
|
|
499
|
+
**Before any architectural change, the implementer MUST:**
|
|
500
|
+
|
|
501
|
+
1. **Find the affected component** in §III. If the change adds or removes a component, this doc must change too.
|
|
502
|
+
2. **Check the principles** in §II. If the change violates a principle, the change is wrong — find a different approach OR change the principle (with rationale, in a PR).
|
|
503
|
+
3. **Check the anti-patterns catalog** in §VIII. If the change resembles an anti-pattern, stop. The right answer is in the catalog.
|
|
504
|
+
4. **For lifecycle/protocol changes**, update §IV–§V; update tests in `extension/test/`; update injectors if templates change.
|
|
505
|
+
5. **For new completion signals**, the signal must be event-driven (P1). No exceptions.
|
|
506
|
+
6. **For new gates**, add the rule to `lifecycle-rules.ts` (pure function, testable) AND surface in the engine if auto-advance applies.
|
|
507
|
+
7. **For new hooks**, the hook must satisfy P5 (5s timeout, silent errors, no exit non-zero except intentional deny).
|
|
508
|
+
|
|
509
|
+
**Worker missions referencing this doc**: every non-trivial worker should be told: "Before edits, read `docs/ARCHITECTURE.md` §X. Your change must not violate anything there." Workers that do violate get reverted.
|
|
510
|
+
|
|
511
|
+
**This doc is the contract.** Updates require an explicit PR titled `docs(architecture): <change>`. CHANGELOG entry required. Every contributor reads it before touching anything load-bearing.
|