harnessed 3.0.1 → 3.0.2

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
@@ -34,6 +34,10 @@
34
34
  npm install -g harnessed && harnessed setup
35
35
  ```
36
36
 
37
+ > Windows PowerShell 5.x 不支持 `&&` 链接,需改 `;` 或分两行 (`npm install -g harnessed; harnessed setup`)。bash / zsh / PowerShell 7+ / cmd.exe 都正常。
38
+
39
+ 🤖 **或让 AI 帮你装** — 复制 [INSTALL-WITH-AI.md](./INSTALL-WITH-AI.md) 整段粘贴进 Claude Code (或任何 AI 助手),AI 自动处理 OS / 权限 / PATH / corepack 等 edge case。
40
+
37
41
  ---
38
42
 
39
43
  ## 📐 4-stage 流程图
package/dist/cli.mjs CHANGED
@@ -793,7 +793,7 @@ var init_resume = __esm({
793
793
 
794
794
  // package.json
795
795
  var package_default = {
796
- version: "3.0.1"};
796
+ version: "3.0.2"};
797
797
 
798
798
  // src/manifest/errors.ts
799
799
  function instancePathToKeyPath(instancePath) {
@@ -3404,6 +3404,9 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
3404
3404
  });
3405
3405
  });
3406
3406
  }
3407
+ function getMcpSpawnCwd() {
3408
+ return homedir();
3409
+ }
3407
3410
 
3408
3411
  // src/installers/ccPluginMarketplace.ts
3409
3412
  function parseCmd(cmd) {
@@ -3455,8 +3458,8 @@ var installCcPluginMarketplace = async (ctx) => {
3455
3458
  }
3456
3459
  };
3457
3460
  }
3458
- const pluginName = parsed.pluginAtMkt.split("@")[0];
3459
- const installArgs = ["plugin", "install", parsed.pluginAtMkt, "--scope", "project"];
3461
+ const pluginName = parsed.pluginAtMkt.split("@")[0] ?? parsed.pluginAtMkt;
3462
+ const installArgs = ["plugin", "install", parsed.pluginAtMkt, "--scope", "user"];
3460
3463
  const allArgs = [];
3461
3464
  if (parsed.marketplaceRef !== null) {
3462
3465
  allArgs.push(["plugin", "marketplace", "add", parsed.marketplaceRef]);
@@ -3464,30 +3467,30 @@ var installCcPluginMarketplace = async (ctx) => {
3464
3467
  allArgs.push(installArgs);
3465
3468
  for (const argSet of allArgs) {
3466
3469
  for (const a of argSet) {
3467
- const violation2 = checkCmdString(a);
3468
- if (violation2) {
3470
+ const violation = checkCmdString(a);
3471
+ if (violation) {
3469
3472
  return {
3470
3473
  ok: false,
3471
3474
  phase: "preflight",
3472
3475
  error: err(
3473
3476
  ctx,
3474
3477
  "/spec/install/cmd",
3475
- `shell escape detected in constructed cc-plugin arg '${a.slice(0, 60)}': ${violation2.label} (${violation2.hint})`,
3478
+ `shell escape detected in constructed cc-plugin arg '${a.slice(0, 60)}': ${violation.label} (${violation.hint})`,
3476
3479
  "security-gate-bypass"
3477
3480
  )
3478
3481
  };
3479
3482
  }
3480
3483
  }
3481
3484
  }
3482
- const settingsFile = `${ctx.cwd}/.claude/settings.json`;
3485
+ const settingsFile = `${getMcpSpawnCwd()}/.claude.json`;
3483
3486
  const newEntry = JSON.stringify({ enabledPlugins: { [parsed.pluginAtMkt]: true } }, null, 2);
3484
3487
  const plan = {
3485
3488
  files: [
3486
3489
  {
3487
3490
  target: settingsFile,
3488
- scope: "PROJECT",
3491
+ scope: "HOME",
3489
3492
  oldText: "",
3490
- newText: `// will be merged into .claude/settings.json enabledPlugins map by \`claude plugin install\`:
3493
+ newText: `// will be merged into ~/.claude.json enabledPlugins map by \`claude plugin install --scope user\`:
3491
3494
  ${newEntry}
3492
3495
  `
