@indigoai-us/hq-cli 5.273.0 → 5.274.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +32 -1
- package/dist/command-catalog.generated.d.ts +72 -0
- package/dist/command-catalog.generated.js +92 -0
- package/dist/command-registration-plan.d.ts +12 -0
- package/dist/command-registration-plan.js +2 -0
- package/dist/commands/cloud.js +40 -0
- package/dist/commands/core-checkpoint.js +3 -1
- package/dist/commands/core-command-catalog.d.ts +32 -0
- package/dist/commands/core-command-catalog.js +58 -0
- package/dist/commands/core-ontology.js +9 -4
- package/dist/commands/core.d.ts +4 -0
- package/dist/commands/core.js +79 -75
- package/dist/commands/deploy-comments.d.ts +3 -0
- package/dist/commands/deploy-comments.js +159 -0
- package/dist/commands/lanes-message.js +40 -7
- package/dist/commands/lanes-move.d.ts +12 -0
- package/dist/commands/lanes-move.js +342 -0
- package/dist/commands/lanes-resume.js +3 -1
- package/dist/commands/lanes-shared.d.ts +40 -1
- package/dist/commands/lanes-shared.js +64 -0
- package/dist/commands/lanes.d.ts +1 -1
- package/dist/commands/lanes.js +211 -460
- package/dist/commands/report.js +2 -14
- package/dist/fast-core.js +1 -1
- package/dist/{commands/scaffold-fast.d.ts → fast-scaffold.d.ts} +1 -1
- package/dist/{commands/scaffold-fast.js → fast-scaffold.js} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/lib/checkpoint-ontology/apply.d.ts +8 -0
- package/dist/lib/checkpoint-ontology/apply.js +33 -0
- package/dist/lib/checkpoint-ontology/plan.d.ts +15 -0
- package/dist/lib/checkpoint-ontology/plan.js +15 -0
- package/dist/lib/deploy-api.d.ts +6 -0
- package/dist/lib/deploy-api.js +21 -0
- package/dist/lib/doctor/fix/apply.d.ts +17 -2
- package/dist/lib/doctor/fix/apply.js +111 -57
- package/dist/lib/doctor/fix/json-output.d.ts +9 -1
- package/dist/lib/doctor/fix/json-output.js +11 -0
- package/dist/lib/first-sync-next-step.d.ts +37 -0
- package/dist/lib/first-sync-next-step.js +189 -0
- package/dist/lib/lanes/hire-grant-dispatch.d.ts +2 -1
- package/dist/lib/lanes/hire-grant-dispatch.js +37 -2
- package/dist/lib/lanes/keep-alive.js +2 -2
- package/dist/main.js +4 -1
- package/dist/utils/cli-telemetry.d.ts +7 -1
- package/dist/utils/cli-telemetry.js +84 -8
- package/dist/utils/contribution-table.d.ts +1 -1
- package/dist/utils/scaffold-fast-import-guard.d.ts +3 -0
- package/dist/utils/scaffold-fast-import-guard.js +28 -0
- package/dist/utils/scaffold-packlist-guard.d.ts +7 -0
- package/dist/utils/scaffold-packlist-guard.js +23 -0
- package/dist/utils/scaffold-sibling-references.d.ts +21 -0
- package/dist/utils/scaffold-sibling-references.js +156 -0
- package/package.json +1 -1
package/dist/commands/core.js
CHANGED
|
@@ -32,7 +32,7 @@ import { Option } from "commander";
|
|
|
32
32
|
import { registerCoreCheckpointCommand } from "./core-checkpoint.js";
|
|
33
33
|
import { registerCoreOntologyCommand } from "./core-ontology.js";
|
|
34
34
|
import { resolveLiveRoot } from "../utils/hq-roots.js";
|
|
35
|
-
import { runBundledScript } from "../utils/run-bundled-script.js";
|
|
35
|
+
import { interpreterFor, runBundledScript } from "../utils/run-bundled-script.js";
|
|
36
36
|
import { renderIndexTarget } from "../lib/index-render/index.js";
|
|
37
37
|
import { generateWorkersRegistry } from "../lib/workers-registry/index.js";
|
|
38
38
|
import { scanPackages } from "../lib/scan-packages/index.js";
|
|
@@ -48,21 +48,22 @@ import { qmdReindexAfterSync } from "../lib/core-utils/qmd-reindex-after-sync.js
|
|
|
48
48
|
import { softTimeoutCommand } from "../lib/core-utils/soft-timeout.js";
|
|
49
49
|
import { timeoutGuardCommand } from "../lib/core-utils/timeout-guard.js";
|
|
50
50
|
import { readSentryReportStdin, reportSentryEvent } from "../lib/core-utils/sentry-report.js";
|
|
51
|
+
import { describeCoreCommand, registeredCoreCommandListing, } from "./core-command-catalog.js";
|
|
51
52
|
/**
|
|
52
53
|
* These commands are now native implementations. Their retained shell assets
|
|
53
54
|
* are deliberately claimed in SCAFFOLD_ONLY_ASSETS: loose HQ trees still ship
|
|
54
55
|
* them for legacy callers, but `hq core` never dispatches through them again.
|
|
55
56
|
*/
|
|
56
57
|
export const NATIVE_UTILITY_COMMANDS = [
|
|
57
|
-
{ name: "detect-stale-core-policy-mirror", root: "live", summary: "Find (and prune) stale personal-policy mirrors in core", run: (args, context) => detectStaleCorePolicyMirror(args, { root: context.hqRoot }) },
|
|
58
|
-
{ name: "hq-status-summary", root: "cwd", summary: "Summarize working-tree status across the HQ tree", run: (args, context) => summarizeHqStatus(args, { cwd: context.cwd, root: context.hqRoot }) },
|
|
59
|
-
{ name: "ontology-readme-drift", root: "live", summary: "Check a company's ontology README for drift", run: (args, context) => ontologyReadmeDrift(args, { root: context.hqRoot }) },
|
|
60
|
-
{ name: "resize-screenshot", root: "cwd", summary: "Downscale a screenshot for context efficiency", run: (args) => resizeScreenshot(args) },
|
|
61
|
-
{ name: "token-usage-report", root: "cwd", summary: "Report session token usage", run: (args) => tokenUsageReport(args) },
|
|
62
|
-
{ name: "worktree", root: "live", summary: "Create a git worktree under workspace/worktrees/", run: (args, context) => createWorktree(args, { cwd: context.cwd, hqRoot: context.hqRoot }) },
|
|
63
|
-
{ name: "codex-skill-bridge", root: "live", summary: "Install/inspect the Codex skill bridge", run: (args, context) => runCodexSkillBridgeCommand(args, { root: context.hqRoot ?? context.cwd }) },
|
|
64
|
-
{ name: "archive-old-threads", root: "live", summary: "Archive aged session-thread files", run: (args, context) => archiveOldThreads(args, { hqRoot: context.hqRoot ?? context.cwd }) },
|
|
65
|
-
{ name: "qmd-reindex-after-sync", root: "cwd", summary: "Refresh qmd collections after a sync", run: (args, context) => qmdReindexAfterSync(args, { cwd: context.cwd }) },
|
|
58
|
+
{ name: "detect-stale-core-policy-mirror", asset: "core/scripts/detect-stale-core-policy-mirror.sh", root: "live", summary: "Find (and prune) stale personal-policy mirrors in core", run: (args, context) => detectStaleCorePolicyMirror(args, { root: context.hqRoot }) },
|
|
59
|
+
{ name: "hq-status-summary", asset: "core/scripts/hq-status-summary.sh", root: "cwd", summary: "Summarize working-tree status across the HQ tree", run: (args, context) => summarizeHqStatus(args, { cwd: context.cwd, root: context.hqRoot }) },
|
|
60
|
+
{ name: "ontology-readme-drift", asset: "core/scripts/ontology-readme-drift.sh", root: "live", summary: "Check a company's ontology README for drift", run: (args, context) => ontologyReadmeDrift(args, { root: context.hqRoot }) },
|
|
61
|
+
{ name: "resize-screenshot", asset: "core/scripts/resize-screenshot.sh", root: "cwd", summary: "Downscale a screenshot for context efficiency", run: (args) => resizeScreenshot(args) },
|
|
62
|
+
{ name: "token-usage-report", asset: "core/scripts/token-usage-report.sh", root: "cwd", summary: "Report session token usage", run: (args) => tokenUsageReport(args) },
|
|
63
|
+
{ name: "worktree", asset: "core/scripts/worktree.sh", root: "live", summary: "Create a git worktree under workspace/worktrees/", run: (args, context) => createWorktree(args, { cwd: context.cwd, hqRoot: context.hqRoot }) },
|
|
64
|
+
{ name: "codex-skill-bridge", asset: "core/scripts/codex-skill-bridge.sh", root: "live", summary: "Install/inspect the Codex skill bridge", run: (args, context) => runCodexSkillBridgeCommand(args, { root: context.hqRoot ?? context.cwd }) },
|
|
65
|
+
{ name: "archive-old-threads", asset: "core/scripts/archive-old-threads.sh", root: "live", summary: "Archive aged session-thread files", run: (args, context) => archiveOldThreads(args, { hqRoot: context.hqRoot ?? context.cwd }) },
|
|
66
|
+
{ name: "qmd-reindex-after-sync", asset: "core/scripts/qmd-reindex-after-sync.sh", root: "cwd", summary: "Refresh qmd collections after a sync", run: (args, context) => qmdReindexAfterSync(args, { cwd: context.cwd }) },
|
|
66
67
|
];
|
|
67
68
|
export const NATIVE_SCAFFOLD_COMMANDS = [
|
|
68
69
|
{
|
|
@@ -110,51 +111,61 @@ export const WORKER_SUBCOMMANDS = [
|
|
|
110
111
|
export const REBUILD_INDEX_TARGETS = [
|
|
111
112
|
{
|
|
112
113
|
target: "all",
|
|
114
|
+
asset: "core/scripts/rebuild-all-indexes.sh",
|
|
113
115
|
renderer: "all",
|
|
114
116
|
summary: "Regenerate every INDEX.md across the HQ tree",
|
|
115
117
|
},
|
|
116
118
|
{
|
|
117
119
|
target: "companies",
|
|
120
|
+
asset: "core/scripts/rebuild-companies-index.sh",
|
|
118
121
|
renderer: "companies",
|
|
119
122
|
summary: "Regenerate companies/INDEX.md",
|
|
120
123
|
},
|
|
121
124
|
{
|
|
122
125
|
target: "company-knowledge",
|
|
126
|
+
asset: "core/scripts/rebuild-company-knowledge-index.sh",
|
|
123
127
|
renderer: "company-knowledge",
|
|
124
128
|
summary: "Regenerate a company's knowledge INDEX.md",
|
|
125
129
|
},
|
|
126
130
|
{
|
|
127
131
|
target: "orchestrator",
|
|
132
|
+
asset: "core/scripts/rebuild-orchestrator-index.sh",
|
|
128
133
|
renderer: "orchestrator",
|
|
129
134
|
summary: "Regenerate the orchestrator workspace INDEX.md",
|
|
130
135
|
},
|
|
131
136
|
{
|
|
132
137
|
target: "projects",
|
|
138
|
+
asset: "core/scripts/rebuild-projects-index.sh",
|
|
133
139
|
renderer: "projects",
|
|
134
140
|
summary: "Regenerate the projects INDEX.md",
|
|
135
141
|
},
|
|
136
142
|
{
|
|
137
143
|
target: "public-knowledge",
|
|
144
|
+
asset: "core/scripts/rebuild-public-knowledge-index.sh",
|
|
138
145
|
renderer: "public-knowledge",
|
|
139
146
|
summary: "Regenerate core/knowledge/public/INDEX.md",
|
|
140
147
|
},
|
|
141
148
|
{
|
|
142
149
|
target: "reports",
|
|
150
|
+
asset: "core/scripts/rebuild-reports-index.sh",
|
|
143
151
|
renderer: "reports",
|
|
144
152
|
summary: "Regenerate the reports INDEX.md",
|
|
145
153
|
},
|
|
146
154
|
{
|
|
147
155
|
target: "social-drafts",
|
|
156
|
+
asset: "core/scripts/rebuild-social-drafts-index.sh",
|
|
148
157
|
renderer: "social-drafts",
|
|
149
158
|
summary: "Regenerate workspace/social-drafts/INDEX.md",
|
|
150
159
|
},
|
|
151
160
|
{
|
|
152
161
|
target: "threads",
|
|
162
|
+
asset: "core/scripts/rebuild-threads-index.sh",
|
|
153
163
|
renderer: "threads",
|
|
154
164
|
summary: "Regenerate the session-threads INDEX.md",
|
|
155
165
|
},
|
|
156
166
|
{
|
|
157
167
|
target: "workers",
|
|
168
|
+
asset: "core/scripts/rebuild-workers-index.sh",
|
|
158
169
|
renderer: "workers",
|
|
159
170
|
summary: "Regenerate the workers INDEX.md",
|
|
160
171
|
},
|
|
@@ -201,65 +212,16 @@ export const SCAFFOLD_ONLY_ASSETS = [
|
|
|
201
212
|
{ asset: "core/scripts/lib/session-scope-capability.sh", root: "live", summary: "Session scope-capability lib sourced by hq-session" },
|
|
202
213
|
{ asset: "core/scripts/lib/session-id.sh", root: "live", summary: "Session-id resolver lib sourced by hq-session" },
|
|
203
214
|
{ asset: "core/scripts/lib/work-mesh-enqueue.sh", root: "live", summary: "Work Mesh Live spool enqueue lib sourced by hooks" },
|
|
204
|
-
{ asset
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
{ asset
|
|
210
|
-
|
|
211
|
-
{ asset: "core/scripts/token-usage-report.sh", root: "cwd", summary: "Scaffold-only native utility companion" },
|
|
212
|
-
{ asset: "core/scripts/worktree.sh", root: "live", summary: "Scaffold-only native utility companion" },
|
|
213
|
-
{
|
|
214
|
-
asset: "core/scripts/rebuild-all-indexes.sh",
|
|
215
|
-
root: "cwd",
|
|
216
|
-
summary: "Scaffold-only native rebuild-index companion",
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
asset: "core/scripts/rebuild-companies-index.sh",
|
|
215
|
+
...NATIVE_UTILITY_COMMANDS.map(({ asset, root, summary }) => ({
|
|
216
|
+
asset,
|
|
217
|
+
root,
|
|
218
|
+
summary: `Scaffold-only native utility companion: ${summary}`,
|
|
219
|
+
})),
|
|
220
|
+
...REBUILD_INDEX_TARGETS.map(({ asset }) => ({
|
|
221
|
+
asset,
|
|
220
222
|
root: "cwd",
|
|
221
223
|
summary: "Scaffold-only native rebuild-index companion",
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
asset: "core/scripts/rebuild-company-knowledge-index.sh",
|
|
225
|
-
root: "cwd",
|
|
226
|
-
summary: "Scaffold-only native rebuild-index companion",
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
asset: "core/scripts/rebuild-orchestrator-index.sh",
|
|
230
|
-
root: "cwd",
|
|
231
|
-
summary: "Scaffold-only native rebuild-index companion",
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
asset: "core/scripts/rebuild-projects-index.sh",
|
|
235
|
-
root: "cwd",
|
|
236
|
-
summary: "Scaffold-only native rebuild-index companion",
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
asset: "core/scripts/rebuild-public-knowledge-index.sh",
|
|
240
|
-
root: "cwd",
|
|
241
|
-
summary: "Scaffold-only native rebuild-index companion",
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
asset: "core/scripts/rebuild-reports-index.sh",
|
|
245
|
-
root: "cwd",
|
|
246
|
-
summary: "Scaffold-only native rebuild-index companion",
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
asset: "core/scripts/rebuild-social-drafts-index.sh",
|
|
250
|
-
root: "cwd",
|
|
251
|
-
summary: "Scaffold-only native rebuild-index companion",
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
asset: "core/scripts/rebuild-threads-index.sh",
|
|
255
|
-
root: "cwd",
|
|
256
|
-
summary: "Scaffold-only native rebuild-index companion",
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
asset: "core/scripts/rebuild-workers-index.sh",
|
|
260
|
-
root: "cwd",
|
|
261
|
-
summary: "Scaffold-only native rebuild-index companion",
|
|
262
|
-
},
|
|
224
|
+
})),
|
|
263
225
|
];
|
|
264
226
|
/** Every claimed bundled asset, used by packaging and interpreter tests. */
|
|
265
227
|
export const SCAFFOLD_ASSETS = [
|
|
@@ -328,7 +290,7 @@ export function registerCoreCommands(program) {
|
|
|
328
290
|
// similarly named scaffold assets are forwarders, so sending them through
|
|
329
291
|
// runEntry would recurse back into this command forever.
|
|
330
292
|
for (const entry of NATIVE_SCAFFOLD_COMMANDS) {
|
|
331
|
-
core
|
|
293
|
+
const command = core
|
|
332
294
|
.command(entry.name)
|
|
333
295
|
.description(entry.summary)
|
|
334
296
|
.allowUnknownOption()
|
|
@@ -344,9 +306,14 @@ export function registerCoreCommands(program) {
|
|
|
344
306
|
if (result.status !== 0)
|
|
345
307
|
process.exit(result.status);
|
|
346
308
|
});
|
|
309
|
+
describeCoreCommand(command, {
|
|
310
|
+
execution: "native",
|
|
311
|
+
asset: entry.asset,
|
|
312
|
+
root: entry.root,
|
|
313
|
+
});
|
|
347
314
|
}
|
|
348
315
|
for (const entry of NATIVE_UTILITY_COMMANDS) {
|
|
349
|
-
core
|
|
316
|
+
const command = core
|
|
350
317
|
.command(entry.name)
|
|
351
318
|
.description(entry.summary)
|
|
352
319
|
.allowUnknownOption()
|
|
@@ -361,6 +328,11 @@ export function registerCoreCommands(program) {
|
|
|
361
328
|
if (code !== 0)
|
|
362
329
|
process.exit(code);
|
|
363
330
|
});
|
|
331
|
+
describeCoreCommand(command, {
|
|
332
|
+
execution: "native",
|
|
333
|
+
asset: entry.asset,
|
|
334
|
+
root: entry.root,
|
|
335
|
+
});
|
|
364
336
|
}
|
|
365
337
|
// `hq core worker <name>` — a nested subgroup for worker-scoped skill
|
|
366
338
|
// maintenance. Nested (rather than flat `hq core worker-<name>`) so the two
|
|
@@ -369,7 +341,7 @@ export function registerCoreCommands(program) {
|
|
|
369
341
|
.command("worker")
|
|
370
342
|
.description("Worker-scoped skill maintenance (lint, share)");
|
|
371
343
|
for (const entry of WORKER_SUBCOMMANDS) {
|
|
372
|
-
worker
|
|
344
|
+
const command = worker
|
|
373
345
|
.command(entry.name)
|
|
374
346
|
.description(entry.summary)
|
|
375
347
|
// Pure passthrough: the wrapped script owns its own argument grammar.
|
|
@@ -382,6 +354,11 @@ export function registerCoreCommands(program) {
|
|
|
382
354
|
const operands = cmd.args.length > 0 ? cmd.args : args;
|
|
383
355
|
runEntry(entry, scope, operands);
|
|
384
356
|
});
|
|
357
|
+
describeCoreCommand(command, {
|
|
358
|
+
execution: interpreterFor(entry.asset) === "node" ? "bundled-node" : "bundled-shell",
|
|
359
|
+
asset: entry.asset,
|
|
360
|
+
root: entry.root,
|
|
361
|
+
});
|
|
385
362
|
}
|
|
386
363
|
const runCommand = (entry, args = [], cmd, target) => {
|
|
387
364
|
const scope = core.opts();
|
|
@@ -393,7 +370,7 @@ export function registerCoreCommands(program) {
|
|
|
393
370
|
runEntry(entry, scope, passthrough);
|
|
394
371
|
};
|
|
395
372
|
const validTargets = REBUILD_INDEX_TARGETS.map((entry) => entry.target);
|
|
396
|
-
core
|
|
373
|
+
const rebuildCommand = core
|
|
397
374
|
.command("rebuild-index <target> [args...]")
|
|
398
375
|
.description(`Regenerate one index (target: ${validTargets.join(", ")})`)
|
|
399
376
|
// Pure passthrough: the wrapped script owns its own argument grammar, so
|
|
@@ -424,8 +401,16 @@ export function registerCoreCommands(program) {
|
|
|
424
401
|
throw expectedUserError(`rebuild-index: refused to overwrite ${refused.length} hand-edited INDEX.md file(s). Re-run with --accept to overwrite.`);
|
|
425
402
|
}
|
|
426
403
|
});
|
|
404
|
+
describeCoreCommand(rebuildCommand, {
|
|
405
|
+
entries: REBUILD_INDEX_TARGETS.map((entry) => ({
|
|
406
|
+
name: `rebuild-index ${entry.target}`,
|
|
407
|
+
execution: "native",
|
|
408
|
+
asset: entry.asset,
|
|
409
|
+
root: "live",
|
|
410
|
+
})),
|
|
411
|
+
});
|
|
427
412
|
for (const entry of SCAFFOLD_COMMANDS) {
|
|
428
|
-
core
|
|
413
|
+
const command = core
|
|
429
414
|
.command(entry.name)
|
|
430
415
|
.description(entry.summary)
|
|
431
416
|
// Pure passthrough: the wrapped script owns its own argument grammar, so
|
|
@@ -438,12 +423,17 @@ export function registerCoreCommands(program) {
|
|
|
438
423
|
.action((args = [], _opts, cmd) => {
|
|
439
424
|
runCommand(entry, args, cmd);
|
|
440
425
|
});
|
|
426
|
+
describeCoreCommand(command, {
|
|
427
|
+
execution: interpreterFor(entry.asset) === "node" ? "bundled-node" : "bundled-shell",
|
|
428
|
+
asset: entry.asset,
|
|
429
|
+
root: entry.root,
|
|
430
|
+
});
|
|
441
431
|
}
|
|
442
432
|
// `hq core soft-timeout <interval> [--hard-cap <spec>] [--label <name>] -- <cmd…>`
|
|
443
433
|
// Async and bespoke (the NATIVE_UTILITY_COMMANDS table is synchronous), since
|
|
444
434
|
// it spawns a child and warns over time. Warn-don't-kill primitive for
|
|
445
435
|
// finding 2.1 — the CLI-hosted twin of core/scripts/lib/soft-timeout.sh.
|
|
446
|
-
core
|
|
436
|
+
const softTimeout = core
|
|
447
437
|
.command("soft-timeout")
|
|
448
438
|
.description("Run a command under a soft (warn, don't kill) timeout")
|
|
449
439
|
// Passthrough: the wrapped command owns everything after `--`.
|
|
@@ -457,10 +447,11 @@ export function registerCoreCommands(program) {
|
|
|
457
447
|
if (code !== 0)
|
|
458
448
|
process.exit(code);
|
|
459
449
|
});
|
|
450
|
+
describeCoreCommand(softTimeout, { execution: "native", root: "cwd" });
|
|
460
451
|
// `hq core timeout-guard` — PreToolUse decision for the foreground-timeout
|
|
461
452
|
// guard (finding 2.1). Reads the hook JSON on stdin and exits 2 to block, 0
|
|
462
453
|
// to allow. The shipped .claude hook is a thin shim over this.
|
|
463
|
-
core
|
|
454
|
+
const timeoutGuard = core
|
|
464
455
|
.command("timeout-guard")
|
|
465
456
|
.description("PreToolUse guard: block over-ceiling foreground timeouts (reads hook JSON on stdin)")
|
|
466
457
|
.helpOption(false)
|
|
@@ -472,10 +463,11 @@ export function registerCoreCommands(program) {
|
|
|
472
463
|
if (code !== 0)
|
|
473
464
|
process.exit(code);
|
|
474
465
|
});
|
|
466
|
+
describeCoreCommand(timeoutGuard, { execution: "native", root: "cwd" });
|
|
475
467
|
// `hq core sentry report` — constrained, stderr-diagnostic-only transport
|
|
476
468
|
// for hook health events. Event data is accepted only on stdin so it never
|
|
477
469
|
// appears in the process list shared by the fleet.
|
|
478
|
-
core
|
|
470
|
+
const sentryReport = core
|
|
479
471
|
.command("sentry")
|
|
480
472
|
.description("Constrained diagnostics transport for HQ hooks")
|
|
481
473
|
.command("report")
|
|
@@ -495,6 +487,18 @@ export function registerCoreCommands(program) {
|
|
|
495
487
|
if (result.code !== 0)
|
|
496
488
|
process.exitCode = result.code;
|
|
497
489
|
});
|
|
490
|
+
describeCoreCommand(sentryReport, { execution: "native", root: "live" });
|
|
491
|
+
const commands = core
|
|
492
|
+
.command("commands", { hidden: true })
|
|
493
|
+
.description("List registered hq core commands as JSON")
|
|
494
|
+
.option("--json", "Print the command catalog as JSON")
|
|
495
|
+
.action((options) => {
|
|
496
|
+
if (!options.json) {
|
|
497
|
+
throw expectedUserError("Usage: hq core commands --json");
|
|
498
|
+
}
|
|
499
|
+
process.stdout.write(`${JSON.stringify(registeredCoreCommandListing(core))}\n`);
|
|
500
|
+
});
|
|
501
|
+
describeCoreCommand(commands, { execution: "native", root: "cwd" });
|
|
498
502
|
return core;
|
|
499
503
|
}
|
|
500
504
|
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { deployApiRequest } from "../lib/deploy-api.js";
|
|
2
|
+
import { DEFAULT_HQ_ROOT, ensureCognitoToken } from "../utils/cognito-session.js";
|
|
3
|
+
import { readActiveCompanySlug } from "./skill.js";
|
|
4
|
+
class DeployCommentsError extends Error {
|
|
5
|
+
}
|
|
6
|
+
function callerOrgHeaders(companyFlag) {
|
|
7
|
+
const slug = companyFlag ?? readActiveCompanySlug(DEFAULT_HQ_ROOT);
|
|
8
|
+
if (slug === undefined)
|
|
9
|
+
return {};
|
|
10
|
+
if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(slug)) {
|
|
11
|
+
throw new DeployCommentsError("The company slug is not valid.");
|
|
12
|
+
}
|
|
13
|
+
return { "X-Org-Slug": slug };
|
|
14
|
+
}
|
|
15
|
+
function errorCode(body, status) {
|
|
16
|
+
const code = body.error?.code;
|
|
17
|
+
return typeof code === "string" && /^[A-Z0-9_]+$/.test(code) ? code : `HTTP_${status}`;
|
|
18
|
+
}
|
|
19
|
+
async function responseError(response) {
|
|
20
|
+
const body = await response.json().catch(() => ({}));
|
|
21
|
+
const code = errorCode(body, response.status);
|
|
22
|
+
if (response.status === 401 || response.status === 403) {
|
|
23
|
+
return new DeployCommentsError(`You need to be the app owner or an org admin to read its comments. (${code})`);
|
|
24
|
+
}
|
|
25
|
+
if (response.status === 404) {
|
|
26
|
+
return new DeployCommentsError(`App was not found in the current org. (${code})`);
|
|
27
|
+
}
|
|
28
|
+
if (response.status === 429) {
|
|
29
|
+
const value = response.headers.get("Retry-After");
|
|
30
|
+
const retryAfter = value && /^\d+$/.test(value) ? value : "unknown";
|
|
31
|
+
return new DeployCommentsError(`Comment reads are rate limited. Retry-After: ${retryAfter} seconds. (${code})`);
|
|
32
|
+
}
|
|
33
|
+
return new DeployCommentsError(`Could not read deploy comments (HTTP ${response.status}, ${code}).`);
|
|
34
|
+
}
|
|
35
|
+
async function deployJson(token, route, orgHeaders) {
|
|
36
|
+
let response;
|
|
37
|
+
try {
|
|
38
|
+
response = await deployApiRequest(token, route, "GET", undefined, orgHeaders);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new DeployCommentsError("Could not reach hq-deploy.");
|
|
42
|
+
}
|
|
43
|
+
if (!response.ok)
|
|
44
|
+
throw await responseError(response);
|
|
45
|
+
try {
|
|
46
|
+
return await response.json();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
throw new DeployCommentsError("hq-deploy returned an invalid response.");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function resolveAppId(token, appNameOrId, orgHeaders) {
|
|
53
|
+
const result = await deployJson(token, "/api/apps", orgHeaders);
|
|
54
|
+
const apps = Array.isArray(result.apps) ? result.apps : [];
|
|
55
|
+
const byId = apps.find((app) => app && typeof app.id === "string" && app.id === appNameOrId);
|
|
56
|
+
const matches = byId
|
|
57
|
+
? [byId]
|
|
58
|
+
: apps.filter((app) => app && typeof app.id === "string" && app.name === appNameOrId);
|
|
59
|
+
const ids = [...new Set(matches.map((app) => app.id).filter((id) => typeof id === "string"))];
|
|
60
|
+
if (ids.length === 0)
|
|
61
|
+
throw new DeployCommentsError("App was not found in the current org.");
|
|
62
|
+
if (ids.length > 1)
|
|
63
|
+
throw new DeployCommentsError("More than one app has that name in the current org; use its app id.");
|
|
64
|
+
return ids[0];
|
|
65
|
+
}
|
|
66
|
+
async function listComments(token, appId, orgHeaders) {
|
|
67
|
+
const route = `/api/apps/${encodeURIComponent(appId)}/manage/comments`;
|
|
68
|
+
const result = await deployJson(token, route, orgHeaders);
|
|
69
|
+
if (typeof result.commentsEnabled !== "boolean" || !Array.isArray(result.comments)) {
|
|
70
|
+
throw new DeployCommentsError("hq-deploy returned an incomplete comments response.");
|
|
71
|
+
}
|
|
72
|
+
return { commentsEnabled: result.commentsEnabled, comments: result.comments };
|
|
73
|
+
}
|
|
74
|
+
function displayText(value) {
|
|
75
|
+
return [...String(value ?? "")].map((character) => {
|
|
76
|
+
const code = character.codePointAt(0) ?? 0;
|
|
77
|
+
return isUnsafeTerminalCodePoint(code)
|
|
78
|
+
? `\\u${code.toString(16).padStart(4, "0")}`
|
|
79
|
+
: character;
|
|
80
|
+
}).join("");
|
|
81
|
+
}
|
|
82
|
+
function isUnsafeTerminalCodePoint(code) {
|
|
83
|
+
return code <= 0x1f
|
|
84
|
+
|| (code >= 0x7f && code <= 0x9f)
|
|
85
|
+
|| (code >= 0x202a && code <= 0x202e)
|
|
86
|
+
|| (code >= 0x2066 && code <= 0x2069);
|
|
87
|
+
}
|
|
88
|
+
function safeJson(value) {
|
|
89
|
+
return JSON.stringify(value, null, 2).replace(/[\u007f-\u009f\u202a-\u202e\u2066-\u2069]/gu, (character) => `\\u${(character.codePointAt(0) ?? 0).toString(16).padStart(4, "0")}`);
|
|
90
|
+
}
|
|
91
|
+
function printCommentLine(comment) {
|
|
92
|
+
const firstLine = comment.body.split(/\r\n|\r|\n/, 1)[0] ?? "";
|
|
93
|
+
console.log([
|
|
94
|
+
displayText(comment.id),
|
|
95
|
+
displayText(comment.status),
|
|
96
|
+
displayText(comment.createdAt),
|
|
97
|
+
displayText(comment.author?.name || "Unknown author"),
|
|
98
|
+
displayText(comment.anchor?.mode || "unknown"),
|
|
99
|
+
displayText(firstLine),
|
|
100
|
+
].join(" | "));
|
|
101
|
+
}
|
|
102
|
+
async function run(action) {
|
|
103
|
+
try {
|
|
104
|
+
await action();
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.error(error instanceof DeployCommentsError ? error.message : "Deploy comments command failed.");
|
|
108
|
+
process.exitCode = 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function listAction(appNameOrId, options) {
|
|
112
|
+
const status = options.status ?? "all";
|
|
113
|
+
if (status !== "open" && status !== "resolved" && status !== "all") {
|
|
114
|
+
throw new DeployCommentsError("--status must be open, resolved, or all.");
|
|
115
|
+
}
|
|
116
|
+
const token = await ensureCognitoToken();
|
|
117
|
+
const orgHeaders = callerOrgHeaders(options.company);
|
|
118
|
+
const appId = await resolveAppId(token, appNameOrId, orgHeaders);
|
|
119
|
+
const result = await listComments(token, appId, orgHeaders);
|
|
120
|
+
const comments = status === "all"
|
|
121
|
+
? result.comments
|
|
122
|
+
: result.comments.filter((comment) => comment.status === status);
|
|
123
|
+
if (options.json) {
|
|
124
|
+
console.log(safeJson({ commentsEnabled: result.commentsEnabled, comments }));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (comments.length === 0) {
|
|
128
|
+
console.log("No comments.");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
for (const comment of comments)
|
|
132
|
+
printCommentLine(comment);
|
|
133
|
+
}
|
|
134
|
+
async function readAction(appNameOrId, commentId, company) {
|
|
135
|
+
const token = await ensureCognitoToken();
|
|
136
|
+
const orgHeaders = callerOrgHeaders(company);
|
|
137
|
+
const appId = await resolveAppId(token, appNameOrId, orgHeaders);
|
|
138
|
+
const result = await listComments(token, appId, orgHeaders);
|
|
139
|
+
const comment = result.comments.find((entry) => entry.id === commentId);
|
|
140
|
+
if (!comment)
|
|
141
|
+
throw new DeployCommentsError(`Comment '${displayText(commentId)}' was not found on that app.`);
|
|
142
|
+
console.log(safeJson(comment));
|
|
143
|
+
}
|
|
144
|
+
export function registerDeployCommentsCommand(deploy) {
|
|
145
|
+
const comments = deploy.command("comments").description("List and read comments on your deployed apps");
|
|
146
|
+
comments
|
|
147
|
+
.command("list <app>")
|
|
148
|
+
.description("List comments on an app in your org")
|
|
149
|
+
.option("--company <slug>", "use this company instead of the active company")
|
|
150
|
+
.option("--json", "print the API comment array as JSON")
|
|
151
|
+
.option("--status <status>", "filter by open, resolved, or all", "all")
|
|
152
|
+
.action((app, options) => run(() => listAction(app, options)));
|
|
153
|
+
comments
|
|
154
|
+
.command("read <app> <commentId>")
|
|
155
|
+
.description("Read a comment on an app in your org")
|
|
156
|
+
.option("--company <slug>", "use this company instead of the active company")
|
|
157
|
+
.action((app, commentId, options) => run(() => readAction(app, commentId, options.company)));
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=deploy-comments.js.map
|
|
@@ -13,7 +13,7 @@ import { keepAliveExclusion, keepAliveSupersededBy, LANE_SUPERSESSION_LINEAGE_AW
|
|
|
13
13
|
import { managerStatusGateEnabledForLane } from "../lib/lanes/manager-status-gate.js";
|
|
14
14
|
import { readLanesRegistryFlag } from "../lib/lanes/registry-flag.js";
|
|
15
15
|
import { resolveLaneCompany } from "../lib/lanes/lane-company.js";
|
|
16
|
-
import { accountMoveSummary, accountRequestKeepsCurrentLane, assertCurrentResumeAccountAvailable, recordedResumeEffort, resetKeepAliveProgress, resolveResumeEffort } from "./lanes-shared.js";
|
|
16
|
+
import { accountMoveSummary, accountRequestKeepsCurrentLane, assertCurrentResumeAccountAvailable, callerMatchesSessionSenior, recordedResumeEffort, resetKeepAliveProgress, resolveResumeEffort } from "./lanes-shared.js";
|
|
17
17
|
export function createLaneMessageHandlers(deps) {
|
|
18
18
|
const { archiveResumeMessage, assertLaneRunsHereForAdmission, assertLaneRunsHereForOperatorMutation, assessLaneAdmission, casUpdateLane, dispatchOutpostLaneCommand, drainHookStatusFor, laneIsHostedHere, laneWithRecoveredProviderSession, loadAllLanes, mintMessageId, persistClaudeAccountSelection, prepareAccountMove, printJson, recordResumeSetup, resolveActiveQuestionCompany, resolveResumeProfile, selectAutomaticClaudeResumeAccount, validateResumeProfileModel, withResumeHireAdmission } = deps;
|
|
19
19
|
function currentSuccessorOf(lane, lanes, lineageAware, companyForLane) {
|
|
@@ -30,7 +30,19 @@ export function createLaneMessageHandlers(deps) {
|
|
|
30
30
|
}
|
|
31
31
|
return current.lane_id === lane.lane_id ? undefined : current;
|
|
32
32
|
}
|
|
33
|
-
function callerMayMessageSupersededLane(ctx, lane, lanes, lineageAware, companyForLane, callerLaneIdOverride) {
|
|
33
|
+
function callerMayMessageSupersededLane(ctx, lane, lanes, lineageAware, companyForLane, callerLaneIdOverride, callerSessionIdOverride) {
|
|
34
|
+
if (lane.senior.kind === "session") {
|
|
35
|
+
const sessionSeniorId = lane.senior.id;
|
|
36
|
+
if (!sessionSeniorId)
|
|
37
|
+
return false;
|
|
38
|
+
if (callerLaneIdOverride !== undefined || ctx.env.HQ_LANE_ID?.trim())
|
|
39
|
+
return false;
|
|
40
|
+
if (callerSessionIdOverride === null)
|
|
41
|
+
return false;
|
|
42
|
+
return callerSessionIdOverride === undefined
|
|
43
|
+
? callerMatchesSessionSenior(ctx, sessionSeniorId)
|
|
44
|
+
: callerSessionIdOverride === sessionSeniorId;
|
|
45
|
+
}
|
|
34
46
|
if (lane.senior.kind !== "lane")
|
|
35
47
|
return false;
|
|
36
48
|
const callerLaneId = callerLaneIdOverride ?? ctx.env.HQ_LANE_ID?.trim();
|
|
@@ -86,11 +98,15 @@ export function createLaneMessageHandlers(deps) {
|
|
|
86
98
|
let lanes = localLanes;
|
|
87
99
|
const retiredLaneIds = loadRetiredLaneIds(ctx.hqRoot());
|
|
88
100
|
const dispatchedCallerId = grantContext?.superseded_message_caller_id;
|
|
89
|
-
const
|
|
90
|
-
|
|
101
|
+
const dispatchedSessionId = grantContext?.superseded_message_session_id;
|
|
102
|
+
const dispatchedContextBound = grantContext !== undefined &&
|
|
91
103
|
ctx.isSsmOwnerDispatch() &&
|
|
92
104
|
sameHireGrantSenior(grantContext.expected_senior, currentLane.senior);
|
|
93
|
-
|
|
105
|
+
const dispatchedSessionContext = dispatchedContextBound && currentLane.senior.kind === "session";
|
|
106
|
+
const dispatchedContextAuthorized = dispatchedContextBound && (dispatchedCallerId !== undefined ||
|
|
107
|
+
(dispatchedSessionId !== undefined && dispatchedSessionId === currentLane.senior.id));
|
|
108
|
+
if (dispatchedContextBound && grantContext &&
|
|
109
|
+
(dispatchedCallerId !== undefined || dispatchedSessionContext)) {
|
|
94
110
|
const merged = new Map(localLanes.map((record) => [record.lane_id, record]));
|
|
95
111
|
for (const snapshot of grantContext.hierarchy) {
|
|
96
112
|
const contextLane = keepAliveLaneFromSnapshot(snapshot);
|
|
@@ -101,14 +117,23 @@ export function createLaneMessageHandlers(deps) {
|
|
|
101
117
|
lanes = [...merged.values()];
|
|
102
118
|
}
|
|
103
119
|
const exclusion = keepAliveExclusion(currentLane, lanes, retiredLaneIds, lineageAware, companyForLane);
|
|
104
|
-
const callerLaneId =
|
|
105
|
-
|
|
120
|
+
const callerLaneId = dispatchedContextBound ? dispatchedCallerId : undefined;
|
|
121
|
+
const callerSessionId = dispatchedContextBound
|
|
122
|
+
? (dispatchedSessionId === undefined ? null : dispatchedSessionId)
|
|
123
|
+
: undefined;
|
|
124
|
+
if (exclusion === "superseded" && callerMayMessageSupersededLane(ctx, currentLane, lanes, lineageAware, companyForLane, callerLaneId, callerSessionId)) {
|
|
106
125
|
return {
|
|
107
126
|
lane: currentLane,
|
|
108
127
|
exclusion: undefined,
|
|
109
128
|
...(!dispatchedContextAuthorized && ctx.env.HQ_LANE_ID?.trim()
|
|
110
129
|
? { supersededMessageCallerId: ctx.env.HQ_LANE_ID.trim() }
|
|
111
130
|
: {}),
|
|
131
|
+
...(!dispatchedContextBound && currentLane.senior.kind === "session" &&
|
|
132
|
+
typeof currentLane.senior.id === "string" &&
|
|
133
|
+
currentLane.senior.id.length > 0 &&
|
|
134
|
+
callerMatchesSessionSenior(ctx, currentLane.senior.id)
|
|
135
|
+
? { supersededMessageSessionId: currentLane.senior.id }
|
|
136
|
+
: {}),
|
|
112
137
|
};
|
|
113
138
|
}
|
|
114
139
|
return { lane: currentLane, exclusion };
|
|
@@ -149,9 +174,17 @@ export function createLaneMessageHandlers(deps) {
|
|
|
149
174
|
const supersededMessageCallerId = callerLaneId === undefined || resolution.remoteOnly
|
|
150
175
|
? undefined
|
|
151
176
|
: (await messageLaneExclusion(ctx, lane)).supersededMessageCallerId;
|
|
177
|
+
const sessionSeniorId = lane.senior.kind === "session" ? lane.senior.id : undefined;
|
|
178
|
+
const supersededMessageSessionId = !resolution.remoteOnly &&
|
|
179
|
+
typeof sessionSeniorId === "string" &&
|
|
180
|
+
sessionSeniorId.length > 0 &&
|
|
181
|
+
callerMatchesSessionSenior(ctx, sessionSeniorId)
|
|
182
|
+
? sessionSeniorId
|
|
183
|
+
: undefined;
|
|
152
184
|
await dispatchOutpostLaneCommand(ctx, lane, "message", {
|
|
153
185
|
...opts,
|
|
154
186
|
...(supersededMessageCallerId === undefined ? {} : { supersededMessageCallerId }),
|
|
187
|
+
...(supersededMessageSessionId === undefined ? {} : { supersededMessageSessionId }),
|
|
155
188
|
}, resolution.remoteOnly
|
|
156
189
|
? { syncPrimary: false, expectedOwnerHost: resolution.ownerHost }
|
|
157
190
|
: undefined);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Ctx, LaneMoveHandlerDeps } from "./lanes-shared.js";
|
|
2
|
+
export declare function createLaneMoveHandlers(deps: LaneMoveHandlerDeps): {
|
|
3
|
+
runMove: (ctx: Ctx, laneId: string, opts: {
|
|
4
|
+
placement?: string;
|
|
5
|
+
}) => Promise<void>;
|
|
6
|
+
runMoveTarget: (ctx: Ctx, laneId: string, opts: {
|
|
7
|
+
hostKind?: string;
|
|
8
|
+
hostId?: string;
|
|
9
|
+
account?: string;
|
|
10
|
+
}) => Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=lanes-move.d.ts.map
|