@indigoai-us/hq-cli 5.345.55 → 5.345.57

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 (66) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/assets/scaffold/core/scripts/derive-trigger-facts.sh +10 -1
  3. package/assets/scaffold/core/scripts/jobs-validate.sh +2 -1
  4. package/assets/scaffold/core/scripts/session-title.sh +18 -15
  5. package/dist/command-catalog.generated.d.ts +124 -0
  6. package/dist/command-catalog.generated.js +155 -0
  7. package/dist/commands/core-delegate-w1b.js +18 -5
  8. package/dist/commands/core-derive-trigger-facts-command.js +14 -0
  9. package/dist/commands/core-derive-trigger-facts-native.d.ts +1 -0
  10. package/dist/commands/core-derive-trigger-facts-native.js +20 -1
  11. package/dist/commands/doctor.d.ts +1 -0
  12. package/dist/commands/doctor.js +15 -2
  13. package/dist/commands/lanes-checks.d.ts +3 -0
  14. package/dist/commands/lanes-checks.js +34 -0
  15. package/dist/commands/lanes-loop.d.ts +12 -0
  16. package/dist/commands/lanes-loop.js +214 -0
  17. package/dist/commands/lanes-questions.js +37 -11
  18. package/dist/commands/lanes-resume.js +19 -1
  19. package/dist/commands/lanes-session-start.d.ts +2 -7
  20. package/dist/commands/lanes-session-start.js +9 -29
  21. package/dist/commands/lanes-shared.d.ts +14 -2
  22. package/dist/commands/lanes.d.ts +3 -1
  23. package/dist/commands/lanes.js +376 -35
  24. package/dist/commands/members.d.ts +4 -0
  25. package/dist/commands/members.js +23 -2
  26. package/dist/lanes-drain-hook.js +6 -2
  27. package/dist/lib/doctor/checks/sync-health.d.ts +2 -0
  28. package/dist/lib/doctor/checks/sync-health.js +47 -23
  29. package/dist/lib/lanes/delivery.d.ts +3 -0
  30. package/dist/lib/lanes/delivery.js +5 -0
  31. package/dist/lib/lanes/dropbox.d.ts +7 -2
  32. package/dist/lib/lanes/dropbox.js +157 -10
  33. package/dist/lib/lanes/flag-keys.d.ts +12 -0
  34. package/dist/lib/lanes/flag-keys.js +12 -0
  35. package/dist/lib/lanes/hooks-install.d.ts +14 -11
  36. package/dist/lib/lanes/hooks-install.js +70 -50
  37. package/dist/lib/lanes/loop.d.ts +158 -0
  38. package/dist/lib/lanes/loop.js +743 -0
  39. package/dist/lib/lanes/outpost-dispatch.d.ts +9 -1
  40. package/dist/lib/lanes/outpost-dispatch.js +21 -7
  41. package/dist/lib/lanes/reaper.d.ts +58 -0
  42. package/dist/lib/lanes/reaper.js +403 -0
  43. package/dist/lib/lanes/reconcile.d.ts +19 -0
  44. package/dist/lib/lanes/reconcile.js +173 -6
  45. package/dist/lib/lanes/spawn.d.ts +4 -0
  46. package/dist/lib/lanes/spawn.js +41 -9
  47. package/dist/lib/lanes/status-fields.d.ts +12 -0
  48. package/dist/lib/lanes/status-fields.js +189 -0
  49. package/dist/lib/lanes/store.js +49 -2
  50. package/dist/lib/lanes/types.d.ts +18 -0
  51. package/dist/lib/lanes/types.js +3 -3
  52. package/dist/lib/lanes/watcher.js +3 -0
  53. package/dist/lib/lanes/worker-continuity.d.ts +18 -0
  54. package/dist/lib/lanes/worker-continuity.js +81 -0
  55. package/dist/lib/lanes/worker-process.d.ts +4 -0
  56. package/dist/lib/lanes/worker-process.js +77 -0
  57. package/dist/main.js +9 -3
  58. package/dist/utils/cli-telemetry.d.ts +4 -1
  59. package/dist/utils/cli-telemetry.js +8 -0
  60. package/dist/utils/jq-windows-compat.d.ts +13 -0
  61. package/dist/utils/jq-windows-compat.js +48 -0
  62. package/dist/utils/run-bundled-script.d.ts +3 -0
  63. package/dist/utils/run-bundled-script.js +9 -80
  64. package/dist/utils/windows-bash.d.ts +5 -0
  65. package/dist/utils/windows-bash.js +75 -0
  66. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.57] — 2026-10-09
