claude-depester 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@ Remove silly thinking words from Claude Code.
7
7
 
8
8
  Instead of seeing "Flibbertigibbeting", "Discombobulating", "Clauding", etc., you'll see a clean "Thinking".
9
9
 
10
- > **Last updated:** 2026-01-11 | **Tested with:** Claude Code 2.1.4
10
+ > **Last updated:** 2026-01-11 | **Tested with:** Claude Code 2.1.4, 2.1.5
11
11
 
12
12
  ![Thinking... instead of silly words](img/screenshot1.png)
13
13
 
@@ -43,16 +43,20 @@ That's it! Restart Claude Code for changes to take effect.
43
43
  ## Features
44
44
 
45
45
  - Works with native binaries (Bun-compiled) and npm installations
46
+ - **Patches VS Code/VSCodium extension webview** (the UI that shows spinner text)
46
47
  - Auto-detects your Claude Code installation
47
48
  - Creates backup before patching (can restore anytime)
48
49
  - Optional SessionStart hook for auto-patching after updates
49
50
  - Content-based detection survives version updates
51
+ - Cross-platform: Linux, macOS, Windows (WSL/Git Bash)
50
52
 
51
53
  ## Commands
52
54
 
53
55
  | Command | Description |
54
56
  |---------|-------------|
55
57
  | `npx claude-depester` | Patch Claude Code |
58
+ | `npx claude-depester --all` | Patch ALL installations (CLI + VS Code) |
59
+ | `npx claude-depester --list` | List all found installations |
56
60
  | `npx claude-depester --dry-run` | Preview changes (no modifications) |
57
61
  | `npx claude-depester --check` | Check patch status |
58
62
  | `npx claude-depester --restore` | Restore original from backup |
@@ -64,14 +68,36 @@ That's it! Restart Claude Code for changes to take effect.
64
68
 
65
69
  ## Supported Installation Methods
66
70
 
71
+ ### Linux
67
72
  | Method | Path | Status |
68
73
  |--------|------|--------|
69
- | Native binary | `~/.local/bin/claude` -> `~/.local/share/claude/versions/X.Y.Z` | Fully supported |
74
+ | Native binary | `~/.local/share/claude/versions/X.Y.Z` | Fully supported |
75
+ | VS Code extension | `~/.vscode/extensions/anthropic.claude-code-*/` | Fully supported |
76
+ | VS Code webview | `~/.vscode/extensions/.../webview/index.js` | Fully supported |
77
+ | VSCodium extension | `~/.vscode-oss/extensions/anthropic.claude-code-*/` | Fully supported |
70
78
  | Local npm | `~/.claude/local/node_modules/@anthropic-ai/claude-code/` | Fully supported |
71
79
  | Global npm | `npm root -g`/@anthropic-ai/claude-code/ | Fully supported |
72
- | Homebrew | `/opt/homebrew/Caskroom/claude-code/` | Fully supported |
73
80
 
