claude-git-hooks 2.6.0 → 2.6.2
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/CHANGELOG.md +43 -0
- package/lib/utils/claude-client.js +47 -3
- package/lib/utils/which-command.js +23 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,49 @@ Todos los cambios notables en este proyecto se documentarán en este archivo.
|
|
|
5
5
|
El formato está basado en [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.6.2] - 2025-12-12
|
|
9
|
+
|
|
10
|
+
### 🐛 Fixed
|
|
11
|
+
|
|
12
|
+
- **Parallel analysis stdin error handling** - Added graceful error handling for EOF errors during parallel execution (#43)
|
|
13
|
+
- **What was broken**: Unhandled 'error' events on stdin stream when Claude CLI process terminates unexpectedly during parallel analysis
|
|
14
|
+
- **Root cause**: `stdin.write()` failures emit asynchronous 'error' events that weren't being caught, causing uncaught exceptions with large prompts (18+ files) in parallel mode
|
|
15
|
+
- **Fix**: Added explicit error handler on `stdin` stream before writing prompt
|
|
16
|
+
- **Files changed**:
|
|
17
|
+
- `lib/utils/claude-client.js:287-308` - Added stdin error handler with detailed diagnostics
|
|
18
|
+
- **Impact**:
|
|
19
|
+
- Provides clear error message: "Failed to write to Claude stdin - process terminated unexpectedly"
|
|
20
|
+
- Logs diagnostic info: prompt length, error code, duration
|
|
21
|
+
- Suggests actionable fix: "Try reducing batch size or number of files per commit"
|
|
22
|
+
- **Compatibility**: No breaking changes, improves error reporting for all platforms
|
|
23
|
+
|
|
24
|
+
### 🎯 User Experience
|
|
25
|
+
|
|
26
|
+
- **Before**: Cryptic unhandled 'write EOF' exceptions crashed commit process with no actionable information
|
|
27
|
+
- **After**: Clear error message with diagnostic context and suggested remediation
|
|
28
|
+
- **Debug**: Added structured logging with prompt length and timing when EOF errors occur
|
|
29
|
+
|
|
30
|
+
## [2.6.1] - 2025-12-04
|
|
31
|
+
|
|
32
|
+
### 🐛 Fixed
|
|
33
|
+
|
|
34
|
+
- **Windows spawn ENOENT errors** - Fixed critical issue where Claude CLI failed to execute on Windows with npm-installed tools
|
|
35
|
+
- **What was broken**: `spawn()` cannot execute `.cmd`/`.bat` files directly on Windows, causing `ENOENT` errors
|
|
36
|
+
- **Root cause**: npm on Windows creates both `claude` (non-executable) and `claude.cmd` (executable), and system was selecting the wrong one
|
|
37
|
+
- **Fix 1**: `which-command.js` now prefers `.cmd`/`.bat` extensions over extensionless entries when multiple matches found
|
|
38
|
+
- **Fix 2**: `claude-client.js` wraps `.cmd`/`.bat` commands with `cmd.exe /c` before spawning
|
|
39
|
+
- **Files changed**:
|
|
40
|
+
- `lib/utils/which-command.js:135-163` - Prefer `.cmd`/`.bat` in Windows path resolution
|
|
41
|
+
- `lib/utils/claude-client.js:165-179` - Wrap batch files with cmd.exe
|
|
42
|
+
- **Impact**: Fixes `analyze-diff` and commit hooks for Windows users with npm-installed Claude CLI
|
|
43
|
+
- **Compatibility**: No impact on Linux/macOS or Windows WSL setups, only affects Windows native npm installations
|
|
44
|
+
|
|
45
|
+
### 🎯 User Experience
|
|
46
|
+
|
|
47
|
+
- **Before**: Windows users with npm-installed Claude CLI got `ENOENT` errors on every command
|
|
48
|
+
- **After**: Commands work seamlessly, same as other platforms
|
|
49
|
+
- **Debug**: Added detailed logging for Windows .cmd file detection and wrapping
|
|
50
|
+
|
|
8
51
|
## [2.6.0] - 2025-12-02
|
|
9
52
|
|
|
10
53
|
### ✨ Added - Node.js 24 Compatibility
|
|
@@ -162,7 +162,23 @@ const executeClaude = (prompt, { timeout = 120000, allowedTools = [] } = {}) =>
|
|
|
162
162
|
finalArgs.push('--allowedTools', allowedTools.join(','));
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
// CRITICAL FIX: Windows .cmd/.bat file handling
|
|
166
|
+
// Why: spawn() cannot execute .cmd/.bat files directly on Windows (ENOENT error)
|
|
167
|
+
// Solution: Wrap with cmd.exe /c when command ends with .cmd or .bat
|
|
168
|
+
// Impact: Only affects Windows npm-installed CLI tools, no impact on other platforms
|
|
169
|
+
let spawnCommand = command;
|
|
170
|
+
let spawnArgs = finalArgs;
|
|
171
|
+
|
|
172
|
+
if (isWindows() && (command.endsWith('.cmd') || command.endsWith('.bat'))) {
|
|
173
|
+
logger.debug('claude-client - executeClaude', 'Wrapping .cmd/.bat with cmd.exe', {
|
|
174
|
+
originalCommand: command,
|
|
175
|
+
originalArgs: finalArgs
|
|
176
|
+
});
|
|
177
|
+
spawnCommand = 'cmd.exe';
|
|
178
|
+
spawnArgs = ['/c', command, ...finalArgs];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const fullCommand = spawnArgs.length > 0 ? `${spawnCommand} ${spawnArgs.join(' ')}` : spawnCommand;
|
|
166
182
|
|
|
167
183
|
logger.debug(
|
|
168
184
|
'claude-client - executeClaude',
|
|
@@ -176,7 +192,8 @@ const executeClaude = (prompt, { timeout = 120000, allowedTools = [] } = {}) =>
|
|
|
176
192
|
// spawn streams data, exec buffers everything in memory
|
|
177
193
|
// Node 24 Fix: Removed shell: true to avoid DEP0190 deprecation warning
|
|
178
194
|
// We now use absolute paths from which(), so shell is not needed
|
|
179
|
-
|
|
195
|
+
// Windows .cmd/.bat fix: Wrapped with cmd.exe /c (see above)
|
|
196
|
+
const claude = spawn(spawnCommand, spawnArgs, {
|
|
180
197
|
stdio: ['pipe', 'pipe', 'pipe'] // stdin, stdout, stderr
|
|
181
198
|
});
|
|
182
199
|
|
|
@@ -263,13 +280,40 @@ const executeClaude = (prompt, { timeout = 120000, allowedTools = [] } = {}) =>
|
|
|
263
280
|
|
|
264
281
|
// Write prompt to stdin
|
|
265
282
|
// Why: Claude CLI reads prompt from stdin, not command arguments
|
|
283
|
+
|
|
284
|
+
// Handle stdin errors (e.g., EOF when process terminates unexpectedly)
|
|
285
|
+
// Why: write() failures can emit 'error' events asynchronously
|
|
286
|
+
// Common in parallel execution with large prompts
|
|
287
|
+
claude.stdin.on('error', (error) => {
|
|
288
|
+
logger.error(
|
|
289
|
+
'claude-client - executeClaude',
|
|
290
|
+
'stdin stream error (process may have terminated early)',
|
|
291
|
+
{
|
|
292
|
+
error: error.message,
|
|
293
|
+
code: error.code,
|
|
294
|
+
promptLength: prompt.length,
|
|
295
|
+
duration: Date.now() - startTime
|
|
296
|
+
}
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
reject(new ClaudeClientError('Failed to write to Claude stdin - process terminated unexpectedly', {
|
|
300
|
+
cause: error,
|
|
301
|
+
context: {
|
|
302
|
+
promptLength: prompt.length,
|
|
303
|
+
errorCode: error.code,
|
|
304
|
+
errorMessage: error.message,
|
|
305
|
+
suggestion: 'Try reducing batch size or number of files per commit'
|
|
306
|
+
}
|
|
307
|
+
}));
|
|
308
|
+
});
|
|
309
|
+
|
|
266
310
|
try {
|
|
267
311
|
claude.stdin.write(prompt);
|
|
268
312
|
claude.stdin.end();
|
|
269
313
|
} catch (error) {
|
|
270
314
|
logger.error(
|
|
271
315
|
'claude-client - executeClaude',
|
|
272
|
-
'Failed to write prompt to Claude CLI stdin',
|
|
316
|
+
'Failed to write prompt to Claude CLI stdin (synchronous error)',
|
|
273
317
|
error
|
|
274
318
|
);
|
|
275
319
|
|
|
@@ -132,12 +132,32 @@ const whichViaCommand = (command) => {
|
|
|
132
132
|
timeout: 3000 // Don't wait forever
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
-
// Windows 'where' returns multiple matches
|
|
136
|
-
const
|
|
135
|
+
// Windows 'where' returns multiple matches
|
|
136
|
+
const matches = result.split('\n').map(line => line.trim()).filter(line => line.length > 0);
|
|
137
|
+
|
|
138
|
+
// CRITICAL FIX: On Windows, prefer .cmd/.bat over extensionless entries
|
|
139
|
+
// Why: npm creates both 'claude' and 'claude.cmd', but only .cmd is executable via spawn()
|
|
140
|
+
// Example: where claude returns:
|
|
141
|
+
// 1. C:\Users\...\npm\claude (NOT executable)
|
|
142
|
+
// 2. C:\Users\...\npm\claude.cmd (executable)
|
|
143
|
+
if (isWin && matches.length > 1) {
|
|
144
|
+
const cmdMatch = matches.find(m => m.endsWith('.cmd') || m.endsWith('.bat'));
|
|
145
|
+
if (cmdMatch) {
|
|
146
|
+
logger.debug('which-command - whichViaCommand', 'Preferring .cmd/.bat over extensionless', {
|
|
147
|
+
command,
|
|
148
|
+
preferred: cmdMatch,
|
|
149
|
+
allMatches: matches
|
|
150
|
+
});
|
|
151
|
+
return cmdMatch;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const firstMatch = matches[0];
|
|
137
156
|
|
|
138
157
|
logger.debug('which-command - whichViaCommand', 'Found via command', {
|
|
139
158
|
command,
|
|
140
|
-
path: firstMatch
|
|
159
|
+
path: firstMatch,
|
|
160
|
+
totalMatches: matches.length
|
|
141
161
|
});
|
|
142
162
|
|
|
143
163
|
return firstMatch;
|