@swestash/agent-lens 0.9.0 → 0.9.2

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.
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
5
- import { dirname as dirname6, join as join12 } from "path";
4
+ import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
5
+ import { dirname as dirname7, join as join13 } from "path";
6
6
  import { fileURLToPath as fileURLToPath3 } from "url";
7
7
 
8
8
  // ../../node_modules/.pnpm/cac@6.7.14/node_modules/cac/dist/index.mjs
@@ -2077,8 +2077,8 @@ function runService(action, params) {
2077
2077
  }
2078
2078
 
2079
2079
  // ../ingest/dist/run.js
2080
- import { readFileSync as readFileSync7, statSync as statSync5, mkdirSync as mkdirSync4, existsSync as existsSync4 } from "fs";
2081
- import { dirname as dirname4, join as join9 } from "path";
2080
+ import { readFileSync as readFileSync7, statSync as statSync5, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
2081
+ import { dirname as dirname5, join as join10 } from "path";
2082
2082
 
2083
2083
  // ../ingest/dist/db.js
2084
2084
  import Database from "better-sqlite3";
@@ -3323,8 +3323,96 @@ function deriveFileChanges(db, dirty) {
3323
3323
  return { count, version: FILECHANGES_VERSION };
3324
3324
  }
3325
3325
 
3326
- // ../ingest/dist/pipeline.js
3326
+ // ../ingest/dist/canonicalize.js
3327
+ import { existsSync as existsSync4 } from "fs";
3328
+ import { homedir as homedir3 } from "os";
3327
3329
  import { createHash as createHash4 } from "crypto";
3330
+ import { dirname as dirname4, join as join6 } from "path";
3331
+ function projectId(agentId, path3) {
3332
+ return createHash4("sha1").update(`${agentId}\0${path3}`).digest("hex").slice(0, 16);
3333
+ }
3334
+ var strip = (p) => p.length > 1 ? p.replace(/\/$/, "") : p;
3335
+ function nearestGitRoot(path3, home) {
3336
+ let cur = strip(path3);
3337
+ while (cur !== home && cur !== "/" && cur.length > 0) {
3338
+ if (existsSync4(join6(cur, ".git")))
3339
+ return cur;
3340
+ const next = dirname4(cur);
3341
+ if (next === cur)
3342
+ break;
3343
+ cur = next;
3344
+ }
3345
+ return null;
3346
+ }
3347
+ function canonicalizeProjects(db, opts = {}) {
3348
+ const home = strip(opts.homeDir ?? homedir3());
3349
+ const rows = db.prepare("SELECT id, agent_id, path FROM projects").all();
3350
+ const byPath = /* @__PURE__ */ new Map();
3351
+ for (const r of rows)
3352
+ byPath.set(strip(r.path), { id: r.id, agent_id: r.agent_id });
3353
+ const withMains = new Set(db.prepare("SELECT DISTINCT project_id FROM sessions WHERE is_sidechain = 0 AND project_id IS NOT NULL").all().map((r) => r.project_id));
3354
+ const memo = /* @__PURE__ */ new Map();
3355
+ function canon(path3) {
3356
+ const p = strip(path3);
3357
+ const hit = memo.get(p);
3358
+ if (hit)
3359
+ return hit;
3360
+ memo.set(p, p);
3361
+ let out = p;
3362
+ if (p !== home) {
3363
+ const git = nearestGitRoot(p, home);
3364
+ if (git) {
3365
+ out = git;
3366
+ } else if (existsSync4(p) || !withMains.has(byPath.get(p)?.id ?? "")) {
3367
+ for (let cur = dirname4(p); cur !== "/" && cur !== home && cur.length > 0; cur = dirname4(cur)) {
3368
+ if (byPath.has(cur)) {
3369
+ out = canon(cur);
3370
+ break;
3371
+ }
3372
+ if (dirname4(cur) === cur)
3373
+ break;
3374
+ }
3375
+ }
3376
+ }
3377
+ memo.set(p, out);
3378
+ return out;
3379
+ }
3380
+ let merged = 0;
3381
+ const upsertProject = db.prepare(`INSERT INTO projects (id, agent_id, path, encoded_dir, first_seen, last_seen)
3382
+ VALUES (?, ?, ?, NULL, NULL, NULL) ON CONFLICT(agent_id, path) DO NOTHING`);
3383
+ const repointSessions = db.prepare("UPDATE sessions SET project_id = ? WHERE project_id = ?");
3384
+ const repointFileChanges = db.prepare("UPDATE file_changes SET project_id = ? WHERE project_id = ?");
3385
+ const touched = /* @__PURE__ */ new Set();
3386
+ db.transaction(() => {
3387
+ for (const r of rows) {
3388
+ const target = canon(r.path);
3389
+ if (target === strip(r.path))
3390
+ continue;
3391
+ const targetId = byPath.get(target)?.id ?? projectId(r.agent_id, target);
3392
+ if (targetId === r.id)
3393
+ continue;
3394
+ if (!byPath.has(target)) {
3395
+ upsertProject.run(targetId, r.agent_id, target);
3396
+ byPath.set(target, { id: targetId, agent_id: r.agent_id });
3397
+ }
3398
+ repointSessions.run(targetId, r.id);
3399
+ repointFileChanges.run(targetId, r.id);
3400
+ touched.add(targetId);
3401
+ merged++;
3402
+ }
3403
+ const refresh = db.prepare(`UPDATE projects SET
3404
+ first_seen = (SELECT MIN(started_at) FROM sessions WHERE project_id = projects.id),
3405
+ last_seen = (SELECT MAX(COALESCE(ended_at, started_at)) FROM sessions WHERE project_id = projects.id)
3406
+ WHERE id = ?`);
3407
+ for (const id of touched)
3408
+ refresh.run(id);
3409
+ })();
3410
+ const removed = db.prepare("DELETE FROM projects WHERE id NOT IN (SELECT DISTINCT project_id FROM sessions WHERE project_id IS NOT NULL)").run().changes;
3411
+ return { merged, removed };
3412
+ }
3413
+
3414
+ // ../ingest/dist/pipeline.js
3415
+ import { createHash as createHash5 } from "crypto";
3328
3416
  function newStats() {
3329
3417
  return { files: 0, skipped: 0, malformed: 0, newEvents: 0 };
3330
3418
  }
@@ -3362,7 +3450,7 @@ function skillSummary(body) {
3362
3450
  return null;
3363
3451
  }
3364
3452
  function skillVersionId(name, body) {
3365
- return createHash4("sha1").update(name).update("\0").update(body).digest("hex").slice(0, 16);
3453
+ return createHash5("sha1").update(name).update("\0").update(body).digest("hex").slice(0, 16);
3366
3454
  }
3367
3455
  function pruneExcluded(db, excludedPaths) {
3368
3456
  if (!excludedPaths.length)
@@ -3744,11 +3832,11 @@ function ingestFile(db, stmts, adapter, file, lines, meta, now2, stats) {
3744
3832
  result_summary: tr.result_summary ?? null
3745
3833
  });
3746
3834
  }
