harnessed 1.0.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -148,8 +148,8 @@ var init_path_guard = __esm({
148
148
  }
149
149
  });
150
150
  function branchOnSchemaVersion(v, handlers) {
151
- const isKnownV1 = Object.values(SCHEMA_VERSIONS).includes(v);
152
- return isKnownV1 ? handlers.v1() : handlers.unknown();
151
+ const isKnownVersion = Object.values(SCHEMA_VERSIONS).includes(v);
152
+ return isKnownVersion ? handlers.v1() : handlers.unknown();
153
153
  }
154
154
  var SCHEMA_VERSIONS;
155
155
  var init_schemaVersion = __esm({
@@ -172,8 +172,14 @@ var init_schemaVersion = __esm({
172
172
  // ← Phase 3.3 W0 T0.5 BACKFILL 11th surface (sister Phase 3.2 W2 T2.2 b875e21 commit msg claim "11th surface" was LATENT STALE — never registered; T0.5 surgical fix per sister Phase 3.2 W2 T2.6 latent W1 c37ee29 Rule 1 pattern)
173
173
  aliases: "harnessed.aliases.v1",
174
174
  // ← Phase 3.3 W1 T1.1 ADD 12th surface (D-01 RICH manifests/aliases.yaml upstream rename redirect + metadata)
175
- knownGood: "harnessed.known-good.v1"
175
+ knownGood: "harnessed.known-good.v1",
176
176
  // ← Phase 3.3 W1 T1.1 ADD 13th surface (D-03 YAML versions/<harnessed-ver>-known-good.yaml per-version lock)
177
+ capabilities: "harnessed.capabilities.v1",
178
+ // ← Phase v2.0-2.3 W0 T2.3.W0.6 ADD 14th surface (R20.2 flat yaml capabilities manifest validate)
179
+ judgment: "harnessed.judgment.v1",
180
+ // ← Phase v2.0-2.3 W0 T2.3.W0.6 ADD 15th surface (R20.4 multi-file judgments triggers/rules validate)
181
+ workflow: "harnessed.workflow.v2"
182
+ // ← Phase v2.0-2.4 W0 T2.4.W0.1 ADD 16th surface (R20.1 + R20.2 + R20.9 — workflow.yaml v2 schema: gate / on / capability / args / fallback / parallelism 字段, sister 4 workflow.yaml: plan-feature + execute-task + research + verify-work)
177
183
  };
178
184
  Type.Union([
179
185
  Type.Literal(SCHEMA_VERSIONS.routingSnapshot),
@@ -193,8 +199,14 @@ var init_schemaVersion = __esm({
193
199
  // ← Phase 3.3 W0 T0.5 BACKFILL 11th surface
194
200
  Type.Literal(SCHEMA_VERSIONS.aliases),
195
201
  // ← Phase 3.3 W1 T1.1 ADD 12th surface
196
- Type.Literal(SCHEMA_VERSIONS.knownGood)
202
+ Type.Literal(SCHEMA_VERSIONS.knownGood),
197
203
  // ← Phase 3.3 W1 T1.1 ADD 13th surface
204
+ Type.Literal(SCHEMA_VERSIONS.capabilities),
205
+ // ← Phase v2.0-2.3 W0 T2.3.W0.6 ADD 14th surface
206
+ Type.Literal(SCHEMA_VERSIONS.judgment),
207
+ // ← Phase v2.0-2.3 W0 T2.3.W0.6 ADD 15th surface
208
+ Type.Literal(SCHEMA_VERSIONS.workflow)
209
+ // ← Phase v2.0-2.4 W0 T2.4.W0.1 ADD 16th surface (workflow.yaml v2 schema, NOTE first .v2 surface in union)
198
210
  ]);
199
211
  }
200
212
  });
@@ -456,6 +468,109 @@ var init_check_token_budget = __esm({
456
468
  PER_SKILL_THRESHOLD = 5e3;
457
469
  }
458
470
  });
471
+ async function checkAgentTeams() {
472
+ const envValue = process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
473
+ const envOn = envValue === "1";
474
+ let settingsValue;
475
+ let settingsOn = false;
476
+ try {
477
+ const path = resolve(homedir(), ".claude", "settings.json");
478
+ const raw = await readFile(path, "utf8");
479
+ const data = JSON.parse(raw);
480
+ settingsValue = data.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
481
+ settingsOn = settingsValue === "1";
482
+ } catch {
483
+ }
484
+ const detected = { env: envOn, settingsJson: settingsOn };
485
+ if (envOn || settingsOn) {
486
+ return { status: "pass", detected, envValue, settingsValue };
487
+ }
488
+ return {
489
+ status: "missing",
490
+ detected,
491
+ envValue,
492
+ settingsValue,
493
+ remediation: 'Agent Teams not enabled. Add to ~/.claude/settings.json:\n "env": { "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" }\nOR run: claude config set env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS 1\nOR export env var:\n export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1\nThen restart Claude Code (CC >= 2.1.133 required).'
494
+ };
495
+ }
496
+ var init_checkAgentTeams = __esm({
497
+ "src/cli/lib/checkAgentTeams.ts"() {
498
+ }
499
+ });
500
+
501
+ // src/cli/lib/check-agent-teams-doctor.ts
502
+ var check_agent_teams_doctor_exports = {};
503
+ __export(check_agent_teams_doctor_exports, {
504
+ checkAgentTeamsDoctor: () => checkAgentTeamsDoctor
505
+ });
506
+ async function checkAgentTeamsDoctor() {
507
+ const r = await checkAgentTeams();
508
+ if (r.status === "pass") {
509
+ const source = r.detected.env ? "env var" : "settings.json";
510
+ return {
511
+ name: "Agent Teams env",
512
+ status: "pass",
513
+ message: `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 (${source})`
514
+ };
515
+ }
516
+ return {
517
+ name: "Agent Teams env",
518
+ status: "warn",
519
+ message: "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS not set (Agent Teams disabled)",
520
+ fix: r.remediation
521
+ };
522
+ }
523
+ var init_check_agent_teams_doctor = __esm({
524
+ "src/cli/lib/check-agent-teams-doctor.ts"() {
525
+ init_checkAgentTeams();
526
+ }
527
+ });
528
+
529
+ // src/cli/lib/check-planning-with-files.ts
530
+ var check_planning_with_files_exports = {};
531
+ __export(check_planning_with_files_exports, {
532
+ checkPlanningWithFiles: () => checkPlanningWithFiles
533
+ });
534
+ async function checkPlanningWithFiles() {
535
+ const root = join(
536
+ homedir(),
537
+ ".claude",
538
+ "plugins",
539
+ "cache",
540
+ "planning-with-files",
541
+ "planning-with-files"
542
+ );
543
+ try {
544
+ const entries = await readdir(root);
545
+ const versions = entries.filter((e) => /^\d+\.\d+/.test(e));
546
+ if (versions.length > 0) {
547
+ return {
548
+ name: "planning-with-files plugin",
549
+ status: "pass",
550
+ message: `installed (version ${versions.join(", ")})`
551
+ };
552
+ }
553
+ return {
554
+ name: "planning-with-files plugin",
555
+ status: "warn",
556
+ message: "plugin directory exists but no version subdir found",
557
+ fix: REMEDIATION
558
+ };
559
+ } catch {
560
+ return {
561
+ name: "planning-with-files plugin",
562
+ status: "warn",
563
+ message: "not installed (plugin cache path missing)",
564
+ fix: REMEDIATION
565
+ };
566
+ }
567
+ }
568
+ var REMEDIATION;
569
+ var init_check_planning_with_files = __esm({
570
+ "src/cli/lib/check-planning-with-files.ts"() {
571
+ REMEDIATION = "install via Claude Code plugin marketplace: `claude plugin install planning-with-files` (requires >=2.2.0 per R20.15 + D-15)";
572
+ }
573
+ });
459
574
  async function withLock(fn) {
460
575
  let release;
461
576
  try {
@@ -670,7 +785,7 @@ var init_resume = __esm({
670
785
 
671
786
  // package.json
672
787
  var package_default = {
673
- version: "1.0.3"};
788
+ version: "2.0.0"};
674
789
 
675
790
  // src/manifest/errors.ts
676
791
  function instancePathToKeyPath(instancePath) {
@@ -1451,7 +1566,7 @@ function renderHumanTable(records) {
1451
1566
  }
1452
1567
  }
1453
1568
  function pipeToJq(filterExpr, lines) {
1454
- return new Promise((resolve10, reject) => {
1569
+ return new Promise((resolve11, reject) => {
1455
1570
  const child = spawn("jq", [filterExpr], {
1456
1571
  stdio: ["pipe", "inherit", "inherit"],
1457
1572
  windowsHide: true
@@ -1460,12 +1575,12 @@ function pipeToJq(filterExpr, lines) {
1460
1575
  const e = err2;
1461
1576
  if (e.code === "ENOENT") {
1462
1577
  console.error("\u2717 jq not found in PATH \u2014 run: harnessed doctor");
1463
- resolve10(1);
1578
+ resolve11(1);
1464
1579
  } else {
1465
1580
  reject(err2);
1466
1581
  }
1467
1582
  });
1468
- child.on("close", (code) => resolve10(code ?? 0));
1583
+ child.on("close", (code) => resolve11(code ?? 0));
1469
1584
  child.stdin.write(lines.join("\n"));
1470
1585
  child.stdin.end();
1471
1586
  });
@@ -1649,29 +1764,36 @@ async function checkDeprecations2() {
1649
1764
  async function checkTokenBudget2() {
1650
1765
  return (await Promise.resolve().then(() => (init_check_token_budget(), check_token_budget_exports))).checkTokenBudget();
1651
1766
  }
1767
+ async function checkAgentTeamsEnv() {
1768
+ return (await Promise.resolve().then(() => (init_check_agent_teams_doctor(), check_agent_teams_doctor_exports))).checkAgentTeamsDoctor();
1769
+ }
1770
+ async function checkPlanningPlugin() {
1771
+ return (await Promise.resolve().then(() => (init_check_planning_with_files(), check_planning_with_files_exports))).checkPlanningWithFiles();
1772
+ }
1652
1773
  function registerDoctor(program2) {
1653
1774
  program2.command("doctor").description(
1654
- "Preflight checks (Node / MCP scope / jq / Win bash / origin URL / gstack prefix / deprecations / token budget)"
1775
+ "Preflight checks (Node / MCP scope / jq / Win bash / origin URL / gstack prefix / deprecations / token budget / Agent Teams / planning-with-files)"
1655
1776
  ).option("--json", "output JSON instead of human-readable").action(async (opts) => {
1656
- const [mcpScope, originUrl, gstackPrefix, deprecations, tokenBudget] = await Promise.all([
1777
+ const [mcp, origin, gstack, dep, tok, at, ppwf] = await Promise.all([
1657
1778
  checkMcpScope(),
1658
1779
  checkOriginUrl(),
1659
1780
  checkGstackPrefix(),
1660
- // ← Phase 3.2 W1 T1.5 ADD 6th check (D-01 PROBE)
1661
1781
  checkDeprecations2(),
1662
- // ← Phase 3.3 W1 T1.7 ADD 7th check (D-02 DOCTOR-ONLY-WARN)
1663
- checkTokenBudget2()
1664
- // ← Phase 3.4 W1 T1.2 ADD 8th check (D-03 + D-04 DOCTOR WARN)
1782
+ checkTokenBudget2(),
1783
+ checkAgentTeamsEnv(),
1784
+ checkPlanningPlugin()
1665
1785
  ]);
1666
1786
  const results = [
1667
1787
  checkNodeVersion(),
1668
- mcpScope,
1788
+ mcp,
1669
1789
  checkJq(),
1670
1790
  checkWinBash(),
1671
- originUrl,
1672
- gstackPrefix,
1673
- deprecations,
1674
- tokenBudget
1791
+ origin,
1792
+ gstack,
1793
+ dep,
1794
+ tok,
1795
+ at,
1796
+ ppwf
1675
1797
  ];
1676
1798
  const hasFail = results.some((r) => r.status === "fail");
1677
1799
  const hasWarn = results.some((r) => r.status === "warn");
@@ -2056,6 +2178,51 @@ async function completePhase(ctx) {
2056
2178
  await complete();
2057
2179
  }
2058
2180
 
2181
+ // src/routing/lib/fallbackHandlers.ts
2182
+ function handleMaxIterationsExceeded(err2, fallback, ctx) {
2183
+ const yamlShort = fallback.message.replace(
2184
+ /\{\{\s*args\.max_iterations\s*\}\}/g,
2185
+ String(ctx.maxIterations)
2186
+ );
2187
+ const truncated = (ctx.lastMessage ?? "<empty>").slice(0, 500);
2188
+ const uxText = `\u274C ralph-loop max-iterations exceeded (${err2.iterations}/${ctx.maxIterations}).
2189
+ Sub-task: ${ctx.subtaskSummary}
2190
+ Workflow: ${ctx.workflowName} / phase ${ctx.phaseId}
2191
+ Last subagent output (truncated): ${truncated}
2192
+ The subagent attempted ${err2.iterations} iterations without emitting verbatim "<promise>COMPLETE</promise>".
2193
+ This indicates one of:
2194
+ 1. Sub-task is genuinely incomplete (escalate to user / re-scope)
2195
+ 2. Subagent is stuck in a loop (review prompt / system instructions)
2196
+ 3. max-iterations too low (override via --max-iterations <N>, hard upper limit 100)
2197
+ Manual options:
2198
+ A) Continue with current state: \`harnessed workflow resume --skip-completion-gate\`
2199
+ B) Re-run from last checkpoint: \`harnessed workflow resume --from-checkpoint\`
2200
+ C) Abort cleanly: exit 1
2201
+ Exit code: ${fallback.exit_code}
2202
+ ${yamlShort}`;
2203
+ console.error(uxText);
2204
+ process.exit(fallback.exit_code);
2205
+ throw new Error("unreachable");
2206
+ }
2207
+ function handleVerbatimCompleteFail(err2, fallback, ctx) {
2208
+ const truncated = err2.lastMessage.slice(0, 500);
2209
+ const uxText = `\u274C ralph-loop verbatim COMPLETE signal missing (F33 P1).
2210
+ Sub-task: ${ctx.subtaskSummary}
2211
+ Workflow: ${ctx.workflowName} / phase ${ctx.phaseId}
2212
+ Last subagent output (truncated): ${truncated}
2213
+ The subagent's final message lacked verbatim "<promise>COMPLETE</promise>" tag.
2214
+ This indicates one of:
2215
+ 1. Subagent skipped the completion-promise contract (review system prompt)
2216
+ 2. Output format misconfigured (check outputFormat schema)
2217
+ Manual options:
2218
+ A) Re-run with explicit COMPLETE instruction in subagent prompt
2219
+ B) Abort cleanly: exit ${fallback.exit_code}
2220
+ Exit code: ${fallback.exit_code}`;
2221
+ console.error(uxText);
2222
+ process.exit(fallback.exit_code);
2223
+ throw new Error("unreachable");
2224
+ }
2225
+
2059
2226
  // src/routing/lib/promiseExtract.ts
2060
2227
  var PROMISE_PATTERN = /<promise>([^<]+)<\/promise>/;
2061
2228
  function extractPromise(text) {
@@ -2265,9 +2432,6 @@ async function runRouting(task, opts = {}) {
2265
2432
  try {
2266
2433
  await ensureSkillsInstalled(decision.required_skills ?? [], skillsRoot);
2267
2434
  } catch (error) {
2268
- if (error instanceof RestartRequiredError || error instanceof SkillNotInstalledError || error instanceof MissingSkillsError) {
2269
- return { ok: false, phase: "install", error };
2270
- }
2271
2435
  return { ok: false, phase: "install", error };
2272
2436
  }
2273
2437
  let agentDef;
@@ -2294,6 +2458,11 @@ async function runRouting(task, opts = {}) {
2294
2458
  onSessionIdInner?.(id);
2295
2459
  }
2296
2460
  });
2461
+ const fbCtx = {
2462
+ subtaskSummary: task.task,
2463
+ workflowName: task.task_type ?? "unknown",
2464
+ phaseId: opts.fallbackPhaseId ?? task.phaseId ?? "unknown"
2465
+ };
2297
2466
  try {
2298
2467
  const result = await ralphLoopWrap(wrappedSpawn, maxIter);
2299
2468
  await completePhase({ phaseId, sessionId: capturedSessionId, status: "complete" });
@@ -2302,10 +2471,16 @@ async function runRouting(task, opts = {}) {
2302
2471
  } catch (error) {
2303
2472
  if (error instanceof MaxIterationsExceededError) {
2304
2473
  emitAudit(task, decision, matched, "max-iter", capturedSessionId);
2474
+ if (opts.fallbackConfig)
2475
+ handleMaxIterationsExceeded(error, opts.fallbackConfig, {
2476
+ ...fbCtx,
2477
+ maxIterations: maxIter
2478
+ });
2305
2479
  return { aborted: true, reason: error.message };
2306
2480
  }
2307
2481
  if (error instanceof VerbatimCompleteFailError) {
2308
2482
  emitAudit(task, decision, matched, "verbatim-fail", capturedSessionId);
2483
+ if (opts.fallbackConfig) handleVerbatimCompleteFail(error, opts.fallbackConfig, fbCtx);
2309
2484
  return { ok: false, phase: "verbatim", error };
2310
2485
  }
2311
2486
  emitAudit(task, decision, matched, "spawn-err", capturedSessionId);
@@ -2346,6 +2521,70 @@ var PhasesSchema = Type.Object(
2346
2521
  { additionalProperties: false }
2347
2522
  );
2348
2523
 
2524
+ // src/workflow/schema/workflow.ts
2525
+ init_schemaVersion();
2526
+ var ModelTier2 = Type.Union([Type.Literal("haiku"), Type.Literal("sonnet"), Type.Literal("opus")]);
2527
+ var OnAction = Type.Union([Type.Literal("skip"), Type.Literal("invoke")]);
2528
+ var OnClause = Type.Object(
2529
+ {
2530
+ if: Type.String(),
2531
+ // expr-eval expression OR judgments.<file>.<gate>.fires ref
2532
+ invoke: Type.Optional(Type.String()),
2533
+ // '{{ capabilities.<name>.cmd }}' OR literal
2534
+ action: Type.Optional(OnAction)
2535
+ },
2536
+ { additionalProperties: false }
2537
+ );
2538
+ var FallbackMaxIterationsExceeded = Type.Object(
2539
+ {
2540
+ action: Type.Literal("emit_warning_and_halt"),
2541
+ // R20.10 acceptance c "explicit NOT silent"
2542
+ message: Type.String(),
2543
+ exit_code: Type.Number()
2544
+ },
2545
+ { additionalProperties: false }
2546
+ );
2547
+ var PhaseFallback = Type.Object(
2548
+ {
2549
+ max_iterations_exceeded: Type.Optional(FallbackMaxIterationsExceeded)
2550
+ },
2551
+ { additionalProperties: false }
2552
+ );
2553
+ var WorkflowPhaseV2 = Type.Object(
2554
+ {
2555
+ id: Type.String({ minLength: 1 }),
2556
+ name: Type.Optional(Type.String()),
2557
+ upstream: Type.Optional(Type.String()),
2558
+ capability: Type.Optional(Type.String()),
2559
+ // '{{ capabilities.ralph-loop.cmd }}'
2560
+ model: Type.Optional(ModelTier2),
2561
+ invokes: Type.Optional(Type.String()),
2562
+ // legacy slash-cmd OR JINJA template
2563
+ args: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
2564
+ gate: Type.Optional(Type.String()),
2565
+ // judgments.<file>.<gate>.fires 4-level ref
2566
+ on: Type.Optional(Type.Array(OnClause)),
2567
+ parallelism: Type.Optional(Type.String()),
2568
+ // judgments.parallelism-gate.<route>.fires
2569
+ fallback: Type.Optional(PhaseFallback),
2570
+ max_iterations: Type.Optional(
2571
+ Type.Union([Type.Number(), Type.String()])
2572
+ // numeric literal OR jinja '{{ defaults.x.y }}'
2573
+ ),
2574
+ artifacts_expected: Type.Optional(Type.Array(Type.String()))
2575
+ },
2576
+ { additionalProperties: false }
2577
+ );
2578
+ var WorkflowSchemaV2 = Type.Object(
2579
+ {
2580
+ schema_version: Type.Literal(SCHEMA_VERSIONS.workflow),
2581
+ workflow: Type.String({ minLength: 1 }),
2582
+ description: Type.Optional(Type.String()),
2583
+ phases: Type.Array(WorkflowPhaseV2, { minItems: 1 })
2584
+ },
2585
+ { additionalProperties: false }
2586
+ );
2587
+
2349
2588
  // src/workflow/loadPhases.ts
2350
2589
  var PhasesValidationError = class extends Error {
2351
2590
  constructor(errors) {
@@ -2358,10 +2597,18 @@ var PhasesValidationError = class extends Error {
2358
2597
  function loadPhases(yamlPath, vars) {
2359
2598
  const raw = readFileSync(yamlPath, "utf8");
2360
2599
  const parsed = parse(raw);
2361
- if (!Value.Check(PhasesSchema, parsed)) {
2362
- throw new PhasesValidationError([...Value.Errors(PhasesSchema, parsed)]);
2600
+ const isV2 = parsed?.schema_version === "harnessed.workflow.v2";
2601
+ if (isV2) {
2602
+ if (!Value.Check(WorkflowSchemaV2, parsed)) {
2603
+ throw new PhasesValidationError([...Value.Errors(WorkflowSchemaV2, parsed)]);
2604
+ }
2605
+ } else {
2606
+ if (!Value.Check(PhasesSchema, parsed)) {
2607
+ throw new PhasesValidationError([...Value.Errors(PhasesSchema, parsed)]);
2608
+ }
2363
2609
  }
2364
- return parsed;
2610
+ const validated = parsed;
2611
+ return validated;
2365
2612
  }
2366
2613
 
2367
2614
  // src/cli/lib/validateFlags.ts
@@ -2407,9 +2654,20 @@ function registerExecuteTask(program2) {
2407
2654
  );
2408
2655
  process.exit(0);
2409
2656
  }
2657
+ let fallbackConfig;
2658
+ let fallbackPhaseId;
2659
+ for (const ph of phases.phases) {
2660
+ if ("fallback" in ph && ph.fallback?.max_iterations_exceeded) {
2661
+ fallbackConfig = ph.fallback.max_iterations_exceeded;
2662
+ fallbackPhaseId = ph.id;
2663
+ break;
2664
+ }
2665
+ }
2410
2666
  const result = await runRouting(taskCtx, {
2411
2667
  maxIterations: raw.maxIterations ?? 20,
2412
- ...raw.model ? { agentOpts: { modelOverride: raw.model } } : {}
2668
+ ...raw.model ? { agentOpts: { modelOverride: raw.model } } : {},
2669
+ ...fallbackConfig ? { fallbackConfig } : {},
2670
+ ...fallbackPhaseId ? { fallbackPhaseId } : {}
2413
2671
  });
2414
2672
  if ("aborted" in result) {
2415
2673
  console.error(`aborted: ${result.reason}`);
@@ -2894,7 +3152,7 @@ var installCcHookAdd = async (ctx) => {
2894
3152
  return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath] };
2895
3153
  };
2896
3154
  function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
2897
- return new Promise((resolve10) => {
3155
+ return new Promise((resolve11) => {
2898
3156
  const isWin = process.platform === "win32";
2899
3157
  const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
2900
3158
  let stderr = "";
@@ -2903,15 +3161,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
2903
3161
  });
2904
3162
  const timer = setTimeout(() => {
2905
3163
  child.kill("SIGKILL");
2906
- resolve10({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
3164
+ resolve11({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
2907
3165
  }, timeoutMs);
2908
3166
  child.on("error", (e) => {
2909
3167
  clearTimeout(timer);
2910
- resolve10({ exitCode: -1, stderr: `${stderr}${e.message}` });
3168
+ resolve11({ exitCode: -1, stderr: `${stderr}${e.message}` });
2911
3169
  });
2912
3170
  child.on("close", (code) => {
2913
3171
  clearTimeout(timer);
2914
- resolve10({ exitCode: code ?? -1, stderr });
3172
+ resolve11({ exitCode: code ?? -1, stderr });
2915
3173
  });
2916
3174
  });
2917
3175
  }
@@ -3052,7 +3310,7 @@ ${newEntry}
3052
3310
  )
3053
3311
  };
3054
3312
  }
3055
- const vr = await new Promise((resolve10) => {
3313
+ const vr = await new Promise((resolve11) => {
3056
3314
  const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
3057
3315
  let stderr = "";
3058
3316
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -3060,15 +3318,15 @@ ${newEntry}
3060
3318
  });
3061
3319
  const timer = setTimeout(() => {
3062
3320
  child.kill("SIGKILL");
3063
- resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
3321
+ resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
3064
3322
  }, 15e3);
3065
3323
  child.on("error", (e) => {
3066
3324
  clearTimeout(timer);
3067
- resolve10({ exitCode: -1, stderr: e.message });
3325
+ resolve11({ exitCode: -1, stderr: e.message });
3068
3326
  });
3069
3327
  child.on("close", (code) => {
3070
3328
  clearTimeout(timer);
3071
- resolve10({ exitCode: code ?? -1, stderr });
3329
+ resolve11({ exitCode: code ?? -1, stderr });
3072
3330
  });
3073
3331
  });
3074
3332
  if (vr.exitCode !== 0) {
@@ -3124,10 +3382,10 @@ async function spawnCmd(ctx, cmd, args) {
3124
3382
  child.stderr?.setEncoding("utf8").on("data", (chunk) => {
3125
3383
  stderr += chunk;
3126
3384
  });
3127
- return await new Promise((resolve10) => {
3385
+ return await new Promise((resolve11) => {
3128
3386
  const timer = setTimeout(() => {
3129
3387
  child.kill("SIGKILL");
3130
- resolve10({
3388
+ resolve11({
3131
3389
  ok: false,
3132
3390
  phase: "spawn",
3133
3391
  error: {
@@ -3142,7 +3400,7 @@ async function spawnCmd(ctx, cmd, args) {
3142
3400
  }, timeoutMs);
3143
3401
  child.on("error", (err2) => {
3144
3402
  clearTimeout(timer);
3145
- resolve10({
3403
+ resolve11({
3146
3404
  ok: false,
3147
3405
  phase: "spawn",
3148
3406
  error: {
@@ -3157,14 +3415,14 @@ async function spawnCmd(ctx, cmd, args) {
3157
3415
  });
3158
3416
  child.on("close", (code) => {
3159
3417
  clearTimeout(timer);
3160
- resolve10({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
3418
+ resolve11({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
3161
3419
  });
3162
3420
  });
3163
3421
  }
3164
3422
 
3165
3423
  // src/installers/gitCloneWithSetup.ts
3166
3424
  function gitRevParseHead(cwd, timeoutMs = 1e4) {
3167
- return new Promise((resolve10) => {
3425
+ return new Promise((resolve11) => {
3168
3426
  const isWin = process.platform === "win32";
3169
3427
  const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
3170
3428
  let stdout2 = "";
@@ -3173,15 +3431,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
3173
3431
  });
3174
3432
  const timer = setTimeout(() => {
3175
3433
  child.kill("SIGKILL");
3176
- resolve10({ sha: "", exit: -1 });
3434
+ resolve11({ sha: "", exit: -1 });
3177
3435
  }, timeoutMs);
3178
3436
  child.on("error", () => {
3179
3437
  clearTimeout(timer);
3180
- resolve10({ sha: "", exit: -1 });
3438
+ resolve11({ sha: "", exit: -1 });
3181
3439
  });
3182
3440
  child.on("close", (code) => {
3183
3441
  clearTimeout(timer);
3184
- resolve10({ sha: stdout2.trim(), exit: code ?? -1 });
3442
+ resolve11({ sha: stdout2.trim(), exit: code ?? -1 });
3185
3443
  });
3186
3444
  });
3187
3445
  }
@@ -3489,6 +3747,9 @@ ${newEntry}
3489
3747
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
3490
3748
  const r = await runArgs(addArgs, install.cwd ?? ctx.cwd);
3491
3749
  if (r.exitCode !== 0) {
3750
+ if (r.stderr.includes("already exists in .mcp.json")) {
3751
+ return { ok: true, alreadyInstalled: true, backupId: bk.backupId };
3752
+ }
3492
3753
  return {
3493
3754
  ok: false,
3494
3755
  phase: "spawn",
@@ -3518,7 +3779,7 @@ ${newEntry}
3518
3779
  )
3519
3780
  };
3520
3781
  }
3521
- const vr = await new Promise((resolve10) => {
3782
+ const vr = await new Promise((resolve11) => {
3522
3783
  const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
3523
3784
  let stderr = "";
3524
3785
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -3526,15 +3787,15 @@ ${newEntry}
3526
3787
  });
3527
3788
  const timer = setTimeout(() => {
3528
3789
  child.kill("SIGKILL");
3529
- resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
3790
+ resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
3530
3791
  }, 15e3);
3531
3792
  child.on("error", (e) => {
3532
3793
  clearTimeout(timer);
3533
- resolve10({ exitCode: -1, stderr: e.message });
3794
+ resolve11({ exitCode: -1, stderr: e.message });
3534
3795
  });
3535
3796
  child.on("close", (code) => {
3536
3797
  clearTimeout(timer);
3537
- resolve10({ exitCode: code ?? -1, stderr });
3798
+ resolve11({ exitCode: code ?? -1, stderr });
3538
3799
  });
3539
3800
  });
3540
3801
  if (vr.exitCode !== 0) {
@@ -3634,6 +3895,9 @@ ${newEntry}
3634
3895
  if (!bk.ok) return { ok: false, phase: "preflight", error: bk.error };
3635
3896
  const r = await runArgs(addArgs, install.cwd ?? ctx.cwd);
3636
3897
  if (r.exitCode !== 0) {
3898
+ if (r.stderr.includes("already exists in .mcp.json")) {
3899
+ return { ok: true, alreadyInstalled: true, backupId: bk.backupId };
3900
+ }
3637
3901
  return {
3638
3902
  ok: false,
3639
3903
  phase: "spawn",
@@ -3663,7 +3927,7 @@ ${newEntry}
3663
3927
  )
3664
3928
  };
3665
3929
  }
3666
- const vr = await new Promise((resolve10) => {
3930
+ const vr = await new Promise((resolve11) => {
3667
3931
  const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
3668
3932
  let stderr = "";
3669
3933
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -3671,15 +3935,15 @@ ${newEntry}
3671
3935
  });
3672
3936
  const timer = setTimeout(() => {
3673
3937
  child.kill("SIGKILL");
3674
- resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
3938
+ resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
3675
3939
  }, 15e3);
3676
3940
  child.on("error", (e) => {
3677
3941
  clearTimeout(timer);
3678
- resolve10({ exitCode: -1, stderr: e.message });
3942
+ resolve11({ exitCode: -1, stderr: e.message });
3679
3943
  });
3680
3944
  child.on("close", (code) => {
3681
3945
  clearTimeout(timer);
3682
- resolve10({ exitCode: code ?? -1, stderr });
3946
+ resolve11({ exitCode: code ?? -1, stderr });
3683
3947
  });
3684
3948
  });
3685
3949
  if (vr.exitCode !== 0) {
@@ -4068,6 +4332,7 @@ function registerInstallBase(program2) {
4068
4332
  color: "auto"
4069
4333
  };
4070
4334
  const installed = [];
4335
+ const alreadyInstalled = [];
4071
4336
  const skipped = [];
4072
4337
  const failed = [];
4073
4338
  for (const path of await listBaseManifests(getPackageRoot())) {
@@ -4091,18 +4356,21 @@ function registerInstallBase(program2) {
4091
4356
  }
4092
4357
  const r = await runInstall(v.manifest, opts);
4093
4358
  if ("aborted" in r) skipped.push({ name, reason: `aborted: ${r.reason}` });
4359
+ else if (r.ok && "alreadyInstalled" in r && r.alreadyInstalled) alreadyInstalled.push(name);
4094
4360
  else if (r.ok) installed.push(name);
4095
4361
  else failed.push({ name, reason: r.error.message });
4096
4362
  }
4097
4363
  console.log(
4098
4364
  `
4099
- installed: ${installed.length} / skipped (deferred phase 2.1): ${skipped.length} / failed: ${failed.length}`
4365
+ installed: ${installed.length} / already-installed: ${alreadyInstalled.length} / skipped (deferred phase 2.1): ${skipped.length} / failed: ${failed.length}`
4100
4366
  );
4101
- for (const i of installed) console.log(` installed ${i}`);
4102
- for (const s of skipped) console.log(` skipped ${s.name} \u2014 ${s.reason}`);
4103
- for (const f of failed) console.error(` failed ${f.name} \u2014 ${f.reason}`);
4367
+ for (const i of installed) console.log(` installed ${i}`);
4368
+ for (const a of alreadyInstalled)
4369
+ console.log(` already-installed ${a} \u2014 run \`/mcp\` in Claude Code to verify connection`);
4370
+ for (const s of skipped) console.log(` skipped ${s.name} \u2014 ${s.reason}`);
4371
+ for (const f of failed) console.error(` failed ${f.name} \u2014 ${f.reason}`);
4104
4372
  if (failed.length > 0) process.exit(1);
4105
- if (installed.length === 0) process.exit(2);
4373
+ if (installed.length === 0 && alreadyInstalled.length === 0) process.exit(2);
4106
4374
  process.exit(0);
4107
4375
  });
4108
4376
  }
@@ -4288,12 +4556,96 @@ function registerRollback(program2) {
4288
4556
  console.log(`restored ${meta.files.length} file(s) from ${timestamp}`);
4289
4557
  });
4290
4558
  }
4559
+ init_checkAgentTeams();
4291
4560
  var PHASE_212 = /* @__PURE__ */ new Set([
4292
4561
  "cc-plugin-marketplace",
4293
4562
  "git-clone-with-setup",
4294
4563
  "npx-skill-installer",
4295
4564
  "mcp-http-add"
4296
4565
  ]);
4566
+ async function warnIfAgentTeamsMissing() {
4567
+ const r = await checkAgentTeams();
4568
+ if (r.status !== "missing") return;
4569
+ console.warn("\n\u26A0\uFE0F Agent Teams \u672A\u542F\u7528 \u2014 parallelism-gate \u5347\u7EA7\u8DEF\u5F84\u4E0D\u53EF\u7528");
4570
+ console.warn(" \u4FEE\u590D: claude config set env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS 1");
4571
+ console.warn(
4572
+ " \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)"
4573
+ );
4574
+ console.warn(
4575
+ " \u4E0D\u963B\u585E setup,\u540E\u7EED parallelism-gate workflow phase \u89E6\u53D1\u65F6\u81EA\u52A8\u964D\u7EA7 subagent fan-out\n"
4576
+ );
4577
+ }
4578
+ async function scanWorkflowsWithSkill(workflowsDir, entries) {
4579
+ const out = [];
4580
+ for (const entry of entries.sort()) {
4581
+ const src = join(workflowsDir, entry);
4582
+ try {
4583
+ const s = await stat(src);
4584
+ if (!s.isDirectory()) continue;
4585
+ await stat(join(src, "SKILL.md"));
4586
+ out.push(entry);
4587
+ } catch {
4588
+ }
4589
+ }
4590
+ return out;
4591
+ }
4592
+ async function runStepBInstall(manifestPaths) {
4593
+ const opts = {
4594
+ apply: true,
4595
+ dryRun: false,
4596
+ system: false,
4597
+ nonInteractive: true,
4598
+ fullDiff: false,
4599
+ color: "auto"
4600
+ };
4601
+ const start = Date.now();
4602
+ const settled = await Promise.allSettled(
4603
+ manifestPaths.map(async (path) => {
4604
+ let yamlSrc;
4605
+ try {
4606
+ yamlSrc = await readFile(path, "utf8");
4607
+ } catch (e) {
4608
+ return { status: "failed", name: path, reason: `read: ${e.message}` };
4609
+ }
4610
+ const v = validateManifestFile(yamlSrc, path);
4611
+ if (!v.ok) {
4612
+ return {
4613
+ status: "failed",
4614
+ name: path,
4615
+ reason: `validate: ${v.errors[0]?.message ?? "unknown"}`
4616
+ };
4617
+ }
4618
+ const name = v.manifest.metadata.name;
4619
+ const method = v.manifest.spec.install.method;
4620
+ if (PHASE_212.has(method)) return { status: "skipped", name };
4621
+ const r = await runInstall(v.manifest, opts);
4622
+ if ("aborted" in r) return { status: "skipped", name };
4623
+ if (r.ok && "alreadyInstalled" in r && r.alreadyInstalled)
4624
+ return { status: "already-installed", name };
4625
+ if (r.ok) return { status: "installed", name };
4626
+ return { status: "failed", name, reason: r.error.message };
4627
+ })
4628
+ );
4629
+ const installed = [];
4630
+ const alreadyInstalled = [];
4631
+ const skipped = [];
4632
+ const failed = [];
4633
+ for (const s of settled) {
4634
+ const v = s.status === "fulfilled" ? s.value : {
4635
+ status: "failed",
4636
+ name: "?",
4637
+ reason: String(s.reason)
4638
+ };
4639
+ if (v.status === "installed") installed.push(v.name);
4640
+ else if (v.status === "already-installed") alreadyInstalled.push(v.name);
4641
+ else if (v.status === "skipped") skipped.push(v.name);
4642
+ else
4643
+ failed.push(`${v.name}: ${v.reason}`);
4644
+ }
4645
+ return { installed, alreadyInstalled, skipped, failed, elapsedMs: Date.now() - start };
4646
+ }
4647
+
4648
+ // src/cli/setup.ts
4297
4649
  async function listBaseManifests2(pkgRoot) {
4298
4650
  const out = [];
4299
4651
  for (const d of ["manifests/tools", "manifests/skill-packs"]) {
@@ -4313,6 +4665,7 @@ function registerSetup(program2) {
4313
4665
  const pkgRoot = getPackageRoot();
4314
4666
  const workflowsDir = resolve(pkgRoot, "workflows");
4315
4667
  const skillsBase = resolve(homedir(), ".claude", "skills");
4668
+ await warnIfAgentTeamsMissing();
4316
4669
  let entries;
4317
4670
  try {
4318
4671
  entries = await readdir(workflowsDir);
@@ -4320,17 +4673,7 @@ function registerSetup(program2) {
4320
4673
  console.error(`error: workflows directory not found at ${workflowsDir}`);
4321
4674
  process.exit(1);
4322
4675
  }
4323
- const toInstall = [];
4324
- for (const entry of entries.sort()) {
4325
- const src = join(workflowsDir, entry);
4326
- try {
4327
- const s = await stat(src);
4328
- if (!s.isDirectory()) continue;
4329
- await stat(join(src, "SKILL.md"));
4330
- toInstall.push(entry);
4331
- } catch {
4332
- }
4333
- }
4676
+ const toInstall = await scanWorkflowsWithSkill(workflowsDir, entries);
4334
4677
  if (toInstall.length === 0) {
4335
4678
  console.log("setup: no workflow directories with SKILL.md found \u2014 nothing to install");
4336
4679
  process.exit(2);
@@ -4362,73 +4705,34 @@ function registerSetup(program2) {
4362
4705
  `
4363
4706
  Step A complete: ${skillsInstalled} workflow skill(s) installed to ${skillsBase}`
4364
4707
  );
4365
- const opts = {
4366
- apply: true,
4367
- dryRun: false,
4368
- system: false,
4369
- nonInteractive: true,
4370
- fullDiff: false,
4371
- color: "auto"
4372
- };
4373
4708
  const manifestPaths = await listBaseManifests2(pkgRoot);
4374
- const stepBStart = Date.now();
4375
- const settled = await Promise.allSettled(
4376
- manifestPaths.map(async (path) => {
4377
- let yamlSrc;
4378
- try {
4379
- yamlSrc = await readFile(path, "utf8");
4380
- } catch (e) {
4381
- return {
4382
- status: "failed",
4383
- name: path,
4384
- reason: `read: ${e.message}`
4385
- };
4386
- }
4387
- const v = validateManifestFile(yamlSrc, path);
4388
- if (!v.ok) {
4389
- return {
4390
- status: "failed",
4391
- name: path,
4392
- reason: `validate: ${v.errors[0]?.message ?? "unknown"}`
4393
- };
4394
- }
4395
- const name = v.manifest.metadata.name;
4396
- const method = v.manifest.spec.install.method;
4397
- if (PHASE_212.has(method)) {
4398
- return { status: "skipped", name };
4399
- }
4400
- const r = await runInstall(v.manifest, opts);
4401
- if ("aborted" in r) return { status: "skipped", name };
4402
- if (r.ok) return { status: "installed", name };
4403
- return { status: "failed", name, reason: r.error.message };
4404
- })
4709
+ const b = await runStepBInstall(manifestPaths);
4710
+ const stepBMs = (b.elapsedMs / 1e3).toFixed(1);
4711
+ console.log(
4712
+ `Step B complete: ${b.installed.length} manifest(s) installed / ${b.alreadyInstalled.length} already-installed / ${b.skipped.length} skipped / ${b.failed.length} failed [parallel ${stepBMs}s]`
4405
4713
  );
4406
- const baseInstalled = [];
4407
- const baseSkipped = [];
4408
- const baseFailed = [];
4409
- for (const s of settled) {
4410
- const v = s.status === "fulfilled" ? s.value : {
4411
- status: "failed",
4412
- name: "?",
4413
- reason: String(s.reason)
4414
- };
4415
- if (v.status === "installed") baseInstalled.push(v.name);
4416
- else if (v.status === "skipped") baseSkipped.push(v.name);
4417
- else
4418
- baseFailed.push(
4419
- `${v.name}: ${v.reason}`
4420
- );
4714
+ for (const n of b.installed) console.log(` [B] installed ${n}`);
4715
+ for (const n of b.alreadyInstalled)
4716
+ console.log(
4717
+ ` [B] already-installed ${n} \u2014 run \`/mcp\` in Claude Code to verify connection`
4718
+ );
4719
+ for (const n of b.skipped) console.log(` [B] skipped ${n}`);
4720
+ for (const n of b.failed) console.error(` [B] failed ${n}`);
4721
+ console.log(
4722
+ `
4723
+ setup complete: ${skillsInstalled} workflow skill(s) + ${b.installed.length + b.alreadyInstalled.length} base manifest(s) configured`
4724
+ );
4725
+ if (b.alreadyInstalled.length > 0 || b.installed.length > 0) {
4726
+ console.log(
4727
+ `
4728
+ MCP servers configured. Run \`/mcp\` in Claude Code to verify each server's connection status. If a server shows disconnected, restart Claude Code or check the MCP command spec.`
4729
+ );
4421
4730
  }
4422
- const stepBMs = ((Date.now() - stepBStart) / 1e3).toFixed(1);
4423
4731
  console.log(
4424
- `Step B complete: ${baseInstalled.length} manifest(s) installed / ${baseSkipped.length} skipped / ${baseFailed.length} failed [parallel ${stepBMs}s]`
4732
+ "\n\u2713 harnessed v2.0 \u4E09\u5C42\u6808\u65B9\u6CD5\u8BBA bundled \u2014 4 workflows + 6 judgments + 37 capabilities ready"
4425
4733
  );
4426
- for (const n of baseInstalled) console.log(` [B] installed ${n}`);
4427
- for (const n of baseSkipped) console.log(` [B] skipped ${n}`);
4428
- for (const n of baseFailed) console.error(` [B] failed ${n}`);
4429
4734
  console.log(
4430
- `
4431
- setup complete: ${skillsInstalled} workflow skill(s) + ${baseInstalled.length} base manifest(s) installed`
4735
+ " workflows in <packageRoot>/workflows/ (Pure bundled, NOT user-dir override per D-01)"
4432
4736
  );
4433
4737
  process.exit(0);
4434
4738
  });
@@ -4654,7 +4958,7 @@ var uninstallNpmCli = async (ctx) => {
4654
4958
  const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
4655
4959
  const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
4656
4960
  const isWin = process.platform === "win32";
4657
- const result = await new Promise((resolve10) => {
4961
+ const result = await new Promise((resolve11) => {
4658
4962
  const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
4659
4963
  let stderr = "";
4660
4964
  child.stderr?.setEncoding("utf8").on("data", (c) => {
@@ -4662,15 +4966,15 @@ var uninstallNpmCli = async (ctx) => {
4662
4966
  });
4663
4967
  const timer = setTimeout(() => {
4664
4968
  child.kill("SIGKILL");
4665
- resolve10({ exitCode: -1, stderr: `${stderr}[timeout]` });
4969
+ resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
4666
4970
  }, 3e4);
4667
4971
  child.on("error", (e) => {
4668
4972
  clearTimeout(timer);
4669
- resolve10({ exitCode: -1, stderr: e.message });
4973
+ resolve11({ exitCode: -1, stderr: e.message });
4670
4974
  });
4671
4975
  child.on("close", (code) => {
4672
4976
  clearTimeout(timer);
4673
- resolve10({ exitCode: code ?? -1, stderr });
4977
+ resolve11({ exitCode: code ?? -1, stderr });
4674
4978
  });
4675
4979
  });
4676
4980
  if (result.exitCode !== 0) {