74
- The tool auto-detects your installation.
81
+ ### macOS
82
+ | Method | Path | Status |
83
+ |--------|------|--------|
84
+ | Native binary | `~/.local/share/claude/versions/X.Y.Z` | Fully supported |
85
+ | Native binary | `~/Library/Application Support/Claude/versions/X.Y.Z` | Fully supported |
86
+ | VS Code extension | `~/.vscode/extensions/anthropic.claude-code-*/` | Fully supported |
87
+ | VS Code webview | `~/.vscode/extensions/.../webview/index.js` | Fully supported |
88
+ | Homebrew | `/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/` | Fully supported |
89
+
90
+ ### Windows (WSL/Git Bash)
91
+ | Method | Path | Status |
92
+ |--------|------|--------|
93
+ | Native binary | `%USERPROFILE%\.local\bin\claude.exe` | Fully supported |
94
+ | Native binary | `%LOCALAPPDATA%\Claude\versions\X.Y.Z` | Fully supported |
95
+ | VS Code extension | `%USERPROFILE%\.vscode\extensions\anthropic.claude-code-*\` | Fully supported |
96
+ | VS Code webview | `...\extensions\...\webview\index.js` | Fully supported |
97
+
98
+ The tool auto-detects your installation. Use `--list` to see all found installations, and `--all` to patch them all at once.
99
+
100
+ > **Important for VS Code users:** The extension has TWO places with spinner words - the native binary AND the webview. Use `--all` to patch both!
75
101
 
76
102
  ## After Claude Code Updates
77
103
 
@@ -112,11 +138,40 @@ Every time Claude Code starts, it checks and re-applies the patch if needed.
112
138
 
113
139
  ## Troubleshooting
114
140
 
141
+ ### Still seeing silly words after patching?
142
+
143
+ **You must fully restart Claude Code / VS Code / VSCodium** after patching. The patch modifies the binary on disk, but running processes still use the old code in memory.
144
+
145
+ For VS Code / VSCodium:
146
+ 1. Close the editor completely (check it's not running in background)
147
+ 2. Kill any remaining processes: `pkill -f codium` or `pkill -f "Code"`
148
+ 3. Reopen the editor
149
+
150
+ To verify processes are stopped:
151
+ ```bash
152
+ ps aux | grep -E "(vscode|codium|claude)" | grep -v grep
153
+ ```
154
+
115
155
  ### "Could not find Claude Code installation"
116
156
 
117
157
  Make sure Claude Code is installed:
118
158
  - Check with `claude --version`
119
159
  - Run with `--verbose` to see searched paths
160
+ - Use `--list` to see all detected installations
161
+
162
+ ### VS Code extension still showing silly words
163
+
164
+ The VS Code extension has **TWO separate components** with spinner words:
165
+ 1. **Native binary** (`resources/native-binary/claude`) - the backend
166
+ 2. **Webview** (`webview/index.js`) - the frontend UI that renders the spinner
167
+
168
+ You must patch BOTH for the fix to work. Use:
169
+ ```bash
170
+ npx claude-depester --list # Should show both binary AND webview
171
+ npx claude-depester --all # Patch ALL components
172
+ ```
173
+
174
+ Then **fully restart VS Code** (not just reload window).
120
175
 
121
176
  ### Patch not working after update
122
177
 
@@ -128,8 +183,8 @@ If the patch fails:
128
183
  ### Want to undo everything
129
184
 
130
185
  ```bash
