@indigoai-us/hq-cli 5.60.0 → 5.62.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 (98) hide show
  1. package/dist/commands/agents.d.ts +109 -0
  2. package/dist/commands/agents.js +385 -0
  3. package/dist/commands/db-migrate.d.ts +6 -0
  4. package/dist/commands/db-migrate.js +42 -0
  5. package/dist/commands/db-provision.d.ts +15 -0
  6. package/dist/commands/db-provision.js +78 -0
  7. package/dist/commands/db-sql.d.ts +9 -0
  8. package/dist/commands/db-sql.js +81 -0
  9. package/dist/commands/db-status.d.ts +7 -0
  10. package/dist/commands/db-status.js +70 -0
  11. package/dist/commands/db.d.ts +9 -0
  12. package/dist/commands/db.js +23 -0
  13. package/dist/commands/integrations.d.ts +78 -0
  14. package/dist/commands/integrations.js +309 -0
  15. package/dist/commands/members.js +4 -4
  16. package/dist/commands/outposts.d.ts +60 -0
  17. package/dist/commands/outposts.js +255 -0
  18. package/dist/commands/pack-install.d.ts +7 -1
  19. package/dist/commands/pack-install.js +86 -15
  20. package/dist/commands/packs.d.ts +2 -1
  21. package/dist/commands/packs.js +13 -8
  22. package/dist/commands/secrets.d.ts +13 -0
  23. package/dist/commands/secrets.js +149 -10
  24. package/dist/commands/skill.d.ts +153 -0
  25. package/dist/commands/skill.js +593 -0
  26. package/dist/commands/workers.d.ts +48 -0
  27. package/dist/commands/workers.js +229 -0
  28. package/dist/index.d.ts +5 -3
  29. package/dist/index.js +14 -240
  30. package/dist/lib/db/control-plane.d.ts +45 -0
  31. package/dist/lib/db/control-plane.js +81 -0
  32. package/dist/lib/db/local.d.ts +49 -0
  33. package/dist/lib/db/local.js +106 -0
  34. package/dist/lib/db/migrate.d.ts +41 -0
  35. package/dist/lib/db/migrate.js +104 -0
  36. package/dist/lib/db/paths.d.ts +56 -0
  37. package/dist/lib/db/paths.js +103 -0
  38. package/dist/lib/db/remote-engine.d.ts +58 -0
  39. package/dist/lib/db/remote-engine.js +90 -0
  40. package/dist/lib/db/remote-sql.d.ts +22 -0
  41. package/dist/lib/db/remote-sql.js +39 -0
  42. package/dist/lib/db/sql.d.ts +49 -0
  43. package/dist/lib/db/sql.js +132 -0
  44. package/dist/main.d.ts +7 -0
  45. package/dist/main.js +272 -0
  46. package/dist/utils/cognito-session.js +3 -3
  47. package/dist/utils/sandbox-runner-client.d.ts +13 -0
  48. package/dist/utils/sandbox-runner-client.js +83 -6
  49. package/dist/utils/version-check.d.ts +6 -0
  50. package/dist/utils/version-check.js +78 -2
  51. package/package.json +9 -1
  52. package/pnpm-workspace.yaml +2 -0
  53. package/src/commands/agents.test.ts +297 -0
  54. package/src/commands/agents.ts +561 -0
  55. package/src/commands/db-migrate.ts +55 -0
  56. package/src/commands/db-provision.ts +102 -0
  57. package/src/commands/db-sql.ts +124 -0
  58. package/src/commands/db-status.ts +100 -0
  59. package/src/commands/db.ts +26 -0
  60. package/src/commands/integrations.test.ts +284 -0
  61. package/src/commands/integrations.ts +438 -0
  62. package/src/commands/members.ts +2 -2
  63. package/src/commands/outposts.test.ts +177 -0
  64. package/src/commands/outposts.ts +338 -0
  65. package/src/commands/pack-install.ts +115 -18
  66. package/src/commands/pack-update-cache.test.ts +149 -0
  67. package/src/commands/packs.ts +28 -7
  68. package/src/commands/secrets.parse-destination.test.ts +38 -0
  69. package/src/commands/secrets.test.ts +342 -0
  70. package/src/commands/secrets.ts +227 -13
  71. package/src/commands/skill.test.ts +770 -0
  72. package/src/commands/skill.ts +796 -0
  73. package/src/commands/workers.test.ts +158 -0
  74. package/src/commands/workers.ts +298 -0
  75. package/src/index.test.ts +32 -0
  76. package/src/index.ts +11 -274
  77. package/src/lib/db/control-plane.test.ts +59 -0
  78. package/src/lib/db/control-plane.ts +113 -0
  79. package/src/lib/db/local.test.ts +81 -0
  80. package/src/lib/db/local.ts +148 -0
  81. package/src/lib/db/migrate.test.ts +133 -0
  82. package/src/lib/db/migrate.ts +137 -0
  83. package/src/lib/db/paths.test.ts +112 -0
  84. package/src/lib/db/paths.ts +128 -0
  85. package/src/lib/db/remote-engine.test.ts +44 -0
  86. package/src/lib/db/remote-engine.ts +148 -0
  87. package/src/lib/db/remote-sql.test.ts +32 -0
  88. package/src/lib/db/remote-sql.ts +62 -0
  89. package/src/lib/db/sql.test.ts +106 -0
  90. package/src/lib/db/sql.ts +192 -0
  91. package/src/main.ts +314 -0
  92. package/src/utils/cognito-session.ts +1 -1
  93. package/src/utils/sandbox-runner-client.test.ts +128 -0
  94. package/src/utils/sandbox-runner-client.ts +100 -4
  95. package/src/utils/version-check.test.ts +30 -0
  96. package/src/utils/version-check.ts +72 -0
  97. package/test/commands/db-tenant-isolation.test.ts +94 -0
  98. package/test/commands/db.test.ts +85 -0
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3b78d543-70d6-52e2-b19f-fa2650534e97")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="136bd272-0091-5fce-9a93-a2a375f98b38")}catch(e){}}();
3
3
  import chalk from "chalk";
