@merittdev/horus 0.8.17 → 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 +1097 -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.17" : "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);
@@ -92005,6 +92064,11 @@ function scoreSeed(s5, index2, hintTokens, changedFiles, hintHasCode) {
92005
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)) {
92006
92065
  score -= 4;
92007
92066
  }
92067
+ if (/(^|\/)(examples?|samples?|demos?|fixtures?|sandbox|playground|docs|docs_src|tutorials?)(\/|$)/i.test(
92068
+ s5.filePath
92069
+ )) {
92070
+ score -= 4;
92071
+ }
92008
92072
  score += Math.max(0, 5 - index2) * (hintHasCode ? 0.8 : 0.1);
92009
92073
  if (s5.score !== void 0) score += s5.score * (hintHasCode ? 8 : 1.5);
92010
92074
  if (/\.[jt]sx?$/i.test(s5.name)) score -= 5;
@@ -92681,12 +92745,12 @@ async function investigate(input, deps) {
92681
92745
  const fp = (s5.filePath ?? "").replace(/^\.?\/+/, "");
92682
92746
  return fp === scopePrefix || fp.startsWith(scopePrefix + "/");
92683
92747
  };
92684
- const seedSearchLimit = scopePrefix ? 25 : 5;
92685
- 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) : [];
92686
92750
  const hintTokens = [...new Set(tokenize(hint))];
92687
92751
  const hintHasCode = /\b(?=[A-Z0-9_]*[0-9_])[A-Z][A-Z0-9_]{3,}\b/.test(hint);
92688
92752
  const ranked = rankSeeds(rawSeeds, hintTokens, changedFilePaths, hintHasCode);
92689
- let seeds = ranked.map((r) => r.symbol);
92753
+ let seeds = ranked.map((r) => r.symbol).slice(0, 5);
92690
92754
  let top = seeds[0];
92691
92755
  if (top && code && isTypeLikeName(top.name)) {
92692
92756
  const base2 = executableBaseName(top.name);
@@ -93481,11 +93545,12 @@ async function investigate(input, deps) {
93481
93545
  const causeChains = buildCauseChains(validated, evidence2, graph, label);
93482
93546
  const findings2 = [];
93483
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));
93484
93549
  findings2.push({
93485
93550
  kind: "observation",
93486
93551
  title: `Seed resolves to ${label} at ${seedLoc}`,
93487
93552
  detail: top.signature ?? void 0,
93488
- confidence: 1,
93553
+ confidence: +seedConfidence.toFixed(2),
93489
93554
  evidenceIds: [seedEv.id]
93490
93555
  });
93491
93556
  }
@@ -93867,12 +93932,31 @@ async function investigate(input, deps) {
93867
93932
  ...deps.queue != null ? { queue: 0.9 } : {},
93868
93933
  ...deps.metrics != null ? { metrics: 0.75 } : {}
93869
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
+ }
93870
93953
  const rankedCauses = rankCauses(causeInputs, {
93871
93954
  evidence: evidence2,
93872
93955
  graph,
93873
93956
  findings: findings2,
93874
93957
  providerReliability,
93875
- request: { hint: input.hint, service: input.service }
93958
+ request: { hint: input.hint, service: input.service },
93959
+ ...priorIncidents.size > 0 ? { priorIncidents } : {}
93876
93960
  });
93877
93961
  const evidenceConfidence = computeWeightedEvidenceConfidence(
93878
93962
  evidence2,
@@ -94444,6 +94528,32 @@ var EXTERNAL_MARKERS = [
94444
94528
  "mysql2",
94445
94529
  "mssql"
94446
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
+ }
94447
94557
  async function discoverArchitecture(deps) {
94448
94558
  const nodeStats = await (async () => {
94449
94559
  try {
@@ -94463,10 +94573,17 @@ async function discoverArchitecture(deps) {
94463
94573
  const result = await deps.code.cypher(
94464
94574
  'MATCH (m)-[r:CodeRelation]->(c:Community) WHERE r.rel_type = "member_of" RETURN c.name, count(m) ORDER BY count(m) DESC'
94465
94575
  );
94466
- return result.rows.slice(0, 12).map((row) => ({
94576
+ const rows = result.rows.map((row) => ({
94467
94577
  name: String(row[0] ?? ""),
94468
94578
  members: Number(row[1] ?? 0)
94469
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);
94470
94587
  } catch {
94471
94588
  return [];
94472
94589
  }
@@ -94478,20 +94595,22 @@ async function discoverArchitecture(deps) {
94478
94595
  for (const edge of edges) {
94479
94596
  const key = edge.queueName;
94480
94597
  if (!byQueue.has(key)) {
94481
- byQueue.set(key, { producers: /* @__PURE__ */ new Set(), workers: /* @__PURE__ */ new Set() });
94598
+ byQueue.set(key, { producers: /* @__PURE__ */ new Map(), workers: /* @__PURE__ */ new Map() });
94482
94599
  }
94483
94600
  const entry2 = byQueue.get(key);
94484
94601
  if (edge.producerSymbol != null && edge.producerSymbol !== "") {
94485
- entry2.producers.add(edge.producerSymbol);
94602
+ const file = edge.producerFile ?? null;
94603
+ entry2.producers.set(`${edge.producerSymbol}\0${file ?? ""}`, { symbol: edge.producerSymbol, file });
94486
94604
  }
94487
94605
  if (edge.workerSymbol != null && edge.workerSymbol !== "") {
94488
- entry2.workers.add(edge.workerSymbol);
94606
+ const file = edge.workerFile ?? null;
94607
+ entry2.workers.set(`${edge.workerSymbol}\0${file ?? ""}`, { symbol: edge.workerSymbol, file });
94489
94608
  }
94490
94609
  }
94491
94610
  return Array.from(byQueue.entries()).map(([queueName, { producers, workers }]) => ({
94492
94611
  queueName,
94493
- producers: Array.from(producers),
94494
- workers: Array.from(workers)
94612
+ producers: Array.from(producers.values()),
94613
+ workers: Array.from(workers.values())
94495
94614
  }));
94496
94615
  } catch {
94497
94616
  return [];
@@ -94527,16 +94646,20 @@ async function discoverArchitecture(deps) {
94527
94646
  return 0;
94528
94647
  }
94529
94648
  })();
94649
+ const ownPackage = deps.project?.toLowerCase();
94530
94650
  const externalSystems = await (async () => {
94531
94651
  try {
94532
94652
  const results = await Promise.all(
94533
94653
  EXTERNAL_MARKERS.map(async (marker) => {
94654
+ if (ownPackage && marker.toLowerCase() === ownPackage) {
94655
+ return { name: marker, files: 0 };
94656
+ }
94534
94657
  try {
94535
94658
  const result = await deps.code.cypher(
94536
- `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`
94537
94660
  );
94538
- const count = Number(result.rows[0]?.[0] ?? 0);
94539
- 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 };
94540
94663
  } catch {
94541
94664
  return { name: marker, files: 0 };
94542
94665
  }
@@ -94563,6 +94686,10 @@ async function discoverArchitecture(deps) {
94563
94686
 
94564
94687
  // ../../packages/engine/src/render-architecture.ts
94565
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
+ }
94566
94693
  function renderArchitecture(m5) {
94567
94694
  const lines = [];
94568
94695
  lines.push("# Architecture (discovered)");
@@ -94583,9 +94710,7 @@ function renderArchitecture(m5) {
94583
94710
  lines.push("(none)");
94584
94711
  } else {
94585
94712
  for (const b5 of m5.asyncBoundaries) {
94586
- const producers = b5.producers.length > 0 ? b5.producers.join(", ") : "(unknown)";
94587
- const workers = b5.workers.length > 0 ? b5.workers.join(", ") : "(unknown)";
94588
- lines.push(`- ${b5.queueName}: ${producers} -> ${workers}`);
94713
+ lines.push(`- ${b5.queueName}: ${fmtEndpoints(b5.producers)} -> ${fmtEndpoints(b5.workers)}`);
94589
94714
  }
94590
94715
  }
94591
94716
  lines.push("");
@@ -95750,7 +95875,7 @@ function filterArchitecture(architecture, tokens) {
95750
95875
  ...architecture,
95751
95876
  subsystems: architecture.subsystems.filter((s5) => matchesArea(s5.name, tokens)),
95752
95877
  asyncBoundaries: architecture.asyncBoundaries.filter(
95753
- (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))
95754
95879
  ),
95755
95880
  keyFlows: architecture.keyFlows.filter((f5) => matchesArea(f5, tokens)),
95756
95881
  externalSystems: architecture.externalSystems.filter((e) => matchesArea(e.name, tokens))
@@ -95864,9 +95989,8 @@ function renderOnboarding(g4) {
95864
95989
  if (g4.architecture.asyncBoundaries.length > 0) {
95865
95990
  lines.push("Async boundaries:");
95866
95991
  for (const b5 of g4.architecture.asyncBoundaries) {
95867
- const producers = b5.producers.length > 0 ? b5.producers.join(", ") : "(unknown)";
95868
- const workers = b5.workers.length > 0 ? b5.workers.join(", ") : "(unknown)";
95869
- 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)}`);
95870
95994
  }
95871
95995
  } else {
95872
95996
  lines.push("No async boundaries detected.");
@@ -97212,27 +97336,27 @@ function extractQueueGraph(input) {
97212
97336
  }
97213
97337
  }
97214
97338
  const producers = [];
97215
- for (const pc49 of input.producerClasses) {
97216
- const loopVarEnums = loopVarEnumsFor(pc49.content);
97217
- 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)) {
97218
97342
  const queue = m5[1] ?? "";
97219
- if (queue) producers.push({ queue, symbol: pc49.name, file: pc49.filePath });
97343
+ if (queue) producers.push({ queue, symbol: pc50.name, file: pc50.filePath });
97220
97344
  }
97221
- for (const m5 of pc49.content.matchAll(NEW_BULL_RE)) {
97345
+ for (const m5 of pc50.content.matchAll(NEW_BULL_RE)) {
97222
97346
  if (m5[2] !== "Queue") continue;
97223
97347
  const queues2 = resolveArg(m5[3] ?? "", constMap, enums, loopVarEnums);
97224
97348
  for (const queue of queues2) {
97225
97349
  producers.push({
97226
97350
  queue,
97227
- symbol: (m5[1] ?? pc49.name) || baseName(pc49.filePath),
97228
- file: pc49.filePath
97351
+ symbol: (m5[1] ?? pc50.name) || baseName(pc50.filePath),
97352
+ file: pc50.filePath
97229
97353
  });
97230
97354
  }
97231
97355
  }
97232
- for (const m5 of pc49.content.matchAll(QUEUE_NAME_CONST_RE)) {
97356
+ for (const m5 of pc50.content.matchAll(QUEUE_NAME_CONST_RE)) {
97233
97357
  const ident = m5[1] ?? "";
97234
97358
  const queue = m5[2] ?? "";
97235
- 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 });
97236
97360
  }
97237
97361
  }
97238
97362
  const workers = [];
@@ -97299,6 +97423,60 @@ function extractQueueGraph(input) {
97299
97423
  }
97300
97424
  return { queues, producers, workers, edges };
97301
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
+ }
97302
97480
  function dedupeBySymbolFile(rows) {
97303
97481
  const seen = /* @__PURE__ */ new Set();
97304
97482
  const out2 = [];
@@ -97332,7 +97510,16 @@ async function stitch(client, db, opts = {}) {
97332
97510
  content: String(r[2] ?? "")
97333
97511
  }));
97334
97512
  const graph = extractQueueGraph({ producerClasses, workerFiles });
97335
- 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) => ({
97336
97523
  queueName: e.queueName,
97337
97524
  producerSymbol: e.producerSymbol,
97338
97525
  producerFile: e.producerFile,
@@ -97343,9 +97530,9 @@ async function stitch(client, db, opts = {}) {
97343
97530
  }));
97344
97531
  await replaceQueueEdges(db, edges, { project: opts.project });
97345
97532
  return {
97346
- queues: graph.queues.length,
97347
- producers: graph.producers.length,
97348
- 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,
97349
97536
  edges: edges.length
97350
97537
  };
97351
97538
  }
@@ -97533,6 +97720,7 @@ async function runIndex(opts) {
97533
97720
  console.log(import_picocolors3.default.dim(" already analyzed"));
97534
97721
  }
97535
97722
  const priorPort = parseHostPort(configuredHost ?? "") ?? parseHostPort(readSourceHostUrl(root) ?? "");
97723
+ await killSpawnedHost(root);
97536
97724
  const MAX_PORT_ATTEMPTS = 4;
97537
97725
  let started = false;
97538
97726
  for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS && !started; attempt += 1) {
@@ -97557,7 +97745,7 @@ async function runIndex(opts) {
97557
97745
  reconcileSpawnedHostPid(root, port);
97558
97746
  started = true;
97559
97747
  } else {
97560
- removeSpawnedHostRecord(root);
97748
+ await killSpawnedHost(root);
97561
97749
  }
97562
97750
  }
97563
97751
  if (!started) {
@@ -110080,7 +110268,7 @@ function formatAge(ms) {
110080
110268
 
110081
110269
  // ../../packages/cli/src/commands/investigate.ts
110082
110270
  init_cjs_shims();
110083
- var import_picocolors10 = __toESM(require_picocolors(), 1);
110271
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
110084
110272
 
110085
110273
  // ../../packages/cli/src/lib/cloud/investigation-sync.ts
110086
110274
  init_cjs_shims();
@@ -110498,9 +110686,110 @@ async function maybePromptFeedback(opts) {
110498
110686
  }
110499
110687
  }
110500
110688
 
110501
- // ../../packages/cli/src/lib/investigation-runner.ts
110689
+ // ../../packages/cli/src/lib/freshness.ts
110502
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");
110503
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);
110504
110793
  async function buildInvestigationContext(renv, opts) {
110505
110794
  const log = opts.log ?? ((l2) => console.error(l2));
110506
110795
  const code = codeForEnv(renv);
@@ -110512,20 +110801,20 @@ async function buildInvestigationContext(renv, opts) {
110512
110801
  const sourceUrl = renv.repositories[0]?.sourceHostUrl;
110513
110802
  let health = await code.health();
110514
110803
  if (!health.ok && sourceUrl) {
110515
- 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`));
110516
110805
  const healed = await ensureSourceHost(renv.path, sourceUrl);
110517
110806
  if (healed.ok) {
110518
- 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}.`));
110519
110808
  health = await code.health();
110520
110809
  } else {
110521
- log(import_picocolors9.default.dim(` ${ensureHostReasonHint(healed.reason)}`));
110810
+ log(import_picocolors10.default.dim(` ${ensureHostReasonHint(healed.reason)}`));
110522
110811
  }
110523
110812
  }
110524
110813
  const runtimeOnly = !health.ok;
110525
110814
  if (runtimeOnly) {
110526
110815
  log(
110527
- import_picocolors9.default.yellow(
110528
- `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.`
110529
110818
  )
110530
110819
  );
110531
110820
  }
@@ -110680,37 +110969,37 @@ function renderStoredAIJudgment(judgment, write = (l2) => console.log(l2)) {
110680
110969
  const sep = "\u2500".repeat(60);
110681
110970
  write(`
110682
110971
  ${sep}`);
110683
- 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})`));
110684
110973
  write(sep);
110685
110974
  if (judgment.hypothesisJudgments && judgment.hypothesisJudgments.length > 0) {
110686
- write(import_picocolors10.default.bold("Hypothesis verdicts:"));
110975
+ write(import_picocolors11.default.bold("Hypothesis verdicts:"));
110687
110976
  for (const j3 of judgment.hypothesisJudgments) {
110688
- 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;
110689
- write(` ${verdictColor(`[${j3.verdict}]`)} ${j3.category}` + import_picocolors10.default.dim(` (${(j3.confidence * 100).toFixed(0)}%)`));
110690
- 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}`));
110691
110980
  }
110692
110981
  write("");
110693
110982
  }
110694
110983
  if (judgment.rootCauseAssessment) {
110695
110984
  const rca = judgment.rootCauseAssessment;
110696
- const uncertaintyColor = rca.uncertainty === "low" ? import_picocolors10.default.green : rca.uncertainty === "medium" ? import_picocolors10.default.yellow : import_picocolors10.default.red;
110697
- 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)}`));
110698
110987
  write(rca.summary);
110699
110988
  if (rca.citedEvidenceIds.length > 0) {
110700
- write(import_picocolors10.default.dim(` Cited: ${rca.citedEvidenceIds.join(", ")}`));
110989
+ write(import_picocolors11.default.dim(` Cited: ${rca.citedEvidenceIds.join(", ")}`));
110701
110990
  }
110702
110991
  write("");
110703
110992
  }
110704
- write(import_picocolors10.default.bold("What:") + " " + judgment.what);
110705
- 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);
110706
110995
  if (judgment.whereNext.length > 0) {
110707
- write(import_picocolors10.default.bold("Next steps:"));
110996
+ write(import_picocolors11.default.bold("Next steps:"));
110708
110997
  for (const step of judgment.whereNext) {
110709
110998
  write(` \u2022 ${step}`);
110710
110999
  }
110711
111000
  }
110712
111001
  if (judgment.citations.length > 0) {
110713
- write(import_picocolors10.default.dim(`
111002
+ write(import_picocolors11.default.dim(`
110714
111003
  Cited evidence: ${judgment.citations.map((c4) => c4.evidenceId).join(", ")}`));
110715
111004
  }
110716
111005
  }
@@ -110736,7 +111025,7 @@ async function runInvestigate(hint, opts) {
110736
111025
  try {
110737
111026
  renv = resolveEnvironment(config2, { project: projectName, env: opts.env });
110738
111027
  } catch (err2) {
110739
- console.error(import_picocolors10.default.red(err2.message));
111028
+ console.error(import_picocolors11.default.red(err2.message));
110740
111029
  return 1;
110741
111030
  }
110742
111031
  const repoRoot = repoRootOrCwd(renv.path);
@@ -110745,8 +111034,8 @@ async function runInvestigate(hint, opts) {
110745
111034
  const cloudSession = cloudActive ? authedClient() : null;
110746
111035
  if (cloudActive && !cloudSession) {
110747
111036
  console.error(
110748
- import_picocolors10.default.yellow(
110749
- `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.`
110750
111039
  )
110751
111040
  );
110752
111041
  }
@@ -110757,7 +111046,7 @@ async function runInvestigate(hint, opts) {
110757
111046
  ...opts.service !== void 0 ? { service: opts.service } : {}
110758
111047
  });
110759
111048
  } catch (err2) {
110760
- console.error(import_picocolors10.default.red(err2.message));
111049
+ console.error(import_picocolors11.default.red(err2.message));
110761
111050
  return 1;
110762
111051
  }
110763
111052
  try {
@@ -110774,8 +111063,24 @@ async function runInvestigate(hint, opts) {
110774
111063
  );
110775
111064
  const db = ctx.dbHandle.db;
110776
111065
  const format = opts.json ? "json" : opts.format ?? "text";
110777
- const rendered = format === "json" ? reportToJSON(report) : format === "markdown" || format === "md" ? reportToMarkdown(report) : renderReport(report);
110778
- 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
+ }
110779
111084
  if (opts.ai && format !== "json") {
110780
111085
  const ai = resolveAiSettings(config2);
110781
111086
  const model = opts.aiModel ?? ai.model ?? "claude-opus-4-8";
@@ -110783,7 +111088,7 @@ async function runInvestigate(hint, opts) {
110783
111088
  model,
110784
111089
  ...ai.anthropicApiKey !== void 0 ? { apiKey: ai.anthropicApiKey } : {}
110785
111090
  });
110786
- console.log(import_picocolors10.default.dim(`[ai] model: ${model}`));
111091
+ console.log(import_picocolors11.default.dim(`[ai] model: ${model}`));
110787
111092
  const narrativeInput = buildNarrativeInput(report);
110788
111093
  const { output, fromProvider, degraded, validationErrors } = await renderNarrative(
110789
111094
  narrativeInput,
@@ -110799,24 +111104,24 @@ async function runInvestigate(hint, opts) {
110799
111104
  renderStoredAIJudgment(stored);
110800
111105
  } else if (degraded) {
110801
111106
  if (validationErrors && validationErrors.length > 0) {
110802
- 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`));
110803
111108
  }
110804
111109
  console.log("");
110805
- console.log(import_picocolors10.default.dim("\u2500".repeat(60)));
110806
- 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)"));
110807
111112
  if (output.what?.trim()) {
110808
- console.log(import_picocolors10.default.bold("\nWhat:"), output.what.trim());
111113
+ console.log(import_picocolors11.default.bold("\nWhat:"), output.what.trim());
110809
111114
  }
110810
111115
  if (output.why?.trim()) {
110811
- console.log(import_picocolors10.default.bold("\nWhy:"), output.why.trim());
111116
+ console.log(import_picocolors11.default.bold("\nWhy:"), output.why.trim());
110812
111117
  }
110813
111118
  if (output.whereNext.length > 0) {
110814
- console.log(import_picocolors10.default.bold("\nNext:"));
111119
+ console.log(import_picocolors11.default.bold("\nNext:"));
110815
111120
  for (const a2 of output.whereNext) console.log(` - ${a2}`);
110816
111121
  }
110817
111122
  } else {
110818
111123
  const reason = classifyAIFailure(validationErrors?.[0]);
110819
- 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}`));
110820
111125
  }
110821
111126
  }
110822
111127
  track({
@@ -110847,7 +111152,7 @@ async function runInvestigate(hint, opts) {
110847
111152
  if (cloudActive && cloudSession && cloudCfg) {
110848
111153
  try {
110849
111154
  const refs = await uploadInvestigationToCloud(cloudSession.client, cloudCfg, report);
110850
- const cloudNote = import_picocolors10.default.dim(`[cloud] investigation saved: ${refs.investigationId}`);
111155
+ const cloudNote = import_picocolors11.default.dim(`[cloud] investigation saved: ${refs.investigationId}`);
110851
111156
  if (format === "json") console.error(cloudNote);
110852
111157
  else console.log(cloudNote);
110853
111158
  } catch (err2) {
@@ -110857,16 +111162,16 @@ async function runInvestigate(hint, opts) {
110857
111162
  if (format !== "json") {
110858
111163
  if (report.persisted === false) {
110859
111164
  console.error(
110860
- import_picocolors10.default.yellow(`
111165
+ import_picocolors11.default.yellow(`
110861
111166
  \u26A0 Results were not saved \u2014 the investigation database was unreachable.`)
110862
111167
  );
110863
111168
  console.error(
110864
- import_picocolors10.default.dim(
111169
+ import_picocolors11.default.dim(
110865
111170
  ` This report is display-only; \`horus ask\` won't find it. Start the database and re-run to persist.`
110866
111171
  )
110867
111172
  );
110868
111173
  } else {
110869
- console.log(import_picocolors10.default.dim(`
111174
+ console.log(import_picocolors11.default.dim(`
110870
111175
  Ask a follow-up: horus ask ${report.id} "<question>"`));
110871
111176
  }
110872
111177
  await maybePromptFeedback({
@@ -110880,15 +111185,15 @@ Ask a follow-up: horus ask ${report.id} "<question>"`));
110880
111185
  return 0;
110881
111186
  } catch (err2) {
110882
111187
  const msg = err2?.message || String(err2);
110883
- console.error(import_picocolors10.default.red(msg.trim() ? msg : "Investigation failed (unknown error)."));
110884
- 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)));
110885
111190
  return 1;
110886
111191
  }
110887
111192
  }
110888
111193
 
110889
111194
  // ../../packages/cli/src/commands/watch.ts
110890
111195
  init_cjs_shims();
110891
- var import_picocolors11 = __toESM(require_picocolors(), 1);
111196
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
110892
111197
  function detectNewSentryIncidents(issues, seen) {
110893
111198
  const fresh = [];
110894
111199
  for (const issue2 of issues) {
@@ -110970,21 +111275,21 @@ async function runWatch(opts) {
110970
111275
  try {
110971
111276
  renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
110972
111277
  } catch (err2) {
110973
- console.error(import_picocolors11.default.red(err2.message));
111278
+ console.error(import_picocolors12.default.red(err2.message));
110974
111279
  return 1;
110975
111280
  }
110976
111281
  let ctx;
110977
111282
  try {
110978
111283
  ctx = await buildInvestigationContext(renv, { databaseUrl: config2.database.url });
110979
111284
  } catch (err2) {
110980
- console.error(import_picocolors11.default.red(err2.message));
111285
+ console.error(import_picocolors12.default.red(err2.message));
110981
111286
  return 1;
110982
111287
  }
110983
111288
  const requested = opts.source ?? "auto";
110984
111289
  const source = resolveSource(requested, ctx);
110985
111290
  if (source === null) {
110986
111291
  console.error(
110987
- import_picocolors11.default.red(
111292
+ import_picocolors12.default.red(
110988
111293
  requested === "auto" ? `No Sentry or Elasticsearch connector configured for "${renv.project}" / "${renv.env}".` : `No ${requested} connector configured for "${renv.project}" / "${renv.env}".`
110989
111294
  )
110990
111295
  );
@@ -110994,7 +111299,7 @@ async function runWatch(opts) {
110994
111299
  const intervalSec = Math.max(1, Number(opts.interval) || 60);
110995
111300
  const seen = /* @__PURE__ */ new Set();
110996
111301
  console.error(
110997
- import_picocolors11.default.dim(
111302
+ import_picocolors12.default.dim(
110998
111303
  `[watch] source: ${source} \xB7 project: ${renv.project} \xB7 env: ${renv.env}` + (opts.once ? " \xB7 mode: once" : ` \xB7 interval: ${intervalSec}s`)
110999
111304
  )
111000
111305
  );
@@ -111003,24 +111308,24 @@ async function runWatch(opts) {
111003
111308
  try {
111004
111309
  incidents = source === "sentry" ? await pollSentry(ctx.sentry, seen) : await pollElasticsearch(ctx.logs, seen, ctx.service);
111005
111310
  } catch (err2) {
111006
- 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}`));
111007
111312
  return;
111008
111313
  }
111009
111314
  if (incidents.length === 0) {
111010
- console.error(import_picocolors11.default.dim(`[watch] no new incidents`));
111315
+ console.error(import_picocolors12.default.dim(`[watch] no new incidents`));
111011
111316
  return;
111012
111317
  }
111013
111318
  for (const incident of incidents) {
111014
111319
  try {
111015
111320
  const report = await runOneInvestigation({ hint: incident.hint }, ctx);
111016
111321
  const { cause, confidence } = headlineFor(report);
111017
- const persisted = report.persisted === false ? import_picocolors11.default.yellow(" (not persisted)") : "";
111322
+ const persisted = report.persisted === false ? import_picocolors12.default.yellow(" (not persisted)") : "";
111018
111323
  console.log(
111019
- `${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}`
111020
111325
  );
111021
111326
  } catch (err2) {
111022
111327
  console.error(
111023
- 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}`)
111024
111329
  );
111025
111330
  }
111026
111331
  }
@@ -111035,7 +111340,7 @@ async function runWatch(opts) {
111035
111340
  const onSignal = () => {
111036
111341
  if (stopping) return;
111037
111342
  stopping = true;
111038
- console.error(import_picocolors11.default.dim(`
111343
+ console.error(import_picocolors12.default.dim(`
111039
111344
  [watch] shutting down\u2026`));
111040
111345
  if (wake) wake();
111041
111346
  };
@@ -111062,7 +111367,7 @@ async function runWatch(opts) {
111062
111367
 
111063
111368
  // ../../packages/cli/src/commands/changes.ts
111064
111369
  init_cjs_shims();
111065
- var import_picocolors12 = __toESM(require_picocolors(), 1);
111370
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
111066
111371
  var CHANGES_AI_CONTRACT = `Provide a clearly separated AI change-impact review with:
111067
111372
 
111068
111373
  Highest-risk changes
@@ -111087,7 +111392,7 @@ async function runChanges(base2, compare, opts) {
111087
111392
  const health = await code.health();
111088
111393
  if (!health.ok) {
111089
111394
  console.error(
111090
- 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")
111091
111396
  );
111092
111397
  return 1;
111093
111398
  }
@@ -111109,20 +111414,20 @@ async function runChanges(base2, compare, opts) {
111109
111414
  });
111110
111415
  console.log("\n" + renderInterpretation(result));
111111
111416
  if (!result.ok) {
111112
- console.error(import_picocolors12.default.yellow(`[ai] ${result.warning}`));
111417
+ console.error(import_picocolors13.default.yellow(`[ai] ${result.warning}`));
111113
111418
  }
111114
111419
  }
111115
111420
  }
111116
111421
  return 0;
111117
111422
  } catch (err2) {
111118
- console.error(import_picocolors12.default.red(err2.message));
111423
+ console.error(import_picocolors13.default.red(err2.message));
111119
111424
  return 1;
111120
111425
  }
111121
111426
  }
111122
111427
 
111123
111428
  // ../../packages/cli/src/commands/timeline.ts
111124
111429
  init_cjs_shims();
111125
- var import_picocolors13 = __toESM(require_picocolors(), 1);
111430
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
111126
111431
  var DEFAULT_SINCE = "7 days ago";
111127
111432
  function resolveTimelineWindow(opts) {
111128
111433
  const usingDefault = !opts.all && opts.since === void 0;
@@ -111152,7 +111457,7 @@ async function runTimeline(service, opts) {
111152
111457
  try {
111153
111458
  renv = resolveEnvironment(config2, { project: opts.repo });
111154
111459
  } catch (err2) {
111155
- console.error(import_picocolors13.default.red(err2.message));
111460
+ console.error(import_picocolors14.default.red(err2.message));
111156
111461
  return 1;
111157
111462
  }
111158
111463
  const { code } = createConnectors(config2);
@@ -111167,9 +111472,9 @@ async function runTimeline(service, opts) {
111167
111472
  console.log(renderChangeTimeline(t2));
111168
111473
  if (usingDefaultWindow) {
111169
111474
  console.log(
111170
- import_picocolors13.default.dim(
111475
+ import_picocolors14.default.dim(
111171
111476
  `
111172
- 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")}.`
111173
111478
  )
111174
111479
  );
111175
111480
  }
@@ -111186,21 +111491,26 @@ async function runTimeline(service, opts) {
111186
111491
  });
111187
111492
  console.log("\n" + renderInterpretation(result));
111188
111493
  if (!result.ok) {
111189
- console.error(import_picocolors13.default.yellow(`[ai] ${result.warning}`));
111494
+ console.error(import_picocolors14.default.yellow(`[ai] ${result.warning}`));
111190
111495
  }
111191
111496
  }
111192
111497
  }
111193
111498
  return 0;
111194
111499
  } catch (err2) {
111195
- console.error(import_picocolors13.default.red(err2.message));
111500
+ console.error(import_picocolors14.default.red(err2.message));
111196
111501
  return 1;
111197
111502
  }
111198
111503
  }
111199
111504
 
111200
111505
  // ../../packages/cli/src/commands/what-changed.ts
111201
111506
  init_cjs_shims();
111202
- var import_picocolors14 = __toESM(require_picocolors(), 1);
111507
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
111203
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
+ }
111204
111514
  var WHAT_CHANGED_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
111205
111515
 
111206
111516
  Evidence used
@@ -111223,11 +111533,18 @@ async function runWhatChanged(service, opts) {
111223
111533
  try {
111224
111534
  renv = resolveEnvironment(config2, { project: opts.repo });
111225
111535
  } catch (err2) {
111226
- console.error(import_picocolors14.default.red(err2.message));
111536
+ console.error(import_picocolors15.default.red(err2.message));
111227
111537
  return 1;
111228
111538
  }
111229
111539
  const { code } = createConnectors(config2);
111230
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
+ }
111231
111548
  const r = await whatChanged(
111232
111549
  { repoPath: renv.path, since, until: opts.until, service },
111233
111550
  { code }
@@ -111246,7 +111563,7 @@ async function runWhatChanged(service, opts) {
111246
111563
  });
111247
111564
  console.log("\n" + renderInterpretation(result));
111248
111565
  if (!result.ok) {
111249
- console.error(import_picocolors14.default.yellow(`[ai] ${result.warning}`));
111566
+ console.error(import_picocolors15.default.yellow(`[ai] ${result.warning}`));
111250
111567
  }
111251
111568
  }
111252
111569
  if (opts.push) {
@@ -111254,19 +111571,19 @@ async function runWhatChanged(service, opts) {
111254
111571
  }
111255
111572
  return 0;
111256
111573
  } catch (err2) {
111257
- console.error(import_picocolors14.default.red(err2.message));
111574
+ console.error(import_picocolors15.default.red(err2.message));
111258
111575
  return 1;
111259
111576
  }
111260
111577
  }
111261
111578
  async function pushChangeReport(r, repoCwd) {
111262
111579
  const session = authedClient();
111263
111580
  if (!session) {
111264
- 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."));
111265
111582
  return;
111266
111583
  }
111267
111584
  const cfg = readCloudConfig(repoRootOrCwd(repoCwd));
111268
111585
  if (!isCloudActive(cfg)) {
111269
- 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."));
111270
111587
  return;
111271
111588
  }
111272
111589
  const projectId = cfg.project?.id;
@@ -111312,15 +111629,15 @@ async function pushChangeReport(r, repoCwd) {
111312
111629
  queueTopologyTouched: r.queueTopology.touched,
111313
111630
  payload
111314
111631
  });
111315
- console.error(import_picocolors14.default.dim("[cloud] pushed change report."));
111632
+ console.error(import_picocolors15.default.dim("[cloud] pushed change report."));
111316
111633
  } catch (err2) {
111317
- console.error(import_picocolors14.default.yellow(`[cloud] push failed: ${err2.message}`));
111634
+ console.error(import_picocolors15.default.yellow(`[cloud] push failed: ${err2.message}`));
111318
111635
  }
111319
111636
  }
