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 CHANGED
@@ -1 +1 @@
1
- d547508
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 : 30;
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 = 30, since = null, from = null, to = null, dryRun = false, projects = null,
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: 30, since: null, from: null, to: null, dryRun: false, projects: null, noRedact: false, analysisMaxAgeDays: 30 };
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
- await importClaudeCode({ days: 30 }); // server URL + git identity were just configured
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-lens",
3
- "version": "0.8.88",
3
+ "version": "0.8.90",
4
4
  "type": "module",
5
5
  "description": "Centralized session analytics for AI coding tools",
6
6
  "bin": {