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,92 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { dryRunLog } = require('./platform.js');
6
+
7
+ // Strip JSONC extensions (line/block comments, trailing commas) from text before
8
+ // JSON.parse. Handles .mcp.json files with VS Code JSONC comments.
9
+ // Matches install.sh json-safe.mjs approach (simple regex version).
10
+ function _stripJsonc(text) {
11
+ return text
12
+ .replace(/\/\*[\s\S]*?\*\//g, '')
13
+ .replace(/\/\/[^\n]*/g, '')
14
+ .replace(/,(\s*[}\]])/g, '$1');
15
+ }
16
+
17
+ /**
18
+ * Idempotently write (or update) the claws entry in <projectRoot>/.mcp.json.
19
+ * Preserves all other mcpServers entries. Atomic write (tmp + rename).
20
+ * Uses absolute path for args so the MCP server resolves correctly regardless
21
+ * of the cwd from which Claude Code is launched (matches install.sh behavior).
22
+ * @param {string} projectRoot
23
+ * @param {boolean} [dryRun]
24
+ */
25
+ function writeMcpJson(projectRoot, dryRun = false) {
26
+ const mcpPath = path.join(projectRoot, '.mcp.json');
27
+
28
+ if (dryRun) {
29
+ dryRunLog(`merge claws entry into ${mcpPath}`);
30
+ return;
31
+ }
32
+
33
+ let config = {};
34
+ if (fs.existsSync(mcpPath)) {
35
+ try {
36
+ config = JSON.parse(_stripJsonc(fs.readFileSync(mcpPath, 'utf8')));
37
+ } catch {
38
+ process.stderr.write(` ! .mcp.json is malformed — preserving original, skipping merge\n`);
39
+ return;
40
+ }
41
+ }
42
+
43
+ if (!config.mcpServers) config.mcpServers = {};
44
+ config.mcpServers.claws = {
45
+ command: 'node',
46
+ args: [path.join(projectRoot, '.claws-bin', 'mcp_server.js')],
47
+ };
48
+
49
+ const tmp = mcpPath + '.claws-tmp.' + process.pid;
50
+ fs.writeFileSync(tmp, JSON.stringify(config, null, 2) + '\n', 'utf8');
51
+ fs.renameSync(tmp, mcpPath);
52
+
53
+ // W7h-27: re-read and parse after rename to catch any FS-level corruption.
54
+ // Matches install.sh:1090-1093.
55
+ try {
56
+ JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
57
+ } catch (e) {
58
+ process.stderr.write(` ! .mcp.json post-write validation failed: ${e.message}\n`);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Remove the claws entry from <projectRoot>/.mcp.json (uninstall path).
64
+ * @param {string} projectRoot
65
+ * @param {boolean} [dryRun]
66
+ */
67
+ function removeMcpEntry(projectRoot, dryRun = false) {
68
+ const mcpPath = path.join(projectRoot, '.mcp.json');
69
+ if (!fs.existsSync(mcpPath)) return;
70
+
71
+ if (dryRun) {
72
+ dryRunLog(`remove claws entry from ${mcpPath}`);
73
+ return;
74
+ }
75
+
76
+ let config;
77
+ try {
78
+ config = JSON.parse(_stripJsonc(fs.readFileSync(mcpPath, 'utf8')));
79
+ } catch {
80
+ process.stderr.write(` ! .mcp.json is malformed — skipping\n`);
81
+ return;
82
+ }
83
+
84
+ if (config.mcpServers && config.mcpServers.claws) {
85
+ delete config.mcpServers.claws;
86
+ const tmp = mcpPath + '.claws-tmp.' + process.pid;
87
+ fs.writeFileSync(tmp, JSON.stringify(config, null, 2) + '\n', 'utf8');
88
+ fs.renameSync(tmp, mcpPath);
89
+ }
90
+ }
91
+
92
+ module.exports = { writeMcpJson, removeMcpEntry };
@@ -0,0 +1,240 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const { spawnSync, execSync } = require('child_process');
7
+
8
+ /**
9
+ * Find all installed editor CLIs (VS Code, Code Insiders, Cursor, Windsurf).
10
+ * Returns an array of { label, cliPath } for each editor found on the system.
11
+ * Mirrors install.sh's `for label in code code-insiders cursor windsurf` loop.
12
+ *
13
+ * Search order per editor:
14
+ * 1. CLAWS_VSCODE_CLI env override (code label only)
15
+ * 2. PATH lookup via which/where
16
+ * 3. Platform-specific known bundle/package locations
17
+ *
18
+ * @param {object} [_opts] - Internal overrides for testing only
19
+ * @returns {{ label: string, cliPath: string }[]}
20
+ */
21
+ function findAllEditorClis(_opts = {}) {
22
+ const platform = _opts.platform !== undefined ? _opts.platform : process.platform;
23
+ const env = _opts.env !== undefined ? _opts.env : process.env;
24
+ const existsFn = _opts.existsFn !== undefined ? _opts.existsFn : fs.existsSync;
25
+ const spawnFn = _opts.spawnFn !== undefined ? _opts.spawnFn : spawnSync;
26
+
27
+ const localApp = env.LOCALAPPDATA || '';
28
+ const programFiles = env.ProgramFiles || 'C:\\Program Files';
29
+ const whichCmd = platform === 'win32' ? 'where' : 'which';
30
+
31
+ const EDITORS = [
32
+ {
33
+ label: 'code',
34
+ macBundle: '/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code',
35
+ linux: ['/usr/bin/code', '/snap/bin/code'],
36
+ win: [
37
+ path.join(localApp, 'Programs', 'Microsoft VS Code', 'bin', 'Code.cmd'),
38
+ path.join(programFiles, 'Microsoft VS Code', 'bin', 'Code.cmd'),
39
+ ],
40
+ },
41
+ {
42
+ label: 'code-insiders',
43
+ macBundle: '/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/bin/code-insiders',
44
+ linux: ['/usr/bin/code-insiders', '/snap/bin/code-insiders'],
45
+ win: [
46
+ path.join(localApp, 'Programs', 'Microsoft VS Code Insiders', 'bin', 'Code - Insiders.cmd'),
47
+ path.join(programFiles, 'Microsoft VS Code Insiders', 'bin', 'Code - Insiders.cmd'),
48
+ ],
49
+ },
50
+ {
51
+ label: 'cursor',
52
+ macBundle: '/Applications/Cursor.app/Contents/Resources/app/bin/cursor',
53
+ linux: ['/usr/bin/cursor', '/snap/bin/cursor', '/usr/local/bin/cursor'],
54
+ win: [
55
+ path.join(localApp, 'Programs', 'cursor', 'cursor.cmd'),
56
+ path.join(localApp, 'cursor', 'resources', 'app', 'bin', 'cursor.cmd'),
57
+ ],
58
+ },
59
+ {
60
+ label: 'windsurf',
61
+ macBundle: '/Applications/Windsurf.app/Contents/Resources/app/bin/windsurf',
62
+ linux: ['/usr/bin/windsurf', '/snap/bin/windsurf', '/usr/local/bin/windsurf'],
63
+ win: [
64
+ path.join(localApp, 'Programs', 'Windsurf', 'windsurf.cmd'),
65
+ ],
66
+ },
67
+ ];
68
+
69
+ const results = [];
70
+ for (const editor of EDITORS) {
71
+ let found = null;
72
+
73
+ // 1. CLAWS_VSCODE_CLI env override (code label only — backward compat)
74
+ if (editor.label === 'code') {
75
+ const override = env.CLAWS_VSCODE_CLI;
76
+ if (override && existsFn(override)) found = override;
77
+ }
78
+
79
+ // 2. PATH lookup
80
+ if (!found) {
81
+ const r = spawnFn(whichCmd, [editor.label], { encoding: 'utf8', stdio: 'pipe' });
82
+ if (r.status === 0 && r.stdout) {
83
+ const first = r.stdout.trim().split('\n')[0];
84
+ if (first) found = first;
85
+ }
86
+ }
87
+
88
+ // 3. Platform-specific known bundle/package locations
89
+ if (!found) {
90
+ const candidates = platform === 'darwin'
91
+ ? (editor.macBundle ? [editor.macBundle] : [])
92
+ : platform === 'win32'
93
+ ? editor.win
94
+ : editor.linux;
95
+ for (const c of candidates) {
96
+ if (existsFn(c)) { found = c; break; }
97
+ }
98
+ }
99
+
100
+ if (found) results.push({ label: editor.label, cliPath: found });
101
+ }
102
+
103
+ return results;
104
+ }
105
+
106
+ /**
107
+ * Find the VS Code CLI executable path.
108
+ * Kept for backward compatibility — returns the first match from findAllEditorClis().
109
+ *
110
+ * @param {object} [_opts] - Internal overrides for testing only
111
+ * @returns {string|null}
112
+ */
113
+ function findCodeCli(_opts = {}) {
114
+ const env = _opts.env !== undefined ? _opts.env : process.env;
115
+ const existsFn = _opts.existsFn !== undefined ? _opts.existsFn : fs.existsSync;
116
+
117
+ // Env override takes precedence (retained for backward compat)
118
+ const override = env.CLAWS_VSCODE_CLI;
119
+ if (override && existsFn(override)) return override;
120
+
121
+ const found = findAllEditorClis(_opts);
122
+ return found.length > 0 ? found[0].cliPath : null;
123
+ }
124
+
125
+ /**
126
+ * Returns true if homeDir contains "OneDrive".
127
+ * @param {string} homeDir
128
+ * @returns {boolean}
129
+ */
130
+ function detectOneDrivePath(homeDir) {
131
+ return homeDir.includes('OneDrive');
132
+ }
133
+
134
+ /**
135
+ * Returns a warning string when homeDir is suspiciously long (>100 chars) or
136
+ * OneDrive-rooted; returns null otherwise.
137
+ * @param {string} homeDir
138
+ * @returns {string|null}
139
+ */
140
+ function longPathPreflight(homeDir) {
141
+ if (homeDir.length > 100) {
142
+ return `Warning: home directory path is very long (${homeDir.length} chars). Node on Windows may hit the 260-character path limit.`;
143
+ }
144
+ if (detectOneDrivePath(homeDir)) {
145
+ return `Warning: home directory appears to be OneDrive-synced (${homeDir}). Use fs.realpathSync() on all install paths to resolve symlinks.`;
146
+ }
147
+ return null;
148
+ }
149
+
150
+ /**
151
+ * Returns a PowerShell one-liner to add a Windows Defender exclusion for
152
+ * installPath; returns null on non-Windows.
153
+ * @param {string} installPath
154
+ * @param {object} [_opts] - Internal overrides for testing only
155
+ * @returns {string|null}
156
+ */
157
+ function defenderExclusionCommand(installPath, _opts = {}) {
158
+ const platform = _opts.platform !== undefined ? _opts.platform : process.platform;
159
+ if (platform !== 'win32') return null;
160
+ return `Add-MpPreference -ExclusionPath "${installPath}"`;
161
+ }
162
+
163
+ /**
164
+ * Returns the default shell rc-file path for the current user.
165
+ *
166
+ * Platform mapping:
167
+ * zsh → ~/.zshrc
168
+ * bash → ~/.bashrc
169
+ * fish → ~/.config/fish/conf.d/claws.fish (conf.d is auto-sourced by fish)
170
+ * win32 PowerShell → $PROFILE (resolved via powershell -c $PROFILE)
171
+ *
172
+ * @param {object} [_opts] - Internal overrides for testing only
173
+ * @returns {string}
174
+ */
175
+ function getDefaultShellRcFile(_opts = {}) {
176
+ const platform = _opts.platform !== undefined ? _opts.platform : process.platform;
177
+ const env = _opts.env !== undefined ? _opts.env : process.env;
178
+ const home = _opts.home !== undefined ? _opts.home : os.homedir();
179
+ const execFn = _opts.execFn !== undefined ? _opts.execFn : execSync;
180
+
181
+ if (platform === 'win32') {
182
+ try {
183
+ const profile = execFn('powershell -c $PROFILE', { encoding: 'utf8' }).trim();
184
+ if (profile) return profile;
185
+ } catch (_) {
186
+ // powershell not available — use canonical default
187
+ }
188
+ return path.join(home, 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1');
189
+ }
190
+
191
+ const shell = env.SHELL || '';
192
+ if (shell.includes('zsh')) return path.join(home, '.zshrc');
193
+ if (shell.includes('fish')) return path.join(home, '.config', 'fish', 'conf.d', 'claws.fish');
194
+ if (shell.includes('bash')) return path.join(home, '.bashrc');
195
+
196
+ // No $SHELL set — fall back by platform
197
+ if (platform === 'darwin') return path.join(home, '.zshrc');
198
+ return path.join(home, '.bashrc');
199
+ }
200
+
201
+ /**
202
+ * Dry-run logger — writes a [dry-run] prefixed line to stdout.
203
+ * Used throughout the installer when --dry-run is active.
204
+ * @param {string} msg
205
+ */
206
+ function dryRunLog(msg) {
207
+ process.stdout.write(`[dry-run] ${msg}\n`);
208
+ }
209
+
210
+ /**
211
+ * Detect the node-pty prebuild key for the current platform and architecture.
212
+ * Maps os.platform() + os.arch() to the directory name used by bundle-native.mjs:
213
+ * darwin-x64 | darwin-arm64 | linux-x64 | linux-arm64
214
+ * Mirrors the case block in scripts/install.sh (CLAWS_PLATFORM_ARCH).
215
+ * @param {object} [_opts]
216
+ * @param {string} [_opts.platform] - os.platform() override for testing
217
+ * @param {string} [_opts.arch] - os.arch() override for testing
218
+ * @returns {string}
219
+ */
220
+ function detectPlatformArch(_opts = {}) {
221
+ const platform = _opts.platform !== undefined ? _opts.platform : os.platform();
222
+ const arch = _opts.arch !== undefined ? _opts.arch : os.arch();
223
+ const normArch = arch === 'arm64' ? 'arm64' : 'x64';
224
+ switch (platform) {
225
+ case 'darwin': return `darwin-${normArch}`;
226
+ case 'linux': return `linux-${normArch}`;
227
+ default: return 'linux-x64';
228
+ }
229
+ }
230
+
231
+ module.exports = {
232
+ findCodeCli,
233
+ findAllEditorClis,
234
+ detectOneDrivePath,
235
+ longPathPreflight,
236
+ defenderExclusionCommand,
237
+ getDefaultShellRcFile,
238
+ dryRunLog,
239
+ detectPlatformArch,
240
+ };
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const { spawnSync } = require('child_process');
6
+
7
+ /**
8
+ * Run preflight checks. Returns { failures, warnings }.
9
+ * failures → hard errors that abort the install.
10
+ * warnings → WARN-not-block advisories printed before continuing.
11
+ *
12
+ * VS Code CLI presence is intentionally NOT checked here — a missing editor CLI
13
+ * is a soft warning emitted by _installExtension itself (phase 7), not a
14
+ * hard failure. install.sh behaves the same way (warn + continue).
15
+ *
16
+ * @param {object} [opts]
17
+ * @returns {{ failures: string[], warnings: string[] }}
18
+ */
19
+ function run(opts = {}) {
20
+ const failures = [];
21
+ const warnings = [];
22
+
23
+ // ── Hard failures ──────────────────────────────────────────────────────────
24
+
25
+ // Node >= 18
26
+ const major = Number(process.versions.node.split('.')[0]);
27
+ if (major < 18) {
28
+ failures.push(`Node.js ≥ 18 required (found ${process.version})`);
29
+ }
30
+
31
+ // git in PATH (any version — version check is a soft warning below)
32
+ const git = spawnSync('git', ['--version'], { encoding: 'utf8', stdio: 'pipe' });
33
+ if (git.status !== 0) {
34
+ failures.push('git not found — install git: https://git-scm.com');
35
+ }
36
+
37
+ // HOME writable
38
+ const home = os.homedir();
39
+ try {
40
+ fs.accessSync(home, fs.constants.W_OK);
41
+ } catch {
42
+ failures.push(`Home directory not writable: ${home}`);
43
+ }
44
+
45
+ // ── Soft warnings (WARN-not-block) ──────────────────────────────────────
46
+
47
+ // git >= 2
48
+ if (git.status === 0) {
49
+ const m = (git.stdout || '').match(/git version (\d+)/);
50
+ if (m && Number(m[1]) < 2) {
51
+ warnings.push(`git ≥ 2 recommended (found ${(git.stdout || '').trim()}) — some git operations may fail`);
52
+ }
53
+ }
54
+
55
+ // npm >= 7
56
+ const npm = spawnSync('npm', ['--version'], { encoding: 'utf8', stdio: 'pipe' });
57
+ if (npm.status === 0) {
58
+ const npmMajor = Number((npm.stdout || '').trim().split('.')[0]);
59
+ if (npmMajor < 7) {
60
+ warnings.push(`npm ≥ 7 recommended (found ${(npm.stdout || '').trim()}) — some install steps may fail`);
61
+ }
62
+ } else {
63
+ warnings.push('npm not found in PATH — extension build may fail');
64
+ }
65
+
66
+ // C++ toolchain (needed by node-gyp / @electron/rebuild)
67
+ _checkCppToolchain(warnings);
68
+
69
+ // python3 (node-gyp fallback); on Windows Python 3 ships as `python`, try that as fallback
70
+ const py = spawnSync('python3', ['--version'], { encoding: 'utf8', stdio: 'pipe' });
71
+ if (py.status !== 0) {
72
+ const pyFallback = spawnSync('python', ['--version'], { encoding: 'utf8', stdio: 'pipe' });
73
+ const pyFallbackOut = (pyFallback.stdout || '') + (pyFallback.stderr || '');
74
+ if (pyFallback.status !== 0 || !pyFallbackOut.includes('Python 3')) {
75
+ warnings.push('python3 not found — node-gyp builds may fail if native modules need recompiling');
76
+ }
77
+ }
78
+
79
+ // Disk space >= 512 MB free in HOME
80
+ _checkDiskSpace(home, warnings);
81
+
82
+ return { failures, warnings };
83
+ }
84
+
85
+ /**
86
+ * Check for a C++ build toolchain. Emits a warning (not a failure) if missing.
87
+ * darwin: Xcode Command Line Tools (clang)
88
+ * linux: g++ or make
89
+ * win32: cl.exe or msbuild.exe (install.ps1 already handles this for Windows)
90
+ * @param {string[]} warnings
91
+ */
92
+ function _checkCppToolchain(warnings) {
93
+ const platform = process.platform;
94
+
95
+ if (platform === 'win32') {
96
+ const cl = spawnSync('cl.exe', [], { encoding: 'utf8', stdio: 'pipe' });
97
+ const msbuild = spawnSync('msbuild.exe', [], { encoding: 'utf8', stdio: 'pipe' });
98
+ if (cl.status === null && msbuild.status === null) {
99
+ warnings.push(
100
+ 'C++ build tools not found (cl.exe / msbuild.exe) — run: winget install Microsoft.VisualStudio.BuildTools'
101
+ );
102
+ }
103
+ return;
104
+ }
105
+
106
+ if (platform === 'darwin') {
107
+ const clang = spawnSync('clang', ['--version'], { encoding: 'utf8', stdio: 'pipe' });
108
+ if (clang.status !== 0) {
109
+ warnings.push(
110
+ 'Xcode Command Line Tools not found — run: xcode-select --install'
111
+ );
112
+ }
113
+ return;
114
+ }
115
+
116
+ // linux
117
+ const gpp = spawnSync('g++', ['--version'], { encoding: 'utf8', stdio: 'pipe' });
118
+ const make = spawnSync('make', ['--version'], { encoding: 'utf8', stdio: 'pipe' });
119
+ if (gpp.status !== 0 || make.status !== 0) {
120
+ warnings.push(
121
+ 'C++ build tools not found (g++ / make) — run: sudo apt-get install build-essential (or distro equivalent)'
122
+ );
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Check free disk space in the given directory. Warns if < 512 MB.
128
+ * Uses `df -k` on POSIX; skips silently on win32.
129
+ * @param {string} dir
130
+ * @param {string[]} warnings
131
+ */
132
+ function _checkDiskSpace(dir, warnings) {
133
+ if (process.platform === 'win32') return;
134
+
135
+ const df = spawnSync('df', ['-k', dir], { encoding: 'utf8', stdio: 'pipe' });
136
+ if (df.status !== 0) return;
137
+
138
+ const lines = (df.stdout || '').trim().split('\n');
139
+ if (lines.length < 2) return;
140
+
141
+ // df -k output: Filesystem 1K-blocks Used Available Capacity Mounted
142
+ // column index 3 is "Available" in blocks of 1024 bytes.
143
+ const cols = lines[lines.length - 1].trim().split(/\s+/);
144
+ const freeKb = Number(cols[3]);
145
+ if (!isNaN(freeKb) && freeKb < 512 * 1024) {
146
+ warnings.push(
147
+ `Low disk space: ${Math.round(freeKb / 1024)} MB free in ${dir} (512 MB recommended)`
148
+ );
149
+ }
150
+ }
151
+
152
+ module.exports = { run };