claude-depester 1.3.6 → 1.4.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/lib/patcher.js CHANGED
@@ -46,26 +46,21 @@ const COMPLETION_REPLACEMENT = 'Thought';
46
46
  */
47
47
  function hasSillyWords(content) {
48
48
  const str = Buffer.isBuffer(content) ? content.toString('utf-8') : content;
49
-
50
- // Check for spinner words (need 3+ matches)
51
- let spinnerFound = 0;
49
+
50
+ // Check for spinner words
51
+ let found = 0;
52
52
  for (const word of MARKER_WORDS) {
53
53
  if (str.includes(`"${word}"`)) {
54
- spinnerFound++;
55
- if (spinnerFound >= 3) return true;
54
+ found++;
55
+ if (found >= 3) return true;
56
56
  }
57
57
  }
58
-
59
- // Also check for completion verbs (need 3+ matches)
60
- // Uses same approach as hasCompletionVerbs for consistency
61
- let completionFound = 0;
62
- for (const word of COMPLETION_VERBS) {
63
- if (str.includes(`"${word}"`)) {
64
- completionFound++;
65
- if (completionFound >= 3) return true;
66
- }
58
+
59
+ // Also check for completion verbs (only in binary)
60
+ if (str.includes('["Baked"')) {
61
+ return true;
67
62
  }
68
-
63
+
69
64
  return false;
70
65
  }
71
66
 
@@ -76,12 +71,7 @@ function hasSillyWords(content) {
76
71
  */
77
72
  function hasCompletionVerbs(content) {
78
73
  const str = Buffer.isBuffer(content) ? content.toString('utf-8') : content;
79
-
80
- let found = 0;
81
- for (const word of COMPLETION_VERBS) {
82
- if (str.includes(`"${word}"`)) found++;
83
- }
84
- return found >= 3;
74
+ return str.includes('["Baked"') && str.includes('"Worked"]');
85
75
  }
86
76
 
87
77
  /**
@@ -90,10 +80,10 @@ function hasCompletionVerbs(content) {
90
80
  * @returns {boolean}
91
81
  */
92
82
  function isPatched(content) {
93
- // If silly words are gone, it's patched.
94
- // We don't check for ["Thinking"] presence because newer versions
95
- // include it natively even when unpatched.
96
- return !hasSillyWords(content);
83
+ const str = Buffer.isBuffer(content) ? content.toString('utf-8') : content;
84
+ const hasReplacement = /=\["Thinking"\]/.test(str);
85
+ const hasOriginalArray = str.includes('["Accomplishing"') && str.includes('"Zigzagging"]');
86
+ return hasReplacement && !hasOriginalArray;
97
87
  }
98
88
 
99
89
  /**
@@ -102,122 +92,10 @@ function isPatched(content) {
102
92
  * @returns {boolean}
103
93
  */
104
94
  function isCompletionPatched(content) {
105
- return !hasCompletionVerbs(content);
106
- }
107
-
108
- /**
109
- * Find array boundaries around a marker word in binary content
110
- * @param {Buffer} buffer - Binary content
111
- * @param {string} markerWord - Word to search for as anchor
112
- * @param {string[]} validationWords - Words that should be present in the array
113
- * @param {number} minValidationCount - Minimum number of validation words required
114
- * @returns {{ startIdx: number, endIdx: number } | null}
115
- */
116
- function findArrayBoundaries(buffer, markerWord, validationWords, minValidationCount) {
117
- const marker = Buffer.from(markerWord);
118
- const idx = buffer.indexOf(marker);
119
-
120
- if (idx === -1) return null;
121
-
122
- // Scan back for '['
123
- let startIdx = idx;
124
- const START_LIMIT = 5000;
125
- let steps = 0;
126
- while (startIdx > 0 && buffer[startIdx] !== 0x5B && steps < START_LIMIT) {
127
- startIdx--;
128
- steps++;
129
- }
130
- if (steps >= START_LIMIT || buffer[startIdx] !== 0x5B) return null;
131
-
132
- // Scan forward for ']'
133
- let endIdx = idx;
134
- const END_LIMIT = 20000;
135
- steps = 0;
136
- while (endIdx < buffer.length && buffer[endIdx] !== 0x5D && steps < END_LIMIT) {
137
- endIdx++;
138
- steps++;
139
- }
140
- if (steps >= END_LIMIT || buffer[endIdx] !== 0x5D) return null;
141
-
142
- // Verify validation words in range
143
- const range = buffer.subarray(startIdx, endIdx + 1);
144
- const rangeStr = range.toString('utf-8');
145
-
146
- let foundCount = 0;
147
- for (const word of validationWords) {
148
- if (rangeStr.includes(`"${word}"`)) foundCount++;
149
- }
150
-
151
- if (foundCount < minValidationCount) return null;
152
-
153
- return { startIdx, endIdx };
154
- }
155
-
156
- /**
157
- * Replace array in buffer with padded replacement, preserving length
158
- * @param {Buffer} buffer - Binary content
159
- * @param {number} startIdx - Start index of array
160
- * @param {number} endIdx - End index of array (inclusive)
161
- * @param {string} replacement - Replacement string (e.g. '["Thinking"]')
162
- * @returns {Buffer | null}
163
- */
164
- function replaceArrayInBuffer(buffer, startIdx, endIdx, replacement) {
165
- const originalLen = endIdx - startIdx + 1;
166
- const replacementBuf = Buffer.from(replacement);
167
-
168
- if (replacementBuf.length > originalLen) return null;
169
-
170
- const padding = Buffer.alloc(originalLen - replacementBuf.length, 0x20); // Space
171
- const newContent = Buffer.concat([replacementBuf, padding]);
172
-
173
- return Buffer.concat([
174
- buffer.subarray(0, startIdx),
175
- newContent,
176
- buffer.subarray(endIdx + 1)
177
- ]);
178
- }
179
-
180
- /**
181
- * Patch binary content by finding array boundaries around marker words
182
- * Preserves exact length by padding with spaces.
183
- * Safe for patching bytecode sections where offset preservation is critical.
184
- * @param {Buffer} buffer - Binary content
185
- * @returns {{ patched: Buffer, count: number, spinnerCount: number, completionCount: number } | null}
186
- */
187
- function patchBinaryContent(buffer) {
188
- let patched = buffer;
189
- let spinnerCount = 0;
190
- let completionCount = 0;
191
-
192
- // Patch spinner words array (anchor: "Flibbertigibbeting")
193
- const spinnerBounds = findArrayBoundaries(patched, 'Flibbertigibbeting', MARKER_WORDS, 3);
194
- if (spinnerBounds) {
195
- const result = replaceArrayInBuffer(patched, spinnerBounds.startIdx, spinnerBounds.endIdx, `["${REPLACEMENT_WORD}"]`);
196
- if (result) {
197
- patched = result;
198
- spinnerCount = 1;
199
- }
200
- }
201
-
202
- // Patch completion verbs array (anchor: "Cogitated")
203
- const completionBounds = findArrayBoundaries(patched, 'Cogitated', COMPLETION_VERBS, 3);
204
- if (completionBounds) {
205
- const result = replaceArrayInBuffer(patched, completionBounds.startIdx, completionBounds.endIdx, `["${COMPLETION_REPLACEMENT}"]`);
206
- if (result) {
207
- patched = result;
208
- completionCount = 1;
209
- }
210
- }
211
-
212
- const count = spinnerCount + completionCount;
213
- if (count === 0) return null;
214
-
215
- return {
216
- patched,
217
- count,
218
- spinnerCount,
219
- completionCount
220
- };
95
+ const str = Buffer.isBuffer(content) ? content.toString('utf-8') : content;
96
+ const hasReplacement = /=\["Thought"\]/.test(str);
97
+ const hasOriginalArray = str.includes('["Baked"') && str.includes('"Worked"]');
98
+ return hasReplacement && !hasOriginalArray;
221
99
  }
222
100
 
223
101
  /**
@@ -230,40 +108,45 @@ function patchBinaryContent(buffer) {
230
108
  function patchJsContent(jsContent, options = {}) {
231
109
  let str = jsContent.toString('utf-8');
232
110
 
233
- // Generic pattern to match any string array assignment: varName=["str","str"...]
234
- // Capture group 1: varName
235
- // Capture group 2: array content including brackets
236
- const arrayPattern = /([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(\[[^\]]+\])/g;
111
+ // Pattern to match the array assignment: varName=["Accomplishing",...,"LastWord"]
112
+ // Different versions may end with different words:
113
+ // - CLI binary: ends with "Zigzagging"
114
+ // - Webview: ends with "Wrangling"
115
+ // We need to find arrays that contain our marker words
116
+ // Note: Webview uses longer var names like "Tte", binaries use shorter like "ouI"
117
+ const spinnerPatterns = [
118
+ /([a-zA-Z_$][a-zA-Z0-9_$]*)=\["Accomplishing"[^\]]*"Zigzagging"\]/g, // CLI binary
119
+ /([a-zA-Z_$][a-zA-Z0-9_$]*)=\["Accomplishing"[^\]]*"Wrangling"\]/g, // Webview
120
+ ];
237
121
 
238
122
  let spinnerCount = 0;
239
- let completionCount = 0;
123
+ for (const arrayPattern of spinnerPatterns) {
124
+ str = str.replace(arrayPattern, (match, varName) => {
125
+ // Verify it contains marker words
126
+ let markerCount = 0;
127
+ for (const marker of MARKER_WORDS) {
128
+ if (match.includes(`"${marker}"`)) markerCount++;
129
+ }
130
+
131
+ if (markerCount >= 3) {
132
+ spinnerCount++;
133
+ return `${varName}=["${REPLACEMENT_WORD}"]`;
134
+ }
135
+ return match;
136
+ });
137
+ }
240
138
 
241
- str = str.replace(arrayPattern, (match, varName, arrayContent) => {
242
- // Check for spinner words in this array
243
- let markerCount = 0;
244
- for (const marker of MARKER_WORDS) {
245
- if (arrayContent.includes(`"${marker}"`)) markerCount++;
246
- }
247
-
248
- // If we found enough marker words, this is the spinner array
249
- if (markerCount >= 3) {
250
- spinnerCount++;
251
- return `${varName}=["${REPLACEMENT_WORD}"]`;
252
- }
253
-
254
- // Check for completion verbs in this array
255
- // (Only exists in the binary, not in the webview)
256
- let completionMarkerCount = 0;
257
- for (const verb of COMPLETION_VERBS) {
258
- if (arrayContent.includes(`"${verb}"`)) completionMarkerCount++;
259
- }
260
-
261
- // If we found enough completion verbs, this is the completion array
262
- if (completionMarkerCount >= 3) {
139
+ // Pattern to match the completion verbs array: varName=["Baked",...,"Worked"]
140
+ // This array only exists in the binary, not in the webview
141
+ const completionPattern = /([a-zA-Z_$][a-zA-Z0-9_$]*)=\["Baked"[^\]]*"Worked"\]/g;
142
+
143
+ let completionCount = 0;
144
+ str = str.replace(completionPattern, (match, varName) => {
145
+ // Verify it contains expected completion verbs
146
+ if (match.includes('"Brewed"') || match.includes('"Churned"')) {
263
147
  completionCount++;
264
148
  return `${varName}=["${COMPLETION_REPLACEMENT}"]`;
265
149
  }
266
-
267
150
  return match;
268
151
  });
269
152
 
@@ -393,19 +276,15 @@ function patch(filePath, options = {}) {
393
276
 
394
277
  if (isBinary) {
395
278
  // Native binary - extract JS, patch, repack
396
- const extraction = extractClaudeJs(filePath);
279
+ const claudeJs = extractClaudeJs(filePath);
397
280
 
398
- if (!extraction) {
281
+ if (!claudeJs) {
399
282
  return {
400
283
  success: false,
401
284
  message: 'Could not extract claude.js from binary. Binary format may not be supported.'
402
285
  };
403
286
  }
404
287
 
405
- const claudeJs = extraction.content;
406
- const targetModuleName = extraction.moduleName;
407
- const targetType = extraction.type;
408
-
409
288
  // Check if already patched
410
289
  if (isPatched(claudeJs)) {
411
290
  return {
@@ -419,23 +298,16 @@ function patch(filePath, options = {}) {
419
298
  if (!hasSillyWords(claudeJs)) {
420
299
  return {
421
300
  success: false,
422
- message: 'Could not find silly words array in extracted content.'
301
+ message: 'Could not find silly words array in extracted JavaScript.'
423
302
  };
424
303
  }
425
304
 
426
- // Patch the content
427
- // Use binary patching for bytecode to preserve length/offsets
428
- let result;
429
- if (targetType === 'bytecode') {
430
- result = patchBinaryContent(claudeJs);
431
- } else {
432
- result = patchJsContent(claudeJs);
433
- }
434
-
305
+ // Patch the JS content
306
+ const result = patchJsContent(claudeJs);
435
307
  if (!result) {
436
308
  return {
437
309
  success: false,
438
- message: 'Could not locate words array pattern in content.'
310
+ message: 'Could not locate words array pattern in JavaScript.'
439
311
  };
440
312
  }
441
313
 
@@ -453,7 +325,7 @@ function patch(filePath, options = {}) {
453
325
  const backupPath = createBackup(filePath);
454
326
 
455
327
  // Repack binary with patched JS
456
- repackNativeInstallation(filePath, result.patched, filePath, targetModuleName, targetType);
328
+ repackNativeInstallation(filePath, result.patched, filePath);
457
329
 
458
330
  return {
459
331
  success: true,
@@ -540,8 +412,8 @@ function checkStatus(filePath, options = {}) {
540
412
 
541
413
  let content;
542
414
  if (isBinary) {
543
- const extraction = extractClaudeJs(filePath);
544
- if (!extraction) {
415
+ content = extractClaudeJs(filePath);
416
+ if (!content) {
545
417
  return {
546
418
  patched: false,
547
419
  hasSillyWords: false,
@@ -553,7 +425,6 @@ function checkStatus(filePath, options = {}) {
553
425
  error: 'Could not extract JavaScript from binary'
554
426
  };
555
427
  }
556
- content = extraction.content;
557
428
  } else {
558
429
  content = fs.readFileSync(filePath);
559
430
  }
@@ -579,113 +450,9 @@ function checkStatus(filePath, options = {}) {
579
450
  }
580
451
  }
581
452
 
582
- /**
583
- * Get detailed debug info for troubleshooting
584
- * @param {string} filePath - Path to cli.js or binary
585
- * @param {object} options - Options
586
- * @param {string} options.type - Override type detection: 'binary', 'js', 'webview'
587
- * @returns {object} Detailed debug information
588
- */
589
- function getDebugInfo(filePath, options = {}) {
590
- const debug = {
591
- filePath,
592
- fileExists: false,
593
- fileSize: null,
594
- fileModified: null,
595
- isBinary: false,
596
- isWebview: options.type === 'webview',
597
- binaryExtractionOk: null,
598
- extractedJsSize: null,
599
- // Detection details
600
- hasReplacementPattern: false, // =["Thinking"]
601
- hasCompletionReplacement: false, // =["Thought"]
602
- hasOriginalSpinnerArray: false, // ["Accomplishing"..."Zigzagging"]
603
- hasOriginalCompletionArray: false, // ["Baked"..."Worked"]
604
- markerWordsFound: [],
605
- completionVerbsFound: [],
606
- // Computed status
607
- spinnerPatched: false,
608
- completionPatched: false,
609
- hasSillyWords: false,
610
- hasBackup: false,
611
- error: null
612
- };
613
-
614
- try {
615
- // File metadata
616
- if (!fs.existsSync(filePath)) {
617
- debug.error = 'File does not exist';
618
- return debug;
619
- }
620
- debug.fileExists = true;
621
-
622
- const stats = fs.statSync(filePath);
623
- debug.fileSize = stats.size;
624
- debug.fileModified = stats.mtime.toISOString();
625
-
626
- // Backup check
627
- debug.hasBackup = hasBackup(filePath);
628
-
629
- // Binary detection
630
- const { type } = options;
631
- debug.isBinary = type !== 'js' && type !== 'webview' && isNativeBinary(filePath);
632
-
633
- // Get content
634
- let content;
635
- if (debug.isBinary) {
636
- const extraction = extractClaudeJs(filePath);
637
- if (!extraction) {
638
- debug.binaryExtractionOk = false;
639
- debug.error = 'Could not extract JavaScript from binary';
640
- return debug;
641
- }
642
- content = extraction.content;
643
- debug.binaryExtractionOk = true;
644
- debug.extractedJsSize = content.length;
645
- } else {
646
- content = fs.readFileSync(filePath);
647
- }
648
-
649
- const str = Buffer.isBuffer(content) ? content.toString('utf-8') : content;
650
-
651
- // Check for replacement patterns
652
- debug.hasReplacementPattern = /=\["Thinking"\]/.test(str);
653
- debug.hasCompletionReplacement = /=\["Thought"\]/.test(str);
654
-
655
- // Check for original arrays
656
- debug.hasOriginalSpinnerArray = str.includes('["Accomplishing"') && str.includes('"Zigzagging"]');
657
- debug.hasOriginalCompletionArray = str.includes('["Baked"') && str.includes('"Worked"]');
658
-
659
- // Check for individual marker words
660
- for (const word of MARKER_WORDS) {
661
- if (str.includes(`"${word}"`)) {
662
- debug.markerWordsFound.push(word);
663
- }
664
- }
665
-
666
- // Check for individual completion verbs
667
- for (const verb of COMPLETION_VERBS) {
668
- if (str.includes(`"${verb}"`)) {
669
- debug.completionVerbsFound.push(verb);
670
- }
671
- }
672
-
673
- // Compute status using existing functions
674
- debug.spinnerPatched = isPatched(content);
675
- debug.completionPatched = isCompletionPatched(content);
676
- debug.hasSillyWords = hasSillyWords(content);
677
-
678
- } catch (err) {
679
- debug.error = err.message;
680
- }
681
-
682
- return debug;
683
- }
684
-
685
453
  module.exports = {
686
454
  patch,
687
455
  checkStatus,
688
- getDebugInfo,
689
456
  restoreBackup,
690
457
  hasBackup,
691
458
  hasSillyWords,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "claude-depester",
3
- "version": "1.3.6",
4
- "description": "Patches Claude Code CLI and VS Code extension to show 'Thinking' instead of whimsical words.",
3
+ "version": "1.4.0",
4
+ "description": "Remove silly thinking words and completion verbs from Claude Code. Auto-patches and survives updates via SessionStart hook.",
5
5
  "main": "lib/patcher.js",
6
6
  "bin": {
7
7
  "claude-depester": "./bin/claude-depester"
@@ -15,10 +15,7 @@
15
15
  "cli",
16
16
  "patch",
17
17
  "thinking-words",
18
- "completion-verbs",
19
- "vscode-remote",
20
- "cursor",
21
- "ssh"
18
+ "completion-verbs"
22
19
  ],
23
20
  "author": "Lorenzo Becchi",
24
21
  "license": "MIT",
@@ -40,6 +37,6 @@
40
37
  "LICENSE"
41
38
  ],
42
39
  "dependencies": {
43
- "node-lief": "^0.1.8"
40
+ "node-lief": "^1.0.0"
44
41
  }
45
42
  }