111320
111637
 
111321
111638
  // ../../packages/cli/src/commands/architecture.ts
111322
111639
  init_cjs_shims();
111323
- var import_picocolors15 = __toESM(require_picocolors(), 1);
111640
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
111324
111641
  var ARCHITECTURE_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
111325
111642
 
111326
111643
  System summary
@@ -111349,7 +111666,7 @@ async function runArchitecture(opts) {
111349
111666
  const health = await code.health();
111350
111667
  if (!health.ok) {
111351
111668
  console.error(
111352
- 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")
111353
111670
  );
111354
111671
  return 1;
111355
111672
  }
@@ -111377,7 +111694,7 @@ async function runArchitecture(opts) {
111377
111694
  });
111378
111695
  console.log("\n" + renderInterpretation(result));
111379
111696
  if (!result.ok) {
111380
- console.error(import_picocolors15.default.yellow(`[ai] ${result.warning}`));
111697
+ console.error(import_picocolors16.default.yellow(`[ai] ${result.warning}`));
111381
111698
  }
111382
111699
  }
111383
111700
  }
@@ -111386,14 +111703,14 @@ async function runArchitecture(opts) {
111386
111703
  }
111387
111704
  return 0;
111388
111705
  } catch (err2) {
111389
- console.error(import_picocolors15.default.red(err2.message));
111706
+ console.error(import_picocolors16.default.red(err2.message));
111390
111707
  return 1;
111391
111708
  }
111392
111709
  }
111393
111710
 
111394
111711
  // ../../packages/cli/src/commands/blast-radius.ts
111395
111712
  init_cjs_shims();
111396
- var import_picocolors16 = __toESM(require_picocolors(), 1);
111713
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
111397
111714
  var BLAST_RADIUS_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
111398
111715
 
111399
111716
  Evidence used
@@ -111420,7 +111737,7 @@ async function runBlastRadius(query, opts) {
111420
111737
  const { code } = createConnectors(config2);
111421
111738
  const health = await code.health();
111422
111739
  if (!health.ok) {
111423
- 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"));
111424
111741
  return 1;
111425
111742
  }
111426
111743
  let project;
@@ -111433,12 +111750,12 @@ async function runBlastRadius(query, opts) {
111433
111750
  const r = await analyzeBlastRadius(query, { code, db, project }, opts.depth ?? 3);
111434
111751
  if (!r) {
111435
111752
  console.log(`No symbol found for: ${query}`);
111436
- 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"`));
111437
111754
  return 1;
111438
111755
  }
111439
111756
  if (r.seed.name.toLowerCase() !== query.toLowerCase()) {
111440
111757
  console.log(
111441
- 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)`)
111442
111759
  );
111443
111760
  }
111444
111761
  if (opts.json) {
@@ -111458,7 +111775,7 @@ async function runBlastRadius(query, opts) {
111458
111775
  });
111459
111776
  console.log("\n" + renderInterpretation(result));
111460
111777
  if (!result.ok) {
111461
- console.error(import_picocolors16.default.yellow(`[ai] ${result.warning}`));
111778
+ console.error(import_picocolors17.default.yellow(`[ai] ${result.warning}`));
111462
111779
  }
111463
111780
  }
111464
111781
  }
@@ -111467,28 +111784,28 @@ async function runBlastRadius(query, opts) {
111467
111784
  }
111468
111785
  return 0;
111469
111786
  } catch (err2) {
111470
- console.error(import_picocolors16.default.red(err2.message));
111787
+ console.error(import_picocolors17.default.red(err2.message));
111471
111788
  return 1;
111472
111789
  }
111473
111790
  }
111474
111791
 
111475
111792
  // ../../packages/cli/src/commands/repos.ts
111476
111793
  init_cjs_shims();
111477
- var import_picocolors17 = __toESM(require_picocolors(), 1);
111794
+ var import_picocolors18 = __toESM(require_picocolors(), 1);
111478
111795
  async function runRepos(opts) {
111479
111796
  const config2 = await loadConfig(opts.config);
111480
111797
  const providers = repoProviders(config2);
111481
111798
  if (providers.length === 0) {
111482
- console.log(import_picocolors17.default.dim("No repositories configured."));
111799
+ console.log(import_picocolors18.default.dim("No repositories configured."));
111483
111800
  return 0;
111484
111801
  }
111485
111802
  const health = await reposHealth(providers);
111486
111803
  for (const h3 of health) {
111487
- const dot = h3.reachable ? import_picocolors17.default.green("\u25CF") : import_picocolors17.default.red("\u25CF");
111488
- const name2 = import_picocolors17.default.bold(h3.repo.padEnd(20));
111489
- const hostUrl = import_picocolors17.default.cyan(h3.hostUrl.padEnd(30));
111490
- const path = import_picocolors17.default.dim(h3.path);
111491
- 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})`) : "";
111492
111809
  console.log(` ${dot} ${name2} ${hostUrl} ${path} ${detail}`);
111493
111810
  }
111494
111811
  return 0;
@@ -111496,7 +111813,7 @@ async function runRepos(opts) {
111496
111813
 
111497
111814
  // ../../packages/cli/src/commands/search.ts
111498
111815
  init_cjs_shims();
111499
- var import_picocolors18 = __toESM(require_picocolors(), 1);
111816
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
111500
111817
  async function runSearch(query, opts) {
111501
111818
  const config2 = await loadConfig(opts.config);
111502
111819
  const providers = repoProviders(config2);
@@ -111507,16 +111824,16 @@ async function runSearch(query, opts) {
111507
111824
  }
111508
111825
  let totalMatches = 0;
111509
111826
  for (const result of results) {
111510
- 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");
111511
111828
  console.log("");
111512
- 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)}`);
111513
111830
  if (!result.reachable) {
111514
- console.log(import_picocolors18.default.dim(" (unreachable)"));
111831
+ console.log(import_picocolors19.default.dim(" (unreachable)"));
111515
111832
  } else if (result.symbols.length === 0) {
111516
- console.log(import_picocolors18.default.dim(" (no matches)"));
111833
+ console.log(import_picocolors19.default.dim(" (no matches)"));
111517
111834
  } else {
111518
111835
  for (const sym of result.symbols) {
111519
- 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)}`);
111520
111837
  }
111521
111838
  totalMatches += result.symbols.length;
111522
111839
  }