6
+ - Add `hq lanes create --loop` and `hq lanes enqueue` for persistent worker lanes that process FIFO phase envelopes, return generic results or section 8 handoffs, reuse sessions per story, retry one stall, and park a second stall for the senior. Loop behavior uses the default-on `lanes.conduct-loop` hq-flags kill switch. See `docs/lanes.md` for command output and exit codes.
7
+
8
+ - Add six-hour escalation for continuously blocked lanes, including durable cross-host inbox delivery and per-lane escalation history.
9
+ - Lane questions addressed to the current operator session record session-specific visibility on the owning host; unanswered questions cannot age past that session before it sees them, and reparenting to a different audience clears the marker. Visibility is separate from answer delivery so conflict-sidecar merging cannot suppress answer delivery. Outpost question show and answer lookups ignore malformed rows for unrelated question ids while still validating the requested record.
10
+
11
+ ## [5.345.56] — 2026-10-09
12
+ - Add `hq lanes reap` to report local lanes whose session senior is confirmed ended, clear stale pid markers on request, and keep process-tree stopping behind the `lanes.reap-stop` hq-flags key. SessionStart runs the report-only sweep detached and stores its report in host-specific user state.
13
+ - Record privacy-limited `hq members invite` success and failure outcomes through the existing CLI feature telemetry event.
14
+
15
+ - Continue a senior's most recent same-company, same-provider worker lane on `hq lanes create`, refuse live duplicate worker lanes, and filter `hq lanes list` by worker.
16
+
17
+ - Pass jq diagnostic filters to Windows batch wrappers as single-line arguments so malformed-ACL parity reports the original jq diagnostic instead of failing on an invalid command line.
18
+ - Keep the bundled derive-trigger-facts helper's closed-stdin error prompt on macOS instead of blocking the hook call.
19
+ - Native delegate diagnostics match the bundled helper's Git Bash jq byte handling, including jq.exe CRLF output when `--binary` is unavailable.
20
+ - Lane drop-box hooks deliver queued messages after tool calls and at a deliverable Stop event. Lane list and show JSON include bounded output, PR, inbox, elapsed time, and worker exit fields.
21
+ - hq doctor now finds a company's sync manifest status instead of reporting the company as unresolved.
22
+
5
23
  ## [5.345.55] — 2026-10-08
6
24
  - Added `hq daemon sync conflicts acknowledge <id>` so the desktop can clear a parked-conflict notice without writing runner-owned sync state.
7
25
  - Add `hq import scan`, which runs the HQ import-context scanner on this
@@ -65,7 +65,16 @@ case "${OSTYPE:-}" in
65
65
  STDIN_JSON="${STDIN_JSON%$'\n'}"
66
66
  done
67
67
  ;;
68
- *) STDIN_JSON="$(</dev/stdin)" || STDIN_JSON='{}' ;;
68
+ *)
69
+ # Bash 3.2 on macOS can block while opening /dev/stdin when fd 0 is closed.
70
+ # Detect that case with a builtin redirection so the existing fail-closed
71
+ # behavior returns promptly on every POSIX host.
72
+ if [[ ! -e /dev/stdin ]]; then
73
+ printf '%s: line 68: /dev/stdin: No such file or directory\n' "$0" >&2
74
+ exit 1
75
+ fi
76
+ STDIN_JSON="$(</dev/stdin)" || STDIN_JSON='{}'
77
+ ;;
69
78
  esac
70
79
  [ -n "$STDIN_JSON" ] || STDIN_JSON='{}'
71
80
  JQ="$(command -v jq || true)"
