opencode-swarm 7.47.0 → 7.48.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/index.js +3 -7
- package/dist/commands/tool-policy.d.ts +1 -1
- package/dist/index.js +1270 -1148
- package/dist/tools/phase-complete/gates/architecture-supervisor-gate.d.ts +9 -0
- package/dist/tools/phase-complete/gates/completion-verify-gate.d.ts +6 -0
- package/dist/tools/phase-complete/gates/drift-gate.d.ts +7 -0
- package/dist/tools/phase-complete/gates/final-council-gate.d.ts +7 -0
- package/dist/tools/phase-complete/gates/hallucination-gate.d.ts +6 -0
- package/dist/tools/phase-complete/gates/index.d.ts +12 -0
- package/dist/tools/phase-complete/gates/mutation-gate.d.ts +6 -0
- package/dist/tools/phase-complete/gates/phase-council-gate.d.ts +6 -0
- package/dist/tools/phase-complete/gates/types.d.ts +39 -0
- package/dist/tools/phase-complete.d.ts +14 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ var package_default;
|
|
|
69
69
|
var init_package = __esm(() => {
|
|
70
70
|
package_default = {
|
|
71
71
|
name: "opencode-swarm",
|
|
72
|
-
version: "7.
|
|
72
|
+
version: "7.48.0",
|
|
73
73
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
74
74
|
main: "dist/index.js",
|
|
75
75
|
types: "dist/index.d.ts",
|
|
@@ -616,6 +616,7 @@ var init_constants = __esm(() => {
|
|
|
616
616
|
"syntax_check",
|
|
617
617
|
"search",
|
|
618
618
|
"summarize_work",
|
|
619
|
+
"knowledge_recall",
|
|
619
620
|
"swarm_command"
|
|
620
621
|
],
|
|
621
622
|
sme: [
|
|
@@ -81429,17 +81430,13 @@ var init_tool_policy = __esm(() => {
|
|
|
81429
81430
|
"agents",
|
|
81430
81431
|
"config",
|
|
81431
81432
|
"config doctor",
|
|
81432
|
-
"config-doctor",
|
|
81433
|
-
"doctor",
|
|
81434
81433
|
"doctor tools",
|
|
81435
81434
|
"status",
|
|
81436
81435
|
"show-plan",
|
|
81437
|
-
"plan",
|
|
81438
81436
|
"help",
|
|
81439
81437
|
"history",
|
|
81440
81438
|
"evidence",
|
|
81441
81439
|
"evidence summary",
|
|
81442
|
-
"evidence-summary",
|
|
81443
81440
|
"retrieve",
|
|
81444
81441
|
"diagnose",
|
|
81445
81442
|
"preflight",
|
|
@@ -81456,8 +81453,7 @@ var init_tool_policy = __esm(() => {
|
|
|
81456
81453
|
"memory import",
|
|
81457
81454
|
"memory migrate",
|
|
81458
81455
|
"sync-plan",
|
|
81459
|
-
"export"
|
|
81460
|
-
"list-agents"
|
|
81456
|
+
"export"
|
|
81461
81457
|
];
|
|
81462
81458
|
SWARM_COMMAND_TOOL_ALLOWLIST = new Set([
|
|
81463
81459
|
"agents",
|
|
@@ -83441,7 +83437,7 @@ the safe \`spec_write\` tool. Use it when:
|
|
|
83441
83437
|
- requirements decomposition is non-trivial,
|
|
83442
83438
|
- you would otherwise inline-author \`.swarm/spec.md\` yourself.
|
|
83443
83439
|
|
|
83444
|
-
Continue handling small touch-ups (typos, cross-references)
|
|
83440
|
+
Continue handling small touch-ups (typos, cross-references) via the spec_writer agent — the architect lacks the spec_write tool and must delegate all spec changes.
|
|
83445
83441
|
|
|
83446
83442
|
### ANTI-RATIONALIZATION
|
|
83447
83443
|
- ✗ "The coder already knows these conventions" → Skills contain project-specific rules the model cannot know from training. Always pass.
|
|
@@ -83745,6 +83741,24 @@ HARD CONSTRAINTS (apply regardless of skill load success):
|
|
|
83745
83741
|
- Do NOT touch .swarm/spec.md, CHANGELOG.md, or docs/releases/pending/* in this mode.
|
|
83746
83742
|
- Requires design_docs.enabled: true — if the docs_design agent is not registered, instruct the user to enable it and stop.
|
|
83747
83743
|
|
|
83744
|
+
### MODE: PR_REVIEW
|
|
83745
|
+
Activates when: architect receives \`[MODE: PR_REVIEW pr="https://github.com/..." council=true/false]\` signal from the pr-review command handler.
|
|
83746
|
+
|
|
83747
|
+
Purpose: Read-only structured PR review using parallel explorer lanes, independent reviewer validation, critic challenge, and synthesis. Does NOT mutate source code. Does NOT delegate to coder.
|
|
83748
|
+
|
|
83749
|
+
ACTION: Load skill file:.opencode/skills/swarm-pr-review/SKILL.md immediately and follow its protocol.
|
|
83750
|
+
|
|
83751
|
+
HARD CONSTRAINTS (apply regardless of skill load success):
|
|
83752
|
+
- Do NOT delegate to coder
|
|
83753
|
+
- Do NOT call declare_scope
|
|
83754
|
+
- Do NOT mutate source code
|
|
83755
|
+
- Do NOT create or modify files outside .swarm/
|
|
83756
|
+
- The orchestrator MUST NOT classify, confirm, disprove, or judge explorer candidates — validation is exclusively the reviewer's job
|
|
83757
|
+
- Explorers produce candidates only — reviewers verify or reject — critics challenge HIGH/CRITICAL and borderline findings
|
|
83758
|
+
- No finding may appear as CONFIRMED in the final report without reviewer validation provenance
|
|
83759
|
+
- Test execution, explorer lanes, reviewer dispatch, and critic challenge are all permitted within this mode
|
|
83760
|
+
- Quality is the only metric — time, tokens, and agent dispatches are irrelevant to correctness
|
|
83761
|
+
|
|
83748
83762
|
### MODE: ISSUE_INGEST
|
|
83749
83763
|
Activates when the user invokes /swarm issue <url> or the architect receives an ISSUE_INGEST signal.
|
|
83750
83764
|
|
|
@@ -92278,11 +92292,11 @@ __export(exports_design_doc_drift, {
|
|
|
92278
92292
|
runDesignDocDriftCheck: () => runDesignDocDriftCheck,
|
|
92279
92293
|
_internals: () => _internals55
|
|
92280
92294
|
});
|
|
92281
|
-
import * as
|
|
92282
|
-
import * as
|
|
92295
|
+
import * as fs94 from "node:fs";
|
|
92296
|
+
import * as path131 from "node:path";
|
|
92283
92297
|
function mtimeMsOrNull(absPath) {
|
|
92284
92298
|
try {
|
|
92285
|
-
return
|
|
92299
|
+
return fs94.statSync(absPath).mtimeMs;
|
|
92286
92300
|
} catch {
|
|
92287
92301
|
return null;
|
|
92288
92302
|
}
|
|
@@ -92290,40 +92304,40 @@ function mtimeMsOrNull(absPath) {
|
|
|
92290
92304
|
function resolveAnchorWithin(directory, anchor) {
|
|
92291
92305
|
if (!anchor || typeof anchor !== "string")
|
|
92292
92306
|
return null;
|
|
92293
|
-
const root =
|
|
92294
|
-
const resolved =
|
|
92295
|
-
const rel =
|
|
92296
|
-
if (rel.startsWith("..") ||
|
|
92307
|
+
const root = path131.resolve(directory);
|
|
92308
|
+
const resolved = path131.resolve(root, anchor);
|
|
92309
|
+
const rel = path131.relative(root, resolved);
|
|
92310
|
+
if (rel.startsWith("..") || path131.isAbsolute(rel))
|
|
92297
92311
|
return null;
|
|
92298
92312
|
return resolved;
|
|
92299
92313
|
}
|
|
92300
92314
|
async function runDesignDocDriftCheck(directory, phase, outDir) {
|
|
92301
92315
|
try {
|
|
92302
|
-
const root =
|
|
92303
|
-
const outAbs =
|
|
92304
|
-
const outRel =
|
|
92305
|
-
if (outRel.startsWith("..") ||
|
|
92316
|
+
const root = path131.resolve(directory);
|
|
92317
|
+
const outAbs = path131.resolve(root, outDir);
|
|
92318
|
+
const outRel = path131.relative(root, outAbs);
|
|
92319
|
+
if (outRel.startsWith("..") || path131.isAbsolute(outRel)) {
|
|
92306
92320
|
return null;
|
|
92307
92321
|
}
|
|
92308
92322
|
const docMtimes = new Map;
|
|
92309
92323
|
const checkedDocs = [];
|
|
92310
92324
|
const missingDocs = [];
|
|
92311
92325
|
for (const [docName, relFile] of Object.entries(DESIGN_DOC_FILES)) {
|
|
92312
|
-
const abs =
|
|
92326
|
+
const abs = path131.join(outAbs, relFile);
|
|
92313
92327
|
const mtime = mtimeMsOrNull(abs);
|
|
92314
92328
|
docMtimes.set(docName, mtime);
|
|
92315
92329
|
if (mtime === null) {
|
|
92316
|
-
missingDocs.push(
|
|
92330
|
+
missingDocs.push(path131.join(outDir, relFile));
|
|
92317
92331
|
} else {
|
|
92318
|
-
checkedDocs.push(
|
|
92332
|
+
checkedDocs.push(path131.join(outDir, relFile));
|
|
92319
92333
|
}
|
|
92320
92334
|
}
|
|
92321
|
-
const traceabilityAbs =
|
|
92335
|
+
const traceabilityAbs = path131.join(outAbs, TRACEABILITY_REL);
|
|
92322
92336
|
let registry3 = null;
|
|
92323
92337
|
try {
|
|
92324
|
-
const stat9 = await
|
|
92338
|
+
const stat9 = await fs94.promises.stat(traceabilityAbs);
|
|
92325
92339
|
if (stat9.size <= MAX_TRACEABILITY_BYTES) {
|
|
92326
|
-
const raw = await
|
|
92340
|
+
const raw = await fs94.promises.readFile(traceabilityAbs, "utf-8");
|
|
92327
92341
|
const parsed = JSON.parse(raw);
|
|
92328
92342
|
registry3 = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
92329
92343
|
}
|
|
@@ -92331,7 +92345,7 @@ async function runDesignDocDriftCheck(directory, phase, outDir) {
|
|
|
92331
92345
|
registry3 = null;
|
|
92332
92346
|
}
|
|
92333
92347
|
const noDocs = checkedDocs.length === 0 || registry3 === null;
|
|
92334
|
-
const specMtime = mtimeMsOrNull(
|
|
92348
|
+
const specMtime = mtimeMsOrNull(path131.join(root, ".swarm", "spec.md"));
|
|
92335
92349
|
const staleSections = [];
|
|
92336
92350
|
if (!noDocs && Array.isArray(registry3?.sections)) {
|
|
92337
92351
|
for (const section of registry3.sections) {
|
|
@@ -92387,8 +92401,8 @@ async function runDesignDocDriftCheck(directory, phase, outDir) {
|
|
|
92387
92401
|
};
|
|
92388
92402
|
const filename = `${DOC_DRIFT_REPORT_PREFIX}${phase}.json`;
|
|
92389
92403
|
const filePath = validateSwarmPath(directory, filename);
|
|
92390
|
-
await
|
|
92391
|
-
await
|
|
92404
|
+
await fs94.promises.mkdir(path131.dirname(filePath), { recursive: true });
|
|
92405
|
+
await fs94.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
|
|
92392
92406
|
getGlobalEventBus().publish("curator.docdrift.completed", {
|
|
92393
92407
|
phase,
|
|
92394
92408
|
verdict,
|
|
@@ -92418,10 +92432,10 @@ var init_design_doc_drift = __esm(() => {
|
|
|
92418
92432
|
domain: "domain.md",
|
|
92419
92433
|
"technical-spec": "technical-spec.md",
|
|
92420
92434
|
"behavior-spec": "behavior-spec.md",
|
|
92421
|
-
"reference-impl":
|
|
92422
|
-
"idiom-notes":
|
|
92435
|
+
"reference-impl": path131.join("reference", "reference-impl.md"),
|
|
92436
|
+
"idiom-notes": path131.join("reference", "idiom-notes.md")
|
|
92423
92437
|
};
|
|
92424
|
-
TRACEABILITY_REL =
|
|
92438
|
+
TRACEABILITY_REL = path131.join("reference", "traceability.json");
|
|
92425
92439
|
_internals55 = {
|
|
92426
92440
|
mtimeMsOrNull,
|
|
92427
92441
|
resolveAnchorWithin,
|
|
@@ -92436,12 +92450,12 @@ __export(exports_project_context, {
|
|
|
92436
92450
|
_internals: () => _internals71,
|
|
92437
92451
|
LANG_BACKEND_DETECTION_TIMEOUT_MS: () => LANG_BACKEND_DETECTION_TIMEOUT_MS
|
|
92438
92452
|
});
|
|
92439
|
-
import * as
|
|
92440
|
-
import * as
|
|
92453
|
+
import * as fs123 from "node:fs";
|
|
92454
|
+
import * as path166 from "node:path";
|
|
92441
92455
|
function detectFileExists2(directory, pattern) {
|
|
92442
92456
|
if (pattern.includes("*") || pattern.includes("?")) {
|
|
92443
92457
|
try {
|
|
92444
|
-
const files =
|
|
92458
|
+
const files = fs123.readdirSync(directory);
|
|
92445
92459
|
const regex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
|
|
92446
92460
|
return files.some((f) => regex.test(f));
|
|
92447
92461
|
} catch {
|
|
@@ -92449,7 +92463,7 @@ function detectFileExists2(directory, pattern) {
|
|
|
92449
92463
|
}
|
|
92450
92464
|
}
|
|
92451
92465
|
try {
|
|
92452
|
-
|
|
92466
|
+
fs123.accessSync(path166.join(directory, pattern));
|
|
92453
92467
|
return true;
|
|
92454
92468
|
} catch {
|
|
92455
92469
|
return false;
|
|
@@ -92458,7 +92472,7 @@ function detectFileExists2(directory, pattern) {
|
|
|
92458
92472
|
function selectTestCommandFromScriptsTest(backend, directory) {
|
|
92459
92473
|
let pkgRaw;
|
|
92460
92474
|
try {
|
|
92461
|
-
pkgRaw =
|
|
92475
|
+
pkgRaw = fs123.readFileSync(path166.join(directory, "package.json"), "utf-8");
|
|
92462
92476
|
} catch {
|
|
92463
92477
|
return null;
|
|
92464
92478
|
}
|
|
@@ -92567,7 +92581,7 @@ var init_project_context = __esm(() => {
|
|
|
92567
92581
|
init_package();
|
|
92568
92582
|
init_agents2();
|
|
92569
92583
|
init_critic();
|
|
92570
|
-
import * as
|
|
92584
|
+
import * as path167 from "node:path";
|
|
92571
92585
|
|
|
92572
92586
|
// src/background/index.ts
|
|
92573
92587
|
init_event_bus();
|
|
@@ -111461,10 +111475,9 @@ init_lint();
|
|
|
111461
111475
|
init_zod();
|
|
111462
111476
|
init_config();
|
|
111463
111477
|
init_schema();
|
|
111464
|
-
init_qa_gate_profile();
|
|
111465
111478
|
init_manager2();
|
|
111466
|
-
import * as
|
|
111467
|
-
import * as
|
|
111479
|
+
import * as fs95 from "node:fs";
|
|
111480
|
+
import * as path132 from "node:path";
|
|
111468
111481
|
|
|
111469
111482
|
// src/full-auto/phase-approval.ts
|
|
111470
111483
|
init_utils2();
|
|
@@ -111643,7 +111656,6 @@ init_ledger();
|
|
|
111643
111656
|
init_manager();
|
|
111644
111657
|
init_snapshot_writer();
|
|
111645
111658
|
init_state();
|
|
111646
|
-
init_store();
|
|
111647
111659
|
init_telemetry();
|
|
111648
111660
|
|
|
111649
111661
|
// src/turbo/lean/phase-ready.ts
|
|
@@ -112137,12 +112149,765 @@ function verifyLeanTurboPhaseReady(directory, phase, sessionIDOrConfig, config3)
|
|
|
112137
112149
|
// src/tools/phase-complete.ts
|
|
112138
112150
|
init_logger();
|
|
112139
112151
|
init_create_tool();
|
|
112152
|
+
|
|
112153
|
+
// src/tools/phase-complete/gates/architecture-supervisor-gate.ts
|
|
112154
|
+
init_store();
|
|
112155
|
+
async function runArchitectureSupervisorGate(ctx) {
|
|
112156
|
+
const { phase, dir, pluginConfig, agentsDispatched, safeWarn } = ctx;
|
|
112157
|
+
const asConfig = pluginConfig.architectural_supervision;
|
|
112158
|
+
const summarizeFindings = (findings) => {
|
|
112159
|
+
if (!Array.isArray(findings) || findings.length === 0)
|
|
112160
|
+
return "";
|
|
112161
|
+
const details = findings.map((f) => f && typeof f === "object" && typeof f.description === "string" ? f.description : undefined).filter((d) => Boolean(d));
|
|
112162
|
+
return details.length > 0 ? `
|
|
112163
|
+
Findings: ${details.join("; ")}` : "";
|
|
112164
|
+
};
|
|
112165
|
+
const asBlocked = (reason, message) => ({
|
|
112166
|
+
blocked: true,
|
|
112167
|
+
reason,
|
|
112168
|
+
message,
|
|
112169
|
+
agentsDispatched,
|
|
112170
|
+
agentsMissing: [],
|
|
112171
|
+
warnings: []
|
|
112172
|
+
});
|
|
112173
|
+
let asEntry = null;
|
|
112174
|
+
try {
|
|
112175
|
+
asEntry = readSupervisorReportRaw(dir, phase);
|
|
112176
|
+
} catch (asError) {
|
|
112177
|
+
return asBlocked("ARCH_SUPERVISOR_ERROR", `Phase ${phase} cannot be completed: architecture supervisor gate encountered an error. Error: ${String(asError)}`);
|
|
112178
|
+
}
|
|
112179
|
+
if (!asEntry) {
|
|
112180
|
+
return asBlocked("ARCH_SUPERVISOR_REQUIRED", `Phase ${phase} cannot be completed: architectural_supervision gate mode is enabled and no architecture supervisor evidence was found at .swarm/evidence/${phase}/architecture-supervisor.json. Dispatch critic_architecture_supervisor with the phase + agent summaries, then call write_architecture_supervisor_evidence.`);
|
|
112181
|
+
}
|
|
112182
|
+
const now = new Date;
|
|
112183
|
+
const asTime = asEntry.timestamp ? new Date(asEntry.timestamp) : null;
|
|
112184
|
+
if (!asTime || Number.isNaN(asTime.getTime())) {
|
|
112185
|
+
return asBlocked("ARCH_SUPERVISOR_INVALID_TIMESTAMP", `Phase ${phase} cannot be completed: architecture supervisor evidence has a missing or invalid timestamp.`);
|
|
112186
|
+
}
|
|
112187
|
+
if (asTime.getTime() > now.getTime()) {
|
|
112188
|
+
return asBlocked("ARCH_SUPERVISOR_FUTURE_TIMESTAMP", `Phase ${phase} cannot be completed: architecture supervisor evidence timestamp is in the future.`);
|
|
112189
|
+
}
|
|
112190
|
+
if (now.getTime() - asTime.getTime() > 24 * 60 * 60 * 1000) {
|
|
112191
|
+
return asBlocked("ARCH_SUPERVISOR_STALE_EVIDENCE", `Phase ${phase} cannot be completed: architecture supervisor evidence is older than 24 hours. Re-run the supervisor for fresh review.`);
|
|
112192
|
+
}
|
|
112193
|
+
if (typeof asEntry.phase_number !== "number" || asEntry.phase_number !== phase) {
|
|
112194
|
+
return asBlocked("ARCH_SUPERVISOR_PHASE_MISMATCH", `Phase ${phase} cannot be completed: architecture supervisor evidence is for phase ${String(asEntry.phase_number)}, not phase ${phase}.`);
|
|
112195
|
+
}
|
|
112196
|
+
const asVerdict = asEntry.verdict;
|
|
112197
|
+
if (asVerdict === "REJECT") {
|
|
112198
|
+
return asBlocked("ARCH_SUPERVISOR_REJECTED", `Phase ${phase} cannot be completed: architecture supervisor returned verdict 'REJECT'. Address the system-level findings before completing the phase.${summarizeFindings(asEntry.findings)}`);
|
|
112199
|
+
}
|
|
112200
|
+
if (asVerdict === "CONCERNS") {
|
|
112201
|
+
if (asConfig?.allow_concerns_to_complete === false) {
|
|
112202
|
+
return asBlocked("ARCH_SUPERVISOR_CONCERNS", `Phase ${phase} cannot be completed: architecture supervisor returned verdict 'CONCERNS' and allow_concerns_to_complete is disabled.${summarizeFindings(asEntry.findings)}`);
|
|
112203
|
+
}
|
|
112204
|
+
safeWarn(`[phase_complete] Architecture supervisor returned CONCERNS for phase ${phase} — proceeding (allow_concerns_to_complete is enabled)`, undefined);
|
|
112205
|
+
} else if (asVerdict !== "APPROVE") {
|
|
112206
|
+
return asBlocked("ARCH_SUPERVISOR_INVALID", `Phase ${phase} cannot be completed: architecture supervisor evidence contains unrecognized verdict '${String(asVerdict)}'. Expected one of: APPROVE, CONCERNS, REJECT.`);
|
|
112207
|
+
}
|
|
112208
|
+
return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
|
|
112209
|
+
}
|
|
112210
|
+
// src/tools/phase-complete/gates/completion-verify-gate.ts
|
|
112211
|
+
async function runCompletionVerifyGate(ctx) {
|
|
112212
|
+
const { phase, dir, agentsDispatched, safeWarn } = ctx;
|
|
112213
|
+
try {
|
|
112214
|
+
const completionResultRaw = await executeCompletionVerify({ phase }, dir);
|
|
112215
|
+
const completionResult = JSON.parse(completionResultRaw);
|
|
112216
|
+
if (completionResult.status === "blocked") {
|
|
112217
|
+
return {
|
|
112218
|
+
blocked: true,
|
|
112219
|
+
reason: "COMPLETION_INCOMPLETE",
|
|
112220
|
+
message: `Phase ${phase} cannot be completed: ${completionResult.reason}`,
|
|
112221
|
+
agentsDispatched,
|
|
112222
|
+
agentsMissing: [],
|
|
112223
|
+
warnings: completionResult.blockedTasks ? [
|
|
112224
|
+
`Blocked tasks: ${completionResult.blockedTasks.map((t) => t.task_id).join(", ")}`
|
|
112225
|
+
] : []
|
|
112226
|
+
};
|
|
112227
|
+
}
|
|
112228
|
+
return {
|
|
112229
|
+
blocked: false,
|
|
112230
|
+
agentsDispatched,
|
|
112231
|
+
agentsMissing: [],
|
|
112232
|
+
warnings: []
|
|
112233
|
+
};
|
|
112234
|
+
} catch (completionError) {
|
|
112235
|
+
safeWarn(`[phase_complete] Completion verify error (non-blocking):`, completionError);
|
|
112236
|
+
return {
|
|
112237
|
+
blocked: false,
|
|
112238
|
+
agentsDispatched,
|
|
112239
|
+
agentsMissing: [],
|
|
112240
|
+
warnings: []
|
|
112241
|
+
};
|
|
112242
|
+
}
|
|
112243
|
+
}
|
|
112244
|
+
// src/tools/phase-complete/gates/drift-gate.ts
|
|
112245
|
+
init_qa_gate_profile();
|
|
112246
|
+
init_manager();
|
|
112247
|
+
init_state();
|
|
112248
|
+
import * as fs89 from "node:fs";
|
|
112249
|
+
import * as path126 from "node:path";
|
|
112250
|
+
async function runDriftGate(ctx) {
|
|
112251
|
+
const { phase, dir, sessionID, agentsDispatched, safeWarn } = ctx;
|
|
112252
|
+
let driftCheckEnabled = true;
|
|
112253
|
+
let driftHasSpecMd = false;
|
|
112254
|
+
try {
|
|
112255
|
+
const specMdPath = path126.join(dir, ".swarm", "spec.md");
|
|
112256
|
+
driftHasSpecMd = fs89.existsSync(specMdPath);
|
|
112257
|
+
const gatePlan = await loadPlan(dir);
|
|
112258
|
+
if (gatePlan) {
|
|
112259
|
+
const gatePlanId = derivePlanId(gatePlan);
|
|
112260
|
+
const gateProfile = getProfile(dir, gatePlanId);
|
|
112261
|
+
if (gateProfile) {
|
|
112262
|
+
const gateSession = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
|
|
112263
|
+
const gateOverrides = gateSession?.qaGateSessionOverrides ?? {};
|
|
112264
|
+
const gateEffective = getEffectiveGates(gateProfile, gateOverrides);
|
|
112265
|
+
driftCheckEnabled = gateEffective.drift_check === true;
|
|
112266
|
+
}
|
|
112267
|
+
}
|
|
112268
|
+
} catch (gateLoadError) {
|
|
112269
|
+
safeWarn(`[phase_complete] QA gate profile load error, drift_check defaults to enabled:`, gateLoadError);
|
|
112270
|
+
}
|
|
112271
|
+
if (!driftCheckEnabled) {
|
|
112272
|
+
return {
|
|
112273
|
+
blocked: false,
|
|
112274
|
+
agentsDispatched,
|
|
112275
|
+
agentsMissing: [],
|
|
112276
|
+
warnings: [
|
|
112277
|
+
`drift_check gate is disabled. Drift verification was skipped for phase ${phase}.`
|
|
112278
|
+
]
|
|
112279
|
+
};
|
|
112280
|
+
}
|
|
112281
|
+
let phaseType;
|
|
112282
|
+
try {
|
|
112283
|
+
const planPath = path126.join(dir, ".swarm", "plan.json");
|
|
112284
|
+
if (fs89.existsSync(planPath)) {
|
|
112285
|
+
const planRaw = fs89.readFileSync(planPath, "utf-8");
|
|
112286
|
+
const plan = JSON.parse(planRaw);
|
|
112287
|
+
const targetPhase = plan.phases?.find((p) => p.id === phase);
|
|
112288
|
+
phaseType = targetPhase?.type;
|
|
112289
|
+
}
|
|
112290
|
+
} catch {}
|
|
112291
|
+
if (phaseType === "non-code") {
|
|
112292
|
+
return {
|
|
112293
|
+
blocked: false,
|
|
112294
|
+
agentsDispatched,
|
|
112295
|
+
agentsMissing: [],
|
|
112296
|
+
warnings: [
|
|
112297
|
+
`Phase ${phase} is annotated as 'non-code'. Drift verification was skipped per phase type annotation.`
|
|
112298
|
+
]
|
|
112299
|
+
};
|
|
112300
|
+
}
|
|
112301
|
+
try {
|
|
112302
|
+
const driftEvidencePath = path126.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
|
|
112303
|
+
let driftVerdictFound = false;
|
|
112304
|
+
let driftVerdictApproved = false;
|
|
112305
|
+
try {
|
|
112306
|
+
const driftEvidenceContent = fs89.readFileSync(driftEvidencePath, "utf-8");
|
|
112307
|
+
const driftEvidence = JSON.parse(driftEvidenceContent);
|
|
112308
|
+
const entries = driftEvidence.entries ?? [];
|
|
112309
|
+
for (const entry of entries) {
|
|
112310
|
+
if (typeof entry.type === "string" && entry.type.includes("drift") && typeof entry.verdict === "string") {
|
|
112311
|
+
driftVerdictFound = true;
|
|
112312
|
+
if (entry.verdict === "approved") {
|
|
112313
|
+
driftVerdictApproved = true;
|
|
112314
|
+
}
|
|
112315
|
+
if (entry.verdict === "rejected" || typeof entry.summary === "string" && entry.summary.includes("NEEDS_REVISION")) {
|
|
112316
|
+
return {
|
|
112317
|
+
blocked: true,
|
|
112318
|
+
reason: "DRIFT_VERIFICATION_REJECTED",
|
|
112319
|
+
message: `Phase ${phase} cannot be completed: drift verifier returned verdict '${entry.verdict}'. Address the drift issues before completing the phase.`,
|
|
112320
|
+
agentsDispatched,
|
|
112321
|
+
agentsMissing: [],
|
|
112322
|
+
warnings: []
|
|
112323
|
+
};
|
|
112324
|
+
}
|
|
112325
|
+
}
|
|
112326
|
+
}
|
|
112327
|
+
} catch (readError) {
|
|
112328
|
+
if (readError.code !== "ENOENT") {
|
|
112329
|
+
safeWarn(`[phase_complete] Drift verifier evidence unreadable:`, readError);
|
|
112330
|
+
}
|
|
112331
|
+
driftVerdictFound = false;
|
|
112332
|
+
}
|
|
112333
|
+
if (!driftVerdictFound) {
|
|
112334
|
+
if (!driftHasSpecMd) {
|
|
112335
|
+
let incompleteTaskCount = 0;
|
|
112336
|
+
let planParseable = false;
|
|
112337
|
+
try {
|
|
112338
|
+
const planPath = path126.join(dir, ".swarm", "plan.json");
|
|
112339
|
+
if (fs89.existsSync(planPath)) {
|
|
112340
|
+
const planRaw = fs89.readFileSync(planPath, "utf-8");
|
|
112341
|
+
const plan = JSON.parse(planRaw);
|
|
112342
|
+
planParseable = true;
|
|
112343
|
+
const planPhase = plan.phases?.find((p) => p.id === phase);
|
|
112344
|
+
if (planPhase?.tasks) {
|
|
112345
|
+
incompleteTaskCount = planPhase.tasks.filter((t) => t.status !== "completed" && t.status !== "closed").length;
|
|
112346
|
+
}
|
|
112347
|
+
}
|
|
112348
|
+
} catch {}
|
|
112349
|
+
if (!planParseable) {
|
|
112350
|
+
return {
|
|
112351
|
+
blocked: false,
|
|
112352
|
+
agentsDispatched,
|
|
112353
|
+
agentsMissing: [],
|
|
112354
|
+
warnings: [
|
|
112355
|
+
`No spec.md found and drift verification evidence missing — consider running critic_drift_verifier before phase completion.`
|
|
112356
|
+
]
|
|
112357
|
+
};
|
|
112358
|
+
} else if (incompleteTaskCount > 0) {
|
|
112359
|
+
return {
|
|
112360
|
+
blocked: false,
|
|
112361
|
+
agentsDispatched,
|
|
112362
|
+
agentsMissing: [],
|
|
112363
|
+
warnings: [
|
|
112364
|
+
`No spec.md found and drift verification evidence missing. Phase ${phase} has ${incompleteTaskCount} incomplete task(s) in plan.json — consider running critic_drift_verifier before phase completion.`
|
|
112365
|
+
]
|
|
112366
|
+
};
|
|
112367
|
+
} else {
|
|
112368
|
+
return {
|
|
112369
|
+
blocked: false,
|
|
112370
|
+
agentsDispatched,
|
|
112371
|
+
agentsMissing: [],
|
|
112372
|
+
warnings: [
|
|
112373
|
+
`No spec.md found. Phase ${phase} tasks are all completed in plan.json. Drift verification was skipped.`
|
|
112374
|
+
]
|
|
112375
|
+
};
|
|
112376
|
+
}
|
|
112377
|
+
} else {
|
|
112378
|
+
return {
|
|
112379
|
+
blocked: true,
|
|
112380
|
+
reason: "DRIFT_VERIFICATION_MISSING",
|
|
112381
|
+
message: `Phase ${phase} cannot be completed: drift_check is enabled and drift verifier evidence not found at .swarm/evidence/${phase}/drift-verifier.json. Run drift verification before completing the phase.`,
|
|
112382
|
+
agentsDispatched,
|
|
112383
|
+
agentsMissing: [],
|
|
112384
|
+
warnings: []
|
|
112385
|
+
};
|
|
112386
|
+
}
|
|
112387
|
+
}
|
|
112388
|
+
if (!driftVerdictApproved && driftVerdictFound) {
|
|
112389
|
+
return {
|
|
112390
|
+
blocked: true,
|
|
112391
|
+
reason: "DRIFT_VERIFICATION_REJECTED",
|
|
112392
|
+
message: `Phase ${phase} cannot be completed: drift verifier verdict is not approved.`,
|
|
112393
|
+
agentsDispatched,
|
|
112394
|
+
agentsMissing: [],
|
|
112395
|
+
warnings: []
|
|
112396
|
+
};
|
|
112397
|
+
}
|
|
112398
|
+
return {
|
|
112399
|
+
blocked: false,
|
|
112400
|
+
agentsDispatched,
|
|
112401
|
+
agentsMissing: [],
|
|
112402
|
+
warnings: []
|
|
112403
|
+
};
|
|
112404
|
+
} catch (driftError) {
|
|
112405
|
+
return {
|
|
112406
|
+
blocked: true,
|
|
112407
|
+
reason: "DRIFT_VERIFICATION_ERROR",
|
|
112408
|
+
message: `Phase ${phase} cannot be completed: drift verification encountered an error: ${driftError instanceof Error ? driftError.message : String(driftError)}. This is a hard block — resolve the error before completing the phase.`,
|
|
112409
|
+
agentsDispatched,
|
|
112410
|
+
agentsMissing: [],
|
|
112411
|
+
warnings: []
|
|
112412
|
+
};
|
|
112413
|
+
}
|
|
112414
|
+
}
|
|
112415
|
+
// src/tools/phase-complete/gates/final-council-gate.ts
|
|
112416
|
+
init_qa_gate_profile();
|
|
112417
|
+
init_manager();
|
|
112418
|
+
init_state();
|
|
112419
|
+
import * as fs90 from "node:fs";
|
|
112420
|
+
import * as path127 from "node:path";
|
|
112421
|
+
async function runFinalCouncilGate(ctx) {
|
|
112422
|
+
const { phase, dir, sessionID, agentsDispatched, safeWarn } = ctx;
|
|
112423
|
+
let finalCouncilEnabled = false;
|
|
112424
|
+
try {
|
|
112425
|
+
const plan = await loadPlan(dir);
|
|
112426
|
+
if (plan) {
|
|
112427
|
+
const lastPhaseId = plan.phases[plan.phases.length - 1]?.id;
|
|
112428
|
+
if (lastPhaseId !== undefined && phase === lastPhaseId) {
|
|
112429
|
+
const planId = derivePlanId(plan);
|
|
112430
|
+
const profile = getProfile(dir, planId);
|
|
112431
|
+
if (profile) {
|
|
112432
|
+
const session = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
|
|
112433
|
+
const overrides = session?.qaGateSessionOverrides ?? {};
|
|
112434
|
+
const effective = getEffectiveGates(profile, overrides);
|
|
112435
|
+
if (effective.final_council === true) {
|
|
112436
|
+
finalCouncilEnabled = true;
|
|
112437
|
+
const fcPath = path127.join(dir, ".swarm", "evidence", "final-council.json");
|
|
112438
|
+
let fcVerdictFound = false;
|
|
112439
|
+
let _fcVerdict;
|
|
112440
|
+
try {
|
|
112441
|
+
const fcContent = fs90.readFileSync(fcPath, "utf-8");
|
|
112442
|
+
const fcBundle = JSON.parse(fcContent);
|
|
112443
|
+
for (const entry of fcBundle.entries ?? []) {
|
|
112444
|
+
if (typeof entry.type === "string" && entry.type === "final-council" && typeof entry.verdict === "string") {
|
|
112445
|
+
fcVerdictFound = true;
|
|
112446
|
+
_fcVerdict = entry.verdict;
|
|
112447
|
+
if (plan) {
|
|
112448
|
+
const currentPlanId = derivePlanId(plan);
|
|
112449
|
+
if (entry.plan_id && entry.plan_id !== currentPlanId) {
|
|
112450
|
+
return {
|
|
112451
|
+
blocked: true,
|
|
112452
|
+
reason: "final_council_plan_mismatch",
|
|
112453
|
+
message: `Final council evidence belongs to a different plan (evidence: ${entry.plan_id}, current: ${currentPlanId}). Re-run the final council.`,
|
|
112454
|
+
agentsDispatched,
|
|
112455
|
+
agentsMissing: [],
|
|
112456
|
+
warnings: []
|
|
112457
|
+
};
|
|
112458
|
+
}
|
|
112459
|
+
if (!entry.plan_id) {
|
|
112460
|
+
return {
|
|
112461
|
+
blocked: true,
|
|
112462
|
+
reason: "FINAL_COUNCIL_PLAN_ID_REQUIRED",
|
|
112463
|
+
message: `Phase ${phase} (last phase) cannot be completed: final council evidence is missing plan_id binding. Re-run the final council to generate evidence with plan identity.`,
|
|
112464
|
+
agentsDispatched,
|
|
112465
|
+
agentsMissing: [],
|
|
112466
|
+
warnings: []
|
|
112467
|
+
};
|
|
112468
|
+
}
|
|
112469
|
+
}
|
|
112470
|
+
if (typeof entry.quorumSize !== "number" || !Number.isFinite(entry.quorumSize) || entry.quorumSize < 5) {
|
|
112471
|
+
return {
|
|
112472
|
+
blocked: true,
|
|
112473
|
+
reason: "FINAL_COUNCIL_MISSING_QUORUM",
|
|
112474
|
+
message: `Phase ${phase} (last phase) cannot be completed: final council evidence is missing valid quorum metadata. Re-run the project-scoped five-member final council and call write_final_council_evidence to generate quorumed evidence.`,
|
|
112475
|
+
agentsDispatched,
|
|
112476
|
+
agentsMissing: [],
|
|
112477
|
+
warnings: []
|
|
112478
|
+
};
|
|
112479
|
+
}
|
|
112480
|
+
const requiredFinalCouncilMembers = [
|
|
112481
|
+
"critic",
|
|
112482
|
+
"reviewer",
|
|
112483
|
+
"sme",
|
|
112484
|
+
"test_engineer",
|
|
112485
|
+
"explorer"
|
|
112486
|
+
];
|
|
112487
|
+
const membersVoted = Array.isArray(entry.membersVoted) ? entry.membersVoted.filter((member) => typeof member === "string") : [];
|
|
112488
|
+
const membersAbsent = Array.isArray(entry.membersAbsent) ? entry.membersAbsent.filter((member) => typeof member === "string") : [];
|
|
112489
|
+
const distinctMembersVoted = new Set(membersVoted);
|
|
112490
|
+
const hasAllRequiredMembers = requiredFinalCouncilMembers.every((member) => distinctMembersVoted.has(member)) && distinctMembersVoted.size === requiredFinalCouncilMembers.length && membersAbsent.length === 0;
|
|
112491
|
+
if (!hasAllRequiredMembers) {
|
|
112492
|
+
return {
|
|
112493
|
+
blocked: true,
|
|
112494
|
+
reason: "FINAL_COUNCIL_MISSING_QUORUM",
|
|
112495
|
+
message: `Phase ${phase} (last phase) cannot be completed: final council evidence does not prove all five required members voted. Re-run the project-scoped five-member final council and call write_final_council_evidence to generate complete evidence.`,
|
|
112496
|
+
agentsDispatched,
|
|
112497
|
+
agentsMissing: [],
|
|
112498
|
+
warnings: []
|
|
112499
|
+
};
|
|
112500
|
+
}
|
|
112501
|
+
if (entry.verdict === "rejected" || entry.verdict === "REJECTED") {
|
|
112502
|
+
return {
|
|
112503
|
+
blocked: true,
|
|
112504
|
+
reason: "FINAL_COUNCIL_REJECTED",
|
|
112505
|
+
message: `Phase ${phase} (last phase) cannot be completed: final council returned verdict 'REJECTED'. Address the required fixes before completing the project.`,
|
|
112506
|
+
agentsDispatched,
|
|
112507
|
+
agentsMissing: [],
|
|
112508
|
+
warnings: []
|
|
112509
|
+
};
|
|
112510
|
+
}
|
|
112511
|
+
if (entry.verdict !== "approved" && entry.verdict !== "APPROVED") {
|
|
112512
|
+
return {
|
|
112513
|
+
blocked: true,
|
|
112514
|
+
reason: "FINAL_COUNCIL_INVALID_VERDICT",
|
|
112515
|
+
message: `Phase ${phase} (last phase) cannot be completed: final council evidence contains unrecognized verdict '${entry.verdict}'. Expected 'approved'.`,
|
|
112516
|
+
agentsDispatched,
|
|
112517
|
+
agentsMissing: [],
|
|
112518
|
+
warnings: []
|
|
112519
|
+
};
|
|
112520
|
+
}
|
|
112521
|
+
}
|
|
112522
|
+
}
|
|
112523
|
+
} catch (readErr) {
|
|
112524
|
+
if (readErr.code !== "ENOENT") {
|
|
112525
|
+
safeWarn(`[phase_complete] Final council evidence unreadable:`, readErr);
|
|
112526
|
+
}
|
|
112527
|
+
fcVerdictFound = false;
|
|
112528
|
+
}
|
|
112529
|
+
if (!fcVerdictFound) {
|
|
112530
|
+
return {
|
|
112531
|
+
blocked: true,
|
|
112532
|
+
reason: "FINAL_COUNCIL_REQUIRED",
|
|
112533
|
+
final_council_required: true,
|
|
112534
|
+
message: `Phase ${phase} (last phase) cannot be completed: final_council is enabled and final council evidence not found at .swarm/evidence/final-council.json. Dispatch critic, reviewer, sme, test_engineer, and explorer with project-scoped context, collect their CouncilMemberVerdict JSON, and call write_final_council_evidence before completing the project. Do not use convene_general_council for this gate.`,
|
|
112535
|
+
agentsDispatched,
|
|
112536
|
+
agentsMissing: [],
|
|
112537
|
+
warnings: [
|
|
112538
|
+
`Final council required - dispatch the five project-scoped council members, then call write_final_council_evidence to persist quorumed evidence.`
|
|
112539
|
+
]
|
|
112540
|
+
};
|
|
112541
|
+
}
|
|
112542
|
+
}
|
|
112543
|
+
}
|
|
112544
|
+
}
|
|
112545
|
+
}
|
|
112546
|
+
} catch (fcError) {
|
|
112547
|
+
if (finalCouncilEnabled) {
|
|
112548
|
+
return {
|
|
112549
|
+
blocked: true,
|
|
112550
|
+
reason: "FINAL_COUNCIL_ERROR",
|
|
112551
|
+
message: `Phase ${phase} (last phase) cannot be completed: final council gate encountered an error. Error: ${String(fcError)}`,
|
|
112552
|
+
agentsDispatched,
|
|
112553
|
+
agentsMissing: [],
|
|
112554
|
+
warnings: [`FINAL_COUNCIL_ERROR: ${String(fcError)}`]
|
|
112555
|
+
};
|
|
112556
|
+
} else {
|
|
112557
|
+
safeWarn(`[phase_complete] Final council gate error (non-blocking):`, fcError);
|
|
112558
|
+
}
|
|
112559
|
+
}
|
|
112560
|
+
return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
|
|
112561
|
+
}
|
|
112562
|
+
// src/tools/phase-complete/gates/hallucination-gate.ts
|
|
112563
|
+
init_qa_gate_profile();
|
|
112564
|
+
init_manager();
|
|
112565
|
+
init_state();
|
|
112566
|
+
import * as fs91 from "node:fs";
|
|
112567
|
+
import * as path128 from "node:path";
|
|
112568
|
+
async function runHallucinationGate(ctx) {
|
|
112569
|
+
const { phase, dir, sessionID, agentsDispatched, safeWarn } = ctx;
|
|
112570
|
+
try {
|
|
112571
|
+
const plan = await loadPlan(dir);
|
|
112572
|
+
if (plan) {
|
|
112573
|
+
const planId = derivePlanId(plan);
|
|
112574
|
+
const profile = getProfile(dir, planId);
|
|
112575
|
+
if (profile) {
|
|
112576
|
+
const session = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
|
|
112577
|
+
const overrides = session?.qaGateSessionOverrides ?? {};
|
|
112578
|
+
const effective = getEffectiveGates(profile, overrides);
|
|
112579
|
+
if (effective.hallucination_guard === true) {
|
|
112580
|
+
const hgPath = path128.join(dir, ".swarm", "evidence", String(phase), "hallucination-guard.json");
|
|
112581
|
+
let hgVerdictFound = false;
|
|
112582
|
+
let hgVerdictApproved = false;
|
|
112583
|
+
try {
|
|
112584
|
+
const hgContent = fs91.readFileSync(hgPath, "utf-8");
|
|
112585
|
+
const hgBundle = JSON.parse(hgContent);
|
|
112586
|
+
for (const entry of hgBundle.entries ?? []) {
|
|
112587
|
+
if (typeof entry.type === "string" && entry.type.includes("hallucination") && typeof entry.verdict === "string") {
|
|
112588
|
+
hgVerdictFound = true;
|
|
112589
|
+
if (entry.verdict === "approved") {
|
|
112590
|
+
hgVerdictApproved = true;
|
|
112591
|
+
}
|
|
112592
|
+
if (entry.verdict === "rejected" || typeof entry.summary === "string" && entry.summary.includes("NEEDS_REVISION")) {
|
|
112593
|
+
return {
|
|
112594
|
+
blocked: true,
|
|
112595
|
+
reason: "HALLUCINATION_VERIFICATION_REJECTED",
|
|
112596
|
+
message: `Phase ${phase} cannot be completed: hallucination verifier returned verdict '${entry.verdict}'. Remove fabricated APIs/signatures and fix broken citations before completing the phase.`,
|
|
112597
|
+
agentsDispatched,
|
|
112598
|
+
agentsMissing: [],
|
|
112599
|
+
warnings: []
|
|
112600
|
+
};
|
|
112601
|
+
}
|
|
112602
|
+
}
|
|
112603
|
+
}
|
|
112604
|
+
} catch (readErr) {
|
|
112605
|
+
if (readErr.code !== "ENOENT") {
|
|
112606
|
+
safeWarn(`[phase_complete] Hallucination guard evidence unreadable:`, readErr);
|
|
112607
|
+
}
|
|
112608
|
+
hgVerdictFound = false;
|
|
112609
|
+
}
|
|
112610
|
+
if (!hgVerdictFound) {
|
|
112611
|
+
return {
|
|
112612
|
+
blocked: true,
|
|
112613
|
+
reason: "HALLUCINATION_VERIFICATION_MISSING",
|
|
112614
|
+
message: `Phase ${phase} cannot be completed: hallucination_guard is enabled and evidence not found at .swarm/evidence/${phase}/hallucination-guard.json. Delegate to critic_hallucination_verifier and call write_hallucination_evidence before completing the phase.`,
|
|
112615
|
+
agentsDispatched,
|
|
112616
|
+
agentsMissing: [],
|
|
112617
|
+
warnings: []
|
|
112618
|
+
};
|
|
112619
|
+
}
|
|
112620
|
+
if (!hgVerdictApproved) {
|
|
112621
|
+
return {
|
|
112622
|
+
blocked: true,
|
|
112623
|
+
reason: "HALLUCINATION_VERIFICATION_REJECTED",
|
|
112624
|
+
message: `Phase ${phase} cannot be completed: hallucination verifier verdict is not approved.`,
|
|
112625
|
+
agentsDispatched,
|
|
112626
|
+
agentsMissing: [],
|
|
112627
|
+
warnings: []
|
|
112628
|
+
};
|
|
112629
|
+
}
|
|
112630
|
+
}
|
|
112631
|
+
}
|
|
112632
|
+
}
|
|
112633
|
+
} catch (hgError) {
|
|
112634
|
+
safeWarn(`[phase_complete] Hallucination guard error (non-blocking):`, hgError);
|
|
112635
|
+
}
|
|
112636
|
+
return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
|
|
112637
|
+
}
|
|
112638
|
+
// src/tools/phase-complete/gates/mutation-gate.ts
|
|
112639
|
+
init_qa_gate_profile();
|
|
112640
|
+
init_manager();
|
|
112641
|
+
init_state();
|
|
112642
|
+
import * as fs92 from "node:fs";
|
|
112643
|
+
import * as path129 from "node:path";
|
|
112644
|
+
async function runMutationGate(ctx) {
|
|
112645
|
+
const { phase, dir, sessionID, agentsDispatched, safeWarn } = ctx;
|
|
112646
|
+
try {
|
|
112647
|
+
const plan = await loadPlan(dir);
|
|
112648
|
+
if (plan) {
|
|
112649
|
+
const planId = derivePlanId(plan);
|
|
112650
|
+
const profile = getProfile(dir, planId);
|
|
112651
|
+
if (profile) {
|
|
112652
|
+
const session = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
|
|
112653
|
+
const overrides = session?.qaGateSessionOverrides ?? {};
|
|
112654
|
+
const effective = getEffectiveGates(profile, overrides);
|
|
112655
|
+
if (effective.mutation_test === true) {
|
|
112656
|
+
const mgPath = path129.join(dir, ".swarm", "evidence", String(phase), "mutation-gate.json");
|
|
112657
|
+
let mgVerdictFound = false;
|
|
112658
|
+
let mgVerdict;
|
|
112659
|
+
try {
|
|
112660
|
+
const mgContent = fs92.readFileSync(mgPath, "utf-8");
|
|
112661
|
+
const mgBundle = JSON.parse(mgContent);
|
|
112662
|
+
for (const entry of mgBundle.entries ?? []) {
|
|
112663
|
+
if (typeof entry.type === "string" && entry.type === "mutation-gate" && typeof entry.verdict === "string") {
|
|
112664
|
+
mgVerdictFound = true;
|
|
112665
|
+
mgVerdict = entry.verdict;
|
|
112666
|
+
if (entry.verdict === "fail") {
|
|
112667
|
+
return {
|
|
112668
|
+
blocked: true,
|
|
112669
|
+
reason: "MUTATION_GATE_FAIL",
|
|
112670
|
+
message: `Phase ${phase} cannot be completed: mutation gate returned verdict 'fail'. Resolve surviving mutants or lower the kill-rate threshold before completing the phase.`,
|
|
112671
|
+
agentsDispatched,
|
|
112672
|
+
agentsMissing: [],
|
|
112673
|
+
warnings: []
|
|
112674
|
+
};
|
|
112675
|
+
} else if (!["pass", "warn", "skip"].includes(entry.verdict)) {
|
|
112676
|
+
return {
|
|
112677
|
+
blocked: true,
|
|
112678
|
+
reason: "MUTATION_GATE_FAIL",
|
|
112679
|
+
message: `Phase ${phase} cannot be completed: mutation gate evidence contains unrecognized verdict '${entry.verdict}'. Expected one of: pass, warn, fail, skip.`,
|
|
112680
|
+
agentsDispatched,
|
|
112681
|
+
agentsMissing: [],
|
|
112682
|
+
warnings: []
|
|
112683
|
+
};
|
|
112684
|
+
}
|
|
112685
|
+
}
|
|
112686
|
+
}
|
|
112687
|
+
} catch (readErr) {
|
|
112688
|
+
if (readErr.code !== "ENOENT") {
|
|
112689
|
+
safeWarn(`[phase_complete] Mutation gate evidence unreadable:`, readErr);
|
|
112690
|
+
}
|
|
112691
|
+
mgVerdictFound = false;
|
|
112692
|
+
}
|
|
112693
|
+
if (!mgVerdictFound) {
|
|
112694
|
+
return {
|
|
112695
|
+
blocked: true,
|
|
112696
|
+
reason: "MUTATION_GATE_MISSING",
|
|
112697
|
+
message: `Phase ${phase} cannot be completed: mutation_test is enabled and evidence not found at .swarm/evidence/${phase}/mutation-gate.json. Run mutation_test, then call write_mutation_evidence before completing the phase.`,
|
|
112698
|
+
agentsDispatched,
|
|
112699
|
+
agentsMissing: [],
|
|
112700
|
+
warnings: []
|
|
112701
|
+
};
|
|
112702
|
+
}
|
|
112703
|
+
if (mgVerdict === "warn") {
|
|
112704
|
+
safeWarn(`[phase_complete] Mutation gate verdict is 'warn' for phase ${phase} — proceeding with warning`, undefined);
|
|
112705
|
+
}
|
|
112706
|
+
}
|
|
112707
|
+
}
|
|
112708
|
+
}
|
|
112709
|
+
} catch (mgError) {
|
|
112710
|
+
safeWarn(`[phase_complete] Mutation gate error (non-blocking):`, mgError);
|
|
112711
|
+
}
|
|
112712
|
+
return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
|
|
112713
|
+
}
|
|
112714
|
+
// src/tools/phase-complete/gates/phase-council-gate.ts
|
|
112715
|
+
init_qa_gate_profile();
|
|
112716
|
+
init_manager();
|
|
112717
|
+
init_state();
|
|
112718
|
+
import * as fs93 from "node:fs";
|
|
112719
|
+
import * as path130 from "node:path";
|
|
112720
|
+
async function runPhaseCouncilGate(ctx) {
|
|
112721
|
+
const { phase, dir, sessionID, pluginConfig, agentsDispatched, safeWarn } = ctx;
|
|
112722
|
+
let councilModeEnabled = false;
|
|
112723
|
+
try {
|
|
112724
|
+
const plan = await loadPlan(dir);
|
|
112725
|
+
if (plan) {
|
|
112726
|
+
const planId = derivePlanId(plan);
|
|
112727
|
+
const profile = getProfile(dir, planId);
|
|
112728
|
+
if (profile) {
|
|
112729
|
+
const session = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
|
|
112730
|
+
const overrides = session?.qaGateSessionOverrides ?? {};
|
|
112731
|
+
const effective = getEffectiveGates(profile, overrides);
|
|
112732
|
+
if (effective.council_mode === true) {
|
|
112733
|
+
councilModeEnabled = true;
|
|
112734
|
+
const pcPath = path130.join(dir, ".swarm", "evidence", String(phase), "phase-council.json");
|
|
112735
|
+
let pcVerdictFound = false;
|
|
112736
|
+
let _pcVerdict;
|
|
112737
|
+
let pcQuorumSize;
|
|
112738
|
+
let pcTimestamp;
|
|
112739
|
+
let pcPhaseNumber;
|
|
112740
|
+
try {
|
|
112741
|
+
const pcContent = fs93.readFileSync(pcPath, "utf-8");
|
|
112742
|
+
const pcBundle = JSON.parse(pcContent);
|
|
112743
|
+
for (const entry of pcBundle.entries ?? []) {
|
|
112744
|
+
if (typeof entry.type === "string" && entry.type === "phase-council" && typeof entry.verdict === "string") {
|
|
112745
|
+
pcVerdictFound = true;
|
|
112746
|
+
_pcVerdict = entry.verdict;
|
|
112747
|
+
pcQuorumSize = typeof entry.quorumSize === "number" ? entry.quorumSize : undefined;
|
|
112748
|
+
pcTimestamp = typeof entry.timestamp === "string" ? entry.timestamp : undefined;
|
|
112749
|
+
pcPhaseNumber = typeof entry.phase_number === "number" ? entry.phase_number : typeof entry.phase === "number" ? entry.phase : undefined;
|
|
112750
|
+
const now = new Date;
|
|
112751
|
+
const pcTime = pcTimestamp ? new Date(pcTimestamp) : null;
|
|
112752
|
+
if (!pcTime || Number.isNaN(pcTime.getTime())) {
|
|
112753
|
+
return {
|
|
112754
|
+
blocked: true,
|
|
112755
|
+
reason: "PHASE_COUNCIL_INVALID_TIMESTAMP",
|
|
112756
|
+
message: `Phase ${phase} cannot be completed: phase council evidence has missing or invalid timestamp.`,
|
|
112757
|
+
agentsDispatched,
|
|
112758
|
+
agentsMissing: [],
|
|
112759
|
+
warnings: []
|
|
112760
|
+
};
|
|
112761
|
+
}
|
|
112762
|
+
const maxAge = 24 * 60 * 60 * 1000;
|
|
112763
|
+
if (pcTime.getTime() > now.getTime()) {
|
|
112764
|
+
return {
|
|
112765
|
+
blocked: true,
|
|
112766
|
+
reason: "PHASE_COUNCIL_FUTURE_TIMESTAMP",
|
|
112767
|
+
message: `Phase ${phase} cannot be completed: phase council evidence timestamp is in the future.`,
|
|
112768
|
+
agentsDispatched,
|
|
112769
|
+
agentsMissing: [],
|
|
112770
|
+
warnings: []
|
|
112771
|
+
};
|
|
112772
|
+
}
|
|
112773
|
+
if (now.getTime() - pcTime.getTime() > maxAge) {
|
|
112774
|
+
return {
|
|
112775
|
+
blocked: true,
|
|
112776
|
+
reason: "PHASE_COUNCIL_STALE_EVIDENCE",
|
|
112777
|
+
message: `Phase ${phase} cannot be completed: phase council evidence is older than 24 hours. Re-convene council for fresh review.`,
|
|
112778
|
+
agentsDispatched,
|
|
112779
|
+
agentsMissing: [],
|
|
112780
|
+
warnings: []
|
|
112781
|
+
};
|
|
112782
|
+
}
|
|
112783
|
+
if (entry.verdict === "REJECT" || entry.verdict === "reject") {
|
|
112784
|
+
const requiredFixes = entry.requiredFixes ?? entry.required_fixes ?? [];
|
|
112785
|
+
const fixesDetail = Array.isArray(requiredFixes) && requiredFixes.length > 0 ? `
|
|
112786
|
+
Required fixes: ${requiredFixes.map((f) => f.detail ?? JSON.stringify(f)).join("; ")}` : "";
|
|
112787
|
+
return {
|
|
112788
|
+
blocked: true,
|
|
112789
|
+
reason: "PHASE_COUNCIL_REJECTED",
|
|
112790
|
+
message: `Phase ${phase} cannot be completed: phase council returned verdict 'REJECT'. Address the required fixes before completing the phase.${fixesDetail}`,
|
|
112791
|
+
agentsDispatched,
|
|
112792
|
+
agentsMissing: [],
|
|
112793
|
+
warnings: []
|
|
112794
|
+
};
|
|
112795
|
+
}
|
|
112796
|
+
if (entry.verdict === "CONCERNS" || entry.verdict === "concerns") {
|
|
112797
|
+
const phaseConcernsAllow = pluginConfig.council?.phaseConcernsAllowComplete ?? true;
|
|
112798
|
+
if (!phaseConcernsAllow) {
|
|
112799
|
+
const advisoryNotes = entry.advisoryNotes ?? entry.advisory_notes ?? [];
|
|
112800
|
+
const notesDetail = Array.isArray(advisoryNotes) && advisoryNotes.length > 0 ? `
|
|
112801
|
+
Advisory notes: ${advisoryNotes.join("; ")}` : "";
|
|
112802
|
+
return {
|
|
112803
|
+
blocked: true,
|
|
112804
|
+
reason: "PHASE_COUNCIL_CONCERNS",
|
|
112805
|
+
message: `Phase ${phase} cannot be completed: phase council returned verdict 'CONCERNS'.${notesDetail}`,
|
|
112806
|
+
agentsDispatched,
|
|
112807
|
+
agentsMissing: [],
|
|
112808
|
+
warnings: []
|
|
112809
|
+
};
|
|
112810
|
+
}
|
|
112811
|
+
safeWarn(`[phase_complete] Phase council returned CONCERNS for phase ${phase} — proceeding (phaseConcernsAllowComplete is enabled)`, undefined);
|
|
112812
|
+
}
|
|
112813
|
+
if (entry.verdict !== "APPROVE" && entry.verdict !== "approve" && entry.verdict !== "CONCERNS" && entry.verdict !== "concerns") {
|
|
112814
|
+
return {
|
|
112815
|
+
blocked: true,
|
|
112816
|
+
reason: "PHASE_COUNCIL_INVALID",
|
|
112817
|
+
message: `Phase ${phase} cannot be completed: phase council evidence contains unrecognized verdict '${entry.verdict}'. Expected one of: APPROVE, CONCERNS, REJECT.`,
|
|
112818
|
+
agentsDispatched,
|
|
112819
|
+
agentsMissing: [],
|
|
112820
|
+
warnings: []
|
|
112821
|
+
};
|
|
112822
|
+
}
|
|
112823
|
+
}
|
|
112824
|
+
}
|
|
112825
|
+
} catch (readErr) {
|
|
112826
|
+
if (readErr.code !== "ENOENT") {
|
|
112827
|
+
safeWarn(`[phase_complete] Phase council evidence unreadable:`, readErr);
|
|
112828
|
+
}
|
|
112829
|
+
pcVerdictFound = false;
|
|
112830
|
+
}
|
|
112831
|
+
if (!pcVerdictFound) {
|
|
112832
|
+
return {
|
|
112833
|
+
blocked: true,
|
|
112834
|
+
reason: "PHASE_COUNCIL_REQUIRED",
|
|
112835
|
+
phase_council_required: true,
|
|
112836
|
+
message: `Phase ${phase} cannot be completed: council_mode is enabled and phase council evidence not found at .swarm/evidence/${phase}/phase-council.json. Convene a phase-level council (dispatch 5 members, collect verdicts, call submit_phase_council_verdicts) before completing the phase.`,
|
|
112837
|
+
agentsDispatched,
|
|
112838
|
+
agentsMissing: [],
|
|
112839
|
+
warnings: [
|
|
112840
|
+
`Phase council required — convene 5 council members (critic, reviewer, sme, test_engineer, explorer) for holistic phase review. Call submit_phase_council_verdicts to synthesize verdicts and write phase-council.json evidence.`
|
|
112841
|
+
]
|
|
112842
|
+
};
|
|
112843
|
+
}
|
|
112844
|
+
if (pcQuorumSize === undefined || typeof pcQuorumSize !== "number") {
|
|
112845
|
+
return {
|
|
112846
|
+
blocked: true,
|
|
112847
|
+
reason: "PHASE_COUNCIL_MISSING_QUORUM",
|
|
112848
|
+
message: `Phase ${phase} cannot be completed: phase council evidence is missing quorumSize field.`,
|
|
112849
|
+
agentsDispatched,
|
|
112850
|
+
agentsMissing: [],
|
|
112851
|
+
warnings: []
|
|
112852
|
+
};
|
|
112853
|
+
}
|
|
112854
|
+
if (pcQuorumSize < 3) {
|
|
112855
|
+
return {
|
|
112856
|
+
blocked: true,
|
|
112857
|
+
reason: "PHASE_COUNCIL_INSUFFICIENT_QUORUM",
|
|
112858
|
+
message: `Phase ${phase} cannot be completed: phase council quorum (${pcQuorumSize}) is below minimum (3). Re-convene council with sufficient members.`,
|
|
112859
|
+
agentsDispatched,
|
|
112860
|
+
agentsMissing: [],
|
|
112861
|
+
warnings: []
|
|
112862
|
+
};
|
|
112863
|
+
}
|
|
112864
|
+
if (pcPhaseNumber === undefined || typeof pcPhaseNumber !== "number") {
|
|
112865
|
+
return {
|
|
112866
|
+
blocked: true,
|
|
112867
|
+
reason: "PHASE_COUNCIL_MISSING_PHASE",
|
|
112868
|
+
message: `Phase ${phase} cannot be completed: phase council evidence is missing phase_number field.`,
|
|
112869
|
+
agentsDispatched,
|
|
112870
|
+
agentsMissing: [],
|
|
112871
|
+
warnings: []
|
|
112872
|
+
};
|
|
112873
|
+
}
|
|
112874
|
+
if (pcPhaseNumber !== phase) {
|
|
112875
|
+
return {
|
|
112876
|
+
blocked: true,
|
|
112877
|
+
reason: "PHASE_COUNCIL_PHASE_MISMATCH",
|
|
112878
|
+
message: `Phase ${phase} cannot be completed: phase council evidence is for phase ${pcPhaseNumber}, not phase ${phase}. Run council for the correct phase.`,
|
|
112879
|
+
agentsDispatched,
|
|
112880
|
+
agentsMissing: [],
|
|
112881
|
+
warnings: []
|
|
112882
|
+
};
|
|
112883
|
+
}
|
|
112884
|
+
}
|
|
112885
|
+
}
|
|
112886
|
+
}
|
|
112887
|
+
} catch (pcError) {
|
|
112888
|
+
if (councilModeEnabled) {
|
|
112889
|
+
return {
|
|
112890
|
+
blocked: true,
|
|
112891
|
+
reason: "PHASE_COUNCIL_ERROR",
|
|
112892
|
+
message: `Phase ${phase} cannot be completed: phase council gate encountered an error when council_mode was enabled. Error: ${String(pcError)}`,
|
|
112893
|
+
agentsDispatched,
|
|
112894
|
+
agentsMissing: [],
|
|
112895
|
+
warnings: [`PHASE_COUNCIL_ERROR: ${String(pcError)}`]
|
|
112896
|
+
};
|
|
112897
|
+
} else {
|
|
112898
|
+
safeWarn(`[phase_complete] Phase council gate error (non-blocking):`, pcError);
|
|
112899
|
+
}
|
|
112900
|
+
}
|
|
112901
|
+
return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
|
|
112902
|
+
}
|
|
112903
|
+
// src/tools/phase-complete.ts
|
|
112140
112904
|
init_resolve_working_directory();
|
|
112141
112905
|
function safeWarn(message, error93) {
|
|
112142
112906
|
try {
|
|
112143
112907
|
warn(message, error93 instanceof Error ? error93.message : String(error93));
|
|
112144
112908
|
} catch {}
|
|
112145
112909
|
}
|
|
112910
|
+
var MAX_OUTPUT_BYTES5 = 512000;
|
|
112146
112911
|
var TASK_GATE_INFERABLE_AGENTS = new Set([
|
|
112147
112912
|
"coder",
|
|
112148
112913
|
"reviewer",
|
|
@@ -112213,6 +112978,28 @@ function _getDelegationsSince(sessionID, sinceTimestamp) {
|
|
|
112213
112978
|
function isValidRetroEntry(entry, phase) {
|
|
112214
112979
|
return entry.type === "retrospective" && "phase_number" in entry && entry.phase_number === phase && "verdict" in entry && entry.verdict === "pass";
|
|
112215
112980
|
}
|
|
112981
|
+
function blockedResult(phase, gateResult) {
|
|
112982
|
+
const {
|
|
112983
|
+
reason,
|
|
112984
|
+
message,
|
|
112985
|
+
agentsDispatched,
|
|
112986
|
+
agentsMissing,
|
|
112987
|
+
warnings,
|
|
112988
|
+
blocked: _blocked,
|
|
112989
|
+
...extra
|
|
112990
|
+
} = gateResult;
|
|
112991
|
+
return JSON.stringify({
|
|
112992
|
+
success: false,
|
|
112993
|
+
phase,
|
|
112994
|
+
status: "blocked",
|
|
112995
|
+
reason,
|
|
112996
|
+
message,
|
|
112997
|
+
agentsDispatched,
|
|
112998
|
+
agentsMissing,
|
|
112999
|
+
warnings,
|
|
113000
|
+
...extra
|
|
113001
|
+
}, null, 2);
|
|
113002
|
+
}
|
|
112216
113003
|
async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
112217
113004
|
const phase = Number(args2.phase);
|
|
112218
113005
|
const summary = args2.summary;
|
|
@@ -112351,719 +113138,66 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
112351
113138
|
]
|
|
112352
113139
|
}, null, 2);
|
|
112353
113140
|
}
|
|
113141
|
+
const gateCtx = {
|
|
113142
|
+
phase,
|
|
113143
|
+
dir,
|
|
113144
|
+
sessionID,
|
|
113145
|
+
pluginConfig: config3,
|
|
113146
|
+
agentsDispatched,
|
|
113147
|
+
safeWarn
|
|
113148
|
+
};
|
|
112354
113149
|
if (hasActiveTurboMode(sessionID)) {
|
|
112355
113150
|
warnings.push(`Turbo mode active — skipped completion-verify, drift-verifier, hallucination-guard, mutation-gate, phase-council, and final-council gates for phase ${phase}.`);
|
|
112356
113151
|
} else {
|
|
112357
|
-
|
|
112358
|
-
const
|
|
112359
|
-
|
|
112360
|
-
|
|
112361
|
-
return JSON.stringify({
|
|
112362
|
-
success: false,
|
|
112363
|
-
phase,
|
|
112364
|
-
status: "blocked",
|
|
112365
|
-
reason: "COMPLETION_INCOMPLETE",
|
|
112366
|
-
message: `Phase ${phase} cannot be completed: ${completionResult.reason}`,
|
|
112367
|
-
agentsDispatched,
|
|
112368
|
-
agentsMissing: [],
|
|
112369
|
-
warnings: completionResult.blockedTasks ? [
|
|
112370
|
-
`Blocked tasks: ${completionResult.blockedTasks.map((t) => t.task_id).join(", ")}`
|
|
112371
|
-
] : []
|
|
112372
|
-
}, null, 2);
|
|
113152
|
+
{
|
|
113153
|
+
const gateResult = await runCompletionVerifyGate(gateCtx);
|
|
113154
|
+
if (gateResult.blocked) {
|
|
113155
|
+
return blockedResult(phase, gateResult);
|
|
112373
113156
|
}
|
|
112374
|
-
|
|
112375
|
-
safeWarn(`[phase_complete] Completion verify error (non-blocking):`, completionError);
|
|
113157
|
+
warnings.push(...gateResult.warnings);
|
|
112376
113158
|
}
|
|
112377
|
-
|
|
112378
|
-
|
|
112379
|
-
|
|
112380
|
-
|
|
112381
|
-
driftHasSpecMd = fs90.existsSync(specMdPath);
|
|
112382
|
-
const gatePlan = await loadPlan(dir);
|
|
112383
|
-
if (gatePlan) {
|
|
112384
|
-
const gatePlanId = derivePlanId(gatePlan);
|
|
112385
|
-
const gateProfile = getProfile(dir, gatePlanId);
|
|
112386
|
-
if (gateProfile) {
|
|
112387
|
-
const gateSession = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
|
|
112388
|
-
const gateOverrides = gateSession?.qaGateSessionOverrides ?? {};
|
|
112389
|
-
const gateEffective = getEffectiveGates(gateProfile, gateOverrides);
|
|
112390
|
-
driftCheckEnabled = gateEffective.drift_check === true;
|
|
112391
|
-
}
|
|
112392
|
-
}
|
|
112393
|
-
} catch (gateLoadError) {
|
|
112394
|
-
safeWarn(`[phase_complete] QA gate profile load error, drift_check defaults to enabled:`, gateLoadError);
|
|
112395
|
-
}
|
|
112396
|
-
if (!driftCheckEnabled) {
|
|
112397
|
-
warnings.push(`drift_check gate is disabled. Drift verification was skipped for phase ${phase}.`);
|
|
112398
|
-
} else {
|
|
112399
|
-
let phaseType;
|
|
112400
|
-
try {
|
|
112401
|
-
const planPath = path127.join(dir, ".swarm", "plan.json");
|
|
112402
|
-
if (fs90.existsSync(planPath)) {
|
|
112403
|
-
const planRaw = fs90.readFileSync(planPath, "utf-8");
|
|
112404
|
-
const plan = JSON.parse(planRaw);
|
|
112405
|
-
const targetPhase = plan.phases?.find((p) => p.id === phase);
|
|
112406
|
-
phaseType = targetPhase?.type;
|
|
112407
|
-
}
|
|
112408
|
-
} catch {}
|
|
112409
|
-
if (phaseType === "non-code") {
|
|
112410
|
-
warnings.push(`Phase ${phase} is annotated as 'non-code'. Drift verification was skipped per phase type annotation.`);
|
|
112411
|
-
} else {
|
|
112412
|
-
try {
|
|
112413
|
-
const driftEvidencePath = path127.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
|
|
112414
|
-
let driftVerdictFound = false;
|
|
112415
|
-
let driftVerdictApproved = false;
|
|
112416
|
-
try {
|
|
112417
|
-
const driftEvidenceContent = fs90.readFileSync(driftEvidencePath, "utf-8");
|
|
112418
|
-
const driftEvidence = JSON.parse(driftEvidenceContent);
|
|
112419
|
-
const entries = driftEvidence.entries ?? [];
|
|
112420
|
-
for (const entry of entries) {
|
|
112421
|
-
if (typeof entry.type === "string" && entry.type.includes("drift") && typeof entry.verdict === "string") {
|
|
112422
|
-
driftVerdictFound = true;
|
|
112423
|
-
if (entry.verdict === "approved") {
|
|
112424
|
-
driftVerdictApproved = true;
|
|
112425
|
-
}
|
|
112426
|
-
if (entry.verdict === "rejected" || typeof entry.summary === "string" && entry.summary.includes("NEEDS_REVISION")) {
|
|
112427
|
-
return JSON.stringify({
|
|
112428
|
-
success: false,
|
|
112429
|
-
phase,
|
|
112430
|
-
status: "blocked",
|
|
112431
|
-
reason: "DRIFT_VERIFICATION_REJECTED",
|
|
112432
|
-
message: `Phase ${phase} cannot be completed: drift verifier returned verdict '${entry.verdict}'. Address the drift issues before completing the phase.`,
|
|
112433
|
-
agentsDispatched,
|
|
112434
|
-
agentsMissing: [],
|
|
112435
|
-
warnings: []
|
|
112436
|
-
}, null, 2);
|
|
112437
|
-
}
|
|
112438
|
-
}
|
|
112439
|
-
}
|
|
112440
|
-
} catch (readError) {
|
|
112441
|
-
if (readError.code !== "ENOENT") {
|
|
112442
|
-
safeWarn(`[phase_complete] Drift verifier evidence unreadable:`, readError);
|
|
112443
|
-
}
|
|
112444
|
-
driftVerdictFound = false;
|
|
112445
|
-
}
|
|
112446
|
-
if (!driftVerdictFound) {
|
|
112447
|
-
if (!driftHasSpecMd) {
|
|
112448
|
-
let incompleteTaskCount = 0;
|
|
112449
|
-
let planParseable = false;
|
|
112450
|
-
try {
|
|
112451
|
-
const planPath = path127.join(dir, ".swarm", "plan.json");
|
|
112452
|
-
if (fs90.existsSync(planPath)) {
|
|
112453
|
-
const planRaw = fs90.readFileSync(planPath, "utf-8");
|
|
112454
|
-
const plan = JSON.parse(planRaw);
|
|
112455
|
-
planParseable = true;
|
|
112456
|
-
const planPhase = plan.phases?.find((p) => p.id === phase);
|
|
112457
|
-
if (planPhase?.tasks) {
|
|
112458
|
-
incompleteTaskCount = planPhase.tasks.filter((t) => t.status !== "completed" && t.status !== "closed").length;
|
|
112459
|
-
}
|
|
112460
|
-
}
|
|
112461
|
-
} catch {}
|
|
112462
|
-
if (!planParseable) {
|
|
112463
|
-
warnings.push(`No spec.md found and drift verification evidence missing — consider running critic_drift_verifier before phase completion.`);
|
|
112464
|
-
} else if (incompleteTaskCount > 0) {
|
|
112465
|
-
warnings.push(`No spec.md found and drift verification evidence missing. Phase ${phase} has ${incompleteTaskCount} incomplete task(s) in plan.json — consider running critic_drift_verifier before phase completion.`);
|
|
112466
|
-
} else {
|
|
112467
|
-
warnings.push(`No spec.md found. Phase ${phase} tasks are all completed in plan.json. Drift verification was skipped.`);
|
|
112468
|
-
}
|
|
112469
|
-
} else {
|
|
112470
|
-
return JSON.stringify({
|
|
112471
|
-
success: false,
|
|
112472
|
-
phase,
|
|
112473
|
-
status: "blocked",
|
|
112474
|
-
reason: "DRIFT_VERIFICATION_MISSING",
|
|
112475
|
-
message: `Phase ${phase} cannot be completed: drift_check is enabled and drift verifier evidence not found at .swarm/evidence/${phase}/drift-verifier.json. Run drift verification before completing the phase.`,
|
|
112476
|
-
agentsDispatched,
|
|
112477
|
-
agentsMissing: [],
|
|
112478
|
-
warnings: []
|
|
112479
|
-
}, null, 2);
|
|
112480
|
-
}
|
|
112481
|
-
}
|
|
112482
|
-
if (!driftVerdictApproved && driftVerdictFound) {
|
|
112483
|
-
return JSON.stringify({
|
|
112484
|
-
success: false,
|
|
112485
|
-
phase,
|
|
112486
|
-
status: "blocked",
|
|
112487
|
-
reason: "DRIFT_VERIFICATION_REJECTED",
|
|
112488
|
-
message: `Phase ${phase} cannot be completed: drift verifier verdict is not approved.`,
|
|
112489
|
-
agentsDispatched,
|
|
112490
|
-
agentsMissing: [],
|
|
112491
|
-
warnings: []
|
|
112492
|
-
}, null, 2);
|
|
112493
|
-
}
|
|
112494
|
-
} catch (driftError) {
|
|
112495
|
-
return JSON.stringify({
|
|
112496
|
-
success: false,
|
|
112497
|
-
phase,
|
|
112498
|
-
status: "blocked",
|
|
112499
|
-
reason: "DRIFT_VERIFICATION_ERROR",
|
|
112500
|
-
message: `Phase ${phase} cannot be completed: drift verification encountered an error: ${driftError instanceof Error ? driftError.message : String(driftError)}. This is a hard block — resolve the error before completing the phase.`,
|
|
112501
|
-
agentsDispatched,
|
|
112502
|
-
agentsMissing: [],
|
|
112503
|
-
warnings: []
|
|
112504
|
-
}, null, 2);
|
|
112505
|
-
}
|
|
113159
|
+
{
|
|
113160
|
+
const gateResult = await runDriftGate(gateCtx);
|
|
113161
|
+
if (gateResult.blocked) {
|
|
113162
|
+
return blockedResult(phase, gateResult);
|
|
112506
113163
|
}
|
|
113164
|
+
warnings.push(...gateResult.warnings);
|
|
112507
113165
|
}
|
|
112508
|
-
|
|
112509
|
-
const
|
|
112510
|
-
if (
|
|
112511
|
-
|
|
112512
|
-
const profile = getProfile(dir, planId);
|
|
112513
|
-
if (profile) {
|
|
112514
|
-
const session2 = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
|
|
112515
|
-
const overrides = session2?.qaGateSessionOverrides ?? {};
|
|
112516
|
-
const effective = getEffectiveGates(profile, overrides);
|
|
112517
|
-
if (effective.hallucination_guard === true) {
|
|
112518
|
-
const hgPath = path127.join(dir, ".swarm", "evidence", String(phase), "hallucination-guard.json");
|
|
112519
|
-
let hgVerdictFound = false;
|
|
112520
|
-
let hgVerdictApproved = false;
|
|
112521
|
-
try {
|
|
112522
|
-
const hgContent = fs90.readFileSync(hgPath, "utf-8");
|
|
112523
|
-
const hgBundle = JSON.parse(hgContent);
|
|
112524
|
-
for (const entry of hgBundle.entries ?? []) {
|
|
112525
|
-
if (typeof entry.type === "string" && entry.type.includes("hallucination") && typeof entry.verdict === "string") {
|
|
112526
|
-
hgVerdictFound = true;
|
|
112527
|
-
if (entry.verdict === "approved") {
|
|
112528
|
-
hgVerdictApproved = true;
|
|
112529
|
-
}
|
|
112530
|
-
if (entry.verdict === "rejected" || typeof entry.summary === "string" && entry.summary.includes("NEEDS_REVISION")) {
|
|
112531
|
-
return JSON.stringify({
|
|
112532
|
-
success: false,
|
|
112533
|
-
phase,
|
|
112534
|
-
status: "blocked",
|
|
112535
|
-
reason: "HALLUCINATION_VERIFICATION_REJECTED",
|
|
112536
|
-
message: `Phase ${phase} cannot be completed: hallucination verifier returned verdict '${entry.verdict}'. Remove fabricated APIs/signatures and fix broken citations before completing the phase.`,
|
|
112537
|
-
agentsDispatched,
|
|
112538
|
-
agentsMissing: [],
|
|
112539
|
-
warnings: []
|
|
112540
|
-
}, null, 2);
|
|
112541
|
-
}
|
|
112542
|
-
}
|
|
112543
|
-
}
|
|
112544
|
-
} catch (readErr) {
|
|
112545
|
-
if (readErr.code !== "ENOENT") {
|
|
112546
|
-
safeWarn(`[phase_complete] Hallucination guard evidence unreadable:`, readErr);
|
|
112547
|
-
}
|
|
112548
|
-
hgVerdictFound = false;
|
|
112549
|
-
}
|
|
112550
|
-
if (!hgVerdictFound) {
|
|
112551
|
-
return JSON.stringify({
|
|
112552
|
-
success: false,
|
|
112553
|
-
phase,
|
|
112554
|
-
status: "blocked",
|
|
112555
|
-
reason: "HALLUCINATION_VERIFICATION_MISSING",
|
|
112556
|
-
message: `Phase ${phase} cannot be completed: hallucination_guard is enabled and evidence not found at .swarm/evidence/${phase}/hallucination-guard.json. Delegate to critic_hallucination_verifier and call write_hallucination_evidence before completing the phase.`,
|
|
112557
|
-
agentsDispatched,
|
|
112558
|
-
agentsMissing: [],
|
|
112559
|
-
warnings: []
|
|
112560
|
-
}, null, 2);
|
|
112561
|
-
}
|
|
112562
|
-
if (!hgVerdictApproved) {
|
|
112563
|
-
return JSON.stringify({
|
|
112564
|
-
success: false,
|
|
112565
|
-
phase,
|
|
112566
|
-
status: "blocked",
|
|
112567
|
-
reason: "HALLUCINATION_VERIFICATION_REJECTED",
|
|
112568
|
-
message: `Phase ${phase} cannot be completed: hallucination verifier verdict is not approved.`,
|
|
112569
|
-
agentsDispatched,
|
|
112570
|
-
agentsMissing: [],
|
|
112571
|
-
warnings: []
|
|
112572
|
-
}, null, 2);
|
|
112573
|
-
}
|
|
112574
|
-
}
|
|
112575
|
-
}
|
|
113166
|
+
{
|
|
113167
|
+
const gateResult = await runHallucinationGate(gateCtx);
|
|
113168
|
+
if (gateResult.blocked) {
|
|
113169
|
+
return blockedResult(phase, gateResult);
|
|
112576
113170
|
}
|
|
112577
|
-
|
|
112578
|
-
safeWarn(`[phase_complete] Hallucination guard error (non-blocking):`, hgError);
|
|
113171
|
+
warnings.push(...gateResult.warnings);
|
|
112579
113172
|
}
|
|
112580
|
-
|
|
112581
|
-
const
|
|
112582
|
-
if (
|
|
112583
|
-
|
|
112584
|
-
const profile = getProfile(dir, planId);
|
|
112585
|
-
if (profile) {
|
|
112586
|
-
const session2 = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
|
|
112587
|
-
const overrides = session2?.qaGateSessionOverrides ?? {};
|
|
112588
|
-
const effective = getEffectiveGates(profile, overrides);
|
|
112589
|
-
if (effective.mutation_test === true) {
|
|
112590
|
-
const mgPath = path127.join(dir, ".swarm", "evidence", String(phase), "mutation-gate.json");
|
|
112591
|
-
let mgVerdictFound = false;
|
|
112592
|
-
let mgVerdict;
|
|
112593
|
-
try {
|
|
112594
|
-
const mgContent = fs90.readFileSync(mgPath, "utf-8");
|
|
112595
|
-
const mgBundle = JSON.parse(mgContent);
|
|
112596
|
-
for (const entry of mgBundle.entries ?? []) {
|
|
112597
|
-
if (typeof entry.type === "string" && entry.type === "mutation-gate" && typeof entry.verdict === "string") {
|
|
112598
|
-
mgVerdictFound = true;
|
|
112599
|
-
mgVerdict = entry.verdict;
|
|
112600
|
-
if (entry.verdict === "fail") {
|
|
112601
|
-
return JSON.stringify({
|
|
112602
|
-
success: false,
|
|
112603
|
-
phase,
|
|
112604
|
-
status: "blocked",
|
|
112605
|
-
reason: "MUTATION_GATE_FAIL",
|
|
112606
|
-
message: `Phase ${phase} cannot be completed: mutation gate returned verdict 'fail'. Resolve surviving mutants or lower the kill-rate threshold before completing the phase.`,
|
|
112607
|
-
agentsDispatched,
|
|
112608
|
-
agentsMissing: [],
|
|
112609
|
-
warnings: []
|
|
112610
|
-
}, null, 2);
|
|
112611
|
-
} else if (!["pass", "warn", "skip"].includes(entry.verdict)) {
|
|
112612
|
-
return JSON.stringify({
|
|
112613
|
-
success: false,
|
|
112614
|
-
phase,
|
|
112615
|
-
status: "blocked",
|
|
112616
|
-
reason: "MUTATION_GATE_FAIL",
|
|
112617
|
-
message: `Phase ${phase} cannot be completed: mutation gate evidence contains unrecognized verdict '${entry.verdict}'. Expected one of: pass, warn, fail, skip.`,
|
|
112618
|
-
agentsDispatched,
|
|
112619
|
-
agentsMissing: [],
|
|
112620
|
-
warnings: []
|
|
112621
|
-
}, null, 2);
|
|
112622
|
-
}
|
|
112623
|
-
}
|
|
112624
|
-
}
|
|
112625
|
-
} catch (readErr) {
|
|
112626
|
-
if (readErr.code !== "ENOENT") {
|
|
112627
|
-
safeWarn(`[phase_complete] Mutation gate evidence unreadable:`, readErr);
|
|
112628
|
-
}
|
|
112629
|
-
mgVerdictFound = false;
|
|
112630
|
-
}
|
|
112631
|
-
if (!mgVerdictFound) {
|
|
112632
|
-
return JSON.stringify({
|
|
112633
|
-
success: false,
|
|
112634
|
-
phase,
|
|
112635
|
-
status: "blocked",
|
|
112636
|
-
reason: "MUTATION_GATE_MISSING",
|
|
112637
|
-
message: `Phase ${phase} cannot be completed: mutation_test is enabled and evidence not found at .swarm/evidence/${phase}/mutation-gate.json. Run mutation_test, then call write_mutation_evidence before completing the phase.`,
|
|
112638
|
-
agentsDispatched,
|
|
112639
|
-
agentsMissing: [],
|
|
112640
|
-
warnings: []
|
|
112641
|
-
}, null, 2);
|
|
112642
|
-
}
|
|
112643
|
-
if (mgVerdict === "warn") {
|
|
112644
|
-
safeWarn(`[phase_complete] Mutation gate verdict is 'warn' for phase ${phase} — proceeding with warning`, undefined);
|
|
112645
|
-
}
|
|
112646
|
-
}
|
|
112647
|
-
}
|
|
113173
|
+
{
|
|
113174
|
+
const gateResult = await runMutationGate(gateCtx);
|
|
113175
|
+
if (gateResult.blocked) {
|
|
113176
|
+
return blockedResult(phase, gateResult);
|
|
112648
113177
|
}
|
|
112649
|
-
|
|
112650
|
-
safeWarn(`[phase_complete] Mutation gate error (non-blocking):`, mgError);
|
|
113178
|
+
warnings.push(...gateResult.warnings);
|
|
112651
113179
|
}
|
|
112652
|
-
|
|
112653
|
-
|
|
112654
|
-
|
|
112655
|
-
|
|
112656
|
-
const planId = derivePlanId(plan);
|
|
112657
|
-
const profile = getProfile(dir, planId);
|
|
112658
|
-
if (profile) {
|
|
112659
|
-
const session2 = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
|
|
112660
|
-
const overrides = session2?.qaGateSessionOverrides ?? {};
|
|
112661
|
-
const effective = getEffectiveGates(profile, overrides);
|
|
112662
|
-
if (effective.council_mode === true) {
|
|
112663
|
-
councilModeEnabled = true;
|
|
112664
|
-
const pcPath = path127.join(dir, ".swarm", "evidence", String(phase), "phase-council.json");
|
|
112665
|
-
let pcVerdictFound = false;
|
|
112666
|
-
let _pcVerdict;
|
|
112667
|
-
let pcQuorumSize;
|
|
112668
|
-
let pcTimestamp;
|
|
112669
|
-
let pcPhaseNumber;
|
|
112670
|
-
try {
|
|
112671
|
-
const pcContent = fs90.readFileSync(pcPath, "utf-8");
|
|
112672
|
-
const pcBundle = JSON.parse(pcContent);
|
|
112673
|
-
for (const entry of pcBundle.entries ?? []) {
|
|
112674
|
-
if (typeof entry.type === "string" && entry.type === "phase-council" && typeof entry.verdict === "string") {
|
|
112675
|
-
pcVerdictFound = true;
|
|
112676
|
-
_pcVerdict = entry.verdict;
|
|
112677
|
-
pcQuorumSize = typeof entry.quorumSize === "number" ? entry.quorumSize : undefined;
|
|
112678
|
-
pcTimestamp = typeof entry.timestamp === "string" ? entry.timestamp : undefined;
|
|
112679
|
-
pcPhaseNumber = typeof entry.phase_number === "number" ? entry.phase_number : typeof entry.phase === "number" ? entry.phase : undefined;
|
|
112680
|
-
const now2 = new Date;
|
|
112681
|
-
const pcTime = pcTimestamp ? new Date(pcTimestamp) : null;
|
|
112682
|
-
if (!pcTime || Number.isNaN(pcTime.getTime())) {
|
|
112683
|
-
return JSON.stringify({
|
|
112684
|
-
success: false,
|
|
112685
|
-
phase,
|
|
112686
|
-
status: "blocked",
|
|
112687
|
-
reason: "PHASE_COUNCIL_INVALID_TIMESTAMP",
|
|
112688
|
-
message: `Phase ${phase} cannot be completed: phase council evidence has missing or invalid timestamp.`,
|
|
112689
|
-
agentsDispatched,
|
|
112690
|
-
agentsMissing: [],
|
|
112691
|
-
warnings: []
|
|
112692
|
-
}, null, 2);
|
|
112693
|
-
}
|
|
112694
|
-
const maxAge = 24 * 60 * 60 * 1000;
|
|
112695
|
-
if (pcTime.getTime() > now2.getTime()) {
|
|
112696
|
-
return JSON.stringify({
|
|
112697
|
-
success: false,
|
|
112698
|
-
phase,
|
|
112699
|
-
status: "blocked",
|
|
112700
|
-
reason: "PHASE_COUNCIL_FUTURE_TIMESTAMP",
|
|
112701
|
-
message: `Phase ${phase} cannot be completed: phase council evidence timestamp is in the future.`,
|
|
112702
|
-
agentsDispatched,
|
|
112703
|
-
agentsMissing: [],
|
|
112704
|
-
warnings: []
|
|
112705
|
-
}, null, 2);
|
|
112706
|
-
}
|
|
112707
|
-
if (now2.getTime() - pcTime.getTime() > maxAge) {
|
|
112708
|
-
return JSON.stringify({
|
|
112709
|
-
success: false,
|
|
112710
|
-
phase,
|
|
112711
|
-
status: "blocked",
|
|
112712
|
-
reason: "PHASE_COUNCIL_STALE_EVIDENCE",
|
|
112713
|
-
message: `Phase ${phase} cannot be completed: phase council evidence is older than 24 hours. Re-convene council for fresh review.`,
|
|
112714
|
-
agentsDispatched,
|
|
112715
|
-
agentsMissing: [],
|
|
112716
|
-
warnings: []
|
|
112717
|
-
}, null, 2);
|
|
112718
|
-
}
|
|
112719
|
-
if (entry.verdict === "REJECT" || entry.verdict === "reject") {
|
|
112720
|
-
const requiredFixes = entry.requiredFixes ?? entry.required_fixes ?? [];
|
|
112721
|
-
const fixesDetail = Array.isArray(requiredFixes) && requiredFixes.length > 0 ? `
|
|
112722
|
-
Required fixes: ${requiredFixes.map((f) => f.detail ?? JSON.stringify(f)).join("; ")}` : "";
|
|
112723
|
-
return JSON.stringify({
|
|
112724
|
-
success: false,
|
|
112725
|
-
phase,
|
|
112726
|
-
status: "blocked",
|
|
112727
|
-
reason: "PHASE_COUNCIL_REJECTED",
|
|
112728
|
-
message: `Phase ${phase} cannot be completed: phase council returned verdict 'REJECT'. Address the required fixes before completing the phase.${fixesDetail}`,
|
|
112729
|
-
agentsDispatched,
|
|
112730
|
-
agentsMissing: [],
|
|
112731
|
-
warnings: []
|
|
112732
|
-
}, null, 2);
|
|
112733
|
-
}
|
|
112734
|
-
if (entry.verdict === "CONCERNS" || entry.verdict === "concerns") {
|
|
112735
|
-
const phaseConcernsAllow = config3.council?.phaseConcernsAllowComplete ?? true;
|
|
112736
|
-
if (!phaseConcernsAllow) {
|
|
112737
|
-
const advisoryNotes = entry.advisoryNotes ?? entry.advisory_notes ?? [];
|
|
112738
|
-
const notesDetail = Array.isArray(advisoryNotes) && advisoryNotes.length > 0 ? `
|
|
112739
|
-
Advisory notes: ${advisoryNotes.join("; ")}` : "";
|
|
112740
|
-
return JSON.stringify({
|
|
112741
|
-
success: false,
|
|
112742
|
-
phase,
|
|
112743
|
-
status: "blocked",
|
|
112744
|
-
reason: "PHASE_COUNCIL_CONCERNS",
|
|
112745
|
-
message: `Phase ${phase} cannot be completed: phase council returned verdict 'CONCERNS'.${notesDetail}`,
|
|
112746
|
-
agentsDispatched,
|
|
112747
|
-
agentsMissing: [],
|
|
112748
|
-
warnings: []
|
|
112749
|
-
}, null, 2);
|
|
112750
|
-
}
|
|
112751
|
-
safeWarn(`[phase_complete] Phase council returned CONCERNS for phase ${phase} — proceeding (phaseConcernsAllowComplete is enabled)`, undefined);
|
|
112752
|
-
}
|
|
112753
|
-
if (entry.verdict !== "APPROVE" && entry.verdict !== "approve" && entry.verdict !== "CONCERNS" && entry.verdict !== "concerns") {
|
|
112754
|
-
return JSON.stringify({
|
|
112755
|
-
success: false,
|
|
112756
|
-
phase,
|
|
112757
|
-
status: "blocked",
|
|
112758
|
-
reason: "PHASE_COUNCIL_INVALID",
|
|
112759
|
-
message: `Phase ${phase} cannot be completed: phase council evidence contains unrecognized verdict '${entry.verdict}'. Expected one of: APPROVE, CONCERNS, REJECT.`,
|
|
112760
|
-
agentsDispatched,
|
|
112761
|
-
agentsMissing: [],
|
|
112762
|
-
warnings: []
|
|
112763
|
-
}, null, 2);
|
|
112764
|
-
}
|
|
112765
|
-
}
|
|
112766
|
-
}
|
|
112767
|
-
} catch (readErr) {
|
|
112768
|
-
if (readErr.code !== "ENOENT") {
|
|
112769
|
-
safeWarn(`[phase_complete] Phase council evidence unreadable:`, readErr);
|
|
112770
|
-
}
|
|
112771
|
-
pcVerdictFound = false;
|
|
112772
|
-
}
|
|
112773
|
-
if (!pcVerdictFound) {
|
|
112774
|
-
return JSON.stringify({
|
|
112775
|
-
success: false,
|
|
112776
|
-
phase,
|
|
112777
|
-
status: "blocked",
|
|
112778
|
-
reason: "PHASE_COUNCIL_REQUIRED",
|
|
112779
|
-
phase_council_required: true,
|
|
112780
|
-
message: `Phase ${phase} cannot be completed: council_mode is enabled and phase council evidence not found at .swarm/evidence/${phase}/phase-council.json. Convene a phase-level council (dispatch 5 members, collect verdicts, call submit_phase_council_verdicts) before completing the phase.`,
|
|
112781
|
-
agentsDispatched,
|
|
112782
|
-
agentsMissing: [],
|
|
112783
|
-
warnings: [
|
|
112784
|
-
`Phase council required — convene 5 council members (critic, reviewer, sme, test_engineer, explorer) for holistic phase review. Call submit_phase_council_verdicts to synthesize verdicts and write phase-council.json evidence.`
|
|
112785
|
-
]
|
|
112786
|
-
}, null, 2);
|
|
112787
|
-
}
|
|
112788
|
-
if (pcQuorumSize === undefined || typeof pcQuorumSize !== "number") {
|
|
112789
|
-
return JSON.stringify({
|
|
112790
|
-
success: false,
|
|
112791
|
-
phase,
|
|
112792
|
-
status: "blocked",
|
|
112793
|
-
reason: "PHASE_COUNCIL_MISSING_QUORUM",
|
|
112794
|
-
message: `Phase ${phase} cannot be completed: phase council evidence is missing quorumSize field.`,
|
|
112795
|
-
agentsDispatched,
|
|
112796
|
-
agentsMissing: [],
|
|
112797
|
-
warnings: []
|
|
112798
|
-
}, null, 2);
|
|
112799
|
-
}
|
|
112800
|
-
if (pcQuorumSize < 3) {
|
|
112801
|
-
return JSON.stringify({
|
|
112802
|
-
success: false,
|
|
112803
|
-
phase,
|
|
112804
|
-
status: "blocked",
|
|
112805
|
-
reason: "PHASE_COUNCIL_INSUFFICIENT_QUORUM",
|
|
112806
|
-
message: `Phase ${phase} cannot be completed: phase council quorum (${pcQuorumSize}) is below minimum (3). Re-convene council with sufficient members.`,
|
|
112807
|
-
agentsDispatched,
|
|
112808
|
-
agentsMissing: [],
|
|
112809
|
-
warnings: []
|
|
112810
|
-
}, null, 2);
|
|
112811
|
-
}
|
|
112812
|
-
if (pcPhaseNumber === undefined || typeof pcPhaseNumber !== "number") {
|
|
112813
|
-
return JSON.stringify({
|
|
112814
|
-
success: false,
|
|
112815
|
-
phase,
|
|
112816
|
-
status: "blocked",
|
|
112817
|
-
reason: "PHASE_COUNCIL_MISSING_PHASE",
|
|
112818
|
-
message: `Phase ${phase} cannot be completed: phase council evidence is missing phase_number field.`,
|
|
112819
|
-
agentsDispatched,
|
|
112820
|
-
agentsMissing: [],
|
|
112821
|
-
warnings: []
|
|
112822
|
-
}, null, 2);
|
|
112823
|
-
}
|
|
112824
|
-
if (pcPhaseNumber !== phase) {
|
|
112825
|
-
return JSON.stringify({
|
|
112826
|
-
success: false,
|
|
112827
|
-
phase,
|
|
112828
|
-
status: "blocked",
|
|
112829
|
-
reason: "PHASE_COUNCIL_PHASE_MISMATCH",
|
|
112830
|
-
message: `Phase ${phase} cannot be completed: phase council evidence is for phase ${pcPhaseNumber}, not phase ${phase}. Run council for the correct phase.`,
|
|
112831
|
-
agentsDispatched,
|
|
112832
|
-
agentsMissing: [],
|
|
112833
|
-
warnings: []
|
|
112834
|
-
}, null, 2);
|
|
112835
|
-
}
|
|
112836
|
-
}
|
|
112837
|
-
}
|
|
112838
|
-
}
|
|
112839
|
-
} catch (pcError) {
|
|
112840
|
-
if (councilModeEnabled) {
|
|
112841
|
-
warnings.push(`PHASE_COUNCIL_ERROR: ${String(pcError)}`);
|
|
112842
|
-
return JSON.stringify({
|
|
112843
|
-
success: false,
|
|
112844
|
-
phase,
|
|
112845
|
-
status: "blocked",
|
|
112846
|
-
reason: "PHASE_COUNCIL_ERROR",
|
|
112847
|
-
message: `Phase ${phase} cannot be completed: phase council gate encountered an error when council_mode was enabled. Error: ${String(pcError)}`,
|
|
112848
|
-
agentsDispatched,
|
|
112849
|
-
agentsMissing: [],
|
|
112850
|
-
warnings: [`PHASE_COUNCIL_ERROR: ${String(pcError)}`]
|
|
112851
|
-
}, null, 2);
|
|
112852
|
-
} else {
|
|
112853
|
-
safeWarn(`[phase_complete] Phase council gate error (non-blocking):`, pcError);
|
|
113180
|
+
{
|
|
113181
|
+
const gateResult = await runPhaseCouncilGate(gateCtx);
|
|
113182
|
+
if (gateResult.blocked) {
|
|
113183
|
+
return blockedResult(phase, gateResult);
|
|
112854
113184
|
}
|
|
113185
|
+
warnings.push(...gateResult.warnings);
|
|
112855
113186
|
}
|
|
112856
113187
|
}
|
|
112857
113188
|
if (config3.architectural_supervision?.enabled && config3.architectural_supervision.mode === "gate") {
|
|
112858
|
-
const
|
|
112859
|
-
|
|
112860
|
-
|
|
112861
|
-
return "";
|
|
112862
|
-
const details = findings.map((f) => f && typeof f === "object" && typeof f.description === "string" ? f.description : undefined).filter((d) => Boolean(d));
|
|
112863
|
-
return details.length > 0 ? `
|
|
112864
|
-
Findings: ${details.join("; ")}` : "";
|
|
112865
|
-
};
|
|
112866
|
-
const asBlocked = (reason, message2) => JSON.stringify({
|
|
112867
|
-
success: false,
|
|
112868
|
-
phase,
|
|
112869
|
-
status: "blocked",
|
|
112870
|
-
reason,
|
|
112871
|
-
message: message2,
|
|
112872
|
-
agentsDispatched,
|
|
112873
|
-
agentsMissing: [],
|
|
112874
|
-
warnings: []
|
|
112875
|
-
}, null, 2);
|
|
112876
|
-
let asEntry = null;
|
|
112877
|
-
try {
|
|
112878
|
-
asEntry = readSupervisorReportRaw(dir, phase);
|
|
112879
|
-
} catch (asError) {
|
|
112880
|
-
return asBlocked("ARCH_SUPERVISOR_ERROR", `Phase ${phase} cannot be completed: architecture supervisor gate encountered an error. Error: ${String(asError)}`);
|
|
112881
|
-
}
|
|
112882
|
-
if (!asEntry) {
|
|
112883
|
-
return asBlocked("ARCH_SUPERVISOR_REQUIRED", `Phase ${phase} cannot be completed: architectural_supervision gate mode is enabled and no architecture supervisor evidence was found at .swarm/evidence/${phase}/architecture-supervisor.json. Dispatch critic_architecture_supervisor with the phase + agent summaries, then call write_architecture_supervisor_evidence.`);
|
|
112884
|
-
}
|
|
112885
|
-
const now2 = new Date;
|
|
112886
|
-
const asTime = asEntry.timestamp ? new Date(asEntry.timestamp) : null;
|
|
112887
|
-
if (!asTime || Number.isNaN(asTime.getTime())) {
|
|
112888
|
-
return asBlocked("ARCH_SUPERVISOR_INVALID_TIMESTAMP", `Phase ${phase} cannot be completed: architecture supervisor evidence has a missing or invalid timestamp.`);
|
|
112889
|
-
}
|
|
112890
|
-
if (asTime.getTime() > now2.getTime()) {
|
|
112891
|
-
return asBlocked("ARCH_SUPERVISOR_FUTURE_TIMESTAMP", `Phase ${phase} cannot be completed: architecture supervisor evidence timestamp is in the future.`);
|
|
112892
|
-
}
|
|
112893
|
-
if (now2.getTime() - asTime.getTime() > 24 * 60 * 60 * 1000) {
|
|
112894
|
-
return asBlocked("ARCH_SUPERVISOR_STALE_EVIDENCE", `Phase ${phase} cannot be completed: architecture supervisor evidence is older than 24 hours. Re-run the supervisor for fresh review.`);
|
|
112895
|
-
}
|
|
112896
|
-
if (typeof asEntry.phase_number !== "number" || asEntry.phase_number !== phase) {
|
|
112897
|
-
return asBlocked("ARCH_SUPERVISOR_PHASE_MISMATCH", `Phase ${phase} cannot be completed: architecture supervisor evidence is for phase ${String(asEntry.phase_number)}, not phase ${phase}.`);
|
|
112898
|
-
}
|
|
112899
|
-
const asVerdict = asEntry.verdict;
|
|
112900
|
-
if (asVerdict === "REJECT") {
|
|
112901
|
-
return asBlocked("ARCH_SUPERVISOR_REJECTED", `Phase ${phase} cannot be completed: architecture supervisor returned verdict 'REJECT'. Address the system-level findings before completing the phase.${summarizeFindings(asEntry.findings)}`);
|
|
112902
|
-
}
|
|
112903
|
-
if (asVerdict === "CONCERNS") {
|
|
112904
|
-
if (asConfig.allow_concerns_to_complete === false) {
|
|
112905
|
-
return asBlocked("ARCH_SUPERVISOR_CONCERNS", `Phase ${phase} cannot be completed: architecture supervisor returned verdict 'CONCERNS' and allow_concerns_to_complete is disabled.${summarizeFindings(asEntry.findings)}`);
|
|
112906
|
-
}
|
|
112907
|
-
safeWarn(`[phase_complete] Architecture supervisor returned CONCERNS for phase ${phase} — proceeding (allow_concerns_to_complete is enabled)`, undefined);
|
|
112908
|
-
} else if (asVerdict !== "APPROVE") {
|
|
112909
|
-
return asBlocked("ARCH_SUPERVISOR_INVALID", `Phase ${phase} cannot be completed: architecture supervisor evidence contains unrecognized verdict '${String(asVerdict)}'. Expected one of: APPROVE, CONCERNS, REJECT.`);
|
|
113189
|
+
const gateResult = await runArchitectureSupervisorGate(gateCtx);
|
|
113190
|
+
if (gateResult.blocked) {
|
|
113191
|
+
return blockedResult(phase, gateResult);
|
|
112910
113192
|
}
|
|
113193
|
+
warnings.push(...gateResult.warnings);
|
|
112911
113194
|
}
|
|
112912
113195
|
if (!hasActiveTurboMode(sessionID)) {
|
|
112913
|
-
|
|
112914
|
-
|
|
112915
|
-
|
|
112916
|
-
if (plan) {
|
|
112917
|
-
const lastPhaseId = plan.phases[plan.phases.length - 1]?.id;
|
|
112918
|
-
if (lastPhaseId !== undefined && phase === lastPhaseId) {
|
|
112919
|
-
const planId = derivePlanId(plan);
|
|
112920
|
-
const profile = getProfile(dir, planId);
|
|
112921
|
-
if (profile) {
|
|
112922
|
-
const session2 = sessionID ? swarmState.agentSessions.get(sessionID) : undefined;
|
|
112923
|
-
const overrides = session2?.qaGateSessionOverrides ?? {};
|
|
112924
|
-
const effective = getEffectiveGates(profile, overrides);
|
|
112925
|
-
if (effective.final_council === true) {
|
|
112926
|
-
finalCouncilEnabled = true;
|
|
112927
|
-
const fcPath = path127.join(dir, ".swarm", "evidence", "final-council.json");
|
|
112928
|
-
let fcVerdictFound = false;
|
|
112929
|
-
let _fcVerdict;
|
|
112930
|
-
try {
|
|
112931
|
-
const fcContent = fs90.readFileSync(fcPath, "utf-8");
|
|
112932
|
-
const fcBundle = JSON.parse(fcContent);
|
|
112933
|
-
for (const entry of fcBundle.entries ?? []) {
|
|
112934
|
-
if (typeof entry.type === "string" && entry.type === "final-council" && typeof entry.verdict === "string") {
|
|
112935
|
-
fcVerdictFound = true;
|
|
112936
|
-
_fcVerdict = entry.verdict;
|
|
112937
|
-
if (plan) {
|
|
112938
|
-
const currentPlanId = derivePlanId(plan);
|
|
112939
|
-
if (entry.plan_id && entry.plan_id !== currentPlanId) {
|
|
112940
|
-
return JSON.stringify({
|
|
112941
|
-
success: false,
|
|
112942
|
-
phase,
|
|
112943
|
-
status: "blocked",
|
|
112944
|
-
reason: "final_council_plan_mismatch",
|
|
112945
|
-
message: `Final council evidence belongs to a different plan (evidence: ${entry.plan_id}, current: ${currentPlanId}). Re-run the final council.`,
|
|
112946
|
-
agentsDispatched,
|
|
112947
|
-
agentsMissing: [],
|
|
112948
|
-
warnings: []
|
|
112949
|
-
}, null, 2);
|
|
112950
|
-
}
|
|
112951
|
-
if (!entry.plan_id) {
|
|
112952
|
-
return JSON.stringify({
|
|
112953
|
-
success: false,
|
|
112954
|
-
phase,
|
|
112955
|
-
status: "blocked",
|
|
112956
|
-
reason: "FINAL_COUNCIL_PLAN_ID_REQUIRED",
|
|
112957
|
-
message: `Phase ${phase} (last phase) cannot be completed: final council evidence is missing plan_id binding. Re-run the final council to generate evidence with plan identity.`,
|
|
112958
|
-
agentsDispatched,
|
|
112959
|
-
agentsMissing: [],
|
|
112960
|
-
warnings: []
|
|
112961
|
-
}, null, 2);
|
|
112962
|
-
}
|
|
112963
|
-
}
|
|
112964
|
-
if (typeof entry.quorumSize !== "number" || !Number.isFinite(entry.quorumSize) || entry.quorumSize < 5) {
|
|
112965
|
-
return JSON.stringify({
|
|
112966
|
-
success: false,
|
|
112967
|
-
phase,
|
|
112968
|
-
status: "blocked",
|
|
112969
|
-
reason: "FINAL_COUNCIL_MISSING_QUORUM",
|
|
112970
|
-
message: `Phase ${phase} (last phase) cannot be completed: final council evidence is missing valid quorum metadata. Re-run the project-scoped five-member final council and call write_final_council_evidence to generate quorumed evidence.`,
|
|
112971
|
-
agentsDispatched,
|
|
112972
|
-
agentsMissing: [],
|
|
112973
|
-
warnings: []
|
|
112974
|
-
}, null, 2);
|
|
112975
|
-
}
|
|
112976
|
-
const requiredFinalCouncilMembers = [
|
|
112977
|
-
"critic",
|
|
112978
|
-
"reviewer",
|
|
112979
|
-
"sme",
|
|
112980
|
-
"test_engineer",
|
|
112981
|
-
"explorer"
|
|
112982
|
-
];
|
|
112983
|
-
const membersVoted = Array.isArray(entry.membersVoted) ? entry.membersVoted.filter((member) => typeof member === "string") : [];
|
|
112984
|
-
const membersAbsent = Array.isArray(entry.membersAbsent) ? entry.membersAbsent.filter((member) => typeof member === "string") : [];
|
|
112985
|
-
const distinctMembersVoted = new Set(membersVoted);
|
|
112986
|
-
const hasAllRequiredMembers = requiredFinalCouncilMembers.every((member) => distinctMembersVoted.has(member)) && distinctMembersVoted.size === requiredFinalCouncilMembers.length && membersAbsent.length === 0;
|
|
112987
|
-
if (!hasAllRequiredMembers) {
|
|
112988
|
-
return JSON.stringify({
|
|
112989
|
-
success: false,
|
|
112990
|
-
phase,
|
|
112991
|
-
status: "blocked",
|
|
112992
|
-
reason: "FINAL_COUNCIL_MISSING_QUORUM",
|
|
112993
|
-
message: `Phase ${phase} (last phase) cannot be completed: final council evidence does not prove all five required members voted. Re-run the project-scoped five-member final council and call write_final_council_evidence to generate complete evidence.`,
|
|
112994
|
-
agentsDispatched,
|
|
112995
|
-
agentsMissing: [],
|
|
112996
|
-
warnings: []
|
|
112997
|
-
}, null, 2);
|
|
112998
|
-
}
|
|
112999
|
-
if (entry.verdict === "rejected" || entry.verdict === "REJECTED") {
|
|
113000
|
-
return JSON.stringify({
|
|
113001
|
-
success: false,
|
|
113002
|
-
phase,
|
|
113003
|
-
status: "blocked",
|
|
113004
|
-
reason: "FINAL_COUNCIL_REJECTED",
|
|
113005
|
-
message: `Phase ${phase} (last phase) cannot be completed: final council returned verdict 'REJECTED'. Address the required fixes before completing the project.`,
|
|
113006
|
-
agentsDispatched,
|
|
113007
|
-
agentsMissing: [],
|
|
113008
|
-
warnings: []
|
|
113009
|
-
}, null, 2);
|
|
113010
|
-
}
|
|
113011
|
-
if (entry.verdict !== "approved" && entry.verdict !== "APPROVED") {
|
|
113012
|
-
return JSON.stringify({
|
|
113013
|
-
success: false,
|
|
113014
|
-
phase,
|
|
113015
|
-
status: "blocked",
|
|
113016
|
-
reason: "FINAL_COUNCIL_INVALID_VERDICT",
|
|
113017
|
-
message: `Phase ${phase} (last phase) cannot be completed: final council evidence contains unrecognized verdict '${entry.verdict}'. Expected 'approved'.`,
|
|
113018
|
-
agentsDispatched,
|
|
113019
|
-
agentsMissing: [],
|
|
113020
|
-
warnings: []
|
|
113021
|
-
}, null, 2);
|
|
113022
|
-
}
|
|
113023
|
-
}
|
|
113024
|
-
}
|
|
113025
|
-
} catch (readErr) {
|
|
113026
|
-
if (readErr.code !== "ENOENT") {
|
|
113027
|
-
safeWarn(`[phase_complete] Final council evidence unreadable:`, readErr);
|
|
113028
|
-
}
|
|
113029
|
-
fcVerdictFound = false;
|
|
113030
|
-
}
|
|
113031
|
-
if (!fcVerdictFound) {
|
|
113032
|
-
return JSON.stringify({
|
|
113033
|
-
success: false,
|
|
113034
|
-
phase,
|
|
113035
|
-
status: "blocked",
|
|
113036
|
-
reason: "FINAL_COUNCIL_REQUIRED",
|
|
113037
|
-
final_council_required: true,
|
|
113038
|
-
message: `Phase ${phase} (last phase) cannot be completed: final_council is enabled and final council evidence not found at .swarm/evidence/final-council.json. Dispatch critic, reviewer, sme, test_engineer, and explorer with project-scoped context, collect their CouncilMemberVerdict JSON, and call write_final_council_evidence before completing the project. Do not use convene_general_council for this gate.`,
|
|
113039
|
-
agentsDispatched,
|
|
113040
|
-
agentsMissing: [],
|
|
113041
|
-
warnings: [
|
|
113042
|
-
`Final council required - dispatch the five project-scoped council members, then call write_final_council_evidence to persist quorumed evidence.`
|
|
113043
|
-
]
|
|
113044
|
-
}, null, 2);
|
|
113045
|
-
}
|
|
113046
|
-
}
|
|
113047
|
-
}
|
|
113048
|
-
}
|
|
113049
|
-
}
|
|
113050
|
-
} catch (fcError) {
|
|
113051
|
-
if (finalCouncilEnabled) {
|
|
113052
|
-
warnings.push(`FINAL_COUNCIL_ERROR: ${String(fcError)}`);
|
|
113053
|
-
return JSON.stringify({
|
|
113054
|
-
success: false,
|
|
113055
|
-
phase,
|
|
113056
|
-
status: "blocked",
|
|
113057
|
-
reason: "FINAL_COUNCIL_ERROR",
|
|
113058
|
-
message: `Phase ${phase} (last phase) cannot be completed: final council gate encountered an error. Error: ${String(fcError)}`,
|
|
113059
|
-
agentsDispatched,
|
|
113060
|
-
agentsMissing: [],
|
|
113061
|
-
warnings: [`FINAL_COUNCIL_ERROR: ${String(fcError)}`]
|
|
113062
|
-
}, null, 2);
|
|
113063
|
-
} else {
|
|
113064
|
-
safeWarn(`[phase_complete] Final council gate error (non-blocking):`, fcError);
|
|
113065
|
-
}
|
|
113196
|
+
const gateResult = await runFinalCouncilGate(gateCtx);
|
|
113197
|
+
if (gateResult.blocked) {
|
|
113198
|
+
return blockedResult(phase, gateResult);
|
|
113066
113199
|
}
|
|
113200
|
+
warnings.push(...gateResult.warnings);
|
|
113067
113201
|
}
|
|
113068
113202
|
{
|
|
113069
113203
|
const approval = verifyFullAutoPhaseApproval(dir, sessionID, phase, config3);
|
|
@@ -113112,7 +113246,7 @@ Findings: ${details.join("; ")}` : "";
|
|
|
113112
113246
|
}
|
|
113113
113247
|
if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
|
|
113114
113248
|
try {
|
|
113115
|
-
const projectName =
|
|
113249
|
+
const projectName = path132.basename(dir);
|
|
113116
113250
|
const curationResult = await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
|
|
113117
113251
|
if (curationResult) {
|
|
113118
113252
|
const sessionState = swarmState.agentSessions.get(sessionID);
|
|
@@ -113212,14 +113346,14 @@ Findings: ${details.join("; ")}` : "";
|
|
|
113212
113346
|
const markerPath = validateSwarmPath(dir, "skill-usage-last-processed.json");
|
|
113213
113347
|
let sinceTimestamp;
|
|
113214
113348
|
try {
|
|
113215
|
-
const markerData = JSON.parse(
|
|
113349
|
+
const markerData = JSON.parse(fs95.readFileSync(markerPath, "utf-8"));
|
|
113216
113350
|
sinceTimestamp = markerData.lastProcessedTimestamp;
|
|
113217
113351
|
} catch {}
|
|
113218
113352
|
const feedbackResult = await applySkillUsageFeedback(dir, {
|
|
113219
113353
|
sinceTimestamp
|
|
113220
113354
|
});
|
|
113221
113355
|
try {
|
|
113222
|
-
|
|
113356
|
+
fs95.writeFileSync(markerPath, JSON.stringify({ lastProcessedTimestamp: new Date().toISOString() }), "utf-8");
|
|
113223
113357
|
} catch {}
|
|
113224
113358
|
if (feedbackResult.processed > 0) {
|
|
113225
113359
|
const sessionState = swarmState.agentSessions.get(sessionID);
|
|
@@ -113239,7 +113373,7 @@ Findings: ${details.join("; ")}` : "";
|
|
|
113239
113373
|
let phaseRequiredAgents;
|
|
113240
113374
|
try {
|
|
113241
113375
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
113242
|
-
const planRaw =
|
|
113376
|
+
const planRaw = fs95.readFileSync(planPath, "utf-8");
|
|
113243
113377
|
const plan = JSON.parse(planRaw);
|
|
113244
113378
|
const phaseObj = plan.phases.find((p) => p.id === phase);
|
|
113245
113379
|
phaseRequiredAgents = phaseObj?.required_agents;
|
|
@@ -113254,7 +113388,7 @@ Findings: ${details.join("; ")}` : "";
|
|
|
113254
113388
|
if (agentsMissing.length > 0) {
|
|
113255
113389
|
try {
|
|
113256
113390
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
113257
|
-
const planRaw =
|
|
113391
|
+
const planRaw = fs95.readFileSync(planPath, "utf-8");
|
|
113258
113392
|
const plan = JSON.parse(planRaw);
|
|
113259
113393
|
const targetPhase = plan.phases.find((p) => p.id === phase);
|
|
113260
113394
|
if (targetPhase && targetPhase.tasks.length > 0 && canInferMissingAgentsFromTaskGates(agentsMissing) && await allCompletedTasksHavePassedGateEvidence(dir, targetPhase.tasks)) {
|
|
@@ -113294,7 +113428,7 @@ Findings: ${details.join("; ")}` : "";
|
|
|
113294
113428
|
if (phaseCompleteConfig.regression_sweep?.enforce) {
|
|
113295
113429
|
try {
|
|
113296
113430
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
113297
|
-
const planRaw =
|
|
113431
|
+
const planRaw = fs95.readFileSync(planPath, "utf-8");
|
|
113298
113432
|
const plan = JSON.parse(planRaw);
|
|
113299
113433
|
const targetPhase = plan.phases.find((p) => p.id === phase);
|
|
113300
113434
|
if (targetPhase) {
|
|
@@ -113348,7 +113482,7 @@ Findings: ${details.join("; ")}` : "";
|
|
|
113348
113482
|
}
|
|
113349
113483
|
try {
|
|
113350
113484
|
const eventsPath = validateSwarmPath(dir, "events.jsonl");
|
|
113351
|
-
|
|
113485
|
+
fs95.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
113352
113486
|
`, "utf-8");
|
|
113353
113487
|
} catch (writeError) {
|
|
113354
113488
|
warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
@@ -113423,12 +113557,12 @@ Findings: ${details.join("; ")}` : "";
|
|
|
113423
113557
|
warnings.push(`Warning: failed to update plan.json phase status`);
|
|
113424
113558
|
try {
|
|
113425
113559
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
113426
|
-
const planRaw =
|
|
113560
|
+
const planRaw = fs95.readFileSync(planPath, "utf-8");
|
|
113427
113561
|
const plan2 = JSON.parse(planRaw);
|
|
113428
113562
|
const phaseObj = plan2.phases.find((p) => p.id === phase);
|
|
113429
113563
|
if (phaseObj) {
|
|
113430
113564
|
phaseObj.status = "complete";
|
|
113431
|
-
|
|
113565
|
+
fs95.writeFileSync(planPath, JSON.stringify(plan2, null, 2), "utf-8");
|
|
113432
113566
|
}
|
|
113433
113567
|
} catch {}
|
|
113434
113568
|
} else if (plan) {
|
|
@@ -113465,12 +113599,12 @@ Findings: ${details.join("; ")}` : "";
|
|
|
113465
113599
|
warnings.push(`Warning: failed to update plan.json phase status`);
|
|
113466
113600
|
try {
|
|
113467
113601
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
113468
|
-
const planRaw =
|
|
113602
|
+
const planRaw = fs95.readFileSync(planPath, "utf-8");
|
|
113469
113603
|
const plan = JSON.parse(planRaw);
|
|
113470
113604
|
const phaseObj = plan.phases.find((p) => p.id === phase);
|
|
113471
113605
|
if (phaseObj) {
|
|
113472
113606
|
phaseObj.status = "complete";
|
|
113473
|
-
|
|
113607
|
+
fs95.writeFileSync(planPath, JSON.stringify(plan, null, 2), "utf-8");
|
|
113474
113608
|
}
|
|
113475
113609
|
} catch {}
|
|
113476
113610
|
}
|
|
@@ -113480,7 +113614,32 @@ Findings: ${details.join("; ")}` : "";
|
|
|
113480
113614
|
}
|
|
113481
113615
|
await flushPendingSnapshot(dir);
|
|
113482
113616
|
await writeCheckpoint(dir).catch(() => {});
|
|
113483
|
-
|
|
113617
|
+
const outputData = {
|
|
113618
|
+
...result,
|
|
113619
|
+
timestamp: event.timestamp,
|
|
113620
|
+
duration_ms: durationMs
|
|
113621
|
+
};
|
|
113622
|
+
return _buildOutputJson(outputData);
|
|
113623
|
+
}
|
|
113624
|
+
function _buildOutputJson(outputData) {
|
|
113625
|
+
let json3 = JSON.stringify(outputData, null, 2);
|
|
113626
|
+
if (json3.length > MAX_OUTPUT_BYTES5) {
|
|
113627
|
+
const truncated = {
|
|
113628
|
+
_truncated: true,
|
|
113629
|
+
_truncation_reason: `Output exceeded MAX_OUTPUT_BYTES (${MAX_OUTPUT_BYTES5}) limit`,
|
|
113630
|
+
phase: outputData.phase,
|
|
113631
|
+
success: outputData.success,
|
|
113632
|
+
status: outputData.status,
|
|
113633
|
+
message: outputData.message,
|
|
113634
|
+
agentsDispatched: outputData.agentsDispatched?.slice(0, 10),
|
|
113635
|
+
agentsMissing: outputData.agentsMissing?.slice(0, 10),
|
|
113636
|
+
warnings: ["(output truncated — full output exceeded size limit)"],
|
|
113637
|
+
timestamp: outputData.timestamp,
|
|
113638
|
+
duration_ms: outputData.duration_ms
|
|
113639
|
+
};
|
|
113640
|
+
json3 = JSON.stringify(truncated, null, 2);
|
|
113641
|
+
}
|
|
113642
|
+
return json3;
|
|
113484
113643
|
}
|
|
113485
113644
|
var phase_complete = createSwarmTool({
|
|
113486
113645
|
description: "Mark a phase as complete and track which agents were dispatched. Used for phase completion gating and tracking. Accepts phase number and optional summary. Returns list of agents that were dispatched.",
|
|
@@ -113528,9 +113687,9 @@ init_discovery();
|
|
|
113528
113687
|
init_utils();
|
|
113529
113688
|
init_bun_compat();
|
|
113530
113689
|
init_create_tool();
|
|
113531
|
-
import * as
|
|
113532
|
-
import * as
|
|
113533
|
-
var
|
|
113690
|
+
import * as fs96 from "node:fs";
|
|
113691
|
+
import * as path133 from "node:path";
|
|
113692
|
+
var MAX_OUTPUT_BYTES6 = 52428800;
|
|
113534
113693
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
113535
113694
|
function isValidEcosystem(value) {
|
|
113536
113695
|
return typeof value === "string" && [
|
|
@@ -113557,31 +113716,31 @@ function validateArgs3(args2) {
|
|
|
113557
113716
|
function detectEcosystems(directory) {
|
|
113558
113717
|
const ecosystems = [];
|
|
113559
113718
|
const cwd = directory;
|
|
113560
|
-
if (
|
|
113719
|
+
if (fs96.existsSync(path133.join(cwd, "package.json"))) {
|
|
113561
113720
|
ecosystems.push("npm");
|
|
113562
113721
|
}
|
|
113563
|
-
if (
|
|
113722
|
+
if (fs96.existsSync(path133.join(cwd, "pyproject.toml")) || fs96.existsSync(path133.join(cwd, "requirements.txt"))) {
|
|
113564
113723
|
ecosystems.push("pip");
|
|
113565
113724
|
}
|
|
113566
|
-
if (
|
|
113725
|
+
if (fs96.existsSync(path133.join(cwd, "Cargo.toml"))) {
|
|
113567
113726
|
ecosystems.push("cargo");
|
|
113568
113727
|
}
|
|
113569
|
-
if (
|
|
113728
|
+
if (fs96.existsSync(path133.join(cwd, "go.mod"))) {
|
|
113570
113729
|
ecosystems.push("go");
|
|
113571
113730
|
}
|
|
113572
113731
|
try {
|
|
113573
|
-
const files =
|
|
113732
|
+
const files = fs96.readdirSync(cwd);
|
|
113574
113733
|
if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
|
|
113575
113734
|
ecosystems.push("dotnet");
|
|
113576
113735
|
}
|
|
113577
113736
|
} catch {}
|
|
113578
|
-
if (
|
|
113737
|
+
if (fs96.existsSync(path133.join(cwd, "Gemfile")) || fs96.existsSync(path133.join(cwd, "Gemfile.lock"))) {
|
|
113579
113738
|
ecosystems.push("ruby");
|
|
113580
113739
|
}
|
|
113581
|
-
if (
|
|
113740
|
+
if (fs96.existsSync(path133.join(cwd, "pubspec.yaml"))) {
|
|
113582
113741
|
ecosystems.push("dart");
|
|
113583
113742
|
}
|
|
113584
|
-
if (
|
|
113743
|
+
if (fs96.existsSync(path133.join(cwd, "composer.lock"))) {
|
|
113585
113744
|
ecosystems.push("composer");
|
|
113586
113745
|
}
|
|
113587
113746
|
return ecosystems;
|
|
@@ -113613,8 +113772,8 @@ async function runNpmAudit(directory) {
|
|
|
113613
113772
|
};
|
|
113614
113773
|
}
|
|
113615
113774
|
let { stdout, stderr } = result;
|
|
113616
|
-
if (stdout.length >
|
|
113617
|
-
stdout = stdout.slice(0,
|
|
113775
|
+
if (stdout.length > MAX_OUTPUT_BYTES6) {
|
|
113776
|
+
stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
|
|
113618
113777
|
}
|
|
113619
113778
|
const exitCode = await proc.exited;
|
|
113620
113779
|
if (exitCode === 0) {
|
|
@@ -113733,8 +113892,8 @@ async function runPipAudit(directory) {
|
|
|
113733
113892
|
};
|
|
113734
113893
|
}
|
|
113735
113894
|
let { stdout, stderr } = result;
|
|
113736
|
-
if (stdout.length >
|
|
113737
|
-
stdout = stdout.slice(0,
|
|
113895
|
+
if (stdout.length > MAX_OUTPUT_BYTES6) {
|
|
113896
|
+
stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
|
|
113738
113897
|
}
|
|
113739
113898
|
const exitCode = await proc.exited;
|
|
113740
113899
|
if (exitCode === 0 && !stdout.trim()) {
|
|
@@ -113861,8 +114020,8 @@ async function runCargoAudit(directory) {
|
|
|
113861
114020
|
};
|
|
113862
114021
|
}
|
|
113863
114022
|
let { stdout, stderr: _stderr } = result;
|
|
113864
|
-
if (stdout.length >
|
|
113865
|
-
stdout = stdout.slice(0,
|
|
114023
|
+
if (stdout.length > MAX_OUTPUT_BYTES6) {
|
|
114024
|
+
stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
|
|
113866
114025
|
}
|
|
113867
114026
|
const exitCode = await proc.exited;
|
|
113868
114027
|
if (exitCode === 0) {
|
|
@@ -113985,8 +114144,8 @@ async function runGoAudit(directory) {
|
|
|
113985
114144
|
};
|
|
113986
114145
|
}
|
|
113987
114146
|
let { stdout } = result;
|
|
113988
|
-
if (stdout.length >
|
|
113989
|
-
stdout = stdout.slice(0,
|
|
114147
|
+
if (stdout.length > MAX_OUTPUT_BYTES6) {
|
|
114148
|
+
stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
|
|
113990
114149
|
}
|
|
113991
114150
|
const exitCode = await proc.exited;
|
|
113992
114151
|
if (exitCode !== 0 && exitCode !== 3) {
|
|
@@ -114118,8 +114277,8 @@ async function runDotnetAudit(directory) {
|
|
|
114118
114277
|
};
|
|
114119
114278
|
}
|
|
114120
114279
|
let { stdout } = result;
|
|
114121
|
-
if (stdout.length >
|
|
114122
|
-
stdout = stdout.slice(0,
|
|
114280
|
+
if (stdout.length > MAX_OUTPUT_BYTES6) {
|
|
114281
|
+
stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
|
|
114123
114282
|
}
|
|
114124
114283
|
const exitCode = await proc.exited;
|
|
114125
114284
|
if (exitCode !== 0 && !stdout.includes("has the following vulnerable packages")) {
|
|
@@ -114234,8 +114393,8 @@ async function runBundleAudit(directory) {
|
|
|
114234
114393
|
};
|
|
114235
114394
|
}
|
|
114236
114395
|
let { stdout } = result;
|
|
114237
|
-
if (stdout.length >
|
|
114238
|
-
stdout = stdout.slice(0,
|
|
114396
|
+
if (stdout.length > MAX_OUTPUT_BYTES6) {
|
|
114397
|
+
stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
|
|
114239
114398
|
}
|
|
114240
114399
|
const exitCode = await proc.exited;
|
|
114241
114400
|
if (exitCode !== 0 && exitCode !== 1) {
|
|
@@ -114379,8 +114538,8 @@ async function runDartAudit(directory) {
|
|
|
114379
114538
|
};
|
|
114380
114539
|
}
|
|
114381
114540
|
let { stdout } = result;
|
|
114382
|
-
if (stdout.length >
|
|
114383
|
-
stdout = stdout.slice(0,
|
|
114541
|
+
if (stdout.length > MAX_OUTPUT_BYTES6) {
|
|
114542
|
+
stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
|
|
114384
114543
|
}
|
|
114385
114544
|
const exitCode = await proc.exited;
|
|
114386
114545
|
if (exitCode !== 0) {
|
|
@@ -114494,8 +114653,8 @@ async function runComposerAudit(directory) {
|
|
|
114494
114653
|
};
|
|
114495
114654
|
}
|
|
114496
114655
|
let { stdout } = result;
|
|
114497
|
-
if (stdout.length >
|
|
114498
|
-
stdout = stdout.slice(0,
|
|
114656
|
+
if (stdout.length > MAX_OUTPUT_BYTES6) {
|
|
114657
|
+
stdout = stdout.slice(0, MAX_OUTPUT_BYTES6);
|
|
114499
114658
|
}
|
|
114500
114659
|
const exitCode = await proc.exited;
|
|
114501
114660
|
const hasVulnerabilities = (exitCode & 1) !== 0;
|
|
@@ -114716,8 +114875,8 @@ var pkg_audit = createSwarmTool({
|
|
|
114716
114875
|
// src/tools/placeholder-scan.ts
|
|
114717
114876
|
init_zod();
|
|
114718
114877
|
init_manager2();
|
|
114719
|
-
import * as
|
|
114720
|
-
import * as
|
|
114878
|
+
import * as fs97 from "node:fs";
|
|
114879
|
+
import * as path134 from "node:path";
|
|
114721
114880
|
init_utils();
|
|
114722
114881
|
init_create_tool();
|
|
114723
114882
|
var MAX_FILE_SIZE = 1024 * 1024;
|
|
@@ -114840,7 +114999,7 @@ function isScaffoldFile(filePath) {
|
|
|
114840
114999
|
if (SCAFFOLD_PATH_PATTERNS.some((pattern) => pattern.test(normalizedPath))) {
|
|
114841
115000
|
return true;
|
|
114842
115001
|
}
|
|
114843
|
-
const filename =
|
|
115002
|
+
const filename = path134.basename(filePath);
|
|
114844
115003
|
if (SCAFFOLD_FILENAME_PATTERNS.some((pattern) => pattern.test(filename))) {
|
|
114845
115004
|
return true;
|
|
114846
115005
|
}
|
|
@@ -114857,7 +115016,7 @@ function isAllowedByGlobs(filePath, allowGlobs) {
|
|
|
114857
115016
|
if (regex.test(normalizedPath)) {
|
|
114858
115017
|
return true;
|
|
114859
115018
|
}
|
|
114860
|
-
const filename =
|
|
115019
|
+
const filename = path134.basename(filePath);
|
|
114861
115020
|
const filenameRegex = new RegExp(`^${regexPattern}$`, "i");
|
|
114862
115021
|
if (filenameRegex.test(filename)) {
|
|
114863
115022
|
return true;
|
|
@@ -114866,7 +115025,7 @@ function isAllowedByGlobs(filePath, allowGlobs) {
|
|
|
114866
115025
|
return false;
|
|
114867
115026
|
}
|
|
114868
115027
|
function isParserSupported(filePath) {
|
|
114869
|
-
const ext =
|
|
115028
|
+
const ext = path134.extname(filePath).toLowerCase();
|
|
114870
115029
|
return SUPPORTED_PARSER_EXTENSIONS.has(ext);
|
|
114871
115030
|
}
|
|
114872
115031
|
function isPlanFile(filePath) {
|
|
@@ -115113,28 +115272,28 @@ async function placeholderScan(input, directory) {
|
|
|
115113
115272
|
let filesScanned = 0;
|
|
115114
115273
|
const filesWithFindings = new Set;
|
|
115115
115274
|
for (const filePath of changed_files) {
|
|
115116
|
-
const fullPath =
|
|
115117
|
-
const resolvedDirectory =
|
|
115118
|
-
if (!fullPath.startsWith(resolvedDirectory +
|
|
115275
|
+
const fullPath = path134.isAbsolute(filePath) ? filePath : path134.resolve(directory, filePath);
|
|
115276
|
+
const resolvedDirectory = path134.resolve(directory);
|
|
115277
|
+
if (!fullPath.startsWith(resolvedDirectory + path134.sep) && fullPath !== resolvedDirectory) {
|
|
115119
115278
|
continue;
|
|
115120
115279
|
}
|
|
115121
|
-
if (!
|
|
115280
|
+
if (!fs97.existsSync(fullPath)) {
|
|
115122
115281
|
continue;
|
|
115123
115282
|
}
|
|
115124
115283
|
if (isAllowedByGlobs(filePath, allow_globs)) {
|
|
115125
115284
|
continue;
|
|
115126
115285
|
}
|
|
115127
|
-
const relativeFilePath =
|
|
115286
|
+
const relativeFilePath = path134.relative(directory, fullPath).replace(/\\/g, "/");
|
|
115128
115287
|
if (FILE_ALLOWLIST.some((allowed) => relativeFilePath.endsWith(allowed))) {
|
|
115129
115288
|
continue;
|
|
115130
115289
|
}
|
|
115131
115290
|
let content;
|
|
115132
115291
|
try {
|
|
115133
|
-
const stat9 =
|
|
115292
|
+
const stat9 = fs97.statSync(fullPath);
|
|
115134
115293
|
if (stat9.size > MAX_FILE_SIZE) {
|
|
115135
115294
|
continue;
|
|
115136
115295
|
}
|
|
115137
|
-
content =
|
|
115296
|
+
content = fs97.readFileSync(fullPath, "utf-8");
|
|
115138
115297
|
} catch {
|
|
115139
115298
|
continue;
|
|
115140
115299
|
}
|
|
@@ -115195,8 +115354,8 @@ var placeholder_scan = createSwarmTool({
|
|
|
115195
115354
|
}
|
|
115196
115355
|
});
|
|
115197
115356
|
// src/tools/pre-check-batch.ts
|
|
115198
|
-
import * as
|
|
115199
|
-
import * as
|
|
115357
|
+
import * as fs101 from "node:fs";
|
|
115358
|
+
import * as path138 from "node:path";
|
|
115200
115359
|
init_zod();
|
|
115201
115360
|
init_manager2();
|
|
115202
115361
|
init_utils();
|
|
@@ -115336,8 +115495,8 @@ var _internals56 = {
|
|
|
115336
115495
|
init_zod();
|
|
115337
115496
|
init_manager2();
|
|
115338
115497
|
init_detector();
|
|
115339
|
-
import * as
|
|
115340
|
-
import * as
|
|
115498
|
+
import * as fs100 from "node:fs";
|
|
115499
|
+
import * as path137 from "node:path";
|
|
115341
115500
|
import { extname as extname20 } from "node:path";
|
|
115342
115501
|
|
|
115343
115502
|
// src/sast/rules/c.ts
|
|
@@ -116052,8 +116211,8 @@ function executeRulesSync(filePath, content, language) {
|
|
|
116052
116211
|
|
|
116053
116212
|
// src/sast/semgrep.ts
|
|
116054
116213
|
import * as child_process9 from "node:child_process";
|
|
116055
|
-
import * as
|
|
116056
|
-
import * as
|
|
116214
|
+
import * as fs98 from "node:fs";
|
|
116215
|
+
import * as path135 from "node:path";
|
|
116057
116216
|
var semgrepAvailableCache = null;
|
|
116058
116217
|
var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
|
|
116059
116218
|
var DEFAULT_TIMEOUT_MS3 = 30000;
|
|
@@ -116240,14 +116399,14 @@ async function runSemgrep(options) {
|
|
|
116240
116399
|
}
|
|
116241
116400
|
function getRulesDirectory(projectRoot) {
|
|
116242
116401
|
if (projectRoot) {
|
|
116243
|
-
return
|
|
116402
|
+
return path135.resolve(projectRoot, DEFAULT_RULES_DIR);
|
|
116244
116403
|
}
|
|
116245
116404
|
return DEFAULT_RULES_DIR;
|
|
116246
116405
|
}
|
|
116247
116406
|
function hasBundledRules(projectRoot) {
|
|
116248
116407
|
const rulesDir = getRulesDirectory(projectRoot);
|
|
116249
116408
|
try {
|
|
116250
|
-
return
|
|
116409
|
+
return fs98.existsSync(rulesDir);
|
|
116251
116410
|
} catch {
|
|
116252
116411
|
return false;
|
|
116253
116412
|
}
|
|
@@ -116260,25 +116419,25 @@ init_create_tool();
|
|
|
116260
116419
|
// src/tools/sast-baseline.ts
|
|
116261
116420
|
init_utils2();
|
|
116262
116421
|
import * as crypto11 from "node:crypto";
|
|
116263
|
-
import * as
|
|
116264
|
-
import * as
|
|
116422
|
+
import * as fs99 from "node:fs";
|
|
116423
|
+
import * as path136 from "node:path";
|
|
116265
116424
|
var BASELINE_SCHEMA_VERSION = "1.0.0";
|
|
116266
116425
|
var MAX_BASELINE_FINDINGS = 2000;
|
|
116267
116426
|
var MAX_BASELINE_BYTES = 2 * 1048576;
|
|
116268
116427
|
var LOCK_RETRY_DELAYS_MS = [50, 100, 200, 400, 800];
|
|
116269
116428
|
function normalizeFindingPath(directory, file3) {
|
|
116270
|
-
const resolved =
|
|
116271
|
-
const rel =
|
|
116429
|
+
const resolved = path136.isAbsolute(file3) ? file3 : path136.resolve(directory, file3);
|
|
116430
|
+
const rel = path136.relative(path136.resolve(directory), resolved);
|
|
116272
116431
|
return rel.replace(/\\/g, "/");
|
|
116273
116432
|
}
|
|
116274
116433
|
function baselineRelPath(phase) {
|
|
116275
|
-
return
|
|
116434
|
+
return path136.join("evidence", String(phase), "sast-baseline.json");
|
|
116276
116435
|
}
|
|
116277
116436
|
function tempRelPath(phase) {
|
|
116278
|
-
return
|
|
116437
|
+
return path136.join("evidence", String(phase), `sast-baseline.json.tmp.${Date.now()}.${process.pid}`);
|
|
116279
116438
|
}
|
|
116280
116439
|
function lockRelPath(phase) {
|
|
116281
|
-
return
|
|
116440
|
+
return path136.join("evidence", String(phase), "sast-baseline.json.lock");
|
|
116282
116441
|
}
|
|
116283
116442
|
function getLine(lines, idx) {
|
|
116284
116443
|
if (idx < 0 || idx >= lines.length)
|
|
@@ -116295,7 +116454,7 @@ function fingerprintFinding(finding, directory, occurrenceIndex) {
|
|
|
116295
116454
|
}
|
|
116296
116455
|
const lineNum = finding.location.line;
|
|
116297
116456
|
try {
|
|
116298
|
-
const content =
|
|
116457
|
+
const content = fs99.readFileSync(finding.location.file, "utf-8");
|
|
116299
116458
|
const lines = content.split(`
|
|
116300
116459
|
`);
|
|
116301
116460
|
const idx = lineNum - 1;
|
|
@@ -116326,7 +116485,7 @@ function assignOccurrenceIndices(findings, directory) {
|
|
|
116326
116485
|
try {
|
|
116327
116486
|
if (relFile.startsWith(".."))
|
|
116328
116487
|
throw new Error("escapes workspace");
|
|
116329
|
-
const content =
|
|
116488
|
+
const content = fs99.readFileSync(finding.location.file, "utf-8");
|
|
116330
116489
|
const lines = content.split(`
|
|
116331
116490
|
`);
|
|
116332
116491
|
const idx = lineNum - 1;
|
|
@@ -116355,11 +116514,11 @@ function assignOccurrenceIndices(findings, directory) {
|
|
|
116355
116514
|
async function acquireLock2(lockPath) {
|
|
116356
116515
|
for (let attempt = 0;attempt <= LOCK_RETRY_DELAYS_MS.length; attempt++) {
|
|
116357
116516
|
try {
|
|
116358
|
-
const fd =
|
|
116359
|
-
|
|
116517
|
+
const fd = fs99.openSync(lockPath, "wx");
|
|
116518
|
+
fs99.closeSync(fd);
|
|
116360
116519
|
return () => {
|
|
116361
116520
|
try {
|
|
116362
|
-
|
|
116521
|
+
fs99.unlinkSync(lockPath);
|
|
116363
116522
|
} catch {}
|
|
116364
116523
|
};
|
|
116365
116524
|
} catch {
|
|
@@ -116399,13 +116558,13 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
|
|
|
116399
116558
|
message: e instanceof Error ? e.message : "Path validation failed"
|
|
116400
116559
|
};
|
|
116401
116560
|
}
|
|
116402
|
-
|
|
116403
|
-
|
|
116561
|
+
fs99.mkdirSync(path136.dirname(baselinePath), { recursive: true });
|
|
116562
|
+
fs99.mkdirSync(path136.dirname(tempPath), { recursive: true });
|
|
116404
116563
|
const releaseLock = await acquireLock2(lockPath);
|
|
116405
116564
|
try {
|
|
116406
116565
|
let existing = null;
|
|
116407
116566
|
try {
|
|
116408
|
-
const raw =
|
|
116567
|
+
const raw = fs99.readFileSync(baselinePath, "utf-8");
|
|
116409
116568
|
const parsed = JSON.parse(raw);
|
|
116410
116569
|
if (parsed.schema_version === BASELINE_SCHEMA_VERSION) {
|
|
116411
116570
|
existing = parsed;
|
|
@@ -116465,8 +116624,8 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
|
|
|
116465
116624
|
message: `Baseline would exceed size cap (${json4.length} bytes > ${MAX_BASELINE_BYTES})`
|
|
116466
116625
|
};
|
|
116467
116626
|
}
|
|
116468
|
-
|
|
116469
|
-
|
|
116627
|
+
fs99.writeFileSync(tempPath, json4, "utf-8");
|
|
116628
|
+
fs99.renameSync(tempPath, baselinePath);
|
|
116470
116629
|
return {
|
|
116471
116630
|
status: "merged",
|
|
116472
116631
|
path: baselinePath,
|
|
@@ -116497,8 +116656,8 @@ async function captureOrMergeBaseline(directory, phase, findings, engine, scanne
|
|
|
116497
116656
|
message: `Baseline would exceed size cap (${json3.length} bytes > ${MAX_BASELINE_BYTES})`
|
|
116498
116657
|
};
|
|
116499
116658
|
}
|
|
116500
|
-
|
|
116501
|
-
|
|
116659
|
+
fs99.writeFileSync(tempPath, json3, "utf-8");
|
|
116660
|
+
fs99.renameSync(tempPath, baselinePath);
|
|
116502
116661
|
return {
|
|
116503
116662
|
status: "written",
|
|
116504
116663
|
path: baselinePath,
|
|
@@ -116523,7 +116682,7 @@ function loadBaseline(directory, phase) {
|
|
|
116523
116682
|
};
|
|
116524
116683
|
}
|
|
116525
116684
|
try {
|
|
116526
|
-
const raw =
|
|
116685
|
+
const raw = fs99.readFileSync(baselinePath, "utf-8");
|
|
116527
116686
|
const parsed = JSON.parse(raw);
|
|
116528
116687
|
if (parsed.schema_version !== BASELINE_SCHEMA_VERSION) {
|
|
116529
116688
|
return {
|
|
@@ -116571,17 +116730,17 @@ var SEVERITY_ORDER = {
|
|
|
116571
116730
|
};
|
|
116572
116731
|
function shouldSkipFile(filePath) {
|
|
116573
116732
|
try {
|
|
116574
|
-
const stats =
|
|
116733
|
+
const stats = fs100.statSync(filePath);
|
|
116575
116734
|
if (stats.size > MAX_FILE_SIZE_BYTES8) {
|
|
116576
116735
|
return { skip: true, reason: "file too large" };
|
|
116577
116736
|
}
|
|
116578
116737
|
if (stats.size === 0) {
|
|
116579
116738
|
return { skip: true, reason: "empty file" };
|
|
116580
116739
|
}
|
|
116581
|
-
const fd =
|
|
116740
|
+
const fd = fs100.openSync(filePath, "r");
|
|
116582
116741
|
const buffer = Buffer.alloc(8192);
|
|
116583
|
-
const bytesRead =
|
|
116584
|
-
|
|
116742
|
+
const bytesRead = fs100.readSync(fd, buffer, 0, 8192, 0);
|
|
116743
|
+
fs100.closeSync(fd);
|
|
116585
116744
|
if (bytesRead > 0) {
|
|
116586
116745
|
let nullCount = 0;
|
|
116587
116746
|
for (let i2 = 0;i2 < bytesRead; i2++) {
|
|
@@ -116620,7 +116779,7 @@ function countBySeverity(findings) {
|
|
|
116620
116779
|
}
|
|
116621
116780
|
function scanFileWithTierA(filePath, language) {
|
|
116622
116781
|
try {
|
|
116623
|
-
const content =
|
|
116782
|
+
const content = fs100.readFileSync(filePath, "utf-8");
|
|
116624
116783
|
const findings = executeRulesSync(filePath, content, language);
|
|
116625
116784
|
return findings.map((f) => ({
|
|
116626
116785
|
rule_id: f.rule_id,
|
|
@@ -116673,13 +116832,13 @@ async function sastScan(input, directory, config3) {
|
|
|
116673
116832
|
_filesSkipped++;
|
|
116674
116833
|
continue;
|
|
116675
116834
|
}
|
|
116676
|
-
const resolvedPath =
|
|
116677
|
-
const resolvedDirectory =
|
|
116678
|
-
if (!resolvedPath.startsWith(resolvedDirectory +
|
|
116835
|
+
const resolvedPath = path137.isAbsolute(filePath) ? filePath : path137.resolve(directory, filePath);
|
|
116836
|
+
const resolvedDirectory = path137.resolve(directory);
|
|
116837
|
+
if (!resolvedPath.startsWith(resolvedDirectory + path137.sep) && resolvedPath !== resolvedDirectory) {
|
|
116679
116838
|
_filesSkipped++;
|
|
116680
116839
|
continue;
|
|
116681
116840
|
}
|
|
116682
|
-
if (!
|
|
116841
|
+
if (!fs100.existsSync(resolvedPath)) {
|
|
116683
116842
|
_filesSkipped++;
|
|
116684
116843
|
continue;
|
|
116685
116844
|
}
|
|
@@ -116990,18 +117149,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
116990
117149
|
let resolved;
|
|
116991
117150
|
const isWinAbs = isWindowsAbsolutePath(inputPath);
|
|
116992
117151
|
if (isWinAbs) {
|
|
116993
|
-
resolved =
|
|
116994
|
-
} else if (
|
|
116995
|
-
resolved =
|
|
117152
|
+
resolved = path138.win32.resolve(inputPath);
|
|
117153
|
+
} else if (path138.isAbsolute(inputPath)) {
|
|
117154
|
+
resolved = path138.resolve(inputPath);
|
|
116996
117155
|
} else {
|
|
116997
|
-
resolved =
|
|
117156
|
+
resolved = path138.resolve(baseDir, inputPath);
|
|
116998
117157
|
}
|
|
116999
|
-
const workspaceResolved =
|
|
117158
|
+
const workspaceResolved = path138.resolve(workspaceDir);
|
|
117000
117159
|
let relative27;
|
|
117001
117160
|
if (isWinAbs) {
|
|
117002
|
-
relative27 =
|
|
117161
|
+
relative27 = path138.win32.relative(workspaceResolved, resolved);
|
|
117003
117162
|
} else {
|
|
117004
|
-
relative27 =
|
|
117163
|
+
relative27 = path138.relative(workspaceResolved, resolved);
|
|
117005
117164
|
}
|
|
117006
117165
|
if (relative27.startsWith("..")) {
|
|
117007
117166
|
return "path traversal detected";
|
|
@@ -117066,7 +117225,7 @@ async function runLintOnFiles(linter, files, workspaceDir) {
|
|
|
117066
117225
|
if (typeof file3 !== "string") {
|
|
117067
117226
|
continue;
|
|
117068
117227
|
}
|
|
117069
|
-
const resolvedPath =
|
|
117228
|
+
const resolvedPath = path138.resolve(file3);
|
|
117070
117229
|
const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
|
|
117071
117230
|
if (validationError) {
|
|
117072
117231
|
continue;
|
|
@@ -117223,7 +117382,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
117223
117382
|
skippedFiles++;
|
|
117224
117383
|
continue;
|
|
117225
117384
|
}
|
|
117226
|
-
const resolvedPath =
|
|
117385
|
+
const resolvedPath = path138.resolve(file3);
|
|
117227
117386
|
const validationError = validatePath(resolvedPath, directory, directory);
|
|
117228
117387
|
if (validationError) {
|
|
117229
117388
|
skippedFiles++;
|
|
@@ -117241,14 +117400,14 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
117241
117400
|
};
|
|
117242
117401
|
}
|
|
117243
117402
|
for (const file3 of validatedFiles) {
|
|
117244
|
-
const ext =
|
|
117403
|
+
const ext = path138.extname(file3).toLowerCase();
|
|
117245
117404
|
if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
|
|
117246
117405
|
skippedFiles++;
|
|
117247
117406
|
continue;
|
|
117248
117407
|
}
|
|
117249
117408
|
let stat9;
|
|
117250
117409
|
try {
|
|
117251
|
-
stat9 =
|
|
117410
|
+
stat9 = fs101.statSync(file3);
|
|
117252
117411
|
} catch {
|
|
117253
117412
|
skippedFiles++;
|
|
117254
117413
|
continue;
|
|
@@ -117259,7 +117418,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
117259
117418
|
}
|
|
117260
117419
|
let content;
|
|
117261
117420
|
try {
|
|
117262
|
-
const buffer =
|
|
117421
|
+
const buffer = fs101.readFileSync(file3);
|
|
117263
117422
|
if (buffer.includes(0)) {
|
|
117264
117423
|
skippedFiles++;
|
|
117265
117424
|
continue;
|
|
@@ -117460,7 +117619,7 @@ function classifySastFindings(findings, changedLineRanges, directory) {
|
|
|
117460
117619
|
const preexistingFindings = [];
|
|
117461
117620
|
for (const finding of findings) {
|
|
117462
117621
|
const filePath = finding.location.file;
|
|
117463
|
-
const normalised =
|
|
117622
|
+
const normalised = path138.relative(directory, filePath).replace(/\\/g, "/");
|
|
117464
117623
|
const changedLines = changedLineRanges.get(normalised);
|
|
117465
117624
|
if (changedLines?.has(finding.location.line)) {
|
|
117466
117625
|
newFindings.push(finding);
|
|
@@ -117511,7 +117670,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
|
117511
117670
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
117512
117671
|
continue;
|
|
117513
117672
|
}
|
|
117514
|
-
changedFiles.push(
|
|
117673
|
+
changedFiles.push(path138.resolve(directory, file3));
|
|
117515
117674
|
}
|
|
117516
117675
|
if (changedFiles.length === 0) {
|
|
117517
117676
|
warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
|
|
@@ -117712,9 +117871,9 @@ var pre_check_batch = createSwarmTool({
|
|
|
117712
117871
|
};
|
|
117713
117872
|
return JSON.stringify(errorResult, null, 2);
|
|
117714
117873
|
}
|
|
117715
|
-
const resolvedDirectory =
|
|
117716
|
-
const workspaceAnchor =
|
|
117717
|
-
if (resolvedDirectory !== workspaceAnchor && resolvedDirectory.startsWith(workspaceAnchor +
|
|
117874
|
+
const resolvedDirectory = path138.resolve(typedArgs.directory);
|
|
117875
|
+
const workspaceAnchor = path138.resolve(directory);
|
|
117876
|
+
if (resolvedDirectory !== workspaceAnchor && resolvedDirectory.startsWith(workspaceAnchor + path138.sep)) {
|
|
117718
117877
|
const subDirError = `directory "${typedArgs.directory}" is a subdirectory of the project root — pre_check_batch requires the project root directory "${workspaceAnchor}"`;
|
|
117719
117878
|
const subDirResult = {
|
|
117720
117879
|
gates_passed: false,
|
|
@@ -117765,7 +117924,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
117765
117924
|
});
|
|
117766
117925
|
// src/tools/repo-map.ts
|
|
117767
117926
|
init_zod();
|
|
117768
|
-
import * as
|
|
117927
|
+
import * as path139 from "node:path";
|
|
117769
117928
|
init_path_security();
|
|
117770
117929
|
init_create_tool();
|
|
117771
117930
|
var VALID_ACTIONS = [
|
|
@@ -117790,7 +117949,7 @@ function validateFile(p) {
|
|
|
117790
117949
|
return "file contains control characters";
|
|
117791
117950
|
if (containsPathTraversal(p))
|
|
117792
117951
|
return "file contains path traversal";
|
|
117793
|
-
if (
|
|
117952
|
+
if (path139.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p)) {
|
|
117794
117953
|
return "file must be a workspace-relative path, not absolute";
|
|
117795
117954
|
}
|
|
117796
117955
|
return null;
|
|
@@ -117813,8 +117972,8 @@ function ok(action, payload) {
|
|
|
117813
117972
|
}
|
|
117814
117973
|
function toRelativeGraphPath(input, workspaceRoot) {
|
|
117815
117974
|
const normalized = input.replace(/\\/g, "/");
|
|
117816
|
-
if (
|
|
117817
|
-
const rel =
|
|
117975
|
+
if (path139.isAbsolute(normalized)) {
|
|
117976
|
+
const rel = path139.relative(workspaceRoot, normalized).replace(/\\/g, "/");
|
|
117818
117977
|
return normalizeGraphPath2(rel);
|
|
117819
117978
|
}
|
|
117820
117979
|
return normalizeGraphPath2(normalized);
|
|
@@ -117958,8 +118117,8 @@ var repo_map = createSwarmTool({
|
|
|
117958
118117
|
// src/tools/req-coverage.ts
|
|
117959
118118
|
init_zod();
|
|
117960
118119
|
init_create_tool();
|
|
117961
|
-
import * as
|
|
117962
|
-
import * as
|
|
118120
|
+
import * as fs102 from "node:fs";
|
|
118121
|
+
import * as path140 from "node:path";
|
|
117963
118122
|
var SPEC_FILE = ".swarm/spec.md";
|
|
117964
118123
|
var EVIDENCE_DIR4 = ".swarm/evidence";
|
|
117965
118124
|
var OBLIGATION_KEYWORDS = ["MUST", "SHOULD", "SHALL"];
|
|
@@ -118018,19 +118177,19 @@ function extractObligationAndText(id, lineText) {
|
|
|
118018
118177
|
var PHASE_TASK_ID_REGEX = /^\d+\.\d+(\.\d+)*$/;
|
|
118019
118178
|
function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
118020
118179
|
const touchedFiles = new Set;
|
|
118021
|
-
if (!
|
|
118180
|
+
if (!fs102.existsSync(evidenceDir) || !fs102.statSync(evidenceDir).isDirectory()) {
|
|
118022
118181
|
return [];
|
|
118023
118182
|
}
|
|
118024
118183
|
let entries;
|
|
118025
118184
|
try {
|
|
118026
|
-
entries =
|
|
118185
|
+
entries = fs102.readdirSync(evidenceDir);
|
|
118027
118186
|
} catch {
|
|
118028
118187
|
return [];
|
|
118029
118188
|
}
|
|
118030
118189
|
for (const entry of entries) {
|
|
118031
|
-
const entryPath =
|
|
118190
|
+
const entryPath = path140.join(evidenceDir, entry);
|
|
118032
118191
|
try {
|
|
118033
|
-
const stat9 =
|
|
118192
|
+
const stat9 = fs102.statSync(entryPath);
|
|
118034
118193
|
if (!stat9.isDirectory()) {
|
|
118035
118194
|
continue;
|
|
118036
118195
|
}
|
|
@@ -118044,14 +118203,14 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
118044
118203
|
if (entryPhase !== String(phase)) {
|
|
118045
118204
|
continue;
|
|
118046
118205
|
}
|
|
118047
|
-
const evidenceFilePath =
|
|
118206
|
+
const evidenceFilePath = path140.join(entryPath, "evidence.json");
|
|
118048
118207
|
try {
|
|
118049
|
-
const resolvedPath =
|
|
118050
|
-
const evidenceDirResolved =
|
|
118051
|
-
if (!resolvedPath.startsWith(evidenceDirResolved +
|
|
118208
|
+
const resolvedPath = path140.resolve(evidenceFilePath);
|
|
118209
|
+
const evidenceDirResolved = path140.resolve(evidenceDir);
|
|
118210
|
+
if (!resolvedPath.startsWith(evidenceDirResolved + path140.sep)) {
|
|
118052
118211
|
continue;
|
|
118053
118212
|
}
|
|
118054
|
-
const stat9 =
|
|
118213
|
+
const stat9 = fs102.lstatSync(evidenceFilePath);
|
|
118055
118214
|
if (!stat9.isFile()) {
|
|
118056
118215
|
continue;
|
|
118057
118216
|
}
|
|
@@ -118063,7 +118222,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
118063
118222
|
}
|
|
118064
118223
|
let content;
|
|
118065
118224
|
try {
|
|
118066
|
-
content =
|
|
118225
|
+
content = fs102.readFileSync(evidenceFilePath, "utf-8");
|
|
118067
118226
|
} catch {
|
|
118068
118227
|
continue;
|
|
118069
118228
|
}
|
|
@@ -118082,7 +118241,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
118082
118241
|
if (Array.isArray(diffEntry.files_changed)) {
|
|
118083
118242
|
for (const file3 of diffEntry.files_changed) {
|
|
118084
118243
|
if (typeof file3 === "string") {
|
|
118085
|
-
touchedFiles.add(
|
|
118244
|
+
touchedFiles.add(path140.resolve(cwd, file3));
|
|
118086
118245
|
}
|
|
118087
118246
|
}
|
|
118088
118247
|
}
|
|
@@ -118095,12 +118254,12 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
118095
118254
|
}
|
|
118096
118255
|
function searchFileForKeywords(filePath, keywords, cwd) {
|
|
118097
118256
|
try {
|
|
118098
|
-
const resolvedPath =
|
|
118099
|
-
const cwdResolved =
|
|
118257
|
+
const resolvedPath = path140.resolve(filePath);
|
|
118258
|
+
const cwdResolved = path140.resolve(cwd);
|
|
118100
118259
|
if (!resolvedPath.startsWith(cwdResolved)) {
|
|
118101
118260
|
return false;
|
|
118102
118261
|
}
|
|
118103
|
-
const content =
|
|
118262
|
+
const content = fs102.readFileSync(resolvedPath, "utf-8");
|
|
118104
118263
|
for (const keyword of keywords) {
|
|
118105
118264
|
const regex = new RegExp(`\\b${keyword}\\b`, "i");
|
|
118106
118265
|
if (regex.test(content)) {
|
|
@@ -118230,10 +118389,10 @@ var req_coverage = createSwarmTool({
|
|
|
118230
118389
|
}, null, 2);
|
|
118231
118390
|
}
|
|
118232
118391
|
const cwd = inputDirectory || directory;
|
|
118233
|
-
const specPath =
|
|
118392
|
+
const specPath = path140.join(cwd, SPEC_FILE);
|
|
118234
118393
|
let specContent;
|
|
118235
118394
|
try {
|
|
118236
|
-
specContent =
|
|
118395
|
+
specContent = fs102.readFileSync(specPath, "utf-8");
|
|
118237
118396
|
} catch (readError) {
|
|
118238
118397
|
return JSON.stringify({
|
|
118239
118398
|
success: false,
|
|
@@ -118257,7 +118416,7 @@ var req_coverage = createSwarmTool({
|
|
|
118257
118416
|
message: "No FR requirements found in spec.md"
|
|
118258
118417
|
}, null, 2);
|
|
118259
118418
|
}
|
|
118260
|
-
const evidenceDir =
|
|
118419
|
+
const evidenceDir = path140.join(cwd, EVIDENCE_DIR4);
|
|
118261
118420
|
const touchedFiles = readTouchedFiles(evidenceDir, phase, cwd);
|
|
118262
118421
|
const analyzedRequirements = [];
|
|
118263
118422
|
let coveredCount = 0;
|
|
@@ -118283,12 +118442,12 @@ var req_coverage = createSwarmTool({
|
|
|
118283
118442
|
requirements: analyzedRequirements
|
|
118284
118443
|
};
|
|
118285
118444
|
const reportFilename = `req-coverage-phase-${phase}.json`;
|
|
118286
|
-
const reportPath =
|
|
118445
|
+
const reportPath = path140.join(evidenceDir, reportFilename);
|
|
118287
118446
|
try {
|
|
118288
|
-
if (!
|
|
118289
|
-
|
|
118447
|
+
if (!fs102.existsSync(evidenceDir)) {
|
|
118448
|
+
fs102.mkdirSync(evidenceDir, { recursive: true });
|
|
118290
118449
|
}
|
|
118291
|
-
|
|
118450
|
+
fs102.writeFileSync(reportPath, JSON.stringify(result, null, 2), "utf-8");
|
|
118292
118451
|
} catch (writeError) {
|
|
118293
118452
|
console.warn(`Failed to write coverage report: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
118294
118453
|
}
|
|
@@ -118370,8 +118529,8 @@ init_plan_schema();
|
|
|
118370
118529
|
init_qa_gate_profile();
|
|
118371
118530
|
init_file_locks();
|
|
118372
118531
|
import * as crypto12 from "node:crypto";
|
|
118373
|
-
import * as
|
|
118374
|
-
import * as
|
|
118532
|
+
import * as fs103 from "node:fs";
|
|
118533
|
+
import * as path141 from "node:path";
|
|
118375
118534
|
init_ledger();
|
|
118376
118535
|
init_manager();
|
|
118377
118536
|
init_state();
|
|
@@ -118452,17 +118611,17 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
118452
118611
|
};
|
|
118453
118612
|
}
|
|
118454
118613
|
if (args2.working_directory && fallbackDir) {
|
|
118455
|
-
const resolvedTarget =
|
|
118456
|
-
const resolvedRoot =
|
|
118614
|
+
const resolvedTarget = path141.resolve(args2.working_directory);
|
|
118615
|
+
const resolvedRoot = path141.resolve(fallbackDir);
|
|
118457
118616
|
let fallbackExists = false;
|
|
118458
118617
|
try {
|
|
118459
|
-
|
|
118618
|
+
fs103.accessSync(resolvedRoot, fs103.constants.F_OK);
|
|
118460
118619
|
fallbackExists = true;
|
|
118461
118620
|
} catch {
|
|
118462
118621
|
fallbackExists = false;
|
|
118463
118622
|
}
|
|
118464
118623
|
if (fallbackExists) {
|
|
118465
|
-
const isSubdirectory = resolvedTarget.startsWith(resolvedRoot +
|
|
118624
|
+
const isSubdirectory = resolvedTarget.startsWith(resolvedRoot + path141.sep);
|
|
118466
118625
|
if (isSubdirectory) {
|
|
118467
118626
|
return {
|
|
118468
118627
|
success: false,
|
|
@@ -118478,11 +118637,11 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
118478
118637
|
let specMtime;
|
|
118479
118638
|
let specHash;
|
|
118480
118639
|
if (process.env.SWARM_SKIP_SPEC_GATE !== "1") {
|
|
118481
|
-
const specPath =
|
|
118640
|
+
const specPath = path141.join(targetWorkspace, ".swarm", "spec.md");
|
|
118482
118641
|
try {
|
|
118483
|
-
const stat9 = await
|
|
118642
|
+
const stat9 = await fs103.promises.stat(specPath);
|
|
118484
118643
|
specMtime = stat9.mtime.toISOString();
|
|
118485
|
-
const content = await
|
|
118644
|
+
const content = await fs103.promises.readFile(specPath, "utf8");
|
|
118486
118645
|
specHash = crypto12.createHash("sha256").update(content).digest("hex");
|
|
118487
118646
|
} catch {
|
|
118488
118647
|
return {
|
|
@@ -118494,10 +118653,10 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
118494
118653
|
}
|
|
118495
118654
|
}
|
|
118496
118655
|
if (process.env.SWARM_SKIP_GATE_SELECTION !== "1") {
|
|
118497
|
-
const contextPath =
|
|
118656
|
+
const contextPath = path141.join(targetWorkspace, ".swarm", "context.md");
|
|
118498
118657
|
let contextContent = "";
|
|
118499
118658
|
try {
|
|
118500
|
-
contextContent = await
|
|
118659
|
+
contextContent = await fs103.promises.readFile(contextPath, "utf8");
|
|
118501
118660
|
} catch {}
|
|
118502
118661
|
const hasPendingSection = contextContent.includes("## Pending QA Gate Selection");
|
|
118503
118662
|
if (!hasPendingSection) {
|
|
@@ -118784,14 +118943,14 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
118784
118943
|
}
|
|
118785
118944
|
await writeCheckpoint(dir).catch(() => {});
|
|
118786
118945
|
try {
|
|
118787
|
-
const markerPath =
|
|
118946
|
+
const markerPath = path141.join(dir, ".swarm", ".plan-write-marker");
|
|
118788
118947
|
const marker = JSON.stringify({
|
|
118789
118948
|
source: "save_plan",
|
|
118790
118949
|
timestamp: new Date().toISOString(),
|
|
118791
118950
|
phases_count: plan.phases.length,
|
|
118792
118951
|
tasks_count: tasksCount
|
|
118793
118952
|
});
|
|
118794
|
-
await
|
|
118953
|
+
await fs103.promises.writeFile(markerPath, marker, "utf8");
|
|
118795
118954
|
} catch {}
|
|
118796
118955
|
const warnings = [];
|
|
118797
118956
|
let criticReviewFound = false;
|
|
@@ -118807,7 +118966,7 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
118807
118966
|
return {
|
|
118808
118967
|
success: true,
|
|
118809
118968
|
message: "Plan saved successfully",
|
|
118810
|
-
plan_path:
|
|
118969
|
+
plan_path: path141.join(dir, ".swarm", "plan.json"),
|
|
118811
118970
|
phases_count: plan.phases.length,
|
|
118812
118971
|
tasks_count: tasksCount,
|
|
118813
118972
|
...resolvedProfile !== undefined ? { execution_profile: resolvedProfile } : {},
|
|
@@ -118872,8 +119031,8 @@ var save_plan = createSwarmTool({
|
|
|
118872
119031
|
// src/tools/sbom-generate.ts
|
|
118873
119032
|
init_zod();
|
|
118874
119033
|
init_manager2();
|
|
118875
|
-
import * as
|
|
118876
|
-
import * as
|
|
119034
|
+
import * as fs104 from "node:fs";
|
|
119035
|
+
import * as path142 from "node:path";
|
|
118877
119036
|
|
|
118878
119037
|
// src/sbom/detectors/index.ts
|
|
118879
119038
|
init_utils();
|
|
@@ -119721,9 +119880,9 @@ function findManifestFiles(rootDir) {
|
|
|
119721
119880
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
119722
119881
|
function searchDir(dir) {
|
|
119723
119882
|
try {
|
|
119724
|
-
const entries =
|
|
119883
|
+
const entries = fs104.readdirSync(dir, { withFileTypes: true });
|
|
119725
119884
|
for (const entry of entries) {
|
|
119726
|
-
const fullPath =
|
|
119885
|
+
const fullPath = path142.join(dir, entry.name);
|
|
119727
119886
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
119728
119887
|
continue;
|
|
119729
119888
|
}
|
|
@@ -119732,7 +119891,7 @@ function findManifestFiles(rootDir) {
|
|
|
119732
119891
|
} else if (entry.isFile()) {
|
|
119733
119892
|
for (const pattern of patterns) {
|
|
119734
119893
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
119735
|
-
manifestFiles.push(
|
|
119894
|
+
manifestFiles.push(path142.relative(rootDir, fullPath));
|
|
119736
119895
|
break;
|
|
119737
119896
|
}
|
|
119738
119897
|
}
|
|
@@ -119748,13 +119907,13 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
119748
119907
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
119749
119908
|
for (const dir of directories) {
|
|
119750
119909
|
try {
|
|
119751
|
-
const entries =
|
|
119910
|
+
const entries = fs104.readdirSync(dir, { withFileTypes: true });
|
|
119752
119911
|
for (const entry of entries) {
|
|
119753
|
-
const fullPath =
|
|
119912
|
+
const fullPath = path142.join(dir, entry.name);
|
|
119754
119913
|
if (entry.isFile()) {
|
|
119755
119914
|
for (const pattern of patterns) {
|
|
119756
119915
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
119757
|
-
found.push(
|
|
119916
|
+
found.push(path142.relative(workingDir, fullPath));
|
|
119758
119917
|
break;
|
|
119759
119918
|
}
|
|
119760
119919
|
}
|
|
@@ -119767,11 +119926,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
119767
119926
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
119768
119927
|
const dirs = new Set;
|
|
119769
119928
|
for (const file3 of changedFiles) {
|
|
119770
|
-
let currentDir =
|
|
119929
|
+
let currentDir = path142.dirname(file3);
|
|
119771
119930
|
while (true) {
|
|
119772
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
119773
|
-
dirs.add(
|
|
119774
|
-
const parent =
|
|
119931
|
+
if (currentDir && currentDir !== "." && currentDir !== path142.sep) {
|
|
119932
|
+
dirs.add(path142.join(workingDir, currentDir));
|
|
119933
|
+
const parent = path142.dirname(currentDir);
|
|
119775
119934
|
if (parent === currentDir)
|
|
119776
119935
|
break;
|
|
119777
119936
|
currentDir = parent;
|
|
@@ -119785,7 +119944,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
|
119785
119944
|
}
|
|
119786
119945
|
function ensureOutputDir(outputDir) {
|
|
119787
119946
|
try {
|
|
119788
|
-
|
|
119947
|
+
fs104.mkdirSync(outputDir, { recursive: true });
|
|
119789
119948
|
} catch (error93) {
|
|
119790
119949
|
if (!error93 || error93.code !== "EEXIST") {
|
|
119791
119950
|
throw error93;
|
|
@@ -119855,7 +120014,7 @@ var sbom_generate = createSwarmTool({
|
|
|
119855
120014
|
const changedFiles = obj.changed_files;
|
|
119856
120015
|
const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
|
|
119857
120016
|
const workingDir = directory;
|
|
119858
|
-
const outputDir =
|
|
120017
|
+
const outputDir = path142.isAbsolute(relativeOutputDir) ? relativeOutputDir : path142.join(workingDir, relativeOutputDir);
|
|
119859
120018
|
let manifestFiles = [];
|
|
119860
120019
|
if (scope === "all") {
|
|
119861
120020
|
manifestFiles = findManifestFiles(workingDir);
|
|
@@ -119878,11 +120037,11 @@ var sbom_generate = createSwarmTool({
|
|
|
119878
120037
|
const processedFiles = [];
|
|
119879
120038
|
for (const manifestFile of manifestFiles) {
|
|
119880
120039
|
try {
|
|
119881
|
-
const fullPath =
|
|
119882
|
-
if (!
|
|
120040
|
+
const fullPath = path142.isAbsolute(manifestFile) ? manifestFile : path142.join(workingDir, manifestFile);
|
|
120041
|
+
if (!fs104.existsSync(fullPath)) {
|
|
119883
120042
|
continue;
|
|
119884
120043
|
}
|
|
119885
|
-
const content =
|
|
120044
|
+
const content = fs104.readFileSync(fullPath, "utf-8");
|
|
119886
120045
|
const components = detectComponents(manifestFile, content);
|
|
119887
120046
|
processedFiles.push(manifestFile);
|
|
119888
120047
|
if (components.length > 0) {
|
|
@@ -119895,8 +120054,8 @@ var sbom_generate = createSwarmTool({
|
|
|
119895
120054
|
const bom = generateCycloneDX(allComponents);
|
|
119896
120055
|
const bomJson = serializeCycloneDX(bom);
|
|
119897
120056
|
const filename = generateSbomFilename();
|
|
119898
|
-
const outputPath =
|
|
119899
|
-
|
|
120057
|
+
const outputPath = path142.join(outputDir, filename);
|
|
120058
|
+
fs104.writeFileSync(outputPath, bomJson, "utf-8");
|
|
119900
120059
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
119901
120060
|
try {
|
|
119902
120061
|
const timestamp = new Date().toISOString();
|
|
@@ -119938,8 +120097,8 @@ var sbom_generate = createSwarmTool({
|
|
|
119938
120097
|
// src/tools/schema-drift.ts
|
|
119939
120098
|
init_zod();
|
|
119940
120099
|
init_create_tool();
|
|
119941
|
-
import * as
|
|
119942
|
-
import * as
|
|
120100
|
+
import * as fs105 from "node:fs";
|
|
120101
|
+
import * as path143 from "node:path";
|
|
119943
120102
|
var SPEC_CANDIDATES = [
|
|
119944
120103
|
"openapi.json",
|
|
119945
120104
|
"openapi.yaml",
|
|
@@ -119971,28 +120130,28 @@ function normalizePath4(p) {
|
|
|
119971
120130
|
}
|
|
119972
120131
|
function discoverSpecFile(cwd, specFileArg) {
|
|
119973
120132
|
if (specFileArg) {
|
|
119974
|
-
const resolvedPath =
|
|
119975
|
-
const normalizedCwd = cwd.endsWith(
|
|
120133
|
+
const resolvedPath = path143.resolve(cwd, specFileArg);
|
|
120134
|
+
const normalizedCwd = cwd.endsWith(path143.sep) ? cwd : cwd + path143.sep;
|
|
119976
120135
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
119977
120136
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
119978
120137
|
}
|
|
119979
|
-
const ext =
|
|
120138
|
+
const ext = path143.extname(resolvedPath).toLowerCase();
|
|
119980
120139
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
119981
120140
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
119982
120141
|
}
|
|
119983
|
-
const stats =
|
|
120142
|
+
const stats = fs105.statSync(resolvedPath);
|
|
119984
120143
|
if (stats.size > MAX_SPEC_SIZE) {
|
|
119985
120144
|
throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
|
|
119986
120145
|
}
|
|
119987
|
-
if (!
|
|
120146
|
+
if (!fs105.existsSync(resolvedPath)) {
|
|
119988
120147
|
throw new Error(`Spec file not found: ${resolvedPath}`);
|
|
119989
120148
|
}
|
|
119990
120149
|
return resolvedPath;
|
|
119991
120150
|
}
|
|
119992
120151
|
for (const candidate of SPEC_CANDIDATES) {
|
|
119993
|
-
const candidatePath =
|
|
119994
|
-
if (
|
|
119995
|
-
const stats =
|
|
120152
|
+
const candidatePath = path143.resolve(cwd, candidate);
|
|
120153
|
+
if (fs105.existsSync(candidatePath)) {
|
|
120154
|
+
const stats = fs105.statSync(candidatePath);
|
|
119996
120155
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
119997
120156
|
return candidatePath;
|
|
119998
120157
|
}
|
|
@@ -120001,8 +120160,8 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
120001
120160
|
return null;
|
|
120002
120161
|
}
|
|
120003
120162
|
function parseSpec(specFile) {
|
|
120004
|
-
const content =
|
|
120005
|
-
const ext =
|
|
120163
|
+
const content = fs105.readFileSync(specFile, "utf-8");
|
|
120164
|
+
const ext = path143.extname(specFile).toLowerCase();
|
|
120006
120165
|
if (ext === ".json") {
|
|
120007
120166
|
return parseJsonSpec(content);
|
|
120008
120167
|
}
|
|
@@ -120073,12 +120232,12 @@ function extractRoutes(cwd) {
|
|
|
120073
120232
|
function walkDir(dir) {
|
|
120074
120233
|
let entries;
|
|
120075
120234
|
try {
|
|
120076
|
-
entries =
|
|
120235
|
+
entries = fs105.readdirSync(dir, { withFileTypes: true });
|
|
120077
120236
|
} catch {
|
|
120078
120237
|
return;
|
|
120079
120238
|
}
|
|
120080
120239
|
for (const entry of entries) {
|
|
120081
|
-
const fullPath =
|
|
120240
|
+
const fullPath = path143.join(dir, entry.name);
|
|
120082
120241
|
if (entry.isSymbolicLink()) {
|
|
120083
120242
|
continue;
|
|
120084
120243
|
}
|
|
@@ -120088,7 +120247,7 @@ function extractRoutes(cwd) {
|
|
|
120088
120247
|
}
|
|
120089
120248
|
walkDir(fullPath);
|
|
120090
120249
|
} else if (entry.isFile()) {
|
|
120091
|
-
const ext =
|
|
120250
|
+
const ext = path143.extname(entry.name).toLowerCase();
|
|
120092
120251
|
const baseName = entry.name.toLowerCase();
|
|
120093
120252
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
120094
120253
|
continue;
|
|
@@ -120106,7 +120265,7 @@ function extractRoutes(cwd) {
|
|
|
120106
120265
|
}
|
|
120107
120266
|
function extractRoutesFromFile(filePath) {
|
|
120108
120267
|
const routes = [];
|
|
120109
|
-
const content =
|
|
120268
|
+
const content = fs105.readFileSync(filePath, "utf-8");
|
|
120110
120269
|
const lines = content.split(/\r?\n/);
|
|
120111
120270
|
const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
120112
120271
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
@@ -120255,8 +120414,8 @@ init_zod();
|
|
|
120255
120414
|
init_bun_compat();
|
|
120256
120415
|
init_path_security();
|
|
120257
120416
|
init_create_tool();
|
|
120258
|
-
import * as
|
|
120259
|
-
import * as
|
|
120417
|
+
import * as fs106 from "node:fs";
|
|
120418
|
+
import * as path144 from "node:path";
|
|
120260
120419
|
var DEFAULT_MAX_RESULTS = 100;
|
|
120261
120420
|
var DEFAULT_MAX_LINES = 200;
|
|
120262
120421
|
var REGEX_TIMEOUT_MS = 5000;
|
|
@@ -120292,11 +120451,11 @@ function containsWindowsAttacks3(str) {
|
|
|
120292
120451
|
}
|
|
120293
120452
|
function isPathInWorkspace3(filePath, workspace) {
|
|
120294
120453
|
try {
|
|
120295
|
-
const resolvedPath =
|
|
120296
|
-
const realWorkspace =
|
|
120297
|
-
const realResolvedPath =
|
|
120298
|
-
const relativePath =
|
|
120299
|
-
if (relativePath.startsWith("..") ||
|
|
120454
|
+
const resolvedPath = path144.resolve(workspace, filePath);
|
|
120455
|
+
const realWorkspace = fs106.realpathSync(workspace);
|
|
120456
|
+
const realResolvedPath = fs106.realpathSync(resolvedPath);
|
|
120457
|
+
const relativePath = path144.relative(realWorkspace, realResolvedPath);
|
|
120458
|
+
if (relativePath.startsWith("..") || path144.isAbsolute(relativePath)) {
|
|
120300
120459
|
return false;
|
|
120301
120460
|
}
|
|
120302
120461
|
return true;
|
|
@@ -120309,12 +120468,12 @@ function validatePathForRead2(filePath, workspace) {
|
|
|
120309
120468
|
}
|
|
120310
120469
|
function findRgInEnvPath() {
|
|
120311
120470
|
const searchPath = process.env.PATH ?? "";
|
|
120312
|
-
for (const dir of searchPath.split(
|
|
120471
|
+
for (const dir of searchPath.split(path144.delimiter)) {
|
|
120313
120472
|
if (!dir)
|
|
120314
120473
|
continue;
|
|
120315
120474
|
const isWindows = process.platform === "win32";
|
|
120316
|
-
const candidate =
|
|
120317
|
-
if (
|
|
120475
|
+
const candidate = path144.join(dir, isWindows ? "rg.exe" : "rg");
|
|
120476
|
+
if (fs106.existsSync(candidate))
|
|
120318
120477
|
return candidate;
|
|
120319
120478
|
}
|
|
120320
120479
|
return null;
|
|
@@ -120441,10 +120600,10 @@ function collectFiles(dir, workspace, includeGlobs, excludeGlobs) {
|
|
|
120441
120600
|
return files;
|
|
120442
120601
|
}
|
|
120443
120602
|
try {
|
|
120444
|
-
const entries =
|
|
120603
|
+
const entries = fs106.readdirSync(dir, { withFileTypes: true });
|
|
120445
120604
|
for (const entry of entries) {
|
|
120446
|
-
const fullPath =
|
|
120447
|
-
const relativePath =
|
|
120605
|
+
const fullPath = path144.join(dir, entry.name);
|
|
120606
|
+
const relativePath = path144.relative(workspace, fullPath);
|
|
120448
120607
|
if (!validatePathForRead2(fullPath, workspace)) {
|
|
120449
120608
|
continue;
|
|
120450
120609
|
}
|
|
@@ -120485,13 +120644,13 @@ async function fallbackSearch(opts) {
|
|
|
120485
120644
|
const matches = [];
|
|
120486
120645
|
let total = 0;
|
|
120487
120646
|
for (const file3 of files) {
|
|
120488
|
-
const fullPath =
|
|
120647
|
+
const fullPath = path144.join(opts.workspace, file3);
|
|
120489
120648
|
if (!validatePathForRead2(fullPath, opts.workspace)) {
|
|
120490
120649
|
continue;
|
|
120491
120650
|
}
|
|
120492
120651
|
let stats;
|
|
120493
120652
|
try {
|
|
120494
|
-
stats =
|
|
120653
|
+
stats = fs106.statSync(fullPath);
|
|
120495
120654
|
if (stats.size > MAX_FILE_SIZE_BYTES10) {
|
|
120496
120655
|
continue;
|
|
120497
120656
|
}
|
|
@@ -120500,7 +120659,7 @@ async function fallbackSearch(opts) {
|
|
|
120500
120659
|
}
|
|
120501
120660
|
let content;
|
|
120502
120661
|
try {
|
|
120503
|
-
content =
|
|
120662
|
+
content = fs106.readFileSync(fullPath, "utf-8");
|
|
120504
120663
|
} catch {
|
|
120505
120664
|
continue;
|
|
120506
120665
|
}
|
|
@@ -120612,7 +120771,7 @@ var search = createSwarmTool({
|
|
|
120612
120771
|
message: "Exclude pattern contains invalid Windows-specific sequence"
|
|
120613
120772
|
}, null, 2);
|
|
120614
120773
|
}
|
|
120615
|
-
if (!
|
|
120774
|
+
if (!fs106.existsSync(directory)) {
|
|
120616
120775
|
return JSON.stringify({
|
|
120617
120776
|
error: true,
|
|
120618
120777
|
type: "unknown",
|
|
@@ -120890,7 +121049,7 @@ init_config();
|
|
|
120890
121049
|
init_schema();
|
|
120891
121050
|
init_create_tool();
|
|
120892
121051
|
import { mkdir as mkdir23, rename as rename9, writeFile as writeFile18 } from "node:fs/promises";
|
|
120893
|
-
import * as
|
|
121052
|
+
import * as path145 from "node:path";
|
|
120894
121053
|
var MAX_SPEC_BYTES = 256 * 1024;
|
|
120895
121054
|
var spec_write = createSwarmTool({
|
|
120896
121055
|
description: "Write the canonical project spec to .swarm/spec.md. Atomic write, size-bounded (256 KiB), heading-required. Honors spec_writer.allow_spec_write.",
|
|
@@ -120931,14 +121090,14 @@ var spec_write = createSwarmTool({
|
|
|
120931
121090
|
reason: 'spec must contain at least one top-level "# Heading"'
|
|
120932
121091
|
}, null, 2);
|
|
120933
121092
|
}
|
|
120934
|
-
const target =
|
|
120935
|
-
await mkdir23(
|
|
121093
|
+
const target = path145.join(directory, ".swarm", "spec.md");
|
|
121094
|
+
await mkdir23(path145.dirname(target), { recursive: true });
|
|
120936
121095
|
const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
|
|
120937
121096
|
let finalContent = content;
|
|
120938
121097
|
if (mode === "append") {
|
|
120939
121098
|
try {
|
|
120940
|
-
const
|
|
120941
|
-
const prior = await
|
|
121099
|
+
const fs107 = await import("node:fs/promises");
|
|
121100
|
+
const prior = await fs107.readFile(target, "utf-8");
|
|
120942
121101
|
finalContent = `${prior.replace(/\s+$/, "")}
|
|
120943
121102
|
|
|
120944
121103
|
${content}
|
|
@@ -120962,12 +121121,12 @@ init_loader();
|
|
|
120962
121121
|
import {
|
|
120963
121122
|
existsSync as existsSync82,
|
|
120964
121123
|
mkdirSync as mkdirSync35,
|
|
120965
|
-
readFileSync as
|
|
121124
|
+
readFileSync as readFileSync68,
|
|
120966
121125
|
renameSync as renameSync21,
|
|
120967
121126
|
unlinkSync as unlinkSync18,
|
|
120968
121127
|
writeFileSync as writeFileSync28
|
|
120969
121128
|
} from "node:fs";
|
|
120970
|
-
import
|
|
121129
|
+
import path146 from "node:path";
|
|
120971
121130
|
init_create_tool();
|
|
120972
121131
|
init_resolve_working_directory();
|
|
120973
121132
|
var VerdictSchema2 = exports_external.object({
|
|
@@ -121097,9 +121256,9 @@ var submit_phase_council_verdicts = createSwarmTool({
|
|
|
121097
121256
|
}
|
|
121098
121257
|
});
|
|
121099
121258
|
function getPhaseMutationGapFinding(phaseNumber, workingDir) {
|
|
121100
|
-
const mutationGatePath =
|
|
121259
|
+
const mutationGatePath = path146.join(workingDir, ".swarm", "evidence", String(phaseNumber), "mutation-gate.json");
|
|
121101
121260
|
try {
|
|
121102
|
-
const raw =
|
|
121261
|
+
const raw = readFileSync68(mutationGatePath, "utf-8");
|
|
121103
121262
|
const parsed = JSON.parse(raw);
|
|
121104
121263
|
const gateEntry = (parsed.entries ?? []).find((entry) => entry?.type === "mutation-gate");
|
|
121105
121264
|
if (!gateEntry) {
|
|
@@ -121159,9 +121318,9 @@ function getPhaseMutationGapFinding(phaseNumber, workingDir) {
|
|
|
121159
121318
|
}
|
|
121160
121319
|
}
|
|
121161
121320
|
function writePhaseCouncilEvidence(workingDir, synthesis) {
|
|
121162
|
-
const evidenceDir =
|
|
121321
|
+
const evidenceDir = path146.join(workingDir, ".swarm", "evidence", String(synthesis.phaseNumber));
|
|
121163
121322
|
mkdirSync35(evidenceDir, { recursive: true });
|
|
121164
|
-
const evidenceFile =
|
|
121323
|
+
const evidenceFile = path146.join(evidenceDir, "phase-council.json");
|
|
121165
121324
|
const evidenceBundle = {
|
|
121166
121325
|
entries: [
|
|
121167
121326
|
{
|
|
@@ -121530,7 +121689,7 @@ init_schema3();
|
|
|
121530
121689
|
init_store();
|
|
121531
121690
|
init_create_tool();
|
|
121532
121691
|
init_resolve_working_directory();
|
|
121533
|
-
import * as
|
|
121692
|
+
import * as path147 from "node:path";
|
|
121534
121693
|
var FindingSchema2 = exports_external.object({
|
|
121535
121694
|
severity: exports_external.enum(["low", "medium", "high", "critical"]),
|
|
121536
121695
|
category: exports_external.string().min(1),
|
|
@@ -121594,7 +121753,7 @@ var write_architecture_supervisor_evidence = createSwarmTool({
|
|
|
121594
121753
|
if (config3.architectural_supervision?.persist_knowledge_recommendations && args2.knowledge_recommendations.length > 0) {
|
|
121595
121754
|
const knowledgeConfig = KnowledgeConfigSchema.parse(config3.knowledge ?? {});
|
|
121596
121755
|
const lessons = args2.knowledge_recommendations.map((r) => r.lesson);
|
|
121597
|
-
const result = await curateAndStoreSwarm(lessons,
|
|
121756
|
+
const result = await curateAndStoreSwarm(lessons, path147.basename(dirResult.directory), { phase_number: args2.phase }, dirResult.directory, knowledgeConfig, { skipAutoPromotion: true });
|
|
121598
121757
|
knowledgeProposed = result.stored;
|
|
121599
121758
|
}
|
|
121600
121759
|
} catch {}
|
|
@@ -121625,8 +121784,8 @@ var write_architecture_supervisor_evidence = createSwarmTool({
|
|
|
121625
121784
|
init_zod();
|
|
121626
121785
|
init_path_security();
|
|
121627
121786
|
init_create_tool();
|
|
121628
|
-
import * as
|
|
121629
|
-
import * as
|
|
121787
|
+
import * as fs107 from "node:fs";
|
|
121788
|
+
import * as path148 from "node:path";
|
|
121630
121789
|
var WINDOWS_RESERVED_NAMES4 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
121631
121790
|
function containsWindowsAttacks4(str) {
|
|
121632
121791
|
if (/:[^\\/]/.test(str))
|
|
@@ -121640,14 +121799,14 @@ function containsWindowsAttacks4(str) {
|
|
|
121640
121799
|
}
|
|
121641
121800
|
function isPathInWorkspace4(filePath, workspace) {
|
|
121642
121801
|
try {
|
|
121643
|
-
const resolvedPath =
|
|
121644
|
-
if (!
|
|
121802
|
+
const resolvedPath = path148.resolve(workspace, filePath);
|
|
121803
|
+
if (!fs107.existsSync(resolvedPath)) {
|
|
121645
121804
|
return true;
|
|
121646
121805
|
}
|
|
121647
|
-
const realWorkspace =
|
|
121648
|
-
const realResolvedPath =
|
|
121649
|
-
const relativePath =
|
|
121650
|
-
if (relativePath.startsWith("..") ||
|
|
121806
|
+
const realWorkspace = fs107.realpathSync(workspace);
|
|
121807
|
+
const realResolvedPath = fs107.realpathSync(resolvedPath);
|
|
121808
|
+
const relativePath = path148.relative(realWorkspace, realResolvedPath);
|
|
121809
|
+
if (relativePath.startsWith("..") || path148.isAbsolute(relativePath)) {
|
|
121651
121810
|
return false;
|
|
121652
121811
|
}
|
|
121653
121812
|
return true;
|
|
@@ -121819,7 +121978,7 @@ var suggestPatch = createSwarmTool({
|
|
|
121819
121978
|
message: "changes cannot be empty"
|
|
121820
121979
|
}, null, 2);
|
|
121821
121980
|
}
|
|
121822
|
-
if (!
|
|
121981
|
+
if (!fs107.existsSync(directory)) {
|
|
121823
121982
|
return JSON.stringify({
|
|
121824
121983
|
success: false,
|
|
121825
121984
|
error: true,
|
|
@@ -121855,8 +122014,8 @@ var suggestPatch = createSwarmTool({
|
|
|
121855
122014
|
});
|
|
121856
122015
|
continue;
|
|
121857
122016
|
}
|
|
121858
|
-
const fullPath =
|
|
121859
|
-
if (!
|
|
122017
|
+
const fullPath = path148.resolve(directory, change.file);
|
|
122018
|
+
if (!fs107.existsSync(fullPath)) {
|
|
121860
122019
|
errors5.push({
|
|
121861
122020
|
success: false,
|
|
121862
122021
|
error: true,
|
|
@@ -121870,7 +122029,7 @@ var suggestPatch = createSwarmTool({
|
|
|
121870
122029
|
}
|
|
121871
122030
|
let content;
|
|
121872
122031
|
try {
|
|
121873
|
-
content =
|
|
122032
|
+
content = fs107.readFileSync(fullPath, "utf-8");
|
|
121874
122033
|
} catch (err3) {
|
|
121875
122034
|
errors5.push({
|
|
121876
122035
|
success: false,
|
|
@@ -122158,12 +122317,12 @@ var lean_turbo_acquire_locks = createSwarmTool({
|
|
|
122158
122317
|
// src/tools/lean-turbo-plan-lanes.ts
|
|
122159
122318
|
init_zod();
|
|
122160
122319
|
init_constants();
|
|
122161
|
-
import * as
|
|
122162
|
-
import * as
|
|
122320
|
+
import * as fs109 from "node:fs";
|
|
122321
|
+
import * as path150 from "node:path";
|
|
122163
122322
|
|
|
122164
122323
|
// src/turbo/lean/conflicts.ts
|
|
122165
|
-
import * as
|
|
122166
|
-
import * as
|
|
122324
|
+
import * as fs108 from "node:fs";
|
|
122325
|
+
import * as path149 from "node:path";
|
|
122167
122326
|
var DEFAULT_GLOBAL_FILES = [
|
|
122168
122327
|
"package.json",
|
|
122169
122328
|
"package-lock.json",
|
|
@@ -122290,12 +122449,12 @@ function isProtectedPath2(normalizedPath) {
|
|
|
122290
122449
|
return false;
|
|
122291
122450
|
}
|
|
122292
122451
|
function readTaskScopes(directory, taskId) {
|
|
122293
|
-
const scopePath =
|
|
122452
|
+
const scopePath = path149.join(directory, ".swarm", "scopes", `scope-${taskId}.json`);
|
|
122294
122453
|
try {
|
|
122295
|
-
if (!
|
|
122454
|
+
if (!fs108.existsSync(scopePath)) {
|
|
122296
122455
|
return null;
|
|
122297
122456
|
}
|
|
122298
|
-
const raw =
|
|
122457
|
+
const raw = fs108.readFileSync(scopePath, "utf-8");
|
|
122299
122458
|
const parsed = JSON.parse(raw);
|
|
122300
122459
|
if (!parsed || !Array.isArray(parsed.files)) {
|
|
122301
122460
|
return null;
|
|
@@ -122678,12 +122837,12 @@ function createEmptyPlan(phaseNumber, planId) {
|
|
|
122678
122837
|
// src/tools/lean-turbo-plan-lanes.ts
|
|
122679
122838
|
init_create_tool();
|
|
122680
122839
|
function readPlanJson(directory) {
|
|
122681
|
-
const planPath =
|
|
122682
|
-
if (!
|
|
122840
|
+
const planPath = path150.join(directory, ".swarm", "plan.json");
|
|
122841
|
+
if (!fs109.existsSync(planPath)) {
|
|
122683
122842
|
return null;
|
|
122684
122843
|
}
|
|
122685
122844
|
try {
|
|
122686
|
-
return JSON.parse(
|
|
122845
|
+
return JSON.parse(fs109.readFileSync(planPath, "utf-8"));
|
|
122687
122846
|
} catch {
|
|
122688
122847
|
return null;
|
|
122689
122848
|
}
|
|
@@ -122732,8 +122891,8 @@ init_config();
|
|
|
122732
122891
|
|
|
122733
122892
|
// src/turbo/lean/reviewer.ts
|
|
122734
122893
|
init_state();
|
|
122735
|
-
import * as
|
|
122736
|
-
import * as
|
|
122894
|
+
import * as fs110 from "node:fs/promises";
|
|
122895
|
+
import * as path151 from "node:path";
|
|
122737
122896
|
init_state3();
|
|
122738
122897
|
var DEFAULT_CONFIG3 = {
|
|
122739
122898
|
reviewerAgent: "",
|
|
@@ -122849,9 +123008,9 @@ function parseReviewerVerdict(responseText) {
|
|
|
122849
123008
|
return { verdict, reason };
|
|
122850
123009
|
}
|
|
122851
123010
|
async function writeReviewerEvidence(directory, phase, verdict, reason) {
|
|
122852
|
-
const evidenceDir =
|
|
122853
|
-
await
|
|
122854
|
-
const evidencePath =
|
|
123011
|
+
const evidenceDir = path151.join(directory, ".swarm", "evidence", String(phase));
|
|
123012
|
+
await fs110.mkdir(evidenceDir, { recursive: true });
|
|
123013
|
+
const evidencePath = path151.join(evidenceDir, "lean-turbo-reviewer.json");
|
|
122855
123014
|
const content = JSON.stringify({
|
|
122856
123015
|
phase,
|
|
122857
123016
|
verdict,
|
|
@@ -122860,11 +123019,11 @@ async function writeReviewerEvidence(directory, phase, verdict, reason) {
|
|
|
122860
123019
|
}, null, 2);
|
|
122861
123020
|
const tempPath = `${evidencePath}.tmp.${process.pid}.${Date.now()}`;
|
|
122862
123021
|
try {
|
|
122863
|
-
await
|
|
122864
|
-
await
|
|
123022
|
+
await fs110.writeFile(tempPath, content, "utf-8");
|
|
123023
|
+
await fs110.rename(tempPath, evidencePath);
|
|
122865
123024
|
} catch (error93) {
|
|
122866
123025
|
try {
|
|
122867
|
-
await
|
|
123026
|
+
await fs110.unlink(tempPath);
|
|
122868
123027
|
} catch {}
|
|
122869
123028
|
throw error93;
|
|
122870
123029
|
}
|
|
@@ -123655,8 +123814,8 @@ var lean_turbo_status = createSwarmTool({
|
|
|
123655
123814
|
// src/tools/lint-spec.ts
|
|
123656
123815
|
init_spec_schema();
|
|
123657
123816
|
init_create_tool();
|
|
123658
|
-
import * as
|
|
123659
|
-
import * as
|
|
123817
|
+
import * as fs111 from "node:fs";
|
|
123818
|
+
import * as path152 from "node:path";
|
|
123660
123819
|
var SPEC_FILE_NAME = "spec.md";
|
|
123661
123820
|
var SWARM_DIR2 = ".swarm";
|
|
123662
123821
|
var OBLIGATION_KEYWORDS2 = ["MUST", "SHALL", "SHOULD", "MAY"];
|
|
@@ -123709,8 +123868,8 @@ var lint_spec = createSwarmTool({
|
|
|
123709
123868
|
async execute(_args, directory) {
|
|
123710
123869
|
const errors5 = [];
|
|
123711
123870
|
const warnings = [];
|
|
123712
|
-
const specPath =
|
|
123713
|
-
if (!
|
|
123871
|
+
const specPath = path152.join(directory, SWARM_DIR2, SPEC_FILE_NAME);
|
|
123872
|
+
if (!fs111.existsSync(specPath)) {
|
|
123714
123873
|
const result2 = {
|
|
123715
123874
|
valid: false,
|
|
123716
123875
|
specMtime: null,
|
|
@@ -123729,12 +123888,12 @@ var lint_spec = createSwarmTool({
|
|
|
123729
123888
|
}
|
|
123730
123889
|
let specMtime = null;
|
|
123731
123890
|
try {
|
|
123732
|
-
const stats =
|
|
123891
|
+
const stats = fs111.statSync(specPath);
|
|
123733
123892
|
specMtime = stats.mtime.toISOString();
|
|
123734
123893
|
} catch {}
|
|
123735
123894
|
let content;
|
|
123736
123895
|
try {
|
|
123737
|
-
content =
|
|
123896
|
+
content = fs111.readFileSync(specPath, "utf-8");
|
|
123738
123897
|
} catch (e) {
|
|
123739
123898
|
const result2 = {
|
|
123740
123899
|
valid: false,
|
|
@@ -123779,13 +123938,13 @@ var lint_spec = createSwarmTool({
|
|
|
123779
123938
|
});
|
|
123780
123939
|
// src/tools/mutation-test.ts
|
|
123781
123940
|
init_zod();
|
|
123782
|
-
import * as
|
|
123783
|
-
import * as
|
|
123941
|
+
import * as fs112 from "node:fs";
|
|
123942
|
+
import * as path154 from "node:path";
|
|
123784
123943
|
|
|
123785
123944
|
// src/mutation/engine.ts
|
|
123786
123945
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
123787
123946
|
import { unlinkSync as unlinkSync19, writeFileSync as writeFileSync29 } from "node:fs";
|
|
123788
|
-
import * as
|
|
123947
|
+
import * as path153 from "node:path";
|
|
123789
123948
|
|
|
123790
123949
|
// src/mutation/equivalence.ts
|
|
123791
123950
|
function isStaticallyEquivalent(originalCode, mutatedCode) {
|
|
@@ -123931,7 +124090,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
123931
124090
|
let patchFile;
|
|
123932
124091
|
try {
|
|
123933
124092
|
const safeId2 = patch.id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
123934
|
-
patchFile =
|
|
124093
|
+
patchFile = path153.join(workingDir, `.mutation_patch_${safeId2}.diff`);
|
|
123935
124094
|
try {
|
|
123936
124095
|
writeFileSync29(patchFile, patch.patch);
|
|
123937
124096
|
} catch (writeErr) {
|
|
@@ -124335,8 +124494,8 @@ var mutation_test = createSwarmTool({
|
|
|
124335
124494
|
];
|
|
124336
124495
|
for (const filePath of uniquePaths) {
|
|
124337
124496
|
try {
|
|
124338
|
-
const resolvedPath =
|
|
124339
|
-
sourceFiles.set(filePath,
|
|
124497
|
+
const resolvedPath = path154.resolve(cwd, filePath);
|
|
124498
|
+
sourceFiles.set(filePath, fs112.readFileSync(resolvedPath, "utf-8"));
|
|
124340
124499
|
} catch {}
|
|
124341
124500
|
}
|
|
124342
124501
|
const report = await executeMutationSuite(typedArgs.patches, typedArgs.test_command, typedArgs.files, cwd, undefined, undefined, sourceFiles.size > 0 ? sourceFiles : undefined);
|
|
@@ -124354,8 +124513,8 @@ var mutation_test = createSwarmTool({
|
|
|
124354
124513
|
init_zod();
|
|
124355
124514
|
init_manager2();
|
|
124356
124515
|
init_detector();
|
|
124357
|
-
import * as
|
|
124358
|
-
import * as
|
|
124516
|
+
import * as fs113 from "node:fs";
|
|
124517
|
+
import * as path155 from "node:path";
|
|
124359
124518
|
init_create_tool();
|
|
124360
124519
|
var MAX_FILE_SIZE2 = 2 * 1024 * 1024;
|
|
124361
124520
|
var BINARY_CHECK_BYTES = 8192;
|
|
@@ -124421,7 +124580,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
124421
124580
|
if (languages?.length) {
|
|
124422
124581
|
const lowerLangs = languages.map((l) => l.toLowerCase());
|
|
124423
124582
|
filesToCheck = filesToCheck.filter((file3) => {
|
|
124424
|
-
const ext =
|
|
124583
|
+
const ext = path155.extname(file3.path).toLowerCase();
|
|
124425
124584
|
const langDef = getLanguageForExtension(ext);
|
|
124426
124585
|
const fileProfile = getProfileForFile(file3.path);
|
|
124427
124586
|
const langId = fileProfile?.id || langDef?.id;
|
|
@@ -124434,7 +124593,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
124434
124593
|
let skippedCount = 0;
|
|
124435
124594
|
for (const fileInfo of filesToCheck) {
|
|
124436
124595
|
const { path: filePath } = fileInfo;
|
|
124437
|
-
const fullPath =
|
|
124596
|
+
const fullPath = path155.isAbsolute(filePath) ? filePath : path155.join(directory, filePath);
|
|
124438
124597
|
const result = {
|
|
124439
124598
|
path: filePath,
|
|
124440
124599
|
language: "",
|
|
@@ -124464,7 +124623,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
124464
124623
|
}
|
|
124465
124624
|
let content;
|
|
124466
124625
|
try {
|
|
124467
|
-
content =
|
|
124626
|
+
content = fs113.readFileSync(fullPath, "utf8");
|
|
124468
124627
|
} catch {
|
|
124469
124628
|
result.skipped_reason = "file_read_error";
|
|
124470
124629
|
skippedCount++;
|
|
@@ -124483,7 +124642,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
124483
124642
|
results.push(result);
|
|
124484
124643
|
continue;
|
|
124485
124644
|
}
|
|
124486
|
-
const ext =
|
|
124645
|
+
const ext = path155.extname(filePath).toLowerCase();
|
|
124487
124646
|
const langDef = getLanguageForExtension(ext);
|
|
124488
124647
|
result.language = profile?.id || langDef?.id || "unknown";
|
|
124489
124648
|
const errors5 = extractSyntaxErrors(parser, content);
|
|
@@ -124580,8 +124739,8 @@ init_zod();
|
|
|
124580
124739
|
init_utils();
|
|
124581
124740
|
init_create_tool();
|
|
124582
124741
|
init_path_security();
|
|
124583
|
-
import * as
|
|
124584
|
-
import * as
|
|
124742
|
+
import * as fs114 from "node:fs";
|
|
124743
|
+
import * as path156 from "node:path";
|
|
124585
124744
|
var MAX_TEXT_LENGTH = 200;
|
|
124586
124745
|
var MAX_FILE_SIZE_BYTES11 = 1024 * 1024;
|
|
124587
124746
|
var SUPPORTED_EXTENSIONS4 = new Set([
|
|
@@ -124647,9 +124806,9 @@ function validatePathsInput(paths, cwd) {
|
|
|
124647
124806
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
124648
124807
|
}
|
|
124649
124808
|
try {
|
|
124650
|
-
const resolvedPath =
|
|
124651
|
-
const normalizedCwd =
|
|
124652
|
-
const normalizedResolved =
|
|
124809
|
+
const resolvedPath = path156.resolve(paths);
|
|
124810
|
+
const normalizedCwd = path156.resolve(cwd);
|
|
124811
|
+
const normalizedResolved = path156.resolve(resolvedPath);
|
|
124653
124812
|
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
124654
124813
|
return {
|
|
124655
124814
|
error: "paths must be within the current working directory",
|
|
@@ -124665,13 +124824,13 @@ function validatePathsInput(paths, cwd) {
|
|
|
124665
124824
|
}
|
|
124666
124825
|
}
|
|
124667
124826
|
function isSupportedExtension(filePath) {
|
|
124668
|
-
const ext =
|
|
124827
|
+
const ext = path156.extname(filePath).toLowerCase();
|
|
124669
124828
|
return SUPPORTED_EXTENSIONS4.has(ext);
|
|
124670
124829
|
}
|
|
124671
124830
|
function findSourceFiles3(dir, files = []) {
|
|
124672
124831
|
let entries;
|
|
124673
124832
|
try {
|
|
124674
|
-
entries =
|
|
124833
|
+
entries = fs114.readdirSync(dir);
|
|
124675
124834
|
} catch {
|
|
124676
124835
|
return files;
|
|
124677
124836
|
}
|
|
@@ -124680,10 +124839,10 @@ function findSourceFiles3(dir, files = []) {
|
|
|
124680
124839
|
if (SKIP_DIRECTORIES5.has(entry)) {
|
|
124681
124840
|
continue;
|
|
124682
124841
|
}
|
|
124683
|
-
const fullPath =
|
|
124842
|
+
const fullPath = path156.join(dir, entry);
|
|
124684
124843
|
let stat9;
|
|
124685
124844
|
try {
|
|
124686
|
-
stat9 =
|
|
124845
|
+
stat9 = fs114.statSync(fullPath);
|
|
124687
124846
|
} catch {
|
|
124688
124847
|
continue;
|
|
124689
124848
|
}
|
|
@@ -124776,7 +124935,7 @@ var todo_extract = createSwarmTool({
|
|
|
124776
124935
|
return JSON.stringify(errorResult, null, 2);
|
|
124777
124936
|
}
|
|
124778
124937
|
const scanPath = resolvedPath;
|
|
124779
|
-
if (!
|
|
124938
|
+
if (!fs114.existsSync(scanPath)) {
|
|
124780
124939
|
const errorResult = {
|
|
124781
124940
|
error: `path not found: ${pathsInput}`,
|
|
124782
124941
|
total: 0,
|
|
@@ -124786,13 +124945,13 @@ var todo_extract = createSwarmTool({
|
|
|
124786
124945
|
return JSON.stringify(errorResult, null, 2);
|
|
124787
124946
|
}
|
|
124788
124947
|
const filesToScan = [];
|
|
124789
|
-
const stat9 =
|
|
124948
|
+
const stat9 = fs114.statSync(scanPath);
|
|
124790
124949
|
if (stat9.isFile()) {
|
|
124791
124950
|
if (isSupportedExtension(scanPath)) {
|
|
124792
124951
|
filesToScan.push(scanPath);
|
|
124793
124952
|
} else {
|
|
124794
124953
|
const errorResult = {
|
|
124795
|
-
error: `unsupported file extension: ${
|
|
124954
|
+
error: `unsupported file extension: ${path156.extname(scanPath)}`,
|
|
124796
124955
|
total: 0,
|
|
124797
124956
|
byPriority: { high: 0, medium: 0, low: 0 },
|
|
124798
124957
|
entries: []
|
|
@@ -124805,11 +124964,11 @@ var todo_extract = createSwarmTool({
|
|
|
124805
124964
|
const allEntries = [];
|
|
124806
124965
|
for (const filePath of filesToScan) {
|
|
124807
124966
|
try {
|
|
124808
|
-
const fileStat =
|
|
124967
|
+
const fileStat = fs114.statSync(filePath);
|
|
124809
124968
|
if (fileStat.size > MAX_FILE_SIZE_BYTES11) {
|
|
124810
124969
|
continue;
|
|
124811
124970
|
}
|
|
124812
|
-
const content =
|
|
124971
|
+
const content = fs114.readFileSync(filePath, "utf-8");
|
|
124813
124972
|
const entries = parseTodoComments(content, filePath, tagsSet);
|
|
124814
124973
|
allEntries.push(...entries);
|
|
124815
124974
|
} catch {}
|
|
@@ -124840,18 +124999,18 @@ init_loader();
|
|
|
124840
124999
|
init_schema();
|
|
124841
125000
|
init_qa_gate_profile();
|
|
124842
125001
|
init_gate_evidence();
|
|
124843
|
-
import * as
|
|
124844
|
-
import * as
|
|
125002
|
+
import * as fs118 from "node:fs";
|
|
125003
|
+
import * as path160 from "node:path";
|
|
124845
125004
|
|
|
124846
125005
|
// src/hooks/diff-scope.ts
|
|
124847
125006
|
init_bun_compat();
|
|
124848
|
-
import * as
|
|
124849
|
-
import * as
|
|
125007
|
+
import * as fs116 from "node:fs";
|
|
125008
|
+
import * as path158 from "node:path";
|
|
124850
125009
|
|
|
124851
125010
|
// src/utils/gitignore-warning.ts
|
|
124852
125011
|
init_bun_compat();
|
|
124853
|
-
import * as
|
|
124854
|
-
import * as
|
|
125012
|
+
import * as fs115 from "node:fs";
|
|
125013
|
+
import * as path157 from "node:path";
|
|
124855
125014
|
var _internals67 = { bunSpawn };
|
|
124856
125015
|
var _swarmGitExcludedChecked = false;
|
|
124857
125016
|
function fileCoversSwarm(content) {
|
|
@@ -124925,16 +125084,16 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
124925
125084
|
const excludeRelPath = excludePathRaw.trim();
|
|
124926
125085
|
if (!excludeRelPath)
|
|
124927
125086
|
return;
|
|
124928
|
-
const excludePath =
|
|
125087
|
+
const excludePath = path157.isAbsolute(excludeRelPath) ? excludeRelPath : path157.join(directory, excludeRelPath);
|
|
124929
125088
|
if (checkIgnoreExitCode !== 0) {
|
|
124930
125089
|
try {
|
|
124931
|
-
|
|
125090
|
+
fs115.mkdirSync(path157.dirname(excludePath), { recursive: true });
|
|
124932
125091
|
let existing = "";
|
|
124933
125092
|
try {
|
|
124934
|
-
existing =
|
|
125093
|
+
existing = fs115.readFileSync(excludePath, "utf8");
|
|
124935
125094
|
} catch {}
|
|
124936
125095
|
if (!fileCoversSwarm(existing)) {
|
|
124937
|
-
|
|
125096
|
+
fs115.appendFileSync(excludePath, `
|
|
124938
125097
|
# opencode-swarm local runtime state
|
|
124939
125098
|
.swarm/
|
|
124940
125099
|
`, "utf8");
|
|
@@ -124972,10 +125131,10 @@ async function ensureSwarmGitExcluded(directory, options = {}) {
|
|
|
124972
125131
|
var _internals68 = { bunSpawn };
|
|
124973
125132
|
function getDeclaredScope(taskId, directory) {
|
|
124974
125133
|
try {
|
|
124975
|
-
const planPath =
|
|
124976
|
-
if (!
|
|
125134
|
+
const planPath = path158.join(directory, ".swarm", "plan.json");
|
|
125135
|
+
if (!fs116.existsSync(planPath))
|
|
124977
125136
|
return null;
|
|
124978
|
-
const raw =
|
|
125137
|
+
const raw = fs116.readFileSync(planPath, "utf-8");
|
|
124979
125138
|
const plan = JSON.parse(raw);
|
|
124980
125139
|
for (const phase of plan.phases ?? []) {
|
|
124981
125140
|
for (const task of phase.tasks ?? []) {
|
|
@@ -125077,8 +125236,8 @@ init_telemetry();
|
|
|
125077
125236
|
|
|
125078
125237
|
// src/turbo/lean/task-completion.ts
|
|
125079
125238
|
init_file_locks();
|
|
125080
|
-
import * as
|
|
125081
|
-
import * as
|
|
125239
|
+
import * as fs117 from "node:fs";
|
|
125240
|
+
import * as path159 from "node:path";
|
|
125082
125241
|
var _internals69 = {
|
|
125083
125242
|
listActiveLocks,
|
|
125084
125243
|
verifyLeanTurboTaskCompletion
|
|
@@ -125097,7 +125256,7 @@ var TIER_3_PATTERNS = [
|
|
|
125097
125256
|
];
|
|
125098
125257
|
function matchesTier3Pattern(files) {
|
|
125099
125258
|
for (const file3 of files) {
|
|
125100
|
-
const fileName =
|
|
125259
|
+
const fileName = path159.basename(file3);
|
|
125101
125260
|
for (const pattern of TIER_3_PATTERNS) {
|
|
125102
125261
|
if (pattern.test(fileName)) {
|
|
125103
125262
|
return true;
|
|
@@ -125109,14 +125268,14 @@ function matchesTier3Pattern(files) {
|
|
|
125109
125268
|
function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
|
|
125110
125269
|
let persisted = null;
|
|
125111
125270
|
try {
|
|
125112
|
-
const statePath =
|
|
125113
|
-
if (!
|
|
125271
|
+
const statePath = path159.join(directory, ".swarm", "turbo-state.json");
|
|
125272
|
+
if (!fs117.existsSync(statePath)) {
|
|
125114
125273
|
return {
|
|
125115
125274
|
ok: false,
|
|
125116
125275
|
reason: "Lean Turbo state file not found"
|
|
125117
125276
|
};
|
|
125118
125277
|
}
|
|
125119
|
-
const raw =
|
|
125278
|
+
const raw = fs117.readFileSync(statePath, "utf-8");
|
|
125120
125279
|
persisted = JSON.parse(raw);
|
|
125121
125280
|
} catch {
|
|
125122
125281
|
return {
|
|
@@ -125193,11 +125352,11 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
|
|
|
125193
125352
|
};
|
|
125194
125353
|
}
|
|
125195
125354
|
const phase = runState.phase ?? 0;
|
|
125196
|
-
const evidencePath =
|
|
125197
|
-
const expectedDir =
|
|
125198
|
-
const resolvedPath =
|
|
125199
|
-
const resolvedDir =
|
|
125200
|
-
if (!resolvedPath.startsWith(resolvedDir +
|
|
125355
|
+
const evidencePath = path159.join(directory, ".swarm", "evidence", String(phase), "lean-turbo", `${lane.laneId}.json`);
|
|
125356
|
+
const expectedDir = path159.join(directory, ".swarm", "evidence", String(phase), "lean-turbo");
|
|
125357
|
+
const resolvedPath = path159.resolve(evidencePath);
|
|
125358
|
+
const resolvedDir = path159.resolve(expectedDir);
|
|
125359
|
+
if (!resolvedPath.startsWith(resolvedDir + path159.sep) && resolvedPath !== resolvedDir) {
|
|
125201
125360
|
return {
|
|
125202
125361
|
ok: false,
|
|
125203
125362
|
reason: `Lane ID causes path traversal: ${lane.laneId}`,
|
|
@@ -125209,7 +125368,7 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
|
|
|
125209
125368
|
}
|
|
125210
125369
|
};
|
|
125211
125370
|
}
|
|
125212
|
-
if (!
|
|
125371
|
+
if (!fs117.existsSync(evidencePath)) {
|
|
125213
125372
|
return {
|
|
125214
125373
|
ok: false,
|
|
125215
125374
|
reason: `Lane ${lane.laneId} evidence file not found: ${evidencePath}`,
|
|
@@ -125237,8 +125396,8 @@ function verifyLeanTurboTaskCompletion(directory, taskId, sessionID) {
|
|
|
125237
125396
|
}
|
|
125238
125397
|
let filesTouched = [];
|
|
125239
125398
|
try {
|
|
125240
|
-
const planPath =
|
|
125241
|
-
const planRaw =
|
|
125399
|
+
const planPath = path159.join(directory, ".swarm", "plan.json");
|
|
125400
|
+
const planRaw = fs117.readFileSync(planPath, "utf-8");
|
|
125242
125401
|
const plan = JSON.parse(planRaw);
|
|
125243
125402
|
for (const planPhase of plan.phases ?? []) {
|
|
125244
125403
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -125321,7 +125480,7 @@ var TIER_3_PATTERNS2 = [
|
|
|
125321
125480
|
];
|
|
125322
125481
|
function matchesTier3Pattern2(files) {
|
|
125323
125482
|
for (const file3 of files) {
|
|
125324
|
-
const fileName =
|
|
125483
|
+
const fileName = path160.basename(file3);
|
|
125325
125484
|
for (const pattern of TIER_3_PATTERNS2) {
|
|
125326
125485
|
if (pattern.test(fileName)) {
|
|
125327
125486
|
return true;
|
|
@@ -125360,8 +125519,8 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
125360
125519
|
if (!skipStandardTurboBypass && hasActiveTurboMode()) {
|
|
125361
125520
|
const resolvedDir2 = workingDirectory;
|
|
125362
125521
|
try {
|
|
125363
|
-
const planPath =
|
|
125364
|
-
const planRaw =
|
|
125522
|
+
const planPath = path160.join(resolvedDir2, ".swarm", "plan.json");
|
|
125523
|
+
const planRaw = fs118.readFileSync(planPath, "utf-8");
|
|
125365
125524
|
const plan = JSON.parse(planRaw);
|
|
125366
125525
|
for (const planPhase of plan.phases ?? []) {
|
|
125367
125526
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -125438,8 +125597,8 @@ function checkReviewerGate(taskId, workingDirectory, stageBParallelEnabled = fal
|
|
|
125438
125597
|
}
|
|
125439
125598
|
if (resolvedDir) {
|
|
125440
125599
|
try {
|
|
125441
|
-
const planPath =
|
|
125442
|
-
const planRaw =
|
|
125600
|
+
const planPath = path160.join(resolvedDir, ".swarm", "plan.json");
|
|
125601
|
+
const planRaw = fs118.readFileSync(planPath, "utf-8");
|
|
125443
125602
|
const plan = JSON.parse(planRaw);
|
|
125444
125603
|
for (const planPhase of plan.phases ?? []) {
|
|
125445
125604
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -125657,72 +125816,35 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
125657
125816
|
session.currentTaskId = args2.task_id;
|
|
125658
125817
|
}
|
|
125659
125818
|
}
|
|
125660
|
-
let normalizedDir;
|
|
125661
125819
|
let directory;
|
|
125662
|
-
if (args2.working_directory
|
|
125663
|
-
|
|
125664
|
-
|
|
125665
|
-
|
|
125666
|
-
|
|
125667
|
-
|
|
125668
|
-
|
|
125669
|
-
|
|
125670
|
-
|
|
125671
|
-
|
|
125672
|
-
|
|
125673
|
-
|
|
125674
|
-
|
|
125675
|
-
|
|
125676
|
-
|
|
125677
|
-
|
|
125678
|
-
|
|
125679
|
-
|
|
125680
|
-
|
|
125681
|
-
|
|
125682
|
-
|
|
125683
|
-
|
|
125684
|
-
|
|
125685
|
-
"Invalid working_directory: path traversal sequences (..) are not allowed"
|
|
125686
|
-
]
|
|
125687
|
-
};
|
|
125688
|
-
}
|
|
125689
|
-
const resolvedDir = path155.resolve(normalizedDir);
|
|
125690
|
-
try {
|
|
125691
|
-
const realPath = fs113.realpathSync(resolvedDir);
|
|
125692
|
-
const planPath = path155.join(realPath, ".swarm", "plan.json");
|
|
125693
|
-
if (!fs113.existsSync(planPath)) {
|
|
125694
|
-
return {
|
|
125695
|
-
success: false,
|
|
125696
|
-
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
125697
|
-
errors: [
|
|
125698
|
-
`Invalid working_directory: plan not found in "${realPath}"`
|
|
125699
|
-
]
|
|
125700
|
-
};
|
|
125701
|
-
}
|
|
125702
|
-
directory = realPath;
|
|
125703
|
-
} catch {
|
|
125704
|
-
return {
|
|
125705
|
-
success: false,
|
|
125706
|
-
message: `Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`,
|
|
125707
|
-
errors: [
|
|
125708
|
-
`Invalid working_directory: path "${resolvedDir}" does not exist or is inaccessible`
|
|
125709
|
-
]
|
|
125710
|
-
};
|
|
125711
|
-
}
|
|
125712
|
-
} else {
|
|
125713
|
-
if (!fallbackDir) {
|
|
125714
|
-
return {
|
|
125715
|
-
success: false,
|
|
125716
|
-
message: "No working_directory provided and fallbackDir is undefined",
|
|
125717
|
-
errors: ["Cannot resolve directory for task status update"]
|
|
125718
|
-
};
|
|
125719
|
-
}
|
|
125720
|
-
directory = fallbackDir;
|
|
125820
|
+
if (!args2.working_directory && !fallbackDir) {
|
|
125821
|
+
return {
|
|
125822
|
+
success: false,
|
|
125823
|
+
message: "No working_directory provided and fallbackDir is undefined",
|
|
125824
|
+
errors: ["Cannot resolve directory for task status update"]
|
|
125825
|
+
};
|
|
125826
|
+
}
|
|
125827
|
+
const resolveResult = resolveWorkingDirectory(args2.working_directory ?? fallbackDir, fallbackDir);
|
|
125828
|
+
if (!resolveResult.success) {
|
|
125829
|
+
return {
|
|
125830
|
+
success: false,
|
|
125831
|
+
message: resolveResult.message,
|
|
125832
|
+
errors: [resolveResult.message]
|
|
125833
|
+
};
|
|
125834
|
+
}
|
|
125835
|
+
directory = resolveResult.directory;
|
|
125836
|
+
const planPath = path160.join(directory, ".swarm", "plan.json");
|
|
125837
|
+
if (!fs118.existsSync(planPath)) {
|
|
125838
|
+
return {
|
|
125839
|
+
success: false,
|
|
125840
|
+
message: `Invalid working_directory: plan not found in "${directory}"`,
|
|
125841
|
+
errors: [`Invalid working_directory: plan not found in "${directory}"`]
|
|
125842
|
+
};
|
|
125721
125843
|
}
|
|
125722
125844
|
if (fallbackDir && directory !== fallbackDir) {
|
|
125723
|
-
const canonicalDir =
|
|
125724
|
-
const canonicalRoot =
|
|
125725
|
-
if (canonicalDir.startsWith(canonicalRoot +
|
|
125845
|
+
const canonicalDir = fs118.realpathSync(path160.resolve(directory));
|
|
125846
|
+
const canonicalRoot = fs118.realpathSync(path160.resolve(fallbackDir));
|
|
125847
|
+
if (canonicalDir.startsWith(canonicalRoot + path160.sep)) {
|
|
125726
125848
|
return {
|
|
125727
125849
|
success: false,
|
|
125728
125850
|
message: `Invalid working_directory: "${directory}" is a subdirectory of ` + `the project root "${fallbackDir}". Pass the project root path or ` + `omit working_directory entirely.`,
|
|
@@ -125734,22 +125856,22 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
125734
125856
|
}
|
|
125735
125857
|
if (args2.status === "in_progress") {
|
|
125736
125858
|
try {
|
|
125737
|
-
const evidencePath =
|
|
125738
|
-
|
|
125739
|
-
const fd =
|
|
125859
|
+
const evidencePath = path160.join(directory, ".swarm", "evidence", `${args2.task_id}.json`);
|
|
125860
|
+
fs118.mkdirSync(path160.dirname(evidencePath), { recursive: true });
|
|
125861
|
+
const fd = fs118.openSync(evidencePath, "wx");
|
|
125740
125862
|
let writeOk = false;
|
|
125741
125863
|
try {
|
|
125742
|
-
|
|
125864
|
+
fs118.writeSync(fd, JSON.stringify({
|
|
125743
125865
|
taskId: args2.task_id,
|
|
125744
125866
|
required_gates: [],
|
|
125745
125867
|
gates: {}
|
|
125746
125868
|
}, null, 2));
|
|
125747
125869
|
writeOk = true;
|
|
125748
125870
|
} finally {
|
|
125749
|
-
|
|
125871
|
+
fs118.closeSync(fd);
|
|
125750
125872
|
if (!writeOk) {
|
|
125751
125873
|
try {
|
|
125752
|
-
|
|
125874
|
+
fs118.unlinkSync(evidencePath);
|
|
125753
125875
|
} catch {}
|
|
125754
125876
|
}
|
|
125755
125877
|
}
|
|
@@ -125759,8 +125881,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir, ctx) {
|
|
|
125759
125881
|
recoverTaskStateFromDelegations(args2.task_id, directory);
|
|
125760
125882
|
let phaseRequiresReviewer = true;
|
|
125761
125883
|
try {
|
|
125762
|
-
const
|
|
125763
|
-
const planRaw =
|
|
125884
|
+
const planPath2 = path160.join(directory, ".swarm", "plan.json");
|
|
125885
|
+
const planRaw = fs118.readFileSync(planPath2, "utf-8");
|
|
125764
125886
|
const plan = JSON.parse(planRaw);
|
|
125765
125887
|
const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
|
|
125766
125888
|
if (taskPhase?.required_agents && !taskPhase.required_agents.includes("reviewer")) {
|
|
@@ -125987,7 +126109,7 @@ init_utils2();
|
|
|
125987
126109
|
init_redaction();
|
|
125988
126110
|
import { createHash as createHash12 } from "node:crypto";
|
|
125989
126111
|
import { appendFile as appendFile14, mkdir as mkdir25 } from "node:fs/promises";
|
|
125990
|
-
import * as
|
|
126112
|
+
import * as path161 from "node:path";
|
|
125991
126113
|
var EVIDENCE_CACHE_FILE = "evidence-cache/documents.jsonl";
|
|
125992
126114
|
var MAX_EVIDENCE_TEXT_LENGTH = 4000;
|
|
125993
126115
|
async function writeEvidenceDocuments(directory, inputs, now = () => new Date) {
|
|
@@ -125995,7 +126117,7 @@ async function writeEvidenceDocuments(directory, inputs, now = () => new Date) {
|
|
|
125995
126117
|
const capturedAt = now().toISOString();
|
|
125996
126118
|
const records = inputs.map((input) => createEvidenceDocumentRecord(input, capturedAt)).filter((record3) => record3 !== null);
|
|
125997
126119
|
if (records.length > 0) {
|
|
125998
|
-
await mkdir25(
|
|
126120
|
+
await mkdir25(path161.dirname(filePath), { recursive: true });
|
|
125999
126121
|
await appendFile14(filePath, `${records.map((record3) => JSON.stringify(record3)).join(`
|
|
126000
126122
|
`)}
|
|
126001
126123
|
`, "utf-8");
|
|
@@ -126188,8 +126310,8 @@ init_utils2();
|
|
|
126188
126310
|
init_ledger();
|
|
126189
126311
|
init_manager();
|
|
126190
126312
|
init_create_tool();
|
|
126191
|
-
import
|
|
126192
|
-
import
|
|
126313
|
+
import fs119 from "node:fs";
|
|
126314
|
+
import path162 from "node:path";
|
|
126193
126315
|
function normalizeVerdict(verdict) {
|
|
126194
126316
|
switch (verdict) {
|
|
126195
126317
|
case "APPROVED":
|
|
@@ -126237,7 +126359,7 @@ async function executeWriteDriftEvidence(args2, directory) {
|
|
|
126237
126359
|
entries: [evidenceEntry]
|
|
126238
126360
|
};
|
|
126239
126361
|
const filename = "drift-verifier.json";
|
|
126240
|
-
const relativePath =
|
|
126362
|
+
const relativePath = path162.join("evidence", String(phase), filename);
|
|
126241
126363
|
let validatedPath;
|
|
126242
126364
|
try {
|
|
126243
126365
|
validatedPath = validateSwarmPath(directory, relativePath);
|
|
@@ -126248,12 +126370,12 @@ async function executeWriteDriftEvidence(args2, directory) {
|
|
|
126248
126370
|
message: error93 instanceof Error ? error93.message : "Failed to validate path"
|
|
126249
126371
|
}, null, 2);
|
|
126250
126372
|
}
|
|
126251
|
-
const evidenceDir =
|
|
126373
|
+
const evidenceDir = path162.dirname(validatedPath);
|
|
126252
126374
|
try {
|
|
126253
|
-
await
|
|
126254
|
-
const tempPath =
|
|
126255
|
-
await
|
|
126256
|
-
await
|
|
126375
|
+
await fs119.promises.mkdir(evidenceDir, { recursive: true });
|
|
126376
|
+
const tempPath = path162.join(evidenceDir, `.${filename}.tmp`);
|
|
126377
|
+
await fs119.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
|
|
126378
|
+
await fs119.promises.rename(tempPath, validatedPath);
|
|
126257
126379
|
let snapshotInfo;
|
|
126258
126380
|
let snapshotError;
|
|
126259
126381
|
let qaProfileLocked;
|
|
@@ -126346,8 +126468,8 @@ var write_drift_evidence = createSwarmTool({
|
|
|
126346
126468
|
// src/tools/write-final-council-evidence.ts
|
|
126347
126469
|
init_zod();
|
|
126348
126470
|
init_loader();
|
|
126349
|
-
import
|
|
126350
|
-
import
|
|
126471
|
+
import fs120 from "node:fs";
|
|
126472
|
+
import path163 from "node:path";
|
|
126351
126473
|
init_utils2();
|
|
126352
126474
|
init_manager();
|
|
126353
126475
|
init_create_tool();
|
|
@@ -126435,7 +126557,7 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
|
|
|
126435
126557
|
timestamp: synthesis.timestamp
|
|
126436
126558
|
};
|
|
126437
126559
|
const filename = "final-council.json";
|
|
126438
|
-
const relativePath =
|
|
126560
|
+
const relativePath = path163.join("evidence", filename);
|
|
126439
126561
|
let validatedPath;
|
|
126440
126562
|
try {
|
|
126441
126563
|
validatedPath = validateSwarmPath(directory, relativePath);
|
|
@@ -126449,12 +126571,12 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
|
|
|
126449
126571
|
const evidenceContent = {
|
|
126450
126572
|
entries: [evidenceEntry]
|
|
126451
126573
|
};
|
|
126452
|
-
const evidenceDir =
|
|
126574
|
+
const evidenceDir = path163.dirname(validatedPath);
|
|
126453
126575
|
try {
|
|
126454
|
-
await
|
|
126455
|
-
const tempPath =
|
|
126456
|
-
await
|
|
126457
|
-
await
|
|
126576
|
+
await fs120.promises.mkdir(evidenceDir, { recursive: true });
|
|
126577
|
+
const tempPath = path163.join(evidenceDir, `.${filename}.tmp`);
|
|
126578
|
+
await fs120.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
|
|
126579
|
+
await fs120.promises.rename(tempPath, validatedPath);
|
|
126458
126580
|
return JSON.stringify({
|
|
126459
126581
|
success: true,
|
|
126460
126582
|
phase: input.phase,
|
|
@@ -126510,8 +126632,8 @@ var write_final_council_evidence = createSwarmTool({
|
|
|
126510
126632
|
init_zod();
|
|
126511
126633
|
init_utils2();
|
|
126512
126634
|
init_create_tool();
|
|
126513
|
-
import
|
|
126514
|
-
import
|
|
126635
|
+
import fs121 from "node:fs";
|
|
126636
|
+
import path164 from "node:path";
|
|
126515
126637
|
function normalizeVerdict2(verdict) {
|
|
126516
126638
|
switch (verdict) {
|
|
126517
126639
|
case "APPROVED":
|
|
@@ -126559,7 +126681,7 @@ async function executeWriteHallucinationEvidence(args2, directory) {
|
|
|
126559
126681
|
entries: [evidenceEntry]
|
|
126560
126682
|
};
|
|
126561
126683
|
const filename = "hallucination-guard.json";
|
|
126562
|
-
const relativePath =
|
|
126684
|
+
const relativePath = path164.join("evidence", String(phase), filename);
|
|
126563
126685
|
let validatedPath;
|
|
126564
126686
|
try {
|
|
126565
126687
|
validatedPath = validateSwarmPath(directory, relativePath);
|
|
@@ -126570,12 +126692,12 @@ async function executeWriteHallucinationEvidence(args2, directory) {
|
|
|
126570
126692
|
message: error93 instanceof Error ? error93.message : "Failed to validate path"
|
|
126571
126693
|
}, null, 2);
|
|
126572
126694
|
}
|
|
126573
|
-
const evidenceDir =
|
|
126695
|
+
const evidenceDir = path164.dirname(validatedPath);
|
|
126574
126696
|
try {
|
|
126575
|
-
await
|
|
126576
|
-
const tempPath =
|
|
126577
|
-
await
|
|
126578
|
-
await
|
|
126697
|
+
await fs121.promises.mkdir(evidenceDir, { recursive: true });
|
|
126698
|
+
const tempPath = path164.join(evidenceDir, `.${filename}.tmp`);
|
|
126699
|
+
await fs121.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
|
|
126700
|
+
await fs121.promises.rename(tempPath, validatedPath);
|
|
126579
126701
|
return JSON.stringify({
|
|
126580
126702
|
success: true,
|
|
126581
126703
|
phase,
|
|
@@ -126621,8 +126743,8 @@ var write_hallucination_evidence = createSwarmTool({
|
|
|
126621
126743
|
init_zod();
|
|
126622
126744
|
init_utils2();
|
|
126623
126745
|
init_create_tool();
|
|
126624
|
-
import
|
|
126625
|
-
import
|
|
126746
|
+
import fs122 from "node:fs";
|
|
126747
|
+
import path165 from "node:path";
|
|
126626
126748
|
function normalizeVerdict3(verdict) {
|
|
126627
126749
|
switch (verdict) {
|
|
126628
126750
|
case "PASS":
|
|
@@ -126696,7 +126818,7 @@ async function executeWriteMutationEvidence(args2, directory) {
|
|
|
126696
126818
|
entries: [evidenceEntry]
|
|
126697
126819
|
};
|
|
126698
126820
|
const filename = "mutation-gate.json";
|
|
126699
|
-
const relativePath =
|
|
126821
|
+
const relativePath = path165.join("evidence", String(phase), filename);
|
|
126700
126822
|
let validatedPath;
|
|
126701
126823
|
try {
|
|
126702
126824
|
validatedPath = validateSwarmPath(directory, relativePath);
|
|
@@ -126707,12 +126829,12 @@ async function executeWriteMutationEvidence(args2, directory) {
|
|
|
126707
126829
|
message: error93 instanceof Error ? error93.message : "Failed to validate path"
|
|
126708
126830
|
}, null, 2);
|
|
126709
126831
|
}
|
|
126710
|
-
const evidenceDir =
|
|
126832
|
+
const evidenceDir = path165.dirname(validatedPath);
|
|
126711
126833
|
try {
|
|
126712
|
-
await
|
|
126713
|
-
const tempPath =
|
|
126714
|
-
await
|
|
126715
|
-
await
|
|
126834
|
+
await fs122.promises.mkdir(evidenceDir, { recursive: true });
|
|
126835
|
+
const tempPath = path165.join(evidenceDir, `.${filename}.tmp`);
|
|
126836
|
+
await fs122.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
|
|
126837
|
+
await fs122.promises.rename(tempPath, validatedPath);
|
|
126716
126838
|
return JSON.stringify({
|
|
126717
126839
|
success: true,
|
|
126718
126840
|
phase,
|
|
@@ -127060,7 +127182,7 @@ async function initializeOpenCodeSwarm(ctx) {
|
|
|
127060
127182
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
127061
127183
|
preflightTriggerManager = new PTM(automationConfig);
|
|
127062
127184
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
127063
|
-
const swarmDir =
|
|
127185
|
+
const swarmDir = path167.resolve(ctx.directory, ".swarm");
|
|
127064
127186
|
statusArtifact = new ASA(swarmDir);
|
|
127065
127187
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
127066
127188
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|
|
@@ -127657,7 +127779,7 @@ ${promptRaw}`;
|
|
|
127657
127779
|
const meta3 = readSkillMetadata(s.skillPath, ctx.directory);
|
|
127658
127780
|
let desc = meta3.description || "";
|
|
127659
127781
|
if (!desc || desc === "No description provided") {
|
|
127660
|
-
desc =
|
|
127782
|
+
desc = path167.basename(path167.dirname(s.skillPath));
|
|
127661
127783
|
}
|
|
127662
127784
|
desc = desc.replace(/,/g, ";");
|
|
127663
127785
|
return `file:${s.skillPath} (-- ${desc})`;
|
|
@@ -127667,7 +127789,7 @@ ${promptRaw}`;
|
|
|
127667
127789
|
|
|
127668
127790
|
${promptRaw}`;
|
|
127669
127791
|
argsRecord.prompt = newPrompt;
|
|
127670
|
-
const skillNames = topSkills.map((s) => `${
|
|
127792
|
+
const skillNames = topSkills.map((s) => `${path167.basename(s.skillPath)} (score: ${s.score.toFixed(2)})`).join(", ");
|
|
127671
127793
|
console.warn(`[skill-propagation-gate] Injected skills: ${skillNames}`);
|
|
127672
127794
|
for (const skill of topSkills) {
|
|
127673
127795
|
try {
|