@@ -111524,7 +111841,7 @@ async function runSearch(query, opts) {
111524
111841
  console.log("");
111525
111842
  const reachableCount = results.filter((r) => r.reachable).length;
111526
111843
  console.log(
111527
- import_picocolors18.default.dim(
111844
+ import_picocolors19.default.dim(
111528
111845
  `${totalMatches} match(es) across ${reachableCount}/${results.length} reachable repo(s) for query: "${query}"`
111529
111846
  )
111530
111847
  );
@@ -111533,7 +111850,7 @@ async function runSearch(query, opts) {
111533
111850
 
111534
111851
  // ../../packages/cli/src/commands/investigations.ts
111535
111852
  init_cjs_shims();
111536
- var import_picocolors19 = __toESM(require_picocolors(), 1);
111853
+ var import_picocolors20 = __toESM(require_picocolors(), 1);
111537
111854
 
111538
111855
  // ../../packages/cli/src/lib/format.ts
111539
111856
  init_cjs_shims();
@@ -111576,8 +111893,8 @@ async function runInvestigations(opts) {
111576
111893
  const session = authedClient();
111577
111894
  if (!session) {
111578
111895
  console.error(
111579
- import_picocolors19.default.red(
111580
- `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.`
111581
111898
  )
111582
111899
  );
111583
111900
  return 1;
@@ -111618,18 +111935,18 @@ async function runInvestigations(opts) {
111618
111935
 
111619
111936
  // ../../packages/cli/src/commands/replay.ts
111620
111937
  init_cjs_shims();
111621
- var import_picocolors20 = __toESM(require_picocolors(), 1);
111938
+ var import_picocolors21 = __toESM(require_picocolors(), 1);
111622
111939
  async function runReplay(id, opts) {
111623
111940
  const { db, sql: sql2 } = await openDb(await resolveDbUrl(opts.config));
111624
111941
  try {
111625
111942
  const row = await getInvestigation(db, id);
111626
111943
  if (!row) {
111627
- console.error(import_picocolors20.default.red("No investigation found: " + id));
111944
+ console.error(import_picocolors21.default.red("No investigation found: " + id));
111628
111945
  return 1;
111629
111946
  }
111630
111947
  if (!row.report) {
111631
111948
  console.error(
111632
- import_picocolors20.default.red(
111949
+ import_picocolors21.default.red(
111633
111950
  "Investigation " + id + " has no stored report (run a newer investigation)."
111634
111951
  )
111635
111952
  );
@@ -111642,7 +111959,7 @@ async function runReplay(id, opts) {
111642
111959
  if (opts.ai && fmt !== "json") {
111643
111960
  if (report.aiJudgment && !opts.refreshAi) {
111644
111961
  renderStoredAIJudgment(report.aiJudgment);
111645
- 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."));
111646
111963
  } else {
111647
111964
  const narrativeInput = buildNarrativeInput(report);
111648
111965
  const { provider } = await buildNarrativeProvider({
@@ -111655,17 +111972,17 @@ async function runReplay(id, opts) {
111655
111972
  );
111656
111973
  if (!fromProvider && degraded) {
111657
111974
  if (validationErrors?.length) {
111658
- 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"));
111659
111976
  }
111660
111977
  console.log("");
111661
- console.log(import_picocolors20.default.dim("\u2500".repeat(60)));
111662
- console.log(import_picocolors20.default.bold("AI narrative ") + import_picocolors20.default.yellow("(unstructured \u2014 not validated)"));
111663
- if (output.what?.trim()) console.log(import_picocolors20.default.bold("\nWhat:"), output.what.trim());
111664
- 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());
111665
111982
  } else if (!fromProvider) {
111666
- 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."));
111667
111984
  if (validationErrors?.length) {
111668
- console.error(import_picocolors20.default.dim(` ${validationErrors[0]}`));
111985
+ console.error(import_picocolors21.default.dim(` ${validationErrors[0]}`));
111669
111986
  }
111670
111987
  } else {
111671
111988
  const stored = narrativeOutputToStoredJudgment(output, "anthropic");
@@ -111686,7 +112003,7 @@ async function runReplay(id, opts) {
111686
112003
 
111687
112004
  // ../../packages/cli/src/commands/owner.ts
111688
112005
  init_cjs_shims();
111689
- var import_picocolors21 = __toESM(require_picocolors(), 1);
112006
+ var import_picocolors22 = __toESM(require_picocolors(), 1);
111690
112007
  async function runOwner(query, opts) {
111691
112008
  try {
111692
112009
  const config2 = await loadConfig(opts.config);
@@ -111694,7 +112011,7 @@ async function runOwner(query, opts) {
111694
112011
  try {
111695
112012
  renv = resolveEnvironment(config2, { project: opts.repo });
111696
112013
  } catch (err2) {
111697
- console.error(import_picocolors21.default.red(err2.message));
112014
+ console.error(import_picocolors22.default.red(err2.message));
111698
112015
  return 1;
111699
112016
  }
111700
112017
  const code = codeForRepo(config2, renv.project);
@@ -111702,16 +112019,16 @@ async function runOwner(query, opts) {
111702
112019
  console.log(opts.json ? ownershipToJSON(o5) : renderOwnership(o5));
111703
112020
  return 0;
111704
112021
  } catch (err2) {
111705
- console.error(import_picocolors21.default.red(err2.message));
112022
+ console.error(import_picocolors22.default.red(err2.message));
111706
112023
  return 1;
111707
112024
  }
111708
112025
  }
111709
112026
 
111710
112027
  // ../../packages/cli/src/commands/postmortem.ts
111711
112028
  init_cjs_shims();
111712
- var import_node_fs15 = require("fs");
111713
- var import_node_path16 = require("path");
111714
- 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);
111715
112032
  async function runPostmortem(id, opts) {
111716
112033
  const log = opts.write ?? ((line2) => console.log(line2));
111717
112034
  let sql2;
@@ -111725,11 +112042,11 @@ async function runPostmortem(id, opts) {
111725
112042
  const { db } = conn;
111726
112043
  const row = await getInvestigation(db, id);
111727
112044
  if (!row) {
111728
- log(import_picocolors22.default.red("No investigation found: " + id));
112045
+ log(import_picocolors23.default.red("No investigation found: " + id));
111729
112046
  return 1;
111730
112047
  }
111731
112048
  if (!row.report) {
111732
- log(import_picocolors22.default.red("Investigation " + id + " has no stored report."));
112049
+ log(import_picocolors23.default.red("Investigation " + id + " has no stored report."));
111733
112050
  return 1;
111734
112051
  }
111735
112052
  report = migrateReport(row.report);
@@ -111821,25 +112138,25 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
111821
112138
  }
111822
112139
  }
111823
112140
  if (opts.output) {
111824
- const outputPath = (0, import_node_path16.resolve)(opts.output);
111825
- if ((0, import_node_fs15.existsSync)(outputPath) && !opts.force) {
111826
- log(import_picocolors22.default.red("\u2717") + " " + outputPath + " already exists");
111827
- 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"));
111828
112145
  return 1;
111829
112146
  }
111830
112147
  try {
111831
- (0, import_node_fs15.writeFileSync)(outputPath, content, "utf8");
112148
+ (0, import_node_fs16.writeFileSync)(outputPath, content, "utf8");
111832
112149
  } catch (err2) {
111833
- 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);
111834
112151
  return 1;
111835
112152
  }
111836
- log(import_picocolors22.default.green("\u2713") + " Saved postmortem to " + outputPath);
112153
+ log(import_picocolors23.default.green("\u2713") + " Saved postmortem to " + outputPath);
111837
112154
  } else {
111838
112155
  log(content);
111839
112156
  }
111840
112157
  } catch (err2) {
111841
112158
  const message = err2 instanceof Error ? err2.message : String(err2);
111842
- log(import_picocolors22.default.red("postmortem failed: " + message));
112159
+ log(import_picocolors23.default.red("postmortem failed: " + message));
111843
112160
  return 1;
111844
112161
  } finally {
111845
112162
  if (sql2) {
@@ -111851,7 +112168,7 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
111851
112168
 
111852
112169
  // ../../packages/cli/src/commands/score.ts
111853
112170
  init_cjs_shims();
111854
- var import_picocolors23 = __toESM(require_picocolors(), 1);
112171
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
111855
112172
  var SCORE_AI_CONTRACT = `Provide a clearly separated AI score explanation with:
111856
112173
 
111857
112174
  Why this scored this way
@@ -111870,11 +112187,11 @@ async function runScore(id, opts) {
111870
112187
  try {
111871
112188
  const row = await getInvestigation(db, id);
111872
112189
  if (!row) {
111873
- console.error(import_picocolors23.default.red("No investigation found: " + id));
112190
+ console.error(import_picocolors24.default.red("No investigation found: " + id));
111874
112191
  return 1;
111875
112192
  }
111876
112193
  if (!row.report) {
111877
- 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."));
111878
112195
  return 1;
111879
112196
  }
111880
112197
  const s5 = scoreInvestigation(migrateReport(row.report));
@@ -111895,7 +112212,7 @@ async function runScore(id, opts) {
111895
112212
  });
111896
112213
  console.log("\n" + renderInterpretation(result));
111897
112214
  if (!result.ok) {
111898
- console.error(import_picocolors23.default.yellow(`[ai] ${result.warning}`));
112215
+ console.error(import_picocolors24.default.yellow(`[ai] ${result.warning}`));
111899
112216
  }
111900
112217
  }
111901
112218
  }
@@ -111935,7 +112252,7 @@ async function runScores(opts) {
111935
112252
 
111936
112253
  // ../../packages/cli/src/commands/ask.ts
111937
112254
  init_cjs_shims();
111938
- var import_picocolors24 = __toESM(require_picocolors(), 1);
112255
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
111939
112256
  function renderAnswer(report, directive, opts) {
111940
112257
  const answer = answerQuestion(report, directive);
111941
112258
  if (answer) {
@@ -111947,13 +112264,13 @@ function renderAnswer(report, directive, opts) {
111947
112264
  if (!opts.json && report.aiJudgment) {
111948
112265
  const j3 = report.aiJudgment;
111949
112266
  console.log("");
111950
- console.log(import_picocolors24.default.dim("\u2500".repeat(60)));
111951
- 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}):`));
111952
112269
  if (j3.rootCauseAssessment) {
111953
- console.log(import_picocolors24.default.bold("Root cause (AI):"), j3.rootCauseAssessment.summary);
111954
- 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}`));
111955
112272
  } else {
111956
- console.log(import_picocolors24.default.bold("AI Why:"), j3.why);
112273
+ console.log(import_picocolors25.default.bold("AI Why:"), j3.why);
111957
112274
  }
111958
112275
  }
111959
112276
  return 0;
@@ -111982,8 +112299,8 @@ async function runAsk(id, directive, opts) {
111982
112299
  const session = authedClient();
111983
112300
  if (!session) {
111984
112301
  console.error(
111985
- import_picocolors24.default.red(
111986
- `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.`
111987
112304
  )
111988
112305
  );
111989
112306
  return 1;
@@ -111992,15 +112309,15 @@ async function runAsk(id, directive, opts) {
111992
112309
  const report = await fetchInvestigationReportFromCloud(session.client, cloudCfg, id);
111993
112310
  if (report) return renderAnswer(report, directive, opts);
111994
112311
  console.error(
111995
- import_picocolors24.default.red(
111996
- `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.`
111997
112314
  )
111998
112315
  );
111999
112316
  return 1;
112000
112317
  } catch (err2) {
112001
112318
  if (err2 instanceof CloudError && err2.status === 404) {
112002
112319
  console.error(
112003
- import_picocolors24.default.red(
112320
+ import_picocolors25.default.red(
112004
112321
  `No investigation found for "${id}" locally or in Horus Cloud. Use the id printed at the top of \`horus investigate\` output.`
112005
112322
  )
112006
112323
  );
@@ -112014,21 +112331,21 @@ async function runAsk(id, directive, opts) {
112014
112331
  case "found":
112015
112332
  return renderAnswer(local.report, directive, opts);
112016
112333
  case "no-report":
112017
- 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."));
112018
112335
  return 1;
112019
112336
  case "error":
112020
- console.error(import_picocolors24.default.red(local.message));
112337
+ console.error(import_picocolors25.default.red(local.message));
112021
112338
  return 1;
112022
112339
  case "not-found":
112023
112340
  default:
112024
- console.error(import_picocolors24.default.red("No investigation found: " + id));
112341
+ console.error(import_picocolors25.default.red("No investigation found: " + id));
112025
112342
  return 1;
112026
112343
  }
112027
112344
  }
112028
112345
 
112029
112346
  // ../../packages/cli/src/commands/onboard.ts
112030
112347
  init_cjs_shims();
112031
- var import_picocolors25 = __toESM(require_picocolors(), 1);
112348
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
112032
112349
  var ONBOARD_AI_CONTRACT = `Provide a clearly separated AI onboarding guide with:
112033
112350
 
112034
112351
  Start here
@@ -112056,7 +112373,7 @@ async function runOnboard(area, opts) {
112056
112373
  try {
112057
112374
  renv = resolveEnvironment(config2, { project: opts.repo });
112058
112375
  } catch (err2) {
112059
- console.error(import_picocolors25.default.red(err2.message));
112376
+ console.error(import_picocolors26.default.red(err2.message));
112060
112377
  return 1;
112061
112378
  }
112062
112379
  const repo = { name: renv.project, path: renv.path };
@@ -112064,7 +112381,7 @@ async function runOnboard(area, opts) {
112064
112381
  const health = await code.health();
112065
112382
  if (!health.ok) {
112066
112383
  console.error(
112067
- 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")
112068
112385
  );
112069
112386
  return 1;
112070
112387
  }
@@ -112091,7 +112408,7 @@ async function runOnboard(area, opts) {
112091
112408
  });
112092
112409
  console.log("\n" + renderInterpretation(result));
112093
112410
  if (!result.ok) {
112094
- console.error(import_picocolors25.default.yellow(`[ai] ${result.warning}`));
112411
+ console.error(import_picocolors26.default.yellow(`[ai] ${result.warning}`));
112095
112412
  }
112096
112413
  }
112097
112414
  }
@@ -112100,14 +112417,14 @@ async function runOnboard(area, opts) {
112100
112417
  }
112101
112418
  return 0;
112102
112419
  } catch (err2) {
112103
- console.error(import_picocolors25.default.red(err2.message));
112420
+ console.error(import_picocolors26.default.red(err2.message));
112104
112421
  return 1;
112105
112422
  }
112106
112423
  }
112107
112424
 
112108
112425
  // ../../packages/cli/src/commands/simulate.ts
112109
112426
  init_cjs_shims();
112110
- var import_picocolors26 = __toESM(require_picocolors(), 1);
112427
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
112111
112428
  async function runSimulate(scenarioId, opts) {
112112
112429
  try {
112113
112430
  if (scenarioId === void 0) {
@@ -112117,7 +112434,7 @@ async function runSimulate(scenarioId, opts) {
112117
112434
  const scenario = getScenario(scenarioId);
112118
112435
  if (scenario === null) {
112119
112436
  console.error(
112120
- import_picocolors26.default.red(
112437
+ import_picocolors27.default.red(
112121
112438
  `Unknown scenario: "${scenarioId}". Run horus simulate to list available scenarios.`
112122
112439
  )
112123
112440
  );
@@ -112128,14 +112445,14 @@ async function runSimulate(scenarioId, opts) {
112128
112445
  try {
112129
112446
  renv = resolveEnvironment(config2, { project: opts.repo });
112130
112447
  } catch (err2) {
112131
- console.error(import_picocolors26.default.red(err2.message));
112448
+ console.error(import_picocolors27.default.red(err2.message));
112132
112449
  return 1;
112133
112450
  }
112134
112451
  const code = codeForRepo(config2, renv.project);
112135
112452
  const health = await code.health();
112136
112453
  if (!health.ok) {
112137
112454
  console.error(
112138
- 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")
112139
112456
  );
112140
112457
  return 1;
112141
112458
  }
@@ -112152,14 +112469,14 @@ async function runSimulate(scenarioId, opts) {
112152
112469
  }
112153
112470
  return 0;
112154
112471
  } catch (err2) {
112155
- console.error(import_picocolors26.default.red(err2.message));
112472
+ console.error(import_picocolors27.default.red(err2.message));
112156
112473
  return 1;
112157
112474
  }
112158
112475
  }
112159
112476
 
112160
112477
  // ../../packages/cli/src/commands/logs.ts
112161
112478
  init_cjs_shims();
112162
- var import_picocolors27 = __toESM(require_picocolors(), 1);
112479
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
112163
112480
  var LOGS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
112164
112481
 
112165
112482
  Evidence used
@@ -112205,9 +112522,9 @@ function parseWhere(raw) {
112205
112522
  }
112206
112523
  var whereLabel = (where) => where.map((w4) => `${w4.field}=${w4.value}`).join(", ");
112207
112524
  function levelColor(level, text2) {
112208
- if (level === "error" || level === "fatal") return import_picocolors27.default.red(text2);
112209
- if (level === "warn") return import_picocolors27.default.yellow(text2);
112210
- 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);
112211
112528
  }
112212
112529
  function matchServiceLabel(requested, candidates) {
112213
112530
  if (candidates.includes(requested)) return { kind: "unique", matched: requested };
@@ -112223,7 +112540,7 @@ async function runLogs(service, opts) {
112223
112540
  try {
112224
112541
  where = parseWhere(opts.where);
112225
112542
  } catch (err2) {
112226
- console.error(import_picocolors27.default.red(err2.message));
112543
+ console.error(import_picocolors28.default.red(err2.message));
112227
112544
  return 1;
112228
112545
  }
112229
112546
  const hasWhere = where.length > 0;
@@ -112233,13 +112550,13 @@ async function runLogs(service, opts) {
112233
112550
  try {
112234
112551
  renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
112235
112552
  } catch (err2) {
112236
- console.error(import_picocolors27.default.red(err2.message));
112553
+ console.error(import_picocolors28.default.red(err2.message));
112237
112554
  return 1;
112238
112555
  }
112239
112556
  const logs = logsForEnv(renv);
112240
112557
  if (logs === null) {
112241
112558
  console.error(
112242
- import_picocolors27.default.red(
112559
+ import_picocolors28.default.red(
112243
112560
  `No Elasticsearch connector configured for ${renv.project}/${renv.env} (set ES_URL / ES_USERNAME / ES_PASSWORD)`
112244
112561
  )
112245
112562
  );
@@ -112247,7 +112564,7 @@ async function runLogs(service, opts) {
112247
112564
  }
112248
112565
  const health = await logs.health();
112249
112566
  if (!health.ok) {
112250
- console.error(import_picocolors27.default.red(`Elasticsearch unreachable: ${health.detail}`));
112567
+ console.error(import_picocolors28.default.red(`Elasticsearch unreachable: ${health.detail}`));
112251
112568
  return 1;
112252
112569
  }
112253
112570
  const resolvedService = service ?? renv.connectors.elasticsearch?.serviceName;
@@ -112260,21 +112577,21 @@ async function runLogs(service, opts) {
112260
112577
  requiresEventCode: aggregatesEventCode
112261
112578
  });
112262
112579
  for (const w4 of compat.issues.filter((i3) => i3.severity === "warning")) {
112263
- console.warn(import_picocolors27.default.yellow(`[warn] ${w4.field}: ${w4.message}`));
112580
+ console.warn(import_picocolors28.default.yellow(`[warn] ${w4.field}: ${w4.message}`));
112264
112581
  }
112265
112582
  const errors2 = compat.issues.filter((i3) => i3.severity === "error");
112266
112583
  if (errors2.length > 0) {
112267
- 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:"));
112268
112585
  for (const e of errors2) {
112269
- console.error(import_picocolors27.default.red(` ${e.field}: ${e.message}`));
112586
+ console.error(import_picocolors28.default.red(` ${e.field}: ${e.message}`));
112270
112587
  }
112271
112588
  console.error(
112272
- 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.")
112273
112590
  );
112274
112591
  return 1;
112275
112592
  }
112276
112593
  } catch {
112277
- 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."));
112278
112595
  }
112279
112596
  const from = sinceToIso(opts.since) ?? new Date(Date.now() - 7 * 864e5).toISOString();
112280
112597
  const fromDisplay = from.slice(0, 16).replace("T", " ");
@@ -112330,12 +112647,12 @@ async function runLogs(service, opts) {
112330
112647
  return 0;
112331
112648
  }
112332
112649
  console.error(
112333
- import_picocolors27.default.dim(
112650
+ import_picocolors28.default.dim(
112334
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)" : "")
112335
112652
  )
112336
112653
  );
112337
112654
  if (records.length === 0) {
112338
- console.log(import_picocolors27.default.dim("No logs matched."));
112655
+ console.log(import_picocolors28.default.dim("No logs matched."));
112339
112656
  return 0;
112340
112657
  }
112341
112658
  for (const r of records) {
@@ -112345,12 +112662,12 @@ async function runLogs(service, opts) {
112345
112662
  const line2 = `${ts} ${lvl} ${comp} ${r.message.slice(0, 120)}`;
112346
112663
  console.log(levelColor(r.level, line2));
112347
112664
  for (const { key, value } of extractContextFields(r)) {
112348
- console.log(import_picocolors27.default.dim(` ${key}: ${value.slice(0, 200)}`));
112665
+ console.log(import_picocolors28.default.dim(` ${key}: ${value.slice(0, 200)}`));
112349
112666
  }
112350
112667
  }
112351
112668
  if (truncated) {
112352
112669
  console.error(
112353
- import_picocolors27.default.yellow(
112670
+ import_picocolors28.default.yellow(
112354
112671
  `[warn] showing the first ${limit} record(s) \u2014 results may be truncated. Narrow with --where/--since/--level or raise --limit (max 1000).`
112355
112672
  )
112356
112673
  );
@@ -112393,22 +112710,22 @@ async function runLogs(service, opts) {
112393
112710
  return 0;
112394
112711
  }
112395
112712
  console.log(
112396
- import_picocolors27.default.bold(`Top ${field}`) + import_picocolors27.default.dim(
112713
+ import_picocolors28.default.bold(`Top ${field}`) + import_picocolors28.default.dim(
112397
112714
  ` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
112398
112715
  )
112399
112716
  );
112400
- 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+`));
112401
112718
  console.log("");
112402
112719
  if (buckets.length === 0) {
112403
- console.log(import_picocolors27.default.dim(` No error logs with field "${field}" in the window.`));
112404
- 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.`));
112405
112722
  return 0;
112406
112723
  }
112407
112724
  for (const b5 of buckets) {
112408
- 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}`);
112409
112726
  }
112410
112727
  console.log("");
112411
- 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).`));
112412
112729
  return 0;
112413
112730
  }
112414
112731
  let analysis = await logs.analyzeErrors({
@@ -112478,46 +112795,46 @@ async function runLogs(service, opts) {
112478
112795
  return 0;
112479
112796
  }
112480
112797
  console.log(
112481
- import_picocolors27.default.bold(`Error analysis`) + import_picocolors27.default.dim(
112798
+ import_picocolors28.default.bold(`Error analysis`) + import_picocolors28.default.dim(
112482
112799
  ` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
112483
112800
  )
112484
112801
  );
112485
- 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+`));
112486
112803
  if (broadeningNote) {
112487
- console.log(import_picocolors27.default.yellow(` ${broadeningNote}`));
112804
+ console.log(import_picocolors28.default.yellow(` ${broadeningNote}`));
112488
112805
  }
112489
112806
  console.log(
112490
- ` ${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`
112491
112808
  );
112492
112809
  console.log("");
112493
112810
  if (analysis.signatures.length === 0) {
112494
- 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."));
112495
112812
  console.log(
112496
- import_picocolors27.default.dim(
112813
+ import_picocolors28.default.dim(
112497
112814
  ` Searched: ${scopeService ? `service "${scopeService}"` : "all services"} \xB7 index: ${indexPattern} \xB7 from: ${fromDisplay} UTC`
112498
112815
  )
112499
112816
  );
112500
- 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).`));
112501
112818
  return 0;
112502
112819
  }
112503
112820
  for (const s5 of analysis.signatures) {
112504
112821
  const tags = [];
112505
- if (s5.isNew) tags.push(import_picocolors27.default.red("NEW"));
112822
+ if (s5.isNew) tags.push(import_picocolors28.default.red("NEW"));
112506
112823
  else if (s5.ratio !== void 0 && Number.isFinite(s5.ratio) && s5.ratio >= 1.5) {
112507
- 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)}`));
112508
112825
  }
112509
- 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(", ")}`) : "";
112510
112827
  const tagStr = tags.length > 0 ? ` [${tags.join(", ")}]` : "";
112511
112828
  console.log(
112512
- ` ${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
112513
112830
  );
112514
112831
  }
112515
112832
  if (analysis.affectedServices.length > 0) {
112516
112833
  console.log("");
112517
- console.log(` ${import_picocolors27.default.bold("Affected services:")} ${analysis.affectedServices.join(", ")}`);
112834
+ console.log(` ${import_picocolors28.default.bold("Affected services:")} ${analysis.affectedServices.join(", ")}`);
112518
112835
  }
112519
112836
  console.log("");
112520
- 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)"));
112521
112838
  if (opts.ai) {
112522
112839
  const result = await renderAiInterpretation({
112523
112840
  command: "logs",
@@ -112536,19 +112853,19 @@ async function runLogs(service, opts) {
112536
112853
  });
112537
112854
  console.log("\n" + renderInterpretation(result));
112538
112855
  if (!result.ok) {
112539
- console.error(import_picocolors27.default.yellow(`[ai] ${result.warning}`));
112856
+ console.error(import_picocolors28.default.yellow(`[ai] ${result.warning}`));
112540
112857
  }
112541
112858
  }
112542
112859
  return 0;
112543
112860
  } catch (err2) {
112544
- console.error(import_picocolors27.default.red(err2.message));
112861
+ console.error(import_picocolors28.default.red(err2.message));
112545
112862
  return 1;
112546
112863
  }
112547
112864
  }
112548
112865
 
112549
112866
  // ../../packages/cli/src/commands/metrics.ts
112550
112867
  init_cjs_shims();
112551
- var import_picocolors28 = __toESM(require_picocolors(), 1);
112868
+ var import_picocolors29 = __toESM(require_picocolors(), 1);
112552
112869
  var METRICS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
112553
112870
 
112554
112871
  Evidence used
@@ -112593,10 +112910,10 @@ function anomalyColor(anomaly) {
112593
112910
  case "latency-spike":
112594
112911
  case "error-rate-change":
112595
112912
  case "throughput-drop":
112596
- return import_picocolors28.default.red;
112913
+ return import_picocolors29.default.red;
112597
112914
  case "queue-growth":
112598
112915
  case "change":
112599
- return import_picocolors28.default.yellow;
112916
+ return import_picocolors29.default.yellow;
112600
112917
  default:
112601
112918
  return (s5) => s5;
112602
112919
  }
@@ -112623,7 +112940,7 @@ async function runMetrics(hint, opts) {
112623
112940
  const metrics = metricsProviderFromConfig(config2);
112624
112941
  if (metrics === null) {
112625
112942
  console.error(
112626
- import_picocolors28.default.red(
112943
+ import_picocolors29.default.red(
112627
112944
  "Grafana not configured \u2014 set GRAFANA_URL + GRAFANA_USER + GRAFANA_PASSWORD"
112628
112945
  )
112629
112946
  );
@@ -112631,7 +112948,7 @@ async function runMetrics(hint, opts) {
112631
112948
  }
112632
112949
  const health = await metrics.health();
112633
112950
  if (!health.ok) {
112634
- console.error(import_picocolors28.default.red(`Grafana unreachable: ${health.detail}`));
112951
+ console.error(import_picocolors29.default.red(`Grafana unreachable: ${health.detail}`));
112635
112952
  return 1;
112636
112953
  }
112637
112954
  const stepNum = opts.step !== void 0 ? Number(opts.step) : void 0;
@@ -112660,14 +112977,14 @@ async function runMetrics(hint, opts) {
112660
112977
  return 0;
112661
112978
  }
112662
112979
  if (series.length === 0) {
112663
- console.log(import_picocolors28.default.dim("No series returned."));
112980
+ console.log(import_picocolors29.default.dim("No series returned."));
112664
112981
  return 0;
112665
112982
  }
112666
112983
  for (const s5 of series) {
112667
112984
  const summary = summarize(s5);
112668
112985
  const label = labelStr(s5.labels);
112669
112986
  console.log(
112670
- ` ${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)}`
112671
112988
  );
112672
112989
  }
112673
112990
  return 0;
@@ -112682,36 +112999,36 @@ async function runMetrics(hint, opts) {
112682
112999
  if (findings2.length === 0) {
112683
113000
  if (hint !== void 0) {
112684
113001
  const tokens = extractHintTokens(hint);
112685
- console.log(import_picocolors28.default.dim(`No panels matched hint "${hint}".`));
113002
+ console.log(import_picocolors29.default.dim(`No panels matched hint "${hint}".`));
112686
113003
  if (tokens.length > 0) {
112687
- 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(", ")}`));
112688
113005
  }
112689
113006
  console.log(
112690
- import_picocolors28.default.dim(
112691
- ` 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.`
112692
113009
  )
112693
113010
  );
112694
113011
  } else {
112695
- 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."));
112696
113013
  }
112697
113014
  return 0;
112698
113015
  }
112699
113016
  const hintSuffix = hint !== void 0 ? ` (hint: "${hint}")` : "";
112700
113017
  const matchSources = hint !== void 0 ? [...new Set(findings2.map((f5) => f5.matchSource).filter(Boolean))] : [];
112701
- 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(", ")}]`) : "";
112702
113019
  console.log(
112703
- import_picocolors28.default.bold(
113020
+ import_picocolors29.default.bold(
112704
113021
  `Grafana metrics${hintSuffix} \u2014 ${findings2.length} series across panels`
112705
113022
  ) + matchSuffix
112706
113023
  );
112707
113024
  console.log(
112708
- import_picocolors28.default.dim(
113025
+ import_picocolors29.default.dim(
112709
113026
  ` window: ${new Date(from * 1e3).toISOString()} \u2013 ${new Date(to * 1e3).toISOString()}`
112710
113027
  )
112711
113028
  );
112712
113029
  console.log("");
112713
113030
  if (flagged.length === 0) {
112714
- console.log(import_picocolors28.default.green(" No anomalies detected."));
113031
+ console.log(import_picocolors29.default.green(" No anomalies detected."));
112715
113032
  } else {
112716
113033
  for (const f5 of flagged) {
112717
113034
  const color = anomalyColor(f5.anomaly);
@@ -112720,14 +113037,14 @@ async function runMetrics(hint, opts) {
112720
113037
  const lblStr = labelStr(f5.labels);
112721
113038
  console.log(
112722
113039
  color(
112723
- ` ${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)}` : "")
112724
113041
  )
112725
113042
  );
112726
113043
  }
112727
113044
  }
112728
113045
  if (ok.length > 0) {
112729
113046
  console.log("");
112730
- 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.`));
112731
113048
  }
112732
113049
  if (opts.ai) {
112733
113050
  const result = await renderAiInterpretation({
@@ -112742,19 +113059,19 @@ async function runMetrics(hint, opts) {
112742
113059
  });
112743
113060
  console.log("\n" + renderInterpretation(result));
112744
113061
  if (!result.ok) {
112745
- console.error(import_picocolors28.default.yellow(`[ai] ${result.warning}`));
113062
+ console.error(import_picocolors29.default.yellow(`[ai] ${result.warning}`));
112746
113063
  }
112747
113064
  }
112748
113065
  return 0;
112749
113066
  } catch (err2) {
112750
- console.error(import_picocolors28.default.red(err2.message));
113067
+ console.error(import_picocolors29.default.red(err2.message));
112751
113068
  return 1;
112752
113069
  }
112753
113070
  }
112754
113071
 
112755
113072
  // ../../packages/cli/src/commands/state.ts
112756
113073
  init_cjs_shims();
112757
- var import_picocolors29 = __toESM(require_picocolors(), 1);
113074
+ var import_picocolors30 = __toESM(require_picocolors(), 1);
112758
113075
  var STATE_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
112759
113076
 
112760
113077
  Evidence used
@@ -112780,13 +113097,13 @@ async function runState(opts) {
112780
113097
  try {
112781
113098
  renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
112782
113099
  } catch (err2) {
112783
- console.error(import_picocolors29.default.red(err2.message));
113100
+ console.error(import_picocolors30.default.red(err2.message));
112784
113101
  return 1;
112785
113102
  }
112786
113103
  const mongo = mongoForEnv(renv);
112787
113104
  if (mongo === null) {
112788
113105
  console.error(
112789
- import_picocolors29.default.red(
113106
+ import_picocolors30.default.red(
112790
113107
  `No MongoDB connector configured for ${renv.project}/${renv.env} (set the project's Mongo URL env var).`
112791
113108
  )
112792
113109
  );
@@ -112794,7 +113111,7 @@ async function runState(opts) {
112794
113111
  }
112795
113112
  const health = await mongo.health();
112796
113113
  if (!health.ok) {
112797
- console.error(import_picocolors29.default.red(`MongoDB unreachable: ${health.detail}`));
113114
+ console.error(import_picocolors30.default.red(`MongoDB unreachable: ${health.detail}`));
112798
113115
  await mongo.close();
112799
113116
  return 1;
112800
113117
  }
@@ -112820,38 +113137,38 @@ async function runState(opts) {
112820
113137
  );
112821
113138
  return 0;
112822
113139
  }
112823
- 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)`) : "";
112824
113141
  console.log(
112825
- 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
112826
113143
  );
112827
113144
  console.log("");
112828
113145
  let signals = 0;
112829
113146
  for (const c4 of analysis.collections) {
112830
113147
  const flags2 = [];
112831
113148
  if (c4.isStale === true) {
112832
- 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)`));
112833
113150
  }
112834
113151
  for (const a2 of c4.anomalies) {
112835
- flags2.push(import_picocolors29.default.red(`${a2.count}\xD7 "${a2.value}"`));
113152
+ flags2.push(import_picocolors30.default.red(`${a2.count}\xD7 "${a2.value}"`));
112836
113153
  signals += 1;
112837
113154
  }
112838
113155
  if (c4.isStale === true) signals += 1;
112839
- const head = ` ${import_picocolors29.default.bold(c4.collection.padEnd(18).slice(0, 18))} ${import_picocolors29.default.dim(String(c4.count).padStart(8) + " docs")}`;
112840
- 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));
112841
113158
  }
