ai-lens 0.8.94 → 0.8.95
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/CHANGELOG.md +5 -0
- package/cli/hooks.js +6 -2
- package/cli/import/claude-code.js +25 -0
- package/cli/init.js +41 -2
- package/package.json +1 -1
package/.commithash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
defcd78
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
History of changes to the `ai-lens` CLI package on npm. New entries go on top. Format: `## X.Y.Z — YYYY-MM-DD`, followed by user-facing bullets.
|
|
4
4
|
|
|
5
|
+
## 0.8.95 — 2026-06-17
|
|
6
|
+
- fix: on Windows, Cursor hooks no longer flash a console window on every event (including new-session start) — the hook command is now wrapped in `conhost.exe --headless` (Windows 10 1809+), the same windowless form Claude Code hooks already use. Older Windows builds and macOS/Linux are unchanged. Re-run `ai-lens init` to apply
|
|
7
|
+
- feat: `ai-lens init` now warns when the configured `projects` filter does not cover the workspace you're setting up — a stale filter silently drops all capture (hooks look configured but record nothing)
|
|
8
|
+
- feat: the Claude Code history-import offer now previews how much there is to import ("Found N sessions from A to B") before asking, instead of a bare yes/no
|
|
9
|
+
|
|
5
10
|
## 0.8.94 — 2026-06-16
|
|
6
11
|
- feat: capture 8 more Claude Code events that were previously ignored — turn failures (session-limit / prompt-too-long / server-overload messages), notifications, permission requests, post-compact summaries, task created/completed, instructions loaded, and slash-command expansions. Re-run `ai-lens init` to install them; they're collected and forwarded only (no dashboard changes yet)
|
|
7
12
|
|
package/cli/hooks.js
CHANGED
|
@@ -375,9 +375,13 @@ export function cursorCaptureCommand(opts = {}) {
|
|
|
375
375
|
// hint when building the underlying command (avoids the cmd.exe `call` prefix
|
|
376
376
|
// which PowerShell doesn't understand).
|
|
377
377
|
const shell = platform === 'win32' ? 'powershell' : null;
|
|
378
|
+
// windowless: on Windows ≥1809 wrap node in `conhost.exe --headless` so Cursor
|
|
379
|
+
// doesn't flash a console window on every event (incl. session start). `& ` still
|
|
380
|
+
// works in front of conhost; the detector strips `& ` then the conhost prefix, so
|
|
381
|
+
// the form stays recognised as current (no churn). Mac/old-Windows: unchanged.
|
|
378
382
|
const cmd = customPath != null
|
|
379
|
-
? captureCommand({
|
|
380
|
-
: captureCommand({ useTilde: effectiveUseTilde, ctx, shell });
|
|
383
|
+
? captureCommand({ rawPath: true, customPath, windowless: true, ctx, shell })
|
|
384
|
+
: captureCommand({ useTilde: effectiveUseTilde, windowless: true, ctx, shell });
|
|
381
385
|
return platform === 'win32' ? `& ${cmd}` : cmd;
|
|
382
386
|
}
|
|
383
387
|
|
|
@@ -92,6 +92,31 @@ export function resolveCutoff({ days, since, from }, now = new Date()) {
|
|
|
92
92
|
return new Date(now.getTime() - d * DAY_MS).toISOString();
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Cheap preview for the init import offer — how many local transcripts (≈ sessions)
|
|
97
|
+
* fall within the window and their date span, by file mtime (no file reads). Lets
|
|
98
|
+
* init show "Found N sessions from A to B" before asking to import.
|
|
99
|
+
* @returns {{ count: number, earliest: string|null, latest: string|null }}
|
|
100
|
+
*/
|
|
101
|
+
export function previewClaudeCode({ days = 90, dir = PROJECTS_DIR } = {}, now = new Date()) {
|
|
102
|
+
if (!existsSync(dir)) return { count: 0, earliest: null, latest: null };
|
|
103
|
+
const cutoffMs = Date.parse(resolveCutoff({ days }, now));
|
|
104
|
+
let count = 0, min = Infinity, max = -Infinity;
|
|
105
|
+
for (const f of walkJsonl(dir)) {
|
|
106
|
+
let m;
|
|
107
|
+
try { m = statSync(f).mtimeMs; } catch { continue; }
|
|
108
|
+
if (m < cutoffMs) continue;
|
|
109
|
+
count++;
|
|
110
|
+
if (m < min) min = m;
|
|
111
|
+
if (m > max) max = m;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
count,
|
|
115
|
+
earliest: count ? new Date(min).toISOString() : null,
|
|
116
|
+
latest: count ? new Date(max).toISOString() : null,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
95
120
|
/**
|
|
96
121
|
* Resolve the INCLUSIVE lower bound from `--from` (or null when open-ended).
|
|
97
122
|
* Unlike the file-level `cutoff` (which only decides whether to read a file),
|
package/cli/init.js
CHANGED
|
@@ -596,6 +596,27 @@ export default async function init() {
|
|
|
596
596
|
info(' Tracking: all projects');
|
|
597
597
|
}
|
|
598
598
|
|
|
599
|
+
// Loud guard: a narrow `projects` filter that does NOT cover this workspace silently
|
|
600
|
+
// drops ALL capture (project_filter) — hooks look configured but record nothing. This
|
|
601
|
+
// bites when init inherits a stale `projects` from an old config (a leftover test path).
|
|
602
|
+
// Warn against the cwd (the workspace init is being run from).
|
|
603
|
+
if (projects) {
|
|
604
|
+
const cwd = resolve(process.cwd());
|
|
605
|
+
const norm = (p) => p.replace(/\\/g, '/').replace(/\/+$/, '').toLowerCase();
|
|
606
|
+
const cwdN = norm(cwd);
|
|
607
|
+
const covered = projects.split(',').map(norm).some(m => m && (cwdN === m || cwdN.startsWith(m + '/')));
|
|
608
|
+
if (!covered) {
|
|
609
|
+
blank();
|
|
610
|
+
warn(` ⚠ Projects filter does not include this workspace — events from here will be DROPPED.`);
|
|
611
|
+
warn(` filter: ${projects}`);
|
|
612
|
+
warn(` here: ${cwd}`);
|
|
613
|
+
info(` Capture will look configured but record nothing. Fix: re-run with \`--projects all\``);
|
|
614
|
+
info(` (track everything), or edit ~/.ai-lens/config.json and remove/adjust "projects".`);
|
|
615
|
+
info(` (Ignore if you deliberately track only other paths.)`);
|
|
616
|
+
blank();
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
599
620
|
// Build new config in memory — saved after "Proceed?" confirmation
|
|
600
621
|
const newConfig = { ...currentConfig, serverUrl, projects };
|
|
601
622
|
|
|
@@ -1256,10 +1277,28 @@ async function maybeOfferImportHistory(flags) {
|
|
|
1256
1277
|
if (flags.noImport) return;
|
|
1257
1278
|
if (!existsSync(join(homedir(), '.claude', 'projects'))) return;
|
|
1258
1279
|
|
|
1280
|
+
// Preview so the offer isn't a blind yes/no: count of local transcripts (≈ sessions)
|
|
1281
|
+
// and their date span within the 90d window (cheap mtime scan, no file reads).
|
|
1282
|
+
let preview = null;
|
|
1283
|
+
try {
|
|
1284
|
+
const mod = await import('./import/claude-code.js');
|
|
1285
|
+
if (mod.previewClaudeCode) preview = mod.previewClaudeCode({ days: 90 });
|
|
1286
|
+
} catch { /* preview is best-effort */ }
|
|
1287
|
+
if (preview && preview.count === 0) {
|
|
1288
|
+
info(' No local Claude Code history in the last 90 days — nothing to import.');
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
const day = (iso) => (iso || '').slice(0, 10);
|
|
1292
|
+
const previewLine = preview
|
|
1293
|
+
? `Found ${preview.count} Claude Code session${preview.count === 1 ? '' : 's'} from ${day(preview.earliest)} to ${day(preview.latest)} (last 90d).`
|
|
1294
|
+
: 'Local Claude Code history found.';
|
|
1295
|
+
|
|
1259
1296
|
let run = flags.importHistory || flags.yes;
|
|
1260
|
-
if (
|
|
1297
|
+
if (run) {
|
|
1298
|
+
info(` ${previewLine}`);
|
|
1299
|
+
} else {
|
|
1261
1300
|
try {
|
|
1262
|
-
const answer = (await ask(
|
|
1301
|
+
const answer = (await ask(`${previewLine} Import now? (Y/n) `)).toLowerCase();
|
|
1263
1302
|
run = answer === '' || answer === 'y' || answer === 'yes';
|
|
1264
1303
|
} catch { run = false; }
|
|
1265
1304
|
}
|