ai-lens 0.8.88 → 0.8.90
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 +6 -0
- package/cli/import/claude-code.js +8 -3
- package/cli/import.js +1 -1
- package/cli/init.js +5 -1
- package/cli/logger.js +36 -0
- package/package.json +1 -1
package/.commithash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
9596d22
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
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.90 — 2026-06-11
|
|
6
|
+
- improve: the default import window for `ai-lens import claude-code` (and the import offer inside `init`) is now 90 days instead of 30 — machines with a longer Claude Code transcript retention get their full recent history. Auto-analysis still covers only the last 30 days; older sessions enter metrics and cost rollups.
|
|
7
|
+
|
|
8
|
+
## 0.8.89 — 2026-06-11
|
|
9
|
+
- improve: `ai-lens import claude-code` (and the import step inside `init`) now shows a live progress spinner — transcripts scanned, events collected, batches shipping — instead of sitting silent for minutes on a large history. Terminal-only; piped/CI output is unchanged.
|
|
10
|
+
|
|
5
11
|
## 0.8.88 — 2026-06-11
|
|
6
12
|
- fix: events now reach the server from behind corporate antivirus/proxy TLS inspection — on a certificate-trust error the sender automatically retries once trusting the OS certificate store (Node 22.15+; opt out with `AI_LENS_TLS_STRICT=1`)
|
|
7
13
|
- feat: `ai-lens status` now diagnoses TLS failures — it shows which certificate authority actually served the connection and names the likely interceptor instead of a bare error code
|
|
@@ -30,7 +30,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
30
30
|
import { writeToSpool, canonicalizeProjectPath, deterministicEventId } from '../../client/capture.js';
|
|
31
31
|
import { PENDING_DIR, SENDING_DIR, DATA_DIR, ensureDataDir, getGitIdentity, getGitMetadata, getServerUrl, getAuthToken, getMonitoredProjects } from '../../client/config.js';
|
|
32
32
|
import { mapTranscript } from './transcript-map.js';
|
|
33
|
-
import { info, success, warn, error, heading, detail, blank } from '../logger.js';
|
|
33
|
+
import { info, success, warn, error, heading, detail, blank, progress, progressDone } from '../logger.js';
|
|
34
34
|
|
|
35
35
|
const PROJECTS_DIR = join(homedir(), '.claude', 'projects');
|
|
36
36
|
const LEDGER_PATH = join(DATA_DIR, 'import-state', 'claude-code.json');
|
|
@@ -88,7 +88,7 @@ export function resolveCutoff({ days, since, from }, now = new Date()) {
|
|
|
88
88
|
const lower = from || since;
|
|
89
89
|
if (lower) return new Date(Date.parse(lower)).toISOString();
|
|
90
90
|
if (days === 0) return new Date(0).toISOString();
|
|
91
|
-
const d = Number.isInteger(days) && days >= 0 ? days :
|
|
91
|
+
const d = Number.isInteger(days) && days >= 0 ? days : 90;
|
|
92
92
|
return new Date(now.getTime() - d * DAY_MS).toISOString();
|
|
93
93
|
}
|
|
94
94
|
|
|
@@ -284,7 +284,7 @@ async function drainSpool({ timeoutMs = 120_000 } = {}) {
|
|
|
284
284
|
|
|
285
285
|
export default async function importClaudeCode(flags) {
|
|
286
286
|
const {
|
|
287
|
-
days =
|
|
287
|
+
days = 90, since = null, from = null, to = null, dryRun = false, projects = null,
|
|
288
288
|
noRedact = false, analysisMaxAgeDays: amAgeFlag = 30,
|
|
289
289
|
} = flags;
|
|
290
290
|
const analysisMaxAgeDays = Number.isInteger(amAgeFlag) && amAgeFlag >= 0 ? amAgeFlag : 30;
|
|
@@ -348,6 +348,7 @@ export default async function importClaudeCode(flags) {
|
|
|
348
348
|
let drainFailed = false;
|
|
349
349
|
const flush = async () => {
|
|
350
350
|
if (dryRun) { inFlight = 0; return true; }
|
|
351
|
+
progress(`shipping ${inFlight} event(s) to the server…`);
|
|
351
352
|
const ok = await drainSpool();
|
|
352
353
|
if (!ok) { drainFailed = true; return false; }
|
|
353
354
|
inFlight = 0;
|
|
@@ -360,6 +361,7 @@ export default async function importClaudeCode(flags) {
|
|
|
360
361
|
// Pass 1 — cheap shortlist (mtime + ledger), no file reads. A transcript's
|
|
361
362
|
// filename basename IS the session UUID, so we can gather session ids for the
|
|
362
363
|
// coverage query without reading anything.
|
|
364
|
+
progress('scanning local transcripts…');
|
|
363
365
|
const candidates = [];
|
|
364
366
|
for (const filePath of allFiles) {
|
|
365
367
|
let st;
|
|
@@ -378,8 +380,11 @@ export default async function importClaudeCode(flags) {
|
|
|
378
380
|
)]);
|
|
379
381
|
|
|
380
382
|
// Pass 2 — read, map, de-overlap, ship.
|
|
383
|
+
let filesProcessed = 0;
|
|
381
384
|
for (const { filePath, fp } of candidates) {
|
|
382
385
|
if (drainFailed) break;
|
|
386
|
+
filesProcessed++;
|
|
387
|
+
progress(`${dryRun ? 'scanning' : 'importing'}… ${filesProcessed}/${candidates.length} transcript(s) · ${eventCount} event(s) from ${sessionCount} session(s)`);
|
|
383
388
|
const { lines, lastTs } = await readTranscript(filePath);
|
|
384
389
|
if (!lastTs || lastTs < cutoff) { filesSkipped++; continue; }
|
|
385
390
|
|
package/cli/import.js
CHANGED
|
@@ -17,7 +17,7 @@ const INT_KEYS = new Set(['days', 'analysisMaxAgeDays']);
|
|
|
17
17
|
* REAL import instead of a preview.)
|
|
18
18
|
*/
|
|
19
19
|
export function parseFlags(argv) {
|
|
20
|
-
const flags = { days:
|
|
20
|
+
const flags = { days: 90, since: null, from: null, to: null, dryRun: false, projects: null, noRedact: false, analysisMaxAgeDays: 30 };
|
|
21
21
|
const errors = [];
|
|
22
22
|
for (let i = 0; i < argv.length; i++) {
|
|
23
23
|
const arg = argv[i];
|
package/cli/init.js
CHANGED
|
@@ -1251,7 +1251,11 @@ async function maybeOfferImportHistory(flags) {
|
|
|
1251
1251
|
heading('Importing local history');
|
|
1252
1252
|
try {
|
|
1253
1253
|
const { default: importClaudeCode } = await import('./import/claude-code.js');
|
|
1254
|
-
|
|
1254
|
+
// 90 days, not 30: Claude Code's local retention (~30d default) bounds the
|
|
1255
|
+
// volume anyway, but machines with longer retention get their full recent
|
|
1256
|
+
// history. LLM-analysis cost stays capped by the importer's
|
|
1257
|
+
// analysisMaxAgeDays=30 — older sessions land in metrics/cost rollups only.
|
|
1258
|
+
await importClaudeCode({ days: 90 }); // server URL + git identity were just configured
|
|
1255
1259
|
} catch (err) {
|
|
1256
1260
|
error(` Import failed: ${err.message}`);
|
|
1257
1261
|
info(' You can retry later: `npx -y ai-lens import claude-code`');
|
package/cli/logger.js
CHANGED
|
@@ -40,37 +40,73 @@ const YELLOW = useColor ? '\x1b[33m' : '';
|
|
|
40
40
|
const RED = useColor ? '\x1b[31m' : '';
|
|
41
41
|
const CYAN = useColor ? '\x1b[36m' : '';
|
|
42
42
|
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Transient progress line (spinner). TTY-only: in pipes/CI it is a no-op, so
|
|
45
|
+
// scripted output and init.log stay exactly as before. Every regular print
|
|
46
|
+
// clears an active progress line first so interleaved warns don't garble it.
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
49
|
+
const PROGRESS_MIN_REPAINT_MS = 80; // cap repaints at ~12fps no matter how hot the caller loop is
|
|
50
|
+
let progressActive = false;
|
|
51
|
+
let progressFrame = 0;
|
|
52
|
+
let progressLastPaint = 0;
|
|
53
|
+
|
|
54
|
+
/** Show/update a transient one-line progress indicator (TTY only, throttled). */
|
|
55
|
+
export function progress(text) {
|
|
56
|
+
if (!useColor) return; // useColor === stdout.isTTY
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
if (progressActive && now - progressLastPaint < PROGRESS_MIN_REPAINT_MS) return;
|
|
59
|
+
progressLastPaint = now;
|
|
60
|
+
const frame = SPINNER_FRAMES[progressFrame++ % SPINNER_FRAMES.length];
|
|
61
|
+
process.stdout.write(`\r\x1b[2K${CYAN}${frame}${RESET} ${DIM}${text}${RESET}`);
|
|
62
|
+
progressActive = true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Clear the progress line (safe to call when none is active). */
|
|
66
|
+
export function progressDone() {
|
|
67
|
+
if (!progressActive) return;
|
|
68
|
+
process.stdout.write('\r\x1b[2K');
|
|
69
|
+
progressActive = false;
|
|
70
|
+
}
|
|
71
|
+
|
|
43
72
|
export function info(msg) {
|
|
73
|
+
progressDone();
|
|
44
74
|
console.log(msg);
|
|
45
75
|
write('INFO', msg);
|
|
46
76
|
}
|
|
47
77
|
|
|
48
78
|
export function success(msg) {
|
|
79
|
+
progressDone();
|
|
49
80
|
console.log(`${GREEN}${msg}${RESET}`);
|
|
50
81
|
write('INFO', msg);
|
|
51
82
|
}
|
|
52
83
|
|
|
53
84
|
export function warn(msg) {
|
|
85
|
+
progressDone();
|
|
54
86
|
console.log(`${YELLOW}${msg}${RESET}`);
|
|
55
87
|
write('WARN', msg);
|
|
56
88
|
}
|
|
57
89
|
|
|
58
90
|
export function error(msg) {
|
|
91
|
+
progressDone();
|
|
59
92
|
console.log(`${RED}${msg}${RESET}`);
|
|
60
93
|
write('ERROR', msg);
|
|
61
94
|
}
|
|
62
95
|
|
|
63
96
|
export function heading(msg) {
|
|
97
|
+
progressDone();
|
|
64
98
|
console.log(`\n${BOLD}${CYAN}${msg}${RESET}`);
|
|
65
99
|
write('INFO', msg);
|
|
66
100
|
}
|
|
67
101
|
|
|
68
102
|
export function detail(msg) {
|
|
103
|
+
progressDone();
|
|
69
104
|
console.log(`${DIM} ${msg}${RESET}`);
|
|
70
105
|
write('INFO', msg);
|
|
71
106
|
}
|
|
72
107
|
|
|
73
108
|
export function blank() {
|
|
109
|
+
progressDone();
|
|
74
110
|
console.log();
|
|
75
111
|
}
|
|
76
112
|
|