112842
113159
  console.log("");
112843
113160
  if (analysis.collections.length === 0) {
112844
113161
  console.log(
112845
- 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.")
112846
113163
  );
112847
113164
  } else {
112848
113165
  const scope = analysis.autoDiscovered ? "collection(s)" : "allowlisted collection(s)";
112849
113166
  console.log(
112850
- 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}.`)
112851
113168
  );
112852
113169
  if (analysis.autoDiscovered) {
112853
113170
  console.log(
112854
- import_picocolors29.default.dim(
113171
+ import_picocolors30.default.dim(
112855
113172
  ` Tip: add a "collections" list to your MongoDB connector config to restrict analysis.`
112856
113173
  )
112857
113174
  );
@@ -112869,7 +113186,7 @@ async function runState(opts) {
112869
113186
  });
112870
113187
  console.log("\n" + renderInterpretation(result));
112871
113188
  if (!result.ok) {
112872
- console.error(import_picocolors29.default.yellow(`[ai] ${result.warning}`));
113189
+ console.error(import_picocolors30.default.yellow(`[ai] ${result.warning}`));
112873
113190
  }
112874
113191
  }
112875
113192
  return 0;
@@ -112877,20 +113194,20 @@ async function runState(opts) {
112877
113194
  await mongo.close();
112878
113195
  }
112879
113196
  } catch (err2) {
112880
- console.error(import_picocolors29.default.red(err2.message));
113197
+ console.error(import_picocolors30.default.red(err2.message));
112881
113198
  return 1;
112882
113199
  }
112883
113200
  }
112884
113201
 
112885
113202
  // ../../packages/cli/src/commands/init.ts
112886
113203
  init_cjs_shims();
112887
- var import_picocolors30 = __toESM(require_picocolors(), 1);
112888
- var import_node_path17 = require("path");
113204
+ var import_picocolors31 = __toESM(require_picocolors(), 1);
113205
+ var import_node_path18 = require("path");
112889
113206
  async function runInit(opts) {
112890
113207
  try {
112891
113208
  const cwd = process.cwd();
112892
- const root = opts.path ? (0, import_node_path17.resolve)(opts.path) : findRepoRoot(cwd) ?? cwd;
112893
- 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);
112894
113211
  const envName = opts.env ?? "production";
112895
113212
  const repo = { name: name2, path: root };
112896
113213
  if (opts.source) repo["source"] = { hostUrl: opts.source };
@@ -112905,65 +113222,65 @@ async function runInit(opts) {
112905
113222
  const configPath = writeLocalConfig(root, file);
112906
113223
  registerProject(name2, root, configPath);
112907
113224
  ensureProjectGitignore(root);
112908
- console.log(`${import_picocolors30.default.green("\u2713")} Initialized Horus project ${import_picocolors30.default.bold(name2)}`);
112909
- console.log(import_picocolors30.default.dim(` config: ${configPath}`));
112910
- 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>"`));
112911
113228
  if (!opts.source) {
112912
113229
  console.log(
112913
- 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>")
112914
113231
  );
112915
113232
  }
112916
113233
  console.log(
112917
- 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")
112918
113235
  );
112919
113236
  console.log(
112920
- 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")
112921
113238
  );
112922
113239
  return 0;
112923
113240
  } catch (err2) {
112924
- console.error(import_picocolors30.default.red(err2.message));
113241
+ console.error(import_picocolors31.default.red(err2.message));
112925
113242
  return 1;
112926
113243
  }
112927
113244
  }
112928
113245
 
112929
113246
  // ../../packages/cli/src/commands/projects.ts
112930
113247
  init_cjs_shims();
