@merittdev/horus 0.2.3 → 0.3.1

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 (2) hide show
  1. package/dist/index.cjs +1366 -569
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -23435,9 +23435,9 @@ var require_client_metadata = __commonJS({
23435
23435
  throw new error_1.MongoInvalidArgumentError("Unable to include driverInfo name and version, metadata cannot exceed 512 bytes");
23436
23436
  }
23437
23437
  let runtimeInfo = getRuntimeInfo();
23438
- for (const { platform = "" } of driverInfoList) {
23439
- if (platform.length > 0) {
23440
- runtimeInfo = `${runtimeInfo}|${platform}`;
23438
+ for (const { platform: platform2 = "" } of driverInfoList) {
23439
+ if (platform2.length > 0) {
23440
+ runtimeInfo = `${runtimeInfo}|${platform2}`;
23441
23441
  }
23442
23442
  }
23443
23443
  if (!metadataDocument.ifItFitsItSits("platform", runtimeInfo)) {
@@ -43447,7 +43447,7 @@ var require_common3 = __commonJS({
43447
43447
  function setup(env) {
43448
43448
  createDebug.debug = createDebug;
43449
43449
  createDebug.default = createDebug;
43450
- createDebug.coerce = coerce2;
43450
+ createDebug.coerce = coerce3;
43451
43451
  createDebug.disable = disable;
43452
43452
  createDebug.enable = enable;
43453
43453
  createDebug.enabled = enabled;
@@ -43602,7 +43602,7 @@ var require_common3 = __commonJS({
43602
43602
  }
43603
43603
  return false;
43604
43604
  }
43605
- function coerce2(val) {
43605
+ function coerce3(val) {
43606
43606
  if (val instanceof Error) {
43607
43607
  return val.stack || val.message;
43608
43608
  }
@@ -57239,9 +57239,8 @@ init_cjs_shims();
57239
57239
 
57240
57240
  // ../../packages/core/src/version.ts
57241
57241
  init_cjs_shims();
57242
- var HORUS_VERSION = true ? "0.2.3" : "dev";
57243
- var PINNED_AXON_VERSION = "1.0.7";
57244
- var PINNED_SOURCE_VERSION = PINNED_AXON_VERSION;
57242
+ var HORUS_VERSION = true ? "0.3.1" : "dev";
57243
+ var PINNED_SOURCE_VERSION = "1.1.1";
57245
57244
 
57246
57245
  // ../../packages/core/src/evidence.ts
57247
57246
  init_cjs_shims();
@@ -61672,7 +61671,7 @@ var horusConfigSchema = external_exports.object({
61672
61671
  projects: external_exports.array(projectSchema).default([]),
61673
61672
  axon: external_exports.object({
61674
61673
  /** Fail fast if the running Axon is not this version. */
61675
- pinnedVersion: external_exports.string().default("1.0.7")
61674
+ pinnedVersion: external_exports.string().default("1.1.1")
61676
61675
  }).default({}),
61677
61676
  database: databaseSchema,
61678
61677
  models: modelsSchema.default({}),
@@ -62052,6 +62051,73 @@ packages/core/src/git.ts @horus/core-team @horus/devex-team
62052
62051
  `.trim();
62053
62052
  var FIXTURE_CODEOWNERS_RULES = parseCodeowners(FIXTURE_CODEOWNERS);
62054
62053
 
62054
+ // ../../packages/core/src/redact.ts
62055
+ init_cjs_shims();
62056
+ var BASE_PATTERNS = [
62057
+ [/(authorization\s*[=:]\s*)(bearer\s+)[^\s,"')]+/gi, "$1$2[REDACTED]"],
62058
+ [/(authorization\s*[=:]\s*)(basic\s+)[^\s,"')]+/gi, "$1$2[REDACTED]"],
62059
+ [
62060
+ /("?(?:password|passwd|secret|token|api[_-]key|apikey|x-api-key)"?\s*[=:]\s*)"?[^"',\s)>]+/gi,
62061
+ "$1[REDACTED]"
62062
+ ],
62063
+ [/((?:cookie|set-cookie)\s*[=:]\s*)[^\s"',;>]{4,}/gi, "$1[REDACTED]"],
62064
+ [
62065
+ /((?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis(?:s)?):\/\/)[^:@/\s]+:[^@\s]+@/gi,
62066
+ "$1[REDACTED]@"
62067
+ ],
62068
+ [/\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g, "[REDACTED-CARD]"]
62069
+ ];
62070
+ var EXTENDED_PATTERNS = [
62071
+ // PEM private key blocks (run first — multi-line, highest risk).
62072
+ [
62073
+ /-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z ]+ )?PRIVATE KEY-----/g,
62074
+ "[REDACTED-PRIVATE-KEY]"
62075
+ ],
62076
+ // JSON Web Tokens (three base64url segments starting with the `{"` header).
62077
+ [/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[REDACTED-JWT]"],
62078
+ // AWS access key ids.
62079
+ [/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g, "[REDACTED-AWS-KEY]"],
62080
+ // Google API keys.
62081
+ [/\bAIza[0-9A-Za-z_-]{35}\b/g, "[REDACTED-GCP-KEY]"],
62082
+ // Slack tokens.
62083
+ [/\bxox[baprs]-[0-9A-Za-z-]{10,}\b/g, "[REDACTED-SLACK-TOKEN]"],
62084
+ // GitHub tokens.
62085
+ [/\bgh[pousr]_[0-9A-Za-z]{36,}\b/g, "[REDACTED-GH-TOKEN]"],
62086
+ // Emails.
62087
+ [/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, "[REDACTED-EMAIL]"],
62088
+ // IPv4 addresses.
62089
+ [/\b(?:\d{1,3}\.){3}\d{1,3}\b/g, "[REDACTED-IP]"]
62090
+ ];
62091
+ var RESIDUAL_HIGH_RISK = [
62092
+ /-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----/,
62093
+ /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/,
62094
+ /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/
62095
+ ];
62096
+ function apply(patterns, input) {
62097
+ let out = input;
62098
+ for (const [pattern, replacement] of patterns) {
62099
+ out = out.replace(pattern, replacement);
62100
+ }
62101
+ return out;
62102
+ }
62103
+ function redactSecrets(input) {
62104
+ return apply(BASE_PATTERNS, input);
62105
+ }
62106
+ function redactContent(input) {
62107
+ return apply(EXTENDED_PATTERNS, apply(BASE_PATTERNS, input));
62108
+ }
62109
+ function redactOrDrop(input) {
62110
+ try {
62111
+ const value = redactContent(input);
62112
+ if (RESIDUAL_HIGH_RISK.some((p) => p.test(value))) {
62113
+ return { value: null, dropped: true };
62114
+ }
62115
+ return { value, dropped: false };
62116
+ } catch {
62117
+ return { value: null, dropped: true };
62118
+ }
62119
+ }
62120
+
62055
62121
  // ../../packages/cli/src/commands/status.ts
62056
62122
  init_cjs_shims();
62057
62123
  var import_picocolors = __toESM(require_picocolors(), 1);
@@ -63135,25 +63201,8 @@ function annotateAgainstBaseline(current, baseline) {
63135
63201
  current.newSignatures = newSignatures;
63136
63202
  return current;
63137
63203
  }
63138
- var REDACTION_PATTERNS = [
63139
- // Authorization headers (Bearer / Basic / API-key tokens)
63140
- [/(authorization\s*[=:]\s*)(bearer\s+)[^\s,"')]+/gi, "$1$2[REDACTED]"],
63141
- [/(authorization\s*[=:]\s*)(basic\s+)[^\s,"')]+/gi, "$1$2[REDACTED]"],
63142
- // Standalone password/token/secret keys in KV strings, query params, JSON
63143
- [/("?(?:password|passwd|secret|token|api[_-]key|apikey|x-api-key)"?\s*[=:]\s*)"?[^"',\s)>]+/gi, "$1[REDACTED]"],
63144
- // Cookie / set-cookie header values
63145
- [/((?:cookie|set-cookie)\s*[=:]\s*)[^\s"',;>]{4,}/gi, "$1[REDACTED]"],
63146
- // Database / service connection strings with embedded credentials
63147
- [/((?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis(?:s)?):\/\/)[^:@/\s]+:[^@\s]+@/gi, "$1[REDACTED]@"],
63148
- // 16-digit card-number-like sequences (PCIDSS)
63149
- [/\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g, "[REDACTED-CARD]"]
63150
- ];
63151
63204
  function redactSensitiveString(s) {
63152
- let out = s;
63153
- for (const [pattern, replacement] of REDACTION_PATTERNS) {
63154
- out = out.replace(pattern, replacement);
63155
- }
63156
- return out;
63205
+ return redactSecrets(s);
63157
63206
  }
63158
63207
  function redactErrorSignature(sig) {
63159
63208
  if (sig.sampleMessage === void 0) return sig;
@@ -66196,7 +66245,7 @@ function sql(strings, ...params) {
66196
66245
  return new SQL([new StringChunk(str3)]);
66197
66246
  }
66198
66247
  sql2.raw = raw;
66199
- function join11(chunks, separator) {
66248
+ function join13(chunks, separator) {
66200
66249
  const result = [];
66201
66250
  for (const [i, chunk] of chunks.entries()) {
66202
66251
  if (i > 0 && separator !== void 0) {
@@ -66206,7 +66255,7 @@ function sql(strings, ...params) {
66206
66255
  }
66207
66256
  return new SQL(result);
66208
66257
  }
66209
- sql2.join = join11;
66258
+ sql2.join = join13;
66210
66259
  function identifier(value) {
66211
66260
  return new Name(value);
66212
66261
  }
@@ -69797,7 +69846,7 @@ var PgSelectQueryBuilderBase = class extends TypedQueryBuilder {
69797
69846
  return (table, on) => {
69798
69847
  const baseTableName = this.tableName;
69799
69848
  const tableName = getTableLikeName(table);
69800
- if (typeof tableName === "string" && this.config.joins?.some((join11) => join11.alias === tableName)) {
69849
+ if (typeof tableName === "string" && this.config.joins?.some((join13) => join13.alias === tableName)) {
69801
69850
  throw new Error(`Alias "${tableName}" is already used in this query`);
69802
69851
  }
69803
69852
  if (!this.isPartialSelect) {
@@ -70809,7 +70858,7 @@ var PgUpdateBase = class extends QueryPromise {
70809
70858
  createJoin(joinType) {
70810
70859
  return (table, on) => {
70811
70860
  const tableName = getTableLikeName(table);
70812
- if (typeof tableName === "string" && this.config.joins.some((join11) => join11.alias === tableName)) {
70861
+ if (typeof tableName === "string" && this.config.joins.some((join13) => join13.alias === tableName)) {
70813
70862
  throw new Error(`Alias "${tableName}" is already used in this query`);
70814
70863
  }
70815
70864
  if (typeof on === "function") {
@@ -70905,10 +70954,10 @@ var PgUpdateBase = class extends QueryPromise {
70905
70954
  const fromFields = this.getTableLikeFields(this.config.from);
70906
70955
  fields[tableName] = fromFields;
70907
70956
  }
70908
- for (const join11 of this.config.joins) {
70909
- const tableName2 = getTableLikeName(join11.table);
70910
- if (typeof tableName2 === "string" && !is(join11.table, SQL)) {
70911
- const fromFields = this.getTableLikeFields(join11.table);
70957
+ for (const join13 of this.config.joins) {
70958
+ const tableName2 = getTableLikeName(join13.table);
70959
+ if (typeof tableName2 === "string" && !is(join13.table, SQL)) {
70960
+ const fromFields = this.getTableLikeFields(join13.table);
70912
70961
  fields[tableName2] = fromFields;
70913
70962
  }
70914
70963
  }
@@ -77143,7 +77192,12 @@ async function investigate(input, deps) {
77143
77192
  code.impact(top.id, 2),
77144
77193
  code.flowsFor(top.id)
77145
77194
  ]);
77146
- const edges = await listQueueEdges(db, { project: input.repo });
77195
+ let edges = [];
77196
+ try {
77197
+ edges = await listQueueEdges(db, { project: input.repo });
77198
+ } catch {
77199
+ edges = [];
77200
+ }
77147
77201
  const symbolNames = /* @__PURE__ */ new Set([
77148
77202
  top.name,
77149
77203
  ...ctx.callers.map((s) => s.name),
@@ -77826,9 +77880,9 @@ async function investigate(input, deps) {
77826
77880
  const confidence = clamp015(0.5 * evidenceConfidence + 0.5 * seedResolved);
77827
77881
  const area = ctx?.community?.name ?? top?.filePath ?? (input.service ?? "runtime evidence");
77828
77882
  const topCause = rankedCauses[0];
77829
- const banner = degradedNoSource ? "Runtime-only (source intelligence unavailable). " : "";
77883
+ const banner2 = degradedNoSource ? "Runtime-only (source intelligence unavailable). " : "";
77830
77884
  const scope = top ? `resolved to ${label} (${area})` : `over runtime evidence (${area})`;
77831
- const summary = topCause ? `${banner}Investigation of "${hint}" ${scope}. Top suspected cause: ${topCause.title}.` : `${banner}Investigation of "${hint}" ${scope}. No dominant suspected cause emerged from the available ${degradedNoSource ? "runtime" : "structural"} evidence.`;
77885
+ const summary = topCause ? `${banner2}Investigation of "${hint}" ${scope}. Top suspected cause: ${topCause.title}.` : `${banner2}Investigation of "${hint}" ${scope}. No dominant suspected cause emerged from the available ${degradedNoSource ? "runtime" : "structural"} evidence.`;
77832
77886
  const nextActions = buildNextActions(top, ctx, impact, queueHits, changes, input);
77833
77887
  if (degradedNoSource) {
77834
77888
  nextActions.unshift(
@@ -77889,6 +77943,7 @@ async function investigate(input, deps) {
77889
77943
  report.sourceStatus = buildRuntimeSourceStatus(evidence2, connectorFlags);
77890
77944
  const persistedId = await persist(db, input, report);
77891
77945
  if (persistedId) report.id = persistedId;
77946
+ report.persisted = persistedId !== null;
77892
77947
  if (persistedId !== null) {
77893
77948
  const tags = deriveTags(report);
77894
77949
  report.similarIncidents = await recallSimilar(db, tags, persistedId, input.repo ?? null);
@@ -80983,22 +81038,22 @@ function extractQueueGraph(input) {
80983
81038
  }
80984
81039
  }
80985
81040
  const producers = [];
80986
- for (const pc43 of input.producerClasses) {
80987
- for (const m of pc43.content.matchAll(INJECT_QUEUE_RE)) {
81041
+ for (const pc47 of input.producerClasses) {
81042
+ for (const m of pc47.content.matchAll(INJECT_QUEUE_RE)) {
80988
81043
  const queue = m[1] ?? "";
80989
- if (queue) producers.push({ queue, symbol: pc43.name, file: pc43.filePath });
81044
+ if (queue) producers.push({ queue, symbol: pc47.name, file: pc47.filePath });
80990
81045
  }
80991
- for (const m of pc43.content.matchAll(NEW_BULL_RE)) {
81046
+ for (const m of pc47.content.matchAll(NEW_BULL_RE)) {
80992
81047
  if (m[2] !== "Queue") continue;
80993
81048
  const queue = resolveArg(m[3] ?? "", constMap);
80994
81049
  if (queue) {
80995
- producers.push({ queue, symbol: (m[1] ?? pc43.name) || baseName(pc43.filePath), file: pc43.filePath });
81050
+ producers.push({ queue, symbol: (m[1] ?? pc47.name) || baseName(pc47.filePath), file: pc47.filePath });
80996
81051
  }
80997
81052
  }
80998
- for (const m of pc43.content.matchAll(QUEUE_NAME_CONST_RE)) {
81053
+ for (const m of pc47.content.matchAll(QUEUE_NAME_CONST_RE)) {
80999
81054
  const ident = m[1] ?? "";
81000
81055
  const queue = m[2] ?? "";
81001
- if (queue) producers.push({ queue, symbol: ident || baseName(pc43.filePath), file: pc43.filePath });
81056
+ if (queue) producers.push({ queue, symbol: ident || baseName(pc47.filePath), file: pc47.filePath });
81002
81057
  }
81003
81058
  }
81004
81059
  const workers = [];
@@ -90068,21 +90123,21 @@ var Protocol = class {
90068
90123
  * the error appropriately (e.g., by failing the task, logging, etc.). The Protocol layer
90069
90124
  * simply propagates the error.
90070
90125
  */
90071
- async _enqueueTaskMessage(taskId, message, sessionId) {
90126
+ async _enqueueTaskMessage(taskId, message, sessionId2) {
90072
90127
  if (!this._taskStore || !this._taskMessageQueue) {
90073
90128
  throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
90074
90129
  }
90075
90130
  const maxQueueSize = this._options?.maxTaskQueueSize;
90076
- await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
90131
+ await this._taskMessageQueue.enqueue(taskId, message, sessionId2, maxQueueSize);
90077
90132
  }
90078
90133
  /**
90079
90134
  * Clears the message queue for a task and rejects any pending request resolvers.
90080
90135
  * @param taskId The task ID whose queue should be cleared
90081
90136
  * @param sessionId Optional session ID for binding the operation to a specific session
90082
90137
  */
90083
- async _clearTaskQueue(taskId, sessionId) {
90138
+ async _clearTaskQueue(taskId, sessionId2) {
90084
90139
  if (this._taskMessageQueue) {
90085
- const messages2 = await this._taskMessageQueue.dequeueAll(taskId, sessionId);
90140
+ const messages2 = await this._taskMessageQueue.dequeueAll(taskId, sessionId2);
90086
90141
  for (const message of messages2) {
90087
90142
  if (message.type === "request" && isJSONRPCRequest(message.message)) {
90088
90143
  const requestId = message.message.id;
@@ -90125,7 +90180,7 @@ var Protocol = class {
90125
90180
  }, { once: true });
90126
90181
  });
90127
90182
  }
90128
- requestTaskStore(request, sessionId) {
90183
+ requestTaskStore(request, sessionId2) {
90129
90184
  const taskStore = this._taskStore;
90130
90185
  if (!taskStore) {
90131
90186
  throw new Error("No task store configured");
@@ -90138,18 +90193,18 @@ var Protocol = class {
90138
90193
  return await taskStore.createTask(taskParams, request.id, {
90139
90194
  method: request.method,
90140
90195
  params: request.params
90141
- }, sessionId);
90196
+ }, sessionId2);
90142
90197
  },
90143
90198
  getTask: async (taskId) => {
90144
- const task = await taskStore.getTask(taskId, sessionId);
90199
+ const task = await taskStore.getTask(taskId, sessionId2);
90145
90200
  if (!task) {
90146
90201
  throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
90147
90202
  }
90148
90203
  return task;
90149
90204
  },
90150
90205
  storeTaskResult: async (taskId, status, result) => {
90151
- await taskStore.storeTaskResult(taskId, status, result, sessionId);
90152
- const task = await taskStore.getTask(taskId, sessionId);
90206
+ await taskStore.storeTaskResult(taskId, status, result, sessionId2);
90207
+ const task = await taskStore.getTask(taskId, sessionId2);
90153
90208
  if (task) {
90154
90209
  const notification = TaskStatusNotificationSchema.parse({
90155
90210
  method: "notifications/tasks/status",
@@ -90162,18 +90217,18 @@ var Protocol = class {
90162
90217
  }
90163
90218
  },
90164
90219
  getTaskResult: (taskId) => {
90165
- return taskStore.getTaskResult(taskId, sessionId);
90220
+ return taskStore.getTaskResult(taskId, sessionId2);
90166
90221
  },
90167
90222
  updateTaskStatus: async (taskId, status, statusMessage) => {
90168
- const task = await taskStore.getTask(taskId, sessionId);
90223
+ const task = await taskStore.getTask(taskId, sessionId2);
90169
90224
  if (!task) {
90170
90225
  throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
90171
90226
  }
90172
90227
  if (isTerminal(task.status)) {
90173
90228
  throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
90174
90229
  }
90175
- await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId);
90176
- const updatedTask = await taskStore.getTask(taskId, sessionId);
90230
+ await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId2);
90231
+ const updatedTask = await taskStore.getTask(taskId, sessionId2);
90177
90232
  if (updatedTask) {
90178
90233
  const notification = TaskStatusNotificationSchema.parse({
90179
90234
  method: "notifications/tasks/status",
@@ -90186,7 +90241,7 @@ var Protocol = class {
90186
90241
  }
90187
90242
  },
90188
90243
  listTasks: (cursor) => {
90189
- return taskStore.listTasks(cursor, sessionId);
90244
+ return taskStore.listTasks(cursor, sessionId2);
90190
90245
  }
90191
90246
  };
90192
90247
  }
@@ -90540,8 +90595,8 @@ var Server = class extends Protocol {
90540
90595
  this._serverInfo = _serverInfo;
90541
90596
  this._loggingLevels = /* @__PURE__ */ new Map();
90542
90597
  this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index2) => [level, index2]));
90543
- this.isMessageIgnored = (level, sessionId) => {
90544
- const currentLevel = this._loggingLevels.get(sessionId);
90598
+ this.isMessageIgnored = (level, sessionId2) => {
90599
+ const currentLevel = this._loggingLevels.get(sessionId2);
90545
90600
  return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
90546
90601
  };
90547
90602
  this._capabilities = options?.capabilities ?? {};
@@ -90884,9 +90939,9 @@ var Server = class extends Protocol {
90884
90939
  * @param params
90885
90940
  * @param sessionId optional for stateless and backward compatibility
90886
90941
  */
90887
- async sendLoggingMessage(params, sessionId) {
90942
+ async sendLoggingMessage(params, sessionId2) {
90888
90943
  if (this._capabilities.logging) {
90889
- if (!this.isMessageIgnored(params.level, sessionId)) {
90944
+ if (!this.isMessageIgnored(params.level, sessionId2)) {
90890
90945
  return this.notification({ method: "notifications/message", params });
90891
90946
  }
90892
90947
  }
@@ -91691,8 +91746,8 @@ var McpServer = class {
91691
91746
  * @param params
91692
91747
  * @param sessionId optional for stateless and backward compatibility
91693
91748
  */
91694
- async sendLoggingMessage(params, sessionId) {
91695
- return this.server.sendLoggingMessage(params, sessionId);
91749
+ async sendLoggingMessage(params, sessionId2) {
91750
+ return this.server.sendLoggingMessage(params, sessionId2);
91696
91751
  }
91697
91752
  /**
91698
91753
  * Sends a resource list changed event to the client, if connected.
@@ -93743,7 +93798,7 @@ function formatAge(ms) {
93743
93798
 
93744
93799
  // ../../packages/cli/src/commands/investigate.ts
93745
93800
  init_cjs_shims();
93746
- var import_picocolors8 = __toESM(require_picocolors(), 1);
93801
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
93747
93802
 
93748
93803
  // ../../packages/cli/src/lib/cloud/investigation-sync.ts
93749
93804
  init_cjs_shims();
@@ -93820,6 +93875,346 @@ async function listCloudInvestigations(client, cfg) {
93820
93875
  return result.investigations;
93821
93876
  }
93822
93877
 
93878
+ // ../../packages/cli/src/lib/telemetry/client.ts
93879
+ init_cjs_shims();
93880
+ var import_node_crypto6 = require("crypto");
93881
+ var import_node_os3 = require("os");
93882
+
93883
+ // ../../packages/cli/src/lib/telemetry/consent.ts
93884
+ init_cjs_shims();
93885
+
93886
+ // ../../packages/cli/src/lib/telemetry/store.ts
93887
+ init_cjs_shims();
93888
+ var import_node_fs13 = require("fs");
93889
+ var import_node_crypto5 = require("crypto");
93890
+
93891
+ // ../../packages/cli/src/lib/telemetry/paths.ts
93892
+ init_cjs_shims();
93893
+ var import_node_path14 = require("path");
93894
+ var TELEMETRY_FILE = "telemetry.json";
93895
+ var TELEMETRY_QUEUE_DIR = "telemetry-queue";
93896
+ function telemetryPath() {
93897
+ return (0, import_node_path14.join)(horusHome(), TELEMETRY_FILE);
93898
+ }
93899
+ function telemetryQueueDir() {
93900
+ return (0, import_node_path14.join)(horusHome(), TELEMETRY_QUEUE_DIR);
93901
+ }
93902
+
93903
+ // ../../packages/cli/src/lib/telemetry/store.ts
93904
+ var TELEMETRY_SCHEMA_VERSION = 1;
93905
+ function nowIso() {
93906
+ return (/* @__PURE__ */ new Date()).toISOString();
93907
+ }
93908
+ function defaultTelemetryState(installId = (0, import_node_crypto5.randomUUID)()) {
93909
+ const now = nowIso();
93910
+ return {
93911
+ schemaVersion: TELEMETRY_SCHEMA_VERSION,
93912
+ installId,
93913
+ tierA: { enabled: true, noticeShownAt: null },
93914
+ tierB: { enabled: false, enabledAt: null },
93915
+ createdAt: now,
93916
+ updatedAt: now
93917
+ };
93918
+ }
93919
+ function coerce2(raw) {
93920
+ if (!raw || typeof raw !== "object") return null;
93921
+ const r = raw;
93922
+ if (typeof r.installId !== "string" || r.installId.length === 0) return null;
93923
+ const tierA = r.tierA ?? {};
93924
+ const tierB = r.tierB ?? {};
93925
+ return {
93926
+ schemaVersion: typeof r.schemaVersion === "number" ? r.schemaVersion : TELEMETRY_SCHEMA_VERSION,
93927
+ installId: r.installId,
93928
+ tierA: {
93929
+ enabled: typeof tierA.enabled === "boolean" ? tierA.enabled : true,
93930
+ noticeShownAt: typeof tierA.noticeShownAt === "string" ? tierA.noticeShownAt : null
93931
+ },
93932
+ tierB: {
93933
+ enabled: typeof tierB.enabled === "boolean" ? tierB.enabled : false,
93934
+ enabledAt: typeof tierB.enabledAt === "string" ? tierB.enabledAt : null
93935
+ },
93936
+ createdAt: typeof r.createdAt === "string" ? r.createdAt : nowIso(),
93937
+ updatedAt: typeof r.updatedAt === "string" ? r.updatedAt : nowIso()
93938
+ };
93939
+ }
93940
+ function readTelemetryState() {
93941
+ const p = telemetryPath();
93942
+ if (!(0, import_node_fs13.existsSync)(p)) return null;
93943
+ try {
93944
+ return coerce2(JSON.parse((0, import_node_fs13.readFileSync)(p, "utf8")));
93945
+ } catch {
93946
+ return null;
93947
+ }
93948
+ }
93949
+ function writeTelemetryState(state) {
93950
+ (0, import_node_fs13.mkdirSync)(horusHome(), { recursive: true });
93951
+ const p = telemetryPath();
93952
+ const tmp = `${p}.${process.pid}.tmp`;
93953
+ (0, import_node_fs13.writeFileSync)(tmp, JSON.stringify(state, null, 2) + "\n", { mode: 384 });
93954
+ try {
93955
+ (0, import_node_fs13.renameSync)(tmp, p);
93956
+ } catch (err) {
93957
+ try {
93958
+ (0, import_node_fs13.rmSync)(tmp);
93959
+ } catch {
93960
+ }
93961
+ throw err;
93962
+ }
93963
+ try {
93964
+ (0, import_node_fs13.chmodSync)(p, 384);
93965
+ } catch {
93966
+ }
93967
+ }
93968
+ function loadOrInitTelemetryState() {
93969
+ const existing = readTelemetryState();
93970
+ if (existing) return existing;
93971
+ const created = defaultTelemetryState();
93972
+ writeTelemetryState(created);
93973
+ return created;
93974
+ }
93975
+ function updateTelemetryState(mutate) {
93976
+ const state = loadOrInitTelemetryState();
93977
+ mutate(state);
93978
+ state.updatedAt = nowIso();
93979
+ writeTelemetryState(state);
93980
+ return state;
93981
+ }
93982
+ function deleteTelemetryState() {
93983
+ const p = telemetryPath();
93984
+ if ((0, import_node_fs13.existsSync)(p)) (0, import_node_fs13.rmSync)(p);
93985
+ }
93986
+
93987
+ // ../../packages/cli/src/lib/telemetry/consent.ts
93988
+ var OFF_VALUES = /* @__PURE__ */ new Set(["0", "false", "off", "no", "disable", "disabled"]);
93989
+ var ON_VALUES = /* @__PURE__ */ new Set(["1", "true", "on", "yes", "enable", "enabled", "all"]);
93990
+ var CI_ENV_VARS = [
93991
+ "CI",
93992
+ "CONTINUOUS_INTEGRATION",
93993
+ "GITHUB_ACTIONS",
93994
+ "GITLAB_CI",
93995
+ "BUILDKITE",
93996
+ "CIRCLECI",
93997
+ "TRAVIS",
93998
+ "JENKINS_URL",
93999
+ "TEAMCITY_VERSION"
94000
+ ];
94001
+ function doNotTrack(env) {
94002
+ const v = env.DO_NOT_TRACK;
94003
+ if (v == null || v === "") return false;
94004
+ return !OFF_VALUES.has(v.trim().toLowerCase());
94005
+ }
94006
+ function parseEnvFlag(value) {
94007
+ if (value == null || value === "") return void 0;
94008
+ const v = value.trim().toLowerCase();
94009
+ if (OFF_VALUES.has(v)) return "off";
94010
+ if (ON_VALUES.has(v)) return "on";
94011
+ return void 0;
94012
+ }
94013
+ function isCI(env = process.env) {
94014
+ return CI_ENV_VARS.some((key) => {
94015
+ const v = env[key];
94016
+ return v != null && v !== "" && !OFF_VALUES.has(v.trim().toLowerCase());
94017
+ });
94018
+ }
94019
+ function off(source, reason) {
94020
+ return { tierA: false, tierB: false, forcedOff: source === "env", source, reason };
94021
+ }
94022
+ function resolveConsent(opts = {}) {
94023
+ const env = opts.env ?? process.env;
94024
+ if (doNotTrack(env)) return off("env", "DO_NOT_TRACK is set");
94025
+ const envFlag = parseEnvFlag(env.HORUS_TELEMETRY);
94026
+ if (envFlag === "off") return off("env", "HORUS_TELEMETRY disables telemetry");
94027
+ const state = opts.state !== void 0 ? opts.state : readTelemetryState();
94028
+ const storedTierA = state?.tierA.enabled ?? true;
94029
+ const storedTierB = state?.tierB.enabled ?? false;
94030
+ if (isCI(env) && envFlag !== "on") {
94031
+ return {
94032
+ tierA: false,
94033
+ tierB: false,
94034
+ forcedOff: false,
94035
+ source: "ci",
94036
+ reason: "CI environment detected \u2014 telemetry off by default"
94037
+ };
94038
+ }
94039
+ if (envFlag === "on") {
94040
+ return {
94041
+ tierA: true,
94042
+ tierB: storedTierB,
94043
+ forcedOff: false,
94044
+ source: "env",
94045
+ reason: "HORUS_TELEMETRY enables usage metadata"
94046
+ };
94047
+ }
94048
+ return {
94049
+ tierA: storedTierA,
94050
+ tierB: storedTierB,
94051
+ forcedOff: false,
94052
+ source: state ? "state" : "default",
94053
+ reason: state ? "from ~/.horus/telemetry.json" : "default settings (no saved preference)"
94054
+ };
94055
+ }
94056
+ function isContentSharingEnabled(env = process.env) {
94057
+ return resolveConsent({ env }).tierB;
94058
+ }
94059
+
94060
+ // ../../packages/cli/src/lib/telemetry/spool.ts
94061
+ init_cjs_shims();
94062
+ var import_node_fs14 = require("fs");
94063
+ var import_node_path15 = require("path");
94064
+ var SPOOL_FILE = "events.jsonl";
94065
+ var MAX_SPOOL_BYTES = 256 * 1024;
94066
+ function spoolFilePath() {
94067
+ return (0, import_node_path15.join)(telemetryQueueDir(), SPOOL_FILE);
94068
+ }
94069
+ function spoolEvent(event) {
94070
+ try {
94071
+ (0, import_node_fs14.mkdirSync)(telemetryQueueDir(), { recursive: true });
94072
+ const file = spoolFilePath();
94073
+ let size2 = 0;
94074
+ try {
94075
+ size2 = (0, import_node_fs14.statSync)(file).size;
94076
+ } catch {
94077
+ }
94078
+ if (size2 >= MAX_SPOOL_BYTES) return;
94079
+ (0, import_node_fs14.appendFileSync)(file, JSON.stringify(event) + "\n", { mode: 384 });
94080
+ } catch {
94081
+ }
94082
+ }
94083
+ function readSpooledEvents() {
94084
+ try {
94085
+ const file = spoolFilePath();
94086
+ if (!(0, import_node_fs14.existsSync)(file)) return [];
94087
+ return (0, import_node_fs14.readFileSync)(file, "utf8").split("\n").filter((line2) => line2.trim().length > 0).map((line2) => {
94088
+ try {
94089
+ return JSON.parse(line2);
94090
+ } catch {
94091
+ return null;
94092
+ }
94093
+ }).filter((e) => e !== null);
94094
+ } catch {
94095
+ return [];
94096
+ }
94097
+ }
94098
+ function clearSpool() {
94099
+ try {
94100
+ const file = spoolFilePath();
94101
+ if ((0, import_node_fs14.existsSync)(file)) (0, import_node_fs14.rmSync)(file);
94102
+ } catch {
94103
+ }
94104
+ }
94105
+
94106
+ // ../../packages/cli/src/lib/telemetry/events.ts
94107
+ init_cjs_shims();
94108
+ var TELEMETRY_EVENT_SCHEMA_VERSION = 1;
94109
+ var TIER_B_EVENT_TYPES = /* @__PURE__ */ new Set(["investigation.content"]);
94110
+ function tierForEventType(type) {
94111
+ return TIER_B_EVENT_TYPES.has(type) ? "B" : "A";
94112
+ }
94113
+ function extractFlagNames(argv) {
94114
+ const names = /* @__PURE__ */ new Set();
94115
+ for (const token of argv.slice(2)) {
94116
+ if (token.startsWith("--") && token.length > 2) {
94117
+ const name = token.slice(2).split("=")[0];
94118
+ if (name) names.add(name);
94119
+ }
94120
+ }
94121
+ return [...names];
94122
+ }
94123
+
94124
+ // ../../packages/cli/src/lib/telemetry/client.ts
94125
+ var sessionId = null;
94126
+ function getSessionId() {
94127
+ if (!sessionId) sessionId = (0, import_node_crypto6.randomUUID)();
94128
+ return sessionId;
94129
+ }
94130
+ function track(input) {
94131
+ try {
94132
+ const state = readTelemetryState();
94133
+ if (!state) return;
94134
+ const tier = tierForEventType(input.type);
94135
+ const decision = resolveConsent({ state });
94136
+ if (tier === "A" ? !decision.tierA : !decision.tierB) return;
94137
+ const event = {
94138
+ schemaVersion: TELEMETRY_EVENT_SCHEMA_VERSION,
94139
+ tier,
94140
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
94141
+ installId: state.installId,
94142
+ sessionId: getSessionId(),
94143
+ cliVersion: HORUS_VERSION,
94144
+ os: (0, import_node_os3.platform)(),
94145
+ arch: (0, import_node_os3.arch)(),
94146
+ ...input
94147
+ };
94148
+ spoolEvent(event);
94149
+ } catch {
94150
+ }
94151
+ }
94152
+
94153
+ // ../../packages/cli/src/lib/telemetry/feedback.ts
94154
+ init_cjs_shims();
94155
+ var import_promises = require("readline/promises");
94156
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
94157
+ var MANUAL_MINUTES = {
94158
+ "1": 5,
94159
+ // < 5 min
94160
+ "2": 30,
94161
+ // ~30 min
94162
+ "3": 120,
94163
+ // ~2 h
94164
+ "4": 480,
94165
+ // half-day+
94166
+ "5": null
94167
+ // unsure
94168
+ };
94169
+ function parseResolved(raw) {
94170
+ const v = raw.trim().toLowerCase();
94171
+ if (v.startsWith("y")) return "yes";
94172
+ if (v.startsWith("p")) return "partly";
94173
+ if (v.startsWith("n")) return "no";
94174
+ return null;
94175
+ }
94176
+ function parseManualEstimate(raw) {
94177
+ return MANUAL_MINUTES[raw.trim()] ?? null;
94178
+ }
94179
+ function submitFeedback(input) {
94180
+ track({ type: "feedback.submitted", ...input });
94181
+ }
94182
+ async function runFeedbackPrompt(investigationId, horusSeconds) {
94183
+ const rl = (0, import_promises.createInterface)({ input: process.stdin, output: process.stdout });
94184
+ try {
94185
+ console.log(import_picocolors8.default.dim("\nQuick feedback to improve Horus (press Enter to skip):"));
94186
+ const a1 = await rl.question(
94187
+ import_picocolors8.default.bold(" Did this point you at the cause? ") + import_picocolors8.default.dim("[y / partly / n] ")
94188
+ );
94189
+ if (a1.trim() === "") return false;
94190
+ const a2 = await rl.question(
94191
+ import_picocolors8.default.bold(" Manually, finding this would have taken? ") + import_picocolors8.default.dim("[1:<5m 2:~30m 3:~2h 4:half-day+ 5:unsure] ")
94192
+ );
94193
+ submitFeedback({
94194
+ investigationId,
94195
+ resolved: parseResolved(a1),
94196
+ manualEstimateMinutes: parseManualEstimate(a2),
94197
+ horusSeconds,
94198
+ source: "prompt"
94199
+ });
94200
+ console.log(import_picocolors8.default.dim(" Thanks \u2014 recorded."));
94201
+ return true;
94202
+ } finally {
94203
+ rl.close();
94204
+ }
94205
+ }
94206
+ async function maybePromptFeedback(opts) {
94207
+ try {
94208
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return;
94209
+ if (!resolveConsent().tierA) return;
94210
+ const rate = opts.sampleRate ?? 0.25;
94211
+ const rand = (opts.random ?? Math.random)();
94212
+ if (rand > rate) return;
94213
+ await runFeedbackPrompt(opts.investigationId, opts.horusSeconds ?? null);
94214
+ } catch {
94215
+ }
94216
+ }
94217
+
93823
94218
  // ../../packages/cli/src/lib/ensure-host.ts
93824
94219
  init_cjs_shims();
93825
94220
  function parseHostPort(hostUrl) {
@@ -93912,37 +94307,37 @@ function renderStoredAIJudgment(judgment, write = (l) => console.log(l)) {
93912
94307
  const sep = "\u2500".repeat(60);
93913
94308
  write(`
93914
94309
  ${sep}`);
93915
- write(import_picocolors8.default.bold("AI Judgment") + import_picocolors8.default.dim(` (confidence: ${(judgment.confidence * 100).toFixed(0)}%, provider: ${judgment.provider})`));
94310
+ write(import_picocolors9.default.bold("AI Judgment") + import_picocolors9.default.dim(` (confidence: ${(judgment.confidence * 100).toFixed(0)}%, provider: ${judgment.provider})`));
93916
94311
  write(sep);
93917
94312
  if (judgment.hypothesisJudgments && judgment.hypothesisJudgments.length > 0) {
93918
- write(import_picocolors8.default.bold("Hypothesis verdicts:"));
94313
+ write(import_picocolors9.default.bold("Hypothesis verdicts:"));
93919
94314
  for (const j of judgment.hypothesisJudgments) {
93920
- const verdictColor = j.verdict === "supported" ? import_picocolors8.default.green : j.verdict === "weakened" ? import_picocolors8.default.yellow : j.verdict === "eliminated" ? import_picocolors8.default.red : import_picocolors8.default.dim;
93921
- write(` ${verdictColor(`[${j.verdict}]`)} ${j.category}` + import_picocolors8.default.dim(` (${(j.confidence * 100).toFixed(0)}%)`));
93922
- write(import_picocolors8.default.dim(` ${j.rationale}`));
94315
+ const verdictColor = j.verdict === "supported" ? import_picocolors9.default.green : j.verdict === "weakened" ? import_picocolors9.default.yellow : j.verdict === "eliminated" ? import_picocolors9.default.red : import_picocolors9.default.dim;
94316
+ write(` ${verdictColor(`[${j.verdict}]`)} ${j.category}` + import_picocolors9.default.dim(` (${(j.confidence * 100).toFixed(0)}%)`));
94317
+ write(import_picocolors9.default.dim(` ${j.rationale}`));
93923
94318
  }
93924
94319
  write("");
93925
94320
  }
93926
94321
  if (judgment.rootCauseAssessment) {
93927
94322
  const rca = judgment.rootCauseAssessment;
93928
- const uncertaintyColor = rca.uncertainty === "low" ? import_picocolors8.default.green : rca.uncertainty === "medium" ? import_picocolors8.default.yellow : import_picocolors8.default.red;
93929
- write(import_picocolors8.default.bold("Root cause assessment:") + import_picocolors8.default.dim(` uncertainty: ${uncertaintyColor(rca.uncertainty)}`));
94323
+ const uncertaintyColor = rca.uncertainty === "low" ? import_picocolors9.default.green : rca.uncertainty === "medium" ? import_picocolors9.default.yellow : import_picocolors9.default.red;
94324
+ write(import_picocolors9.default.bold("Root cause assessment:") + import_picocolors9.default.dim(` uncertainty: ${uncertaintyColor(rca.uncertainty)}`));
93930
94325
  write(rca.summary);
93931
94326
  if (rca.citedEvidenceIds.length > 0) {
93932
- write(import_picocolors8.default.dim(` Cited: ${rca.citedEvidenceIds.join(", ")}`));
94327
+ write(import_picocolors9.default.dim(` Cited: ${rca.citedEvidenceIds.join(", ")}`));
93933
94328
  }
93934
94329
  write("");
93935
94330
  }
93936
- write(import_picocolors8.default.bold("What:") + " " + judgment.what);
93937
- write(import_picocolors8.default.bold("Why:") + " " + judgment.why);
94331
+ write(import_picocolors9.default.bold("What:") + " " + judgment.what);
94332
+ write(import_picocolors9.default.bold("Why:") + " " + judgment.why);
93938
94333
  if (judgment.whereNext.length > 0) {
93939
- write(import_picocolors8.default.bold("Next steps:"));
94334
+ write(import_picocolors9.default.bold("Next steps:"));
93940
94335
  for (const step of judgment.whereNext) {
93941
94336
  write(` \u2022 ${step}`);
93942
94337
  }
93943
94338
  }
93944
94339
  if (judgment.citations.length > 0) {
93945
- write(import_picocolors8.default.dim(`
94340
+ write(import_picocolors9.default.dim(`
93946
94341
  Cited evidence: ${judgment.citations.map((c) => c.evidenceId).join(", ")}`));
93947
94342
  }
93948
94343
  }
@@ -93960,6 +94355,7 @@ function classifyAIFailure(firstError) {
93960
94355
  return firstError;
93961
94356
  }
93962
94357
  async function runInvestigate(hint, opts) {
94358
+ const startedAtMs = Date.now();
93963
94359
  try {
93964
94360
  const config2 = await loadConfig(opts.config, { name: opts.name });
93965
94361
  const projectName = opts.project ?? opts.repo;
@@ -93967,7 +94363,7 @@ async function runInvestigate(hint, opts) {
93967
94363
  try {
93968
94364
  renv = resolveEnvironment(config2, { project: projectName, env: opts.env });
93969
94365
  } catch (err) {
93970
- console.error(import_picocolors8.default.red(err.message));
94366
+ console.error(import_picocolors9.default.red(err.message));
93971
94367
  return 1;
93972
94368
  }
93973
94369
  const repoRoot = repoRootOrCwd(renv.path);
@@ -93976,16 +94372,15 @@ async function runInvestigate(hint, opts) {
93976
94372
  const cloudSession = cloudActive ? authedClient() : null;
93977
94373
  if (cloudActive && !cloudSession) {
93978
94374
  console.error(
93979
- import_picocolors8.default.red(
93980
- `This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors8.default.bold("horus login")} first.`
94375
+ import_picocolors9.default.yellow(
94376
+ `This repo is linked to Horus Cloud but you are not logged in \u2014 running locally only. Run ${import_picocolors9.default.bold("horus login")} to also sync results to the cloud.`
93981
94377
  )
93982
94378
  );
93983
- return 1;
93984
94379
  }
93985
94380
  const code = codeForEnv(renv);
93986
94381
  if (!code) {
93987
94382
  console.error(
93988
- import_picocolors8.default.red(
94383
+ import_picocolors9.default.red(
93989
94384
  `No source-intelligence connector configured for project "${renv.project}" / env "${renv.env}".`
93990
94385
  )
93991
94386
  );
@@ -93995,21 +94390,21 @@ async function runInvestigate(hint, opts) {
93995
94390
  let health = await code.health();
93996
94391
  if (!health.ok && sourceUrl) {
93997
94392
  console.error(
93998
- import_picocolors8.default.yellow(`Source-intelligence host unreachable (${sourceUrl}) \u2014 attempting to start it\u2026`)
94393
+ import_picocolors9.default.yellow(`Source-intelligence host unreachable (${sourceUrl}) \u2014 attempting to start it\u2026`)
93999
94394
  );
94000
94395
  const healed = await ensureSourceHost(renv.path, sourceUrl);
94001
94396
  if (healed.ok) {
94002
- console.error(import_picocolors8.default.green(`Source-intelligence host is up at ${healed.hostUrl}.`));
94397
+ console.error(import_picocolors9.default.green(`Source-intelligence host is up at ${healed.hostUrl}.`));
94003
94398
  health = await code.health();
94004
94399
  } else {
94005
- console.error(import_picocolors8.default.dim(` ${ensureHostReasonHint(healed.reason)}`));
94400
+ console.error(import_picocolors9.default.dim(` ${ensureHostReasonHint(healed.reason)}`));
94006
94401
  }
94007
94402
  }
94008
94403
  const runtimeOnly = !health.ok;
94009
94404
  if (runtimeOnly) {
94010
94405
  console.error(
94011
- import_picocolors8.default.yellow(
94012
- `Proceeding in runtime-only mode \u2014 no source intelligence. Run ${import_picocolors8.default.bold("horus index")} for a full (code-aware) investigation.`
94406
+ import_picocolors9.default.yellow(
94407
+ `Proceeding in runtime-only mode \u2014 no source intelligence. Run ${import_picocolors9.default.bold("horus index")} for a full (code-aware) investigation.`
94013
94408
  )
94014
94409
  );
94015
94410
  }
@@ -94054,7 +94449,7 @@ async function runInvestigate(hint, opts) {
94054
94449
  model,
94055
94450
  ...ai.anthropicApiKey !== void 0 ? { apiKey: ai.anthropicApiKey } : {}
94056
94451
  });
94057
- console.log(import_picocolors8.default.dim(`[ai] model: ${model}`));
94452
+ console.log(import_picocolors9.default.dim(`[ai] model: ${model}`));
94058
94453
  const narrativeInput = buildNarrativeInput(report);
94059
94454
  const { output, fromProvider, degraded, validationErrors } = await renderNarrative(
94060
94455
  narrativeInput,
@@ -94070,37 +94465,78 @@ async function runInvestigate(hint, opts) {
94070
94465
  renderStoredAIJudgment(stored);
94071
94466
  } else if (degraded) {
94072
94467
  if (validationErrors && validationErrors.length > 0) {
94073
- console.error(import_picocolors8.default.yellow(`[ai] structured validation incomplete \u2014 showing raw AI narrative`));
94468
+ console.error(import_picocolors9.default.yellow(`[ai] structured validation incomplete \u2014 showing raw AI narrative`));
94074
94469
  }
94075
94470
  console.log("");
94076
- console.log(import_picocolors8.default.dim("\u2500".repeat(60)));
94077
- console.log(import_picocolors8.default.bold("AI narrative ") + import_picocolors8.default.yellow("(unstructured \u2014 not validated)"));
94471
+ console.log(import_picocolors9.default.dim("\u2500".repeat(60)));
94472
+ console.log(import_picocolors9.default.bold("AI narrative ") + import_picocolors9.default.yellow("(unstructured \u2014 not validated)"));
94078
94473
  if (output.what?.trim()) {
94079
- console.log(import_picocolors8.default.bold("\nWhat:"), output.what.trim());
94474
+ console.log(import_picocolors9.default.bold("\nWhat:"), output.what.trim());
94080
94475
  }
94081
94476
  if (output.why?.trim()) {
94082
- console.log(import_picocolors8.default.bold("\nWhy:"), output.why.trim());
94477
+ console.log(import_picocolors9.default.bold("\nWhy:"), output.why.trim());
94083
94478
  }
94084
94479
  if (output.whereNext.length > 0) {
94085
- console.log(import_picocolors8.default.bold("\nNext:"));
94480
+ console.log(import_picocolors9.default.bold("\nNext:"));
94086
94481
  for (const a of output.whereNext) console.log(` - ${a}`);
94087
94482
  }
94088
94483
  } else {
94089
94484
  const reason = classifyAIFailure(validationErrors?.[0]);
94090
- console.error(import_picocolors8.default.yellow(`[ai] fallback to deterministic \u2014 ${reason}`));
94485
+ console.error(import_picocolors9.default.yellow(`[ai] fallback to deterministic \u2014 ${reason}`));
94486
+ }
94487
+ }
94488
+ track({
94489
+ type: "investigation.completed",
94490
+ investigationId: report.id,
94491
+ confidence: typeof report.confidence === "number" ? report.confidence : null,
94492
+ evidenceCount: report.evidence?.length ?? 0,
94493
+ findingCount: report.findings?.length ?? 0,
94494
+ suspectedCauseCount: report.suspectedCauses?.length ?? 0,
94495
+ degraded: Boolean(report.degraded),
94496
+ gapCount: report.gapAnalysis?.gaps?.length ?? 0,
94497
+ hasAi: Boolean(report.aiJudgment)
94498
+ });
94499
+ if (isContentSharingEnabled()) {
94500
+ const hint2 = redactOrDrop(report.input.hint ?? "");
94501
+ if (!hint2.dropped) {
94502
+ track({
94503
+ type: "investigation.content",
94504
+ investigationId: report.id,
94505
+ hint: hint2.value ?? "",
94506
+ summary: redactContent(report.summary ?? ""),
94507
+ findingTitles: (report.findings ?? []).slice(0, 20).map((f) => redactContent(f.title ?? "")),
94508
+ suspectedCauseTitles: (report.suspectedCauses ?? []).slice(0, 20).map((c) => redactContent(c.title ?? "")),
94509
+ confidence: typeof report.confidence === "number" ? report.confidence : null
94510
+ });
94091
94511
  }
94092
94512
  }
94093
94513
  if (cloudActive && cloudSession && cloudCfg) {
94094
94514
  try {
94095
94515
  const refs = await uploadInvestigationToCloud(cloudSession.client, cloudCfg, report);
94096
- console.log(import_picocolors8.default.dim(`[cloud] investigation saved: ${refs.investigationId}`));
94516
+ console.log(import_picocolors9.default.dim(`[cloud] investigation saved: ${refs.investigationId}`));
94097
94517
  } catch (err) {
94098
94518
  return reportCloudError(err);
94099
94519
  }
94100
94520
  }
94101
94521
  if (format !== "json") {
94102
- console.log(import_picocolors8.default.dim(`
94522
+ if (report.persisted === false) {
94523
+ console.error(
94524
+ import_picocolors9.default.yellow(`
94525
+ \u26A0 Results were not saved \u2014 the investigation database was unreachable.`)
94526
+ );
94527
+ console.error(
94528
+ import_picocolors9.default.dim(
94529
+ ` This report is display-only; \`horus ask\` won't find it. Start the database and re-run to persist.`
94530
+ )
94531
+ );
94532
+ } else {
94533
+ console.log(import_picocolors9.default.dim(`
94103
94534
  Ask a follow-up: horus ask ${report.id} "<question>"`));
94535
+ }
94536
+ await maybePromptFeedback({
94537
+ investigationId: report.id,
94538
+ horusSeconds: (Date.now() - startedAtMs) / 1e3
94539
+ });
94104
94540
  }
94105
94541
  } finally {
94106
94542
  await sql2.end();
@@ -94109,14 +94545,16 @@ Ask a follow-up: horus ask ${report.id} "<question>"`));
94109
94545
  }
94110
94546
  return 0;
94111
94547
  } catch (err) {
94112
- console.error(import_picocolors8.default.red(err.message));
94548
+ const msg = err?.message || String(err);
94549
+ console.error(import_picocolors9.default.red(msg.trim() ? msg : "Investigation failed (unknown error)."));
94550
+ if (process.env.HORUS_DEBUG) console.error(import_picocolors9.default.dim(err?.stack ?? String(err)));
94113
94551
  return 1;
94114
94552
  }
94115
94553
  }
94116
94554
 
94117
94555
  // ../../packages/cli/src/commands/changes.ts
94118
94556
  init_cjs_shims();
94119
- var import_picocolors9 = __toESM(require_picocolors(), 1);
94557
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
94120
94558
  var CHANGES_AI_CONTRACT = `Provide a clearly separated AI change-impact review with:
94121
94559
 
94122
94560
  Highest-risk changes
@@ -94141,7 +94579,7 @@ async function runChanges(base2, compare, opts) {
94141
94579
  const health = await code.health();
94142
94580
  if (!health.ok) {
94143
94581
  console.error(
94144
- import_picocolors9.default.red("Source-intelligence host unreachable \u2014 run: horus index")
94582
+ import_picocolors10.default.red("Source-intelligence host unreachable \u2014 run: horus index")
94145
94583
  );
94146
94584
  return 1;
94147
94585
  }
@@ -94163,20 +94601,20 @@ async function runChanges(base2, compare, opts) {
94163
94601
  });
94164
94602
  console.log("\n" + renderInterpretation(result));
94165
94603
  if (!result.ok) {
94166
- console.error(import_picocolors9.default.yellow(`[ai] ${result.warning}`));
94604
+ console.error(import_picocolors10.default.yellow(`[ai] ${result.warning}`));
94167
94605
  }
94168
94606
  }
94169
94607
  }
94170
94608
  return 0;
94171
94609
  } catch (err) {
94172
- console.error(import_picocolors9.default.red(err.message));
94610
+ console.error(import_picocolors10.default.red(err.message));
94173
94611
  return 1;
94174
94612
  }
94175
94613
  }
94176
94614
 
94177
94615
  // ../../packages/cli/src/commands/timeline.ts
94178
94616
  init_cjs_shims();
94179
- var import_picocolors10 = __toESM(require_picocolors(), 1);
94617
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
94180
94618
  var DEFAULT_SINCE = "7 days ago";
94181
94619
  function resolveTimelineWindow(opts) {
94182
94620
  const usingDefault = !opts.all && opts.since === void 0;
@@ -94206,7 +94644,7 @@ async function runTimeline(service, opts) {
94206
94644
  try {
94207
94645
  renv = resolveEnvironment(config2, { project: opts.repo });
94208
94646
  } catch (err) {
94209
- console.error(import_picocolors10.default.red(err.message));
94647
+ console.error(import_picocolors11.default.red(err.message));
94210
94648
  return 1;
94211
94649
  }
94212
94650
  const { code } = createConnectors(config2);
@@ -94221,9 +94659,9 @@ async function runTimeline(service, opts) {
94221
94659
  console.log(renderChangeTimeline(t));
94222
94660
  if (usingDefaultWindow) {
94223
94661
  console.log(
94224
- import_picocolors10.default.dim(
94662
+ import_picocolors11.default.dim(
94225
94663
  `
94226
- Showing the last 7 days (default). Widen with ${import_picocolors10.default.bold('--since "30 days ago"')}, pin a range with ${import_picocolors10.default.bold("--since <when> --until <when>")}, or see everything with ${import_picocolors10.default.bold("--all")}.`
94664
+ Showing the last 7 days (default). Widen with ${import_picocolors11.default.bold('--since "30 days ago"')}, pin a range with ${import_picocolors11.default.bold("--since <when> --until <when>")}, or see everything with ${import_picocolors11.default.bold("--all")}.`
94227
94665
  )
94228
94666
  );
94229
94667
  }
@@ -94240,20 +94678,20 @@ async function runTimeline(service, opts) {
94240
94678
  });
94241
94679
  console.log("\n" + renderInterpretation(result));
94242
94680
  if (!result.ok) {
94243
- console.error(import_picocolors10.default.yellow(`[ai] ${result.warning}`));
94681
+ console.error(import_picocolors11.default.yellow(`[ai] ${result.warning}`));
94244
94682
  }
94245
94683
  }
94246
94684
  }
94247
94685
  return 0;
94248
94686
  } catch (err) {
94249
- console.error(import_picocolors10.default.red(err.message));
94687
+ console.error(import_picocolors11.default.red(err.message));
94250
94688
  return 1;
94251
94689
  }
94252
94690
  }
94253
94691
 
94254
94692
  // ../../packages/cli/src/commands/what-changed.ts
94255
94693
  init_cjs_shims();
94256
- var import_picocolors11 = __toESM(require_picocolors(), 1);
94694
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
94257
94695
  var DEFAULT_SINCE2 = "7 days ago";
94258
94696
  var WHAT_CHANGED_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
94259
94697
 
@@ -94277,7 +94715,7 @@ async function runWhatChanged(service, opts) {
94277
94715
  try {
94278
94716
  renv = resolveEnvironment(config2, { project: opts.repo });
94279
94717
  } catch (err) {
94280
- console.error(import_picocolors11.default.red(err.message));
94718
+ console.error(import_picocolors12.default.red(err.message));
94281
94719
  return 1;
94282
94720
  }
94283
94721
  const { code } = createConnectors(config2);
@@ -94300,19 +94738,19 @@ async function runWhatChanged(service, opts) {
94300
94738
  });
94301
94739
  console.log("\n" + renderInterpretation(result));
94302
94740
  if (!result.ok) {
94303
- console.error(import_picocolors11.default.yellow(`[ai] ${result.warning}`));
94741
+ console.error(import_picocolors12.default.yellow(`[ai] ${result.warning}`));
94304
94742
  }
94305
94743
  }
94306
94744
  return 0;
94307
94745
  } catch (err) {
94308
- console.error(import_picocolors11.default.red(err.message));
94746
+ console.error(import_picocolors12.default.red(err.message));
94309
94747
  return 1;
94310
94748
  }
94311
94749
  }
94312
94750
 
94313
94751
  // ../../packages/cli/src/commands/architecture.ts
94314
94752
  init_cjs_shims();
94315
- var import_picocolors12 = __toESM(require_picocolors(), 1);
94753
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
94316
94754
  var ARCHITECTURE_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
94317
94755
 
94318
94756
  System summary
@@ -94341,7 +94779,7 @@ async function runArchitecture(opts) {
94341
94779
  const health = await code.health();
94342
94780
  if (!health.ok) {
94343
94781
  console.error(
94344
- import_picocolors12.default.red("Source-intelligence host unreachable \u2014 run: horus index")
94782
+ import_picocolors13.default.red("Source-intelligence host unreachable \u2014 run: horus index")
94345
94783
  );
94346
94784
  return 1;
94347
94785
  }
@@ -94369,7 +94807,7 @@ async function runArchitecture(opts) {
94369
94807
  });
94370
94808
  console.log("\n" + renderInterpretation(result));
94371
94809
  if (!result.ok) {
94372
- console.error(import_picocolors12.default.yellow(`[ai] ${result.warning}`));
94810
+ console.error(import_picocolors13.default.yellow(`[ai] ${result.warning}`));
94373
94811
  }
94374
94812
  }
94375
94813
  }
@@ -94378,14 +94816,14 @@ async function runArchitecture(opts) {
94378
94816
  }
94379
94817
  return 0;
94380
94818
  } catch (err) {
94381
- console.error(import_picocolors12.default.red(err.message));
94819
+ console.error(import_picocolors13.default.red(err.message));
94382
94820
  return 1;
94383
94821
  }
94384
94822
  }
94385
94823
 
94386
94824
  // ../../packages/cli/src/commands/blast-radius.ts
94387
94825
  init_cjs_shims();
94388
- var import_picocolors13 = __toESM(require_picocolors(), 1);
94826
+ var import_picocolors14 = __toESM(require_picocolors(), 1);
94389
94827
  var BLAST_RADIUS_AI_CONTRACT = `Provide a clearly separated AI interpretation section with:
94390
94828
 
94391
94829
  Evidence used
@@ -94412,7 +94850,7 @@ async function runBlastRadius(query, opts) {
94412
94850
  const { code } = createConnectors(config2);
94413
94851
  const health = await code.health();
94414
94852
  if (!health.ok) {
94415
- console.error(import_picocolors13.default.red("Source-intelligence host unreachable \u2014 run: horus index"));
94853
+ console.error(import_picocolors14.default.red("Source-intelligence host unreachable \u2014 run: horus index"));
94416
94854
  return 1;
94417
94855
  }
94418
94856
  let project;
@@ -94425,12 +94863,12 @@ async function runBlastRadius(query, opts) {
94425
94863
  const r = await analyzeBlastRadius(query, { code, db, project }, opts.depth ?? 3);
94426
94864
  if (!r) {
94427
94865
  console.log(`No symbol found for: ${query}`);
94428
- console.log(import_picocolors13.default.dim(` Tip: use an exact class or function name, e.g. "MyService"`));
94866
+ console.log(import_picocolors14.default.dim(` Tip: use an exact class or function name, e.g. "MyService"`));
94429
94867
  return 1;
94430
94868
  }
94431
94869
  if (r.seed.name.toLowerCase() !== query.toLowerCase()) {
94432
94870
  console.log(
94433
- import_picocolors13.default.yellow(` No exact match for "${query}"`) + import_picocolors13.default.dim(` \u2014 showing closest: "${r.seed.name}" (fuzzy match)`)
94871
+ import_picocolors14.default.yellow(` No exact match for "${query}"`) + import_picocolors14.default.dim(` \u2014 showing closest: "${r.seed.name}" (fuzzy match)`)
94434
94872
  );
94435
94873
  }
94436
94874
  if (opts.json) {
@@ -94450,7 +94888,7 @@ async function runBlastRadius(query, opts) {
94450
94888
  });
94451
94889
  console.log("\n" + renderInterpretation(result));
94452
94890
  if (!result.ok) {
94453
- console.error(import_picocolors13.default.yellow(`[ai] ${result.warning}`));
94891
+ console.error(import_picocolors14.default.yellow(`[ai] ${result.warning}`));
94454
94892
  }
94455
94893
  }
94456
94894
  }
@@ -94459,28 +94897,28 @@ async function runBlastRadius(query, opts) {
94459
94897
  }
94460
94898
  return 0;
94461
94899
  } catch (err) {
94462
- console.error(import_picocolors13.default.red(err.message));
94900
+ console.error(import_picocolors14.default.red(err.message));
94463
94901
  return 1;
94464
94902
  }
94465
94903
  }
94466
94904
 
94467
94905
  // ../../packages/cli/src/commands/repos.ts
94468
94906
  init_cjs_shims();
94469
- var import_picocolors14 = __toESM(require_picocolors(), 1);
94907
+ var import_picocolors15 = __toESM(require_picocolors(), 1);
94470
94908
  async function runRepos(opts) {
94471
94909
  const config2 = await loadConfig(opts.config);
94472
94910
  const providers = repoProviders(config2);
94473
94911
  if (providers.length === 0) {
94474
- console.log(import_picocolors14.default.dim("No repositories configured."));
94912
+ console.log(import_picocolors15.default.dim("No repositories configured."));
94475
94913
  return 0;
94476
94914
  }
94477
94915
  const health = await reposHealth(providers);
94478
94916
  for (const h of health) {
94479
- const dot = h.reachable ? import_picocolors14.default.green("\u25CF") : import_picocolors14.default.red("\u25CF");
94480
- const name = import_picocolors14.default.bold(h.repo.padEnd(20));
94481
- const hostUrl = import_picocolors14.default.cyan(h.hostUrl.padEnd(30));
94482
- const path = import_picocolors14.default.dim(h.path);
94483
- const detail = h.detail ? import_picocolors14.default.dim(`(${h.detail})`) : "";
94917
+ const dot = h.reachable ? import_picocolors15.default.green("\u25CF") : import_picocolors15.default.red("\u25CF");
94918
+ const name = import_picocolors15.default.bold(h.repo.padEnd(20));
94919
+ const hostUrl = import_picocolors15.default.cyan(h.hostUrl.padEnd(30));
94920
+ const path = import_picocolors15.default.dim(h.path);
94921
+ const detail = h.detail ? import_picocolors15.default.dim(`(${h.detail})`) : "";
94484
94922
  console.log(` ${dot} ${name} ${hostUrl} ${path} ${detail}`);
94485
94923
  }
94486
94924
  return 0;
@@ -94488,7 +94926,7 @@ async function runRepos(opts) {
94488
94926
 
94489
94927
  // ../../packages/cli/src/commands/search.ts
94490
94928
  init_cjs_shims();
94491
- var import_picocolors15 = __toESM(require_picocolors(), 1);
94929
+ var import_picocolors16 = __toESM(require_picocolors(), 1);
94492
94930
  async function runSearch(query, opts) {
94493
94931
  const config2 = await loadConfig(opts.config);
94494
94932
  const providers = repoProviders(config2);
@@ -94499,16 +94937,16 @@ async function runSearch(query, opts) {
94499
94937
  }
94500
94938
  let totalMatches = 0;
94501
94939
  for (const result of results) {
94502
- const statusDot = result.reachable ? import_picocolors15.default.green("\u25CF") : import_picocolors15.default.red("\u25CF");
94940
+ const statusDot = result.reachable ? import_picocolors16.default.green("\u25CF") : import_picocolors16.default.red("\u25CF");
94503
94941
  console.log("");
94504
- console.log(`${statusDot} ${import_picocolors15.default.bold("## " + result.repo)} ${import_picocolors15.default.dim(result.hostUrl)}`);
94942
+ console.log(`${statusDot} ${import_picocolors16.default.bold("## " + result.repo)} ${import_picocolors16.default.dim(result.hostUrl)}`);
94505
94943
  if (!result.reachable) {
94506
- console.log(import_picocolors15.default.dim(" (unreachable)"));
94944
+ console.log(import_picocolors16.default.dim(" (unreachable)"));
94507
94945
  } else if (result.symbols.length === 0) {
94508
- console.log(import_picocolors15.default.dim(" (no matches)"));
94946
+ console.log(import_picocolors16.default.dim(" (no matches)"));
94509
94947
  } else {
94510
94948
  for (const sym of result.symbols) {
94511
- console.log(` - ${import_picocolors15.default.bold(sym.name)} ${import_picocolors15.default.dim(sym.filePath)}`);
94949
+ console.log(` - ${import_picocolors16.default.bold(sym.name)} ${import_picocolors16.default.dim(sym.filePath)}`);
94512
94950
  }
94513
94951
  totalMatches += result.symbols.length;
94514
94952
  }
@@ -94516,7 +94954,7 @@ async function runSearch(query, opts) {
94516
94954
  console.log("");
94517
94955
  const reachableCount = results.filter((r) => r.reachable).length;
94518
94956
  console.log(
94519
- import_picocolors15.default.dim(
94957
+ import_picocolors16.default.dim(
94520
94958
  `${totalMatches} match(es) across ${reachableCount}/${results.length} reachable repo(s) for query: "${query}"`
94521
94959
  )
94522
94960
  );
@@ -94525,7 +94963,7 @@ async function runSearch(query, opts) {
94525
94963
 
94526
94964
  // ../../packages/cli/src/commands/investigations.ts
94527
94965
  init_cjs_shims();
94528
- var import_picocolors16 = __toESM(require_picocolors(), 1);
94966
+ var import_picocolors17 = __toESM(require_picocolors(), 1);
94529
94967
 
94530
94968
  // ../../packages/cli/src/lib/format.ts
94531
94969
  init_cjs_shims();
@@ -94568,8 +95006,8 @@ async function runInvestigations(opts) {
94568
95006
  const session = authedClient();
94569
95007
  if (!session) {
94570
95008
  console.error(
94571
- import_picocolors16.default.red(
94572
- `This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors16.default.bold("horus login")} first.`
95009
+ import_picocolors17.default.red(
95010
+ `This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors17.default.bold("horus login")} first.`
94573
95011
  )
94574
95012
  );
94575
95013
  return 1;
@@ -94610,18 +95048,18 @@ async function runInvestigations(opts) {
94610
95048
 
94611
95049
  // ../../packages/cli/src/commands/replay.ts
94612
95050
  init_cjs_shims();
94613
- var import_picocolors17 = __toESM(require_picocolors(), 1);
95051
+ var import_picocolors18 = __toESM(require_picocolors(), 1);
94614
95052
  async function runReplay(id, opts) {
94615
95053
  const { db, sql: sql2 } = createDb(await resolveDbUrl(opts.config));
94616
95054
  try {
94617
95055
  const row = await getInvestigation(db, id);
94618
95056
  if (!row) {
94619
- console.error(import_picocolors17.default.red("No investigation found: " + id));
95057
+ console.error(import_picocolors18.default.red("No investigation found: " + id));
94620
95058
  return 1;
94621
95059
  }
94622
95060
  if (!row.report) {
94623
95061
  console.error(
94624
- import_picocolors17.default.red(
95062
+ import_picocolors18.default.red(
94625
95063
  "Investigation " + id + " has no stored report (run a newer investigation)."
94626
95064
  )
94627
95065
  );
@@ -94634,7 +95072,7 @@ async function runReplay(id, opts) {
94634
95072
  if (opts.ai && fmt !== "json") {
94635
95073
  if (report.aiJudgment && !opts.refreshAi) {
94636
95074
  renderStoredAIJudgment(report.aiJudgment);
94637
- console.log(import_picocolors17.default.dim("[ai] Stored judgment replayed. Use --refresh-ai to regenerate."));
95075
+ console.log(import_picocolors18.default.dim("[ai] Stored judgment replayed. Use --refresh-ai to regenerate."));
94638
95076
  } else {
94639
95077
  const narrativeInput = buildNarrativeInput(report);
94640
95078
  const { provider } = await buildNarrativeProvider({
@@ -94647,17 +95085,17 @@ async function runReplay(id, opts) {
94647
95085
  );
94648
95086
  if (!fromProvider && degraded) {
94649
95087
  if (validationErrors?.length) {
94650
- console.error(import_picocolors17.default.yellow("[ai] structured validation incomplete \u2014 showing raw AI narrative"));
95088
+ console.error(import_picocolors18.default.yellow("[ai] structured validation incomplete \u2014 showing raw AI narrative"));
94651
95089
  }
94652
95090
  console.log("");
94653
- console.log(import_picocolors17.default.dim("\u2500".repeat(60)));
94654
- console.log(import_picocolors17.default.bold("AI narrative ") + import_picocolors17.default.yellow("(unstructured \u2014 not validated)"));
94655
- if (output.what?.trim()) console.log(import_picocolors17.default.bold("\nWhat:"), output.what.trim());
94656
- if (output.why?.trim()) console.log(import_picocolors17.default.bold("\nWhy:"), output.why.trim());
95091
+ console.log(import_picocolors18.default.dim("\u2500".repeat(60)));
95092
+ console.log(import_picocolors18.default.bold("AI narrative ") + import_picocolors18.default.yellow("(unstructured \u2014 not validated)"));
95093
+ if (output.what?.trim()) console.log(import_picocolors18.default.bold("\nWhat:"), output.what.trim());
95094
+ if (output.why?.trim()) console.log(import_picocolors18.default.bold("\nWhy:"), output.why.trim());
94657
95095
  } else if (!fromProvider) {
94658
- console.error(import_picocolors17.default.yellow("[ai] Provider unavailable \u2014 deterministic output shown above."));
95096
+ console.error(import_picocolors18.default.yellow("[ai] Provider unavailable \u2014 deterministic output shown above."));
94659
95097
  if (validationErrors?.length) {
94660
- console.error(import_picocolors17.default.dim(` ${validationErrors[0]}`));
95098
+ console.error(import_picocolors18.default.dim(` ${validationErrors[0]}`));
94661
95099
  }
94662
95100
  } else {
94663
95101
  const stored = narrativeOutputToStoredJudgment(output, "anthropic");
@@ -94678,7 +95116,7 @@ async function runReplay(id, opts) {
94678
95116
 
94679
95117
  // ../../packages/cli/src/commands/owner.ts
94680
95118
  init_cjs_shims();
94681
- var import_picocolors18 = __toESM(require_picocolors(), 1);
95119
+ var import_picocolors19 = __toESM(require_picocolors(), 1);
94682
95120
  async function runOwner(query, opts) {
94683
95121
  try {
94684
95122
  const config2 = await loadConfig(opts.config);
@@ -94686,7 +95124,7 @@ async function runOwner(query, opts) {
94686
95124
  try {
94687
95125
  renv = resolveEnvironment(config2, { project: opts.repo });
94688
95126
  } catch (err) {
94689
- console.error(import_picocolors18.default.red(err.message));
95127
+ console.error(import_picocolors19.default.red(err.message));
94690
95128
  return 1;
94691
95129
  }
94692
95130
  const code = codeForRepo(config2, renv.project);
@@ -94694,16 +95132,16 @@ async function runOwner(query, opts) {
94694
95132
  console.log(opts.json ? ownershipToJSON(o) : renderOwnership(o));
94695
95133
  return 0;
94696
95134
  } catch (err) {
94697
- console.error(import_picocolors18.default.red(err.message));
95135
+ console.error(import_picocolors19.default.red(err.message));
94698
95136
  return 1;
94699
95137
  }
94700
95138
  }
94701
95139
 
94702
95140
  // ../../packages/cli/src/commands/postmortem.ts
94703
95141
  init_cjs_shims();
94704
- var import_node_fs13 = require("fs");
94705
- var import_node_path14 = require("path");
94706
- var import_picocolors19 = __toESM(require_picocolors(), 1);
95142
+ var import_node_fs15 = require("fs");
95143
+ var import_node_path16 = require("path");
95144
+ var import_picocolors20 = __toESM(require_picocolors(), 1);
94707
95145
  async function runPostmortem(id, opts) {
94708
95146
  const log = opts.write ?? ((line2) => console.log(line2));
94709
95147
  let sql2;
@@ -94717,11 +95155,11 @@ async function runPostmortem(id, opts) {
94717
95155
  const { db } = conn;
94718
95156
  const row = await getInvestigation(db, id);
94719
95157
  if (!row) {
94720
- log(import_picocolors19.default.red("No investigation found: " + id));
95158
+ log(import_picocolors20.default.red("No investigation found: " + id));
94721
95159
  return 1;
94722
95160
  }
94723
95161
  if (!row.report) {
94724
- log(import_picocolors19.default.red("Investigation " + id + " has no stored report."));
95162
+ log(import_picocolors20.default.red("Investigation " + id + " has no stored report."));
94725
95163
  return 1;
94726
95164
  }
94727
95165
  report = migrateReport(row.report);
@@ -94813,25 +95251,25 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
94813
95251
  }
94814
95252
  }
94815
95253
  if (opts.output) {
94816
- const outputPath = (0, import_node_path14.resolve)(opts.output);
94817
- if ((0, import_node_fs13.existsSync)(outputPath) && !opts.force) {
94818
- log(import_picocolors19.default.red("\u2717") + " " + outputPath + " already exists");
94819
- log(import_picocolors19.default.dim(" pass --force to overwrite"));
95254
+ const outputPath = (0, import_node_path16.resolve)(opts.output);
95255
+ if ((0, import_node_fs15.existsSync)(outputPath) && !opts.force) {
95256
+ log(import_picocolors20.default.red("\u2717") + " " + outputPath + " already exists");
95257
+ log(import_picocolors20.default.dim(" pass --force to overwrite"));
94820
95258
  return 1;
94821
95259
  }
94822
95260
  try {
94823
- (0, import_node_fs13.writeFileSync)(outputPath, content, "utf8");
95261
+ (0, import_node_fs15.writeFileSync)(outputPath, content, "utf8");
94824
95262
  } catch (err) {
94825
- log(import_picocolors19.default.red("\u2717 Could not write " + outputPath + ": ") + err.message);
95263
+ log(import_picocolors20.default.red("\u2717 Could not write " + outputPath + ": ") + err.message);
94826
95264
  return 1;
94827
95265
  }
94828
- log(import_picocolors19.default.green("\u2713") + " Saved postmortem to " + outputPath);
95266
+ log(import_picocolors20.default.green("\u2713") + " Saved postmortem to " + outputPath);
94829
95267
  } else {
94830
95268
  log(content);
94831
95269
  }
94832
95270
  } catch (err) {
94833
95271
  const message = err instanceof Error ? err.message : String(err);
94834
- log(import_picocolors19.default.red("postmortem failed: " + message));
95272
+ log(import_picocolors20.default.red("postmortem failed: " + message));
94835
95273
  return 1;
94836
95274
  } finally {
94837
95275
  if (sql2) {
@@ -94843,7 +95281,7 @@ _AI summary unavailable: ${validationErrors?.[0] ?? "provider error"}_
94843
95281
 
94844
95282
  // ../../packages/cli/src/commands/score.ts
94845
95283
  init_cjs_shims();
94846
- var import_picocolors20 = __toESM(require_picocolors(), 1);
95284
+ var import_picocolors21 = __toESM(require_picocolors(), 1);
94847
95285
  var SCORE_AI_CONTRACT = `Provide a clearly separated AI score explanation with:
94848
95286
 
94849
95287
  Why this scored this way
@@ -94862,11 +95300,11 @@ async function runScore(id, opts) {
94862
95300
  try {
94863
95301
  const row = await getInvestigation(db, id);
94864
95302
  if (!row) {
94865
- console.error(import_picocolors20.default.red("No investigation found: " + id));
95303
+ console.error(import_picocolors21.default.red("No investigation found: " + id));
94866
95304
  return 1;
94867
95305
  }
94868
95306
  if (!row.report) {
94869
- console.error(import_picocolors20.default.red("Investigation " + id + " has no stored report."));
95307
+ console.error(import_picocolors21.default.red("Investigation " + id + " has no stored report."));
94870
95308
  return 1;
94871
95309
  }
94872
95310
  const s = scoreInvestigation(migrateReport(row.report));
@@ -94887,7 +95325,7 @@ async function runScore(id, opts) {
94887
95325
  });
94888
95326
  console.log("\n" + renderInterpretation(result));
94889
95327
  if (!result.ok) {
94890
- console.error(import_picocolors20.default.yellow(`[ai] ${result.warning}`));
95328
+ console.error(import_picocolors21.default.yellow(`[ai] ${result.warning}`));
94891
95329
  }
94892
95330
  }
94893
95331
  }
@@ -94927,7 +95365,7 @@ async function runScores(opts) {
94927
95365
 
94928
95366
  // ../../packages/cli/src/commands/ask.ts
94929
95367
  init_cjs_shims();
94930
- var import_picocolors21 = __toESM(require_picocolors(), 1);
95368
+ var import_picocolors22 = __toESM(require_picocolors(), 1);
94931
95369
  function renderAnswer(report, directive, opts) {
94932
95370
  const answer = answerQuestion(report, directive);
94933
95371
  if (answer) {
@@ -94939,13 +95377,13 @@ function renderAnswer(report, directive, opts) {
94939
95377
  if (!opts.json && report.aiJudgment) {
94940
95378
  const j = report.aiJudgment;
94941
95379
  console.log("");
94942
- console.log(import_picocolors21.default.dim("\u2500".repeat(60)));
94943
- console.log(import_picocolors21.default.dim(`Stored AI judgment (${j.provider}, ${j.generatedAt}):`));
95380
+ console.log(import_picocolors22.default.dim("\u2500".repeat(60)));
95381
+ console.log(import_picocolors22.default.dim(`Stored AI judgment (${j.provider}, ${j.generatedAt}):`));
94944
95382
  if (j.rootCauseAssessment) {
94945
- console.log(import_picocolors21.default.bold("Root cause (AI):"), j.rootCauseAssessment.summary);
94946
- console.log(import_picocolors21.default.dim(`Uncertainty: ${j.rootCauseAssessment.uncertainty}`));
95383
+ console.log(import_picocolors22.default.bold("Root cause (AI):"), j.rootCauseAssessment.summary);
95384
+ console.log(import_picocolors22.default.dim(`Uncertainty: ${j.rootCauseAssessment.uncertainty}`));
94947
95385
  } else {
94948
- console.log(import_picocolors21.default.bold("AI Why:"), j.why);
95386
+ console.log(import_picocolors22.default.bold("AI Why:"), j.why);
94949
95387
  }
94950
95388
  }
94951
95389
  return 0;
@@ -94974,8 +95412,8 @@ async function runAsk(id, directive, opts) {
94974
95412
  const session = authedClient();
94975
95413
  if (!session) {
94976
95414
  console.error(
94977
- import_picocolors21.default.red(
94978
- `This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors21.default.bold("horus login")} first.`
95415
+ import_picocolors22.default.red(
95416
+ `This repo is linked to Horus Cloud but you are not logged in. Run ${import_picocolors22.default.bold("horus login")} first.`
94979
95417
  )
94980
95418
  );
94981
95419
  return 1;
@@ -94984,15 +95422,15 @@ async function runAsk(id, directive, opts) {
94984
95422
  const report = await fetchInvestigationReportFromCloud(session.client, cloudCfg, id);
94985
95423
  if (report) return renderAnswer(report, directive, opts);
94986
95424
  console.error(
94987
- import_picocolors21.default.red(
94988
- `Investigation ${id} has no saved report locally or in Horus Cloud. Run ${import_picocolors21.default.bold("horus investigate")} first.`
95425
+ import_picocolors22.default.red(
95426
+ `Investigation ${id} has no saved report locally or in Horus Cloud. Run ${import_picocolors22.default.bold("horus investigate")} first.`
94989
95427
  )
94990
95428
  );
94991
95429
  return 1;
94992
95430
  } catch (err) {
94993
95431
  if (err instanceof CloudError && err.status === 404) {
94994
95432
  console.error(
94995
- import_picocolors21.default.red(
95433
+ import_picocolors22.default.red(
94996
95434
  `No investigation found for "${id}" locally or in Horus Cloud. Use the id printed at the top of \`horus investigate\` output.`
94997
95435
  )
94998
95436
  );
@@ -95006,21 +95444,21 @@ async function runAsk(id, directive, opts) {
95006
95444
  case "found":
95007
95445
  return renderAnswer(local.report, directive, opts);
95008
95446
  case "no-report":
95009
- console.error(import_picocolors21.default.red("Investigation " + id + " has no stored report."));
95447
+ console.error(import_picocolors22.default.red("Investigation " + id + " has no stored report."));
95010
95448
  return 1;
95011
95449
  case "error":
95012
- console.error(import_picocolors21.default.red(local.message));
95450
+ console.error(import_picocolors22.default.red(local.message));
95013
95451
  return 1;
95014
95452
  case "not-found":
95015
95453
  default:
95016
- console.error(import_picocolors21.default.red("No investigation found: " + id));
95454
+ console.error(import_picocolors22.default.red("No investigation found: " + id));
95017
95455
  return 1;
95018
95456
  }
95019
95457
  }
95020
95458
 
95021
95459
  // ../../packages/cli/src/commands/onboard.ts
95022
95460
  init_cjs_shims();
95023
- var import_picocolors22 = __toESM(require_picocolors(), 1);
95461
+ var import_picocolors23 = __toESM(require_picocolors(), 1);
95024
95462
  var ONBOARD_AI_CONTRACT = `Provide a clearly separated AI onboarding guide with:
95025
95463
 
95026
95464
  Start here
@@ -95048,7 +95486,7 @@ async function runOnboard(area, opts) {
95048
95486
  try {
95049
95487
  renv = resolveEnvironment(config2, { project: opts.repo });
95050
95488
  } catch (err) {
95051
- console.error(import_picocolors22.default.red(err.message));
95489
+ console.error(import_picocolors23.default.red(err.message));
95052
95490
  return 1;
95053
95491
  }
95054
95492
  const repo = { name: renv.project, path: renv.path };
@@ -95056,7 +95494,7 @@ async function runOnboard(area, opts) {
95056
95494
  const health = await code.health();
95057
95495
  if (!health.ok) {
95058
95496
  console.error(
95059
- import_picocolors22.default.red("Source-intelligence host unreachable \u2014 run: horus index")
95497
+ import_picocolors23.default.red("Source-intelligence host unreachable \u2014 run: horus index")
95060
95498
  );
95061
95499
  return 1;
95062
95500
  }
@@ -95083,7 +95521,7 @@ async function runOnboard(area, opts) {
95083
95521
  });
95084
95522
  console.log("\n" + renderInterpretation(result));
95085
95523
  if (!result.ok) {
95086
- console.error(import_picocolors22.default.yellow(`[ai] ${result.warning}`));
95524
+ console.error(import_picocolors23.default.yellow(`[ai] ${result.warning}`));
95087
95525
  }
95088
95526
  }
95089
95527
  }
@@ -95092,14 +95530,14 @@ async function runOnboard(area, opts) {
95092
95530
  }
95093
95531
  return 0;
95094
95532
  } catch (err) {
95095
- console.error(import_picocolors22.default.red(err.message));
95533
+ console.error(import_picocolors23.default.red(err.message));
95096
95534
  return 1;
95097
95535
  }
95098
95536
  }
95099
95537
 
95100
95538
  // ../../packages/cli/src/commands/simulate.ts
95101
95539
  init_cjs_shims();
95102
- var import_picocolors23 = __toESM(require_picocolors(), 1);
95540
+ var import_picocolors24 = __toESM(require_picocolors(), 1);
95103
95541
  async function runSimulate(scenarioId, opts) {
95104
95542
  try {
95105
95543
  if (scenarioId === void 0) {
@@ -95109,7 +95547,7 @@ async function runSimulate(scenarioId, opts) {
95109
95547
  const scenario = getScenario(scenarioId);
95110
95548
  if (scenario === null) {
95111
95549
  console.error(
95112
- import_picocolors23.default.red(
95550
+ import_picocolors24.default.red(
95113
95551
  `Unknown scenario: "${scenarioId}". Run horus simulate to list available scenarios.`
95114
95552
  )
95115
95553
  );
@@ -95120,14 +95558,14 @@ async function runSimulate(scenarioId, opts) {
95120
95558
  try {
95121
95559
  renv = resolveEnvironment(config2, { project: opts.repo });
95122
95560
  } catch (err) {
95123
- console.error(import_picocolors23.default.red(err.message));
95561
+ console.error(import_picocolors24.default.red(err.message));
95124
95562
  return 1;
95125
95563
  }
95126
95564
  const code = codeForRepo(config2, renv.project);
95127
95565
  const health = await code.health();
95128
95566
  if (!health.ok) {
95129
95567
  console.error(
95130
- import_picocolors23.default.red("Source-intelligence host unreachable \u2014 run: horus index")
95568
+ import_picocolors24.default.red("Source-intelligence host unreachable \u2014 run: horus index")
95131
95569
  );
95132
95570
  return 1;
95133
95571
  }
@@ -95144,14 +95582,14 @@ async function runSimulate(scenarioId, opts) {
95144
95582
  }
95145
95583
  return 0;
95146
95584
  } catch (err) {
95147
- console.error(import_picocolors23.default.red(err.message));
95585
+ console.error(import_picocolors24.default.red(err.message));
95148
95586
  return 1;
95149
95587
  }
95150
95588
  }
95151
95589
 
95152
95590
  // ../../packages/cli/src/commands/logs.ts
95153
95591
  init_cjs_shims();
95154
- var import_picocolors24 = __toESM(require_picocolors(), 1);
95592
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
95155
95593
  var LOGS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
95156
95594
 
95157
95595
  Evidence used
@@ -95184,9 +95622,9 @@ function resolveRawLevel(opts) {
95184
95622
  return opts.level ?? "error";
95185
95623
  }
95186
95624
  function levelColor(level, text2) {
95187
- if (level === "error" || level === "fatal") return import_picocolors24.default.red(text2);
95188
- if (level === "warn") return import_picocolors24.default.yellow(text2);
95189
- return import_picocolors24.default.dim(text2);
95625
+ if (level === "error" || level === "fatal") return import_picocolors25.default.red(text2);
95626
+ if (level === "warn") return import_picocolors25.default.yellow(text2);
95627
+ return import_picocolors25.default.dim(text2);
95190
95628
  }
95191
95629
  function matchServiceLabel(requested, candidates) {
95192
95630
  if (candidates.includes(requested)) return { kind: "unique", matched: requested };
@@ -95203,13 +95641,13 @@ async function runLogs(service, opts) {
95203
95641
  try {
95204
95642
  renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
95205
95643
  } catch (err) {
95206
- console.error(import_picocolors24.default.red(err.message));
95644
+ console.error(import_picocolors25.default.red(err.message));
95207
95645
  return 1;
95208
95646
  }
95209
95647
  const logs = logsForEnv(renv);
95210
95648
  if (logs === null) {
95211
95649
  console.error(
95212
- import_picocolors24.default.red(
95650
+ import_picocolors25.default.red(
95213
95651
  `No Elasticsearch connector configured for ${renv.project}/${renv.env} (set ES_URL / ES_USERNAME / ES_PASSWORD)`
95214
95652
  )
95215
95653
  );
@@ -95217,7 +95655,7 @@ async function runLogs(service, opts) {
95217
95655
  }
95218
95656
  const health = await logs.health();
95219
95657
  if (!health.ok) {
95220
- console.error(import_picocolors24.default.red(`Elasticsearch unreachable: ${health.detail}`));
95658
+ console.error(import_picocolors25.default.red(`Elasticsearch unreachable: ${health.detail}`));
95221
95659
  return 1;
95222
95660
  }
95223
95661
  const resolvedService = service ?? renv.connectors.elasticsearch?.serviceName;
@@ -95230,21 +95668,21 @@ async function runLogs(service, opts) {
95230
95668
  requiresEventCode: aggregatesEventCode
95231
95669
  });
95232
95670
  for (const w of compat.issues.filter((i) => i.severity === "warning")) {
95233
- console.warn(import_picocolors24.default.yellow(`[warn] ${w.field}: ${w.message}`));
95671
+ console.warn(import_picocolors25.default.yellow(`[warn] ${w.field}: ${w.message}`));
95234
95672
  }
95235
95673
  const errors2 = compat.issues.filter((i) => i.severity === "error");
95236
95674
  if (errors2.length > 0) {
95237
- console.error(import_picocolors24.default.red("Elasticsearch field mapping is incompatible with the index:"));
95675
+ console.error(import_picocolors25.default.red("Elasticsearch field mapping is incompatible with the index:"));
95238
95676
  for (const e of errors2) {
95239
- console.error(import_picocolors24.default.red(` ${e.field}: ${e.message}`));
95677
+ console.error(import_picocolors25.default.red(` ${e.field}: ${e.message}`));
95240
95678
  }
95241
95679
  console.error(
95242
- import_picocolors24.default.dim("Fix fields.* overrides in your connector config or choose the correct preset.")
95680
+ import_picocolors25.default.dim("Fix fields.* overrides in your connector config or choose the correct preset.")
95243
95681
  );
95244
95682
  return 1;
95245
95683
  }
95246
95684
  } catch {
95247
- console.warn(import_picocolors24.default.dim("[warn] Elasticsearch compatibility check unavailable \u2014 proceeding."));
95685
+ console.warn(import_picocolors25.default.dim("[warn] Elasticsearch compatibility check unavailable \u2014 proceeding."));
95248
95686
  }
95249
95687
  const from = sinceToIso(opts.since) ?? new Date(Date.now() - 7 * 864e5).toISOString();
95250
95688
  const fromDisplay = from.slice(0, 16).replace("T", " ");
@@ -95295,7 +95733,7 @@ async function runLogs(service, opts) {
95295
95733
  return 0;
95296
95734
  }
95297
95735
  if (records.length === 0) {
95298
- console.log(import_picocolors24.default.dim("No logs matched."));
95736
+ console.log(import_picocolors25.default.dim("No logs matched."));
95299
95737
  return 0;
95300
95738
  }
95301
95739
  for (const r of records) {
@@ -95305,7 +95743,7 @@ async function runLogs(service, opts) {
95305
95743
  const line2 = `${ts} ${lvl} ${comp} ${r.message.slice(0, 120)}`;
95306
95744
  console.log(levelColor(r.level, line2));
95307
95745
  for (const { key, value } of extractContextFields(r)) {
95308
- console.log(import_picocolors24.default.dim(` ${key}: ${value.slice(0, 200)}`));
95746
+ console.log(import_picocolors25.default.dim(` ${key}: ${value.slice(0, 200)}`));
95309
95747
  }
95310
95748
  }
95311
95749
  return 0;
@@ -95344,22 +95782,22 @@ async function runLogs(service, opts) {
95344
95782
  return 0;
95345
95783
  }
95346
95784
  console.log(
95347
- import_picocolors24.default.bold(`Top ${field}`) + import_picocolors24.default.dim(
95785
+ import_picocolors25.default.bold(`Top ${field}`) + import_picocolors25.default.dim(
95348
95786
  ` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "")
95349
95787
  )
95350
95788
  );
95351
- console.log(import_picocolors24.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
95789
+ console.log(import_picocolors25.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
95352
95790
  console.log("");
95353
95791
  if (buckets.length === 0) {
95354
- console.log(import_picocolors24.default.dim(` No error logs with field "${field}" in the window.`));
95355
- console.log(import_picocolors24.default.dim(` Tip: ensure "${field}" is an aggregatable (keyword) field.`));
95792
+ console.log(import_picocolors25.default.dim(` No error logs with field "${field}" in the window.`));
95793
+ console.log(import_picocolors25.default.dim(` Tip: ensure "${field}" is an aggregatable (keyword) field.`));
95356
95794
  return 0;
95357
95795
  }
95358
95796
  for (const b2 of buckets) {
95359
- console.log(` ${import_picocolors24.default.red(String(b2.count).padStart(6))} ${b2.key}`);
95797
+ console.log(` ${import_picocolors25.default.red(String(b2.count).padStart(6))} ${b2.key}`);
95360
95798
  }
95361
95799
  console.log("");
95362
- console.log(import_picocolors24.default.dim(` ${buckets.length} distinct ${field} value(s) shown (top by error count).`));
95800
+ console.log(import_picocolors25.default.dim(` ${buckets.length} distinct ${field} value(s) shown (top by error count).`));
95363
95801
  return 0;
95364
95802
  }
95365
95803
  let analysis = await logs.analyzeErrors({
@@ -95426,46 +95864,46 @@ async function runLogs(service, opts) {
95426
95864
  return 0;
95427
95865
  }
95428
95866
  console.log(
95429
- import_picocolors24.default.bold(`Error analysis`) + import_picocolors24.default.dim(
95867
+ import_picocolors25.default.bold(`Error analysis`) + import_picocolors25.default.dim(
95430
95868
  ` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "")
95431
95869
  )
95432
95870
  );
95433
- console.log(import_picocolors24.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
95871
+ console.log(import_picocolors25.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
95434
95872
  if (broadeningNote) {
95435
- console.log(import_picocolors24.default.yellow(` ${broadeningNote}`));
95873
+ console.log(import_picocolors25.default.yellow(` ${broadeningNote}`));
95436
95874
  }
95437
95875
  console.log(
95438
- ` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${import_picocolors24.default.yellow(String(analysis.newSignatures.length))} new`
95876
+ ` ${analysis.totalErrors} error(s) \xB7 ${analysis.signatures.length} signature(s) \xB7 ${import_picocolors25.default.yellow(String(analysis.newSignatures.length))} new`
95439
95877
  );
95440
95878
  console.log("");
95441
95879
  if (analysis.signatures.length === 0) {
95442
- console.log(import_picocolors24.default.dim(" No error-level logs in the window."));
95880
+ console.log(import_picocolors25.default.dim(" No error-level logs in the window."));
95443
95881
  console.log(
95444
- import_picocolors24.default.dim(
95882
+ import_picocolors25.default.dim(
95445
95883
  ` Searched: ${scopeService ? `service "${scopeService}"` : "all services"} \xB7 index: ${indexPattern} \xB7 from: ${fromDisplay} UTC`
95446
95884
  )
95447
95885
  );
95448
- console.log(import_picocolors24.default.dim(` Tip: use --since to widen the window (e.g. --since 30d).`));
95886
+ console.log(import_picocolors25.default.dim(` Tip: use --since to widen the window (e.g. --since 30d).`));
95449
95887
  return 0;
95450
95888
  }
95451
95889
  for (const s of analysis.signatures) {
95452
95890
  const tags = [];
95453
- if (s.isNew) tags.push(import_picocolors24.default.red("NEW"));
95891
+ if (s.isNew) tags.push(import_picocolors25.default.red("NEW"));
95454
95892
  else if (s.ratio !== void 0 && Number.isFinite(s.ratio) && s.ratio >= 1.5) {
95455
- tags.push(import_picocolors24.default.yellow(`spike x${s.ratio.toFixed(1)}`));
95893
+ tags.push(import_picocolors25.default.yellow(`spike x${s.ratio.toFixed(1)}`));
95456
95894
  }
95457
- const svc = s.services.length > 0 ? import_picocolors24.default.dim(` \xB7 ${s.services.slice(0, 3).join(", ")}`) : "";
95895
+ const svc = s.services.length > 0 ? import_picocolors25.default.dim(` \xB7 ${s.services.slice(0, 3).join(", ")}`) : "";
95458
95896
  const tagStr = tags.length > 0 ? ` [${tags.join(", ")}]` : "";
95459
95897
  console.log(
95460
- ` ${import_picocolors24.default.red(String(s.count).padStart(6))} ${import_picocolors24.default.bold(s.key.padEnd(14).slice(0, 14))} ` + import_picocolors24.default.dim(`first ${shortTs(s.firstSeen)} \xB7 last ${shortTs(s.lastSeen)}`) + svc + tagStr
95898
+ ` ${import_picocolors25.default.red(String(s.count).padStart(6))} ${import_picocolors25.default.bold(s.key.padEnd(14).slice(0, 14))} ` + import_picocolors25.default.dim(`first ${shortTs(s.firstSeen)} \xB7 last ${shortTs(s.lastSeen)}`) + svc + tagStr
95461
95899
  );
95462
95900
  }
95463
95901
  if (analysis.affectedServices.length > 0) {
95464
95902
  console.log("");
95465
- console.log(` ${import_picocolors24.default.bold("Affected services:")} ${analysis.affectedServices.join(", ")}`);
95903
+ console.log(` ${import_picocolors25.default.bold("Affected services:")} ${analysis.affectedServices.join(", ")}`);
95466
95904
  }
95467
95905
  console.log("");
95468
- console.log(import_picocolors24.default.dim(" (use --raw to see individual log lines)"));
95906
+ console.log(import_picocolors25.default.dim(" (use --raw to see individual log lines)"));
95469
95907
  if (opts.ai) {
95470
95908
  const result = await renderAiInterpretation({
95471
95909
  command: "logs",
@@ -95484,19 +95922,19 @@ async function runLogs(service, opts) {
95484
95922
  });
95485
95923
  console.log("\n" + renderInterpretation(result));
95486
95924
  if (!result.ok) {
95487
- console.error(import_picocolors24.default.yellow(`[ai] ${result.warning}`));
95925
+ console.error(import_picocolors25.default.yellow(`[ai] ${result.warning}`));
95488
95926
  }
95489
95927
  }
95490
95928
  return 0;
95491
95929
  } catch (err) {
95492
- console.error(import_picocolors24.default.red(err.message));
95930
+ console.error(import_picocolors25.default.red(err.message));
95493
95931
  return 1;
95494
95932
  }
95495
95933
  }
95496
95934
 
95497
95935
  // ../../packages/cli/src/commands/metrics.ts
95498
95936
  init_cjs_shims();
95499
- var import_picocolors25 = __toESM(require_picocolors(), 1);
95937
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
95500
95938
  var METRICS_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
95501
95939
 
95502
95940
  Evidence used
@@ -95541,10 +95979,10 @@ function anomalyColor(anomaly) {
95541
95979
  case "latency-spike":
95542
95980
  case "error-rate-change":
95543
95981
  case "throughput-drop":
95544
- return import_picocolors25.default.red;
95982
+ return import_picocolors26.default.red;
95545
95983
  case "queue-growth":
95546
95984
  case "change":
95547
- return import_picocolors25.default.yellow;
95985
+ return import_picocolors26.default.yellow;
95548
95986
  default:
95549
95987
  return (s) => s;
95550
95988
  }
@@ -95571,7 +96009,7 @@ async function runMetrics(hint, opts) {
95571
96009
  const metrics = metricsProviderFromConfig(config2);
95572
96010
  if (metrics === null) {
95573
96011
  console.error(
95574
- import_picocolors25.default.red(
96012
+ import_picocolors26.default.red(
95575
96013
  "Grafana not configured \u2014 set GRAFANA_URL + GRAFANA_USER + GRAFANA_PASSWORD"
95576
96014
  )
95577
96015
  );
@@ -95579,7 +96017,7 @@ async function runMetrics(hint, opts) {
95579
96017
  }
95580
96018
  const health = await metrics.health();
95581
96019
  if (!health.ok) {
95582
- console.error(import_picocolors25.default.red(`Grafana unreachable: ${health.detail}`));
96020
+ console.error(import_picocolors26.default.red(`Grafana unreachable: ${health.detail}`));
95583
96021
  return 1;
95584
96022
  }
95585
96023
  const stepNum = opts.step !== void 0 ? Number(opts.step) : void 0;
@@ -95608,14 +96046,14 @@ async function runMetrics(hint, opts) {
95608
96046
  return 0;
95609
96047
  }
95610
96048
  if (series.length === 0) {
95611
- console.log(import_picocolors25.default.dim("No series returned."));
96049
+ console.log(import_picocolors26.default.dim("No series returned."));
95612
96050
  return 0;
95613
96051
  }
95614
96052
  for (const s of series) {
95615
96053
  const summary = summarize(s);
95616
96054
  const label = labelStr(s.labels);
95617
96055
  console.log(
95618
- ` ${import_picocolors25.default.cyan(label || "(no labels)")} last=${import_picocolors25.default.bold(fmtNum(summary.last))} avg=${fmtNum(summary.avg)}`
96056
+ ` ${import_picocolors26.default.cyan(label || "(no labels)")} last=${import_picocolors26.default.bold(fmtNum(summary.last))} avg=${fmtNum(summary.avg)}`
95619
96057
  );
95620
96058
  }
95621
96059
  return 0;
@@ -95630,36 +96068,36 @@ async function runMetrics(hint, opts) {
95630
96068
  if (findings2.length === 0) {
95631
96069
  if (hint !== void 0) {
95632
96070
  const tokens = extractHintTokens(hint);
95633
- console.log(import_picocolors25.default.dim(`No panels matched hint "${hint}".`));
96071
+ console.log(import_picocolors26.default.dim(`No panels matched hint "${hint}".`));
95634
96072
  if (tokens.length > 0) {
95635
- console.log(import_picocolors25.default.dim(` Searched for panels containing: ${tokens.join(", ")}`));
96073
+ console.log(import_picocolors26.default.dim(` Searched for panels containing: ${tokens.join(", ")}`));
95636
96074
  }
95637
96075
  console.log(
95638
- import_picocolors25.default.dim(
95639
- ` Tip: run ${import_picocolors25.default.bold("horus metrics")} (no hint) to see all available panels, then use a panel title or keyword directly.`
96076
+ import_picocolors26.default.dim(
96077
+ ` Tip: run ${import_picocolors26.default.bold("horus metrics")} (no hint) to see all available panels, then use a panel title or keyword directly.`
95640
96078
  )
95641
96079
  );
95642
96080
  } else {
95643
- console.log(import_picocolors25.default.dim("No panels found in configured Grafana dashboards."));
96081
+ console.log(import_picocolors26.default.dim("No panels found in configured Grafana dashboards."));
95644
96082
  }
95645
96083
  return 0;
95646
96084
  }
95647
96085
  const hintSuffix = hint !== void 0 ? ` (hint: "${hint}")` : "";
95648
96086
  const matchSources = hint !== void 0 ? [...new Set(findings2.map((f) => f.matchSource).filter(Boolean))] : [];
95649
- const matchSuffix = matchSources.length > 0 ? import_picocolors25.default.dim(` [matched via ${matchSources.join(", ")}]`) : "";
96087
+ const matchSuffix = matchSources.length > 0 ? import_picocolors26.default.dim(` [matched via ${matchSources.join(", ")}]`) : "";
95650
96088
  console.log(
95651
- import_picocolors25.default.bold(
96089
+ import_picocolors26.default.bold(
95652
96090
  `Grafana metrics${hintSuffix} \u2014 ${findings2.length} series across panels`
95653
96091
  ) + matchSuffix
95654
96092
  );
95655
96093
  console.log(
95656
- import_picocolors25.default.dim(
96094
+ import_picocolors26.default.dim(
95657
96095
  ` window: ${new Date(from * 1e3).toISOString()} \u2013 ${new Date(to * 1e3).toISOString()}`
95658
96096
  )
95659
96097
  );
95660
96098
  console.log("");
95661
96099
  if (flagged.length === 0) {
95662
- console.log(import_picocolors25.default.green(" No anomalies detected."));
96100
+ console.log(import_picocolors26.default.green(" No anomalies detected."));
95663
96101
  } else {
95664
96102
  for (const f of flagged) {
95665
96103
  const color = anomalyColor(f.anomaly);
@@ -95668,14 +96106,14 @@ async function runMetrics(hint, opts) {
95668
96106
  const lblStr = labelStr(f.labels);
95669
96107
  console.log(
95670
96108
  color(
95671
- ` ${label.padEnd(22)} ${f.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f.baselineAvg)} -> ${fmtNum(f.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${import_picocolors25.default.dim(lblStr)}` : "")
96109
+ ` ${label.padEnd(22)} ${f.panelTitle.padEnd(40).slice(0, 40)} ${fmtNum(f.baselineAvg)} -> ${fmtNum(f.currentAvg)} (${ratioStr})` + (lblStr !== "" ? ` ${import_picocolors26.default.dim(lblStr)}` : "")
95672
96110
  )
95673
96111
  );
95674
96112
  }
95675
96113
  }
95676
96114
  if (ok.length > 0) {
95677
96115
  console.log("");
95678
- console.log(import_picocolors25.default.dim(` ${ok.length} panel series with no anomaly.`));
96116
+ console.log(import_picocolors26.default.dim(` ${ok.length} panel series with no anomaly.`));
95679
96117
  }
95680
96118
  if (opts.ai) {
95681
96119
  const result = await renderAiInterpretation({
@@ -95690,19 +96128,19 @@ async function runMetrics(hint, opts) {
95690
96128
  });
95691
96129
  console.log("\n" + renderInterpretation(result));
95692
96130
  if (!result.ok) {
95693
- console.error(import_picocolors25.default.yellow(`[ai] ${result.warning}`));
96131
+ console.error(import_picocolors26.default.yellow(`[ai] ${result.warning}`));
95694
96132
  }
95695
96133
  }
95696
96134
  return 0;
95697
96135
  } catch (err) {
95698
- console.error(import_picocolors25.default.red(err.message));
96136
+ console.error(import_picocolors26.default.red(err.message));
95699
96137
  return 1;
95700
96138
  }
95701
96139
  }
95702
96140
 
95703
96141
  // ../../packages/cli/src/commands/state.ts
95704
96142
  init_cjs_shims();
95705
- var import_picocolors26 = __toESM(require_picocolors(), 1);
96143
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
95706
96144
  var STATE_AI_CONTRACT = `Provide a clearly separated AI evidence narration with:
95707
96145
 
95708
96146
  Evidence used
@@ -95728,13 +96166,13 @@ async function runState(opts) {
95728
96166
  try {
95729
96167
  renv = resolveEnvironment(config2, { project: opts.project, env: opts.env });
95730
96168
  } catch (err) {
95731
- console.error(import_picocolors26.default.red(err.message));
96169
+ console.error(import_picocolors27.default.red(err.message));
95732
96170
  return 1;
95733
96171
  }
95734
96172
  const mongo = mongoForEnv(renv);
95735
96173
  if (mongo === null) {
95736
96174
  console.error(
95737
- import_picocolors26.default.red(
96175
+ import_picocolors27.default.red(
95738
96176
  `No MongoDB connector configured for ${renv.project}/${renv.env} (set the project's Mongo URL env var).`
95739
96177
  )
95740
96178
  );
@@ -95742,7 +96180,7 @@ async function runState(opts) {
95742
96180
  }
95743
96181
  const health = await mongo.health();
95744
96182
  if (!health.ok) {
95745
- console.error(import_picocolors26.default.red(`MongoDB unreachable: ${health.detail}`));
96183
+ console.error(import_picocolors27.default.red(`MongoDB unreachable: ${health.detail}`));
95746
96184
  await mongo.close();
95747
96185
  return 1;
95748
96186
  }
@@ -95768,38 +96206,38 @@ async function runState(opts) {
95768
96206
  );
95769
96207
  return 0;
95770
96208
  }
95771
- const discoveryNote = analysis.autoDiscovered ? import_picocolors26.default.dim(` (${analysis.collections.length} collections, auto-discovered)`) : "";
96209
+ const discoveryNote = analysis.autoDiscovered ? import_picocolors27.default.dim(` (${analysis.collections.length} collections, auto-discovered)`) : "";
95772
96210
  console.log(
95773
- import_picocolors26.default.bold("State analysis") + import_picocolors26.default.dim(` \u2014 ${renv.project}/${renv.env} \xB7 db ${analysis.database}`) + discoveryNote
96211
+ import_picocolors27.default.bold("State analysis") + import_picocolors27.default.dim(` \u2014 ${renv.project}/${renv.env} \xB7 db ${analysis.database}`) + discoveryNote
95774
96212
  );
95775
96213
  console.log("");
95776
96214
  let signals = 0;
95777
96215
  for (const c of analysis.collections) {
95778
96216
  const flags = [];
95779
96217
  if (c.isStale === true) {
95780
- flags.push(import_picocolors26.default.yellow(`stale (last ${c.dateField}, ~${Math.round(c.ageHours ?? 0)}h)`));
96218
+ flags.push(import_picocolors27.default.yellow(`stale (last ${c.dateField}, ~${Math.round(c.ageHours ?? 0)}h)`));
95781
96219
  }
95782
96220
  for (const a of c.anomalies) {
95783
- flags.push(import_picocolors26.default.red(`${a.count}\xD7 "${a.value}"`));
96221
+ flags.push(import_picocolors27.default.red(`${a.count}\xD7 "${a.value}"`));
95784
96222
  signals += 1;
95785
96223
  }
95786
96224
  if (c.isStale === true) signals += 1;
95787
- const head = ` ${import_picocolors26.default.bold(c.collection.padEnd(18).slice(0, 18))} ${import_picocolors26.default.dim(String(c.count).padStart(8) + " docs")}`;
95788
- console.log(flags.length > 0 ? `${head} ${flags.join(" ")}` : import_picocolors26.default.dim(head));
96225
+ const head = ` ${import_picocolors27.default.bold(c.collection.padEnd(18).slice(0, 18))} ${import_picocolors27.default.dim(String(c.count).padStart(8) + " docs")}`;
96226
+ console.log(flags.length > 0 ? `${head} ${flags.join(" ")}` : import_picocolors27.default.dim(head));
95789
96227
  }
95790
96228
  console.log("");
95791
96229
  if (analysis.collections.length === 0) {
95792
96230
  console.log(
95793
- import_picocolors26.default.yellow(" No collections discovered in this database.") + import_picocolors26.default.dim(" Verify the database name in your MongoDB connector config.")
96231
+ import_picocolors27.default.yellow(" No collections discovered in this database.") + import_picocolors27.default.dim(" Verify the database name in your MongoDB connector config.")
95794
96232
  );
95795
96233
  } else {
95796
96234
  const scope = analysis.autoDiscovered ? "collection(s)" : "allowlisted collection(s)";
95797
96235
  console.log(
95798
- signals > 0 ? ` ${import_picocolors26.default.bold(String(signals))} state signal(s) across ${analysis.collections.length} ${scope}` : import_picocolors26.default.dim(` No state anomalies across ${analysis.collections.length} ${scope}.`)
96236
+ signals > 0 ? ` ${import_picocolors27.default.bold(String(signals))} state signal(s) across ${analysis.collections.length} ${scope}` : import_picocolors27.default.dim(` No state anomalies across ${analysis.collections.length} ${scope}.`)
95799
96237
  );
95800
96238
  if (analysis.autoDiscovered) {
95801
96239
  console.log(
95802
- import_picocolors26.default.dim(
96240
+ import_picocolors27.default.dim(
95803
96241
  ` Tip: add a "collections" list to your MongoDB connector config to restrict analysis.`
95804
96242
  )
95805
96243
  );
@@ -95817,7 +96255,7 @@ async function runState(opts) {
95817
96255
  });
95818
96256
  console.log("\n" + renderInterpretation(result));
95819
96257
  if (!result.ok) {
95820
- console.error(import_picocolors26.default.yellow(`[ai] ${result.warning}`));
96258
+ console.error(import_picocolors27.default.yellow(`[ai] ${result.warning}`));
95821
96259
  }
95822
96260
  }
95823
96261
  return 0;
@@ -95825,20 +96263,20 @@ async function runState(opts) {
95825
96263
  await mongo.close();
95826
96264
  }
95827
96265
  } catch (err) {
95828
- console.error(import_picocolors26.default.red(err.message));
96266
+ console.error(import_picocolors27.default.red(err.message));
95829
96267
  return 1;
95830
96268
  }
95831
96269
  }
95832
96270
 
95833
96271
  // ../../packages/cli/src/commands/init.ts
95834
96272
  init_cjs_shims();
95835
- var import_picocolors27 = __toESM(require_picocolors(), 1);
95836
- var import_node_path15 = require("path");
96273
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
96274
+ var import_node_path17 = require("path");
95837
96275
  async function runInit(opts) {
95838
96276
  try {
95839
96277
  const cwd = process.cwd();
95840
- const root = opts.path ? (0, import_node_path15.resolve)(opts.path) : findRepoRoot(cwd) ?? cwd;
95841
- const name = opts.name ?? (0, import_node_path15.basename)(root);
96278
+ const root = opts.path ? (0, import_node_path17.resolve)(opts.path) : findRepoRoot(cwd) ?? cwd;
96279
+ const name = opts.name ?? (0, import_node_path17.basename)(root);
95842
96280
  const envName = opts.env ?? "production";
95843
96281
  const repo = { name, path: root };
95844
96282
  if (opts.source) repo["source"] = { hostUrl: opts.source };
@@ -95853,65 +96291,65 @@ async function runInit(opts) {
95853
96291
  const configPath = writeLocalConfig(root, file);
95854
96292
  registerProject(name, root, configPath);
95855
96293
  ensureProjectGitignore(root);
95856
- console.log(`${import_picocolors27.default.green("\u2713")} Initialized Horus project ${import_picocolors27.default.bold(name)}`);
95857
- console.log(import_picocolors27.default.dim(` config: ${configPath}`));
95858
- console.log(import_picocolors27.default.dim(` registered: horus investigate --name ${name} "<hint>"`));
96294
+ console.log(`${import_picocolors28.default.green("\u2713")} Initialized Horus project ${import_picocolors28.default.bold(name)}`);
96295
+ console.log(import_picocolors28.default.dim(` config: ${configPath}`));
96296
+ console.log(import_picocolors28.default.dim(` registered: horus investigate --name ${name} "<hint>"`));
95859
96297
  if (!opts.source) {
95860
96298
  console.log(
95861
- import_picocolors27.default.dim(" no source-intelligence host set \u2014 run `horus index` to analyze + host, or pass --source <url>")
96299
+ import_picocolors28.default.dim(" no source-intelligence host set \u2014 run `horus index` to analyze + host, or pass --source <url>")
95862
96300
  );
95863
96301
  }
95864
96302
  console.log(
95865
- import_picocolors27.default.dim(" add runtime connectors (elasticsearch/mongodb/grafana) to .horus/config.json")
96303
+ import_picocolors28.default.dim(" add runtime connectors (elasticsearch/mongodb/grafana) to .horus/config.json")
95866
96304
  );
95867
96305
  console.log(
95868
- import_picocolors27.default.dim(" .horus/ is gitignored \u2014 local config and runtime state stay out of version control")
96306
+ import_picocolors28.default.dim(" .horus/ is gitignored \u2014 local config and runtime state stay out of version control")
95869
96307
  );
95870
96308
  return 0;
95871
96309
  } catch (err) {
95872
- console.error(import_picocolors27.default.red(err.message));
96310
+ console.error(import_picocolors28.default.red(err.message));
95873
96311
  return 1;
95874
96312
  }
95875
96313
  }
95876
96314
 
95877
96315
  // ../../packages/cli/src/commands/projects.ts
95878
96316
  init_cjs_shims();
95879
- var import_picocolors28 = __toESM(require_picocolors(), 1);
96317
+ var import_picocolors29 = __toESM(require_picocolors(), 1);
95880
96318
  async function runProjects() {
95881
96319
  const reg = readRegistry();
95882
96320
  const names = Object.keys(reg.projects).sort();
95883
96321
  if (names.length === 0) {
95884
96322
  console.log(
95885
- import_picocolors28.default.dim("No registered projects. Run `horus index` (or `horus init`) inside a repo.")
96323
+ import_picocolors29.default.dim("No registered projects. Run `horus index` (or `horus init`) inside a repo.")
95886
96324
  );
95887
96325
  return 0;
95888
96326
  }
95889
- console.log(import_picocolors28.default.bold("Registered Horus projects"));
96327
+ console.log(import_picocolors29.default.bold("Registered Horus projects"));
95890
96328
  console.log("");
95891
96329
  for (const name of names) {
95892
96330
  const entry2 = reg.projects[name];
95893
96331
  if (entry2 === void 0) continue;
95894
- console.log(` ${import_picocolors28.default.bold(name.padEnd(22))} ${import_picocolors28.default.dim(entry2.root)}`);
96332
+ console.log(` ${import_picocolors29.default.bold(name.padEnd(22))} ${import_picocolors29.default.dim(entry2.root)}`);
95895
96333
  }
95896
96334
  console.log("");
95897
- console.log(import_picocolors28.default.dim(' horus investigate --name <name> "<hint>"'));
96335
+ console.log(import_picocolors29.default.dim(' horus investigate --name <name> "<hint>"'));
95898
96336
  return 0;
95899
96337
  }
95900
96338
 
95901
96339
  // ../../packages/cli/src/commands/setup.ts
95902
96340
  init_cjs_shims();
95903
- var import_picocolors29 = __toESM(require_picocolors(), 1);
96341
+ var import_picocolors30 = __toESM(require_picocolors(), 1);
95904
96342
  var DEFAULT_DB_URL3 = "postgresql://horus:horus@localhost:5433/horus";
95905
96343
  async function runSetup(opts) {
95906
96344
  const write = opts.write ?? ((line2) => console.log(line2));
95907
96345
  let ok = true;
95908
- write(import_picocolors29.default.bold("\nHorus setup\n"));
96346
+ write(import_picocolors30.default.bold("\nHorus setup\n"));
95909
96347
  const backendVersion = await getSourceVersion();
95910
96348
  if (backendVersion === null) {
95911
96349
  ok = false;
95912
- write(` ${import_picocolors29.default.red("\u25CF")} Horus source-intelligence backend not found`);
96350
+ write(` ${import_picocolors30.default.red("\u25CF")} Horus source-intelligence backend not found`);
95913
96351
  write(
95914
- import_picocolors29.default.dim(
96352
+ import_picocolors30.default.dim(
95915
96353
  ` install it (Python 3.11+ required):
95916
96354
  pip install horus-source
95917
96355
  ensure ~/.local/bin is on your PATH`
@@ -95920,17 +96358,17 @@ async function runSetup(opts) {
95920
96358
  } else if (backendVersion !== PINNED_SOURCE_VERSION) {
95921
96359
  ok = false;
95922
96360
  write(
95923
- ` ${import_picocolors29.default.yellow("\u25CF")} Horus source-intelligence backend version mismatch` + import_picocolors29.default.dim(` (installed: ${backendVersion}, required: ${PINNED_SOURCE_VERSION})`)
96361
+ ` ${import_picocolors30.default.yellow("\u25CF")} Horus source-intelligence backend version mismatch` + import_picocolors30.default.dim(` (installed: ${backendVersion}, required: ${PINNED_SOURCE_VERSION})`)
95924
96362
  );
95925
96363
  write(
95926
- import_picocolors29.default.dim(
96364
+ import_picocolors30.default.dim(
95927
96365
  ` update it:
95928
96366
  pip install --upgrade horus-source`
95929
96367
  )
95930
96368
  );
95931
96369
  } else {
95932
96370
  write(
95933
- ` ${import_picocolors29.default.green("\u25CF")} Horus source-intelligence backend ` + import_picocolors29.default.dim(`(${backendVersion})`)
96371
+ ` ${import_picocolors30.default.green("\u25CF")} Horus source-intelligence backend ` + import_picocolors30.default.dim(`(${backendVersion})`)
95934
96372
  );
95935
96373
  }
95936
96374
  let dbUrl = process.env["DATABASE_URL"] ?? DEFAULT_DB_URL3;
@@ -95942,11 +96380,11 @@ async function runSetup(opts) {
95942
96380
  }
95943
96381
  const db = await checkDatabase(dbUrl);
95944
96382
  if (db.reachable) {
95945
- write(` ${import_picocolors29.default.green("\u25CF")} Postgres reachable ${import_picocolors29.default.dim(`(${db.schemaDetail})`)}`);
96383
+ write(` ${import_picocolors30.default.green("\u25CF")} Postgres reachable ${import_picocolors30.default.dim(`(${db.schemaDetail})`)}`);
95946
96384
  if (!db.schemaReady) {
95947
96385
  ok = false;
95948
96386
  write(
95949
- import_picocolors29.default.dim(
96387
+ import_picocolors30.default.dim(
95950
96388
  ` schema not applied \u2014 run migrations:
95951
96389
  from the Horus repo: pnpm db migrate
95952
96390
  or apply the schema directly: https://github.com/meritt-dev/horus/tree/master/packages/db/drizzle`
@@ -95955,9 +96393,9 @@ async function runSetup(opts) {
95955
96393
  }
95956
96394
  } else {
95957
96395
  ok = false;
95958
- write(` ${import_picocolors29.default.red("\u25CF")} Postgres unreachable`);
96396
+ write(` ${import_picocolors30.default.red("\u25CF")} Postgres unreachable`);
95959
96397
  write(
95960
- import_picocolors29.default.dim(
96398
+ import_picocolors30.default.dim(
95961
96399
  ` start a local instance:
95962
96400
  docker run -d --name horus-db \\
95963
96401
  -e POSTGRES_USER=horus -e POSTGRES_PASSWORD=horus -e POSTGRES_DB=horus \\
@@ -95982,10 +96420,10 @@ async function runSetup(opts) {
95982
96420
  if (!health.ok) {
95983
96421
  ok = false;
95984
96422
  write(
95985
- ` ${import_picocolors29.default.red("\u25CF")} Source intelligence host unreachable for ${import_picocolors29.default.bold(repo.name)} ` + import_picocolors29.default.dim(`(${repoHostUrl})`)
96423
+ ` ${import_picocolors30.default.red("\u25CF")} Source intelligence host unreachable for ${import_picocolors30.default.bold(repo.name)} ` + import_picocolors30.default.dim(`(${repoHostUrl})`)
95986
96424
  );
95987
96425
  write(
95988
- import_picocolors29.default.dim(
96426
+ import_picocolors30.default.dim(
95989
96427
  ` start the source intelligence host:
95990
96428
  cd ${repo.path}
95991
96429
  horus index`
@@ -95996,10 +96434,10 @@ async function runSetup(opts) {
95996
96434
  if (count === 0) {
95997
96435
  ok = false;
95998
96436
  write(
95999
- ` ${import_picocolors29.default.yellow("\u25CF")} Source intelligence host running but ${import_picocolors29.default.bold(repo.name)} is not indexed`
96437
+ ` ${import_picocolors30.default.yellow("\u25CF")} Source intelligence host running but ${import_picocolors30.default.bold(repo.name)} is not indexed`
96000
96438
  );
96001
96439
  write(
96002
- import_picocolors29.default.dim(
96440
+ import_picocolors30.default.dim(
96003
96441
  ` index the repo:
96004
96442
  cd ${repo.path}
96005
96443
  horus index`
@@ -96007,7 +96445,7 @@ async function runSetup(opts) {
96007
96445
  );
96008
96446
  } else {
96009
96447
  write(
96010
- ` ${import_picocolors29.default.green("\u25CF")} ${import_picocolors29.default.bold(repo.name)} \u2014 ${count} nodes indexed ` + import_picocolors29.default.dim(`(${repoHostUrl})`)
96448
+ ` ${import_picocolors30.default.green("\u25CF")} ${import_picocolors30.default.bold(repo.name)} \u2014 ${count} nodes indexed ` + import_picocolors30.default.dim(`(${repoHostUrl})`)
96011
96449
  );
96012
96450
  }
96013
96451
  }
@@ -96017,10 +96455,10 @@ async function runSetup(opts) {
96017
96455
  write("");
96018
96456
  if (ok) {
96019
96457
  write(
96020
- import_picocolors29.default.green("Ready.") + ' Next: `cd` into a repo and run `horus index`, then `horus investigate "<hint>"`.'
96458
+ import_picocolors30.default.green("Ready.") + ' Next: `cd` into a repo and run `horus index`, then `horus investigate "<hint>"`.'
96021
96459
  );
96022
96460
  } else {
96023
- write(import_picocolors29.default.yellow("Resolve the items above, then re-run `horus setup`."));
96461
+ write(import_picocolors30.default.yellow("Resolve the items above, then re-run `horus setup`."));
96024
96462
  }
96025
96463
  return ok ? 0 : 1;
96026
96464
  }
@@ -96029,12 +96467,12 @@ async function runSetup(opts) {
96029
96467
  init_cjs_shims();
96030
96468
  var import_node_readline = require("readline");
96031
96469
  var import_node_child_process6 = require("child_process");
96032
- var import_picocolors32 = __toESM(require_picocolors(), 1);
96470
+ var import_picocolors33 = __toESM(require_picocolors(), 1);
96033
96471
 
96034
96472
  // ../../packages/cli/src/lib/tty-selector.ts
96035
96473
  init_cjs_shims();
96036
96474
  var import_node_tty = require("tty");
96037
- var import_picocolors30 = __toESM(require_picocolors(), 1);
96475
+ var import_picocolors31 = __toESM(require_picocolors(), 1);
96038
96476
  var ExitPromptError = class extends Error {
96039
96477
  constructor(message = "Prompt was cancelled") {
96040
96478
  super(message);
@@ -96090,7 +96528,7 @@ async function checkboxSearch(opts) {
96090
96528
  clear();
96091
96529
  const lines = [];
96092
96530
  lines.push(
96093
- `? ${import_picocolors30.default.bold(message)} ${import_picocolors30.default.dim("(\u2191\u2193 navigate \u2022 space toggle \u2022 enter confirm \u2022 type filter)")}`
96531
+ `? ${import_picocolors31.default.bold(message)} ${import_picocolors31.default.dim("(\u2191\u2193 navigate \u2022 space toggle \u2022 enter confirm \u2022 type filter)")}`
96094
96532
  );
96095
96533
  const visible = filtered.slice(visibleOffset, visibleOffset + pageSize);
96096
96534
  for (let i = 0; i < visible.length; i++) {
@@ -96098,29 +96536,29 @@ async function checkboxSearch(opts) {
96098
96536
  const choice = visible[i];
96099
96537
  const isCursor = idx === cursor;
96100
96538
  const isSelected = selected.has(choice);
96101
- const marker = isSelected ? import_picocolors30.default.green("\u25CF") : " ";
96539
+ const marker = isSelected ? import_picocolors31.default.green("\u25CF") : " ";
96102
96540
  const prefix = `[${marker}]`;
96103
- const label = isCursor ? import_picocolors30.default.cyan(choice) : choice;
96104
- const pointer = isCursor ? import_picocolors30.default.cyan("\u276F") : " ";
96541
+ const label = isCursor ? import_picocolors31.default.cyan(choice) : choice;
96542
+ const pointer = isCursor ? import_picocolors31.default.cyan("\u276F") : " ";
96105
96543
  lines.push(` ${pointer} ${prefix} ${label}`);
96106
96544
  }
96107
96545
  if (filtered.length === 0) {
96108
- lines.push(import_picocolors30.default.dim(" No matches"));
96546
+ lines.push(import_picocolors31.default.dim(" No matches"));
96109
96547
  }
96110
96548
  const statusParts = [
96111
96549
  `${selected.size} selected`,
96112
96550
  `${filtered.length}/${choices.length} matches`
96113
96551
  ];
96114
96552
  if (filter) statusParts.push(`filter: ${filter}`);
96115
- lines.push(import_picocolors30.default.dim(` ${statusParts.join(" \xB7 ")}`));
96553
+ lines.push(import_picocolors31.default.dim(` ${statusParts.join(" \xB7 ")}`));
96116
96554
  output.write(lines.join("\n"));
96117
96555
  lastRenderLines = lines.length;
96118
96556
  }
96119
96557
  function finish(values2) {
96120
96558
  cleanup();
96121
- const summary = values2.length > 0 ? values2.join(", ") : import_picocolors30.default.dim("none");
96559
+ const summary = values2.length > 0 ? values2.join(", ") : import_picocolors31.default.dim("none");
96122
96560
  output.write(`
96123
- ? ${import_picocolors30.default.bold(message)} ${summary}
96561
+ ? ${import_picocolors31.default.bold(message)} ${summary}
96124
96562
  `);
96125
96563
  resolve8(values2);
96126
96564
  }
@@ -96212,7 +96650,7 @@ async function checkboxSearch(opts) {
96212
96650
  // ../../packages/cli/src/commands/connect-ai.ts
96213
96651
  init_cjs_shims();
96214
96652
  var import_node_child_process5 = require("child_process");
96215
- var import_picocolors31 = __toESM(require_picocolors(), 1);
96653
+ var import_picocolors32 = __toESM(require_picocolors(), 1);
96216
96654
  function cliInstalled(id) {
96217
96655
  const r = (0, import_node_child_process5.spawnSync)(id, ["--version"], { stdio: "ignore", timeout: 2e3 });
96218
96656
  return !r.error;
@@ -96240,13 +96678,13 @@ async function runConnectAi(opts) {
96240
96678
  const root = findRepoRoot(cwd) ?? cwd;
96241
96679
  const configPath = discoverLocalConfig(cwd) ?? localConfigPath(root);
96242
96680
  const installed = LOCAL_PROVIDER_IDS.filter((id) => cliInstalled(id));
96243
- console.log(import_picocolors31.default.bold("\nAI providers"));
96681
+ console.log(import_picocolors32.default.bold("\nAI providers"));
96244
96682
  console.log(
96245
- import_picocolors31.default.dim(
96683
+ import_picocolors32.default.dim(
96246
96684
  ` Local CLIs on PATH: ${installed.length > 0 ? installed.join(", ") : "none detected"}`
96247
96685
  )
96248
96686
  );
96249
- console.log(import_picocolors31.default.dim(" Cloud: anthropic (Anthropic Claude API \u2014 used by `investigate --ai`)\n"));
96687
+ console.log(import_picocolors32.default.dim(" Cloud: anthropic (Anthropic Claude API \u2014 used by `investigate --ai`)\n"));
96250
96688
  let provider = opts.provider;
96251
96689
  if (provider === void 0) {
96252
96690
  if (isInteractive()) {
@@ -96257,7 +96695,7 @@ async function runConnectAi(opts) {
96257
96695
  }
96258
96696
  }
96259
96697
  if (!AI_PROVIDERS.includes(provider)) {
96260
- console.error(import_picocolors31.default.red(`Unknown AI provider: ${provider}`) + import_picocolors31.default.dim(`
96698
+ console.error(import_picocolors32.default.red(`Unknown AI provider: ${provider}`) + import_picocolors32.default.dim(`
96261
96699
  supported: ${AI_PROVIDERS.join(", ")}`));
96262
96700
  return 1;
96263
96701
  }
@@ -96270,7 +96708,7 @@ async function runConnectAi(opts) {
96270
96708
  }
96271
96709
  if (!apiKey) {
96272
96710
  console.error(
96273
- import_picocolors31.default.red("No API key provided.") + import_picocolors31.default.dim("\n Pass --api-key sk-ant-\u2026 or set ANTHROPIC_API_KEY and re-run.")
96711
+ import_picocolors32.default.red("No API key provided.") + import_picocolors32.default.dim("\n Pass --api-key sk-ant-\u2026 or set ANTHROPIC_API_KEY and re-run.")
96274
96712
  );
96275
96713
  return 1;
96276
96714
  }
@@ -96278,28 +96716,28 @@ async function runConnectAi(opts) {
96278
96716
  const probe2 = await probeAnthropic(apiKey);
96279
96717
  if (!probe2.ok) {
96280
96718
  console.error(`
96281
- ${import_picocolors31.default.red("\u2717 Anthropic key check failed:")} ${probe2.detail}`);
96282
- console.error(import_picocolors31.default.dim(" Fix the key and retry, or pass --no-test to skip."));
96719
+ ${import_picocolors32.default.red("\u2717 Anthropic key check failed:")} ${probe2.detail}`);
96720
+ console.error(import_picocolors32.default.dim(" Fix the key and retry, or pass --no-test to skip."));
96283
96721
  return 1;
96284
96722
  }
96285
96723
  console.log(`
96286
- ${import_picocolors31.default.green("\u2713")} anthropic ${import_picocolors31.default.dim(`(${probe2.detail})`)}`);
96724
+ ${import_picocolors32.default.green("\u2713")} anthropic ${import_picocolors32.default.dim(`(${probe2.detail})`)}`);
96287
96725
  }
96288
96726
  if (opts.model) aiBlock["anthropic"] = { model: opts.model };
96289
96727
  storedKey = apiKey;
96290
96728
  } else {
96291
96729
  if (!installed.includes(provider)) {
96292
96730
  console.error(
96293
- import_picocolors31.default.red(`${provider} CLI not found on PATH.`) + import_picocolors31.default.dim(`
96731
+ import_picocolors32.default.red(`${provider} CLI not found on PATH.`) + import_picocolors32.default.dim(`
96294
96732
  Install it first, or choose anthropic.`)
96295
96733
  );
96296
96734
  return 1;
96297
96735
  }
96298
96736
  const desc2 = DEFAULT_LOCAL_PROVIDER_REGISTRY.get(provider);
96299
96737
  console.log(`
96300
- ${import_picocolors31.default.green("\u2713")} ${desc2?.displayName ?? provider} ${import_picocolors31.default.dim("detected on PATH")}`);
96738
+ ${import_picocolors32.default.green("\u2713")} ${desc2?.displayName ?? provider} ${import_picocolors32.default.dim("detected on PATH")}`);
96301
96739
  console.log(
96302
- import_picocolors31.default.dim(
96740
+ import_picocolors32.default.dim(
96303
96741
  " Note: `investigate --ai` currently uses the Anthropic API for narrative.\n Local-CLI narrative generation is recorded as your preference for future use."
96304
96742
  )
96305
96743
  );
@@ -96317,15 +96755,15 @@ ${import_picocolors31.default.green("\u2713")} ${desc2?.displayName ?? provider}
96317
96755
  });
96318
96756
  }
96319
96757
  console.log(`
96320
- ${import_picocolors31.default.green("\u2713")} ${import_picocolors31.default.bold("ai")} provider saved \u2192 ${import_picocolors31.default.dim(configPath)}`);
96321
- console.log(import_picocolors31.default.dim(` provider: ${provider}`));
96758
+ ${import_picocolors32.default.green("\u2713")} ${import_picocolors32.default.bold("ai")} provider saved \u2192 ${import_picocolors32.default.dim(configPath)}`);
96759
+ console.log(import_picocolors32.default.dim(` provider: ${provider}`));
96322
96760
  if (storedKey && secretsPathStr) {
96323
- console.log(import_picocolors31.default.dim(` anthropic key: ${redactKey(storedKey)} \u2192 ${secretsPathStr} (gitignored)`));
96761
+ console.log(import_picocolors32.default.dim(` anthropic key: ${redactKey(storedKey)} \u2192 ${secretsPathStr} (gitignored)`));
96324
96762
  }
96325
- console.log(import_picocolors31.default.dim(' run: horus investigate "<hint>" --ai'));
96763
+ console.log(import_picocolors32.default.dim(' run: horus investigate "<hint>" --ai'));
96326
96764
  return 0;
96327
96765
  } catch (err) {
96328
- console.error(import_picocolors31.default.red(err instanceof Error ? err.message : String(err)));
96766
+ console.error(import_picocolors32.default.red(err instanceof Error ? err.message : String(err)));
96329
96767
  return 1;
96330
96768
  }
96331
96769
  }
@@ -96357,7 +96795,7 @@ async function runConnect(type, opts) {
96357
96795
  }
96358
96796
  if (!SUPPORTED.includes(type)) {
96359
96797
  console.error(
96360
- import_picocolors32.default.red(`Unknown connector type: ${type}`) + import_picocolors32.default.dim(`
96798
+ import_picocolors33.default.red(`Unknown connector type: ${type}`) + import_picocolors33.default.dim(`
96361
96799
  supported: ${SUPPORTED.join(", ")}, ai`)
96362
96800
  );
96363
96801
  return 1;
@@ -96388,20 +96826,20 @@ async function runConnect(type, opts) {
96388
96826
  if (!probeResult.ok) {
96389
96827
  console.error(
96390
96828
  `
96391
- ${import_picocolors32.default.red(`\u2717 Could not reach ${connectorType}:`)} ${probeResult.detail}` + import_picocolors32.default.dim("\n Fix the connection and retry, or pass --no-test to skip.")
96829
+ ${import_picocolors33.default.red(`\u2717 Could not reach ${connectorType}:`)} ${probeResult.detail}` + import_picocolors33.default.dim("\n Fix the connection and retry, or pass --no-test to skip.")
96392
96830
  );
96393
96831
  return 1;
96394
96832
  }
96395
96833
  console.log(
96396
96834
  `
96397
- ${import_picocolors32.default.green("\u2713")} ${connectorType} reachable ${import_picocolors32.default.dim(`(${probeResult.detail})`)}`
96835
+ ${import_picocolors33.default.green("\u2713")} ${connectorType} reachable ${import_picocolors33.default.dim(`(${probeResult.detail})`)}`
96398
96836
  );
96399
96837
  }
96400
96838
  const hasLiteralCredentials = filled.url !== void 0 || filled.password !== void 0 || filled.username !== void 0;
96401
96839
  if (hasLiteralCredentials) {
96402
96840
  if (isGitTracked(configPath, root)) {
96403
96841
  console.error(
96404
- import_picocolors32.default.red(".horus/config.json is already tracked by Git.") + "\nStoring credentials here would expose them in the repository.\n" + import_picocolors32.default.dim(
96842
+ import_picocolors33.default.red(".horus/config.json is already tracked by Git.") + "\nStoring credentials here would expose them in the repository.\n" + import_picocolors33.default.dim(
96405
96843
  " 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."
96406
96844
  )
96407
96845
  );
@@ -96411,18 +96849,18 @@ ${import_picocolors32.default.green("\u2713")} ${connectorType} reachable ${impo
96411
96849
  }
96412
96850
  patchLocalConnector(configPath, connectorType, patch, filled.env);
96413
96851
  console.log(
96414
- `${import_picocolors32.default.green("\u2713")} ${import_picocolors32.default.bold(connectorType)} connector saved \u2192 ${import_picocolors32.default.dim(configPath)}`
96852
+ `${import_picocolors33.default.green("\u2713")} ${import_picocolors33.default.bold(connectorType)} connector saved \u2192 ${import_picocolors33.default.dim(configPath)}`
96415
96853
  );
96416
96854
  printSummary(connectorType, filled);
96417
- console.log(import_picocolors32.default.dim(`
96855
+ console.log(import_picocolors33.default.dim(`
96418
96856
  run: horus investigate "<hint>"`));
96419
96857
  return 0;
96420
96858
  } catch (err) {
96421
96859
  if (err instanceof ExitPromptError) {
96422
- console.error(import_picocolors32.default.red("Cancelled."));
96860
+ console.error(import_picocolors33.default.red("Cancelled."));
96423
96861
  return 1;
96424
96862
  }
96425
- console.error(import_picocolors32.default.red(err.message));
96863
+ console.error(import_picocolors33.default.red(err.message));
96426
96864
  return 1;
96427
96865
  }
96428
96866
  }
@@ -96431,7 +96869,7 @@ async function fillInteractive(type, opts) {
96431
96869
  if (!needsInteraction) return opts;
96432
96870
  console.log(
96433
96871
  `
96434
- ${import_picocolors32.default.bold(`Connect ${type}`)} ${import_picocolors32.default.dim("(press Enter to skip optional fields)")}
96872
+ ${import_picocolors33.default.bold(`Connect ${type}`)} ${import_picocolors33.default.dim("(press Enter to skip optional fields)")}
96435
96873
  `
96436
96874
  );
96437
96875
  const filled = { ...opts };
@@ -96486,7 +96924,7 @@ ${import_picocolors32.default.bold(`Connect ${type}`)} ${import_picocolors32.def
96486
96924
  break;
96487
96925
  case "redis": {
96488
96926
  console.log(
96489
- import_picocolors32.default.dim(
96927
+ import_picocolors33.default.dim(
96490
96928
  " Tip: embed credentials directly in the URL \u2014 redis://:password@host:6379\n or enter the URL and password separately below."
96491
96929
  )
96492
96930
  );
@@ -96524,8 +96962,8 @@ function missingRequired(type, opts) {
96524
96962
  }
96525
96963
  function ask(label, placeholder = "", required2 = true) {
96526
96964
  return new Promise((resolve8) => {
96527
- const hint = placeholder ? import_picocolors32.default.dim(` (${placeholder})`) : "";
96528
- const suffix = required2 ? "" : import_picocolors32.default.dim(" [optional]");
96965
+ const hint = placeholder ? import_picocolors33.default.dim(` (${placeholder})`) : "";
96966
+ const suffix = required2 ? "" : import_picocolors33.default.dim(" [optional]");
96529
96967
  process.stdout.write(` ${label}${suffix}${hint}: `);
96530
96968
  const rl = (0, import_node_readline.createInterface)({
96531
96969
  input: process.stdin,
@@ -96542,7 +96980,7 @@ function askPassword(label) {
96542
96980
  return new Promise((resolve8) => {
96543
96981
  const stdin = process.stdin;
96544
96982
  if (typeof stdin.setRawMode === "function") {
96545
- process.stdout.write(` ${label}${import_picocolors32.default.dim(" [optional]")}: `);
96983
+ process.stdout.write(` ${label}${import_picocolors33.default.dim(" [optional]")}: `);
96546
96984
  stdin.setRawMode(true);
96547
96985
  stdin.resume();
96548
96986
  let value = "";
@@ -96642,15 +97080,15 @@ function describeProbe(p) {
96642
97080
  return `${p.keyCount} keys \xB7 ${p.suggestedRoles.join("/")}${examples ? ` \xB7 ${examples}` : ""}`;
96643
97081
  }
96644
97082
  async function discoverAndSelectDbs(url, bullmqPrefix) {
96645
- console.log(import_picocolors32.default.dim("\n Scanning DBs 0-15 (read-only, sampled)\u2026"));
97083
+ console.log(import_picocolors33.default.dim("\n Scanning DBs 0-15 (read-only, sampled)\u2026"));
96646
97084
  const probes = await probeRedisDatabases(url, { bullmqPrefix });
96647
97085
  const nonEmpty = probes.filter((p) => p.reachable && p.keyCount > 0);
96648
97086
  if (nonEmpty.length === 0) {
96649
- console.log(import_picocolors32.default.dim(" No populated DBs found."));
97087
+ console.log(import_picocolors33.default.dim(" No populated DBs found."));
96650
97088
  return [];
96651
97089
  }
96652
97090
  for (const p of nonEmpty) {
96653
- console.log(` ${import_picocolors32.default.bold(`DB ${p.db}`)} ${import_picocolors32.default.dim("\xB7 " + describeProbe(p))}`);
97091
+ console.log(` ${import_picocolors33.default.bold(`DB ${p.db}`)} ${import_picocolors33.default.dim("\xB7 " + describeProbe(p))}`);
96654
97092
  }
96655
97093
  const byLabel = /* @__PURE__ */ new Map();
96656
97094
  const choices = nonEmpty.map((p) => {
@@ -96761,7 +97199,7 @@ function printSummary(type, opts) {
96761
97199
  } else if (opts.dashboard) {
96762
97200
  lines.push(` dashboard: ${opts.dashboard}`);
96763
97201
  }
96764
- if (lines.length > 0) console.log(import_picocolors32.default.dim(lines.join("\n")));
97202
+ if (lines.length > 0) console.log(import_picocolors33.default.dim(lines.join("\n")));
96765
97203
  }
96766
97204
  function isGitTracked(filePath, cwd) {
96767
97205
  try {
@@ -96800,11 +97238,11 @@ async function askIndexSelection(indices) {
96800
97238
  const shown = indices.slice(0, MAX_DISPLAY);
96801
97239
  console.log("\n Available Elasticsearch indexes/data streams:");
96802
97240
  shown.forEach((name, i) => {
96803
- console.log(` ${import_picocolors32.default.dim(`[${i + 1}]`)} ${name}`);
97241
+ console.log(` ${import_picocolors33.default.dim(`[${i + 1}]`)} ${name}`);
96804
97242
  });
96805
97243
  if (indices.length > MAX_DISPLAY) {
96806
97244
  console.log(
96807
- import_picocolors32.default.dim(
97245
+ import_picocolors33.default.dim(
96808
97246
  ` \u2026 and ${indices.length - MAX_DISPLAY} more (type a pattern manually to match all)`
96809
97247
  )
96810
97248
  );
@@ -96848,11 +97286,11 @@ async function askDashboardSelection(dashboards) {
96848
97286
  const shown = dashboards.slice(0, MAX_DISPLAY);
96849
97287
  console.log("\n Available Grafana dashboards:");
96850
97288
  shown.forEach((d, i) => {
96851
- const folder = d.folderTitle ? import_picocolors32.default.dim(` (${d.folderTitle})`) : "";
96852
- console.log(` ${import_picocolors32.default.dim(`[${i + 1}]`)} ${d.title}${folder}`);
97289
+ const folder = d.folderTitle ? import_picocolors33.default.dim(` (${d.folderTitle})`) : "";
97290
+ console.log(` ${import_picocolors33.default.dim(`[${i + 1}]`)} ${d.title}${folder}`);
96853
97291
  });
96854
97292
  if (dashboards.length > MAX_DISPLAY) {
96855
- console.log(import_picocolors32.default.dim(` \u2026 and ${dashboards.length - MAX_DISPLAY} more`));
97293
+ console.log(import_picocolors33.default.dim(` \u2026 and ${dashboards.length - MAX_DISPLAY} more`));
96856
97294
  }
96857
97295
  const input = (await ask(
96858
97296
  ` Select dashboards to use (e.g. 1,2 or Enter to type uid manually)`,
@@ -96903,12 +97341,12 @@ function redactUrl(raw) {
96903
97341
  // ../../packages/cli/src/commands/stop.ts
96904
97342
  init_cjs_shims();
96905
97343
  var import_node_child_process7 = require("child_process");
96906
- var import_node_fs14 = require("fs");
97344
+ var import_node_fs16 = require("fs");
96907
97345
  var import_node_util4 = require("util");
96908
- var import_node_path16 = require("path");
96909
- var import_picocolors33 = __toESM(require_picocolors(), 1);
97346
+ var import_node_path18 = require("path");
97347
+ var import_picocolors34 = __toESM(require_picocolors(), 1);
96910
97348
  var execFileAsync = (0, import_node_util4.promisify)(import_node_child_process7.execFile);
96911
- var unlinkAsync = (0, import_node_util4.promisify)(import_node_fs14.unlink);
97349
+ var unlinkAsync = (0, import_node_util4.promisify)(import_node_fs16.unlink);
96912
97350
  var SPAWNED_HOST_FILE2 = "spawned-host.json";
96913
97351
  var START_TIME_TOLERANCE_S = 60;
96914
97352
  var STOP_WAIT_MS = 5e3;
@@ -96923,30 +97361,30 @@ async function runStop(opts) {
96923
97361
  const root = findRepoRoot(cwd) ?? cwd;
96924
97362
  const hostUrl = readSourceHostUrl(root);
96925
97363
  if (!hostUrl) {
96926
- console.log(import_picocolors33.default.dim("No source-intelligence host found for this repo (.horus/source/host.json absent)."));
97364
+ console.log(import_picocolors34.default.dim("No source-intelligence host found for this repo (.horus/source/host.json absent)."));
96927
97365
  return 0;
96928
97366
  }
96929
97367
  return await stopHost(root, hostUrl);
96930
97368
  } catch (err) {
96931
- console.error(import_picocolors33.default.red(err.message));
97369
+ console.error(import_picocolors34.default.red(err.message));
96932
97370
  return 1;
96933
97371
  }
96934
97372
  }
96935
97373
  async function stopHost(root, hostUrl) {
96936
97374
  const alive = await isHostHealthy(hostUrl);
96937
97375
  if (!alive) {
96938
- console.log(import_picocolors33.default.dim(`Host ${hostUrl} is already stopped.`));
97376
+ console.log(import_picocolors34.default.dim(`Host ${hostUrl} is already stopped.`));
96939
97377
  return 0;
96940
97378
  }
96941
97379
  const port = extractPort(hostUrl);
96942
97380
  if (port === null) {
96943
- console.error(import_picocolors33.default.red(`Cannot determine port from host URL: ${hostUrl}`));
97381
+ console.error(import_picocolors34.default.red(`Cannot determine port from host URL: ${hostUrl}`));
96944
97382
  return 1;
96945
97383
  }
96946
97384
  const spawned = readSpawnedHost(root);
96947
97385
  if (spawned === null) {
96948
97386
  console.error(
96949
- import_picocolors33.default.red(
97387
+ import_picocolors34.default.red(
96950
97388
  `No ownership record found (.horus/${SPAWNED_HOST_FILE2} absent). Horus will not stop a host it did not spawn.`
96951
97389
  )
96952
97390
  );
@@ -96954,12 +97392,12 @@ async function stopHost(root, hostUrl) {
96954
97392
  }
96955
97393
  const recordError = validateSpawnedRecord(spawned);
96956
97394
  if (recordError !== null) {
96957
- console.error(import_picocolors33.default.red(`Ownership record is malformed: ${recordError}. Aborting for safety.`));
97395
+ console.error(import_picocolors34.default.red(`Ownership record is malformed: ${recordError}. Aborting for safety.`));
96958
97396
  return 1;
96959
97397
  }
96960
97398
  if (spawned.port !== port) {
96961
97399
  console.error(
96962
- import_picocolors33.default.red(
97400
+ import_picocolors34.default.red(
96963
97401
  `Ownership record port (${spawned.port}) does not match host URL port (${port}). Record may be stale.`
96964
97402
  )
96965
97403
  );
@@ -96967,7 +97405,7 @@ async function stopHost(root, hostUrl) {
96967
97405
  }
96968
97406
  if (spawned.root !== root) {
96969
97407
  console.error(
96970
- import_picocolors33.default.red(
97408
+ import_picocolors34.default.red(
96971
97409
  `Ownership record root (${spawned.root}) does not match resolved root (${root}). Record may be stale.`
96972
97410
  )
96973
97411
  );
@@ -96975,9 +97413,9 @@ async function stopHost(root, hostUrl) {
96975
97413
  }
96976
97414
  const info = await getProcessInfo(spawned.pid);
96977
97415
  if (info === null) {
96978
- console.log(import_picocolors33.default.dim(`Process pid ${spawned.pid} is no longer running \u2014 already stopped.`));
97416
+ console.log(import_picocolors34.default.dim(`Process pid ${spawned.pid} is no longer running \u2014 already stopped.`));
96979
97417
  try {
96980
- await unlinkAsync((0, import_node_path16.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
97418
+ await unlinkAsync((0, import_node_path18.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
96981
97419
  } catch {
96982
97420
  }
96983
97421
  return 0;
@@ -96988,7 +97426,7 @@ async function stopHost(root, hostUrl) {
96988
97426
  );
96989
97427
  if (!hostPortRe.test(info.args)) {
96990
97428
  console.error(
96991
- import_picocolors33.default.red(
97429
+ import_picocolors34.default.red(
96992
97430
  `Pid ${spawned.pid} args do not match "horus-source host --port ${portStr}". Got: "${info.args.slice(0, 120)}". Aborting for safety.`
96993
97431
  )
96994
97432
  );
@@ -96997,12 +97435,12 @@ async function stopHost(root, hostUrl) {
96997
97435
  const startTs = new Date(spawned.startedAt).getTime();
96998
97436
  const recordedAgeS = Math.round((Date.now() - startTs) / 1e3);
96999
97437
  if (!Number.isFinite(info.etimeSeconds)) {
97000
- console.error(import_picocolors33.default.red(`Could not read elapsed time for pid ${spawned.pid}. Aborting for safety.`));
97438
+ console.error(import_picocolors34.default.red(`Could not read elapsed time for pid ${spawned.pid}. Aborting for safety.`));
97001
97439
  return 1;
97002
97440
  }
97003
97441
  if (Math.abs(info.etimeSeconds - recordedAgeS) > START_TIME_TOLERANCE_S) {
97004
97442
  console.error(
97005
- import_picocolors33.default.red(
97443
+ import_picocolors34.default.red(
97006
97444
  `Pid ${spawned.pid} age mismatch: record says ~${recordedAgeS}s, process reports ${info.etimeSeconds}s elapsed. Possible PID reuse \u2014 aborting for safety.`
97007
97445
  )
97008
97446
  );
@@ -97014,9 +97452,9 @@ async function stopHost(root, hostUrl) {
97014
97452
  signaled = true;
97015
97453
  } catch (err) {
97016
97454
  if (err.code === "ESRCH") {
97017
- console.log(import_picocolors33.default.dim(`Process pid ${spawned.pid} exited before signal \u2014 already stopped.`));
97455
+ console.log(import_picocolors34.default.dim(`Process pid ${spawned.pid} exited before signal \u2014 already stopped.`));
97018
97456
  } else {
97019
- console.error(import_picocolors33.default.red(`Failed to signal pid ${spawned.pid}: ${err.message}`));
97457
+ console.error(import_picocolors34.default.red(`Failed to signal pid ${spawned.pid}: ${err.message}`));
97020
97458
  return 1;
97021
97459
  }
97022
97460
  }
@@ -97032,18 +97470,18 @@ async function stopHost(root, hostUrl) {
97032
97470
  }
97033
97471
  if (!exited) {
97034
97472
  console.error(
97035
- import_picocolors33.default.red(
97473
+ import_picocolors34.default.red(
97036
97474
  `Host pid ${spawned.pid} did not exit within ${STOP_WAIT_MS / 1e3}s after SIGTERM.`
97037
97475
  )
97038
97476
  );
97039
97477
  return 1;
97040
97478
  }
97041
97479
  console.log(
97042
- `${import_picocolors33.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors33.default.dim(`(pid ${spawned.pid}, port ${port})`) + ` for ${root}`
97480
+ `${import_picocolors34.default.green("\u2713")} Stopped source-intelligence host ` + import_picocolors34.default.dim(`(pid ${spawned.pid}, port ${port})`) + ` for ${root}`
97043
97481
  );
97044
97482
  }
97045
97483
  try {
97046
- await unlinkAsync((0, import_node_path16.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
97484
+ await unlinkAsync((0, import_node_path18.join)(root, HORUS_DIR, SPAWNED_HOST_FILE2));
97047
97485
  } catch {
97048
97486
  }
97049
97487
  return 0;
@@ -97052,7 +97490,7 @@ async function stopAll() {
97052
97490
  const registry2 = readRegistry();
97053
97491
  const projects2 = Object.entries(registry2.projects);
97054
97492
  if (projects2.length === 0) {
97055
- console.log(import_picocolors33.default.dim("No registered projects."));
97493
+ console.log(import_picocolors34.default.dim("No registered projects."));
97056
97494
  return 0;
97057
97495
  }
97058
97496
  let stopped = 0;
@@ -97062,17 +97500,17 @@ async function stopAll() {
97062
97500
  if (!hostUrl) continue;
97063
97501
  const alive = await isHostHealthy(hostUrl);
97064
97502
  if (!alive) continue;
97065
- console.log(` Stopping ${import_picocolors33.default.bold(name)} ${import_picocolors33.default.dim(`(${hostUrl})`)}`);
97503
+ console.log(` Stopping ${import_picocolors34.default.bold(name)} ${import_picocolors34.default.dim(`(${hostUrl})`)}`);
97066
97504
  const code = await stopHost(entry2.root, hostUrl);
97067
97505
  if (code === 0) stopped++;
97068
97506
  else failed++;
97069
97507
  }
97070
97508
  if (stopped === 0 && failed === 0) {
97071
- console.log(import_picocolors33.default.dim("No running source-intelligence hosts found."));
97509
+ console.log(import_picocolors34.default.dim("No running source-intelligence hosts found."));
97072
97510
  } else {
97073
97511
  console.log(
97074
97512
  `
97075
- Stopped ${stopped} host(s)${failed > 0 ? import_picocolors33.default.red(`, ${failed} failed`) : ""}.`
97513
+ Stopped ${stopped} host(s)${failed > 0 ? import_picocolors34.default.red(`, ${failed} failed`) : ""}.`
97076
97514
  );
97077
97515
  }
97078
97516
  return failed > 0 ? 1 : 0;
@@ -97124,12 +97562,12 @@ function extractPort(hostUrl) {
97124
97562
 
97125
97563
  // ../../packages/cli/src/commands/hosts.ts
97126
97564
  init_cjs_shims();
97127
- var import_picocolors34 = __toESM(require_picocolors(), 1);
97565
+ var import_picocolors35 = __toESM(require_picocolors(), 1);
97128
97566
  async function runHosts() {
97129
97567
  const registry2 = readRegistry();
97130
97568
  const projects2 = Object.entries(registry2.projects);
97131
97569
  if (projects2.length === 0) {
97132
- console.log(import_picocolors34.default.dim("No registered projects. Run `horus index` in a repo first."));
97570
+ console.log(import_picocolors35.default.dim("No registered projects. Run `horus index` in a repo first."));
97133
97571
  return 0;
97134
97572
  }
97135
97573
  const rows = [];
@@ -97146,29 +97584,29 @@ async function runHosts() {
97146
97584
  });
97147
97585
  const anyHost = rows.some((r) => r.hostUrl !== null);
97148
97586
  if (!anyHost) {
97149
- console.log(import_picocolors34.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
97587
+ console.log(import_picocolors35.default.dim("No source-intelligence hosts found. Run `horus index` to start one."));
97150
97588
  return 0;
97151
97589
  }
97152
97590
  console.log("");
97153
97591
  for (const row of rows) {
97154
97592
  if (row.hostUrl === null) continue;
97155
- const status = row.healthy ? import_picocolors34.default.green("\u25CF running") : import_picocolors34.default.red("\u25CF stopped");
97593
+ const status = row.healthy ? import_picocolors35.default.green("\u25CF running") : import_picocolors35.default.red("\u25CF stopped");
97156
97594
  const port = extractPort2(row.hostUrl) ?? "?";
97157
97595
  console.log(
97158
- ` ${status} ${import_picocolors34.default.bold(row.name.padEnd(24))} port ${String(port).padEnd(6)} ${import_picocolors34.default.dim(row.root)}`
97596
+ ` ${status} ${import_picocolors35.default.bold(row.name.padEnd(24))} port ${String(port).padEnd(6)} ${import_picocolors35.default.dim(row.root)}`
97159
97597
  );
97160
97598
  }
97161
97599
  console.log("");
97162
97600
  const noHost = rows.filter((r) => r.hostUrl === null);
97163
97601
  if (noHost.length > 0) {
97164
97602
  for (const row of noHost) {
97165
- console.log(` ${import_picocolors34.default.dim("\u25CB no host")} ${import_picocolors34.default.dim(row.name)}`);
97603
+ console.log(` ${import_picocolors35.default.dim("\u25CB no host")} ${import_picocolors35.default.dim(row.name)}`);
97166
97604
  }
97167
97605
  console.log("");
97168
97606
  }
97169
97607
  const running = rows.filter((r) => r.healthy).length;
97170
97608
  console.log(
97171
- import_picocolors34.default.dim(
97609
+ import_picocolors35.default.dim(
97172
97610
  `${running} running \xB7 horus stop to reap \xB7 horus stop --all to stop everything`
97173
97611
  )
97174
97612
  );
@@ -97185,12 +97623,12 @@ function extractPort2(hostUrl) {
97185
97623
 
97186
97624
  // ../../packages/cli/src/commands/doctor.ts
97187
97625
  init_cjs_shims();
97188
- var import_picocolors35 = __toESM(require_picocolors(), 1);
97626
+ var import_picocolors36 = __toESM(require_picocolors(), 1);
97189
97627
  var DEFAULT_DB_URL4 = "postgresql://horus:horus@localhost:5433/horus";
97190
97628
  function mark2(status) {
97191
- if (status === "pass") return import_picocolors35.default.green("\u2713");
97192
- if (status === "warn") return import_picocolors35.default.yellow("~");
97193
- return import_picocolors35.default.red("\u2717");
97629
+ if (status === "pass") return import_picocolors36.default.green("\u2713");
97630
+ if (status === "warn") return import_picocolors36.default.yellow("~");
97631
+ return import_picocolors36.default.red("\u2717");
97194
97632
  }
97195
97633
  async function runDoctor(opts) {
97196
97634
  const cwd = opts?.cwd ?? process.cwd();
@@ -97417,11 +97855,11 @@ async function runDoctor(opts) {
97417
97855
  write(JSON.stringify(output, null, 2));
97418
97856
  return hasFailure ? 1 : 0;
97419
97857
  }
97420
- write(import_picocolors35.default.bold("\nHorus readiness check\n"));
97858
+ write(import_picocolors36.default.bold("\nHorus readiness check\n"));
97421
97859
  for (const check2 of checks) {
97422
- write(` ${mark2(check2.status)} ${import_picocolors35.default.bold(check2.label.padEnd(26))} ${import_picocolors35.default.dim(check2.detail)}`);
97860
+ write(` ${mark2(check2.status)} ${import_picocolors36.default.bold(check2.label.padEnd(26))} ${import_picocolors36.default.dim(check2.detail)}`);
97423
97861
  if (check2.next) {
97424
- write(` ${import_picocolors35.default.dim("\u2192 " + check2.next)}`);
97862
+ write(` ${import_picocolors36.default.dim("\u2192 " + check2.next)}`);
97425
97863
  }
97426
97864
  }
97427
97865
  write("");
@@ -97431,16 +97869,16 @@ async function runDoctor(opts) {
97431
97869
  // ../../packages/cli/src/commands/providers-doctor.ts
97432
97870
  init_cjs_shims();
97433
97871
  var import_node_child_process8 = require("child_process");
97434
- var import_picocolors36 = __toESM(require_picocolors(), 1);
97872
+ var import_picocolors37 = __toESM(require_picocolors(), 1);
97435
97873
  function statusMark(status) {
97436
- if (status === "ready") return import_picocolors36.default.green("\u2713");
97437
- if (status === "installed") return import_picocolors36.default.yellow("~");
97438
- return import_picocolors36.default.red("\u2717");
97874
+ if (status === "ready") return import_picocolors37.default.green("\u2713");
97875
+ if (status === "installed") return import_picocolors37.default.yellow("~");
97876
+ return import_picocolors37.default.red("\u2717");
97439
97877
  }
97440
97878
  function statusLabel(status) {
97441
- if (status === "ready") return import_picocolors36.default.green("ready");
97442
- if (status === "installed") return import_picocolors36.default.yellow("installed (not configured)");
97443
- return import_picocolors36.default.dim("not found on PATH");
97879
+ if (status === "ready") return import_picocolors37.default.green("ready");
97880
+ if (status === "installed") return import_picocolors37.default.yellow("installed (not configured)");
97881
+ return import_picocolors37.default.dim("not found on PATH");
97444
97882
  }
97445
97883
  function detectBinary(id) {
97446
97884
  const result = (0, import_node_child_process8.spawnSync)(id, ["--version"], { stdio: "pipe", timeout: 2e3 });
@@ -97457,7 +97895,7 @@ async function runProvidersDoctorCommand(opts) {
97457
97895
  const registry2 = opts?.registry ?? DEFAULT_LOCAL_PROVIDER_REGISTRY;
97458
97896
  const write = opts?.write ?? ((line2) => console.log(line2));
97459
97897
  const results = buildProviderResults(registry2, opts?._detect);
97460
- write(import_picocolors36.default.bold("\nLocal AI providers\n"));
97898
+ write(import_picocolors37.default.bold("\nLocal AI providers\n"));
97461
97899
  for (const result of results) {
97462
97900
  const descriptor = registry2.get(result.id);
97463
97901
  const name = descriptor?.displayName ?? result.id;
@@ -97465,11 +97903,11 @@ async function runProvidersDoctorCommand(opts) {
97465
97903
  ` ${statusMark(result.status)} ${result.id.padEnd(8)} ${name.padEnd(22)} ${statusLabel(result.status)}`
97466
97904
  );
97467
97905
  if (result.status !== "ready" && result.detail) {
97468
- write(` ${import_picocolors36.default.dim("\u2192 " + result.detail)}`);
97906
+ write(` ${import_picocolors37.default.dim("\u2192 " + result.detail)}`);
97469
97907
  }
97470
97908
  }
97471
97909
  write("");
97472
- write(import_picocolors36.default.bold("Cloud AI providers\n"));
97910
+ write(import_picocolors37.default.bold("Cloud AI providers\n"));
97473
97911
  let source = null;
97474
97912
  if (opts?._anthropicKey !== void 0) {
97475
97913
  source = opts._anthropicKey ? "env" : null;
@@ -97484,18 +97922,18 @@ async function runProvidersDoctorCommand(opts) {
97484
97922
  }
97485
97923
  if (source === "config") {
97486
97924
  write(
97487
- ` ${import_picocolors36.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors36.default.green("configured (local .horus config)")}`
97925
+ ` ${import_picocolors37.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors37.default.green("configured (local .horus config)")}`
97488
97926
  );
97489
97927
  } else if (source === "env") {
97490
97928
  write(
97491
- ` ${import_picocolors36.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors36.default.green("configured (ANTHROPIC_API_KEY env)")}`
97929
+ ` ${import_picocolors37.default.green("\u2713")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors37.default.green("configured (ANTHROPIC_API_KEY env)")}`
97492
97930
  );
97493
97931
  } else {
97494
97932
  write(
97495
- ` ${import_picocolors36.default.red("\u2717")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors36.default.dim("not configured")}`
97933
+ ` ${import_picocolors37.default.red("\u2717")} ${"anthropic".padEnd(8)} ${"Anthropic Claude API".padEnd(22)} ${import_picocolors37.default.dim("not configured")}`
97496
97934
  );
97497
97935
  write(
97498
- ` ${import_picocolors36.default.dim("\u2192 run `horus connect ai` (or set ANTHROPIC_API_KEY) to enable `horus investigate --ai`")}`
97936
+ ` ${import_picocolors37.default.dim("\u2192 run `horus connect ai` (or set ANTHROPIC_API_KEY) to enable `horus investigate --ai`")}`
97499
97937
  );
97500
97938
  }
97501
97939
  write("");
@@ -97504,9 +97942,9 @@ async function runProvidersDoctorCommand(opts) {
97504
97942
 
97505
97943
  // ../../packages/cli/src/commands/generate-config.ts
97506
97944
  init_cjs_shims();
97507
- var import_node_fs15 = require("fs");
97508
- var import_node_path17 = require("path");
97509
- var import_picocolors37 = __toESM(require_picocolors(), 1);
97945
+ var import_node_fs17 = require("fs");
97946
+ var import_node_path19 = require("path");
97947
+ var import_picocolors38 = __toESM(require_picocolors(), 1);
97510
97948
  function configTemplate(name, repoPath) {
97511
97949
  return `export default {
97512
97950
  database: {
@@ -97554,39 +97992,39 @@ async function runGenerateConfig(opts) {
97554
97992
  const defaults = localConfigPath2 != null ? projectDefaults(localConfigPath2) : null;
97555
97993
  const hasLocalConfig = defaults != null;
97556
97994
  const defaultOut = hasLocalConfig ? "horus.config.example.js" : "horus.config.js";
97557
- const outPath = (0, import_node_path17.resolve)(cwd, opts.out ?? defaultOut);
97995
+ const outPath = (0, import_node_path19.resolve)(cwd, opts.out ?? defaultOut);
97558
97996
  const name = opts.name ?? defaults?.name ?? "my-project";
97559
97997
  const repoPath = opts.repo ?? defaults?.repoPath ?? `/path/to/${name}`;
97560
- if ((0, import_node_fs15.existsSync)(outPath) && !opts.force) {
97561
- log(`${import_picocolors37.default.red("\u2717")} ${outPath} already exists`);
97562
- log(import_picocolors37.default.dim(" pass --force to overwrite"));
97998
+ if ((0, import_node_fs17.existsSync)(outPath) && !opts.force) {
97999
+ log(`${import_picocolors38.default.red("\u2717")} ${outPath} already exists`);
98000
+ log(import_picocolors38.default.dim(" pass --force to overwrite"));
97563
98001
  return 1;
97564
98002
  }
97565
98003
  try {
97566
- (0, import_node_fs15.mkdirSync)((0, import_node_path17.dirname)(outPath), { recursive: true });
97567
- (0, import_node_fs15.writeFileSync)(outPath, configTemplate(name, repoPath), "utf8");
98004
+ (0, import_node_fs17.mkdirSync)((0, import_node_path19.dirname)(outPath), { recursive: true });
98005
+ (0, import_node_fs17.writeFileSync)(outPath, configTemplate(name, repoPath), "utf8");
97568
98006
  } catch (err) {
97569
- log(`${import_picocolors37.default.red("\u2717")} Could not write ${outPath}: ${err.message}`);
98007
+ log(`${import_picocolors38.default.red("\u2717")} Could not write ${outPath}: ${err.message}`);
97570
98008
  return 1;
97571
98009
  }
97572
- log(`${import_picocolors37.default.green("\u2713")} Created ${outPath}`);
97573
- log(import_picocolors37.default.dim(` project: ${name}`));
97574
- log(import_picocolors37.default.dim(` repo: ${repoPath}`));
98010
+ log(`${import_picocolors38.default.green("\u2713")} Created ${outPath}`);
98011
+ log(import_picocolors38.default.dim(` project: ${name}`));
98012
+ log(import_picocolors38.default.dim(` repo: ${repoPath}`));
97575
98013
  if (hasLocalConfig && localConfigPath2 != null) {
97576
98014
  log("");
97577
- log(import_picocolors37.default.yellow("Note:") + ` an initialized Horus project config exists at ${localConfigPath2}`);
98015
+ log(import_picocolors38.default.yellow("Note:") + ` an initialized Horus project config exists at ${localConfigPath2}`);
97578
98016
  log(" \u2022 .horus/config.json \u2014 project config used by `horus investigate` from this repo");
97579
98017
  log(" \u2022 horus.config.js \u2014 standalone/global config used with `horus doctor --config <path>`");
97580
- log(import_picocolors37.default.dim(` next: review ${outPath} and copy/adapt it as needed`));
98018
+ log(import_picocolors38.default.dim(` next: review ${outPath} and copy/adapt it as needed`));
97581
98019
  } else {
97582
- log(import_picocolors37.default.dim(` next: horus doctor --config ${outPath}`));
98020
+ log(import_picocolors38.default.dim(` next: horus doctor --config ${outPath}`));
97583
98021
  }
97584
98022
  return 0;
97585
98023
  }
97586
98024
 
97587
98025
  // ../../packages/cli/src/commands/readiness.ts
97588
98026
  init_cjs_shims();
97589
- var import_picocolors38 = __toESM(require_picocolors(), 1);
98027
+ var import_picocolors39 = __toESM(require_picocolors(), 1);
97590
98028
  var READINESS_AI_CONTRACT = `Provide a clearly separated AI readiness summary with:
97591
98029
 
97592
98030
  Overall assessment
@@ -97609,9 +98047,9 @@ Confidence / gaps
97609
98047
  - Which checks Horus could verify vs. which require manual confirmation`;
97610
98048
  var DEFAULT_DB_URL5 = "postgresql://horus:horus@localhost:5433/horus";
97611
98049
  function mark3(status) {
97612
- if (status === "pass") return import_picocolors38.default.green("\u2713");
97613
- if (status === "warn") return import_picocolors38.default.yellow("~");
97614
- return import_picocolors38.default.red("\u2717");
98050
+ if (status === "pass") return import_picocolors39.default.green("\u2713");
98051
+ if (status === "warn") return import_picocolors39.default.yellow("~");
98052
+ return import_picocolors39.default.red("\u2717");
97615
98053
  }
97616
98054
  async function runReadiness(opts) {
97617
98055
  const cwd = opts?.cwd ?? process.cwd();
@@ -97766,20 +98204,20 @@ async function runReadiness(opts) {
97766
98204
  }
97767
98205
  const blockingChecks = checks.filter((c) => c.blocking);
97768
98206
  const optionalChecks = checks.filter((c) => !c.blocking);
97769
- write(import_picocolors38.default.bold("\nHorus release readiness\n"));
97770
- write(import_picocolors38.default.bold(" Blocking"));
98207
+ write(import_picocolors39.default.bold("\nHorus release readiness\n"));
98208
+ write(import_picocolors39.default.bold(" Blocking"));
97771
98209
  for (const check2 of blockingChecks) {
97772
- write(` ${mark3(check2.status)} ${import_picocolors38.default.bold(check2.label.padEnd(22))} ${import_picocolors38.default.dim(check2.detail)}`);
98210
+ write(` ${mark3(check2.status)} ${import_picocolors39.default.bold(check2.label.padEnd(22))} ${import_picocolors39.default.dim(check2.detail)}`);
97773
98211
  if (check2.next) {
97774
- write(` ${import_picocolors38.default.dim("\u2192 " + check2.next)}`);
98212
+ write(` ${import_picocolors39.default.dim("\u2192 " + check2.next)}`);
97775
98213
  }
97776
98214
  }
97777
98215
  write("");
97778
- write(import_picocolors38.default.bold(" Optional"));
98216
+ write(import_picocolors39.default.bold(" Optional"));
97779
98217
  for (const check2 of optionalChecks) {
97780
- write(` ${mark3(check2.status)} ${import_picocolors38.default.bold(check2.label.padEnd(22))} ${import_picocolors38.default.dim(check2.detail)}`);
98218
+ write(` ${mark3(check2.status)} ${import_picocolors39.default.bold(check2.label.padEnd(22))} ${import_picocolors39.default.dim(check2.detail)}`);
97781
98219
  if (check2.next) {
97782
- write(` ${import_picocolors38.default.dim("\u2192 " + check2.next)}`);
98220
+ write(` ${import_picocolors39.default.dim("\u2192 " + check2.next)}`);
97783
98221
  }
97784
98222
  }
97785
98223
  write("");
@@ -97787,21 +98225,21 @@ async function runReadiness(opts) {
97787
98225
  const optionalWarns = optionalChecks.filter((c) => c.status === "warn").length;
97788
98226
  if (blockingFails.length === 0) {
97789
98227
  if (optionalWarns === 0) {
97790
- write(import_picocolors38.default.green(" Ready for demo/release."));
98228
+ write(import_picocolors39.default.green(" Ready for demo/release."));
97791
98229
  } else {
97792
98230
  write(
97793
- import_picocolors38.default.yellow(
98231
+ import_picocolors39.default.yellow(
97794
98232
  ` Ready for a basic demo. ${optionalWarns} optional item(s) not configured \u2014 investigation evidence will be limited.`
97795
98233
  )
97796
98234
  );
97797
98235
  }
97798
98236
  } else {
97799
98237
  write(
97800
- import_picocolors38.default.red(
98238
+ import_picocolors39.default.red(
97801
98239
  ` Not ready. ${blockingFails.length} blocking item(s) must be resolved before demo/release.`
97802
98240
  )
97803
98241
  );
97804
- write(import_picocolors38.default.dim(" Re-run `horus readiness` after resolving the items above."));
98242
+ write(import_picocolors39.default.dim(" Re-run `horus readiness` after resolving the items above."));
97805
98243
  }
97806
98244
  write("");
97807
98245
  if (opts?.ai) {
@@ -97816,7 +98254,7 @@ async function runReadiness(opts) {
97816
98254
  });
97817
98255
  console.log("\n" + renderInterpretation(result));
97818
98256
  if (!result.ok) {
97819
- console.error(import_picocolors38.default.yellow(`[ai] ${result.warning}`));
98257
+ console.error(import_picocolors39.default.yellow(`[ai] ${result.warning}`));
97820
98258
  }
97821
98259
  }
97822
98260
  return blockingFails.length > 0 ? 1 : 0;
@@ -97824,33 +98262,33 @@ async function runReadiness(opts) {
97824
98262
 
97825
98263
  // ../../packages/cli/src/commands/skill.ts
97826
98264
  init_cjs_shims();
97827
- var import_node_fs16 = require("fs");
97828
- var import_node_path18 = require("path");
97829
- var import_node_os3 = require("os");
97830
- var import_promises = require("readline/promises");
97831
- var import_picocolors39 = __toESM(require_picocolors(), 1);
98265
+ var import_node_fs18 = require("fs");
98266
+ var import_node_path20 = require("path");
98267
+ var import_node_os4 = require("os");
98268
+ var import_promises2 = require("readline/promises");
98269
+ var import_picocolors40 = __toESM(require_picocolors(), 1);
97832
98270
  var SUPPORTED_TARGETS = ["claude", "codex", "gemini", "cursor", "generic"];
97833
98271
  function isValidTarget(target) {
97834
98272
  return SUPPORTED_TARGETS.includes(target);
97835
98273
  }
97836
98274
  function getLegacyClaudeSkillPath(opts) {
97837
- const base2 = opts.global ? (0, import_node_os3.homedir)() : opts.cwd ?? process.cwd();
97838
- return (0, import_node_path18.join)(base2, ".claude", "skills", "horus.md");
98275
+ const base2 = opts.global ? (0, import_node_os4.homedir)() : opts.cwd ?? process.cwd();
98276
+ return (0, import_node_path20.join)(base2, ".claude", "skills", "horus.md");
97839
98277
  }
97840
98278
  function getSkillInstallPath(target, opts) {
97841
- const base2 = opts.global ? (0, import_node_os3.homedir)() : opts.cwd ?? process.cwd();
98279
+ const base2 = opts.global ? (0, import_node_os4.homedir)() : opts.cwd ?? process.cwd();
97842
98280
  switch (target) {
97843
98281
  case "claude":
97844
- return (0, import_node_path18.join)(base2, ".claude", "skills", "horus", "SKILL.md");
98282
+ return (0, import_node_path20.join)(base2, ".claude", "skills", "horus", "SKILL.md");
97845
98283
  case "gemini":
97846
- return opts.global ? (0, import_node_path18.join)(base2, ".gemini", "GEMINI.md") : (0, import_node_path18.join)(base2, "GEMINI.md");
98284
+ return opts.global ? (0, import_node_path20.join)(base2, ".gemini", "GEMINI.md") : (0, import_node_path20.join)(base2, "GEMINI.md");
97847
98285
  case "cursor":
97848
- return opts.global ? (0, import_node_path18.join)(base2, ".cursorrules") : (0, import_node_path18.join)(base2, ".cursor", "rules", "horus.mdc");
98286
+ return opts.global ? (0, import_node_path20.join)(base2, ".cursorrules") : (0, import_node_path20.join)(base2, ".cursor", "rules", "horus.mdc");
97849
98287
  case "codex":
97850
- return opts.global ? (0, import_node_path18.join)(base2, ".codex", "AGENTS.md") : (0, import_node_path18.join)(base2, "AGENTS.md");
98288
+ return opts.global ? (0, import_node_path20.join)(base2, ".codex", "AGENTS.md") : (0, import_node_path20.join)(base2, "AGENTS.md");
97851
98289
  case "generic":
97852
98290
  default:
97853
- return (0, import_node_path18.join)(base2, ".horus", "skills", `horus-${target}.md`);
98291
+ return (0, import_node_path20.join)(base2, ".horus", "skills", `horus-${target}.md`);
97854
98292
  }
97855
98293
  }
97856
98294
  var BASE_SKILL = `# Horus Grounded Investigation Skill
@@ -97893,6 +98331,27 @@ horus ask <id> "what evidence contradicts the likely root cause?"
97893
98331
  horus score <id>
97894
98332
  \`\`\`
97895
98333
 
98334
+ ## Report whether Horus helped (close the loop)
98335
+
98336
+ Once you have **acted on an investigation and know the outcome** \u2014 you verified
98337
+ whether Horus pointed at the real cause \u2014 record it. This is how Horus measures and
98338
+ improves its own accuracy, and without it the impact signal is blind to agent-driven
98339
+ runs (most of them).
98340
+
98341
+ \`\`\`bash
98342
+ horus feedback <id> --resolved yes # Horus pointed at the real cause
98343
+ horus feedback <id> --resolved partly # useful lead, but not the whole cause
98344
+ horus feedback <id> --resolved no # Horus missed the cause
98345
+ \`\`\`
98346
+
98347
+ It is non-interactive and safe to run unattended. Report **once** per investigation,
98348
+ **after** you confirm the outcome \u2014 never before, and never guess. If you can estimate
98349
+ how long the same debugging would have taken you manually, add it:
98350
+
98351
+ \`\`\`bash
98352
+ horus feedback <id> --resolved yes --manual-estimate-min 120
98353
+ \`\`\`
98354
+
97896
98355
  ## Runtime evidence
97897
98356
 
97898
98357
  Use runtime commands when the question depends on live or recent runtime behavior:
@@ -98135,7 +98594,7 @@ async function confirm(message) {
98135
98594
  if (!process.stdin.isTTY) {
98136
98595
  return false;
98137
98596
  }
98138
- const rl = (0, import_promises.createInterface)({ input: process.stdin, output: process.stdout });
98597
+ const rl = (0, import_promises2.createInterface)({ input: process.stdin, output: process.stdout });
98139
98598
  try {
98140
98599
  const answer = await rl.question(`${message} [y/N] `);
98141
98600
  return /^y(es)?$/i.test(answer.trim());
@@ -98146,66 +98605,66 @@ async function confirm(message) {
98146
98605
  async function runSkillInstall(target, opts) {
98147
98606
  const log = opts.write ?? ((line2) => console.log(line2));
98148
98607
  if (!isValidTarget(target)) {
98149
- log(`${import_picocolors39.default.red("\u2717")} Unknown target: ${import_picocolors39.default.bold(target)}`);
98150
- log(import_picocolors39.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
98608
+ log(`${import_picocolors40.default.red("\u2717")} Unknown target: ${import_picocolors40.default.bold(target)}`);
98609
+ log(import_picocolors40.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
98151
98610
  return 1;
98152
98611
  }
98153
98612
  const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
98154
98613
  const content = generateSkillContent(target);
98155
98614
  if (target === "claude") {
98156
98615
  const legacyPath = getLegacyClaudeSkillPath({ global: opts.global, cwd: opts.cwd });
98157
- if ((0, import_node_fs16.existsSync)(legacyPath)) {
98616
+ if ((0, import_node_fs18.existsSync)(legacyPath)) {
98158
98617
  if (!opts.force) {
98159
98618
  const confirmed = await (opts.confirm ?? confirm)(
98160
98619
  `Found old flat skill file at ${legacyPath}. Replace it with the new directory layout?`
98161
98620
  );
98162
98621
  if (!confirmed) {
98163
- log(`${import_picocolors39.default.yellow("!")} Migration cancelled; existing skill left untouched.`);
98164
- log(import_picocolors39.default.dim(" Pass --force to replace without prompting."));
98622
+ log(`${import_picocolors40.default.yellow("!")} Migration cancelled; existing skill left untouched.`);
98623
+ log(import_picocolors40.default.dim(" Pass --force to replace without prompting."));
98165
98624
  return 1;
98166
98625
  }
98167
98626
  }
98168
98627
  try {
98169
- (0, import_node_fs16.rmSync)(legacyPath);
98170
- log(`${import_picocolors39.default.yellow("!")} Replaced old flat skill file: ${legacyPath}`);
98628
+ (0, import_node_fs18.rmSync)(legacyPath);
98629
+ log(`${import_picocolors40.default.yellow("!")} Replaced old flat skill file: ${legacyPath}`);
98171
98630
  } catch (err) {
98172
- log(`${import_picocolors39.default.red("\u2717")} Could not remove legacy skill file ${legacyPath}: ${err.message}`);
98631
+ log(`${import_picocolors40.default.red("\u2717")} Could not remove legacy skill file ${legacyPath}: ${err.message}`);
98173
98632
  return 1;
98174
98633
  }
98175
98634
  }
98176
98635
  }
98177
- if ((0, import_node_fs16.existsSync)(outPath) && !opts.force) {
98178
- log(`${import_picocolors39.default.red("\u2717")} ${outPath} already exists`);
98179
- log(import_picocolors39.default.dim(" pass --force to overwrite"));
98636
+ if ((0, import_node_fs18.existsSync)(outPath) && !opts.force) {
98637
+ log(`${import_picocolors40.default.red("\u2717")} ${outPath} already exists`);
98638
+ log(import_picocolors40.default.dim(" pass --force to overwrite"));
98180
98639
  return 1;
98181
98640
  }
98182
98641
  try {
98183
- (0, import_node_fs16.mkdirSync)((0, import_node_path18.dirname)(outPath), { recursive: true });
98184
- (0, import_node_fs16.writeFileSync)(outPath, content, "utf8");
98642
+ (0, import_node_fs18.mkdirSync)((0, import_node_path20.dirname)(outPath), { recursive: true });
98643
+ (0, import_node_fs18.writeFileSync)(outPath, content, "utf8");
98185
98644
  } catch (err) {
98186
- log(`${import_picocolors39.default.red("\u2717")} Could not write ${outPath}: ${err.message}`);
98645
+ log(`${import_picocolors40.default.red("\u2717")} Could not write ${outPath}: ${err.message}`);
98187
98646
  return 1;
98188
98647
  }
98189
- log(`${import_picocolors39.default.green("\u2713")} Horus skill installed \u2192 ${outPath}`);
98648
+ log(`${import_picocolors40.default.green("\u2713")} Horus skill installed \u2192 ${outPath}`);
98190
98649
  if (target === "claude") {
98191
- log(import_picocolors39.default.dim(" Claude Code will load this skill automatically."));
98192
- log(import_picocolors39.default.dim(" Invoke it with: /horus"));
98650
+ log(import_picocolors40.default.dim(" Claude Code will load this skill automatically."));
98651
+ log(import_picocolors40.default.dim(" Invoke it with: /horus"));
98193
98652
  } else if (target === "gemini") {
98194
- log(import_picocolors39.default.dim(" Gemini CLI will load this context file automatically."));
98653
+ log(import_picocolors40.default.dim(" Gemini CLI will load this context file automatically."));
98195
98654
  } else if (target === "cursor") {
98196
- log(import_picocolors39.default.dim(" Cursor will load this rule automatically."));
98655
+ log(import_picocolors40.default.dim(" Cursor will load this rule automatically."));
98197
98656
  } else if (target === "codex") {
98198
- log(import_picocolors39.default.dim(" Codex CLI will load this file automatically."));
98657
+ log(import_picocolors40.default.dim(" Codex CLI will load this file automatically."));
98199
98658
  } else {
98200
- log(import_picocolors39.default.dim(` Copy or reference this file in your ${target} agent instructions.`));
98659
+ log(import_picocolors40.default.dim(` Copy or reference this file in your ${target} agent instructions.`));
98201
98660
  }
98202
98661
  return 0;
98203
98662
  }
98204
98663
  async function runSkillPrint(target, opts) {
98205
98664
  const log = opts.write ?? ((line2) => console.log(line2));
98206
98665
  if (!isValidTarget(target)) {
98207
- log(`${import_picocolors39.default.red("\u2717")} Unknown target: ${import_picocolors39.default.bold(target)}`);
98208
- log(import_picocolors39.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
98666
+ log(`${import_picocolors40.default.red("\u2717")} Unknown target: ${import_picocolors40.default.bold(target)}`);
98667
+ log(import_picocolors40.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
98209
98668
  return 1;
98210
98669
  }
98211
98670
  process.stdout.write(generateSkillContent(target));
@@ -98214,8 +98673,8 @@ async function runSkillPrint(target, opts) {
98214
98673
  async function runSkillPath(target, opts) {
98215
98674
  const log = opts.write ?? ((line2) => console.log(line2));
98216
98675
  if (!isValidTarget(target)) {
98217
- log(`${import_picocolors39.default.red("\u2717")} Unknown target: ${import_picocolors39.default.bold(target)}`);
98218
- log(import_picocolors39.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
98676
+ log(`${import_picocolors40.default.red("\u2717")} Unknown target: ${import_picocolors40.default.bold(target)}`);
98677
+ log(import_picocolors40.default.dim(` Supported targets: ${SUPPORTED_TARGETS.join(", ")}`));
98219
98678
  return 1;
98220
98679
  }
98221
98680
  const outPath = getSkillInstallPath(target, { global: opts.global, cwd: opts.cwd });
@@ -98225,11 +98684,11 @@ async function runSkillPath(target, opts) {
98225
98684
 
98226
98685
  // ../../packages/cli/src/commands/update.ts
98227
98686
  init_cjs_shims();
98228
- var import_node_fs17 = require("fs");
98229
- var import_node_path19 = require("path");
98230
- var import_node_os4 = require("os");
98231
- var import_node_crypto5 = require("crypto");
98232
- var import_picocolors40 = __toESM(require_picocolors(), 1);
98687
+ var import_node_fs19 = require("fs");
98688
+ var import_node_path21 = require("path");
98689
+ var import_node_os5 = require("os");
98690
+ var import_node_crypto7 = require("crypto");
98691
+ var import_picocolors41 = __toESM(require_picocolors(), 1);
98233
98692
  var RELEASES_API = "https://api.github.com/repos/meritt-dev/horus/releases/latest";
98234
98693
  function parseVersion(v) {
98235
98694
  const m = v.replace(/^v/, "").match(/^(\d+)\.(\d+)\.(\d+)/);
@@ -98254,13 +98713,13 @@ async function fetchLatestRelease(_fetch = fetch) {
98254
98713
  return res.json();
98255
98714
  }
98256
98715
  function sha256hex(buf) {
98257
- return (0, import_node_crypto5.createHash)("sha256").update(buf).digest("hex");
98716
+ return (0, import_node_crypto7.createHash)("sha256").update(buf).digest("hex");
98258
98717
  }
98259
98718
  function resolveBinaryPath() {
98260
98719
  const raw = process.argv[1];
98261
98720
  if (!raw) throw new Error("Cannot determine binary path from process.argv[1]");
98262
98721
  try {
98263
- return (0, import_node_fs17.realpathSync)(raw);
98722
+ return (0, import_node_fs19.realpathSync)(raw);
98264
98723
  } catch {
98265
98724
  return raw;
98266
98725
  }
@@ -98270,33 +98729,33 @@ async function runUpdate(opts) {
98270
98729
  const _fetch = opts._fetch ?? fetch;
98271
98730
  const currentVersion = opts._currentVersion ?? HORUS_VERSION;
98272
98731
  write(`
98273
- ${import_picocolors40.default.bold("Horus update")}
98732
+ ${import_picocolors41.default.bold("Horus update")}
98274
98733
  `);
98275
- write(` Current version: ${import_picocolors40.default.cyan(currentVersion)}`);
98734
+ write(` Current version: ${import_picocolors41.default.cyan(currentVersion)}`);
98276
98735
  let release;
98277
98736
  try {
98278
98737
  release = await fetchLatestRelease(_fetch);
98279
98738
  } catch (e) {
98280
- write(` ${import_picocolors40.default.red("\u2717")} Could not reach GitHub: ${e.message}`);
98739
+ write(` ${import_picocolors41.default.red("\u2717")} Could not reach GitHub: ${e.message}`);
98281
98740
  return 1;
98282
98741
  }
98283
98742
  const latest = release.tag_name.replace(/^v/, "");
98284
- write(` Latest version: ${import_picocolors40.default.cyan(latest)}`);
98743
+ write(` Latest version: ${import_picocolors41.default.cyan(latest)}`);
98285
98744
  const newer = isNewer(latest, currentVersion);
98286
98745
  if (opts.check) {
98287
98746
  if (newer) {
98288
98747
  write(`
98289
- ${import_picocolors40.default.yellow("\u2192")} ${import_picocolors40.default.bold(latest)} is available.`);
98290
- write(` Run ${import_picocolors40.default.bold("horus update")} to upgrade.`);
98748
+ ${import_picocolors41.default.yellow("\u2192")} ${import_picocolors41.default.bold(latest)} is available.`);
98749
+ write(` Run ${import_picocolors41.default.bold("horus update")} to upgrade.`);
98291
98750
  } else {
98292
98751
  write(`
98293
- ${import_picocolors40.default.green("\u2713")} Already on the latest version.`);
98752
+ ${import_picocolors41.default.green("\u2713")} Already on the latest version.`);
98294
98753
  }
98295
98754
  return 0;
98296
98755
  }
98297
98756
  if (!newer && !opts.force) {
98298
98757
  write(`
98299
- ${import_picocolors40.default.green("\u2713")} Already on the latest version.`);
98758
+ ${import_picocolors41.default.green("\u2713")} Already on the latest version.`);
98300
98759
  return 0;
98301
98760
  }
98302
98761
  const tag = release.tag_name;
@@ -98305,8 +98764,8 @@ ${import_picocolors40.default.bold("Horus update")}
98305
98764
  const binaryAsset = release.assets.find((a) => a.name === assetName);
98306
98765
  const checksumAsset = release.assets.find((a) => a.name === checksumName);
98307
98766
  if (!binaryAsset) {
98308
- write(` ${import_picocolors40.default.red("\u2717")} Release ${tag} has no binary asset named '${assetName}'.`);
98309
- write(` Fallback: ${import_picocolors40.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
98767
+ write(` ${import_picocolors41.default.red("\u2717")} Release ${tag} has no binary asset named '${assetName}'.`);
98768
+ write(` Fallback: ${import_picocolors41.default.dim("curl -fsSL https://horus.sh/install.sh | bash")}`);
98310
98769
  return 1;
98311
98770
  }
98312
98771
  write(`
@@ -98316,7 +98775,7 @@ ${import_picocolors40.default.bold("Horus update")}
98316
98775
  redirect: "follow"
98317
98776
  });
98318
98777
  if (!binRes.ok || !binRes.body) {
98319
- write(` ${import_picocolors40.default.red("\u2717")} Download failed: ${binRes.status}`);
98778
+ write(` ${import_picocolors41.default.red("\u2717")} Download failed: ${binRes.status}`);
98320
98779
  return 1;
98321
98780
  }
98322
98781
  const binBuf = Buffer.from(await binRes.arrayBuffer());
@@ -98330,75 +98789,75 @@ ${import_picocolors40.default.bold("Horus update")}
98330
98789
  const expectedHash = csText.trim().split(/\s+/)[0];
98331
98790
  const actualHash = sha256hex(binBuf);
98332
98791
  if (expectedHash !== actualHash) {
98333
- write(` ${import_picocolors40.default.red("\u2717")} Checksum mismatch \u2014 download may be corrupt.`);
98792
+ write(` ${import_picocolors41.default.red("\u2717")} Checksum mismatch \u2014 download may be corrupt.`);
98334
98793
  write(` Expected: ${expectedHash}`);
98335
98794
  write(` Got: ${actualHash}`);
98336
98795
  return 1;
98337
98796
  }
98338
- write(` ${import_picocolors40.default.green("\u2713")} Checksum verified.`);
98797
+ write(` ${import_picocolors41.default.green("\u2713")} Checksum verified.`);
98339
98798
  }
98340
98799
  }
98341
98800
  let binaryPath;
98342
98801
  try {
98343
98802
  binaryPath = resolveBinaryPath();
98344
98803
  } catch (e) {
98345
- write(` ${import_picocolors40.default.red("\u2717")} ${e.message}`);
98804
+ write(` ${import_picocolors41.default.red("\u2717")} ${e.message}`);
98346
98805
  return 1;
98347
98806
  }
98348
- if (!(0, import_node_fs17.existsSync)(binaryPath)) {
98349
- write(` ${import_picocolors40.default.red("\u2717")} Binary not found at ${binaryPath}`);
98807
+ if (!(0, import_node_fs19.existsSync)(binaryPath)) {
98808
+ write(` ${import_picocolors41.default.red("\u2717")} Binary not found at ${binaryPath}`);
98350
98809
  return 1;
98351
98810
  }
98352
- const tmpPath = (0, import_node_path19.join)((0, import_node_os4.tmpdir)(), `horus-update-${tag}-${process.pid}`);
98811
+ const tmpPath = (0, import_node_path21.join)((0, import_node_os5.tmpdir)(), `horus-update-${tag}-${process.pid}`);
98353
98812
  const backupPath = `${binaryPath}.bak`;
98354
98813
  try {
98355
- (0, import_node_fs17.writeFileSync)(tmpPath, binBuf, { mode: 493 });
98814
+ (0, import_node_fs19.writeFileSync)(tmpPath, binBuf, { mode: 493 });
98356
98815
  } catch (e) {
98357
- write(` ${import_picocolors40.default.red("\u2717")} Could not write to temp dir: ${e.message}`);
98816
+ write(` ${import_picocolors41.default.red("\u2717")} Could not write to temp dir: ${e.message}`);
98358
98817
  return 1;
98359
98818
  }
98360
98819
  try {
98361
- (0, import_node_fs17.renameSync)(binaryPath, backupPath);
98820
+ (0, import_node_fs19.renameSync)(binaryPath, backupPath);
98362
98821
  try {
98363
- (0, import_node_fs17.renameSync)(tmpPath, binaryPath);
98364
- (0, import_node_fs17.chmodSync)(binaryPath, 493);
98822
+ (0, import_node_fs19.renameSync)(tmpPath, binaryPath);
98823
+ (0, import_node_fs19.chmodSync)(binaryPath, 493);
98365
98824
  } catch (e) {
98366
98825
  try {
98367
- (0, import_node_fs17.renameSync)(backupPath, binaryPath);
98826
+ (0, import_node_fs19.renameSync)(backupPath, binaryPath);
98368
98827
  } catch {
98369
98828
  }
98370
98829
  throw e;
98371
98830
  }
98372
98831
  try {
98373
- (0, import_node_fs17.unlinkSync)(backupPath);
98832
+ (0, import_node_fs19.unlinkSync)(backupPath);
98374
98833
  } catch {
98375
98834
  }
98376
98835
  } catch (e) {
98377
98836
  try {
98378
- (0, import_node_fs17.unlinkSync)(tmpPath);
98837
+ (0, import_node_fs19.unlinkSync)(tmpPath);
98379
98838
  } catch {
98380
98839
  }
98381
98840
  const msg = e.message ?? String(e);
98382
98841
  const needsSudo = e.code === "EACCES" || e.code === "EPERM";
98383
- write(` ${import_picocolors40.default.red("\u2717")} Could not replace binary at ${binaryPath}: ${msg}`);
98842
+ write(` ${import_picocolors41.default.red("\u2717")} Could not replace binary at ${binaryPath}: ${msg}`);
98384
98843
  if (needsSudo) {
98385
98844
  write(`
98386
98845
  Try with sudo:`);
98387
98846
  write(
98388
- ` ${import_picocolors40.default.dim(`sudo curl -fsSL https://github.com/meritt-dev/horus/releases/download/${tag}/${assetName} -o ${binaryPath} && sudo chmod +x ${binaryPath}`)}`
98847
+ ` ${import_picocolors41.default.dim(`sudo curl -fsSL https://github.com/meritt-dev/horus/releases/download/${tag}/${assetName} -o ${binaryPath} && sudo chmod +x ${binaryPath}`)}`
98389
98848
  );
98390
98849
  }
98391
98850
  return 1;
98392
98851
  }
98393
- write(` ${import_picocolors40.default.green("\u2713")} Updated: ${import_picocolors40.default.bold(currentVersion)} \u2192 ${import_picocolors40.default.bold(latest)}`);
98394
- write(` ${import_picocolors40.default.dim(binaryPath)}`);
98852
+ write(` ${import_picocolors41.default.green("\u2713")} Updated: ${import_picocolors41.default.bold(currentVersion)} \u2192 ${import_picocolors41.default.bold(latest)}`);
98853
+ write(` ${import_picocolors41.default.dim(binaryPath)}`);
98395
98854
  return 0;
98396
98855
  }
98397
98856
 
98398
98857
  // ../../packages/cli/src/commands/login.ts
98399
98858
  init_cjs_shims();
98400
98859
  var import_node_child_process9 = require("child_process");
98401
- var import_picocolors41 = __toESM(require_picocolors(), 1);
98860
+ var import_picocolors42 = __toESM(require_picocolors(), 1);
98402
98861
  var sleep2 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
98403
98862
  function openBrowser(url) {
98404
98863
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
@@ -98423,19 +98882,19 @@ async function loginWithToken(apiBaseUrl, token) {
98423
98882
  }
98424
98883
  });
98425
98884
  console.log(
98426
- `${import_picocolors41.default.green("\u2713")} Logged in as ${import_picocolors41.default.bold(me.user.primaryEmail)}. Run ${import_picocolors41.default.bold("horus cloud link")} to connect this repo.`
98885
+ `${import_picocolors42.default.green("\u2713")} Logged in as ${import_picocolors42.default.bold(me.user.primaryEmail)}. Run ${import_picocolors42.default.bold("horus cloud link")} to connect this repo.`
98427
98886
  );
98428
98887
  return 0;
98429
98888
  } catch (err) {
98430
98889
  if (err instanceof CloudOfflineError) {
98431
- console.error(import_picocolors41.default.red(err.message));
98890
+ console.error(import_picocolors42.default.red(err.message));
98432
98891
  return 1;
98433
98892
  }
98434
98893
  if (err instanceof CloudError && err.status === 401) {
98435
- console.error(import_picocolors41.default.red("That token is not valid. Create a fresh one in the dashboard and try again."));
98894
+ console.error(import_picocolors42.default.red("That token is not valid. Create a fresh one in the dashboard and try again."));
98436
98895
  return 1;
98437
98896
  }
98438
- console.error(import_picocolors41.default.red(`Login failed: ${err.message}`));
98897
+ console.error(import_picocolors42.default.red(`Login failed: ${err.message}`));
98439
98898
  return 1;
98440
98899
  }
98441
98900
  }
@@ -98446,19 +98905,19 @@ async function loginWithBrowser(apiBaseUrl) {
98446
98905
  session = await client.startCliSession();
98447
98906
  } catch (err) {
98448
98907
  if (err instanceof CloudOfflineError) {
98449
- console.error(import_picocolors41.default.red(err.message));
98908
+ console.error(import_picocolors42.default.red(err.message));
98450
98909
  return 1;
98451
98910
  }
98452
- console.error(import_picocolors41.default.red(`Could not start login: ${err.message}`));
98911
+ console.error(import_picocolors42.default.red(`Could not start login: ${err.message}`));
98453
98912
  return 1;
98454
98913
  }
98455
98914
  console.log("");
98456
98915
  console.log(` To sign in, open this URL in your browser:`);
98457
- console.log(` ${import_picocolors41.default.cyan(import_picocolors41.default.bold(session.verificationUri))}`);
98916
+ console.log(` ${import_picocolors42.default.cyan(import_picocolors42.default.bold(session.verificationUri))}`);
98458
98917
  console.log("");
98459
- console.log(` and enter the code: ${import_picocolors41.default.bold(session.userCode)}`);
98918
+ console.log(` and enter the code: ${import_picocolors42.default.bold(session.userCode)}`);
98460
98919
  console.log("");
98461
- console.log(import_picocolors41.default.dim(` Waiting for approval\u2026 (press Ctrl+C to cancel)`));
98920
+ console.log(import_picocolors42.default.dim(` Waiting for approval\u2026 (press Ctrl+C to cancel)`));
98462
98921
  openBrowser(session.verificationUriComplete);
98463
98922
  const deadline = Date.now() + session.expiresIn * 1e3;
98464
98923
  let intervalMs = Math.max(session.interval, 1) * 1e3;
@@ -98469,7 +98928,7 @@ async function loginWithBrowser(apiBaseUrl) {
98469
98928
  poll = await client.pollCliSession(session.deviceCode);
98470
98929
  } catch (err) {
98471
98930
  if (err instanceof CloudOfflineError) {
98472
- console.error(import_picocolors41.default.red(`
98931
+ console.error(import_picocolors42.default.red(`
98473
98932
  ${err.message}`));
98474
98933
  return 1;
98475
98934
  }
@@ -98482,14 +98941,14 @@ ${err.message}`));
98482
98941
  intervalMs += 2e3;
98483
98942
  break;
98484
98943
  case "denied":
98485
- console.error(import_picocolors41.default.red("\nLogin was denied in the browser."));
98944
+ console.error(import_picocolors42.default.red("\nLogin was denied in the browser."));
98486
98945
  return 1;
98487
98946
  case "expired":
98488
- console.error(import_picocolors41.default.red("\nThis login request expired. Run `horus login` again."));
98947
+ console.error(import_picocolors42.default.red("\nThis login request expired. Run `horus login` again."));
98489
98948
  return 1;
98490
98949
  case "approved":
98491
98950
  if (!poll.token || !poll.account) {
98492
- console.error(import_picocolors41.default.red("\nLogin approved but no token was returned. Try again."));
98951
+ console.error(import_picocolors42.default.red("\nLogin approved but no token was returned. Try again."));
98493
98952
  return 1;
98494
98953
  }
98495
98954
  writeAuth({
@@ -98499,12 +98958,12 @@ ${err.message}`));
98499
98958
  });
98500
98959
  console.log(
98501
98960
  `
98502
- ${import_picocolors41.default.green("\u2713")} Logged in as ${import_picocolors41.default.bold(poll.account.email)}. Run ${import_picocolors41.default.bold("horus cloud link")} to connect this repo.`
98961
+ ${import_picocolors42.default.green("\u2713")} Logged in as ${import_picocolors42.default.bold(poll.account.email)}. Run ${import_picocolors42.default.bold("horus cloud link")} to connect this repo.`
98503
98962
  );
98504
98963
  return 0;
98505
98964
  }
98506
98965
  }
98507
- console.error(import_picocolors41.default.red("\nTimed out waiting for approval. Run `horus login` again."));
98966
+ console.error(import_picocolors42.default.red("\nTimed out waiting for approval. Run `horus login` again."));
98508
98967
  return 1;
98509
98968
  }
98510
98969
  async function runLogin(opts = {}) {
@@ -98518,7 +98977,7 @@ async function runLogin(opts = {}) {
98518
98977
  async function runLogout() {
98519
98978
  const auth = readAuth();
98520
98979
  if (!auth) {
98521
- console.log(import_picocolors41.default.dim("Not logged in."));
98980
+ console.log(import_picocolors42.default.dim("Not logged in."));
98522
98981
  return 0;
98523
98982
  }
98524
98983
  const session = authedClient();
@@ -98532,14 +98991,14 @@ async function runLogout() {
98532
98991
  }
98533
98992
  }
98534
98993
  clearAuth();
98535
- console.log(`${import_picocolors41.default.green("\u2713")} Logged out. Local mode still works.`);
98994
+ console.log(`${import_picocolors42.default.green("\u2713")} Logged out. Local mode still works.`);
98536
98995
  return 0;
98537
98996
  }
98538
98997
 
98539
98998
  // ../../packages/cli/src/commands/cloud.ts
98540
98999
  init_cjs_shims();
98541
- var import_picocolors42 = __toESM(require_picocolors(), 1);
98542
- var import_promises2 = require("readline/promises");
99000
+ var import_picocolors43 = __toESM(require_picocolors(), 1);
99001
+ var import_promises3 = require("readline/promises");
98543
99002
 
98544
99003
  // ../../packages/cli/src/lib/cloud/git.ts
98545
99004
  init_cjs_shims();
@@ -98583,7 +99042,7 @@ async function runCloudLink(opts = {}) {
98583
99042
  const root = repoRootOrCwd(opts.cwd);
98584
99043
  const session = authedClient();
98585
99044
  if (!session) {
98586
- console.error(import_picocolors42.default.red(`Not logged in. Run ${import_picocolors42.default.bold("horus login")} first.`));
99045
+ console.error(import_picocolors43.default.red(`Not logged in. Run ${import_picocolors43.default.bold("horus login")} first.`));
98587
99046
  return 1;
98588
99047
  }
98589
99048
  let ctx;
@@ -98593,7 +99052,7 @@ async function runCloudLink(opts = {}) {
98593
99052
  return reportCloudError(err);
98594
99053
  }
98595
99054
  const remote = detectGitRemote(root);
98596
- if (remote) console.log(import_picocolors42.default.dim(`Detected git remote: ${remote.remoteUrl}`));
99055
+ if (remote) console.log(import_picocolors43.default.dim(`Detected git remote: ${remote.remoteUrl}`));
98597
99056
  let resolved = null;
98598
99057
  let target = opts.project;
98599
99058
  if (!target) {
@@ -98609,7 +99068,7 @@ async function runCloudLink(opts = {}) {
98609
99068
  const org = ctx.organizations.find((o) => o.id === match.organizationId);
98610
99069
  if (ws && org) {
98611
99070
  target = `${org.slug}/${ws.slug}/${match.slug}`;
98612
- console.log(import_picocolors42.default.dim(`Matched existing project by remote: ${import_picocolors42.default.bold(target)}`));
99071
+ console.log(import_picocolors43.default.dim(`Matched existing project by remote: ${import_picocolors43.default.bold(target)}`));
98613
99072
  }
98614
99073
  }
98615
99074
  }
@@ -98618,8 +99077,8 @@ async function runCloudLink(opts = {}) {
98618
99077
  if (created === "aborted") return 1;
98619
99078
  if (!created) {
98620
99079
  console.error(
98621
- import_picocolors42.default.red("No project to link.") + `
98622
- Run ${import_picocolors42.default.bold("horus cloud link")} in a TTY to create one, pass ${import_picocolors42.default.bold("--project <org/workspace/project>")}, or create a project at ${import_picocolors42.default.bold("https://cloud.horus.sh")}.`
99080
+ import_picocolors43.default.red("No project to link.") + `
99081
+ Run ${import_picocolors43.default.bold("horus cloud link")} in a TTY to create one, pass ${import_picocolors43.default.bold("--project <org/workspace/project>")}, or create a project at ${import_picocolors43.default.bold("https://cloud.horus.sh")}.`
98623
99082
  );
98624
99083
  return 1;
98625
99084
  }
@@ -98629,8 +99088,8 @@ async function runCloudLink(opts = {}) {
98629
99088
  resolved = resolveTriple(ctx, target);
98630
99089
  if (!resolved) {
98631
99090
  console.error(
98632
- import_picocolors42.default.red(`You don't have access to ${import_picocolors42.default.bold(target)}.`) + `
98633
- Run ${import_picocolors42.default.bold("horus context list")} to see available projects.`
99091
+ import_picocolors43.default.red(`You don't have access to ${import_picocolors43.default.bold(target)}.`) + `
99092
+ Run ${import_picocolors43.default.bold("horus context list")} to see available projects.`
98634
99093
  );
98635
99094
  return 1;
98636
99095
  }
@@ -98643,10 +99102,10 @@ async function runCloudLink(opts = {}) {
98643
99102
  project: resolved.project
98644
99103
  });
98645
99104
  console.log(
98646
- `${import_picocolors42.default.green("\u2713")} Linked. Context for this repo is now ${import_picocolors42.default.bold(label)} ${import_picocolors42.default.dim("(cloud)")}.`
99105
+ `${import_picocolors43.default.green("\u2713")} Linked. Context for this repo is now ${import_picocolors43.default.bold(label)} ${import_picocolors43.default.dim("(cloud)")}.`
98647
99106
  );
98648
99107
  console.log(
98649
- import_picocolors42.default.dim(` Investigations here save to Horus Cloud. ${import_picocolors42.default.bold("horus context use local")} to switch back.`)
99108
+ import_picocolors43.default.dim(` Investigations here save to Horus Cloud. ${import_picocolors43.default.bold("horus context use local")} to switch back.`)
98650
99109
  );
98651
99110
  return 0;
98652
99111
  }
@@ -98654,11 +99113,11 @@ async function runCloudUnlink(opts = {}) {
98654
99113
  const root = repoRootOrCwd(opts.cwd);
98655
99114
  const cfg = readCloudConfig(root);
98656
99115
  if (!cfg || cfg.context !== "cloud") {
98657
- console.log(import_picocolors42.default.dim("This repo isn't linked to a cloud project."));
99116
+ console.log(import_picocolors43.default.dim("This repo isn't linked to a cloud project."));
98658
99117
  return 0;
98659
99118
  }
98660
99119
  clearCloudConfig(root);
98661
- console.log(`${import_picocolors42.default.green("\u2713")} Unlinked. This repo is back to ${import_picocolors42.default.bold("local")} mode.`);
99120
+ console.log(`${import_picocolors43.default.green("\u2713")} Unlinked. This repo is back to ${import_picocolors43.default.bold("local")} mode.`);
98662
99121
  return 0;
98663
99122
  }
98664
99123
  async function runCloudStatus(opts = {}) {
@@ -98666,18 +99125,18 @@ async function runCloudStatus(opts = {}) {
98666
99125
  const cfg = readCloudConfig(root);
98667
99126
  const session = authedClient();
98668
99127
  if (!isCloudActive(cfg) || !cfg?.project) {
98669
- console.log(`${import_picocolors42.default.bold("Context:")} local`);
98670
- console.log(`${import_picocolors42.default.bold("Storage:")} local Postgres (~/.horus)`);
99128
+ console.log(`${import_picocolors43.default.bold("Context:")} local`);
99129
+ console.log(`${import_picocolors43.default.bold("Storage:")} local Postgres (~/.horus)`);
98671
99130
  console.log(
98672
- import_picocolors42.default.dim(`Tip: ${import_picocolors42.default.bold("horus login")} then ${import_picocolors42.default.bold("horus cloud link")} to share investigations with your team.`)
99131
+ import_picocolors43.default.dim(`Tip: ${import_picocolors43.default.bold("horus login")} then ${import_picocolors43.default.bold("horus cloud link")} to share investigations with your team.`)
98673
99132
  );
98674
99133
  return 0;
98675
99134
  }
98676
99135
  const triple = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
98677
- console.log(`${import_picocolors42.default.bold("Context:")} ${triple} ${import_picocolors42.default.dim("(cloud)")}`);
99136
+ console.log(`${import_picocolors43.default.bold("Context:")} ${triple} ${import_picocolors43.default.dim("(cloud)")}`);
98678
99137
  let projectLine = `${cfg.project.slug}`;
98679
99138
  let metaLines = [];
98680
- let account = import_picocolors42.default.dim("not logged in");
99139
+ let account = import_picocolors43.default.dim("not logged in");
98681
99140
  if (session) {
98682
99141
  account = session.auth.account.email;
98683
99142
  try {
@@ -98692,9 +99151,9 @@ async function runCloudStatus(opts = {}) {
98692
99151
  } catch {
98693
99152
  }
98694
99153
  }
98695
- console.log(`${import_picocolors42.default.bold("Project:")} ${projectLine}`);
99154
+ console.log(`${import_picocolors43.default.bold("Project:")} ${projectLine}`);
98696
99155
  for (const line2 of metaLines) console.log(line2);
98697
- console.log(`${import_picocolors42.default.bold("Account:")} ${account}`);
99156
+ console.log(`${import_picocolors43.default.bold("Account:")} ${account}`);
98698
99157
  return 0;
98699
99158
  }
98700
99159
  async function runCloudSync(opts = {}) {
@@ -98702,13 +99161,13 @@ async function runCloudSync(opts = {}) {
98702
99161
  const cfg = readCloudConfig(root);
98703
99162
  if (!isCloudActive(cfg) || !cfg.project) {
98704
99163
  console.error(
98705
- import_picocolors42.default.red("This repo isn't linked to a cloud project.") + ` Run ${import_picocolors42.default.bold("horus cloud link")} first.`
99164
+ import_picocolors43.default.red("This repo isn't linked to a cloud project.") + ` Run ${import_picocolors43.default.bold("horus cloud link")} first.`
98706
99165
  );
98707
99166
  return 1;
98708
99167
  }
98709
99168
  const session = authedClient();
98710
99169
  if (!session) {
98711
- console.error(import_picocolors42.default.red(`Not logged in. Run ${import_picocolors42.default.bold("horus login")} first.`));
99170
+ console.error(import_picocolors43.default.red(`Not logged in. Run ${import_picocolors43.default.bold("horus login")} first.`));
98712
99171
  return 1;
98713
99172
  }
98714
99173
  const { db, sql: sql2 } = createDb(await resolveDbUrl(opts.config));
@@ -98729,34 +99188,34 @@ async function runCloudSync(opts = {}) {
98729
99188
  const target = `${cfg.organization?.slug}/${cfg.workspace?.slug}/${cfg.project.slug}`;
98730
99189
  if (uploadable.length === 0) {
98731
99190
  console.log(
98732
- import_picocolors42.default.dim(
99191
+ import_picocolors43.default.dim(
98733
99192
  `No local investigations with reports to upload.` + (skippedNoReport ? ` (${skippedNoReport} without a report skipped)` : "")
98734
99193
  )
98735
99194
  );
98736
99195
  return 0;
98737
99196
  }
98738
- console.log(import_picocolors42.default.bold(`Will upload ${uploadable.length} investigation(s) to ${target} ${import_picocolors42.default.dim("(cloud)")}:`));
99197
+ console.log(import_picocolors43.default.bold(`Will upload ${uploadable.length} investigation(s) to ${target} ${import_picocolors43.default.dim("(cloud)")}:`));
98739
99198
  for (const c of uploadable.slice(0, 20)) {
98740
- console.log(` ${import_picocolors42.default.dim(c.id.slice(0, 8))} ${(c.title ?? "untitled").slice(0, 70)}`);
99199
+ console.log(` ${import_picocolors43.default.dim(c.id.slice(0, 8))} ${(c.title ?? "untitled").slice(0, 70)}`);
98741
99200
  }
98742
- if (uploadable.length > 20) console.log(import_picocolors42.default.dim(` \u2026and ${uploadable.length - 20} more`));
98743
- if (skippedNoReport) console.log(import_picocolors42.default.dim(` (${skippedNoReport} local investigation(s) without a report will be skipped)`));
99201
+ if (uploadable.length > 20) console.log(import_picocolors43.default.dim(` \u2026and ${uploadable.length - 20} more`));
99202
+ if (skippedNoReport) console.log(import_picocolors43.default.dim(` (${skippedNoReport} local investigation(s) without a report will be skipped)`));
98744
99203
  if (opts.dryRun) {
98745
- console.log(import_picocolors42.default.dim("Dry run \u2014 nothing uploaded. Re-run without --dry-run to upload."));
99204
+ console.log(import_picocolors43.default.dim("Dry run \u2014 nothing uploaded. Re-run without --dry-run to upload."));
98746
99205
  return 0;
98747
99206
  }
98748
99207
  if (!opts.yes) {
98749
99208
  if (!process.stdin.isTTY) {
98750
99209
  console.error(
98751
- import_picocolors42.default.yellow(`Re-run with ${import_picocolors42.default.bold("--yes")} to upload, or ${import_picocolors42.default.bold("--dry-run")} to preview only.`)
99210
+ import_picocolors43.default.yellow(`Re-run with ${import_picocolors43.default.bold("--yes")} to upload, or ${import_picocolors43.default.bold("--dry-run")} to preview only.`)
98752
99211
  );
98753
99212
  return 1;
98754
99213
  }
98755
- const rl = (0, import_promises2.createInterface)({ input: process.stdin, output: process.stdout });
99214
+ const rl = (0, import_promises3.createInterface)({ input: process.stdin, output: process.stdout });
98756
99215
  try {
98757
99216
  const answer = (await rl.question(`Upload ${uploadable.length} investigation(s) to ${target}? (y/N) `)).trim().toLowerCase();
98758
99217
  if (answer !== "y" && answer !== "yes") {
98759
- console.log(import_picocolors42.default.dim("Aborted. Nothing uploaded."));
99218
+ console.log(import_picocolors43.default.dim("Aborted. Nothing uploaded."));
98760
99219
  return 0;
98761
99220
  }
98762
99221
  } finally {
@@ -98769,18 +99228,18 @@ async function runCloudSync(opts = {}) {
98769
99228
  try {
98770
99229
  const refs = await uploadInvestigationToCloud(session.client, cfg, c.report);
98771
99230
  created++;
98772
- console.log(`${import_picocolors42.default.green("\u2713")} ${import_picocolors42.default.dim(c.id.slice(0, 8))} \u2192 ${refs.investigationId}`);
99231
+ console.log(`${import_picocolors43.default.green("\u2713")} ${import_picocolors43.default.dim(c.id.slice(0, 8))} \u2192 ${refs.investigationId}`);
98773
99232
  } catch (err) {
98774
99233
  failed++;
98775
- console.error(`${import_picocolors42.default.red("\u2717")} ${import_picocolors42.default.dim(c.id.slice(0, 8))} ${err.message}`);
99234
+ console.error(`${import_picocolors43.default.red("\u2717")} ${import_picocolors43.default.dim(c.id.slice(0, 8))} ${err.message}`);
98776
99235
  }
98777
99236
  }
98778
99237
  console.log("");
98779
99238
  console.log(
98780
- `${import_picocolors42.default.bold("Sync complete:")} ${import_picocolors42.default.green(`${created} uploaded`)}, ${skippedNoReport} skipped, ` + (failed ? import_picocolors42.default.red(`${failed} failed`) : "0 failed") + "."
99239
+ `${import_picocolors43.default.bold("Sync complete:")} ${import_picocolors43.default.green(`${created} uploaded`)}, ${skippedNoReport} skipped, ` + (failed ? import_picocolors43.default.red(`${failed} failed`) : "0 failed") + "."
98781
99240
  );
98782
99241
  console.log(
98783
- import_picocolors42.default.dim("Local data was not modified. Re-running is safe \u2014 duplicates are deduped by idempotency key.")
99242
+ import_picocolors43.default.dim("Local data was not modified. Re-running is safe \u2014 duplicates are deduped by idempotency key.")
98784
99243
  );
98785
99244
  return failed > 0 ? 1 : 0;
98786
99245
  }
@@ -98788,12 +99247,12 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
98788
99247
  if (!process.stdin.isTTY && !yes) return null;
98789
99248
  if (ctx.organizations.length === 0) {
98790
99249
  console.error(
98791
- import_picocolors42.default.red("You're not a member of any organization yet.") + `
98792
- Create one at ${import_picocolors42.default.bold("https://cloud.horus.sh")} first.`
99250
+ import_picocolors43.default.red("You're not a member of any organization yet.") + `
99251
+ Create one at ${import_picocolors43.default.bold("https://cloud.horus.sh")} first.`
98793
99252
  );
98794
99253
  return null;
98795
99254
  }
98796
- const rl = (0, import_promises2.createInterface)({ input: process.stdin, output: process.stdout });
99255
+ const rl = (0, import_promises3.createInterface)({ input: process.stdin, output: process.stdout });
98797
99256
  const ask2 = async (q, def) => {
98798
99257
  if (yes && def !== void 0) return def;
98799
99258
  const a = (await rl.question(def ? `${q} (${def}): ` : `${q}: `)).trim();
@@ -98814,9 +99273,9 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
98814
99273
  const wsName = await ask2("New workspace name", repoName || "default");
98815
99274
  const ws = await client.createWorkspace(org.id, { name: wsName });
98816
99275
  workspace = { id: ws.id, slug: ws.slug };
98817
- console.log(import_picocolors42.default.dim(`Created workspace ${import_picocolors42.default.bold(ws.slug)}.`));
99276
+ console.log(import_picocolors43.default.dim(`Created workspace ${import_picocolors43.default.bold(ws.slug)}.`));
98818
99277
  } else {
98819
- console.log(`Workspaces in ${import_picocolors42.default.bold(org.slug)}:`);
99278
+ console.log(`Workspaces in ${import_picocolors43.default.bold(org.slug)}:`);
98820
99279
  orgWorkspaces.forEach((w, i) => console.log(` ${i + 1}) ${w.slug}`));
98821
99280
  console.log(` ${orgWorkspaces.length + 1}) + create a new workspace`);
98822
99281
  const pick2 = Number(await ask2("Workspace number", "1"));
@@ -98824,7 +99283,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
98824
99283
  const wsName = await ask2("New workspace name", repoName || "default");
98825
99284
  const ws = await client.createWorkspace(org.id, { name: wsName });
98826
99285
  workspace = { id: ws.id, slug: ws.slug };
98827
- console.log(import_picocolors42.default.dim(`Created workspace ${import_picocolors42.default.bold(ws.slug)}.`));
99286
+ console.log(import_picocolors43.default.dim(`Created workspace ${import_picocolors43.default.bold(ws.slug)}.`));
98828
99287
  } else {
98829
99288
  const chosen = orgWorkspaces[pick2 - 1] ?? orgWorkspaces[0];
98830
99289
  workspace = { id: chosen.id, slug: chosen.slug };
@@ -98836,7 +99295,7 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
98836
99295
  remoteUrl: remote?.remoteUrl,
98837
99296
  provider: remote ? inferProvider(remote.remoteUrl) : void 0
98838
99297
  });
98839
- console.log(import_picocolors42.default.dim(`Created project ${import_picocolors42.default.bold(project.slug)}.`));
99298
+ console.log(import_picocolors43.default.dim(`Created project ${import_picocolors43.default.bold(project.slug)}.`));
98840
99299
  return {
98841
99300
  organization: { id: org.id, slug: org.slug },
98842
99301
  workspace,
@@ -98850,6 +99309,297 @@ async function createProjectFromRepo(client, ctx, remote, yes) {
98850
99309
  }
98851
99310
  }
98852
99311
 
99312
+ // ../../packages/cli/src/commands/telemetry.ts
99313
+ init_cjs_shims();
99314
+ var import_picocolors45 = __toESM(require_picocolors(), 1);
99315
+ var import_node_crypto8 = require("crypto");
99316
+
99317
+ // ../../packages/cli/src/lib/telemetry/transport.ts
99318
+ init_cjs_shims();
99319
+ var CHUNK = 100;
99320
+ var DEFAULT_TIMEOUT_MS = 1500;
99321
+ function telemetryBaseUrl() {
99322
+ return process.env.HORUS_TELEMETRY_URL ?? process.env.HORUS_API_BASE_URL ?? readAuth()?.apiBaseUrl ?? DEFAULT_API_BASE_URL;
99323
+ }
99324
+ async function postBatch(url, events, signal) {
99325
+ const res = await fetch(url, {
99326
+ method: "POST",
99327
+ headers: { "content-type": "application/json" },
99328
+ body: JSON.stringify({ events }),
99329
+ signal
99330
+ });
99331
+ return res.ok;
99332
+ }
99333
+ async function flushTelemetry(opts = {}) {
99334
+ try {
99335
+ if (!resolveConsent().tierA) return;
99336
+ const events = readSpooledEvents();
99337
+ if (events.length === 0) return;
99338
+ const url = `${telemetryBaseUrl().replace(/\/$/, "")}/v1/telemetry`;
99339
+ const controller = new AbortController();
99340
+ const timer2 = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
99341
+ try {
99342
+ for (let i = 0; i < events.length; i += CHUNK) {
99343
+ const ok = await postBatch(url, events.slice(i, i + CHUNK), controller.signal);
99344
+ if (!ok) return;
99345
+ }
99346
+ clearSpool();
99347
+ } finally {
99348
+ clearTimeout(timer2);
99349
+ }
99350
+ } catch {
99351
+ }
99352
+ }
99353
+ async function deleteRemoteTelemetry(installId, opts = {}) {
99354
+ try {
99355
+ if (!installId) return;
99356
+ const url = `${telemetryBaseUrl().replace(/\/$/, "")}/v1/telemetry/delete`;
99357
+ const controller = new AbortController();
99358
+ const timer2 = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
99359
+ try {
99360
+ await fetch(url, {
99361
+ method: "POST",
99362
+ headers: { "content-type": "application/json" },
99363
+ body: JSON.stringify({ installId }),
99364
+ signal: controller.signal
99365
+ });
99366
+ } finally {
99367
+ clearTimeout(timer2);
99368
+ }
99369
+ } catch {
99370
+ }
99371
+ }
99372
+
99373
+ // ../../packages/cli/src/lib/telemetry/notice.ts
99374
+ init_cjs_shims();
99375
+ var import_picocolors44 = __toESM(require_picocolors(), 1);
99376
+ var PRIVACY_URL = "https://horus.sh/privacy";
99377
+ var SUPPRESS_FIRST_TOKEN = /* @__PURE__ */ new Set(["telemetry", "help"]);
99378
+ var SUPPRESS_FLAGS = /* @__PURE__ */ new Set(["-V", "--version", "-h", "--help"]);
99379
+ function shouldSuppressFor(argv) {
99380
+ const args = argv.slice(2);
99381
+ if (args.length === 0) return true;
99382
+ if (args.some((a) => SUPPRESS_FLAGS.has(a))) return true;
99383
+ const firstToken = args.find((a) => !a.startsWith("-"));
99384
+ return firstToken != null && SUPPRESS_FIRST_TOKEN.has(firstToken);
99385
+ }
99386
+ function banner() {
99387
+ const b2 = import_picocolors44.default.bold;
99388
+ const dim = import_picocolors44.default.dim;
99389
+ return [
99390
+ "",
99391
+ import_picocolors44.default.cyan(b2("Horus collects anonymous usage data to improve the tool.")),
99392
+ dim(" \u2022 On by default: command names, timings, success/failure, and where the"),
99393
+ dim(" engine ran out of evidence. No file contents, queries, or secrets."),
99394
+ dim(" \u2022 Off by default: your investigation inputs/outputs (opt in to help train"),
99395
+ ` ${dim("Horus)")} ${dim("\u2014 run")} ${b2("horus telemetry enable-content")}`,
99396
+ dim(" \u2022 If you use Horus Cloud, linked investigations sync to your workspace."),
99397
+ "",
99398
+ ` ${dim("Opt out anytime:")} ${b2("horus telemetry disable")} ${dim("\xB7")} ${b2("HORUS_TELEMETRY=0")} ${dim("\xB7")} ${b2("DO_NOT_TRACK=1")}`,
99399
+ ` ${dim("Details:")} ${import_picocolors44.default.underline(PRIVACY_URL)}`,
99400
+ ""
99401
+ ].join("\n");
99402
+ }
99403
+ function maybeShowFirstRunNotice(argv = process.argv) {
99404
+ try {
99405
+ const env = process.env;
99406
+ if (resolveConsent({ env }).forcedOff) return;
99407
+ if (isCI(env)) return;
99408
+ const state = loadOrInitTelemetryState();
99409
+ if (state.tierA.noticeShownAt) return;
99410
+ if (shouldSuppressFor(argv)) return;
99411
+ if (!process.stderr.isTTY) return;
99412
+ process.stderr.write(banner() + "\n");
99413
+ updateTelemetryState((s) => {
99414
+ s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
99415
+ });
99416
+ } catch {
99417
+ }
99418
+ }
99419
+
99420
+ // ../../packages/cli/src/commands/telemetry.ts
99421
+ function onOff(enabled) {
99422
+ return enabled ? import_picocolors45.default.green("on") : import_picocolors45.default.dim("off");
99423
+ }
99424
+ function warnIfEnvOverrides() {
99425
+ const decision = resolveConsent();
99426
+ if (decision.forcedOff) {
99427
+ console.log(
99428
+ import_picocolors45.default.yellow(
99429
+ `
99430
+ Note: telemetry is currently forced off by your environment (${decision.reason}).`
99431
+ )
99432
+ );
99433
+ console.log(import_picocolors45.default.dim("Your saved preference will apply once that override is removed."));
99434
+ } else if (decision.source === "ci") {
99435
+ console.log(
99436
+ import_picocolors45.default.yellow("\nNote: CI environment detected \u2014 telemetry stays off here regardless.")
99437
+ );
99438
+ }
99439
+ }
99440
+ async function runTelemetryStatus() {
99441
+ const state = readTelemetryState();
99442
+ const decision = resolveConsent({ state });
99443
+ console.log(import_picocolors45.default.bold("Horus telemetry"));
99444
+ console.log(
99445
+ ` Usage metadata (Tier A): ${onOff(decision.tierA)} ${import_picocolors45.default.dim(`[${decision.source}]`)}`
99446
+ );
99447
+ console.log(` Content sharing (Tier B): ${onOff(decision.tierB)}`);
99448
+ console.log(import_picocolors45.default.dim(` Reason: ${decision.reason}`));
99449
+ if (state) {
99450
+ console.log(import_picocolors45.default.dim(` Install ID: ${state.installId}`));
99451
+ console.log(
99452
+ import_picocolors45.default.dim(
99453
+ ` Saved: Tier A ${state.tierA.enabled ? "on" : "off"}, Tier B ${state.tierB.enabled ? "on" : "off"}`
99454
+ )
99455
+ );
99456
+ } else {
99457
+ console.log(import_picocolors45.default.dim(" No saved preference yet (defaults apply)."));
99458
+ }
99459
+ console.log(import_picocolors45.default.dim(` Config: ${telemetryPath()}`));
99460
+ console.log(import_picocolors45.default.dim(` Privacy: ${PRIVACY_URL}`));
99461
+ if (!state || decision.source === "env" || decision.source === "ci") {
99462
+ warnIfEnvOverrides();
99463
+ }
99464
+ return 0;
99465
+ }
99466
+ async function runTelemetryEnable() {
99467
+ updateTelemetryState((s) => {
99468
+ s.tierA.enabled = true;
99469
+ if (!s.tierA.noticeShownAt) s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
99470
+ });
99471
+ console.log(import_picocolors45.default.green("Anonymous usage metadata (Tier A) is now ON. Thank you!"));
99472
+ warnIfEnvOverrides();
99473
+ return 0;
99474
+ }
99475
+ async function runTelemetryDisable() {
99476
+ updateTelemetryState((s) => {
99477
+ s.tierA.enabled = false;
99478
+ s.tierB.enabled = false;
99479
+ if (!s.tierA.noticeShownAt) s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
99480
+ });
99481
+ console.log(import_picocolors45.default.yellow("All telemetry is now OFF (Tier A and Tier B)."));
99482
+ return 0;
99483
+ }
99484
+ async function runTelemetryEnableContent() {
99485
+ updateTelemetryState((s) => {
99486
+ s.tierA.enabled = true;
99487
+ s.tierB.enabled = true;
99488
+ s.tierB.enabledAt = (/* @__PURE__ */ new Date()).toISOString();
99489
+ if (!s.tierA.noticeShownAt) s.tierA.noticeShownAt = (/* @__PURE__ */ new Date()).toISOString();
99490
+ });
99491
+ console.log(
99492
+ import_picocolors45.default.green("Content sharing (Tier B) is now ON \u2014 redacted inputs/outputs help improve Horus.")
99493
+ );
99494
+ console.log(import_picocolors45.default.dim("Content is scrubbed of secrets/PII before it ever leaves your machine."));
99495
+ console.log(import_picocolors45.default.dim(`What this means: ${PRIVACY_URL}`));
99496
+ warnIfEnvOverrides();
99497
+ return 0;
99498
+ }
99499
+ async function runTelemetryDisableContent() {
99500
+ updateTelemetryState((s) => {
99501
+ s.tierB.enabled = false;
99502
+ });
99503
+ console.log(import_picocolors45.default.yellow("Content sharing (Tier B) is now OFF. Usage metadata is unchanged."));
99504
+ return 0;
99505
+ }
99506
+ async function runTelemetryResetId() {
99507
+ const next = (0, import_node_crypto8.randomUUID)();
99508
+ updateTelemetryState((s) => {
99509
+ s.installId = next;
99510
+ });
99511
+ console.log(import_picocolors45.default.green("Install ID reset."));
99512
+ console.log(import_picocolors45.default.dim(` New install ID: ${next}`));
99513
+ return 0;
99514
+ }
99515
+ async function runTelemetryDelete() {
99516
+ const installId = readTelemetryState()?.installId;
99517
+ if (installId) await deleteRemoteTelemetry(installId);
99518
+ deleteTelemetryState();
99519
+ clearSpool();
99520
+ console.log(import_picocolors45.default.green("Telemetry deleted \u2014 local state cleared and a server purge requested."));
99521
+ console.log(
99522
+ import_picocolors45.default.dim(
99523
+ "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."
99524
+ )
99525
+ );
99526
+ return 0;
99527
+ }
99528
+
99529
+ // ../../packages/cli/src/lib/telemetry/command-hooks.ts
99530
+ init_cjs_shims();
99531
+ function commandPath(cmd) {
99532
+ const parts = [];
99533
+ let current = cmd;
99534
+ while (current && current.parent) {
99535
+ parts.unshift(current.name());
99536
+ current = current.parent;
99537
+ }
99538
+ return parts.join(" ") || cmd.name();
99539
+ }
99540
+ function installCommandTelemetry(program2) {
99541
+ let startedAt = 0;
99542
+ program2.hook("preAction", (_thisCommand, actionCommand) => {
99543
+ startedAt = Date.now();
99544
+ track({
99545
+ type: "command.invoked",
99546
+ command: commandPath(actionCommand),
99547
+ flags: extractFlagNames(process.argv)
99548
+ });
99549
+ });
99550
+ program2.hook("postAction", (_thisCommand, actionCommand) => {
99551
+ const exitCode = typeof process.exitCode === "number" ? process.exitCode : 0;
99552
+ track({
99553
+ type: "command.completed",
99554
+ command: commandPath(actionCommand),
99555
+ flags: extractFlagNames(process.argv),
99556
+ durationMs: startedAt ? Date.now() - startedAt : 0,
99557
+ exitCode,
99558
+ ok: exitCode === 0
99559
+ });
99560
+ });
99561
+ }
99562
+
99563
+ // ../../packages/cli/src/commands/feedback.ts
99564
+ init_cjs_shims();
99565
+ var import_picocolors46 = __toESM(require_picocolors(), 1);
99566
+ async function runFeedback(investigationId, opts = {}) {
99567
+ const id = investigationId?.trim();
99568
+ if (!id) {
99569
+ console.error(import_picocolors46.default.red("Usage: horus feedback <investigationId> [--resolved yes|partly|no]"));
99570
+ return 1;
99571
+ }
99572
+ if (opts.resolved !== void 0) {
99573
+ const resolved = parseResolved(opts.resolved);
99574
+ if (resolved === null) {
99575
+ console.error(import_picocolors46.default.red(`Invalid --resolved "${opts.resolved}". Use: yes | partly | no.`));
99576
+ return 1;
99577
+ }
99578
+ let manualEstimateMinutes = null;
99579
+ if (opts.manualEstimateMin !== void 0) {
99580
+ const n = Number.parseInt(opts.manualEstimateMin, 10);
99581
+ if (!Number.isFinite(n) || n < 0) {
99582
+ console.error(import_picocolors46.default.red(`Invalid --manual-estimate-min "${opts.manualEstimateMin}" (expect a non-negative integer).`));
99583
+ return 1;
99584
+ }
99585
+ manualEstimateMinutes = n;
99586
+ }
99587
+ submitFeedback({ investigationId: id, resolved, manualEstimateMinutes, horusSeconds: null, source: "flag" });
99588
+ console.log(import_picocolors46.default.dim(`Feedback recorded (${resolved}) \u2014 thanks, this helps improve Horus.`));
99589
+ return 0;
99590
+ }
99591
+ if (!process.stdin.isTTY) {
99592
+ console.error(
99593
+ import_picocolors46.default.yellow(
99594
+ "horus feedback needs an interactive terminal \u2014 or pass --resolved yes|partly|no for non-interactive (agent) use."
99595
+ )
99596
+ );
99597
+ return 1;
99598
+ }
99599
+ await runFeedbackPrompt(id, null);
99600
+ return 0;
99601
+ }
99602
+
98853
99603
  // ../../packages/cli/src/index.ts
98854
99604
  function buildProgram() {
98855
99605
  const program2 = new Command();
@@ -99271,11 +100021,58 @@ Examples:
99271
100021
  horus cloud sync --dry-run # preview what would upload
99272
100022
  horus cloud sync # confirm, then upload
99273
100023
  horus cloud sync --yes # upload without prompting`);
100024
+ program2.command("feedback [investigationId]").description("Leave quick impact feedback on an investigation (helps improve Horus)").option(
100025
+ "--resolved <verdict>",
100026
+ "did Horus point at the cause? yes | partly | no \u2014 non-interactive, for agents/scripts"
100027
+ ).option(
100028
+ "--manual-estimate-min <minutes>",
100029
+ "estimated minutes this would have taken manually (optional, pair with --resolved)"
100030
+ ).action(
100031
+ async (investigationId, opts) => {
100032
+ process.exitCode = await runFeedback(investigationId, opts);
100033
+ }
100034
+ );
100035
+ const telemetry = program2.command("telemetry").description("Manage anonymous usage telemetry that helps improve Horus");
100036
+ telemetry.command("status").description("Show the effective telemetry decision, saved preferences, and install ID").action(async () => {
100037
+ process.exitCode = await runTelemetryStatus();
100038
+ });
100039
+ telemetry.command("enable").description("Turn on anonymous usage metadata (Tier A)").action(async () => {
100040
+ process.exitCode = await runTelemetryEnable();
100041
+ });
100042
+ telemetry.command("disable").description("Turn off all telemetry (Tier A and Tier B)").action(async () => {
100043
+ process.exitCode = await runTelemetryDisable();
100044
+ });
100045
+ telemetry.command("enable-content").description("Opt in to sharing redacted investigation inputs/outputs (Tier B)").action(async () => {
100046
+ process.exitCode = await runTelemetryEnableContent();
100047
+ });
100048
+ telemetry.command("disable-content").description("Stop sharing inputs/outputs; keep usage metadata (Tier B off)").action(async () => {
100049
+ process.exitCode = await runTelemetryDisableContent();
100050
+ });
100051
+ telemetry.command("reset-id").description("Generate a new random install ID").action(async () => {
100052
+ process.exitCode = await runTelemetryResetId();
100053
+ });
100054
+ telemetry.command("delete").description("Delete local telemetry state (install ID + saved preferences)").action(async () => {
100055
+ process.exitCode = await runTelemetryDelete();
100056
+ });
100057
+ telemetry.addHelpText(
100058
+ "after",
100059
+ `
100060
+ Defaults: usage metadata ON (with this notice), content sharing OFF.
100061
+ Hard opt-out via env: HORUS_TELEMETRY=0 or DO_NOT_TRACK=1.
100062
+
100063
+ Examples:
100064
+ horus telemetry status
100065
+ horus telemetry disable
100066
+ horus telemetry enable-content`
100067
+ );
99274
100068
  return program2;
99275
100069
  }
99276
100070
  async function run(argv = process.argv) {
100071
+ maybeShowFirstRunNotice(argv);
99277
100072
  const program2 = buildProgram();
100073
+ installCommandTelemetry(program2);
99278
100074
  await program2.parseAsync(argv);
100075
+ await flushTelemetry();
99279
100076
  }
99280
100077
 
99281
100078
  // src/index.ts