@indigoai-us/hq-cli 5.309.0 → 5.310.0

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 (84) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/assets/scaffold/core/scripts/hq-dm-bind.sh +585 -0
  3. package/assets/scaffold/core/scripts/work-mesh-live-bind-trusted.sh +152 -0
  4. package/dist/command-catalog.generated.d.ts +30 -1
  5. package/dist/command-catalog.generated.js +37 -1
  6. package/dist/commands/agents.js +6 -0
  7. package/dist/commands/core-checkpoint-gate-setting.d.ts +0 -10
  8. package/dist/commands/core-checkpoint-gate-setting.js +3 -3
  9. package/dist/commands/core-dm-bind-native.d.ts +32 -0
  10. package/dist/commands/core-dm-bind-native.js +668 -0
  11. package/dist/commands/core-native-utilities.d.ts +2 -1
  12. package/dist/commands/core-native-utilities.js +4 -2
  13. package/dist/commands/core-ontology.d.ts +1 -9
  14. package/dist/commands/core-ontology.js +4 -4
  15. package/dist/commands/core-policy.d.ts +0 -3
  16. package/dist/commands/core-policy.js +3 -3
  17. package/dist/commands/core-work-mesh-bind-trusted-native.d.ts +32 -0
  18. package/dist/commands/core-work-mesh-bind-trusted-native.js +190 -0
  19. package/dist/commands/core.js +6 -0
  20. package/dist/commands/files.js +1 -1
  21. package/dist/commands/index-cmd.js +1 -1
  22. package/dist/commands/integrations-api.d.ts +9 -4
  23. package/dist/commands/integrations-api.js +9 -6
  24. package/dist/commands/integrations-manage.js +11 -3
  25. package/dist/commands/integrations.d.ts +8 -0
  26. package/dist/commands/integrations.js +83 -0
  27. package/dist/commands/lanes-checks.d.ts +0 -2
  28. package/dist/commands/lanes-checks.js +2 -2
  29. package/dist/commands/lanes.d.ts +0 -11
  30. package/dist/commands/lanes.js +6 -6
  31. package/dist/commands/mcp-registration.d.ts +5 -11
  32. package/dist/commands/mcp-registration.js +2 -6
  33. package/dist/lib/core-utils/sentry-report.d.ts +5 -11
  34. package/dist/lib/core-utils/sentry-report.js +2 -2
  35. package/dist/lib/db/local.d.ts +0 -5
  36. package/dist/lib/db/local.js +1 -1
  37. package/dist/lib/db/migrate.d.ts +0 -12
  38. package/dist/lib/db/migrate.js +5 -5
  39. package/dist/lib/db/sql.d.ts +0 -10
  40. package/dist/lib/db/sql.js +4 -8
  41. package/dist/lib/doctor/checks/codex-wiring.js +1 -33
  42. package/dist/lib/doctor/checks/grok-wiring.js +1 -34
  43. package/dist/lib/doctor/deep/effects.js +1 -3
  44. package/dist/lib/doctor/deep/sandbox.js +1 -3
  45. package/dist/lib/doctor/fixtures/schema.js +1 -3
  46. package/dist/lib/doctor/shared.d.ts +8 -0
  47. package/dist/lib/doctor/shared.js +38 -0
  48. package/dist/lib/lanes/autopilot.js +6 -4
  49. package/dist/lib/monitor.d.ts +2 -6
  50. package/dist/lib/monitor.js +5 -5
  51. package/dist/lib/report-publish.d.ts +1 -10
  52. package/dist/lib/report-publish.js +3 -9
  53. package/dist/lib/search-index/index.d.ts +7 -0
  54. package/dist/lib/search-index/index.js +21 -2
  55. package/dist/lib/search-index/qmd-exit-diagnostics.d.ts +5 -0
  56. package/dist/lib/search-index/qmd-exit-diagnostics.js +40 -0
  57. package/dist/lib/service-manager/launchd.d.ts +0 -7
  58. package/dist/lib/service-manager/launchd.js +2 -2
  59. package/dist/main.js +7 -2
  60. package/dist/startup-registration.js +9 -1
  61. package/dist/utils/client-health.d.ts +0 -4
  62. package/dist/utils/client-health.js +2 -2
  63. package/dist/utils/feedback-diagnostics.d.ts +2 -1
  64. package/dist/utils/feedback-log-bundle.d.ts +0 -25
  65. package/dist/utils/feedback-log-bundle.js +5 -5
  66. package/dist/utils/feedback-logs.d.ts +4 -38
  67. package/dist/utils/feedback-logs.js +7 -7
  68. package/dist/utils/feedback-screenshots.d.ts +0 -1
  69. package/dist/utils/feedback-screenshots.js +1 -1
  70. package/dist/utils/install-tree-torn.d.ts +19 -13
  71. package/dist/utils/install-tree-torn.js +77 -2
  72. package/dist/utils/local-files-overview.d.ts +0 -6
  73. package/dist/utils/local-files-overview.js +1 -1
  74. package/dist/utils/qmd-model-download-error.d.ts +0 -8
  75. package/dist/utils/qmd-model-download-error.js +1 -1
  76. package/dist/utils/qmd-query-document-error.d.ts +0 -6
  77. package/dist/utils/qmd-query-document-error.js +1 -1
  78. package/dist/utils/scaffold-fast-import-guard.d.ts +0 -1
  79. package/dist/utils/scaffold-fast-import-guard.js +1 -1
  80. package/dist/utils/scaffold-sibling-references.d.ts +3 -2
  81. package/dist/utils/sentry-fingerprint.js +17 -0
  82. package/dist/utils/version-gate.d.ts +4 -0
  83. package/dist/utils/version-gate.js +13 -0
  84. package/package.json +1 -1
