@indigoai-us/hq-cli 5.110.0 → 5.111.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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.111.0] — 2026-09-14
6
+
7
+ ### Added
8
+
9
+ - `hq skill delete <target>`: delete a company skill and all of its files for
10
+ everyone. Accepts a `skl_…` id, a SKILL.md path, or a skill slug; names the
11
+ skill and asks for confirmation (`--yes` skips it, and it refuses to run
12
+ unattended without `--yes`). Synced copies are removed on the next sync.
13
+ Requires access-admin rights on the skill.
14
+
5
15
  ## [5.110.0] — 2026-09-14
6
16
 
7
17
  ### Added
@@ -5,12 +5,13 @@
5
5
  * immutable UID, surfaces its generated runtime wrapper, and syncs it.
6
6
  * `hq skill propose <uid|path> --message "…"` posts a whole-skill comment to
7
7
  * the same improvement thread shown in HQ Console. It never uploads a modified
8
- * SKILL.md and cannot overwrite live content. Structured suggest/list/review
9
- * commands intentionally are not registered.
8
+ * SKILL.md and cannot overwrite live content. `hq skill delete <target>` removes
9
+ * a skill and its files for the whole company (confirmation unless --yes).
10
+ * Structured suggest/list/review commands intentionally are not registered.
10
11
  */
11
12
  import { Command } from "commander";
12
13
  import { ensureCognitoToken } from "../utils/cognito-session.js";
13
- import { vaultApiFetch } from "../utils/vault-api.js";
14
+ import { vaultApiFetch, getCompanyUid } from "../utils/vault-api.js";
14
15
  import { surfaceCompanySkill } from "../lib/company-skill-wrapper.js";
15
16
  export declare const SKILL_UID_PATTERN: RegExp;
16
17
  export declare const SKILL_SLUG_PATTERN: RegExp;
