ai-lens 0.8.21 → 0.8.22
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/.commithash +1 -1
- package/cli/hooks.js +7 -1
- package/cli/status.js +63 -10
- package/package.json +1 -1
package/.commithash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
23120cb
|
package/cli/hooks.js
CHANGED
|
@@ -106,9 +106,15 @@ export function captureCommand() {
|
|
|
106
106
|
const capturePath = CAPTURE_PATH.replace(/\\/g, '/');
|
|
107
107
|
const escaped = shellEscape(capturePath);
|
|
108
108
|
// /usr/bin/env doesn't need shell-escaping, but named paths do
|
|
109
|
-
|
|
109
|
+
const base = nodePath === '/usr/bin/env node'
|
|
110
110
|
? `/usr/bin/env node ${escaped}`
|
|
111
111
|
: `${shellEscape(nodePath.replace(/\\/g, '/'))} ${escaped}`;
|
|
112
|
+
// On Windows, prefix with "& " so the command works in both PowerShell and cmd.exe.
|
|
113
|
+
// PowerShell treats a quoted path like "node.exe" as a string expression, not a command;
|
|
114
|
+
// the & (call) operator is required. In cmd.exe, & is a command separator — the empty
|
|
115
|
+
// first part is a no-op, and the real command runs as the second part.
|
|
116
|
+
if (process.platform === 'win32') return `& ${base}`;
|
|
117
|
+
return base;
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
// ---------------------------------------------------------------------------
|
package/cli/status.js
CHANGED
|
@@ -98,8 +98,10 @@ function validateHookCommandPaths(tool) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// Extract node path (first token). Skip if /usr/bin/env node (node resolved via PATH)
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
// Strip "& " prefix (PowerShell call operator, added on Windows) before matching.
|
|
102
|
+
const cmdForNode = command.replace(/^& /, '');
|
|
103
|
+
if (!cmdForNode.startsWith('/usr/bin/env node')) {
|
|
104
|
+
const nodeMatch = cmdForNode.match(/^["']([^"']+)["']|^(\S+)/);
|
|
103
105
|
if (nodeMatch) {
|
|
104
106
|
const nodePath = nodeMatch[1] || nodeMatch[2];
|
|
105
107
|
if (nodePath !== 'node' && !existsSync(nodePath)) {
|
|
@@ -174,21 +176,72 @@ function checkCaptureRun(installedTools) {
|
|
|
174
176
|
}
|
|
175
177
|
} catch { /* best effort */ }
|
|
176
178
|
|
|
177
|
-
|
|
179
|
+
// Shell command test: execute the actual hook command string through the shell
|
|
180
|
+
// to catch path/quoting/slash issues that direct spawn bypasses.
|
|
181
|
+
// This simulates how Claude Code / Cursor actually invoke hooks on the OS.
|
|
182
|
+
const shellSessionId = 'status-shell-' + Date.now() + '-' + Math.random().toString(36).slice(2, 7);
|
|
183
|
+
const shellEvent = JSON.stringify({
|
|
184
|
+
hook_event_name: 'Stop',
|
|
185
|
+
session_id: shellSessionId,
|
|
186
|
+
stop_reason: 'test',
|
|
187
|
+
});
|
|
188
|
+
let shellOk = false;
|
|
189
|
+
let shellDetail = '';
|
|
190
|
+
try {
|
|
191
|
+
const shellResult = spawnSync(command, {
|
|
192
|
+
input: shellEvent,
|
|
193
|
+
shell: true,
|
|
194
|
+
encoding: 'utf-8',
|
|
195
|
+
timeout: 10_000,
|
|
196
|
+
env: { ...process.env, AI_LENS_PROJECTS: join(homedir(), '.ai-lens-status-check-nonexistent') },
|
|
197
|
+
windowsHide: true,
|
|
198
|
+
});
|
|
199
|
+
if (shellResult.error) throw shellResult.error;
|
|
200
|
+
shellOk = shellResult.status === 0;
|
|
201
|
+
shellDetail = shellOk ? 'exit 0' : `Exit code: ${shellResult.status}\nError: ${(shellResult.stderr || '').trim() || '(no stderr)'}`;
|
|
202
|
+
} catch (err) {
|
|
203
|
+
shellDetail = `Exit code: N/A\nError: ${err.message}`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let shellCaptureNote = '';
|
|
207
|
+
try {
|
|
208
|
+
if (existsSync(CAPTURE_LOG_PATH)) {
|
|
209
|
+
const logLines = readFileSync(CAPTURE_LOG_PATH, 'utf-8').split(/\r?\n/).filter(Boolean);
|
|
210
|
+
for (let i = logLines.length - 1; i >= 0; i--) {
|
|
211
|
+
try {
|
|
212
|
+
const entry = JSON.parse(logLines[i]);
|
|
213
|
+
if (entry.session_id === shellSessionId) {
|
|
214
|
+
shellCaptureNote = entry.reason || entry.msg || 'unknown';
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
} catch { /* skip unparseable lines */ }
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} catch { /* best effort */ }
|
|
221
|
+
|
|
222
|
+
toolResults.push({ name, ok: exitOk, cmd: testCmd, exitDetail, captureNote, shellOk, shellCmd: command, shellDetail, shellCaptureNote });
|
|
178
223
|
}
|
|
179
224
|
|
|
180
|
-
const allOk = toolResults.every(r => r.ok);
|
|
181
|
-
const failedTools = toolResults.filter(r => !r.ok).map(r => r.name);
|
|
225
|
+
const allOk = toolResults.every(r => r.ok && r.shellOk);
|
|
226
|
+
const failedTools = toolResults.filter(r => !r.ok || !r.shellOk).map(r => r.name);
|
|
182
227
|
const summaryParts = toolResults.map(r => {
|
|
183
|
-
const
|
|
184
|
-
|
|
228
|
+
const directStatus = r.ok ? 'OK' : 'FAILED';
|
|
229
|
+
const shellStatus = r.shellOk ? 'OK' : 'FAILED';
|
|
230
|
+
const parts = [`${r.name}: ${directStatus}`];
|
|
231
|
+
if (!r.shellOk) parts[0] += `, shell: ${shellStatus}`;
|
|
232
|
+
if (r.captureNote) parts[0] += ` (${r.captureNote})`;
|
|
233
|
+
return parts[0];
|
|
185
234
|
});
|
|
186
235
|
const summary = allOk
|
|
187
236
|
? `capture runs OK (${summaryParts.join(', ')})`
|
|
188
237
|
: `capture failed for: ${failedTools.join(', ')}`;
|
|
189
|
-
const detail = toolResults.map(r =>
|
|
190
|
-
`[${r.name}]\n Ran: ${r.cmd}\n Result: ${r.exitDetail}
|
|
191
|
-
|
|
238
|
+
const detail = toolResults.map(r => {
|
|
239
|
+
let text = `[${r.name}]\n Ran: ${r.cmd}\n Result: ${r.exitDetail}`;
|
|
240
|
+
if (r.captureNote) text += `\n Capture log: ${r.captureNote}`;
|
|
241
|
+
text += `\n Shell: ${r.shellCmd} < (test event)\n Shell result: ${r.shellDetail}`;
|
|
242
|
+
if (r.shellCaptureNote) text += `\n Shell capture log: ${r.shellCaptureNote}`;
|
|
243
|
+
return text;
|
|
244
|
+
}).join('\n\n');
|
|
192
245
|
|
|
193
246
|
return { ok: allOk, summary, detail };
|
|
194
247
|
}
|