@@ -23,7 +23,7 @@ function mappingsOverlap(left, right) {
23
23
  const commonPrefix = includes.sort((a, b) => b.length - a.length)[0] ?? "";
24
24
  return ![left.excludePrefix, right.excludePrefix].some((excluded) => excluded !== undefined && commonPrefix.startsWith(excluded));
25
25
  }
26
- export function ambiguousProjectMappings(mappings) {
26
+ function ambiguousProjectMappings(mappings) {
27
27
  const byProject = new Map();
28
28
  for (const mapping of mappings)
29
29
  byProject.set(mapping.projectId, [...(byProject.get(mapping.projectId) ?? []), mapping]);
@@ -641,7 +641,7 @@ function requiredRecordTimestamp(kind, record, projectDirectory, field, value) {
641
641
  return parseTimestamp(value, field, pathForTrackerRecord(projectDirectory, kind, record.id));
642
642
  return value;
643
643
  }
644
- export function listTrackerRecords(projectDirectory, kind) {
644
+ function listTrackerRecords(projectDirectory, kind) {
645
645
  try {
646
646
  return createLocalTrackerStore(projectDirectory).list(kind);
647
647
  }
@@ -112,8 +112,6 @@ export declare function parseQuestionBody(raw: unknown): QuestionBody | null;
112
112
  */
113
113
  export declare const ASK_BACKOFF_MS: readonly [5000, 15000];
114
114
  export declare const ASK_ATTEMPTS: number;
115
- /** Flip to true in the next release to make --priority mandatory. */
116
- export declare const ASK_PRIORITY_REQUIRED = false;
117
115
  export declare function casUpdateQuestion(hqRoot: string, questionId: string, updater: (current: QuestionRecord) => QuestionRecord): QuestionRecord;
118
116
  export declare function casUpdateLane(hqRoot: string, laneId: string, updater: (current: LaneRecord) => LaneRecord): LaneRecord;
119
117
  /**
@@ -128,12 +126,7 @@ export declare function composeWorkerBrief(worker: ResolvedLaneWorker, taskBrief
128
126
  * and the foreground-only rule.
129
127
  */
130
128
  export declare function composeGeneralBrief(taskBrief: string): string;
131
- export declare function parseMaxRuntimeSeconds(raw: unknown, workerId: string): number | undefined;
132
- export declare const PICKUP_TERMINAL_REASONS: readonly ["account_not_logged_in", "unknown_account", "account_disabled", "model_unsupported"];
133
129
  export declare const DISPATCH_TERMINAL_REASON: "outpost_dispatch_failed";
134
- export type PickupTerminalReason = (typeof PICKUP_TERMINAL_REASONS)[number];
135
- /** Only explicit non-persistence evidence can prove a failed dispatch has no owner snapshot. */
136
- export declare function isUndispatchedOutpostMirror(lane: LaneRecord): boolean;
137
130
  /**
138
131
  * The CAS updater a pickup failure applies. A lane that is already terminal
139
132
  * for any reason (a cancel that landed after the caller's last read, an
@@ -149,9 +142,5 @@ export declare function pickupFailureUpdater(reasonRaw: string, details: Record<
149
142
  export declare function applyOutpostAccountPickup(hqRoot: string, laneId: string, modelDeps?: KnownModelsDeps): LaneRecord;
150
143
  export declare function cancelPendingQuestionsForLane(hqRoot: string, laneId: string, nowIso: string, operatorReason?: string): QuestionRecord[];
151
144
  export declare function stopCommittedState(state: LaneRecord["state"]): "cancelled" | "killed";
152
- /** Stderr notice that `stop` ends a lane for good, naming the resumable path. */
153
- export declare function stopTerminalWarning(laneId: string): string;
154
- /** Stderr notice when an interrupted lane has no session to resume from. */
155
- export declare function interruptNotResumableWarning(laneId: string): string;
156
145
  export declare function registerLanesCommand(parent: Command, deps?: LanesCommandDeps): Command;
157
146
  //# sourceMappingURL=lanes.d.ts.map
@@ -2253,7 +2253,7 @@ function assertKnownOutpost(requested, known) {
2253
2253
  export const ASK_BACKOFF_MS = [5_000, 15_000];
2254
2254
  export const ASK_ATTEMPTS = ASK_BACKOFF_MS.length + 1;
2255
2255
  /** Flip to true in the next release to make --priority mandatory. */
2256
- export const ASK_PRIORITY_REQUIRED = false;
2256
+ const ASK_PRIORITY_REQUIRED = false;
2257
2257
  const ASK_PRIORITY_WARNING = "--priority will be required in the next release.";
2258
2258
  const QUESTION_PRIORITIES = ["P0", "P1", "P2"];
2259
2259
  function withCasRetry(fn, attempts = 5) {
@@ -2390,7 +2390,7 @@ export function composeGeneralBrief(taskBrief) {
2390
2390
  "",
2391
2391
  ].join("\n");
2392
2392
  }
2393
- export function parseMaxRuntimeSeconds(raw, workerId) {
2393
+ function parseMaxRuntimeSeconds(raw, workerId) {
2394
2394
  if (raw === undefined || raw === null || raw === "")
2395
2395
  return undefined;
2396
2396
  if (typeof raw === "number") {
@@ -2540,7 +2540,7 @@ async function storyBoardCheck(ctx, company, projectId, storyIds) {
2540
2540
  throw err;
2541
2541
  }
2542
2542
  }
2543
- export const PICKUP_TERMINAL_REASONS = [
2543
+ const PICKUP_TERMINAL_REASONS = [
2544
2544
  "account_not_logged_in",
2545
2545
  "unknown_account",
2546
2546
  "account_disabled",
@@ -2548,7 +2548,7 @@ export const PICKUP_TERMINAL_REASONS = [
2548
2548
  ];
2549
2549
  export const DISPATCH_TERMINAL_REASON = "outpost_dispatch_failed";
2550
2550
  /** Only explicit non-persistence evidence can prove a failed dispatch has no owner snapshot. */
2551
- export function isUndispatchedOutpostMirror(lane) {
2551
+ function isUndispatchedOutpostMirror(lane) {
2552
2552
  return lane.host.kind === "outpost" &&
2553
2553
  isTerminalLaneState(lane.state) &&
2554
2554
  lane.terminal_reason === DISPATCH_TERMINAL_REASON &&
@@ -7302,7 +7302,7 @@ async function runStop(ctx, laneId, reason, quiet = false) {
7302
7302
  });
7303
7303
  }
7304
7304
  /** Stderr notice that `stop` ends a lane for good, naming the resumable path. */
7305
- export function stopTerminalWarning(laneId) {
7305
+ function stopTerminalWarning(laneId) {
7306
7306
  return `hq lanes: warning: stop kills lane ${laneId} and it cannot be resumed. ` +
7307
7307
  `To pause it and keep it resumable, use hq lanes interrupt ${laneId}.\n`;
7308
7308
  }
@@ -7395,7 +7395,7 @@ function persistInterruptedSession(hqRoot, lane) {
7395
7395
  : { ...current, provider_session_id: sessionId, provider_session_id_absent: undefined });
7396
7396
  }
7397
7397
  /** Stderr notice when an interrupted lane has no session to resume from. */
7398
- export function interruptNotResumableWarning(laneId) {
7398
+ function interruptNotResumableWarning(laneId) {
7399
7399
  return `hq lanes: warning: lane ${laneId} was interrupted before its provider reported a session id, ` +
7400
7400
  `so hq lanes resume cannot continue it. Start a new lane for the remaining work.\n`;
7401
7401
  }
@@ -74,8 +74,8 @@ export declare class ConfigParseError extends McpRegistrationError {
74
74
  export declare class ConfigPermissionError extends McpRegistrationError {
75
75
  readonly code: "ConfigPermissionError";
76
76
  }
77
- export type CodexMcpCollisionOwner = 'hq' | 'foreign' | 'unknown';
78
- export type CodexMcpCollisionField = 'command' | 'args' | 'env' | 'url' | 'other';
77
+ type CodexMcpCollisionOwner = 'hq' | 'foreign' | 'unknown';
78
+ type CodexMcpCollisionField = 'command' | 'args' | 'env' | 'url' | 'other';
79
79
  /** Safe, value-free context for a Codex name collision reported to Sentry. */
80
80
  export interface CodexMcpCollisionDiagnostics {
81
81
  client: 'codex';
@@ -146,7 +146,7 @@ export declare function backupRoot(env: SafeWriteEnv): string;
146
146
  /** `~/.hq/logs/mcp-registry.log` — the append-only JSONL audit log (mirrors backupRoot style). */
147
147
  export declare function mcpRegistryLogPath(env: SafeWriteEnv): string;
148
148
  /** The action recorded on an audit line. */
149
- export type AuditAction = 'register' | 'unregister';
149
+ type AuditAction = 'register' | 'unregister';
150
150
  /**
151
151
  * The result recorded on an audit line:
152
152
  * - `registered` — a register write that changed the file.
@@ -156,7 +156,7 @@ export type AuditAction = 'register' | 'unregister';
156
156
  * foreign/unstamped entry left in place). PARTIAL is auditable.
157
157
  * - `error` — the surface emit threw (the operation failed for this surface).
158
158
  */
159
- export type AuditResult = 'registered' | 'unregistered' | 'noop' | 'skipped' | 'error';
159
+ type AuditResult = 'registered' | 'unregistered' | 'noop' | 'skipped' | 'error';
160
160
  /**
161
161
  * One append-only audit-log entry. EXACT shape (field order is the JSON key order):
162
162
  * `{ ts, action, pack, server, transport, target, file, prevHash, newHash, result }`.
@@ -213,8 +213,6 @@ export declare function codexConfigPath(env: SafeWriteEnv): string;
213
213
  export declare function isCodexInstalled(env: SafeWriteEnv): boolean;
214
214
  /** `~/.claude.json` — the PINNED single Claude surface (top-level mcpServers). */
215
215
  export declare function claudeConfigPath(env: SafeWriteEnv): string;
216
- /** `~/.mcp.json` — the project-pinned Claude surface (a symlink on this host). */
217
- export declare function mcpJsonPath(env: SafeWriteEnv): string;
218
216
  /** Outcome of {@link readConfigDoc}: a parsed doc plus whether the file pre-existed. */
219
217
  export interface ReadConfigResult<T> {
220
218
  /** The parsed doc, or the `emptyDoc` when the file is absent/empty. */
@@ -284,10 +282,6 @@ export declare function readConfigDoc<T>(target: string, format: ConfigFormat<T>
284
282
  * separate snippet block) cannot leak. The result never contains raw file content.
285
283
  */
286
284
  export declare function sanitizeParserMessage(message: string): string;
287
- /** Default stale-lock timeout — a lock older than this whose PID is dead is broken. */
288
- export declare const DEFAULT_LOCK_STALE_MS = 30000;
289
- /** Default total time to wait for a held lock before giving up. */
290
- export declare const DEFAULT_LOCK_WAIT_MS = 10000;
291
285
  /** A held advisory lock; release with {@link releaseLock} (idempotent). */
292
286
  export interface ConfigLock {
293
287
  /** The lockfile path (`~/.<basename>.hqlock`). */
@@ -752,7 +746,7 @@ export declare function mergeCodexServer(name: string, def: TomlTable): (doc: Co
752
746
  */
753
747
  export declare function registerCodexServer(opts: RegisterServerOptions): RegisterSurfaceResult | RegisterSurfaceSkip;
754
748
  /** Discriminates an un-registration outcome on one surface. */
755
- export type UnregisterOutcome =
749
+ type UnregisterOutcome =
756
750
  /** The provenance-stamped entry was found and removed (file changed). */
757
751
  'removed'
758
752
  /** The entry was already absent / never installed — a clean idempotent no-op. */
@@ -242,10 +242,6 @@ export function isCodexInstalled(env) {
242
242
  export function claudeConfigPath(env) {
243
243
  return path.join(env.home, '.claude.json');
244
244
  }
245
- /** `~/.mcp.json` — the project-pinned Claude surface (a symlink on this host). */
246
- export function mcpJsonPath(env) {
247
- return path.join(env.home, '.mcp.json');
248
- }
249
245
  /**
250
246
  * Built-in JSON {@link ConfigFormat} for the Claude surfaces. Pretty-prints with
251
247
  * a trailing newline; an empty doc is `{}`. Parsing the empty string is the
@@ -368,9 +364,9 @@ export function sanitizeParserMessage(message) {
368
364
  // Advisory O_EXCL lock (held across read->merge->write).
369
365
  // ---------------------------------------------------------------------------
370
366
  /** Default stale-lock timeout — a lock older than this whose PID is dead is broken. */
371
- export const DEFAULT_LOCK_STALE_MS = 30_000;
367
+ const DEFAULT_LOCK_STALE_MS = 30_000;
372
368
  /** Default total time to wait for a held lock before giving up. */
373
- export const DEFAULT_LOCK_WAIT_MS = 10_000;
369
+ const DEFAULT_LOCK_WAIT_MS = 10_000;
374
370
  /** The lockfile path for a target: `~/.<basename>.hqlock` (a sibling, hidden). */
375
371
  export function lockPathFor(target) {
376
372
  const dir = path.dirname(target);
@@ -18,7 +18,6 @@
18
18
  import * as Sentry from "@sentry/node";
19
19
  import type { CaptureContext, SeverityLevel } from "@sentry/node";
20
20
  export declare const DEFAULT_SENTRY_REPORT_TIMEOUT_MS = 750;
21
- export declare const MAX_SENTRY_REPORT_TIMEOUT_MS = 5000;
22
21
  export declare const MAX_SENTRY_REPORT_PAYLOAD_BYTES = 2048;
23
22
  export declare const MAX_SENTRY_REPORT_METADATA_VALUE_BYTES = 256;
24
23
  /**
@@ -34,11 +33,11 @@ export type HookSequenceEntry = {
34
33
  event: string;
35
34
  ms: number;
36
35
  };
37
- export type HookTimeoutDebugPhaseTiming = {
36
+ type HookTimeoutDebugPhaseTiming = {
38
37
  phase: string;
39
38
  elapsed_ms: number;
40
39
  };
41
- export type HookTimeoutDebugContext = {
40
+ type HookTimeoutDebugContext = {
42
41
  hook_name: string;
43
42
  hook_event: string;
44
43
  budget_ms: number;
@@ -52,9 +51,9 @@ export type HookTimeoutDebugContext = {
52
51
  spawn_ms?: number | "unavailable";
53
52
  process_count?: number | "unavailable";
54
53
  };
55
- export type SentryReportMetadataValue = string | number | boolean | HookSequenceEntry[] | HookTimeoutDebugContext;
54
+ type SentryReportMetadataValue = string | number | boolean | HookSequenceEntry[] | HookTimeoutDebugContext;
56
55
  export type SentryReportMetadata = Record<string, SentryReportMetadataValue>;
57
- export type SentryReportEvent = {
56
+ type SentryReportEvent = {
58
57
  type: string;
59
58
  message: string;
60
59
  fingerprint: string;
@@ -102,12 +101,6 @@ export type SentryReportResult = {
102
101
  export declare function readSentryReportStdin(stdin: AsyncIterable<Buffer | string>): Promise<string | null>;
103
102
  /** Unknown metadata is ignored before this cap is evaluated. */
104
103
  export declare function exceedsSentryReportMetadataKeyCap(keys: Iterable<string>): boolean;
105
- /**
106
- * Parse and validate the closed report schema without touching Sentry. The
107
- * function intentionally returns an ordinary result instead of throwing so a
108
- * hook can ignore the CLI exit code without creating an unhandled rejection.
109
- */
110
- export declare function parseSentryReportEvent(stdin: string): SentryReportResult;
111
104
  /**
112
105
  * Capture one validated Sentry message and wait only for the supplied bounded
113
106
  * flush budget. A zero result means the event entered the local transport and
@@ -117,4 +110,5 @@ export declare function parseSentryReportEvent(stdin: string): SentryReportResul
117
110
  * transport errors cannot echo event data back to a hook.
118
111
  */
119
112
  export declare function reportSentryEvent(stdin: string, options?: SentryReportOptions, dependencies?: SentryReportDependencies): Promise<SentryReportResult>;
113
+ export {};
120
114
  //# sourceMappingURL=sentry-report.d.ts.map
@@ -25,7 +25,7 @@ import { beforeSend } from "../../sentry-before-send.js";
25
25
  import { resolveLiveRoot } from "../../utils/hq-roots.js";
26
26
  import { hookTimeoutDebugContextEnabled } from "./hook-timeout-debug-context-flag.js";
27
27
  export const DEFAULT_SENTRY_REPORT_TIMEOUT_MS = 750;
28
- export const MAX_SENTRY_REPORT_TIMEOUT_MS = 5_000;
28
+ const MAX_SENTRY_REPORT_TIMEOUT_MS = 5_000;
29
29
  export const MAX_SENTRY_REPORT_PAYLOAD_BYTES = 2_048;
30
30
  export const MAX_SENTRY_REPORT_METADATA_VALUE_BYTES = 256;
31
31
  const MAX_SENTRY_REPORT_TYPE_BYTES = 64;
@@ -349,7 +349,7 @@ function invalidInput() {
349
349
  * function intentionally returns an ordinary result instead of throwing so a
350
350
  * hook can ignore the CLI exit code without creating an unhandled rejection.
351
351
  */
352
- export function parseSentryReportEvent(stdin) {
352
+ function parseSentryReportEvent(stdin) {
353
353
  if (byteLength(stdin) > MAX_SENTRY_REPORT_PAYLOAD_BYTES)
354
354
  return invalidInput();
355
355
  let parsed;
@@ -23,11 +23,6 @@ export interface LocalDbStatus {
23
23
  /** Fingerprint of path for logs that prefer not to echo full home paths. */
24
24
  pathFingerprint: string;
25
25
  }
26
- /**
27
- * Short non-reversible fingerprint of a path (for status display).
28
- * Not a secret — just avoids dumping full home paths when preferred.
29
- */
30
- export declare function fingerprintPath(filePath: string): string;
31
26
  /**
32
27
  * Open (and create if missing) the company local vault DB with WAL mode.
33
28
  * Idempotent: reopening an existing file does not recreate or wipe it.
@@ -12,7 +12,7 @@ const MIGRATIONS_TABLE = "hq_schema_migrations";
12
12
  * Short non-reversible fingerprint of a path (for status display).
13
13
  * Not a secret — just avoids dumping full home paths when preferred.
14
14
  */
15
- export function fingerprintPath(filePath) {
15
+ function fingerprintPath(filePath) {
16
16
  let h = 2166136261;
17
17
  for (let i = 0; i < filePath.length; i++) {
18
18
  h ^= filePath.charCodeAt(i);
@@ -5,9 +5,7 @@
5
5
  * companies/{company}/db/migrations/*.sql
6
6
  * Applied versions are recorded in hq_schema_migrations inside the local DB.
7
7
  */
8
- import type { LocalDb } from "./local.js";
9
8
  import type { LocalDbPathEnv } from "./paths.js";
10
- export declare const MIGRATIONS_TABLE = "hq_schema_migrations";
11
9
  export interface MigrateOptions extends LocalDbPathEnv {
12
10
  company: string;
13
11
  /**
@@ -24,16 +22,6 @@ export interface MigrateResult {
24
22
  head: string | null;
25
23
  }
26
24
  export declare function resolveMigrationsDir(hqRoot: string, companySlug: string): string;
27
- /**
28
- * Ensure migrations directory exists (creates empty dir on first migrate).
29
- */
30
- export declare function ensureMigrationsDir(hqRoot: string, companySlug: string): string;
31
- /**
32
- * List pending migration files in lexical/version order (filename sort).
33
- */
34
- export declare function listMigrationFiles(migrationsDir: string): string[];
35
- export declare function ensureMigrationsLedger(db: LocalDb): void;
36
- export declare function listAppliedVersions(db: LocalDb): Set<string>;
37
25
  /**
38
26
  * Apply pending migrations. On failure, does not mark the failed file applied.
39
27
  */
@@ -9,7 +9,7 @@ import fs from "node:fs";
9
9
  import path from "node:path";
10
10
  import { openLocalDb, readMigrationHead } from "./local.js";
11
11
  import { normalizeCompanySlugForLocalDb } from "./paths.js";
12
- export const MIGRATIONS_TABLE = "hq_schema_migrations";
12
+ const MIGRATIONS_TABLE = "hq_schema_migrations";
13
13
  export function resolveMigrationsDir(hqRoot, companySlug) {
14
14
  const slug = normalizeCompanySlugForLocalDb(companySlug);
15
15
  return path.join(hqRoot, "companies", slug, "db", "migrations");
@@ -17,7 +17,7 @@ export function resolveMigrationsDir(hqRoot, companySlug) {
17
17
  /**
18
18
  * Ensure migrations directory exists (creates empty dir on first migrate).
19
19
  */
20
- export function ensureMigrationsDir(hqRoot, companySlug) {
20
+ function ensureMigrationsDir(hqRoot, companySlug) {
21
21
  const dir = resolveMigrationsDir(hqRoot, companySlug);
22
22
  fs.mkdirSync(dir, { recursive: true });
23
23
  return dir;
@@ -25,7 +25,7 @@ export function ensureMigrationsDir(hqRoot, companySlug) {
25
25
  /**
26
26
  * List pending migration files in lexical/version order (filename sort).
27
27
  */
28
- export function listMigrationFiles(migrationsDir) {
28
+ function listMigrationFiles(migrationsDir) {
29
29
  if (!fs.existsSync(migrationsDir))
30
30
  return [];
31
31
  return fs
@@ -33,7 +33,7 @@ export function listMigrationFiles(migrationsDir) {
33
33
  .filter((f) => f.endsWith(".sql"))
34
34
  .sort((a, b) => a.localeCompare(b, "en"));
35
35
  }
36
- export function ensureMigrationsLedger(db) {
36
+ function ensureMigrationsLedger(db) {
37
37
  db.exec(`
38
38
  CREATE TABLE IF NOT EXISTS ${MIGRATIONS_TABLE} (
39
39
  version TEXT PRIMARY KEY NOT NULL,
@@ -41,7 +41,7 @@ export function ensureMigrationsLedger(db) {
41
41
  );
42
42
  `);
43
43
  }
44
- export function listAppliedVersions(db) {
44
+ function listAppliedVersions(db) {
45
45
  ensureMigrationsLedger(db);
46
46
  const rows = db
47
47
  .prepare(`SELECT version FROM ${MIGRATIONS_TABLE} ORDER BY version`)
@@ -27,14 +27,6 @@ export interface SqlRunResult {
27
27
  changes: number;
28
28
  readonly: boolean;
29
29
  }
30
- export declare function stripSqlNoise(sql: string): string;
31
- export declare function isWriteSql(sql: string): boolean;
32
- /**
33
- * Statements that must never run via `hq db sql`, even with --write.
34
- * ATTACH/DETACH would let a company-scoped session open another company's
35
- * vault.db by absolute path (category-1 isolation).
36
- */
37
- export declare function assertSqlAllowed(sql: string): void;
38
30
  /**
39
31
  * Resolve which file path to open, enforcing company isolation by default.
40
32
  */
@@ -44,6 +36,4 @@ export declare function resolveSqlDbPath(opts: SqlRunOptions): string;
44
36
  */
45
37
  export declare function runLocalSql(opts: SqlRunOptions): SqlRunResult;
46
38
  export declare function formatSqlResult(result: SqlRunResult, format?: "jsonl" | "table"): string;
47
- /** Test helper: company dir for isolation assertions. */
48
- export declare function companyLocalDbDir(company: string, env?: LocalDbPathEnv): string;
49
39
  //# sourceMappingURL=sql.d.ts.map
@@ -7,17 +7,17 @@
7
7
  */
8
8
  import Database from "better-sqlite3";
9
9
  import { openLocalDb } from "./local.js";
10
- import { normalizeCompanySlugForLocalDb, resolveLocalDbDir, resolveLocalDbPath, } from "./paths.js";
10
+ import { normalizeCompanySlugForLocalDb, resolveLocalDbPath, } from "./paths.js";
11
11
  const WRITE_PATTERN = /^\s*(INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|ALTER|TRUNCATE|REINDEX|VACUUM|BEGIN|COMMIT|ROLLBACK)\b/i;
12
12
  /** Always forbidden — opens other files/DBs and breaks company path isolation. */
13
13
  const FORBIDDEN_SQL_PATTERN = /\b(ATTACH|DETACH|LOAD_EXTENSION)\b/i;
14
- export function stripSqlNoise(sql) {
14
+ function stripSqlNoise(sql) {
15
15
  return sql
16
16
  .replace(/^\s*--[^\n]*\n/gm, "")
17
17
  .replace(/^\s*\/\*[\s\S]*?\*\//gm, "")
18
18
  .trim();
19
19
  }
20
- export function isWriteSql(sql) {
20
+ function isWriteSql(sql) {
21
21
  return WRITE_PATTERN.test(stripSqlNoise(sql));
22
22
  }
23
23
  /**
@@ -25,7 +25,7 @@ export function isWriteSql(sql) {
25
25
  * ATTACH/DETACH would let a company-scoped session open another company's
26
26
  * vault.db by absolute path (category-1 isolation).
27
27
  */
28
- export function assertSqlAllowed(sql) {
28
+ function assertSqlAllowed(sql) {
29
29
  const stripped = stripSqlNoise(sql);
30
30
  if (FORBIDDEN_SQL_PATTERN.test(stripped)) {
31
31
  throw new Error("ATTACH/DETACH/LOAD_EXTENSION are not allowed via hq db sql (tenant isolation); use the company-canonical local path only");
@@ -122,8 +122,4 @@ export function formatSqlResult(result, format = "jsonl") {
122
122
  .join("\n");
123
123
  return body ? `${header}\n${body}` : header;
124
124
  }
125
- /** Test helper: company dir for isolation assertions. */
126
- export function companyLocalDbDir(company, env) {
127
- return resolveLocalDbDir(company, env);
128
- }
129
125
  //# sourceMappingURL=sql.js.map
@@ -42,6 +42,7 @@
42
42
  import * as fs from "node:fs";
43
43
  import * as path from "node:path";
44
44
  import { parse as parseToml } from "smol-toml";
45
+ import { collectCommands, fileState } from "../shared.js";
45
46
  import { ALLOWED_DIVERGENCE_RELPATH, loadAllowedDivergence, } from "../allowed-divergence.js";
46
47
  /** Basename of the single-adapter script the current architecture routes to. */
47
48
  const CODEX_ADAPTER_BASENAME = "hq-codex-hook-adapter.sh";
@@ -549,44 +550,11 @@ function extractBraceBlock(source, from) {
549
550
  return source.slice(open + 1);
550
551
  }
551
552
  // --- shared low-level helpers --------------------------------------------------
552
- /** Collect every `command` string from a `{hooks:{Event:[{hooks:[…]}]}}` doc. */
553
- function collectCommands(parsed) {
554
- const commands = [];
555
- if (!parsed || typeof parsed !== "object")
556
- return commands;
557
- const hooks = parsed.hooks;
558
- if (!hooks || typeof hooks !== "object")
559
- return commands;
560
- for (const entries of Object.values(hooks)) {
561
- if (!Array.isArray(entries))
562
- continue;
563
- for (const entry of entries) {
564
- const inner = entry?.hooks;
565
- if (!Array.isArray(inner))
566
- continue;
567
- for (const hook of inner) {
568
- const command = hook?.command;
569
- if (typeof command === "string")
570
- commands.push(command);
571
- }
572
- }
573
- }
574
- return commands;
575
- }
576
553
  function resolvePath(hqRoot, target) {
577
554
  if (!target)
578
555
  return null;
579
556
  return path.isAbsolute(target) ? target : path.resolve(hqRoot, target);
580
557
  }
581
- function fileState(target) {
582
- try {
583
- const stat = fs.statSync(target);
584
- return { present: stat.isFile(), executable: (stat.mode & 0o111) !== 0 };
585
- }
586
- catch {
587
- return { present: false, executable: false };
588
- }
589
- }
590
558
  function filesEqual(a, b) {
591
559
  try {
592
560
  return fs.readFileSync(a).equals(fs.readFileSync(b));
@@ -22,6 +22,7 @@
22
22
  import * as fs from "node:fs";
23
23
  import * as os from "node:os";
24
24
  import * as path from "node:path";
25
+ import { collectCommands, fileState } from "../shared.js";
25
26
  /** The command a user runs to install the user-global Grok bridge. */
26
27
  export const GROK_TRUST_REMEDIATION = "bash core/scripts/grok-trust.sh";
27
28
  /** Files that grok-trust.sh installs into `~/.grok/hooks/` for the bridge. */
@@ -149,38 +150,4 @@ function bridgeInstalled(bridgeDir) {
149
150
  }
150
151
  return false;
151
152
  }
152
- // --- shared low-level helpers --------------------------------------------------
153
- /** Collect every `command` string from a `{hooks:{Event:[{hooks:[…]}]}}` doc. */
154
- function collectCommands(parsed) {
155
- const commands = [];
156
- if (!parsed || typeof parsed !== "object")
157
- return commands;
158
- const hooks = parsed.hooks;
159
- if (!hooks || typeof hooks !== "object")
160
- return commands;
161
- for (const entries of Object.values(hooks)) {
162
- if (!Array.isArray(entries))
163
- continue;
164
- for (const entry of entries) {
165
- const inner = entry?.hooks;
166
- if (!Array.isArray(inner))
167
- continue;
168
- for (const hook of inner) {
169
- const command = hook?.command;
170
- if (typeof command === "string")
171
- commands.push(command);
172
- }
173
- }
174
- }
175
- return commands;
176
- }
177
- function fileState(target) {
178
- try {
179
- const stat = fs.statSync(target);
180
- return { present: stat.isFile(), executable: (stat.mode & 0o111) !== 0 };
181
- }
182
- catch {
183
- return { present: false, executable: false };
184
- }
185
- }
186
153
  //# sourceMappingURL=grok-wiring.js.map
@@ -36,6 +36,7 @@ import { spawnSync } from "node:child_process";
36
36
  import { createHash } from "node:crypto";
37
37
  import * as fs from "node:fs";
38
38
  import * as path from "node:path";
39
+ import { isRecord } from "../shared.js";
39
40
  /**
40
41
  * Parse a fixture case's `effects:` field into a list of {@link
41
42
  * EffectExpectation}. Returns the list, or an error string naming what was
@@ -223,7 +224,4 @@ function hashFile(abs) {
223
224
  return null;
224
225
  }
225
226
  }
226
- function isRecord(value) {
227
- return typeof value === "object" && value !== null && !Array.isArray(value);
228
- }
229
227
  //# sourceMappingURL=effects.js.map
@@ -39,6 +39,7 @@ import * as fs from "node:fs";
39
39
  import * as os from "node:os";
40
40
  import * as path from "node:path";
41
41
  import * as yaml from "js-yaml";
42
+ import { isRecord } from "../shared.js";
42
43
  import { classifyCaseStatus, looksLikeFixture, } from "../fixtures/schema.js";
43
44
  import { HOOK_TESTS_RELDIR, registeredHookIds } from "../fixtures/discover.js";
44
45
  import { isSideEffectingHookId } from "./classify.js";
@@ -566,7 +567,4 @@ function oneLine(text) {
566
567
  const collapsed = text.replace(/\s+/g, " ").trim();
567
568
  return collapsed.length > 200 ? `${collapsed.slice(0, 197)}…` : collapsed;
568
569
  }
569
- function isRecord(value) {
570
- return typeof value === "object" && value !== null && !Array.isArray(value);
571
- }
572
570
  //# sourceMappingURL=sandbox.js.map
@@ -27,6 +27,7 @@
27
27
  * module owns only the classification semantics, so both the doctor and its
28
28
  * tests agree on what a case outcome means.
29
29
  */
30
+ import { isRecord } from "../shared.js";
30
31
  /**
31
32
  * Every fixture schema version this build of the CLI can interpret. A fixture
32
33
  * declaring a version outside this set is reported UNKNOWN, never PASS — see
@@ -242,7 +243,4 @@ export function classifyCaseStatus(met, expectedFailure) {
242
243
  return met ? "WARN" : "KNOWN-DEFECT";
243
244
  return met ? "PASS" : "FAIL";
244
245
  }
245
- function isRecord(value) {
246
- return typeof value === "object" && value !== null && !Array.isArray(value);
247
- }
248
246
  //# sourceMappingURL=schema.js.map
@@ -0,0 +1,8 @@
1
+ /** Collect every `command` string from a `{hooks:{Event:[{hooks:[…]}]}}` doc. */
2
+ export declare function collectCommands(parsed: unknown): string[];
3
+ export declare function fileState(target: string): {
4
+ present: boolean;
5
+ executable: boolean;
6
+ };
7
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
8
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1,38 @@
1
+ import * as fs from "node:fs";
2
+ /** Collect every `command` string from a `{hooks:{Event:[{hooks:[…]}]}}` doc. */
3
+ export function collectCommands(parsed) {
4
+ const commands = [];
5
+ if (!parsed || typeof parsed !== "object")
6
+ return commands;
7
+ const hooks = parsed.hooks;
8
+ if (!hooks || typeof hooks !== "object")
9
+ return commands;
10
+ for (const entries of Object.values(hooks)) {
11
+ if (!Array.isArray(entries))
12
+ continue;
13
+ for (const entry of entries) {
14
+ const inner = entry?.hooks;
15
+ if (!Array.isArray(inner))
16
+ continue;
17
+ for (const hook of inner) {
18
+ const command = hook?.command;
19
+ if (typeof command === "string")
20
+ commands.push(command);
21
+ }
22
+ }
23
+ }
24
+ return commands;
25
+ }
26
+ export function fileState(target) {
27
+ try {
28
+ const stat = fs.statSync(target);
29
+ return { present: stat.isFile(), executable: (stat.mode & 0o111) !== 0 };
30
+ }
31
+ catch {
32
+ return { present: false, executable: false };
33
+ }
34
+ }
35
+ export function isRecord(value) {
36
+ return typeof value === "object" && value !== null && !Array.isArray(value);
37
+ }
38
+ //# sourceMappingURL=shared.js.map
@@ -3,6 +3,7 @@ import * as path from "node:path";
3
3
  import { laneRunsOnHost } from "./admission.js";
4
4
  import { appendDropboxMessage } from "./dropbox.js";
5
5
  import { LanesError } from "./errors.js";
6
+ import { resolveLaneCompany } from "./lane-company.js";
6
7
  import { readLanesRegistryFlag } from "./registry-flag.js";
7
8
  import { readAllLanes } from "./store.js";
8
9
  export const LEAD_AUTOPILOT_FLAG = "lanes.lead-autopilot";
@@ -418,7 +419,8 @@ async function runLeadAutopilotPassUnlocked(input) {
418
419
  result.skipped.push({ lead_lane: registered.lane, reason: "lead_lane_missing" });
419
420
  continue;
420
421
  }
421
- if (lead.company === null) {
422
+ const company = resolveLaneCompany(hqRoot, lead).company;
423
+ if (!company) {
422
424
  result.skipped.push({ lead_lane: lead.lane_id, reason: "lead_company_unresolved" });
423
425
  continue;
424
426
  }
@@ -433,16 +435,16 @@ async function runLeadAutopilotPassUnlocked(input) {
433
435
  }
434
436
  let stateChanged = false;
435
437
  try {
436
- const enabled = await (input.readFlag ?? readLanesRegistryFlag)(LEAD_AUTOPILOT_FLAG, { company: lead.company });
438
+ const enabled = await (input.readFlag ?? readLanesRegistryFlag)(LEAD_AUTOPILOT_FLAG, { company });
437
439
  if (!enabled) {
438
440
  result.skipped.push({ lead_lane: lead.lane_id, reason: "flag_off" });
439
441
  continue;
440
442
  }
441
- const status = readStatus(hqRoot, registered.status, lead.company.slug);
443
+ const status = readStatus(hqRoot, registered.status, company.slug);
442
444
  const statusUpdatedAt = typeof status.updated_at === "string" && Number.isFinite(Date.parse(status.updated_at))
443
445
  ? new Date(Date.parse(status.updated_at)).toISOString()
444
446
  : undefined;
445
- const stories = projectStories(hqRoot, registered.prd, lead.company.slug, lead.project_id);
447
+ const stories = projectStories(hqRoot, registered.prd, company.slug, lead.project_id);
446
448
  const children = lanes.filter((lane) => lane.senior.kind === "lane" && lane.senior.id === lead.lane_id);
447
449
  const planned = planLeadAutopilotWakes({
448
450
  lead,