112931
- var import_picocolors31 = __toESM(require_picocolors(), 1);
113248
+ var import_picocolors32 = __toESM(require_picocolors(), 1);
112932
113249
  async function runProjects() {
112933
113250
  const reg = readRegistry();
112934
113251
  const names = Object.keys(reg.projects).sort();
112935
113252
  if (names.length === 0) {
112936
113253
  console.log(
112937
- 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.")
112938
113255
  );
112939
113256
  return 0;
112940
113257
  }
112941
- console.log(import_picocolors31.default.bold("Registered Horus projects"));
113258
+ console.log(import_picocolors32.default.bold("Registered Horus projects"));
112942
113259
  console.log("");
112943
113260
  for (const name2 of names) {
112944
113261
  const entry2 = reg.projects[name2];
112945
113262
  if (entry2 === void 0) continue;
112946
- 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)}`);
112947
113264
  }
112948
113265
  console.log("");
112949
- console.log(import_picocolors31.default.dim(' horus investigate --name <name> "<hint>"'));
113266
+ console.log(import_picocolors32.default.dim(' horus investigate --name <name> "<hint>"'));
112950
113267
  return 0;
112951
113268
  }
112952
113269
 
112953
113270
  // ../../packages/cli/src/commands/setup.ts
112954
113271
  init_cjs_shims();
112955
- var import_picocolors32 = __toESM(require_picocolors(), 1);
113272
+ var import_picocolors33 = __toESM(require_picocolors(), 1);
112956
113273
  var DEFAULT_DB_URL3 = "postgresql://horus:horus@localhost:5433/horus";
112957
113274
  async function runSetup(opts) {
112958
113275
  const write = opts.write ?? ((line2) => console.log(line2));
112959
113276
  let ok = true;
112960
- write(import_picocolors32.default.bold("\nHorus setup\n"));
113277
+ write(import_picocolors33.default.bold("\nHorus setup\n"));
112961
113278
  const backendVersion = await getSourceVersion();
112962
113279
  if (backendVersion === null) {
112963
113280
  ok = false;
112964
- 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`);
112965
113282
  write(
112966
- import_picocolors32.default.dim(
113283
+ import_picocolors33.default.dim(
112967
113284
  ` install it (Python 3.11+ required):
112968
113285
  curl -fsSL https://horus.sh/install.sh | bash
112969
113286
  ensure ~/.local/bin is on your PATH`
@@ -112972,17 +113289,17 @@ async function runSetup(opts) {
112972
113289
  } else if (backendVersion !== PINNED_SOURCE_VERSION) {
112973
113290
  ok = false;
112974
113291
  write(
112975
- ` ${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})`)
112976
113293
  );
112977
113294
  write(
112978
- import_picocolors32.default.dim(
113295
+ import_picocolors33.default.dim(
112979
113296
  ` update it:
112980
113297
  horus update`
112981
113298
  )
112982
113299
  );
112983
113300
  } else {
112984
113301
  write(
112985
- ` ${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})`)
112986
113303
  );
112987
113304
  }
112988
113305
  let dbUrl = process.env["DATABASE_URL"] ?? DEFAULT_DB_URL3;
@@ -112994,11 +113311,11 @@ async function runSetup(opts) {
112994
113311
  }
112995
113312
  const db = await checkDatabase(dbUrl);
112996
113313
  if (db.reachable) {
112997
- 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})`)}`);
112998
113315
  if (!db.schemaReady) {
112999
113316
  ok = false;
113000
113317
  write(
113001
- import_picocolors32.default.dim(
113318
+ import_picocolors33.default.dim(
113002
113319
  ` schema not applied \u2014 run migrations:
113003
113320
  from the Horus repo: pnpm db migrate
113004
113321
  or apply the schema directly: https://github.com/meritt-dev/horus/tree/master/packages/db/drizzle`
@@ -113007,9 +113324,9 @@ async function runSetup(opts) {
113007
113324
  }
113008
113325
  } else {
113009
113326
  ok = false;
113010
- write(` ${import_picocolors32.default.red("\u25CF")} Postgres unreachable`);
113327
+ write(` ${import_picocolors33.default.red("\u25CF")} Postgres unreachable`);
113011
113328
  write(
113012
- import_picocolors32.default.dim(
113329
+ import_picocolors33.default.dim(
113013
113330
  ` start a local instance:
113014
113331
  docker run -d --name horus-db \\
113015
113332
  -e POSTGRES_USER=horus -e POSTGRES_PASSWORD=horus -e POSTGRES_DB=horus \\
@@ -113034,10 +113351,10 @@ async function runSetup(opts) {
113034
113351
  if (!health.ok) {
113035
113352
  ok = false;
113036
113353
  write(
113037
- ` ${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})`)
113038
113355
  );
113039
113356
  write(
113040
- import_picocolors32.default.dim(
113357
+ import_picocolors33.default.dim(
113041
113358
  ` start the source intelligence host:
113042
113359
  cd ${repo.path}
113043
113360
  horus index`
@@ -113048,10 +113365,10 @@ async function runSetup(opts) {
113048
113365
  if (count === 0) {
113049
113366
  ok = false;
113050
113367
  write(
113051
- ` ${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`
113052
113369
  );
113053
113370
  write(
113054
- import_picocolors32.default.dim(
113371
+ import_picocolors33.default.dim(
113055
113372
  ` index the repo:
113056
113373
  cd ${repo.path}
113057
113374
  horus index`
@@ -113059,7 +113376,7 @@ async function runSetup(opts) {
113059
113376
  );
113060
113377
  } else {
113061
113378
  write(
113062
- ` ${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})`)
113063
113380
  );
113064
113381
  }
113065
113382
  }
@@ -113069,10 +113386,10 @@ async function runSetup(opts) {
113069
113386
  write("");
113070
113387
  if (ok) {
113071
113388
  write(
113072
- 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>"`.'
113073
113390
  );
113074
113391
  } else {
113075
- 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`."));
113076
113393
  }
113077
113394
  return ok ? 0 : 1;
113078
113395
  }
@@ -113080,13 +113397,13 @@ async function runSetup(opts) {
113080
113397
  // ../../packages/cli/src/commands/connect.ts
113081
113398
  init_cjs_shims();
113082
113399
  var import_node_readline = require("readline");
113083
- var import_node_child_process6 = require("child_process");
113084
- var import_picocolors35 = __toESM(require_picocolors(), 1);
113400
+ var import_node_child_process7 = require("child_process");
113401
+ var import_picocolors36 = __toESM(require_picocolors(), 1);
113085
113402
 
113086
113403
  // ../../packages/cli/src/lib/tty-selector.ts
113087
113404
  init_cjs_shims();
113088
113405
  var import_node_tty = require("tty");
113089
- var import_picocolors33 = __toESM(require_picocolors(), 1);
113406
+ var import_picocolors34 = __toESM(require_picocolors(), 1);
113090
113407
  var ExitPromptError = class extends Error {
113091
113408
  constructor(message = "Prompt was cancelled") {
113092
113409
  super(message);
@@ -113142,7 +113459,7 @@ async function checkboxSearch(opts) {
113142
113459
  clear();
113143
113460
  const lines = [];
113144
113461
  lines.push(
113145
- `? ${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)")}`
113146
113463
  );
113147
113464
  const visible = filtered.slice(visibleOffset, visibleOffset + pageSize);
113148
113465
  for (let i3 = 0; i3 < visible.length; i3++) {
@@ -113150,29 +113467,29 @@ async function checkboxSearch(opts) {
113150
113467
  const choice = visible[i3];
113151
113468
  const isCursor = idx === cursor;
113152
113469
  const isSelected = selected.has(choice);
113153
- const marker = isSelected ? import_picocolors33.default.green("\u25CF") : " ";
113470
+ const marker = isSelected ? import_picocolors34.default.green("\u25CF") : " ";
113154
113471
  const prefix = `[${marker}]`;
113155
- const label = isCursor ? import_picocolors33.default.cyan(choice) : choice;
113156
- 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") : " ";
113157
113474
  lines.push(` ${pointer} ${prefix} ${label}`);
113158
113475
  }
113159
113476
  if (filtered.length === 0) {
113160
- lines.push(import_picocolors33.default.dim(" No matches"));
113477
+ lines.push(import_picocolors34.default.dim(" No matches"));
113161
113478
  }
113162
113479
  const statusParts = [
113163
113480
  `${selected.size} selected`,
113164
113481
  `${filtered.length}/${choices.length} matches`
113165
113482
  ];
113166
113483
  if (filter) statusParts.push(`filter: ${filter}`);
113167
- lines.push(import_picocolors33.default.dim(` ${statusParts.join(" \xB7 ")}`));
113484
+ lines.push(import_picocolors34.default.dim(` ${statusParts.join(" \xB7 ")}`));
113168
113485
  output.write(lines.join("\n"));
113169
113486
  lastRenderLines = lines.length;
113170
113487
  }
113171
113488
  function finish(values2) {
113172
113489
  cleanup();
113173
- 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");
113174
113491
  output.write(`
113175
- ? ${import_picocolors33.default.bold(message)} ${summary}
113492
+ ? ${import_picocolors34.default.bold(message)} ${summary}
113176
113493
  `);
113177
113494
  resolve9(values2);
113178
113495
  }
@@ -113263,10 +113580,10 @@ async function checkboxSearch(opts) {
113263
113580
 
113264
113581
  // ../../packages/cli/src/commands/connect-ai.ts
113265
113582
  init_cjs_shims();
113266
- var import_node_child_process5 = require("child_process");
113267
- var import_picocolors34 = __toESM(require_picocolors(), 1);
113583
+ var import_node_child_process6 = require("child_process");
113584
+ var import_picocolors35 = __toESM(require_picocolors(), 1);
113268
113585
  function cliInstalled(id) {
113269
- 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 });
113270
113587
  return !r.error;
113271
113588
  }
113272
113589
  async function probeAnthropic(apiKey) {
@@ -113292,13 +113609,13 @@ async function runConnectAi(opts) {
113292
113609
  const root = findRepoRoot(cwd) ?? cwd;
113293
113610
  const configPath = discoverLocalConfig(cwd) ?? localConfigPath(root);
113294
113611
  const installed = LOCAL_PROVIDER_IDS.filter((id) => cliInstalled(id));
113295
- console.log(import_picocolors34.default.bold("\nAI providers"));
113612
+ console.log(import_picocolors35.default.bold("\nAI providers"));
113296
113613
  console.log(
113297
- import_picocolors34.default.dim(
113614
+ import_picocolors35.default.dim(
113298
113615
  ` Local CLIs on PATH: ${installed.length > 0 ? installed.join(", ") : "none detected"}`
113299
113616
  )
113300
113617
  );
113301
- 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"));
113302
113619
  let provider = opts.provider;
113303
113620
  if (provider === void 0) {
113304
113621
  if (isInteractive()) {
@@ -113309,7 +113626,7 @@ async function runConnectAi(opts) {
113309
113626
  }
113310
113627
  }
113311
113628
  if (!AI_PROVIDERS.includes(provider)) {
113312
- 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(`
113313
113630
  supported: ${AI_PROVIDERS.join(", ")}`));
113314
113631
  return 1;
113315
113632
  }
@@ -113322,7 +113639,7 @@ async function runConnectAi(opts) {
113322
113639
  }
113323
113640
  if (!apiKey) {
113324
113641
  console.error(
113325
- 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.")
113326
113643
  );
113327
113644
  return 1;
113328
113645
  }
@@ -113330,28 +113647,28 @@ async function runConnectAi(opts) {
113330
113647
  const probe2 = await probeAnthropic(apiKey);
113331
113648
  if (!probe2.ok) {
113332
113649
  console.error(`
113333
- ${import_picocolors34.default.red("\u2717 Anthropic key check failed:")} ${probe2.detail}`);
113334
- 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."));
113335
113652
  return 1;
113336
113653
  }
113337
113654
  console.log(`
113338
- ${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})`)}`);
113339
113656
  }
113340
113657
  if (opts.model) aiBlock["anthropic"] = { model: opts.model };
113341
113658
  storedKey = apiKey;
113342
113659
  } else {
113343
113660
  if (!installed.includes(provider)) {
113344
113661
  console.error(
113345
- 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(`
113346
113663
  Install it first, or choose anthropic.`)
113347
113664
  );
113348
113665
  return 1;
113349
113666
  }
113350
113667
  const desc2 = DEFAULT_LOCAL_PROVIDER_REGISTRY.get(provider);
113351
113668
  console.log(`
113352
- ${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")}`);
113353
113670
  console.log(
113354
- import_picocolors34.default.dim(
113671
+ import_picocolors35.default.dim(
113355
113672
  " Note: `investigate --ai` currently uses the Anthropic API for narrative.\n Local-CLI narrative generation is recorded as your preference for future use."
113356
113673
  )
113357
113674
  );
@@ -113369,15 +113686,15 @@ ${import_picocolors34.default.green("\u2713")} ${desc2?.displayName ?? provider}
113369
113686
  });
113370
113687
  }
113371
113688
  console.log(`
113372
- ${import_picocolors34.default.green("\u2713")} ${import_picocolors34.default.bold("ai")} provider saved \u2192 ${import_picocolors34.default.dim(configPath)}`);
113373
- 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}`));
113374
113691
  if (storedKey && secretsPathStr) {
113375
- 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)`));
113376
113693
  }
113377
- console.log(import_picocolors34.default.dim(' run: horus investigate "<hint>" --ai'));
113694
+ console.log(import_picocolors35.default.dim(' run: horus investigate "<hint>" --ai'));
113378
113695
  return 0;
113379
113696
  } catch (err2) {
113380
- 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)));
113381
113698
  return 1;
113382
113699
  }
113383
113700
  }
@@ -113409,7 +113726,7 @@ async function runConnect(type, opts) {
113409
113726
  }