@@ -219,7 +219,8 @@ validate_job_file() {
219
219
  err "$file" "yaml" "failed to parse YAML"
220
220
  return
221
221
  fi
222
- if [ "$(jq -r 'type' <<<"$json")" != "object" ]; then
222
+ # Use jq's exit status instead of its text output: jq.exe writes CRLF on Windows.
223
+ if ! jq -es 'length == 1 and (.[0] | type == "object")' <<<"$json" >/dev/null; then
223
224
  err "$file" "yaml" "job root must be a mapping"
224
225
  return
225
226
  fi
@@ -38,13 +38,14 @@ done
38
38
 
39
39
  command -v node >/dev/null 2>&1 || exit 0
40
40
 
41
- HQ_ROOT="$HQ_ROOT" SESSION_ID="$SESSION_ID" CMD="$COMMAND" SESSION_CWD="$SESSION_CWD" node - <<'JS'
41
+ HQ_ROOT="$HQ_ROOT" SESSION_ID="$SESSION_ID" SESSION_CWD="$SESSION_CWD" \
42
+ MSYS2_ARG_CONV_EXCL="*" node - "$COMMAND" <<'JS'
42
43
  const fs = require("fs");
43
44
  const path = require("path");
44
45
 
45
46
  const hq = process.env.HQ_ROOT || "";
46
47
  const sid = process.env.SESSION_ID || "default";
47
- let command = (process.env.CMD || "").trim().replace(/^\/+/, "");
48
+ let command = (process.argv[2] || "").trim().replace(/^\/+/, "");
48
49
 
49
50
  const key = sid.replace(/[^A-Za-z0-9._-]/g, "_") || "default";
50
51
  const state = path.join(hq, ".claude", "state");
@@ -133,12 +134,12 @@ if (project) {
133
134
  if (matchesProject) {
134
135
  const st = p.state || "";
135
136
  if (st === "IN_PROGRESS") {
136
- emoji = "▶️";
137
+ emoji = "\u25b6\ufe0f";
137
138
  } else if (st === "COMPLETED") {
138
139
  const ts = p.updatedAt || p.updated_at || "";
139
140
  const t = new Date(ts);
140
141
  if (!isNaN(t.getTime()) && (Date.now() - t.getTime()) / 1000 < 86400) {
141
- emoji = "✅"; // only recent completions
142
+ emoji = "\u2705"; // only recent completions
142
143
  }
143
144
  }
144
145
  break;
@@ -171,15 +172,15 @@ if (project) {
171
172
  // while /handoff runs; only the model can know when it finished, so it sets 📤.
172
173
  // Both are distinct from ✅ "the work shipped" and 🤝 "a person owns it now".
173
174
  const MODES = [
174
- [/^(brainstorm|idea|dream|discover|strategize)$/, "💭"],
175
- [/^(plan|prd|deep-plan|storyboard|architect|review-plan|codebase-design)$/, "📐"],
176
- [/^(run-project|execute-task|ship|land|land-batch|run-pipeline|orchestrate|tdd|deploy)$/, "⚡"],
177
- [/^(review|code-review|security-review)$/, "👀"],
178
- [/^(quality-gate|smoke|verify|diagnose|investigate)$/, "🧪"],
179
- [/^(handoff|checkpoint-handoff|retro)$/, "📝"],
180
- [/^(delegate|new-hire|new-agent|promote)$/, "🤝"],
181
- [/^(dm|hq-slack|meeting-notes|work-broadcast|signals|hq-share)$/, "💬"],
182
- [/^(schedule|job|loop)$/, "🔁"],
175
+ [/^(brainstorm|idea|dream|discover|strategize)$/, "\u{1f4ad}"],
176
+ [/^(plan|prd|deep-plan|storyboard|architect|review-plan|codebase-design)$/, "\u{1f4d0}"],
177
+ [/^(run-project|execute-task|ship|land|land-batch|run-pipeline|orchestrate|tdd|deploy)$/, "\u{26a1}"],
178
+ [/^(review|code-review|security-review)$/, "\u{1f440}"],
179
+ [/^(quality-gate|smoke|verify|diagnose|investigate)$/, "\u{1f9ea}"],
180
+ [/^(handoff|checkpoint-handoff|retro)$/, "\u{1f4dd}"],
181
+ [/^(delegate|new-hire|new-agent|promote)$/, "\u{1f91d}"],
182
+ [/^(dm|hq-slack|meeting-notes|work-broadcast|signals|hq-share)$/, "\u{1f4ac}"],
183
+ [/^(schedule|job|loop)$/, "\u{1f501}"],
183
184
  ];
184
185
 
185
186
  const modeFor = (word) => {
@@ -192,7 +193,7 @@ if (!command) command = "chat";
192
193
  // Precedence: a finished or running project outranks the command word — "it
193
194
  // shipped" and "it is executing right now" beat "you typed /plan an hour ago".
194
195
  // `emoji` is set above from orchestrator state (✅ recent completion, ▶️ running).
195
- const mode = emoji === "▶️" ? "⚡" : (emoji || modeFor(command));
196
+ const mode = emoji === "\u25b6\ufe0f" ? "\u{26a1}" : (emoji || modeFor(command));
196
197
 
197
198
  // --- company ---
198
199
  // Always the first text token. Long slugs get an explicit short form from the
@@ -301,5 +302,7 @@ let title = compose(parts);
301
302
  if (title.length > MAX && parts.length > 1) title = compose([parts[parts.length - 1]]);
302
303
  if (title.length > MAX) title = title.slice(0, MAX - 1).replace(/\s+$/, "") + "…";
303
304
 
304
- console.log(title);
305
+ // The script is also run under Git Bash with LC_ALL=C from its caller. Write
306
+ // the protocol bytes explicitly so the host locale cannot replace UTF-8 glyphs.
307
+ process.stdout.write(Buffer.from(`${title}\n`, "utf8"));
305
308
  JS
@@ -11008,6 +11008,38 @@ export declare const COMMAND_CATALOG: readonly [{
11008
11008
  readonly hidden: false;
11009
11009
  }];
11010
11010
  readonly subcommands: readonly [];
11011
+ }, {
11012
+ readonly name: "reap";
11013
+ readonly description: "Classify local lanes whose session senior has ended; report only by default";
11014
+ readonly aliases: readonly ["_reap"];
11015
+ readonly hidden: false;
11016
+ readonly helpOption: true;
11017
+ readonly helpCommand: false;
11018
+ readonly usage: "[options]";
11019
+ readonly helpText: readonly [];
11020
+ readonly arguments: readonly [];
11021
+ readonly options: readonly [{
11022
+ readonly flags: "--min-age <minutes>";
11023
+ readonly description: "Minimum lane age in minutes (default: 120)";
11024
+ readonly hidden: false;
11025
+ }, {
11026
+ readonly flags: "--apply-stale-state";
11027
+ readonly description: "Remove stale worker and watcher pid markers";
11028
+ readonly hidden: false;
11029
+ }, {
11030
+ readonly flags: "--stop";
11031
+ readonly description: "Stop candidate process trees when the hq-flags stop key is enabled";
11032
+ readonly hidden: false;
11033
+ }, {
11034
+ readonly flags: "--reason <text>";
11035
+ readonly description: "Required audit reason when --stop is set";
11036
+ readonly hidden: false;
11037
+ }, {
11038
+ readonly flags: "--json";
11039
+ readonly description: "Print machine-readable JSON (default)";
11040
+ readonly hidden: false;
11041
+ }];
11042
+ readonly subcommands: readonly [];
11011
11043
  }, {
11012
11044
  readonly name: "checks";
11013
11045
  readonly description: "Evaluate tracker and lane health checks";
@@ -11618,6 +11650,14 @@ export declare const COMMAND_CATALOG: readonly [{
11618
11650
  readonly flags: "--worker <name>";
11619
11651
  readonly description: "HQ worker or reusable profile id";
11620
11652
  readonly hidden: false;
11653
+ }, {
11654
+ readonly flags: "--fresh-thread";
11655
+ readonly description: "Skip worker continuity and start a cold provider session";
11656
+ readonly hidden: false;
11657
+ }, {
11658
+ readonly flags: "--loop";
11659
+ readonly description: "Keep one worker lane registered to process queued phase envelopes";
11660
+ readonly hidden: false;
11621
11661
  }, {
11622
11662
  readonly flags: "--provider <name>";
11623
11663
  readonly description: "claude | codex | grok";
@@ -11698,6 +11738,52 @@ export declare const COMMAND_CATALOG: readonly [{
11698
11738
  readonly hidden: false;
11699
11739
  }];
11700
11740
  readonly subcommands: readonly [];
11741
+ }, {
11742
+ readonly name: "_loop-worker";
11743
+ readonly description: "";
11744
+ readonly aliases: readonly [];
11745
+ readonly hidden: true;
11746
+ readonly helpOption: true;
11747
+ readonly helpCommand: false;
11748
+ readonly usage: "[options] <lane-id>";
11749
+ readonly helpText: readonly [];
11750
+ readonly arguments: readonly [{
11751
+ readonly name: "lane-id";
11752
+ readonly required: true;
11753
+ readonly variadic: false;
11754
+ readonly description: "Loop lane id";
11755
+ }];
11756
+ readonly options: readonly [{
11757
+ readonly flags: "--json";
11758
+ readonly description: "JSON on stdout (always)";
11759
+ readonly hidden: false;
11760
+ }];
11761
+ readonly subcommands: readonly [];
11762
+ }, {
11763
+ readonly name: "enqueue";
11764
+ readonly description: "Validate and queue one phase envelope for a loop lane";
11765
+ readonly aliases: readonly [];
11766
+ readonly hidden: false;
11767
+ readonly helpOption: true;
11768
+ readonly helpCommand: false;
11769
+ readonly usage: "[options] <lane-id>";
11770
+ readonly helpText: readonly [];
11771
+ readonly arguments: readonly [{
11772
+ readonly name: "lane-id";
11773
+ readonly required: true;
11774
+ readonly variadic: false;
11775
+ readonly description: "Loop lane id";
11776
+ }];
11777
+ readonly options: readonly [{
11778
+ readonly flags: "--envelope <file>";
11779
+ readonly description: "Generic phase or hq-phase-envelope/v1 JSON file";
11780
+ readonly hidden: false;
11781
+ }, {
11782
+ readonly flags: "--json";
11783
+ readonly description: "JSON on stdout (always)";
11784
+ readonly hidden: false;
11785
+ }];
11786
+ readonly subcommands: readonly [];
11701
11787
  }, {
11702
11788
  readonly name: "reparent-target";
11703
11789
  readonly description: "";
@@ -11950,6 +12036,10 @@ export declare const COMMAND_CATALOG: readonly [{
11950
12036
  readonly flags: "--senior <ref>";
11951
12037
  readonly description: "Filter by senior ref";
11952
12038
  readonly hidden: false;
12039
+ }, {
12040
+ readonly flags: "--worker <id>";
12041
+ readonly description: "Filter by worker id";
12042
+ readonly hidden: false;
11953
12043
  }, {
11954
12044
  readonly flags: "--lane <id>";
11955
12045
  readonly description: "Filter by lane id (repeatable)";
@@ -12566,6 +12656,40 @@ export declare const COMMAND_CATALOG: readonly [{
12566
12656
  readonly hidden: false;
12567
12657
  }];
12568
12658
  readonly subcommands: readonly [];
12659
+ }, {
12660
+ readonly name: "_enqueue-blocked-escalation";
12661
+ readonly description: "";
12662
+ readonly aliases: readonly [];
12663
+ readonly hidden: true;
12664
+ readonly helpOption: true;
12665
+ readonly helpCommand: false;
12666
+ readonly usage: "[options] <recipient-kind> <recipient-id>";
12667
+ readonly helpText: readonly [];
12668
+ readonly arguments: readonly [{
12669
+ readonly name: "recipient-kind";
12670
+ readonly required: true;
12671
+ readonly variadic: false;
12672
+ readonly description: "";
12673
+ }, {
12674
+ readonly name: "recipient-id";
12675
+ readonly required: true;
12676
+ readonly variadic: false;
12677
+ readonly description: "";
12678
+ }];
12679
+ readonly options: readonly [{
12680
+ readonly flags: "--text <text>";
12681
+ readonly description: "Escalation notice";
12682
+ readonly hidden: false;
12683
+ }, {
12684
+ readonly flags: "--message-id <id>";
12685
+ readonly description: "Idempotency key";
12686
+ readonly hidden: false;
12687
+ }, {
12688
+ readonly flags: "--json";
12689
+ readonly description: "JSON on stdout (always)";
12690
+ readonly hidden: false;
12691
+ }];
12692
+ readonly subcommands: readonly [];
12569
12693
  }, {
12570
12694
  readonly name: "_spawn-envelope-repair";
12571
12695
  readonly description: "";
@@ -13374,6 +13374,47 @@ export const COMMAND_CATALOG = [
13374
13374
  ],
13375
13375
  "subcommands": []
13376
13376
  },
13377
+ {
13378
+ "name": "reap",
13379
+ "description": "Classify local lanes whose session senior has ended; report only by default",
13380
+ "aliases": [
13381
+ "_reap"
13382
+ ],
13383
+ "hidden": false,
13384
+ "helpOption": true,
13385
+ "helpCommand": false,
13386
+ "usage": "[options]",
13387
+ "helpText": [],
13388
+ "arguments": [],
13389
+ "options": [
13390
+ {
13391
+ "flags": "--min-age <minutes>",
13392
+ "description": "Minimum lane age in minutes (default: 120)",
13393
+ "hidden": false
13394
+ },
13395
+ {
13396
+ "flags": "--apply-stale-state",
13397
+ "description": "Remove stale worker and watcher pid markers",
13398
+ "hidden": false
13399
+ },
13400
+ {
13401
+ "flags": "--stop",
13402
+ "description": "Stop candidate process trees when the hq-flags stop key is enabled",
13403
+ "hidden": false
13404
+ },
13405
+ {
13406
+ "flags": "--reason <text>",
13407
+ "description": "Required audit reason when --stop is set",
13408
+ "hidden": false
13409
+ },
13410
+ {
13411
+ "flags": "--json",
13412
+ "description": "Print machine-readable JSON (default)",
13413
+ "hidden": false
13414
+ }
13415
+ ],
13416
+ "subcommands": []
13417
+ },
13377
13418
  {
13378
13419
  "name": "checks",
13379
13420
  "description": "Evaluate tracker and lane health checks",
@@ -14116,6 +14157,16 @@ export const COMMAND_CATALOG = [
14116
14157
  "description": "HQ worker or reusable profile id",
14117
14158
  "hidden": false
14118
14159
  },
14160
+ {
14161
+ "flags": "--fresh-thread",
14162
+ "description": "Skip worker continuity and start a cold provider session",
14163
+ "hidden": false
14164
+ },
14165
+ {
14166
+ "flags": "--loop",
14167
+ "description": "Keep one worker lane registered to process queued phase envelopes",
14168
+ "hidden": false
14169
+ },
14119
14170
  {
14120
14171
  "flags": "--provider <name>",
14121
14172
  "description": "claude | codex | grok",
@@ -14216,6 +14267,63 @@ export const COMMAND_CATALOG = [
14216
14267
  ],
14217
14268
  "subcommands": []
14218
14269
  },
14270
+ {
14271
+ "name": "_loop-worker",
14272
+ "description": "",
14273
+ "aliases": [],
14274
+ "hidden": true,
14275
+ "helpOption": true,
14276
+ "helpCommand": false,
14277
+ "usage": "[options] <lane-id>",
14278
+ "helpText": [],
14279
+ "arguments": [
14280
+ {
14281
+ "name": "lane-id",
14282
+ "required": true,
14283
+ "variadic": false,
14284
+ "description": "Loop lane id"
14285
+ }
14286
+ ],
14287
+ "options": [
14288
+ {
14289
+ "flags": "--json",
14290
+ "description": "JSON on stdout (always)",
14291
+ "hidden": false
14292
+ }
14293
+ ],
14294
+ "subcommands": []
14295
+ },
14296
+ {
14297
+ "name": "enqueue",
14298
+ "description": "Validate and queue one phase envelope for a loop lane",
14299
+ "aliases": [],
14300
+ "hidden": false,
14301
+ "helpOption": true,
14302
+ "helpCommand": false,
14303
+ "usage": "[options] <lane-id>",
14304
+ "helpText": [],
14305
+ "arguments": [
14306
+ {
14307
+ "name": "lane-id",
14308
+ "required": true,
14309
+ "variadic": false,
14310
+ "description": "Loop lane id"
14311
+ }
14312
+ ],
14313
+ "options": [
14314
+ {
14315
+ "flags": "--envelope <file>",
14316
+ "description": "Generic phase or hq-phase-envelope/v1 JSON file",
14317
+ "hidden": false
14318
+ },
14319
+ {
14320
+ "flags": "--json",
14321
+ "description": "JSON on stdout (always)",
14322
+ "hidden": false
14323
+ }
14324
+ ],
14325
+ "subcommands": []
14326
+ },
14219
14327
  {
14220
14328
  "name": "reparent-target",
14221
14329
  "description": "",
@@ -14526,6 +14634,11 @@ export const COMMAND_CATALOG = [
14526
14634
  "description": "Filter by senior ref",
14527
14635
  "hidden": false
14528
14636
  },
14637
+ {
14638
+ "flags": "--worker <id>",
14639
+ "description": "Filter by worker id",
14640
+ "hidden": false
14641
+ },
14529
14642
  {
14530
14643
  "flags": "--lane <id>",
14531
14644
  "description": "Filter by lane id (repeatable)",
@@ -15283,6 +15396,48 @@ export const COMMAND_CATALOG = [
15283
15396
  ],
15284
15397
  "subcommands": []
15285
15398
  },
15399
+ {
15400
+ "name": "_enqueue-blocked-escalation",
15401
+ "description": "",
15402
+ "aliases": [],
15403
+ "hidden": true,
15404
+ "helpOption": true,
15405
+ "helpCommand": false,
15406
+ "usage": "[options] <recipient-kind> <recipient-id>",
15407
+ "helpText": [],
15408
+ "arguments": [
15409
+ {
15410
+ "name": "recipient-kind",
15411
+ "required": true,
15412
+ "variadic": false,
15413
+ "description": ""
15414
+ },
15415
+ {
15416
+ "name": "recipient-id",
15417
+ "required": true,
15418
+ "variadic": false,
15419
+ "description": ""
15420
+ }
15421
+ ],
15422
+ "options": [
15423
+ {
15424
+ "flags": "--text <text>",
15425
+ "description": "Escalation notice",
15426
+ "hidden": false
15427
+ },
15428
+ {
15429
+ "flags": "--message-id <id>",
15430
+ "description": "Idempotency key",
15431
+ "hidden": false
15432
+ },
15433
+ {
15434
+ "flags": "--json",
15435
+ "description": "JSON on stdout (always)",
15436
+ "hidden": false
15437
+ }
15438
+ ],
15439
+ "subcommands": []
15440
+ },
15286
15441
  {
15287
15442
  "name": "_spawn-envelope-repair",
15288
15443
  "description": "",
@@ -11,6 +11,8 @@ import { commandExists, delegateDiagnosticPath, isRecord, jsonAt, jqFallback, jq
11
11
  import { resolveLiveRoot } from "../utils/hq-roots.js";
12
12
  import { ACL_SOURCE_DISPLAY_LABELS } from "../utils/files-browse-acl.js";
13
13
  import { runBundledScript } from "../utils/run-bundled-script.js";
14
+ import { resolveWindowsBash } from "../utils/windows-bash.js";
15
+ import { createShellCompatibleJqInvoker } from "../utils/jq-windows-compat.js";
14
16
  const FLAG = "core-native-delegate";
15
17
  const ASSETS = {
16
18
  bundle: "core/scripts/hq-delegate-bundle.sh",
@@ -367,6 +369,13 @@ function runBundle(args, context, onMalformedWhoami) {
367
369
  function runHq(args, context, stdout = "capture") {
368
370
  return subprocess("hq", args, context.cwd, { stdout });
369
371
  }
372
+ const runJq = createShellCompatibleJqInvoker((args, input, cwd, options) => subprocess("jq", args, cwd, { input, ...options }), process.platform, process.env, (cwd) => {
373
+ const bash = resolveWindowsBash(process.env).executable;
374
+ if (!bash)
375
+ return undefined;
376
+ const result = subprocess(bash, ["-c", "printf '%s' \"$OSTYPE\""], cwd, { stderr: "ignore" });
377
+ return result.code === 0 ? result.stdout?.trim() : undefined;
378
+ });
370
379
  function commandSubstitutionBytes(value) {
371
380
  const bytes = value.stdoutBytes ?? Buffer.from(value.stdout ?? "");
372
381
  let end = bytes.length;
@@ -377,11 +386,15 @@ function commandSubstitutionBytes(value) {
377
386
  function emitJqParseDiagnostic(input, args, context) {
378
387
  if (!commandExists("jq", context.cwd))
379
388
  return;
380
- const result = subprocess("jq", args, context.cwd, { input, stdout: "ignore", stderr: "pipe" });
381
- const rawDiagnostic = result.stderrBytes ?? Buffer.from(result.stderr ?? "");
382
- const diagnostic = process.platform === "win32"
383
- ? Buffer.from(rawDiagnostic.toString("latin1").replace(/\r(?=\n|$)/g, ""), "latin1")
384
- : rawDiagnostic;
389
+ // cmd.exe cannot pass CR, LF, or NUL inside an argument. jq treats line
390
+ // breaks between tokens as whitespace, and these fixed filters contain no
391
+ // multiline string literals, so send the program as one argument on Windows.
392
+ const jqArgs = [...args];
393
+ const programIndex = jqArgs.length - 1;
394
+ if (programIndex >= 0)
395
+ jqArgs[programIndex] = (jqArgs[programIndex] ?? "").replace(/[\r\n\0]+/gu, " ");
396
+ const result = runJq(jqArgs, input, context.cwd, { stdout: "ignore", stderr: "pipe" });
397
+ const diagnostic = result.stderrBytes ?? Buffer.from(result.stderr ?? "");
385
398
  if (diagnostic.length)
386
399
  err(diagnostic);
387
400
  }
@@ -13,6 +13,20 @@ export function registerCoreDeriveTriggerFactsCommand(core) {
13
13
  .helpOption(false)
14
14
  .argument("[args...]")
15
15
  .action(async (args = [], _options, commandContext) => {
16
+ if (process.env.HQ_CORE_STDIN_CLOSED === "1") {
17
+ const root = liveRoot();
18
+ const result = runBundledScript({
19
+ asset: DERIVE_TRIGGER_FACTS_ASSET,
20
+ args: commandContext.args.length ? commandContext.args : args,
21
+ cwd: root,
22
+ hqRoot: root,
23
+ msysScriptPath: process.platform === "win32",
24
+ closeStdin: true,
25
+ });
26
+ if (result.code !== 0)
27
+ process.exit(result.code);
28
+ return;
29
+ }
16
30
  if (!await coreNativeHookHelpersEnabled()) {
17
31
  const root = liveRoot();
18
32
  const stdin = process.platform === "win32" ? await readStdin() : undefined;
@@ -20,4 +20,5 @@ export declare function commandOnPath(command: string, env: Record<string, strin
20
20
  /** Run the native handler contract, retaining exact bytes for differential tests. */
21
21
  export declare function runDeriveTriggerFacts(options: DeriveTriggerRunOptions): DeriveTriggerRunResult;
22
22
  export declare function runDeriveTriggerFactsFast(argv: readonly string[], stdin: string, cwd: string, hqRoot: string | undefined): number;
23
+ export declare function runDeriveTriggerFactsFastResult(argv: readonly string[], stdin: string, cwd: string, hqRoot: string | undefined): DeriveTriggerRunResult;
23
24
  //# sourceMappingURL=core-derive-trigger-facts-native.d.ts.map
@@ -583,11 +583,30 @@ export function runDeriveTriggerFacts(options) {
583
583
  }
584
584
  }
585
585
  export function runDeriveTriggerFactsFast(argv, stdin, cwd, hqRoot) {
586
- const result = runDeriveTriggerFacts({ argv, stdin, cwd, hqRoot: hqRoot ?? cwd });
586
+ const result = runDeriveTriggerFactsFastResult(argv, stdin, cwd, hqRoot);
587
587
  if (result.stdout.length)
588
588
  process.stdout.write(result.stdout);
589
589
  if (result.stderr.length)
590
590
  process.stderr.write(result.stderr);
591
591
  return result.code;
592
592
  }
593
+ export function runDeriveTriggerFactsFastResult(argv, stdin, cwd, hqRoot) {
594
+ if (process.env.HQ_CORE_STDIN_CLOSED === "1") {
595
+ const result = runBundledScript({
596
+ asset: DERIVE_TRIGGER_FACTS_ASSET,
597
+ args: [...argv],
598
+ cwd,
599
+ hqRoot: hqRoot ?? cwd,
600
+ msysScriptPath: process.platform === "win32",
601
+ closeStdin: true,
602
+ captureOutput: true,
603
+ });
604
+ return {
605
+ code: result.code,
606
+ stdout: result.stdout ?? Buffer.alloc(0),
607
+ stderr: result.stderr ?? Buffer.alloc(0),
608
+ };
609
+ }
610
+ return runDeriveTriggerFacts({ argv, stdin, cwd, hqRoot: hqRoot ?? cwd });
611
+ }
593
612
  //# sourceMappingURL=core-derive-trigger-facts-native.js.map
@@ -119,6 +119,7 @@ export interface RunDoctorResult {
119
119
  * read; all other default tiers remain local unless explicitly opted in.
120
120
  */
121
121
  export declare function runDoctor(options?: RunDoctorOptions): Promise<RunDoctorResult>;
122
+ export declare function createClientHealthReasonCodeCompanyResolver(hqRoot: string): (companySlug: string | null) => Promise<boolean>;
122
123
  export declare function createClientHealthCheckStatusCompanyResolver(hqRoot: string): (companySlug: string | null) => Promise<boolean>;
123
124
  /** Register the top-level `hq doctor` command so it appears in `hq --help`. */
124
125
  export declare function registerDoctorCommand(program: Command): void;
@@ -207,7 +207,14 @@ async function resolveClientHealthReasonCodesCheckIds(families, resolveForCompan
207
207
  continue;
208
208
  }
209
209
  const scoped = /^sync\.(?:journal|manifest)\.(.+)$/.exec(result.checkId);
210
- const companySlug = scoped && isCompanyJournalSlug(scoped[1]) ? scoped[1] : null;
210
+ const companyUidScope = scoped
211
+ ? /^company:(cmp_[A-Za-z0-9]{3,128})$/.exec(scoped[1])
212
+ : null;
213
+ const companySlug = companyUidScope
214
+ ? `company:${companyUidScope[1]}`
215
+ : scoped && isCompanyJournalSlug(scoped[1])
216
+ ? scoped[1]
217
+ : null;
211
218
  const checkIds = checksByCompany.get(companySlug) ?? [];
212
219
  checkIds.push(result.checkId);
213
220
  checksByCompany.set(companySlug, checkIds);
@@ -254,12 +261,18 @@ async function resolveClientHealthCheckStatusCheckIds(families, resolveForCompan
254
261
  }
255
262
  return enabledChecks;
256
263
  }
257
- function createClientHealthReasonCodeCompanyResolver(hqRoot) {
264
+ export function createClientHealthReasonCodeCompanyResolver(hqRoot) {
258
265
  const companies = readCompaniesManifestMap(hqRoot);
259
266
  return async (companySlug) => {
260
267
  if (companySlug === null) {
261
268
  return isActiveCompanyClientHealthReasonCodesEnabled();
262
269
  }
270
+ const scopedCompanyUid = /^company:(cmp_[A-Za-z0-9]{3,128})$/.exec(companySlug);
271
+ if (scopedCompanyUid) {
272
+ return clientHealthReasonCodesEnabledForCompany({
273
+ companyUid: scopedCompanyUid[1],
274
+ });
275
+ }
263
276
  if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(companySlug)) {
264
277
  return false;
265
278
  }
@@ -2,12 +2,15 @@ import type { Command } from "commander";
2
2
  import { type TrackerCheckTeam } from "../lib/lanes/tracker-checks.js";
3
3
  import { type TrackerKind, type TrackerRecord } from "../lib/lanes/tracker-store.js";
4
4
  import type { LaneRecord, LaneCompanyRef, QuestionRecord } from "../lib/lanes/types.js";
5
+ import { type ReapDeps } from "../lib/lanes/reaper.js";
5
6
  export interface LaneChecksDeps {
6
7
  hqRoot: () => string;
7
8
  resolveCompany: (company?: string) => Promise<LaneCompanyRef>;
8
9
  now: () => Date;
9
10
  stopLane?: (laneId: string, reason: string) => Promise<void>;
10
11
  sessionState?: (root: string, sessionId: string, now: Date) => string | null;
12
+ stopEnabled?: (lane: LaneRecord) => Promise<boolean> | boolean;
13
+ reaperDeps?: Partial<ReapDeps>;
11
14
  }
12
15
  type WrapHandler = <Args extends unknown[]>(action: (...args: Args) => Promise<void>) => (...args: Args) => Promise<void>;
13
16
  export interface ProjectMapping {