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
@@ -0,0 +1,174 @@
1
+ #!/bin/bash
2
+ # Claws — post-install test + live demo of multi-terminal orchestration
3
+ # Run after install.sh. Proves every feature works end-to-end.
4
+ # Usage: bash scripts/test-install.sh
5
+
6
+ set -e
7
+
8
+ SOCK=".claws/claws.sock"
9
+ PASS=0
10
+ FAIL=0
11
+
12
+ passed() { echo " ✓ $1"; PASS=$((PASS+1)); }
13
+ failed() { echo " ✗ $1"; FAIL=$((FAIL+1)); }
14
+
15
+ echo ""
16
+ echo " ╔═══════════════════════════════════════════╗"
17
+ echo " ║ CLAWS — Installation Test + Live Demo ║"
18
+ echo " ╚═══════════════════════════════════════════╝"
19
+ echo ""
20
+
21
+ # Test 1: Socket exists
22
+ echo "── Test 1: Socket connection ──"
23
+ if [ -S "$SOCK" ]; then
24
+ passed "socket found at $SOCK"
25
+ else
26
+ failed "no socket at $SOCK — did you reload VS Code?"
27
+ echo " Run: Cmd+Shift+P → 'Developer: Reload Window'"
28
+ exit 1
29
+ fi
30
+
31
+ # Test 2: List terminals
32
+ echo "── Test 2: List terminals ──"
33
+ COUNT=$(node -e "
34
+ const net=require('net');
35
+ const s=net.createConnection('$SOCK');
36
+ s.on('connect',()=>s.write(JSON.stringify({id:1,cmd:'list'})+'\n'));
37
+ let b='';
38
+ s.on('data',d=>{b+=d;if(b.includes('\n')){try{const d2=JSON.parse(b.split('\n')[0]);if(!d2.ok)throw new Error('not ok');console.log(d2.terminals.length)}catch(e){console.log('ERROR:'+e.message)};s.destroy()}});
39
+ s.on('error',e=>{console.log('ERROR:'+e.message);s.destroy()});
40
+ setTimeout(()=>{console.log('ERROR:timeout');s.destroy()},5000);
41
+ " 2>/dev/null)
42
+ if echo "$COUNT" | grep -q "^[0-9]"; then
43
+ passed "listed $COUNT terminal(s)"
44
+ else
45
+ failed "list command failed: $COUNT"
46
+ fi
47
+
48
+ # Test 3: Node socket module
49
+ echo "── Test 3: Node net module ──"
50
+ if node -e "require('net'); console.log('OK')" 2>/dev/null; then
51
+ passed "node net module available (MCP server registered via settings.json)"
52
+ else
53
+ failed "node not available or net module missing"
54
+ fi
55
+
56
+ # Test 4: Create + exec + read + close (full loop)
57
+ echo "── Test 4: Full terminal lifecycle ──"
58
+ node -e "
59
+ const net=require('net'),fs=require('fs'),crypto=require('crypto');
60
+ const SOCK='$SOCK';
61
+
62
+ function send(cmd){return new Promise((resolve,reject)=>{const s=net.createConnection(SOCK);s.on('connect',()=>s.write(JSON.stringify(cmd)+'\n'));let b='';s.on('data',d=>{b+=d;if(b.includes('\n')){try{resolve(JSON.parse(b.split('\n')[0]))}catch(e){reject(e)};s.destroy()}});s.on('error',e=>reject(e));setTimeout(()=>reject(new Error('timeout')),10000)})}
63
+
64
+ async function run(){
65
+ // Create terminal
66
+ const cr=await send({id:1,cmd:'create',name:'claws-test',wrapped:true});
67
+ if(!cr.ok)throw new Error('create failed: '+cr.error);
68
+ const tid=cr.id;
69
+
70
+ // Wait for terminal to start
71
+ await new Promise(r=>setTimeout(r,1500));
72
+
73
+ // Exec command via send + poll
74
+ const eid=crypto.randomBytes(4).toString('hex');
75
+ const base='/tmp/claws-exec';
76
+ try{fs.mkdirSync(base,{recursive:true})}catch(e){}
77
+ const outF=base+'/'+eid+'.out',doneF=base+'/'+eid+'.done';
78
+ const wrapper='{ echo CLAWS_TEST_PASS && date && uname -a; } > '+outF+' 2>&1; echo \$? > '+doneF;
79
+ await send({id:1,cmd:'send',id:tid,text:wrapper});
80
+
81
+ // Poll for completion
82
+ const deadline=Date.now()+30000;
83
+ while(Date.now()<deadline){if(fs.existsSync(doneF))break;await new Promise(r=>setTimeout(r,200))}
84
+ if(!fs.existsSync(doneF))throw new Error('exec timed out');
85
+ const exitCode=fs.readFileSync(doneF,'utf8').trim();
86
+ if(exitCode!=='0')throw new Error('exit code '+exitCode);
87
+ const output=fs.readFileSync(outF,'utf8');
88
+ if(!output.includes('CLAWS_TEST_PASS'))throw new Error('output missing marker');
89
+ try{fs.unlinkSync(outF)}catch(e){}
90
+ try{fs.unlinkSync(doneF)}catch(e){}
91
+
92
+ // Read log
93
+ const lr=await send({id:1,cmd:'readLog',id:tid,strip:true});
94
+ if(!lr.ok)throw new Error('readLog failed: '+lr.error);
95
+ if(!lr.bytes||lr.bytes.length===0)throw new Error('empty log');
96
+
97
+ // Close terminal
98
+ await send({id:1,cmd:'close',id:tid});
99
+ console.log(' ✓ create → exec → readLog → close — all passed');
100
+ }
101
+
102
+ run().catch(e=>{console.log(' ✗ lifecycle test failed: '+e.message);process.exit(1)});
103
+ " 2>/dev/null
104
+
105
+ # Test 5: Multi-terminal orchestration demo
106
+ echo ""
107
+ echo "── Test 5: LIVE DEMO — Multi-terminal orchestration ──"
108
+ echo ""
109
+ node -e "
110
+ const net=require('net'),fs=require('fs'),crypto=require('crypto');
111
+ const SOCK='$SOCK';
112
+
113
+ function send(cmd){return new Promise((resolve,reject)=>{const s=net.createConnection(SOCK);s.on('connect',()=>s.write(JSON.stringify(cmd)+'\n'));let b='';s.on('data',d=>{b+=d;if(b.includes('\n')){try{resolve(JSON.parse(b.split('\n')[0]))}catch(e){reject(e)};s.destroy()}});s.on('error',e=>reject(e));setTimeout(()=>reject(new Error('timeout')),10000)})}
114
+
115
+ async function run(){
116
+ // Spawn 3 workers
117
+ console.log(' Spawning 3 parallel workers...');
118
+ const workers={};
119
+ const specs=[
120
+ {name:'worker-alpha',cmd:\"echo 'Alpha reporting' && sleep 1 && echo 'Alpha done'\"},
121
+ {name:'worker-beta',cmd:\"echo 'Beta reporting' && ls -1 | head -5 && echo 'Beta done'\"},
122
+ {name:'worker-gamma',cmd:\"echo 'Gamma reporting' && date && whoami && echo 'Gamma done'\"}
123
+ ];
124
+ for(const spec of specs){
125
+ const cr=await send({id:1,cmd:'create',name:spec.name,wrapped:true});
126
+ workers[spec.name]={id:cr.id,cmd:spec.cmd};
127
+ await new Promise(r=>setTimeout(r,500));
128
+ }
129
+ console.log(' ✓ 3 terminals spawned: '+Object.keys(workers).join(', '));
130
+
131
+ // Fire all commands in parallel
132
+ console.log(' Firing commands into all 3...');
133
+ for(const [name,w] of Object.entries(workers)){
134
+ await send({id:1,cmd:'send',id:w.id,text:w.cmd});
135
+ }
136
+ console.log(' ✓ Commands sent to all 3 workers');
137
+
138
+ // Wait and collect results
139
+ console.log(' Waiting for results...');
140
+ await new Promise(r=>setTimeout(r,3000));
141
+
142
+ console.log('');
143
+ console.log(' ┌─────────────────────────────────────────────┐');
144
+ for(const [name,w] of Object.entries(workers)){
145
+ const lr=await send({id:1,cmd:'readLog',id:w.id,strip:true});
146
+ const log=lr.ok?(lr.bytes||''):'';
147
+ const lines=log.split('\n').filter(l=>l.trim()&&l.toLowerCase().includes('done'));
148
+ const status=lines.length>0?'DONE':'...';
149
+ console.log(' │ '+name.padEnd(20)+' ['+status+']');
150
+ }
151
+ console.log(' └─────────────────────────────────────────────┘');
152
+ console.log('');
153
+
154
+ // Cleanup
155
+ for(const [name,w] of Object.entries(workers)){
156
+ await send({id:1,cmd:'close',id:w.id});
157
+ }
158
+ console.log(' ✓ All 3 workers closed. Terminals cleaned up.');
159
+ console.log('');
160
+ console.log(' Multi-terminal orchestration works. You\\'re ready.');
161
+ }
162
+
163
+ run().catch(e=>{console.log(' ✗ demo failed: '+e.message);process.exit(1)});
164
+ " 2>/dev/null
165
+
166
+ echo ""
167
+ echo " ════════════════════════════════════════════"
168
+ echo " Results: $PASS passed"
169
+ echo ""
170
+ echo " Your terminals are now programmable."
171
+ echo " Docs: https://github.com/neunaha/claws"
172
+ echo " Website: https://neunaha.github.io/claws/"
173
+ echo " ════════════════════════════════════════════"
174
+ echo ""
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/test-installer-parity.sh — parity harness: bash install.sh vs node bin/cli.js install
3
+ #
4
+ # Creates two temp project dirs, runs bash and node installers, diffs:
5
+ # .mcp.json claws entry (normalized), CLAUDE.md CLAWS:BEGIN block,
6
+ # .claude/settings.json hooks, .claws-bin/ file list, shell rc hook line.
7
+ # Exits 0 on zero diff, 1 otherwise with concrete diff output.
8
+
9
+ set -euo pipefail
10
+
11
+ REPO="$(cd "$(dirname "$0")/.." && pwd)"
12
+ TMP="$(mktemp -d /tmp/claws-parity-XXXXXX)"
13
+ # Use same basename so {PROJECT_NAME} matches in both CLAUDE.md blocks
14
+ A="$TMP/a/claws-parity-project"
15
+ B="$TMP/b/claws-parity-project"
16
+ SRC="$TMP/src"
17
+ HOME_A="$TMP/home-a"
18
+ HOME_B="$TMP/home-b"
19
+ trap 'rm -rf "$TMP"' EXIT
20
+ FAIL=0
21
+
22
+ _ok() { printf ' \033[32m✓\033[0m %s\n' "$1"; }
23
+ _fail() { printf ' \033[31m✗\033[0m %s\n' "$1"; FAIL=$((FAIL+1)); }
24
+ _diff() { diff -u "$1" "$2" 2>/dev/null | head -30 || true; }
25
+
26
+ mkdir -p "$A" "$B" "$HOME_A/.claude" "$HOME_B/.claude"
27
+
28
+ # ── Build source clone for bash installer ─────────────────────────────────────
29
+ printf '\n[parity] cloning source for bash installer...\n'
30
+ git clone --local --no-hardlinks -q "$REPO" "$SRC"
31
+ # Pre-seed pre-built artifacts so bash skips extension build
32
+ [ -d "$REPO/extension/dist" ] && cp -R "$REPO/extension/dist" "$SRC/extension/"
33
+ [ -d "$REPO/extension/native" ] && cp -R "$REPO/extension/native" "$SRC/extension/"
34
+ # Align .build-sha with clone HEAD so needs_build() returns false
35
+ git -C "$SRC" rev-parse HEAD > "$SRC/extension/dist/.build-sha" 2>/dev/null || true
36
+ # Touch bundle so find -newer finds no src files newer than it
37
+ touch "$SRC/extension/dist/extension.js" 2>/dev/null || true
38
+
39
+ # ── Run bash installer ────────────────────────────────────────────────────────
40
+ printf '[parity] running bash install.sh in proj A...\n'
41
+ (
42
+ cd "$A"
43
+ CLAWS_DIR="$SRC" CLAWS_NO_LOG=1 CLAWS_NO_GLOBAL_HOOKS=1 \
44
+ CLAWS_EDITOR=skip CLAWS_SKIP_EXTENSION_COPY=1 CLAWS_SKIP_VSCODE_RECOMMEND=1 \
45
+ HOME="$HOME_A" \
46
+ bash "$REPO/scripts/install.sh" 2>&1
47
+ ) | grep -E '^\s*(✓|!|✗|\[)' | head -20 || true
48
+
49
+ # ── Run node installer ────────────────────────────────────────────────────────
50
+ printf '[parity] running node bin/cli.js install in proj B...\n'
51
+ (
52
+ cd "$B"
53
+ HOME="$HOME_B" CLAWS_SKIP_EXTENSION_COPY=1 \
54
+ node "$REPO/bin/cli.js" install --no-hooks 2>&1
55
+ ) | grep -E '^\s*(✓|!|\[)' | head -20 || true
56
+
57
+ # ── Compare artifacts ─────────────────────────────────────────────────────────
58
+ printf '\n[parity] comparing artifacts...\n'
59
+
60
+ # (a) .mcp.json: normalize absolute paths → relative before comparing
61
+ _norm_mcp() {
62
+ node -e "
63
+ const c = JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));
64
+ const e = (c.mcpServers||{}).claws || {};
65
+ const args = (e.args||[]).map(a => a.replace(/^.*\\/\\.claws-bin\\//, '.claws-bin/'));
66
+ process.stdout.write(JSON.stringify({ command: e.command||'', args }) + '\n');
67
+ " "$1"
68
+ }
69
+ if [ -f "$A/.mcp.json" ] && [ -f "$B/.mcp.json" ]; then
70
+ A_MCP="$(_norm_mcp "$A/.mcp.json")"; B_MCP="$(_norm_mcp "$B/.mcp.json")"
71
+ if [ "$A_MCP" = "$B_MCP" ]; then _ok ".mcp.json"
72
+ else _fail ".mcp.json"; printf ' bash: %s\n node: %s\n' "$A_MCP" "$B_MCP"; fi
73
+ else
74
+ _fail ".mcp.json missing: bash=$( [ -f "$A/.mcp.json" ] && echo Y || echo N) node=$( [ -f "$B/.mcp.json" ] && echo Y || echo N)"
75
+ fi
76
+
77
+ # (b) CLAUDE.md CLAWS:BEGIN block (normalize project-name and cmds-list lines)
78
+ _extract_block() {
79
+ awk '/<!-- CLAWS:BEGIN/,/<!-- CLAWS:END/{print}' "$1" 2>/dev/null \
80
+ | grep -vE '\{PROJECT_NAME\}|claws-parity-project|^.*\bclaws-[a-z-]+\b.*$|CMDS_COUNT|CMDS_LIST|Slash commands \([0-9]+\)' \
81
+ || true
82
+ }
83
+ if [ -f "$A/CLAUDE.md" ] && [ -f "$B/CLAUDE.md" ]; then
84
+ BA="$(_extract_block "$A/CLAUDE.md")"; BB="$(_extract_block "$B/CLAUDE.md")"
85
+ if [ "$BA" = "$BB" ] && [ -n "$BA" ]; then _ok "CLAUDE.md CLAWS:BEGIN block"
86
+ else _fail "CLAUDE.md CLAWS:BEGIN block"; _diff <(echo "$BA") <(echo "$BB"); fi
87
+ else
88
+ _fail "CLAUDE.md missing: bash=$( [ -f "$A/CLAUDE.md" ] && echo Y || echo N) node=$( [ -f "$B/CLAUDE.md" ] && echo Y || echo N)"
89
+ fi
90
+
91
+ # (c) .claude/settings.json hooks (both --no-hooks → should be absent)
92
+ _get_hooks() {
93
+ [ -f "$1" ] && node -e "
94
+ try{ const c=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));
95
+ process.stdout.write(JSON.stringify(c.hooks||{})); }catch{process.stdout.write('{}')}
96
+ " "$1" 2>/dev/null || printf '{}'
97
+ }
98
+ HA="$(_get_hooks "$HOME_A/.claude/settings.json")"
99
+ HB="$(_get_hooks "$HOME_B/.claude/settings.json")"
100
+ if [ "$HA" = "$HB" ]; then _ok ".claude/settings.json hooks"
101
+ else _fail ".claude/settings.json hooks differ"; printf ' bash: %s\n node: %s\n' "$HA" "$HB"; fi
102
+
103
+ # (d) .claws-bin/ file list
104
+ FILES_A="$(cd "$A" && find .claws-bin -type f 2>/dev/null | sort || true)"
105
+ FILES_B="$(cd "$B" && find .claws-bin -type f 2>/dev/null | sort || true)"
106
+ if [ "$FILES_A" = "$FILES_B" ] && [ -n "$FILES_A" ]; then _ok ".claws-bin/ file list"
107
+ else _fail ".claws-bin/ file list"; _diff <(echo "$FILES_A") <(echo "$FILES_B"); fi
108
+
109
+ # (e) Shell rc-file: both must reference shell-hook.sh
110
+ RC_A="$(grep -rh 'shell-hook\.sh' "$HOME_A"/.zshrc "$HOME_A"/.bashrc 2>/dev/null | head -1 || true)"
111
+ RC_B="$(grep -rh 'shell-hook\.sh' "$HOME_B"/.zshrc "$HOME_B"/.bashrc 2>/dev/null | head -1 || true)"
112
+ if [ -n "$RC_A" ] && [ -n "$RC_B" ]; then _ok "shell rc-file (shell-hook.sh sourced in both)"
113
+ else _fail "shell rc-file: bash='${RC_A:-MISSING}' node='${RC_B:-MISSING}'"; fi
114
+
115
+ # (f) Project-local .claude/rules/ and .claude/skills/ — W7-6 regression guard
116
+ # Both installers must write claws-default-behavior.md into <project>/.claude/rules/
117
+ # and at least one skill dir into <project>/.claude/skills/.
118
+ RULES_A="$([ -d "$A/.claude/rules" ] && echo "Y" || echo "N")"
119
+ RULES_B="$([ -d "$B/.claude/rules" ] && echo "Y" || echo "N")"
120
+ SKILLS_A="$([ -d "$A/.claude/skills" ] && echo "Y" || echo "N")"
121
+ SKILLS_B="$([ -d "$B/.claude/skills" ] && echo "Y" || echo "N")"
122
+ if [ "$RULES_A" = "Y" ] && [ "$RULES_B" = "Y" ]; then _ok ".claude/rules/ project-local (both)"
123
+ else _fail ".claude/rules/ project-local: bash=$RULES_A node=$RULES_B"; fi
124
+ if [ "$SKILLS_A" = "Y" ] && [ "$SKILLS_B" = "Y" ]; then _ok ".claude/skills/ project-local (both)"
125
+ else _fail ".claude/skills/ project-local: bash=$SKILLS_A node=$SKILLS_B"; fi
126
+ # Guard: neither installer should put rules/skills into the fake HOME
127
+ RULES_HOME_A="$([ -d "$HOME_A/.claude/rules" ] && echo "Y" || echo "N")"
128
+ RULES_HOME_B="$([ -d "$HOME_B/.claude/rules" ] && echo "Y" || echo "N")"
129
+ if [ "$RULES_HOME_A" = "N" ] && [ "$RULES_HOME_B" = "N" ]; then _ok ".claude/rules/ not leaked to HOME (both)"
130
+ else _fail ".claude/rules/ leaked to HOME: bash=$RULES_HOME_A node=$RULES_HOME_B"; fi
131
+
132
+ printf '\n[parity] '
133
+ [ "$FAIL" -eq 0 ] && printf '\033[32mPASS\033[0m — all %d checks green\n' 8 || \
134
+ printf '\033[31mFAIL\033[0m — %d check(s) failed\n' "$FAIL"
135
+ [ "$FAIL" -eq 0 ] && exit 0 || exit 1
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT='/Users/ANISH.NEUNAHA/Desktop/Claws'
5
+ TMPDIR=$(mktemp -d)
6
+ TMPHOME=$(mktemp -d)
7
+ trap 'rm -rf "$TMPDIR" "$TMPHOME"' EXIT
8
+
9
+ fail() { echo "FAIL: $*" >&2; exit 1; }
10
+
11
+ VERSION=$(node -e "console.log(require('$ROOT/package.json').version)")
12
+
13
+ # 1. Run project injector against TMPDIR
14
+ mkdir -p "$TMPDIR/.claude/commands"
15
+ cp "$ROOT/.claude/commands/"claws*.md "$TMPDIR/.claude/commands/" 2>/dev/null || true
16
+ node "$ROOT/scripts/inject-claude-md.js" "$TMPDIR" >/dev/null
17
+ PROJECT_MD="$TMPDIR/CLAUDE.md"
18
+ [ -f "$PROJECT_MD" ] || fail "project CLAUDE.md not created"
19
+
20
+ # 2. Run global injector with HOME redirected
21
+ HOME="$TMPHOME" node "$ROOT/scripts/inject-global-claude-md.js" >/dev/null
22
+ GLOBAL_MD="$TMPHOME/.claude/CLAUDE.md"
23
+ [ -f "$GLOBAL_MD" ] || fail "global CLAUDE.md not created"
24
+
25
+ # 3. PROJECT presence assertions
26
+ for s in 'claws_done' '__CLAWS_DONE__' 'claws_fleet' 'claws_dispatch_subworker' 'claws_drain_events' 'claws_workers_wait' 'SESSION-BOOT' 'SESSION-END'; do
27
+ grep -q -- "$s" "$PROJECT_MD" || fail "project: missing required string '$s'"
28
+ done
29
+ grep -q "<!-- CLAWS:BEGIN v$VERSION -->" "$PROJECT_MD" || fail "project: missing versioned sentinel v$VERSION"
30
+
31
+ # 4. PROJECT absence assertions
32
+ for s in 'MARK_M??_OK_COLOR' 'bypass permissions' 'No marker required'; do
33
+ grep -q -- "$s" "$PROJECT_MD" && fail "project: forbidden string '$s' present" || true
34
+ done
35
+
36
+ # 5. GLOBAL presence assertions
37
+ for s in 'claws_done' '__CLAWS_DONE__' 'SESSION-BOOT' 'SESSION-END' 'FAILED' 'mode-aware'; do
38
+ grep -q -- "$s" "$GLOBAL_MD" || fail "global: missing required string '$s'"
39
+ done
40
+ grep -q "<!-- CLAWS-GLOBAL:BEGIN v$VERSION -->" "$GLOBAL_MD" || fail "global: missing versioned sentinel v$VERSION"
41
+
42
+ # 6. GLOBAL absence assertions
43
+ for s in 'MARK_M??_OK_COLOR' 'bypass permissions' 'No marker required'; do
44
+ grep -q -- "$s" "$GLOBAL_MD" && fail "global: forbidden string '$s' present" || true
45
+ done
46
+
47
+ # 7. Tool-list completeness: extracted tools must equal mcp_server.js dispatch surface
48
+ INJECTED_TOOLS=$(grep -oE 'claws_[a-z_]+' "$PROJECT_MD" | sort -u)
49
+ ACTUAL_TOOLS=$(grep -oE "name === 'claws_[a-z_]+'" "$ROOT/mcp_server.js" | grep -oE 'claws_[a-z_]+' | sort -u)
50
+ if [ "$INJECTED_TOOLS" != "$ACTUAL_TOOLS" ]; then
51
+ echo 'TOOL DRIFT:' >&2
52
+ diff <(echo "$INJECTED_TOOLS") <(echo "$ACTUAL_TOOLS") >&2 || true
53
+ fail 'project: injected tool list does not match mcp_server.js surface'
54
+ fi
55
+
56
+ # 8. Phase completeness: phases in injected block must equal Phase enum in lifecycle-store.ts
57
+ ACTUAL_PHASES=$(grep -oE "'[A-Z][A-Z0-9-]+'" "$ROOT/extension/src/lifecycle-store.ts" | head -20 | tr -d "'" | sort -u)
58
+ for phase in $ACTUAL_PHASES; do
59
+ grep -q -- "$phase" "$PROJECT_MD" || fail "project: missing phase '$phase'"
60
+ done
61
+
62
+ # 9. Idempotence: second run should report unchanged
63
+ SECOND=$(node "$ROOT/scripts/inject-claude-md.js" "$TMPDIR")
64
+ echo "$SECOND" | grep -q 'already has the current Claws block' || fail "project injector not idempotent: $SECOND"
65
+
66
+ # 10. Sentinel migration: pre-seed an old unversioned sentinel and verify it's replaced
67
+ MIG=$(mktemp -d)
68
+ mkdir -p "$MIG/.claude/commands"
69
+ printf '# old project\n\n<!-- CLAWS:BEGIN -->\nlegacy text\n<!-- CLAWS:END -->\n' > "$MIG/CLAUDE.md"
70
+ node "$ROOT/scripts/inject-claude-md.js" "$MIG" >/dev/null
71
+ BLOCKS=$(grep -c '<!-- CLAWS:BEGIN' "$MIG/CLAUDE.md" || true)
72
+ [ "$BLOCKS" = '1' ] || fail "sentinel migration: expected 1 BEGIN block, found $BLOCKS"
73
+ grep -q "<!-- CLAWS:BEGIN v$VERSION -->" "$MIG/CLAUDE.md" || fail "sentinel migration: new versioned sentinel not present after upgrade"
74
+ rm -rf "$MIG"
75
+
76
+ echo 'OK — all template enforcement checks passed (v'"$VERSION"', '"$(echo "$ACTUAL_TOOLS" | wc -l | tr -d ' ')"' tools)'
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env bash
2
+ # Claws — uninstall script
3
+ # Removes all Claws artifacts from the current project and the user's shell config.
4
+ # Idempotent: re-running is safe even if some components were already removed.
5
+ #
6
+ # Usage (from your project root or any directory):
7
+ # bash ~/.claws-src/scripts/uninstall.sh
8
+ # bash /path/to/claws/scripts/uninstall.sh
9
+ #
10
+ # What is NOT removed automatically (requires manual steps):
11
+ # - The VS Code extension — run: code --uninstall-extension neunaha.claws
12
+ # - The Claws source repo (~/.claws-src) — remove manually if desired: rm -rf ~/.claws-src
13
+
14
+ set -eo pipefail
15
+
16
+ # ── Colour helpers ────────────────────────────────────────────────────────────
17
+ if [ -t 1 ]; then
18
+ C_RESET='\033[0m'; C_BOLD='\033[1m'
19
+ C_BLUE='\033[0;34m'; C_GREEN='\033[0;32m'; C_YELLOW='\033[0;33m'; C_RED='\033[0;31m'; C_DIM='\033[2m'
20
+ else
21
+ C_RESET=''; C_BOLD=''; C_BLUE=''; C_GREEN=''; C_YELLOW=''; C_RED=''; C_DIM=''
22
+ fi
23
+
24
+ ok() { printf " ${C_GREEN}✓${C_RESET} %s\n" "$*"; }
25
+ warn() { printf " ${C_YELLOW}!${C_RESET} %s\n" "$*"; }
26
+ info() { printf " ${C_DIM}%s${C_RESET}\n" "$*"; }
27
+ step() { printf "\n${C_BOLD}${C_BLUE}%s${C_RESET}\n" "$*"; }
28
+
29
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
30
+ INSTALL_DIR="$(dirname "$SCRIPT_DIR")"
31
+ PROJECT_ROOT="${PWD}"
32
+
33
+ echo ""
34
+ printf "${C_BOLD}${C_BLUE}Claws Uninstaller${C_RESET}\n"
35
+ printf "${C_DIM}Project root: %s${C_RESET}\n" "$PROJECT_ROOT"
36
+ printf "${C_DIM}Claws source: %s${C_RESET}\n" "$INSTALL_DIR"
37
+ echo ""
38
+
39
+ # ── Step 1: Deregister lifecycle hooks from ~/.claude/settings.json ──────────
40
+ step "[1/5] Removing lifecycle hooks from ~/.claude/settings.json"
41
+ HOOKS_SCRIPT="$INSTALL_DIR/scripts/inject-settings-hooks.js"
42
+ if [ -f "$HOOKS_SCRIPT" ]; then
43
+ if node "$HOOKS_SCRIPT" --remove 2>/dev/null; then
44
+ ok "hooks deregistered"
45
+ else
46
+ warn "hook removal returned non-zero — hooks may already be absent"
47
+ fi
48
+ else
49
+ warn "inject-settings-hooks.js not found at $HOOKS_SCRIPT — skipping hook removal"
50
+ fi
51
+
52
+ # ── Step 2: Strip CLAWS:BEGIN/END blocks from CLAUDE.md files ────────────────
53
+ step "[2/5] Stripping CLAWS:BEGIN blocks from CLAUDE.md files"
54
+
55
+ strip_claws_block() {
56
+ local file="$1"
57
+ if [ ! -f "$file" ]; then
58
+ info "not found: $file — skipping"
59
+ return
60
+ fi
61
+ if ! grep -q "CLAWS:BEGIN" "$file" 2>/dev/null; then
62
+ info "no CLAWS:BEGIN block in $file — nothing to strip"
63
+ return
64
+ fi
65
+ local tmp
66
+ tmp=$(mktemp)
67
+ awk '/# CLAWS:BEGIN/,/# CLAWS:END/{next} {print}' "$file" > "$tmp" && mv "$tmp" "$file"
68
+ ok "stripped CLAWS:BEGIN block from $file"
69
+ }
70
+
71
+ strip_claws_block "$PROJECT_ROOT/CLAUDE.md"
72
+ strip_claws_block "$HOME/.claude/CLAUDE.md"
73
+
74
+ # ── Step 3: Remove shell hook source line from rc files ──────────────────────
75
+ step "[3/5] Removing shell hook from rc files"
76
+
77
+ remove_hook_from_rc() {
78
+ local rcfile="$1"
79
+ if [ ! -f "$rcfile" ]; then
80
+ return
81
+ fi
82
+ if ! grep -q "CLAWS terminal hook\|shell-hook\.sh" "$rcfile" 2>/dev/null; then
83
+ return
84
+ fi
85
+ local tmp
86
+ tmp=$(mktemp)
87
+ awk '
88
+ /# CLAWS terminal hook/ { skip=1; next }
89
+ skip && /shell-hook\.sh/ { skip=0; next }
90
+ skip { skip=0; print }
91
+ { print }
92
+ ' "$rcfile" > "$tmp" && mv "$tmp" "$rcfile"
93
+ ok "removed Claws hook from $rcfile"
94
+ }
95
+
96
+ remove_hook_from_rc "$HOME/.zshrc"
97
+ remove_hook_from_rc "$HOME/.bashrc"
98
+ remove_hook_from_rc "$HOME/.bash_profile"
99
+
100
+ FISH_CONF="${XDG_CONFIG_HOME:-$HOME/.config}/fish/conf.d/claws.fish"
101
+ if [ -f "$FISH_CONF" ]; then
102
+ rm -f "$FISH_CONF"
103
+ ok "removed $FISH_CONF"
104
+ fi
105
+
106
+ NUSHELL_ENV="${XDG_CONFIG_HOME:-$HOME/.config}/nushell/env.nu"
107
+ if [ -f "$NUSHELL_ENV" ] && grep -q "CLAWS_DIR\|claws" "$NUSHELL_ENV" 2>/dev/null; then
108
+ _nu_tmp=$(mktemp)
109
+ grep -v "CLAWS_DIR\|claws" "$NUSHELL_ENV" > "$_nu_tmp" && mv "$_nu_tmp" "$NUSHELL_ENV"
110
+ ok "removed Claws lines from $NUSHELL_ENV"
111
+ fi
112
+
113
+ # ── Step 4: Remove project-local Claws directories ───────────────────────────
114
+ step "[4/5] Removing .claws-bin/ and .claws/ from project root"
115
+
116
+ if [ -d "$PROJECT_ROOT/.claws-bin" ]; then
117
+ rm -rf "$PROJECT_ROOT/.claws-bin"
118
+ ok "removed $PROJECT_ROOT/.claws-bin"
119
+ else
120
+ info ".claws-bin/ not found — already removed"
121
+ fi
122
+
123
+ if [ -d "$PROJECT_ROOT/.claws" ]; then
124
+ rm -rf "$PROJECT_ROOT/.claws"
125
+ ok "removed $PROJECT_ROOT/.claws"
126
+ else
127
+ info ".claws/ not found — already removed"
128
+ fi
129
+
130
+ # ── Step 5: Manual step — uninstall VS Code extension ────────────────────────
131
+ step "[5/5] VS Code extension (manual step required)"
132
+ echo ""
133
+ printf " Run this command to uninstall the Claws VS Code extension:\n\n"
134
+ printf " ${C_YELLOW}NOTE: this uninstalls Claws machine-wide from your editor (not just this project).${C_RESET}\n"
135
+ printf " ${C_YELLOW}If Claws is installed in other projects you want to keep, do NOT run this.${C_RESET}\n\n"
136
+ printf " ${C_BOLD}code --uninstall-extension neunaha.claws${C_RESET}\n\n"
137
+ printf " (If you use Cursor: ${C_BOLD}cursor --uninstall-extension neunaha.claws${C_RESET})\n"
138
+ printf " (If you use Windsurf: ${C_BOLD}windsurf --uninstall-extension neunaha.claws${C_RESET})\n\n"
139
+
140
+ echo ""
141
+ ok "Claws uninstall complete."
142
+ info "You may also remove the source repo: rm -rf $INSTALL_DIR"
143
+ echo ""