harnessed 4.6.0 → 4.7.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/cli.mjs CHANGED
@@ -38,7 +38,7 @@ var init_package = __esm({
38
38
  "package.json"() {
39
39
  package_default = {
40
40
  name: "harnessed",
41
- version: "4.6.0",
41
+ version: "4.7.0",
42
42
  description: "AI coding harness package manager + composition orchestrator",
43
43
  type: "module",
44
44
  license: "Apache-2.0",
@@ -852,10 +852,92 @@ var init_origin_check = __esm({
852
852
  "src/cli/lib/origin-check.ts"() {
853
853
  }
854
854
  });
855
- function getHarnessedRoot() {
855
+ function claudeDescriptor(home = homedir()) {
856
+ const claudeHome = join(home, ".claude");
857
+ return {
858
+ id: "claude",
859
+ homeDir: claudeHome,
860
+ stateRoot: join(claudeHome, "harnessed"),
861
+ settingsPath: join(claudeHome, "settings.json"),
862
+ skillsDir: join(claudeHome, "skills"),
863
+ commandsDir: join(claudeHome, "commands"),
864
+ pluginsRegistry: join(claudeHome, "plugins", "installed_plugins.json"),
865
+ // mcpConfigPath is a SIBLING of `.claude` (`<home>/.claude.json`), based on
866
+ // the same home base — not a child of homeDir.
867
+ mcpConfigPath: join(home, ".claude.json"),
868
+ // claude writes its env keys into JSON settings.json (capability present).
869
+ supportsEnvKeyWrite: true
870
+ };
871
+ }
872
+ function codexDescriptor(home = homedir()) {
873
+ const codexHome = join(home, ".codex");
874
+ const configToml = join(codexHome, "config.toml");
875
+ return {
876
+ id: "codex",
877
+ homeDir: codexHome,
878
+ stateRoot: join(codexHome, "harnessed"),
879
+ settingsPath: configToml,
880
+ // SHARED convention dir, NOT <homeDir>/skills.
881
+ skillsDir: join(home, ".agents", "skills"),
882
+ commandsDir: join(codexHome, "prompts"),
883
+ pluginsRegistry: null,
884
+ mcpConfigPath: configToml,
885
+ supportsEnvKeyWrite: false
886
+ };
887
+ }
888
+ function descriptorById(id, home) {
889
+ if (id === "claude") return claudeDescriptor(home);
890
+ if (id === "codex") return codexDescriptor(home);
891
+ return void 0;
892
+ }
893
+ function detectPlatform(home = homedir()) {
894
+ const base = claudeDescriptor(home);
856
895
  const override = process.env.HARNESSED_ROOT_OVERRIDE;
857
- if (override !== void 0 && override !== "") return override;
858
- return join(homedir(), ".claude", "harnessed");
896
+ if (override !== void 0 && override !== "") return { ...base, stateRoot: override };
897
+ const envPlatform = process.env.HARNESSED_PLATFORM;
898
+ if (envPlatform !== void 0 && envPlatform !== "") {
899
+ const d = descriptorById(envPlatform, home);
900
+ if (d) return d;
901
+ }
902
+ try {
903
+ const pin = readFileSync(join(base.stateRoot, ".platform"), "utf8").trim();
904
+ const d = descriptorById(pin, home);
905
+ if (d) return d;
906
+ } catch {
907
+ }
908
+ try {
909
+ if (existsSync(base.homeDir)) return base;
910
+ const codex = codexDescriptor(home);
911
+ if (existsSync(codex.homeDir)) return codex;
912
+ } catch {
913
+ }
914
+ return base;
915
+ }
916
+ function getSettingsPath(home) {
917
+ return detectPlatform(home).settingsPath;
918
+ }
919
+ function getSkillsDir(home) {
920
+ return detectPlatform(home).skillsDir;
921
+ }
922
+ function getCommandsDir(home) {
923
+ return detectPlatform(home).commandsDir;
924
+ }
925
+ function getPluginsRegistry(home) {
926
+ return detectPlatform(home).pluginsRegistry;
927
+ }
928
+ function getMcpConfigPath(home) {
929
+ return detectPlatform(home).mcpConfigPath;
930
+ }
931
+ function harnessSkillsDirs(home) {
932
+ const dirs = [claudeDescriptor(home).skillsDir, codexDescriptor(home).skillsDir];
933
+ return [...new Set(dirs)];
934
+ }
935
+ var init_platform = __esm({
936
+ "src/installers/lib/platform.ts"() {
937
+ }
938
+ });
939
+ function getHarnessedRoot() {
940
+ return detectPlatform().stateRoot;
859
941
  }
860
942
  function harnessedSubdir(name) {
861
943
  return join(getHarnessedRoot(), name);
@@ -890,6 +972,7 @@ function migrateLegacyHarnessedRoot() {
890
972
  }
891
973
  var init_harnessedRoot = __esm({
892
974
  "src/installers/lib/harnessedRoot.ts"() {
975
+ init_platform();
893
976
  }
894
977
  });
895
978
  function getBackupRoot() {
@@ -2056,7 +2139,7 @@ function nameToYamlHintPath(name) {
2056
2139
  function shouldOverwriteFile(content) {
2057
2140
  return HARNESSED_MARKER_RX.test(content) || V3_4_3_SIGNATURE_SUB_RX.test(content) || V3_4_3_SIGNATURE_MASTER_RX.test(content);
2058
2141
  }
2059
- async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabilities, installedPlugins, installedUserSkills, writer, fileExists2 = existsSync, readFileSync12 = (p5) => readFileSync(p5, "utf8")) {
2142
+ async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabilities, installedPlugins, installedUserSkills, writer, fileExists2 = existsSync, readFileSync13 = (p5) => readFileSync(p5, "utf8")) {
2060
2143
  const results = [];
2061
2144
  const aggregatedWarnings = /* @__PURE__ */ new Set();
2062
2145
  for (const name of slashNames) {
@@ -2075,7 +2158,7 @@ async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabiliti
2075
2158
  if (fileExists2(path)) {
2076
2159
  let existing = "";
2077
2160
  try {
2078
- existing = readFileSync12(path);
2161
+ existing = readFileSync13(path);
2079
2162
  } catch {
2080
2163
  existing = "";
2081
2164
  }
@@ -4373,7 +4456,7 @@ var init_check_mattpocock_skills = __esm({
4373
4456
  }
4374
4457
  });
4375
4458
  function getUserClaudeJsonPath() {
4376
- return join(homedir(), ".claude.json");
4459
+ return getMcpConfigPath();
4377
4460
  }
4378
4461
  async function readUserClaudeJson() {
4379
4462
  const path = getUserClaudeJsonPath();
@@ -4399,21 +4482,20 @@ async function isMcpServerRegistered(name) {
4399
4482
  return Object.hasOwn(servers, name);
4400
4483
  }
4401
4484
  async function isPluginRegistered(pluginName) {
4402
- try {
4403
- const path = join(homedir(), ".claude", "plugins", "installed_plugins.json");
4404
- const raw = await readFile(path, "utf8");
4405
- const parsed = JSON.parse(raw);
4406
- const plugins = parsed.plugins;
4407
- if (plugins && typeof plugins === "object") {
4408
- if (Object.hasOwn(plugins, pluginName)) return true;
4409
- if (Object.keys(plugins).some((k) => k.split("@")[0] === pluginName)) return true;
4485
+ const registryPath = getPluginsRegistry();
4486
+ if (registryPath !== null) {
4487
+ try {
4488
+ const raw = await readFile(registryPath, "utf8");
4489
+ const parsed = JSON.parse(raw);
4490
+ const plugins = parsed.plugins;
4491
+ if (plugins && typeof plugins === "object") {
4492
+ if (Object.hasOwn(plugins, pluginName)) return true;
4493
+ if (Object.keys(plugins).some((k) => k.split("@")[0] === pluginName)) return true;
4494
+ }
4495
+ } catch {
4410
4496
  }
4411
- } catch {
4412
4497
  }
4413
- for (const path of [
4414
- join(homedir(), ".claude", "settings.json"),
4415
- join(homedir(), ".claude.json")
4416
- ]) {
4498
+ for (const path of [getSettingsPath(), getMcpConfigPath()]) {
4417
4499
  try {
4418
4500
  const raw = await readFile(path, "utf8");
4419
4501
  const parsed = JSON.parse(raw);
@@ -4429,6 +4511,7 @@ async function isPluginRegistered(pluginName) {
4429
4511
  }
4430
4512
  var init_readClaudeConfig = __esm({
4431
4513
  "src/installers/lib/readClaudeConfig.ts"() {
4514
+ init_platform();
4432
4515
  }
4433
4516
  });
4434
4517
 
@@ -4827,6 +4910,7 @@ var init_ccHookAdd2 = __esm({
4827
4910
  init_confirm();
4828
4911
  init_diff();
4829
4912
  init_err();
4913
+ init_platform();
4830
4914
  init_preflight();
4831
4915
  init_state2();
4832
4916
  installCcHookAdd = async (ctx) => {
@@ -4850,10 +4934,10 @@ var init_ccHookAdd2 = __esm({
4850
4934
  const e = pre.errors[0] ?? err(ctx, "/", "preflight failed", "preflight");
4851
4935
  return { ok: false, phase: "preflight", error: e };
4852
4936
  }
4853
- const settingsPath3 = join(homedir(), ".claude", "settings.json");
4937
+ const settingsPath = getSettingsPath();
4854
4938
  let existing;
4855
4939
  try {
4856
- existing = await readFile(settingsPath3, "utf8");
4940
+ existing = await readFile(settingsPath, "utf8");
4857
4941
  } catch {
4858
4942
  existing = null;
4859
4943
  }
@@ -4884,7 +4968,7 @@ var init_ccHookAdd2 = __esm({
4884
4968
  const newText = `${JSON.stringify(settings, null, 2)}
4885
4969
  `;
4886
4970
  const plan = {
4887
- files: [{ target: settingsPath3, scope: "HOME", oldText: existing ?? "", newText }]
4971
+ files: [{ target: settingsPath, scope: "HOME", oldText: existing ?? "", newText }]
4888
4972
  };
4889
4973
  if (!ctx.opts.quiet) process.stdout.write(renderDiff(plan, ctx));
4890
4974
  const conf = await confirmAt("L3", { ...ctx});
@@ -4895,10 +4979,10 @@ var init_ccHookAdd2 = __esm({
4895
4979
  if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
4896
4980
  const bk = await backup(plan, ctx);
4897
4981
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
4898
- await writeFile(settingsPath3, newText);
4982
+ await writeFile(settingsPath, newText);
4899
4983
  let verify;
4900
4984
  try {
4901
- verify = JSON.parse(await readFile(settingsPath3, "utf8"));
4985
+ verify = JSON.parse(await readFile(settingsPath, "utf8"));
4902
4986
  } catch (e) {
4903
4987
  return {
4904
4988
  ok: false,
@@ -4926,7 +5010,7 @@ var init_ccHookAdd2 = __esm({
4926
5010
  };
4927
5011
  }
4928
5012
  await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, "", "");
4929
- return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath3] };
5013
+ return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath] };
4930
5014
  };
4931
5015
  }
4932
5016
  });
@@ -4982,10 +5066,10 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
4982
5066
  child.stderr?.setEncoding("utf8").on("data", (chunk) => {
4983
5067
  stderr += chunk;
4984
5068
  });
4985
- return await new Promise((resolve20) => {
5069
+ return await new Promise((resolve18) => {
4986
5070
  const timer = setTimeout(() => {
4987
5071
  child.kill("SIGKILL");
4988
- resolve20({
5072
+ resolve18({
4989
5073
  ok: false,
4990
5074
  phase: "spawn",
4991
5075
  error: {
@@ -5001,7 +5085,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
5001
5085
  child.on("error", (err2) => {
5002
5086
  clearTimeout(timer);
5003
5087
  const bashMissing = triedBash && err2.code === "ENOENT";
5004
- resolve20({
5088
+ resolve18({
5005
5089
  ok: false,
5006
5090
  phase: "spawn",
5007
5091
  error: {
@@ -5016,7 +5100,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
5016
5100
  });
5017
5101
  child.on("close", (code) => {
5018
5102
  clearTimeout(timer);
5019
- resolve20({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
5103
+ resolve18({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
5020
5104
  });
5021
5105
  });
5022
5106
  }
@@ -5056,7 +5140,7 @@ async function detectNative(ctx) {
5056
5140
  const indicators = INSTALLED_INDICATORS[name];
5057
5141
  if (indicators) {
5058
5142
  for (const ind of indicators) {
5059
- const dir = join(homedir(), ".claude", "skills", ind);
5143
+ const dir = join(getSkillsDir(), ind);
5060
5144
  try {
5061
5145
  await access(dir);
5062
5146
  return true;
@@ -5075,8 +5159,8 @@ async function detectNative(ctx) {
5075
5159
  }
5076
5160
  if (method === "npx-skill-installer") {
5077
5161
  const skillName = extractSkillName(cmd, name);
5078
- for (const base of [".claude", ".agents"]) {
5079
- const skillMd = join(homedir(), base, "skills", skillName, "SKILL.md");
5162
+ for (const skillsDir of harnessSkillsDirs()) {
5163
+ const skillMd = join(skillsDir, skillName, "SKILL.md");
5080
5164
  try {
5081
5165
  await access(skillMd);
5082
5166
  return true;
@@ -5095,7 +5179,7 @@ async function detectNative(ctx) {
5095
5179
  }
5096
5180
  }
5097
5181
  if (method === "npm-cli") {
5098
- const skillDir = join(homedir(), ".claude", "skills", name);
5182
+ const skillDir = join(getSkillsDir(), name);
5099
5183
  try {
5100
5184
  await access(skillDir);
5101
5185
  return true;
@@ -5129,6 +5213,7 @@ async function isAlreadyInstalled(ctx, opts = {}) {
5129
5213
  var IDEMPOTENT_CHECK_TIMEOUT_MS, INSTALLED_INDICATORS;
5130
5214
  var init_idempotent = __esm({
5131
5215
  "src/installers/lib/idempotent.ts"() {
5216
+ init_platform();
5132
5217
  init_readClaudeConfig();
5133
5218
  init_spawn();
5134
5219
  IDEMPOTENT_CHECK_TIMEOUT_MS = 1e4;
@@ -5139,7 +5224,7 @@ var init_idempotent = __esm({
5139
5224
  }
5140
5225
  });
5141
5226
  function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
5142
- return new Promise((resolve20) => {
5227
+ return new Promise((resolve18) => {
5143
5228
  const isWin = process.platform === "win32";
5144
5229
  const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
5145
5230
  let stderr = "";
@@ -5148,15 +5233,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
5148
5233
  });
5149
5234
  const timer = setTimeout(() => {
5150
5235
  child.kill("SIGKILL");
5151
- resolve20({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
5236
+ resolve18({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
5152
5237
  }, timeoutMs);
5153
5238
  child.on("error", (e) => {
5154
5239
  clearTimeout(timer);
5155
- resolve20({ exitCode: -1, stderr: `${stderr}${e.message}` });
5240
+ resolve18({ exitCode: -1, stderr: `${stderr}${e.message}` });
5156
5241
  });
5157
5242
  child.on("close", (code) => {
5158
5243
  clearTimeout(timer);
5159
- resolve20({ exitCode: code ?? -1, stderr });
5244
+ resolve18({ exitCode: code ?? -1, stderr });
5160
5245
  });
5161
5246
  });
5162
5247
  }
@@ -5326,7 +5411,7 @@ ${newEntry}
5326
5411
  }
5327
5412
  });
5328
5413
  function gitRevParseHead(cwd, timeoutMs = 1e4) {
5329
- return new Promise((resolve20) => {
5414
+ return new Promise((resolve18) => {
5330
5415
  const isWin = process.platform === "win32";
5331
5416
  const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
5332
5417
  let stdout2 = "";
@@ -5335,15 +5420,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
5335
5420
  });
5336
5421
  const timer = setTimeout(() => {
5337
5422
  child.kill("SIGKILL");
5338
- resolve20({ sha: "", exit: -1 });
5423
+ resolve18({ sha: "", exit: -1 });
5339
5424
  }, timeoutMs);
5340
5425
  child.on("error", () => {
5341
5426
  clearTimeout(timer);
5342
- resolve20({ sha: "", exit: -1 });
5427
+ resolve18({ sha: "", exit: -1 });
5343
5428
  });
5344
5429
  child.on("close", (code) => {
5345
5430
  clearTimeout(timer);
5346
- resolve20({ sha: stdout2.trim(), exit: code ?? -1 });
5431
+ resolve18({ sha: stdout2.trim(), exit: code ?? -1 });
5347
5432
  });
5348
5433
  });
5349
5434
  }
@@ -6284,8 +6369,14 @@ var init_install_base = __esm({
6284
6369
  // src/checkpoint/nextStep.ts
6285
6370
  var nextStep_exports = {};
6286
6371
  __export(nextStep_exports, {
6372
+ resolveAutoTransition: () => resolveAutoTransition,
6287
6373
  resolveNext: () => resolveNext
6288
6374
  });
6375
+ function resolveAutoTransition(envelopeValue, env = process.env.HARNESSED_AUTO_TRANSITION) {
6376
+ if (env === "true") return true;
6377
+ if (env === "false") return false;
6378
+ return envelopeValue ?? true;
6379
+ }
6289
6380
  function resolveNext(ledger, autoTransition) {
6290
6381
  const sub = nextPending(ledger);
6291
6382
  if (sub === null) return { next: "done" };
@@ -6882,7 +6973,7 @@ function renderHumanTable(records) {
6882
6973
  }
6883
6974
  }
6884
6975
  function pipeToJq(filterExpr, lines) {
6885
- return new Promise((resolve20, reject) => {
6976
+ return new Promise((resolve18, reject) => {
6886
6977
  const child = spawn("jq", [filterExpr], {
6887
6978
  stdio: ["pipe", "inherit", "inherit"],
6888
6979
  windowsHide: true
@@ -6891,12 +6982,12 @@ function pipeToJq(filterExpr, lines) {
6891
6982
  const e = err2;
6892
6983
  if (e.code === "ENOENT") {
6893
6984
  console.error(t("audit_log.jq_missing"));
6894
- resolve20(1);
6985
+ resolve18(1);
6895
6986
  } else {
6896
6987
  reject(err2);
6897
6988
  }
6898
6989
  });
6899
- child.on("close", (code) => resolve20(code ?? 0));
6990
+ child.on("close", (code) => resolve18(code ?? 0));
6900
6991
  child.stdin.write(lines.join("\n"));
6901
6992
  child.stdin.end();
6902
6993
  });
@@ -7458,24 +7549,26 @@ function registerInstall(program2) {
7458
7549
  const { resolveAlias: resolveAlias2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
7459
7550
  const resolvedName = resolveAlias2(name) ?? name;
7460
7551
  checkPathSafe(resolvedName);
7461
- const manifestPath = resolve(getPackageRoot(), `manifests/tools/${resolvedName}.yaml`);
7462
- const skillPackPath = resolve(getPackageRoot(), `manifests/skill-packs/${resolvedName}.yaml`);
7552
+ const candidatePaths = ["tools", "skill-packs", "optional"].map(
7553
+ (dir) => resolve(getPackageRoot(), `manifests/${dir}/${resolvedName}.yaml`)
7554
+ );
7463
7555
  let yamlSrc;
7464
- let chosenPath = manifestPath;
7465
- try {
7466
- yamlSrc = await readFile(manifestPath, "utf8");
7467
- } catch {
7556
+ let chosenPath;
7557
+ for (const p5 of candidatePaths) {
7468
7558
  try {
7469
- yamlSrc = await readFile(skillPackPath, "utf8");
7470
- chosenPath = skillPackPath;
7559
+ yamlSrc = await readFile(p5, "utf8");
7560
+ chosenPath = p5;
7561
+ break;
7471
7562
  } catch {
7472
- console.error(
7473
- `${t("install.manifest_not_found", { name: resolvedName })}
7474
- ${t("install.manifest_not_found.fix", { name: resolvedName })}`
7475
- );
7476
- process.exit(1);
7477
7563
  }
7478
7564
  }
7565
+ if (yamlSrc === void 0 || chosenPath === void 0) {
7566
+ console.error(
7567
+ `${t("install.manifest_not_found", { name: resolvedName })}
7568
+ ${t("install.manifest_not_found.fix", { name: resolvedName })}`
7569
+ );
7570
+ process.exit(1);
7571
+ }
7479
7572
  const v = validateManifestFile(yamlSrc, chosenPath);
7480
7573
  if (!v.ok) {
7481
7574
  for (const e of v.errors) console.error(`error: ${e.message} at ${e.path}`);
@@ -7535,6 +7628,181 @@ function registerLearn(program2) {
7535
7628
  process.exit(0);
7536
7629
  });
7537
7630
  }
7631
+
7632
+ // src/cli/lib/here.ts
7633
+ init_nextStep();
7634
+ init_harnessedRoot();
7635
+
7636
+ // src/cli/status.ts
7637
+ init_evidence();
7638
+ init_ledger();
7639
+ init_state();
7640
+ init_harnessedRoot();
7641
+ init_state2();
7642
+ function statusMarker(status) {
7643
+ switch (status) {
7644
+ case "done":
7645
+ return "\u2705 done";
7646
+ case "pending":
7647
+ return "\u23F3 pending";
7648
+ case "failed":
7649
+ return "\u2717 failed";
7650
+ case "skipped":
7651
+ return "\u2B1C skipped";
7652
+ case "rejected":
7653
+ return "\u{1F6AB} rejected";
7654
+ }
7655
+ }
7656
+ function evidenceLabel(entry) {
7657
+ switch (entry.evidence_status) {
7658
+ case "verified":
7659
+ return "evidence: verified";
7660
+ case "overridden":
7661
+ return "evidence: overridden (--force)";
7662
+ default:
7663
+ return "evidence: none_declared";
7664
+ }
7665
+ }
7666
+ async function buildRecoverLines(workflow, ledger) {
7667
+ const lines = [];
7668
+ if (!workflow || ledger.length === 0) {
7669
+ lines.push("no ledger \u2014 run gates + start");
7670
+ return lines;
7671
+ }
7672
+ lines.push(`workflow: ${workflow.phase} (${workflow.status}) started ${workflow.started_at}`);
7673
+ const next = nextPending(ledger);
7674
+ for (const e of ledger) {
7675
+ const marker = statusMarker(e.status);
7676
+ let suffix = "";
7677
+ if (e.status === "skipped") {
7678
+ suffix = e.reason ? ` (skipped: ${e.reason})` : "";
7679
+ } else if (e.status === "done") {
7680
+ suffix = ` ${evidenceLabel(e)}`;
7681
+ }
7682
+ const arrow = e.sub === next ? " \u2190 next" : "";
7683
+ lines.push(` ${e.sub} ${marker}${suffix}${arrow}`);
7684
+ }
7685
+ if (next) {
7686
+ lines.push(`\u2192 next: harnessed prompt ${next}`);
7687
+ } else {
7688
+ lines.push("\u2192 all subs resolved (no pending work)");
7689
+ }
7690
+ for (const e of ledger) {
7691
+ if (!e.evidence || e.evidence.length === 0) continue;
7692
+ const drift = await detectDrift(e.evidence);
7693
+ for (const d of drift) {
7694
+ const now = d.now === "" ? "missing" : `${d.now.slice(0, 7)}\u2026`;
7695
+ lines.push(`\u26A0 drift: ${d.path} sha256 changed (was ${d.was.slice(0, 7)}\u2026, now ${now})`);
7696
+ }
7697
+ }
7698
+ return lines;
7699
+ }
7700
+ function registerStatus(program2) {
7701
+ program2.command("status").description("Show installed upstreams (from <harnessed-root>/state.json)").option("--recover", "structured recovery view of the active workflow ledger (v5.0 Spec 1 B)").action(async (opts) => {
7702
+ if (opts.recover) {
7703
+ const wf = await readCurrentWorkflow();
7704
+ const ledger = wf?.sub_progress ?? [];
7705
+ const lines = await buildRecoverLines(wf, ledger);
7706
+ for (const l of lines) console.log(l);
7707
+ return;
7708
+ }
7709
+ const state = await readState(process.cwd());
7710
+ const names = Object.keys(state.installed).sort();
7711
+ if (names.length === 0) {
7712
+ console.log(t("status.no_installs", { path: harnessedFile("state.json") }));
7713
+ } else {
7714
+ for (const n of names) {
7715
+ const e = state.installed[n];
7716
+ if (!e) continue;
7717
+ console.log(`${n} @ ${e.version} (installed ${e.installedAt})`);
7718
+ }
7719
+ console.log(t("status.summary_installs", { count: names.length }));
7720
+ }
7721
+ const lockPath = harnessedFile(".lock");
7722
+ try {
7723
+ const isLocked = await lockfile.check(getHarnessedRoot(), {
7724
+ lockfilePath: lockPath,
7725
+ stale: 1e4
7726
+ });
7727
+ if (isLocked) {
7728
+ const s = await stat(lockPath);
7729
+ const ageMs = Date.now() - s.mtime.getTime();
7730
+ const stale = ageMs > 1e4;
7731
+ console.log(
7732
+ t("status.lock_held", {
7733
+ since: s.mtime.toISOString(),
7734
+ staleSuffix: stale ? t("status.lock_held.stale_suffix") : ""
7735
+ })
7736
+ );
7737
+ console.log(t("status.lock_release_hint", { path: lockPath }));
7738
+ } else {
7739
+ console.log(t("status.lock_free"));
7740
+ }
7741
+ } catch {
7742
+ }
7743
+ });
7744
+ }
7745
+
7746
+ // src/cli/lib/here.ts
7747
+ function parseBareInvocation(argvRest) {
7748
+ const tokens = [];
7749
+ for (let i = 0; i < argvRest.length; i++) {
7750
+ const a = argvRest[i];
7751
+ if (a === "--lang") {
7752
+ i++;
7753
+ continue;
7754
+ }
7755
+ if (a?.startsWith("--lang=")) continue;
7756
+ if (a !== void 0) tokens.push(a);
7757
+ }
7758
+ if (tokens.length === 0) return { here: true, json: false };
7759
+ if (tokens.length === 1 && tokens[0] === "--json") return { here: true, json: true };
7760
+ return { here: false, json: false };
7761
+ }
7762
+ async function buildHereView(wf, ledger, auto) {
7763
+ if (!wf) {
7764
+ return {
7765
+ lines: [
7766
+ t("here.no_workflow", { path: harnessedFile("workflows.json") }),
7767
+ t("here.no_workflow.hint")
7768
+ ],
7769
+ json: { active: false }
7770
+ };
7771
+ }
7772
+ const body = await buildRecoverLines(wf, ledger);
7773
+ const step = resolveNext(ledger, auto);
7774
+ const lines = [
7775
+ ...body,
7776
+ `NEXT: ${step.next}`,
7777
+ ...step.sub ? [`\u2192 run: harnessed prompt ${step.sub}`] : []
7778
+ ];
7779
+ return {
7780
+ lines,
7781
+ json: {
7782
+ active: true,
7783
+ phase: wf.phase,
7784
+ status: wf.status,
7785
+ started_at: wf.started_at,
7786
+ next: step.next,
7787
+ sub: step.sub,
7788
+ hint: step.hint,
7789
+ sub_progress: ledger
7790
+ }
7791
+ };
7792
+ }
7793
+ async function runHere(opts) {
7794
+ const { readCurrentWorkflow: readCurrentWorkflow2 } = await Promise.resolve().then(() => (init_state(), state_exports));
7795
+ const wf = await readCurrentWorkflow2();
7796
+ const ledger = wf?.sub_progress ?? [];
7797
+ const auto = resolveAutoTransition(wf?.auto_transition);
7798
+ const view = await buildHereView(wf, ledger, auto);
7799
+ if (opts.json) {
7800
+ console.log(JSON.stringify(view.json, null, 2));
7801
+ } else {
7802
+ for (const l of view.lines) console.log(l);
7803
+ }
7804
+ process.exit(0);
7805
+ }
7538
7806
  var QA = [
7539
7807
  { q: "\u2460 \u662F\u771F reusable surface \u8FD8\u662F\u4E34\u65F6 wrapper?", f: "q1_reusable_surface" },
7540
7808
  { q: "\u2461 \u4E0A\u6E38\u540D\u5B57 fit \u9879\u76EE shape \u5417? \u6709\u73B0\u6709\u547D\u540D\u51B2\u7A81\u5417?", f: "q2_name_fit" },
@@ -7588,21 +7856,15 @@ function registerManifestAdd(program2) {
7588
7856
  }
7589
7857
 
7590
7858
  // src/cli/next.ts
7591
- function resolveAutoTransition(envelopeValue) {
7592
- const env = process.env.HARNESSED_AUTO_TRANSITION;
7593
- if (env === "true") return true;
7594
- if (env === "false") return false;
7595
- return envelopeValue ?? true;
7596
- }
7597
7859
  function registerNext(program2) {
7598
7860
  program2.command("next").description(
7599
7861
  "Print the deterministic next-step contract (NEXT: auto|manual|done) for the active workflow"
7600
7862
  ).action(async () => {
7601
7863
  const { readCurrentWorkflow: readCurrentWorkflow2 } = await Promise.resolve().then(() => (init_state(), state_exports));
7602
- const { resolveNext: resolveNext2 } = await Promise.resolve().then(() => (init_nextStep(), nextStep_exports));
7864
+ const { resolveNext: resolveNext2, resolveAutoTransition: resolveAutoTransition2 } = await Promise.resolve().then(() => (init_nextStep(), nextStep_exports));
7603
7865
  const current = await readCurrentWorkflow2();
7604
7866
  const ledger = current?.sub_progress ?? [];
7605
- const auto = resolveAutoTransition(current?.auto_transition);
7867
+ const auto = resolveAutoTransition2(current?.auto_transition);
7606
7868
  const step = resolveNext2(ledger, auto);
7607
7869
  console.log(`NEXT: ${step.next}`);
7608
7870
  if (step.sub) console.log(`SUB: ${step.sub}`);
@@ -7988,9 +8250,13 @@ ${t("rollback.metadata_unreadable.fix")}`
7988
8250
 
7989
8251
  // src/cli.ts
7990
8252
  init_run2();
8253
+ init_platform();
8254
+
8255
+ // src/cli/lib/capabilityResolver.ts
8256
+ init_platform();
7991
8257
  function readInstalledPlugins(homedirOverride) {
7992
- const home = homedir();
7993
- const path = join(home, ".claude", "plugins", "installed_plugins.json");
8258
+ const path = getPluginsRegistry(homedirOverride);
8259
+ if (path === null) return /* @__PURE__ */ new Set();
7994
8260
  let raw;
7995
8261
  try {
7996
8262
  raw = readFileSync(path, "utf8");
@@ -8015,8 +8281,7 @@ function readInstalledPlugins(homedirOverride) {
8015
8281
  return out;
8016
8282
  }
8017
8283
  function readInstalledUserSkills(homedirOverride) {
8018
- const home = homedir();
8019
- const skillsRoot = join(home, ".claude", "skills");
8284
+ const skillsRoot = getSkillsDir(homedirOverride);
8020
8285
  try {
8021
8286
  const entries = readdirSync(skillsRoot, { withFileTypes: true });
8022
8287
  const out = /* @__PURE__ */ new Set();
@@ -8093,55 +8358,57 @@ function renderSkillBody(body, capabilities, installedPlugins, installedUserSkil
8093
8358
  }
8094
8359
 
8095
8360
  // src/cli/lib/enableAgentTeamsInSettings.ts
8361
+ init_platform();
8362
+
8363
+ // src/cli/lib/settingsWriter.ts
8096
8364
  init_harnessedRoot();
8097
- function settingsPath() {
8098
- return resolve(homedir(), ".claude", "settings.json");
8099
- }
8100
- async function enableAgentTeamsInSettings() {
8101
- const path = settingsPath();
8365
+ init_platform();
8366
+ async function mergeSettingsEnvKey(key, value, opts = {}) {
8367
+ const path = getSettingsPath();
8102
8368
  let raw;
8103
8369
  try {
8104
8370
  raw = await readFile(path, "utf8");
8105
8371
  } catch (err2) {
8106
8372
  const code = err2.code;
8107
8373
  if (code !== "ENOENT") {
8108
- return { status: "warn", message: `read ${path} failed: ${err2.message}` };
8374
+ return { outcome: "warn", message: `read ${path} failed: ${err2.message}` };
8109
8375
  }
8110
- return createFreshSettings(path);
8376
+ return createFresh(path, key, value);
8111
8377
  }
8112
8378
  let data;
8113
8379
  try {
8114
8380
  const parsed = JSON.parse(raw);
8115
8381
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
8116
- return { status: "warn", message: `${path} is not a JSON object` };
8382
+ return { outcome: "warn", message: `${path} is not a JSON object` };
8117
8383
  }
8118
8384
  data = parsed;
8119
8385
  } catch (err2) {
8120
- return { status: "warn", message: `${path} malformed JSON: ${err2.message}` };
8386
+ return { outcome: "warn", message: `${path} malformed JSON: ${err2.message}` };
8121
8387
  }
8122
8388
  const env = data.env ?? {};
8123
- if (env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1") {
8124
- return { status: "already-enabled", path };
8389
+ const existing = env[key];
8390
+ if (typeof existing === "string" && existing.length > 0 && opts.skipIfPresent?.(existing) === true) {
8391
+ return { outcome: "already", path, existing };
8125
8392
  }
8126
- const backupPath = await backupOriginal(raw);
8127
- if (backupPath.status === "warn") return backupPath;
8128
- data.env = { ...env, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" };
8393
+ const backup2 = await backupOriginal(raw);
8394
+ if (backup2.status === "warn") return { outcome: "warn", message: backup2.message };
8395
+ data.env = { ...env, [key]: value };
8129
8396
  const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
8130
8397
  `);
8131
- if (writeErr) return { status: "warn", message: writeErr };
8132
- return { status: "enabled", path, backupPath: backupPath.path };
8398
+ if (writeErr) return { outcome: "warn", message: writeErr };
8399
+ return { outcome: "merged", path, backupPath: backup2.path };
8133
8400
  }
8134
- async function createFreshSettings(path) {
8135
- const data = { env: { CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" } };
8401
+ async function createFresh(path, key, value) {
8402
+ const data = { env: { [key]: value } };
8136
8403
  try {
8137
- await mkdir(join(homedir(), ".claude"), { recursive: true });
8404
+ await mkdir(dirname(path), { recursive: true });
8138
8405
  } catch (err2) {
8139
- return { status: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
8406
+ return { outcome: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
8140
8407
  }
8141
8408
  const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
8142
8409
  `);
8143
- if (writeErr) return { status: "warn", message: writeErr };
8144
- return { status: "created", path };
8410
+ if (writeErr) return { outcome: "warn", message: writeErr };
8411
+ return { outcome: "created", path };
8145
8412
  }
8146
8413
  async function backupOriginal(raw) {
8147
8414
  const backupRoot = harnessedSubdir("backups");
@@ -8166,11 +8433,34 @@ async function atomicWrite(path, content) {
8166
8433
  }
8167
8434
  }
8168
8435
 
8169
- // src/cli/lib/enableUserLangInSettings.ts
8170
- init_harnessedRoot();
8171
- function settingsPath2() {
8172
- return resolve(homedir(), ".claude", "settings.json");
8436
+ // src/cli/lib/enableAgentTeamsInSettings.ts
8437
+ var ENV_KEY = "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS";
8438
+ async function enableAgentTeamsInSettings() {
8439
+ const platform = detectPlatform();
8440
+ if (!platform.supportsEnvKeyWrite) {
8441
+ return {
8442
+ status: "warn",
8443
+ message: `platform '${platform.id}' does not support env-key settings writes (capability-absent) \u2014 ${ENV_KEY} skipped`
8444
+ };
8445
+ }
8446
+ const r = await mergeSettingsEnvKey(ENV_KEY, "1", {
8447
+ skipIfPresent: (existing) => existing === "1"
8448
+ });
8449
+ switch (r.outcome) {
8450
+ case "created":
8451
+ return { status: "created", path: r.path };
8452
+ case "already":
8453
+ return { status: "already-enabled", path: r.path };
8454
+ case "merged":
8455
+ return { status: "enabled", path: r.path, backupPath: r.backupPath };
8456
+ case "warn":
8457
+ return { status: "warn", message: r.message };
8458
+ }
8173
8459
  }
8460
+
8461
+ // src/cli/lib/enableUserLangInSettings.ts
8462
+ init_platform();
8463
+ var ENV_KEY2 = "HARNESSED_USER_LANG";
8174
8464
  function detectUserLang(override) {
8175
8465
  if (override) {
8176
8466
  if (/^zh([^a-z]|$)/i.test(override)) return "zh-Hans";
@@ -8188,73 +8478,26 @@ function safeIntlLocale2() {
8188
8478
  }
8189
8479
  }
8190
8480
  async function enableUserLangInSettings(override) {
8191
- const path = settingsPath2();
8192
- const detected = detectUserLang(override);
8193
- let raw;
8194
- try {
8195
- raw = await readFile(path, "utf8");
8196
- } catch (err2) {
8197
- const code = err2.code;
8198
- if (code !== "ENOENT") {
8199
- return { status: "warn", message: `read ${path} failed: ${err2.message}` };
8200
- }
8201
- return createFreshSettings2(path, detected);
8202
- }
8203
- let data;
8204
- try {
8205
- const parsed = JSON.parse(raw);
8206
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
8207
- return { status: "warn", message: `${path} is not a JSON object` };
8208
- }
8209
- data = parsed;
8210
- } catch (err2) {
8211
- return { status: "warn", message: `${path} malformed JSON: ${err2.message}` };
8212
- }
8213
- const env = data.env ?? {};
8214
- const existing = env.HARNESSED_USER_LANG;
8215
- if (typeof existing === "string" && existing.length > 0 && override === void 0) {
8216
- return { status: "already-set", path, existing };
8217
- }
8218
- const backupPath = await backupOriginal2(raw);
8219
- if (backupPath.status === "warn") return backupPath;
8220
- data.env = { ...env, HARNESSED_USER_LANG: detected };
8221
- const writeErr = await atomicWrite2(path, `${JSON.stringify(data, null, 2)}
8222
- `);
8223
- if (writeErr) return { status: "warn", message: writeErr };
8224
- return { status: "enabled", path, backupPath: backupPath.path, detected };
8225
- }
8226
- async function createFreshSettings2(path, detected) {
8227
- const data = { env: { HARNESSED_USER_LANG: detected } };
8228
- try {
8229
- await mkdir(join(homedir(), ".claude"), { recursive: true });
8230
- } catch (err2) {
8231
- return { status: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
8232
- }
8233
- const writeErr = await atomicWrite2(path, `${JSON.stringify(data, null, 2)}
8234
- `);
8235
- if (writeErr) return { status: "warn", message: writeErr };
8236
- return { status: "created", path, detected };
8237
- }
8238
- async function backupOriginal2(raw) {
8239
- const backupRoot = harnessedSubdir("backups");
8240
- const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
8241
- const backupPath = join(backupRoot, `settings.json.${ts}.bak`);
8242
- try {
8243
- await mkdir(backupRoot, { recursive: true });
8244
- await writeFile(backupPath, raw, "utf8");
8245
- return { status: "ok", path: backupPath };
8246
- } catch (err2) {
8247
- return { status: "warn", message: `backup ${backupPath} failed: ${err2.message}` };
8481
+ const platform = detectPlatform();
8482
+ if (!platform.supportsEnvKeyWrite) {
8483
+ return {
8484
+ status: "warn",
8485
+ message: `platform '${platform.id}' does not support env-key settings writes (capability-absent) \u2014 ${ENV_KEY2} skipped`
8486
+ };
8248
8487
  }
8249
- }
8250
- async function atomicWrite2(path, content) {
8251
- const tmpPath = `${path}.tmp-${process.pid}-${Date.now()}`;
8252
- try {
8253
- await writeFile(tmpPath, content, "utf8");
8254
- await rename(tmpPath, path);
8255
- return void 0;
8256
- } catch (err2) {
8257
- return `write ${path} failed: ${err2.message}`;
8488
+ const detected = detectUserLang(override);
8489
+ const r = await mergeSettingsEnvKey(ENV_KEY2, detected, {
8490
+ skipIfPresent: () => override === void 0
8491
+ });
8492
+ switch (r.outcome) {
8493
+ case "created":
8494
+ return { status: "created", path: r.path, detected };
8495
+ case "already":
8496
+ return { status: "already-set", path: r.path, existing: r.existing };
8497
+ case "merged":
8498
+ return { status: "enabled", path: r.path, backupPath: r.backupPath, detected };
8499
+ case "warn":
8500
+ return { status: "warn", message: r.message };
8258
8501
  }
8259
8502
  }
8260
8503
 
@@ -8314,8 +8557,8 @@ async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOver
8314
8557
  ]
8315
8558
  };
8316
8559
  }
8317
- const installedPlugins = readInstalledPlugins();
8318
- const installedUserSkills = readInstalledUserSkills();
8560
+ const installedPlugins = readInstalledPlugins(homedirOverride);
8561
+ const installedUserSkills = readInstalledUserSkills(homedirOverride);
8319
8562
  const results = [];
8320
8563
  const warningSet = /* @__PURE__ */ new Set();
8321
8564
  for (const name of skillNames) {
@@ -8478,6 +8721,23 @@ async function runStepBInstall(manifestPaths, runOpts = {}) {
8478
8721
  }
8479
8722
 
8480
8723
  // src/cli/setup.ts
8724
+ var KNOWN_PLATFORMS = ["claude", "codex"];
8725
+ async function applyPlatformOption(platform) {
8726
+ if (!KNOWN_PLATFORMS.includes(platform)) {
8727
+ console.error(
8728
+ `--platform: unknown id '${platform}' (expected one of: ${KNOWN_PLATFORMS.join(" | ")})`
8729
+ );
8730
+ process.exit(1);
8731
+ }
8732
+ process.env.HARNESSED_PLATFORM = platform;
8733
+ const stateRoot = detectPlatform().stateRoot;
8734
+ try {
8735
+ await mkdir(stateRoot, { recursive: true });
8736
+ await writeFile(join(stateRoot, ".platform"), platform, "utf8");
8737
+ } catch (e) {
8738
+ console.warn(` [--platform] could not persist .platform pin (${e.message})`);
8739
+ }
8740
+ }
8481
8741
  async function listBaseManifests2(pkgRoot) {
8482
8742
  const out = [];
8483
8743
  for (const d of ["manifests/tools", "manifests/skill-packs"]) {
@@ -8530,14 +8790,15 @@ function registerSetup(program2) {
8530
8790
  ).option("--dry-run", "preview only \u2014 do not write to disk (opt-in for advanced users)").option(
8531
8791
  "--user-lang <code>",
8532
8792
  "override detected OS locale for env.HARNESSED_USER_LANG (en | zh-Hans / zh-CN / zh-TW)"
8533
- ).option("--non-interactive", "skip all confirm prompts (CI / scripted setup)").option("--no-auto-install", "do not prompt to auto-install missing plugins (advisory only)").option(
8793
+ ).option("--platform <id>", "target harness platform (claude | codex); default: auto-detect").option("--non-interactive", "skip all confirm prompts (CI / scripted setup)").option("--no-auto-install", "do not prompt to auto-install missing plugins (advisory only)").option(
8534
8794
  "--update-installed",
8535
8795
  "force re-install already-installed plugins (excludes MCP servers); default: skip if installed"
8536
8796
  ).action(async (raw) => {
8537
8797
  const dryRun = raw.dryRun === true;
8798
+ if (raw.platform !== void 0) await applyPlatformOption(raw.platform);
8538
8799
  const pkgRoot = getPackageRoot();
8539
8800
  const workflowsDir = resolve(pkgRoot, "workflows");
8540
- const skillsBase = resolve(homedir(), ".claude", "skills");
8801
+ const skillsBase = getSkillsDir();
8541
8802
  await warnIfAgentTeamsMissing();
8542
8803
  let entries;
8543
8804
  try {
@@ -8581,7 +8842,7 @@ function registerSetup(program2) {
8581
8842
  console.warn(` - ${w}`);
8582
8843
  }
8583
8844
  }
8584
- const commandsBase = resolve(homedir(), ".claude", "commands");
8845
+ const commandsBase = getCommandsDir();
8585
8846
  try {
8586
8847
  await mkdir(commandsBase, { recursive: true });
8587
8848
  } catch (e) {
@@ -8676,116 +8937,6 @@ Force-update pass complete: ${b2.installed.length} installed / ${b2.alreadyInsta
8676
8937
  process.exit(0);
8677
8938
  });
8678
8939
  }
8679
-
8680
- // src/cli/status.ts
8681
- init_evidence();
8682
- init_ledger();
8683
- init_state();
8684
- init_harnessedRoot();
8685
- init_state2();
8686
- function statusMarker(status) {
8687
- switch (status) {
8688
- case "done":
8689
- return "\u2705 done";
8690
- case "pending":
8691
- return "\u23F3 pending";
8692
- case "failed":
8693
- return "\u2717 failed";
8694
- case "skipped":
8695
- return "\u2B1C skipped";
8696
- case "rejected":
8697
- return "\u{1F6AB} rejected";
8698
- }
8699
- }
8700
- function evidenceLabel(entry) {
8701
- switch (entry.evidence_status) {
8702
- case "verified":
8703
- return "evidence: verified";
8704
- case "overridden":
8705
- return "evidence: overridden (--force)";
8706
- default:
8707
- return "evidence: none_declared";
8708
- }
8709
- }
8710
- async function buildRecoverLines(workflow, ledger) {
8711
- const lines = [];
8712
- if (!workflow || ledger.length === 0) {
8713
- lines.push("no ledger \u2014 run gates + start");
8714
- return lines;
8715
- }
8716
- lines.push(`workflow: ${workflow.phase} (${workflow.status}) started ${workflow.started_at}`);
8717
- const next = nextPending(ledger);
8718
- for (const e of ledger) {
8719
- const marker = statusMarker(e.status);
8720
- let suffix = "";
8721
- if (e.status === "skipped") {
8722
- suffix = e.reason ? ` (skipped: ${e.reason})` : "";
8723
- } else if (e.status === "done") {
8724
- suffix = ` ${evidenceLabel(e)}`;
8725
- }
8726
- const arrow = e.sub === next ? " \u2190 next" : "";
8727
- lines.push(` ${e.sub} ${marker}${suffix}${arrow}`);
8728
- }
8729
- if (next) {
8730
- lines.push(`\u2192 next: harnessed prompt ${next}`);
8731
- } else {
8732
- lines.push("\u2192 all subs resolved (no pending work)");
8733
- }
8734
- for (const e of ledger) {
8735
- if (!e.evidence || e.evidence.length === 0) continue;
8736
- const drift = await detectDrift(e.evidence);
8737
- for (const d of drift) {
8738
- const now = d.now === "" ? "missing" : `${d.now.slice(0, 7)}\u2026`;
8739
- lines.push(`\u26A0 drift: ${d.path} sha256 changed (was ${d.was.slice(0, 7)}\u2026, now ${now})`);
8740
- }
8741
- }
8742
- return lines;
8743
- }
8744
- function registerStatus(program2) {
8745
- program2.command("status").description("Show installed upstreams (from <harnessed-root>/state.json)").option("--recover", "structured recovery view of the active workflow ledger (v5.0 Spec 1 B)").action(async (opts) => {
8746
- if (opts.recover) {
8747
- const wf = await readCurrentWorkflow();
8748
- const ledger = wf?.sub_progress ?? [];
8749
- const lines = await buildRecoverLines(wf, ledger);
8750
- for (const l of lines) console.log(l);
8751
- return;
8752
- }
8753
- const state = await readState(process.cwd());
8754
- const names = Object.keys(state.installed).sort();
8755
- if (names.length === 0) {
8756
- console.log(t("status.no_installs", { path: harnessedFile("state.json") }));
8757
- } else {
8758
- for (const n of names) {
8759
- const e = state.installed[n];
8760
- if (!e) continue;
8761
- console.log(`${n} @ ${e.version} (installed ${e.installedAt})`);
8762
- }
8763
- console.log(t("status.summary_installs", { count: names.length }));
8764
- }
8765
- const lockPath = harnessedFile(".lock");
8766
- try {
8767
- const isLocked = await lockfile.check(getHarnessedRoot(), {
8768
- lockfilePath: lockPath,
8769
- stale: 1e4
8770
- });
8771
- if (isLocked) {
8772
- const s = await stat(lockPath);
8773
- const ageMs = Date.now() - s.mtime.getTime();
8774
- const stale = ageMs > 1e4;
8775
- console.log(
8776
- t("status.lock_held", {
8777
- since: s.mtime.toISOString(),
8778
- staleSuffix: stale ? t("status.lock_held.stale_suffix") : ""
8779
- })
8780
- );
8781
- console.log(t("status.lock_release_hint", { path: lockPath }));
8782
- } else {
8783
- console.log(t("status.lock_free"));
8784
- }
8785
- } catch {
8786
- }
8787
- });
8788
- }
8789
8940
  init_harnessedRoot();
8790
8941
  init_path_guard();
8791
8942
  init_validate();
@@ -8804,10 +8955,10 @@ var uninstallCcHookAdd = async (ctx) => {
8804
8955
  }
8805
8956
  const abort = dryRunGate(ctx);
8806
8957
  if (abort) return abort;
8807
- const settingsPath3 = join(homedir(), ".claude", "settings.json");
8958
+ const settingsPath = join(homedir(), ".claude", "settings.json");
8808
8959
  let existing;
8809
8960
  try {
8810
- existing = await readFile(settingsPath3, "utf8");
8961
+ existing = await readFile(settingsPath, "utf8");
8811
8962
  } catch {
8812
8963
  return { ok: true, removedPaths: [] };
8813
8964
  }
@@ -8836,8 +8987,8 @@ var uninstallCcHookAdd = async (ctx) => {
8836
8987
  if (settings.hooks?.[ev]?.length === before || before === settings.hooks?.[ev]?.length) ;
8837
8988
  const newText = `${JSON.stringify(settings, null, 2)}
8838
8989
  `;
8839
- await writeFile(settingsPath3, newText);
8840
- return { ok: true, removedPaths: [settingsPath3] };
8990
+ await writeFile(settingsPath, newText);
8991
+ return { ok: true, removedPaths: [settingsPath] };
8841
8992
  };
8842
8993
 
8843
8994
  // src/uninstallers/ccPluginMarketplace.ts
@@ -8976,7 +9127,7 @@ var uninstallNpmCli = async (ctx) => {
8976
9127
  const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
8977
9128
  const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
8978
9129
  const isWin = process.platform === "win32";
8979
- const result = await new Promise((resolve20) => {
9130
+ const result = await new Promise((resolve18) => {
8980
9131
  const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
8981
9132
  let stderr = "";
8982
9133
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -8984,15 +9135,15 @@ var uninstallNpmCli = async (ctx) => {
8984
9135
  });
8985
9136
  const timer = setTimeout(() => {
8986
9137
  child.kill("SIGKILL");
8987
- resolve20({ exitCode: -1, stderr: `${stderr}[timeout]` });
9138
+ resolve18({ exitCode: -1, stderr: `${stderr}[timeout]` });
8988
9139
  }, 3e4);
8989
9140
  child.on("error", (e) => {
8990
9141
  clearTimeout(timer);
8991
- resolve20({ exitCode: -1, stderr: e.message });
9142
+ resolve18({ exitCode: -1, stderr: e.message });
8992
9143
  });
8993
9144
  child.on("close", (code) => {
8994
9145
  clearTimeout(timer);
8995
- resolve20({ exitCode: code ?? -1, stderr });
9146
+ resolve18({ exitCode: code ?? -1, stderr });
8996
9147
  });
8997
9148
  });
8998
9149
  if (result.exitCode !== 0) {
@@ -9058,9 +9209,9 @@ async function discoverCommandFiles(commandsDir) {
9058
9209
  }
9059
9210
  return owned;
9060
9211
  }
9061
- async function checkSettingsEnv(settingsPath3) {
9212
+ async function checkSettingsEnv(settingsPath) {
9062
9213
  try {
9063
- const raw = await readFile(settingsPath3, "utf8");
9214
+ const raw = await readFile(settingsPath, "utf8");
9064
9215
  const data = JSON.parse(raw);
9065
9216
  const env = data.env ?? {};
9066
9217
  return {
@@ -9071,8 +9222,8 @@ async function checkSettingsEnv(settingsPath3) {
9071
9222
  return { hasAgentTeams: false, hasUserLang: false };
9072
9223
  }
9073
9224
  }
9074
- async function removeSettingsEnv(settingsPath3) {
9075
- const raw = await readFile(settingsPath3, "utf8");
9225
+ async function removeSettingsEnv(settingsPath) {
9226
+ const raw = await readFile(settingsPath, "utf8");
9076
9227
  const data = JSON.parse(raw);
9077
9228
  const env = data.env ?? {};
9078
9229
  let changed = false;
@@ -9087,14 +9238,14 @@ async function removeSettingsEnv(settingsPath3) {
9087
9238
  if (!changed) return false;
9088
9239
  if (Object.keys(env).length === 0) delete data.env;
9089
9240
  else data.env = env;
9090
- await writeFile(settingsPath3, `${JSON.stringify(data, null, 2)}
9241
+ await writeFile(settingsPath, `${JSON.stringify(data, null, 2)}
9091
9242
  `, "utf8");
9092
9243
  return true;
9093
9244
  }
9094
9245
  async function runUnifiedUninstall(home, dryRun) {
9095
9246
  const commandsDir = join(home, ".claude", "commands");
9096
9247
  const skillsDir = join(home, ".claude", "skills");
9097
- const settingsPath3 = join(home, ".claude", "settings.json");
9248
+ const settingsPath = join(home, ".claude", "settings.json");
9098
9249
  const harnessedRoot = getHarnessedRoot();
9099
9250
  const pkgRoot = getPackageRoot();
9100
9251
  let workflowNames = [];
@@ -9105,7 +9256,7 @@ async function runUnifiedUninstall(home, dryRun) {
9105
9256
  } catch {
9106
9257
  }
9107
9258
  const commandFiles = await discoverCommandFiles(commandsDir);
9108
- const settingsEnv = await checkSettingsEnv(settingsPath3);
9259
+ const settingsEnv = await checkSettingsEnv(settingsPath);
9109
9260
  const hasSettingsChanges = settingsEnv.hasAgentTeams || settingsEnv.hasUserLang;
9110
9261
  const skillDirs = [];
9111
9262
  for (const name of workflowNames) {
@@ -9163,9 +9314,9 @@ async function runUnifiedUninstall(home, dryRun) {
9163
9314
  let removedSettings = false;
9164
9315
  if (hasSettingsChanges) {
9165
9316
  try {
9166
- removedSettings = await removeSettingsEnv(settingsPath3);
9317
+ removedSettings = await removeSettingsEnv(settingsPath);
9167
9318
  } catch (e) {
9168
- failures.push(`${settingsPath3}: ${e.message}`);
9319
+ failures.push(`${settingsPath}: ${e.message}`);
9169
9320
  }
9170
9321
  }
9171
9322
  const normalizedRoot = resolve(harnessedRoot);
@@ -9413,6 +9564,11 @@ registerLearn(program);
9413
9564
  registerUpdate(program);
9414
9565
  registerReleasePreflight(program);
9415
9566
  registerRetro(program);
9416
- program.parse(process.argv);
9567
+ var bare = parseBareInvocation(process.argv.slice(2));
9568
+ if (bare.here) {
9569
+ void runHere({ json: bare.json });
9570
+ } else {
9571
+ program.parse(process.argv);
9572
+ }
9417
9573
  //# sourceMappingURL=cli.mjs.map
9418
9574
  //# sourceMappingURL=cli.mjs.map