harnessed 3.3.0 → 3.3.1

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/README.md CHANGED
@@ -36,7 +36,15 @@ npm install -g harnessed && harnessed setup
36
36
 
37
37
  > Windows PowerShell 5.x 不支持 `&&` 链接,需改 `;` 或分两行 (`npm install -g harnessed; harnessed setup`)。bash / zsh / PowerShell 7+ / cmd.exe 都正常。
38
38
 
39
- 🤖 **或让 AI 帮你装** — 复制 [INSTALL-WITH-AI.md](./INSTALL-WITH-AI.md) 整段粘贴进 Claude Code (或任何 AI 助手),AI 自动处理 OS / 权限 / PATH / corepack 等 edge case。
39
+ 🤖 **或让 AI 帮你装** — 把下面这句话发给 Claude Code (或任何 AI 助手):
40
+
41
+ > 按 `https://github.com/easyinplay/harnessed/blob/main/INSTALL-WITH-AI.md` 的指导帮我装 harnessed
42
+
43
+ AI 会自动 fetch 文档 + 跑安装,处理 OS / 权限 / PATH / corepack 等 edge case,无需复制大段文字。
44
+
45
+ > [!TIP]
46
+ > 🚀 **很多人关心的 Agent Teams 和 Subagent 功能,在 harnessed 中会根据任务自动启用!**
47
+ > 无需手动配置 `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`,`harnessed setup` v3.3.1+ 会自动写入 `~/.claude/settings.json`;Pattern A 全栈三路 / Pattern C 4-specialist 等 multi-agent workflow 即开即用。
40
48
 
41
49
  ---
42
50
 
