harnessed 4.6.0 → 4.8.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.
Files changed (48) hide show
  1. package/README.md +37 -3
  2. package/dist/cli.mjs +682 -433
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/dist/index.mjs.map +1 -1
  6. package/manifests/optional/codegraph.yaml +6 -4
  7. package/manifests/optional/ecc.ee5-answers.json +13 -0
  8. package/manifests/optional/ecc.yaml +64 -0
  9. package/messages/en.json +4 -1
  10. package/messages/zh-Hans.json +20 -3
  11. package/package.json +1 -1
  12. package/workflows/auto/SKILL.zh-Hans.md +129 -0
  13. package/workflows/capabilities.yaml +120 -0
  14. package/workflows/disciplines/doc-discipline.zh-Hans.yaml +49 -0
  15. package/workflows/disciplines/karpathy.yaml +5 -5
  16. package/workflows/disciplines/karpathy.zh-Hans.yaml +47 -0
  17. package/workflows/disciplines/operational.yaml +6 -6
  18. package/workflows/disciplines/operational.zh-Hans.yaml +79 -0
  19. package/workflows/disciplines/output-style.yaml +7 -7
  20. package/workflows/disciplines/output-style.zh-Hans.yaml +62 -0
  21. package/workflows/disciplines/priority.yaml +2 -2
  22. package/workflows/disciplines/priority.zh-Hans.yaml +28 -0
  23. package/workflows/discuss/auto/SKILL.zh-Hans.md +75 -0
  24. package/workflows/discuss/phase/SKILL.zh-Hans.md +73 -0
  25. package/workflows/discuss/strategic/SKILL.zh-Hans.md +78 -0
  26. package/workflows/discuss/subtask/SKILL.zh-Hans.md +79 -0
  27. package/workflows/plan/architecture/SKILL.zh-Hans.md +74 -0
  28. package/workflows/plan/auto/SKILL.zh-Hans.md +75 -0
  29. package/workflows/plan/phase/SKILL.zh-Hans.md +76 -0
  30. package/workflows/research/SKILL.zh-Hans.md +81 -0
  31. package/workflows/retro/SKILL.zh-Hans.md +71 -0
  32. package/workflows/role-prompts.zh-Hans.yaml +501 -0
  33. package/workflows/ship/auto/SKILL.zh-Hans.md +46 -0
  34. package/workflows/ship/preflight/SKILL.zh-Hans.md +38 -0
  35. package/workflows/task/auto/SKILL.zh-Hans.md +80 -0
  36. package/workflows/task/clarify/SKILL.zh-Hans.md +79 -0
  37. package/workflows/task/code/SKILL.zh-Hans.md +85 -0
  38. package/workflows/task/deliver/SKILL.zh-Hans.md +113 -0
  39. package/workflows/task/test/SKILL.zh-Hans.md +90 -0
  40. package/workflows/verify/auto/SKILL.zh-Hans.md +89 -0
  41. package/workflows/verify/code-review/SKILL.zh-Hans.md +71 -0
  42. package/workflows/verify/design/SKILL.zh-Hans.md +74 -0
  43. package/workflows/verify/multispec/SKILL.zh-Hans.md +88 -0
  44. package/workflows/verify/paranoid/SKILL.zh-Hans.md +74 -0
  45. package/workflows/verify/progress/SKILL.zh-Hans.md +69 -0
  46. package/workflows/verify/qa/SKILL.zh-Hans.md +76 -0
  47. package/workflows/verify/security/SKILL.zh-Hans.md +70 -0
  48. package/workflows/verify/simplify/SKILL.zh-Hans.md +70 -0
package/dist/cli.mjs CHANGED
@@ -6,6 +6,7 @@ import * as ajvFormatsNs from 'ajv-formats';
6
6
  import { execFileSync, spawnSync, spawn, exec, execFile, execSync } from 'child_process';
7
7
  import { existsSync, mkdirSync, renameSync, writeFileSync, readFileSync, readdirSync } from 'fs';
8
8
  import { join, dirname, resolve, sep, relative } from 'path';
9
+ import { fileURLToPath } from 'url';
9
10
  import { homedir } from 'os';
10
11
  import { createHash } from 'crypto';
11
12
  import { readFile, readdir, unlink, writeFile, stat, rm, cp, mkdir, rename, appendFile, access } from 'fs/promises';
@@ -13,7 +14,6 @@ import { Value } from '@sinclair/typebox/value';
13
14
  import lockfile from 'proper-lockfile';
14
15
  import { Parser } from 'expr-eval';
15
16
  import { query } from '@anthropic-ai/claude-agent-sdk';
16
- import { fileURLToPath } from 'url';
17
17
  import { promisify } from 'util';
18
18
  import * as p from '@clack/prompts';
19
19
  import { createPatch } from 'diff';
@@ -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.8.0",
42
42
  description: "AI coding harness package manager + composition orchestrator",
43
43
  type: "module",
44
44
  license: "Apache-2.0",
@@ -852,10 +852,162 @@ var init_origin_check = __esm({
852
852
  "src/cli/lib/origin-check.ts"() {
853
853
  }
854
854
  });
