@merittdev/horus 0.8.16 → 0.8.18

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.
Files changed (3) hide show
  1. package/README.md +11 -9
  2. package/dist/index.cjs +1100 -688
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -43846,7 +43846,7 @@ var require_pg_pool = __commonJS({
43846
43846
  function throwOnDoubleRelease() {
43847
43847
  throw new Error("Release called on client which has already been released to the pool.");
43848
43848
  }
43849
- function promisify6(Promise2, callback) {
43849
+ function promisify8(Promise2, callback) {
43850
43850
  if (callback) {
43851
43851
  return { callback, result: void 0 };
43852
43852
  }
@@ -43984,7 +43984,7 @@ var require_pg_pool = __commonJS({
43984
43984
  const err2 = new Error("Cannot use a pool after calling end on the pool");
43985
43985
  return cb ? cb(err2) : this.Promise.reject(err2);
43986
43986
  }
43987
- const response = promisify6(this.Promise, cb);
43987
+ const response = promisify8(this.Promise, cb);
43988
43988
  const result = response.result;
43989
43989
  if (this._isFull() || this._idle.length) {
43990
43990
  if (this._idle.length) {
@@ -44169,7 +44169,7 @@ var require_pg_pool = __commonJS({
44169
44169
  }
44170
44170
  query(text2, values2, cb) {
44171
44171
  if (typeof text2 === "function") {
44172
- const response2 = promisify6(this.Promise, text2);
44172
+ const response2 = promisify8(this.Promise, text2);
44173
44173
  setImmediate(function() {
44174
44174
  return response2.callback(new Error("Passing a function as the first parameter to pool.query is not supported"));
44175
44175
  });
@@ -44179,7 +44179,7 @@ var require_pg_pool = __commonJS({
44179
44179
  cb = values2;
44180
44180
  values2 = void 0;
44181
44181
  }
44182
- const response = promisify6(this.Promise, cb);
44182
+ const response = promisify8(this.Promise, cb);
44183
44183
  cb = response.callback;
44184
44184
  this.connect((err2, client) => {
44185
44185
  if (err2) {
@@ -44224,7 +44224,7 @@ var require_pg_pool = __commonJS({
44224
44224
  return cb ? cb(err2) : this.Promise.reject(err2);
44225
44225
  }
44226
44226
  this.ending = true;
44227
- const promised = promisify6(this.Promise, cb);
44227
+ const promised = promisify8(this.Promise, cb);
44228
44228
  this._endCallback = promised.callback;
44229
44229
  this._pulseQueue();
44230
44230
  return promised.result;
@@ -65880,8 +65880,8 @@ init_cjs_shims();
65880
65880
 
65881
65881
  // ../../packages/core/src/version.ts
65882
65882
  init_cjs_shims();
65883
- var HORUS_VERSION = true ? "0.8.16" : "dev";
65884
- var PINNED_SOURCE_VERSION = "1.5.3";
65883
+ var HORUS_VERSION = true ? "0.8.18" : "dev";
65884
+ var PINNED_SOURCE_VERSION = "1.5.4";
65885
65885
 
65886
65886
  // ../../packages/core/src/evidence.ts
65887
65887
  init_cjs_shims();
@@ -71105,6 +71105,34 @@ function removeSpawnedHostRecord(root) {
71105
71105
  } catch {
71106
71106
  }
71107
71107
  }
71108
+ async function killSpawnedHost(root) {
71109
+ const pids = /* @__PURE__ */ new Set();
71110
+ const spawned = readSpawnedHost(root);
71111
+ if (spawned?.pid) pids.add(spawned.pid);
71112
+ const backend = readSourceHostPid(root);
71113
+ if (backend?.pid) pids.add(backend.pid);
71114
+ for (const pid of pids) {
71115
+ for (const signal of ["SIGTERM", "SIGKILL"]) {
71116
+ try {
71117
+ process.kill(pid, signal);
71118
+ } catch {
71119
+ break;
71120
+ }
71121
+ let dead = false;
71122
+ for (let i3 = 0; i3 < 15; i3 += 1) {
71123
+ await new Promise((r) => setTimeout(r, 200));
71124
+ try {
71125
+ process.kill(pid, 0);
71126
+ } catch {
71127
+ dead = true;
71128
+ break;
71129
+ }
71130
+ }
71131
+ if (dead) break;
71132
+ }
71133
+ }
71134
+ removeSpawnedHostRecord(root);
71135
+ }
71108
71136
  function reconcileSpawnedHostPid(root, port) {
71109
71137
  try {
71110
71138
  const backend = readSourceHostPid(root);
@@ -75560,7 +75588,7 @@ function sql(strings, ...params) {
75560
75588
  return new SQL([new StringChunk(str3)]);
75561
75589
  }
75562
75590
  sql2.raw = raw;
75563
- function join14(chunks, separator) {
75591
+ function join15(chunks, separator) {
75564
75592
  const result = [];
75565
75593
  for (const [i3, chunk] of chunks.entries()) {
75566
75594
  if (i3 > 0 && separator !== void 0) {
@@ -75570,7 +75598,7 @@ function sql(strings, ...params) {
75570
75598
  }
75571
75599
  return new SQL(result);
75572
75600
  }
75573
- sql2.join = join14;
75601
+ sql2.join = join15;
75574
75602
  function identifier(value) {
75575
75603
  return new Name(value);
75576
75604
  }
@@ -79161,7 +79189,7 @@ var PgSelectQueryBuilderBase = class extends TypedQueryBuilder {
79161
79189
  return (table, on2) => {
79162
79190
  const baseTableName = this.tableName;
79163
79191
  const tableName = getTableLikeName(table);
79164
- if (typeof tableName === "string" && this.config.joins?.some((join14) => join14.alias === tableName)) {
79192
+ if (typeof tableName === "string" && this.config.joins?.some((join15) => join15.alias === tableName)) {
79165
79193
  throw new Error(`Alias "${tableName}" is already used in this query`);
79166
79194
  }
79167
79195
  if (!this.isPartialSelect) {
@@ -80173,7 +80201,7 @@ var PgUpdateBase = class extends QueryPromise {
80173
80201
  createJoin(joinType) {
80174
80202
  return (table, on2) => {
80175
80203
  const tableName = getTableLikeName(table);
80176
- if (typeof tableName === "string" && this.config.joins.some((join14) => join14.alias === tableName)) {
80204
+ if (typeof tableName === "string" && this.config.joins.some((join15) => join15.alias === tableName)) {
80177
80205
  throw new Error(`Alias "${tableName}" is already used in this query`);
80178
80206
  }
80179
80207
  if (typeof on2 === "function") {
@@ -80269,10 +80297,10 @@ var PgUpdateBase = class extends QueryPromise {
80269
80297
  const fromFields = this.getTableLikeFields(this.config.from);
80270
80298
  fields[tableName] = fromFields;
80271
80299
  }
80272
- for (const join14 of this.config.joins) {
80273
- const tableName2 = getTableLikeName(join14.table);
80274
- if (typeof tableName2 === "string" && !is(join14.table, SQL)) {
80275
- const fromFields = this.getTableLikeFields(join14.table);
80300
+ for (const join15 of this.config.joins) {
80301
+ const tableName2 = getTableLikeName(join15.table);
80302
+ if (typeof tableName2 === "string" && !is(join15.table, SQL)) {
80303
+ const fromFields = this.getTableLikeFields(join15.table);
80276
80304
  fields[tableName2] = fromFields;
80277
80305
  }
80278
80306
  }
@@ -90401,6 +90429,10 @@ function reportToMarkdown(r) {
90401
90429
 
90402
90430
  // ../../packages/engine/src/ownership.ts
90403
90431
  init_cjs_shims();
90432
+ var BOT_AUTHOR_RE = /\[bot\]|^(?:github-actions|dependabot|renovate(?:-bot)?|greenkeeper|snyk-bot|mergify|codecov|semantic-release-bot|allcontributors)\b/i;
90433
+ function isBotAuthor(author) {
90434
+ return BOT_AUTHOR_RE.test((author ?? "").trim());
90435
+ }
90404
90436
  var CODE_EXTENSIONS = /* @__PURE__ */ new Set([
90405
90437
  "ts",
90406
90438
  "tsx",
@@ -90507,10 +90539,12 @@ async function estimateOwnership(query, deps) {
90507
90539
  };
90508
90540
  }
90509
90541
  const contributors = await gitFileContributors(deps.repoPath, file);
90510
- const total = contributors.reduce((n4, c4) => n4 + c4.commits, 0);
90511
- const lead = contributors[0] ?? null;
90542
+ const humans = contributors.filter((c4) => !isBotAuthor(c4.author));
90543
+ const ranked = humans.length > 0 ? humans : contributors;
90544
+ const total = ranked.reduce((n4, c4) => n4 + c4.commits, 0);
90545
+ const lead = ranked[0] ?? null;
90512
90546
  const maintainerShare = total > 0 && lead !== null ? lead.commits / total : 0;
90513
- const byRecent = [...contributors].sort(
90547
+ const byRecent = [...ranked].sort(
90514
90548
  (a2, b5) => b5.lastDate < a2.lastDate ? -1 : b5.lastDate > a2.lastDate ? 1 : 0
90515
90549
  );
90516
90550
  const mostActiveRecent = byRecent[0]?.author ?? null;
@@ -91250,6 +91284,30 @@ function factorRequestContext(sourceEvidenceIds, graph, request) {
91250
91284
  reason: `Cause directly implicates the investigated service (${request.service})`
91251
91285
  };
91252
91286
  }
91287
+ function factorIncidentHistory(sourceEvidenceIds, graph, priorIncidents) {
91288
+ if (priorIncidents.size === 0) return null;
91289
+ const idSet = new Set(sourceEvidenceIds);
91290
+ const codeNodes = graph.nodes.filter(
91291
+ (n4) => (n4.type === "symbol" || n4.type === "file") && n4.evidenceIds.some((eid) => idSet.has(eid))
91292
+ );
91293
+ const matched = [];
91294
+ let totalPrior = 0;
91295
+ for (const n4 of codeNodes) {
91296
+ const count = priorIncidents.get(n4.id);
91297
+ if (count && count > 0) {
91298
+ matched.push(n4);
91299
+ totalPrior += count;
91300
+ }
91301
+ }
91302
+ if (matched.length === 0) return null;
91303
+ const delta = +Math.min(matched.length * 0.04, 0.1).toFixed(3);
91304
+ const names = matched.slice(0, 3).map((n4) => n4.label);
91305
+ return {
91306
+ factor: "incident-history",
91307
+ delta,
91308
+ reason: `Implicated code has a prior-incident history (${totalPrior} prior investigation(s) on ${names.join(", ")}${matched.length > 3 ? ", \u2026" : ""})`
91309
+ };
91310
+ }
91253
91311
  function scoreCause(input, ctx) {
91254
91312
  const now = ctx.now ?? (/* @__PURE__ */ new Date()).toISOString();
91255
91313
  const idSet = new Set(input.sourceEvidenceIds);
@@ -91263,7 +91321,8 @@ function scoreCause(input, ctx) {
91263
91321
  factorSignalStrength(attached),
91264
91322
  factorFindingUncertainty(input.sourceEvidenceIds, ctx.findings ?? []),
91265
91323
  ctx.providerReliability ? factorProviderReliability(attached, ctx.providerReliability) : null,
91266
- ctx.request ? factorRequestContext(input.sourceEvidenceIds, ctx.graph, ctx.request) : null
91324
+ ctx.request ? factorRequestContext(input.sourceEvidenceIds, ctx.graph, ctx.request) : null,
91325
+ ctx.priorIncidents ? factorIncidentHistory(input.sourceEvidenceIds, ctx.graph, ctx.priorIncidents) : null
91267
91326
  ];
91268
91327
  const explanations = rawFactors.filter((f5) => f5 !== null);
91269
91328
  const totalDelta = explanations.reduce((sum, e) => sum + e.delta, 0);
@@ -92002,6 +92061,14 @@ function scoreSeed(s5, index2, hintTokens, changedFiles, hintHasCode) {
92002
92061
  if (changedFiles !== void 0 && changedFiles.has(s5.filePath)) score += 5;
92003
92062
  if (/\.(resolver|controller|service)\.[jt]sx?$/i.test(s5.filePath)) score += hintHasCode ? 0 : 2;
92004
92063
  if (/(^|\/)scripts?\//i.test(s5.filePath)) score -= 2;
92064
+ if (/(^|\/)(tests?|__tests__|spec)\//i.test(s5.filePath) || /(\.|_)(test|spec)\.[jt]sx?$/i.test(s5.filePath) || /(^|\/)test_[^/]*\.py$/i.test(s5.filePath) || /_test\.py$/i.test(s5.filePath)) {
92065
+ score -= 4;
92066
+ }
92067
+ if (/(^|\/)(examples?|samples?|demos?|fixtures?|sandbox|playground|docs|docs_src|tutorials?)(\/|$)/i.test(
92068
+ s5.filePath
92069
+ )) {
92070
+ score -= 4;
92071
+ }
92005
92072
  score += Math.max(0, 5 - index2) * (hintHasCode ? 0.8 : 0.1);
92006
92073
  if (s5.score !== void 0) score += s5.score * (hintHasCode ? 8 : 1.5);
92007
92074
  if (/\.[jt]sx?$/i.test(s5.name)) score -= 5;
@@ -92678,12 +92745,12 @@ async function investigate(input, deps) {
92678
92745
  const fp = (s5.filePath ?? "").replace(/^\.?\/+/, "");
92679
92746
  return fp === scopePrefix || fp.startsWith(scopePrefix + "/");
92680
92747
  };
92681
- const seedSearchLimit = scopePrefix ? 25 : 5;
92682
- const rawSeeds = code ? (await code.searchSymbols(hint, seedSearchLimit)).filter(inScope).slice(0, 5) : [];
92748
+ const seedSearchLimit = scopePrefix ? 30 : 20;
92749
+ const rawSeeds = code ? (await code.searchSymbols(hint, seedSearchLimit)).filter(inScope) : [];
92683
92750
  const hintTokens = [...new Set(tokenize(hint))];
92684
92751
  const hintHasCode = /\b(?=[A-Z0-9_]*[0-9_])[A-Z][A-Z0-9_]{3,}\b/.test(hint);
92685
92752
  const ranked = rankSeeds(rawSeeds, hintTokens, changedFilePaths, hintHasCode);
92686
- let seeds = ranked.map((r) => r.symbol);
92753
+ let seeds = ranked.map((r) => r.symbol).slice(0, 5);
92687
92754
  let top = seeds[0];
92688
92755
  if (top && code && isTypeLikeName(top.name)) {
92689
92756
  const base2 = executableBaseName(top.name);
@@ -93478,11 +93545,12 @@ async function investigate(input, deps) {
93478
93545
  const causeChains = buildCauseChains(validated, evidence2, graph, label);
93479
93546
  const findings2 = [];
93480
93547
  if (top && seedEv) {
93548
+ const seedConfidence = seedIsLowConfidence ? Math.min(0.4, top.score ?? 0.3) : Math.min(0.95, Math.max(0.5, top.score ?? 0.75));
93481
93549
  findings2.push({
93482
93550
  kind: "observation",
93483
93551
  title: `Seed resolves to ${label} at ${seedLoc}`,
93484
93552
  detail: top.signature ?? void 0,
93485
- confidence: 1,
93553
+ confidence: +seedConfidence.toFixed(2),
93486
93554
  evidenceIds: [seedEv.id]
93487
93555
  });
93488
93556
  }
@@ -93864,12 +93932,31 @@ async function investigate(input, deps) {
93864
93932
  ...deps.queue != null ? { queue: 0.9 } : {},
93865
93933
  ...deps.metrics != null ? { metrics: 0.75 } : {}
93866
93934
  };
93935
+ const priorIncidents = /* @__PURE__ */ new Map();
93936
+ try {
93937
+ const priors = await listInvestigationsWithReports(deps.db, 50);
93938
+ for (const row of priors) {
93939
+ const rep = row.report;
93940
+ if (!rep || !Array.isArray(rep.evidence) || rep.evidence.length === 0) continue;
93941
+ if (typeof rep.confidence === "number" && rep.confidence < 0.5) continue;
93942
+ const priorGraph = buildGraph(rep.evidence);
93943
+ const seen = /* @__PURE__ */ new Set();
93944
+ for (const n4 of priorGraph.nodes) {
93945
+ if ((n4.type === "symbol" || n4.type === "file") && !seen.has(n4.id)) {
93946
+ seen.add(n4.id);
93947
+ priorIncidents.set(n4.id, (priorIncidents.get(n4.id) ?? 0) + 1);
93948
+ }
93949
+ }
93950
+ }
93951
+ } catch {
93952
+ }
93867
93953
  const rankedCauses = rankCauses(causeInputs, {
93868
93954
  evidence: evidence2,
93869
93955
  graph,
93870
93956
  findings: findings2,
93871
93957
  providerReliability,
93872
- request: { hint: input.hint, service: input.service }
93958
+ request: { hint: input.hint, service: input.service },
93959
+ ...priorIncidents.size > 0 ? { priorIncidents } : {}
93873
93960
  });
93874
93961
  const evidenceConfidence = computeWeightedEvidenceConfidence(
93875
93962
  evidence2,
@@ -94441,6 +94528,32 @@ var EXTERNAL_MARKERS = [
94441
94528
  "mysql2",
94442
94529
  "mssql"
94443
94530
  ];
94531
+ var TESTY_TOKENS = /* @__PURE__ */ new Set([
94532
+ "test",
94533
+ "tests",
94534
+ "spec",
94535
+ "specs",
94536
+ "example",
94537
+ "examples",
94538
+ "sample",
94539
+ "samples",
94540
+ "demo",
94541
+ "demos",
94542
+ "docs",
94543
+ "doc",
94544
+ "tutorial",
94545
+ "tutorials",
94546
+ "fixture",
94547
+ "fixtures",
94548
+ "e2e"
94549
+ ]);
94550
+ function isTestyCommunity(name2) {
94551
+ return name2.toLowerCase().split(/[+\s/_.-]+/).some((t2) => TESTY_TOKENS.has(t2));
94552
+ }
94553
+ var TEST_EXAMPLE_PATH_RE = /(^|\/)(tests?|__tests__|examples?|samples?|demos?|docs|docs_src|tutorials?|fixtures?|spec|specs)(\/|$)/i;
94554
+ function isTestOrExamplePath(p5) {
94555
+ return TEST_EXAMPLE_PATH_RE.test(p5);
94556
+ }
94444
94557
  async function discoverArchitecture(deps) {
94445
94558
  const nodeStats = await (async () => {
94446
94559
  try {
@@ -94460,10 +94573,17 @@ async function discoverArchitecture(deps) {
94460
94573
  const result = await deps.code.cypher(
94461
94574
  'MATCH (m)-[r:CodeRelation]->(c:Community) WHERE r.rel_type = "member_of" RETURN c.name, count(m) ORDER BY count(m) DESC'
94462
94575
  );
94463
- return result.rows.slice(0, 12).map((row) => ({
94576
+ const rows = result.rows.map((row) => ({
94464
94577
  name: String(row[0] ?? ""),
94465
94578
  members: Number(row[1] ?? 0)
94466
94579
  }));
94580
+ rows.sort((a2, b5) => {
94581
+ const at4 = isTestyCommunity(a2.name) ? 1 : 0;
94582
+ const bt2 = isTestyCommunity(b5.name) ? 1 : 0;
94583
+ if (at4 !== bt2) return at4 - bt2;
94584
+ return b5.members - a2.members;
94585
+ });
94586
+ return rows.slice(0, 12);
94467
94587
  } catch {
94468
94588
  return [];
94469
94589
  }
@@ -94475,20 +94595,22 @@ async function discoverArchitecture(deps) {
94475
94595
  for (const edge of edges) {
94476
94596
  const key = edge.queueName;
94477
94597
  if (!byQueue.has(key)) {
94478
- byQueue.set(key, { producers: /* @__PURE__ */ new Set(), workers: /* @__PURE__ */ new Set() });
94598
+ byQueue.set(key, { producers: /* @__PURE__ */ new Map(), workers: /* @__PURE__ */ new Map() });
94479
94599
  }
94480
94600
  const entry2 = byQueue.get(key);
94481
94601
  if (edge.producerSymbol != null && edge.producerSymbol !== "") {
94482
- entry2.producers.add(edge.producerSymbol);
94602
+ const file = edge.producerFile ?? null;
94603
+ entry2.producers.set(`${edge.producerSymbol}\0${file ?? ""}`, { symbol: edge.producerSymbol, file });
94483
94604
  }
94484
94605
  if (edge.workerSymbol != null && edge.workerSymbol !== "") {
94485
- entry2.workers.add(edge.workerSymbol);
94606
+ const file = edge.workerFile ?? null;
94607
+ entry2.workers.set(`${edge.workerSymbol}\0${file ?? ""}`, { symbol: edge.workerSymbol, file });
94486
94608
  }
94487
94609
  }
94488
94610
  return Array.from(byQueue.entries()).map(([queueName, { producers, workers }]) => ({
94489
94611
  queueName,
94490
- producers: Array.from(producers),
94491
- workers: Array.from(workers)
94612
+ producers: Array.from(producers.values()),
94613
+ workers: Array.from(workers.values())
94492
94614
  }));
94493
94615
  } catch {
94494
94616
  return [];
@@ -94524,16 +94646,20 @@ async function discoverArchitecture(deps) {
94524
94646
  return 0;
94525
94647
  }
94526
94648
  })();
94649
+ const ownPackage = deps.project?.toLowerCase();
94527
94650
  const externalSystems = await (async () => {
94528
94651
  try {
94529
94652
  const results = await Promise.all(
94530
94653
  EXTERNAL_MARKERS.map(async (marker) => {
94654
+ if (ownPackage && marker.toLowerCase() === ownPackage) {
94655
+ return { name: marker, files: 0 };
94656
+ }
94531
94657
  try {
94532
94658
  const result = await deps.code.cypher(
94533
- `MATCH (n:File) WHERE n.content CONTAINS "${marker}" RETURN count(n)`
94659
+ `MATCH (n:File) WHERE n.content CONTAINS "${marker}" RETURN n.file_path LIMIT 500`
94534
94660
  );
94535
- const count = Number(result.rows[0]?.[0] ?? 0);
94536
- return { name: marker, files: count };
94661
+ const files = result.rows.map((r) => String(r[0] ?? "")).filter((p5) => p5 !== "" && !isTestOrExamplePath(p5)).length;
94662
+ return { name: marker, files };
94537
94663
  } catch {
94538
94664
  return { name: marker, files: 0 };
94539
94665
  }
@@ -94560,6 +94686,10 @@ async function discoverArchitecture(deps) {
94560
94686
 
94561
94687
  // ../../packages/engine/src/render-architecture.ts
94562
94688
  init_cjs_shims();
94689
+ function fmtEndpoints(endpoints2) {
94690
+ if (endpoints2.length === 0) return "(unknown)";
94691
+ return endpoints2.map((e) => e.file ? `${e.symbol} (${e.file.split("/").pop()})` : e.symbol).join(", ");
94692
+ }
94563
94693
  function renderArchitecture(m5) {
94564
94694
  const lines = [];
94565
94695
  lines.push("# Architecture (discovered)");
@@ -94580,9 +94710,7 @@ function renderArchitecture(m5) {
94580
94710
  lines.push("(none)");
94581
94711
  } else {
94582
94712
  for (const b5 of m5.asyncBoundaries) {
94583
- const producers = b5.producers.length > 0 ? b5.producers.join(", ") : "(unknown)";
94584
- const workers = b5.workers.length > 0 ? b5.workers.join(", ") : "(unknown)";
94585
- lines.push(`- ${b5.queueName}: ${producers} -> ${workers}`);
94713
+ lines.push(`- ${b5.queueName}: ${fmtEndpoints(b5.producers)} -> ${fmtEndpoints(b5.workers)}`);
94586
94714
  }
94587
94715
  }
94588
94716
  lines.push("");
@@ -95747,7 +95875,7 @@ function filterArchitecture(architecture, tokens) {
95747
95875
  ...architecture,
95748
95876
  subsystems: architecture.subsystems.filter((s5) => matchesArea(s5.name, tokens)),
95749
95877
  asyncBoundaries: architecture.asyncBoundaries.filter(
95750
- (b5) => matchesArea(b5.queueName, tokens) || b5.producers.some((p5) => matchesArea(p5, tokens)) || b5.workers.some((w4) => matchesArea(w4, tokens))
95878
+ (b5) => matchesArea(b5.queueName, tokens) || b5.producers.some((p5) => matchesArea(p5.symbol, tokens)) || b5.workers.some((w4) => matchesArea(w4.symbol, tokens))
95751
95879
  ),
95752
95880
  keyFlows: architecture.keyFlows.filter((f5) => matchesArea(f5, tokens)),
95753
95881
  externalSystems: architecture.externalSystems.filter((e) => matchesArea(e.name, tokens))
@@ -95861,9 +95989,8 @@ function renderOnboarding(g4) {
95861
95989
  if (g4.architecture.asyncBoundaries.length > 0) {
95862
95990
  lines.push("Async boundaries:");
95863
95991
  for (const b5 of g4.architecture.asyncBoundaries) {
95864
- const producers = b5.producers.length > 0 ? b5.producers.join(", ") : "(unknown)";
95865
- const workers = b5.workers.length > 0 ? b5.workers.join(", ") : "(unknown)";
95866
- lines.push(`- ${b5.queueName}: ${producers} -> ${workers}`);
95992
+ const fmt = (xs) => xs.length > 0 ? xs.map((x4) => x4.file ? `${x4.symbol} (${x4.file.split("/").pop()})` : x4.symbol).join(", ") : "(unknown)";
95993
+ lines.push(`- ${b5.queueName}: ${fmt(b5.producers)} -> ${fmt(b5.workers)}`);
95867
95994
  }
95868
95995
  } else {
95869
95996
  lines.push("No async boundaries detected.");
@@ -97209,27 +97336,27 @@ function extractQueueGraph(input) {
97209
97336
  }
97210
97337
  }
97211
97338
  const producers = [];
97212
- for (const pc49 of input.producerClasses) {
97213
- const loopVarEnums = loopVarEnumsFor(pc49.content);
97214
- for (const m5 of pc49.content.matchAll(INJECT_QUEUE_RE)) {
97339
+ for (const pc50 of input.producerClasses) {
97340
+ const loopVarEnums = loopVarEnumsFor(pc50.content);
97341
+ for (const m5 of pc50.content.matchAll(INJECT_QUEUE_RE)) {
97215
97342
  const queue = m5[1] ?? "";
97216
- if (queue) producers.push({ queue, symbol: pc49.name, file: pc49.filePath });
97343
+ if (queue) producers.push({ queue, symbol: pc50.name, file: pc50.filePath });
97217
97344
  }
97218
- for (const m5 of pc49.content.matchAll(NEW_BULL_RE)) {
97345
+ for (const m5 of pc50.content.matchAll(NEW_BULL_RE)) {
97219
97346
  if (m5[2] !== "Queue") continue;
97220
97347
  const queues2 = resolveArg(m5[3] ?? "", constMap, enums, loopVarEnums);
97221
97348
  for (const queue of queues2) {
97222
97349
  producers.push({
97223
97350
  queue,
97224
- symbol: (m5[1] ?? pc49.name) || baseName(pc49.filePath),
97225
- file: pc49.filePath
97351
+ symbol: (m5[1] ?? pc50.name) || baseName(pc50.filePath),
97352
+ file: pc50.filePath
97226
97353
  });
97227
97354
  }
97228
97355
  }
97229
- for (const m5 of pc49.content.matchAll(QUEUE_NAME_CONST_RE)) {
97356
+ for (const m5 of pc50.content.matchAll(QUEUE_NAME_CONST_RE)) {
97230
97357
  const ident = m5[1] ?? "";
97231
97358
  const queue = m5[2] ?? "";
97232
- if (queue) producers.push({ queue, symbol: ident || baseName(pc49.filePath), file: pc49.filePath });
97359
+ if (queue) producers.push({ queue, symbol: ident || baseName(pc50.filePath), file: pc50.filePath });
97233
97360
  }
97234
97361
  }
97235
97362
  const workers = [];
@@ -97296,6 +97423,60 @@ function extractQueueGraph(input) {
97296
97423
  }
97297
97424
  return { queues, producers, workers, edges };
97298
97425
  }
97426
+ var CELERY_TASK_DEF_RE = /@(?:[\w]+\.)*(?:shared_task|periodic_task|task)\b[^\n]*(?:\n[ \t]*@[^\n]*)*\n[ \t]*(?:async[ \t]+)?def[ \t]+(\w+)/g;
97427
+ var CELERY_ENQUEUE_RE = /([A-Za-z_]\w*)\s*\.\s*(?:delay|apply_async)\s*\(/g;
97428
+ function extractCeleryQueueGraph(nodes) {
97429
+ const producers = [];
97430
+ const workers = [];
97431
+ const FILE_NODE_RE = /\.(py|pyi|js|jsx|ts|tsx|mjs|cjs)$/i;
97432
+ for (const n4 of nodes) {
97433
+ for (const m5 of n4.content.matchAll(CELERY_TASK_DEF_RE)) {
97434
+ const task = m5[1];
97435
+ if (task) workers.push({ queue: task, symbol: task, file: n4.filePath });
97436
+ }
97437
+ if (FILE_NODE_RE.test(n4.name)) continue;
97438
+ for (const m5 of n4.content.matchAll(CELERY_ENQUEUE_RE)) {
97439
+ const task = m5[1];
97440
+ if (task) producers.push({ queue: task, symbol: n4.name || baseName(n4.filePath), file: n4.filePath });
97441
+ }
97442
+ }
97443
+ const taskQueues = new Set(workers.map((w4) => w4.queue));
97444
+ const queues = [...taskQueues].sort();
97445
+ const edges = [];
97446
+ for (const q2 of queues) {
97447
+ const P5 = dedupeBySymbolFile(producers.filter((p5) => p5.queue === q2));
97448
+ const W5 = dedupeBySymbolFile(workers.filter((w4) => w4.queue === q2));
97449
+ if (P5.length) {
97450
+ for (const p5 of P5) {
97451
+ for (const w4 of W5) {
97452
+ edges.push({
97453
+ queueName: q2,
97454
+ producerSymbol: p5.symbol,
97455
+ producerFile: p5.file,
97456
+ workerSymbol: w4.symbol,
97457
+ workerFile: w4.file
97458
+ });
97459
+ }
97460
+ }
97461
+ } else {
97462
+ for (const w4 of W5) {
97463
+ edges.push({
97464
+ queueName: q2,
97465
+ producerSymbol: null,
97466
+ producerFile: null,
97467
+ workerSymbol: w4.symbol,
97468
+ workerFile: w4.file
97469
+ });
97470
+ }
97471
+ }
97472
+ }
97473
+ return {
97474
+ queues,
97475
+ producers: producers.filter((p5) => taskQueues.has(p5.queue)),
97476
+ workers,
97477
+ edges
97478
+ };
97479
+ }
97299
97480
  function dedupeBySymbolFile(rows) {
97300
97481
  const seen = /* @__PURE__ */ new Set();
97301
97482
  const out2 = [];
@@ -97329,7 +97510,16 @@ async function stitch(client, db, opts = {}) {
97329
97510
  content: String(r[2] ?? "")
97330
97511
  }));
97331
97512
  const graph = extractQueueGraph({ producerClasses, workerFiles });
97332
- const edges = graph.edges.map((e) => ({
97513
+ const celeryRows = (await client.cypher(
97514
+ 'MATCH (n) WHERE n.content CONTAINS ".delay(" OR n.content CONTAINS ".apply_async(" OR n.content CONTAINS "@shared_task" OR n.content CONTAINS "@task" OR n.content CONTAINS ".task" RETURN n.name, n.file_path, n.content'
97515
+ )).rows;
97516
+ const celeryNodes = celeryRows.map((r) => ({
97517
+ name: String(r[0] ?? ""),
97518
+ filePath: String(r[1] ?? ""),
97519
+ content: String(r[2] ?? "")
97520
+ }));
97521
+ const celery = extractCeleryQueueGraph(celeryNodes);
97522
+ const edges = [...graph.edges, ...celery.edges].map((e) => ({
97333
97523
  queueName: e.queueName,
97334
97524
  producerSymbol: e.producerSymbol,
97335
97525
  producerFile: e.producerFile,
@@ -97340,9 +97530,9 @@ async function stitch(client, db, opts = {}) {
97340
97530
  }));
97341
97531
  await replaceQueueEdges(db, edges, { project: opts.project });
97342
97532
  return {
97343
- queues: graph.queues.length,
97344
- producers: graph.producers.length,
97345
- workers: graph.workers.length,
97533
+ queues: graph.queues.length + celery.queues.length,
97534
+ producers: graph.producers.length + celery.producers.length,
97535
+ workers: graph.workers.length + celery.workers.length,
97346
97536
  edges: edges.length
97347
97537
  };
97348
97538
  }
@@ -97530,6 +97720,7 @@ async function runIndex(opts) {
97530
97720
  console.log(import_picocolors3.default.dim(" already analyzed"));
97531
97721
  }
97532
97722
  const priorPort = parseHostPort(configuredHost ?? "") ?? parseHostPort(readSourceHostUrl(root) ?? "");
97723
+ await killSpawnedHost(root);
97533
97724
  const MAX_PORT_ATTEMPTS = 4;
97534
97725
  let started = false;
97535
97726
  for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS && !started; attempt += 1) {
@@ -97554,7 +97745,7 @@ async function runIndex(opts) {
97554
97745
  reconcileSpawnedHostPid(root, port);
97555
97746
  started = true;
97556
97747
  } else {
97557
- removeSpawnedHostRecord(root);
97748
+ await killSpawnedHost(root);
97558
97749
  }
97559
97750
  }
97560
97751
  if (!started) {
@@ -110077,7 +110268,7 @@ function formatAge(ms) {
110077
110268
 
110078
110269
  // ../../packages/cli/src/commands/investigate.ts
110079
110270
  init_cjs_shims();
110080
- var import_picocolors10 = __toESM(require_picocolors(), 1);
110271
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
110081
110272
 
110082
110273
  // ../../packages/cli/src/lib/cloud/investigation-sync.ts
110083
110274
  init_cjs_shims();
@@ -110495,9 +110686,110 @@ async function maybePromptFeedback(opts) {
110495
110686
  }
110496
110687
  }
110497
110688
 
110498
- // ../../packages/cli/src/lib/investigation-runner.ts
110689
+ // ../../packages/cli/src/lib/freshness.ts
110499
110690
  init_cjs_shims();
110691
+ var import_node_child_process5 = require("child_process");
110692
+ var import_node_fs15 = require("fs");
110693
+ var import_node_path16 = require("path");
110694
+ var import_node_util4 = require("util");
110500
110695
  var import_picocolors9 = __toESM(require_picocolors(), 1);
110696
+ var execFileAsync = (0, import_node_util4.promisify)(import_node_child_process5.execFile);
110697
+ async function commitsSince(repoRoot, sinceIso) {
110698
+ try {
110699
+ const { stdout } = await execFileAsync(
110700
+ "git",
110701
+ ["-C", repoRoot, "rev-list", "--count", `--since=${sinceIso}`, "HEAD"],
110702
+ { timeout: 5e3 }
110703
+ );
110704
+ const n4 = parseInt(stdout.trim(), 10);
110705
+ return Number.isFinite(n4) ? n4 : null;
110706
+ } catch {
110707
+ return null;
110708
+ }
110709
+ }
110710
+ var RUNTIME_SOURCES = /* @__PURE__ */ new Set(["logs", "metrics", "state", "queue"]);
110711
+ var STALE_INDEX_MS = 7 * 24 * 60 * 60 * 1e3;
110712
+ function readIndexMeta(repoRoot) {
110713
+ try {
110714
+ const raw = (0, import_node_fs15.readFileSync)((0, import_node_path16.join)(repoRoot, ".horus", "source", "meta.json"), "utf8");
110715
+ const j3 = JSON.parse(raw);
110716
+ return {
110717
+ lastIndexedAt: typeof j3.last_indexed_at === "string" ? j3.last_indexed_at : void 0,
110718
+ version: typeof j3.version === "string" ? j3.version : void 0,
110719
+ stats: j3.stats ?? void 0
110720
+ };
110721
+ } catch {
110722
+ return null;
110723
+ }
110724
+ }
110725
+ function humanAge(ms) {
110726
+ const s5 = Math.max(0, Math.floor(ms / 1e3));
110727
+ if (s5 < 60) return `${s5}s`;
110728
+ const m5 = Math.floor(s5 / 60);
110729
+ if (m5 < 60) return `${m5}m`;
110730
+ const h3 = Math.floor(m5 / 60);
110731
+ if (h3 < 24) return `${h3}h`;
110732
+ return `${Math.floor(h3 / 24)}d`;
110733
+ }
110734
+ function computeFreshness(opts) {
110735
+ const now = Date.parse(opts.nowIso);
110736
+ const meta = opts.meta !== void 0 ? opts.meta : readIndexMeta(opts.repoRoot);
110737
+ const commitsSinceIndex = opts.commitsSinceIndex ?? null;
110738
+ const caveats = [];
110739
+ let indexAgeMs = null;
110740
+ let indexAgeLabel = "index age unknown";
110741
+ let indexStale = false;
110742
+ const indexedAt = meta?.lastIndexedAt ? Date.parse(meta.lastIndexedAt) : NaN;
110743
+ if (!Number.isNaN(indexedAt)) {
110744
+ indexAgeMs = Math.max(0, now - indexedAt);
110745
+ indexAgeLabel = `indexed ${humanAge(indexAgeMs)} ago`;
110746
+ indexStale = indexAgeMs > STALE_INDEX_MS;
110747
+ if (indexStale) {
110748
+ caveats.push(
110749
+ `code index is ${humanAge(indexAgeMs)} old \u2014 re-run \`horus index\` so analysis reflects current code`
110750
+ );
110751
+ }
110752
+ } else {
110753
+ caveats.push("code index age unknown (no .horus/source/meta.json) \u2014 freshness unverified");
110754
+ }
110755
+ if (typeof commitsSinceIndex === "number" && commitsSinceIndex > 0) {
110756
+ caveats.push(
110757
+ `${commitsSinceIndex} commit(s) since the last index \u2014 re-run \`horus index\` so analysis reflects current code`
110758
+ );
110759
+ }
110760
+ const runtime = opts.evidence.filter((e) => e.source && RUNTIME_SOURCES.has(e.source));
110761
+ const runtimeSources = [...new Set(runtime.map((e) => e.source))].sort();
110762
+ const stamps = runtime.map((e) => e.timestamp ? Date.parse(e.timestamp) : NaN).filter((n4) => !Number.isNaN(n4)).sort((a2, b5) => a2 - b5);
110763
+ const runtimeWindow = stamps.length > 0 ? { fromIso: new Date(stamps[0]).toISOString(), toIso: new Date(stamps[stamps.length - 1]).toISOString() } : null;
110764
+ if (runtime.length === 0) {
110765
+ caveats.push(
110766
+ "no runtime evidence \u2014 source-only investigation; connect logs/metrics/queues for deeper grounding"
110767
+ );
110768
+ }
110769
+ return {
110770
+ indexAgeMs,
110771
+ indexAgeLabel,
110772
+ indexStale,
110773
+ commitsSinceIndex,
110774
+ runtimeWindow,
110775
+ runtimeSources,
110776
+ caveats
110777
+ };
110778
+ }
110779
+ function renderFreshness(f5) {
110780
+ const lines = [];
110781
+ const behind = f5.commitsSinceIndex && f5.commitsSinceIndex > 0 ? ` (${f5.commitsSinceIndex} behind)` : "";
110782
+ const label = f5.indexAgeLabel + behind;
110783
+ const idx = f5.indexStale || behind ? import_picocolors9.default.yellow(label) : label;
110784
+ const runtime = f5.runtimeWindow !== null ? `runtime ${f5.runtimeWindow.fromIso.slice(0, 10)}\u2192${f5.runtimeWindow.toIso.slice(0, 10)}` + (f5.runtimeSources.length ? ` (${f5.runtimeSources.join(", ")})` : "") : "no runtime window";
110785
+ lines.push(import_picocolors9.default.dim(`Freshness: code ${idx} \xB7 ${runtime}`));
110786
+ for (const c4 of f5.caveats) lines.push(import_picocolors9.default.yellow(` \u26A0 ${c4}`));
110787
+ return lines.join("\n");
110788
+ }
110789
+
110790
+ // ../../packages/cli/src/lib/investigation-runner.ts
110791
+ init_cjs_shims();
110792
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
110501
110793
  async function buildInvestigationContext(renv, opts) {
110502
110794
  const log = opts.log ?? ((l2) => console.error(l2));
110503
110795
  const code = codeForEnv(renv);
@@ -110509,20 +110801,20 @@ async function buildInvestigationContext(renv, opts) {
110509
110801
  const sourceUrl = renv.repositories[0]?.sourceHostUrl;
110510
110802
  let health = await code.health();
110511
110803
  if (!health.ok && sourceUrl) {
110512
- log(import_picocolors9.default.yellow(`Source-intelligence host unreachable (${sourceUrl}) \u2014 attempting to start it\u2026`));
110804
+ log(import_picocolors10.default.yellow(`Source-intelligence host unreachable (${sourceUrl}) \u2014 attempting to start it\u2026`));
110513
110805
  const healed = await ensureSourceHost(renv.path, sourceUrl);
110514
110806
  if (healed.ok) {
110515
- log(import_picocolors9.default.green(`Source-intelligence host is up at ${healed.hostUrl}.`));
110807
+ log(import_picocolors10.default.green(`Source-intelligence host is up at ${healed.hostUrl}.`));
110516
110808
  health = await code.health();
110517
110809
  } else {
110518
- log(import_picocolors9.default.dim(` ${ensureHostReasonHint(healed.reason)}`));
110810
+ log(import_picocolors10.default.dim(` ${ensureHostReasonHint(healed.reason)}`));
110519
110811
  }
110520
110812
  }
110521
110813
  const runtimeOnly = !health.ok;
110522
110814
  if (runtimeOnly) {
110523
110815
  log(
110524
- import_picocolors9.default.yellow(
110525
- `Proceeding in runtime-only mode \u2014 no source intelligence. Run ${import_picocolors9.default.bold("horus index")} for a full (code-aware) investigation.`
110816
+ import_picocolors10.default.yellow(
110817
+ `Proceeding in runtime-only mode \u2014 no source intelligence. Run ${import_picocolors10.default.bold("horus index")} for a full (code-aware) investigation.`
110526
110818
  )
110527
110819
  );
110528
110820
  }
@@ -110677,37 +110969,37 @@ function renderStoredAIJudgment(judgment, write = (l2) => console.log(l2)) {
110677
110969
  const sep = "\u2500".repeat(60);
110678
110970
  write(`
110679
110971
  ${sep}`);
110680
- write(import_picocolors10.default.bold("AI Judgment") + import_picocolors10.default.dim(` (confidence: ${(judgment.confidence * 100).toFixed(0)}%, provider: ${judgment.provider})`));
110972
+ write(import_picocolors11.default.bold("AI Judgment") + import_picocolors11.default.dim(` (confidence: ${(judgment.confidence * 100).toFixed(0)}%, provider: ${judgment.provider})`));
110681
110973
  write(sep);
110682
110974
  if (judgment.hypothesisJudgments && judgment.hypothesisJudgments.length > 0) {
110683
- write(import_picocolors10.default.bold("Hypothesis verdicts:"));
110975
+ write(import_picocolors11.default.bold("Hypothesis verdicts:"));
110684
110976
  for (const j3 of judgment.hypothesisJudgments) {
110685
- const verdictColor = j3.verdict === "supported" ? import_picocolors10.default.green : j3.verdict === "weakened" ? import_picocolors10.default.yellow : j3.verdict === "eliminated" ? import_picocolors10.default.red : import_picocolors10.default.dim;
110686
- write(` ${verdictColor(`[${j3.verdict}]`)} ${j3.category}` + import_picocolors10.default.dim(` (${(j3.confidence * 100).toFixed(0)}%)`));
110687
- write(import_picocolors10.default.dim(` ${j3.rationale}`));
110977
+ const verdictColor = j3.verdict === "supported" ? import_picocolors11.default.green : j3.verdict === "weakened" ? import_picocolors11.default.yellow : j3.verdict === "eliminated" ? import_picocolors11.default.red : import_picocolors11.default.dim;
110978
+ write(` ${verdictColor(`[${j3.verdict}]`)} ${j3.category}` + import_picocolors11.default.dim(` (${(j3.confidence * 100).toFixed(0)}%)`));
110979
+ write(import_picocolors11.default.dim(` ${j3.rationale}`));
110688
110980
  }
110689
110981
  write("");
110690
110982
  }
110691
110983
  if (judgment.rootCauseAssessment) {
110692
110984
  const rca = judgment.rootCauseAssessment;
110693
- const uncertaintyColor = rca.uncertainty === "low" ? import_picocolors10.default.green : rca.uncertainty === "medium" ? import_picocolors10.default.yellow : import_picocolors10.default.red;
110694
- write(import_picocolors10.default.bold("Root cause assessment:") + import_picocolors10.default.dim(` uncertainty: ${uncertaintyColor(rca.uncertainty)}`));
110985
+ const uncertaintyColor = rca.uncertainty === "low" ? import_picocolors11.default.green : rca.uncertainty === "medium" ? import_picocolors11.default.yellow : import_picocolors11.default.red;
110986
+ write(import_picocolors11.default.bold("Root cause assessment:") + import_picocolors11.default.dim(` uncertainty: ${uncertaintyColor(rca.uncertainty)}`));
110695
110987
  write(rca.summary);
110696
110988
  if (rca.citedEvidenceIds.length > 0) {
110697
- write(import_picocolors10.default.dim(` Cited: ${rca.citedEvidenceIds.join(", ")}`));
110989
+ write(import_picocolors11.default.dim(` Cited: ${rca.citedEvidenceIds.join(", ")}`));
110698
110990
  }
110699
110991
  write("");
110700
110992
  }
110701
- write(import_picocolors10.default.bold("What:") + " " + judgment.what);
110702
- write(import_picocolors10.default.bold("Why:") + " " + judgment.why);
110993
+ write(import_picocolors11.default.bold("What:") + " " + judgment.what);
110994
+ write(import_picocolors11.default.bold("Why:") + " " + judgment.why);
110703
110995
  if (judgment.whereNext.length > 0) {
110704
- write(import_picocolors10.default.bold("Next steps:"));
110996
+ write(import_picocolors11.default.bold("Next steps:"));
110705
110997
  for (const step of judgment.whereNext) {
110706
110998
  write(` \u2022 ${step}`);
110707
110999
  }
110708
111000
  }
110709
111001
  if (judgment.citations.length > 0) {
110710
- write(import_picocolors10.default.dim(`
111002
+ write(import_picocolors11.default.dim(`
110711
111003
  Cited evidence: ${judgment.citations.map((c4) => c4.evidenceId).join(", ")}`));
110712
111004
  }
110713
111005
  }
@@ -110733,7 +111025,7 @@ async function runInvestigate(hint, opts) {
110733
111025
  try {
110734
111026
  renv = resolveEnvironment(config2, { project: projectName, env: opts.env });
110735
111027
  } catch (err2) {
110736
- console.error(import_picocolors10.default.red(err2.message));
111028
+ console.error(import_picocolors11.default.red(err2.message));
110737
111029
  return 1;
110738
111030
  }
110739
111031
  const repoRoot = repoRootOrCwd(renv.path);
@@ -110742,8 +111034,8 @@ async function runInvestigate(hint, opts) {
110742
111034
  const cloudSession = cloudActive ? authedClient() : null;
110743
111035
  if (cloudActive && !cloudSession) {
110744
111036
  console.error(
110745
- import_picocolors10.default.yellow(
110746
- `This repo is linked to Horus Cloud but you are not logged in \u2014 running locally only. Run ${import_picocolors10.default.bold("horus login")} to also sync results to the cloud.`
111037
+ import_picocolors11.default.yellow(
111038
+ `This repo is linked to Horus Cloud but you are not logged in \u2014 running locally only. Run ${import_picocolors11.default.bold("horus login")} to also sync results to the cloud.`
110747
111039
  )
110748
111040
  );
110749
111041
  }
@@ -110754,7 +111046,7 @@ async function runInvestigate(hint, opts) {
110754
111046
  ...opts.service !== void 0 ? { service: opts.service } : {}
110755
111047
  });
110756
111048
  } catch (err2) {
110757
- console.error(import_picocolors10.default.red(err2.message));
111049
+ console.error(import_picocolors11.default.red(err2.message));
110758
111050
  return 1;
110759
111051
  }
110760
111052
  try {
@@ -110771,8 +111063,24 @@ async function runInvestigate(hint, opts) {
110771
111063
  );
110772
111064
  const db = ctx.dbHandle.db;
110773
111065
  const format = opts.json ? "json" : opts.format ?? "text";
110774
- const rendered = format === "json" ? reportToJSON(report) : format === "markdown" || format === "md" ? reportToMarkdown(report) : renderReport(report);
110775
- console.log(rendered);
111066
+ const fmeta = readIndexMeta(repoRoot);
111067
+ const commitsSinceIndex = fmeta?.lastIndexedAt ? await commitsSince(repoRoot, fmeta.lastIndexedAt) : null;
111068
+ const freshness = computeFreshness({
111069
+ repoRoot,
111070
+ evidence: report.evidence,
111071
+ nowIso: (/* @__PURE__ */ new Date()).toISOString(),
111072
+ meta: fmeta,
111073
+ commitsSinceIndex
111074
+ });
111075
+ if (format === "json") {
111076
+ const obj = JSON.parse(reportToJSON(report));
111077
+ obj.freshness = freshness;
111078
+ console.log(JSON.stringify(obj, null, 2));
111079
+ } else {
111080
+ console.log(format === "markdown" || format === "md" ? reportToMarkdown(report) : renderReport(report));
111081
+ console.log("");
111082
+ console.log(renderFreshness(freshness));
111083
+ }
110776
111084
  if (opts.ai && format !== "json") {
110777
111085
  const ai = resolveAiSettings(config2);
110778
111086
  const model = opts.aiModel ?? ai.model ?? "claude-opus-4-8";
@@ -110780,7 +111088,7 @@ async function runInvestigate(hint, opts) {
110780
111088
  model,
110781
111089
  ...ai.anthropicApiKey !== void 0 ? { apiKey: ai.anthropicApiKey } : {}
110782
111090
  });
110783
- console.log(import_picocolors10.default.dim(`[ai] model: ${model}`));
111091
+ console.log(import_picocolors11.default.dim(`[ai] model: ${model}`));
110784
111092
  const narrativeInput = buildNarrativeInput(report);
110785
111093
  const { output, fromProvider, degraded, validationErrors } = await renderNarrative(
110786
111094
  narrativeInput,
@@ -110796,24 +111104,24 @@ async function runInvestigate(hint, opts) {
110796
111104
  renderStoredAIJudgment(stored);
110797
111105
  } else if (degraded) {
110798
111106
  if (validationErrors && validationErrors.length > 0) {
110799
- console.error(import_picocolors10.default.yellow(`[ai] structured validation incomplete \u2014 showing raw AI narrative`));
111107
+ console.error(import_picocolors11.default.yellow(`[ai] structured validation incomplete \u2014 showing raw AI narrative`));
110800
111108
  }
110801
111109
  console.log("");
110802
- console.log(import_picocolors10.default.dim("\u2500".repeat(60)));
110803
- console.log(import_picocolors10.default.bold("AI narrative ") + import_picocolors10.default.yellow("(unstructured \u2014 not validated)"));
111110
+ console.log(import_picocolors11.default.dim("\u2500".repeat(60)));
111111
+ console.log(import_picocolors11.default.bold("AI narrative ") + import_picocolors11.default.yellow("(unstructured \u2014 not validated)"));
110804
111112
  if (output.what?.trim()) {
110805
- console.log(import_picocolors10.default.bold("\nWhat:"), output.what.trim());
111113
+ console.log(import_picocolors11.default.bold("\nWhat:"), output.what.trim());
110806
111114
  }
110807
111115
  if (output.why?.trim()) {
110808
- console.log(import_picocolors10.default.bold("\nWhy:"), output.why.trim());
111116
+ console.log(import_picocolors11.default.bold("\nWhy:"), output.why.trim());
110809
111117
  }
110810
111118
  if (output.whereNext.length > 0) {
110811
- console.log(import_picocolors10.default.bold("\nNext:"));
111119
+ console.log(import_picocolors11.default.bold("\nNext:"));
110812
111120
  for (const a2 of output.whereNext) console.log(` - ${a2}`);
110813
111121
  }
110814
111122
  } else {
110815
111123
  const reason = classifyAIFailure(validationErrors?.[0]);
110816
- console.error(import_picocolors10.default.yellow(`[ai] fallback to deterministic \u2014 ${reason}`));
111124
+ console.error(import_picocolors11.default.yellow(`[ai] fallback to deterministic \u2014 ${reason}`));
110817
111125
  }
110818
111126
  }
110819
111127
  track({
@@ -110844,7 +111152,7 @@ async function runInvestigate(hint, opts) {
110844
111152
  if (cloudActive && cloudSession && cloudCfg) {
110845
111153
  try {
110846
111154
  const refs = await uploadInvestigationToCloud(cloudSession.client, cloudCfg, report);
110847
- const cloudNote = import_picocolors10.default.dim(`[cloud] investigation saved: ${refs.investigationId}`);
111155
+ const cloudNote = import_picocolors11.default.dim(`[cloud] investigation saved: ${refs.investigationId}`);
110848
111156
  if (format === "json") console.error(cloudNote);
110849
111157
  else console.log(cloudNote);
110850
111158
  } catch (err2) {
@@ -110854,16 +111162,16 @@ async function runInvestigate(hint, opts) {
110854
111162
  if (format !== "json") {
110855
111163
  if (report.persisted === false) {
110856
111164
  console.error(
110857
- import_picocolors10.default.yellow(`
111165
+ import_picocolors11.default.yellow(`
110858
111166
  \u26A0 Results were not saved \u2014 the investigation database was unreachable.`)
110859
111167
  );
110860
111168
  console.error(
110861
- import_picocolors10.default.dim(
111169
+ import_picocolors11.default.dim(
110862
111170
  ` This report is display-only; \`horus ask\` won't find it. Start the database and re-run to persist.`
110863
111171
  )
110864
111172
  );
110865
111173
  } else {
110866
- console.log(import_picocolors10.default.dim(`
111174
+ console.log(import_picocolors11.default.dim(`
110867
111175
  Ask a follow-up: horus ask ${report.id} "<question>"`));
110868
111176
  }
110869
111177
  await maybePromptFeedback({
@@ -110877,15 +111185,15 @@ Ask a follow-up: horus ask ${report.id} "<question>"`));
110877
111185
  return 0;
110878
111186
  } catch (err2) {
110879
111187
  const msg = err2?.message || String(err2);
110880
- console.error(import_picocolors10.default.red(msg.trim() ? msg : "Investigation failed (unknown error)."));
110881
- if (process.env.HORUS_DEBUG) console.error(import_picocolors10.default.dim(err2?.stack ?? String(err2)));
111188
+ console.error(import_picocolors11.default.red(msg.trim() ? msg : "Investigation failed (unknown error)."));
111189
+ if (process.env.HORUS_DEBUG) console.error(import_picocolors11.default.dim(err2?.stack ?? String(err2)));
110882
111190
  return 1;
110883
111191
  }
110884
111192
  }
110885
111193
 
110886
111194
  // ../../packages/cli/src/commands/watch.ts
110887
111195
  init_cjs_shims();
110888
- var import_picocolors11 = __toESM(require_picocolors(), 1);
111196
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
110889
111197
  function detectNewSentryIncidents(issues, seen) {
110890
111198
  const fresh = [];
110891
111199
  for (const issue2 of issues) {
@@ -110967,21 +111275,21 @@ async function runWatch(opts) {
110967
111275
  try {
110968
111276
  renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
110969
111277
  } catch (err2) {
110970
- console.error(import_picocolors11.default.red(err2.message));
111278
+ console.error(import_picocolors12.default.red(err2.message));
110971
111279
  return 1;
110972
111280
  }
110973
111281
  let ctx;
110974
111282
  try {
110975
111283
  ctx = await buildInvestigationContext(renv, { databaseUrl: config2.database.url });
110976
111284
  } catch (err2) {
110977
- console.error(import_picocolors11.default.red(err2.message));
111285
+ console.error(import_picocolors12.default.red(err2.message));
110978
111286
  return 1;
110979
111287
  }
110980
111288
  const requested = opts.source ?? "auto";
110981
111289
  const source = resolveSource(requested, ctx);
110982
111290
  if (source === null) {
110983
111291
  console.error(
110984
- import_picocolors11.default.red(
111292
+ import_picocolors12.default.red(
110985
111293
  requested === "auto" ? `No Sentry or Elasticsearch connector configured for "${renv.project}" / "${renv.env}".` : `No ${requested} connector configured for "${renv.project}" / "${renv.env}".`
110986
111294
  )
110987
111295
  );
@@ -110991,7 +111299,7 @@ async function runWatch(opts) {
110991
111299
  const intervalSec = Math.max(1, Number(opts.interval) || 60);
110992
111300
  const seen = /* @__PURE__ */ new Set();
110993
111301
  console.error(
110994
- import_picocolors11.default.dim(
111302
+ import_picocolors12.default.dim(
110995
111303
  `[watch] source: ${source} \xB7 project: ${renv.project} \xB7 env: ${renv.env}` + (opts.once ? " \xB7 mode: once" : ` \xB7 interval: ${intervalSec}s`)
110996
111304
  )
110997
111305
  );
@@ -111000,24 +111308,24 @@ async function runWatch(opts) {
111000
111308
  try {
111001
111309
  incidents = source === "sentry" ? await pollSentry(ctx.sentry, seen) : await pollElasticsearch(ctx.logs, seen, ctx.service);
111002
111310
  } catch (err2) {
111003
- console.error(import_picocolors11.default.yellow(`[watch] poll failed \u2014 ${err2.message}`));
111311
+ console.error(import_picocolors12.default.yellow(`[watch] poll failed \u2014 ${err2.message}`));
111004
111312
  return;
111005
111313
  }
111006
111314
  if (incidents.length === 0) {
111007
- console.error(import_picocolors11.default.dim(`[watch] no new incidents`));
111315
+ console.error(import_picocolors12.default.dim(`[watch] no new incidents`));
111008
111316
  return;
111009
111317
  }
111010
111318
  for (const incident of incidents) {
111011
111319
  try {
111012
111320
  const report = await runOneInvestigation({ hint: incident.hint }, ctx);
111013
111321
  const { cause, confidence } = headlineFor(report);
111014
- const persisted = report.persisted === false ? import_picocolors11.default.yellow(" (not persisted)") : "";
111322
+ const persisted = report.persisted === false ? import_picocolors12.default.yellow(" (not persisted)") : "";
111015
111323
  console.log(
111016
- `${import_picocolors11.default.bold("\u25CF")} ${incident.hint} ${import_picocolors11.default.dim("\u2192")} ${cause} ${import_picocolors11.default.dim(`(${(confidence * 100).toFixed(0)}%)`)} ${import_picocolors11.default.dim(`id: ${report.id}`)}${persisted}`
111324
+ `${import_picocolors12.default.bold("\u25CF")} ${incident.hint} ${import_picocolors12.default.dim("\u2192")} ${cause} ${import_picocolors12.default.dim(`(${(confidence * 100).toFixed(0)}%)`)} ${import_picocolors12.default.dim(`id: ${report.id}`)}${persisted}`
111017
111325
  );
111018
111326
  } catch (err2) {
111019
111327
  console.error(
111020
- import_picocolors11.default.yellow(`[watch] investigation failed for "${incident.ref}" \u2014 ${err2.message}`)
111328
+ import_picocolors12.default.yellow(`[watch] investigation failed for "${incident.ref}" \u2014 ${err2.message}`)
111021
111329
  );
111022
111330
  }
111023
111331
  }
@@ -111032,7 +111340,7 @@ async function runWatch(opts) {
111032
111340
  const onSignal = () => {
111033
111341
  if (stopping) return;
111034
111342
  stopping = true;
111035
- console.error(import_picocolors11.default.dim(`
111343
+ console.error(import_picocolors12.default.dim(`
111036
111344
  [watch] shutting down\u2026`));
111037
111345
  if (wake) wake();
111038
111346
  };
@@ -111059,7 +111367,7 @@ async function runWatch(opts) {
111059
111367
 
111060
111368
  // ../../packages/cli/src/commands/changes.ts
111061
111369
  init_cjs_shims();
111062
- var import_picocolors12 = __toESM(require_picocolors(), 1);
111370
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
111063
111371
  var CHANGES_AI_CONTRACT = `Provide a clearly separated AI change-impact review with:
111064
111372
 
111065
111373
  Highest-risk changes
@@ -111084,7 +111392,7 @@ async function runChanges(base2, compare, opts) {
111084
111392
  const health = await code.health();
111085
111393
  if (!health.ok) {
111086
111394
  console.error(
111087
- import_picocolors12.default.red("Source-intelligence host unreachable \u2014 run: horus index")
111395
+ import_picocolors13.default.red("Source-intelligence host unreachable \u2014 run: horus index")
111088
111396
  );
111089
111397
  return 1;
111090
111398
  }
@@ -111106,20 +111414,20 @@ async function runChanges(base2, compare, opts) {
111106
111414
  });
111107
111415
  console.log("\n" + renderInterpretation(result));
111108
111416
  if (!result.ok) {
111109
- console.error(import_picocolors12.default.yellow(`[ai] ${result.warning}`));
111417
+ console.error(import_picocolors13.default.yellow(`[ai] ${result.warning}`));
111110
111418
  }
111111
111419
  }
111112
111420
  }
111113
111421
  return 0;
111114
111422
  } catch (err2) {
111115
- console.error(import_picocolors12.default.red(err2.message));
111423
+ console.error(import_picocolors13.default.red(err2.message));
111116
111424
  return 1;
111117
111425
  }
111118
111426
  }
111119
111427
 
111120
111428
  // ../../packages/cli/src/commands/timeline.ts
111121
111429
  init_cjs_shims();
111122
- var import_picocolors13 = __toESM(require_picocolors(), 1);
111430
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
111123
111431
  var DEFAULT_SINCE = "7 days ago";
111124
111432
  function resolveTimelineWindow(opts) {
111125
111433
  const usingDefault = !opts.all && opts.since === void 0;
@@ -111149,7 +111457,7 @@ async function runTimeline(service, opts) {
111149
111457
  try {
111150
111458
  renv = resolveEnvironment(config2, { project: opts.repo });
111151
111459
  } catch (err2) {
111152
- console.error(import_picocolors13.default.red(err2.message));
111460
+ console.error(import_picocolors14.default.red(err2.message));
111153
111461
  return 1;
111154
111462
  }
111155
111463
  const { code } = createConnectors(config2);
@@ -111164,9 +111472,9 @@ async function runTimeline(service, opts) {
111164
111472
  console.log(renderChangeTimeline(t2));
111165
111473
  if (usingDefaultWindow) {
111166
111474
  console.log(
111167
- import_picocolors13.default.dim(
111475
+ import_picocolors14.default.dim(
111168
111476
  `
111169
- Showing the last 7 days (default). Widen with ${import_picocolors13.default.bold('--since "30 days ago"')}, pin a range with ${import_picocolors13.default.bold("--since <when> --until <when>")}, or see everything with ${import_picocolors13.default.bold("--all")}.`
111477
+ Showing the last 7 days (default). Widen with ${import_picocolors14.default.bold('--since "30 days ago"')}, pin a range with ${import_picocolors14.default.bold("--since <when> --until <when>")}, or see everything with ${import_picocolors14.default.bold("--all")}.`
111170
111478
  )
111171
111479
  );
111172
111480
  }
@@ -111183,21 +111491,26 @@ async function runTimeline(service, opts) {
111183
111491
  });
111184
111492
  console.log("\n" + renderInterpretation(result));
111185
111493
  if (!result.ok) {
111186
- console.error(import_picocolors13.default.yellow(`[ai] ${result.warning}`));
111494
+ console.error(import_picocolors14.default.yellow(`[ai] ${result.warning}`));
111187
111495
  }
111188
111496
  }
111189
111497
  }
111190
111498
  return 0;
111191
111499
  } catch (err2) {
111192
- console.error(import_picocolors13.default.red(err2.message));
111500
+ console.error(import_picocolors14.default.red(err2.message));
111193
111501
  return 1;
111194
111502
  }
111195
111503
  }
111196
111504
 
111197
111505
  // ../../packages/cli/src/commands/what-changed.ts
111198
111506
  init_cjs_shims();
111199
- var import_picocolors14 = __toESM(require_picocolors(), 1);
111507
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
111200
111508
  var DEFAULT_SINCE2 = "7 days ago";
111509
+ function looksLikeGitRef(s5) {
111510
+ const t2 = s5.trim();
111511
+ if (/\s/.test(t2) || /^\d{4}-\d{2}-\d{2}/.test(t2)) return false;
111512
+ return /[~^]/.test(t2) || /^HEAD\b/.test(t2) || /^[0-9a-f]{7,40}$/i.test(t2) || /^v?\d+\.\d+/.test(t2);
111513
+ }
111201
111514
  var WHAT_CHANGED_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
111202
111515
 
111203
111516
  Evidence used
@@ -111220,11 +111533,18 @@ async function runWhatChanged(service, opts) {
111220
111533
  try {
111221
111534
  renv = resolveEnvironment(config2, { project: opts.repo });
111222
111535
  } catch (err2) {
111223
- console.error(import_picocolors14.default.red(err2.message));
111536
+ console.error(import_picocolors15.default.red(err2.message));
111224
111537
  return 1;
111225
111538
  }
111226
111539
  const { code } = createConnectors(config2);
111227
111540
  const since = opts.since ?? DEFAULT_SINCE2;
111541
+ if (opts.since !== void 0 && looksLikeGitRef(opts.since) && !await resolvesToCommit(renv.path, opts.since)) {
111542
+ console.error(
111543
+ import_picocolors15.default.yellow(
111544
+ `Warning: --since "${opts.since}" does not resolve to a commit (shallow clone or unknown ref?); it will be treated as a date filter and likely return no commits.`
111545
+ )
111546
+ );
111547
+ }
111228
111548
  const r = await whatChanged(
111229
111549
  { repoPath: renv.path, since, until: opts.until, service },
111230
111550
  { code }
@@ -111243,7 +111563,7 @@ async function runWhatChanged(service, opts) {
111243
111563
  });
111244
111564
  console.log("\n" + renderInterpretation(result));
111245
111565
  if (!result.ok) {
111246
- console.error(import_picocolors14.default.yellow(`[ai] ${result.warning}`));
111566
+ console.error(import_picocolors15.default.yellow(`[ai] ${result.warning}`));
111247
111567
  }
111248
111568
  }
111249
111569
  if (opts.push) {
@@ -111251,19 +111571,19 @@ async function runWhatChanged(service, opts) {
111251
111571
  }
111252
111572
  return 0;
111253
111573
  } catch (err2) {
111254
- console.error(import_picocolors14.default.red(err2.message));
111574
+ console.error(import_picocolors15.default.red(err2.message));
111255
111575
  return 1;
111256
111576
  }
111257
111577
  }
111258
111578
  async function pushChangeReport(r, repoCwd) {
111259
111579
  const session = authedClient();
111260
111580
  if (!session) {
111261
- console.error(import_picocolors14.default.yellow("[cloud] not logged in \u2014 run `horus login` to push reports."));
111581
+ console.error(import_picocolors15.default.yellow("[cloud] not logged in \u2014 run `horus login` to push reports."));
111262
111582
  return;
111263
111583
  }
111264
111584
  const cfg = readCloudConfig(repoRootOrCwd(repoCwd));
111265
111585
  if (!isCloudActive(cfg)) {
111266
- console.error(import_picocolors14.default.yellow("[cloud] repo not linked \u2014 run `horus cloud link` to push reports."));
111586
+ console.error(import_picocolors15.default.yellow("[cloud] repo not linked \u2014 run `horus cloud link` to push reports."));
111267
111587
  return;
111268
111588
  }
111269
111589
  const projectId = cfg.project?.id;
@@ -111309,15 +111629,15 @@ async function pushChangeReport(r, repoCwd) {
111309
111629
  queueTopologyTouched: r.queueTopology.touched,
111310
111630
  payload
111311
111631
  });
111312
- console.error(import_picocolors14.default.dim("[cloud] pushed change report."));
111632
+ console.error(import_picocolors15.default.dim("[cloud] pushed change report."));
111313
111633
  } catch (err2) {
111314
- console.error(import_picocolors14.default.yellow(`[cloud] push failed: ${err2.message}`));
111634
+ console.error(import_picocolors15.default.yellow(`[cloud] push failed: ${err2.message}`));
111315
111635
  }
111316
111636
  }
111317
111637
 
111318
111638
  // ../../packages/cli/src/commands/architecture.ts
111319
111639
  init_cjs_shims();
111320
- var import_picocolors15 = __toESM(require_picocolors(), 1);
111640
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
111321
111641
  var ARCHITECTURE_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
111322
111642
 
111323
111643
  System summary
@@ -111346,7 +111666,7 @@ async function runArchitecture(opts) {
111346
111666
  const health = await code.health();
111347
111667
  if (!health.ok) {
111348
111668
  console.error(
111349
- import_picocolors15.default.red("Source-intelligence host unreachable \u2014 run: horus index")
111669
+ import_picocolors16.default.red("Source-intelligence host unreachable \u2014 run: horus index")
111350
111670
  );
111351
111671
  return 1;
111352
111672
  }
@@ -111374,7 +111694,7 @@ async function runArchitecture(opts) {
111374
111694
  });
111375
111695
  console.log("\n" + renderInterpretation(result));
111376
111696
  if (!result.ok) {
111377
- console.error(import_picocolors15.default.yellow(`[ai] ${result.warning}`));
111697
+ console.error(import_picocolors16.default.yellow(`[ai] ${result.warning}`));
111378
111698
  }
111379
111699
  }
111380
111700
  }
@@ -111383,14 +111703,14 @@ async function runArchitecture(opts) {
111383
111703
  }
111384
111704
  return 0;
111385
111705
  } catch (err2) {
111386
- console.error(import_picocolors15.default.red(err2.message));
111706
+ console.error(import_picocolors16.default.red(err2.message));
111387
111707
  return 1;
111388
111708
  }
111389
111709
  }
111390
111710
 
111391
111711
  // ../../packages/cli/src/commands/blast-radius.ts
111392
111712
  init_cjs_shims();
111393
- var import_picocolors16 = __toESM(require_picocolors(), 1);
111713
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
111394
111714
  var BLAST_RADIUS_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
111395
111715
 
111396
111716
  Evidence used
@@ -111417,7 +111737,7 @@ async function runBlastRadius(query, opts) {
111417
111737
  const { code } = createConnectors(config2);
111418
111738
  const health = await code.health();
111419
111739
  if (!health.ok) {
111420
- console.error(import_picocolors16.default.red("Source-intelligence host unreachable \u2014 run: horus index"));
111740
+ console.error(import_picocolors17.default.red("Source-intelligence host unreachable \u2014 run: horus index"));
111421
111741
  return 1;
111422
111742
  }
111423
111743
  let project;
@@ -111430,12 +111750,12 @@ async function runBlastRadius(query, opts) {
111430
111750
  const r = await analyzeBlastRadius(query, { code, db, project }, opts.depth ?? 3);
111431
111751
  if (!r) {
111432
111752
  console.log(`No symbol found for: ${query}`);
111433
- console.log(import_picocolors16.default.dim(` Tip: use an exact class or function name, e.g. "MyService"`));
111753
+ console.log(import_picocolors17.default.dim(` Tip: use an exact class or function name, e.g. "MyService"`));
111434
111754
  return 1;
111435
111755
  }
111436
111756
  if (r.seed.name.toLowerCase() !== query.toLowerCase()) {
111437
111757
  console.log(
111438
- import_picocolors16.default.yellow(` No exact match for "${query}"`) + import_picocolors16.default.dim(` \u2014 showing closest: "${r.seed.name}" (fuzzy match)`)
111758
+ import_picocolors17.default.yellow(` No exact match for "${query}"`) + import_picocolors17.default.dim(` \u2014 showing closest: "${r.seed.name}" (fuzzy match)`)
111439
111759
  );
111440
111760
  }
111441
111761
  if (opts.json) {
@@ -111455,7 +111775,7 @@ async function runBlastRadius(query, opts) {
111455
111775
  });
111456
111776
  console.log("\n" + renderInterpretation(result));
111457
111777
  if (!result.ok) {
111458
- console.error(import_picocolors16.default.yellow(`[ai] ${result.warning}`));
111778
+ console.error(import_picocolors17.default.yellow(`[ai] ${result.warning}`));
111459
111779
  }
111460
111780
  }
111461
111781
  }
@@ -111464,28 +111784,28 @@ async function runBlastRadius(query, opts) {
111464
111784
  }
111465
111785
  return 0;
111466
111786
  } catch (err2) {
111467
- console.error(import_picocolors16.default.red(err2.message));
111787
+ console.error(import_picocolors17.default.red(err2.message));
111468
111788
  return 1;
111469
111789
  }
111470
111790
  }
111471
111791
 
111472
111792
  // ../../packages/cli/src/commands/repos.ts
111473
111793
  init_cjs_shims();
111474
- var import_picocolors17 = __toESM(require_picocolors(), 1);
111794
+ var import_picocolors18 = __toESM(require_picocolors(), 1);
111475
111795
  async function runRepos(opts) {
111476
111796
  const config2 = await loadConfig(opts.config);
111477
111797
  const providers = repoProviders(config2);
111478
111798
  if (providers.length === 0) {
111479
- console.log(import_picocolors17.default.dim("No repositories configured."));
111799
+ console.log(import_picocolors18.default.dim("No repositories configured."));
111480
111800
  return 0;
111481
111801
  }
111482
111802
  const health = await reposHealth(providers);
111483
111803
  for (const h3 of health) {
111484
- const dot = h3.reachable ? import_picocolors17.default.green("\u25CF") : import_picocolors17.default.red("\u25CF");
111485
- const name2 = import_picocolors17.default.bold(h3.repo.padEnd(20));
111486
- const hostUrl = import_picocolors17.default.cyan(h3.hostUrl.padEnd(30));
111487
- const path = import_picocolors17.default.dim(h3.path);
111488
- const detail = h3.detail ? import_picocolors17.default.dim(`(${h3.detail})`) : "";
111804
+ const dot = h3.reachable ? import_picocolors18.default.green("\u25CF") : import_picocolors18.default.red("\u25CF");
111805
+ const name2 = import_picocolors18.default.bold(h3.repo.padEnd(20));
111806
+ const hostUrl = import_picocolors18.default.cyan(h3.hostUrl.padEnd(30));
111807
+ const path = import_picocolors18.default.dim(h3.path);
111808
+ const detail = h3.detail ? import_picocolors18.default.dim(`(${h3.detail})`) : "";
111489
111809
  console.log(` ${dot} ${name2} ${hostUrl} ${path} ${detail}`);
111490
111810
  }
111491
111811
  return 0;
@@ -111493,7 +111813,7 @@ async function runRepos(opts) {
111493
111813
 
111494
111814
  // ../../packages/cli/src/commands/search.ts
111495
111815
  init_cjs_shims();
111496
- var import_picocolors18 = __toESM(require_picocolors(), 1);
111816
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
111497
111817
  async function runSearch(query, opts) {
111498
111818
  const config2 = await loadConfig(opts.config);
111499
111819
  const providers = repoProviders(config2);
@@ -111504,16 +111824,16 @@ async function runSearch(query, opts) {
111504
111824
  }
111505
111825
  let totalMatches = 0;
111506
111826
  for (const result of results) {
111507
- const statusDot = result.reachable ? import_picocolors18.default.green("\u25CF") : import_picocolors18.default.red("\u25CF");
111827
+ const statusDot = result.reachable ? import_picocolors19.default.green("\u25CF") : import_picocolors19.default.red("\u25CF");
111508
111828
  console.log("");
111509
- console.log(`${statusDot} ${import_picocolors18.default.bold("## " + result.repo)} ${import_picocolors18.default.dim(result.hostUrl)}`);
111829
+ console.log(`${statusDot} ${import_picocolors19.default.bold("## " + result.repo)} ${import_picocolors19.default.dim(result.hostUrl)}`);
111510
111830
  if (!result.reachable) {
111511
- console.log(import_picocolors18.default.dim(" (unreachable)"));
111831
+ console.log(import_picocolors19.default.dim(" (unreachable)"));
111512
111832
  } else if (result.symbols.length === 0) {
111513
- console.log(import_picocolors18.default.dim(" (no matches)"));
111833
+ console.log(import_picocolors19.default.dim(" (no matches)"));
111514
111834
  } else {
111515
111835
  for (const sym of result.symbols) {
111516
- console.log(` - ${import_picocolors18.default.bold(sym.name)} ${import_picocolors18.default.dim(sym.filePath)}`);
111836
+ console.log(` - ${import_picocolors19.default.bold(sym.name)} ${import_picocolors19.default.dim(sym.filePath)}`);
111517
111837
  }
111518
111838
  totalMatches += result.symbols.length;
111519
111839
  }
@@ -111521,7 +111841,7 @@ async function runSearch(query, opts) {
111521
111841
  console.log("");
111522
111842
  const reachableCount = results.filter((r) => r.reachable).length;
111523
111843
  console.log(
111524
- import_picocolors18.default.dim(
111844
+ import_picocolors19.default.dim(
111525
111845
  `${totalMatches} match(es) across ${reachableCount}/${results.length} reachable repo(s) for query: "${query}"`
111526
111846
  )
111527
111847
  );
@@ -111530,7 +111850,7 @@ async function runSearch(query, opts) {
111530
111850
 
111531
111851
  // ../../packages/cli/src/commands/investigations.ts
111532
111852
  init_cjs_shims();
111533
- var import_picocolors19 = __toESM(require_picocolors(), 1);
111853
+ var import_picocolors20 = __toESM(require_picocolors(), 1);
111534
111854
 
111535
111855
  // ../../packages/cli/src/lib/format.ts
111536
111856
  init_cjs_shims();
@@ -111573,8 +111893,8 @@ async function runInvestigations(opts) {
111573
111893
  const session = authedClient();
111574
111894
  if (!session) {
111575
111895
  console.error(
111576
- import_picocolors19.default.red(
111577
- `This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors19.default.bold("horus login")} first.`
111896
+ import_picocolors20.default.red(
111897
+ `This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors20.default.bold("horus login")} first.`
111578
111898
  )
111579
111899
  );
111580
111900
  return 1;
@@ -111615,18 +111935,18 @@ async function runInvestigations(opts) {
111615
111935
 
111616
111936
  // ../../packages/cli/src/commands/replay.ts
111617
111937
  init_cjs_shims();
111618
- var import_picocolors20 = __toESM(require_picocolors(), 1);
111938
+ var import_picocolors21 = __toESM(require_picocolors(), 1);
111619
111939
  async function runReplay(id, opts) {
111620
111940
  const { db, sql: sql2 } = await openDb(await resolveDbUrl(opts.config));
111621
111941
  try {
111622
111942
  const row = await getInvestigation(db, id);
111623
111943
  if (!row) {
111624
- console.error(import_picocolors20.default.red("No investigation found: " + id));
111944
+ console.error(import_picocolors21.default.red("No investigation found: " + id));
111625
111945
  return 1;
111626
111946
  }
111627
111947
  if (!row.report) {
111628
111948
  console.error(
111629
- import_picocolors20.default.red(
111949
+ import_picocolors21.default.red(
111630
111950
  "Investigation " + id + " has no stored report (run a newer investigation)."
111631
111951
  )
111632
111952
  );
@@ -111639,7 +111959,7 @@ async function runReplay(id, opts) {
111639
111959
  if (opts.ai && fmt !== "json") {
111640
111960
  if (report.aiJudgment && !opts.refreshAi) {
111641
111961
  renderStoredAIJudgment(report.aiJudgment);
111642
- console.log(import_picocolors20.default.dim("[ai] Stored judgment replayed. Use --refresh-ai to regenerate."));
111962
+ console.log(import_picocolors21.default.dim("[ai] Stored judgment replayed. Use --refresh-ai to regenerate."));
111643
111963
  } else {
111644
111964
  const narrativeInput = buildNarrativeInput(report);
111645
111965
  const { provider } = await buildNarrativeProvider({
@@ -111652,17 +111972,17 @@ async function runReplay(id, opts) {
111652
111972
  );
111653
111973
  if (!fromProvider && degraded) {
111654
111974
  if (validationErrors?.length) {
111655
- console.error(import_picocolors20.default.yellow("[ai] structured validation incomplete \u2014 showing raw AI narrative"));
111975
+ console.error(import_picocolors21.default.yellow("[ai] structured validation incomplete \u2014 showing raw AI narrative"));
111656
111976
  }
111657
111977
  console.log("");
111658
- console.log(import_picocolors20.default.dim("\u2500".repeat(60)));
111659
- console.log(import_picocolors20.default.bold("AI narrative ") + import_picocolors20.default.yellow("(unstructured \u2014 not validated)"));
111660
- if (output.what?.trim()) console.log(import_picocolors20.default.bold("\nWhat:"), output.what.trim());
111661
- if (output.why?.trim()) console.log(import_picocolors20.default.bold("\nWhy:"), output.why.trim());
111978
+ console.log(import_picocolors21.default.dim("\u2500".repeat(60)));
111979
+ console.log(import_picocolors21.default.bold("AI narrative ") + import_picocolors21.default.yellow("(unstructured \u2014 not validated)"));
111980
+ if (output.what?.trim()) console.log(import_picocolors21.default.bold("\nWhat:"), output.what.trim());
111981
+ if (output.why?.trim()) console.log(import_picocolors21.default.bold("\nWhy:"), output.why.trim());
111662
111982
  } else if (!fromProvider) {
111663
- console.error(import_picocolors20.default.yellow("[ai] Provider unavailable \u2014 deterministic output shown above."));
111983
+ console.error(import_picocolors21.default.yellow("[ai] Provider unavailable \u2014 deterministic output shown above."));
111664
111984
  if (validationErrors?.length) {
111665
- console.error(import_picocolors20.default.dim(` ${validationErrors[0]}`));
111985
+ console.error(import_picocolors21.default.dim(` ${validationErrors[0]}`));
111666
111986
  }
111667
111987
  } else {
111668
111988
  const stored = narrativeOutputToStoredJudgment(output, "anthropic");
@@ -111683,7 +112003,7 @@ async function runReplay(id, opts) {
111683
112003
 
111684
112004
  // ../../packages/cli/src/commands/owner.ts
111685
112005
  init_cjs_shims();
111686
- var import_picocolors21 = __toESM(require_picocolors(), 1);
112006
+ var import_picocolors22 = __toESM(require_picocolors(), 1);
111687
112007
  async function runOwner(query, opts) {
111688
112008
  try {
111689
112009
  const config2 = await loadConfig(opts.config);
@@ -111691,7 +112011,7 @@ async function runOwner(query, opts) {
111691
112011
  try {
111692
112012
  renv = resolveEnvironment(config2, { project: opts.repo });
111693
112013
  } catch (err2) {
111694
- console.error(import_picocolors21.default.red(err2.message));
112014
+ console.error(import_picocolors22.default.red(err2.message));
111695
112015
  return 1;
111696
112016
  }
111697
112017
  const code = codeForRepo(config2, renv.project);
@@ -111699,16 +112019,16 @@ async function runOwner(query, opts) {
111699
112019
  console.log(opts.json ? ownershipToJSON(o5) : renderOwnership(o5));
111700
112020
  return 0;
111701
112021
  } catch (err2) {
111702
- console.error(import_picocolors21.default.red(err2.message));
112022
+ console.error(import_picocolors22.default.red(err2.message));
111703
112023
  return 1;
111704
112024
  }
111705
112025
  }
111706
112026
 
111707
112027
  // ../../packages/cli/src/commands/postmortem.ts
111708
112028
  init_cjs_shims();
111709
- var import_node_fs15 = require("fs");
111710
- var import_node_path16 = require("path");
111711
- var import_picocolors22 = __toESM(require_picocolors(), 1);
112029
+ var import_node_fs16 = require("fs");
112030
+ var import_node_path17 = require("path");
112031
+ var import_picocolors23 = __toESM(require_picocolors(), 1);
111712
112032
  async function runPostmortem(id, opts) {
111713
112033
  const log = opts.write ?? ((line2) => console.log(line2));
111714
112034
  let sql2;
@@ -111722,11 +112042,11 @@ async function runPostmortem(id, opts) {
111722
112042
  const { db } = conn;
111723
112043
  const row = await getInvestigation(db, id);
111724
112044
  if (!row) {
111725
- log(import_picocolors22.default.red("No investigation found: " + id));
112045
+ log(import_picocolors23.default.red("No investigation found: " + id));
111726
112046
  return 1;
111727
112047
  }
111728
112048
  if (!row.report) {
111729
- log(import_picocolors22.default.red("Investigation " + id + " has no stored report."));
112049
+ log(import_picocolors23.default.red("Investigation " + id + " has no stored report."));
111730
112050
  return 1;
111731
112051
  }
111732
112052
  report = migrateReport(row.report);
@@ -111818,25 +112138,25 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
111818
112138
  }
111819
112139
  }
111820
112140
  if (opts.output) {
111821
- const outputPath = (0, import_node_path16.resolve)(opts.output);
111822
- if ((0, import_node_fs15.existsSync)(outputPath) && !opts.force) {
111823
- log(import_picocolors22.default.red("\u2717") + " " + outputPath + " already exists");
111824
- log(import_picocolors22.default.dim(" pass --force to overwrite"));
112141
+ const outputPath = (0, import_node_path17.resolve)(opts.output);
112142
+ if ((0, import_node_fs16.existsSync)(outputPath) && !opts.force) {
112143
+ log(import_picocolors23.default.red("\u2717") + " " + outputPath + " already exists");
112144
+ log(import_picocolors23.default.dim(" pass --force to overwrite"));
111825
112145
  return 1;
111826
112146
  }
111827
112147
  try {
111828
- (0, import_node_fs15.writeFileSync)(outputPath, content, "utf8");
112148
+ (0, import_node_fs16.writeFileSync)(outputPath, content, "utf8");
111829
112149
  } catch (err2) {
111830
- log(import_picocolors22.default.red("\u2717 Could not write " + outputPath + ": ") + err2.message);
112150
+ log(import_picocolors23.default.red("\u2717 Could not write " + outputPath + ": ") + err2.message);
111831
112151
  return 1;
111832
112152
  }
111833
- log(import_picocolors22.default.green("\u2713") + " Saved postmortem to " + outputPath);
112153
+ log(import_picocolors23.default.green("\u2713") + " Saved postmortem to " + outputPath);
111834
112154
  } else {
111835
112155
  log(content);
111836
112156
  }
111837
112157
  } catch (err2) {
111838
112158
  const message = err2 instanceof Error ? err2.message : String(err2);
111839
- log(import_picocolors22.default.red("postmortem failed: " + message));
112159
+ log(import_picocolors23.default.red("postmortem failed: " + message));
111840
112160
  return 1;
111841
112161
  } finally {
111842
112162
  if (sql2) {
@@ -111848,7 +112168,7 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
111848
112168
 
111849
112169
  // ../../packages/cli/src/commands/score.ts
111850
112170
  init_cjs_shims();
111851
- var import_picocolors23 = __toESM(require_picocolors(), 1);
112171
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
111852
112172
  var SCORE_AI_CONTRACT = `Provide a clearly separated AI score explanation with:
111853
112173
 
111854
112174
  Why this scored this way
@@ -111867,11 +112187,11 @@ async function runScore(id, opts) {
111867
112187
  try {
111868
112188
  const row = await getInvestigation(db, id);
111869
112189
  if (!row) {
111870
- console.error(import_picocolors23.default.red("No investigation found: " + id));
112190
+ console.error(import_picocolors24.default.red("No investigation found: " + id));
111871
112191
  return 1;
111872
112192
  }
111873
112193
  if (!row.report) {
111874
- console.error(import_picocolors23.default.red("Investigation " + id + " has no stored report."));
112194
+ console.error(import_picocolors24.default.red("Investigation " + id + " has no stored report."));
111875
112195
  return 1;
111876
112196
  }
111877
112197
  const s5 = scoreInvestigation(migrateReport(row.report));
@@ -111892,7 +112212,7 @@ async function runScore(id, opts) {
111892
112212
  });
111893
112213
  console.log("\n" + renderInterpretation(result));
111894
112214
  if (!result.ok) {
111895
- console.error(import_picocolors23.default.yellow(`[ai] ${result.warning}`));
112215
+ console.error(import_picocolors24.default.yellow(`[ai] ${result.warning}`));
111896
112216
  }
111897
112217
  }
111898
112218
  }
@@ -111932,7 +112252,7 @@ async function runScores(opts) {
111932
112252
 
111933
112253
  // ../../packages/cli/src/commands/ask.ts
111934
112254
  init_cjs_shims();
111935
- var import_picocolors24 = __toESM(require_picocolors(), 1);
112255
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
111936
112256
  function renderAnswer(report, directive, opts) {
111937
112257
  const answer = answerQuestion(report, directive);
111938
112258
  if (answer) {
@@ -111944,13 +112264,13 @@ function renderAnswer(report, directive, opts) {
111944
112264
  if (!opts.json && report.aiJudgment) {
111945
112265
  const j3 = report.aiJudgment;
111946
112266
  console.log("");
111947
- console.log(import_picocolors24.default.dim("\u2500".repeat(60)));
111948
- console.log(import_picocolors24.default.dim(`Stored AI judgment (${j3.provider}, ${j3.generatedAt}):`));
112267
+ console.log(import_picocolors25.default.dim("\u2500".repeat(60)));
112268
+ console.log(import_picocolors25.default.dim(`Stored AI judgment (${j3.provider}, ${j3.generatedAt}):`));
111949
112269
  if (j3.rootCauseAssessment) {
111950
- console.log(import_picocolors24.default.bold("Root cause (AI):"), j3.rootCauseAssessment.summary);
111951
- console.log(import_picocolors24.default.dim(`Uncertainty: ${j3.rootCauseAssessment.uncertainty}`));
112270
+ console.log(import_picocolors25.default.bold("Root cause (AI):"), j3.rootCauseAssessment.summary);
112271
+ console.log(import_picocolors25.default.dim(`Uncertainty: ${j3.rootCauseAssessment.uncertainty}`));
111952
112272
  } else {
111953
- console.log(import_picocolors24.default.bold("AI Why:"), j3.why);
112273
+ console.log(import_picocolors25.default.bold("AI Why:"), j3.why);
111954
112274
  }
111955
112275
  }
111956
112276
  return 0;
@@ -111979,8 +112299,8 @@ async function runAsk(id, directive, opts) {
111979
112299
  const session = authedClient();
111980
112300
  if (!session) {
111981
112301
  console.error(
111982
- import_picocolors24.default.red(
111983
- `This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors24.default.bold("horus login")} first.`
112302
+ import_picocolors25.default.red(
112303
+ `This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors25.default.bold("horus login")} first.`
111984
112304
  )
111985
112305
  );
111986
112306
  return 1;
@@ -111989,15 +112309,15 @@ async function runAsk(id, directive, opts) {
111989
112309
  const report = await fetchInvestigationReportFromCloud(session.client, cloudCfg, id);
111990
112310
  if (report) return renderAnswer(report, directive, opts);
111991
112311
  console.error(
111992
- import_picocolors24.default.red(
111993
- `Investigation ${id} has no saved report locally or in Horus Cloud. Run ${import_picocolors24.default.bold("horus investigate")} first.`
112312
+ import_picocolors25.default.red(
112313
+ `Investigation ${id} has no saved report locally or in Horus Cloud. Run ${import_picocolors25.default.bold("horus investigate")} first.`
111994
112314
  )
111995
112315
  );
111996
112316
  return 1;
111997
112317
  } catch (err2) {
111998
112318
  if (err2 instanceof CloudError && err2.status === 404) {
111999
112319
  console.error(
112000
- import_picocolors24.default.red(
112320
+ import_picocolors25.default.red(
112001
112321
  `No investigation found for "${id}" locally or in Horus Cloud. Use the id printed at the top of \`horus investigate\` output.`
112002
112322
  )
112003
112323
  );
@@ -112011,21 +112331,21 @@ async function runAsk(id, directive, opts) {
112011
112331
  case "found":
112012
112332
  return renderAnswer(local.report, directive, opts);
112013
112333
  case "no-report":
112014
- console.error(import_picocolors24.default.red("Investigation " + id + " has no stored report."));
112334
+ console.error(import_picocolors25.default.red("Investigation " + id + " has no stored report."));
112015
112335
  return 1;
112016
112336
  case "error":
112017
- console.error(import_picocolors24.default.red(local.message));
112337
+ console.error(import_picocolors25.default.red(local.message));
112018
112338
  return 1;
112019
112339
  case "not-found":
112020
112340
  default:
112021
- console.error(import_picocolors24.default.red("No investigation found: " + id));
112341
+ console.error(import_picocolors25.default.red("No investigation found: " + id));
112022
112342
  return 1;
112023
112343
  }
112024
112344
  }
112025
112345
 
112026
112346
  // ../../packages/cli/src/commands/onboard.ts
112027
112347
  init_cjs_shims();
112028
- var import_picocolors25 = __toESM(require_picocolors(), 1);
112348
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
112029
112349
  var ONBOARD_AI_CONTRACT = `Provide a clearly separated AI onboarding guide with:
112030
112350
 
112031
112351
  Start here
@@ -112053,7 +112373,7 @@ async function runOnboard(area, opts) {
112053
112373
  try {
112054
112374
  renv = resolveEnvironment(config2, { project: opts.repo });
112055
112375
  } catch (err2) {
112056
- console.error(import_picocolors25.default.red(err2.message));
112376
+ console.error(import_picocolors26.default.red(err2.message));
112057
112377
  return 1;
112058
112378
  }
112059
112379
  const repo = { name: renv.project, path: renv.path };
@@ -112061,7 +112381,7 @@ async function runOnboard(area, opts) {
112061
112381
  const health = await code.health();
112062
112382
  if (!health.ok) {
112063
112383
  console.error(
112064
- import_picocolors25.default.red("Source-intelligence host unreachable \u2014 run: horus index")
112384
+ import_picocolors26.default.red("Source-intelligence host unreachable \u2014 run: horus index")
112065
112385
  );
112066
112386
  return 1;
112067
112387
  }
@@ -112088,7 +112408,7 @@ async function runOnboard(area, opts) {
112088
112408
  });
112089
112409
  console.log("\n" + renderInterpretation(result));
112090
112410
  if (!result.ok) {
112091
- console.error(import_picocolors25.default.yellow(`[ai] ${result.warning}`));
112411
+ console.error(import_picocolors26.default.yellow(`[ai] ${result.warning}`));
112092
112412
  }
112093
112413
  }
112094
112414
  }
@@ -112097,14 +112417,14 @@ async function runOnboard(area, opts) {
112097
112417
  }
112098
112418
  return 0;
112099
112419
  } catch (err2) {
112100
- console.error(import_picocolors25.default.red(err2.message));
112420
+ console.error(import_picocolors26.default.red(err2.message));
112101
112421
  return 1;
112102
112422
  }
112103
112423
  }
112104
112424
 
112105
112425
  // ../../packages/cli/src/commands/simulate.ts
112106
112426
  init_cjs_shims();
112107
- var import_picocolors26 = __toESM(require_picocolors(), 1);
112427
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
112108
112428
  async function runSimulate(scenarioId, opts) {
112109
112429
  try {
112110
112430
  if (scenarioId === void 0) {
@@ -112114,7 +112434,7 @@ async function runSimulate(scenarioId, opts) {
112114
112434
  const scenario = getScenario(scenarioId);
112115
112435
  if (scenario === null) {
112116
112436
  console.error(
112117
- import_picocolors26.default.red(
112437
+ import_picocolors27.default.red(
112118
112438
  `Unknown scenario: "${scenarioId}". Run horus simulate to list available scenarios.`
112119
112439
  )
112120
112440
  );
@@ -112125,14 +112445,14 @@ async function runSimulate(scenarioId, opts) {
112125
112445
  try {
112126
112446
  renv = resolveEnvironment(config2, { project: opts.repo });
112127
112447
  } catch (err2) {
112128
- console.error(import_picocolors26.default.red(err2.message));
112448
+ console.error(import_picocolors27.default.red(err2.message));
112129
112449
  return 1;
112130
112450
  }
112131
112451
  const code = codeForRepo(config2, renv.project);
112132
112452
  const health = await code.health();
112133
112453
  if (!health.ok) {
112134
112454
  console.error(
112135
- import_picocolors26.default.red("Source-intelligence host unreachable \u2014 run: horus index")
112455
+ import_picocolors27.default.red("Source-intelligence host unreachable \u2014 run: horus index")
112136
112456
  );
112137
112457
  return 1;
112138
112458
  }
@@ -112149,14 +112469,14 @@ async function runSimulate(scenarioId, opts) {
112149
112469
  }
112150
112470
  return 0;
112151
112471
  } catch (err2) {
112152
- console.error(import_picocolors26.default.red(err2.message));
112472
+ console.error(import_picocolors27.default.red(err2.message));
112153
112473
  return 1;
112154
112474
  }
112155
112475
  }
112156
112476
 
112157
112477
  // ../../packages/cli/src/commands/logs.ts
112158
112478
  init_cjs_shims();
112159
- var import_picocolors27 = __toESM(require_picocolors(), 1);
112479
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
112160
112480
  var LOGS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
112161
112481
 
112162
112482
  Evidence used
@@ -112202,9 +112522,9 @@ function parseWhere(raw) {
112202
112522
  }
112203
112523
  var whereLabel = (where) => where.map((w4) => `${w4.field}=${w4.value}`).join(", ");
112204
112524
  function levelColor(level, text2) {
112205
- if (level === "error" || level === "fatal") return import_picocolors27.default.red(text2);
112206
- if (level === "warn") return import_picocolors27.default.yellow(text2);
112207
- return import_picocolors27.default.dim(text2);
112525
+ if (level === "error" || level === "fatal") return import_picocolors28.default.red(text2);
112526
+ if (level === "warn") return import_picocolors28.default.yellow(text2);
112527
+ return import_picocolors28.default.dim(text2);
112208
112528
  }
112209
112529
  function matchServiceLabel(requested, candidates) {
112210
112530
  if (candidates.includes(requested)) return { kind: "unique", matched: requested };
@@ -112220,7 +112540,7 @@ async function runLogs(service, opts) {
112220
112540
  try {
112221
112541
  where = parseWhere(opts.where);
112222
112542
  } catch (err2) {
112223
- console.error(import_picocolors27.default.red(err2.message));
112543
+ console.error(import_picocolors28.default.red(err2.message));
112224
112544
  return 1;
112225
112545
  }
112226
112546
  const hasWhere = where.length > 0;
@@ -112230,13 +112550,13 @@ async function runLogs(service, opts) {
112230
112550
  try {
112231
112551
  renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
112232
112552
  } catch (err2) {
112233
- console.error(import_picocolors27.default.red(err2.message));
112553
+ console.error(import_picocolors28.default.red(err2.message));
112234
112554
  return 1;
112235
112555
  }
112236
112556
  const logs = logsForEnv(renv);
112237
112557
  if (logs === null) {
112238
112558
  console.error(
112239
- import_picocolors27.default.red(
112559
+ import_picocolors28.default.red(
112240
112560
  `No Elasticsearch connector configured for ${renv.project}/${renv.env} (set ES_URL / ES_USERNAME / ES_PASSWORD)`
112241
112561
  )
112242
112562
  );
@@ -112244,7 +112564,7 @@ async function runLogs(service, opts) {
112244
112564
  }
112245
112565
  const health = await logs.health();
112246
112566
  if (!health.ok) {
112247
- console.error(import_picocolors27.default.red(`Elasticsearch unreachable: ${health.detail}`));
112567
+ console.error(import_picocolors28.default.red(`Elasticsearch unreachable: ${health.detail}`));
112248
112568
  return 1;
112249
112569
  }
112250
112570
  const resolvedService = service ?? renv.connectors.elasticsearch?.serviceName;
@@ -112257,21 +112577,21 @@ async function runLogs(service, opts) {
112257
112577
  requiresEventCode: aggregatesEventCode
112258
112578
  });
112259
112579
  for (const w4 of compat.issues.filter((i3) => i3.severity === "warning")) {
112260
- console.warn(import_picocolors27.default.yellow(`[warn] ${w4.field}: ${w4.message}`));
112580
+ console.warn(import_picocolors28.default.yellow(`[warn] ${w4.field}: ${w4.message}`));
112261
112581
  }
112262
112582
  const errors2 = compat.issues.filter((i3) => i3.severity === "error");
112263
112583
  if (errors2.length > 0) {
112264
- console.error(import_picocolors27.default.red("Elasticsearch field mapping is incompatible with the index:"));
112584
+ console.error(import_picocolors28.default.red("Elasticsearch field mapping is incompatible with the index:"));
112265
112585
  for (const e of errors2) {
112266
- console.error(import_picocolors27.default.red(` ${e.field}: ${e.message}`));
112586
+ console.error(import_picocolors28.default.red(` ${e.field}: ${e.message}`));
112267
112587
  }
112268
112588
  console.error(
112269
- import_picocolors27.default.dim("Fix fields.* overrides in your connector config or choose the correct preset.")
112589
+ import_picocolors28.default.dim("Fix fields.* overrides in your connector config or choose the correct preset.")
112270
112590
  );
112271
112591
  return 1;
112272
112592
  }
112273
112593
  } catch {
112274
- console.warn(import_picocolors27.default.dim("[warn] Elasticsearch compatibility check unavailable \u2014 proceeding."));
112594
+ console.warn(import_picocolors28.default.dim("[warn] Elasticsearch compatibility check unavailable \u2014 proceeding."));
112275
112595
  }
112276
112596
  const from = sinceToIso(opts.since) ?? new Date(Date.now() - 7 * 864e5).toISOString();
112277
112597
  const fromDisplay = from.slice(0, 16).replace("T", " ");
@@ -112327,12 +112647,12 @@ async function runLogs(service, opts) {
112327
112647
  return 0;
112328
112648
  }
112329
112649
  console.error(
112330
- import_picocolors27.default.dim(
112650
+ import_picocolors28.default.dim(
112331
112651
  `logs: ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + ` \xB7 from ${fromDisplay} UTC \xB7 level ${rawLevel ?? "all"}` + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (rawLevel === "error" && !hasWhere ? " (error+ only \u2014 use --all-levels for info/debug)" : "")
112332
112652
  )
112333
112653
  );
112334
112654
  if (records.length === 0) {
112335
- console.log(import_picocolors27.default.dim("No logs matched."));
112655
+ console.log(import_picocolors28.default.dim("No logs matched."));
112336
112656
  return 0;
112337
112657
  }
112338
112658
  for (const r of records) {
@@ -112342,12 +112662,12 @@ async function runLogs(service, opts) {
112342
112662
  const line2 = `${ts} ${lvl} ${comp} ${r.message.slice(0, 120)}`;
112343
112663
  console.log(levelColor(r.level, line2));
112344
112664
  for (const { key, value } of extractContextFields(r)) {
112345
- console.log(import_picocolors27.default.dim(` ${key}: ${value.slice(0, 200)}`));
112665
+ console.log(import_picocolors28.default.dim(` ${key}: ${value.slice(0, 200)}`));
112346
112666
  }
112347
112667
  }
112348
112668
  if (truncated) {
112349
112669
  console.error(
112350
- import_picocolors27.default.yellow(
112670
+ import_picocolors28.default.yellow(
112351
112671
  `[warn] showing the first ${limit} record(s) \u2014 results may be truncated. Narrow with --where/--since/--level or raise --limit (max 1000).`
112352
112672
  )
112353
112673
  );
@@ -112390,22 +112710,22 @@ async function runLogs(service, opts) {
112390
112710
  return 0;
112391
112711
  }
112392
112712
  console.log(
112393
- import_picocolors27.default.bold(`Top ${field}`) + import_picocolors27.default.dim(
112713
+ import_picocolors28.default.bold(`Top ${field}`) + import_picocolors28.default.dim(
112394
112714
  ` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
112395
112715
  )
112396
112716
  );
112397
- console.log(import_picocolors27.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
112717
+ console.log(import_picocolors28.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
112398
112718
  console.log("");
112399
112719
  if (buckets.length === 0) {
112400
- console.log(import_picocolors27.default.dim(` No error logs with field "${field}" in the window.`));
112401
- console.log(import_picocolors27.default.dim(` Tip: ensure "${field}" is an aggregatable (keyword) field.`));
112720
+ console.log(import_picocolors28.default.dim(` No error logs with field "${field}" in the window.`));
112721
+ console.log(import_picocolors28.default.dim(` Tip: ensure "${field}" is an aggregatable (keyword) field.`));
112402
112722
  return 0;
112403
112723
  }
112404
112724
  for (const b5 of buckets) {
112405
- console.log(` ${import_picocolors27.default.red(String(b5.count).padStart(6))} ${b5.key}`);
112725
+ console.log(` ${import_picocolors28.default.red(String(b5.count).padStart(6))} ${b5.key}`);
112406
112726
  }
112407
112727
  console.log("");
112408
- console.log(import_picocolors27.default.dim(` ${buckets.length} distinct ${field} value(s) shown (top by error count).`));
112728
+ console.log(import_picocolors28.default.dim(` ${buckets.length} distinct ${field} value(s) shown (top by error count).`));
112409
112729
  return 0;
112410
112730
  }
112411
112731
  let analysis = await logs.analyzeErrors({
@@ -112475,46 +112795,46 @@ async function runLogs(service, opts) {
112475
112795
  return 0;
112476
112796
  }
112477
112797
  console.log(
112478
- import_picocolors27.default.bold(`Error analysis`) + import_picocolors27.default.dim(
112798
+ import_picocolors28.default.bold(`Error analysis`) + import_picocolors28.default.dim(
112479
112799
  ` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
112480
112800
  )
112481
112801
  );
112482
- console.log(import_picocolors27.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
112802
+ console.log(import_picocolors28.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
112483
112803
  if (broadeningNote) {
112484
- console.log(import_picocolors27.default.yellow(` ${broadeningNote}`));
112804
+ console.log(import_picocolors28.default.yellow(` ${broadeningNote}`));
112485
112805
  }
112486
112806
  console.log(
112487
- ` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${import_picocolors27.default.yellow(String(analysis.newSignatures.length))} new`
112807
+ ` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${import_picocolors28.default.yellow(String(analysis.newSignatures.length))} new`
112488
112808
  );
112489
112809
  console.log("");
112490
112810
  if (analysis.signatures.length === 0) {
112491
- console.log(import_picocolors27.default.dim(" No error-level logs in the window."));
112811
+ console.log(import_picocolors28.default.dim(" No error-level logs in the window."));
112492
112812
  console.log(
112493
- import_picocolors27.default.dim(
112813
+ import_picocolors28.default.dim(
112494
112814
  ` Searched: ${scopeService ? `service "${scopeService}"` : "all services"} \xB7 index: ${indexPattern} \xB7 from: ${fromDisplay} UTC`
112495
112815
  )
112496
112816
  );
112497
- console.log(import_picocolors27.default.dim(` Tip: use --since to widen the window (e.g. --since 30d).`));
112817
+ console.log(import_picocolors28.default.dim(` Tip: use --since to widen the window (e.g. --since 30d).`));
112498
112818
  return 0;
112499
112819
  }
112500
112820
  for (const s5 of analysis.signatures) {
112501
112821
  const tags = [];
112502
- if (s5.isNew) tags.push(import_picocolors27.default.red("NEW"));
112822
+ if (s5.isNew) tags.push(import_picocolors28.default.red("NEW"));
112503
112823
  else if (s5.ratio !== void 0 && Number.isFinite(s5.ratio) && s5.ratio >= 1.5) {
112504
- tags.push(import_picocolors27.default.yellow(`spike x${s5.ratio.toFixed(1)}`));
112824
+ tags.push(import_picocolors28.default.yellow(`spike x${s5.ratio.toFixed(1)}`));
112505
112825
  }
112506
- const svc = s5.services.length > 0 ? import_picocolors27.default.dim(` \xB7 ${s5.services.slice(0, 3).join(", ")}`) : "";
112826
+ const svc = s5.services.length > 0 ? import_picocolors28.default.dim(` \xB7 ${s5.services.slice(0, 3).join(", ")}`) : "";
112507
112827
  const tagStr = tags.length > 0 ? ` [${tags.join(", ")}]` : "";
112508
112828
  console.log(
112509
- ` ${import_picocolors27.default.red(String(s5.count).padStart(6))} ${import_picocolors27.default.bold(s5.key.padEnd(14).slice(0, 14))} ` + import_picocolors27.default.dim(`first ${shortTs(s5.firstSeen)} \xB7 last ${shortTs(s5.lastSeen)}`) + svc + tagStr
112829
+ ` ${import_picocolors28.default.red(String(s5.count).padStart(6))} ${import_picocolors28.default.bold(s5.key.padEnd(14).slice(0, 14))} ` + import_picocolors28.default.dim(`first ${shortTs(s5.firstSeen)} \xB7 last ${shortTs(s5.lastSeen)}`) + svc + tagStr
112510
112830
  );
112511
112831
  }
112512
112832
  if (analysis.affectedServices.length > 0) {
112513
112833
  console.log("");
112514
- console.log(` ${import_picocolors27.default.bold("Affected services:")} ${analysis.affectedServices.join(", ")}`);
112834
+ console.log(` ${import_picocolors28.default.bold("Affected services:")} ${analysis.affectedServices.join(", ")}`);
112515
112835
  }
112516
112836
  console.log("");
112517
- console.log(import_picocolors27.default.dim(" (use --raw to see individual log lines)"));
112837
+ console.log(import_picocolors28.default.dim(" (use --raw to see individual log lines)"));
112518
112838
  if (opts.ai) {
112519
112839
  const result = await renderAiInterpretation({
112520
112840
  command: "logs",
@@ -112533,19 +112853,19 @@ async function runLogs(service, opts) {
112533
112853
  });
112534
112854
  console.log("\n" + renderInterpretation(result));
112535
112855
  if (!result.ok) {
112536
- console.error(import_picocolors27.default.yellow(`[ai] ${result.warning}`));
112856
+ console.error(import_picocolors28.default.yellow(`[ai] ${result.warning}`));
112537
112857
  }
112538
112858
  }
112539
112859
  return 0;
112540
112860
  } catch (err2) {
112541
- console.error(import_picocolors27.default.red(err2.message));
112861
+ console.error(import_picocolors28.default.red(err2.message));
112542
112862
  return 1;
112543
112863
  }
112544
112864
  }
112545
112865
 
112546
112866
  // ../../packages/cli/src/commands/metrics.ts
112547
112867
  init_cjs_shims();
112548
- var import_picocolors28 = __toESM(require_picocolors(), 1);
112868
+ var import_picocolors29 = __toESM(require_picocolors(), 1);
112549
112869
  var METRICS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
112550
112870
 
112551
112871
  Evidence used
@@ -112590,10 +112910,10 @@ function anomalyColor(anomaly) {
112590
112910
  case "latency-spike":
112591
112911
  case "error-rate-change":
112592
112912
  case "throughput-drop":
112593
- return import_picocolors28.default.red;
112913
+ return import_picocolors29.default.red;
112594
112914
  case "queue-growth":
112595
112915
  case "change":
112596
- return import_picocolors28.default.yellow;
112916
+ return import_picocolors29.default.yellow;
112597
112917
  default:
112598
112918
  return (s5) => s5;
112599
112919
  }
@@ -112620,7 +112940,7 @@ async function runMetrics(hint, opts) {
112620
112940
  const metrics = metricsProviderFromConfig(config2);
112621
112941
  if (metrics === null) {
112622
112942
  console.error(
112623
- import_picocolors28.default.red(
112943
+ import_picocolors29.default.red(
112624
112944
  "Grafana not configured \u2014 set GRAFANA_URL + GRAFANA_USER + GRAFANA_PASSWORD"
112625
112945
  )
112626
112946
  );
@@ -112628,7 +112948,7 @@ async function runMetrics(hint, opts) {
112628
112948
  }
112629
112949
  const health = await metrics.health();
112630
112950
  if (!health.ok) {
112631
- console.error(import_picocolors28.default.red(`Grafana unreachable: ${health.detail}`));
112951
+ console.error(import_picocolors29.default.red(`Grafana unreachable: ${health.detail}`));
112632
112952
  return 1;
112633
112953
  }
112634
112954
  const stepNum = opts.step !== void 0 ? Number(opts.step) : void 0;
@@ -112657,14 +112977,14 @@ async function runMetrics(hint, opts) {
112657
112977
  return 0;
112658
112978
  }
112659
112979
  if (series.length === 0) {
112660
- console.log(import_picocolors28.default.dim("No series returned."));
112980
+ console.log(import_picocolors29.default.dim("No series returned."));
112661
112981
  return 0;
112662
112982
  }
112663
112983
  for (const s5 of series) {
112664
112984
  const summary = summarize(s5);
112665
112985
  const label = labelStr(s5.labels);
112666
112986
  console.log(
112667
- ` ${import_picocolors28.default.cyan(label || "(no labels)")} last=${import_picocolors28.default.bold(fmtNum(summary.last))} avg=${fmtNum(summary.avg)}`
112987
+ ` ${import_picocolors29.default.cyan(label || "(no labels)")} last=${import_picocolors29.default.bold(fmtNum(summary.last))} avg=${fmtNum(summary.avg)}`
112668
112988
  );
112669
112989
  }
112670
112990
  return 0;
@@ -112679,36 +112999,36 @@ async function runMetrics(hint, opts) {
112679
112999
  if (findings2.length === 0) {
112680
113000
  if (hint !== void 0) {
112681
113001
  const tokens = extractHintTokens(hint);
112682
- console.log(import_picocolors28.default.dim(`No panels matched hint "${hint}".`));
113002
+ console.log(import_picocolors29.default.dim(`No panels matched hint "${hint}".`));
112683
113003
  if (tokens.length > 0) {
112684
- console.log(import_picocolors28.default.dim(` Searched for panels containing: ${tokens.join(", ")}`));
113004
+ console.log(import_picocolors29.default.dim(` Searched for panels containing: ${tokens.join(", ")}`));
112685
113005
  }
112686
113006
  console.log(
112687
- import_picocolors28.default.dim(
112688
- ` Tip: run ${import_picocolors28.default.bold("horus metrics")} (no hint) to see all available panels, then use a panel title or keyword directly.`
113007
+ import_picocolors29.default.dim(
113008
+ ` Tip: run ${import_picocolors29.default.bold("horus metrics")} (no hint) to see all available panels, then use a panel title or keyword directly.`
112689
113009
  )
112690
113010
  );
112691
113011
  } else {
112692
- console.log(import_picocolors28.default.dim("No panels found in configured Grafana dashboards."));
113012
+ console.log(import_picocolors29.default.dim("No panels found in configured Grafana dashboards."));
112693
113013
  }
112694
113014
  return 0;
112695
113015
  }
112696
113016
  const hintSuffix = hint !== void 0 ? ` (hint: "${hint}")` : "";
112697
113017
  const matchSources = hint !== void 0 ? [...new Set(findings2.map((f5) => f5.matchSource).filter(Boolean))] : [];
112698
- const matchSuffix = matchSources.length > 0 ? import_picocolors28.default.dim(` [matched via ${matchSources.join(", ")}]`) : "";
113018
+ const matchSuffix = matchSources.length > 0 ? import_picocolors29.default.dim(` [matched via ${matchSources.join(", ")}]`) : "";
112699
113019
  console.log(
112700
- import_picocolors28.default.bold(
113020
+ import_picocolors29.default.bold(
112701
113021
  `Grafana metrics${hintSuffix} \u2014 ${findings2.length} series across panels`
112702
113022
  ) + matchSuffix
112703
113023
  );
112704
113024
  console.log(
112705
- import_picocolors28.default.dim(
113025
+ import_picocolors29.default.dim(
112706
113026
  ` window: ${new Date(from * 1e3).toISOString()} \u2013 ${new Date(to * 1e3).toISOString()}`
112707
113027
  )
112708
113028
  );
112709
113029
  console.log("");
112710
113030
  if (flagged.length === 0) {
112711
- console.log(import_picocolors28.default.green(" No anomalies detected."));
113031
+ console.log(import_picocolors29.default.green(" No anomalies detected."));
112712
113032
  } else {
112713
113033
  for (const f5 of flagged) {
112714
113034
  const color = anomalyColor(f5.anomaly);
@@ -112717,14 +113037,14 @@ async function runMetrics(hint, opts) {
112717
113037
  const lblStr = labelStr(f5.labels);
112718
113038
  console.log(
112719
113039
  color(
112720
- ` ${label.padEnd(22)} ${f5.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f5.baselineAvg)} -> ${fmtNum(f5.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${import_picocolors28.default.dim(lblStr)}` : "")
113040
+ ` ${label.padEnd(22)} ${f5.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f5.baselineAvg)} -> ${fmtNum(f5.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${import_picocolors29.default.dim(lblStr)}` : "")
112721
113041
  )
112722
113042
  );
112723
113043
  }
112724
113044
  }
112725
113045
  if (ok.length > 0) {
112726
113046
  console.log("");
112727
- console.log(import_picocolors28.default.dim(` ${ok.length} panel series with no anomaly.`));
113047
+ console.log(import_picocolors29.default.dim(` ${ok.length} panel series with no anomaly.`));
112728
113048
  }
112729
113049
  if (opts.ai) {
112730
113050
  const result = await renderAiInterpretation({
@@ -112739,19 +113059,19 @@ async function runMetrics(hint, opts) {
112739
113059
  });
112740
113060
  console.log("\n" + renderInterpretation(result));
112741
113061
  if (!result.ok) {
112742
- console.error(import_picocolors28.default.yellow(`[ai] ${result.warning}`));
113062
+ console.error(import_picocolors29.default.yellow(`[ai] ${result.warning}`));
112743
113063
  }
112744
113064
  }
112745
113065
  return 0;
112746
113066
  } catch (err2) {
112747
- console.error(import_picocolors28.default.red(err2.message));
113067
+ console.error(import_picocolors29.default.red(err2.message));
112748
113068
  return 1;
112749
113069
  }
112750
113070
  }
112751
113071
 
112752
113072
  // ../../packages/cli/src/commands/state.ts
112753
113073
  init_cjs_shims();
112754
- var import_picocolors29 = __toESM(require_picocolors(), 1);
113074
+ var import_picocolors30 = __toESM(require_picocolors(), 1);
112755
113075
  var STATE_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
112756
113076
 
112757
113077
  Evidence used
@@ -112777,13 +113097,13 @@ async function runState(opts) {
112777
113097
  try {
112778
113098
  renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
112779
113099
  } catch (err2) {
112780
- console.error(import_picocolors29.default.red(err2.message));
113100
+ console.error(import_picocolors30.default.red(err2.message));
112781
113101
  return 1;
112782
113102
  }
112783
113103
  const mongo = mongoForEnv(renv);
112784
113104
  if (mongo === null) {
112785
113105
  console.error(
112786
- import_picocolors29.default.red(
113106
+ import_picocolors30.default.red(
112787
113107
  `No MongoDB connector configured for ${renv.project}/${renv.env} (set the project's Mongo URL env var).`
112788
113108
  )
112789
113109
  );
@@ -112791,7 +113111,7 @@ async function runState(opts) {
112791
113111
  }
112792
113112
  const health = await mongo.health();
112793
113113
  if (!health.ok) {
112794
- console.error(import_picocolors29.default.red(`MongoDB unreachable: ${health.detail}`));
113114
+ console.error(import_picocolors30.default.red(`MongoDB unreachable: ${health.detail}`));
112795
113115
  await mongo.close();
112796
113116
  return 1;
112797
113117
  }
@@ -112817,38 +113137,38 @@ async function runState(opts) {
112817
113137
  );
112818
113138
  return 0;
112819
113139
  }
112820
- const discoveryNote = analysis.autoDiscovered ? import_picocolors29.default.dim(` (${analysis.collections.length} collections, auto-discovered)`) : "";
113140
+ const discoveryNote = analysis.autoDiscovered ? import_picocolors30.default.dim(` (${analysis.collections.length} collections, auto-discovered)`) : "";
112821
113141
  console.log(
112822
- import_picocolors29.default.bold("State analysis") + import_picocolors29.default.dim(` \u2014 ${renv.project}/${renv.env} \xB7 db ${analysis.database}`) + discoveryNote
113142
+ import_picocolors30.default.bold("State analysis") + import_picocolors30.default.dim(` \u2014 ${renv.project}/${renv.env} \xB7 db ${analysis.database}`) + discoveryNote
112823
113143
  );
112824
113144
  console.log("");
112825
113145
  let signals = 0;
112826
113146
  for (const c4 of analysis.collections) {
112827
113147
  const flags2 = [];
112828
113148
  if (c4.isStale === true) {
112829
- flags2.push(import_picocolors29.default.yellow(`stale (last ${c4.dateField}, ~${Math.round(c4.ageHours ?? 0)}h)`));
113149
+ flags2.push(import_picocolors30.default.yellow(`stale (last ${c4.dateField}, ~${Math.round(c4.ageHours ?? 0)}h)`));
112830
113150
  }
112831
113151
  for (const a2 of c4.anomalies) {
112832
- flags2.push(import_picocolors29.default.red(`${a2.count}\xD7 "${a2.value}"`));
113152
+ flags2.push(import_picocolors30.default.red(`${a2.count}\xD7 "${a2.value}"`));
112833
113153
  signals += 1;
112834
113154
  }
112835
113155
  if (c4.isStale === true) signals += 1;
112836
- const head = ` ${import_picocolors29.default.bold(c4.collection.padEnd(18).slice(0, 18))} ${import_picocolors29.default.dim(String(c4.count).padStart(8) + " docs")}`;
112837
- console.log(flags2.length > 0 ? `${head} ${flags2.join(" ")}` : import_picocolors29.default.dim(head));
113156
+ const head = ` ${import_picocolors30.default.bold(c4.collection.padEnd(18).slice(0, 18))} ${import_picocolors30.default.dim(String(c4.count).padStart(8) + " docs")}`;
113157
+ console.log(flags2.length > 0 ? `${head} ${flags2.join(" ")}` : import_picocolors30.default.dim(head));
112838
113158
  }
112839
113159
  console.log("");
112840
113160
  if (analysis.collections.length === 0) {
112841
113161
  console.log(
112842
- import_picocolors29.default.yellow(" No collections discovered in this database.") + import_picocolors29.default.dim(" Verify the database name in your MongoDB connector config.")
113162
+ import_picocolors30.default.yellow(" No collections discovered in this database.") + import_picocolors30.default.dim(" Verify the database name in your MongoDB connector config.")
112843
113163
  );
112844
113164
  } else {
112845
113165
  const scope = analysis.autoDiscovered ? "collection(s)" : "allowlisted collection(s)";
112846
113166
  console.log(
112847
- signals > 0 ? ` ${import_picocolors29.default.bold(String(signals))} state signal(s) across ${analysis.collections.length} ${scope}` : import_picocolors29.default.dim(` No state anomalies across ${analysis.collections.length} ${scope}.`)
113167
+ signals > 0 ? ` ${import_picocolors30.default.bold(String(signals))} state signal(s) across ${analysis.collections.length} ${scope}` : import_picocolors30.default.dim(` No state anomalies across ${analysis.collections.length} ${scope}.`)
112848
113168
  );
112849
113169
  if (analysis.autoDiscovered) {
112850
113170
  console.log(
112851
- import_picocolors29.default.dim(
113171
+ import_picocolors30.default.dim(
112852
113172
  ` Tip: add a "collections" list to your MongoDB connector config to restrict analysis.`
112853
113173
  )
112854
113174
  );
@@ -112866,7 +113186,7 @@ async function runState(opts) {
112866
113186
  });
112867
113187
  console.log("\n" + renderInterpretation(result));
112868
113188
  if (!result.ok) {
112869
- console.error(import_picocolors29.default.yellow(`[ai] ${result.warning}`));
113189
+ console.error(import_picocolors30.default.yellow(`[ai] ${result.warning}`));
112870
113190
  }
112871
113191
  }
112872
113192
  return 0;
@@ -112874,20 +113194,20 @@ async function runState(opts) {
112874
113194
  await mongo.close();
112875
113195
  }
112876
113196
  } catch (err2) {
112877
- console.error(import_picocolors29.default.red(err2.message));
113197
+ console.error(import_picocolors30.default.red(err2.message));
112878
113198
  return 1;
112879
113199
  }
112880
113200
  }
112881
113201
 
112882
113202
  // ../../packages/cli/src/commands/init.ts
112883
113203
  init_cjs_shims();
112884
- var import_picocolors30 = __toESM(require_picocolors(), 1);
112885
- var import_node_path17 = require("path");
113204
+ var import_picocolors31 = __toESM(require_picocolors(), 1);
113205
+ var import_node_path18 = require("path");
112886
113206
  async function runInit(opts) {
112887
113207
  try {
112888
113208
  const cwd = process.cwd();
112889
- const root = opts.path ? (0, import_node_path17.resolve)(opts.path) : findRepoRoot(cwd) ?? cwd;
112890
- const name2 = opts.name ?? (0, import_node_path17.basename)(root);
113209
+ const root = opts.path ? (0, import_node_path18.resolve)(opts.path) : findRepoRoot(cwd) ?? cwd;
113210
+ const name2 = opts.name ?? (0, import_node_path18.basename)(root);
112891
113211
  const envName = opts.env ?? "production";
112892
113212
  const repo = { name: name2, path: root };
112893
113213
  if (opts.source) repo["source"] = { hostUrl: opts.source };
@@ -112902,65 +113222,65 @@ async function runInit(opts) {
112902
113222
  const configPath = writeLocalConfig(root, file);
112903
113223
  registerProject(name2, root, configPath);
112904
113224
  ensureProjectGitignore(root);
112905
- console.log(`${import_picocolors30.default.green("\u2713")} Initialized Horus project ${import_picocolors30.default.bold(name2)}`);
112906
- console.log(import_picocolors30.default.dim(` config: ${configPath}`));
112907
- console.log(import_picocolors30.default.dim(` registered: horus investigate --name ${name2} "<hint>"`));
113225
+ console.log(`${import_picocolors31.default.green("\u2713")} Initialized Horus project ${import_picocolors31.default.bold(name2)}`);
113226
+ console.log(import_picocolors31.default.dim(` config: ${configPath}`));
113227
+ console.log(import_picocolors31.default.dim(` registered: horus investigate --name ${name2} "<hint>"`));
112908
113228
  if (!opts.source) {
112909
113229
  console.log(
112910
- import_picocolors30.default.dim(" no source-intelligence host set \u2014 run `horus index` to analyze + host, or pass --source <url>")
113230
+ import_picocolors31.default.dim(" no source-intelligence host set \u2014 run `horus index` to analyze + host, or pass --source <url>")
112911
113231
  );
112912
113232
  }
112913
113233
  console.log(
112914
- import_picocolors30.default.dim(" add runtime connectors (elasticsearch/mongodb/grafana) to .horus/config.json")
113234
+ import_picocolors31.default.dim(" add runtime connectors (elasticsearch/mongodb/grafana) to .horus/config.json")
112915
113235
  );
112916
113236
  console.log(
112917
- import_picocolors30.default.dim(" .horus/ is gitignored \u2014 local config and runtime state stay out of version control")
113237
+ import_picocolors31.default.dim(" .horus/ is gitignored \u2014 local config and runtime state stay out of version control")
112918
113238
  );
112919
113239
  return 0;
112920
113240
  } catch (err2) {
112921
- console.error(import_picocolors30.default.red(err2.message));
113241
+ console.error(import_picocolors31.default.red(err2.message));
112922
113242
  return 1;
112923
113243
  }
112924
113244
  }
112925
113245
 
112926
113246
  // ../../packages/cli/src/commands/projects.ts
112927
113247
  init_cjs_shims();
112928
- var import_picocolors31 = __toESM(require_picocolors(), 1);
113248
+ var import_picocolors32 = __toESM(require_picocolors(), 1);
112929
113249
  async function runProjects() {
112930
113250
  const reg = readRegistry();
112931
113251
  const names = Object.keys(reg.projects).sort();
112932
113252
  if (names.length === 0) {
112933
113253
  console.log(
112934
- import_picocolors31.default.dim("No registered projects. Run `horus index` (or `horus init`) inside a repo.")
113254
+ import_picocolors32.default.dim("No registered projects. Run `horus index` (or `horus init`) inside a repo.")
112935
113255
  );
112936
113256
  return 0;
112937
113257
  }
112938
- console.log(import_picocolors31.default.bold("Registered Horus projects"));
113258
+ console.log(import_picocolors32.default.bold("Registered Horus projects"));
112939
113259
  console.log("");
112940
113260
  for (const name2 of names) {
112941
113261
  const entry2 = reg.projects[name2];
112942
113262
  if (entry2 === void 0) continue;
112943
- console.log(` ${import_picocolors31.default.bold(name2.padEnd(22))} ${import_picocolors31.default.dim(entry2.root)}`);
113263
+ console.log(` ${import_picocolors32.default.bold(name2.padEnd(22))} ${import_picocolors32.default.dim(entry2.root)}`);
112944
113264
  }
112945
113265
  console.log("");
112946
- console.log(import_picocolors31.default.dim(' horus investigate --name <name> "<hint>"'));
113266
+ console.log(import_picocolors32.default.dim(' horus investigate --name <name> "<hint>"'));
112947
113267
  return 0;
112948
113268
  }
112949
113269
 
112950
113270
  // ../../packages/cli/src/commands/setup.ts
112951
113271
  init_cjs_shims();
112952
- var import_picocolors32 = __toESM(require_picocolors(), 1);
113272
+ var import_picocolors33 = __toESM(require_picocolors(), 1);
112953
113273
  var DEFAULT_DB_URL3 = "postgresql://horus:horus@localhost:5433/horus";
112954
113274
  async function runSetup(opts) {
112955
113275
  const write = opts.write ?? ((line2) => console.log(line2));
112956
113276
  let ok = true;
112957
- write(import_picocolors32.default.bold("\nHorus setup\n"));
113277
+ write(import_picocolors33.default.bold("\nHorus setup\n"));
112958
113278
  const backendVersion = await getSourceVersion();
112959
113279
  if (backendVersion === null) {
112960
113280
  ok = false;
112961
- write(` ${import_picocolors32.default.red("\u25CF")} Horus source-intelligence backend not found`);
113281
+ write(` ${import_picocolors33.default.red("\u25CF")} Horus source-intelligence backend not found`);
112962
113282
  write(
112963
- import_picocolors32.default.dim(
113283
+ import_picocolors33.default.dim(
112964
113284
  ` install it (Python 3.11+ required):
112965
113285
  curl -fsSL https://horus.sh/install.sh | bash
112966
113286
  ensure ~/.local/bin is on your PATH`
@@ -112969,17 +113289,17 @@ async function runSetup(opts) {
112969
113289
  } else if (backendVersion !== PINNED_SOURCE_VERSION) {
112970
113290
  ok = false;
112971
113291
  write(
112972
- ` ${import_picocolors32.default.yellow("\u25CF")} Horus source-intelligence backend version mismatch` + import_picocolors32.default.dim(` (installed: ${backendVersion}, required: ${PINNED_SOURCE_VERSION})`)
113292
+ ` ${import_picocolors33.default.yellow("\u25CF")} Horus source-intelligence backend version mismatch` + import_picocolors33.default.dim(` (installed: ${backendVersion}, required: ${PINNED_SOURCE_VERSION})`)
112973
113293
  );
112974
113294
  write(
112975
- import_picocolors32.default.dim(
113295
+ import_picocolors33.default.dim(
112976
113296
  ` update it:
112977
113297
  horus update`
112978
113298
  )
112979
113299
  );
112980
113300
  } else {
112981
113301
  write(
112982
- ` ${import_picocolors32.default.green("\u25CF")} Horus source-intelligence backend ` + import_picocolors32.default.dim(`(${backendVersion})`)
113302
+ ` ${import_picocolors33.default.green("\u25CF")} Horus source-intelligence backend ` + import_picocolors33.default.dim(`(${backendVersion})`)
112983
113303
  );
112984
113304
  }
112985
113305
  let dbUrl = process.env["DATABASE_URL"] ?? DEFAULT_DB_URL3;
@@ -112991,11 +113311,11 @@ async function runSetup(opts) {
112991
113311
  }
112992
113312
  const db = await checkDatabase(dbUrl);
112993
113313
  if (db.reachable) {
112994
- write(` ${import_picocolors32.default.green("\u25CF")} Postgres reachable ${import_picocolors32.default.dim(`(${db.schemaDetail})`)}`);
113314
+ write(` ${import_picocolors33.default.green("\u25CF")} Postgres reachable ${import_picocolors33.default.dim(`(${db.schemaDetail})`)}`);
112995
113315
  if (!db.schemaReady) {
112996
113316
  ok = false;
112997
113317
  write(
112998
- import_picocolors32.default.dim(
113318
+ import_picocolors33.default.dim(
112999
113319
  ` schema not applied \u2014 run migrations:
113000
113320
  from the Horus repo: pnpm db migrate
113001
113321
  or apply the schema directly: https://github.com/meritt-dev/horus/tree/master/packages/db/drizzle`
@@ -113004,9 +113324,9 @@ async function runSetup(opts) {
113004
113324
  }
113005
113325
  } else {
113006
113326
  ok = false;
113007
- write(` ${import_picocolors32.default.red("\u25CF")} Postgres unreachable`);
113327
+ write(` ${import_picocolors33.default.red("\u25CF")} Postgres unreachable`);
113008
113328
  write(
113009
- import_picocolors32.default.dim(
113329
+ import_picocolors33.default.dim(
113010
113330
  ` start a local instance:
113011
113331
  docker run -d --name horus-db \\
113012
113332
  -e POSTGRES_USER=horus -e POSTGRES_PASSWORD=horus -e POSTGRES_DB=horus \\
@@ -113031,10 +113351,10 @@ async function runSetup(opts) {
113031
113351
  if (!health.ok) {
113032
113352
  ok = false;
113033
113353
  write(
113034
- ` ${import_picocolors32.default.red("\u25CF")} Source intelligence host unreachable for ${import_picocolors32.default.bold(repo.name)} ` + import_picocolors32.default.dim(`(${repoHostUrl})`)
113354
+ ` ${import_picocolors33.default.red("\u25CF")} Source intelligence host unreachable for ${import_picocolors33.default.bold(repo.name)} ` + import_picocolors33.default.dim(`(${repoHostUrl})`)
113035
113355
  );
113036
113356
  write(
113037
- import_picocolors32.default.dim(
113357
+ import_picocolors33.default.dim(
113038
113358
  ` start the source intelligence host:
113039
113359
  cd ${repo.path}
113040
113360
  horus index`
@@ -113045,10 +113365,10 @@ async function runSetup(opts) {
113045
113365
  if (count === 0) {
113046
113366
  ok = false;
113047
113367
  write(
113048
- ` ${import_picocolors32.default.yellow("\u25CF")} Source intelligence host running but ${import_picocolors32.default.bold(repo.name)} is not indexed`
113368
+ ` ${import_picocolors33.default.yellow("\u25CF")} Source intelligence host running but ${import_picocolors33.default.bold(repo.name)} is not indexed`
113049
113369
  );
113050
113370
  write(
113051
- import_picocolors32.default.dim(
113371
+ import_picocolors33.default.dim(
113052
113372
  ` index the repo:
113053
113373
  cd ${repo.path}
113054
113374
  horus index`
@@ -113056,7 +113376,7 @@ async function runSetup(opts) {
113056
113376
  );
113057
113377
  } else {
113058
113378
  write(
113059
- ` ${import_picocolors32.default.green("\u25CF")} ${import_picocolors32.default.bold(repo.name)} \u2014 ${count} nodes indexed ` + import_picocolors32.default.dim(`(${repoHostUrl})`)
113379
+ ` ${import_picocolors33.default.green("\u25CF")} ${import_picocolors33.default.bold(repo.name)} \u2014 ${count} nodes indexed ` + import_picocolors33.default.dim(`(${repoHostUrl})`)
113060
113380
  );
113061
113381
  }
113062
113382
  }
@@ -113066,10 +113386,10 @@ async function runSetup(opts) {
113066
113386
  write("");
113067
113387
  if (ok) {
113068
113388
  write(
113069
- import_picocolors32.default.green("Ready.") + ' Next: `cd` into a repo and run `horus index`, then `horus investigate "<hint>"`.'
113389
+ import_picocolors33.default.green("Ready.") + ' Next: `cd` into a repo and run `horus index`, then `horus investigate "<hint>"`.'
113070
113390
  );
113071
113391
  } else {
113072
- write(import_picocolors32.default.yellow("Resolve the items above, then re-run `horus setup`."));
113392
+ write(import_picocolors33.default.yellow("Resolve the items above, then re-run `horus setup`."));
113073
113393
  }
113074
113394
  return ok ? 0 : 1;
113075
113395
  }
@@ -113077,13 +113397,13 @@ async function runSetup(opts) {
113077
113397
  // ../../packages/cli/src/commands/connect.ts
113078
113398
  init_cjs_shims();
113079
113399
  var import_node_readline = require("readline");
113080
- var import_node_child_process6 = require("child_process");
113081
- var import_picocolors35 = __toESM(require_picocolors(), 1);
113400
+ var import_node_child_process7 = require("child_process");
113401
+ var import_picocolors36 = __toESM(require_picocolors(), 1);
113082
113402
 
113083
113403
  // ../../packages/cli/src/lib/tty-selector.ts
113084
113404
  init_cjs_shims();
113085
113405
  var import_node_tty = require("tty");
113086
- var import_picocolors33 = __toESM(require_picocolors(), 1);
113406
+ var import_picocolors34 = __toESM(require_picocolors(), 1);
113087
113407
  var ExitPromptError = class extends Error {
113088
113408
  constructor(message = "Prompt was cancelled") {
113089
113409
  super(message);
@@ -113139,7 +113459,7 @@ async function checkboxSearch(opts) {
113139
113459
  clear();
113140
113460
  const lines = [];
113141
113461
  lines.push(
113142
- `? ${import_picocolors33.default.bold(message)} ${import_picocolors33.default.dim("(\u2191\u2193 navigate \u2022 space toggle \u2022 enter confirm \u2022 type filter)")}`
113462
+ `? ${import_picocolors34.default.bold(message)} ${import_picocolors34.default.dim("(\u2191\u2193 navigate \u2022 space toggle \u2022 enter confirm \u2022 type filter)")}`
113143
113463
  );
113144
113464
  const visible = filtered.slice(visibleOffset, visibleOffset + pageSize);
113145
113465
  for (let i3 = 0; i3 < visible.length; i3++) {
@@ -113147,29 +113467,29 @@ async function checkboxSearch(opts) {
113147
113467
  const choice = visible[i3];
113148
113468
  const isCursor = idx === cursor;
113149
113469
  const isSelected = selected.has(choice);
113150
- const marker = isSelected ? import_picocolors33.default.green("\u25CF") : " ";
113470
+ const marker = isSelected ? import_picocolors34.default.green("\u25CF") : " ";
113151
113471
  const prefix = `[${marker}]`;
113152
- const label = isCursor ? import_picocolors33.default.cyan(choice) : choice;
113153
- const pointer = isCursor ? import_picocolors33.default.cyan("\u276F") : " ";
113472
+ const label = isCursor ? import_picocolors34.default.cyan(choice) : choice;
113473
+ const pointer = isCursor ? import_picocolors34.default.cyan("\u276F") : " ";
113154
113474
  lines.push(` ${pointer} ${prefix} ${label}`);
113155
113475
  }
113156
113476
  if (filtered.length === 0) {
113157
- lines.push(import_picocolors33.default.dim(" No matches"));
113477
+ lines.push(import_picocolors34.default.dim(" No matches"));
113158
113478
  }
113159
113479
  const statusParts = [
113160
113480
  `${selected.size} selected`,
113161
113481
  `${filtered.length}/${choices.length} matches`
113162
113482
  ];
113163
113483
  if (filter) statusParts.push(`filter: ${filter}`);
113164
- lines.push(import_picocolors33.default.dim(` ${statusParts.join(" \xB7 ")}`));
113484
+ lines.push(import_picocolors34.default.dim(` ${statusParts.join(" \xB7 ")}`));
113165
113485
  output.write(lines.join("\n"));
113166
113486
  lastRenderLines = lines.length;
113167
113487
  }
113168
113488
  function finish(values2) {
113169
113489
  cleanup();
113170
- const summary = values2.length > 0 ? values2.join(", ") : import_picocolors33.default.dim("none");
113490
+ const summary = values2.length > 0 ? values2.join(", ") : import_picocolors34.default.dim("none");
113171
113491
  output.write(`
113172
- ? ${import_picocolors33.default.bold(message)} ${summary}
113492
+ ? ${import_picocolors34.default.bold(message)} ${summary}
113173
113493
  `);
113174
113494
  resolve9(values2);
113175
113495
  }
@@ -113260,10 +113580,10 @@ async function checkboxSearch(opts) {
113260
113580
 
113261
113581
  // ../../packages/cli/src/commands/connect-ai.ts
113262
113582
  init_cjs_shims();
113263
- var import_node_child_process5 = require("child_process");
113264
- var import_picocolors34 = __toESM(require_picocolors(), 1);
113583
+ var import_node_child_process6 = require("child_process");
113584
+ var import_picocolors35 = __toESM(require_picocolors(), 1);
113265
113585
  function cliInstalled(id) {
113266
- const r = (0, import_node_child_process5.spawnSync)(id, ["--version"], { stdio: "ignore", timeout: 2e3 });
113586
+ const r = (0, import_node_child_process6.spawnSync)(id, ["--version"], { stdio: "ignore", timeout: 2e3 });
113267
113587
  return !r.error;
113268
113588
  }
113269
113589
  async function probeAnthropic(apiKey) {
@@ -113289,13 +113609,13 @@ async function runConnectAi(opts) {
113289
113609
  const root = findRepoRoot(cwd) ?? cwd;
113290
113610
  const configPath = discoverLocalConfig(cwd) ?? localConfigPath(root);
113291
113611
  const installed = LOCAL_PROVIDER_IDS.filter((id) => cliInstalled(id));
113292
- console.log(import_picocolors34.default.bold("\nAI providers"));
113612
+ console.log(import_picocolors35.default.bold("\nAI providers"));
113293
113613
  console.log(
113294
- import_picocolors34.default.dim(
113614
+ import_picocolors35.default.dim(
113295
113615
  ` Local CLIs on PATH: ${installed.length > 0 ? installed.join(", ") : "none detected"}`
113296
113616
  )
113297
113617
  );
113298
- console.log(import_picocolors34.default.dim(" Cloud: anthropic (Anthropic Claude API \u2014 used by `investigate --ai`)\n"));
113618
+ console.log(import_picocolors35.default.dim(" Cloud: anthropic (Anthropic Claude API \u2014 used by `investigate --ai`)\n"));
113299
113619
  let provider = opts.provider;
113300
113620
  if (provider === void 0) {
113301
113621
  if (isInteractive()) {
@@ -113306,7 +113626,7 @@ async function runConnectAi(opts) {
113306
113626
  }
113307
113627
  }
113308
113628
  if (!AI_PROVIDERS.includes(provider)) {
113309
- console.error(import_picocolors34.default.red(`Unknown AI provider: ${provider}`) + import_picocolors34.default.dim(`
113629
+ console.error(import_picocolors35.default.red(`Unknown AI provider: ${provider}`) + import_picocolors35.default.dim(`
113310
113630
  supported: ${AI_PROVIDERS.join(", ")}`));
113311
113631
  return 1;
113312
113632
  }
@@ -113319,7 +113639,7 @@ async function runConnectAi(opts) {
113319
113639
  }
113320
113640
  if (!apiKey) {
113321
113641
  console.error(
113322
- import_picocolors34.default.red("No API key provided.") + import_picocolors34.default.dim("\n Pass --api-key sk-ant-\u2026 or set ANTHROPIC_API_KEY and re-run.")
113642
+ import_picocolors35.default.red("No API key provided.") + import_picocolors35.default.dim("\n Pass --api-key sk-ant-\u2026 or set ANTHROPIC_API_KEY and re-run.")
113323
113643
  );
113324
113644
  return 1;
113325
113645
  }
@@ -113327,28 +113647,28 @@ async function runConnectAi(opts) {
113327
113647
  const probe2 = await probeAnthropic(apiKey);
113328
113648
  if (!probe2.ok) {
113329
113649
  console.error(`
113330
- ${import_picocolors34.default.red("\u2717 Anthropic key check failed:")} ${probe2.detail}`);
113331
- console.error(import_picocolors34.default.dim(" Fix the key and retry, or pass --no-test to skip."));
113650
+ ${import_picocolors35.default.red("\u2717 Anthropic key check failed:")} ${probe2.detail}`);
113651
+ console.error(import_picocolors35.default.dim(" Fix the key and retry, or pass --no-test to skip."));
113332
113652
  return 1;
113333
113653
  }
113334
113654
  console.log(`
113335
- ${import_picocolors34.default.green("\u2713")} anthropic ${import_picocolors34.default.dim(`(${probe2.detail})`)}`);
113655
+ ${import_picocolors35.default.green("\u2713")} anthropic ${import_picocolors35.default.dim(`(${probe2.detail})`)}`);
113336
113656
  }
113337
113657
  if (opts.model) aiBlock["anthropic"] = { model: opts.model };
113338
113658
  storedKey = apiKey;
113339
113659
  } else {
113340
113660
  if (!installed.includes(provider)) {
113341
113661
  console.error(
113342
- import_picocolors34.default.red(`${provider} CLI not found on PATH.`) + import_picocolors34.default.dim(`
113662
+ import_picocolors35.default.red(`${provider} CLI not found on PATH.`) + import_picocolors35.default.dim(`
113343
113663
  Install it first, or choose anthropic.`)
113344
113664
  );
113345
113665
  return 1;
113346
113666
  }
113347
113667
  const desc2 = DEFAULT_LOCAL_PROVIDER_REGISTRY.get(provider);
113348
113668
  console.log(`
113349
- ${import_picocolors34.default.green("\u2713")} ${desc2?.displayName ?? provider} ${import_picocolors34.default.dim("detected on PATH")}`);
113669
+ ${import_picocolors35.default.green("\u2713")} ${desc2?.displayName ?? provider} ${import_picocolors35.default.dim("detected on PATH")}`);
113350
113670
  console.log(
113351
- import_picocolors34.default.dim(
113671
+ import_picocolors35.default.dim(
113352
113672
  " Note: `investigate --ai` currently uses the Anthropic API for narrative.\n Local-CLI narrative generation is recorded as your preference for future use."
113353
113673
  )
113354
113674
  );
@@ -113366,15 +113686,15 @@ ${import_picocolors34.default.green("\u2713")} ${desc2?.displayName ?? provider}
113366
113686
  });
113367
113687
  }
113368
113688
  console.log(`
113369
- ${import_picocolors34.default.green("\u2713")} ${import_picocolors34.default.bold("ai")} provider saved \u2192 ${import_picocolors34.default.dim(configPath)}`);
113370
- console.log(import_picocolors34.default.dim(` provider: ${provider}`));
113689
+ ${import_picocolors35.default.green("\u2713")} ${import_picocolors35.default.bold("ai")} provider saved \u2192 ${import_picocolors35.default.dim(configPath)}`);
113690
+ console.log(import_picocolors35.default.dim(` provider: ${provider}`));
113371
113691
  if (storedKey && secretsPathStr) {
113372
- console.log(import_picocolors34.default.dim(` anthropic key: ${redactKey(storedKey)} \u2192 ${secretsPathStr} (gitignored)`));
113692
+ console.log(import_picocolors35.default.dim(` anthropic key: ${redactKey(storedKey)} \u2192 ${secretsPathStr} (gitignored)`));
113373
113693
  }
113374
- console.log(import_picocolors34.default.dim(' run: horus investigate "<hint>" --ai'));
113694
+ console.log(import_picocolors35.default.dim(' run: horus investigate "<hint>" --ai'));
113375
113695
  return 0;
113376
113696
  } catch (err2) {
113377
- console.error(import_picocolors34.default.red(err2 instanceof Error ? err2.message : String(err2)));
113697
+ console.error(import_picocolors35.default.red(err2 instanceof Error ? err2.message : String(err2)));
113378
113698
  return 1;
113379
113699
  }
113380
113700
  }
@@ -113406,7 +113726,7 @@ async function runConnect(type, opts) {
113406
113726
  }
113407
113727
  if (!SUPPORTED.includes(type)) {
113408
113728
  console.error(
113409
- import_picocolors35.default.red(`Unknown connector type: ${type}`) + import_picocolors35.default.dim(`
113729
+ import_picocolors36.default.red(`Unknown connector type: ${type}`) + import_picocolors36.default.dim(`
113410
113730
  supported: ${SUPPORTED.join(", ")}, ai`)
113411
113731
  );
113412
113732
  return 1;
@@ -113443,20 +113763,20 @@ async function runConnect(type, opts) {
113443
113763
  if (!probeResult.ok) {
113444
113764
  console.error(
113445
113765
  `
113446
- ${import_picocolors35.default.red(`\u2717 Could not reach ${connectorType}:`)} ${probeResult.detail}` + import_picocolors35.default.dim("\n Fix the connection and retry, or pass --no-test to skip.")
113766
+ ${import_picocolors36.default.red(`\u2717 Could not reach ${connectorType}:`)} ${probeResult.detail}` + import_picocolors36.default.dim("\n Fix the connection and retry, or pass --no-test to skip.")
113447
113767
  );
113448
113768
  return 1;
113449
113769
  }
113450
113770
  console.log(
113451
113771
  `
113452
- ${import_picocolors35.default.green("\u2713")} ${connectorType} reachable ${import_picocolors35.default.dim(`(${probeResult.detail})`)}`
113772
+ ${import_picocolors36.default.green("\u2713")} ${connectorType} reachable ${import_picocolors36.default.dim(`(${probeResult.detail})`)}`
113453
113773
  );
113454
113774
  }
113455
113775
  const hasLiteralCredentials = filled.url !== void 0 || filled.password !== void 0 || filled.username !== void 0 || filled.authToken !== void 0;
113456
113776
  if (hasLiteralCredentials) {
113457
113777
  if (isGitTracked(configPath, root)) {
113458
113778
  console.error(
113459
- import_picocolors35.default.red(".horus/config.json is already tracked by Git.") + "\nStoring credentials here would expose them in the repository.\n" + import_picocolors35.default.dim(
113779
+ import_picocolors36.default.red(".horus/config.json is already tracked by Git.") + "\nStoring credentials here would expose them in the repository.\n" + import_picocolors36.default.dim(
113460
113780
  " Option A \u2014 remove from Git index then re-run:\n git rm --cached .horus/config.json\n horus connect " + type + " ...\n\n Option B \u2014 keep credentials in the environment and reference them:\n export ES_URL=https://...\n export ES_USERNAME=...\n export ES_PASSWORD=...\n Then edit .horus/config.json manually to use urlEnv/usernameEnv/passwordEnv."
113461
113781
  )
113462
113782
  );
@@ -113466,18 +113786,18 @@ ${import_picocolors35.default.green("\u2713")} ${connectorType} reachable ${impo
113466
113786
  }
113467
113787
  patchLocalConnector(configPath, connectorType, patch, filled.env);
113468
113788
  console.log(
113469
- `${import_picocolors35.default.green("\u2713")} ${import_picocolors35.default.bold(connectorType)} connector saved \u2192 ${import_picocolors35.default.dim(configPath)}`
113789
+ `${import_picocolors36.default.green("\u2713")} ${import_picocolors36.default.bold(connectorType)} connector saved \u2192 ${import_picocolors36.default.dim(configPath)}`
113470
113790
  );
113471
113791
  printSummary(connectorType, filled);
113472
- console.log(import_picocolors35.default.dim(`
113792
+ console.log(import_picocolors36.default.dim(`
113473
113793
  run: horus investigate "<hint>"`));
113474
113794
  return 0;
113475
113795
  } catch (err2) {
113476
113796
  if (err2 instanceof ExitPromptError) {
113477
- console.error(import_picocolors35.default.red("Cancelled."));
113797
+ console.error(import_picocolors36.default.red("Cancelled."));
113478
113798
  return 1;
113479
113799
  }
113480
- console.error(import_picocolors35.default.red(err2.message));
113800
+ console.error(import_picocolors36.default.red(err2.message));
113481
113801
  return 1;
113482
113802
  }
113483
113803
  }
@@ -113486,7 +113806,7 @@ async function fillInteractive(type, opts) {
113486
113806
  if (!needsInteraction) return opts;
113487
113807
  console.log(
113488
113808
  `
113489
- ${import_picocolors35.default.bold(`Connect ${type}`)} ${import_picocolors35.default.dim("(press Enter to skip optional fields)")}
113809
+ ${import_picocolors36.default.bold(`Connect ${type}`)} ${import_picocolors36.default.dim("(press Enter to skip optional fields)")}
113490
113810
  `
113491
113811
  );
113492
113812
  const filled = { ...opts };
@@ -113552,7 +113872,7 @@ ${import_picocolors35.default.bold(`Connect ${type}`)} ${import_picocolors35.def
113552
113872
  break;
113553
113873
  case "redis": {
113554
113874
  console.log(
113555
- import_picocolors35.default.dim(
113875
+ import_picocolors36.default.dim(
113556
113876
  " Tip: embed credentials directly in the URL \u2014 redis://:password@host:6379\n or enter the URL and password separately below."
113557
113877
  )
113558
113878
  );
@@ -113594,8 +113914,8 @@ function missingRequired(type, opts) {
113594
113914
  }
113595
113915
  function ask(label, placeholder = "", required2 = true) {
113596
113916
  return new Promise((resolve9) => {
113597
- const hint = placeholder ? import_picocolors35.default.dim(` (${placeholder})`) : "";
113598
- const suffix = required2 ? "" : import_picocolors35.default.dim(" [optional]");
113917
+ const hint = placeholder ? import_picocolors36.default.dim(` (${placeholder})`) : "";
113918
+ const suffix = required2 ? "" : import_picocolors36.default.dim(" [optional]");
113599
113919
  process.stdout.write(` ${label}${suffix}${hint}: `);
113600
113920
  const rl = (0, import_node_readline.createInterface)({
113601
113921
  input: process.stdin,
@@ -113612,7 +113932,7 @@ function askPassword(label) {
113612
113932
  return new Promise((resolve9) => {
113613
113933
  const stdin = process.stdin;
113614
113934
  if (typeof stdin.setRawMode === "function") {
113615
- process.stdout.write(` ${label}${import_picocolors35.default.dim(" [optional]")}: `);
113935
+ process.stdout.write(` ${label}${import_picocolors36.default.dim(" [optional]")}: `);
113616
113936
  stdin.setRawMode(true);
113617
113937
  stdin.resume();
113618
113938
  let value = "";
@@ -113729,15 +114049,15 @@ function describeProbe(p5) {
113729
114049
  return `${p5.keyCount} keys \xB7 ${p5.suggestedRoles.join("/")}${examples ? ` \xB7 ${examples}` : ""}`;
113730
114050
  }
113731
114051
  async function discoverAndSelectDbs(url, bullmqPrefix) {
113732
- console.log(import_picocolors35.default.dim("\n Scanning DBs 0-15 (read-only, sampled)\u2026"));
114052
+ console.log(import_picocolors36.default.dim("\n Scanning DBs 0-15 (read-only, sampled)\u2026"));
113733
114053
  const probes = await probeRedisDatabases(url, { bullmqPrefix });
113734
114054
  const nonEmpty = probes.filter((p5) => p5.reachable && p5.keyCount > 0);
113735
114055
  if (nonEmpty.length === 0) {
113736
- console.log(import_picocolors35.default.dim(" No populated DBs found."));
114056
+ console.log(import_picocolors36.default.dim(" No populated DBs found."));
113737
114057
  return [];
113738
114058
  }
113739
114059
  for (const p5 of nonEmpty) {
113740
- console.log(` ${import_picocolors35.default.bold(`DB ${p5.db}`)} ${import_picocolors35.default.dim("\xB7 " + describeProbe(p5))}`);
114060
+ console.log(` ${import_picocolors36.default.bold(`DB ${p5.db}`)} ${import_picocolors36.default.dim("\xB7 " + describeProbe(p5))}`);
113741
114061
  }
113742
114062
  const byLabel = /* @__PURE__ */ new Map();
113743
114063
  const choices = nonEmpty.map((p5) => {
@@ -113877,11 +114197,11 @@ function printSummary(type, opts) {
113877
114197
  } else if (opts.dashboard) {
113878
114198
  lines.push(` dashboard: ${opts.dashboard}`);
113879
114199
  }
113880
- if (lines.length > 0) console.log(import_picocolors35.default.dim(lines.join("\n")));
114200
+ if (lines.length > 0) console.log(import_picocolors36.default.dim(lines.join("\n")));
113881
114201
  }
113882
114202
  function isGitTracked(filePath, cwd) {
113883
114203
  try {
113884
- (0, import_node_child_process6.execFileSync)("git", ["ls-files", "--error-unmatch", filePath], {
114204
+ (0, import_node_child_process7.execFileSync)("git", ["ls-files", "--error-unmatch", filePath], {
113885
114205
  cwd,
113886
114206
  stdio: "pipe"
113887
114207
  });
@@ -113916,11 +114236,11 @@ async function askIndexSelection(indices) {
113916
114236
  const shown = indices.slice(0, MAX_DISPLAY);
113917
114237
  console.log("\n Available Elasticsearch indexes/data streams:");
113918
114238
  shown.forEach((name2, i3) => {
113919
- console.log(` ${import_picocolors35.default.dim(`[${i3 + 1}]`)} ${name2}`);
114239
+ console.log(` ${import_picocolors36.default.dim(`[${i3 + 1}]`)} ${name2}`);
113920
114240
  });
113921
114241
  if (indices.length > MAX_DISPLAY) {
113922
114242
  console.log(
113923
- import_picocolors35.default.dim(
114243
+ import_picocolors36.default.dim(
113924
114244
  ` \u2026 and ${indices.length - MAX_DISPLAY} more (type a pattern manually to match all)`
113925
114245
  )
113926
114246
  );
@@ -113964,11 +114284,11 @@ async function askDashboardSelection(dashboards) {
113964
114284
  const shown = dashboards.slice(0, MAX_DISPLAY);
113965
114285
  console.log("\n Available Grafana dashboards:");
113966
114286
  shown.forEach((d3, i3) => {
113967
- const folder = d3.folderTitle ? import_picocolors35.default.dim(` (${d3.folderTitle})`) : "";
113968
- console.log(` ${import_picocolors35.default.dim(`[${i3 + 1}]`)} ${d3.title}${folder}`);
114287
+ const folder = d3.folderTitle ? import_picocolors36.default.dim(` (${d3.folderTitle})`) : "";
114288
+ console.log(` ${import_picocolors36.default.dim(`[${i3 + 1}]`)} ${d3.title}${folder}`);
113969
114289
  });
113970
114290
  if (dashboards.length > MAX_DISPLAY) {
113971
- console.log(import_picocolors35.default.dim(` \u2026 and ${dashboards.length - MAX_DISPLAY} more`));
114291
+ console.log(import_picocolors36.default.dim(` \u2026 and ${dashboards.length - MAX_DISPLAY} more`));
113972
114292
  }
113973
114293
  const input = (await ask(
113974
114294
  ` Select dashboards to use (e.g. 1,2 or Enter to type uid manually)`,
@@ -114018,16 +114338,17 @@ function redactUrl(raw) {
114018
114338
 
114019
114339
  // ../../packages/cli/src/commands/stop.ts
114020
114340
  init_cjs_shims();
114021
- var import_node_child_process7 = require("child_process");
114022
- var import_node_fs16 = require("fs");
114023
- var import_node_util4 = require("util");
114024
- var import_node_path18 = require("path");
114025
- var import_picocolors36 = __toESM(require_picocolors(), 1);
114026
- var execFileAsync = (0, import_node_util4.promisify)(import_node_child_process7.execFile);
114027
- var unlinkAsync = (0, import_node_util4.promisify)(import_node_fs16.unlink);
114341
+ var import_node_child_process8 = require("child_process");
114342
+ var import_node_fs17 = require("fs");
114343
+ var import_node_util5 = require("util");
114344
+ var import_node_path19 = require("path");
114345
+ var import_picocolors37 = __toESM(require_picocolors(), 1);
114346
+ var execFileAsync2 = (0, import_node_util5.promisify)(import_node_child_process8.execFile);
114347
+ var unlinkAsync = (0, import_node_util5.promisify)(import_node_fs17.unlink);
114028
114348
  var SPAWNED_HOST_FILE2 = "spawned-host.json";
114029
114349
  var START_TIME_TOLERANCE_S = 60;
114030
114350
  var STOP_WAIT_MS = 5e3;
114351
+ var STOP_KILL_WAIT_MS = 3e3;
114031
114352
  var STOP_POLL_MS = 200;
114032
114353
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
114033
114354
  async function runStop(opts) {
@@ -114039,19 +114360,19 @@ async function runStop(opts) {
114039
114360
  const root = findRepoRoot(cwd) ?? cwd;
114040
114361
  const hostUrl = readSourceHostUrl(root);
114041
114362
  if (!hostUrl) {
114042
- console.log(import_picocolors36.default.dim("No source-intelligence host found for this repo (.horus/source/host.json absent)."));
114363
+ console.log(import_picocolors37.default.dim("No source-intelligence host found for this repo (.horus/source/host.json absent)."));
114043
114364
  return 0;
114044
114365
  }
114045
114366
  return await stopHost(root, hostUrl);
114046
114367
  } catch (err2) {
114047
- console.error(import_picocolors36.default.red(err2.message));
114368
+ console.error(import_picocolors37.default.red(err2.message));
114048
114369
  return 1;
114049
114370
  }
114050
114371
  }
114051
114372
  async function stopHost(root, hostUrl) {
114052
114373
  const port = extractPort(hostUrl);
114053
114374
  if (port === null) {
114054
- console.error(import_picocolors36.default.red(`Cannot determine port from host URL: ${hostUrl}`));
114375
+ console.error(import_picocolors37.default.red(`Cannot determine port from host URL: ${hostUrl}`));
114055
114376
  return 1;
114056
114377
  }
114057
114378
  const healthy = await isHostHealthy(hostUrl);
@@ -114061,12 +114382,12 @@ async function stopHost(root, hostUrl) {
114061
114382
  if (!wrapperAlive) {
114062
114383
  const res = await stopBackendServerPid(root, port);
114063
114384
  if (res === "failed") return 1;
114064
- if (res !== "stopped") console.log(import_picocolors36.default.dim(`Host ${hostUrl} is already stopped.`));
114385
+ if (res !== "stopped") console.log(import_picocolors37.default.dim(`Host ${hostUrl} is already stopped.`));
114065
114386
  await cleanupSpawnedRecord(root);
114066
114387
  return 0;
114067
114388
  }
114068
114389
  console.log(
114069
- import_picocolors36.default.dim(
114390
+ import_picocolors37.default.dim(
114070
114391
  `Host ${hostUrl} is unreachable but pid ${owned.pid} is still running \u2014 terminating a stuck host.`
114071
114392
  )
114072
114393
  );
@@ -114074,7 +114395,7 @@ async function stopHost(root, hostUrl) {
114074
114395
  const spawned = readSpawnedHost(root);
114075
114396
  if (spawned === null) {
114076
114397
  console.error(
114077
- import_picocolors36.default.red(
114398
+ import_picocolors37.default.red(
114078
114399
  `No ownership record found (.horus/${SPAWNED_HOST_FILE2} absent). Horus will not stop a host it did not spawn.`
114079
114400
  )
114080
114401
  );
@@ -114082,12 +114403,12 @@ async function stopHost(root, hostUrl) {
114082
114403
  }
114083
114404
  const recordError = validateSpawnedRecord(spawned);
114084
114405
  if (recordError !== null) {
114085
- console.error(import_picocolors36.default.red(`Ownership record is malformed: ${recordError}. Aborting for safety.`));
114406
+ console.error(import_picocolors37.default.red(`Ownership record is malformed: ${recordError}. Aborting for safety.`));
114086
114407
  return 1;
114087
114408
  }
114088
114409
  if (spawned.port !== port) {
114089
114410
  console.error(
114090
- import_picocolors36.default.red(
114411
+ import_picocolors37.default.red(
114091
114412
  `Ownership record port (${spawned.port}) does not match host URL port (${port}). Record may be stale.`
114092
114413
  )
114093
114414
  );
@@ -114095,7 +114416,7 @@ async function stopHost(root, hostUrl) {
114095
114416
  }
114096
114417
  if (spawned.root !== root) {
114097
114418
  console.error(
114098
- import_picocolors36.default.red(
114419
+ import_picocolors37.default.red(
114099
114420
  `Ownership record root (${spawned.root}) does not match resolved root (${root}). Record may be stale.`
114100
114421
  )
114101
114422
  );
@@ -114106,14 +114427,14 @@ async function stopHost(root, hostUrl) {
114106
114427
  const res = await stopBackendServerPid(root, port);
114107
114428
  if (res === "failed") return 1;
114108
114429
  if (res !== "stopped") {
114109
- console.log(import_picocolors36.default.dim(`Process pid ${spawned.pid} is no longer running \u2014 already stopped.`));
114430
+ console.log(import_picocolors37.default.dim(`Process pid ${spawned.pid} is no longer running \u2014 already stopped.`));
114110
114431
  }
114111
114432
  await cleanupSpawnedRecord(root);
114112
114433
  return 0;
114113
114434
  }
114114
114435
  if (!hostArgvRegex(port).test(info2.args)) {
114115
114436
  console.error(
114116
- import_picocolors36.default.red(
114437
+ import_picocolors37.default.red(
114117
114438
  `Pid ${spawned.pid} args do not match "horus-source host --port ${port}". Got: "${info2.args.slice(0, 120)}". Aborting for safety.`
114118
114439
  )
114119
114440
  );
@@ -114122,51 +114443,33 @@ async function stopHost(root, hostUrl) {
114122
114443
  const startTs = new Date(spawned.startedAt).getTime();
114123
114444
  const recordedAgeS = Math.round((Date.now() - startTs) / 1e3);
114124
114445
  if (!Number.isFinite(info2.etimeSeconds)) {
114125
- console.error(import_picocolors36.default.red(`Could not read elapsed time for pid ${spawned.pid}. Aborting for safety.`));
114446
+ console.error(import_picocolors37.default.red(`Could not read elapsed time for pid ${spawned.pid}. Aborting for safety.`));
114126
114447
  return 1;
114127
114448
  }
114128
114449
  if (Math.abs(info2.etimeSeconds - recordedAgeS) > START_TIME_TOLERANCE_S) {
114129
114450
  console.error(
114130
- import_picocolors36.default.red(
114451
+ import_picocolors37.default.red(
114131
114452
  `Pid ${spawned.pid} age mismatch: record says ~${recordedAgeS}s, process reports ${info2.etimeSeconds}s elapsed. Possible PID reuse \u2014 aborting for safety.`
114132
114453
  )
114133
114454
  );
114134
114455
  return 1;
114135
114456
  }
114136
- let signaled = false;
114457
+ let terminated;
114137
114458
  try {
114138
- process.kill(spawned.pid, "SIGTERM");
114139
- signaled = true;
114459
+ terminated = await terminatePid(spawned.pid);
114140
114460
  } catch (err2) {
114141
- if (err2.code === "ESRCH") {
114142
- console.log(import_picocolors36.default.dim(`Process pid ${spawned.pid} exited before signal \u2014 already stopped.`));
114143
- } else {
114144
- console.error(import_picocolors36.default.red(`Failed to signal pid ${spawned.pid}: ${err2.message}`));
114145
- return 1;
114146
- }
114461
+ console.error(import_picocolors37.default.red(`Failed to signal pid ${spawned.pid}: ${err2.message}`));
114462
+ return 1;
114147
114463
  }
114148
- if (signaled) {
114149
- const deadline = Date.now() + STOP_WAIT_MS;
114150
- let exited = false;
114151
- while (Date.now() < deadline) {
114152
- await sleep(STOP_POLL_MS);
114153
- if (await getProcessInfo(spawned.pid) === null) {
114154
- exited = true;
114155
- break;
114156
- }
114157
- }
114158
- if (!exited) {
114159
- console.error(
114160
- import_picocolors36.default.red(
114161
- `Host pid ${spawned.pid} did not exit within ${STOP_WAIT_MS / 1e3}s after SIGTERM.`
114162
- )
114163
- );
114164
- return 1;
114165
- }
114166
- console.log(
114167
- `${import_picocolors36.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors36.default.dim(`(pid ${spawned.pid}, port ${port})`) + ` for ${root}`
114464
+ if (!terminated) {
114465
+ console.error(
114466
+ import_picocolors37.default.red(`Host pid ${spawned.pid} did not exit even after SIGKILL \u2014 could not reclaim the port.`)
114168
114467
  );
114468
+ return 1;
114169
114469
  }
114470
+ console.log(
114471
+ `${import_picocolors37.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors37.default.dim(`(pid ${spawned.pid}, port ${port})`) + ` for ${root}`
114472
+ );
114170
114473
  if (await stopBackendServerPid(root, port) === "failed") return 1;
114171
114474
  await cleanupSpawnedRecord(root);
114172
114475
  return 0;
@@ -114178,7 +114481,7 @@ function hostArgvRegex(port) {
114178
114481
  }
114179
114482
  async function cleanupSpawnedRecord(root) {
114180
114483
  try {
114181
- await unlinkAsync((0, import_node_path18.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
114484
+ await unlinkAsync((0, import_node_path19.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
114182
114485
  } catch {
114183
114486
  }
114184
114487
  }
@@ -114191,39 +114494,35 @@ async function stopBackendServerPid(root, port) {
114191
114494
  if (info2 === null) return "none";
114192
114495
  if (!hostArgvRegex(port).test(info2.args)) {
114193
114496
  console.error(
114194
- import_picocolors36.default.red(
114497
+ import_picocolors37.default.red(
114195
114498
  `Backend host pid ${rec.pid} args do not match "horus-source host --port ${port}". Not signalling \u2014 possible stale source/host.json or PID reuse.`
114196
114499
  )
114197
114500
  );
114198
114501
  return "failed";
114199
114502
  }
114503
+ let terminated;
114200
114504
  try {
114201
- process.kill(rec.pid, "SIGTERM");
114505
+ terminated = await terminatePid(rec.pid);
114202
114506
  } catch (err2) {
114203
- if (err2.code === "ESRCH") return "stopped";
114204
- console.error(import_picocolors36.default.red(`Failed to signal pid ${rec.pid}: ${err2.message}`));
114507
+ console.error(import_picocolors37.default.red(`Failed to signal pid ${rec.pid}: ${err2.message}`));
114205
114508
  return "failed";
114206
114509
  }
114207
- const deadline = Date.now() + STOP_WAIT_MS;
114208
- while (Date.now() < deadline) {
114209
- await sleep(STOP_POLL_MS);
114210
- if (await getProcessInfo(rec.pid) === null) {
114211
- console.log(
114212
- `${import_picocolors36.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors36.default.dim(`(pid ${rec.pid}, port ${port})`) + ` for ${root}`
114213
- );
114214
- return "stopped";
114215
- }
114510
+ if (!terminated) {
114511
+ console.error(
114512
+ import_picocolors37.default.red(`Host pid ${rec.pid} did not exit even after SIGKILL \u2014 could not reclaim the port.`)
114513
+ );
114514
+ return "failed";
114216
114515
  }
114217
- console.error(
114218
- import_picocolors36.default.red(`Host pid ${rec.pid} did not exit within ${STOP_WAIT_MS / 1e3}s after SIGTERM.`)
114516
+ console.log(
114517
+ `${import_picocolors37.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors37.default.dim(`(pid ${rec.pid}, port ${port})`) + ` for ${root}`
114219
114518
  );
114220
- return "failed";
114519
+ return "stopped";
114221
114520
  }
114222
114521
  async function stopAll() {
114223
114522
  const registry2 = readRegistry();
114224
114523
  const projects2 = Object.entries(registry2.projects);
114225
114524
  if (projects2.length === 0) {
114226
- console.log(import_picocolors36.default.dim("No registered projects."));
114525
+ console.log(import_picocolors37.default.dim("No registered projects."));
114227
114526
  return 0;
114228
114527
  }
114229
114528
  let stopped = 0;
@@ -114235,17 +114534,17 @@ async function stopAll() {
114235
114534
  if (!healthy && readSpawnedHost(entry2.root) === null && readSourceHostPid(entry2.root) === null) {
114236
114535
  continue;
114237
114536
  }
114238
- console.log(` Stopping ${import_picocolors36.default.bold(name2)} ${import_picocolors36.default.dim(`(${hostUrl})`)}`);
114537
+ console.log(` Stopping ${import_picocolors37.default.bold(name2)} ${import_picocolors37.default.dim(`(${hostUrl})`)}`);
114239
114538
  const code = await stopHost(entry2.root, hostUrl);
114240
114539
  if (code === 0) stopped++;
114241
114540
  else failed++;
114242
114541
  }
114243
114542
  if (stopped === 0 && failed === 0) {
114244
- console.log(import_picocolors36.default.dim("No running source-intelligence hosts found."));
114543
+ console.log(import_picocolors37.default.dim("No running source-intelligence hosts found."));
114245
114544
  } else {
114246
114545
  console.log(
114247
114546
  `
114248
- Stopped ${stopped} host(s)${failed > 0 ? import_picocolors36.default.red(`, ${failed} failed`) : ""}.`
114547
+ Stopped ${stopped} host(s)${failed > 0 ? import_picocolors37.default.red(`, ${failed} failed`) : ""}.`
114249
114548
  );
114250
114549
  }
114251
114550
  return failed > 0 ? 1 : 0;
@@ -114272,10 +114571,30 @@ function validateSpawnedRecord(r) {
114272
114571
  }
114273
114572
  return null;
114274
114573
  }
114574
+ async function terminatePid(pid) {
114575
+ const escalation = [
114576
+ { signal: "SIGTERM", waitMs: STOP_WAIT_MS },
114577
+ { signal: "SIGKILL", waitMs: STOP_KILL_WAIT_MS }
114578
+ ];
114579
+ for (const { signal, waitMs } of escalation) {
114580
+ try {
114581
+ process.kill(pid, signal);
114582
+ } catch (err2) {
114583
+ if (err2.code === "ESRCH") return true;
114584
+ throw err2;
114585
+ }
114586
+ const deadline = Date.now() + waitMs;
114587
+ while (Date.now() < deadline) {
114588
+ await sleep(STOP_POLL_MS);
114589
+ if (await getProcessInfo(pid) === null) return true;
114590
+ }
114591
+ }
114592
+ return await getProcessInfo(pid) === null;
114593
+ }
114275
114594
  async function getProcessInfo(pid) {
114276
114595
  let args2;
114277
114596
  try {
114278
- const argsResult = await execFileAsync("ps", ["-p", String(pid), "-o", "args="], {
114597
+ const argsResult = await execFileAsync2("ps", ["-p", String(pid), "-o", "args="], {
114279
114598
  timeout: 3e3
114280
114599
  });
114281
114600
  args2 = argsResult.stdout.trim();
@@ -114285,7 +114604,7 @@ async function getProcessInfo(pid) {
114285
114604
  if (!args2) return null;
114286
114605
  let etimeSeconds = Number.NaN;
114287
114606
  try {
114288
- const etimeResult = await execFileAsync("ps", ["-p", String(pid), "-o", "etime="], {
114607
+ const etimeResult = await execFileAsync2("ps", ["-p", String(pid), "-o", "etime="], {
114289
114608
  timeout: 3e3
114290
114609
  });
114291
114610
  etimeSeconds = parseEtimeSeconds(etimeResult.stdout.trim());
@@ -114313,14 +114632,77 @@ function extractPort(hostUrl) {
114313
114632
 
114314
114633
  // ../../packages/cli/src/commands/hosts.ts
114315
114634
  init_cjs_shims();
114316
- var import_picocolors37 = __toESM(require_picocolors(), 1);
114317
- async function runHosts() {
114635
+ var import_picocolors38 = __toESM(require_picocolors(), 1);
114636
+
114637
+ // ../../packages/cli/src/lib/host-reaper.ts
114638
+ init_cjs_shims();
114639
+ var import_node_child_process9 = require("child_process");
114640
+ var import_node_util6 = require("util");
114641
+ var execFileAsync3 = (0, import_node_util6.promisify)(import_node_child_process9.execFile);
114642
+ var HOST_ARGV = /(?:^|\s)(?:\S*\/)?horus-source\s+host\s+--port(?:=|\s+)(\d+)(?=\s|$)/;
114643
+ function parseSourceHosts(psStdout) {
114644
+ const hosts = [];
114645
+ for (const line2 of psStdout.split("\n")) {
114646
+ const trimmed = line2.trim();
114647
+ if (!trimmed) continue;
114648
+ const sp = trimmed.indexOf(" ");
114649
+ if (sp < 0) continue;
114650
+ const pid = Number(trimmed.slice(0, sp));
114651
+ if (!Number.isInteger(pid) || pid <= 0) continue;
114652
+ const m5 = HOST_ARGV.exec(trimmed.slice(sp + 1));
114653
+ if (!m5) continue;
114654
+ hosts.push({ pid, port: Number(m5[1]) });
114655
+ }
114656
+ return hosts;
114657
+ }
114658
+ function selectOrphans(running, claimedPids) {
114659
+ return running.filter((h3) => !claimedPids.has(h3.pid));
114660
+ }
114661
+ async function listRunningSourceHosts() {
114662
+ try {
114663
+ const { stdout } = await execFileAsync3("ps", ["-axww", "-o", "pid=,args="], {
114664
+ maxBuffer: 8 * 1024 * 1024,
114665
+ timeout: 5e3
114666
+ });
114667
+ return parseSourceHosts(stdout);
114668
+ } catch {
114669
+ return [];
114670
+ }
114671
+ }
114672
+ function isAlive(pid) {
114673
+ try {
114674
+ process.kill(pid, 0);
114675
+ return true;
114676
+ } catch (err2) {
114677
+ return err2.code === "EPERM";
114678
+ }
114679
+ }
114680
+ async function reapPid(pid, opts = {}) {
114681
+ const termMs = opts.termMs ?? 4e3;
114682
+ const killMs = opts.killMs ?? 3e3;
114683
+ const pollMs = opts.pollMs ?? 200;
114684
+ for (const [signal, wait] of [
114685
+ ["SIGTERM", termMs],
114686
+ ["SIGKILL", killMs]
114687
+ ]) {
114688
+ try {
114689
+ process.kill(pid, signal);
114690
+ } catch (err2) {
114691
+ if (err2.code === "ESRCH") return true;
114692
+ }
114693
+ const deadline = Date.now() + wait;
114694
+ while (Date.now() < deadline) {
114695
+ await new Promise((r) => setTimeout(r, pollMs));
114696
+ if (!isAlive(pid)) return true;
114697
+ }
114698
+ }
114699
+ return !isAlive(pid);
114700
+ }
114701
+
114702
+ // ../../packages/cli/src/commands/hosts.ts
114703
+ async function runHosts(opts = {}) {
114318
114704
  const registry2 = readRegistry();
114319
114705
  const projects2 = Object.entries(registry2.projects);
114320
- if (projects2.length === 0) {
114321
- console.log(import_picocolors37.default.dim("No registered projects. Run `horus index` in a repo first."));
114322
- return 0;
114323
- }
114324
114706
  const rows = [];
114325
114707
  await Promise.all(
114326
114708
  projects2.map(async ([name2, entry2]) => {
@@ -114329,39 +114711,69 @@ async function runHosts() {
114329
114711
  rows.push({ name: name2, hostUrl: hostUrl ?? null, healthy, root: entry2.root });
114330
114712
  })
114331
114713
  );
114332
- rows.sort((a2, b5) => {
114333
- if (a2.healthy !== b5.healthy) return a2.healthy ? -1 : 1;
114334
- return a2.name.localeCompare(b5.name);
114335
- });
114336
- const anyHost = rows.some((r) => r.hostUrl !== null);
114337
- if (!anyHost) {
114338
- console.log(import_picocolors37.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
114339
- return 0;
114340
- }
114714
+ rows.sort(
114715
+ (a2, b5) => a2.healthy !== b5.healthy ? a2.healthy ? -1 : 1 : a2.name.localeCompare(b5.name)
114716
+ );
114341
114717
  console.log("");
114718
+ let printedAny = false;
114342
114719
  for (const row of rows) {
114343
114720
  if (row.hostUrl === null) continue;
114344
- const status = row.healthy ? import_picocolors37.default.green("\u25CF running") : import_picocolors37.default.red("\u25CF stopped");
114721
+ printedAny = true;
114722
+ const status = row.healthy ? import_picocolors38.default.green("\u25CF running") : import_picocolors38.default.red("\u25CF stopped");
114345
114723
  const port = extractPort2(row.hostUrl) ?? "?";
114346
114724
  console.log(
114347
- ` ${status} ${import_picocolors37.default.bold(row.name.padEnd(24))} port ${String(port).padEnd(6)} ${import_picocolors37.default.dim(row.root)}`
114725
+ ` ${status} ${import_picocolors38.default.bold(row.name.padEnd(24))} port ${String(port).padEnd(6)} ${import_picocolors38.default.dim(row.root)}`
114348
114726
  );
114349
114727
  }
114350
- console.log("");
114351
114728
  const noHost = rows.filter((r) => r.hostUrl === null);
114352
- if (noHost.length > 0) {
114353
- for (const row of noHost) {
114354
- console.log(` ${import_picocolors37.default.dim("\u25CB no host")} ${import_picocolors37.default.dim(row.name)}`);
114729
+ for (const row of noHost) console.log(` ${import_picocolors38.default.dim("\u25CB no host")} ${import_picocolors38.default.dim(row.name)}`);
114730
+ if (printedAny || noHost.length > 0) console.log("");
114731
+ const claimedPids = /* @__PURE__ */ new Set();
114732
+ for (const [, entry2] of projects2) {
114733
+ const hp = readSourceHostPid(entry2.root);
114734
+ if (hp?.pid) claimedPids.add(hp.pid);
114735
+ const sp = readSpawnedHost(entry2.root);
114736
+ if (sp?.pid) claimedPids.add(sp.pid);
114737
+ }
114738
+ const orphans = selectOrphans(await listRunningSourceHosts(), claimedPids);
114739
+ let reapFailed = 0;
114740
+ if (orphans.length > 0) {
114741
+ console.log(
114742
+ import_picocolors38.default.yellow(` \u26A0 ${orphans.length} orphaned source host(s) \u2014 no registered repo owns them:`)
114743
+ );
114744
+ for (const o5 of orphans) console.log(import_picocolors38.default.dim(` pid ${o5.pid} port ${o5.port}`));
114745
+ if (opts.reap) {
114746
+ let reaped = 0;
114747
+ for (const o5 of orphans) {
114748
+ if (await reapPid(o5.pid)) {
114749
+ reaped++;
114750
+ console.log(` ${import_picocolors38.default.green("\u2713")} reaped pid ${o5.pid} ${import_picocolors38.default.dim(`(port ${o5.port})`)}`);
114751
+ } else {
114752
+ reapFailed++;
114753
+ console.log(import_picocolors38.default.red(` \u2717 could not stop pid ${o5.pid} (port ${o5.port})`));
114754
+ }
114755
+ }
114756
+ console.log(import_picocolors38.default.dim(` Reaped ${reaped}/${orphans.length} orphan(s).`));
114757
+ } else {
114758
+ console.log(import_picocolors38.default.dim(" Run `horus hosts --reap` to stop them."));
114355
114759
  }
114356
114760
  console.log("");
114357
114761
  }
114762
+ if (projects2.length === 0 && orphans.length === 0) {
114763
+ console.log(import_picocolors38.default.dim("No registered projects. Run `horus index` in a repo first."));
114764
+ return 0;
114765
+ }
114766
+ if (!printedAny && orphans.length === 0) {
114767
+ console.log(import_picocolors38.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
114768
+ return 0;
114769
+ }
114358
114770
  const running = rows.filter((r) => r.healthy).length;
114359
114771
  console.log(
114360
- import_picocolors37.default.dim(
114361
- `${running} running \xB7 horus stop to reap \xB7 horus stop --all to stop everything`
114772
+ import_picocolors38.default.dim(
114773
+ `${running} running${orphans.length ? ` \xB7 ${orphans.length} orphan(s)` : ""} \xB7 horus hosts --reap to clear orphans \xB7 horus stop --all to stop everything`
114362
114774
  )
114363
114775
  );
114364
- return 0;
114776
+ return reapFailed > 0 ? 1 : 0;
114365
114777
  }
114366
114778
  function extractPort2(hostUrl) {
114367
114779
  try {
@@ -114374,12 +114786,12 @@ function extractPort2(hostUrl) {
114374
114786
 
114375
114787
  // ../../packages/cli/src/commands/doctor.ts
114376
114788
  init_cjs_shims();
114377
- var import_picocolors38 = __toESM(require_picocolors(), 1);
114789
+ var import_picocolors39 = __toESM(require_picocolors(), 1);
114378
114790
  var DEFAULT_DB_URL4 = "postgresql://horus:horus@localhost:5433/horus";
114379
114791
  function mark2(status) {
114380
- if (status === "pass") return import_picocolors38.default.green("\u2713");
114381
- if (status === "warn") return import_picocolors38.default.yellow("~");
114382
- return import_picocolors38.default.red("\u2717");
114792
+ if (status === "pass") return import_picocolors39.default.green("\u2713");
114793
+ if (status === "warn") return import_picocolors39.default.yellow("~");
114794
+ return import_picocolors39.default.red("\u2717");
114383
114795
  }
114384
114796
  async function runDoctor(opts) {
114385
114797
  const cwd = opts?.cwd ?? process.cwd();
@@ -114659,11 +115071,11 @@ async function runDoctor(opts) {
114659
115071
  write(JSON.stringify(output, null, 2));
114660
115072
  return hasFailure ? 1 : 0;
114661
115073
  }
114662
- write(import_picocolors38.default.bold("\nHorus readiness check\n"));
115074
+ write(import_picocolors39.default.bold("\nHorus readiness check\n"));
114663
115075
  for (const check2 of checks) {
114664
- write(` ${mark2(check2.status)} ${import_picocolors38.default.bold(check2.label.padEnd(26))} ${import_picocolors38.default.dim(check2.detail)}`);
115076
+ write(` ${mark2(check2.status)} ${import_picocolors39.default.bold(check2.label.padEnd(26))} ${import_picocolors39.default.dim(check2.detail)}`);
114665
115077
  if (check2.next) {
114666
- write(` ${import_picocolors38.default.dim("\u2192 " + check2.next)}`);
115078
+ write(` ${import_picocolors39.default.dim("\u2192 " + check2.next)}`);
114667
115079
  }
114668
115080
  }
114669
115081
  write("");
@@ -114672,20 +115084,20 @@ async function runDoctor(opts) {
114672
115084
 
114673
115085
  // ../../packages/cli/src/commands/providers-doctor.ts
114674
115086
  init_cjs_shims();
114675
- var import_node_child_process8 = require("child_process");
114676
- var import_picocolors39 = __toESM(require_picocolors(), 1);
115087
+ var import_node_child_process10 = require("child_process");
115088
+ var import_picocolors40 = __toESM(require_picocolors(), 1);
114677
115089
  function statusMark(status) {
114678
- if (status === "ready") return import_picocolors39.default.green("\u2713");
114679
- if (status === "installed") return import_picocolors39.default.yellow("~");
114680
- return import_picocolors39.default.red("\u2717");
115090
+ if (status === "ready") return import_picocolors40.default.green("\u2713");
115091
+ if (status === "installed") return import_picocolors40.default.yellow("~");
115092
+ return import_picocolors40.default.red("\u2717");
114681
115093
  }
114682
115094
  function statusLabel(status) {
114683
- if (status === "ready") return import_picocolors39.default.green("ready");
114684
- if (status === "installed") return import_picocolors39.default.yellow("installed (not configured)");
114685
- return import_picocolors39.default.dim("not found on PATH");
115095
+ if (status === "ready") return import_picocolors40.default.green("ready");
115096
+ if (status === "installed") return import_picocolors40.default.yellow("installed (not configured)");
115097
+ return import_picocolors40.default.dim("not found on PATH");
114686
115098
  }
114687
115099
  function detectBinary(id) {
114688
- const result = (0, import_node_child_process8.spawnSync)(id, ["--version"], { stdio: "pipe", timeout: 2e3 });
115100
+ const result = (0, import_node_child_process10.spawnSync)(id, ["--version"], { stdio: "pipe", timeout: 2e3 });
114689
115101
  if (result.error) {
114690
115102
  return { id, status: "unavailable", detail: `${id}: command not found` };
114691
115103
  }
@@ -114699,7 +115111,7 @@ async function runProvidersDoctorCommand(opts) {
114699
115111
  const registry2 = opts?.registry ?? DEFAULT_LOCAL_PROVIDER_REGISTRY;
114700
115112
  const write = opts?.write ?? ((line2) => console.log(line2));
114701
115113
  const results = buildProviderResults(registry2, opts?._detect);
114702
- write(import_picocolors39.default.bold("\nLocal AI providers\n"));
115114
+ write(import_picocolors40.default.bold("\nLocal AI providers\n"));
114703
115115
  for (const result of results) {
114704
115116
  const descriptor = registry2.get(result.id);
114705
115117
  const name2 = descriptor?.displayName ?? result.id;
@@ -114707,11 +115119,11 @@ async function runProvidersDoctorCommand(opts) {
114707
115119
  ` ${statusMark(result.status)} ${result.id.padEnd(8)} ${name2.padEnd(22)} ${statusLabel(result.status)}`
114708
115120
  );
114709
115121
  if (result.status !== "ready" && result.detail) {
114710
- write(` ${import_picocolors39.default.dim("\u2192 " + result.detail)}`);
115122
+ write(` ${import_picocolors40.default.dim("\u2192 " + result.detail)}`);
114711
115123
  }
114712
115124
  }
114713
115125
  write("");
114714
- write(import_picocolors39.default.bold("Cloud AI providers\n"));
115126
+ write(import_picocolors40.default.bold("Cloud AI providers\n"));
114715
115127
  let source = null;
114716
115128
  if (opts?._anthropicKey !== void 0) {
114717
115129
  source = opts._anthropicKey ? "env" : null;
@@ -114726,18 +115138,18 @@ async function runProvidersDoctorCommand(opts) {
114726
115138
  }
114727
115139
  if (source === "config") {
114728
115140
  write(
114729
- ` ${import_picocolors39.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors39.default.green("configured (local .horus config)")}`
115141
+ ` ${import_picocolors40.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors40.default.green("configured (local .horus config)")}`
114730
115142
  );
114731
115143
  } else if (source === "env") {
114732
115144
  write(
114733
- ` ${import_picocolors39.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors39.default.green("configured (ANTHROPIC_API_KEY env)")}`
115145
+ ` ${import_picocolors40.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors40.default.green("configured (ANTHROPIC_API_KEY env)")}`
114734
115146
  );
114735
115147
  } else {
114736
115148
  write(
114737
- ` ${import_picocolors39.default.red("\u2717")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors39.default.dim("not configured")}`
115149
+ ` ${import_picocolors40.default.red("\u2717")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors40.default.dim("not configured")}`
114738
115150
  );
114739
115151
  write(
114740
- ` ${import_picocolors39.default.dim("\u2192 run `horus connect ai` (or set ANTHROPIC_API_KEY) to enable `horus investigate --ai`")}`
115152
+ ` ${import_picocolors40.default.dim("\u2192 run `horus connect ai` (or set ANTHROPIC_API_KEY) to enable `horus investigate --ai`")}`
114741
115153
  );
114742
115154
  }
114743
115155
  write("");
@@ -114746,9 +115158,9 @@ async function runProvidersDoctorCommand(opts) {
114746
115158
 
114747
115159
  // ../../packages/cli/src/commands/generate-config.ts
114748
115160
  init_cjs_shims();
114749
- var import_node_fs17 = require("fs");
114750
- var import_node_path19 = require("path");
114751
- var import_picocolors40 = __toESM(require_picocolors(), 1);
115161
+ var import_node_fs18 = require("fs");
115162
+ var import_node_path20 = require("path");
115163
+ var import_picocolors41 = __toESM(require_picocolors(), 1);
114752
115164
  function configTemplate(name2, repoPath) {
114753
115165
  return `export default {
114754
115166
  database: {
@@ -114796,39 +115208,39 @@ async function runGenerateConfig(opts) {
114796
115208
  const defaults2 = localConfigPath2 != null ? projectDefaults(localConfigPath2) : null;
114797
115209
  const hasLocalConfig = defaults2 != null;
114798
115210
  const defaultOut = hasLocalConfig ? "horus.config.example.js" : "horus.config.js";
114799
- const outPath = (0, import_node_path19.resolve)(cwd, opts.out ?? defaultOut);
115211
+ const outPath = (0, import_node_path20.resolve)(cwd, opts.out ?? defaultOut);
114800
115212
  const name2 = opts.name ?? defaults2?.name ?? "my-project";
114801
115213
  const repoPath = opts.repo ?? defaults2?.repoPath ?? `/path/to/${name2}`;
114802
- if ((0, import_node_fs17.existsSync)(outPath) && !opts.force) {
114803
- log(`${import_picocolors40.default.red("\u2717")} ${outPath} already exists`);
114804
- log(import_picocolors40.default.dim(" pass --force to overwrite"));
115214
+ if ((0, import_node_fs18.existsSync)(outPath) && !opts.force) {
115215
+ log(`${import_picocolors41.default.red("\u2717")} ${outPath} already exists`);
115216
+ log(import_picocolors41.default.dim(" pass --force to overwrite"));
114805
115217
  return 1;
114806
115218
  }
114807
115219
  try {
114808
- (0, import_node_fs17.mkdirSync)((0, import_node_path19.dirname)(outPath), { recursive: true });
114809
- (0, import_node_fs17.writeFileSync)(outPath, configTemplate(name2, repoPath), "utf8");
115220
+ (0, import_node_fs18.mkdirSync)((0, import_node_path20.dirname)(outPath), { recursive: true });
115221
+ (0, import_node_fs18.writeFileSync)(outPath, configTemplate(name2, repoPath), "utf8");
114810
115222
  } catch (err2) {
114811
- log(`${import_picocolors40.default.red("\u2717")} Could not write ${outPath}: ${err2.message}`);
115223
+ log(`${import_picocolors41.default.red("\u2717")} Could not write ${outPath}: ${err2.message}`);
114812
115224
  return 1;
114813
115225
  }
114814
- log(`${import_picocolors40.default.green("\u2713")} Created ${outPath}`);
114815
- log(import_picocolors40.default.dim(` project: ${name2}`));
114816
- log(import_picocolors40.default.dim(` repo: ${repoPath}`));
115226
+ log(`${import_picocolors41.default.green("\u2713")} Created ${outPath}`);
115227
+ log(import_picocolors41.default.dim(` project: ${name2}`));
115228
+ log(import_picocolors41.default.dim(` repo: ${repoPath}`));
114817
115229
  if (hasLocalConfig && localConfigPath2 != null) {
114818
115230
  log("");
114819
- log(import_picocolors40.default.yellow("Note:") + ` an initialized Horus project config exists at ${localConfigPath2}`);
115231
+ log(import_picocolors41.default.yellow("Note:") + ` an initialized Horus project config exists at ${localConfigPath2}`);
114820
115232
  log(" \u2022 .horus/config.json \u2014 project config used by `horus investigate` from this repo");
114821
115233
  log(" \u2022 horus.config.js \u2014 standalone/global config used with `horus doctor --config <path>`");
114822
- log(import_picocolors40.default.dim(` next: review ${outPath} and copy/adapt it as needed`));
115234
+ log(import_picocolors41.default.dim(` next: review ${outPath} and copy/adapt it as needed`));
114823
115235
  } else {
114824
- log(import_picocolors40.default.dim(` next: horus doctor --config ${outPath}`));
115236
+ log(import_picocolors41.default.dim(` next: horus doctor --config ${outPath}`));
114825
115237
  }
114826
115238
  return 0;
114827
115239
  }
114828
115240
 
114829
115241
  // ../../packages/cli/src/commands/readiness.ts
114830
115242
  init_cjs_shims();
114831
- var import_picocolors41 = __toESM(require_picocolors(), 1);
115243
+ var import_picocolors42 = __toESM(require_picocolors(), 1);
114832
115244
  var READINESS_AI_CONTRACT = `Provide a clearly separated AI readiness summary with:
114833
115245
 
114834
115246
  Overall assessment
@@ -114851,9 +115263,9 @@ Confidence / gaps
114851
115263
  - Which checks Horus could verify vs. which require manual confirmation`;
114852
115264
  var DEFAULT_DB_URL5 = "postgresql://horus:horus@localhost:5433/horus";
114853
115265
  function mark3(status) {
114854
- if (status === "pass") return import_picocolors41.default.green("\u2713");
114855
- if (status === "warn") return import_picocolors41.default.yellow("~");
114856
- return import_picocolors41.default.red("\u2717");
115266
+ if (status === "pass") return import_picocolors42.default.green("\u2713");
115267
+ if (status === "warn") return import_picocolors42.default.yellow("~");
115268
+ return import_picocolors42.default.red("\u2717");
114857
115269
  }
114858
115270
  async function runReadiness(opts) {
114859
115271
  const cwd = opts?.cwd ?? process.cwd();
@@ -115030,20 +115442,20 @@ async function runReadiness(opts) {
115030
115442
  }
115031
115443
  const blockingChecks = checks.filter((c4) => c4.blocking);
115032
115444
  const optionalChecks = checks.filter((c4) => !c4.blocking);
115033
- write(import_picocolors41.default.bold("\nHorus release readiness\n"));
115034
- write(import_picocolors41.default.bold(" Blocking"));
115445
+ write(import_picocolors42.default.bold("\nHorus release readiness\n"));
115446
+ write(import_picocolors42.default.bold(" Blocking"));
115035
115447
  for (const check2 of blockingChecks) {
115036
- write(` ${mark3(check2.status)} ${import_picocolors41.default.bold(check2.label.padEnd(22))} ${import_picocolors41.default.dim(check2.detail)}`);
115448
+ write(` ${mark3(check2.status)} ${import_picocolors42.default.bold(check2.label.padEnd(22))} ${import_picocolors42.default.dim(check2.detail)}`);
115037
115449
  if (check2.next) {
115038
- write(` ${import_picocolors41.default.dim("\u2192 " + check2.next)}`);
115450
+ write(` ${import_picocolors42.default.dim("\u2192 " + check2.next)}`);
115039
115451
  }
115040
115452
  }
115041
115453
  write("");
115042
- write(import_picocolors41.default.bold(" Optional"));
115454
+ write(import_picocolors42.default.bold(" Optional"));
115043
115455
  for (const check2 of optionalChecks) {
115044
- write(` ${mark3(check2.status)} ${import_picocolors41.default.bold(check2.label.padEnd(22))} ${import_picocolors41.default.dim(check2.detail)}`);
115456
+ write(` ${mark3(check2.status)} ${import_picocolors42.default.bold(check2.label.padEnd(22))} ${import_picocolors42.default.dim(check2.detail)}`);
115045
115457
  if (check2.next) {
115046
- write(` ${import_picocolors41.default.dim("\u2192 " + check2.next)}`);
115458
+ write(` ${import_picocolors42.default.dim("\u2192 " + check2.next)}`);
115047
115459
  }
115048
115460
  }
115049
115461
  write("");
@@ -115051,21 +115463,21 @@ async function runReadiness(opts) {
115051
115463
  const optionalWarns = optionalChecks.filter((c4) => c4.status === "warn").length;
115052
115464
  if (blockingFails.length === 0) {
115053
115465
  if (optionalWarns === 0) {
115054
- write(import_picocolors41.default.green(" Ready for demo/release."));
115466
+ write(import_picocolors42.default.green(" Ready for demo/release."));
115055
115467
  } else {
115056
115468
  write(
115057
- import_picocolors41.default.yellow(
115469
+ import_picocolors42.default.yellow(
115058
115470
  ` Ready for a basic demo. ${optionalWarns} optional item(s) not configured \u2014 investigation evidence will be limited.`
115059
115471
  )
115060
115472
  );
115061
115473
  }
115062
115474
  } else {
115063
115475
  write(
115064
- import_picocolors41.default.red(
115476
+ import_picocolors42.default.red(
115065
115477
  ` Not ready. ${blockingFails.length} blocking item(s) must be resolved before demo/release.`
115066
115478
  )
115067
115479
  );
115068
- write(import_picocolors41.default.dim(" Re-run `horus readiness` after resolving the items above."));
115480
+ write(import_picocolors42.default.dim(" Re-run `horus readiness` after resolving the items above."));
115069
115481
  }
115070
115482
  write("");
115071
115483
  if (opts?.ai) {
@@ -115080,7 +115492,7 @@ async function runReadiness(opts) {
115080
115492
  });
115081
115493
  console.log("\n" + renderInterpretation(result));
115082
115494
  if (!result.ok) {
115083
- console.error(import_picocolors41.default.yellow(`[ai] ${result.warning}`));
115495
+ console.error(import_picocolors42.default.yellow(`[ai] ${result.warning}`));
115084
115496
  }
115085
115497
  }
115086
115498
  return blockingFails.length > 0 ? 1 : 0;
@@ -115088,33 +115500,33 @@ async function runReadiness(opts) {
115088
115500
 
115089
115501
  // ../../packages/cli/src/commands/skill.ts
115090
115502
  init_cjs_shims();
115091
- var import_node_fs18 = require("fs");
115092
- var import_node_path20 = require("path");
115503
+ var import_node_fs19 = require("fs");
115504
+ var import_node_path21 = require("path");
115093
115505
  var import_node_os5 = require("os");
115094
115506
  var import_promises2 = require("readline/promises");
115095
- var import_picocolors42 = __toESM(require_picocolors(), 1);
115507
+ var import_picocolors43 = __toESM(require_picocolors(), 1);
115096
115508
  var SUPPORTED_TARGETS = ["claude", "codex", "gemini", "cursor", "generic"];
115097
115509
  function isValidTarget(target) {
115098
115510
  return SUPPORTED_TARGETS.includes(target);
115099
115511
  }
115100
115512
  function getLegacyClaudeSkillPath(opts) {
115101
115513
  const base2 = opts.global ? (0, import_node_os5.homedir)() : opts.cwd ?? process.cwd();
115102
- return (0, import_node_path20.join)(base2, ".claude", "skills", "horus.md");
115514
+ return (0, import_node_path21.join)(base2, ".claude", "skills", "horus.md");
115103
115515
  }
115104
115516
  function getSkillInstallPath(target, opts) {
115105
115517
  const base2 = opts.global ? (0, import_node_os5.homedir)() : opts.cwd ?? process.cwd();
115106
115518
  switch (target) {
115107
115519
  case "claude":
115108
- return (0, import_node_path20.join)(base2, ".claude", "skills", "horus", "SKILL.md");
115520
+ return (0, import_node_path21.join)(base2, ".claude", "skills", "horus", "SKILL.md");
115109
115521
  case "gemini":
115110
- return opts.global ? (0, import_node_path20.join)(base2, ".gemini", "GEMINI.md") : (0, import_node_path20.join)(base2, "GEMINI.md");
115522
+ return opts.global ? (0, import_node_path21.join)(base2, ".gemini", "GEMINI.md") : (0, import_node_path21.join)(base2, "GEMINI.md");
115111
115523
  case "cursor":
115112
- return opts.global ? (0, import_node_path20.join)(base2, ".cursorrules") : (0, import_node_path20.join)(base2, ".cursor", "rules", "horus.mdc");
115524
+ return opts.global ? (0, import_node_path21.join)(base2, ".cursorrules") : (0, import_node_path21.join)(base2, ".cursor", "rules", "horus.mdc");
115113
115525
  case "codex":
115114
- return opts.global ? (0, import_node_path20.join)(base2, ".codex", "AGENTS.md") : (0, import_node_path20.join)(base2, "AGENTS.md");
115526
+ return opts.global ? (0, import_node_path21.join)(base2, ".codex", "AGENTS.md") : (0, import_node_path21.join)(base2, "AGENTS.md");
115115
115527
  case "generic":
115116
115528
  default:
115117
- return (0, import_node_path20.join)(base2, ".horus", "skills", `horus-${target}.md`);
115529
+ return (0, import_node_path21.join)(base2, ".horus", "skills", `horus-${target}.md`);
115118
115530
  }
115119
115531
  }
115120
115532
  var BASE_SKILL = `# Horus Grounded Investigation Skill
@@ -115431,66 +115843,66 @@ async function confirm(message) {
115431
115843
  async function runSkillInstall(target, opts) {
115432
115844
  const log = opts.write ?? ((line2) => console.log(line2));
115433
115845
  if (!isValidTarget(target)) {
115434
- log(`${import_picocolors42.default.red("\u2717")} Unknown target: ${import_picocolors42.default.bold(target)}`);
115435
- log(import_picocolors42.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
115846
+ log(`${import_picocolors43.default.red("\u2717")} Unknown target: ${import_picocolors43.default.bold(target)}`);
115847
+ log(import_picocolors43.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
115436
115848
  return 1;
115437
115849
  }
115438
115850
  const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
115439
115851
  const content = generateSkillContent(target);
115440
115852
  if (target === "claude") {
115441
115853
  const legacyPath = getLegacyClaudeSkillPath({ global: opts.global, cwd: opts.cwd });
115442
- if ((0, import_node_fs18.existsSync)(legacyPath)) {
115854
+ if ((0, import_node_fs19.existsSync)(legacyPath)) {
115443
115855
  if (!opts.force) {
115444
115856
  const confirmed = await (opts.confirm ?? confirm)(
115445
115857
  `Found old flat skill file at ${legacyPath}. Replace it with the new directory layout?`
115446
115858
  );
115447
115859
  if (!confirmed) {
115448
- log(`${import_picocolors42.default.yellow("!")} Migration cancelled; existing skill left untouched.`);
115449
- log(import_picocolors42.default.dim(" Pass --force to replace without prompting."));
115860
+ log(`${import_picocolors43.default.yellow("!")} Migration cancelled; existing skill left untouched.`);
115861
+ log(import_picocolors43.default.dim(" Pass --force to replace without prompting."));
115450
115862
  return 1;
115451
115863
  }
115452
115864
  }
115453
115865
  try {
115454
- (0, import_node_fs18.rmSync)(legacyPath);
115455
- log(`${import_picocolors42.default.yellow("!")} Replaced old flat skill file: ${legacyPath}`);
115866
+ (0, import_node_fs19.rmSync)(legacyPath);
115867
+ log(`${import_picocolors43.default.yellow("!")} Replaced old flat skill file: ${legacyPath}`);
115456
115868
  } catch (err2) {
115457
- log(`${import_picocolors42.default.red("\u2717")} Could not remove legacy skill file ${legacyPath}: ${err2.message}`);
115869
+ log(`${import_picocolors43.default.red("\u2717")} Could not remove legacy skill file ${legacyPath}: ${err2.message}`);
115458
115870
  return 1;
115459
115871
  }
115460
115872
  }
115461
115873
  }
115462
- if ((0, import_node_fs18.existsSync)(outPath) && !opts.force) {
115463
- log(`${import_picocolors42.default.red("\u2717")} ${outPath} already exists`);
115464
- log(import_picocolors42.default.dim(" pass --force to overwrite"));
115874
+ if ((0, import_node_fs19.existsSync)(outPath) && !opts.force) {
115875
+ log(`${import_picocolors43.default.red("\u2717")} ${outPath} already exists`);
115876
+ log(import_picocolors43.default.dim(" pass --force to overwrite"));
115465
115877
  return 1;
115466
115878
  }
115467
115879
  try {
115468
- (0, import_node_fs18.mkdirSync)((0, import_node_path20.dirname)(outPath), { recursive: true });
115469
- (0, import_node_fs18.writeFileSync)(outPath, content, "utf8");
115880
+ (0, import_node_fs19.mkdirSync)((0, import_node_path21.dirname)(outPath), { recursive: true });
115881
+ (0, import_node_fs19.writeFileSync)(outPath, content, "utf8");
115470
115882
  } catch (err2) {
115471
- log(`${import_picocolors42.default.red("\u2717")} Could not write ${outPath}: ${err2.message}`);
115883
+ log(`${import_picocolors43.default.red("\u2717")} Could not write ${outPath}: ${err2.message}`);
115472
115884
  return 1;
115473
115885
  }
115474
- log(`${import_picocolors42.default.green("\u2713")} Horus skill installed \u2192 ${outPath}`);
115886
+ log(`${import_picocolors43.default.green("\u2713")} Horus skill installed \u2192 ${outPath}`);
115475
115887
  if (target === "claude") {
115476
- log(import_picocolors42.default.dim(" Claude Code will load this skill automatically."));
115477
- log(import_picocolors42.default.dim(" Invoke it with: /horus"));
115888
+ log(import_picocolors43.default.dim(" Claude Code will load this skill automatically."));
115889
+ log(import_picocolors43.default.dim(" Invoke it with: /horus"));
115478
115890
  } else if (target === "gemini") {
115479
- log(import_picocolors42.default.dim(" Gemini CLI will load this context file automatically."));
115891
+ log(import_picocolors43.default.dim(" Gemini CLI will load this context file automatically."));
115480
115892
  } else if (target === "cursor") {
115481
- log(import_picocolors42.default.dim(" Cursor will load this rule automatically."));
115893
+ log(import_picocolors43.default.dim(" Cursor will load this rule automatically."));
115482
115894
  } else if (target === "codex") {
115483
- log(import_picocolors42.default.dim(" Codex CLI will load this file automatically."));
115895
+ log(import_picocolors43.default.dim(" Codex CLI will load this file automatically."));
115484
115896
  } else {
115485
- log(import_picocolors42.default.dim(` Copy or reference this file in your ${target} agent instructions.`));
115897
+ log(import_picocolors43.default.dim(` Copy or reference this file in your ${target} agent instructions.`));
115486
115898
  }
115487
115899
  return 0;
115488
115900
  }
115489
115901
  async function runSkillPrint(target, opts) {
115490
115902
  const log = opts.write ?? ((line2) => console.log(line2));
115491
115903
  if (!isValidTarget(target)) {
115492
- log(`${import_picocolors42.default.red("\u2717")} Unknown target: ${import_picocolors42.default.bold(target)}`);
115493
- log(import_picocolors42.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
115904
+ log(`${import_picocolors43.default.red("\u2717")} Unknown target: ${import_picocolors43.default.bold(target)}`);
115905
+ log(import_picocolors43.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
115494
115906
  return 1;
115495
115907
  }
115496
115908
  process.stdout.write(generateSkillContent(target));
@@ -115499,8 +115911,8 @@ async function runSkillPrint(target, opts) {
115499
115911
  async function runSkillPath(target, opts) {
115500
115912
  const log = opts.write ?? ((line2) => console.log(line2));
115501
115913
  if (!isValidTarget(target)) {
115502
- log(`${import_picocolors42.default.red("\u2717")} Unknown target: ${import_picocolors42.default.bold(target)}`);
115503
- log(import_picocolors42.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
115914
+ log(`${import_picocolors43.default.red("\u2717")} Unknown target: ${import_picocolors43.default.bold(target)}`);
115915
+ log(import_picocolors43.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
115504
115916
  return 1;
115505
115917
  }
115506
115918
  const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
@@ -115510,14 +115922,14 @@ async function runSkillPath(target, opts) {
115510
115922
 
115511
115923
  // ../../packages/cli/src/commands/update.ts
115512
115924
  init_cjs_shims();
115513
- var import_node_fs19 = require("fs");
115514
- var import_node_path21 = require("path");
115925
+ var import_node_fs20 = require("fs");
115926
+ var import_node_path22 = require("path");
115515
115927
  var import_node_os6 = require("os");
115516
115928
  var import_node_crypto7 = require("crypto");
115517
- var import_node_child_process9 = require("child_process");
115518
- var import_node_util5 = require("util");
115519
- var import_picocolors43 = __toESM(require_picocolors(), 1);
115520
- var execFileAsync2 = (0, import_node_util5.promisify)(import_node_child_process9.execFile);
115929
+ var import_node_child_process11 = require("child_process");
115930
+ var import_node_util7 = require("util");
115931
+ var import_picocolors44 = __toESM(require_picocolors(), 1);
115932
+ var execFileAsync4 = (0, import_node_util7.promisify)(import_node_child_process11.execFile);
115521
115933
  async function ensureBackendPinned(write) {
115522
115934
  let installed = null;
115523
115935
  try {
@@ -115526,18 +115938,18 @@ async function ensureBackendPinned(write) {
115526
115938
  installed = null;
115527
115939
  }
115528
115940
  if (installed === PINNED_SOURCE_VERSION) {
115529
- write(` ${import_picocolors43.default.green("\u2713")} Source backend already on pinned ${PINNED_SOURCE_VERSION}.`);
115941
+ write(` ${import_picocolors44.default.green("\u2713")} Source backend already on pinned ${PINNED_SOURCE_VERSION}.`);
115530
115942
  return;
115531
115943
  }
115532
115944
  if (installed === null) {
115533
115945
  write(
115534
- ` ${import_picocolors43.default.dim("Source backend not installed \u2014 install it: curl -fsSL https://horus.sh/install.sh | bash")}`
115946
+ ` ${import_picocolors44.default.dim("Source backend not installed \u2014 install it: curl -fsSL https://horus.sh/install.sh | bash")}`
115535
115947
  );
115536
115948
  return;
115537
115949
  }
115538
115950
  write(` Upgrading source backend ${installed} \u2192 ${PINNED_SOURCE_VERSION}\u2026`);
115539
115951
  try {
115540
- await execFileAsync2(
115952
+ await execFileAsync4(
115541
115953
  "uv",
115542
115954
  // --refresh: a CLI update commonly lands seconds after a backend release, so uv's
115543
115955
  // cached PyPI index may not list the just-published version yet and the install
@@ -115546,10 +115958,10 @@ async function ensureBackendPinned(write) {
115546
115958
  ["tool", "install", "--force", "--refresh", `horus-source==${PINNED_SOURCE_VERSION}`],
115547
115959
  { timeout: 3e5 }
115548
115960
  );
115549
- write(` ${import_picocolors43.default.green("\u2713")} Source backend upgraded to ${PINNED_SOURCE_VERSION}.`);
115961
+ write(` ${import_picocolors44.default.green("\u2713")} Source backend upgraded to ${PINNED_SOURCE_VERSION}.`);
115550
115962
  } catch {
115551
- write(` ${import_picocolors43.default.yellow("!")} Couldn't auto-upgrade the source backend. Re-run the installer:`);
115552
- write(` ${import_picocolors43.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
115963
+ write(` ${import_picocolors44.default.yellow("!")} Couldn't auto-upgrade the source backend. Re-run the installer:`);
115964
+ write(` ${import_picocolors44.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
115553
115965
  }
115554
115966
  }
115555
115967
  var RELEASES_API = "https://api.github.com/repos/meritt-dev/horus/releases/latest";
@@ -115582,7 +115994,7 @@ function resolveBinaryPath() {
115582
115994
  const raw = process.argv[1];
115583
115995
  if (!raw) throw new Error("Cannot determine binary path from process.argv[1]");
115584
115996
  try {
115585
- return (0, import_node_fs19.realpathSync)(raw);
115997
+ return (0, import_node_fs20.realpathSync)(raw);
115586
115998
  } catch {
115587
115999
  return raw;
115588
116000
  }
@@ -115592,33 +116004,33 @@ async function runUpdate(opts) {
115592
116004
  const _fetch = opts._fetch ?? fetch;
115593
116005
  const currentVersion = opts._currentVersion ?? HORUS_VERSION;
115594
116006
  write(`
115595
- ${import_picocolors43.default.bold("Horus update")}
116007
+ ${import_picocolors44.default.bold("Horus update")}
115596
116008
  `);
115597
- write(` Current version: ${import_picocolors43.default.cyan(currentVersion)}`);
116009
+ write(` Current version: ${import_picocolors44.default.cyan(currentVersion)}`);
115598
116010
  let release;
115599
116011
  try {
115600
116012
  release = await fetchLatestRelease(_fetch);
115601
116013
  } catch (e) {
115602
- write(` ${import_picocolors43.default.red("\u2717")} Could not reach GitHub: ${e.message}`);
116014
+ write(` ${import_picocolors44.default.red("\u2717")} Could not reach GitHub: ${e.message}`);
115603
116015
  return 1;
115604
116016
  }
115605
116017
  const latest = release.tag_name.replace(/^v/, "");
115606
- write(` Latest version: ${import_picocolors43.default.cyan(latest)}`);
116018
+ write(` Latest version: ${import_picocolors44.default.cyan(latest)}`);
115607
116019
  const newer = isNewer(latest, currentVersion);
115608
116020
  if (opts.check) {
115609
116021
  if (newer) {
115610
116022
  write(`
115611
- ${import_picocolors43.default.yellow("\u2192")} ${import_picocolors43.default.bold(latest)} is available.`);
115612
- write(` Run ${import_picocolors43.default.bold("horus update")} to upgrade.`);
116023
+ ${import_picocolors44.default.yellow("\u2192")} ${import_picocolors44.default.bold(latest)} is available.`);
116024
+ write(` Run ${import_picocolors44.default.bold("horus update")} to upgrade.`);
115613
116025
  } else {
115614
116026
  write(`
115615
- ${import_picocolors43.default.green("\u2713")} Already on the latest version.`);
116027
+ ${import_picocolors44.default.green("\u2713")} Already on the latest version.`);
115616
116028
  }
115617
116029
  return 0;
115618
116030
  }
115619
116031
  if (!newer && !opts.force) {
115620
116032
  write(`
115621
- ${import_picocolors43.default.green("\u2713")} Already on the latest version.`);
116033
+ ${import_picocolors44.default.green("\u2713")} Already on the latest version.`);
115622
116034
  await ensureBackendPinned(write);
115623
116035
  return 0;
115624
116036
  }
@@ -115628,8 +116040,8 @@ ${import_picocolors43.default.bold("Horus update")}
115628
116040
  const binaryAsset = release.assets.find((a2) => a2.name === assetName);
115629
116041
  const checksumAsset = release.assets.find((a2) => a2.name === checksumName);
115630
116042
  if (!binaryAsset) {
115631
- write(` ${import_picocolors43.default.red("\u2717")} Release ${tag} has no binary asset named '${assetName}'.`);
115632
- write(` Fallback: ${import_picocolors43.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
116043
+ write(` ${import_picocolors44.default.red("\u2717")} Release ${tag} has no binary asset named '${assetName}'.`);
116044
+ write(` Fallback: ${import_picocolors44.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
115633
116045
  return 1;
115634
116046
  }
115635
116047
  write(`
@@ -115639,7 +116051,7 @@ ${import_picocolors43.default.bold("Horus update")}
115639
116051
  redirect: "follow"
115640
116052
  });
115641
116053
  if (!binRes.ok || !binRes.body) {
115642
- write(` ${import_picocolors43.default.red("\u2717")} Download failed: ${binRes.status}`);
116054
+ write(` ${import_picocolors44.default.red("\u2717")} Download failed: ${binRes.status}`);
115643
116055
  return 1;
115644
116056
  }
115645
116057
  const binBuf = Buffer.from(await binRes.arrayBuffer());
@@ -115653,82 +116065,82 @@ ${import_picocolors43.default.bold("Horus update")}
115653
116065
  const expectedHash = csText.trim().split(/\s+/)[0];
115654
116066
  const actualHash = sha256hex(binBuf);
115655
116067
  if (expectedHash !== actualHash) {
115656
- write(` ${import_picocolors43.default.red("\u2717")} Checksum mismatch \u2014 download may be corrupt.`);
116068
+ write(` ${import_picocolors44.default.red("\u2717")} Checksum mismatch \u2014 download may be corrupt.`);
115657
116069
  write(` Expected: ${expectedHash}`);
115658
116070
  write(` Got: ${actualHash}`);
115659
116071
  return 1;
115660
116072
  }
115661
- write(` ${import_picocolors43.default.green("\u2713")} Checksum verified.`);
116073
+ write(` ${import_picocolors44.default.green("\u2713")} Checksum verified.`);
115662
116074
  }
115663
116075
  }
115664
116076
  let binaryPath;
115665
116077
  try {
115666
116078
  binaryPath = resolveBinaryPath();
115667
116079
  } catch (e) {
115668
- write(` ${import_picocolors43.default.red("\u2717")} ${e.message}`);
116080
+ write(` ${import_picocolors44.default.red("\u2717")} ${e.message}`);
115669
116081
  return 1;
115670
116082
  }
115671
- if (!(0, import_node_fs19.existsSync)(binaryPath)) {
115672
- write(` ${import_picocolors43.default.red("\u2717")} Binary not found at ${binaryPath}`);
116083
+ if (!(0, import_node_fs20.existsSync)(binaryPath)) {
116084
+ write(` ${import_picocolors44.default.red("\u2717")} Binary not found at ${binaryPath}`);
115673
116085
  return 1;
115674
116086
  }
115675
- const tmpPath = (0, import_node_path21.join)((0, import_node_os6.tmpdir)(), `horus-update-${tag}-${process.pid}`);
116087
+ const tmpPath = (0, import_node_path22.join)((0, import_node_os6.tmpdir)(), `horus-update-${tag}-${process.pid}`);
115676
116088
  const backupPath = `${binaryPath}.bak`;
115677
116089
  try {
115678
- (0, import_node_fs19.writeFileSync)(tmpPath, binBuf, { mode: 493 });
116090
+ (0, import_node_fs20.writeFileSync)(tmpPath, binBuf, { mode: 493 });
115679
116091
  } catch (e) {
115680
- write(` ${import_picocolors43.default.red("\u2717")} Could not write to temp dir: ${e.message}`);
116092
+ write(` ${import_picocolors44.default.red("\u2717")} Could not write to temp dir: ${e.message}`);
115681
116093
  return 1;
115682
116094
  }
115683
116095
  try {
115684
- (0, import_node_fs19.renameSync)(binaryPath, backupPath);
116096
+ (0, import_node_fs20.renameSync)(binaryPath, backupPath);
115685
116097
  try {
115686
- (0, import_node_fs19.renameSync)(tmpPath, binaryPath);
115687
- (0, import_node_fs19.chmodSync)(binaryPath, 493);
116098
+ (0, import_node_fs20.renameSync)(tmpPath, binaryPath);
116099
+ (0, import_node_fs20.chmodSync)(binaryPath, 493);
115688
116100
  } catch (e) {
115689
116101
  try {
115690
- (0, import_node_fs19.renameSync)(backupPath, binaryPath);
116102
+ (0, import_node_fs20.renameSync)(backupPath, binaryPath);
115691
116103
  } catch {
115692
116104
  }
115693
116105
  throw e;
115694
116106
  }
115695
116107
  try {
115696
- (0, import_node_fs19.unlinkSync)(backupPath);
116108
+ (0, import_node_fs20.unlinkSync)(backupPath);
115697
116109
  } catch {
115698
116110
  }
115699
116111
  } catch (e) {
115700
116112
  try {
115701
- (0, import_node_fs19.unlinkSync)(tmpPath);
116113
+ (0, import_node_fs20.unlinkSync)(tmpPath);
115702
116114
  } catch {
115703
116115
  }
115704
116116
  const msg = e.message ?? String(e);
115705
116117
  const needsSudo = e.code === "EACCES" || e.code === "EPERM";
115706
- write(` ${import_picocolors43.default.red("\u2717")} Could not replace binary at ${binaryPath}: ${msg}`);
116118
+ write(` ${import_picocolors44.default.red("\u2717")} Could not replace binary at ${binaryPath}: ${msg}`);
115707
116119
  if (needsSudo) {
115708
116120
  write(`
115709
116121
  Try with sudo:`);
115710
116122
  write(
115711
- ` ${import_picocolors43.default.dim(`sudo curl -fsSL https://github.com/meritt-dev/horus/releases/download/${tag}/${assetName} -o ${binaryPath} && sudo chmod +x ${binaryPath}`)}`
116123
+ ` ${import_picocolors44.default.dim(`sudo curl -fsSL https://github.com/meritt-dev/horus/releases/download/${tag}/${assetName} -o ${binaryPath} && sudo chmod +x ${binaryPath}`)}`
115712
116124
  );
115713
116125
  }
115714
116126
  return 1;
115715
116127
  }
115716
- write(` ${import_picocolors43.default.green("\u2713")} Updated: ${import_picocolors43.default.bold(currentVersion)} \u2192 ${import_picocolors43.default.bold(latest)}`);
115717
- write(` ${import_picocolors43.default.dim(binaryPath)}`);
116128
+ write(` ${import_picocolors44.default.green("\u2713")} Updated: ${import_picocolors44.default.bold(currentVersion)} \u2192 ${import_picocolors44.default.bold(latest)}`);
116129
+ write(` ${import_picocolors44.default.dim(binaryPath)}`);
115718
116130
  await ensureBackendPinned(write);
115719
116131
  return 0;
115720
116132
  }
115721
116133
 
115722
116134
  // ../../packages/cli/src/commands/login.ts
115723
116135
  init_cjs_shims();
115724
- var import_node_child_process10 = require("child_process");
115725
- var import_picocolors44 = __toESM(require_picocolors(), 1);
116136
+ var import_node_child_process12 = require("child_process");
116137
+ var import_picocolors45 = __toESM(require_picocolors(), 1);
115726
116138
  var sleep2 = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
115727
116139
  function openBrowser(url) {
115728
116140
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
115729
116141
  const args2 = process.platform === "win32" ? ["/c", "start", "", url] : [url];
115730
116142
  try {
115731
- (0, import_node_child_process10.execFile)(cmd, args2, () => {
116143
+ (0, import_node_child_process12.execFile)(cmd, args2, () => {
115732
116144
  });
115733
116145
  } catch {
115734
116146
  }
@@ -115747,19 +116159,19 @@ async function loginWithToken(apiBaseUrl, token) {
115747
116159
  }
115748
116160
  });
115749
116161
  console.log(
115750
- `${import_picocolors44.default.green("\u2713")} Logged in as ${import_picocolors44.default.bold(me3.user.primaryEmail)}. Run ${import_picocolors44.default.bold("horus cloud link")} to connect this repo.`
116162
+ `${import_picocolors45.default.green("\u2713")} Logged in as ${import_picocolors45.default.bold(me3.user.primaryEmail)}. Run ${import_picocolors45.default.bold("horus cloud link")} to connect this repo.`
115751
116163
  );
115752
116164
  return 0;
115753
116165
  } catch (err2) {
115754
116166
  if (err2 instanceof CloudOfflineError) {
115755
- console.error(import_picocolors44.default.red(err2.message));
116167
+ console.error(import_picocolors45.default.red(err2.message));
115756
116168
  return 1;
115757
116169
  }
115758
116170
  if (err2 instanceof CloudError && err2.status === 401) {
115759
- console.error(import_picocolors44.default.red("That token is not valid. Create a fresh one in the dashboard and try again."));
116171
+ console.error(import_picocolors45.default.red("That token is not valid. Create a fresh one in the dashboard and try again."));
115760
116172
  return 1;
115761
116173
  }
115762
- console.error(import_picocolors44.default.red(`Login failed: ${err2.message}`));
116174
+ console.error(import_picocolors45.default.red(`Login failed: ${err2.message}`));
115763
116175
  return 1;
115764
116176
  }
115765
116177
  }
@@ -115770,19 +116182,19 @@ async function loginWithBrowser(apiBaseUrl) {
115770
116182
  session = await client.startCliSession();
115771
116183
  } catch (err2) {
115772
116184
  if (err2 instanceof CloudOfflineError) {
115773
- console.error(import_picocolors44.default.red(err2.message));
116185
+ console.error(import_picocolors45.default.red(err2.message));
115774
116186
  return 1;
115775
116187
  }
115776
- console.error(import_picocolors44.default.red(`Could not start login: ${err2.message}`));
116188
+ console.error(import_picocolors45.default.red(`Could not start login: ${err2.message}`));
115777
116189
  return 1;
115778
116190
  }
115779
116191
  console.log("");
115780
116192
  console.log(` To sign in, open this URL in your browser:`);
115781
- console.log(` ${import_picocolors44.default.cyan(import_picocolors44.default.bold(session.verificationUri))}`);
116193
+ console.log(` ${import_picocolors45.default.cyan(import_picocolors45.default.bold(session.verificationUri))}`);
115782
116194
  console.log("");
115783
- console.log(` and enter the code: ${import_picocolors44.default.bold(session.userCode)}`);
116195
+ console.log(` and enter the code: ${import_picocolors45.default.bold(session.userCode)}`);
115784
116196
  console.log("");
115785
- console.log(import_picocolors44.default.dim(` Waiting for approval\u2026 (press Ctrl+C to cancel)`));
116197
+ console.log(import_picocolors45.default.dim(` Waiting for approval\u2026 (press Ctrl+C to cancel)`));
115786
116198
  openBrowser(session.verificationUriComplete);
115787
116199
  const deadline = Date.now() + session.expiresIn * 1e3;
115788
116200
  let intervalMs = Math.max(session.interval, 1) * 1e3;
@@ -115793,7 +116205,7 @@ async function loginWithBrowser(apiBaseUrl) {
115793
116205
  poll = await client.pollCliSession(session.deviceCode);
115794
116206
  } catch (err2) {
115795
116207
  if (err2 instanceof CloudOfflineError) {
115796
- console.error(import_picocolors44.default.red(`
116208
+ console.error(import_picocolors45.default.red(`
115797
116209
  ${err2.message}`));
115798
116210
  return 1;
115799
116211
  }
@@ -115806,14 +116218,14 @@ ${err2.message}`));
115806
116218
  intervalMs += 2e3;
115807
116219
  break;
115808
116220
  case "denied":
115809
- console.error(import_picocolors44.default.red("\nLogin was denied in the browser."));
116221
+ console.error(import_picocolors45.default.red("\nLogin was denied in the browser."));
115810
116222
  return 1;
115811
116223
  case "expired":
115812
- console.error(import_picocolors44.default.red("\nThis login request expired. Run `horus login` again."));
116224
+ console.error(import_picocolors45.default.red("\nThis login request expired. Run `horus login` again."));
115813
116225
  return 1;
115814
116226
  case "approved":
115815
116227
  if (!poll.token || !poll.account) {
115816
- console.error(import_picocolors44.default.red("\nLogin approved but no token was returned. Try again."));
116228
+ console.error(import_picocolors45.default.red("\nLogin approved but no token was returned. Try again."));
115817
116229
  return 1;
115818
116230
  }
115819
116231
  writeAuth({
@@ -115823,12 +116235,12 @@ ${err2.message}`));
115823
116235
  });
115824
116236
  console.log(
115825
116237
  `
115826
- ${import_picocolors44.default.green("\u2713")} Logged in as ${import_picocolors44.default.bold(poll.account.email)}. Run ${import_picocolors44.default.bold("horus cloud link")} to connect this repo.`
116238
+ ${import_picocolors45.default.green("\u2713")} Logged in as ${import_picocolors45.default.bold(poll.account.email)}. Run ${import_picocolors45.default.bold("horus cloud link")} to connect this repo.`
115827
116239
  );
115828
116240
  return 0;
115829
116241
  }
115830
116242
  }
115831
- console.error(import_picocolors44.default.red("\nTimed out waiting for approval. Run `horus login` again."));
116243
+ console.error(import_picocolors45.default.red("\nTimed out waiting for approval. Run `horus login` again."));
115832
116244
  return 1;
115833
116245
  }
115834
116246
  async function runLogin(opts = {}) {
@@ -115842,7 +116254,7 @@ async function runLogin(opts = {}) {
115842
116254
  async function runLogout() {
115843
116255
  const auth = readAuth();
115844
116256
  if (!auth) {
115845
- console.log(import_picocolors44.default.dim("Not logged in."));
116257
+ console.log(import_picocolors45.default.dim("Not logged in."));
115846
116258
  return 0;
115847
116259
  }
115848
116260
  const session = authedClient();
@@ -115856,18 +116268,18 @@ async function runLogout() {
115856
116268
  }
115857
116269
  }
115858
116270
  clearAuth();
115859
- console.log(`${import_picocolors44.default.green("\u2713")} Logged out. Local mode still works.`);
116271
+ console.log(`${import_picocolors45.default.green("\u2713")} Logged out. Local mode still works.`);
115860
116272
  return 0;
115861
116273
  }
115862
116274
 
115863
116275
  // ../../packages/cli/src/commands/cloud.ts
115864
116276
  init_cjs_shims();
115865
- var import_picocolors45 = __toESM(require_picocolors(), 1);
116277
+ var import_picocolors46 = __toESM(require_picocolors(), 1);
115866
116278
  var import_promises3 = require("readline/promises");
115867
116279
 
115868
116280
  // ../../packages/cli/src/lib/cloud/git.ts
115869
116281
  init_cjs_shims();
115870
- var import_node_child_process11 = require("child_process");
116282
+ var import_node_child_process13 = require("child_process");
115871
116283
  function parseRepoName(remoteUrl) {
115872
116284
  const cleaned = remoteUrl.trim().replace(/\.git$/, "");
115873
116285
  const lastSegment = cleaned.split(/[/:]/).filter(Boolean).pop() ?? "";
@@ -115891,7 +116303,7 @@ function inferProvider(remoteUrl) {
115891
116303
  }
115892
116304
  function detectGitRemote(root) {
115893
116305
  try {
115894
- const remoteUrl = (0, import_node_child_process11.execFileSync)("git", ["-C", root, "remote", "get-url", "origin"], {
116306
+ const remoteUrl = (0, import_node_child_process13.execFileSync)("git", ["-C", root, "remote", "get-url", "origin"], {
115895
116307
  encoding: "utf8",
115896
116308
  stdio: ["ignore", "pipe", "ignore"]
115897
116309
  }).trim();
@@ -115907,7 +116319,7 @@ async function runCloudLink(opts = {}) {
115907
116319
  const root = repoRootOrCwd(opts.cwd);
115908
116320
  const session = authedClient();
115909
116321
  if (!session) {
115910
- console.error(import_picocolors45.default.red(`Not logged in. Run ${import_picocolors45.default.bold("horus login")} first.`));
116322
+ console.error(import_picocolors46.default.red(`Not logged in. Run ${import_picocolors46.default.bold("horus login")} first.`));
115911
116323
  return 1;
115912
116324
  }
115913
116325
  let ctx;
@@ -115917,7 +116329,7 @@ async function runCloudLink(opts = {}) {
115917
116329
  return reportCloudError(err2);
115918
116330
  }
115919
116331
  const remote = detectGitRemote(root);
115920
- if (remote) console.log(import_picocolors45.default.dim(`Detected git remote: ${remote.remoteUrl}`));
116332
+ if (remote) console.log(import_picocolors46.default.dim(`Detected git remote: ${remote.remoteUrl}`));
115921
116333
  let resolved = null;
115922
116334
  let target = opts.project;
115923
116335
  if (!target) {
@@ -115933,7 +116345,7 @@ async function runCloudLink(opts = {}) {
115933
116345
  const org = ctx.organizations.find((o5) => o5.id === match.organizationId);
115934
116346
  if (ws && org) {
115935
116347
  target = `${org.slug}/${ws.slug}/${match.slug}`;
115936
- console.log(import_picocolors45.default.dim(`Matched existing project by remote: ${import_picocolors45.default.bold(target)}`));
116348
+ console.log(import_picocolors46.default.dim(`Matched existing project by remote: ${import_picocolors46.default.bold(target)}`));
115937
116349
  }
115938
116350
  }
115939
116351
  }
@@ -115942,8 +116354,8 @@ async function runCloudLink(opts = {}) {
115942
116354
  if (created === "aborted") return 1;
115943
116355
  if (!created) {
115944
116356
  console.error(
115945
- import_picocolors45.default.red("No project to link.") + `
115946
- Run ${import_picocolors45.default.bold("horus cloud link")} in a TTY to create one, pass ${import_picocolors45.default.bold("--project <org/workspace/project>")}, or create a project at ${import_picocolors45.default.bold("https://cloud.horus.sh")}.`
116357
+ import_picocolors46.default.red("No project to link.") + `
116358
+ Run ${import_picocolors46.default.bold("horus cloud link")} in a TTY to create one, pass ${import_picocolors46.default.bold("--project <org/workspace/project>")}, or create a project at ${import_picocolors46.default.bold("https://cloud.horus.sh")}.`
115947
116359
  );
115948
116360
  return 1;
115949
116361
  }
@@ -115953,8 +116365,8 @@ async function runCloudLink(opts = {}) {
115953
116365
  resolved = resolveTriple(ctx, target);
115954
116366
  if (!resolved) {
115955
116367
  console.error(
115956
- import_picocolors45.default.red(`You don't have access to ${import_picocolors45.default.bold(target)}.`) + `
115957
- Run ${import_picocolors45.default.bold("horus context list")} to see available projects.`
116368
+ import_picocolors46.default.red(`You don't have access to ${import_picocolors46.default.bold(target)}.`) + `
116369
+ Run ${import_picocolors46.default.bold("horus context list")} to see available projects.`
115958
116370
  );
115959
116371
  return 1;
115960
116372
  }
@@ -115967,10 +116379,10 @@ async function runCloudLink(opts = {}) {
115967
116379
  project: resolved.project
115968
116380
  });
115969
116381
  console.log(
115970
- `${import_picocolors45.default.green("\u2713")} Linked. Context for this repo is now ${import_picocolors45.default.bold(label)} ${import_picocolors45.default.dim("(cloud)")}.`
116382
+ `${import_picocolors46.default.green("\u2713")} Linked. Context for this repo is now ${import_picocolors46.default.bold(label)} ${import_picocolors46.default.dim("(cloud)")}.`
115971
116383
  );
115972
116384
  console.log(
115973
- import_picocolors45.default.dim(` Investigations here save to Horus Cloud. ${import_picocolors45.default.bold("horus context use local")} to switch back.`)
116385
+ import_picocolors46.default.dim(` Investigations here save to Horus Cloud. ${import_picocolors46.default.bold("horus context use local")} to switch back.`)
115974
116386
  );
115975
116387
  return 0;
115976
116388
  }
@@ -115978,11 +116390,11 @@ async function runCloudUnlink(opts = {}) {
115978
116390
  const root = repoRootOrCwd(opts.cwd);
115979
116391
  const cfg = readCloudConfig(root);
115980
116392
  if (!cfg || cfg.context !== "cloud") {
115981
- console.log(import_picocolors45.default.dim("This repo isn't linked to a cloud project."));
116393
+ console.log(import_picocolors46.default.dim("This repo isn't linked to a cloud project."));
115982
116394
  return 0;
115983
116395
  }
115984
116396
  clearCloudConfig(root);
115985
- console.log(`${import_picocolors45.default.green("\u2713")} Unlinked. This repo is back to ${import_picocolors45.default.bold("local")} mode.`);
116397
+ console.log(`${import_picocolors46.default.green("\u2713")} Unlinked. This repo is back to ${import_picocolors46.default.bold("local")} mode.`);
115986
116398
  return 0;
115987
116399
  }
115988
116400
  async function runCloudStatus(opts = {}) {
@@ -115990,18 +116402,18 @@ async function runCloudStatus(opts = {}) {
115990
116402
  const cfg = readCloudConfig(root);
115991
116403
  const session = authedClient();
115992
116404
  if (!isCloudActive(cfg) || !cfg?.project) {
115993
- console.log(`${import_picocolors45.default.bold("Context:")} local`);
115994
- console.log(`${import_picocolors45.default.bold("Storage:")} local Postgres (~/.horus)`);
116405
+ console.log(`${import_picocolors46.default.bold("Context:")} local`);
116406
+ console.log(`${import_picocolors46.default.bold("Storage:")} local Postgres (~/.horus)`);
115995
116407
  console.log(
115996
- import_picocolors45.default.dim(`Tip: ${import_picocolors45.default.bold("horus login")} then ${import_picocolors45.default.bold("horus cloud link")} to share investigations with your team.`)
116408
+ import_picocolors46.default.dim(`Tip: ${import_picocolors46.default.bold("horus login")} then ${import_picocolors46.default.bold("horus cloud link")} to share investigations with your team.`)
115997
116409
  );
115998
116410
  return 0;
115999
116411
  }
116000
116412
  const triple = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
116001
- console.log(`${import_picocolors45.default.bold("Context:")} ${triple} ${import_picocolors45.default.dim("(cloud)")}`);
116413
+ console.log(`${import_picocolors46.default.bold("Context:")} ${triple} ${import_picocolors46.default.dim("(cloud)")}`);
116002
116414
  let projectLine = `${cfg.project.slug}`;
116003
116415
  let metaLines = [];
116004
- let account = import_picocolors45.default.dim("not logged in");
116416
+ let account = import_picocolors46.default.dim("not logged in");
116005
116417
  if (session) {
116006
116418
  account = session.auth.account.email;
116007
116419
  try {
@@ -116016,9 +116428,9 @@ async function runCloudStatus(opts = {}) {
116016
116428
  } catch {
116017
116429
  }
116018
116430
  }
116019
- console.log(`${import_picocolors45.default.bold("Project:")} ${projectLine}`);
116431
+ console.log(`${import_picocolors46.default.bold("Project:")} ${projectLine}`);
116020
116432
  for (const line2 of metaLines) console.log(line2);
116021
- console.log(`${import_picocolors45.default.bold("Account:")} ${account}`);
116433
+ console.log(`${import_picocolors46.default.bold("Account:")} ${account}`);
116022
116434
  return 0;
116023
116435
  }
116024
116436
  async function runCloudSync(opts = {}) {
@@ -116026,13 +116438,13 @@ async function runCloudSync(opts = {}) {
116026
116438
  const cfg = readCloudConfig(root);
116027
116439
  if (!isCloudActive(cfg) || !cfg.project) {
116028
116440
  console.error(
116029
- import_picocolors45.default.red("This repo isn't linked to a cloud project.") + ` Run ${import_picocolors45.default.bold("horus cloud link")} first.`
116441
+ import_picocolors46.default.red("This repo isn't linked to a cloud project.") + ` Run ${import_picocolors46.default.bold("horus cloud link")} first.`
116030
116442
  );
116031
116443
  return 1;
116032
116444
  }
116033
116445
  const session = authedClient();
116034
116446
  if (!session) {
116035
- console.error(import_picocolors45.default.red(`Not logged in. Run ${import_picocolors45.default.bold("horus login")} first.`));
116447
+ console.error(import_picocolors46.default.red(`Not logged in. Run ${import_picocolors46.default.bold("horus login")} first.`));
116036
116448
  return 1;
116037
116449
  }
116038
116450
  const { db, sql: sql2 } = await openDb(await resolveDbUrl(opts.config));
@@ -116053,26 +116465,26 @@ async function runCloudSync(opts = {}) {
116053
116465
  const target = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
116054
116466
  if (uploadable.length === 0) {
116055
116467
  console.log(
116056
- import_picocolors45.default.dim(
116468
+ import_picocolors46.default.dim(
116057
116469
  `No local investigations with reports to upload.` + (skippedNoReport ? ` (${skippedNoReport} without a report skipped)` : "")
116058
116470
  )
116059
116471
  );
116060
116472
  return 0;
116061
116473
  }
116062
- console.log(import_picocolors45.default.bold(`Will upload ${uploadable.length} investigation(s) to ${target} ${import_picocolors45.default.dim("(cloud)")}:`));
116474
+ console.log(import_picocolors46.default.bold(`Will upload ${uploadable.length} investigation(s) to ${target} ${import_picocolors46.default.dim("(cloud)")}:`));
116063
116475
  for (const c4 of uploadable.slice(0, 20)) {
116064
- console.log(` ${import_picocolors45.default.dim(c4.id.slice(0, 8))} ${(c4.title ?? "untitled").slice(0, 70)}`);
116476
+ console.log(` ${import_picocolors46.default.dim(c4.id.slice(0, 8))} ${(c4.title ?? "untitled").slice(0, 70)}`);
116065
116477
  }
116066
- if (uploadable.length > 20) console.log(import_picocolors45.default.dim(` \u2026and ${uploadable.length - 20} more`));
116067
- if (skippedNoReport) console.log(import_picocolors45.default.dim(` (${skippedNoReport} local investigation(s) without a report will be skipped)`));
116478
+ if (uploadable.length > 20) console.log(import_picocolors46.default.dim(` \u2026and ${uploadable.length - 20} more`));
116479
+ if (skippedNoReport) console.log(import_picocolors46.default.dim(` (${skippedNoReport} local investigation(s) without a report will be skipped)`));
116068
116480
  if (opts.dryRun) {
116069
- console.log(import_picocolors45.default.dim("Dry run \u2014 nothing uploaded. Re-run without --dry-run to upload."));
116481
+ console.log(import_picocolors46.default.dim("Dry run \u2014 nothing uploaded. Re-run without --dry-run to upload."));
116070
116482
  return 0;
116071
116483
  }
116072
116484
  if (!opts.yes) {
116073
116485
  if (!process.stdin.isTTY) {
116074
116486
  console.error(
116075
- import_picocolors45.default.yellow(`Re-run with ${import_picocolors45.default.bold("--yes")} to upload, or ${import_picocolors45.default.bold("--dry-run")} to preview only.`)
116487
+ import_picocolors46.default.yellow(`Re-run with ${import_picocolors46.default.bold("--yes")} to upload, or ${import_picocolors46.default.bold("--dry-run")} to preview only.`)
116076
116488
  );
116077
116489
  return 1;
116078
116490
  }
@@ -116080,7 +116492,7 @@ async function runCloudSync(opts = {}) {
116080
116492
  try {
116081
116493
  const answer = (await rl.question(`Upload ${uploadable.length} investigation(s) to ${target}? (y/N) `)).trim().toLowerCase();
116082
116494
  if (answer !== "y" && answer !== "yes") {
116083
- console.log(import_picocolors45.default.dim("Aborted. Nothing uploaded."));
116495
+ console.log(import_picocolors46.default.dim("Aborted. Nothing uploaded."));
116084
116496
  return 0;
116085
116497
  }
116086
116498
  } finally {
@@ -116093,18 +116505,18 @@ async function runCloudSync(opts = {}) {
116093
116505
  try {
116094
116506
  const refs = await uploadInvestigationToCloud(session.client, cfg, c4.report);
116095
116507
  created++;
116096
- console.log(`${import_picocolors45.default.green("\u2713")} ${import_picocolors45.default.dim(c4.id.slice(0, 8))} \u2192 ${refs.investigationId}`);
116508
+ console.log(`${import_picocolors46.default.green("\u2713")} ${import_picocolors46.default.dim(c4.id.slice(0, 8))} \u2192 ${refs.investigationId}`);
116097
116509
  } catch (err2) {
116098
116510
  failed++;
116099
- console.error(`${import_picocolors45.default.red("\u2717")} ${import_picocolors45.default.dim(c4.id.slice(0, 8))} ${err2.message}`);
116511
+ console.error(`${import_picocolors46.default.red("\u2717")} ${import_picocolors46.default.dim(c4.id.slice(0, 8))} ${err2.message}`);
116100
116512
  }
116101
116513
  }
116102
116514
  console.log("");
116103
116515
  console.log(
116104
- `${import_picocolors45.default.bold("Sync complete:")} ${import_picocolors45.default.green(`${created} uploaded`)}, ${skippedNoReport} skipped, ` + (failed ? import_picocolors45.default.red(`${failed} failed`) : "0 failed") + "."
116516
+ `${import_picocolors46.default.bold("Sync complete:")} ${import_picocolors46.default.green(`${created} uploaded`)}, ${skippedNoReport} skipped, ` + (failed ? import_picocolors46.default.red(`${failed} failed`) : "0 failed") + "."
116105
116517
  );
116106
116518
  console.log(
116107
- import_picocolors45.default.dim("Local data was not modified. Re-running is safe \u2014 duplicates are deduped by idempotency key.")
116519
+ import_picocolors46.default.dim("Local data was not modified. Re-running is safe \u2014 duplicates are deduped by idempotency key.")
116108
116520
  );
116109
116521
  return failed > 0 ? 1 : 0;
116110
116522
  }
@@ -116112,8 +116524,8 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
116112
116524
  if (!process.stdin.isTTY && !yes) return null;
116113
116525
  if (ctx.organizations.length === 0) {
116114
116526
  console.error(
116115
- import_picocolors45.default.red("You're not a member of any organization yet.") + `
116116
- Create one at ${import_picocolors45.default.bold("https://cloud.horus.sh")} first.`
116527
+ import_picocolors46.default.red("You're not a member of any organization yet.") + `
116528
+ Create one at ${import_picocolors46.default.bold("https://cloud.horus.sh")} first.`
116117
116529
  );
116118
116530
  return null;
116119
116531
  }
@@ -116138,9 +116550,9 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
116138
116550
  const wsName = await ask2("New workspace name", repoName || "default");
116139
116551
  const ws = await client.createWorkspace(org.id, { name: wsName });
116140
116552
  workspace = { id: ws.id, slug: ws.slug };
116141
- console.log(import_picocolors45.default.dim(`Created workspace ${import_picocolors45.default.bold(ws.slug)}.`));
116553
+ console.log(import_picocolors46.default.dim(`Created workspace ${import_picocolors46.default.bold(ws.slug)}.`));
116142
116554
  } else {
116143
- console.log(`Workspaces in ${import_picocolors45.default.bold(org.slug)}:`);
116555
+ console.log(`Workspaces in ${import_picocolors46.default.bold(org.slug)}:`);
116144
116556
  orgWorkspaces.forEach((w4, i3) => console.log(` ${i3 + 1}) ${w4.slug}`));
116145
116557
  console.log(` ${orgWorkspaces.length + 1}) + create a new workspace`);
116146
116558
  const pick2 = Number(await ask2("Workspace number", "1"));
@@ -116148,7 +116560,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
116148
116560
  const wsName = await ask2("New workspace name", repoName || "default");
116149
116561
  const ws = await client.createWorkspace(org.id, { name: wsName });
116150
116562
  workspace = { id: ws.id, slug: ws.slug };
116151
- console.log(import_picocolors45.default.dim(`Created workspace ${import_picocolors45.default.bold(ws.slug)}.`));
116563
+ console.log(import_picocolors46.default.dim(`Created workspace ${import_picocolors46.default.bold(ws.slug)}.`));
116152
116564
  } else {
116153
116565
  const chosen = orgWorkspaces[pick2 - 1] ?? orgWorkspaces[0];
116154
116566
  workspace = { id: chosen.id, slug: chosen.slug };
@@ -116160,7 +116572,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
116160
116572
  remoteUrl: remote?.remoteUrl,
116161
116573
  provider: remote ? inferProvider(remote.remoteUrl) : void 0
116162
116574
  });
116163
- console.log(import_picocolors45.default.dim(`Created project ${import_picocolors45.default.bold(project.slug)}.`));
116575
+ console.log(import_picocolors46.default.dim(`Created project ${import_picocolors46.default.bold(project.slug)}.`));
116164
116576
  return {
116165
116577
  organization: { id: org.id, slug: org.slug },
116166
116578
  workspace,
@@ -116176,7 +116588,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
116176
116588
 
116177
116589
  // ../../packages/cli/src/commands/telemetry.ts
116178
116590
  init_cjs_shims();
116179
- var import_picocolors47 = __toESM(require_picocolors(), 1);
116591
+ var import_picocolors48 = __toESM(require_picocolors(), 1);
116180
116592
  var import_node_crypto8 = require("crypto");
116181
116593
 
116182
116594
  // ../../packages/cli/src/lib/telemetry/transport.ts
@@ -116237,7 +116649,7 @@ async function deleteRemoteTelemetry(installId, opts = {}) {
116237
116649
 
116238
116650
  // ../../packages/cli/src/lib/telemetry/notice.ts
116239
116651
  init_cjs_shims();
116240
- var import_picocolors46 = __toESM(require_picocolors(), 1);
116652
+ var import_picocolors47 = __toESM(require_picocolors(), 1);
116241
116653
  var PRIVACY_URL = "https://horus.sh/privacy";
116242
116654
  var SUPPRESS_FIRST_TOKEN = /* @__PURE__ */ new Set(["telemetry", "help"]);
116243
116655
  var SUPPRESS_FLAGS = /* @__PURE__ */ new Set(["-V", "--version", "-h", "--help"]);
@@ -116249,11 +116661,11 @@ function shouldSuppressFor(argv) {
116249
116661
  return firstToken != null && SUPPRESS_FIRST_TOKEN.has(firstToken);
116250
116662
  }
116251
116663
  function banner() {
116252
- const b5 = import_picocolors46.default.bold;
116253
- const dim = import_picocolors46.default.dim;
116664
+ const b5 = import_picocolors47.default.bold;
116665
+ const dim = import_picocolors47.default.dim;
116254
116666
  return [
116255
116667
  "",
116256
- import_picocolors46.default.cyan(b5("Horus collects anonymous usage data to improve the tool.")),
116668
+ import_picocolors47.default.cyan(b5("Horus collects anonymous usage data to improve the tool.")),
116257
116669
  dim(" \u2022 On by default: command names, timings, success/failure, and where the"),
116258
116670
  dim(" engine ran out of evidence. No file contents, queries, or secrets."),
116259
116671
  dim(" \u2022 Off by default: your investigation inputs/outputs (opt in to help train"),
@@ -116261,7 +116673,7 @@ function banner() {
116261
116673
  dim(" \u2022 If you use Horus Cloud, linked investigations sync to your workspace."),
116262
116674
  "",
116263
116675
  ` ${dim("Opt out anytime:")} ${b5("horus telemetry disable")} ${dim("\xB7")} ${b5("HORUS_TELEMETRY=0")} ${dim("\xB7")} ${b5("DO_NOT_TRACK=1")}`,
116264
- ` ${dim("Details:")} ${import_picocolors46.default.underline(PRIVACY_URL)}`,
116676
+ ` ${dim("Details:")} ${import_picocolors47.default.underline(PRIVACY_URL)}`,
116265
116677
  ""
116266
116678
  ].join("\n");
116267
116679
  }
@@ -116284,45 +116696,45 @@ function maybeShowFirstRunNotice(argv = process.argv) {
116284
116696
 
116285
116697
  // ../../packages/cli/src/commands/telemetry.ts
116286
116698
  function onOff(enabled) {
116287
- return enabled ? import_picocolors47.default.green("on") : import_picocolors47.default.dim("off");
116699
+ return enabled ? import_picocolors48.default.green("on") : import_picocolors48.default.dim("off");
116288
116700
  }
116289
116701
  function warnIfEnvOverrides() {
116290
116702
  const decision = resolveConsent();
116291
116703
  if (decision.forcedOff) {
116292
116704
  console.log(
116293
- import_picocolors47.default.yellow(
116705
+ import_picocolors48.default.yellow(
116294
116706
  `
116295
116707
  Note: telemetry is currently forced off by your environment (${decision.reason}).`
116296
116708
  )
116297
116709
  );
116298
- console.log(import_picocolors47.default.dim("Your saved preference will apply once that override is removed."));
116710
+ console.log(import_picocolors48.default.dim("Your saved preference will apply once that override is removed."));
116299
116711
  } else if (decision.source === "ci") {
116300
116712
  console.log(
116301
- import_picocolors47.default.yellow("\nNote: CI environment detected \u2014 telemetry stays off here regardless.")
116713
+ import_picocolors48.default.yellow("\nNote: CI environment detected \u2014 telemetry stays off here regardless.")
116302
116714
  );
116303
116715
  }
116304
116716
  }
116305
116717
  async function runTelemetryStatus() {
116306
116718
  const state = readTelemetryState();
116307
116719
  const decision = resolveConsent({ state });
116308
- console.log(import_picocolors47.default.bold("Horus telemetry"));
116720
+ console.log(import_picocolors48.default.bold("Horus telemetry"));
116309
116721
  console.log(
116310
- ` Usage metadata (Tier A): ${onOff(decision.tierA)} ${import_picocolors47.default.dim(`[${decision.source}]`)}`
116722
+ ` Usage metadata (Tier A): ${onOff(decision.tierA)} ${import_picocolors48.default.dim(`[${decision.source}]`)}`
116311
116723
  );
116312
116724
  console.log(` Content sharing (Tier B): ${onOff(decision.tierB)}`);
116313
- console.log(import_picocolors47.default.dim(` Reason: ${decision.reason}`));
116725
+ console.log(import_picocolors48.default.dim(` Reason: ${decision.reason}`));
116314
116726
  if (state) {
116315
- console.log(import_picocolors47.default.dim(` Install ID: ${state.installId}`));
116727
+ console.log(import_picocolors48.default.dim(` Install ID: ${state.installId}`));
116316
116728
  console.log(
116317
- import_picocolors47.default.dim(
116729
+ import_picocolors48.default.dim(
116318
116730
  ` Saved: Tier A ${state.tierA.enabled ? "on" : "off"}, Tier B ${state.tierB.enabled ? "on" : "off"}`
116319
116731
  )
116320
116732
  );
116321
116733
  } else {
116322
- console.log(import_picocolors47.default.dim(" No saved preference yet (defaults apply)."));
116734
+ console.log(import_picocolors48.default.dim(" No saved preference yet (defaults apply)."));
116323
116735
  }
116324
- console.log(import_picocolors47.default.dim(` Config: ${telemetryPath()}`));
116325
- console.log(import_picocolors47.default.dim(` Privacy: ${PRIVACY_URL}`));
116736
+ console.log(import_picocolors48.default.dim(` Config: ${telemetryPath()}`));
116737
+ console.log(import_picocolors48.default.dim(` Privacy: ${PRIVACY_URL}`));
116326
116738
  if (!state || decision.source === "env" || decision.source === "ci") {
116327
116739
  warnIfEnvOverrides();
116328
116740
  }
@@ -116333,7 +116745,7 @@ async function runTelemetryEnable() {
116333
116745
  s5.tierA.enabled = true;
116334
116746
  if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
116335
116747
  });
116336
- console.log(import_picocolors47.default.green("Anonymous usage metadata (Tier A) is now ON. Thank you!"));
116748
+ console.log(import_picocolors48.default.green("Anonymous usage metadata (Tier A) is now ON. Thank you!"));
116337
116749
  warnIfEnvOverrides();
116338
116750
  return 0;
116339
116751
  }
@@ -116343,7 +116755,7 @@ async function runTelemetryDisable() {
116343
116755
  s5.tierB.enabled = false;
116344
116756
  if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
116345
116757
  });
116346
- console.log(import_picocolors47.default.yellow("All telemetry is now OFF (Tier A and Tier B)."));
116758
+ console.log(import_picocolors48.default.yellow("All telemetry is now OFF (Tier A and Tier B)."));
116347
116759
  return 0;
116348
116760
  }
116349
116761
  async function runTelemetryEnableContent() {
@@ -116354,10 +116766,10 @@ async function runTelemetryEnableContent() {
116354
116766
  if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
116355
116767
  });
116356
116768
  console.log(
116357
- import_picocolors47.default.green("Content sharing (Tier B) is now ON \u2014 redacted inputs/outputs help improve Horus.")
116769
+ import_picocolors48.default.green("Content sharing (Tier B) is now ON \u2014 redacted inputs/outputs help improve Horus.")
116358
116770
  );
116359
- console.log(import_picocolors47.default.dim("Content is scrubbed of secrets/PII before it ever leaves your machine."));
116360
- console.log(import_picocolors47.default.dim(`What this means: ${PRIVACY_URL}`));
116771
+ console.log(import_picocolors48.default.dim("Content is scrubbed of secrets/PII before it ever leaves your machine."));
116772
+ console.log(import_picocolors48.default.dim(`What this means: ${PRIVACY_URL}`));
116361
116773
  warnIfEnvOverrides();
116362
116774
  return 0;
116363
116775
  }
@@ -116365,7 +116777,7 @@ async function runTelemetryDisableContent() {
116365
116777
  updateTelemetryState((s5) => {
116366
116778
  s5.tierB.enabled = false;
116367
116779
  });
116368
- console.log(import_picocolors47.default.yellow("Content sharing (Tier B) is now OFF. Usage metadata is unchanged."));
116780
+ console.log(import_picocolors48.default.yellow("Content sharing (Tier B) is now OFF. Usage metadata is unchanged."));
116369
116781
  return 0;
116370
116782
  }
116371
116783
  async function runTelemetryResetId() {
@@ -116373,8 +116785,8 @@ async function runTelemetryResetId() {
116373
116785
  updateTelemetryState((s5) => {
116374
116786
  s5.installId = next;
116375
116787
  });
116376
- console.log(import_picocolors47.default.green("Install ID reset."));
116377
- console.log(import_picocolors47.default.dim(` New install ID: ${next}`));
116788
+ console.log(import_picocolors48.default.green("Install ID reset."));
116789
+ console.log(import_picocolors48.default.dim(` New install ID: ${next}`));
116378
116790
  return 0;
116379
116791
  }
116380
116792
  async function runTelemetryDelete() {
@@ -116382,9 +116794,9 @@ async function runTelemetryDelete() {
116382
116794
  if (installId) await deleteRemoteTelemetry(installId);
116383
116795
  deleteTelemetryState();
116384
116796
  clearSpool();
116385
- console.log(import_picocolors47.default.green("Telemetry deleted \u2014 local state cleared and a server purge requested."));
116797
+ console.log(import_picocolors48.default.green("Telemetry deleted \u2014 local state cleared and a server purge requested."));
116386
116798
  console.log(
116387
- import_picocolors47.default.dim(
116799
+ import_picocolors48.default.dim(
116388
116800
  "Next run will treat this as a fresh install and re-show the notice. If the purge could not reach the server (offline), re-run this command when back online."
116389
116801
  )
116390
116802
  );
@@ -116427,35 +116839,35 @@ function installCommandTelemetry(program2) {
116427
116839
 
116428
116840
  // ../../packages/cli/src/commands/feedback.ts
116429
116841
  init_cjs_shims();
116430
- var import_picocolors48 = __toESM(require_picocolors(), 1);
116842
+ var import_picocolors49 = __toESM(require_picocolors(), 1);
116431
116843
  async function runFeedback(investigationId, opts = {}) {
116432
116844
  const id = investigationId?.trim();
116433
116845
  if (!id) {
116434
- console.error(import_picocolors48.default.red("Usage: horus feedback <investigationId> [--resolved yes|partly|no]"));
116846
+ console.error(import_picocolors49.default.red("Usage: horus feedback <investigationId> [--resolved yes|partly|no]"));
116435
116847
  return 1;
116436
116848
  }
116437
116849
  if (opts.resolved !== void 0) {
116438
116850
  const resolved = parseResolved(opts.resolved);
116439
116851
  if (resolved === null) {
116440
- console.error(import_picocolors48.default.red(`Invalid --resolved "${opts.resolved}". Use: yes | partly | no.`));
116852
+ console.error(import_picocolors49.default.red(`Invalid --resolved "${opts.resolved}". Use: yes | partly | no.`));
116441
116853
  return 1;
116442
116854
  }
116443
116855
  let manualEstimateMinutes = null;
116444
116856
  if (opts.manualEstimateMin !== void 0) {
116445
116857
  const n4 = Number.parseInt(opts.manualEstimateMin, 10);
116446
116858
  if (!Number.isFinite(n4) || n4 < 0) {
116447
- console.error(import_picocolors48.default.red(`Invalid --manual-estimate-min "${opts.manualEstimateMin}" (expect a non-negative integer).`));
116859
+ console.error(import_picocolors49.default.red(`Invalid --manual-estimate-min "${opts.manualEstimateMin}" (expect a non-negative integer).`));
116448
116860
  return 1;
116449
116861
  }
116450
116862
  manualEstimateMinutes = n4;
116451
116863
  }
116452
116864
  submitFeedback({ investigationId: id, resolved, manualEstimateMinutes, horusSeconds: null, source: "flag" });
116453
- console.log(import_picocolors48.default.dim(`Feedback recorded (${resolved}) \u2014 thanks, this helps improve Horus.`));
116865
+ console.log(import_picocolors49.default.dim(`Feedback recorded (${resolved}) \u2014 thanks, this helps improve Horus.`));
116454
116866
  return 0;
116455
116867
  }
116456
116868
  if (!process.stdin.isTTY) {
116457
116869
  console.error(
116458
- import_picocolors48.default.yellow(
116870
+ import_picocolors49.default.yellow(
116459
116871
  "horus feedback needs an interactive terminal \u2014 or pass --resolved yes|partly|no for non-interactive (agent) use."
116460
116872
  )
116461
116873
  );
@@ -116548,8 +116960,8 @@ Examples:
116548
116960
  program2.command("stop").description("Stop the source-intelligence host for the current repo (or --all to stop every host)").option("--all", "stop all registered source-intelligence hosts").action(async (opts) => {
116549
116961
  process.exitCode = await runStop(opts);
116550
116962
  });
116551
- program2.command("hosts").description("List registered source-intelligence hosts and their live status (port, repo, running/stopped)").action(async () => {
116552
- process.exitCode = await runHosts();
116963
+ program2.command("hosts").description("List registered source-intelligence hosts and their live status (port, repo, running/stopped)").option("--reap", "stop orphaned source hosts that no registered repo owns").action(async (opts) => {
116964
+ process.exitCode = await runHosts({ reap: opts.reap });
116553
116965
  });
116554
116966
  program2.command("status").description("Show config, provider health, and project/environment matrix").option("-c, --config <path>", "path to horus.config.ts").option("--name <name>", "registered project name (resolves via the registry)").option("--project <name>", "project name (show only this project)").option("--env <name>", "environment name (e.g. production)").action(
116555
116967
  async (opts) => {