package/dist/cli.mjs CHANGED
@@ -847,7 +847,7 @@ var init_resume = __esm({
847
847
 
848
848
  // package.json
849
849
  var package_default = {
850
- version: "3.3.0"};
850
+ version: "3.3.1"};
851
851
 
852
852
  // src/manifest/errors.ts
853
853
  function instancePathToKeyPath(instancePath) {
@@ -1633,7 +1633,7 @@ function renderHumanTable(records) {
1633
1633
  }
1634
1634
  }
1635
1635
  function pipeToJq(filterExpr, lines) {
1636
- return new Promise((resolve9, reject) => {
1636
+ return new Promise((resolve10, reject) => {
1637
1637
  const child = spawn("jq", [filterExpr], {
1638
1638
  stdio: ["pipe", "inherit", "inherit"],
1639
1639
  windowsHide: true
@@ -1642,12 +1642,12 @@ function pipeToJq(filterExpr, lines) {
1642
1642
  const e = err2;
1643
1643
  if (e.code === "ENOENT") {
1644
1644
  console.error("\u2717 jq not found in PATH \u2014 run: harnessed doctor");
1645
- resolve9(1);
1645
+ resolve10(1);
1646
1646
  } else {
1647
1647
  reject(err2);
1648
1648
  }
1649
1649
  });
1650
- child.on("close", (code) => resolve9(code ?? 0));
1650
+ child.on("close", (code) => resolve10(code ?? 0));
1651
1651
  child.stdin.write(lines.join("\n"));
1652
1652
  child.stdin.end();
1653
1653
  });
@@ -3364,10 +3364,10 @@ var installCcHookAdd = async (ctx) => {
3364
3364
  const e = pre.errors[0] ?? err(ctx, "/", "preflight failed", "preflight");
3365
3365
  return { ok: false, phase: "preflight", error: e };
3366
3366
  }
3367
- const settingsPath = join(homedir(), ".claude", "settings.json");
3367
+ const settingsPath2 = join(homedir(), ".claude", "settings.json");
3368
3368
  let existing;
3369
3369
  try {
3370
- existing = await readFile(settingsPath, "utf8");
3370
+ existing = await readFile(settingsPath2, "utf8");
3371
3371
  } catch {
3372
3372
  existing = null;
3373
3373
  }
@@ -3398,7 +3398,7 @@ var installCcHookAdd = async (ctx) => {
3398
3398
  const newText = `${JSON.stringify(settings, null, 2)}
3399
3399
  `;
3400
3400
  const plan = {
3401
- files: [{ target: settingsPath, scope: "HOME", oldText: existing ?? "", newText }]
3401
+ files: [{ target: settingsPath2, scope: "HOME", oldText: existing ?? "", newText }]
3402
3402
  };
3403
3403
  process.stdout.write(renderDiff(plan, ctx));
3404
3404
  const conf = await confirmAt("L3", { ...ctx});
@@ -3409,10 +3409,10 @@ var installCcHookAdd = async (ctx) => {
3409
3409
  if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
3410
3410
  const bk = await backup(plan, ctx);
3411
3411
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
3412
- await writeFile(settingsPath, newText);
3412
+ await writeFile(settingsPath2, newText);
3413
3413
  let verify;
3414
3414
  try {
3415
- verify = JSON.parse(await readFile(settingsPath, "utf8"));
3415
+ verify = JSON.parse(await readFile(settingsPath2, "utf8"));
3416
3416
  } catch (e) {
3417
3417
  return {
3418
3418
  ok: false,
@@ -3440,7 +3440,7 @@ var installCcHookAdd = async (ctx) => {
3440
3440
  };
3441
3441
  }
3442
3442
  await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, "", "");
3443
- return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath] };
3443
+ return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath2] };
3444
3444
  };
3445
3445
  function getUserClaudeJsonPath() {
3446
3446
  return join(homedir(), ".claude.json");
@@ -3476,7 +3476,7 @@ async function isPluginRegistered(pluginName) {
3476
3476
  return Object.keys(plugins).some((k) => k.split("@")[0] === pluginName);
3477
3477
  }
3478
3478
  function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
3479
- return new Promise((resolve9) => {
3479
+ return new Promise((resolve10) => {
3480
3480
  const isWin = process.platform === "win32";
3481
3481
  const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
3482
3482
  let stderr = "";
@@ -3485,15 +3485,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
3485
3485
  });
3486
3486
  const timer = setTimeout(() => {
3487
3487
  child.kill("SIGKILL");
3488
- resolve9({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
3488
+ resolve10({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
3489
3489
  }, timeoutMs);
3490
3490
  child.on("error", (e) => {
3491
3491
  clearTimeout(timer);
3492
- resolve9({ exitCode: -1, stderr: `${stderr}${e.message}` });
3492
+ resolve10({ exitCode: -1, stderr: `${stderr}${e.message}` });
3493
3493
  });
3494
3494
  child.on("close", (code) => {
3495
3495
  clearTimeout(timer);
3496
- resolve9({ exitCode: code ?? -1, stderr });
3496
+ resolve10({ exitCode: code ?? -1, stderr });
3497
3497
  });
3498
3498
  });
3499
3499
  }
@@ -3672,10 +3672,10 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
3672
3672
  child.stderr?.setEncoding("utf8").on("data", (chunk) => {
3673
3673
  stderr += chunk;
3674
3674
  });
3675
- return await new Promise((resolve9) => {
3675
+ return await new Promise((resolve10) => {
3676
3676
  const timer = setTimeout(() => {
3677
3677
  child.kill("SIGKILL");
3678
- resolve9({
3678
+ resolve10({
3679
3679
  ok: false,
3680
3680
  phase: "spawn",
3681
3681
  error: {
@@ -3690,7 +3690,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
3690
3690
  }, effectiveTimeoutMs);
3691
3691
  child.on("error", (err2) => {
3692
3692
  clearTimeout(timer);
3693
- resolve9({
3693
+ resolve10({
3694
3694
  ok: false,
3695
3695
  phase: "spawn",
3696
3696
  error: {
@@ -3705,14 +3705,14 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
3705
3705
  });
3706
3706
  child.on("close", (code) => {
3707
3707
  clearTimeout(timer);
3708
- resolve9({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
3708
+ resolve10({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
3709
3709
  });
3710
3710
  });
3711
3711
  }
3712
3712
 
3713
3713
  // src/installers/gitCloneWithSetup.ts
3714
3714
  function gitRevParseHead(cwd, timeoutMs = 1e4) {
3715
- return new Promise((resolve9) => {
3715
+ return new Promise((resolve10) => {
3716
3716
  const isWin = process.platform === "win32";
3717
3717
  const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
3718
3718
  let stdout2 = "";
@@ -3721,15 +3721,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
3721
3721
  });
3722
3722
  const timer = setTimeout(() => {
3723
3723
  child.kill("SIGKILL");
3724
- resolve9({ sha: "", exit: -1 });
3724
+ resolve10({ sha: "", exit: -1 });
3725
3725
  }, timeoutMs);
3726
3726
  child.on("error", () => {
3727
3727
  clearTimeout(timer);
3728
- resolve9({ sha: "", exit: -1 });
3728
+ resolve10({ sha: "", exit: -1 });
3729
3729
  });
3730
3730
  child.on("close", (code) => {
3731
3731
  clearTimeout(timer);
3732
- resolve9({ sha: stdout2.trim(), exit: code ?? -1 });
3732
+ resolve10({ sha: stdout2.trim(), exit: code ?? -1 });
3733
3733
  });
3734
3734
  });
3735
3735
  }
@@ -4782,6 +4782,80 @@ function registerRollback(program2) {
4782
4782
  console.log(`restored ${meta.files.length} file(s) from ${timestamp}`);
4783
4783
  });
4784
4784
  }
4785
+
4786
+ // src/cli/lib/enableAgentTeamsInSettings.ts
4787
+ init_harnessedRoot();
4788
+ function settingsPath() {
4789
+ return resolve(homedir(), ".claude", "settings.json");
4790
+ }
4791
+ async function enableAgentTeamsInSettings() {
4792
+ const path = settingsPath();
4793
+ let raw;
4794
+ try {
4795
+ raw = await readFile(path, "utf8");
4796
+ } catch (err2) {
4797
+ const code = err2.code;
4798
+ if (code !== "ENOENT") {
4799
+ return { status: "warn", message: `read ${path} failed: ${err2.message}` };
4800
+ }
4801
+ return createFreshSettings(path);
4802
+ }
4803
+ let data;
4804
+ try {
4805
+ const parsed = JSON.parse(raw);
4806
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
4807
+ return { status: "warn", message: `${path} is not a JSON object` };
4808
+ }
4809
+ data = parsed;
4810
+ } catch (err2) {
4811
+ return { status: "warn", message: `${path} malformed JSON: ${err2.message}` };
4812
+ }
4813
+ const env = data.env ?? {};
4814
+ if (env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1") {
4815
+ return { status: "already-enabled", path };
4816
+ }
4817
+ const backupPath = await backupOriginal(raw);
4818
+ if (backupPath.status === "warn") return backupPath;
4819
+ data.env = { ...env, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" };
4820
+ const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
4821
+ `);
4822
+ if (writeErr) return { status: "warn", message: writeErr };
4823
+ return { status: "enabled", path, backupPath: backupPath.path };
4824
+ }
4825
+ async function createFreshSettings(path) {
4826
+ const data = { env: { CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" } };
4827
+ try {
4828
+ await mkdir(join(homedir(), ".claude"), { recursive: true });
4829
+ } catch (err2) {
4830
+ return { status: "warn", message: `mkdir ~/.claude failed: ${err2.message}` };
4831
+ }
4832
+ const writeErr = await atomicWrite(path, `${JSON.stringify(data, null, 2)}
4833
+ `);
4834
+ if (writeErr) return { status: "warn", message: writeErr };
4835
+ return { status: "created", path };
4836
+ }
4837
+ async function backupOriginal(raw) {
4838
+ const backupRoot = harnessedSubdir("backups");
4839
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
4840
+ const backupPath = join(backupRoot, `settings.json.${ts}.bak`);
4841
+ try {
4842
+ await mkdir(backupRoot, { recursive: true });
4843
+ await writeFile(backupPath, raw, "utf8");
4844
+ return { status: "ok", path: backupPath };
4845
+ } catch (err2) {
4846
+ return { status: "warn", message: `backup ${backupPath} failed: ${err2.message}` };
4847
+ }
4848
+ }
4849
+ async function atomicWrite(path, content) {
4850
+ const tmpPath = `${path}.tmp-${process.pid}-${Date.now()}`;
4851
+ try {
4852
+ await writeFile(tmpPath, content, "utf8");
4853
+ await rename(tmpPath, path);
4854
+ return void 0;
4855
+ } catch (err2) {
4856
+ return `write ${path} failed: ${err2.message}`;
4857
+ }
4858
+ }
4785
4859
  init_checkAgentTeams();
4786
4860
  var FLAT_LEGACY_DEPRECATED = /* @__PURE__ */ new Set(["plan-feature", "execute-task", "verify-work"]);
4787
4861
  var FLAT_LEGACY_KEEP = /* @__PURE__ */ new Set(["research", "retro", "auto"]);
@@ -5004,6 +5078,18 @@ function registerSetup(program2) {
5004
5078
  `
5005
5079
  Step A complete: ${skillsInstalled} workflow skill(s) installed to ${skillsBase}`
5006
5080
  );
5081
+ const cResult = await enableAgentTeamsInSettings();
5082
+ if (cResult.status === "created") {
5083
+ console.log(` [C] created ${cResult.path} + enabled Agent Teams`);
5084
+ } else if (cResult.status === "already-enabled") {
5085
+ console.log(` [C] Agent Teams already enabled (${cResult.path})`);
5086
+ } else if (cResult.status === "enabled") {
5087
+ console.log(
5088
+ ` [C] enabled Agent Teams in ${cResult.path} (backup saved \u2192 ${cResult.backupPath})`
5089
+ );
5090
+ } else {
5091
+ console.warn(` [C] Agent Teams enable skipped: ${cResult.message}`);
5092
+ }
5007
5093
  const manifestPaths = await listBaseManifests2(pkgRoot);
5008
5094
  const b = await runStepBInstall(manifestPaths);
5009
5095
  const stepBMs = (b.elapsedMs / 1e3).toFixed(1);
@@ -5092,10 +5178,10 @@ var uninstallCcHookAdd = async (ctx) => {
5092
5178
  }
5093
5179
  const abort = dryRunGate(ctx);
5094
5180
  if (abort) return abort;
5095
- const settingsPath = join(homedir(), ".claude", "settings.json");
5181
+ const settingsPath2 = join(homedir(), ".claude", "settings.json");
5096
5182
  let existing;
5097
5183
  try {
5098
- existing = await readFile(settingsPath, "utf8");
5184
+ existing = await readFile(settingsPath2, "utf8");
5099
5185
  } catch {
5100
5186
  return { ok: true, removedPaths: [] };
5101
5187
  }
@@ -5124,8 +5210,8 @@ var uninstallCcHookAdd = async (ctx) => {
5124
5210
  if (settings.hooks?.[ev]?.length === before || before === settings.hooks?.[ev]?.length) ;
5125
5211
  const newText = `${JSON.stringify(settings, null, 2)}
5126
5212
  `;
5127
- await writeFile(settingsPath, newText);
5128
- return { ok: true, removedPaths: [settingsPath] };
5213
+ await writeFile(settingsPath2, newText);
5214
+ return { ok: true, removedPaths: [settingsPath2] };
5129
5215
  };
5130
5216
 
5131
5217
  // src/uninstallers/ccPluginMarketplace.ts
@@ -5261,7 +5347,7 @@ var uninstallNpmCli = async (ctx) => {
5261
5347
  const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
5262
5348
  const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
5263
5349
  const isWin = process.platform === "win32";
5264
- const result = await new Promise((resolve9) => {
5350
+ const result = await new Promise((resolve10) => {
5265
5351
  const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
5266
5352
  let stderr = "";
5267
5353
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -5269,15 +5355,15 @@ var uninstallNpmCli = async (ctx) => {
5269
5355
  });
5270
5356
  const timer = setTimeout(() => {
5271
5357
  child.kill("SIGKILL");
5272
- resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
5358
+ resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
5273
5359
  }, 3e4);
5274
5360
  child.on("error", (e) => {
5275
5361
  clearTimeout(timer);
5276
- resolve9({ exitCode: -1, stderr: e.message });
5362
+ resolve10({ exitCode: -1, stderr: e.message });
5277
5363
  });
5278
5364
  child.on("close", (code) => {
5279
5365
  clearTimeout(timer);
5280
- resolve9({ exitCode: code ?? -1, stderr });
5366
+ resolve10({ exitCode: code ?? -1, stderr });
5281
5367
  });
5282
5368
  });
5283
5369
  if (result.exitCode !== 0) {