@indigoai-us/hq-cli 5.273.0 → 5.274.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +32 -1
  3. package/dist/command-catalog.generated.d.ts +72 -0
  4. package/dist/command-catalog.generated.js +92 -0
  5. package/dist/command-registration-plan.d.ts +12 -0
  6. package/dist/command-registration-plan.js +2 -0
  7. package/dist/commands/cloud.js +40 -0
  8. package/dist/commands/core-checkpoint.js +3 -1
  9. package/dist/commands/core-command-catalog.d.ts +32 -0
  10. package/dist/commands/core-command-catalog.js +58 -0
  11. package/dist/commands/core-ontology.js +9 -4
  12. package/dist/commands/core.d.ts +4 -0
  13. package/dist/commands/core.js +79 -75
  14. package/dist/commands/deploy-comments.d.ts +3 -0
  15. package/dist/commands/deploy-comments.js +159 -0
  16. package/dist/commands/lanes-move.d.ts +12 -0
  17. package/dist/commands/lanes-move.js +342 -0
  18. package/dist/commands/lanes-resume.js +3 -1
  19. package/dist/commands/lanes-shared.d.ts +36 -1
  20. package/dist/commands/lanes-shared.js +59 -0
  21. package/dist/commands/lanes.d.ts +1 -1
  22. package/dist/commands/lanes.js +204 -456
  23. package/dist/commands/report.js +2 -14
  24. package/dist/fast-core.js +1 -1
  25. package/dist/{commands/scaffold-fast.d.ts → fast-scaffold.d.ts} +1 -1
  26. package/dist/{commands/scaffold-fast.js → fast-scaffold.js} +1 -1
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +2 -2
  29. package/dist/lib/checkpoint-ontology/apply.d.ts +8 -0
  30. package/dist/lib/checkpoint-ontology/apply.js +33 -0
  31. package/dist/lib/checkpoint-ontology/plan.d.ts +15 -0
  32. package/dist/lib/checkpoint-ontology/plan.js +15 -0
  33. package/dist/lib/deploy-api.d.ts +6 -0
  34. package/dist/lib/deploy-api.js +21 -0
  35. package/dist/lib/doctor/fix/apply.d.ts +17 -2
  36. package/dist/lib/doctor/fix/apply.js +111 -57
  37. package/dist/lib/doctor/fix/json-output.d.ts +9 -1
  38. package/dist/lib/doctor/fix/json-output.js +11 -0
  39. package/dist/lib/first-sync-next-step.d.ts +37 -0
  40. package/dist/lib/first-sync-next-step.js +189 -0
  41. package/dist/main.js +4 -1
  42. package/dist/utils/cli-telemetry.d.ts +7 -1
  43. package/dist/utils/cli-telemetry.js +84 -8
  44. package/dist/utils/contribution-table.d.ts +1 -1
  45. package/dist/utils/scaffold-fast-import-guard.d.ts +3 -0
  46. package/dist/utils/scaffold-fast-import-guard.js +28 -0
  47. package/dist/utils/scaffold-packlist-guard.d.ts +7 -0
  48. package/dist/utils/scaffold-packlist-guard.js +23 -0
  49. package/dist/utils/scaffold-sibling-references.d.ts +21 -0
  50. package/dist/utils/scaffold-sibling-references.js +156 -0
  51. package/package.json +1 -1
@@ -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: "core/scripts/archive-old-threads.sh", root: "live", summary: "Scaffold-only native utility companion" },
205
- { asset: "core/scripts/codex-skill-bridge.sh", root: "live", summary: "Scaffold-only native utility companion" },
206
- { asset: "core/scripts/qmd-reindex-after-sync.sh", root: "cwd", summary: "Scaffold-only native utility companion" },
207
- { asset: "core/scripts/detect-stale-core-policy-mirror.sh", root: "live", summary: "Scaffold-only native utility companion" },
208
- { asset: "core/scripts/hq-status-summary.sh", root: "cwd", summary: "Scaffold-only native utility companion" },
209
- { asset: "core/scripts/ontology-readme-drift.sh", root: "live", summary: "Scaffold-only native utility companion" },
210
- { asset: "core/scripts/resize-screenshot.sh", root: "cwd", summary: "Scaffold-only native utility companion" },
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,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerDeployCommentsCommand(deploy: Command): void;
3
+ //# sourceMappingURL=deploy-comments.d.ts.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
@@ -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