113410
113727
  if (!SUPPORTED.includes(type)) {
113411
113728
  console.error(
113412
- 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(`
113413
113730
  supported: ${SUPPORTED.join(", ")}, ai`)
113414
113731
  );
113415
113732
  return 1;
@@ -113446,20 +113763,20 @@ async function runConnect(type, opts) {
113446
113763
  if (!probeResult.ok) {
113447
113764
  console.error(
113448
113765
  `
113449
- ${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.")
113450
113767
  );
113451
113768
  return 1;
113452
113769
  }
113453
113770
  console.log(
113454
113771
  `
113455
- ${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})`)}`
113456
113773
  );
113457
113774
  }
113458
113775
  const hasLiteralCredentials = filled.url !== void 0 || filled.password !== void 0 || filled.username !== void 0 || filled.authToken !== void 0;
113459
113776
  if (hasLiteralCredentials) {
113460
113777
  if (isGitTracked(configPath, root)) {
113461
113778
  console.error(
113462
- 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(
113463
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."
113464
113781
  )
113465
113782
  );
@@ -113469,18 +113786,18 @@ ${import_picocolors35.default.green("\u2713")} ${connectorType} reachable ${impo
113469
113786
  }
113470
113787
  patchLocalConnector(configPath, connectorType, patch, filled.env);
113471
113788
  console.log(
113472
- `${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)}`
113473
113790
  );
113474
113791
  printSummary(connectorType, filled);
113475
- console.log(import_picocolors35.default.dim(`
113792
+ console.log(import_picocolors36.default.dim(`
113476
113793
  run: horus investigate "<hint>"`));
113477
113794
  return 0;
113478
113795
  } catch (err2) {
113479
113796
  if (err2 instanceof ExitPromptError) {
113480
- console.error(import_picocolors35.default.red("Cancelled."));
113797
+ console.error(import_picocolors36.default.red("Cancelled."));
113481
113798
  return 1;
113482
113799
  }
113483
- console.error(import_picocolors35.default.red(err2.message));
113800
+ console.error(import_picocolors36.default.red(err2.message));
113484
113801
  return 1;
113485
113802
  }
113486
113803
  }
@@ -113489,7 +113806,7 @@ async function fillInteractive(type, opts) {
113489
113806
  if (!needsInteraction) return opts;
113490
113807
  console.log(
113491
113808
  `
113492
- ${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)")}
113493
113810
  `
113494
113811
  );
113495
113812
  const filled = { ...opts };
@@ -113555,7 +113872,7 @@ ${import_picocolors35.default.bold(`Connect ${type}`)} ${import_picocolors35.def
113555
113872
  break;
113556
113873
  case "redis": {
113557
113874
  console.log(
113558
- import_picocolors35.default.dim(
113875
+ import_picocolors36.default.dim(
113559
113876
  " Tip: embed credentials directly in the URL \u2014 redis://:password@host:6379\n or enter the URL and password separately below."
113560
113877
  )
113561
113878
  );
@@ -113597,8 +113914,8 @@ function missingRequired(type, opts) {
113597
113914
  }
113598
113915
  function ask(label, placeholder = "", required2 = true) {
113599
113916
  return new Promise((resolve9) => {
113600
- const hint = placeholder ? import_picocolors35.default.dim(` (${placeholder})`) : "";
113601
- 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]");
113602
113919
  process.stdout.write(` ${label}${suffix}${hint}: `);
113603
113920
  const rl = (0, import_node_readline.createInterface)({
113604
113921
  input: process.stdin,
@@ -113615,7 +113932,7 @@ function askPassword(label) {
113615
113932
  return new Promise((resolve9) => {
113616
113933
  const stdin = process.stdin;
113617
113934
  if (typeof stdin.setRawMode === "function") {
113618
- process.stdout.write(` ${label}${import_picocolors35.default.dim(" [optional]")}: `);
113935
+ process.stdout.write(` ${label}${import_picocolors36.default.dim(" [optional]")}: `);
113619
113936
  stdin.setRawMode(true);
113620
113937
  stdin.resume();
113621
113938
  let value = "";
@@ -113732,15 +114049,15 @@ function describeProbe(p5) {
113732
114049
  return `${p5.keyCount} keys \xB7 ${p5.suggestedRoles.join("/")}${examples ? ` \xB7 ${examples}` : ""}`;
113733
114050
  }
113734
114051
  async function discoverAndSelectDbs(url, bullmqPrefix) {
113735
- 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"));
113736
114053
  const probes = await probeRedisDatabases(url, { bullmqPrefix });
113737
114054
  const nonEmpty = probes.filter((p5) => p5.reachable && p5.keyCount > 0);
113738
114055
  if (nonEmpty.length === 0) {
113739
- console.log(import_picocolors35.default.dim(" No populated DBs found."));
114056
+ console.log(import_picocolors36.default.dim(" No populated DBs found."));
113740
114057
  return [];
113741
114058
  }
113742
114059
  for (const p5 of nonEmpty) {
113743
- 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))}`);
113744
114061
  }
113745
114062
  const byLabel = /* @__PURE__ */ new Map();
113746
114063
  const choices = nonEmpty.map((p5) => {
@@ -113880,11 +114197,11 @@ function printSummary(type, opts) {
113880
114197
  } else if (opts.dashboard) {
113881
114198
  lines.push(` dashboard: ${opts.dashboard}`);
113882
114199
  }
113883
- 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")));
113884
114201
  }
113885
114202
  function isGitTracked(filePath, cwd) {
113886
114203
  try {
113887
- (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], {
113888
114205
  cwd,
113889
114206
  stdio: "pipe"
113890
114207
  });
@@ -113919,11 +114236,11 @@ async function askIndexSelection(indices) {
113919
114236
  const shown = indices.slice(0, MAX_DISPLAY);
113920
114237
  console.log("\n Available Elasticsearch indexes/data streams:");
113921
114238
  shown.forEach((name2, i3) => {
113922
- console.log(` ${import_picocolors35.default.dim(`[${i3 + 1}]`)} ${name2}`);
114239
+ console.log(` ${import_picocolors36.default.dim(`[${i3 + 1}]`)} ${name2}`);
113923
114240
  });
113924
114241
  if (indices.length > MAX_DISPLAY) {
113925
114242
  console.log(
113926
- import_picocolors35.default.dim(
114243
+ import_picocolors36.default.dim(
113927
114244
  ` \u2026 and ${indices.length - MAX_DISPLAY} more (type a pattern manually to match all)`
113928
114245
  )
113929
114246
  );
@@ -113967,11 +114284,11 @@ async function askDashboardSelection(dashboards) {
113967
114284
  const shown = dashboards.slice(0, MAX_DISPLAY);
113968
114285
  console.log("\n Available Grafana dashboards:");
113969
114286
  shown.forEach((d3, i3) => {
113970
- const folder = d3.folderTitle ? import_picocolors35.default.dim(` (${d3.folderTitle})`) : "";
113971
- 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}`);
113972
114289
  });
113973
114290
  if (dashboards.length > MAX_DISPLAY) {
113974
- 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`));
113975
114292
  }
113976
114293
  const input = (await ask(
113977
114294
  ` Select dashboards to use (e.g. 1,2 or Enter to type uid manually)`,
@@ -114021,16 +114338,17 @@ function redactUrl(raw) {
114021
114338
 
114022
114339
  // ../../packages/cli/src/commands/stop.ts
114023
114340
  init_cjs_shims();
114024
- var import_node_child_process7 = require("child_process");
114025
- var import_node_fs16 = require("fs");
114026
- var import_node_util4 = require("util");
114027
- var import_node_path18 = require("path");
114028
- var import_picocolors36 = __toESM(require_picocolors(), 1);
114029
- var execFileAsync = (0, import_node_util4.promisify)(import_node_child_process7.execFile);
114030
- 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);
114031
114348
  var SPAWNED_HOST_FILE2 = "spawned-host.json";
114032
114349
  var START_TIME_TOLERANCE_S = 60;
114033
114350
  var STOP_WAIT_MS = 5e3;
114351
+ var STOP_KILL_WAIT_MS = 3e3;
114034
114352
  var STOP_POLL_MS = 200;
114035
114353
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
114036
114354
  async function runStop(opts) {
@@ -114042,19 +114360,19 @@ async function runStop(opts) {
114042
114360
  const root = findRepoRoot(cwd) ?? cwd;
114043
114361
  const hostUrl = readSourceHostUrl(root);
114044
114362
  if (!hostUrl) {
114045
- 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)."));
114046
114364
  return 0;
114047
114365
  }
114048
114366
  return await stopHost(root, hostUrl);
114049
114367
  } catch (err2) {
114050
- console.error(import_picocolors36.default.red(err2.message));
114368
+ console.error(import_picocolors37.default.red(err2.message));
114051
114369
  return 1;
114052
114370
  }
114053
114371
  }
114054
114372
  async function stopHost(root, hostUrl) {
114055
114373
  const port = extractPort(hostUrl);
114056
114374
  if (port === null) {
114057
- 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}`));
114058
114376
  return 1;
114059
114377
  }
114060
114378
  const healthy = await isHostHealthy(hostUrl);
@@ -114064,12 +114382,12 @@ async function stopHost(root, hostUrl) {
114064
114382
  if (!wrapperAlive) {
114065
114383
  const res = await stopBackendServerPid(root, port);
114066
114384
  if (res === "failed") return 1;
114067
- 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.`));
114068
114386
  await cleanupSpawnedRecord(root);
114069
114387
  return 0;
114070
114388
  }
114071
114389
  console.log(
114072
- import_picocolors36.default.dim(
114390
+ import_picocolors37.default.dim(
114073
114391
  `Host ${hostUrl} is unreachable but pid ${owned.pid} is still running \u2014 terminating a stuck host.`
114074
114392
  )
114075
114393
  );
@@ -114077,7 +114395,7 @@ async function stopHost(root, hostUrl) {
114077
114395
  const spawned = readSpawnedHost(root);
114078
114396
  if (spawned === null) {
114079
114397
  console.error(
114080
- import_picocolors36.default.red(
114398
+ import_picocolors37.default.red(
114081
114399
  `No ownership record found (.horus/${SPAWNED_HOST_FILE2} absent). Horus will not stop a host it did not spawn.`
114082
114400
  )
114083
114401
  );
@@ -114085,12 +114403,12 @@ async function stopHost(root, hostUrl) {
114085
114403
  }
114086
114404
  const recordError = validateSpawnedRecord(spawned);
114087
114405
  if (recordError !== null) {
114088
- 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.`));
114089
114407
  return 1;
114090
114408
  }
114091
114409
  if (spawned.port !== port) {
114092
114410
  console.error(
114093
- import_picocolors36.default.red(
114411
+ import_picocolors37.default.red(
114094
114412
  `Ownership record port (${spawned.port}) does not match host URL port (${port}). Record may be stale.`
114095
114413
  )
114096
114414
  );
@@ -114098,7 +114416,7 @@ async function stopHost(root, hostUrl) {
114098
114416
  }
114099
114417
  if (spawned.root !== root) {
114100
114418
  console.error(
114101
- import_picocolors36.default.red(
114419
+ import_picocolors37.default.red(
114102
114420
  `Ownership record root (${spawned.root}) does not match resolved root (${root}). Record may be stale.`
114103
114421
  )
114104
114422
  );
@@ -114109,14 +114427,14 @@ async function stopHost(root, hostUrl) {
114109
114427
  const res = await stopBackendServerPid(root, port);
114110
114428
  if (res === "failed") return 1;
114111
114429
  if (res !== "stopped") {
114112
- 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.`));
114113
114431
  }
114114
114432
  await cleanupSpawnedRecord(root);
114115
114433
  return 0;
114116
114434
  }
114117
114435
  if (!hostArgvRegex(port).test(info2.args)) {
114118
114436
  console.error(
114119
- import_picocolors36.default.red(
114437
+ import_picocolors37.default.red(
114120
114438
  `Pid ${spawned.pid} args do not match "horus-source host --port ${port}". Got: "${info2.args.slice(0, 120)}". Aborting for safety.`
114121
114439
  )
114122
114440
  );
@@ -114125,51 +114443,33 @@ async function stopHost(root, hostUrl) {
114125
114443
  const startTs = new Date(spawned.startedAt).getTime();
114126
114444
  const recordedAgeS = Math.round((Date.now() - startTs) / 1e3);
114127
114445
  if (!Number.isFinite(info2.etimeSeconds)) {
114128
- 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.`));
114129
114447
  return 1;
114130
114448
  }
114131
114449
  if (Math.abs(info2.etimeSeconds - recordedAgeS) > START_TIME_TOLERANCE_S) {
114132
114450
  console.error(
114133
- import_picocolors36.default.red(
114451
+ import_picocolors37.default.red(
114134
114452
  `Pid ${spawned.pid} age mismatch: record says ~${recordedAgeS}s, process reports ${info2.etimeSeconds}s elapsed. Possible PID reuse \u2014 aborting for safety.`
114135
114453
  )
114136
114454
  );
114137
114455
  return 1;
114138
114456
  }
114139
- let signaled = false;
114457
+ let terminated;
114140
114458
  try {
114141
- process.kill(spawned.pid, "SIGTERM");
114142
- signaled = true;
114459
+ terminated = await terminatePid(spawned.pid);
114143
114460
  } catch (err2) {
114144
- if (err2.code === "ESRCH") {
114145
- console.log(import_picocolors36.default.dim(`Process pid ${spawned.pid} exited before signal \u2014 already stopped.`));
114146
- } else {
114147
- console.error(import_picocolors36.default.red(`Failed to signal pid ${spawned.pid}: ${err2.message}`));
114148
- return 1;
114149
- }
114461
+ console.error(import_picocolors37.default.red(`Failed to signal pid ${spawned.pid}: ${err2.message}`));
114462
+ return 1;
114150
114463
  }
114151
- if (signaled) {
114152
- const deadline = Date.now() + STOP_WAIT_MS;
114153
- let exited = false;
114154
- while (Date.now() < deadline) {
114155
- await sleep(STOP_POLL_MS);
114156
- if (await getProcessInfo(spawned.pid) === null) {
114157
- exited = true;
114158
- break;
114159
- }
114160
- }
114161
- if (!exited) {
114162
- console.error(
114163
- import_picocolors36.default.red(
114164
- `Host pid ${spawned.pid} did not exit within ${STOP_WAIT_MS / 1e3}s after SIGTERM.`
114165
- )
114166
- );
114167
- return 1;
114168
- }
114169
- console.log(
114170
- `${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.`)
114171
114467
  );
114468
+ return 1;
114172
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
+ );
114173
114473
  if (await stopBackendServerPid(root, port) === "failed") return 1;
114174
114474
  await cleanupSpawnedRecord(root);
114175
114475
  return 0;
@@ -114181,7 +114481,7 @@ function hostArgvRegex(port) {
114181
114481
  }
114182
114482
  async function cleanupSpawnedRecord(root) {
114183
114483
  try {
114184
- 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));
114185
114485
  } catch {
114186
114486
  }
114187
114487
  }
@@ -114194,39 +114494,35 @@ async function stopBackendServerPid(root, port) {
114194
114494
  if (info2 === null) return "none";
114195
114495
  if (!hostArgvRegex(port).test(info2.args)) {
114196
114496
  console.error(
114197
- import_picocolors36.default.red(
114497
+ import_picocolors37.default.red(
114198
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.`
114199
114499
  )
114200
114500
  );
114201
114501
  return "failed";
114202
114502
  }
114503
+ let terminated;
114203
114504
  try {
114204
- process.kill(rec.pid, "SIGTERM");
114505
+ terminated = await terminatePid(rec.pid);
114205
114506
  } catch (err2) {
114206
- if (err2.code === "ESRCH") return "stopped";
114207
- 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}`));
114208
114508
  return "failed";
114209
114509
  }
114210
- const deadline = Date.now() + STOP_WAIT_MS;
114211
- while (Date.now() < deadline) {
114212
- await sleep(STOP_POLL_MS);
114213
- if (await getProcessInfo(rec.pid) === null) {
114214
- console.log(
114215
- `${import_picocolors36.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors36.default.dim(`(pid ${rec.pid}, port ${port})`) + ` for ${root}`
114216
- );
114217
- return "stopped";
114218
- }
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";
114219
114515
  }
114220
- console.error(
114221
- 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}`
114222
114518
  );
114223
- return "failed";
114519
+ return "stopped";
114224
114520
  }
114225
114521
  async function stopAll() {
114226
114522
  const registry2 = readRegistry();
114227
114523
  const projects2 = Object.entries(registry2.projects);
114228
114524
  if (projects2.length === 0) {
114229
- console.log(import_picocolors36.default.dim("No registered projects."));
114525
+ console.log(import_picocolors37.default.dim("No registered projects."));
114230
114526
  return 0;
114231
114527
  }
114232
114528
  let stopped = 0;
@@ -114238,17 +114534,17 @@ async function stopAll() {
114238
114534
  if (!healthy && readSpawnedHost(entry2.root) === null && readSourceHostPid(entry2.root) === null) {
114239
114535
  continue;
114240
114536
  }
114241
- 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})`)}`);
114242
114538
  const code = await stopHost(entry2.root, hostUrl);
114243
114539
  if (code === 0) stopped++;
114244
114540
  else failed++;
114245
114541
  }
114246
114542
  if (stopped === 0 && failed === 0) {
114247
- 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."));
114248
114544
  } else {
114249
114545
  console.log(
114250
114546
  `
114251
- 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`) : ""}.`
114252
114548
  );
114253
114549
  }
114254
114550
  return failed > 0 ? 1 : 0;
@@ -114275,10 +114571,30 @@ function validateSpawnedRecord(r) {
114275
114571
  }
114276
114572
  return null;
114277
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
+ }
114278
114594
  async function getProcessInfo(pid) {
114279
114595
  let args2;
114280
114596
  try {
114281
- const argsResult = await execFileAsync("ps", ["-p", String(pid), "-o", "args="], {
114597
+ const argsResult = await execFileAsync2("ps", ["-p", String(pid), "-o", "args="], {
114282
114598
  timeout: 3e3
114283
114599
  });
114284
114600
  args2 = argsResult.stdout.trim();
@@ -114288,7 +114604,7 @@ async function getProcessInfo(pid) {
114288
114604
  if (!args2) return null;
114289
114605
  let etimeSeconds = Number.NaN;
114290
114606
  try {
114291
- const etimeResult = await execFileAsync("ps", ["-p", String(pid), "-o", "etime="], {
114607
+ const etimeResult = await execFileAsync2("ps", ["-p", String(pid), "-o", "etime="], {
114292
114608
  timeout: 3e3
114293
114609
  });
114294
114610
  etimeSeconds = parseEtimeSeconds(etimeResult.stdout.trim());
@@ -114316,14 +114632,77 @@ function extractPort(hostUrl) {
114316
114632
 
114317
114633
  // ../../packages/cli/src/commands/hosts.ts
114318
114634
  init_cjs_shims();
114319
- var import_picocolors37 = __toESM(require_picocolors(), 1);
114320
- 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 = {}) {
114321
114704
  const registry2 = readRegistry();
114322
114705
  const projects2 = Object.entries(registry2.projects);
114323
- if (projects2.length === 0) {
114324
- console.log(import_picocolors37.default.dim("No registered projects. Run `horus index` in a repo first."));
114325
- return 0;
114326
- }
114327
114706
  const rows = [];
114328
114707
  await Promise.all(
114329
114708
  projects2.map(async ([name2, entry2]) => {
@@ -114332,39 +114711,69 @@ async function runHosts() {
114332
114711
  rows.push({ name: name2, hostUrl: hostUrl ?? null, healthy, root: entry2.root });
114333
114712
  })
114334
114713
  );
114335
- rows.sort((a2, b5) => {
114336
- if (a2.healthy !== b5.healthy) return a2.healthy ? -1 : 1;
114337
- return a2.name.localeCompare(b5.name);
114338
- });
114339
- const anyHost = rows.some((r) => r.hostUrl !== null);
114340
- if (!anyHost) {
114341
- console.log(import_picocolors37.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
114342
- return 0;
114343
- }
114714
+ rows.sort(
114715
+ (a2, b5) => a2.healthy !== b5.healthy ? a2.healthy ? -1 : 1 : a2.name.localeCompare(b5.name)
114716
+ );
114344
114717
  console.log("");
114718
+ let printedAny = false;
114345
114719
  for (const row of rows) {
114346
114720
  if (row.hostUrl === null) continue;
114347
- 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");
114348
114723
  const port = extractPort2(row.hostUrl) ?? "?";
114349
114724
  console.log(
114350
- ` ${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)}`
114351
114726
  );
114352
114727
  }
114353
- console.log("");
114354
114728
  const noHost = rows.filter((r) => r.hostUrl === null);
114355
- if (noHost.length > 0) {
114356
- for (const row of noHost) {
114357
- 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."));
114358
114759
  }
114359
114760
  console.log("");
114360
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
+ }
114361
114770
  const running = rows.filter((r) => r.healthy).length;
114362
114771
  console.log(
114363
- import_picocolors37.default.dim(
114364
- `${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`
114365
114774
  )
114366
114775
  );
114367
- return 0;
114776
+ return reapFailed > 0 ? 1 : 0;
114368
114777
  }
114369
114778
  function extractPort2(hostUrl) {
114370
114779
  try {
@@ -114377,12 +114786,12 @@ function extractPort2(hostUrl) {
114377
114786
 
114378
114787
  // ../../packages/cli/src/commands/doctor.ts
114379
114788
  init_cjs_shims();
114380
- var import_picocolors38 = __toESM(require_picocolors(), 1);
114789
+ var import_picocolors39 = __toESM(require_picocolors(), 1);
114381
114790
  var DEFAULT_DB_URL4 = "postgresql://horus:horus@localhost:5433/horus";
114382
114791
  function mark2(status) {
114383
- if (status === "pass") return import_picocolors38.default.green("\u2713");
114384
- if (status === "warn") return import_picocolors38.default.yellow("~");
114385
- 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");
114386
114795
  }
114387
114796
  async function runDoctor(opts) {
114388
114797
  const cwd = opts?.cwd ?? process.cwd();
@@ -114662,11 +115071,11 @@ async function runDoctor(opts) {
114662
115071
  write(JSON.stringify(output, null, 2));
114663
115072
  return hasFailure ? 1 : 0;
114664
115073
  }
114665
- write(import_picocolors38.default.bold("\nHorus readiness check\n"));
115074
+ write(import_picocolors39.default.bold("\nHorus readiness check\n"));
114666
115075
  for (const check2 of checks) {
114667
- 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)}`);
114668
115077
  if (check2.next) {
114669
- write(` ${import_picocolors38.default.dim("\u2192 " + check2.next)}`);
115078
+ write(` ${import_picocolors39.default.dim("\u2192 " + check2.next)}`);
114670
115079
  }
114671
115080
  }
114672
115081
  write("");
@@ -114675,20 +115084,20 @@ async function runDoctor(opts) {
114675
115084
 
114676
115085
  // ../../packages/cli/src/commands/providers-doctor.ts
114677
115086
  init_cjs_shims();
114678
- var import_node_child_process8 = require("child_process");
114679
- var import_picocolors39 = __toESM(require_picocolors(), 1);
115087
+ var import_node_child_process10 = require("child_process");
115088
+ var import_picocolors40 = __toESM(require_picocolors(), 1);
114680
115089
  function statusMark(status) {
114681
- if (status === "ready") return import_picocolors39.default.green("\u2713");
114682
- if (status === "installed") return import_picocolors39.default.yellow("~");
114683
- 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");
114684
115093
  }
114685
115094
  function statusLabel(status) {
114686
- if (status === "ready") return import_picocolors39.default.green("ready");
114687
- if (status === "installed") return import_picocolors39.default.yellow("installed (not configured)");
114688
- 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");
114689
115098
  }
114690
115099
  function detectBinary(id) {
114691
- 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 });
114692
115101
  if (result.error) {
114693
115102
  return { id, status: "unavailable", detail: `${id}: command not found` };
114694
115103
  }
@@ -114702,7 +115111,7 @@ async function runProvidersDoctorCommand(opts) {
114702
115111
  const registry2 = opts?.registry ?? DEFAULT_LOCAL_PROVIDER_REGISTRY;
114703
115112
  const write = opts?.write ?? ((line2) => console.log(line2));
114704
115113
  const results = buildProviderResults(registry2, opts?._detect);
114705
- write(import_picocolors39.default.bold("\nLocal AI providers\n"));
115114
+ write(import_picocolors40.default.bold("\nLocal AI providers\n"));
114706
115115
  for (const result of results) {
114707
115116
  const descriptor = registry2.get(result.id);
114708
115117
  const name2 = descriptor?.displayName ?? result.id;
@@ -114710,11 +115119,11 @@ async function runProvidersDoctorCommand(opts) {
114710
115119
  ` ${statusMark(result.status)} ${result.id.padEnd(8)} ${name2.padEnd(22)} ${statusLabel(result.status)}`
114711
115120
  );
114712
115121
  if (result.status !== "ready" && result.detail) {
114713
- write(` ${import_picocolors39.default.dim("\u2192 " + result.detail)}`);
115122
+ write(` ${import_picocolors40.default.dim("\u2192 " + result.detail)}`);
114714
115123
  }
114715
115124
  }
114716
115125
  write("");
114717
- write(import_picocolors39.default.bold("Cloud AI providers\n"));
115126
+ write(import_picocolors40.default.bold("Cloud AI providers\n"));
114718
115127
  let source = null;
114719
115128
  if (opts?._anthropicKey !== void 0) {
114720
115129
  source = opts._anthropicKey ? "env" : null;
@@ -114729,18 +115138,18 @@ async function runProvidersDoctorCommand(opts) {
114729
115138
  }
114730
115139
  if (source === "config") {
114731
115140
  write(
114732
- ` ${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)")}`
114733
115142
  );
114734
115143
  } else if (source === "env") {
114735
115144
  write(
114736
- ` ${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)")}`
114737
115146
  );
114738
115147
  } else {
114739
115148
  write(
114740
- ` ${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")}`
114741
115150
  );
114742
115151
  write(
114743
- ` ${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`")}`
114744
115153
  );
114745
115154
  }
114746
115155
  write("");
@@ -114749,9 +115158,9 @@ async function runProvidersDoctorCommand(opts) {
114749
115158
 
114750
115159
  // ../../packages/cli/src/commands/generate-config.ts
114751
115160
  init_cjs_shims();
114752
- var import_node_fs17 = require("fs");
114753
- var import_node_path19 = require("path");
114754
- 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);
114755
115164
  function configTemplate(name2, repoPath) {
114756
115165
  return `export default {
114757
115166
  database: {
@@ -114799,39 +115208,39 @@ async function runGenerateConfig(opts) {
114799
115208
  const defaults2 = localConfigPath2 != null ? projectDefaults(localConfigPath2) : null;
114800
115209
  const hasLocalConfig = defaults2 != null;
114801
115210
  const defaultOut = hasLocalConfig ? "horus.config.example.js" : "horus.config.js";
114802
- const outPath = (0, import_node_path19.resolve)(cwd, opts.out ?? defaultOut);
115211
+ const outPath = (0, import_node_path20.resolve)(cwd, opts.out ?? defaultOut);
114803
115212
  const name2 = opts.name ?? defaults2?.name ?? "my-project";
114804
115213
  const repoPath = opts.repo ?? defaults2?.repoPath ?? `/path/to/${name2}`;
114805
- if ((0, import_node_fs17.existsSync)(outPath) && !opts.force) {
114806
- log(`${import_picocolors40.default.red("\u2717")} ${outPath} already exists`);
114807
- 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"));
114808
115217
  return 1;
114809
115218
  }
114810
115219
  try {
114811
- (0, import_node_fs17.mkdirSync)((0, import_node_path19.dirname)(outPath), { recursive: true });
114812
- (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");
114813
115222
  } catch (err2) {
114814
- 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}`);
114815
115224
  return 1;
114816
115225
  }
114817
- log(`${import_picocolors40.default.green("\u2713")} Created ${outPath}`);
114818
- log(import_picocolors40.default.dim(` project: ${name2}`));
114819
- 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}`));
114820
115229
  if (hasLocalConfig && localConfigPath2 != null) {
114821
115230
  log("");
114822
- 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}`);
114823
115232
  log(" \u2022 .horus/config.json \u2014 project config used by `horus investigate` from this repo");
114824
115233
  log(" \u2022 horus.config.js \u2014 standalone/global config used with `horus doctor --config <path>`");
114825
- 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`));
114826
115235
  } else {
114827
- log(import_picocolors40.default.dim(` next: horus doctor --config ${outPath}`));
115236
+ log(import_picocolors41.default.dim(` next: horus doctor --config ${outPath}`));
114828
115237
  }
114829
115238
  return 0;
114830
115239
  }
114831
115240
 
114832
115241
  // ../../packages/cli/src/commands/readiness.ts
114833
115242
  init_cjs_shims();
114834
- var import_picocolors41 = __toESM(require_picocolors(), 1);
115243
+ var import_picocolors42 = __toESM(require_picocolors(), 1);
114835
115244
  var READINESS_AI_CONTRACT = `Provide a clearly separated AI readiness summary with:
114836
115245
 
114837
115246
  Overall assessment
@@ -114854,9 +115263,9 @@ Confidence / gaps
114854
115263
  - Which checks Horus could verify vs. which require manual confirmation`;
114855
115264
  var DEFAULT_DB_URL5 = "postgresql://horus:horus@localhost:5433/horus";
114856
115265
  function mark3(status) {
114857
- if (status === "pass") return import_picocolors41.default.green("\u2713");
114858
- if (status === "warn") return import_picocolors41.default.yellow("~");
114859
- 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");
114860
115269
  }
114861
115270
  async function runReadiness(opts) {
114862
115271
  const cwd = opts?.cwd ?? process.cwd();
@@ -115033,20 +115442,20 @@ async function runReadiness(opts) {
115033
115442
  }
115034
115443
  const blockingChecks = checks.filter((c4) => c4.blocking);
115035
115444
  const optionalChecks = checks.filter((c4) => !c4.blocking);
115036
- write(import_picocolors41.default.bold("\nHorus release readiness\n"));
115037
- write(import_picocolors41.default.bold(" Blocking"));
115445
+ write(import_picocolors42.default.bold("\nHorus release readiness\n"));
115446
+ write(import_picocolors42.default.bold(" Blocking"));
115038
115447
  for (const check2 of blockingChecks) {
115039
- 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)}`);
115040
115449
  if (check2.next) {
115041
- write(` ${import_picocolors41.default.dim("\u2192 " + check2.next)}`);
115450
+ write(` ${import_picocolors42.default.dim("\u2192 " + check2.next)}`);
115042
115451
  }
115043
115452
  }
115044
115453
  write("");
115045
- write(import_picocolors41.default.bold(" Optional"));
115454
+ write(import_picocolors42.default.bold(" Optional"));
115046
115455
  for (const check2 of optionalChecks) {
115047
- 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)}`);
115048
115457
  if (check2.next) {
115049
- write(` ${import_picocolors41.default.dim("\u2192 " + check2.next)}`);
115458
+ write(` ${import_picocolors42.default.dim("\u2192 " + check2.next)}`);
115050
115459
  }
115051
115460
  }
115052
115461
  write("");
@@ -115054,21 +115463,21 @@ async function runReadiness(opts) {
115054
115463
  const optionalWarns = optionalChecks.filter((c4) => c4.status === "warn").length;
115055
115464
  if (blockingFails.length === 0) {
115056
115465
  if (optionalWarns === 0) {
115057
- write(import_picocolors41.default.green(" Ready for demo/release."));
115466
+ write(import_picocolors42.default.green(" Ready for demo/release."));
115058
115467
  } else {
115059
115468
  write(
115060
- import_picocolors41.default.yellow(
115469
+ import_picocolors42.default.yellow(
115061
115470
  ` Ready for a basic demo. ${optionalWarns} optional item(s) not configured \u2014 investigation evidence will be limited.`
115062
115471
  )
115063
115472
  );
115064
115473
  }
115065
115474
  } else {
115066
115475
  write(
115067
- import_picocolors41.default.red(
115476
+ import_picocolors42.default.red(
115068
115477
  ` Not ready. ${blockingFails.length} blocking item(s) must be resolved before demo/release.`
115069
115478
  )
115070
115479
  );
115071
- 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."));
115072
115481
  }
115073
115482
  write("");
115074
115483
  if (opts?.ai) {
@@ -115083,7 +115492,7 @@ async function runReadiness(opts) {
115083
115492
  });
115084
115493
  console.log("\n" + renderInterpretation(result));
115085
115494
  if (!result.ok) {
115086
- console.error(import_picocolors41.default.yellow(`[ai] ${result.warning}`));
115495
+ console.error(import_picocolors42.default.yellow(`[ai] ${result.warning}`));
115087
115496
  }
115088
115497
  }
115089
115498
  return blockingFails.length > 0 ? 1 : 0;
@@ -115091,33 +115500,33 @@ async function runReadiness(opts) {
115091
115500
 
115092
115501
  // ../../packages/cli/src/commands/skill.ts
115093
115502
  init_cjs_shims();
115094
- var import_node_fs18 = require("fs");
115095
- var import_node_path20 = require("path");
115503
+ var import_node_fs19 = require("fs");
115504
+ var import_node_path21 = require("path");
115096
115505
  var import_node_os5 = require("os");
115097
115506
  var import_promises2 = require("readline/promises");
115098
- var import_picocolors42 = __toESM(require_picocolors(), 1);
115507
+ var import_picocolors43 = __toESM(require_picocolors(), 1);
115099
115508
  var SUPPORTED_TARGETS = ["claude", "codex", "gemini", "cursor", "generic"];
115100
115509
  function isValidTarget(target) {
115101
115510
  return SUPPORTED_TARGETS.includes(target);
115102
115511
  }
115103
115512
  function getLegacyClaudeSkillPath(opts) {
115104
115513
  const base2 = opts.global ? (0, import_node_os5.homedir)() : opts.cwd ?? process.cwd();
115105
- return (0, import_node_path20.join)(base2, ".claude", "skills", "horus.md");
115514
+ return (0, import_node_path21.join)(base2, ".claude", "skills", "horus.md");
115106
115515
  }
115107
115516
  function getSkillInstallPath(target, opts) {
115108
115517
  const base2 = opts.global ? (0, import_node_os5.homedir)() : opts.cwd ?? process.cwd();
115109
115518
  switch (target) {
115110
115519
  case "claude":
115111
- 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");
115112
115521
  case "gemini":
115113
- 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");
115114
115523
  case "cursor":
115115
- 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");
115116
115525
  case "codex":
115117
- 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");
115118
115527
  case "generic":
115119
115528
  default:
115120
- 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`);
115121
115530
  }
115122
115531
  }
115123
115532
  var BASE_SKILL = `# Horus Grounded Investigation Skill
@@ -115434,66 +115843,66 @@ async function confirm(message) {
115434
115843
  async function runSkillInstall(target, opts) {
115435
115844
  const log = opts.write ?? ((line2) => console.log(line2));
115436
115845
  if (!isValidTarget(target)) {
115437
- log(`${import_picocolors42.default.red("\u2717")} Unknown target: ${import_picocolors42.default.bold(target)}`);
115438
- 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(", ")}`));
115439
115848
  return 1;
115440
115849
  }
115441
115850
  const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
115442
115851
  const content = generateSkillContent(target);
115443
115852
  if (target === "claude") {
115444
115853
  const legacyPath = getLegacyClaudeSkillPath({ global: opts.global, cwd: opts.cwd });
115445
- if ((0, import_node_fs18.existsSync)(legacyPath)) {
115854
+ if ((0, import_node_fs19.existsSync)(legacyPath)) {
115446
115855
  if (!opts.force) {
115447
115856
  const confirmed = await (opts.confirm ?? confirm)(
115448
115857
  `Found old flat skill file at ${legacyPath}. Replace it with the new directory layout?`
115449
115858
  );
115450
115859
  if (!confirmed) {
115451
- log(`${import_picocolors42.default.yellow("!")} Migration cancelled; existing skill left untouched.`);
115452
- 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."));
115453
115862
  return 1;
115454
115863
  }
115455
115864
  }
115456
115865
  try {
115457
- (0, import_node_fs18.rmSync)(legacyPath);
115458
- 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}`);
115459
115868
  } catch (err2) {
115460
- 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}`);
115461
115870
  return 1;
115462
115871
  }
115463
115872
  }
115464
115873
  }
