@indigoai-us/hq-cli 5.273.0 → 5.274.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 (51) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +32 -1
  3. package/dist/command-catalog.generated.d.ts +72 -0
  4. package/dist/command-catalog.generated.js +92 -0
  5. package/dist/command-registration-plan.d.ts +12 -0
  6. package/dist/command-registration-plan.js +2 -0
  7. package/dist/commands/cloud.js +40 -0
  8. package/dist/commands/core-checkpoint.js +3 -1
  9. package/dist/commands/core-command-catalog.d.ts +32 -0
  10. package/dist/commands/core-command-catalog.js +58 -0
  11. package/dist/commands/core-ontology.js +9 -4
  12. package/dist/commands/core.d.ts +4 -0
  13. package/dist/commands/core.js +79 -75
  14. package/dist/commands/deploy-comments.d.ts +3 -0
  15. package/dist/commands/deploy-comments.js +159 -0
  16. package/dist/commands/lanes-move.d.ts +12 -0
  17. package/dist/commands/lanes-move.js +342 -0
  18. package/dist/commands/lanes-resume.js +3 -1
  19. package/dist/commands/lanes-shared.d.ts +36 -1
  20. package/dist/commands/lanes-shared.js +59 -0
  21. package/dist/commands/lanes.d.ts +1 -1
  22. package/dist/commands/lanes.js +204 -456
  23. package/dist/commands/report.js +2 -14
  24. package/dist/fast-core.js +1 -1
  25. package/dist/{commands/scaffold-fast.d.ts → fast-scaffold.d.ts} +1 -1
  26. package/dist/{commands/scaffold-fast.js → fast-scaffold.js} +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +2 -2
  29. package/dist/lib/checkpoint-ontology/apply.d.ts +8 -0
  30. package/dist/lib/checkpoint-ontology/apply.js +33 -0
  31. package/dist/lib/checkpoint-ontology/plan.d.ts +15 -0
  32. package/dist/lib/checkpoint-ontology/plan.js +15 -0
  33. package/dist/lib/deploy-api.d.ts +6 -0
  34. package/dist/lib/deploy-api.js +21 -0
  35. package/dist/lib/doctor/fix/apply.d.ts +17 -2
  36. package/dist/lib/doctor/fix/apply.js +111 -57
  37. package/dist/lib/doctor/fix/json-output.d.ts +9 -1
  38. package/dist/lib/doctor/fix/json-output.js +11 -0
  39. package/dist/lib/first-sync-next-step.d.ts +37 -0
  40. package/dist/lib/first-sync-next-step.js +189 -0
  41. package/dist/main.js +4 -1
  42. package/dist/utils/cli-telemetry.d.ts +7 -1
  43. package/dist/utils/cli-telemetry.js +84 -8
  44. package/dist/utils/contribution-table.d.ts +1 -1
  45. package/dist/utils/scaffold-fast-import-guard.d.ts +3 -0
  46. package/dist/utils/scaffold-fast-import-guard.js +28 -0
  47. package/dist/utils/scaffold-packlist-guard.d.ts +7 -0
  48. package/dist/utils/scaffold-packlist-guard.js +23 -0
  49. package/dist/utils/scaffold-sibling-references.d.ts +21 -0
  50. package/dist/utils/scaffold-sibling-references.js +156 -0
  51. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,10 +2,50 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.274.0] — 2026-09-28