855
- function getHarnessedRoot() {
855
+ function mapToSupported(raw) {
856
+ if (!raw) return "en";
857
+ if (/^zh([^a-z]|$)/i.test(raw)) return "zh-Hans";
858
+ return "en";
859
+ }
860
+ function detectLocale() {
861
+ const raw = process.env.HARNESSED_LANG || process.env.LC_ALL || process.env.LANG || process.env.LANGUAGE || safeIntlLocale();
862
+ return mapToSupported(raw);
863
+ }
864
+ function safeIntlLocale() {
865
+ try {
866
+ return Intl.DateTimeFormat().resolvedOptions().locale;
867
+ } catch {
868
+ return void 0;
869
+ }
870
+ }
871
+ function messagesDir() {
872
+ const here = dirname(fileURLToPath(import.meta.url));
873
+ const candidates = [resolve(here, "..", "..", "messages"), resolve(here, "..", "messages")];
874
+ for (const c of candidates) {
875
+ try {
876
+ readFileSync(join(c, "en.json"), "utf8");
877
+ return c;
878
+ } catch {
879
+ }
880
+ }
881
+ return resolve(process.cwd(), "messages");
882
+ }
883
+ function loadLocale(locale) {
884
+ if (cache[locale]) return cache[locale];
885
+ const path = join(messagesDir(), `${locale}.json`);
886
+ try {
887
+ const raw = readFileSync(path, "utf8");
888
+ cache[locale] = JSON.parse(raw);
889
+ } catch {
890
+ cache[locale] = {};
891
+ }
892
+ return cache[locale];
893
+ }
894
+ function setLocale(locale) {
895
+ if (!locale) return;
896
+ const mapped = mapToSupported(locale);
897
+ if (SUPPORTED.has(mapped)) currentLocale = mapped;
898
+ }
899
+ function getLocale() {
900
+ if (currentLocale === null) currentLocale = detectLocale();
901
+ return currentLocale;
902
+ }
903
+ function t(key, params) {
904
+ const locale = getLocale();
905
+ const primary = loadLocale(locale);
906
+ let template = primary[key];
907
+ if (template === void 0 && locale !== "en") {
908
+ template = loadLocale("en")[key];
909
+ }
910
+ if (template === void 0) template = key;
911
+ if (!params) return template;
912
+ return template.replace(/\{\{(\w+)\}\}/g, (_match, name) => {
913
+ const v = params[name];
914
+ return v === void 0 ? `{{${name}}}` : String(v);
915
+ });
916
+ }
917
+ var SUPPORTED, currentLocale, cache;
918
+ var init_i18n = __esm({
919
+ "src/i18n/index.ts"() {
920
+ SUPPORTED = /* @__PURE__ */ new Set(["en", "zh-Hans"]);
921
+ currentLocale = null;
922
+ cache = {};
923
+ }
924
+ });
925
+ function claudeDescriptor(home = homedir()) {
926
+ const claudeHome = join(home, ".claude");
927
+ return {
928
+ id: "claude",
929
+ homeDir: claudeHome,
930
+ stateRoot: join(claudeHome, "harnessed"),
931
+ settingsPath: join(claudeHome, "settings.json"),
932
+ skillsDir: join(claudeHome, "skills"),
933
+ commandsDir: join(claudeHome, "commands"),
934
+ pluginsRegistry: join(claudeHome, "plugins", "installed_plugins.json"),
935
+ // mcpConfigPath is a SIBLING of `.claude` (`<home>/.claude.json`), based on
936
+ // the same home base — not a child of homeDir.
937
+ mcpConfigPath: join(home, ".claude.json"),
938
+ // claude writes its env keys into JSON settings.json (capability present).
939
+ supportsEnvKeyWrite: true
940
+ };
941
+ }
942
+ function codexDescriptor(home = homedir()) {
943
+ const codexHome = join(home, ".codex");
944
+ const configToml = join(codexHome, "config.toml");
945
+ return {
946
+ id: "codex",
947
+ homeDir: codexHome,
948
+ stateRoot: join(codexHome, "harnessed"),
949
+ settingsPath: configToml,
950
+ // SHARED convention dir, NOT <homeDir>/skills.
951
+ skillsDir: join(home, ".agents", "skills"),
952
+ commandsDir: join(codexHome, "prompts"),
953
+ pluginsRegistry: null,
954
+ mcpConfigPath: configToml,
955
+ supportsEnvKeyWrite: false
956
+ };
957
+ }
958
+ function descriptorById(id, home) {
959
+ if (id === "claude") return claudeDescriptor(home);
960
+ if (id === "codex") return codexDescriptor(home);
961
+ return void 0;
962
+ }
963
+ function detectPlatform(home = homedir()) {
964
+ const base = claudeDescriptor(home);
856
965
  const override = process.env.HARNESSED_ROOT_OVERRIDE;
857
- if (override !== void 0 && override !== "") return override;
858
- return join(homedir(), ".claude", "harnessed");
966
+ if (override !== void 0 && override !== "") return { ...base, stateRoot: override };
967
+ const envPlatform = process.env.HARNESSED_PLATFORM;
968
+ if (envPlatform !== void 0 && envPlatform !== "") {
969
+ const d = descriptorById(envPlatform, home);
970
+ if (d) return d;
971
+ }
972
+ try {
973
+ const pin = readFileSync(join(base.stateRoot, ".platform"), "utf8").trim();
974
+ const d = descriptorById(pin, home);
975
+ if (d) return d;
976
+ } catch {
977
+ }
978
+ try {
979
+ if (existsSync(base.homeDir)) return base;
980
+ const codex = codexDescriptor(home);
981
+ if (existsSync(codex.homeDir)) return codex;
982
+ } catch {
983
+ }
984
+ return base;
985
+ }
986
+ function getSettingsPath(home) {
987
+ return detectPlatform(home).settingsPath;
988
+ }
989
+ function getSkillsDir(home) {
990
+ return detectPlatform(home).skillsDir;
991
+ }
992
+ function getCommandsDir(home) {
993
+ return detectPlatform(home).commandsDir;
994
+ }
995
+ function getPluginsRegistry(home) {
996
+ return detectPlatform(home).pluginsRegistry;
997
+ }
998
+ function getMcpConfigPath(home) {
999
+ return detectPlatform(home).mcpConfigPath;
1000
+ }
1001
+ function harnessSkillsDirs(home) {
1002
+ const dirs = [claudeDescriptor(home).skillsDir, codexDescriptor(home).skillsDir];
1003
+ return [...new Set(dirs)];
1004
+ }
1005
+ var init_platform = __esm({
1006
+ "src/installers/lib/platform.ts"() {
1007
+ }
1008
+ });
1009
+ function getHarnessedRoot() {
1010
+ return detectPlatform().stateRoot;
859
1011
  }
860
1012
  function harnessedSubdir(name) {
861
1013
  return join(getHarnessedRoot(), name);
@@ -890,6 +1042,7 @@ function migrateLegacyHarnessedRoot() {
890
1042
  }
891
1043
  var init_harnessedRoot = __esm({
892
1044
  "src/installers/lib/harnessedRoot.ts"() {
1045
+ init_platform();
893
1046
  }
894
1047
  });
895
1048
  function getBackupRoot() {
@@ -1895,8 +2048,20 @@ var init_loadPhases = __esm({
1895
2048
  };
1896
2049
  }
1897
2050
  });
1898
- async function loadRolePrompts(workflowsDir) {
1899
- const path = join(workflowsDir, "role-prompts.yaml");
2051
+ function resolveLocaleYaml(dir, baseName, locale = getLocale()) {
2052
+ if (locale !== "en") {
2053
+ const sibling = join(dir, `${baseName}.${locale}.yaml`);
2054
+ if (existsSync(sibling)) return sibling;
2055
+ }
2056
+ return join(dir, `${baseName}.yaml`);
2057
+ }
2058
+ var init_localeYaml = __esm({
2059
+ "src/i18n/localeYaml.ts"() {
2060
+ init_i18n();
2061
+ }
2062
+ });
2063
+ async function loadRolePrompts(workflowsDir, locale = getLocale()) {
2064
+ const path = resolveLocaleYaml(workflowsDir, "role-prompts", locale);
1900
2065
  let raw;
1901
2066
  try {
1902
2067
  raw = await readFile(path, "utf8");
@@ -2056,7 +2221,7 @@ function nameToYamlHintPath(name) {
2056
2221
  function shouldOverwriteFile(content) {
2057
2222
  return HARNESSED_MARKER_RX.test(content) || V3_4_3_SIGNATURE_SUB_RX.test(content) || V3_4_3_SIGNATURE_MASTER_RX.test(content);
2058
2223
  }
2059
- async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabilities, installedPlugins, installedUserSkills, writer, fileExists2 = existsSync, readFileSync12 = (p5) => readFileSync(p5, "utf8")) {
2224
+ async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabilities, installedPlugins, installedUserSkills, writer, fileExists2 = existsSync, readFileSync13 = (p5) => readFileSync(p5, "utf8")) {
2060
2225
  const results = [];
2061
2226
  const aggregatedWarnings = /* @__PURE__ */ new Set();
2062
2227
  for (const name of slashNames) {
@@ -2075,7 +2240,7 @@ async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabiliti
2075
2240
  if (fileExists2(path)) {
2076
2241
  let existing = "";
2077
2242
  try {
2078
- existing = readFileSync12(path);
2243
+ existing = readFileSync13(path);
2079
2244
  } catch {
2080
2245
  existing = "";
2081
2246
  }
@@ -2110,6 +2275,8 @@ async function writeAllCommands(slashNames, commandsDir, rolePrompts, capabiliti
2110
2275
  var INTERACTIVE_COMMANDS, ORCHESTRATOR_COMMANDS, MARKER, LANG_DIRECTIVE, HARNESSED_MARKER_RX, V3_4_3_SIGNATURE_SUB_RX, V3_4_3_SIGNATURE_MASTER_RX;
2111
2276
  var init_generateCommands = __esm({
2112
2277
  "src/cli/lib/generateCommands.ts"() {
2278
+ init_i18n();
2279
+ init_localeYaml();
2113
2280
  INTERACTIVE_COMMANDS = /* @__PURE__ */ new Set([
2114
2281
  "discuss",
2115
2282
  "discuss-strategic",
@@ -4373,7 +4540,7 @@ var init_check_mattpocock_skills = __esm({
4373
4540
  }
4374
4541
  });
4375
4542
  function getUserClaudeJsonPath() {
4376
- return join(homedir(), ".claude.json");
4543
+ return getMcpConfigPath();
4377
4544
  }
4378
4545
  async function readUserClaudeJson() {
4379
4546
  const path = getUserClaudeJsonPath();
@@ -4399,21 +4566,20 @@ async function isMcpServerRegistered(name) {
4399
4566
  return Object.hasOwn(servers, name);
4400
4567
  }
4401
4568
  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;
4569
+ const registryPath = getPluginsRegistry();
4570
+ if (registryPath !== null) {
4571
+ try {
4572
+ const raw = await readFile(registryPath, "utf8");
4573
+ const parsed = JSON.parse(raw);
4574
+ const plugins = parsed.plugins;
4575
+ if (plugins && typeof plugins === "object") {
4576
+ if (Object.hasOwn(plugins, pluginName)) return true;
4577
+ if (Object.keys(plugins).some((k) => k.split("@")[0] === pluginName)) return true;
4578
+ }
4579
+ } catch {
4410
4580
  }
4411
- } catch {
4412
4581
  }
4413
- for (const path of [
4414
- join(homedir(), ".claude", "settings.json"),
4415
- join(homedir(), ".claude.json")
4416
- ]) {
4582
+ for (const path of [getSettingsPath(), getMcpConfigPath()]) {
4417
4583
  try {
4418
4584
  const raw = await readFile(path, "utf8");
4419
4585
  const parsed = JSON.parse(raw);
@@ -4429,6 +4595,7 @@ async function isPluginRegistered(pluginName) {
4429
4595
  }
4430
4596
  var init_readClaudeConfig = __esm({
4431
4597
  "src/installers/lib/readClaudeConfig.ts"() {
4598
+ init_platform();
4432
4599
  }
4433
4600
  });
4434
4601
 
@@ -4827,6 +4994,7 @@ var init_ccHookAdd2 = __esm({
4827
4994
  init_confirm();
4828
4995
  init_diff();
4829
4996
  init_err();
4997
+ init_platform();
4830
4998
  init_preflight();
4831
4999
  init_state2();
4832
5000
  installCcHookAdd = async (ctx) => {
@@ -4850,10 +5018,10 @@ var init_ccHookAdd2 = __esm({
4850
5018
  const e = pre.errors[0] ?? err(ctx, "/", "preflight failed", "preflight");
4851
5019
  return { ok: false, phase: "preflight", error: e };
4852
5020
  }
4853
- const settingsPath3 = join(homedir(), ".claude", "settings.json");
5021
+ const settingsPath = getSettingsPath();
4854
5022
  let existing;
4855
5023
  try {
4856
- existing = await readFile(settingsPath3, "utf8");
5024
+ existing = await readFile(settingsPath, "utf8");
4857
5025
  } catch {
4858
5026
  existing = null;
4859
5027
  }
@@ -4884,7 +5052,7 @@ var init_ccHookAdd2 = __esm({
4884
5052
  const newText = `${JSON.stringify(settings, null, 2)}
4885
5053
  `;
4886
5054
  const plan = {
4887
- files: [{ target: settingsPath3, scope: "HOME", oldText: existing ?? "", newText }]
5055
+ files: [{ target: settingsPath, scope: "HOME", oldText: existing ?? "", newText }]
4888
5056
  };
4889
5057
  if (!ctx.opts.quiet) process.stdout.write(renderDiff(plan, ctx));
4890
5058
  const conf = await confirmAt("L3", { ...ctx});
@@ -4895,10 +5063,10 @@ var init_ccHookAdd2 = __esm({
4895
5063
  if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
4896
5064
  const bk = await backup(plan, ctx);
4897
5065
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
4898
- await writeFile(settingsPath3, newText);
5066
+ await writeFile(settingsPath, newText);
4899
5067
  let verify;
4900
5068
  try {
4901
- verify = JSON.parse(await readFile(settingsPath3, "utf8"));
5069
+ verify = JSON.parse(await readFile(settingsPath, "utf8"));
4902
5070
  } catch (e) {
4903
5071
  return {
4904
5072
  ok: false,
@@ -4926,7 +5094,7 @@ var init_ccHookAdd2 = __esm({
4926
5094
  };
4927
5095
  }
4928
5096
  await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, "", "");
4929
- return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath3] };
5097
+ return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath] };
4930
5098
  };
4931
5099
  }
4932
5100
  });
@@ -4982,10 +5150,10 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
4982
5150
  child.stderr?.setEncoding("utf8").on("data", (chunk) => {
4983
5151
  stderr += chunk;
4984
5152
  });
4985
- return await new Promise((resolve20) => {
5153
+ return await new Promise((resolve18) => {
4986
5154
  const timer = setTimeout(() => {
4987
5155
  child.kill("SIGKILL");
4988
- resolve20({
5156
+ resolve18({
4989
5157
  ok: false,
4990
5158
  phase: "spawn",
4991
5159
  error: {
@@ -5001,7 +5169,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
5001
5169
  child.on("error", (err2) => {
5002
5170
  clearTimeout(timer);
5003
5171
  const bashMissing = triedBash && err2.code === "ENOENT";
5004
- resolve20({
5172
+ resolve18({
5005
5173
  ok: false,
5006
5174
  phase: "spawn",
5007
5175
  error: {
@@ -5016,7 +5184,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs, opts) {
5016
5184
  });
5017
5185
  child.on("close", (code) => {
5018
5186
  clearTimeout(timer);
5019
- resolve20({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
5187
+ resolve18({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
5020
5188
  });
5021
5189
  });
5022
5190
  }
@@ -5056,7 +5224,7 @@ async function detectNative(ctx) {
5056
5224
  const indicators = INSTALLED_INDICATORS[name];
5057
5225
  if (indicators) {
5058
5226
  for (const ind of indicators) {
5059
- const dir = join(homedir(), ".claude", "skills", ind);
5227
+ const dir = join(getSkillsDir(), ind);
5060
5228
  try {
5061
5229
  await access(dir);
5062
5230
  return true;
@@ -5075,8 +5243,8 @@ async function detectNative(ctx) {
5075
5243
  }
5076
5244
  if (method === "npx-skill-installer") {
5077
5245
  const skillName = extractSkillName(cmd, name);
5078
- for (const base of [".claude", ".agents"]) {
5079
- const skillMd = join(homedir(), base, "skills", skillName, "SKILL.md");
5246
+ for (const skillsDir of harnessSkillsDirs()) {
5247
+ const skillMd = join(skillsDir, skillName, "SKILL.md");
5080
5248
  try {
5081
5249
  await access(skillMd);
5082
5250
  return true;
@@ -5095,7 +5263,7 @@ async function detectNative(ctx) {
5095
5263
  }
5096
5264
  }
5097
5265
  if (method === "npm-cli") {
5098
- const skillDir = join(homedir(), ".claude", "skills", name);
5266
+ const skillDir = join(getSkillsDir(), name);
5099
5267
  try {
5100
5268
  await access(skillDir);
5101
5269
  return true;
@@ -5129,6 +5297,7 @@ async function isAlreadyInstalled(ctx, opts = {}) {
5129
5297
  var IDEMPOTENT_CHECK_TIMEOUT_MS, INSTALLED_INDICATORS;
5130
5298
  var init_idempotent = __esm({
5131
5299
  "src/installers/lib/idempotent.ts"() {
5300
+ init_platform();
5132
5301
  init_readClaudeConfig();
5133
5302
  init_spawn();
5134
5303
  IDEMPOTENT_CHECK_TIMEOUT_MS = 1e4;
@@ -5139,7 +5308,7 @@ var init_idempotent = __esm({
5139
5308
  }
5140
5309
  });
5141
5310
  function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
5142
- return new Promise((resolve20) => {
5311
+ return new Promise((resolve18) => {
5143
5312
  const isWin = process.platform === "win32";
5144
5313
  const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
5145
5314
  let stderr = "";
@@ -5148,15 +5317,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
5148
5317
  });
5149
5318
  const timer = setTimeout(() => {
5150
5319
  child.kill("SIGKILL");
5151
- resolve20({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
5320
+ resolve18({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
5152
5321
  }, timeoutMs);
5153
5322
  child.on("error", (e) => {
5154
5323
  clearTimeout(timer);
5155
- resolve20({ exitCode: -1, stderr: `${stderr}${e.message}` });
5324
+ resolve18({ exitCode: -1, stderr: `${stderr}${e.message}` });
5156
5325
  });
5157
5326
  child.on("close", (code) => {
5158
5327
  clearTimeout(timer);
5159
- resolve20({ exitCode: code ?? -1, stderr });
5328
+ resolve18({ exitCode: code ?? -1, stderr });
5160
5329
  });
5161
5330
  });
5162
5331
  }
@@ -5326,7 +5495,7 @@ ${newEntry}
5326
5495
  }
5327
5496
  });
5328
5497
  function gitRevParseHead(cwd, timeoutMs = 1e4) {
5329
- return new Promise((resolve20) => {
5498
+ return new Promise((resolve18) => {
5330
5499
  const isWin = process.platform === "win32";
5331
5500
  const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
5332
5501
  let stdout2 = "";
@@ -5335,15 +5504,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
5335
5504
  });
5336
5505
  const timer = setTimeout(() => {
5337
5506
  child.kill("SIGKILL");
5338
- resolve20({ sha: "", exit: -1 });
5507
+ resolve18({ sha: "", exit: -1 });
5339
5508
  }, timeoutMs);
5340
5509
  child.on("error", () => {
5341
5510
  clearTimeout(timer);
5342
- resolve20({ sha: "", exit: -1 });
5511
+ resolve18({ sha: "", exit: -1 });
5343
5512
  });
5344
5513
  child.on("close", (code) => {
5345
5514
  clearTimeout(timer);
5346
- resolve20({ sha: stdout2.trim(), exit: code ?? -1 });
5515
+ resolve18({ sha: stdout2.trim(), exit: code ?? -1 });
5347
5516
  });
5348
5517
  });
5349
5518
  }
@@ -6284,8 +6453,14 @@ var init_install_base = __esm({
6284
6453
  // src/checkpoint/nextStep.ts
6285
6454
  var nextStep_exports = {};
6286
6455
  __export(nextStep_exports, {
6456
+ resolveAutoTransition: () => resolveAutoTransition,
6287
6457
  resolveNext: () => resolveNext
6288
6458
  });
6459
+ function resolveAutoTransition(envelopeValue, env = process.env.HARNESSED_AUTO_TRANSITION) {
6460
+ if (env === "true") return true;
6461
+ if (env === "false") return false;
6462
+ return envelopeValue ?? true;
6463
+ }
6289
6464
  function resolveNext(ledger, autoTransition) {
6290
6465
  const sub = nextPending(ledger);
6291
6466
  if (sub === null) return { next: "done" };
@@ -6784,73 +6959,9 @@ audited ${yamls.length} manifest${yamls.length === 1 ? "" : "s"} \u2014 ${findin
6784
6959
  process.exit(errorCount > 0 ? 1 : 0);
6785
6960
  });
6786
6961
  }
6787
- var SUPPORTED = /* @__PURE__ */ new Set(["en", "zh-Hans"]);
6788
- var currentLocale = null;
6789
- var cache = {};
6790
- function mapToSupported(raw) {
6791
- if (!raw) return "en";
6792
- if (/^zh([^a-z]|$)/i.test(raw)) return "zh-Hans";
6793
- return "en";
6794
- }
6795
- function detectLocale() {
6796
- const raw = process.env.HARNESSED_LANG || process.env.LC_ALL || process.env.LANG || process.env.LANGUAGE || safeIntlLocale();
6797
- return mapToSupported(raw);
6798
- }
6799
- function safeIntlLocale() {
6800
- try {
6801
- return Intl.DateTimeFormat().resolvedOptions().locale;
6802
- } catch {
6803
- return void 0;
6804
- }
6805
- }
6806
- function messagesDir() {
6807
- const here = dirname(fileURLToPath(import.meta.url));
6808
- const candidates = [resolve(here, "..", "..", "messages"), resolve(here, "..", "messages")];
6809
- for (const c of candidates) {
6810
- try {
6811
- readFileSync(join(c, "en.json"), "utf8");
6812
- return c;
6813
- } catch {
6814
- }
6815
- }
6816
- return resolve(process.cwd(), "messages");
6817
- }
6818
- function loadLocale(locale) {
6819
- if (cache[locale]) return cache[locale];
6820
- const path = join(messagesDir(), `${locale}.json`);
6821
- try {
6822
- const raw = readFileSync(path, "utf8");
6823
- cache[locale] = JSON.parse(raw);
6824
- } catch {
6825
- cache[locale] = {};
6826
- }
6827
- return cache[locale];
6828
- }
6829
- function setLocale(locale) {
6830
- if (!locale) return;
6831
- const mapped = mapToSupported(locale);
6832
- if (SUPPORTED.has(mapped)) currentLocale = mapped;
6833
- }
6834
- function getLocale() {
6835
- if (currentLocale === null) currentLocale = detectLocale();
6836
- return currentLocale;
6837
- }
6838
- function t(key, params) {
6839
- const locale = getLocale();
6840
- const primary = loadLocale(locale);
6841
- let template = primary[key];
6842
- if (template === void 0 && locale !== "en") {
6843
- template = loadLocale("en")[key];
6844
- }
6845
- if (template === void 0) template = key;
6846
- if (!params) return template;
6847
- return template.replace(/\{\{(\w+)\}\}/g, (_match, name) => {
6848
- const v = params[name];
6849
- return v === void 0 ? `{{${name}}}` : String(v);
6850
- });
6851
- }
6852
6962
 
6853
6963
  // src/cli/audit-log.ts
6964
+ init_i18n();
6854
6965
  init_harnessedRoot();
6855
6966
  function auditPath() {
6856
6967
  return harnessedFile("audit.log");
@@ -6882,7 +6993,7 @@ function renderHumanTable(records) {
6882
6993
  }
6883
6994
  }
6884
6995
  function pipeToJq(filterExpr, lines) {
6885
- return new Promise((resolve20, reject) => {
6996
+ return new Promise((resolve18, reject) => {
6886
6997
  const child = spawn("jq", [filterExpr], {
6887
6998
  stdio: ["pipe", "inherit", "inherit"],
6888
6999
  windowsHide: true
@@ -6891,12 +7002,12 @@ function pipeToJq(filterExpr, lines) {
6891
7002
  const e = err2;
6892
7003
  if (e.code === "ENOENT") {
6893
7004
  console.error(t("audit_log.jq_missing"));
6894
- resolve20(1);
7005
+ resolve18(1);
6895
7006
  } else {
6896
7007
  reject(err2);
6897
7008
  }
6898
7009
  });
6899
- child.on("close", (code) => resolve20(code ?? 0));
7010
+ child.on("close", (code) => resolve18(code ?? 0));
6900
7011
  child.stdin.write(lines.join("\n"));
6901
7012
  child.stdin.end();
6902
7013
  });
@@ -6957,6 +7068,9 @@ function registerAuditLog(program2) {
6957
7068
  }
6958
7069
  );
6959
7070
  }
7071
+
7072
+ // src/cli/backup-list.ts
7073
+ init_i18n();
6960
7074
  init_backup();
6961
7075
  function registerBackupList(program2) {
6962
7076
  const backup2 = program2.command("backup").description("Backup snapshot operations");
@@ -7192,6 +7306,7 @@ function registerCompact(program2) {
7192
7306
  }
7193
7307
 
7194
7308
  // src/cli/doctor.ts
7309
+ init_i18n();
7195
7310
  init_doctor_registry();
7196
7311
  function registerDoctor(program2) {
7197
7312
  program2.command("doctor").description(
@@ -7348,6 +7463,9 @@ function fireEntry(clause, master) {
7348
7463
  if (isMaster) entry.is_master = true;
7349
7464
  return entry;
7350
7465
  }
7466
+
7467
+ // src/cli/gc.ts
7468
+ init_i18n();
7351
7469
  init_backup();
7352
7470
  var DURATION_RE = /^(\d+)([dhmw])$/;
7353
7471
  function parseDuration(s) {
@@ -7438,6 +7556,7 @@ ${t("gc.invalid_duration.fix")}`
7438
7556
 
7439
7557
  // src/cli/install.ts
7440
7558
  init_package();
7559
+ init_i18n();
7441
7560
  init_installers();
7442
7561
  init_path_guard();
7443
7562
  init_validate();
@@ -7458,24 +7577,26 @@ function registerInstall(program2) {
7458
7577
  const { resolveAlias: resolveAlias2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
7459
7578
  const resolvedName = resolveAlias2(name) ?? name;
7460
7579
  checkPathSafe(resolvedName);
7461
- const manifestPath = resolve(getPackageRoot(), `manifests/tools/${resolvedName}.yaml`);
7462
- const skillPackPath = resolve(getPackageRoot(), `manifests/skill-packs/${resolvedName}.yaml`);
7580
+ const candidatePaths = ["tools", "skill-packs", "optional"].map(
7581
+ (dir) => resolve(getPackageRoot(), `manifests/${dir}/${resolvedName}.yaml`)
7582
+ );
7463
7583
  let yamlSrc;
7464
- let chosenPath = manifestPath;
7465
- try {
7466
- yamlSrc = await readFile(manifestPath, "utf8");
7467
- } catch {
7584
+ let chosenPath;
7585
+ for (const p5 of candidatePaths) {
7468
7586
  try {
7469
- yamlSrc = await readFile(skillPackPath, "utf8");
7470
- chosenPath = skillPackPath;
7587
+ yamlSrc = await readFile(p5, "utf8");
7588
+ chosenPath = p5;
7589
+ break;
7471
7590
  } 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
7591
  }
7478
7592
  }
7593
+ if (yamlSrc === void 0 || chosenPath === void 0) {
7594
+ console.error(
7595
+ `${t("install.manifest_not_found", { name: resolvedName })}
7596
+ ${t("install.manifest_not_found.fix", { name: resolvedName })}`
7597
+ );
7598
+ process.exit(1);
7599
+ }
7479
7600
  const v = validateManifestFile(yamlSrc, chosenPath);
7480
7601
  if (!v.ok) {
7481
7602
  for (const e of v.errors) console.error(`error: ${e.message} at ${e.path}`);
@@ -7535,74 +7656,248 @@ function registerLearn(program2) {
7535
7656
  process.exit(0);
7536
7657
  });
7537
7658
  }
7538
- var QA = [
7539
- { q: "\u2460 \u662F\u771F reusable surface \u8FD8\u662F\u4E34\u65F6 wrapper?", f: "q1_reusable_surface" },
7540
- { q: "\u2461 \u4E0A\u6E38\u540D\u5B57 fit \u9879\u76EE shape \u5417? \u6709\u73B0\u6709\u547D\u540D\u51B2\u7A81\u5417?", f: "q2_name_fit" },
7541
- { q: "\u2462 \u4E0E\u5DF2\u88C5\u914D\u7EC4\u4EF6\u6709 overlap surface \u5417?", f: "q3_overlap" },
7542
- { q: "\u2463 \u662F import \u6982\u5FF5 (\u53EF\u63A7) \u8FD8\u662F import \u522B\u4EBA\u4EA7\u54C1\u8EAB\u4EFD (\u9AD8\u8026\u5408)?", f: "q4_concept_vs_identity" },
7543
- { q: "\u2464 user \u4E0D\u77E5 upstream \u8FD8\u80FD\u7406\u89E3\u8BE5\u88C5\u914D\u5417?", f: "q5_user_understanding" }
7544
- ];
7545
- function basename(upstream) {
7546
- return (upstream.split("/").pop() ?? upstream).replace(/\.git$/, "");
7547
- }
7548
- function registerManifestAdd(program2) {
7549
- program2.command("manifest-add <upstream>").description(
7550
- "Add a new upstream adapter (EE-5 5-question merge gate; immediate by default \u2014 use --dry-run for preview)"
7551
- ).option("--category <cat>", "manifest category (skill-packs | tools)", "skill-packs").option("--name <name>", "short adapter name (defaults to <upstream> basename)").option("--dry-run", "preview only \u2014 do not write JSON (opt-in for advanced users)").option("--non-interactive", "CI/scripts \u2014 WARN-only dry-run").action(async (upstream, raw) => {
7552
- const name = raw.name ?? basename(upstream);
7553
- const category = raw.category ?? "skill-packs";
7554
- const outPath = `manifests/${category}/${name}.ee5-answers.json`;
7555
- if (raw.nonInteractive) {
7556
- console.warn(t("manifest_add.non_interactive_warn"));
7557
- console.log(t("manifest_add.dry_run_preview", { upstream, path: outPath }));
7558
- process.exit(0);
7559
- }
7560
- const rl = readline.createInterface({ input: stdin, output: stdout });
7561
- const payload = {
7562
- upstream,
7563
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
7564
- author: process.env.USER ?? process.env.USERNAME ?? "unknown"
7565
- };
7566
- for (const { q, f } of QA) {
7567
- const a = (await rl.question(`${q}
7568
- > `)).trim();
7569
- if (!a) {
7570
- console.error(t("manifest_add.empty_answer"));
7571
- rl.close();
7572
- process.exit(1);
7573
- }
7574
- payload[f] = a;
7575
- }
7576
- rl.close();
7577
- const dryRun = raw.dryRun === true;
7578
- if (!dryRun) {
7579
- writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}
7580
- `, "utf8");
7581
- console.log(t("manifest_add.gate_passed_wrote", { path: outPath }));
7582
- } else {
7583
- console.log(t("manifest_add.gate_passed_dry_run", { path: outPath }));
7584
- console.log(JSON.stringify(payload, null, 2));
7585
- }
7586
- process.exit(0);
7587
- });
7588
- }
7589
7659
 
7590
- // 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
- function registerNext(program2) {
7598
- program2.command("next").description(
7599
- "Print the deterministic next-step contract (NEXT: auto|manual|done) for the active workflow"
7600
- ).action(async () => {
7601
- const { readCurrentWorkflow: readCurrentWorkflow2 } = await Promise.resolve().then(() => (init_state(), state_exports));
7602
- const { resolveNext: resolveNext2 } = await Promise.resolve().then(() => (init_nextStep(), nextStep_exports));
7603
- const current = await readCurrentWorkflow2();
7604
- const ledger = current?.sub_progress ?? [];
7605
- const auto = resolveAutoTransition(current?.auto_transition);
7660
+ // src/cli/lib/here.ts
7661
+ init_nextStep();
7662
+ init_i18n();
7663
+ init_harnessedRoot();
7664
+
7665
+ // src/cli/status.ts
7666
+ init_evidence();
7667
+ init_ledger();
7668
+ init_state();
7669
+ init_i18n();
7670
+ init_harnessedRoot();
7671
+ init_state2();
7672
+ function statusMarker(status) {
7673
+ switch (status) {
7674
+ case "done":
7675
+ return "\u2705 done";
7676
+ case "pending":
7677
+ return "\u23F3 pending";
7678
+ case "failed":
7679
+ return "\u2717 failed";
7680
+ case "skipped":
7681
+ return "\u2B1C skipped";
7682
+ case "rejected":
7683
+ return "\u{1F6AB} rejected";
7684
+ }
7685
+ }
7686
+ function evidenceLabel(entry) {
7687
+ switch (entry.evidence_status) {
7688
+ case "verified":
7689
+ return "evidence: verified";
7690
+ case "overridden":
7691
+ return "evidence: overridden (--force)";
7692
+ default:
7693
+ return "evidence: none_declared";
7694
+ }
7695
+ }
7696
+ async function buildRecoverLines(workflow, ledger) {
7697
+ const lines = [];
7698
+ if (!workflow || ledger.length === 0) {
7699
+ lines.push("no ledger \u2014 run gates + start");
7700
+ return lines;
7701
+ }
7702
+ lines.push(`workflow: ${workflow.phase} (${workflow.status}) started ${workflow.started_at}`);
7703
+ const next = nextPending(ledger);
7704
+ for (const e of ledger) {
7705
+ const marker = statusMarker(e.status);
7706
+ let suffix = "";
7707
+ if (e.status === "skipped") {
7708
+ suffix = e.reason ? ` (skipped: ${e.reason})` : "";
7709
+ } else if (e.status === "done") {
7710
+ suffix = ` ${evidenceLabel(e)}`;
7711
+ }
7712
+ const arrow = e.sub === next ? " \u2190 next" : "";
7713
+ lines.push(` ${e.sub} ${marker}${suffix}${arrow}`);
7714
+ }
7715
+ if (next) {
7716
+ lines.push(`\u2192 next: harnessed prompt ${next}`);
7717
+ } else {
7718
+ lines.push("\u2192 all subs resolved (no pending work)");
7719
+ }
7720
+ for (const e of ledger) {
7721
+ if (!e.evidence || e.evidence.length === 0) continue;
7722
+ const drift = await detectDrift(e.evidence);
7723
+ for (const d of drift) {
7724
+ const now = d.now === "" ? "missing" : `${d.now.slice(0, 7)}\u2026`;
7725
+ lines.push(`\u26A0 drift: ${d.path} sha256 changed (was ${d.was.slice(0, 7)}\u2026, now ${now})`);
7726
+ }
7727
+ }
7728
+ return lines;
7729
+ }
7730
+ function registerStatus(program2) {
7731
+ 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) => {
7732
+ if (opts.recover) {
7733
+ const wf = await readCurrentWorkflow();
7734
+ const ledger = wf?.sub_progress ?? [];
7735
+ const lines = await buildRecoverLines(wf, ledger);
7736
+ for (const l of lines) console.log(l);
7737
+ return;
7738
+ }
7739
+ const state = await readState(process.cwd());
7740
+ const names = Object.keys(state.installed).sort();
7741
+ if (names.length === 0) {
7742
+ console.log(t("status.no_installs", { path: harnessedFile("state.json") }));
7743
+ } else {
7744
+ for (const n of names) {
7745
+ const e = state.installed[n];
7746
+ if (!e) continue;
7747
+ console.log(`${n} @ ${e.version} (installed ${e.installedAt})`);
7748
+ }
7749
+ console.log(t("status.summary_installs", { count: names.length }));
7750
+ }
7751
+ const lockPath = harnessedFile(".lock");
7752
+ try {
7753
+ const isLocked = await lockfile.check(getHarnessedRoot(), {
7754
+ lockfilePath: lockPath,
7755
+ stale: 1e4
7756
+ });
7757
+ if (isLocked) {
7758
+ const s = await stat(lockPath);
7759
+ const ageMs = Date.now() - s.mtime.getTime();
7760
+ const stale = ageMs > 1e4;
7761
+ console.log(
7762
+ t("status.lock_held", {
7763
+ since: s.mtime.toISOString(),
7764
+ staleSuffix: stale ? t("status.lock_held.stale_suffix") : ""
7765
+ })
7766
+ );
7767
+ console.log(t("status.lock_release_hint", { path: lockPath }));
7768
+ } else {
7769
+ console.log(t("status.lock_free"));
7770
+ }
7771
+ } catch {
7772
+ }
7773
+ });
7774
+ }
7775
+
7776
+ // src/cli/lib/here.ts
7777
+ function parseBareInvocation(argvRest) {
7778
+ const tokens = [];
7779
+ for (let i = 0; i < argvRest.length; i++) {
7780
+ const a = argvRest[i];
7781
+ if (a === "--lang") {
7782
+ i++;
7783
+ continue;
7784
+ }
7785
+ if (a?.startsWith("--lang=")) continue;
7786
+ if (a !== void 0) tokens.push(a);
7787
+ }
7788
+ if (tokens.length === 0) return { here: true, json: false };
7789
+ if (tokens.length === 1 && tokens[0] === "--json") return { here: true, json: true };
7790
+ return { here: false, json: false };
7791
+ }
7792
+ async function buildHereView(wf, ledger, auto) {
7793
+ if (!wf) {
7794
+ return {
7795
+ lines: [
7796
+ t("here.no_workflow", { path: harnessedFile("workflows.json") }),
7797
+ t("here.no_workflow.hint")
7798
+ ],
7799
+ json: { active: false }
7800
+ };
7801
+ }
7802
+ const body = await buildRecoverLines(wf, ledger);
7803
+ const step = resolveNext(ledger, auto);
7804
+ const lines = [
7805
+ ...body,
7806
+ `NEXT: ${step.next}`,
7807
+ ...step.sub ? [`\u2192 run: harnessed prompt ${step.sub}`] : []
7808
+ ];
7809
+ return {
7810
+ lines,
7811
+ json: {
7812
+ active: true,
7813
+ phase: wf.phase,
7814
+ status: wf.status,
7815
+ started_at: wf.started_at,
7816
+ next: step.next,
7817
+ sub: step.sub,
7818
+ hint: step.hint,
7819
+ sub_progress: ledger
7820
+ }
7821
+ };
7822
+ }
7823
+ async function runHere(opts) {
7824
+ const { readCurrentWorkflow: readCurrentWorkflow2 } = await Promise.resolve().then(() => (init_state(), state_exports));
7825
+ const wf = await readCurrentWorkflow2();
7826
+ const ledger = wf?.sub_progress ?? [];
7827
+ const auto = resolveAutoTransition(wf?.auto_transition);
7828
+ const view = await buildHereView(wf, ledger, auto);
7829
+ if (opts.json) {
7830
+ console.log(JSON.stringify(view.json, null, 2));
7831
+ } else {
7832
+ for (const l of view.lines) console.log(l);
7833
+ }
7834
+ process.exit(0);
7835
+ }
7836
+
7837
+ // src/cli/manifest-add.ts
7838
+ init_i18n();
7839
+ var QA = [
7840
+ { q: "\u2460 \u662F\u771F reusable surface \u8FD8\u662F\u4E34\u65F6 wrapper?", f: "q1_reusable_surface" },
7841
+ { q: "\u2461 \u4E0A\u6E38\u540D\u5B57 fit \u9879\u76EE shape \u5417? \u6709\u73B0\u6709\u547D\u540D\u51B2\u7A81\u5417?", f: "q2_name_fit" },
7842
+ { q: "\u2462 \u4E0E\u5DF2\u88C5\u914D\u7EC4\u4EF6\u6709 overlap surface \u5417?", f: "q3_overlap" },
7843
+ { q: "\u2463 \u662F import \u6982\u5FF5 (\u53EF\u63A7) \u8FD8\u662F import \u522B\u4EBA\u4EA7\u54C1\u8EAB\u4EFD (\u9AD8\u8026\u5408)?", f: "q4_concept_vs_identity" },
7844
+ { q: "\u2464 user \u4E0D\u77E5 upstream \u8FD8\u80FD\u7406\u89E3\u8BE5\u88C5\u914D\u5417?", f: "q5_user_understanding" }
7845
+ ];
7846
+ function basename(upstream) {
7847
+ return (upstream.split("/").pop() ?? upstream).replace(/\.git$/, "");
7848
+ }
7849
+ function registerManifestAdd(program2) {
7850
+ program2.command("manifest-add <upstream>").description(
7851
+ "Add a new upstream adapter (EE-5 5-question merge gate; immediate by default \u2014 use --dry-run for preview)"
7852
+ ).option("--category <cat>", "manifest category (skill-packs | tools)", "skill-packs").option("--name <name>", "short adapter name (defaults to <upstream> basename)").option("--dry-run", "preview only \u2014 do not write JSON (opt-in for advanced users)").option("--non-interactive", "CI/scripts \u2014 WARN-only dry-run").action(async (upstream, raw) => {
7853
+ const name = raw.name ?? basename(upstream);
7854
+ const category = raw.category ?? "skill-packs";
7855
+ const outPath = `manifests/${category}/${name}.ee5-answers.json`;
7856
+ if (raw.nonInteractive) {
7857
+ console.warn(t("manifest_add.non_interactive_warn"));
7858
+ console.log(t("manifest_add.dry_run_preview", { upstream, path: outPath }));
7859
+ process.exit(0);
7860
+ }
7861
+ const rl = readline.createInterface({ input: stdin, output: stdout });
7862
+ const payload = {
7863
+ upstream,
7864
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
7865
+ author: process.env.USER ?? process.env.USERNAME ?? "unknown"
7866
+ };
7867
+ for (const { q, f } of QA) {
7868
+ const a = (await rl.question(`${q}
7869
+ > `)).trim();
7870
+ if (!a) {
7871
+ console.error(t("manifest_add.empty_answer"));
7872
+ rl.close();
7873
+ process.exit(1);
7874
+ }
7875
+ payload[f] = a;
7876
+ }
7877
+ rl.close();
7878
+ const dryRun = raw.dryRun === true;
7879
+ if (!dryRun) {
7880
+ writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}
7881
+ `, "utf8");
7882
+ console.log(t("manifest_add.gate_passed_wrote", { path: outPath }));
7883
+ } else {
7884
+ console.log(t("manifest_add.gate_passed_dry_run", { path: outPath }));
7885
+ console.log(JSON.stringify(payload, null, 2));
7886
+ }
7887
+ process.exit(0);
7888
+ });
7889
+ }
7890
+
7891
+ // src/cli/next.ts
7892
+ function registerNext(program2) {
7893
+ program2.command("next").description(
7894
+ "Print the deterministic next-step contract (NEXT: auto|manual|done) for the active workflow"
7895
+ ).action(async () => {
7896
+ const { readCurrentWorkflow: readCurrentWorkflow2 } = await Promise.resolve().then(() => (init_state(), state_exports));
7897
+ const { resolveNext: resolveNext2, resolveAutoTransition: resolveAutoTransition2 } = await Promise.resolve().then(() => (init_nextStep(), nextStep_exports));
7898
+ const current = await readCurrentWorkflow2();
7899
+ const ledger = current?.sub_progress ?? [];
7900
+ const auto = resolveAutoTransition2(current?.auto_transition);
7606
7901
  const step = resolveNext2(ledger, auto);
7607
7902
  console.log(`NEXT: ${step.next}`);
7608
7903
  if (step.sub) console.log(`SUB: ${step.sub}`);
@@ -7612,6 +7907,8 @@ function registerNext(program2) {
7612
7907
  }
7613
7908
 
7614
7909
  // src/cli/prompt.ts
7910
+ init_i18n();
7911
+ init_localeYaml();
7615
7912
  init_run();
7616
7913
  init_generateCommands();
7617
7914
  init_packagePath();
@@ -7657,16 +7954,17 @@ ${lines.join("\n")}
7657
7954
  return "";
7658
7955
  }
7659
7956
  }
7660
- async function buildDisciplinesSection(sub, packageRoot) {
7957
+ async function buildDisciplinesSection(sub, packageRoot, locale = getLocale()) {
7661
7958
  try {
7662
7959
  const workflowsDir = resolve(packageRoot, "workflows");
7960
+ const disciplinesDir = resolve(workflowsDir, "disciplines");
7663
7961
  const applied = await loadSubArrayField(sub, packageRoot, "disciplines_applied");
7664
7962
  const names = applied.filter((d) => d !== "language");
7665
7963
  if (names.length === 0) return "";
7666
7964
  const blocks = [];
7667
7965
  for (const name of names) {
7668
7966
  try {
7669
- const dRaw = await readFile(resolve(workflowsDir, "disciplines", `${name}.yaml`), "utf8");
7967
+ const dRaw = await readFile(resolveLocaleYaml(disciplinesDir, name, locale), "utf8");
7670
7968
  const dDoc = parse(dRaw);
7671
7969
  const rules = Array.isArray(dDoc?.rules) ? dDoc.rules : [];
7672
7970
  const descs = rules.map((r) => typeof r.description === "string" ? r.description.trim() : "").filter((s) => s.length > 0).map((s) => ` - ${s.replace(/\s+/g, " ")}`);
@@ -7812,6 +8110,9 @@ function registerReleasePreflight(program2) {
7812
8110
  process.exit(failed ? 1 : 0);
7813
8111
  });
7814
8112
  }
8113
+
8114
+ // src/cli/research.ts
8115
+ init_i18n();
7815
8116
  init_run();
7816
8117
  init_packagePath();
7817
8118
  init_run2();
@@ -7853,6 +8154,7 @@ function registerResearch(program2) {
7853
8154
  }
7854
8155
 
7855
8156
  // src/cli/resume.ts
8157
+ init_i18n();
7856
8158
  function registerResume(program2) {
7857
8159
  program2.command("resume").description(
7858
8160
  "Reload checkpoint from paused workflow + print resume hint (D-03 \u2014 user invokes phase command manually)"
@@ -7917,6 +8219,9 @@ function registerRetro(program2) {
7917
8219
  process.exit(0);
7918
8220
  });
7919
8221
  }
8222
+
8223
+ // src/cli/rollback.ts
8224
+ init_i18n();
7920
8225
  init_backup();
7921
8226
  function normalizeEol(buf, eol) {
7922
8227
  const lf = buf.toString("utf8").replace(/\r\n/g, "\n");
@@ -7988,9 +8293,16 @@ ${t("rollback.metadata_unreadable.fix")}`
7988
8293
 
7989
8294
  // src/cli.ts
7990
8295
  init_run2();
8296
+
8297
+ // src/cli/setup.ts
8298
+ init_i18n();
8299
+ init_platform();
8300
+
8301
+ // src/cli/lib/capabilityResolver.ts
8302
+ init_platform();
7991
8303
  function readInstalledPlugins(homedirOverride) {
7992
- const home = homedir();
7993
- const path = join(home, ".claude", "plugins", "installed_plugins.json");
8304
+ const path = getPluginsRegistry(homedirOverride);
8305
+ if (path === null) return /* @__PURE__ */ new Set();
7994
8306
  let raw;
7995
8307
  try {
7996
8308
  raw = readFileSync(path, "utf8");
@@ -8015,8 +8327,7 @@ function readInstalledPlugins(homedirOverride) {
8015
8327
  return out;
8016
8328
  }
8017
8329
  function readInstalledUserSkills(homedirOverride) {
8018
- const home = homedir();
8019
- const skillsRoot = join(home, ".claude", "skills");
8330
+ const skillsRoot = getSkillsDir(homedirOverride);
8020
8331
  try {
8021
8332
  const entries = readdirSync(skillsRoot, { withFileTypes: true });
8022
8333
  const out = /* @__PURE__ */ new Set();
@@ -8093,55 +8404,57 @@ function renderSkillBody(body, capabilities, installedPlugins, installedUserSkil
8093
8404
  }
8094
8405
 
8095
8406
  // src/cli/lib/enableAgentTeamsInSettings.ts
8407
+ init_platform();
8408
+
8409
+ // src/cli/lib/settingsWriter.ts
8096
8410
  init_harnessedRoot();
8097
- function settingsPath() {
8098
- return resolve(homedir(), ".claude", "settings.json");
8099
- }
8100
- async function enableAgentTeamsInSettings() {
8101
- const path = settingsPath();
8411
+ init_platform();
8412
+ async function mergeSettingsEnvKey(key, value, opts = {}) {
8413
+ const path = getSettingsPath();
8102
8414
  let raw;
8103
8415
  try {
8104
8416
  raw = await readFile(path, "utf8");
8105
8417
  } catch (err2) {
8106
8418
  const code = err2.code;
8107
8419
  if (code !== "ENOENT") {
8108
- return { status: "warn", message: `read ${path} failed: ${err2.message}` };
8420
+ return { outcome: "warn", message: `read ${path} failed: ${err2.message}` };
8109
8421
  }
8110
- return createFreshSettings(path);
8422
+ return createFresh(path, key, value);
8111
8423
  }
8112
8424
  let data;
8113
8425
  try {
8114
8426
  const parsed = JSON.parse(raw);
8115
8427
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
8116
- return { status: "warn", message: `${path} is not a JSON object` };
8428
+ return { outcome: "warn", message: `${path} is not a JSON object` };
8117
8429
  }
8118
8430
  data = parsed;
8119
8431
  } catch (err2) {
8120
- return { status: "warn", message: `${path} malformed JSON: ${err2.message}` };
8432
+ return { outcome: "warn", message: `${path} malformed JSON: ${err2.message}` };
8121
8433
  }
8122
8434
  const env = data.env ?? {};
8123
- if (env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1") {
8124
- return { status: "already-enabled", path };
8435
+ const existing = env[key];
8436
+ if (typeof existing === "string" && existing.length > 0 && opts.skipIfPresent?.(existing) === true) {
8437
+ return { outcome: "already", path, existing };
8125
8438
  }
8126
- const backupPath = await backupOriginal(raw);
8127
- if (backupPath.status === "warn") return backupPath;
8128
- data.env = { ...env, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" };
8439
+ const backup2 = await backupOriginal(raw);
8440
+ if (backup2.status === "warn") return { outcome: "warn", message: backup2.message };
8441
+ data.env = { ...env, [key]: value };
8129
8442
  const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
8130
8443
  `);
8131
- if (writeErr) return { status: "warn", message: writeErr };
8132
- return { status: "enabled", path, backupPath: backupPath.path };
8444
+ if (writeErr) return { outcome: "warn", message: writeErr };
8445
+ return { outcome: "merged", path, backupPath: backup2.path };
8133
8446
  }
8134
- async function createFreshSettings(path) {
8135
- const data = { env: { CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" } };
8447
+ async function createFresh(path, key, value) {
8448
+ const data = { env: { [key]: value } };
8136
8449
  try {
8137
- await mkdir(join(homedir(), ".claude"), { recursive: true });
8450
+ await mkdir(dirname(path), { recursive: true });
8138
8451
  } catch (err2) {
8139
- return { status: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
8452
+ return { outcome: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
8140
8453
  }
8141
8454
  const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
8142
8455
  `);
8143
- if (writeErr) return { status: "warn", message: writeErr };
8144
- return { status: "created", path };
8456
+ if (writeErr) return { outcome: "warn", message: writeErr };
8457
+ return { outcome: "created", path };
8145
8458
  }
8146
8459
  async function backupOriginal(raw) {
8147
8460
  const backupRoot = harnessedSubdir("backups");
@@ -8166,11 +8479,34 @@ async function atomicWrite(path, content) {
8166
8479
  }
8167
8480
  }
8168
8481
 
8169
- // src/cli/lib/enableUserLangInSettings.ts
8170
- init_harnessedRoot();
8171
- function settingsPath2() {
8172
- return resolve(homedir(), ".claude", "settings.json");
8482
+ // src/cli/lib/enableAgentTeamsInSettings.ts
8483
+ var ENV_KEY = "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS";
8484
+ async function enableAgentTeamsInSettings() {
8485
+ const platform = detectPlatform();
8486
+ if (!platform.supportsEnvKeyWrite) {
8487
+ return {
8488
+ status: "warn",
8489
+ message: `platform '${platform.id}' does not support env-key settings writes (capability-absent) \u2014 ${ENV_KEY} skipped`
8490
+ };
8491
+ }
8492
+ const r = await mergeSettingsEnvKey(ENV_KEY, "1", {
8493
+ skipIfPresent: (existing) => existing === "1"
8494
+ });
8495
+ switch (r.outcome) {
8496
+ case "created":
8497
+ return { status: "created", path: r.path };
8498
+ case "already":
8499
+ return { status: "already-enabled", path: r.path };
8500
+ case "merged":
8501
+ return { status: "enabled", path: r.path, backupPath: r.backupPath };
8502
+ case "warn":
8503
+ return { status: "warn", message: r.message };
8504
+ }
8173
8505
  }
8506
+
8507
+ // src/cli/lib/enableUserLangInSettings.ts
8508
+ init_platform();
8509
+ var ENV_KEY2 = "HARNESSED_USER_LANG";
8174
8510
  function detectUserLang(override) {
8175
8511
  if (override) {
8176
8512
  if (/^zh([^a-z]|$)/i.test(override)) return "zh-Hans";
@@ -8188,115 +8524,104 @@ function safeIntlLocale2() {
8188
8524
  }
8189
8525
  }
8190
8526
  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}` };
8527
+ const platform = detectPlatform();
8528
+ if (!platform.supportsEnvKeyWrite) {
8529
+ return {
8530
+ status: "warn",
8531
+ message: `platform '${platform.id}' does not support env-key settings writes (capability-absent) \u2014 ${ENV_KEY2} skipped`
8532
+ };
8248
8533
  }
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}`;
8534
+ const detected = detectUserLang(override);
8535
+ const r = await mergeSettingsEnvKey(ENV_KEY2, detected, {
8536
+ skipIfPresent: () => override === void 0
8537
+ });
8538
+ switch (r.outcome) {
8539
+ case "created":
8540
+ return { status: "created", path: r.path, detected };
8541
+ case "already":
8542
+ return { status: "already-set", path: r.path, existing: r.existing };
8543
+ case "merged":
8544
+ return { status: "enabled", path: r.path, backupPath: r.backupPath, detected };
8545
+ case "warn":
8546
+ return { status: "warn", message: r.message };
8258
8547
  }
8259
8548
  }
8260
8549
 
8261
8550
  // src/cli/setup.ts
8262
8551
  init_generateCommands();
8263
8552
  init_packagePath();
8553
+
8554
+ // src/cli/lib/renderSkillTemplates.ts
8555
+ init_i18n();
8556
+
8557
+ // src/cli/lib/resolveSkillBody.ts
8558
+ init_i18n();
8559
+ function skillBodyFilename(locale) {
8560
+ return locale === "en" ? "SKILL.md" : `SKILL.${locale}.md`;
8561
+ }
8562
+ function resolveSkillBodyFilename(skillDir, locale) {
8563
+ const resolved = locale ?? getLocale();
8564
+ const candidate = skillBodyFilename(resolved);
8565
+ if (candidate === "SKILL.md") return "SKILL.md";
8566
+ return existsSync(join(skillDir, candidate)) ? candidate : "SKILL.md";
8567
+ }
8568
+
8569
+ // src/cli/lib/renderSkillTemplates.ts
8570
+ var LOCALE_SIBLINGS = ["zh-Hans"];
8264
8571
  async function loadCapabilities(workflowsDir) {
8265
8572
  const path = join(workflowsDir, "capabilities.yaml");
8266
8573
  const raw = await readFile(path, "utf8");
8267
8574
  const doc = parse(raw);
8268
8575
  return doc?.capabilities ?? {};
8269
8576
  }
8270
- async function renderSkillFile(skillName, skillsBase, capabilities, installedPlugins, installedUserSkills) {
8271
- const skillPath = join(skillsBase, skillName, "SKILL.md");
8577
+ async function renderSkillFile(skillName, skillsBase, capabilities, installedPlugins, installedUserSkills, locale) {
8578
+ const dir = join(skillsBase, skillName);
8579
+ const resolved = locale ?? getLocale();
8580
+ const srcName = resolveSkillBodyFilename(dir, resolved);
8581
+ const srcPath = join(dir, srcName);
8582
+ const destPath = join(dir, "SKILL.md");
8272
8583
  const result = {
8273
8584
  name: skillName,
8274
- skillPath,
8585
+ skillPath: destPath,
8275
8586
  rendered: false,
8276
8587
  warnings: []
8277
8588
  };
8278
8589
  let body;
8279
8590
  try {
8280
- body = await readFile(skillPath, "utf8");
8591
+ body = await readFile(srcPath, "utf8");
8281
8592
  } catch (e) {
8282
8593
  result.error = `read failed: ${e.message}`;
8283
8594
  return result;
8284
8595
  }
8285
8596
  const rendered = renderSkillBody(body, capabilities, installedPlugins, installedUserSkills);
8286
- if (rendered.body === body) {
8597
+ const localeBodySelected = srcName !== "SKILL.md";
8598
+ const needsWrite = localeBodySelected || rendered.body !== body;
8599
+ if (!needsWrite) {
8287
8600
  result.warnings = rendered.warnings;
8288
8601
  return result;
8289
8602
  }
8290
8603
  try {
8291
- await writeFile(skillPath, rendered.body, "utf8");
8292
- result.rendered = true;
8604
+ await writeFile(destPath, rendered.body, "utf8");
8605
+ result.rendered = rendered.body !== body;
8293
8606
  result.warnings = rendered.warnings;
8294
8607
  } catch (e) {
8295
8608
  result.error = `write failed: ${e.message}`;
8609
+ return result;
8610
+ }
8611
+ if (localeBodySelected) {
8612
+ for (const loc of LOCALE_SIBLINGS) {
8613
+ const sibling = skillBodyFilename(loc);
8614
+ if (sibling === "SKILL.md") continue;
8615
+ try {
8616
+ await rm(join(dir, sibling), { force: true });
8617
+ } catch {
8618
+ }
8619
+ }
8296
8620
  }
8297
8621
  return result;
8298
8622
  }
8299
- async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOverride) {
8623
+ async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOverride, locale) {
8624
+ const resolvedLocale = locale ?? getLocale();
8300
8625
  let capabilities = {};
8301
8626
  try {
8302
8627
  capabilities = await loadCapabilities(workflowsDir);
@@ -8314,8 +8639,8 @@ async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOver
8314
8639
  ]
8315
8640
  };
8316
8641
  }
8317
- const installedPlugins = readInstalledPlugins();
8318
- const installedUserSkills = readInstalledUserSkills();
8642
+ const installedPlugins = readInstalledPlugins(homedirOverride);
8643
+ const installedUserSkills = readInstalledUserSkills(homedirOverride);
8319
8644
  const results = [];
8320
8645
  const warningSet = /* @__PURE__ */ new Set();
8321
8646
  for (const name of skillNames) {
@@ -8324,7 +8649,8 @@ async function renderAllSkills(skillNames, skillsBase, workflowsDir, homedirOver
8324
8649
  skillsBase,
8325
8650
  capabilities,
8326
8651
  installedPlugins,
8327
- installedUserSkills
8652
+ installedUserSkills,
8653
+ resolvedLocale
8328
8654
  );
8329
8655
  results.push(r);
8330
8656
  for (const w of r.warnings) warningSet.add(w);
@@ -8478,6 +8804,23 @@ async function runStepBInstall(manifestPaths, runOpts = {}) {
8478
8804
  }
8479
8805
 
8480
8806
  // src/cli/setup.ts
8807
+ var KNOWN_PLATFORMS = ["claude", "codex"];
8808
+ async function applyPlatformOption(platform) {
8809
+ if (!KNOWN_PLATFORMS.includes(platform)) {
8810
+ console.error(
8811
+ `--platform: unknown id '${platform}' (expected one of: ${KNOWN_PLATFORMS.join(" | ")})`
8812
+ );
8813
+ process.exit(1);
8814
+ }
8815
+ process.env.HARNESSED_PLATFORM = platform;
8816
+ const stateRoot = detectPlatform().stateRoot;
8817
+ try {
8818
+ await mkdir(stateRoot, { recursive: true });
8819
+ await writeFile(join(stateRoot, ".platform"), platform, "utf8");
8820
+ } catch (e) {
8821
+ console.warn(` [--platform] could not persist .platform pin (${e.message})`);
8822
+ }
8823
+ }
8481
8824
  async function listBaseManifests2(pkgRoot) {
8482
8825
  const out = [];
8483
8826
  for (const d of ["manifests/tools", "manifests/skill-packs"]) {
@@ -8530,14 +8873,15 @@ function registerSetup(program2) {
8530
8873
  ).option("--dry-run", "preview only \u2014 do not write to disk (opt-in for advanced users)").option(
8531
8874
  "--user-lang <code>",
8532
8875
  "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(
8876
+ ).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
8877
  "--update-installed",
8535
8878
  "force re-install already-installed plugins (excludes MCP servers); default: skip if installed"
8536
8879
  ).action(async (raw) => {
8537
8880
  const dryRun = raw.dryRun === true;
8881
+ if (raw.platform !== void 0) await applyPlatformOption(raw.platform);
8538
8882
  const pkgRoot = getPackageRoot();
8539
8883
  const workflowsDir = resolve(pkgRoot, "workflows");
8540
- const skillsBase = resolve(homedir(), ".claude", "skills");
8884
+ const skillsBase = getSkillsDir();
8541
8885
  await warnIfAgentTeamsMissing();
8542
8886
  let entries;
8543
8887
  try {
@@ -8574,14 +8918,20 @@ function registerSetup(program2) {
8574
8918
  }
8575
8919
  console.log(t("setup.step_a_complete", { count: skillsInstalled, path: skillsBase }));
8576
8920
  const skillNames = toInstall.map((wf) => wf.name);
8577
- const rendered = await renderAllSkills(skillNames, skillsBase, workflowsDir);
8921
+ const rendered = await renderAllSkills(
8922
+ skillNames,
8923
+ skillsBase,
8924
+ workflowsDir,
8925
+ void 0,
8926
+ getLocale()
8927
+ );
8578
8928
  if (rendered.aggregatedWarnings.length > 0) {
8579
8929
  console.warn(t("setup.step_a_render.warnings_header"));
8580
8930
  for (const w of rendered.aggregatedWarnings) {
8581
8931
  console.warn(` - ${w}`);
8582
8932
  }
8583
8933
  }
8584
- const commandsBase = resolve(homedir(), ".claude", "commands");
8934
+ const commandsBase = getCommandsDir();
8585
8935
  try {
8586
8936
  await mkdir(commandsBase, { recursive: true });
8587
8937
  } catch (e) {
@@ -8677,115 +9027,8 @@ Force-update pass complete: ${b2.installed.length} installed / ${b2.alreadyInsta
8677
9027
  });
8678
9028
  }
8679
9029
 
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
- }
9030
+ // src/cli/uninstall.ts
9031
+ init_i18n();
8789
9032
  init_harnessedRoot();
8790
9033
  init_path_guard();
8791
9034
  init_validate();
@@ -8804,10 +9047,10 @@ var uninstallCcHookAdd = async (ctx) => {
8804
9047
  }
8805
9048
  const abort = dryRunGate(ctx);
8806
9049
  if (abort) return abort;
8807
- const settingsPath3 = join(homedir(), ".claude", "settings.json");
9050
+ const settingsPath = join(homedir(), ".claude", "settings.json");
8808
9051
  let existing;
8809
9052
  try {
8810
- existing = await readFile(settingsPath3, "utf8");
9053
+ existing = await readFile(settingsPath, "utf8");
8811
9054
  } catch {
8812
9055
  return { ok: true, removedPaths: [] };
8813
9056
  }
@@ -8836,8 +9079,8 @@ var uninstallCcHookAdd = async (ctx) => {
8836
9079
  if (settings.hooks?.[ev]?.length === before || before === settings.hooks?.[ev]?.length) ;
8837
9080
  const newText = `${JSON.stringify(settings, null, 2)}
8838
9081
  `;
8839
- await writeFile(settingsPath3, newText);
8840
- return { ok: true, removedPaths: [settingsPath3] };
9082
+ await writeFile(settingsPath, newText);
9083
+ return { ok: true, removedPaths: [settingsPath] };
8841
9084
  };
8842
9085
 
8843
9086
  // src/uninstallers/ccPluginMarketplace.ts
@@ -8976,7 +9219,7 @@ var uninstallNpmCli = async (ctx) => {
8976
9219
  const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
8977
9220
  const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
8978
9221
  const isWin = process.platform === "win32";
8979
- const result = await new Promise((resolve20) => {
9222
+ const result = await new Promise((resolve18) => {
8980
9223
  const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
8981
9224
  let stderr = "";
8982
9225
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -8984,15 +9227,15 @@ var uninstallNpmCli = async (ctx) => {
8984
9227
  });
8985
9228
  const timer = setTimeout(() => {
8986
9229
  child.kill("SIGKILL");
8987
- resolve20({ exitCode: -1, stderr: `${stderr}[timeout]` });
9230
+ resolve18({ exitCode: -1, stderr: `${stderr}[timeout]` });
8988
9231
  }, 3e4);
8989
9232
  child.on("error", (e) => {
8990
9233
  clearTimeout(timer);
8991
- resolve20({ exitCode: -1, stderr: e.message });
9234
+ resolve18({ exitCode: -1, stderr: e.message });
8992
9235
  });
8993
9236
  child.on("close", (code) => {
8994
9237
  clearTimeout(timer);
8995
- resolve20({ exitCode: code ?? -1, stderr });
9238
+ resolve18({ exitCode: code ?? -1, stderr });
8996
9239
  });
8997
9240
  });
8998
9241
  if (result.exitCode !== 0) {
@@ -9058,9 +9301,9 @@ async function discoverCommandFiles(commandsDir) {
9058
9301
  }
9059
9302
  return owned;
9060
9303
  }
9061
- async function checkSettingsEnv(settingsPath3) {
9304
+ async function checkSettingsEnv(settingsPath) {
9062
9305
  try {
9063
- const raw = await readFile(settingsPath3, "utf8");
9306
+ const raw = await readFile(settingsPath, "utf8");
9064
9307
  const data = JSON.parse(raw);
9065
9308
  const env = data.env ?? {};
9066
9309
  return {
@@ -9071,8 +9314,8 @@ async function checkSettingsEnv(settingsPath3) {
9071
9314
  return { hasAgentTeams: false, hasUserLang: false };
9072
9315
  }
9073
9316
  }
9074
- async function removeSettingsEnv(settingsPath3) {
9075
- const raw = await readFile(settingsPath3, "utf8");
9317
+ async function removeSettingsEnv(settingsPath) {
9318
+ const raw = await readFile(settingsPath, "utf8");
9076
9319
  const data = JSON.parse(raw);
9077
9320
  const env = data.env ?? {};
9078
9321
  let changed = false;
@@ -9087,14 +9330,14 @@ async function removeSettingsEnv(settingsPath3) {
9087
9330
  if (!changed) return false;
9088
9331
  if (Object.keys(env).length === 0) delete data.env;
9089
9332
  else data.env = env;
9090
- await writeFile(settingsPath3, `${JSON.stringify(data, null, 2)}
9333
+ await writeFile(settingsPath, `${JSON.stringify(data, null, 2)}
9091
9334
  `, "utf8");
9092
9335
  return true;
9093
9336
  }
9094
9337
  async function runUnifiedUninstall(home, dryRun) {
9095
9338
  const commandsDir = join(home, ".claude", "commands");
9096
9339
  const skillsDir = join(home, ".claude", "skills");
9097
- const settingsPath3 = join(home, ".claude", "settings.json");
9340
+ const settingsPath = join(home, ".claude", "settings.json");
9098
9341
  const harnessedRoot = getHarnessedRoot();
9099
9342
  const pkgRoot = getPackageRoot();
9100
9343
  let workflowNames = [];
@@ -9105,7 +9348,7 @@ async function runUnifiedUninstall(home, dryRun) {
9105
9348
  } catch {
9106
9349
  }
9107
9350
  const commandFiles = await discoverCommandFiles(commandsDir);
9108
- const settingsEnv = await checkSettingsEnv(settingsPath3);
9351
+ const settingsEnv = await checkSettingsEnv(settingsPath);
9109
9352
  const hasSettingsChanges = settingsEnv.hasAgentTeams || settingsEnv.hasUserLang;
9110
9353
  const skillDirs = [];
9111
9354
  for (const name of workflowNames) {
@@ -9163,9 +9406,9 @@ async function runUnifiedUninstall(home, dryRun) {
9163
9406
  let removedSettings = false;
9164
9407
  if (hasSettingsChanges) {
9165
9408
  try {
9166
- removedSettings = await removeSettingsEnv(settingsPath3);
9409
+ removedSettings = await removeSettingsEnv(settingsPath);
9167
9410
  } catch (e) {
9168
- failures.push(`${settingsPath3}: ${e.message}`);
9411
+ failures.push(`${settingsPath}: ${e.message}`);
9169
9412
  }
9170
9413
  }
9171
9414
  const normalizedRoot = resolve(harnessedRoot);
@@ -9362,6 +9605,7 @@ function registerWorkflows(program2) {
9362
9605
  }
9363
9606
 
9364
9607
  // src/cli.ts
9608
+ init_i18n();
9365
9609
  init_harnessedRoot();
9366
9610
  migrateLegacyHarnessedRoot();
9367
9611
  var argv = process.argv;
@@ -9413,6 +9657,11 @@ registerLearn(program);
9413
9657
  registerUpdate(program);
9414
9658
  registerReleasePreflight(program);
9415
9659
  registerRetro(program);
9416
- program.parse(process.argv);
9660
+ var bare = parseBareInvocation(process.argv.slice(2));
9661
+ if (bare.here) {
9662
+ void runHere({ json: bare.json });
9663
+ } else {
9664
+ program.parse(process.argv);
9665
+ }
9417
9666
  //# sourceMappingURL=cli.mjs.map
9418
9667
  //# sourceMappingURL=cli.mjs.map