115465
- if ((0, import_node_fs18.existsSync)(outPath) && !opts.force) {
115466
- log(`${import_picocolors42.default.red("\u2717")} ${outPath} already exists`);
115467
- 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"));
115468
115877
  return 1;
115469
115878
  }
115470
115879
  try {
115471
- (0, import_node_fs18.mkdirSync)((0, import_node_path20.dirname)(outPath), { recursive: true });
115472
- (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");
115473
115882
  } catch (err2) {
115474
- 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}`);
115475
115884
  return 1;
115476
115885
  }
115477
- log(`${import_picocolors42.default.green("\u2713")} Horus skill installed \u2192 ${outPath}`);
115886
+ log(`${import_picocolors43.default.green("\u2713")} Horus skill installed \u2192 ${outPath}`);
115478
115887
  if (target === "claude") {
115479
- log(import_picocolors42.default.dim(" Claude Code will load this skill automatically."));
115480
- 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"));
115481
115890
  } else if (target === "gemini") {
115482
- 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."));
115483
115892
  } else if (target === "cursor") {
115484
- log(import_picocolors42.default.dim(" Cursor will load this rule automatically."));
115893
+ log(import_picocolors43.default.dim(" Cursor will load this rule automatically."));
115485
115894
  } else if (target === "codex") {
115486
- 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."));
115487
115896
  } else {
115488
- 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.`));
115489
115898
  }
115490
115899
  return 0;
115491
115900
  }
115492
115901
  async function runSkillPrint(target, opts) {
115493
115902
  const log = opts.write ?? ((line2) => console.log(line2));
115494
115903
  if (!isValidTarget(target)) {
115495
- log(`${import_picocolors42.default.red("\u2717")} Unknown target: ${import_picocolors42.default.bold(target)}`);
115496
- 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(", ")}`));
115497
115906
  return 1;
115498
115907
  }
115499
115908
  process.stdout.write(generateSkillContent(target));
@@ -115502,8 +115911,8 @@ async function runSkillPrint(target, opts) {
115502
115911
  async function runSkillPath(target, opts) {
115503
115912
  const log = opts.write ?? ((line2) => console.log(line2));
115504
115913
  if (!isValidTarget(target)) {
115505
- log(`${import_picocolors42.default.red("\u2717")} Unknown target: ${import_picocolors42.default.bold(target)}`);
115506
- 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(", ")}`));
115507
115916
  return 1;
115508
115917
  }
115509
115918
  const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
@@ -115513,14 +115922,14 @@ async function runSkillPath(target, opts) {
115513
115922
 
115514
115923
  // ../../packages/cli/src/commands/update.ts
115515
115924
  init_cjs_shims();
115516
- var import_node_fs19 = require("fs");
115517
- var import_node_path21 = require("path");
115925
+ var import_node_fs20 = require("fs");
115926
+ var import_node_path22 = require("path");
115518
115927
  var import_node_os6 = require("os");
115519
115928
  var import_node_crypto7 = require("crypto");
115520
- var import_node_child_process9 = require("child_process");
115521
- var import_node_util5 = require("util");
115522
- var import_picocolors43 = __toESM(require_picocolors(), 1);
115523
- 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);
115524
115933
  async function ensureBackendPinned(write) {
115525
115934
  let installed = null;
115526
115935
  try {
@@ -115529,18 +115938,18 @@ async function ensureBackendPinned(write) {
115529
115938
  installed = null;
115530
115939
  }
115531
115940
  if (installed === PINNED_SOURCE_VERSION) {
115532
- 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}.`);
115533
115942
  return;
115534
115943
  }
115535
115944
  if (installed === null) {
115536
115945
  write(
115537
- ` ${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")}`
115538
115947
  );
115539
115948
  return;
115540
115949
  }
115541
115950
  write(` Upgrading source backend ${installed} \u2192 ${PINNED_SOURCE_VERSION}\u2026`);
115542
115951
  try {
115543
- await execFileAsync2(
115952
+ await execFileAsync4(
115544
115953
  "uv",
115545
115954
  // --refresh: a CLI update commonly lands seconds after a backend release, so uv's
115546
115955
  // cached PyPI index may not list the just-published version yet and the install
@@ -115549,10 +115958,10 @@ async function ensureBackendPinned(write) {
115549
115958
  ["tool", "install", "--force", "--refresh", `horus-source==${PINNED_SOURCE_VERSION}`],
115550
115959
  { timeout: 3e5 }
115551
115960
  );
115552
- 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}.`);
115553
115962
  } catch {
115554
- write(` ${import_picocolors43.default.yellow("!")} Couldn't auto-upgrade the source backend. Re-run the installer:`);
115555
- 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")}`);
115556
115965
  }
115557
115966
  }
115558
115967
  var RELEASES_API = "https://api.github.com/repos/meritt-dev/horus/releases/latest";
@@ -115585,7 +115994,7 @@ function resolveBinaryPath() {
115585
115994
  const raw = process.argv[1];
115586
115995
  if (!raw) throw new Error("Cannot determine binary path from process.argv[1]");
115587
115996
  try {
115588
- return (0, import_node_fs19.realpathSync)(raw);
115997
+ return (0, import_node_fs20.realpathSync)(raw);
115589
115998
  } catch {
115590
115999
  return raw;
115591
116000
  }
@@ -115595,33 +116004,33 @@ async function runUpdate(opts) {
115595
116004
  const _fetch = opts._fetch ?? fetch;
115596
116005
  const currentVersion = opts._currentVersion ?? HORUS_VERSION;
115597
116006
  write(`
115598
- ${import_picocolors43.default.bold("Horus update")}
116007
+ ${import_picocolors44.default.bold("Horus update")}
115599
116008
  `);
115600
- write(` Current version: ${import_picocolors43.default.cyan(currentVersion)}`);
116009
+ write(` Current version: ${import_picocolors44.default.cyan(currentVersion)}`);
115601
116010
  let release;
115602
116011
  try {
115603
116012
  release = await fetchLatestRelease(_fetch);
115604
116013
  } catch (e) {
115605
- 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}`);
115606
116015
  return 1;
115607
116016
  }
115608
116017
  const latest = release.tag_name.replace(/^v/, "");
115609
- write(` Latest version: ${import_picocolors43.default.cyan(latest)}`);
116018
+ write(` Latest version: ${import_picocolors44.default.cyan(latest)}`);
115610
116019
  const newer = isNewer(latest, currentVersion);
115611
116020
  if (opts.check) {
115612
116021
  if (newer) {
115613
116022
  write(`
115614
- ${import_picocolors43.default.yellow("\u2192")} ${import_picocolors43.default.bold(latest)} is available.`);
115615
- 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.`);
115616
116025
  } else {
115617
116026
  write(`
115618
- ${import_picocolors43.default.green("\u2713")} Already on the latest version.`);
116027
+ ${import_picocolors44.default.green("\u2713")} Already on the latest version.`);
115619
116028
  }
115620
116029
  return 0;
115621
116030
  }
115622
116031
  if (!newer && !opts.force) {
115623
116032
  write(`
115624
- ${import_picocolors43.default.green("\u2713")} Already on the latest version.`);
116033
+ ${import_picocolors44.default.green("\u2713")} Already on the latest version.`);
115625
116034
  await ensureBackendPinned(write);
115626
116035
  return 0;
115627
116036
  }
@@ -115631,8 +116040,8 @@ ${import_picocolors43.default.bold("Horus update")}
115631
116040
  const binaryAsset = release.assets.find((a2) => a2.name === assetName);
115632
116041
  const checksumAsset = release.assets.find((a2) => a2.name === checksumName);
115633
116042
  if (!binaryAsset) {
115634
- write(` ${import_picocolors43.default.red("\u2717")} Release ${tag} has no binary asset named '${assetName}'.`);
115635
- 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")}`);
115636
116045
  return 1;
115637
116046
  }
115638
116047
  write(`
@@ -115642,7 +116051,7 @@ ${import_picocolors43.default.bold("Horus update")}
115642
116051
  redirect: "follow"
115643
116052
  });
115644
116053
  if (!binRes.ok || !binRes.body) {
115645
- write(` ${import_picocolors43.default.red("\u2717")} Download failed: ${binRes.status}`);
116054
+ write(` ${import_picocolors44.default.red("\u2717")} Download failed: ${binRes.status}`);
115646
116055
  return 1;
115647
116056
  }
115648
116057
  const binBuf = Buffer.from(await binRes.arrayBuffer());
@@ -115656,82 +116065,82 @@ ${import_picocolors43.default.bold("Horus update")}
115656
116065
  const expectedHash = csText.trim().split(/\s+/)[0];
115657
116066
  const actualHash = sha256hex(binBuf);
115658
116067
  if (expectedHash !== actualHash) {
115659
- 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.`);
115660
116069
  write(` Expected: ${expectedHash}`);
115661
116070
  write(` Got: ${actualHash}`);
115662
116071
  return 1;
115663
116072
  }
115664
- write(` ${import_picocolors43.default.green("\u2713")} Checksum verified.`);
116073
+ write(` ${import_picocolors44.default.green("\u2713")} Checksum verified.`);
115665
116074
  }
115666
116075
  }
115667
116076
  let binaryPath;
115668
116077
  try {
115669
116078
  binaryPath = resolveBinaryPath();
115670
116079
  } catch (e) {
115671
- write(` ${import_picocolors43.default.red("\u2717")} ${e.message}`);
116080
+ write(` ${import_picocolors44.default.red("\u2717")} ${e.message}`);
115672
116081
  return 1;
115673
116082
  }
115674
- if (!(0, import_node_fs19.existsSync)(binaryPath)) {
115675
- 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}`);
115676
116085
  return 1;
115677
116086
  }
115678
- 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}`);
115679
116088
  const backupPath = `${binaryPath}.bak`;
115680
116089
  try {
115681
- (0, import_node_fs19.writeFileSync)(tmpPath, binBuf, { mode: 493 });
116090
+ (0, import_node_fs20.writeFileSync)(tmpPath, binBuf, { mode: 493 });
115682
116091
  } catch (e) {
115683
- 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}`);
115684
116093
  return 1;
115685
116094
  }
115686
116095
  try {
115687
- (0, import_node_fs19.renameSync)(binaryPath, backupPath);
116096
+ (0, import_node_fs20.renameSync)(binaryPath, backupPath);
115688
116097
  try {
115689
- (0, import_node_fs19.renameSync)(tmpPath, binaryPath);
115690
- (0, import_node_fs19.chmodSync)(binaryPath, 493);
116098
+ (0, import_node_fs20.renameSync)(tmpPath, binaryPath);
116099
+ (0, import_node_fs20.chmodSync)(binaryPath, 493);
115691
116100
  } catch (e) {
115692
116101
  try {
115693
- (0, import_node_fs19.renameSync)(backupPath, binaryPath);
116102
+ (0, import_node_fs20.renameSync)(backupPath, binaryPath);
115694
116103
  } catch {
115695
116104
  }
115696
116105
  throw e;
115697
116106
  }
115698
116107
  try {
115699
- (0, import_node_fs19.unlinkSync)(backupPath);
116108
+ (0, import_node_fs20.unlinkSync)(backupPath);
115700
116109
  } catch {
115701
116110
  }
115702
116111
  } catch (e) {
115703
116112
  try {
115704
- (0, import_node_fs19.unlinkSync)(tmpPath);
116113
+ (0, import_node_fs20.unlinkSync)(tmpPath);
115705
116114
  } catch {
115706
116115
  }
115707
116116
  const msg = e.message ?? String(e);
115708
116117
  const needsSudo = e.code === "EACCES" || e.code === "EPERM";
115709
- 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}`);
115710
116119
  if (needsSudo) {
115711
116120
  write(`
115712
116121
  Try with sudo:`);
115713
116122
  write(
115714
- ` ${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}`)}`
115715
116124
  );
115716
116125
  }
115717
116126
  return 1;
115718
116127
  }
115719
- write(` ${import_picocolors43.default.green("\u2713")} Updated: ${import_picocolors43.default.bold(currentVersion)} \u2192 ${import_picocolors43.default.bold(latest)}`);
115720
- 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)}`);
115721
116130
  await ensureBackendPinned(write);
115722
116131
  return 0;
115723
116132
  }
115724
116133
 
115725
116134
  // ../../packages/cli/src/commands/login.ts
115726
116135
  init_cjs_shims();
115727
- var import_node_child_process10 = require("child_process");
115728
- var import_picocolors44 = __toESM(require_picocolors(), 1);
116136
+ var import_node_child_process12 = require("child_process");
116137
+ var import_picocolors45 = __toESM(require_picocolors(), 1);
115729
116138
  var sleep2 = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
115730
116139
  function openBrowser(url) {
115731
116140
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
115732
116141
  const args2 = process.platform === "win32" ? ["/c", "start", "", url] : [url];
115733
116142
  try {
115734
- (0, import_node_child_process10.execFile)(cmd, args2, () => {
116143
+ (0, import_node_child_process12.execFile)(cmd, args2, () => {
115735
116144
  });
115736
116145
  } catch {
115737
116146
  }
@@ -115750,19 +116159,19 @@ async function loginWithToken(apiBaseUrl, token) {
115750
116159
  }
115751
116160
  });
115752
116161
  console.log(
115753
- `${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.`
115754
116163
  );
115755
116164
  return 0;
115756
116165
  } catch (err2) {
115757
116166
  if (err2 instanceof CloudOfflineError) {
115758
- console.error(import_picocolors44.default.red(err2.message));
116167
+ console.error(import_picocolors45.default.red(err2.message));
115759
116168
  return 1;
115760
116169
  }
115761
116170
  if (err2 instanceof CloudError && err2.status === 401) {
115762
- 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."));
115763
116172
  return 1;
115764
116173
  }
115765
- console.error(import_picocolors44.default.red(`Login failed: ${err2.message}`));
116174
+ console.error(import_picocolors45.default.red(`Login failed: ${err2.message}`));
115766
116175
  return 1;
115767
116176
  }
115768
116177
  }
@@ -115773,19 +116182,19 @@ async function loginWithBrowser(apiBaseUrl) {
115773
116182
  session = await client.startCliSession();
115774
116183
  } catch (err2) {
115775
116184
  if (err2 instanceof CloudOfflineError) {
115776
- console.error(import_picocolors44.default.red(err2.message));
116185
+ console.error(import_picocolors45.default.red(err2.message));
115777
116186
  return 1;
115778
116187
  }
115779
- 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}`));
115780
116189
  return 1;
115781
116190
  }
115782
116191
  console.log("");
115783
116192
  console.log(` To sign in, open this URL in your browser:`);
115784
- 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))}`);
115785
116194
  console.log("");
115786
- 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)}`);
115787
116196
  console.log("");
115788
- 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)`));
115789
116198
  openBrowser(session.verificationUriComplete);
115790
116199
  const deadline = Date.now() + session.expiresIn * 1e3;
115791
116200
  let intervalMs = Math.max(session.interval, 1) * 1e3;
