@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.
- package/dist/commands/agents.d.ts +109 -0
- package/dist/commands/agents.js +385 -0
- package/dist/commands/db-migrate.d.ts +6 -0
- package/dist/commands/db-migrate.js +42 -0
- package/dist/commands/db-provision.d.ts +15 -0
- package/dist/commands/db-provision.js +78 -0
- package/dist/commands/db-sql.d.ts +9 -0
- package/dist/commands/db-sql.js +81 -0
- package/dist/commands/db-status.d.ts +7 -0
- package/dist/commands/db-status.js +70 -0
- package/dist/commands/db.d.ts +9 -0
- package/dist/commands/db.js +23 -0
- package/dist/commands/integrations.d.ts +78 -0
- package/dist/commands/integrations.js +309 -0
- package/dist/commands/members.js +4 -4
- package/dist/commands/outposts.d.ts +60 -0
- package/dist/commands/outposts.js +255 -0
- package/dist/commands/pack-install.d.ts +7 -1
- package/dist/commands/pack-install.js +86 -15
- package/dist/commands/packs.d.ts +2 -1
- package/dist/commands/packs.js +13 -8
- package/dist/commands/secrets.d.ts +13 -0
- package/dist/commands/secrets.js +149 -10
- package/dist/commands/skill.d.ts +153 -0
- package/dist/commands/skill.js +593 -0
- package/dist/commands/workers.d.ts +48 -0
- package/dist/commands/workers.js +229 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +14 -240
- package/dist/lib/db/control-plane.d.ts +45 -0
- package/dist/lib/db/control-plane.js +81 -0
- package/dist/lib/db/local.d.ts +49 -0
- package/dist/lib/db/local.js +106 -0
- package/dist/lib/db/migrate.d.ts +41 -0
- package/dist/lib/db/migrate.js +104 -0
- package/dist/lib/db/paths.d.ts +56 -0
- package/dist/lib/db/paths.js +103 -0
- package/dist/lib/db/remote-engine.d.ts +58 -0
- package/dist/lib/db/remote-engine.js +90 -0
- package/dist/lib/db/remote-sql.d.ts +22 -0
- package/dist/lib/db/remote-sql.js +39 -0
- package/dist/lib/db/sql.d.ts +49 -0
- package/dist/lib/db/sql.js +132 -0
- package/dist/main.d.ts +7 -0
- package/dist/main.js +272 -0
- package/dist/utils/cognito-session.js +3 -3
- package/dist/utils/sandbox-runner-client.d.ts +13 -0
- package/dist/utils/sandbox-runner-client.js +83 -6
- package/dist/utils/version-check.d.ts +6 -0
- package/dist/utils/version-check.js +78 -2
- package/package.json +9 -1
- package/pnpm-workspace.yaml +2 -0
- package/src/commands/agents.test.ts +297 -0
- package/src/commands/agents.ts +561 -0
- package/src/commands/db-migrate.ts +55 -0
- package/src/commands/db-provision.ts +102 -0
- package/src/commands/db-sql.ts +124 -0
- package/src/commands/db-status.ts +100 -0
- package/src/commands/db.ts +26 -0
- package/src/commands/integrations.test.ts +284 -0
- package/src/commands/integrations.ts +438 -0
- package/src/commands/members.ts +2 -2
- package/src/commands/outposts.test.ts +177 -0
- package/src/commands/outposts.ts +338 -0
- package/src/commands/pack-install.ts +115 -18
- package/src/commands/pack-update-cache.test.ts +149 -0
- package/src/commands/packs.ts +28 -7
- package/src/commands/secrets.parse-destination.test.ts +38 -0
- package/src/commands/secrets.test.ts +342 -0
- package/src/commands/secrets.ts +227 -13
- package/src/commands/skill.test.ts +770 -0
- package/src/commands/skill.ts +796 -0
- package/src/commands/workers.test.ts +158 -0
- package/src/commands/workers.ts +298 -0
- package/src/index.test.ts +32 -0
- package/src/index.ts +11 -274
- package/src/lib/db/control-plane.test.ts +59 -0
- package/src/lib/db/control-plane.ts +113 -0
- package/src/lib/db/local.test.ts +81 -0
- package/src/lib/db/local.ts +148 -0
- package/src/lib/db/migrate.test.ts +133 -0
- package/src/lib/db/migrate.ts +137 -0
- package/src/lib/db/paths.test.ts +112 -0
- package/src/lib/db/paths.ts +128 -0
- package/src/lib/db/remote-engine.test.ts +44 -0
- package/src/lib/db/remote-engine.ts +148 -0
- package/src/lib/db/remote-sql.test.ts +32 -0
- package/src/lib/db/remote-sql.ts +62 -0
- package/src/lib/db/sql.test.ts +106 -0
- package/src/lib/db/sql.ts +192 -0
- package/src/main.ts +314 -0
- package/src/utils/cognito-session.ts +1 -1
- package/src/utils/sandbox-runner-client.test.ts +128 -0
- package/src/utils/sandbox-runner-client.ts +100 -4
- package/src/utils/version-check.test.ts +30 -0
- package/src/utils/version-check.ts +72 -0
- package/test/commands/db-tenant-isolation.test.ts +94 -0
- package/test/commands/db.test.ts +85 -0
package/dist/commands/secrets.js
CHANGED
|
@@ -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]="
|
|
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
|
|
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: {
|
|
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
|
|
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>", "
|
|
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.
|
|
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>", "
|
|
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>", "
|
|
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=
|
|
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
|