harnessed 3.9.17 → 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';
@@ -1218,7 +1218,7 @@ var init_auto_install = __esm({
1218
1218
 
1219
1219
  // package.json
1220
1220
  var package_default = {
1221
- version: "3.9.17"};
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
  }
@@ -6373,6 +6373,7 @@ function registerStatus(program2) {
6373
6373
  }
6374
6374
  });
6375
6375
  }
6376
+ init_harnessedRoot();
6376
6377
  init_path_guard();
6377
6378
 
6378
6379
  // src/uninstallers/lib/runOrPreview.ts
@@ -6558,7 +6559,7 @@ var uninstallNpmCli = async (ctx) => {
6558
6559
  const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
6559
6560
  const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
6560
6561
  const isWin = process.platform === "win32";
6561
- const result = await new Promise((resolve15) => {
6562
+ const result = await new Promise((resolve14) => {
6562
6563
  const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
6563
6564
  let stderr = "";
6564
6565
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -6566,15 +6567,15 @@ var uninstallNpmCli = async (ctx) => {
6566
6567
  });
6567
6568
  const timer = setTimeout(() => {
6568
6569
  child.kill("SIGKILL");
6569
- resolve15({ exitCode: -1, stderr: `${stderr}[timeout]` });
6570
+ resolve14({ exitCode: -1, stderr: `${stderr}[timeout]` });
6570
6571
  }, 3e4);
6571
6572
  child.on("error", (e) => {
6572
6573
  clearTimeout(timer);
6573
- resolve15({ exitCode: -1, stderr: e.message });
6574
+ resolve14({ exitCode: -1, stderr: e.message });
6574
6575
  });
6575
6576
  child.on("close", (code) => {
6576
6577
  clearTimeout(timer);
6577
- resolve15({ exitCode: code ?? -1, stderr });
6578
+ resolve14({ exitCode: code ?? -1, stderr });
6578
6579
  });
6579
6580
  });
6580
6581
  if (result.exitCode !== 0) {
@@ -6616,21 +6617,177 @@ async function runUninstall(manifest, opts) {
6616
6617
  }
6617
6618
 
6618
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
+ }
6619
6774
  function registerUninstall(program2) {
6620
- 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) => {
6621
- const yes = raw.yes === true || raw.nonInteractive === true;
6622
- if (yes && raw.dryRun) {
6623
- console.error(
6624
- `${t("uninstall.yes_dryrun_conflict")}
6625
- ${t("uninstall.yes_dryrun_conflict.fix", { name })}`
6626
- );
6627
- 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;
6628
6782
  }
6629
6783
  const { resolveAlias: resolveAlias2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
6630
6784
  const resolvedName = resolveAlias2(name) ?? name;
6631
6785
  checkPathSafe(resolvedName);
6632
6786
  const manifestPath = resolve(getPackageRoot(), `manifests/tools/${resolvedName}.yaml`);
6633
- const skillPackPath = resolve(getPackageRoot(), `manifests/skill-packs/${resolvedName}.yaml`);
6787
+ const skillPackPath = resolve(
6788
+ getPackageRoot(),
6789
+ `manifests/skill-packs/${resolvedName}.yaml`
6790
+ );
6634
6791
  let yamlSrc;
6635
6792
  let chosenPath = manifestPath;
6636
6793
  try {
@@ -6652,25 +6809,25 @@ ${t("install.manifest_not_found.fix", { name: resolvedName })}`
6652
6809
  for (const e of v.errors) console.error(`error: ${e.message} at ${e.path}`);
6653
6810
  process.exit(1);
6654
6811
  }
6655
- const method = v.manifest.spec.install.method;
6656
- const dryRun = raw.dryRun === true;
6657
6812
  if (dryRun) {
6658
- 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
+ );
6659
6819
  console.log(t("uninstall.dry_run.run_hint"));
6660
6820
  process.exit(2);
6661
6821
  }
6662
- if (!yes) {
6663
- const answer = await p.confirm({
6664
- message: t("uninstall.confirm.prompt", { name: resolvedName }),
6665
- initialValue: false
6666
- });
6667
- if (p.isCancel(answer) || answer === false) {
6668
- console.error(t("uninstall.cancelled"));
6669
- process.exit(2);
6670
- }
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);
6671
6829
  }
6672
- const opts = { apply: true, dryRun: false, yes };
6673
- const result = await runUninstall(v.manifest, opts);
6830
+ const result = await runUninstall(v.manifest, { apply: true, dryRun: false, yes: true });
6674
6831
  if ("aborted" in result) {
6675
6832
  console.error(t("install.aborted", { reason: result.reason }));
6676
6833
  process.exit(2);