@indigoai-us/hq-cli 5.345.106 → 5.345.108

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 (60) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/assets/scaffold/core/scripts/hq-task.mjs +259 -0
  3. package/assets/scaffold/core/scripts/hq-task.sh +31 -0
  4. package/assets/scaffold/core/scripts/pipeline-worker-table.sh +285 -0
  5. package/dist/command-catalog.generated.d.ts +175 -6
  6. package/dist/command-catalog.generated.js +209 -6
  7. package/dist/commands/auth.js +42 -0
  8. package/dist/commands/capacity.d.ts +1 -1
  9. package/dist/commands/capacity.js +74 -11
  10. package/dist/commands/core-checkpoint-stop-gate-native.js +17 -5
  11. package/dist/commands/core-hq-task-fast.d.ts +4 -0
  12. package/dist/commands/core-hq-task-fast.js +11 -0
  13. package/dist/commands/core-hq-task-native.d.ts +20 -0
  14. package/dist/commands/core-hq-task-native.js +275 -0
  15. package/dist/commands/core-native-bucket-c-flag.d.ts +3 -0
  16. package/dist/commands/core-native-bucket-c-flag.js +6 -0
  17. package/dist/commands/core-native-family-flag.d.ts +0 -2
  18. package/dist/commands/core-native-family-flag.js +0 -4
  19. package/dist/commands/core-native-pipeline-flag.d.ts +3 -0
  20. package/dist/commands/core-native-pipeline-flag.js +6 -0
  21. package/dist/commands/core-ontology-native.js +24 -30
  22. package/dist/commands/core-pipeline-workers-command.d.ts +3 -0
  23. package/dist/commands/core-pipeline-workers-command.js +37 -0
  24. package/dist/commands/core-pipeline-workers-native.d.ts +13 -0
  25. package/dist/commands/core-pipeline-workers-native.js +361 -0
  26. package/dist/commands/core.js +12 -10
  27. package/dist/commands/lanes.js +104 -19
  28. package/dist/commands/outposts.d.ts +5 -0
  29. package/dist/commands/outposts.js +5 -0
  30. package/dist/fast-scaffold.d.ts +1 -1
  31. package/dist/fast-scaffold.js +13 -5
  32. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +56015 -63476
  33. package/dist/hq-cloud-sync-runner/package.json +1 -1
  34. package/dist/lib/capacity/ledger.d.ts +1 -0
  35. package/dist/lib/capacity/ledger.js +10 -0
  36. package/dist/lib/capacity/route.d.ts +146 -0
  37. package/dist/lib/capacity/route.js +452 -0
  38. package/dist/lib/daemon/remote-control-pools.d.ts +2 -2
  39. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  40. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  41. package/dist/lib/lanes/reconcile.js +4 -0
  42. package/dist/lib/lanes/remote-control-lane.d.ts +33 -0
  43. package/dist/lib/lanes/remote-control-lane.js +276 -0
  44. package/dist/lib/lanes/spawn.d.ts +5 -0
  45. package/dist/lib/lanes/spawn.js +65 -14
  46. package/dist/lib/lanes/store.js +11 -4
  47. package/dist/lib/lanes/types.d.ts +22 -4
  48. package/dist/lib/lanes/types.js +1 -1
  49. package/dist/lib/lanes/watcher.js +8 -1
  50. package/dist/utils/cognito-session.d.ts +12 -4
  51. package/dist/utils/cognito-session.js +42 -9
  52. package/dist/utils/token-keychain.d.ts +96 -0
  53. package/dist/utils/token-keychain.js +218 -0
  54. package/package.json +2 -2
  55. package/assets/scaffold/core/scripts/ontology-audience-grant.sh +0 -81
  56. package/assets/scaffold/core/scripts/ontology-brief.mjs +0 -93
  57. package/assets/scaffold/core/scripts/ontology-candidate.sh +0 -100
  58. package/assets/scaffold/core/scripts/ontology-garden.mjs +0 -327
  59. package/assets/scaffold/core/scripts/ontology-source-worklist.sh +0 -226
  60. package/assets/scaffold/core/scripts/source-yaml-validate.sh +0 -36