131
- npx claude-depester --restore # Restore original file
132
- npx claude-depester --remove-hook # Remove auto-patch hook
186
+ npx claude-depester --restore --all # Restore all installations
187
+ npx claude-depester --remove-hook # Remove auto-patch hook
133
188
  ```
134
189
 
135
190
  ## Technical Details
@@ -154,6 +209,19 @@ npx claude-depester --remove-hook # Remove auto-patch hook
154
209
  - Node.js >= 18.0.0
155
210
  - Claude Code installed
156
211
 
212
+ ## Platform Support
213
+
214
+ | Platform | Binary Patching | Webview Patching | Status |
215
+ |----------|-----------------|------------------|--------|
216
+ | Linux x64 | ELF | Yes | Tested |
217
+ | Linux ARM64 | ELF | Yes | Should work |
218
+ | macOS Intel | MachO | Yes | Should work |
219
+ | macOS Apple Silicon | MachO | Yes | Should work |
220
+ | Windows x64 | PE | Yes | Should work |
221
+ | Windows ARM64 | PE | Yes | Should work |
222
+
223
+ The tool uses [node-lief](https://www.npmjs.com/package/node-lief) which has prebuilt binaries for all these platforms.
224
+
157
225
  ## Contributing
158
226
 
159
227
  If Claude Code updates and the patch stops working:
@@ -5,7 +5,7 @@
5
5
  * Remove silly thinking words from Claude Code
6
6
  */
7
7
 
8
- const { findCliJs, getSearchedPaths } = require('../lib/detector');
8
+ const { findCliJs, findAllClaudeCode, getSearchedPaths } = require('../lib/detector');
9
9
  const { patch, checkStatus, restoreBackup } = require('../lib/patcher');
10
10
  const { installHook, removeHook, getHookStatus } = require('../lib/hooks');
11
11
 
@@ -23,7 +23,9 @@ const flags = {
23
23
  removeHook: args.includes('--remove-hook'),
24
24
  hookStatus: args.includes('--hook-status'),
25
25
  silent: args.includes('--silent') || args.includes('-s'),
26
- verbose: args.includes('--verbose')
26
+ verbose: args.includes('--verbose'),
27
+ all: args.includes('--all') || args.includes('-a'),
28
+ list: args.includes('--list') || args.includes('-l')
27
29
  };
28
30
 
29
31
  function log(...msg) {
@@ -48,6 +50,8 @@ USAGE:
48
50
 
49
51
  OPTIONS:
50
52
  (no options) Patch Claude Code now
53
+ -a, --all Patch ALL installations (CLI + VS Code extensions)
54
+ -l, --list List all found installations
51
55
  -c, --check Check if Claude Code is patched
52
56
  -r, --restore Restore original file from backup
53
57
  -n, --dry-run Preview changes without applying
@@ -62,7 +66,9 @@ OPTIONS:
62
66
  -h, --help Show this help
63
67
 
64
68
  EXAMPLES:
65
- npx claude-depester # Patch now
69
+ npx claude-depester # Patch first found installation
70
+ npx claude-depester --all # Patch ALL installations
71
+ npx claude-depester --list # List all installations
66
72
  npx claude-depester --check # Check status
67
73
  npx claude-depester --install-hook # Auto-patch after updates
68
74
  npx claude-depester --restore # Undo patch
@@ -116,7 +122,86 @@ async function main() {
116
122
  process.exit(result.success ? 0 : 1);
117
123
  }
118
124
 
119
- // Find Claude Code installation
125
+ // Handle --list
126
+ if (flags.list) {
127
+ const installations = findAllClaudeCode();
128
+ if (installations.length === 0) {
129
+ error('No Claude Code installations found.');
130
+ if (flags.verbose) {
131
+ showSearchedPaths();
132
+ }
133
+ process.exit(1);
134
+ }
135
+
136
+ log(`Found ${installations.length} installation(s):\n`);
137
+ for (const inst of installations) {
138
+ const status = checkStatus(inst.path, { type: inst.type });
139
+ const statusStr = status.patched ? '[PATCHED]' : '[NOT PATCHED]';
140
+ const typeStr = inst.type === 'webview' ? ' (webview)' : inst.type === 'binary' ? ' (binary)' : '';
141
+ log(`${statusStr} ${inst.method}${typeStr}`);
142
+ log(` ${inst.path}\n`);
143
+ }
144
+ process.exit(0);
145
+ }
146
+
147
+ // Handle --all (patch all installations)
148
+ if (flags.all) {
149
+ const installations = findAllClaudeCode();
150
+ if (installations.length === 0) {
151
+ error('No Claude Code installations found.');
152
+ if (flags.verbose) {
153
+ showSearchedPaths();
154
+ }
155
+ process.exit(1);
156
+ }
157
+
158
+ log(`Found ${installations.length} installation(s):\n`);
159
+
160
+ let successCount = 0;
161
+ let skipCount = 0;
162
+ let failCount = 0;
163
+
164
+ for (const inst of installations) {
165
+ log(`${inst.method}:`);
166
+ log(` ${inst.path}`);
167
+
168
+ if (flags.restore) {
169
+ const success = restoreBackup(inst.path);
170
+ if (success) {
171
+ log(' -> Restored from backup\n');
172
+ successCount++;
173
+ } else {
174
+ log(' -> No backup found\n');
175
+ skipCount++;
176
+ }
177
+ } else {
178
+ const result = patch(inst.path, { dryRun: flags.dryRun, type: inst.type });
179
+ if (result.success) {
180
+ if (result.alreadyPatched) {
181
+ log(' -> Already patched\n');
182
+ skipCount++;
183
+ } else if (result.dryRun) {
184
+ log(' -> Would patch\n');
185
+ successCount++;
186
+ } else {
187
+ log(' -> Patched!\n');
188
+ successCount++;
189
+ }
190
+ } else {
191
+ log(` -> Failed: ${result.message}\n`);
192
+ failCount++;
193
+ }
194
+ }
195
+ }
196
+
197
+ log(`Done: ${successCount} patched, ${skipCount} skipped, ${failCount} failed`);
198
+ if (!flags.dryRun && successCount > 0) {
199
+ log('Restart Claude Code for changes to take effect.');
200
+ }
201
+ process.exit(failCount > 0 ? 1 : 0);
202
+ }
203
+
204
+ // Find Claude Code installation (single)
120
205
  const cliInfo = findCliJs();
121
206
 
122
207
  if (!cliInfo) {
@@ -138,7 +223,7 @@ async function main() {
138
223
 
139
224
  // Handle check
140
225
  if (flags.check) {
141
- const status = checkStatus(cliInfo.path);
226
+ const status = checkStatus(cliInfo.path, { type: cliInfo.type });
142
227
 
143
228
  if (status.error) {
144
229
  error(`Error: ${status.error}`);
@@ -180,7 +265,7 @@ async function main() {
180
265
  }
181
266
 
182
267
  // Default action: patch
183
- const result = patch(cliInfo.path, { dryRun: flags.dryRun });
268
+ const result = patch(cliInfo.path, { dryRun: flags.dryRun, type: cliInfo.type });
184
269
 
185
270
  if (result.success) {
186
271
  if (result.alreadyPatched) {
package/lib/detector.js CHANGED
@@ -15,10 +15,13 @@ const HOME = os.homedir();
15
15
  */
16
16
  function getPotentialPaths() {
17
17
  const paths = [];
18
+ const isWindows = process.platform === 'win32';
19
+ const isMac = process.platform === 'darwin';
18
20
 
19
21
  // Priority 1: Native binary (recommended installation method)
20
- // The binary is at ~/.local/bin/claude which symlinks to ~/.local/share/claude/versions/X.Y.Z
21
- if (process.platform !== 'win32') {
22
+ // Linux/macOS: ~/.local/bin/claude symlinks to ~/.local/share/claude/versions/X.Y.Z
23
+ // Windows: %USERPROFILE%\.local\bin\claude.exe
24
+ if (!isWindows) {
22
25
  try {
23
26
  const claudePath = execSync('which claude', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
24
27
  if (claudePath) {
@@ -33,30 +36,84 @@ function getPotentialPaths() {
33
36
  } catch (e) {
34
37
  // which failed or claude not found
35
38
  }
36
- }
37
-
38
- // Also check versions directory directly
39
- const versionsDir = path.join(HOME, '.local', 'share', 'claude', 'versions');
40
- try {
41
- if (fs.existsSync(versionsDir)) {
42
- const versions = fs.readdirSync(versionsDir)
43
- .filter(f => /^\d+\.\d+\.\d+$/.test(f))
44
- .sort((a, b) => {
45
- const [aMajor, aMinor, aPatch] = a.split('.').map(Number);
46
- const [bMajor, bMinor, bPatch] = b.split('.').map(Number);
47
- return bMajor - aMajor || bMinor - aMinor || bPatch - aPatch;
48
- });
49
-
50
- if (versions.length > 0) {
39
+ } else {
40
+ // Windows: try where command
41
+ try {
42
+ const claudePath = execSync('where claude', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim().split('\n')[0];
43
+ if (claudePath) {
51
44
  paths.push({
52
- method: 'native binary (~/.local/share/claude/versions)',
53
- path: path.join(versionsDir, versions[0]),
45
+ method: 'native binary (where claude)',
46
+ path: claudePath,
54
47
  type: 'binary'
55
48
  });
56
49
  }
50
+ } catch (e) {
51
+ // where failed or claude not found
57
52
  }
58
- } catch (e) {
59
- // Failed to read versions dir
53
+ }
54
+
55
+ // Check versions directory directly
56
+ // Linux: ~/.local/share/claude/versions/X.Y.Z
57
+ // macOS: ~/Library/Application Support/Claude/versions/X.Y.Z (also ~/.local/share/claude)
58
+ // Windows: %LOCALAPPDATA%\Claude\versions\X.Y.Z
59
+ const versionsDirs = [];
60
+
61
+ if (isWindows) {
62
+ const localAppData = process.env.LOCALAPPDATA || path.join(HOME, 'AppData', 'Local');
63
+ versionsDirs.push(path.join(localAppData, 'Claude', 'versions'));
64
+ versionsDirs.push(path.join(HOME, '.local', 'share', 'claude', 'versions'));
65
+ } else if (isMac) {
66
+ versionsDirs.push(path.join(HOME, 'Library', 'Application Support', 'Claude', 'versions'));
67
+ versionsDirs.push(path.join(HOME, '.local', 'share', 'claude', 'versions'));
68
+ } else {
69
+ versionsDirs.push(path.join(HOME, '.local', 'share', 'claude', 'versions'));
70
+ }
71
+
72
+ for (const versionsDir of versionsDirs) {
73
+ try {
74
+ if (fs.existsSync(versionsDir)) {
75
+ const versions = fs.readdirSync(versionsDir)
76
+ .filter(f => /^\d+\.\d+\.\d+$/.test(f))
77
+ .sort((a, b) => {
78
+ const [aMajor, aMinor, aPatch] = a.split('.').map(Number);
79
+ const [bMajor, bMinor, bPatch] = b.split('.').map(Number);
80
+ return bMajor - aMajor || bMinor - aMinor || bPatch - aPatch;
81
+ });
82
+
83
+ for (const version of versions) {
84
+ const binaryName = isWindows ? 'claude.exe' : 'claude';
85
+ const binaryPath = path.join(versionsDir, version, binaryName);
86
+ // Also check if version folder itself is the binary (some installations)
87
+ const versionPath = path.join(versionsDir, version);
88
+
89
+ if (fs.existsSync(binaryPath)) {
90
+ paths.push({
91
+ method: `native binary (${versionsDir})`,
92
+ path: binaryPath,
93
+ type: 'binary'
94
+ });
95
+ } else if (fs.existsSync(versionPath) && fs.statSync(versionPath).isFile()) {
96
+ paths.push({
97
+ method: `native binary (${versionsDir})`,
98
+ path: versionPath,
99
+ type: 'binary'
100
+ });
101
+ }
102
+ }
103
+ }
104
+ } catch (e) {
105
+ // Failed to read versions dir
106
+ }
107
+ }
108
+
109
+ // Windows: also check .local\bin directly
110
+ if (isWindows) {
111
+ const winBinaryPath = path.join(HOME, '.local', 'bin', 'claude.exe');
112
+ paths.push({
113
+ method: 'native binary (Windows .local\\bin)',
114
+ path: winBinaryPath,
115
+ type: 'binary'
116
+ });
60
117
  }
61
118
 
62
119
  // Priority 2: Local npm installations
@@ -85,7 +142,68 @@ function getPotentialPaths() {
85
142
  // npm not available or failed
86
143
  }
87
144
 
88
- // Priority 4: Derive from process.execPath
145
+ // Priority 4: VS Code / VSCodium extensions
146
+ // Linux: ~/.vscode/extensions, ~/.vscode-oss/extensions
147
+ // macOS: ~/.vscode/extensions, ~/Library/Application Support/Code/User/extensions (rare)
148
+ // Windows: %USERPROFILE%\.vscode\extensions, %APPDATA%\Code\User\extensions
149
+ const vscodeExtDirs = [
150
+ path.join(HOME, '.vscode', 'extensions'), // VS Code (all platforms)
151
+ path.join(HOME, '.vscode-oss', 'extensions'), // VSCodium (Linux)
152
+ path.join(HOME, '.vscode-insiders', 'extensions'), // VS Code Insiders
153
+ ];
154
+
155
+ // Add Windows-specific paths
156
+ if (isWindows) {
157
+ const appData = process.env.APPDATA || path.join(HOME, 'AppData', 'Roaming');
158
+ vscodeExtDirs.push(path.join(appData, 'Code', 'User', 'extensions'));
159
+ vscodeExtDirs.push(path.join(appData, 'Code - Insiders', 'User', 'extensions'));
160
+ vscodeExtDirs.push(path.join(appData, 'VSCodium', 'User', 'extensions'));
161
+ }
162
+
163
+ // Add macOS-specific paths
164
+ if (isMac) {
165
+ vscodeExtDirs.push(path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'extensions'));
166
+ vscodeExtDirs.push(path.join(HOME, 'Library', 'Application Support', 'Code - Insiders', 'User', 'extensions'));
167
+ vscodeExtDirs.push(path.join(HOME, 'Library', 'Application Support', 'VSCodium', 'User', 'extensions'));
168
+ }
169
+
170
+ for (const extDir of vscodeExtDirs) {
171
+ try {
172
+ if (fs.existsSync(extDir)) {
173
+ const extensions = fs.readdirSync(extDir)
174
+ .filter(f => f.startsWith('anthropic.claude-code-'))
175
+ .sort()
176
+ .reverse(); // Latest version first
177
+
178
+ for (const ext of extensions) {
179
+ // Native binary in extension
180
+ const binaryName = isWindows ? 'claude.exe' : 'claude';
181
+ const binaryPath = path.join(extDir, ext, 'resources', 'native-binary', binaryName);
182
+ if (fs.existsSync(binaryPath)) {
183
+ paths.push({
184
+ method: `VS Code extension binary (${ext})`,
185
+ path: binaryPath,
186
+ type: 'binary'
187
+ });
188
+ }
189
+
190
+ // Webview frontend JS - has separate copy of spinner words
191
+ const webviewPath = path.join(extDir, ext, 'webview', 'index.js');
192
+ if (fs.existsSync(webviewPath)) {
193
+ paths.push({
194
+ method: `VS Code extension webview (${ext})`,
195
+ path: webviewPath,
196
+ type: 'webview'
197
+ });
198
+ }
199
+ }
200
+ }
201
+ } catch (e) {
202
+ // Failed to read extensions dir
203
+ }
204
+ }
205
+
206
+ // Priority 5: Derive from process.execPath
89
207
  try {
90
208
  const nodeDir = path.dirname(process.execPath);
91
209
  paths.push({
@@ -97,7 +215,7 @@ function getPotentialPaths() {
97
215
  // Failed to derive
98
216
  }
99
217
 
100
- // Priority 5: Common homebrew locations (macOS)
218
+ // Priority 6: Common homebrew locations (macOS)
101
219
  if (process.platform === 'darwin') {
102
220
  paths.push({
103
221
  method: 'homebrew (arm64)',
@@ -115,14 +233,19 @@ function getPotentialPaths() {
115
233
  }
116
234
 
117
235
  /**
118
- * Check if a file contains the silly words (works for both JS and binary)
236
+ * Check if a file contains the silly words OR has been patched (works for both JS and binary)
119
237
  */
120
- function containsSillyWords(filePath) {
238
+ function containsSillyWordsOrPatched(filePath) {
121
239
  try {
122
240
  // Read as buffer to handle both text and binary
123
241
  const content = fs.readFileSync(filePath);
124
242
  const contentStr = content.toString('utf-8');
125
243
 
244
+ // Check if already patched (replacement pattern)
245
+ if (/=\["Thinking"\]/.test(contentStr)) {
246
+ return true;
247
+ }
248
+
126
249
  // Check for marker words
127
250
  const markers = ['Flibbertigibbeting', 'Discombobulating', 'Clauding'];
128
251
  let found = 0;
@@ -137,9 +260,14 @@ function containsSillyWords(filePath) {
137
260
  }
138
261
  }
139
262
 
263
+ // Legacy alias
264
+ function containsSillyWords(filePath) {
265
+ return containsSillyWordsOrPatched(filePath);
266
+ }
267
+
140
268
  /**
141
269
  * Find Claude Code installation (cli.js or native binary)
142
- * @returns {{ path: string, method: string, type: 'js' | 'binary' } | null}
270
+ * @returns {{ path: string, method: string, type: 'js' | 'binary' | 'webview' } | null}
143
271
  */
144
272
  function findClaudeCode() {
145
273
  const potentialPaths = getPotentialPaths();
@@ -170,6 +298,35 @@ function findCliJs() {
170
298
  return null;
171
299
  }
172
300
 
301
+ /**
302
+ * Find ALL Claude Code installations (CLI + VS Code extensions + webviews)
303
+ * @returns {Array<{ path: string, method: string, type: 'js' | 'binary' | 'webview' }>}
304
+ */
305
+ function findAllClaudeCode() {
306
+ const potentialPaths = getPotentialPaths();
307
+ const found = [];
308
+ const seenPaths = new Set();
309
+
310
+ for (const { method, path: codePath, type } of potentialPaths) {
311
+ try {
312
+ const resolvedPath = path.resolve(codePath);
313
+ if (seenPaths.has(resolvedPath)) continue;
314
+
315
+ if (fs.existsSync(resolvedPath)) {
316
+ const stats = fs.statSync(resolvedPath);
317
+ if (stats.isFile() && containsSillyWords(resolvedPath)) {
318
+ found.push({ path: resolvedPath, method, type });
319
+ seenPaths.add(resolvedPath);
320
+ }
321
+ }
322
+ } catch (e) {
323
+ // File doesn't exist or can't be read
324
+ }
325
+ }
326
+
327
+ return found;
328
+ }
329
+
173
330
  /**
174
331
  * Get all searched paths for error reporting
175
332
  */
@@ -180,5 +337,6 @@ function getSearchedPaths() {
180
337
  module.exports = {
181
338
  findClaudeCode,
182
339
  findCliJs,
340
+ findAllClaudeCode,
183
341
  getSearchedPaths
184
342
  };
package/lib/patcher.js CHANGED
@@ -52,29 +52,40 @@ function isPatched(content) {
52
52
  /**
53
53
  * Find and replace the silly words array in JavaScript content
54
54
  * @param {Buffer} jsContent - JavaScript content as buffer
55
+ * @param {object} options - Options
56
+ * @param {boolean} options.isWebview - Whether this is a webview file (uses different var names)
55
57
  * @returns {{ patched: Buffer, count: number } | null}
56
58
  */
57
- function patchJsContent(jsContent) {
59
+ function patchJsContent(jsContent, options = {}) {
58
60
  let str = jsContent.toString('utf-8');
59
61
 
60
- // Pattern to match the array assignment: varName=["Accomplishing",...,"Zigzagging"]
62
+ // Pattern to match the array assignment: varName=["Accomplishing",...,"LastWord"]
63
+ // Different versions may end with different words:
64
+ // - CLI binary: ends with "Zigzagging"
65
+ // - Webview: ends with "Wrangling"
61
66
  // We need to find arrays that contain our marker words
62
- const arrayPattern = /([a-zA-Z_$][a-zA-Z0-9_$]*)=\["Accomplishing"[^\]]*"Zigzagging"\]/g;
67
+ // Note: Webview uses longer var names like "Tte", binaries use shorter like "ouI"
68
+ const arrayPatterns = [
69
+ /([a-zA-Z_$][a-zA-Z0-9_$]*)=\["Accomplishing"[^\]]*"Zigzagging"\]/g, // CLI binary
70
+ /([a-zA-Z_$][a-zA-Z0-9_$]*)=\["Accomplishing"[^\]]*"Wrangling"\]/g, // Webview
71
+ ];
63
72
 
64
73
  let count = 0;
65
- str = str.replace(arrayPattern, (match, varName) => {
66
- // Verify it contains marker words
67
- let markerCount = 0;
68
- for (const marker of MARKER_WORDS) {
69
- if (match.includes(`"${marker}"`)) markerCount++;
70
- }
71
-
72
- if (markerCount >= 3) {
73
- count++;
74
- return `${varName}=["${REPLACEMENT_WORD}"]`;
75
- }
76
- return match;
77
- });
74
+ for (const arrayPattern of arrayPatterns) {
75
+ str = str.replace(arrayPattern, (match, varName) => {
76
+ // Verify it contains marker words
77
+ let markerCount = 0;
78
+ for (const marker of MARKER_WORDS) {
79
+ if (match.includes(`"${marker}"`)) markerCount++;
80
+ }
81
+
82
+ if (markerCount >= 3) {
83
+ count++;
84
+ return `${varName}=["${REPLACEMENT_WORD}"]`;
85
+ }
86
+ return match;
87
+ });
88
+ }
78
89
 
79
90
  if (count === 0) return null;
80
91
 
@@ -161,13 +172,17 @@ function isNativeBinary(filePath) {
161
172
  * Main patch function
162
173
  * @param {string} filePath - Path to cli.js or binary
163
174
  * @param {object} options - Options
175
+ * @param {boolean} options.dryRun - Don't actually patch
176
+ * @param {string} options.type - Override type detection: 'binary', 'js', 'webview'
164
177
  * @returns {{ success: boolean, message: string, alreadyPatched?: boolean }}
165
178
  */
166
179
  function patch(filePath, options = {}) {
167
- const { dryRun = false } = options;
180
+ const { dryRun = false, type } = options;
168
181
 
169
182
  try {
170
- const isBinary = isNativeBinary(filePath);
183
+ // Use provided type or detect
184
+ const isBinary = type === 'binary' || (type !== 'js' && type !== 'webview' && isNativeBinary(filePath));
185
+ const isWebview = type === 'webview';
171
186
 
172
187
  if (isBinary) {
173
188
  // Native binary - extract JS, patch, repack
@@ -227,8 +242,9 @@ function patch(filePath, options = {}) {
227
242
  };
228
243
 
229
244
  } else {
230
- // Plain JS file (npm installation)
245
+ // Plain JS file (npm installation) or webview file
231
246
  const content = fs.readFileSync(filePath);
247
+ const fileType = isWebview ? 'webview' : 'JavaScript';
232
248
 
233
249
  if (isPatched(content)) {
234
250
  return {
@@ -241,22 +257,22 @@ function patch(filePath, options = {}) {
241
257
  if (!hasSillyWords(content)) {
242
258
  return {
243
259
  success: false,
244
- message: 'Could not find silly words array. Claude Code version may not be supported.'
260
+ message: `Could not find silly words array in ${fileType}. Claude Code version may not be supported.`
245
261
  };
246
262
  }
247
263
 
248
- const result = patchJsContent(content);
264
+ const result = patchJsContent(content, { isWebview });
249
265
  if (!result) {
250
266
  return {
251
267
  success: false,
252
- message: 'Could not locate words array pattern'
268
+ message: `Could not locate words array pattern in ${fileType}`
253
269
  };
254
270
  }
255
271
 
256
272
  if (dryRun) {
257
273
  return {
258
274
  success: true,
259
- message: `Dry run - would patch ${result.count} occurrence(s) of silly words array`,
275
+ message: `Dry run - would patch ${result.count} occurrence(s) of silly words array in ${fileType}`,
260
276
  dryRun: true
261
277
  };
262
278
  }
@@ -269,7 +285,7 @@ function patch(filePath, options = {}) {
269
285
 
270
286
  return {
271
287
  success: true,
272
- message: `Patched ${result.count} occurrence(s) successfully. Backup at: ${backupPath}`,
288
+ message: `Patched ${result.count} occurrence(s) in ${fileType} successfully. Backup at: ${backupPath}`,
273
289
  backupPath
274
290
  };
275
291
  }
@@ -285,11 +301,15 @@ function patch(filePath, options = {}) {
285
301
  /**
286
302
  * Check patch status
287
303
  * @param {string} filePath - Path to cli.js or binary
288
- * @returns {{ patched: boolean, hasSillyWords: boolean, hasBackup: boolean, isBinary: boolean }}
304
+ * @param {object} options - Options
305
+ * @param {string} options.type - Override type detection: 'binary', 'js', 'webview'
306
+ * @returns {{ patched: boolean, hasSillyWords: boolean, hasBackup: boolean, isBinary: boolean, isWebview: boolean }}
289
307
  */
290
- function checkStatus(filePath) {
308
+ function checkStatus(filePath, options = {}) {
291
309
  try {
292
- const isBinary = isNativeBinary(filePath);
310
+ const { type } = options;
311
+ const isBinary = type === 'binary' || (type !== 'js' && type !== 'webview' && isNativeBinary(filePath));
312
+ const isWebview = type === 'webview';
293
313
 
294
314
  let content;
295
315
  if (isBinary) {
@@ -300,6 +320,7 @@ function checkStatus(filePath) {
300
320
  hasSillyWords: false,
301
321
  hasBackup: hasBackup(filePath),
302
322
  isBinary: true,
323
+ isWebview: false,
303
324
  error: 'Could not extract JavaScript from binary'
304
325
  };
305
326
  }
@@ -311,7 +332,8 @@ function checkStatus(filePath) {
311
332
  patched: isPatched(content),
312
333
  hasSillyWords: hasSillyWords(content),
313
334
  hasBackup: hasBackup(filePath),
314
- isBinary
335
+ isBinary,
336
+ isWebview
315
337
  };
316
338
  } catch (err) {
317
339
  return {
@@ -319,6 +341,7 @@ function checkStatus(filePath) {
319
341
  hasSillyWords: false,
320
342
  hasBackup: false,
321
343
  isBinary: false,
344
+ isWebview: false,
322
345
  error: err.message
323
346
  };
324
347
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-depester",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Remove silly thinking words from Claude Code. Auto-patches and survives updates via SessionStart hook.",
5
5
  "main": "lib/patcher.js",
6
6
  "bin": {