6
+
7
+ ### Fixed
8
+
9
+ - `hq lanes grant` and other senior-authorized actions treat a missing `senior`
10
+ key in valid session metadata as `user`; explicit non-user values still refuse
11
+ authorization with a reason.
12
+ - Native Outpost message and resume routes now carry the owner's hire hierarchy,
13
+ authorize local-host resume contexts on their owner, and continue lane lookup
14
+ when another registered Outpost is unavailable.
15
+
16
+ ### Added
17
+
18
+ - Guide interactive CLI users to their first work-bearing sync and confirm it
19
+ once, behind the `cli.first-sync-next-step` HQ flag.
20
+ - Add `hq core commands --json` for the registered command catalog and guard the fast path, sibling references, and packaged assets.
21
+ - Add a corpus recorder and differential runner for scaffold migration scripts.
22
+ - Add `hq deploy comments list` and `read` for comments on apps in the caller's org.
23
+ - Checkpoint ontology: behind `checkpoint.ontology.named-evidence` (off by
24
+ default), a knowledge link to the company's own company entity, or a
25
+ concept linked to its same-name project, needs evidence that names it as a
26
+ word. A name that only appears inside a path, domain, handle or tag, such as
27
+ `indigoai-us/hq` or `scope: company:indigo`, is rejected as
28
+ `unnamed_in_evidence` (CO-57).
29
+
30
+ ### Fixed
31
+
32
+ - Add byte-for-byte checks for files fetched by `hq files get` and for
33
+ `company.yaml` after cloud demotion, using hq-core-staging's company seed.
34
+
35
+ ### Changed
36
+
37
+ - Internal: moved the move, move-target and placement subcommands out of
38
+ `src/commands/lanes.ts`; no behaviour change.
39
+
5
40
  ## [5.273.0] — 2026-09-28
6
41
 
7
42
  ### Fixed
8
43
 
44
+ - `hq doctor --fix` rechecks after each repair and applies newly found fixes
45
+ for up to three passes. It reports each repair's pass and lists every backup
46
+ restore command.
47
+ - `hq doctor --fix --json` includes each repair's pass and backup, and its
48
+ restore command now undoes changes from every repair pass.
9
49
  - Wrapped merge commands now pass through both hold and review checks. If a
10
50
  merge command cannot be parsed, it is allowed with an unresolved repository
11
51
  when no enabled hold or review check applies.
package/README.md CHANGED
@@ -7,6 +7,25 @@ npm install -g @indigoai-us/hq-cli
7
7
  hq --help
8
8
  ```
9
9
 
10
+ ## Deploy comments
11
+
12
+ List comments left on an app in your current hq-deploy organization. Use the
13
+ app's id or exact name; the CLI resolves it from the apps visible in that org.
14
+ The list shows id, status, creation time, author display name, anchor mode and
15
+ the first body line. `--status` accepts `open`, `resolved` or `all` (default).
16
+ Use `--json` to print the API's comment objects with the `commentsEnabled`
17
+ value.
18
+
19
+ ```bash
20
+ hq deploy comments list <app> [--status open|resolved|all] [--json]
21
+ hq deploy comments read <app> <commentId>
22
+ ```
23
+
24
+ `read` prints the complete comment, including body, anchor, deploy id, status
25
+ and timestamps. Reading works when comments are disabled on the app. The
26
+ control-plane API requires the app owner or an org admin; comments are scoped
27
+ to the caller's organization. Resolve/reopen commands are not included.
28
+
10
29
  ## Telemetry
11
30
 
12
31
  When you are signed in, `hq` may POST one `cli_session_started` event to
@@ -19,10 +38,14 @@ When you are signed in, `hq` may POST one `cli_session_started` event to
19
38
  - `consentBasis`: `disclosed` after the first-run notice, otherwise `no-consent`
20
39
  - `schemaVersion`: `1`
21
40
  - `properties.version`: the CLI version
41
+ - for the optional first-sync guidance, `properties.step` (`first_sync_next_step`),
42
+ `properties.outcome` (`shown` or `followed`), and `properties.flagKey`
43
+ (`cli.first-sync-next-step`)
22
44
 
23
45
  The request is authenticated with your cached session token so the control
24
46
  plane can accept the event. The body does not include command names, arguments,
25
- timings, file contents, or secrets.
47
+ timings, file contents, or secrets. The guidance fields are sent only when that
48
+ optional feature is enabled and a person sees or follows its next step.
26
49
 
27
50
  The first signed-in interactive run (stdout is a TTY, and the command is not
28
51
  `--json`) prints this line once on stderr and records that it was shown under
@@ -32,6 +55,14 @@ The first signed-in interactive run (stdout is a TTY, and the command is not
32
55
  HQ sends a usage event when you run a signed-in command (CLI version and a timestamp, never command names, file contents, or secrets) to improve the CLI. Set HQ_TELEMETRY=0 to turn this off.
33
56
  ```
34
57
 
