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,155 @@
1
+ #!/usr/bin/env node
2
+ // Layer 1 of per-worker Monitor enforcement.
3
+ //
4
+ // Catches the gap where lifecycle.monitors[] is pre-populated by mcp_server
5
+ // atomically at spawn time, so the PostToolUse hook's monitors.some() check
6
+ // is vacuously satisfied even when no OS-level stream-events.js process is
7
+ // actually arming. This script sleeps grace_ms, then checks via pgrep whether
8
+ // a real stream-events.js --wait <corrId> process is alive. If not, it
9
+ // connects to the claws socket and publishes system.monitor.unarmed.
10
+ //
11
+ // Layer 2 (planned: hello-with-monitorCorrelationId) will close the loophole
12
+ // structurally by having stream-events.js register its corrId via hello;
13
+ // Layer 1 detects and emits a warning event.
14
+ //
15
+ // Idempotent: publishing system.monitor.unarmed twice for the same corrId is
16
+ // harmless — consumers can dedupe on correlation_id.
17
+ //
18
+ // Usage: node monitor-arm-watch.js --corr-id <uuid> --term-id <id>
19
+ // [--grace-ms <ms>] --socket <path>
20
+ // Exit codes: 0 = monitor found, 1 = unarmed (event published), 2 = error
21
+ 'use strict';
22
+
23
+ const { spawnSync } = require('child_process');
24
+ const net = require('net');
25
+
26
+ const rawArgs = process.argv.slice(2);
27
+ let corrId = null;
28
+ let termId = null;
29
+ let graceMs = 10000;
30
+ let socketPath = null;
31
+
32
+ for (let i = 0; i < rawArgs.length; i++) {
33
+ const a = rawArgs[i];
34
+ if (a === '--corr-id') { corrId = rawArgs[++i] || null; }
35
+ else if (a === '--term-id') { termId = String(rawArgs[++i] ?? ''); }
36
+ else if (a === '--grace-ms') { graceMs = parseInt(rawArgs[++i], 10) || 10000; }
37
+ else if (a === '--socket') { socketPath = rawArgs[++i] || null; }
38
+ }
39
+
40
+ if (!corrId || !socketPath) {
41
+ process.stderr.write(`monitor-arm-watch: exit 2 | reason=missing-args | corr=${corrId}\n`);
42
+ process.exit(2);
43
+ }
44
+
45
+ // Emit a startup line immediately (before the grace-period sleep) so the log
46
+ // file is non-empty even if we crash during the sleep or pgrep phases.
47
+ process.stderr.write(
48
+ `monitor-arm-watch: starting | corr=${corrId} | term=${termId}` +
49
+ ` | socket=${socketPath} | cwd=${process.cwd()}\n`
50
+ );
51
+
52
+ (async () => {
53
+ await sleep(graceMs);
54
+
55
+ let pg;
56
+ try {
57
+ pg = spawnSync('/usr/bin/pgrep', ['-f', `stream-events.js.*--wait ${corrId}`], {
58
+ encoding: 'utf8',
59
+ timeout: 5000,
60
+ });
61
+ } catch (e) {
62
+ process.stderr.write(`monitor-arm-watch: exit 2 | reason=pgrep-spawn-failed:${e.message} | corr=${corrId}\n`);
63
+ process.exit(2);
64
+ }
65
+
66
+ if (pg.error) {
67
+ process.stderr.write(`monitor-arm-watch: exit 2 | reason=pgrep-error:${pg.error.message} | corr=${corrId}\n`);
68
+ process.exit(2);
69
+ }
70
+
71
+ if (pg.status === 0) {
72
+ // Monitor process is alive — nothing to do.
73
+ process.stderr.write(`monitor-arm-watch: exit 0 | reason=monitor-found | corr=${corrId}\n`);
74
+ process.exit(0);
75
+ }
76
+
77
+ if (pg.status === 1) {
78
+ // No matching process — publish warning event.
79
+ try {
80
+ await publishUnarmed(socketPath, {
81
+ terminal_id: termId,
82
+ correlation_id: corrId,
83
+ grace_ms: graceMs,
84
+ detected_at: new Date().toISOString(),
85
+ });
86
+ } catch (e) {
87
+ process.stderr.write(`monitor-arm-watch: exit 2 | reason=socket-error:${e.message} | corr=${corrId}\n`);
88
+ process.exit(2);
89
+ }
90
+ process.stderr.write(`monitor-arm-watch: exit 1 | reason=unarmed-published | corr=${corrId}\n`);
91
+ process.exit(1);
92
+ }
93
+
94
+ process.stderr.write(`monitor-arm-watch: exit 2 | reason=pgrep-unexpected-status:${pg.status} | corr=${corrId}\n`);
95
+ process.exit(2);
96
+ })();
97
+
98
+ function publishUnarmed(sockPath, payload) {
99
+ return new Promise((resolve, reject) => {
100
+ const sock = net.createConnection(sockPath);
101
+ let buf = '';
102
+ let state = 'hello'; // 'hello' → 'publish' → done
103
+ let done = false;
104
+
105
+ const finish = (err) => {
106
+ if (done) return;
107
+ done = true;
108
+ clearTimeout(timer);
109
+ try { sock.destroy(); } catch {}
110
+ if (err) reject(err);
111
+ else resolve();
112
+ };
113
+
114
+ const timer = setTimeout(() => finish(new Error('socket timeout')), 5000);
115
+
116
+ const send = (obj) => {
117
+ const id = Math.random().toString(36).slice(2);
118
+ try { sock.write(JSON.stringify({ id, ...obj }) + '\n'); } catch (e) { finish(e); }
119
+ };
120
+
121
+ sock.on('connect', () => {
122
+ send({ cmd: 'hello', protocol: 'claws/2', peerName: 'monitor-arm-watch', role: 'observer' });
123
+ });
124
+
125
+ sock.on('data', (chunk) => {
126
+ buf += chunk.toString('utf8');
127
+ let nl;
128
+ while ((nl = buf.indexOf('\n')) !== -1) {
129
+ const line = buf.slice(0, nl).trim();
130
+ buf = buf.slice(nl + 1);
131
+ if (!line) continue;
132
+ try { JSON.parse(line); } catch { continue; }
133
+ if (state === 'hello') {
134
+ state = 'publish';
135
+ send({
136
+ cmd: 'publish', protocol: 'claws/2',
137
+ topic: 'system.monitor.unarmed',
138
+ payload,
139
+ });
140
+ } else if (state === 'publish') {
141
+ finish(null);
142
+ }
143
+ }
144
+ });
145
+
146
+ sock.on('error', (e) => finish(e));
147
+ sock.on('close', () => {
148
+ if (!done) finish(new Error('connection closed before publish ack'));
149
+ });
150
+ });
151
+ }
152
+
153
+ function sleep(ms) {
154
+ return new Promise(r => setTimeout(r, ms));
155
+ }
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env bash
2
+ # Claws — force-rebuild node-pty against VS Code's Electron ABI
3
+ #
4
+ # Run when the extension keeps saying "[claws] running in pipe-mode
5
+ # (node-pty unavailable)" and /claws-update didn't fix it. This script is
6
+ # deliberately independent of /claws-update / /claws-fix — pure bash, shows
7
+ # every step, so you (and I) can see exactly where things go wrong.
8
+ #
9
+ # Usage:
10
+ # # From repo clone:
11
+ # bash ~/.claws-src/scripts/rebuild-node-pty.sh
12
+ #
13
+ # # Direct from GitHub (no clone needed):
14
+ # bash <(curl -fsSL https://raw.githubusercontent.com/neunaha/claws/main/scripts/rebuild-node-pty.sh)
15
+ #
16
+ # Env overrides:
17
+ # CLAWS_DIR=/path Override ~/.claws-src location
18
+ # ELECTRON_VERSION=X.Y.Z Force a specific Electron target (otherwise auto-detected)
19
+
20
+ # Deliberately NOT using `set -e` — we want to continue past individual
21
+ # step failures so the user sees the full diagnostic.
22
+
23
+ INSTALL_DIR="${CLAWS_DIR:-$HOME/.claws-src}"
24
+ C_RESET=$'\033[0m'; C_BOLD=$'\033[1m'
25
+ C_GREEN=$'\033[0;32m'; C_YELLOW=$'\033[0;33m'; C_RED=$'\033[0;31m'; C_BLUE=$'\033[0;34m'; C_DIM=$'\033[2m'
26
+
27
+ h1() { printf "\n${C_BOLD}${C_BLUE}═══ %s ═══${C_RESET}\n" "$*"; }
28
+ ok() { printf " ${C_GREEN}✓${C_RESET} %s\n" "$*"; }
29
+ warn() { printf " ${C_YELLOW}!${C_RESET} %s\n" "$*"; }
30
+ bad() { printf " ${C_RED}✗${C_RESET} %s\n" "$*"; }
31
+ info() { printf " ${C_DIM}%s${C_RESET}\n" "$*"; }
32
+
33
+ # ─── 0. Pre-flight ─────────────────────────────────────────────────────────
34
+ h1 "Pre-flight"
35
+ info "PLATFORM: $(uname -s) $(uname -r) $(uname -m)"
36
+ info "node: $(command -v node 2>/dev/null) $(node --version 2>/dev/null)"
37
+ info "npm: $(command -v npm 2>/dev/null) $(npm --version 2>/dev/null)"
38
+ info "npx: $(command -v npx 2>/dev/null)"
39
+ if [ "$(uname -s)" = "Darwin" ]; then
40
+ if xcode-select -p &>/dev/null; then
41
+ ok "Xcode CLT at $(xcode-select -p)"
42
+ else
43
+ bad "Xcode Command Line Tools NOT installed"
44
+ warn "without them node-pty cannot compile. Install with:"
45
+ echo " xcode-select --install"
46
+ echo " then re-run this script."
47
+ exit 1
48
+ fi
49
+ fi
50
+
51
+ if [ ! -d "$INSTALL_DIR/extension" ]; then
52
+ bad "Claws source not found at $INSTALL_DIR/extension"
53
+ warn "install Claws first: bash <(curl -fsSL https://raw.githubusercontent.com/neunaha/claws/main/scripts/install.sh)"
54
+ exit 1
55
+ fi
56
+ ok "source clone at $INSTALL_DIR"
57
+
58
+ NPTY_DIR="$INSTALL_DIR/extension/node_modules/node-pty"
59
+ NPTY_BIN="$NPTY_DIR/build/Release/pty.node"
60
+ ABI_FILE="$INSTALL_DIR/extension/dist/.electron-abi"
61
+
62
+ # ─── 1. Detect VS Code's Electron version ──────────────────────────────────
63
+ h1 "Detect VS Code Electron version"
64
+ if [ -n "$ELECTRON_VERSION" ]; then
65
+ info "ELECTRON_VERSION env override: $ELECTRON_VERSION"
66
+ elif [ "$(uname -s)" = "Darwin" ]; then
67
+ # M-36: TERM_PROGRAM-aware ordering + CURSOR_CHANNEL secondary signal.
68
+ _rn_tp="${TERM_PROGRAM:-}"
69
+ [ "$_rn_tp" = "vscode" ] && [ -n "${CURSOR_CHANNEL:-}" ] && _rn_tp="cursor"
70
+ _rn_tp=$(echo "$_rn_tp" | tr '[:upper:]' '[:lower:]')
71
+ case "$_rn_tp" in
72
+ cursor) _rn_darwin_apps=('/Applications/Cursor.app' '/Applications/Visual Studio Code.app' '/Applications/Visual Studio Code - Insiders.app' '/Applications/Windsurf.app') ;;
73
+ windsurf) _rn_darwin_apps=('/Applications/Windsurf.app' '/Applications/Visual Studio Code.app' '/Applications/Visual Studio Code - Insiders.app' '/Applications/Cursor.app') ;;
74
+ *) _rn_darwin_apps=('/Applications/Visual Studio Code.app' '/Applications/Visual Studio Code - Insiders.app' '/Applications/Cursor.app' '/Applications/Windsurf.app') ;;
75
+ esac
76
+ for _rn_app in "${_rn_darwin_apps[@]}"; do
77
+ plist="$_rn_app/Contents/Frameworks/Electron Framework.framework/Resources/Info.plist"
78
+ if [ -f "$plist" ]; then
79
+ v=$(plutil -extract CFBundleVersion raw "$plist" 2>/dev/null || true)
80
+ if [ -n "$v" ]; then
81
+ ELECTRON_VERSION="$v"
82
+ ok "$(basename "$_rn_app") uses Electron $v"
83
+ break
84
+ fi
85
+ else
86
+ info "$(basename "$_rn_app"): not installed"
87
+ fi
88
+ done
89
+ elif [ "$(uname -s)" = "Linux" ]; then
90
+ # M-36: Linux Cursor/Windsurf paths + TERM_PROGRAM ordering.
91
+ _rn_tp="${TERM_PROGRAM:-}"
92
+ [ "$_rn_tp" = "vscode" ] && [ -n "${CURSOR_CHANNEL:-}" ] && _rn_tp="cursor"
93
+ _rn_tp=$(echo "$_rn_tp" | tr '[:upper:]' '[:lower:]')
94
+ _rn_linux_vscode=('/usr/share/code/electron' '/usr/lib/code/electron' '/opt/visual-studio-code/electron' '/snap/code/current/electron')
95
+ _rn_linux_cursor=('/usr/share/cursor/electron' '/opt/cursor/electron' '/snap/cursor/current/usr/share/cursor/electron')
96
+ _rn_linux_windsurf=('/usr/share/windsurf/electron' '/opt/windsurf/electron')
97
+ _rn_linux_candidates=()
98
+ case "$_rn_tp" in
99
+ cursor) _rn_linux_candidates=("${_rn_linux_cursor[@]}" "${_rn_linux_vscode[@]}" "${_rn_linux_windsurf[@]}") ;;
100
+ windsurf) _rn_linux_candidates=("${_rn_linux_windsurf[@]}" "${_rn_linux_vscode[@]}" "${_rn_linux_cursor[@]}") ;;
101
+ *) _rn_linux_candidates=("${_rn_linux_vscode[@]}" "${_rn_linux_cursor[@]}" "${_rn_linux_windsurf[@]}") ;;
102
+ esac
103
+ for _rn_ep in "${_rn_linux_candidates[@]}"; do
104
+ if [ -x "$_rn_ep" ]; then
105
+ v=$("$_rn_ep" --version 2>/dev/null | sed 's/^v//' || true)
106
+ if echo "$v" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
107
+ ELECTRON_VERSION="$v"
108
+ ok "detected Electron $v from $_rn_ep"
109
+ break
110
+ fi
111
+ fi
112
+ done
113
+ fi
114
+ if [ -z "$ELECTRON_VERSION" ]; then
115
+ ELECTRON_VERSION="39.8.5"
116
+ warn "couldn't auto-detect — using fallback $ELECTRON_VERSION"
117
+ fi
118
+
119
+ # ─── 2. Current binary state ───────────────────────────────────────────────
120
+ h1 "Current node-pty state"
121
+ if [ -d "$NPTY_DIR" ]; then
122
+ ok "node-pty package installed at $NPTY_DIR"
123
+ pkg_ver=$(node -p "require('$NPTY_DIR/package.json').version" 2>/dev/null)
124
+ info "node-pty version: $pkg_ver"
125
+ else
126
+ warn "node-pty not installed — running npm install first"
127
+ ( cd "$INSTALL_DIR/extension" && npm install --no-audit --no-fund --loglevel=error 2>&1 | tail -5 )
128
+ fi
129
+
130
+ if [ -f "$NPTY_BIN" ]; then
131
+ size=$(wc -c < "$NPTY_BIN" | tr -d ' ')
132
+ mtime=$(stat -f '%Sm' "$NPTY_BIN" 2>/dev/null || stat -c '%y' "$NPTY_BIN" 2>/dev/null)
133
+ info "binary exists: $size bytes, mtime $mtime"
134
+ else
135
+ warn "binary NOT present at $NPTY_BIN"
136
+ fi
137
+
138
+ if [ -f "$ABI_FILE" ]; then
139
+ last_abi=$(cat "$ABI_FILE")
140
+ info "last-built ABI: Electron $last_abi"
141
+ if [ "$last_abi" = "$ELECTRON_VERSION" ]; then
142
+ ok "recorded ABI matches current Electron"
143
+ else
144
+ warn "recorded ABI '$last_abi' ≠ current Electron '$ELECTRON_VERSION' — rebuild needed"
145
+ fi
146
+ else
147
+ warn "no ABI marker — treating as stale"
148
+ fi
149
+
150
+ # ─── 3. Force-rebuild with @electron/rebuild ───────────────────────────────
151
+ h1 "Rebuilding node-pty for Electron $ELECTRON_VERSION"
152
+ info "removing old binary + ABI marker to force a clean rebuild"
153
+ rm -f "$NPTY_BIN" "$ABI_FILE" 2>/dev/null
154
+
155
+ info "running: npx --yes @electron/rebuild --version=$ELECTRON_VERSION --only=node-pty --force"
156
+ # M-36: 5-minute timeout ceiling — prevents indefinite hang on slow Electron header fetch.
157
+ _rn_timeout_cmd=""
158
+ if command -v timeout >/dev/null 2>&1; then _rn_timeout_cmd="timeout 300"
159
+ elif command -v gtimeout >/dev/null 2>&1; then _rn_timeout_cmd="gtimeout 300"
160
+ fi
161
+ rebuild_log=$(mktemp -t claws-rebuild.XXXXXX.log)
162
+ if ( cd "$INSTALL_DIR/extension" && $_rn_timeout_cmd npx --yes @electron/rebuild --version="$ELECTRON_VERSION" --only=node-pty --force ) >"$rebuild_log" 2>&1; then
163
+ ok "@electron/rebuild completed"
164
+ info "log: $rebuild_log (last 5 lines below)"
165
+ tail -5 "$rebuild_log" | sed 's/^/ /'
166
+ else
167
+ _rn_rc=$?
168
+ if [ "$_rn_rc" = "124" ]; then
169
+ bad "@electron/rebuild timed out after 5 min — likely a slow Electron headers download. Check network / proxy settings."
170
+ else
171
+ bad "@electron/rebuild FAILED"
172
+ fi
173
+ info "full log: $rebuild_log"
174
+ info "last 20 lines:"
175
+ tail -20 "$rebuild_log" | sed 's/^/ /'
176
+ echo ""
177
+ bad "cannot proceed — node-pty will not work until the build succeeds"
178
+ exit 1
179
+ fi
180
+
181
+ # ─── 4. Verify the new binary ──────────────────────────────────────────────
182
+ h1 "Verify new binary"
183
+ if [ ! -f "$NPTY_BIN" ]; then
184
+ bad "rebuild reported success but $NPTY_BIN is still missing"
185
+ exit 1
186
+ fi
187
+ new_size=$(wc -c < "$NPTY_BIN" | tr -d ' ')
188
+ new_mtime=$(stat -f '%Sm' "$NPTY_BIN" 2>/dev/null || stat -c '%y' "$NPTY_BIN" 2>/dev/null)
189
+ ok "binary: $NPTY_BIN ($new_size bytes, mtime $new_mtime)"
190
+
191
+ echo "$ELECTRON_VERSION" > "$ABI_FILE"
192
+ ok "recorded Electron ABI → $ABI_FILE"
193
+
194
+ # ─── 5. ABI sanity check ───────────────────────────────────────────────────
195
+ # We can't fully simulate Electron's Node from a plain shell. But we can
196
+ # detect the NODE_MODULE_VERSION embedded in the .node binary by looking at
197
+ # raw bytes. Matching ABIs:
198
+ # Node 22 (Electron 39) → NODE_MODULE_VERSION=127
199
+ # Node 24 (system) → NODE_MODULE_VERSION=131
200
+ h1 "ABI sanity check"
201
+ info "system node would load the binary as:"
202
+ sys_load=$(node -e "try{require('$NPTY_BIN');console.log('LOADS IN SYSTEM NODE')}catch(e){console.log('FAILS IN SYSTEM NODE:',e.message.split('\\n')[0])}" 2>&1)
203
+ info " $sys_load"
204
+
205
+ if echo "$sys_load" | grep -q "FAILS IN SYSTEM NODE.*NODE_MODULE_VERSION"; then
206
+ ok "binary REJECTED by system Node — this is the GOOD sign"
207
+ info " it means the binary's ABI doesn't match system Node 24,"
208
+ info " which is exactly what we want for Electron 39 (Node 22)"
209
+ elif echo "$sys_load" | grep -q "LOADS IN SYSTEM NODE"; then
210
+ if [ "$ELECTRON_VERSION" = "39.8.5" ] || echo "$ELECTRON_VERSION" | grep -qE "^(3[0-9]|4[0-9])\."; then
211
+ bad "binary LOADS in system Node — that's WRONG for Electron $ELECTRON_VERSION"
212
+ warn "it means the rebuild actually targeted system Node, not Electron."
213
+ warn "this is the bug that keeps pipe-mode warnings active. Try:"
214
+ echo ""
215
+ echo " cd $INSTALL_DIR/extension"
216
+ echo " rm -rf node_modules/node-pty"
217
+ echo " npm install node-pty"
218
+ echo " npx @electron/rebuild --version=$ELECTRON_VERSION --only=node-pty --force"
219
+ echo ""
220
+ exit 1
221
+ else
222
+ ok "binary loads in system Node (expected for Electron versions that share Node ABI)"
223
+ fi
224
+ else
225
+ warn "couldn't interpret result: $sys_load"
226
+ fi
227
+
228
+ # ─── 6. Done ───────────────────────────────────────────────────────────────
229
+ h1 "Next step"
230
+ echo ""
231
+ printf " ${C_BOLD}1. Reload VS Code${C_RESET}\n"
232
+ echo " Cmd+Shift+P → Developer: Reload Window"
233
+ echo ""
234
+ printf " ${C_BOLD}2. Open the Output panel${C_RESET}\n"
235
+ echo " Cmd+Shift+U → dropdown → 'Claws'"
236
+ echo " You should see 'activating (typescript)' with NO pipe-mode warning."
237
+ echo ""
238
+ printf " ${C_BOLD}3. Spawn a wrapped terminal${C_RESET}\n"
239
+ echo " + dropdown → 'Claws Wrapped Terminal'"
240
+ echo " Should open cleanly, no '[claws] running in pipe-mode' line."
241
+ echo ""
242
+ printf " ${C_BOLD}If it still shows pipe-mode after reload:${C_RESET}\n"
243
+ echo " bash $INSTALL_DIR/scripts/report.sh \"\$(pwd)\""
244
+ echo " and share the generated ~/claws-report-*.txt"
245
+ echo ""
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env bash
2
+ # Claws — diagnostic bundle generator
3
+ # Usage: bash ~/.claws-src/scripts/report.sh [project-dir]
4
+ #
5
+ # Produces a single file with everything needed to diagnose install/runtime
6
+ # issues: OS info, Node version, install log, extension state, VS Code
7
+ # extension logs, project-local file presence. The output file path is
8
+ # printed at the end — share it in a GitHub issue.
9
+
10
+ set -eo pipefail
11
+
12
+ PROJECT_ROOT="${1:-$(pwd)}"
13
+ INSTALL_DIR="${CLAWS_DIR:-$HOME/.claws-src}"
14
+ REPORT_DIR="${CLAWS_REPORT_DIR:-$HOME}"
15
+ REPORT="$REPORT_DIR/claws-report-$(date +%Y%m%d-%H%M%S).txt"
16
+
17
+ redact() {
18
+ # Replace the user's home path with $HOME to reduce leaked info in pastes.
19
+ # Also strip anything that looks like a token/key.
20
+ sed -e "s|$HOME|\$HOME|g" \
21
+ -e 's|[A-Za-z0-9]\{32,\}|<redacted>|g'
22
+ }
23
+
24
+ say() { echo "$@" >> "$REPORT"; }
25
+ section() { echo "" >> "$REPORT"; echo "═════ $1 ═════" >> "$REPORT"; }
26
+
27
+ {
28
+ echo "Claws Diagnostic Report"
29
+ echo "Generated: $(date)"
30
+ echo "Project: $PROJECT_ROOT"
31
+ echo "Install: $INSTALL_DIR"
32
+ } > "$REPORT"
33
+
34
+ section "System dependencies"
35
+ PLATFORM="$(uname -s)"
36
+ say "OS: $(uname -a)"
37
+ say "Shell: $SHELL ($(basename "$SHELL"))"
38
+ say "bash: $BASH_VERSION"
39
+ say ""
40
+ say "git: $(command -v git 2>/dev/null || echo '—') $(git --version 2>/dev/null || echo 'NOT INSTALLED')"
41
+ say "node: $(command -v node 2>/dev/null || echo '—') $(node --version 2>/dev/null || echo 'NOT INSTALLED')"
42
+ if command -v node &>/dev/null; then
43
+ nm=$(node -e "console.log(process.versions.node.split('.')[0])" 2>/dev/null)
44
+ say "node major: $nm$([ "$nm" -lt 18 ] 2>/dev/null && echo ' (TOO OLD — requires 18+)')"
45
+ fi
46
+ say "npm: $(command -v npm 2>/dev/null || echo '—') $(npm --version 2>/dev/null || echo 'NOT INSTALLED')"
47
+ say "python3: $(command -v python3 2>/dev/null || echo '—') $(python3 --version 2>&1 | awk '{print $2}' || echo 'NOT INSTALLED')"
48
+ say "npx: $(command -v npx 2>/dev/null || echo '—')"
49
+ case "$PLATFORM" in
50
+ Darwin)
51
+ say "xcode-select: $(xcode-select -p 2>/dev/null || echo 'NOT INSTALLED')"
52
+ ;;
53
+ Linux)
54
+ say "g++: $(command -v g++ 2>/dev/null || echo '—') $(g++ -dumpversion 2>/dev/null || echo '')"
55
+ say "make: $(command -v make 2>/dev/null || echo '—')"
56
+ ;;
57
+ esac
58
+
59
+ section "Editor CLIs"
60
+ for pair in \
61
+ "code:/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" \
62
+ "code-insiders:/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code-insiders" \
63
+ "cursor:/Applications/Cursor.app/Contents/Resources/app/bin/cursor" \
64
+ "windsurf:/Applications/Windsurf.app/Contents/Resources/app/bin/windsurf"; do
65
+ label="${pair%%:*}"
66
+ bundled="${pair#*:}"
67
+ if command -v "$label" &>/dev/null; then
68
+ say " ✓ $label ($(command -v "$label")) — $("$label" --version 2>&1 | head -1)"
69
+ elif [ -x "$bundled" ]; then
70
+ say " ✓ $label (bundled: $bundled) — $("$bundled" --version 2>&1 | head -1)"
71
+ else
72
+ say " — $label not found"
73
+ fi
74
+ done
75
+
76
+ section "Claws source clone"
77
+ if [ -d "$INSTALL_DIR/.git" ]; then
78
+ say "Exists: $INSTALL_DIR"
79
+ say "HEAD: $(cd "$INSTALL_DIR" && git log --oneline -1 2>/dev/null)"
80
+ say "Branch: $(cd "$INSTALL_DIR" && git rev-parse --abbrev-ref HEAD 2>/dev/null)"
81
+ say "Remote: $(cd "$INSTALL_DIR" && git config --get remote.origin.url 2>/dev/null)"
82
+ else
83
+ say "MISSING: $INSTALL_DIR (no git clone — installer may have failed at step 1)"
84
+ fi
85
+
86
+ section "Extension manifest + bundle"
87
+ if [ -f "$INSTALL_DIR/extension/package.json" ]; then
88
+ say "Version: $(node -e "console.log(require('$INSTALL_DIR/extension/package.json').version)" 2>/dev/null)"
89
+ say "Main: $(node -e "console.log(require('$INSTALL_DIR/extension/package.json').main)" 2>/dev/null)"
90
+ say "Bundle: $([ -f "$INSTALL_DIR/extension/dist/extension.js" ] && wc -c < "$INSTALL_DIR/extension/dist/extension.js" | tr -d ' ' || echo 'MISSING')"
91
+ say "Build SHA: $(cat "$INSTALL_DIR/extension/dist/.build-sha" 2>/dev/null || echo 'unknown')"
92
+
93
+ # Detailed node-pty state: installed? binary built? against which ABI?
94
+ # The Electron version matters — a binary built against system Node
95
+ # loads from `node` fine but fails inside VS Code's extension host.
96
+ NPTY_DIR="$INSTALL_DIR/extension/node_modules/node-pty"
97
+ NPTY_BIN="$NPTY_DIR/build/Release/pty.node"
98
+ NPTY_ABI=$(cat "$INSTALL_DIR/extension/dist/.electron-abi" 2>/dev/null || echo "unknown")
99
+ if [ -f "$NPTY_BIN" ]; then
100
+ say "node-pty: ✓ installed, binary present ($(wc -c < "$NPTY_BIN" | tr -d ' ') bytes)"
101
+ say " built for Electron $NPTY_ABI"
102
+ elif [ -d "$NPTY_DIR" ]; then
103
+ say "node-pty: ✗ installed but BINARY MISSING — extension in pipe-mode fallback"
104
+ say " fix with: /claws-fix"
105
+ else
106
+ say "node-pty: ✗ not installed — pipe-mode fallback active"
107
+ fi
108
+ else
109
+ say "MISSING manifest at $INSTALL_DIR/extension/package.json"
110
+ fi
111
+
112
+ section "Editor extension symlinks"
113
+ for dir in "$HOME/.vscode/extensions" "$HOME/.vscode-insiders/extensions" "$HOME/.cursor/extensions" "$HOME/.windsurf/extensions"; do
114
+ if [ -d "$dir" ]; then
115
+ matches=$(find "$dir" -maxdepth 1 -name 'neunaha.claws-*' 2>/dev/null)
116
+ if [ -n "$matches" ]; then
117
+ echo "$dir:" >> "$REPORT"
118
+ ls -la "$dir" 2>/dev/null | grep 'neunaha.claws' >> "$REPORT" 2>&1 || true
119
+ fi
120
+ fi
121
+ done
122
+
123
+ section "Project-local files"
124
+ for f in .mcp.json .claws-bin/mcp_server.js .claws-bin/shell-hook.sh .vscode/extensions.json CLAUDE.md; do
125
+ if [ -e "$PROJECT_ROOT/$f" ]; then
126
+ say " ✓ $f ($(wc -c < "$PROJECT_ROOT/$f" | tr -d ' ') bytes)"
127
+ else
128
+ say " ✗ $f MISSING"
129
+ fi
130
+ done
131
+ # Confirm neunaha.claws is in the recommendations list specifically
132
+ if [ -f "$PROJECT_ROOT/.vscode/extensions.json" ]; then
133
+ if grep -q "neunaha.claws" "$PROJECT_ROOT/.vscode/extensions.json" 2>/dev/null; then
134
+ say " ✓ .vscode/extensions.json recommends neunaha.claws"
135
+ else
136
+ say " ✗ .vscode/extensions.json MISSING claws recommendation"
137
+ fi
138
+ fi
139
+ for d in .claude/commands .claude/rules .claude/skills; do
140
+ if [ -d "$PROJECT_ROOT/$d" ]; then
141
+ count=$(find "$PROJECT_ROOT/$d" -type f 2>/dev/null | wc -l | tr -d ' ')
142
+ say " ✓ $d/ ($count files)"
143
+ else
144
+ say " ✗ $d/ MISSING"
145
+ fi
146
+ done
147
+
148
+ section "Socket state"
149
+ SOCK_PATH="$PROJECT_ROOT/.claws/claws.sock"
150
+ if [ -S "$SOCK_PATH" ]; then
151
+ say "Socket: $SOCK_PATH"
152
+ say "Mtime: $(stat -f '%Sm' "$SOCK_PATH" 2>/dev/null || stat -c '%y' "$SOCK_PATH" 2>/dev/null)"
153
+ if command -v nc &>/dev/null; then
154
+ if echo '{"id":1,"cmd":"list"}' | nc -U "$SOCK_PATH" 2>/dev/null | head -c 200 | grep -q '"ok"'; then
155
+ say "Status: LIVE — extension is listening"
156
+ else
157
+ say "Status: STALE — file exists but no process listening"
158
+ fi
159
+ fi
160
+ else
161
+ say "Socket: NOT PRESENT at $SOCK_PATH (extension not activated in this project yet)"
162
+ fi
163
+
164
+ section "MCP server handshake test"
165
+ MCP_PATH="$INSTALL_DIR/mcp_server.js"
166
+ [ -f "$PROJECT_ROOT/.claws-bin/mcp_server.js" ] && MCP_PATH="$PROJECT_ROOT/.claws-bin/mcp_server.js"
167
+ if command -v node &>/dev/null && [ -f "$MCP_PATH" ]; then
168
+ say "Testing: $MCP_PATH"
169
+ HANDSHAKE=$(node --no-deprecation -e '
170
+ const { spawn } = require("child_process");
171
+ const mcp = spawn("node", [process.argv[1]], { stdio: ["pipe", "pipe", "ignore"] });
172
+ const req = JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: {} });
173
+ const msg = `Content-Length: ${Buffer.byteLength(req)}\r\n\r\n${req}`;
174
+ let buf = "";
175
+ const done = (c, o) => { try { mcp.kill(); } catch {}; process.stdout.write(o); process.exit(c); };
176
+ const timer = setTimeout(() => done(1, "TIMEOUT"), 4000);
177
+ mcp.stdout.on("data", d => { buf += d.toString("utf8"); if (buf.includes("claws")) { clearTimeout(timer); done(0, buf.slice(0, 200)); } });
178
+ mcp.on("error", e => { clearTimeout(timer); done(1, "SPAWN_ERROR: " + e.message); });
179
+ mcp.stdin.write(msg);
180
+ ' "$MCP_PATH" 2>&1)
181
+ say "Response: ${HANDSHAKE:0:300}"
182
+ fi
183
+
184
+ section "Shell hook state"
185
+ for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile"; do
186
+ if [ -f "$rc" ]; then
187
+ if grep -q "CLAWS terminal hook" "$rc" 2>/dev/null; then
188
+ say " ✓ $(basename "$rc") — hook installed"
189
+ else
190
+ say " ✗ $(basename "$rc") — hook NOT installed"
191
+ fi
192
+ fi
193
+ done
194
+
195
+ section "Latest install log"
196
+ LATEST_LOG=$(ls -t /tmp/claws-install-*.log 2>/dev/null | head -1)
197
+ if [ -n "$LATEST_LOG" ] && [ -f "$LATEST_LOG" ]; then
198
+ say "File: $LATEST_LOG"
199
+ say "Last 100 lines:"
200
+ tail -100 "$LATEST_LOG" | redact >> "$REPORT"
201
+ else
202
+ say "No recent install log found in /tmp/claws-install-*.log"
203
+ fi
204
+
205
+ section "VS Code extension host logs (claws only, last 50 matches)"
206
+ LOGDIR=$(ls -dt "$HOME/Library/Application Support/Code/logs/"*/ 2>/dev/null | head -1)
207
+ if [ -n "$LOGDIR" ] && [ -d "$LOGDIR" ]; then
208
+ say "From: $LOGDIR"
209
+ grep -r "claws\|neunaha" "$LOGDIR" 2>/dev/null | tail -50 | redact >> "$REPORT" || say "(no claws references)"
210
+ else
211
+ say "No VS Code logs found at ~/Library/Application Support/Code/logs/"
212
+ fi
213
+
214
+ section "Report location"
215
+ echo "" >> "$REPORT"
216
+ echo "Share this file in an issue: https://github.com/neunaha/claws/issues/new" >> "$REPORT"
217
+ echo "File path: $REPORT" >> "$REPORT"
218
+
219
+ # Print to stdout for the caller
220
+ echo ""
221
+ echo "╔════════════════════════════════════════════════════════╗"
222
+ echo "║ Claws diagnostic report written to: ║"
223
+ echo "╚════════════════════════════════════════════════════════╝"
224
+ echo " $REPORT"
225
+ echo ""
226
+ echo "Summary of checks:"
227
+ grep -E '✓|✗|MISSING|STALE|LIVE|Status:' "$REPORT" | head -30 | sed 's/^/ /'
228
+ echo ""
229
+ echo "Next steps:"
230
+ echo " 1. Open the report: cat \"$REPORT\""
231
+ echo " 2. Share it: https://github.com/neunaha/claws/issues/new"
232
+ echo " 3. Or paste contents into a new issue — HOMEDIR already redacted."