ai-lens 0.8.22 → 0.8.24
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 +28 -22
- package/cli/init.js +1 -14
- package/cli/status.js +16 -9
- package/package.json +1 -1
package/.commithash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
39d3d60
|
package/cli/hooks.js
CHANGED
|
@@ -106,15 +106,17 @@ 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
|
+
return nodePath === '/usr/bin/env node'
|
|
110
110
|
? `/usr/bin/env node ${escaped}`
|
|
111
111
|
: `${shellEscape(nodePath.replace(/\\/g, '/'))} ${escaped}`;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Cursor on Windows executes hooks via PowerShell, which treats a quoted path like
|
|
115
|
+
// "node.exe" as a string expression, not a command invocation. The & (call) operator
|
|
116
|
+
// is required. Claude Code uses bash or cmd.exe where & is not needed (and breaks bash).
|
|
117
|
+
export function cursorCaptureCommand() {
|
|
118
|
+
const cmd = captureCommand();
|
|
119
|
+
return process.platform === 'win32' ? `& ${cmd}` : cmd;
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
// ---------------------------------------------------------------------------
|
|
@@ -169,20 +171,20 @@ const CLAUDE_CODE_HOOKS = {
|
|
|
169
171
|
};
|
|
170
172
|
|
|
171
173
|
const CURSOR_HOOKS = {
|
|
172
|
-
sessionStart: () => ({ command:
|
|
173
|
-
beforeSubmitPrompt: () => ({ command:
|
|
174
|
-
postToolUse: () => ({ command:
|
|
175
|
-
postToolUseFailure: () => ({ command:
|
|
176
|
-
afterFileEdit: () => ({ command:
|
|
177
|
-
afterShellExecution: () => ({ command:
|
|
178
|
-
afterMCPExecution: () => ({ command:
|
|
179
|
-
subagentStart: () => ({ command:
|
|
180
|
-
subagentStop: () => ({ command:
|
|
181
|
-
preCompact: () => ({ command:
|
|
182
|
-
afterAgentResponse: () => ({ command:
|
|
183
|
-
afterAgentThought: () => ({ command:
|
|
184
|
-
stop: () => ({ command:
|
|
185
|
-
sessionEnd: () => ({ command:
|
|
174
|
+
sessionStart: () => ({ command: cursorCaptureCommand() }),
|
|
175
|
+
beforeSubmitPrompt: () => ({ command: cursorCaptureCommand() }),
|
|
176
|
+
postToolUse: () => ({ command: cursorCaptureCommand() }),
|
|
177
|
+
postToolUseFailure: () => ({ command: cursorCaptureCommand() }),
|
|
178
|
+
afterFileEdit: () => ({ command: cursorCaptureCommand() }),
|
|
179
|
+
afterShellExecution: () => ({ command: cursorCaptureCommand() }),
|
|
180
|
+
afterMCPExecution: () => ({ command: cursorCaptureCommand() }),
|
|
181
|
+
subagentStart: () => ({ command: cursorCaptureCommand() }),
|
|
182
|
+
subagentStop: () => ({ command: cursorCaptureCommand() }),
|
|
183
|
+
preCompact: () => ({ command: cursorCaptureCommand() }),
|
|
184
|
+
afterAgentResponse: () => ({ command: cursorCaptureCommand() }),
|
|
185
|
+
afterAgentThought: () => ({ command: cursorCaptureCommand() }),
|
|
186
|
+
stop: () => ({ command: cursorCaptureCommand() }),
|
|
187
|
+
sessionEnd: () => ({ command: cursorCaptureCommand() }),
|
|
186
188
|
};
|
|
187
189
|
|
|
188
190
|
export const TOOL_CONFIGS = [
|
|
@@ -224,7 +226,11 @@ export function isAiLensHook(entry) {
|
|
|
224
226
|
}
|
|
225
227
|
|
|
226
228
|
function isCurrentAiLensHook(entry, expected) {
|
|
227
|
-
|
|
229
|
+
// Extract expected command from the hook definition (supports per-tool commands,
|
|
230
|
+
// e.g. Cursor uses & prefix on Windows for PowerShell, Claude Code does not).
|
|
231
|
+
const expected_cmd = expected?.command
|
|
232
|
+
|| expected?.hooks?.[0]?.command
|
|
233
|
+
|| captureCommand(); // fallback
|
|
228
234
|
// Normalize separators so hooks installed before path-normalization fix still match
|
|
229
235
|
const norm = s => (s || '').replace(/\\/g, '/');
|
|
230
236
|
// Flat format (Cursor)
|
package/cli/init.js
CHANGED
|
@@ -326,7 +326,6 @@ export default async function init() {
|
|
|
326
326
|
// Banner
|
|
327
327
|
heading(`AI Lens — Init v${version} (${commit})`);
|
|
328
328
|
detail(`capture.js: ${CAPTURE_PATH}`);
|
|
329
|
-
blank();
|
|
330
329
|
|
|
331
330
|
// Detect installed tools
|
|
332
331
|
heading('Detecting installed AI tools...');
|
|
@@ -342,7 +341,6 @@ export default async function init() {
|
|
|
342
341
|
for (const tool of tools) {
|
|
343
342
|
success(` Found: ${tool.name} (${tool.dirPath})`);
|
|
344
343
|
}
|
|
345
|
-
blank();
|
|
346
344
|
|
|
347
345
|
// Configuration
|
|
348
346
|
heading('Configuration');
|
|
@@ -408,7 +406,6 @@ export default async function init() {
|
|
|
408
406
|
|
|
409
407
|
// Build new config in memory — saved after "Proceed?" confirmation
|
|
410
408
|
const newConfig = { ...currentConfig, serverUrl, projects };
|
|
411
|
-
blank();
|
|
412
409
|
|
|
413
410
|
// Install client files to ~/.ai-lens/client/
|
|
414
411
|
heading('Installing client files...');
|
|
@@ -420,8 +417,6 @@ export default async function init() {
|
|
|
420
417
|
return;
|
|
421
418
|
}
|
|
422
419
|
|
|
423
|
-
blank();
|
|
424
|
-
|
|
425
420
|
// Authentication
|
|
426
421
|
heading('Authentication');
|
|
427
422
|
if (currentConfig.authToken) {
|
|
@@ -476,7 +471,6 @@ export default async function init() {
|
|
|
476
471
|
info(' Or re-run init when Auth0 is configured on the server.');
|
|
477
472
|
}
|
|
478
473
|
}
|
|
479
|
-
blank();
|
|
480
474
|
|
|
481
475
|
// Analyze each tool
|
|
482
476
|
heading('Analyzing hook configurations...');
|
|
@@ -529,7 +523,6 @@ export default async function init() {
|
|
|
529
523
|
const plan = describePlan(tool, analysis);
|
|
530
524
|
if (!plan) continue;
|
|
531
525
|
info(` ${plan.description}`);
|
|
532
|
-
detail(`hooks: ${plan.hooks.join(', ')}`);
|
|
533
526
|
}
|
|
534
527
|
blank();
|
|
535
528
|
|
|
@@ -541,7 +534,6 @@ export default async function init() {
|
|
|
541
534
|
return;
|
|
542
535
|
}
|
|
543
536
|
}
|
|
544
|
-
blank();
|
|
545
537
|
|
|
546
538
|
// Apply
|
|
547
539
|
heading('Applying changes...');
|
|
@@ -567,7 +559,6 @@ export default async function init() {
|
|
|
567
559
|
// Persist config only after hooks are written — avoids inconsistency where
|
|
568
560
|
// config has new serverUrl/projects but hooks still use old commands.
|
|
569
561
|
saveLensConfig(newConfig);
|
|
570
|
-
blank();
|
|
571
562
|
|
|
572
563
|
// Verify hooks were written correctly
|
|
573
564
|
heading('Verifying hooks...');
|
|
@@ -596,7 +587,6 @@ export default async function init() {
|
|
|
596
587
|
}
|
|
597
588
|
}
|
|
598
589
|
}
|
|
599
|
-
blank();
|
|
600
590
|
|
|
601
591
|
// Summary
|
|
602
592
|
heading('Summary');
|
|
@@ -610,7 +600,6 @@ export default async function init() {
|
|
|
610
600
|
if (results.some(r => !r.ok) || verifyFailed) {
|
|
611
601
|
process.exitCode = 1;
|
|
612
602
|
}
|
|
613
|
-
blank();
|
|
614
603
|
}
|
|
615
604
|
|
|
616
605
|
// Enable hooks if globally disabled (e.g. disableAllHooks in settings.local.json)
|
|
@@ -728,7 +717,6 @@ export default async function init() {
|
|
|
728
717
|
} else {
|
|
729
718
|
info(' Skipped');
|
|
730
719
|
}
|
|
731
|
-
blank();
|
|
732
720
|
}
|
|
733
721
|
|
|
734
722
|
// Cursor MCP
|
|
@@ -754,7 +742,6 @@ export default async function init() {
|
|
|
754
742
|
} else {
|
|
755
743
|
info(' Skipped');
|
|
756
744
|
}
|
|
757
|
-
blank();
|
|
758
745
|
}
|
|
759
746
|
|
|
760
747
|
// Quick verification
|
|
@@ -828,7 +815,7 @@ export default async function init() {
|
|
|
828
815
|
'X-Auth-Token': finalConfig.authToken,
|
|
829
816
|
};
|
|
830
817
|
if (gitEmail) headers['X-Developer-Git-Email'] = gitEmail;
|
|
831
|
-
if (gitName) headers['X-Developer-Name'] = gitName;
|
|
818
|
+
if (gitName) headers['X-Developer-Name'] = encodeURIComponent(gitName);
|
|
832
819
|
|
|
833
820
|
const parsed = new URL(`${verifyUrl}/api/events`);
|
|
834
821
|
const isHttps = parsed.protocol === 'https:';
|
package/cli/status.js
CHANGED
|
@@ -188,14 +188,21 @@ function checkCaptureRun(installedTools) {
|
|
|
188
188
|
let shellOk = false;
|
|
189
189
|
let shellDetail = '';
|
|
190
190
|
try {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
191
|
+
// On Windows, Cursor runs hooks via PowerShell, but shell:true uses cmd.exe.
|
|
192
|
+
// Use powershell.exe explicitly so the `& "..."` call-operator syntax works.
|
|
193
|
+
const isWin = process.platform === 'win32';
|
|
194
|
+
const shellResult = spawnSync(
|
|
195
|
+
isWin ? 'powershell.exe' : command,
|
|
196
|
+
isWin ? ['-NoProfile', '-Command', command] : [],
|
|
197
|
+
{
|
|
198
|
+
input: shellEvent,
|
|
199
|
+
shell: !isWin,
|
|
200
|
+
encoding: 'utf-8',
|
|
201
|
+
timeout: 10_000,
|
|
202
|
+
env: { ...process.env, AI_LENS_PROJECTS: join(homedir(), '.ai-lens-status-check-nonexistent') },
|
|
203
|
+
windowsHide: true,
|
|
204
|
+
},
|
|
205
|
+
);
|
|
199
206
|
if (shellResult.error) throw shellResult.error;
|
|
200
207
|
shellOk = shellResult.status === 0;
|
|
201
208
|
shellDetail = shellOk ? 'exit 0' : `Exit code: ${shellResult.status}\nError: ${(shellResult.stderr || '').trim() || '(no stderr)'}`;
|
|
@@ -692,7 +699,7 @@ async function checkE2e(serverUrl, authToken) {
|
|
|
692
699
|
'Content-Type': 'application/json',
|
|
693
700
|
'X-Auth-Token': authToken,
|
|
694
701
|
...(email && { 'X-Developer-Git-Email': email }),
|
|
695
|
-
...(name && { 'X-Developer-Name': name }),
|
|
702
|
+
...(name && { 'X-Developer-Name': encodeURIComponent(name) }),
|
|
696
703
|
},
|
|
697
704
|
body: JSON.stringify(testEvent),
|
|
698
705
|
signal: AbortSignal.timeout(8000),
|