@@ -115796,7 +116205,7 @@ async function loginWithBrowser(apiBaseUrl) {
115796
116205
  poll = await client.pollCliSession(session.deviceCode);
115797
116206
  } catch (err2) {
115798
116207
  if (err2 instanceof CloudOfflineError) {
115799
- console.error(import_picocolors44.default.red(`
116208
+ console.error(import_picocolors45.default.red(`
115800
116209
  ${err2.message}`));
115801
116210
  return 1;
115802
116211
  }
@@ -115809,14 +116218,14 @@ ${err2.message}`));
115809
116218
  intervalMs += 2e3;
115810
116219
  break;
115811
116220
  case "denied":
115812
- 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."));
115813
116222
  return 1;
115814
116223
  case "expired":
115815
- 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."));
115816
116225
  return 1;
115817
116226
  case "approved":
115818
116227
  if (!poll.token || !poll.account) {
115819
- 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."));
115820
116229
  return 1;
115821
116230
  }
115822
116231
  writeAuth({
@@ -115826,12 +116235,12 @@ ${err2.message}`));
115826
116235
  });
115827
116236
  console.log(
115828
116237
  `
115829
- ${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.`
115830
116239
  );
115831
116240
  return 0;
115832
116241
  }
115833
116242
  }
115834
- 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."));
115835
116244
  return 1;
115836
116245
  }
115837
116246
  async function runLogin(opts = {}) {
@@ -115845,7 +116254,7 @@ async function runLogin(opts = {}) {
115845
116254
  async function runLogout() {
115846
116255
  const auth = readAuth();
115847
116256
  if (!auth) {
115848
- console.log(import_picocolors44.default.dim("Not logged in."));
116257
+ console.log(import_picocolors45.default.dim("Not logged in."));
115849
116258
  return 0;
115850
116259
  }
115851
116260
  const session = authedClient();
@@ -115859,18 +116268,18 @@ async function runLogout() {
115859
116268
  }
115860
116269
  }
115861
116270
  clearAuth();
115862
- 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.`);
115863
116272
  return 0;
115864
116273
  }
115865
116274
 
115866
116275
  // ../../packages/cli/src/commands/cloud.ts
115867
116276
  init_cjs_shims();
115868
- var import_picocolors45 = __toESM(require_picocolors(), 1);
116277
+ var import_picocolors46 = __toESM(require_picocolors(), 1);
115869
116278
  var import_promises3 = require("readline/promises");
115870
116279
 
115871
116280
  // ../../packages/cli/src/lib/cloud/git.ts
115872
116281
  init_cjs_shims();
115873
- var import_node_child_process11 = require("child_process");
116282
+ var import_node_child_process13 = require("child_process");
115874
116283
  function parseRepoName(remoteUrl) {
115875
116284
  const cleaned = remoteUrl.trim().replace(/\.git$/, "");
115876
116285
  const lastSegment = cleaned.split(/[/:]/).filter(Boolean).pop() ?? "";
@@ -115894,7 +116303,7 @@ function inferProvider(remoteUrl) {
115894
116303
  }
115895
116304
  function detectGitRemote(root) {
115896
116305
  try {
115897
- 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"], {
115898
116307
  encoding: "utf8",
115899
116308
  stdio: ["ignore", "pipe", "ignore"]
115900
116309
  }).trim();
@@ -115910,7 +116319,7 @@ async function runCloudLink(opts = {}) {
115910
116319
  const root = repoRootOrCwd(opts.cwd);
115911
116320
  const session = authedClient();
115912
116321
  if (!session) {
115913
- 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.`));
115914
116323
  return 1;
115915
116324
  }
115916
116325
  let ctx;
@@ -115920,7 +116329,7 @@ async function runCloudLink(opts = {}) {
115920
116329
  return reportCloudError(err2);
115921
116330
  }
115922
116331
  const remote = detectGitRemote(root);
115923
- 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}`));
115924
116333
  let resolved = null;
115925
116334
  let target = opts.project;
115926
116335
  if (!target) {
@@ -115936,7 +116345,7 @@ async function runCloudLink(opts = {}) {
115936
116345
  const org = ctx.organizations.find((o5) => o5.id === match.organizationId);
115937
116346
  if (ws && org) {
115938
116347
  target = `${org.slug}/${ws.slug}/${match.slug}`;
115939
- 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)}`));
115940
116349
  }
115941
116350
  }
115942
116351
  }
@@ -115945,8 +116354,8 @@ async function runCloudLink(opts = {}) {
115945
116354
  if (created === "aborted") return 1;
115946
116355
  if (!created) {
115947
116356
  console.error(
115948
- import_picocolors45.default.red("No project to link.") + `
115949
- 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")}.`
115950
116359
  );
115951
116360
  return 1;
115952
116361
  }
@@ -115956,8 +116365,8 @@ async function runCloudLink(opts = {}) {
115956
116365
  resolved = resolveTriple(ctx, target);
115957
116366
  if (!resolved) {
115958
116367
  console.error(
115959
- import_picocolors45.default.red(`You don't have access to ${import_picocolors45.default.bold(target)}.`) + `
115960
- 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.`
115961
116370
  );
115962
116371
  return 1;
115963
116372
  }
@@ -115970,10 +116379,10 @@ async function runCloudLink(opts = {}) {
115970
116379
  project: resolved.project
115971
116380
  });
115972
116381
  console.log(
115973
- `${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)")}.`
115974
116383
  );
115975
116384
  console.log(
115976
- 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.`)
115977
116386
  );
115978
116387
  return 0;
115979
116388
  }
@@ -115981,11 +116390,11 @@ async function runCloudUnlink(opts = {}) {
115981
116390
  const root = repoRootOrCwd(opts.cwd);
115982
116391
  const cfg = readCloudConfig(root);
115983
116392
  if (!cfg || cfg.context !== "cloud") {
115984
- 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."));
115985
116394
  return 0;
115986
116395
  }
115987
116396
  clearCloudConfig(root);
115988
- 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.`);
115989
116398
  return 0;
115990
116399
  }
115991
116400
  async function runCloudStatus(opts = {}) {
@@ -115993,18 +116402,18 @@ async function runCloudStatus(opts = {}) {
115993
116402
  const cfg = readCloudConfig(root);
115994
116403
  const session = authedClient();
115995
116404
  if (!isCloudActive(cfg) || !cfg?.project) {
115996
- console.log(`${import_picocolors45.default.bold("Context:")} local`);
115997
- 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)`);
115998
116407
  console.log(
115999
- 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.`)
116000
116409
  );
116001
116410
  return 0;
116002
116411
  }
116003
116412
  const triple = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
116004
- 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)")}`);
116005
116414
  let projectLine = `${cfg.project.slug}`;
116006
116415
  let metaLines = [];
116007
- let account = import_picocolors45.default.dim("not logged in");
116416
+ let account = import_picocolors46.default.dim("not logged in");
116008
116417
  if (session) {
116009
116418
  account = session.auth.account.email;
116010
116419
  try {
@@ -116019,9 +116428,9 @@ async function runCloudStatus(opts = {}) {
116019
116428
  } catch {
116020
116429
  }
116021
116430
  }
116022
- console.log(`${import_picocolors45.default.bold("Project:")} ${projectLine}`);
116431
+ console.log(`${import_picocolors46.default.bold("Project:")} ${projectLine}`);
116023
116432
  for (const line2 of metaLines) console.log(line2);
116024
- console.log(`${import_picocolors45.default.bold("Account:")} ${account}`);
116433
+ console.log(`${import_picocolors46.default.bold("Account:")} ${account}`);
116025
116434
  return 0;
116026
116435
  }
116027
116436
  async function runCloudSync(opts = {}) {
@@ -116029,13 +116438,13 @@ async function runCloudSync(opts = {}) {
116029
116438
  const cfg = readCloudConfig(root);
116030
116439
  if (!isCloudActive(cfg) || !cfg.project) {
116031
116440
  console.error(
116032
- 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.`
116033
116442
  );
116034
116443
  return 1;
116035
116444
  }
116036
116445
  const session = authedClient();
116037
116446
  if (!session) {
116038
- 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.`));
116039
116448
  return 1;
116040
116449
  }
116041
116450
  const { db, sql: sql2 } = await openDb(await resolveDbUrl(opts.config));
@@ -116056,26 +116465,26 @@ async function runCloudSync(opts = {}) {
116056
116465
  const target = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
116057
116466
  if (uploadable.length === 0) {
116058
116467
  console.log(
116059
- import_picocolors45.default.dim(
116468
+ import_picocolors46.default.dim(
116060
116469
  `No local investigations with reports to upload.` + (skippedNoReport ? ` (${skippedNoReport} without a report skipped)` : "")
116061
116470
  )
116062
116471
  );
116063
116472
  return 0;
116064
116473
  }
116065
- 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)")}:`));
116066
116475
  for (const c4 of uploadable.slice(0, 20)) {
116067
- 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)}`);
116068
116477
  }
116069
- if (uploadable.length > 20) console.log(import_picocolors45.default.dim(` \u2026and ${uploadable.length - 20} more`));
116070
- 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)`));
116071
116480
  if (opts.dryRun) {
116072
- 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."));
116073
116482
  return 0;
116074
116483
  }
116075
116484
  if (!opts.yes) {
116076
116485
  if (!process.stdin.isTTY) {
116077
116486
  console.error(
116078
- 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.`)
116079
116488
  );
116080
116489
  return 1;
116081
116490
  }
@@ -116083,7 +116492,7 @@ async function runCloudSync(opts = {}) {
116083
116492
  try {
116084
116493
  const answer = (await rl.question(`Upload ${uploadable.length} investigation(s) to ${target}? (y/N) `)).trim().toLowerCase();
116085
116494
  if (answer !== "y" && answer !== "yes") {
116086
- console.log(import_picocolors45.default.dim("Aborted. Nothing uploaded."));
116495
+ console.log(import_picocolors46.default.dim("Aborted. Nothing uploaded."));
116087
116496
  return 0;
116088
116497
  }
116089
116498
  } finally {
@@ -116096,18 +116505,18 @@ async function runCloudSync(opts = {}) {
116096
116505
  try {
116097
116506
  const refs = await uploadInvestigationToCloud(session.client, cfg, c4.report);
116098
116507
  created++;
116099
- 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}`);
116100
116509
  } catch (err2) {
116101
116510
  failed++;
116102
- 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}`);
116103
116512
  }
116104
116513
  }
116105
116514
  console.log("");
116106
116515
  console.log(
116107
- `${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") + "."
116108
116517
  );
116109
116518
  console.log(
116110
- 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.")
116111
116520
  );
116112
116521
  return failed > 0 ? 1 : 0;
116113
116522
  }
@@ -116115,8 +116524,8 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
116115
116524
  if (!process.stdin.isTTY && !yes) return null;
116116
116525
  if (ctx.organizations.length === 0) {
116117
116526
  console.error(
116118
- import_picocolors45.default.red("You're not a member of any organization yet.") + `
116119
- 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.`
116120
116529
  );
116121
116530
  return null;
116122
116531
  }
@@ -116141,9 +116550,9 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
116141
116550
  const wsName = await ask2("New workspace name", repoName || "default");
116142
116551
  const ws = await client.createWorkspace(org.id, { name: wsName });
116143
116552
  workspace = { id: ws.id, slug: ws.slug };
116144
- 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)}.`));
116145
116554
  } else {
116146
- console.log(`Workspaces in ${import_picocolors45.default.bold(org.slug)}:`);
116555
+ console.log(`Workspaces in ${import_picocolors46.default.bold(org.slug)}:`);
116147
116556
  orgWorkspaces.forEach((w4, i3) => console.log(` ${i3 + 1}) ${w4.slug}`));
116148
116557
  console.log(` ${orgWorkspaces.length + 1}) + create a new workspace`);
116149
116558
  const pick2 = Number(await ask2("Workspace number", "1"));
@@ -116151,7 +116560,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
116151
116560
  const wsName = await ask2("New workspace name", repoName || "default");
116152
116561
  const ws = await client.createWorkspace(org.id, { name: wsName });
116153
116562
  workspace = { id: ws.id, slug: ws.slug };
116154
- 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)}.`));
116155
116564
  } else {
116156
116565
  const chosen = orgWorkspaces[pick2 - 1] ?? orgWorkspaces[0];
116157
116566
  workspace = { id: chosen.id, slug: chosen.slug };
@@ -116163,7 +116572,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
116163
116572
  remoteUrl: remote?.remoteUrl,
116164
116573
  provider: remote ? inferProvider(remote.remoteUrl) : void 0
116165
116574
  });
116166
- 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)}.`));
116167
116576
  return {
116168
116577
  organization: { id: org.id, slug: org.slug },
116169
116578
  workspace,
@@ -116179,7 +116588,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
116179
116588
 
116180
116589
  // ../../packages/cli/src/commands/telemetry.ts
116181
116590
  init_cjs_shims();
116182
- var import_picocolors47 = __toESM(require_picocolors(), 1);
116591
+ var import_picocolors48 = __toESM(require_picocolors(), 1);
116183
116592
  var import_node_crypto8 = require("crypto");
116184
116593
 
116185
116594
  // ../../packages/cli/src/lib/telemetry/transport.ts
@@ -116240,7 +116649,7 @@ async function deleteRemoteTelemetry(installId, opts = {}) {
116240
116649
 
116241
116650
  // ../../packages/cli/src/lib/telemetry/notice.ts
116242
116651
  init_cjs_shims();
116243
- var import_picocolors46 = __toESM(require_picocolors(), 1);
116652
+ var import_picocolors47 = __toESM(require_picocolors(), 1);
116244
116653
  var PRIVACY_URL = "https://horus.sh/privacy";
116245
116654
  var SUPPRESS_FIRST_TOKEN = /* @__PURE__ */ new Set(["telemetry", "help"]);
116246
116655
  var SUPPRESS_FLAGS = /* @__PURE__ */ new Set(["-V", "--version", "-h", "--help"]);
@@ -116252,11 +116661,11 @@ function shouldSuppressFor(argv) {
116252
116661
  return firstToken != null && SUPPRESS_FIRST_TOKEN.has(firstToken);
116253
116662
  }
116254
116663
  function banner() {
116255
- const b5 = import_picocolors46.default.bold;
116256
- const dim = import_picocolors46.default.dim;
116664
+ const b5 = import_picocolors47.default.bold;
116665
+ const dim = import_picocolors47.default.dim;
116257
116666
  return [
116258
116667
  "",
116259
- 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.")),
116260
116669
  dim(" \u2022 On by default: command names, timings, success/failure, and where the"),
116261
116670
  dim(" engine ran out of evidence. No file contents, queries, or secrets."),
116262
116671
  dim(" \u2022 Off by default: your investigation inputs/outputs (opt in to help train"),
@@ -116264,7 +116673,7 @@ function banner() {
116264
116673
  dim(" \u2022 If you use Horus Cloud, linked investigations sync to your workspace."),
116265
116674
  "",
116266
116675
  ` ${dim("Opt out anytime:")} ${b5("horus telemetry disable")} ${dim("\xB7")} ${b5("HORUS_TELEMETRY=0")} ${dim("\xB7")} ${b5("DO_NOT_TRACK=1")}`,
116267
- ` ${dim("Details:")} ${import_picocolors46.default.underline(PRIVACY_URL)}`,
116676
+ ` ${dim("Details:")} ${import_picocolors47.default.underline(PRIVACY_URL)}`,
116268
116677
  ""
116269
116678
  ].join("\n");
116270
116679
  }
@@ -116287,45 +116696,45 @@ function maybeShowFirstRunNotice(argv = process.argv) {
116287
116696
 
116288
116697
  // ../../packages/cli/src/commands/telemetry.ts
116289
116698
  function onOff(enabled) {
116290
- 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");
116291
116700
  }
116292
116701
  function warnIfEnvOverrides() {
116293
116702
  const decision = resolveConsent();
116294
116703
  if (decision.forcedOff) {
116295
116704
  console.log(
116296
- import_picocolors47.default.yellow(
116705
+ import_picocolors48.default.yellow(
116297
116706
  `
116298
116707
  Note: telemetry is currently forced off by your environment (${decision.reason}).`
116299
116708
  )
116300
116709
  );
116301
- 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."));
116302
116711
  } else if (decision.source === "ci") {
116303
116712
  console.log(
116304
- 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.")
116305
116714
  );
116306
116715
  }
116307
116716
  }
116308
116717
  async function runTelemetryStatus() {
116309
116718
  const state = readTelemetryState();
116310
116719
  const decision = resolveConsent({ state });
116311
- console.log(import_picocolors47.default.bold("Horus telemetry"));
116720
+ console.log(import_picocolors48.default.bold("Horus telemetry"));
116312
116721
  console.log(
116313
- ` 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}]`)}`
116314
116723
  );
116315
116724
  console.log(` Content sharing (Tier B): ${onOff(decision.tierB)}`);
116316
- console.log(import_picocolors47.default.dim(` Reason: ${decision.reason}`));
116725
+ console.log(import_picocolors48.default.dim(` Reason: ${decision.reason}`));
116317
116726
  if (state) {
116318
- console.log(import_picocolors47.default.dim(` Install ID: ${state.installId}`));
116727
+ console.log(import_picocolors48.default.dim(` Install ID: ${state.installId}`));
116319
116728
  console.log(
116320
- import_picocolors47.default.dim(
116729
+ import_picocolors48.default.dim(
116321
116730
  ` Saved: Tier A ${state.tierA.enabled ? "on" : "off"}, Tier B ${state.tierB.enabled ? "on" : "off"}`
116322
116731
  )
116323
116732
  );
116324
116733
  } else {
116325
- 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)."));
116326
116735
  }
116327
- console.log(import_picocolors47.default.dim(` Config: ${telemetryPath()}`));
116328
- 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}`));
116329
116738
  if (!state || decision.source === "env" || decision.source === "ci") {
116330
116739
  warnIfEnvOverrides();
116331
116740
  }
@@ -116336,7 +116745,7 @@ async function runTelemetryEnable() {
116336
116745
  s5.tierA.enabled = true;
116337
116746
  if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
116338
116747
  });
116339
- 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!"));
116340
116749
  warnIfEnvOverrides();
116341
116750
  return 0;
116342
116751
  }
@@ -116346,7 +116755,7 @@ async function runTelemetryDisable() {
116346
116755
  s5.tierB.enabled = false;
116347
116756
  if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
116348
116757
  });
116349
- 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)."));
116350
116759
  return 0;
116351
116760
  }
116352
116761
  async function runTelemetryEnableContent() {
@@ -116357,10 +116766,10 @@ async function runTelemetryEnableContent() {
116357
116766
  if (!s5.tierA.noticeShownAt) s5.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
116358
116767
  });
116359
116768
  console.log(
116360
- 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.")
116361
116770
  );
116362
- console.log(import_picocolors47.default.dim("Content is scrubbed of secrets/PII before it ever leaves your machine."));
116363
- 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}`));
116364
116773
  warnIfEnvOverrides();
116365
116774
  return 0;
116366
116775
  }
@@ -116368,7 +116777,7 @@ async function runTelemetryDisableContent() {
116368
116777
  updateTelemetryState((s5) => {
116369
116778
  s5.tierB.enabled = false;
116370
116779
  });
116371
- 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."));
116372
116781
  return 0;
116373
116782
  }
116374
116783
  async function runTelemetryResetId() {
@@ -116376,8 +116785,8 @@ async function runTelemetryResetId() {
116376
116785
  updateTelemetryState((s5) => {
116377
116786
  s5.installId = next;
116378
116787
  });
116379
- console.log(import_picocolors47.default.green("Install ID reset."));
116380
- 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}`));
116381
116790
  return 0;
116382
116791
  }
116383
116792
  async function runTelemetryDelete() {
@@ -116385,9 +116794,9 @@ async function runTelemetryDelete() {
116385
116794
  if (installId) await deleteRemoteTelemetry(installId);
116386
116795
  deleteTelemetryState();
116387
116796
  clearSpool();
116388
- 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."));
116389
116798
  console.log(
116390
- import_picocolors47.default.dim(
116799
+ import_picocolors48.default.dim(
116391
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."
116392
116801
  )
116393
116802
  );
@@ -116430,35 +116839,35 @@ function installCommandTelemetry(program2) {
116430
116839
 
116431
116840
  // ../../packages/cli/src/commands/feedback.ts
116432
116841
  init_cjs_shims();
116433
- var import_picocolors48 = __toESM(require_picocolors(), 1);
116842
+ var import_picocolors49 = __toESM(require_picocolors(), 1);
116434
116843
  async function runFeedback(investigationId, opts = {}) {
116435
116844
  const id = investigationId?.trim();
116436
116845
  if (!id) {
116437
- 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]"));
116438
116847
  return 1;
116439
116848
  }
116440
116849
  if (opts.resolved !== void 0) {
116441
116850
  const resolved = parseResolved(opts.resolved);
116442
116851
  if (resolved === null) {
116443
- 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.`));
116444
116853
  return 1;
116445
116854
  }
116446
116855
  let manualEstimateMinutes = null;
116447
116856
  if (opts.manualEstimateMin !== void 0) {
116448
116857
  const n4 = Number.parseInt(opts.manualEstimateMin, 10);
116449
116858
  if (!Number.isFinite(n4) || n4 < 0) {
116450
- 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).`));
116451
116860
  return 1;
116452
116861
  }
116453
116862
  manualEstimateMinutes = n4;
116454
116863
  }
116455
116864
  submitFeedback({ investigationId: id, resolved, manualEstimateMinutes, horusSeconds: null, source: "flag" });
116456
- 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.`));
116457
116866
  return 0;
116458
116867
  }
116459
116868
  if (!process.stdin.isTTY) {
116460
116869
  console.error(
116461
- import_picocolors48.default.yellow(
116870
+ import_picocolors49.default.yellow(
116462
116871
  "horus feedback needs an interactive terminal \u2014 or pass --resolved yes|partly|no for non-interactive (agent) use."
116463
116872
  )
116464
116873
  );
@@ -116551,8 +116960,8 @@ Examples:
116551
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) => {
116552
116961
  process.exitCode = await runStop(opts);
116553
116962
  });
116554
- program2.command("hosts").description("List registered source-intelligence hosts and their live status (port, repo, running/stopped)").action(async () => {
116555
- 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 });
116556
116965
  });
116557
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(
116558
116967
  async (opts) => {