58
+ When the optional first-sync guidance is enabled, the first interactive run that
59
+ is about to send its fields prints this line once instead, and records it under
60
+ `~/.hq/cli-telemetry-disclosed-first-sync-v1`:
61
+
62
+ ```
63
+ HQ sends a usage event when you run a signed-in command (CLI version and a timestamp; when enabled, first-sync guidance shown or followed and the flag key cli.first-sync-next-step; never command names, file contents, or secrets) to improve the CLI. Set HQ_TELEMETRY=0 to turn this off.
64
+ ```
65
+
35
66
  Turn telemetry off:
36
67
 
37
68
  ```bash
@@ -5458,6 +5458,18 @@ export declare const COMMAND_CATALOG: readonly [{
5458
5458
  }];
5459
5459
  readonly subcommands: readonly [];
5460
5460
  }];
5461
+ }, {
5462
+ readonly name: "commands";
5463
+ readonly description: "List registered hq core commands as JSON";
5464
+ readonly aliases: readonly [];
5465
+ readonly hidden: true;
5466
+ readonly usage: "[options]";
5467
+ readonly arguments: readonly [];
5468
+ readonly options: readonly [{
5469
+ readonly flags: "--json";
5470
+ readonly description: "Print the command catalog as JSON";
5471
+ }];
5472
+ readonly subcommands: readonly [];
5461
5473
  }];
5462
5474
  }, {
5463
5475
  readonly name: "search";
@@ -9472,6 +9484,66 @@ export declare const COMMAND_CATALOG: readonly [{
9472
9484
  }];
9473
9485
  readonly subcommands: readonly [];
9474
9486
  }];
9487
+ }, {
9488
+ readonly name: "deploy";
9489
+ readonly description: "Manage deployments in hq-deploy";
9490
+ readonly aliases: readonly [];
9491
+ readonly hidden: false;
9492
+ readonly usage: "[options] [command]";
9493
+ readonly arguments: readonly [];
9494
+ readonly options: readonly [];
9495
+ readonly subcommands: readonly [{
9496
+ readonly name: "comments";
9497
+ readonly description: "List and read comments on your deployed apps";
9498
+ readonly aliases: readonly [];
9499
+ readonly hidden: false;
9500
+ readonly usage: "[options] [command]";
9501
+ readonly arguments: readonly [];
9502
+ readonly options: readonly [];
9503
+ readonly subcommands: readonly [{
9504
+ readonly name: "list";
9505
+ readonly description: "List comments on an app in your org";
9506
+ readonly aliases: readonly [];
9507
+ readonly hidden: false;
9508
+ readonly usage: "[options] <app>";
9509
+ readonly arguments: readonly [{
9510
+ readonly name: "app";
9511
+ readonly required: true;
9512
+ readonly variadic: false;
9513
+ }];
9514
+ readonly options: readonly [{
9515
+ readonly flags: "--company <slug>";
9516
+ readonly description: "use this company instead of the active company";
9517
+ }, {
9518
+ readonly flags: "--json";
9519
+ readonly description: "print the API comment array as JSON";
9520
+ }, {
9521
+ readonly flags: "--status <status>";
9522
+ readonly description: "filter by open, resolved, or all";
9523
+ }];
9524
+ readonly subcommands: readonly [];
9525
+ }, {
9526
+ readonly name: "read";
9527
+ readonly description: "Read a comment on an app in your org";
9528
+ readonly aliases: readonly [];
9529
+ readonly hidden: false;
9530
+ readonly usage: "[options] <app> <commentId>";
9531
+ readonly arguments: readonly [{
9532
+ readonly name: "app";
9533
+ readonly required: true;
9534
+ readonly variadic: false;
9535
+ }, {
9536
+ readonly name: "commentId";
9537
+ readonly required: true;
9538
+ readonly variadic: false;
9539
+ }];
9540
+ readonly options: readonly [{
9541
+ readonly flags: "--company <slug>";
9542
+ readonly description: "use this company instead of the active company";
9543
+ }];
9544
+ readonly subcommands: readonly [];
9545
+ }];
9546
+ }];
9475
9547
  }, {
9476
9548
  readonly name: "staff";
9477
9549
  readonly description: "Staff-only commands (super-admin required)";
@@ -7040,6 +7040,21 @@ export const COMMAND_CATALOG = [
7040
7040
  "subcommands": []
7041
7041
  }
7042
7042
  ]
7043
+ },
7044
+ {
7045
+ "name": "commands",
7046
+ "description": "List registered hq core commands as JSON",
7047
+ "aliases": [],
7048
+ "hidden": true,
7049
+ "usage": "[options]",
7050
+ "arguments": [],
7051
+ "options": [
7052
+ {
7053
+ "flags": "--json",
7054
+ "description": "Print the command catalog as JSON"
7055
+ }
7056
+ ],
7057
+ "subcommands": []
7043
7058
  }
7044
7059
  ]
7045
7060
  },
@@ -12252,6 +12267,83 @@ export const COMMAND_CATALOG = [
12252
12267
  }
12253
12268
  ]
12254
12269
  },
12270
+ {
12271
+ "name": "deploy",
12272
+ "description": "Manage deployments in hq-deploy",
12273
+ "aliases": [],
12274
+ "hidden": false,
12275
+ "usage": "[options] [command]",
12276
+ "arguments": [],
12277
+ "options": [],
12278
+ "subcommands": [
12279
+ {
12280
+ "name": "comments",
12281
+ "description": "List and read comments on your deployed apps",
12282
+ "aliases": [],
12283
+ "hidden": false,
12284
+ "usage": "[options] [command]",
12285
+ "arguments": [],
12286
+ "options": [],
12287
+ "subcommands": [
12288
+ {
12289
+ "name": "list",
12290
+ "description": "List comments on an app in your org",
12291
+ "aliases": [],
12292
+ "hidden": false,
12293
+ "usage": "[options] <app>",
12294
+ "arguments": [
12295
+ {
12296
+ "name": "app",
12297
+ "required": true,
12298
+ "variadic": false
12299
+ }
12300
+ ],
12301
+ "options": [
12302
+ {
12303
+ "flags": "--company <slug>",
12304
+ "description": "use this company instead of the active company"
12305
+ },
12306
+ {
12307
+ "flags": "--json",
12308
+ "description": "print the API comment array as JSON"
12309
+ },
12310
+ {
12311
+ "flags": "--status <status>",
12312
+ "description": "filter by open, resolved, or all"
12313
+ }
12314
+ ],
12315
+ "subcommands": []
12316
+ },
12317
+ {
12318
+ "name": "read",
12319
+ "description": "Read a comment on an app in your org",
12320
+ "aliases": [],
12321
+ "hidden": false,
12322
+ "usage": "[options] <app> <commentId>",
12323
+ "arguments": [
12324
+ {
12325
+ "name": "app",
12326
+ "required": true,
12327
+ "variadic": false
12328
+ },
12329
+ {
12330
+ "name": "commentId",
12331
+ "required": true,
12332
+ "variadic": false
12333
+ }
12334
+ ],
12335
+ "options": [
12336
+ {
12337
+ "flags": "--company <slug>",
12338
+ "description": "use this company instead of the active company"
12339
+ }
12340
+ ],
12341
+ "subcommands": []
12342
+ }
12343
+ ]
12344
+ }
12345
+ ]
12346
+ },
12255
12347
  {
12256
12348
  "name": "staff",
12257
12349
  "description": "Staff-only commands (super-admin required)",
@@ -432,6 +432,18 @@ export declare const REGISTRATION_PLAN: readonly [{
432
432
  readonly parent: "program";
433
433
  readonly module: "./commands/report.js";
434
434
  readonly exportName: "registerReportCommand";
435
+ }, {
436
+ readonly type: "group";
437
+ readonly root: "deploy";
438
+ readonly id: "deploy";
439
+ readonly name: "deploy";
440
+ readonly description: "Manage deployments in hq-deploy";
441
+ }, {
442
+ readonly type: "registrar";
443
+ readonly root: "deploy";
444
+ readonly parent: "deploy";
445
+ readonly module: "./commands/deploy-comments.js";
446
+ readonly exportName: "registerDeployCommentsCommand";
435
447
  }, {
436
448
  readonly type: "group";
437
449
  readonly root: "staff";
@@ -74,6 +74,8 @@ export const REGISTRATION_PLAN = [
74
74
  { type: "registrar", root: "daemon", parent: "program", module: "./commands/daemon.js", exportName: "registerDaemonCommand", hidden: true },
75
75
  { type: "registrar", root: "bot", parent: "program", module: "./commands/bot.js", exportName: "registerBotCommand" },
76
76
  { type: "registrar", root: "report", parent: "program", module: "./commands/report.js", exportName: "registerReportCommand" },
77
+ { type: "group", root: "deploy", id: "deploy", name: "deploy", description: "Manage deployments in hq-deploy" },
78
+ { type: "registrar", root: "deploy", parent: "deploy", module: "./commands/deploy-comments.js", exportName: "registerDeployCommentsCommand" },
77
79
  // onboarding-test-accounts US-007 — super-admin staff CLI wrappers over the
78
80
  // hq-pro admin surface. New wrappers land under this group; the reset
79
81
  // command mirrors POST /v1/admin/test-account/reset.
@@ -21,6 +21,7 @@ import { DEFAULT_HQ_ROOT, ensureCognitoToken, buildVaultConfig, } from "../utils
21
21
  import { resolveUploadAuthor } from "../utils/upload-author.js";
22
22
  import { companyFolderExceedsThreshold, emitNarrowHint, isStrictRefusal, resolveBannerLevel, resolveNarrowHintPresentationLevel, resolveNarrowHintMinBytes, } from "../lib/narrow-hint-banner.js";
23
23
  import { beginSyncHealthReport, } from "../utils/client-health.js";
24
+ import { recordFirstWorkBearingSync } from "../lib/first-sync-next-step.js";
24
25
  /**
25
26
  * Terminal-failure signal for the sync fanout runners. The runners
26
27
  * (`runPushAll` / `runPullAll` / `runPullPersonal` / `runNowSingle` /
@@ -882,6 +883,11 @@ export function registerCloudCommands(program) {
882
883
  await syncHealth.failed();
883
884
  process.exit(1);
884
885
  }
886
+ recordFirstWorkBearingSync({
887
+ filesUploaded: result.filesUploaded,
888
+ filesDeleted: result.filesDeleted,
889
+ aborted: result.aborted,
890
+ });
885
891
  if (result.filesExcludedByScope > 0) {
886
892
  log(chalk.yellow(`\n⚠ Pushed ${result.filesUploaded} file(s) (${formatBytes(result.bytesUploaded)}, ` +
887
893
  `${result.filesSkipped} skipped, ${result.filesExcludedByScope} scope-excluded)`));
@@ -1026,6 +1032,10 @@ export function registerCloudCommands(program) {
1026
1032
  await syncHealth.failed();
1027
1033
  process.exit(1);
1028
1034
  }
1035
+ recordFirstWorkBearingSync({
1036
+ filesDownloaded: result.filesDownloaded,
1037
+ aborted: result.aborted,
1038
+ });
1029
1039
  console.log(chalk.green(`\n✓ Pulled ${result.filesDownloaded} file(s) (${formatBytes(result.bytesDownloaded)}, ${result.filesSkipped} skipped, ${result.conflicts} conflicts)`));
1030
1040
  // Emit the hint banner after success so it appears alongside the
1031
1041
  // summary line. Mirrors the wiring in runPullAll and runNowSingle.
@@ -1325,6 +1335,14 @@ async function runPullAll(hqRoot, onConflict, modeAllOverride, skipPersonal, for
1325
1335
  `(${formatBytes(result.bytesDownloaded)}) across ${result.attempted} ` +
1326
1336
  `target(s); ${result.conflicts} conflict(s); ${errored} error(s)`;
1327
1337
  console.log(errored > 0 ? chalk.yellow(summary) : chalk.green(summary));
1338
+ for (const row of result.perCompany) {
1339
+ if (row.result && !row.error) {
1340
+ recordFirstWorkBearingSync({
1341
+ filesDownloaded: row.result.filesDownloaded,
1342
+ aborted: row.result.aborted,
1343
+ });
1344
+ }
1345
+ }
1328
1346
  if (errored > 0)
1329
1347
  throw new SyncExitError(1);
1330
1348
  }
@@ -1355,6 +1373,10 @@ export async function runPullPersonal(hqRoot, onConflict) {
1355
1373
  throw new SyncExitError(1);
1356
1374
  }
1357
1375
  console.log(chalk.green(`\n✓ Pulled ${result.filesDownloaded} file(s) (${formatBytes(result.bytesDownloaded)}, ${result.filesSkipped} skipped, ${result.conflicts} conflicts)`));
1376
+ recordFirstWorkBearingSync({
1377
+ filesDownloaded: result.filesDownloaded,
1378
+ aborted: result.aborted,
1379
+ });
1358
1380
  }
1359
1381
  catch (err) {
1360
1382
  if (err instanceof SyncExitError)
@@ -1454,6 +1476,15 @@ async function runPushAll(hqRoot, message, onConflict, skipPersonal) {
1454
1476
  console.log(errored > 0 || result.filesExcludedByScope > 0
1455
1477
  ? chalk.yellow(summary)
1456
1478
  : chalk.green(summary));
1479
+ for (const row of result.perCompany) {
1480
+ if (row.result && !row.error) {
1481
+ recordFirstWorkBearingSync({
1482
+ filesUploaded: row.result.filesUploaded,
1483
+ filesDeleted: row.result.filesDeleted,
1484
+ aborted: row.result.aborted,
1485
+ });
1486
+ }
1487
+ }
1457
1488
  for (const row of result.perCompany) {
1458
1489
  const excluded = row.result?.filesExcludedByScope ?? 0;
1459
1490
  for (const line of scopeExcludedLines(excluded, row.prefixSet)) {
@@ -1544,6 +1575,11 @@ async function runNowSingle(hqRoot, company, personal, message, onConflict, mode
1544
1575
  console.log(chalk.yellow("\n⚠ Sync now aborted on push leg; pull skipped."));
1545
1576
  throw new SyncExitError(1);
1546
1577
  }
1578
+ recordFirstWorkBearingSync({
1579
+ filesUploaded: pushResult.filesUploaded,
1580
+ filesDeleted: pushResult.filesDeleted,
1581
+ aborted: pushResult.aborted,
1582
+ });
1547
1583
  // US-011: resolve membership sync-config so we can either nudge an
1548
1584
  // all-mode owner or refuse the pull when strict-mode is on. Skipped
1549
1585
  // for personal targets (personal vault has no membership row) and
@@ -1635,6 +1671,10 @@ async function runNowSingle(hqRoot, company, personal, message, onConflict, mode
1635
1671
  console.log(chalk.yellow("\n⚠ Sync now finished with pull leg aborted."));
1636
1672
  throw new SyncExitError(1);
1637
1673
  }
1674
+ recordFirstWorkBearingSync({
1675
+ filesDownloaded: pullResult.filesDownloaded,
1676
+ aborted: pullResult.aborted,
1677
+ });
1638
1678
  // Emit the hint banner after a successful pull so it appears at the
1639
1679
  // bottom of the summary rather than mid-stream. Size-gated: only a large
1640
1680
  // local folder is nudged.
@@ -17,6 +17,7 @@ import { toHqStateWriteError } from "../utils/hq-state-write-error.js";
17
17
  import { checkpointStampWriteErrorFor, isCheckpointStampWriteError, } from "../utils/checkpoint-stamp-write-error.js";
18
18
  import { readBackendPreference } from "../lib/checkpoint-ontology/plan.js";
19
19
  import { resolveOntologyCompany } from "../lib/checkpoint-ontology/company.js";
20
+ import { describeCoreCommand } from "./core-command-catalog.js";
20
21
  const DEFAULT_TRIGGER = "stop-gate";
21
22
  const BACKENDS = new Set(["auto", "claude", "codex", "grok", "none"]);
22
23
  // Pinned by operator directive 2026-09-15; change defaults here deliberately.
@@ -1175,7 +1176,7 @@ function reportUsage(error) {
1175
1176
  }
1176
1177
  /** Attach the native checkpoint command to the hidden `hq core` group. */
1177
1178
  export function registerCoreCheckpointCommand(core) {
1178
- core
1179
+ const checkpoint = core
1179
1180
  .command("checkpoint")
1180
1181
  .description("Record an end-of-turn checkpoint and start HQ maintenance")
1181
1182
  .option("--summary <text>", "1–2 sentence outcome summary")
@@ -1211,5 +1212,6 @@ export function registerCoreCheckpointCommand(core) {
1211
1212
  throw error;
1212
1213
  }
1213
1214
  });
1215
+ describeCoreCommand(checkpoint, { execution: "native", root: "live" });
1214
1216
  }
1215
1217
  //# sourceMappingURL=core-checkpoint.js.map
@@ -0,0 +1,32 @@
1
+ import type { Command } from "commander";
2
+ export type CoreCommandExecution = "native" | "bundled-shell" | "bundled-node";
3
+ export type CoreCommandRoot = "live" | "source" | "cwd";
4
+ export type CoreCommandListingEntry = {
5
+ name: string;
6
+ execution: CoreCommandExecution;
7
+ asset?: string;
8
+ root: CoreCommandRoot;
9
+ };
10
+ type CoreCommandMetadata = (Omit<CoreCommandListingEntry, "name"> & {
11
+ entries?: never;
12
+ }) | {
13
+ entries: readonly CoreCommandListingEntry[];
14
+ };
15
+ /** Mark the exact Commander registration that supplies a catalog row. */
16
+ export declare function describeCoreCommand(command: Command, metadata: CoreCommandMetadata): Command;
17
+ /**
18
+ * Reflect executable leaf commands from the Commander tree. Asset-backed rows
19
+ * are attached at the point where core.ts registers each table entry, so the
20
+ * JSON output cannot drift into a second, hand-maintained command list.
21
+ */
22
+ export declare function registeredCoreCommandListing(core: Command): CoreCommandListingEntry[];
23
+ export declare function fastScaffoldListingMismatches(listing: readonly CoreCommandListingEntry[], fastScripts: readonly {
24
+ name: string;
25
+ asset: string;
26
+ }[]): string[];
27
+ export declare function assertFastScaffoldListingParity(listing: readonly CoreCommandListingEntry[], fastScripts: readonly {
28
+ name: string;
29
+ asset: string;
30
+ }[]): void;
31
+ export {};
32
+ //# sourceMappingURL=core-command-catalog.d.ts.map
@@ -0,0 +1,58 @@
1
+ const metadataByCommand = new WeakMap();
2
+ /** Mark the exact Commander registration that supplies a catalog row. */
3
+ export function describeCoreCommand(command, metadata) {
4
+ metadataByCommand.set(command, metadata);
5
+ return command;
6
+ }
7
+ /**
8
+ * Reflect executable leaf commands from the Commander tree. Asset-backed rows
9
+ * are attached at the point where core.ts registers each table entry, so the
10
+ * JSON output cannot drift into a second, hand-maintained command list.
11
+ */
12
+ export function registeredCoreCommandListing(core) {
13
+ const entries = [];
14
+ const visit = (parent, parents) => {
15
+ for (const command of parent.commands) {
16
+ const path = [...parents, command.name()].join(" ");
17
+ if (command.commands.length > 0) {
18
+ visit(command, [...parents, command.name()]);
19
+ continue;
20
+ }
21
+ const metadata = metadataByCommand.get(command);
22
+ if (!metadata) {
23
+ throw new Error(`hq core command ${path} has no listing metadata`);
24
+ }
25
+ const described = metadata.entries
26
+ ? metadata.entries
27
+ : [{
28
+ name: path,
29
+ execution: metadata.execution,
30
+ ...(metadata.asset ? { asset: metadata.asset } : {}),
31
+ root: metadata.root,
32
+ }];
33
+ entries.push(...described);
34
+ }
35
+ };
36
+ visit(core, []);
37
+ return entries.sort((a, b) => a.name.localeCompare(b.name));
38
+ }
39
+ export function fastScaffoldListingMismatches(listing, fastScripts) {
40
+ const mismatches = [];
41
+ for (const fast of fastScripts) {
42
+ const listed = listing.find((entry) => entry.name === fast.name);
43
+ if (!listed) {
44
+ mismatches.push(`${fast.name} is missing from hq core commands`);
45
+ }
46
+ else if (listed.asset !== fast.asset) {
47
+ mismatches.push(`${fast.name} lists ${listed.asset ?? "no asset"}, fast path uses ${fast.asset}`);
48
+ }
49
+ }
50
+ return mismatches;
51
+ }
52
+ export function assertFastScaffoldListingParity(listing, fastScripts) {
53
+ const mismatches = fastScaffoldListingMismatches(listing, fastScripts);
54
+ if (mismatches.length > 0) {
55
+ throw new Error(mismatches.join("\n"));
56
+ }
57
+ }
58
+ //# sourceMappingURL=core-command-catalog.js.map
@@ -10,6 +10,7 @@
10
10
  import * as fs from "node:fs";
11
11
  import * as path from "node:path";
12
12
  import { resolveLiveRoot } from "../utils/hq-roots.js";
13
+ import { describeCoreCommand } from "./core-command-catalog.js";
13
14
  import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
14
15
  import { MULTI_WRITER_FLAG, planOntologyRun, } from "../lib/checkpoint-ontology/plan.js";
15
16
  import { applyOntologyRun, ApplyError } from "../lib/checkpoint-ontology/apply.js";
@@ -205,7 +206,7 @@ export function registerCoreOntologyCommand(core) {
205
206
  .command("ontology")
206
207
  .description("Checkpoint ontology steps run by the checkpoint sibling (gated by checkpoint.ontology)");
207
208
  const rootFor = (options) => resolveLiveRoot({ hqRoot: options.hqRoot ?? core.opts().hqRoot });
208
- ontology
209
+ const plan = ontology
209
210
  .command("plan")
210
211
  .description("Check the flag and list the changed company files for this checkpoint run")
211
212
  .option("--run-dir <path>", "checkpoint sibling run directory holding payload.json")
@@ -218,7 +219,8 @@ export function registerCoreOntologyCommand(core) {
218
219
  fail(error);
219
220
  }
220
221
  });
221
- ontology
222
+ describeCoreCommand(plan, { execution: "native", root: "live" });
223
+ const duplicates = ontology
222
224
  .command("duplicates")
223
225
  .description("List same-type entities whose names collide, with a proposed survivor (read-only)")
224
226
  .requiredOption("--company <slug>", "company to scan")
@@ -232,7 +234,8 @@ export function registerCoreOntologyCommand(core) {
232
234
  fail(error);
233
235
  }
234
236
  });
235
- ontology
237
+ describeCoreCommand(duplicates, { execution: "native", root: "live" });
238
+ const mergeCopies = ontology
236
239
  .command("merge-copies")
237
240
  .description("Merge '<name> N.md' entity copies into their original, then delete them from the vault (dry run by default)")
238
241
  .requiredOption("--company <slug>", "company to change")
@@ -249,7 +252,8 @@ export function registerCoreOntologyCommand(core) {
249
252
  fail(error);
250
253
  }
251
254
  });
252
- ontology
255
+ describeCoreCommand(mergeCopies, { execution: "native", root: "live" });
256
+ const apply = ontology
253
257
  .command("apply")
254
258
  .description("Validate the run's entity candidates and write them to the payload company")
255
259
  .option("--run-dir <path>", "checkpoint sibling run directory holding the plan and candidates")
@@ -262,5 +266,6 @@ export function registerCoreOntologyCommand(core) {
262
266
  fail(error);
263
267
  }
264
268
  });
269
+ describeCoreCommand(apply, { execution: "native", root: "live" });
265
270
  }
266
271
  //# sourceMappingURL=core-ontology.js.map
@@ -68,6 +68,8 @@ export type ScaffoldCommand = ScaffoldAsset & {
68
68
  export type RebuildIndexTarget = {
69
69
  /** Rebuild target — `hq core rebuild-index <target>`. */
70
70
  target: string;
71
+ /** Legacy scaffold companion, retained for compatibility and packaging. */
72
+ asset: string;
71
73
  /** Native renderer identifier. */
72
74
  renderer: IndexTarget;
73
75
  /** One-line summary, shown in `hq core --help`. */
@@ -87,6 +89,8 @@ export type NativeScaffoldCommand = ScaffoldAsset & {
87
89
  };
88
90
  type NativeUtilityCommand = {
89
91
  name: string;
92
+ /** Retained loose-file companion for old scaffold callers. */
93
+ asset: string;
90
94
  root: ScaffoldRoot;
91
95
  summary: string;
92
96
  run: (args: string[], context: {