@@ -0,0 +1,37 @@
1
+ import { resolveLiveRoot } from "../utils/hq-roots.js";
2
+ import { runBundledScript } from "../utils/run-bundled-script.js";
3
+ import { describeCoreCommand } from "./core-command-catalog.js";
4
+ import { coreNativePipelineEnabled } from "./core-native-pipeline-flag.js";
5
+ import { PIPELINE_WORKER_TABLE_ASSET, pipelineWorkerFallbackArgs, runPipelineWorkerTable, writePipelineWorkerTableResult } from "./core-pipeline-workers-native.js";
6
+ export function registerCorePipelineWorkerTableCommand(core) {
7
+ const pipeline = core.command("pipeline").description("Pipeline orchestration helpers");
8
+ const command = pipeline.command("workers")
9
+ .description("Propose or render the pipeline worker engine table")
10
+ .helpOption(false)
11
+ .allowUnknownOption()
12
+ .allowExcessArguments(true)
13
+ .argument("[args...]")
14
+ .action(async (args, _options, current) => {
15
+ const argv = current.args.length > 0 ? current.args : args;
16
+ const groupHqRoot = core.opts().hqRoot;
17
+ if (!await coreNativePipelineEnabled()) {
18
+ const root = resolveLiveRoot({ hqRoot: groupHqRoot });
19
+ const result = runBundledScript({
20
+ asset: PIPELINE_WORKER_TABLE_ASSET,
21
+ args: pipelineWorkerFallbackArgs(argv, root, process.env),
22
+ cwd: process.cwd(),
23
+ hqRoot: root,
24
+ });
25
+ if (result.code !== 0)
26
+ process.exit(result.code);
27
+ return;
28
+ }
29
+ const root = resolveLiveRoot({ hqRoot: groupHqRoot });
30
+ const result = runPipelineWorkerTable(argv, root);
31
+ const code = writePipelineWorkerTableResult(result);
32
+ if (code !== 0)
33
+ process.exit(code);
34
+ });
35
+ describeCoreCommand(command, { execution: "native", asset: PIPELINE_WORKER_TABLE_ASSET, root: "live" });
36
+ }
37
+ //# sourceMappingURL=core-pipeline-workers-command.js.map
@@ -0,0 +1,13 @@
1
+ export declare const PIPELINE_WORKER_TABLE_ASSET = "core/scripts/pipeline-worker-table.sh";
2
+ export declare const CORE_NATIVE_PIPELINE_FLAG = "core-native-pipeline";
3
+ type Result = {
4
+ code: number;
5
+ stdout: string;
6
+ stderr: string;
7
+ };
8
+ export declare function pipelineWorkerRoots(argsRoots: string[], root: string, env: NodeJS.ProcessEnv): string[];
9
+ export declare function pipelineWorkerFallbackArgs(args: string[], root: string, env: NodeJS.ProcessEnv): string[];
10
+ export declare function runPipelineWorkerTable(args: string[], root: string, env?: NodeJS.ProcessEnv): Result;
11
+ export declare function writePipelineWorkerTableResult(result: Result): number;
12
+ export {};
13
+ //# sourceMappingURL=core-pipeline-workers-native.d.ts.map
@@ -0,0 +1,361 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ export const PIPELINE_WORKER_TABLE_ASSET = "core/scripts/pipeline-worker-table.sh";
4
+ export const CORE_NATIVE_PIPELINE_FLAG = "core-native-pipeline";
5
+ const USAGE = "# pipeline-worker-table.sh — the per-worker engine/model table behind\n# /run-project --pipeline.\n#\n# Each worker lane runs on its own engine and model. This script proposes that\n# table from a prd.json and, once the owner has confirmed it, turns it into the\n# launcher exports each lane starts with. It never reads an engine or model\n# default from a settings file (orchestrator.yaml, settings.json, ...): the only\n# seed is each worker's own worker.yaml, and anything that cannot be read off\n# it is left empty and flagged for the owner to answer.\n#\n# Usage:\n# pipeline-worker-table.sh [--workers-root <dir>]... [--overlay <file>] <prd.json>\n# pipeline-worker-table.sh [--workers-root <dir>]... [--overlay <file>] --confirmed <table.tsv> <prd.json>\n#\n# PROPOSE MODE (no --confirmed) prints tab-separated lines:\n#\n# row <worker> <engine> <model> <effort> <status> <stories> <note>\n# hint <worker> <story> <model>\n# unclassified <story>\n#\n# status is `ok` or `needs-answer`. The lane set is the union of every\n# story's worker sequence: one `row` per distinct worker that appears in any\n# sequence (architect and qa-tester included), in first-seen order. A `hint`\n# line is a story's model_hint, shown for the owner; a confirmed table pin wins\n# over it (see CONFIRMED MODE).\n#\n# WORKER SEQUENCE (story -> workers, the full phase sequence in order), first\n# match wins; pipeline-conductor.sh classify reads the same three sources:\n# 1. --overlay <file> (the run's <state>/overlay.json, or the preflight plan):\n# {id: {\"worker_sequence\": [...]}}, {id: [...]}, or\n# {\"ordered_stories\": [{\"id\": .., \"worker_sequence\": [...]}]}.\n# 2. worker_preference — a string or an array of worker ids, used as given.\n# 3. Fallback, only when both are empty: the keyword table from\n# .claude/skills/execute-task/SKILL.md step 3, matched case-insensitively\n# against title + description + labels:\n# schema_change (database|migration|schema|prisma|sql) -> database-dev\n# ui_component (component|page|form|button|react|ui) -> frontend-dev\n# api_development (endpoint|api|rest|graphql|route|service) -> backend-dev\n# giving architect, the matched implementers (backend-dev also for a\n# schema change), then qa-tester.\n# A story matching none is printed as `unclassified`; no worker is guessed.\n#\n# UNKNOWN WORKERS: a worker with no worker.yaml under the worker roots gets an\n# empty, needs-answer row noted \"no worker.yaml\", and a line on stderr naming\n# the id, the roots searched and every known id. pipeline-conductor.sh classify\n# refuses such a worker before launch.\n#\n# ENGINE SEED (from worker.yaml `execution:`):\n# model only -> engine claude, model = execution.model\n# codex_model only -> engine codex, model = execution.codex_model,\n# effort = `--reasoning <x>` from execution.codex_flags\n# both set -> ambiguous: engine and model empty, needs-answer, the\n# candidates listed in the note column\n# neither / no file -> engine and model empty, needs-answer\n# worker.yaml has no grok field, so grok is never seeded; the owner chooses it\n# in the confirmed table.\n#\n# CONFIRMED MODE: --confirmed <file> holds one lane per line,\n# <worker> <engine> <model> <effort> (tabs or spaces; # comments allowed;\n# effort may be omitted). engine must be claude, codex, or grok and model must\n# be non-empty, or the script exits 2. Output per lane, using the pins in\n# core/scripts/workflow-runner.mjs, plus HQ_CONDUCT_ENGINE=<engine> for\n# every lane (a loop lane runs pipeline phase envelopes on that engine):\n# claude -> HQ_WORKFLOW_CLAUDE_PLAN_MODEL, HQ_WORKFLOW_CLAUDE_EXEC_MODEL,\n# HQ_WORKFLOW_CLAUDE_EFFORT\n# codex|grok -> HQ_WORKFLOW_MODEL, HQ_WORKFLOW_EFFORT\n# The effort export is omitted when effort is empty (runner default applies).\n# A story's model_hint never overrides the lane: the lane's table pin wins.\n# Each hinted story on a confirmed lane gets a `# lane <w> story <id>\n# (model_hint)` block whose exports are commented out and that says \"hint\n# ignored, table pins <model>\". A bare alias (opus, sonnet, haiku, fable) is\n# never printed as a model.\n#\n# Worker roots: --workers-root (repeatable) or PIPELINE_WORKERS_ROOTS\n# (colon-separated). Default: <hq>/core/workers, <hq>/companies/*/workers,\n# <hq>/personal/workers. A worker is the directory named <id> holding worker.yaml.\n#\n# POSIX sh (dash-clean); JSON work is done by jq.\n";
6
+ function isRecord(value) {
7
+ return value !== null && typeof value === "object" && !Array.isArray(value);
8
+ }
9
+ function stringValue(value) {
10
+ if (value === null || value === undefined)
11
+ return "null";
12
+ if (typeof value === "string")
13
+ return value;
14
+ return JSON.stringify(value);
15
+ }
16
+ function sequence(value) {
17
+ if (Array.isArray(value))
18
+ return value.filter((item) => typeof item === "string" && item.length > 0);
19
+ if (typeof value === "string")
20
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
21
+ if (isRecord(value))
22
+ return sequence(value.worker_sequence ?? []);
23
+ return [];
24
+ }
25
+ function asciiLower(text) {
26
+ return text.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
27
+ }
28
+ function storyText(story) {
29
+ const title = story.title ?? "";
30
+ const description = story.description ?? "";
31
+ const labels = Array.isArray(story.labels) ? story.labels.join(" ") : "";
32
+ return asciiLower([stringValue(title === "" ? "" : title), stringValue(description === "" ? "" : description), labels].join(" "));
33
+ }
34
+ const WORD = {
35
+ schema: /\b(database|migration|schema|prisma|sql)\b/,
36
+ ui: /\b(component|page|form|button|react|ui)\b/,
37
+ api: /\b(endpoint|api|rest|graphql|route|service)\b/,
38
+ };
39
+ function fallbackSequence(story) {
40
+ const text = storyText(story);
41
+ const schema = WORD.schema.test(text);
42
+ const ui = WORD.ui.test(text);
43
+ const api = WORD.api.test(text);
44
+ if (!schema && !ui && !api)
45
+ return [];
46
+ return [
47
+ "architect",
48
+ ...(schema ? ["database-dev"] : []),
49
+ ...(api || schema ? ["backend-dev"] : []),
50
+ ...(ui ? ["frontend-dev"] : []),
51
+ "qa-tester",
52
+ ];
53
+ }
54
+ function storyId(story) {
55
+ return story.id === null || story.id === undefined ? "" : stringValue(story.id);
56
+ }
57
+ function deriveSequences(stories, overlay) {
58
+ const overrides = {};
59
+ if (Array.isArray(overlay.ordered_stories)) {
60
+ for (const row of overlay.ordered_stories) {
61
+ if (isRecord(row) && typeof row.id === "string")
62
+ overrides[row.id] = sequence(row.worker_sequence ?? []);
63
+ }
64
+ }
65
+ else {
66
+ for (const [id, value] of Object.entries(overlay))
67
+ overrides[id] = sequence(value);
68
+ }
69
+ return stories.map((story) => {
70
+ const id = storyId(story);
71
+ const override = overrides[id] ?? [];
72
+ const preferred = sequence(story.worker_preference ?? []);
73
+ const workers = override.length > 0 ? override : preferred.length > 0 ? preferred : fallbackSequence(story);
74
+ return { id, workers, story };
75
+ });
76
+ }
77
+ function parseArgs(args) {
78
+ const roots = [];
79
+ let confirmed = "";
80
+ let overlay = "";
81
+ let prd = "";
82
+ for (let i = 0; i < args.length; i += 1) {
83
+ const arg = args[i];
84
+ if (arg === "--workers-root" || arg === "--confirmed" || arg === "--overlay") {
85
+ const value = args[i + 1];
86
+ if (value === undefined)
87
+ return `${arg} needs ${arg === "--workers-root" ? "a dir" : "a file"}`;
88
+ if (arg === "--workers-root")
89
+ roots.push(value);
90
+ if (arg === "--confirmed")
91
+ confirmed = value;
92
+ if (arg === "--overlay")
93
+ overlay = value;
94
+ i += 1;
95
+ }
96
+ else if (arg === "-h" || arg === "--help") {
97
+ return { roots, confirmed, overlay, prd, help: true };
98
+ }
99
+ else if (arg.startsWith("-")) {
100
+ return `unknown flag: ${arg}`;
101
+ }
102
+ else if (!prd) {
103
+ prd = arg;
104
+ }
105
+ else {
106
+ return "only one prd.json";
107
+ }
108
+ }
109
+ if (!prd)
110
+ return "usage: pipeline-worker-table.sh [--workers-root <dir>] [--overlay <file>] [--confirmed <table.tsv>] <prd.json>";
111
+ return { roots, confirmed, overlay, prd, help: false };
112
+ }
113
+ function readJson(file) {
114
+ return JSON.parse(fs.readFileSync(file, "utf8"));
115
+ }
116
+ export function pipelineWorkerRoots(argsRoots, root, env) {
117
+ if (argsRoots.length > 0)
118
+ return argsRoots;
119
+ if (env.PIPELINE_WORKERS_ROOTS !== undefined)
120
+ return env.PIPELINE_WORKERS_ROOTS.split(":");
121
+ const roots = [path.join(root, "core/workers")];
122
+ const companies = path.join(root, "companies");
123
+ try {
124
+ for (const name of fs.readdirSync(companies).sort()) {
125
+ const workers = path.join(companies, name, "workers");
126
+ try {
127
+ if (fs.statSync(workers).isDirectory())
128
+ roots.push(workers);
129
+ }
130
+ catch { /* shell skips missing company worker roots */ }
131
+ }
132
+ }
133
+ catch { /* shell glob has no matches when companies is absent */ }
134
+ roots.push(path.join(root, "personal/workers"));
135
+ return roots;
136
+ }
137
+ export function pipelineWorkerFallbackArgs(args, root, env) {
138
+ if (args.includes("--workers-root") || env.PIPELINE_WORKERS_ROOTS !== undefined)
139
+ return args;
140
+ const roots = pipelineWorkerRoots([], root, env);
141
+ return [...roots.flatMap((workerRoot) => ["--workers-root", workerRoot]), ...args];
142
+ }
143
+ function listWorkerYaml(root) {
144
+ const results = [];
145
+ const stack = [root];
146
+ while (stack.length > 0) {
147
+ const current = stack.pop();
148
+ let entries;
149
+ try {
150
+ entries = fs.readdirSync(current, { withFileTypes: true });
151
+ }
152
+ catch {
153
+ continue;
154
+ }
155
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
156
+ const entry = entries[i];
157
+ const file = path.join(current, entry.name);
158
+ if (entry.isDirectory())
159
+ stack.push(file);
160
+ else if (entry.isFile() && entry.name === "worker.yaml")
161
+ results.push(file);
162
+ }
163
+ }
164
+ return results;
165
+ }
166
+ function findWorkerYaml(worker, roots) {
167
+ for (const root of roots) {
168
+ for (const file of listWorkerYaml(root)) {
169
+ if (path.basename(path.dirname(file)) === worker)
170
+ return file;
171
+ }
172
+ }
173
+ return undefined;
174
+ }
175
+ function knownWorkers(roots) {
176
+ return [...new Set(roots.flatMap((root) => listWorkerYaml(root).map((file) => path.basename(path.dirname(file)))))].sort();
177
+ }
178
+ function executionField(file, key) {
179
+ let text;
180
+ try {
181
+ text = fs.readFileSync(file, "utf8");
182
+ }
183
+ catch {
184
+ return "";
185
+ }
186
+ let inExecution = false;
187
+ for (const line of text.split(/\r?\n/)) {
188
+ if (/^[^\s#]/.test(line)) {
189
+ inExecution = /^execution:\s*$/.test(line);
190
+ continue;
191
+ }
192
+ if (!inExecution)
193
+ continue;
194
+ const match = new RegExp(`^[\\t ]+${key}:[\\t ]*(.*)$`).exec(line);
195
+ if (!match)
196
+ continue;
197
+ let value = match[1].replace(/[\t ]+#.*$/, "").replace(/[\t ]+$/, "");
198
+ if (value.startsWith("\"") || value.startsWith("'"))
199
+ value = value.slice(1);
200
+ if (value.endsWith("\"") || value.endsWith("'"))
201
+ value = value.slice(0, -1);
202
+ return value;
203
+ }
204
+ return "";
205
+ }
206
+ function quoteShell(value) {
207
+ return `'${value.replace(/'/g, "'\\''")}'`;
208
+ }
209
+ function emitExports(engine, model, effort, prefix = "") {
210
+ let output = `${prefix}export HQ_CONDUCT_ENGINE=${quoteShell(engine)}\n`;
211
+ if (engine === "claude") {
212
+ output += `${prefix}export HQ_WORKFLOW_CLAUDE_PLAN_MODEL=${quoteShell(model)}\n`;
213
+ output += `${prefix}export HQ_WORKFLOW_CLAUDE_EXEC_MODEL=${quoteShell(model)}\n`;
214
+ if (effort)
215
+ output += `${prefix}export HQ_WORKFLOW_CLAUDE_EFFORT=${quoteShell(effort)}\n`;
216
+ }
217
+ else if (engine === "codex" || engine === "grok") {
218
+ output += `${prefix}export HQ_WORKFLOW_MODEL=${quoteShell(model)}\n`;
219
+ if (effort)
220
+ output += `${prefix}export HQ_WORKFLOW_EFFORT=${quoteShell(effort)}\n`;
221
+ }
222
+ return output;
223
+ }
224
+ function bareAlias(hint) {
225
+ return ["opus", "sonnet", "haiku", "fable", "Opus", "Sonnet", "Haiku", "Fable"].includes(hint);
226
+ }
227
+ function confirmedOutput(file, stories, sequences) {
228
+ let text;
229
+ try {
230
+ text = fs.readFileSync(file, "utf8");
231
+ }
232
+ catch {
233
+ return { code: 1, stdout: "", stderr: `pipeline-worker-table: no such confirmed table: ${file}\n` };
234
+ }
235
+ let stdout = "";
236
+ for (const line of text.split(/\r?\n/)) {
237
+ const columns = line.trim().split(/\s+/);
238
+ if (!columns[0] || columns[0].startsWith("#") || columns[0] === "worker")
239
+ continue;
240
+ const [worker, engine, model, effort = ""] = columns;
241
+ if (!(engine === "claude" || engine === "codex" || engine === "grok")) {
242
+ return { code: 2, stdout: "", stderr: `pipeline-worker-table: lane ${worker}: engine must be claude, codex, or grok (got '${engine ?? ""}')\n` };
243
+ }
244
+ if (!model)
245
+ return { code: 2, stdout: "", stderr: `pipeline-worker-table: lane ${worker}: model is empty — answer it before launch\n` };
246
+ stdout += `# lane ${worker} (${engine})\n${emitExports(engine, model, effort)}`;
247
+ for (const story of stories) {
248
+ const id = storyId(story);
249
+ const hint = story.model_hint !== null && story.model_hint !== undefined && story.model_hint !== "" ? stringValue(story.model_hint) : "";
250
+ if (!hint || !sequences.some((row) => row.id === id && row.workers.includes(worker)))
251
+ continue;
252
+ if (bareAlias(hint))
253
+ stdout += `# lane ${worker} story ${id} (model_hint) bare alias ignored, table pins ${model}\n`;
254
+ else {
255
+ stdout += `# lane ${worker} story ${id} (model_hint) hint ignored, table pins ${model}\n`;
256
+ stdout += emitExports(engine, hint, effort, "# ");
257
+ }
258
+ }
259
+ }
260
+ return { code: 0, stdout, stderr: "" };
261
+ }
262
+ export function runPipelineWorkerTable(args, root, env = process.env) {
263
+ const parsed = parseArgs(args);
264
+ if (typeof parsed === "string")
265
+ return { code: parsed.startsWith("usage:") ? 1 : 1, stdout: "", stderr: `pipeline-worker-table: ${parsed}\n` };
266
+ if (parsed.help)
267
+ return { code: 0, stdout: USAGE, stderr: "" };
268
+ let prd;
269
+ try {
270
+ prd = readJson(parsed.prd);
271
+ }
272
+ catch {
273
+ return { code: 1, stdout: "", stderr: `pipeline-worker-table: not a prd (no userStories array): ${parsed.prd}\n` };
274
+ }
275
+ if (!isRecord(prd) || !Array.isArray(prd.userStories))
276
+ return { code: 1, stdout: "", stderr: `pipeline-worker-table: not a prd (no userStories array): ${parsed.prd}\n` };
277
+ let overlay = {};
278
+ if (parsed.overlay) {
279
+ try {
280
+ overlay = readJson(parsed.overlay);
281
+ }
282
+ catch {
283
+ return { code: 1, stdout: "", stderr: `pipeline-worker-table: overlay is not a JSON object: ${parsed.overlay}\n` };
284
+ }
285
+ if (!isRecord(overlay))
286
+ return { code: 1, stdout: "", stderr: `pipeline-worker-table: overlay is not a JSON object: ${parsed.overlay}\n` };
287
+ }
288
+ if (parsed.confirmed) {
289
+ if (!fs.existsSync(parsed.confirmed))
290
+ return { code: 1, stdout: "", stderr: `pipeline-worker-table: no such confirmed table: ${parsed.confirmed}\n` };
291
+ return confirmedOutput(parsed.confirmed, prd.userStories, deriveSequences(prd.userStories, overlay));
292
+ }
293
+ const roots = pipelineWorkerRoots(parsed.roots, root, env);
294
+ const sequences = deriveSequences(prd.userStories, overlay);
295
+ const workers = [];
296
+ const seen = new Set();
297
+ for (const row of sequences)
298
+ for (const worker of row.workers)
299
+ if (!seen.has(worker)) {
300
+ seen.add(worker);
301
+ workers.push(worker);
302
+ }
303
+ let stdout = "";
304
+ let stderr = "";
305
+ for (const row of sequences)
306
+ if (row.workers.length === 0)
307
+ stdout += `unclassified\t${row.id}\n`;
308
+ for (const worker of workers) {
309
+ const file = findWorkerYaml(worker, roots);
310
+ let engine = "";
311
+ let model = "";
312
+ let effort = "";
313
+ let status = "needs-answer";
314
+ let note = "";
315
+ if (!file) {
316
+ note = "no worker.yaml";
317
+ const rootText = roots.join(" ");
318
+ const known = knownWorkers(roots).join(" ") || "none";
319
+ stderr += `pipeline-worker-table: unknown worker ${worker}: no worker.yaml under ${rootText}; known: ${known}\n`;
320
+ }
321
+ else {
322
+ const claudeModel = executionField(file, "model");
323
+ const codexModel = executionField(file, "codex_model");
324
+ if (claudeModel && codexModel)
325
+ note = `ambiguous: claude:${claudeModel} codex:${codexModel}`;
326
+ else if (claudeModel) {
327
+ engine = "claude";
328
+ model = claudeModel;
329
+ status = "ok";
330
+ }
331
+ else if (codexModel) {
332
+ engine = "codex";
333
+ model = codexModel;
334
+ status = "ok";
335
+ const flags = executionField(file, "codex_flags");
336
+ effort = /.*--reasoning[= ]([A-Za-z]*).*/.exec(flags)?.[1] ?? "";
337
+ }
338
+ else
339
+ note = "no execution.model or execution.codex_model";
340
+ }
341
+ const storiesText = sequences.filter((row) => row.workers.includes(worker)).map((row) => row.id).join(" ");
342
+ stdout += `row\t${worker}\t${engine}\t${model}\t${effort}\t${status}\t${storiesText}\t${note}\n`;
343
+ }
344
+ for (const story of prd.userStories) {
345
+ const hint = story.model_hint !== null && story.model_hint !== undefined && story.model_hint !== "" ? stringValue(story.model_hint) : "";
346
+ if (!hint)
347
+ continue;
348
+ const id = storyId(story);
349
+ for (const worker of sequences.find((row) => row.id === id)?.workers ?? [])
350
+ stdout += `hint\t${worker}\t${id}\t${hint}\n`;
351
+ }
352
+ return { code: 0, stdout, stderr };
353
+ }
354
+ export function writePipelineWorkerTableResult(result) {
355
+ if (result.stdout)
356
+ process.stdout.write(result.stdout);
357
+ if (result.stderr)
358
+ process.stderr.write(result.stderr);
359
+ return result.code;
360
+ }
361
+ //# sourceMappingURL=core-pipeline-workers-native.js.map
@@ -50,6 +50,7 @@ import { registerCoreDeriveTriggerFactsCommand } from "./core-derive-trigger-fac
50
50
  import { DERIVE_TRIGGER_FACTS_ASSET } from "./core-derive-trigger-facts-native.js";
51
51
  import { registerCoreHookStateHelpers } from "./core-hook-state-helpers-native.js";
52
52
  import { registerCoreHookBatchCommand } from "./core-hook-batch-command.js";
53
+ import { registerCorePipelineWorkerTableCommand } from "./core-pipeline-workers-command.js";
53
54
  import { resolveLiveRoot } from "../utils/hq-roots.js";
54
55
  import { interpreterFor, runBundledScript } from "../utils/run-bundled-script.js";
55
56
  import { CompanyIndexUserError, renderIndexTarget } from "../lib/index-render/index.js";
@@ -218,6 +219,12 @@ export const REBUILD_INDEX_TARGETS = [
218
219
  * would make the forwarders harder to audit against this table.
219
220
  */
220
221
  export const SCAFFOLD_COMMANDS = [
222
+ {
223
+ name: "hq-task",
224
+ asset: "core/scripts/hq-task.sh",
225
+ root: "live",
226
+ summary: "List and update tasks on personal or company boards",
227
+ },
221
228
  // Session/harness plumbing relocated out of the HQ tree so a single published
222
229
  // CLI owns it. Both are `root: "live"`: they read and write the live HQ tree
223
230
  // (workspace/sessions, companies/, workspace/orchestrator/hook-state), so the
@@ -243,6 +250,7 @@ export const SCAFFOLD_COMMANDS = [
243
250
  * direct `hq core` command.
244
251
  */
245
252
  export const SCAFFOLD_ONLY_ASSETS = [
253
+ { asset: "core/scripts/hq-task.mjs", root: "live", summary: "Sibling implementation used by the hq-task shell rollback companion" },
246
254
  { asset: "core/scripts/session-journal.sh", root: "live", summary: "Rollback companion for native session journal" },
247
255
  { asset: "core/scripts/session-project.sh", root: "live", summary: "Rollback companion for native session project" },
248
256
  { asset: "core/scripts/session-title.sh", root: "live", summary: "Rollback companion for native session title" },
@@ -255,6 +263,7 @@ export const SCAFFOLD_ONLY_ASSETS = [
255
263
  { asset: "core/scripts/hq-job-run.sh", root: "live", summary: "Scheduled jobs runner used by generated systemd units" },
256
264
  { asset: "core/scripts/hq-job-notify.sh", root: "live", summary: "Scheduled jobs failure notification helper" },
257
265
  { asset: "core/scripts/hq-job-remediate.sh", root: "live", summary: "Scheduled jobs bounded remediation helper" },
266
+ { asset: "core/scripts/pipeline-worker-table.sh", root: "live", summary: "Rollback companion for native pipeline worker-table" },
258
267
  { asset: "core/scripts/hq-dm-bind.sh", root: "live", summary: "Rollback shell for native dm-bind" },
259
268
  { asset: "core/scripts/work-mesh-live-bind-trusted.sh", root: "live", summary: "Rollback shell for native work-mesh-bind-trusted" },
260
269
  { asset: "core/scripts/share-suggestion-state.sh", root: "live", summary: "Rollback shell for native share-suggestion-state" },
@@ -295,16 +304,8 @@ export const SCAFFOLD_ONLY_ASSETS = [
295
304
  { asset: "core/scripts/hq-delegate-send.sh", root: "live", summary: "Rollback shell for native delegate send" },
296
305
  { asset: "core/scripts/hq-delegate-verify.sh", root: "live", summary: "Rollback shell for native delegate verify" },
297
306
  { asset: "core/scripts/lib/secret-patterns.sh", root: "live", summary: "Secret-pattern library sourced by the delegate rollback shells" },
298
- // Retained rollback companions for the gated native ontology commands. These
299
- // are loose HQ-tree assets, but `hq core ontology` dispatches natively.
300
- { asset: "core/scripts/ontology-audience-grant.sh", root: "live", summary: "Rollback companion for native ontology audience grant" },
301
- { asset: "core/scripts/ontology-brief.mjs", root: "live", summary: "Rollback companion for native ontology brief" },
302
- { asset: "core/scripts/ontology-candidate.sh", root: "live", summary: "Rollback companion for native ontology candidate" },
303
- { asset: "core/scripts/ontology-garden.mjs", root: "live", summary: "Rollback companion for native ontology garden" },
304
- { asset: "core/scripts/ontology-source-worklist.sh", root: "live", summary: "Rollback companion for native ontology source worklist" },
305
- { asset: "core/scripts/source-yaml-validate.sh", root: "cwd", summary: "Rollback companion for native source YAML validation" },
306
- // Shared by native knowledge-prefs and the ontology candidate shell fallback.
307
- { asset: "core/scripts/knowledge-prefs.sh", root: "live", summary: "Sibling dependency for ontology candidate rollback companion" },
307
+ // Native knowledge-prefs retains its own shell companion until its family PR.
308
+ { asset: "core/scripts/knowledge-prefs.sh", root: "live", summary: "Rollback companion for native knowledge-prefs" },
308
309
  // Rollback companions for the default-off native knowledge and policy routes.
309
310
  // policy-retire also runs the scaffold validator in-process when native, but
310
311
  // its bundled shell fallback needs both helper assets at these exact paths.
@@ -412,6 +413,7 @@ export function registerCoreCommands(program) {
412
413
  registerCoreKnowledgePrefsCommand(core);
413
414
  registerCorePolicyCommand(core);
414
415
  registerCoreJobsNativeCommand(core);
416
+ registerCorePipelineWorkerTableCommand(core);
415
417
  registerCoreResolveCompanyNativeCommand(core);
416
418
  registerCoreAuditLogNative(core);
417
419
  registerCoreGitPackExtensionCheckNative(core);