3493
3496
  }
@@ -3502,15 +3505,13 @@ ${newEntry}
3502
3505
  if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
3503
3506
  const bk = await backup(plan, ctx);
3504
3507
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
3508
+ const spawnCwd = install.cwd ?? getMcpSpawnCwd();
3505
3509
  let stepOneStderr = "";
3506
3510
  if (parsed.marketplaceRef !== null) {
3507
- const r1 = await runArgs(
3508
- ["plugin", "marketplace", "add", parsed.marketplaceRef],
3509
- install.cwd ?? ctx.cwd
3510
- );
3511
+ const r1 = await runArgs(["plugin", "marketplace", "add", parsed.marketplaceRef], spawnCwd);
3511
3512
  stepOneStderr = r1.stderr;
3512
3513
  }
3513
- const r2 = await runArgs(installArgs, install.cwd ?? ctx.cwd);
3514
+ const r2 = await runArgs(installArgs, spawnCwd);
3514
3515
  if (r2.exitCode !== 0) {
3515
3516
  return {
3516
3517
  ok: false,
@@ -3524,43 +3525,34 @@ ${newEntry}
3524
3525
  )
3525
3526
  };
3526
3527
  }
3527
- const verifyShell = process.platform === "win32" ? "cmd.exe" : "/bin/sh";
3528
- const verifyFlag = process.platform === "win32" ? "/c" : "-c";
3529
- const verifyLine = `claude plugin list --json | grep -q ${pluginName}`;
3530
- const violation = checkCmdString(verifyLine);
3531
- if (violation) {
3532
- return {
3533
- ok: false,
3534
- phase: "verify",
3535
- backupId: bk.backupId,
3536
- error: err(
3537
- ctx,
3538
- "/spec/verify/cmd",
3539
- `verify shell escape: ${violation.label}`,
3540
- "security-gate-bypass"
3541
- )
3542
- };
3543
- }
3544
3528
  const vr = await new Promise((resolve9) => {
3545
- const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
3529
+ const child = spawn("claude", ["plugin", "list", "--json"], {
3530
+ cwd: spawnCwd,
3531
+ shell: process.platform === "win32",
3532
+ windowsHide: true
3533
+ });
3534
+ let stdout2 = "";
3546
3535
  let stderr = "";
3536
+ child.stdout?.setEncoding("utf8").on("data", (c) => {
3537
+ stdout2 += c;
3538
+ });
3547
3539
  child.stderr?.setEncoding("utf8").on("data", (c) => {
3548
3540
  stderr += c;
3549
3541
  });
3550
3542
  const timer = setTimeout(() => {
3551
3543
  child.kill("SIGKILL");
3552
- resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
3544
+ resolve9({ exitCode: -1, stderr: `${stderr}[timeout]`, stdout: stdout2 });
3553
3545
  }, 15e3);
3554
3546
  child.on("error", (e) => {
3555
3547
  clearTimeout(timer);
3556
- resolve9({ exitCode: -1, stderr: e.message });
3548
+ resolve9({ exitCode: -1, stderr: e.message, stdout: stdout2 });
3557
3549
  });
3558
3550
  child.on("close", (code) => {
3559
3551
  clearTimeout(timer);
3560
- resolve9({ exitCode: code ?? -1, stderr });
3552
+ resolve9({ exitCode: code ?? -1, stderr, stdout: stdout2 });
3561
3553
  });
3562
3554
  });
3563
- if (vr.exitCode !== 0) {
3555
+ if (vr.exitCode !== 0 || !vr.stdout.includes(pluginName)) {
3564
3556
  return {
3565
3557
  ok: false,
3566
3558
  phase: "verify",
@@ -3568,7 +3560,7 @@ ${newEntry}
3568
3560
  error: err(
3569
3561
  ctx,
3570
3562
  "/spec/verify/cmd",
3571
- `verify exit ${vr.exitCode}: ${vr.stderr.slice(0, 200)}`,
3563
+ `verify exit ${vr.exitCode} or '${pluginName}' not in plugin list stdout: ${vr.stderr.slice(0, 200)}`,
3572
3564
  "verify-failed"
3573
3565
  )
3574
3566
  };
@@ -3576,8 +3568,9 @@ ${newEntry}
3576
3568
  await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, install.git_ref, "");
3577
3569
  return { ok: true, backupId: bk.backupId, appliedFiles: [settingsFile] };
3578
3570
  };