3747
- let projectId = null;
3835
+ let projectId2 = null;
3748
3836
  if (sessionMeta.cwd) {
3749
- projectId = createHash4("sha1").update(`${adapter.agentId}\0${sessionMeta.cwd}`).digest("hex").slice(0, 16);
3837
+ projectId2 = createHash5("sha1").update(`${adapter.agentId}\0${sessionMeta.cwd}`).digest("hex").slice(0, 16);
3750
3838
  stmts.upsertProject.run({
3751
- id: projectId,
3839
+ id: projectId2,
3752
3840
  agent_id: adapter.agentId,
3753
3841
  path: sessionMeta.cwd,
3754
3842
  encoded_dir: file.encodedDir || null,
@@ -3759,7 +3847,7 @@ function ingestFile(db, stmts, adapter, file, lines, meta, now2, stats) {
3759
3847
  id: file.sessionId,
3760
3848
  agent_id: adapter.agentId,
3761
3849
  source_id: file.sourceId,
3762
- project_id: projectId,
3850
+ project_id: projectId2,
3763
3851
  slug: sessionMeta.slug ?? null,
3764
3852
  ai_title: sessionMeta.ai_title ?? null,
3765
3853
  cli_version: sessionMeta.cli_version ?? null,
@@ -3782,10 +3870,10 @@ function ingestFile(db, stmts, adapter, file, lines, meta, now2, stats) {
3782
3870
 
3783
3871
  // ../ingest/dist/workflows.js
3784
3872
  import { readdirSync as readdirSync3, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
3785
- import { basename as basename3, join as join6 } from "path";
3873
+ import { basename as basename3, join as join7 } from "path";
3786
3874
 
3787
3875
  // ../ingest/dist/redact.js
3788
- import { createHash as createHash5, randomBytes } from "crypto";
3876
+ import { createHash as createHash6, randomBytes } from "crypto";
3789
3877
  function parseExcludes(csv) {
3790
3878
  return (csv ?? "").split(",").map((s) => s.trim()).filter(Boolean).map(encodeProjectPath);
3791
3879
  }
@@ -3794,17 +3882,17 @@ function isExcludedArchivePath(filePath, excluded) {
3794
3882
  }
3795
3883
 
3796
3884
  // ../ingest/dist/fileread.js
3797
- import { createHash as createHash6 } from "crypto";
3885
+ import { createHash as createHash7 } from "crypto";
3798
3886
  import { openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
3799
3887
  import { StringDecoder } from "string_decoder";
3800
3888
  var STREAM_THRESHOLD = 8 * 1024 * 1024;
3801
3889
  var CHUNK2 = 64 * 1024;
3802
3890
  function sha256(buf) {
3803
- return createHash6("sha256").update(buf).digest("hex");
3891
+ return createHash7("sha256").update(buf).digest("hex");
3804
3892
  }
3805
3893
  function sha256File(path3) {
3806
3894
  const fd = openSync2(path3, "r");
3807
- const h = createHash6("sha256");
3895
+ const h = createHash7("sha256");
3808
3896
  const buf = Buffer.allocUnsafe(CHUNK2);
3809
3897
  try {
3810
3898
  let n;
@@ -3850,7 +3938,7 @@ function walkSidecars(dir, inWorkflows, out) {
3850
3938
  return;
3851
3939
  }
3852
3940
  for (const e of entries) {
3853
- const p = join6(dir, e.name);
3941
+ const p = join7(dir, e.name);
3854
3942
  if (e.isDirectory())
3855
3943
  walkSidecars(p, inWorkflows || e.name === "workflows", out);
3856
3944
  else if (e.isFile() && inWorkflows && /^wf_.+\.json$/.test(e.name))
@@ -3859,11 +3947,11 @@ function walkSidecars(dir, inWorkflows, out) {
3859
3947
  }
3860
3948
  function discoverSidecars(sourceArchiveDir) {
3861
3949
  const out = [];
3862
- walkSidecars(join6(sourceArchiveDir, "projects"), false, out);
3950
+ walkSidecars(join7(sourceArchiveDir, "projects"), false, out);
3863
3951
  try {
3864
- for (const ts of readdirSync3(join6(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3952
+ for (const ts of readdirSync3(join7(sourceArchiveDir, ".versions"), { withFileTypes: true }))
3865
3953
  if (ts.isDirectory())
3866
- walkSidecars(join6(sourceArchiveDir, ts.name, "projects"), false, out);
3954
+ walkSidecars(join7(sourceArchiveDir, ts.name, "projects"), false, out);
3867
3955
  } catch {
3868
3956
  }
3869
3957
  return out;
@@ -3981,7 +4069,7 @@ function ingestWorkflowResults(db, sourceArchiveDir, sourceId, excludedDirs, now
3981
4069
 
3982
4070
  // ../ingest/dist/meta.js
3983
4071
  import { readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
3984
- import { basename as basename4, join as join7 } from "path";
4072
+ import { basename as basename4, join as join8 } from "path";
3985
4073
  function newMetaStats() {
3986
4074
  return { files: 0, upserted: 0, skipped: 0, malformed: 0 };
3987
4075
  }
@@ -3993,7 +4081,7 @@ function walkMeta(dir, inSubagents, out) {
3993
4081
  return;
3994
4082
  }
3995
4083
  for (const e of entries) {
3996
- const p = join7(dir, e.name);
4084
+ const p = join8(dir, e.name);
3997
4085
  if (e.isDirectory())
3998
4086
  walkMeta(p, inSubagents || e.name === "subagents", out);
3999
4087
  else if (e.isFile() && inSubagents && /^agent-.+\.meta\.json$/.test(e.name))
@@ -4002,11 +4090,11 @@ function walkMeta(dir, inSubagents, out) {
4002
4090
  }
4003
4091
  function discoverMeta(sourceArchiveDir) {
4004
4092
  const out = [];
4005
- walkMeta(join7(sourceArchiveDir, "projects"), false, out);
4093
+ walkMeta(join8(sourceArchiveDir, "projects"), false, out);
4006
4094
  try {
4007
- for (const ts of readdirSync4(join7(sourceArchiveDir, ".versions"), { withFileTypes: true }))
4095
+ for (const ts of readdirSync4(join8(sourceArchiveDir, ".versions"), { withFileTypes: true }))
4008
4096
  if (ts.isDirectory())
4009
- walkMeta(join7(sourceArchiveDir, ts.name, "projects"), false, out);
4097
+ walkMeta(join8(sourceArchiveDir, ts.name, "projects"), false, out);
4010
4098
  } catch {
4011
4099
  }
4012
4100
  return out;
@@ -4080,7 +4168,7 @@ function ingestSubagentMeta(db, sourceArchiveDir, sourceId, excludedDirs, now2,
4080
4168
 
4081
4169
  // ../ingest/dist/toolResults.js
4082
4170
  import { readdirSync as readdirSync5, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
4083
- import { basename as basename5, join as join8 } from "path";
4171
+ import { basename as basename5, join as join9 } from "path";
4084
4172
  function newToolResultStats() {
4085
4173
  return { files: 0, upserted: 0, skipped: 0, malformed: 0 };
4086
4174
  }
@@ -4092,7 +4180,7 @@ function walkResults(dir, inResults, out) {
4092
4180
  return;
4093
4181
  }
4094
4182
  for (const e of entries) {
4095
- const p = join8(dir, e.name);
4183
+ const p = join9(dir, e.name);
4096
4184
  if (e.isDirectory())
4097
4185
  walkResults(p, inResults || e.name === "tool-results", out);
4098
4186
  else if (e.isFile() && inResults && e.name.endsWith(".txt"))
@@ -4101,11 +4189,11 @@ function walkResults(dir, inResults, out) {
4101
4189
  }
4102
4190
  function discoverResults(sourceArchiveDir) {
4103
4191
  const out = [];
4104
- walkResults(join8(sourceArchiveDir, "projects"), false, out);
4192
+ walkResults(join9(sourceArchiveDir, "projects"), false, out);
4105
4193
  try {
4106
- for (const ts of readdirSync5(join8(sourceArchiveDir, ".versions"), { withFileTypes: true }))
4194
+ for (const ts of readdirSync5(join9(sourceArchiveDir, ".versions"), { withFileTypes: true }))
4107
4195
  if (ts.isDirectory())
4108
- walkResults(join8(sourceArchiveDir, ts.name, "projects"), false, out);
4196
+ walkResults(join9(sourceArchiveDir, ts.name, "projects"), false, out);
4109
4197
  } catch {
4110
4198
  }
4111
4199
  return out;
@@ -4183,11 +4271,11 @@ function runIngest(argv = process.argv.slice(2)) {
4183
4271
  const args = parseIngestArgs(argv);
4184
4272
  const archiveRoot = args.archive || resolveArchiveDir();
4185
4273
  const { path: dbPath } = resolveDbPath(args.db);
4186
- if (!existsSync4(archiveRoot)) {
4274
+ if (!existsSync5(archiveRoot)) {
4187
4275
  console.error(`agent-lens-ingest: archive not found: ${archiveRoot} (run 'agent-lens collect' first)`);
4188
4276
  process.exit(1);
4189
4277
  }
4190
- mkdirSync4(dirname4(dbPath), { recursive: true });
4278
+ mkdirSync4(dirname5(dbPath), { recursive: true });
4191
4279
  const db = args.full ? openRaw(dbPath) : openDb(dbPath);
4192
4280
  if (args.full) {
4193
4281
  resetSchema(db);
@@ -4220,7 +4308,7 @@ function runIngest(argv = process.argv.slice(2)) {
4220
4308
  }
4221
4309
  stmts.insAgent.run(adapter.agentId, adapter.agentName);
4222
4310
  stmts.insSource.run({ id: source.label, label: source.label, agent_id: adapter.agentId, config_dir: source.configDir });
4223
- let files = adapter.discover(join9(archiveRoot, source.label), source.label);
4311
+ let files = adapter.discover(join10(archiveRoot, source.label), source.label);
4224
4312
  if (excludedDirs.length)
4225
4313
  files = files.filter((f) => !isExcludedArchivePath(f.path, excludedDirs));
4226
4314
  files.sort((a, b) => Number(a.isVersion) - Number(b.isVersion));
@@ -4252,11 +4340,12 @@ function runIngest(argv = process.argv.slice(2)) {
4252
4340
  ingestFile(db, stmts, adapter, file, lines, { size: st.size, mtimeMs, hash: hash2 }, now2, stats);
4253
4341
  dirty.add(file.sessionId);
4254
4342
  }
4255
- ingestWorkflowResults(db, join9(archiveRoot, source.label), source.label, excludedDirs, now2, wfStats, args.full);
4256
- ingestSubagentMeta(db, join9(archiveRoot, source.label), source.label, excludedDirs, now2, metaStats, args.full);
4257
- ingestToolResults(db, join9(archiveRoot, source.label), excludedDirs, now2, trStats, args.full);
4343
+ ingestWorkflowResults(db, join10(archiveRoot, source.label), source.label, excludedDirs, now2, wfStats, args.full);
4344
+ ingestSubagentMeta(db, join10(archiveRoot, source.label), source.label, excludedDirs, now2, metaStats, args.full);
4345
+ ingestToolResults(db, join10(archiveRoot, source.label), excludedDirs, now2, trStats, args.full);
4258
4346
  }
4259
4347
  const expanded = rebuildDerived(db, args.full ? null : dirty);
4348
+ canonicalizeProjects(db);
4260
4349
  const classified = classify(db, args.full ? null : expanded);
4261
4350
  const detected = detect(db, args.full ? null : expanded);
4262
4351
  classifyErrors(db, args.full ? null : expanded);
@@ -4295,11 +4384,12 @@ function runMetrics(argv = process.argv.slice(2)) {
4295
4384
  if (argv[i] === "--db")
4296
4385
  dbArg = argv[++i] ?? "";
4297
4386
  const { path: dbPath } = resolveDbPath(dbArg);
4298
- if (!existsSync4(dbPath)) {
4387
+ if (!existsSync5(dbPath)) {
4299
4388
  console.error(`agent-lens-metrics: db not found: ${dbPath} (run ingest first)`);
4300
4389
  process.exit(1);
4301
4390
  }
4302
4391
  const db = openDb(dbPath);
4392
+ canonicalizeProjects(db);
4303
4393
  const r = classify(db);
4304
4394
  const d = detect(db);
4305
4395
  const e = classifyErrors(db);
@@ -4309,8 +4399,8 @@ function runMetrics(argv = process.argv.slice(2)) {
4309
4399
  }
4310
4400
 
4311
4401
  // ../server/dist/run.js
4312
- import { existsSync as existsSync7 } from "fs";
4313
- import { dirname as dirname5 } from "path";
4402
+ import { existsSync as existsSync8 } from "fs";
4403
+ import { dirname as dirname6 } from "path";
4314
4404
  import { fileURLToPath as fileURLToPath2 } from "url";
4315
4405
 
4316
4406
  // ../server/dist/db.js
@@ -4816,13 +4906,13 @@ function listFiles(db, f = {}) {
4816
4906
  ORDER BY ${col} ${dir}, g.file_path ASC LIMIT ? OFFSET ?`).all(...params, limit, offset);
4817
4907
  return { total, files };
4818
4908
  }
4819
- function getFileTimeline(db, filePath, projectId) {
4909
+ function getFileTimeline(db, filePath, projectId2) {
4820
4910
  if (!tableExists(db, "file_changes"))
4821
4911
  return null;
4822
4912
  const params = [filePath];
4823
4913
  let scope = "fc.file_path = ?";
4824
- if (projectId)
4825
- scope += " AND fc.project_id = ?", params.push(projectId);
4914
+ if (projectId2)
4915
+ scope += " AND fc.project_id = ?", params.push(projectId2);
4826
4916
  const rows = db.prepare(`SELECT fc.id, fc.session_id, fc.turn_id, fc.event_uuid, fc.tool_name, fc.lines_added,
4827
4917
  fc.lines_removed, fc.timestamp, fc.project_id,
4828
4918
  s.ai_title, s.slug, s.source_id, s.started_at, t.seq AS turn_seq, t.prompt_preview,
@@ -4996,7 +5086,7 @@ function securitySummary(db) {
4996
5086
  }
4997
5087
 
4998
5088
  // ../server/dist/app.js
4999
- import { existsSync as existsSync6 } from "fs";
5089
+ import { existsSync as existsSync7 } from "fs";
5000
5090
  import Fastify from "fastify";
5001
5091
  import fastifyStatic from "@fastify/static";
5002
5092
 
@@ -5272,8 +5362,8 @@ function dashboardBreakdowns(db, f) {
5272
5362
  }
5273
5363
 
5274
5364
  // ../server/dist/refresh.js
5275
- import { existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
5276
- import { join as join10 } from "path";
5365
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5 } from "fs";
5366
+ import { join as join11 } from "path";
5277
5367
  function originAllowed(origin) {
5278
5368
  if (!origin)
5279
5369
  return true;
@@ -5294,13 +5384,13 @@ function writeBlocked(headers) {
5294
5384
  }
5295
5385
  function runRefresh() {
5296
5386
  const dataDir = resolveDataDir(findRepoRoot());
5297
- const lock = acquireLock(join10(dataDir, ".agent-lens.lock"));
5387
+ const lock = acquireLock(join11(dataDir, ".agent-lens.lock"));
5298
5388
  if (!lock)
5299
5389
  return null;
5300
5390
  try {
5301
5391
  const collected = collectAll();
5302
5392
  const archiveRoot = resolveArchiveDir();
5303
- if (!existsSync5(archiveRoot))
5393
+ if (!existsSync6(archiveRoot))
5304
5394
  mkdirSync5(archiveRoot, { recursive: true });
5305
5395
  runIngest([]);
5306
5396
  return { ok: true, collected };
@@ -5625,7 +5715,7 @@ async function createApp(db, opts = {}) {
5625
5715
  return reply.code(404).send({ error: "not found" });
5626
5716
  reply.header("content-type", "text/markdown; charset=utf-8").header("content-disposition", `attachment; filename="${out.filename}"`).send(out.markdown);
5627
5717
  });
5628
- if (opts.webDist && existsSync6(opts.webDist)) {
5718
+ if (opts.webDist && existsSync7(opts.webDist)) {
5629
5719
  await app.register(fastifyStatic, { root: opts.webDist });
5630
5720
  app.setNotFoundHandler((req, reply) => {
5631
5721
  if (req.url.startsWith("/api/"))
@@ -5638,7 +5728,7 @@ async function createApp(db, opts = {}) {
5638
5728
 
5639
5729
  // ../server/dist/run.js
5640
5730
  async function startServer(overrides = {}) {
5641
- const here = dirname5(fileURLToPath2(import.meta.url));
5731
+ const here = dirname6(fileURLToPath2(import.meta.url));
5642
5732
  const repoRoot = findRepoRoot(here);
5643
5733
  const { path: dbPath } = resolveDbPath(overrides.db);
5644
5734
  const { host, port } = resolveServerConfig({ port: overrides.port, host: overrides.host });
@@ -5647,7 +5737,7 @@ async function startServer(overrides = {}) {
5647
5737
  console.error(`agent-lens-server: refusing to bind non-loopback host '${host}' (privacy). Set AGENT_LENS_ALLOW_NONLOCAL=1 to override.`);
5648
5738
  process.exit(1);
5649
5739
  }
5650
- if (!existsSync7(dbPath)) {
5740
+ if (!existsSync8(dbPath)) {
5651
5741
  console.error(`agent-lens-server: db not found: ${dbPath} (run ingest first)`);
5652
5742
  process.exit(1);
5653
5743
  }
@@ -5658,7 +5748,7 @@ async function startServer(overrides = {}) {
5658
5748
  try {
5659
5749
  await app.listen({ host, port });
5660
5750
  console.log(`agent-lens-server: http://${host}:${port} (db: ${dbPath})`);
5661
- if (!existsSync7(webDist))
5751
+ if (!existsSync8(webDist))
5662
5752
  console.log(" note: web SPA not built \u2014 run `pnpm --filter @agent-lens/web build`");
5663
5753
  } catch (err) {
5664
5754
  console.error(err);
@@ -5667,12 +5757,12 @@ async function startServer(overrides = {}) {
5667
5757
  }
5668
5758
 
5669
5759
  // src/watch.ts
5670
- import { existsSync as existsSync8 } from "fs";
5671
- import { join as join11 } from "path";
5760
+ import { existsSync as existsSync9 } from "fs";
5761
+ import { join as join12 } from "path";
5672
5762
  import chokidar from "chokidar";
5673
5763
  async function runWatch(opts = {}) {
5674
5764
  const sources = loadSources();
5675
- const lockPath = join11(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5765
+ const lockPath = join12(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5676
5766
  let running = false;
5677
5767
  let pending = false;
5678
5768
  const cycle = () => {
@@ -5705,7 +5795,7 @@ async function runWatch(opts = {}) {
5705
5795
  }
5706
5796
  };
5707
5797
  cycle();
5708
- const watchPaths = sources.map((s) => join11(s.configDir, "projects")).filter((p) => existsSync8(p));
5798
+ const watchPaths = sources.map((s) => join12(s.configDir, "projects")).filter((p) => existsSync9(p));
5709
5799
  if (watchPaths.length === 0) {
5710
5800
  console.error("agent-lens watch: no source 'projects' directories found \u2014 nothing to watch");
5711
5801
  }
@@ -5733,14 +5823,14 @@ async function runWatch(opts = {}) {
5733
5823
  // src/index.ts
5734
5824
  function version() {
5735
5825
  try {
5736
- const pkg = JSON.parse(readFileSync8(join12(dirname6(fileURLToPath3(import.meta.url)), "../package.json"), "utf8"));
5826
+ const pkg = JSON.parse(readFileSync8(join13(dirname7(fileURLToPath3(import.meta.url)), "../package.json"), "utf8"));
5737
5827
  return pkg.version ?? "0.0.0";
5738
5828
  } catch {
5739
5829
  return "0.0.0";
5740
5830
  }
5741
5831
  }
5742
5832
  function withLock(fn) {
5743
- const lockPath = join12(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5833
+ const lockPath = join13(resolveDataDir(findRepoRoot()), ".agent-lens.lock");
5744
5834
  const lock = acquireLock(lockPath);
5745
5835
  if (!lock) {
5746
5836
  console.error("agent-lens: another collect/ingest run is in progress \u2014 skipping");
@@ -5785,7 +5875,7 @@ cli.command("metrics", "Re-run classification over an already-ingested DB").opti
5785
5875
  });
5786
5876
  cli.command("export <sessionId>", "Write a session to a shareable Markdown file (redacted by default)").option("--out <file>", "Write to this file (default: stdout)").option("--level <level>", "Redaction level: secrets (default) | structure").option("--no-redact", "Verbatim, UNREDACTED output (opt-out)").option("--db <path>", "SQLite DB path (overrides AGENT_LENS_DB / config db)").action((sessionId, opts) => {
5787
5877
  const { path: dbPath } = resolveDbPath(opts.db);
5788
- if (!existsSync9(dbPath)) {
5878
+ if (!existsSync10(dbPath)) {
5789
5879
  console.error(`agent-lens: db not found: ${dbPath} (run ingest first)`);
5790
5880
  process.exit(1);
5791
5881
  }