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/README.md +1 -1
- package/dist/cli.mjs +435 -131
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/workflows/capabilities.yaml +413 -0
- package/workflows/defaults.yaml +36 -0
- package/workflows/execute-task/SKILL.md +22 -9
- package/workflows/execute-task/phases.yaml +60 -14
- package/workflows/judgments/fallback.yaml +35 -0
- package/workflows/judgments/parallelism-gate.yaml +47 -0
- package/workflows/judgments/phase-gate.yaml +17 -0
- package/workflows/judgments/strategic-gate.yaml +25 -0
- package/workflows/judgments/subtask-gate.yaml +17 -0
- package/workflows/judgments/tdd-gate.yaml +17 -0
- package/workflows/plan-feature/SKILL.md +56 -14
- package/workflows/plan-feature/workflow.yaml +66 -25
- package/workflows/research/SKILL.md +35 -0
- package/workflows/research/workflow.yaml +18 -0
- package/workflows/verify-work/SKILL.md +92 -0
- package/workflows/verify-work/workflow.yaml +143 -0
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
|
|
152
|
-
return
|
|
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: "
|
|
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((
|
|
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
|
-
|
|
1578
|
+
resolve11(1);
|
|
1464
1579
|
} else {
|
|
1465
1580
|
reject(err2);
|
|
1466
1581
|
}
|
|
1467
1582
|
});
|
|
1468
|
-
child.on("close", (code) =>
|
|
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 [
|
|
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
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1782
|
+
checkTokenBudget2(),
|
|
1783
|
+
checkAgentTeamsEnv(),
|
|
1784
|
+
checkPlanningPlugin()
|
|
1665
1785
|
]);
|
|
1666
1786
|
const results = [
|
|
1667
1787
|
checkNodeVersion(),
|
|
1668
|
-
|
|
1788
|
+
mcp,
|
|
1669
1789
|
checkJq(),
|
|
1670
1790
|
checkWinBash(),
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
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
|
-
|
|
2362
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
3164
|
+
resolve11({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
|
|
2907
3165
|
}, timeoutMs);
|
|
2908
3166
|
child.on("error", (e) => {
|
|
2909
3167
|
clearTimeout(timer);
|
|
2910
|
-
|
|
3168
|
+
resolve11({ exitCode: -1, stderr: `${stderr}${e.message}` });
|
|
2911
3169
|
});
|
|
2912
3170
|
child.on("close", (code) => {
|
|
2913
3171
|
clearTimeout(timer);
|
|
2914
|
-
|
|
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((
|
|
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
|
-
|
|
3321
|
+
resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3064
3322
|
}, 15e3);
|
|
3065
3323
|
child.on("error", (e) => {
|
|
3066
3324
|
clearTimeout(timer);
|
|
3067
|
-
|
|
3325
|
+
resolve11({ exitCode: -1, stderr: e.message });
|
|
3068
3326
|
});
|
|
3069
3327
|
child.on("close", (code) => {
|
|
3070
3328
|
clearTimeout(timer);
|
|
3071
|
-
|
|
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((
|
|
3385
|
+
return await new Promise((resolve11) => {
|
|
3128
3386
|
const timer = setTimeout(() => {
|
|
3129
3387
|
child.kill("SIGKILL");
|
|
3130
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
3434
|
+
resolve11({ sha: "", exit: -1 });
|
|
3177
3435
|
}, timeoutMs);
|
|
3178
3436
|
child.on("error", () => {
|
|
3179
3437
|
clearTimeout(timer);
|
|
3180
|
-
|
|
3438
|
+
resolve11({ sha: "", exit: -1 });
|
|
3181
3439
|
});
|
|
3182
3440
|
child.on("close", (code) => {
|
|
3183
3441
|
clearTimeout(timer);
|
|
3184
|
-
|
|
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((
|
|
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
|
-
|
|
3790
|
+
resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3530
3791
|
}, 15e3);
|
|
3531
3792
|
child.on("error", (e) => {
|
|
3532
3793
|
clearTimeout(timer);
|
|
3533
|
-
|
|
3794
|
+
resolve11({ exitCode: -1, stderr: e.message });
|
|
3534
3795
|
});
|
|
3535
3796
|
child.on("close", (code) => {
|
|
3536
3797
|
clearTimeout(timer);
|
|
3537
|
-
|
|
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((
|
|
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
|
-
|
|
3938
|
+
resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3675
3939
|
}, 15e3);
|
|
3676
3940
|
child.on("error", (e) => {
|
|
3677
3941
|
clearTimeout(timer);
|
|
3678
|
-
|
|
3942
|
+
resolve11({ exitCode: -1, stderr: e.message });
|
|
3679
3943
|
});
|
|
3680
3944
|
child.on("close", (code) => {
|
|
3681
3945
|
clearTimeout(timer);
|
|
3682
|
-
|
|
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
|
|
4102
|
-
for (const
|
|
4103
|
-
|
|
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
|
|
4375
|
-
const
|
|
4376
|
-
|
|
4377
|
-
|
|
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
|
|
4407
|
-
const
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
4969
|
+
resolve11({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
4666
4970
|
}, 3e4);
|
|
4667
4971
|
child.on("error", (e) => {
|
|
4668
4972
|
clearTimeout(timer);
|
|
4669
|
-
|
|
4973
|
+
resolve11({ exitCode: -1, stderr: e.message });
|
|
4670
4974
|
});
|
|
4671
4975
|
child.on("close", (code) => {
|
|
4672
4976
|
clearTimeout(timer);
|
|
4673
|
-
|
|
4977
|
+
resolve11({ exitCode: code ?? -1, stderr });
|
|
4674
4978
|
});
|
|
4675
4979
|
});
|
|
4676
4980
|
if (result.exitCode !== 0) {
|