3579
- var DEFAULT_TIMEOUT_MS = 15e3;
3580
- async function spawnCmd(ctx, cmd, args) {
3571
+ var DEFAULT_VERIFY_TIMEOUT_MS = 15e3;
3572
+ var DEFAULT_INSTALL_TIMEOUT_MS = 6e4;
3573
+ async function spawnCmd(ctx, cmd, args, timeoutMs) {
3581
3574
  const violation = checkCmdString(cmd);
3582
3575
  if (violation) {
3583
3576
  return {
@@ -3594,8 +3587,7 @@ async function spawnCmd(ctx, cmd, args) {
3594
3587
  };
3595
3588
  }
3596
3589
  const installCfg = ctx.manifest.spec.install;
3597
- const verifyCfg = ctx.manifest.spec.verify;
3598
- const timeoutMs = verifyCfg.timeout_ms ?? DEFAULT_TIMEOUT_MS;
3590
+ const effectiveTimeoutMs = timeoutMs ?? DEFAULT_INSTALL_TIMEOUT_MS;
3599
3591
  const env = { ...process.env, ...installCfg.env ?? {} };
3600
3592
  const cwd = installCfg.cwd ?? ctx.cwd;
3601
3593
  let child;
@@ -3622,13 +3614,13 @@ async function spawnCmd(ctx, cmd, args) {
3622
3614
  error: {
3623
3615
  file: ctx.manifest.metadata.name,
3624
3616
  path: "/spec/install/cmd",
3625
- message: `spawn timed out after ${timeoutMs}ms (cmd: ${cmd}); partial stderr: ${stderr.slice(0, 200)}`,
3617
+ message: `spawn timed out after ${effectiveTimeoutMs}ms (cmd: ${cmd}); partial stderr: ${stderr.slice(0, 200)}`,
3626
3618
  line: null,
3627
3619
  column: null,
3628
3620
  keyword: "spawn-timeout"
3629
3621
  }
3630
3622
  });
3631
- }, timeoutMs);
3623
+ }, effectiveTimeoutMs);
3632
3624
  child.on("error", (err2) => {
3633
3625
  clearTimeout(timer);
3634
3626
  resolve9({
@@ -3774,7 +3766,7 @@ var installGitCloneWithSetup = async (ctx) => {
3774
3766
  if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
3775
3767
  const bk = await backup(plan, ctx);
3776
3768
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
3777
- const sp = await spawnCmd(ctx, install.cmd, []);
3769
+ const sp = await spawnCmd(ctx, install.cmd, [], DEFAULT_INSTALL_TIMEOUT_MS);
3778
3770
  if (!("exitCode" in sp)) return { ...sp, backupId: bk.backupId };
3779
3771
  if (sp.exitCode !== 0) {
3780
3772
  return {
@@ -3816,7 +3808,8 @@ var installGitCloneWithSetup = async (ctx) => {
3816
3808
  )
3817
3809
  };
3818
3810
  }
3819
- const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, []);
3811
+ const verifyTimeoutMs = ctx.manifest.spec.verify.timeout_ms ?? DEFAULT_VERIFY_TIMEOUT_MS;
3812
+ const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, [], verifyTimeoutMs);
3820
3813
  if (!("exitCode" in vr)) return { ...vr, backupId: bk.backupId };
3821
3814
  const expected = ctx.manifest.spec.verify.expected_exit_code ?? 0;
3822
3815
  if (vr.exitCode !== expected) {
@@ -3919,33 +3912,23 @@ var installMcpHttpAdd = async (ctx) => {
3919
3912
  }
3920
3913
  };
3921
3914
  }
3922
- const addArgs = [
3923
- "mcp",
3924
- "add",
3925
- "--scope",
3926
- "project",
3927
- "--transport",
3928
- "http",
3929
- ...hdr.flat,
3930
- name,
3931
- url
3932
- ];
3915
+ const addArgs = ["mcp", "add", "--scope", "user", "--transport", "http", ...hdr.flat, name, url];
3933
3916
  for (const a of addArgs) {
3934
- const violation2 = checkCmdString(a);
3935
- if (violation2) {
3917
+ const violation = checkCmdString(a);
3918
+ if (violation) {
3936
3919
  return {
3937
3920
  ok: false,
3938
3921
  phase: "preflight",
3939
3922
  error: err(
3940
3923
  ctx,
3941
3924
  "/spec/install/cmd",
3942
- `shell escape detected in constructed mcp-http-add arg '${a.slice(0, 60)}': ${violation2.label} (${violation2.hint})`,
3925
+ `shell escape detected in constructed mcp-http-add arg '${a.slice(0, 60)}': ${violation.label} (${violation.hint})`,
3943
3926
  "security-gate-bypass"
3944
3927
  )
3945
3928
  };
3946
3929
  }
3947
3930
  }
3948
- const mcpFile = `${ctx.cwd}/.mcp.json`;
3931
+ const mcpFile = `${getMcpSpawnCwd()}/.claude.json`;
3949
3932
  const headersObj = {};
3950
3933
  for (let i = 0; i < hdr.flat.length; i += 2) {
3951
3934
  const kv = hdr.flat[i + 1];
@@ -3959,9 +3942,9 @@ var installMcpHttpAdd = async (ctx) => {
3959
3942
  files: [
3960
3943
  {
3961
3944
  target: mcpFile,
3962
- scope: "PROJECT",
3945
+ scope: "HOME",
3963
3946
  oldText: "",
3964
- newText: `// will be merged into .mcp.json mcpServers map by \`claude mcp add\`:
3947
+ newText: `// will be merged into ~/.claude.json mcpServers map by \`claude mcp add --scope user\`:
3965
3948
  ${newEntry}
3966
3949
  `
3967
3950
  }
@@ -3976,9 +3959,10 @@ ${newEntry}
3976
3959
  if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
3977
3960
  const bk = await backup(plan, ctx);
3978
3961
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
3979
- const r = await runArgs(addArgs, install.cwd ?? ctx.cwd);
3962
+ const spawnCwd = install.cwd ?? getMcpSpawnCwd();
3963
+ const r = await runArgs(addArgs, spawnCwd);
3980
3964
  if (r.exitCode !== 0) {
3981
- if (r.stderr.includes("already exists in .mcp.json")) {
3965
+ if (r.stderr.includes("already exists")) {
3982
3966
  return { ok: true, alreadyInstalled: true, backupId: bk.backupId };
3983
3967
  }
3984
3968
  return {
@@ -3993,43 +3977,34 @@ ${newEntry}
3993
3977
  )
3994
3978
  };
3995
3979
  }
3996
- const verifyShell = process.platform === "win32" ? "cmd.exe" : "/bin/sh";
3997
- const verifyFlag = process.platform === "win32" ? "/c" : "-c";
3998
- const verifyLine = `claude mcp list | grep -q ${name}`;
3999
- const violation = checkCmdString(verifyLine);
4000
- if (violation) {
4001
- return {
4002
- ok: false,
4003
- phase: "verify",
4004
- backupId: bk.backupId,
4005
- error: err(
4006
- ctx,
4007
- "/spec/verify/cmd",
4008
- `verify shell escape: ${violation.label}`,
4009
- "security-gate-bypass"
4010
- )
4011
- };
4012
- }
4013
3980
  const vr = await new Promise((resolve9) => {
4014
- const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
3981
+ const child = spawn("claude", ["mcp", "list"], {
3982
+ cwd: spawnCwd,
3983
+ shell: process.platform === "win32",
3984
+ windowsHide: true
3985
+ });
3986
+ let stdout2 = "";
4015
3987
  let stderr = "";
3988
+ child.stdout?.setEncoding("utf8").on("data", (c) => {
3989
+ stdout2 += c;
3990
+ });
4016
3991
  child.stderr?.setEncoding("utf8").on("data", (c) => {
4017
3992
  stderr += c;
4018
3993
  });
4019
3994
  const timer = setTimeout(() => {
4020
3995
  child.kill("SIGKILL");
4021
- resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
3996
+ resolve9({ exitCode: -1, stderr: `${stderr}[timeout]`, stdout: stdout2 });
4022
3997
  }, 15e3);
4023
3998
  child.on("error", (e) => {
4024
3999
  clearTimeout(timer);
4025
- resolve9({ exitCode: -1, stderr: e.message });
4000
+ resolve9({ exitCode: -1, stderr: e.message, stdout: stdout2 });
4026
4001
  });
4027
4002
  child.on("close", (code) => {
4028
4003
  clearTimeout(timer);
4029
- resolve9({ exitCode: code ?? -1, stderr });
4004
+ resolve9({ exitCode: code ?? -1, stderr, stdout: stdout2 });
4030
4005
  });
4031
4006
  });
4032
- if (vr.exitCode !== 0) {
4007
+ if (vr.exitCode !== 0 || !vr.stdout.includes(name)) {
4033
4008
  return {
4034
4009
  ok: false,
4035
4010
  phase: "verify",
@@ -4037,7 +4012,7 @@ ${newEntry}
4037
4012
  error: err(
4038
4013
  ctx,
4039
4014
  "/spec/verify/cmd",
4040
- `verify exit ${vr.exitCode}: ${vr.stderr.slice(0, 200)}`,
4015
+ `verify exit ${vr.exitCode} or '${name}' not in mcp list stdout: ${vr.stderr.slice(0, 200)}`,
4041
4016
  "verify-failed"
4042
4017
  )
4043
4018
  };
@@ -4073,7 +4048,7 @@ var installMcpStdioAdd = async (ctx) => {
4073
4048
  "mcp",
4074
4049
  "add",
4075
4050
  "--scope",
4076
- "project",
4051
+ "user",
4077
4052
  "--transport",
4078
4053
  "stdio",
4079
4054
  name,
@@ -4083,21 +4058,21 @@ var installMcpStdioAdd = async (ctx) => {
4083
4058
  `${pkg}@${ver}`
4084
4059
  ];
4085
4060
  for (const a of addArgs) {
4086
- const violation2 = checkCmdString(a);
4087
- if (violation2) {
4061
+ const violation = checkCmdString(a);
4062
+ if (violation) {
4088
4063
  return {
4089
4064
  ok: false,
4090
4065
  phase: "preflight",
4091
4066
  error: err(
4092
4067
  ctx,
4093
4068
  "/spec/install/cmd",
4094
- `shell escape detected in constructed mcp-add arg '${a.slice(0, 60)}': ${violation2.label} (${violation2.hint})`,
4069
+ `shell escape detected in constructed mcp-add arg '${a.slice(0, 60)}': ${violation.label} (${violation.hint})`,
4095
4070
  "security-gate-bypass"
4096
4071
  )
4097
4072
  };
4098
4073
  }
4099
4074
  }
4100
- const mcpFile = `${ctx.cwd}/.mcp.json`;
4075
+ const mcpFile = `${getMcpSpawnCwd()}/.claude.json`;
4101
4076
  const newEntry = JSON.stringify(
4102
4077
  { [name]: { type: "stdio", command: "npx", args: ["--yes", `${pkg}@${ver}`] } },
4103
4078
  null,
@@ -4107,9 +4082,9 @@ var installMcpStdioAdd = async (ctx) => {
4107
4082
  files: [
4108
4083
  {
4109
4084
  target: mcpFile,
4110
- scope: "PROJECT",
4085
+ scope: "HOME",
4111
4086
  oldText: "",
4112
- newText: `// will be merged into .mcp.json mcpServers map by \`claude mcp add\`:
4087
+ newText: `// will be merged into ~/.claude.json mcpServers map by \`claude mcp add --scope user\`:
4113
4088
  ${newEntry}
4114
4089
  `
4115
4090
  }
@@ -4124,9 +4099,10 @@ ${newEntry}
4124
4099
  if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
4125
4100
  const bk = await backup(plan, ctx);
4126
4101
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
4127
- const r = await runArgs(addArgs, install.cwd ?? ctx.cwd);
4102
+ const spawnCwd = install.cwd ?? getMcpSpawnCwd();
4103
+ const r = await runArgs(addArgs, spawnCwd);
4128
4104
  if (r.exitCode !== 0) {
4129
- if (r.stderr.includes("already exists in .mcp.json")) {
4105
+ if (r.stderr.includes("already exists")) {
4130
4106
  return { ok: true, alreadyInstalled: true, backupId: bk.backupId };
4131
4107
  }
4132
4108
  return {
@@ -4141,43 +4117,34 @@ ${newEntry}
4141
4117
  )
4142
4118
  };
4143
4119
  }
4144
- const verifyShell = process.platform === "win32" ? "cmd.exe" : "/bin/sh";
4145
- const verifyFlag = process.platform === "win32" ? "/c" : "-c";
4146
- const verifyLine = `claude mcp list | grep -q ${name}`;
4147
- const violation = checkCmdString(verifyLine);
4148
- if (violation) {
4149
- return {
4150
- ok: false,
4151
- phase: "verify",
4152
- backupId: bk.backupId,
4153
- error: err(
4154
- ctx,
4155
- "/spec/verify/cmd",
4156
- `verify shell escape: ${violation.label}`,
4157
- "security-gate-bypass"
4158
- )
4159
- };
4160
- }
4161
4120
  const vr = await new Promise((resolve9) => {
4162
- const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
4121
+ const child = spawn("claude", ["mcp", "list"], {
4122
+ cwd: spawnCwd,
4123
+ shell: process.platform === "win32",
4124
+ windowsHide: true
4125
+ });
4126
+ let stdout2 = "";
4163
4127
  let stderr = "";
4128
+ child.stdout?.setEncoding("utf8").on("data", (c) => {
4129
+ stdout2 += c;
4130
+ });
4164
4131
  child.stderr?.setEncoding("utf8").on("data", (c) => {
4165
4132
  stderr += c;
4166
4133
  });
4167
4134
  const timer = setTimeout(() => {
4168
4135
  child.kill("SIGKILL");
4169
- resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
4136
+ resolve9({ exitCode: -1, stderr: `${stderr}[timeout]`, stdout: stdout2 });
4170
4137
  }, 15e3);
4171
4138
  child.on("error", (e) => {
4172
4139
  clearTimeout(timer);
4173
- resolve9({ exitCode: -1, stderr: e.message });
4140
+ resolve9({ exitCode: -1, stderr: e.message, stdout: stdout2 });
4174
4141
  });
4175
4142
  child.on("close", (code) => {
4176
4143
  clearTimeout(timer);
4177
- resolve9({ exitCode: code ?? -1, stderr });
4144
+ resolve9({ exitCode: code ?? -1, stderr, stdout: stdout2 });
4178
4145
  });
4179
4146
  });
4180
- if (vr.exitCode !== 0) {
4147
+ if (vr.exitCode !== 0 || !vr.stdout.includes(name)) {
4181
4148
  return {
4182
4149
  ok: false,
4183
4150
  phase: "verify",
@@ -4185,7 +4152,7 @@ ${newEntry}
4185
4152
  error: err(
4186
4153
  ctx,
4187
4154
  "/spec/verify/cmd",
4188
- `verify exit ${vr.exitCode}: ${vr.stderr.slice(0, 200)}`,
4155
+ `verify exit ${vr.exitCode} or '${name}' not in mcp list stdout: ${vr.stderr.slice(0, 200)}`,
4189
4156
  "verify-failed"
4190
4157
  )
4191
4158
  };
@@ -4251,7 +4218,7 @@ var installNpmCli = async (ctx) => {
4251
4218
  if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
4252
4219
  const bk = await backup(plan, ctx);
4253
4220
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
4254
- const sp = await spawnCmd(ctx, cmd, []);
4221
+ const sp = await spawnCmd(ctx, cmd, [], DEFAULT_INSTALL_TIMEOUT_MS);
4255
4222
  if (!("exitCode" in sp)) return { ...sp, backupId: bk.backupId };
4256
4223
  if (sp.exitCode !== 0) {
4257
4224
  return {
@@ -4266,7 +4233,8 @@ var installNpmCli = async (ctx) => {
4266
4233
  )
4267
4234
  };
4268
4235
  }
4269
- const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, []);
4236
+ const verifyTimeoutMs = ctx.manifest.spec.verify.timeout_ms ?? DEFAULT_VERIFY_TIMEOUT_MS;
4237
+ const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, [], verifyTimeoutMs);
4270
4238
  if (!("exitCode" in vr)) return { ...vr, backupId: bk.backupId };
4271
4239
  const expected = ctx.manifest.spec.verify.expected_exit_code ?? 0;
4272
4240
  if (vr.exitCode !== expected) {
@@ -4367,7 +4335,7 @@ var installNpxSkillInstaller = async (ctx) => {
4367
4335
  if (ctx.opts.dryRun) return { aborted: true, reason: "user-cancel" };
4368
4336
  const bk = await backup(plan, ctx);
4369
4337
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
4370
- const sp = await spawnCmd(ctx, install.cmd, []);
4338
+ const sp = await spawnCmd(ctx, install.cmd, [], DEFAULT_INSTALL_TIMEOUT_MS);
4371
4339
  if (!("exitCode" in sp)) return { ...sp, backupId: bk.backupId };
4372
4340
  if (sp.exitCode !== 0) {
4373
4341
  return {
@@ -4400,7 +4368,8 @@ var installNpxSkillInstaller = async (ctx) => {
4400
4368
  }
4401
4369
  };
4402
4370
  }
4403
- const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, []);
4371
+ const verifyTimeoutMs = ctx.manifest.spec.verify.timeout_ms ?? DEFAULT_VERIFY_TIMEOUT_MS;
4372
+ const vr = await spawnCmd(ctx, ctx.manifest.spec.verify.cmd, [], verifyTimeoutMs);
4404
4373
  if (!("exitCode" in vr)) return { ...vr, backupId: bk.backupId };
4405
4374
  const expected = ctx.manifest.spec.verify.expected_exit_code ?? 0;
4406
4375
  if (vr.exitCode !== expected) {
@@ -4896,7 +4865,7 @@ async function warnIfAgentTeamsMissing() {
4896
4865
  console.warn("\n\u26A0\uFE0F Agent Teams \u672A\u542F\u7528 \u2014 parallelism-gate \u5347\u7EA7\u8DEF\u5F84\u4E0D\u53EF\u7528");
4897
4866
  console.warn(" \u4FEE\u590D: claude config set env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS 1");
4898
4867
  console.warn(
4899
- " \u8BF4\u660E: harnessed v2.0 \u4E09\u5C42\u6808\u65B9\u6CD5\u8BBA parallelism-gate \u5347\u7EA7\u8DEF\u5F84\u9700 CC 2.1.133+ Agent Teams enable (sister ~/.claude/rules/agent-teams.md)"
4868
+ " \u8BF4\u660E: harnessed v3.0 \u4E09\u5C42\u6808\u65B9\u6CD5\u8BBA parallelism-gate \u5347\u7EA7\u8DEF\u5F84\u9700 CC 2.1.133+ Agent Teams enable"
4900
4869
  );
4901
4870
  console.warn(
4902
4871
  " \u4E0D\u963B\u585E setup,\u540E\u7EED parallelism-gate workflow phase \u89E6\u53D1\u65F6\u81EA\u52A8\u964D\u7EA7 subagent fan-out\n"
@@ -5052,7 +5021,7 @@ MCP servers configured. Run \`/mcp\` in Claude Code to verify each server's conn
5052
5021
  );
5053
5022
  }
5054
5023
  console.log(
5055
- "\n\u2713 harnessed v2.0 \u4E09\u5C42\u6808\u65B9\u6CD5\u8BBA bundled \u2014 4 workflows + 6 judgments + 37 capabilities ready"
5024
+ "\n\u2713 harnessed v3.0 \u4E09\u5C42\u6808\u65B9\u6CD5\u8BBA bundled \u2014 23 workflows (4 master + 18 sub + 1 standalone) + 6 disciplines + 10 judgments + ~83 capabilities ready"
5056
5025
  );
5057
5026
  console.log(
5058
5027
  " workflows in <packageRoot>/workflows/ (Pure bundled, NOT user-dir override per D-01)"