@@ -98,9 +99,17 @@ export declare function mapSkillError(status: number, body: Record<string, unkno
98
99
  export declare function skillApiError(status: number, body: Record<string, unknown>, opts?: {
99
100
  machineIdentity?: boolean;
100
101
  }): Error;
102
+ export type SkillConfirmFn = (message: string) => Promise<boolean>;
103
+ /**
104
+ * Resolve a delete target: a `skl_…` uid, a SKILL.md path or its directory, or
105
+ * a bare skill slug under `companies/<company>/skills/<slug>/`.
106
+ */
107
+ export declare function resolveDeleteTarget(target: string, cwd: string, hqRoot: string, companySlug: string): string;
101
108
  interface SkillCommandDeps {
102
109
  ensureToken?: typeof ensureCognitoToken;
103
110
  apiFetch?: typeof vaultApiFetch;
111
+ confirm?: SkillConfirmFn;
112
+ resolveCompanyUid?: typeof getCompanyUid;
104
113
  cwd?: () => string;
105
114
  hqRoot?: string;
106
115
  syncFile?: (input: SkillSyncInput) => Promise<SkillSyncResult>;
@@ -5,8 +5,9 @@
5
5
  * immutable UID, surfaces its generated runtime wrapper, and syncs it.
6
6
  * `hq skill propose <uid|path> --message "…"` posts a whole-skill comment to
7
7
  * the same improvement thread shown in HQ Console. It never uploads a modified
8
- * SKILL.md and cannot overwrite live content. Structured suggest/list/review
9
- * commands intentionally are not registered.
8
+ * SKILL.md and cannot overwrite live content. `hq skill delete <target>` removes
9
+ * a skill and its files for the whole company (confirmation unless --yes).
10
+ * Structured suggest/list/review commands intentionally are not registered.
10
11
  */
11
12
  import * as fs from "node:fs";
12
13
  import * as path from "node:path";
@@ -14,7 +15,8 @@ import chalk from "chalk";
14
15
  import yaml from "js-yaml";
15
16
  import { share } from "@indigoai-us/hq-cloud";
16
17
  import { ensureCognitoToken, DEFAULT_HQ_ROOT, buildVaultConfig, isMachineIdentity, } from "../utils/cognito-session.js";
17
- import { vaultApiFetch } from "../utils/vault-api.js";
18
+ import * as readline from "node:readline";
19
+ import { vaultApiFetch, getCompanyUid } from "../utils/vault-api.js";
18
20
  import { surfaceCompanySkill } from "../lib/company-skill-wrapper.js";
19
21
  import { AuthError } from "../utils/auth-error.js";
20
22
  import { redactErrorText } from "../utils/redact-error-text.js";
@@ -322,6 +324,31 @@ export function skillApiError(status, body, opts = {}) {
322
324
  }
323
325
  return new Error(message);
324
326
  }
327
+ function realConfirm(message) {
328
+ const rl = readline.createInterface({
329
+ input: process.stdin,
330
+ output: process.stdout,
331
+ });
332
+ return new Promise((resolve) => {
333
+ rl.question(`${message} [y/N] `, (answer) => {
334
+ rl.close();
335
+ resolve(/^y(es)?$/i.test(answer.trim()));
336
+ });
337
+ });
338
+ }
339
+ /**
340
+ * Resolve a delete target: a `skl_…` uid, a SKILL.md path or its directory, or
341
+ * a bare skill slug under `companies/<company>/skills/<slug>/`.
342
+ */
343
+ export function resolveDeleteTarget(target, cwd, hqRoot, companySlug) {
344
+ if (SKILL_UID_PATTERN.test(target))
345
+ return target;
346
+ const asPath = path.resolve(cwd, target);
347
+ if (!fs.existsSync(asPath) && SKILL_SLUG_PATTERN.test(target)) {
348
+ return resolveSkillUid(canonicalCompanySkillPath(hqRoot, companySlug, target), cwd);
349
+ }
350
+ return resolveSkillUid(target, cwd);
351
+ }
325
352
  export function registerSkillCommand(program, deps = {}) {
326
353
  const ensureToken = deps.ensureToken ?? ensureCognitoToken;
327
354
  const apiFetch = deps.apiFetch ?? vaultApiFetch;
@@ -329,6 +356,8 @@ export function registerSkillCommand(program, deps = {}) {
329
356
  const hqRoot = deps.hqRoot ?? DEFAULT_HQ_ROOT;
330
357
  const syncFile = deps.syncFile ?? defaultSyncFile;
331
358
  const surfaceSkillFn = deps.surfaceSkillFn ?? surfaceCompanySkill;
359
+ const confirm = deps.confirm ?? realConfirm;
360
+ const resolveCompanyUid = deps.resolveCompanyUid ?? getCompanyUid;
332
361
  const skill = program
333
362
  .command("skill")
334
363
  .description("Create company skills and discuss improvements")
@@ -516,6 +545,53 @@ export function registerSkillCommand(program, deps = {}) {
516
545
  console.log(` Message: ${posted.body}`);
517
546
  console.log(chalk.dim(" Review it in HQ Console → Skills → Improvements"));
518
547
  });
548
+ skill
549
+ .command("delete <target>")
550
+ .description("Delete a company skill and all of its files for everyone (skl_… uid, SKILL.md path, or skill slug). Prompts for confirmation unless --yes.")
551
+ .option("-y, --yes", "Skip the confirmation prompt (for scripts)")
552
+ .action(async (target, opts) => {
553
+ const parentOpts = skill.opts();
554
+ const resolvedRoot = path.resolve(parentOpts.hqRoot ?? hqRoot);
555
+ const companySlug = resolveCompanySlug(parentOpts.company, resolvedRoot);
556
+ const skillUid = resolveDeleteTarget(target, cwd(), resolvedRoot, companySlug);
557
+ const token = await ensureToken();
558
+ const companyUid = await resolveCompanyUid(token, companySlug);
559
+ const skillPath = `/v1/skills/${encodeURIComponent(companyUid)}/${encodeURIComponent(skillUid)}`;
560
+ const detail = await apiFetch({ token, path: skillPath, method: "GET" });
561
+ if (!detail.ok) {
562
+ const body = (await detail.json().catch(() => ({})));
563
+ throw skillApiError(detail.status, body, {
564
+ machineIdentity: isMachineIdentity(),
565
+ });
566
+ }
567
+ const { skill: record } = (await detail.json());
568
+ const name = typeof record?.name === "string" ? record.name : skillUid;
569
+ if (opts.yes !== true) {
570
+ if (!process.stdin.isTTY) {
571
+ throw localSkillError(`Refusing to delete '${name}' without confirmation. Re-run with --yes.`);
572
+ }
573
+ const ok = await confirm(`Delete skill '${name}' (${skillUid}) and all of its files for everyone in ${companySlug}? This can't be undone from the CLI.`);
574
+ if (!ok) {
575
+ console.log("Cancelled — nothing was deleted.");
576
+ return;
577
+ }
578
+ }
579
+ const response = await apiFetch({ token, path: skillPath, method: "DELETE" });
580
+ if (!response.ok) {
581
+ const body = (await response.json().catch(() => ({})));
582
+ throw skillApiError(response.status, body, {
583
+ machineIdentity: isMachineIdentity(),
584
+ });
585
+ }
586
+ const result = (await response.json());
587
+ console.log(chalk.green(`Skill deleted: ${name}`));
588
+ console.log(` Skill: ${skillUid}`);
589
+ console.log(` Files: ${typeof result.filesDeleted === "number" ? result.filesDeleted : 0} removed`);
590
+ if (result.tombstoneIncomplete === true) {
591
+ console.warn(chalk.yellow("⚠ Some synced computers may not be told to remove their copy. Delete it locally if it reappears."));
592
+ }
593
+ console.log(chalk.dim(" Local copies are removed on the next sync (hq sync now)."));
594
+ });
519
595
  return skill;
520
596
  }
521
597
  //# sourceMappingURL=skill.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.110.0",
3
+ "version": "5.111.0",
4
4
  "description": "HQ by Indigo management CLI \u2014 modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {