harnessed 3.9.16 → 3.9.18

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/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawnSync, spawn, execSync } from 'child_process';
3
3
  import { existsSync, mkdirSync, renameSync, writeFileSync, readFileSync, readdirSync } from 'fs';
4
- import { join, dirname, resolve, relative } from 'path';
4
+ import { join, dirname, resolve, sep, relative } from 'path';
5
5
  import { homedir } from 'os';
6
6
  import { readFile, readdir, unlink, writeFile, stat, rm, cp, mkdir, access, rename } from 'fs/promises';
7
7
  import { Type } from '@sinclair/typebox';
@@ -287,25 +287,25 @@ var init_schemaVersion = __esm({
287
287
  routeDecisionLog: "harnessed.route-decision-log.v1",
288
288
  checkpoint: "harnessed.checkpoint.v1",
289
289
  currentWorkflow: "harnessed.current-workflow.v1",
290
- // ← Phase 3.1 W1 T1.1 ADD 8th surface (D-02 KARPATHY 3-state)
290
+ // ← Phase 3.1 W1 T1.1 ADD (D-02 KARPATHY 3-state)
291
291
  config: "harnessed.config.v1",
292
- // ← Phase 3.2 W1 T1.1 ADD 9th surface (D-01 PROBE gstack_prefix store)
292
+ // ← Phase 3.2 W1 T1.1 ADD (D-01 PROBE gstack_prefix store)
293
293
  governance: "harnessed.governance.v1",
294
- // ← Phase 3.2 W1 T1.1 ADD 10th surface (D-04 PUSH veto status)
294
+ // ← Phase 3.2 W1 T1.1 ADD (D-04 PUSH veto status)
295
295
  aliases: "harnessed.aliases.v1",
296
- // ← Phase 3.3 W1 T1.1 ADD 12th surface (D-01 RICH)
296
+ // ← Phase 3.3 W1 T1.1 ADD (D-01 RICH)
297
297
  knownGood: "harnessed.known-good.v1",
298
- // ← Phase 3.3 W1 T1.1 ADD 13th surface (D-03 YAML manifest)
298
+ // ← Phase 3.3 W1 T1.1 ADD (D-03 YAML manifest)
299
299
  capabilities: "harnessed.capabilities.v1",
300
- // ← Phase v2.0-2.3 W0 ADD 14th surface (R20.2 flat yaml capabilities manifest validate)
300
+ // ← Phase v2.0-2.3 W0 ADD (R20.2 flat yaml capabilities manifest validate)
301
301
  judgment: "harnessed.judgment.v1",
302
- // ← Phase v2.0-2.3 W0 ADD 15th surface (R20.4 multi-file judgments validate)
302
+ // ← Phase v2.0-2.3 W0 ADD (R20.4 multi-file judgments validate)
303
303
  workflow: "harnessed.workflow.v2",
304
- // ← Phase v2.0-2.4 W0 T2.4.W0.1 ADD 16th surface (R20.1+R20.2+R20.9 workflow.yaml v2)
304
+ // ← Phase v2.0-2.4 W0 T2.4.W0.1 ADD (R20.1+R20.2+R20.9 workflow.yaml v2)
305
305
  workflow_v3: "harnessed.workflow.v3",
306
- // ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD 17th surface (D-09 disciplines_applied + D-05 tools_available + master delegates_to per Pattern A B.1 LOCK)
306
+ // ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD (D-09 disciplines_applied + D-05 tools_available + master delegates_to per Pattern A B.1 LOCK)
307
307
  discipline: "harnessed.discipline.v1"
308
- // ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD 18th surface (D-09 L0 Discipline Substrate, sister judgment.v1 multi-file pattern)
308
+ // ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD (D-09 L0 Discipline Substrate, sister judgment.v1 multi-file pattern)
309
309
  };
310
310
  Type.Union([
311
311
  Type.Literal(SCHEMA_VERSIONS.routingSnapshot),
@@ -316,25 +316,25 @@ var init_schemaVersion = __esm({
316
316
  Type.Literal(SCHEMA_VERSIONS.routeDecisionLog),
317
317
  Type.Literal(SCHEMA_VERSIONS.checkpoint),
318
318
  Type.Literal(SCHEMA_VERSIONS.currentWorkflow),
319
- // ← Phase 3.1 W1 T1.1 ADD 8th surface
319
+ // ← Phase 3.1 W1 T1.1 ADD
320
320
  Type.Literal(SCHEMA_VERSIONS.config),
321
- // ← Phase 3.2 W1 T1.1 ADD 9th surface
321
+ // ← Phase 3.2 W1 T1.1 ADD
322
322
  Type.Literal(SCHEMA_VERSIONS.governance),
323
- // ← Phase 3.2 W1 T1.1 ADD 10th surface
323
+ // ← Phase 3.2 W1 T1.1 ADD
324
324
  Type.Literal(SCHEMA_VERSIONS.aliases),
325
- // ← Phase 3.3 W1 T1.1 ADD 12th surface
325
+ // ← Phase 3.3 W1 T1.1 ADD
326
326
  Type.Literal(SCHEMA_VERSIONS.knownGood),
327
- // ← Phase 3.3 W1 T1.1 ADD 13th surface
327
+ // ← Phase 3.3 W1 T1.1 ADD
328
328
  Type.Literal(SCHEMA_VERSIONS.capabilities),
329
- // ← Phase v2.0-2.3 W0 ADD 14th surface
329
+ // ← Phase v2.0-2.3 W0 ADD
330
330
  Type.Literal(SCHEMA_VERSIONS.judgment),
331
- // ← Phase v2.0-2.3 W0 ADD 15th surface
331
+ // ← Phase v2.0-2.3 W0 ADD
332
332
  Type.Literal(SCHEMA_VERSIONS.workflow),
333
- // ← Phase v2.0-2.4 W0 T2.4.W0.1 ADD 16th surface (first .v2 in union)
333
+ // ← Phase v2.0-2.4 W0 T2.4.W0.1 ADD (first .v2 in union)
334
334
  Type.Literal(SCHEMA_VERSIONS.workflow_v3),
335
- // ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD 17th surface (first .v3 in union)
335
+ // ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD (first .v3 in union)
336
336
  Type.Literal(SCHEMA_VERSIONS.discipline)
337
- // ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD 18th surface
337
+ // ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD
338
338
  ]);
339
339
  }
340
340
  });
@@ -1218,7 +1218,7 @@ var init_auto_install = __esm({
1218
1218
 
1219
1219
  // package.json
1220
1220
  var package_default = {
1221
- version: "3.9.16"};
1221
+ version: "3.9.18"};
1222
1222
 
1223
1223
  // src/manifest/errors.ts
1224
1224
  function instancePathToKeyPath(instancePath) {
@@ -2057,9 +2057,9 @@ function redactRecord(r) {
2057
2057
  }
2058
2058
  function renderHumanTable(records) {
2059
2059
  const header = `${"ts".padEnd(19)} | ${"phase".padEnd(6)} | ${"category".padEnd(11)} | ${"matched_rule_id".padEnd(20)} | outcome`;
2060
- const sep = `${"-".repeat(19)}-+-${"-".repeat(6)}-+-${"-".repeat(11)}-+-${"-".repeat(20)}-+--------`;
2060
+ const sep2 = `${"-".repeat(19)}-+-${"-".repeat(6)}-+-${"-".repeat(11)}-+-${"-".repeat(20)}-+--------`;
2061
2061
  console.log(header);
2062
- console.log(sep);
2062
+ console.log(sep2);
2063
2063
  for (const r of records) {
2064
2064
  const ts = r.ts.slice(0, 19);
2065
2065
  const phase = r.phase.padEnd(6);
@@ -2069,7 +2069,7 @@ function renderHumanTable(records) {
2069
2069
  }
2070
2070
  }
2071
2071
  function pipeToJq(filterExpr, lines) {
2072
- return new Promise((resolve15, reject) => {
2072
+ return new Promise((resolve14, reject) => {
2073
2073
  const child = spawn("jq", [filterExpr], {
2074
2074
  stdio: ["pipe", "inherit", "inherit"],
2075
2075
  windowsHide: true
@@ -2078,12 +2078,12 @@ function pipeToJq(filterExpr, lines) {
2078
2078
  const e = err2;
2079
2079
  if (e.code === "ENOENT") {
2080
2080
  console.error(t("audit_log.jq_missing"));
2081
- resolve15(1);
2081
+ resolve14(1);
2082
2082
  } else {
2083
2083
  reject(err2);
2084
2084
  }
2085
2085
  });
2086
- child.on("close", (code) => resolve15(code ?? 0));
2086
+ child.on("close", (code) => resolve14(code ?? 0));
2087
2087
  child.stdin.write(lines.join("\n"));
2088
2088
  child.stdin.end();
2089
2089
  });
@@ -2740,10 +2740,10 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
2740
2740
  child.stderr?.setEncoding("utf8").on("data", (chunk) => {
2741
2741
  stderr += chunk;
2742
2742
  });
2743
- return await new Promise((resolve15) => {
2743
+ return await new Promise((resolve14) => {
2744
2744
  const timer = setTimeout(() => {
2745
2745
  child.kill("SIGKILL");
2746
- resolve15({
2746
+ resolve14({
2747
2747
  ok: false,
2748
2748
  phase: "spawn",
2749
2749
  error: {
@@ -2758,7 +2758,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
2758
2758
  }, effectiveTimeoutMs);
2759
2759
  child.on("error", (err2) => {
2760
2760
  clearTimeout(timer);
2761
- resolve15({
2761
+ resolve14({
2762
2762
  ok: false,
2763
2763
  phase: "spawn",
2764
2764
  error: {
@@ -2773,7 +2773,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
2773
2773
  });
2774
2774
  child.on("close", (code) => {
2775
2775
  clearTimeout(timer);
2776
- resolve15({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
2776
+ resolve14({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
2777
2777
  });
2778
2778
  });
2779
2779
  }
@@ -2871,7 +2871,7 @@ async function isAlreadyInstalled(ctx, opts = {}) {
2871
2871
  // src/installers/ccPluginMarketplace.ts
2872
2872
  init_readClaudeConfig();
2873
2873
  function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
2874
- return new Promise((resolve15) => {
2874
+ return new Promise((resolve14) => {
2875
2875
  const isWin = process.platform === "win32";
2876
2876
  const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
2877
2877
  let stderr = "";
@@ -2880,15 +2880,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
2880
2880
  });
2881
2881
  const timer = setTimeout(() => {
2882
2882
  child.kill("SIGKILL");
2883
- resolve15({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
2883
+ resolve14({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
2884
2884
  }, timeoutMs);
2885
2885
  child.on("error", (e) => {
2886
2886
  clearTimeout(timer);
2887
- resolve15({ exitCode: -1, stderr: `${stderr}${e.message}` });
2887
+ resolve14({ exitCode: -1, stderr: `${stderr}${e.message}` });
2888
2888
  });
2889
2889
  child.on("close", (code) => {
2890
2890
  clearTimeout(timer);
2891
- resolve15({ exitCode: code ?? -1, stderr });
2891
+ resolve14({ exitCode: code ?? -1, stderr });
2892
2892
  });
2893
2893
  });
2894
2894
  }
@@ -3034,7 +3034,7 @@ ${newEntry}
3034
3034
  return { ok: true, backupId: bk.backupId, appliedFiles: [settingsFile] };
3035
3035
  };
3036
3036
  function gitRevParseHead(cwd, timeoutMs = 1e4) {
3037
- return new Promise((resolve15) => {
3037
+ return new Promise((resolve14) => {
3038
3038
  const isWin = process.platform === "win32";
3039
3039
  const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
3040
3040
  let stdout2 = "";
@@ -3043,15 +3043,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
3043
3043
  });
3044
3044
  const timer = setTimeout(() => {
3045
3045
  child.kill("SIGKILL");
3046
- resolve15({ sha: "", exit: -1 });
3046
+ resolve14({ sha: "", exit: -1 });
3047
3047
  }, timeoutMs);
3048
3048
  child.on("error", () => {
3049
3049
  clearTimeout(timer);
3050
- resolve15({ sha: "", exit: -1 });
3050
+ resolve14({ sha: "", exit: -1 });
3051
3051
  });
3052
3052
  child.on("close", (code) => {
3053
3053
  clearTimeout(timer);
3054
- resolve15({ sha: stdout2.trim(), exit: code ?? -1 });
3054
+ resolve14({ sha: stdout2.trim(), exit: code ?? -1 });
3055
3055
  });
3056
3056
  });
3057
3057
  }
@@ -3608,13 +3608,6 @@ var installNpmCli = async (ctx) => {
3608
3608
  await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, install.npm_version, "");
3609
3609
  return { ok: true, backupId: bk.backupId, appliedFiles: [] };
3610
3610
  };
3611
- function extractSkillName2(cmd, fallback) {
3612
- const m = cmd.match(/\bskills(?:@\S+)?\s+add\s+(\S+)/i);
3613
- if (!m || m[1] === void 0) return fallback;
3614
- const ref = m[1];
3615
- const seg = ref.split("/");
3616
- return seg[seg.length - 1] ?? fallback;
3617
- }
3618
3611
  var installNpxSkillInstaller = async (ctx) => {
3619
3612
  const install = ctx.manifest.spec.install;
3620
3613
  if (install.method !== "npx-skill-installer") {
@@ -3655,7 +3648,7 @@ var installNpxSkillInstaller = async (ctx) => {
3655
3648
  };
3656
3649
  }
3657
3650
  const name = ctx.manifest.metadata.name;
3658
- const skillSegment = extractSkillName2(install.cmd, name);
3651
+ const skillSegment = extractSkillName(install.cmd, name);
3659
3652
  const skillDir = join(homedir(), ".claude", "skills", skillSegment);
3660
3653
  const skillMdPath = join(skillDir, "SKILL.md");
3661
3654
  const plan = {
@@ -5455,8 +5448,10 @@ function registerRun(program2) {
5455
5448
  `\u2139 user-override detected: ${matchedTriggers.length} trigger(s) forced fires=true via keyword match (${matchedTriggers.join(", ")})`
5456
5449
  );
5457
5450
  }
5451
+ const stage = name.includes("-") ? name.split("-")[0] ?? "" : name;
5458
5452
  const gateContext = {
5459
5453
  task,
5454
+ phase: { stage, is_critical_module: true },
5460
5455
  ...raw.model ? { modelOverride: raw.model } : {},
5461
5456
  ...raw.maxIterations ? { maxIterations: raw.maxIterations } : {},
5462
5457
  ...raw.staged ? { staged: true } : {},
@@ -6036,11 +6031,7 @@ async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOver
6036
6031
  return { results, aggregatedWarnings: [...warningSet] };
6037
6032
  }
6038
6033
  init_checkAgentTeams();
6039
- var FLAT_LEGACY_KEEP = /* @__PURE__ */ new Set([
6040
- "research",
6041
- "retro",
6042
- "auto"
6043
- ]);
6034
+ var FLAT_LEGACY_KEEP = /* @__PURE__ */ new Set(["research", "retro", "auto"]);
6044
6035
  var FLAT_TOP_LEVEL_MASTERS = /* @__PURE__ */ new Set(["auto"]);
6045
6036
  var NON_WORKFLOW_DIRS = /* @__PURE__ */ new Set(["disciplines", "judgments"]);
6046
6037
  async function scanWorkflowsNested(workflowsDir, entries) {
@@ -6271,15 +6262,16 @@ function registerSetup(program2) {
6271
6262
  const writtenCount = cmdResult.results.filter((r) => r.written).length;
6272
6263
  const skippedCount = cmdResult.results.filter((r) => !r.written && r.warning).length;
6273
6264
  if (writtenCount > 0 || skippedCount > 0) {
6274
- console.log(
6275
- ` generated ${writtenCount} commands/<x>.md file(s) (${skippedCount} skipped)`
6276
- );
6265
+ console.log(` generated ${writtenCount} commands/<x>.md file(s) (${skippedCount} skipped)`);
6277
6266
  }
6278
6267
  await enableAgentTeamsInSettings();
6279
6268
  await enableUserLangInSettings(raw.userLang);
6280
6269
  const manifestPaths = await listBaseManifests2(pkgRoot);
6281
6270
  const forceFirstPass = raw.updateInstalled === true;
6282
- const b = await runStepBInstall(manifestPaths, { updateInstalled: forceFirstPass, quiet: true });
6271
+ const b = await runStepBInstall(manifestPaths, {
6272
+ updateInstalled: forceFirstPass,
6273
+ quiet: true
6274
+ });
6283
6275
  const stepBMs = (b.elapsedMs / 1e3).toFixed(1);
6284
6276
  console.log(
6285
6277
  t("setup.step_b_complete", {
@@ -6291,8 +6283,7 @@ function registerSetup(program2) {
6291
6283
  })
6292
6284
  );
6293
6285
  for (const n of b.installed) console.log(` [B] installed ${n}`);
6294
- for (const n of b.alreadyInstalled)
6295
- console.log(` [B] already-installed ${n}`);
6286
+ for (const n of b.alreadyInstalled) console.log(` [B] already-installed ${n}`);
6296
6287
  for (const s of b.skipped) console.log(` [B] skipped ${s.name} \u2014 ${s.reason}`);
6297
6288
  for (const n of b.failed) console.error(` [B] failed ${n}`);
6298
6289
  if (!forceFirstPass && !dryRun && raw.nonInteractive !== true && b.alreadyInstalled.length > 0) {
@@ -6382,6 +6373,7 @@ function registerStatus(program2) {
6382
6373
  }
6383
6374
  });
6384
6375
  }
6376
+ init_harnessedRoot();
6385
6377
  init_path_guard();
6386
6378
 
6387
6379
  // src/uninstallers/lib/runOrPreview.ts
@@ -6567,7 +6559,7 @@ var uninstallNpmCli = async (ctx) => {
6567
6559
  const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
6568
6560
  const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
6569
6561
  const isWin = process.platform === "win32";
6570
- const result = await new Promise((resolve15) => {
6562
+ const result = await new Promise((resolve14) => {
6571
6563
  const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
6572
6564
  let stderr = "";
6573
6565
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -6575,15 +6567,15 @@ var uninstallNpmCli = async (ctx) => {
6575
6567
  });
6576
6568
  const timer = setTimeout(() => {
6577
6569
  child.kill("SIGKILL");
6578
- resolve15({ exitCode: -1, stderr: `${stderr}[timeout]` });
6570
+ resolve14({ exitCode: -1, stderr: `${stderr}[timeout]` });
6579
6571
  }, 3e4);
6580
6572
  child.on("error", (e) => {
6581
6573
  clearTimeout(timer);
6582
- resolve15({ exitCode: -1, stderr: e.message });
6574
+ resolve14({ exitCode: -1, stderr: e.message });
6583
6575
  });
6584
6576
  child.on("close", (code) => {
6585
6577
  clearTimeout(timer);
6586
- resolve15({ exitCode: code ?? -1, stderr });
6578
+ resolve14({ exitCode: code ?? -1, stderr });
6587
6579
  });
6588
6580
  });
6589
6581
  if (result.exitCode !== 0) {
@@ -6595,13 +6587,6 @@ var uninstallNpmCli = async (ctx) => {
6595
6587
  }
6596
6588
  return { ok: true, removedPaths: [pkg] };
6597
6589
  };
6598
- function extractSkillName3(cmd, fallback) {
6599
- const m = cmd.match(/\bskills(?:@\S+)?\s+add\s+(\S+)/i);
6600
- if (!m || m[1] === void 0) return fallback;
6601
- const ref = m[1];
6602
- const seg = ref.split("/");
6603
- return seg[seg.length - 1] ?? fallback;
6604
- }
6605
6590
  var uninstallNpxSkillInstaller = async (ctx) => {
6606
6591
  const install = ctx.manifest.spec.install;
6607
6592
  if (install.method !== "npx-skill-installer") {
@@ -6610,7 +6595,7 @@ var uninstallNpxSkillInstaller = async (ctx) => {
6610
6595
  const abort = dryRunGate(ctx);
6611
6596
  if (abort) return abort;
6612
6597
  const name = ctx.manifest.metadata.name;
6613
- const skillName = extractSkillName3(install.cmd, name);
6598
+ const skillName = extractSkillName(install.cmd, name);
6614
6599
  const skillDir = join(homedir(), ".claude", "skills", skillName);
6615
6600
  await rm(skillDir, { recursive: true, force: true, maxRetries: 3 });
6616
6601
  return { ok: true, removedPaths: [skillDir] };
@@ -6632,21 +6617,177 @@ async function runUninstall(manifest, opts) {
6632
6617
  }
6633
6618
 
6634
6619
  // src/cli/uninstall.ts
6620
+ async function discoverCommandFiles(commandsDir) {
6621
+ const owned = [];
6622
+ let entries;
6623
+ try {
6624
+ entries = await readdir(commandsDir);
6625
+ } catch {
6626
+ return owned;
6627
+ }
6628
+ for (const entry of entries) {
6629
+ if (!entry.endsWith(".md")) continue;
6630
+ const p5 = join(commandsDir, entry);
6631
+ try {
6632
+ const content = await readFile(p5, "utf8");
6633
+ if (shouldOverwriteFile(content)) owned.push(p5);
6634
+ } catch {
6635
+ }
6636
+ }
6637
+ return owned;
6638
+ }
6639
+ async function checkSettingsEnv(settingsPath3) {
6640
+ try {
6641
+ const raw = await readFile(settingsPath3, "utf8");
6642
+ const data = JSON.parse(raw);
6643
+ const env = data.env ?? {};
6644
+ return {
6645
+ hasAgentTeams: "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS" in env,
6646
+ hasUserLang: "HARNESSED_USER_LANG" in env
6647
+ };
6648
+ } catch {
6649
+ return { hasAgentTeams: false, hasUserLang: false };
6650
+ }
6651
+ }
6652
+ async function removeSettingsEnv(settingsPath3) {
6653
+ const raw = await readFile(settingsPath3, "utf8");
6654
+ const data = JSON.parse(raw);
6655
+ const env = data.env ?? {};
6656
+ let changed = false;
6657
+ if ("CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS" in env) {
6658
+ delete env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
6659
+ changed = true;
6660
+ }
6661
+ if ("HARNESSED_USER_LANG" in env) {
6662
+ delete env.HARNESSED_USER_LANG;
6663
+ changed = true;
6664
+ }
6665
+ if (!changed) return false;
6666
+ if (Object.keys(env).length === 0) delete data.env;
6667
+ else data.env = env;
6668
+ await writeFile(settingsPath3, JSON.stringify(data, null, 2) + "\n", "utf8");
6669
+ return true;
6670
+ }
6671
+ async function runUnifiedUninstall(home, dryRun) {
6672
+ const commandsDir = join(home, ".claude", "commands");
6673
+ const skillsDir = join(home, ".claude", "skills");
6674
+ const settingsPath3 = join(home, ".claude", "settings.json");
6675
+ const harnessedRoot = getHarnessedRoot();
6676
+ const pkgRoot = getPackageRoot();
6677
+ let workflowNames = [];
6678
+ try {
6679
+ const wfEntries = await readdir(resolve(pkgRoot, "workflows"));
6680
+ const scanResult = await scanWorkflowsNested(resolve(pkgRoot, "workflows"), wfEntries);
6681
+ workflowNames = scanResult.workflows.map((w) => w.name);
6682
+ } catch {
6683
+ }
6684
+ const commandFiles = await discoverCommandFiles(commandsDir);
6685
+ const settingsEnv = await checkSettingsEnv(settingsPath3);
6686
+ const hasSettingsChanges = settingsEnv.hasAgentTeams || settingsEnv.hasUserLang;
6687
+ const skillDirs = [];
6688
+ for (const name of workflowNames) {
6689
+ const dir = join(skillsDir, name);
6690
+ try {
6691
+ await stat(dir);
6692
+ skillDirs.push(dir);
6693
+ } catch {
6694
+ }
6695
+ }
6696
+ const discoverable = commandFiles.length + skillDirs.length + (hasSettingsChanges ? 1 : 0);
6697
+ if (discoverable === 0) {
6698
+ console.log(t("uninstall.unified.nothing"));
6699
+ process.exit(0);
6700
+ }
6701
+ console.log(t("uninstall.unified.header"));
6702
+ if (commandFiles.length > 0)
6703
+ console.log(t("uninstall.unified.commands", { count: commandFiles.length }));
6704
+ if (skillDirs.length > 0) console.log(t("uninstall.unified.skills", { count: skillDirs.length }));
6705
+ if (hasSettingsChanges) console.log(t("uninstall.unified.settings"));
6706
+ console.log(t("uninstall.unified.state_dir"));
6707
+ console.log(t("uninstall.unified.upstream_note"));
6708
+ if (dryRun) {
6709
+ console.log(t("uninstall.unified.dry_run_hint"));
6710
+ process.exit(2);
6711
+ }
6712
+ const answer = await p.confirm({
6713
+ message: t("uninstall.unified.confirm"),
6714
+ initialValue: false
6715
+ });
6716
+ if (p.isCancel(answer) || answer === false) {
6717
+ console.error(t("uninstall.cancelled"));
6718
+ process.exit(2);
6719
+ }
6720
+ console.log(t("uninstall.unified.removing"));
6721
+ const failures = [];
6722
+ let removedCommands = 0;
6723
+ for (const path of commandFiles) {
6724
+ try {
6725
+ await rm(path, { force: true });
6726
+ removedCommands++;
6727
+ } catch (e) {
6728
+ failures.push(`${path}: ${e.message}`);
6729
+ }
6730
+ }
6731
+ let removedSkills = 0;
6732
+ for (const dir of skillDirs) {
6733
+ try {
6734
+ await rm(dir, { recursive: true, force: true });
6735
+ removedSkills++;
6736
+ } catch (e) {
6737
+ failures.push(`${dir}: ${e.message}`);
6738
+ }
6739
+ }
6740
+ let removedSettings = false;
6741
+ if (hasSettingsChanges) {
6742
+ try {
6743
+ removedSettings = await removeSettingsEnv(settingsPath3);
6744
+ } catch (e) {
6745
+ failures.push(`${settingsPath3}: ${e.message}`);
6746
+ }
6747
+ }
6748
+ const normalizedRoot = resolve(harnessedRoot);
6749
+ const claudeDir = join(home, ".claude");
6750
+ if (!normalizedRoot.startsWith(resolve(claudeDir) + sep)) {
6751
+ console.error(`error: state dir ${harnessedRoot} is not under ${claudeDir} \u2014 refusing`);
6752
+ process.exit(1);
6753
+ }
6754
+ let removedStateDir = false;
6755
+ try {
6756
+ await rm(harnessedRoot, { recursive: true, force: true });
6757
+ removedStateDir = true;
6758
+ } catch (e) {
6759
+ failures.push(`${harnessedRoot}: ${e.message}`);
6760
+ }
6761
+ if (removedCommands > 0)
6762
+ console.log(t("uninstall.unified.removed_commands", { count: removedCommands }));
6763
+ if (removedSkills > 0)
6764
+ console.log(t("uninstall.unified.removed_skills", { count: removedSkills }));
6765
+ if (removedSettings) console.log(t("uninstall.unified.removed_settings"));
6766
+ if (removedStateDir) console.log(t("uninstall.unified.removed_state_dir"));
6767
+ if (failures.length > 0) {
6768
+ console.error(t("uninstall.unified.partial_failure", { count: failures.length }));
6769
+ for (const f of failures) console.error(` ${f}`);
6770
+ }
6771
+ console.log(t("uninstall.unified.complete"));
6772
+ process.exit(0);
6773
+ }
6635
6774
  function registerUninstall(program2) {
6636
- program2.command("uninstall <name>").description("Uninstall an upstream (immediate by default \u2014 use --dry-run for preview)").option("--dry-run", "preview only \u2014 do not delete files (opt-in for advanced users)").option("--yes", "skip interactive confirm (CI / scripts) \u2014 fatal with --dry-run").option("--non-interactive", "alias for --yes (CI compat)").action(async (name, raw) => {
6637
- const yes = raw.yes === true || raw.nonInteractive === true;
6638
- if (yes && raw.dryRun) {
6639
- console.error(
6640
- `${t("uninstall.yes_dryrun_conflict")}
6641
- ${t("uninstall.yes_dryrun_conflict.fix", { name })}`
6642
- );
6643
- process.exit(2);
6775
+ program2.command("uninstall [name]").description(
6776
+ "Uninstall an upstream by name, or remove all harnessed own files when no name given"
6777
+ ).option("--dry-run", "preview only \u2014 do not delete files").action(async (name, raw) => {
6778
+ const dryRun = raw.dryRun === true;
6779
+ if (!name) {
6780
+ await runUnifiedUninstall(homedir(), dryRun);
6781
+ return;
6644
6782
  }
6645
6783
  const { resolveAlias: resolveAlias2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
6646
6784
  const resolvedName = resolveAlias2(name) ?? name;
6647
6785
  checkPathSafe(resolvedName);
6648
6786
  const manifestPath = resolve(getPackageRoot(), `manifests/tools/${resolvedName}.yaml`);
6649
- const skillPackPath = resolve(getPackageRoot(), `manifests/skill-packs/${resolvedName}.yaml`);
6787
+ const skillPackPath = resolve(
6788
+ getPackageRoot(),
6789
+ `manifests/skill-packs/${resolvedName}.yaml`
6790
+ );
6650
6791
  let yamlSrc;
6651
6792
  let chosenPath = manifestPath;
6652
6793
  try {
@@ -6668,25 +6809,25 @@ ${t("install.manifest_not_found.fix", { name: resolvedName })}`
6668
6809
  for (const e of v.errors) console.error(`error: ${e.message} at ${e.path}`);
6669
6810
  process.exit(1);
6670
6811
  }
6671
- const method = v.manifest.spec.install.method;
6672
- const dryRun = raw.dryRun === true;
6673
6812
  if (dryRun) {
6674
- console.log(t("uninstall.dry_run.preview", { name: resolvedName, method }));
6813
+ console.log(
6814
+ t("uninstall.dry_run.preview", {
6815
+ name: resolvedName,
6816
+ method: v.manifest.spec.install.method
6817
+ })
6818
+ );
6675
6819
  console.log(t("uninstall.dry_run.run_hint"));
6676
6820
  process.exit(2);
6677
6821
  }
6678
- if (!yes) {
6679
- const answer = await p.confirm({
6680
- message: t("uninstall.confirm.prompt", { name: resolvedName }),
6681
- initialValue: false
6682
- });
6683
- if (p.isCancel(answer) || answer === false) {
6684
- console.error(t("uninstall.cancelled"));
6685
- process.exit(2);
6686
- }
6822
+ const answer = await p.confirm({
6823
+ message: t("uninstall.confirm.prompt", { name: resolvedName }),
6824
+ initialValue: false
6825
+ });
6826
+ if (p.isCancel(answer) || answer === false) {
6827
+ console.error(t("uninstall.cancelled"));
6828
+ process.exit(2);
6687
6829
  }
6688
- const opts = { apply: true, dryRun: false, yes };
6689
- const result = await runUninstall(v.manifest, opts);
6830
+ const result = await runUninstall(v.manifest, { apply: true, dryRun: false, yes: true });
6690
6831
  if ("aborted" in result) {
6691
6832
  console.error(t("install.aborted", { reason: result.reason }));
6692
6833
  process.exit(2);