4
4
  import * as readline from "node:readline";
5
5
  import { spawn } from "node:child_process";
@@ -153,6 +153,81 @@ function describeSecretAclPrincipal(principal) {
153
153
  ? "@all (entire company)"
154
154
  : principal.granteeId;
155
155
  }
156
+ // Mirrors hq-pro's server-authoritative KNOWN_DESTINATION_REGISTRY
157
+ // (src/vault-service/handlers/destination-registry.ts) — a KNOWN host's
158
+ // --auth-style is optional because the server resolves the recipe itself.
159
+ // This client-side copy exists purely so an UNKNOWN host with no
160
+ // --auth-style can be rejected immediately with a clear, actionable message
161
+ // instead of a round trip; the server remains the authoritative validator
162
+ // (this list drifting stale merely means one extra CLI round trip, not a
163
+ // security gap — the server still 400s an unrecognized host with no recipe).
164
+ const KNOWN_DESTINATION_HOSTS = new Set([
165
+ "api.anthropic.com",
166
+ "api.openai.com",
167
+ "api.stripe.com",
168
+ ]);
169
+ // Parses `--auth-style` into the InjectionRecipe shape the server expects.
170
+ // Returns `null` (with a printed error) for an unrecognized value.
171
+ function parseAuthStyle(authStyle) {
172
+ if (authStyle === "bearer") {
173
+ return { header: "authorization", scheme: "bearer" };
174
+ }
175
+ if (authStyle === "x-api-key") {
176
+ return { header: "x-api-key", scheme: "raw" };
177
+ }
178
+ const headerMatch = authStyle.match(/^header:(.+)$/);
179
+ if (headerMatch) {
180
+ const headerName = headerMatch[1].trim();
181
+ if (!headerName) {
182
+ console.error(chalk.red(`Invalid --auth-style 'header:': must name a header, e.g. header:X-Custom-Key`));
183
+ return null;
184
+ }
185
+ return { header: headerName, scheme: "raw" };
186
+ }
187
+ console.error(chalk.red(`Invalid --auth-style '${authStyle}': must be one of bearer, x-api-key, or header:NAME`));
188
+ return null;
189
+ }
190
+ // Validates `--destination` is a bare HTTPS scheme+host URL (no path, query,
191
+ // port). Mirrors hq-pro's `validateDestinations` server-side check
192
+ // (src/vault-service/handlers/secrets.ts) so a malformed URL is caught
193
+ // locally with an actionable message rather than a round trip — the server
194
+ // re-validates and remains authoritative.
195
+ export function parseDestinationUrl(raw) {
196
+ let parsed;
197
+ try {
198
+ parsed = new URL(raw);
199
+ }
200
+ catch {
201
+ console.error(chalk.red(`Invalid --destination '${raw}': must be a valid URL`));
202
+ return { ok: false };
203
+ }
204
+ if (parsed.protocol !== "https:") {
205
+ console.error(chalk.red(`Invalid --destination '${raw}': must use https://`));
206
+ return { ok: false };
207
+ }
208
+ if (!parsed.hostname) {
209
+ console.error(chalk.red(`Invalid --destination '${raw}': missing hostname`));
210
+ return { ok: false };
211
+ }
212
+ // Reject an embedded `user:pass@host` credential segment explicitly rather
213
+ // than letting `new URL()` silently drop it (mirrors hq-pro's authoritative
214
+ // `validateDestinations`; the server re-validates and remains authoritative).
215
+ if (parsed.username !== "" || parsed.password !== "") {
216
+ console.error(chalk.red(`Invalid --destination '${raw}': must not contain embedded userinfo (user:pass@) credentials`));
217
+ return { ok: false };
218
+ }
219
+ if ((parsed.pathname !== "" && parsed.pathname !== "/") ||
220
+ parsed.search !== "" ||
221
+ parsed.hash !== "") {
222
+ console.error(chalk.red(`Invalid --destination '${raw}': must be a bare scheme+host URL with no path, query, or fragment (e.g. https://api.openai.com)`));
223
+ return { ok: false };
224
+ }
225
+ if (parsed.port !== "") {
226
+ console.error(chalk.red(`Invalid --destination '${raw}': must not specify a port`));
227
+ return { ok: false };
228
+ }
229
+ return { ok: true, url: `https://${parsed.hostname}`, hostname: parsed.hostname };
230
+ }
156
231
  function normalizeSecretTier(tier) {
157
232
  return tier === "sensitive" || tier === "nuclear" ? tier : "standard";
158
233
  }
@@ -184,8 +259,16 @@ async function buildSecretUsage(channel, scriptPath, scriptId, attestationLevel
184
259
  },
185
260
  };
186
261
  }
262
+ // Commander collector for `--only`: accumulates across REPEATED flags instead
263
+ // of the last one silently winning, and each value may still be
264
+ // comma-separated. So `--only A,B`, `--only A --only B`, and
265
+ // `--only A,B --only C` all resolve to the full list.
266
+ export function collectSecretNames(value, previous) {
267
+ const parsed = value.split(",").map((k) => k.trim()).filter(Boolean);
268
+ return (previous ?? []).concat(parsed);
269
+ }
187
270
  function parseSecretNameList(input) {
188
- const keys = input.split(",").map((k) => k.trim()).filter(Boolean);
271
+ const keys = input ?? [];
189
272
  if (keys.length === 0) {
190
273
  console.error(chalk.red("Error: --only requires at least one secret name."));
191
274
  process.exit(1);
@@ -378,12 +461,56 @@ export function registerSecretsCommand(program) {
378
461
  .command("set <name>")
379
462
  .description("Create or update a secret")
380
463
  .option("--from-stdin", "Read secret value from piped stdin")
464
+ .option("--high-security", "Mark the secret high-security: it can never be revealed or injected locally, only used through the HQ secret proxy (requires --destination)")
465
+ .option("--destination <https-url>", "Approved scheme+host HTTPS URL the proxy may forward this secret to (e.g. https://api.openai.com); required with --high-security")
466
+ .option("--auth-style <style>", "How the proxy attaches the key upstream: bearer | x-api-key | header:NAME. Optional for known destinations (auto-resolved server-side); required for unknown ones")
381
467
  .action(async (name, opts) => {
382
468
  try {
383
469
  if (!SECRET_NAME_PATTERN.test(name)) {
384
470
  console.error(chalk.red(`Invalid secret name '${name}': must match ^[A-Z][A-Z0-9_]*(/[A-Z][A-Z0-9_]+)*$ (e.g. MY_API_KEY or DEV/MY_KEY)`));
385
471
  process.exit(1);
386
472
  }
473
+ // secrets-proxy-per-secret-destination US-006: --high-security marks
474
+ // the secret so it can only ever be used through the server-side
475
+ // proxy (never revealed/injected locally — that refusal is the
476
+ // pre-existing consumption-side behavior in `get`/`exec`/`env` above,
477
+ // unchanged by this story). It REQUIRES a --destination: the proxy
478
+ // (hq-pro US-002) fails closed with no destination configured, so
479
+ // catching the missing pin here is a clear, immediate CLI error
480
+ // rather than a deferred proxy-time failure.
481
+ let destinations;
482
+ let injection;
483
+ if (opts.highSecurity) {
484
+ if (!opts.destination) {
485
+ console.error(chalk.red("Error: --high-security requires --destination <https-url> (e.g. --destination https://api.openai.com)."));
486
+ process.exit(1);
487
+ }
488
+ const destResult = parseDestinationUrl(opts.destination);
489
+ if (!destResult.ok) {
490
+ process.exit(1);
491
+ }
492
+ destinations = [destResult.url];
493
+ if (opts.authStyle) {
494
+ const recipe = parseAuthStyle(opts.authStyle);
495
+ if (!recipe) {
496
+ process.exit(1);
497
+ }
498
+ injection = recipe;
499
+ }
500
+ else if (!KNOWN_DESTINATION_HOSTS.has(destResult.hostname)) {
501
+ // Unknown host + no explicit recipe: the server would reject this
502
+ // 400 anyway (US-004 registry lookup only, never guesses) — fail
503
+ // fast locally with an actionable message instead of a round trip.
504
+ console.error(chalk.red(`Error: unknown destination host '${destResult.hostname}' — provide --auth-style <bearer|x-api-key|header:NAME> (known hosts auto-resolve: ${[...KNOWN_DESTINATION_HOSTS].join(", ")}).`));
505
+ process.exit(1);
506
+ }
507
+ // Known host + no --auth-style: leave `injection` undefined so the
508
+ // server (US-004) auto-resolves the recipe from its registry.
509
+ }
510
+ else if (opts.destination || opts.authStyle) {
511
+ console.error(chalk.red("Error: --destination/--auth-style require --high-security."));
512
+ process.exit(1);
513
+ }
387
514
  let value;
388
515
  if (opts.fromStdin) {
389
516
  if (process.stdin.isTTY) {
@@ -419,15 +546,27 @@ export function registerSecretsCommand(program) {
419
546
  token,
420
547
  path: `/secrets/${encodeURIComponent(companyUid)}`,
421
548
  method: "POST",
422
- body: { name, value },
549
+ body: {
550
+ name,
551
+ value,
552
+ // Only present when --high-security was passed — an ordinary
553
+ // `set` with no flags sends exactly `{ name, value }`, byte-for-
554
+ // byte unchanged from before this story.
555
+ ...(opts.highSecurity ? { highSecurity: true } : {}),
556
+ ...(destinations ? { destinations } : {}),
557
+ ...(injection ? { injection } : {}),
558
+ },
423
559
  });
424
560
  if (!res.ok) {
425
- const body = await res.json().catch(() => ({}));
426
- console.error(chalk.red(`Failed to set secret: ${body.error ?? res.statusText}`));
561
+ const body = (await res.json().catch(() => ({})));
562
+ console.error(chalk.red(`Failed to set secret: ${extractApiMessage(body, res.statusText)}`));
427
563
  process.exit(1);
428
564
  }
429
565
  removeCacheEntry(companyUid, name);
430
566
  console.log(chalk.green(formatSecretSaved(name, scopeLabel)));
567
+ if (opts.highSecurity) {
568
+ console.log(chalk.dim(` High-security: destination pinned to ${destinations?.[0]}. This value can never be revealed or injected locally — only used through the HQ secret proxy.`));
569
+ }
431
570
  }
432
571
  catch (err) {
433
572
  console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
@@ -873,11 +1012,11 @@ export function registerSecretsCommand(program) {
873
1012
  .description("Run a command in the hosted sandbox with named secrets injected as env vars; open egress, secrets never touch this machine")
874
1013
  .option("--company <slug>", "Company slug (resolves to companyUid)")
875
1014
  .option("--personal", "Operate on the caller's personal vault (no sharing)")
876
- .option("--only <keys>", "Comma-separated list of secret names to inject (required)")
1015
+ .option("--only <keys>", "Secret names to inject (comma-separated; may be repeated) (required)", collectSecretNames)
877
1016
  .allowUnknownOption(true)
878
1017
  .action(async (opts, cmd) => {
879
1018
  try {
880
- if (!opts.only || opts.only.trim().length === 0) {
1019
+ if (!opts.only || opts.only.length === 0) {
881
1020
  console.error(chalk.red("Error: --only is required and must name at least one secret."));
882
1021
  process.exit(1);
883
1022
  }
@@ -920,7 +1059,7 @@ export function registerSecretsCommand(program) {
920
1059
  secrets
921
1060
  .command("exec")
922
1061
  .description("Run a command with secrets injected as env vars")
923
- .requiredOption("--only <keys>", "Comma-separated list of secret names to inject (required)")
1062
+ .requiredOption("--only <keys>", "Secret names to inject (comma-separated; may be repeated) (required)", collectSecretNames)
924
1063
  .option("--script <path>", "Attach local script identity for script-locked secrets")
925
1064
  .allowUnknownOption(true)
926
1065
  .action(async (_opts, cmd) => {
@@ -976,7 +1115,7 @@ export function registerSecretsCommand(program) {
976
1115
  secrets
977
1116
  .command("env")
978
1117
  .description("Print 'export KEY=VALUE' lines suitable for: source <(hq secrets env --only K1,K2)")
979
- .requiredOption("--only <keys>", "Comma-separated list of secret names to print (required)")
1118
+ .requiredOption("--only <keys>", "Secret names to print (comma-separated; may be repeated) (required)", collectSecretNames)
980
1119
  .option("--script <path>", "Attach local script identity for script-locked secrets")
981
1120
  .action(async (opts) => {
982
1121
  try {
@@ -1258,4 +1397,4 @@ export function registerSecretsCommand(program) {
1258
1397
  });
1259
1398
  }
1260
1399
  //# sourceMappingURL=secrets.js.map
1261
- //# debugId=3b78d543-70d6-52e2-b19f-fa2650534e97
1400
+ //# debugId=136bd272-0091-5fce-9a93-a2a375f98b38
@@ -0,0 +1,153 @@
1
+ /**
2
+ * `hq skill` subcommand group (US-017).
3
+ *
4
+ * Terminal / scriptable access to the skill collaboration loop:
5
+ *
6
+ * hq skill suggest <uid|path> Propose a change to a skill from working edits.
7
+ * hq skill list-suggestions Show the review inbox (suggestions on skills you own).
8
+ * hq skill review <sgn_…> Accept (merge) or decline a pending suggestion.
9
+ *
10
+ * This is a THIN front-end over the SAME wired hq-pro routes the MCP surface
11
+ * (US-007) and the console merge path (US-009) use — there is NO forked
12
+ * suggestion or merge logic here. The CLI reads the local working SKILL.md,
13
+ * computes the proposed content (+ its base for a diff), and POSTs to:
14
+ *
15
+ * CREATE POST /v1/files/skills/company/{slug}/{skillUid}/suggestions
16
+ * LIST POST /v1/files/skills/company/{slug}/suggestions/list
17
+ * ACCEPT POST /v1/files/skills/company/{slug}/{skillUid}/suggestions/{id}/accept
18
+ * DECLINE POST /v1/files/skills/company/{slug}/{skillUid}/suggestions/{id}/decline
19
+ *
20
+ * The skill routes are keyed on the company SLUG (path param, resolved
21
+ * server-side via findEntityBySlug) — NOT the companyUid the vault/ACL routes
22
+ * use — so this module resolves a slug (from `--company` or the active company)
23
+ * and passes it straight through.
24
+ *
25
+ * Lock semantics (AC3): a suggest against a skill the caller cannot WRITE still
26
+ * SUCCEEDS as a proposal. The CREATE route is MEMBER-gated (never write-gated),
27
+ * so this command performs NO client-side lock/permission pre-check — it always
28
+ * posts and renders whatever the server returns. A locked-out member lands a
29
+ * suggestion, never a hard permission error.
30
+ *
31
+ * Attribution (AC4): the invoking identity (from the Cognito JWT) is the server-
32
+ * derived `authorPersonUid`; the CLI never sends an author. An optional
33
+ * `--note` rides as `authorNote` (the change's rationale / failure context).
34
+ */
35
+ import { Command } from "commander";
36
+ /**
37
+ * A `skl_…` argument to `suggest` is a skill UID (resolve the local file from
38
+ * it); anything else is a filesystem path. Lenient on the suffix (the strict
39
+ * `skl_<ulid>` shape is `isSkillUid` on the server) so a hand-typed / fixture
40
+ * uid still routes to the uid branch.
41
+ */
42
+ export declare const SKILL_UID_PATTERN: RegExp;
43
+ /** A `sgn_…` suggestion id — the `review` target and the LIST row key. */
44
+ export declare const SUGGESTION_ID_PATTERN: RegExp;
45
+ /**
46
+ * Read the top-level `skill_uid` from a SKILL.md's YAML frontmatter. Mirrors
47
+ * hq-pro's `parseSkillFrontmatter` (block extraction → YAML parse → read the
48
+ * top-level `skill_uid` string). Returns `undefined` when there is no
49
+ * frontmatter, it fails to parse, or `skill_uid` is absent / not a `skl_…`
50
+ * string. Never throws.
51
+ */
52
+ export declare function parseSkillUid(md: string): string | undefined;
53
+ /** sha256 hex of a string — the base-version fingerprint the server records (AC2). */
54
+ export declare function sha256Hex(content: string): string;
55
+ export interface SuggestionCreateBody {
56
+ proposedContent: string;
57
+ baseContent?: string;
58
+ baseContentHash?: string;
59
+ authorNote?: string;
60
+ }
61
+ /**
62
+ * Build the CREATE request body from the resolved proposed content and an
63
+ * optional base. Mirrors the server's two payload shapes:
64
+ * - `baseContent` present → the server derives the unified diff + base hash
65
+ * (the diff-by-default path). A no-op (base === proposed) is rejected here
66
+ * rather than round-tripped to an EmptySuggestion 400.
67
+ * - no base → a `full-file` proposal; the server REQUIRES a `baseContentHash`,
68
+ * so we fingerprint the proposed content (a "here is my whole file" proposal
69
+ * with no base to diff against).
70
+ * An empty / whitespace-only note is dropped (kept absent, not blank).
71
+ */
72
+ export declare function buildSuggestionCreateBody(input: {
73
+ proposedContent: string;
74
+ baseContent?: string;
75
+ note?: string;
76
+ }): SuggestionCreateBody;
77
+ /**
78
+ * Map an hq-pro skill route error to a single user-facing line. Pure so the
79
+ * status → copy mapping is unit-tested independently of the network. Prefers the
80
+ * server's own `error` / `message` (they carry the actionable specifics — e.g.
81
+ * "Skill not found", "You need write access to this skill…").
82
+ */
83
+ export declare function mapSkillError(status: number, body: Record<string, unknown>): string;
84
+ /** LIST inbox row shape returned by `suggestionToWire` on the server. */
85
+ export interface SuggestionRow {
86
+ suggestionId: string;
87
+ skillUid: string;
88
+ authorPersonUid: string;
89
+ status: string;
90
+ baseChanged: boolean;
91
+ presentation?: string;
92
+ unifiedDiff?: string;
93
+ fullContent?: string;
94
+ baseContentHash?: string;
95
+ currentContentHash?: string;
96
+ authorNote?: string;
97
+ createdAt: string;
98
+ path: string;
99
+ }
100
+ /**
101
+ * Render the review inbox as a table (one row per suggestion), optionally
102
+ * printing each suggestion's unified diff (or full proposed file, when the base
103
+ * drifted / the proposal is full-file) beneath its row. Pure → snapshot-testable.
104
+ */
105
+ export declare function formatSuggestionsList(suggestions: SuggestionRow[], opts?: {
106
+ showDiff?: boolean;
107
+ }): string;
108
+ /** Read `.hq/config.json`'s `activeCompany` (mirrors signals/sources). */
109
+ export declare function readActiveCompanySlug(hqRoot: string): string | undefined;
110
+ /**
111
+ * The skill routes are keyed on the company SLUG. Precedence: explicit
112
+ * `--company` → `.hq/config.json` activeCompany. Throws with actionable copy
113
+ * when neither is available.
114
+ */
115
+ export declare function resolveCompanySlug(flag: string | undefined, hqRoot?: string): string;
116
+ export interface ResolvedSkillTarget {
117
+ /** Absolute path to the SKILL.md. */
118
+ filePath: string;
119
+ /** The `skl_…` uid read from its frontmatter (or the uid arg). */
120
+ skillUid: string;
121
+ /** The working-tree SKILL.md content — the proposed content. */
122
+ content: string;
123
+ }
124
+ /**
125
+ * Recursively find a SKILL.md whose frontmatter `skill_uid` equals `uid`,
126
+ * searching each root breadth-first with a bounded depth (skips VCS / build /
127
+ * dependency dirs). Returns the first match's absolute path, or null.
128
+ */
129
+ export declare function findSkillFileByUid(roots: string[], uid: string, maxDepth?: number): string | null;
130
+ /**
131
+ * Resolve a `suggest` target (a `skl_…` uid OR a filesystem path) to the local
132
+ * SKILL.md, its uid, and its content. A uid is resolved by scanning the company
133
+ * skills dir and the cwd; a path is read directly (a directory → its SKILL.md),
134
+ * with the uid read from the file's frontmatter.
135
+ */
136
+ export declare function resolveSkillTarget(target: string, deps: {
137
+ cwd: string;
138
+ hqRoot: string;
139
+ companySlug: string;
140
+ }): ResolvedSkillTarget;
141
+ /**
142
+ * Read the committed (HEAD) version of a file from its git repo — the diff base
143
+ * for "propose my working changes". Returns null when the file is untracked, not
144
+ * in a repo, or git is unavailable (the caller then falls back to full-file, or
145
+ * errors under `--diff`). Never throws.
146
+ */
147
+ export declare function readGitBase(filePath: string): Promise<string | null>;
148
+ /** Injectable git-base seam so `suggest` is testable without a real repo. */
149
+ export type GitBaseReader = (filePath: string) => Promise<string | null>;
150
+ export declare function registerSkillCommand(program: Command, deps?: {
151
+ gitBase?: GitBaseReader;
152
+ }): Command;
153
+ //# sourceMappingURL=skill.d.ts.map