opencode-swarm 6.73.1 → 6.74.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +17 -13
- package/dist/config/evidence-schema.d.ts +115 -0
- package/dist/index.js +706 -253
- package/dist/tools/pre-check-batch.d.ts +8 -0
- package/dist/tools/sast-baseline.d.ts +126 -0
- package/dist/tools/sast-scan.d.ts +27 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14365,7 +14365,7 @@ var init_zod = __esm(() => {
|
|
|
14365
14365
|
});
|
|
14366
14366
|
|
|
14367
14367
|
// src/config/evidence-schema.ts
|
|
14368
|
-
var EVIDENCE_MAX_JSON_BYTES, EVIDENCE_MAX_PATCH_BYTES, EVIDENCE_MAX_TASK_BYTES, EvidenceTypeSchema, EvidenceVerdictSchema, BaseEvidenceSchema, ReviewEvidenceSchema, TestEvidenceSchema, DiffEvidenceSchema, ApprovalEvidenceSchema, NoteEvidenceSchema, RetrospectiveEvidenceSchema, SyntaxEvidenceSchema, PlaceholderEvidenceSchema, SastEvidenceSchema, SbomEvidenceSchema, BuildEvidenceSchema, QualityBudgetEvidenceSchema, SecretscanEvidenceSchema, EvidenceSchema, EvidenceBundleSchema;
|
|
14368
|
+
var EVIDENCE_MAX_JSON_BYTES, EVIDENCE_MAX_PATCH_BYTES, EVIDENCE_MAX_TASK_BYTES, EvidenceTypeSchema, EvidenceVerdictSchema, BaseEvidenceSchema, ReviewEvidenceSchema, TestEvidenceSchema, DiffEvidenceSchema, ApprovalEvidenceSchema, NoteEvidenceSchema, RetrospectiveEvidenceSchema, SyntaxEvidenceSchema, PlaceholderEvidenceSchema, SastFindingSchema, SastEvidenceSchema, SbomEvidenceSchema, BuildEvidenceSchema, QualityBudgetEvidenceSchema, SecretscanEvidenceSchema, EvidenceSchema, EvidenceBundleSchema;
|
|
14369
14369
|
var init_evidence_schema = __esm(() => {
|
|
14370
14370
|
init_zod();
|
|
14371
14371
|
EVIDENCE_MAX_JSON_BYTES = 500 * 1024;
|
|
@@ -14504,19 +14504,20 @@ var init_evidence_schema = __esm(() => {
|
|
|
14504
14504
|
files_with_findings: exports_external.number().int(),
|
|
14505
14505
|
findings_count: exports_external.number().int()
|
|
14506
14506
|
});
|
|
14507
|
+
SastFindingSchema = exports_external.object({
|
|
14508
|
+
rule_id: exports_external.string(),
|
|
14509
|
+
severity: exports_external.enum(["critical", "high", "medium", "low"]),
|
|
14510
|
+
message: exports_external.string(),
|
|
14511
|
+
location: exports_external.object({
|
|
14512
|
+
file: exports_external.string(),
|
|
14513
|
+
line: exports_external.number().int(),
|
|
14514
|
+
column: exports_external.number().int().optional()
|
|
14515
|
+
}),
|
|
14516
|
+
remediation: exports_external.string().optional()
|
|
14517
|
+
});
|
|
14507
14518
|
SastEvidenceSchema = BaseEvidenceSchema.extend({
|
|
14508
14519
|
type: exports_external.literal("sast"),
|
|
14509
|
-
findings: exports_external.array(
|
|
14510
|
-
rule_id: exports_external.string(),
|
|
14511
|
-
severity: exports_external.enum(["critical", "high", "medium", "low"]),
|
|
14512
|
-
message: exports_external.string(),
|
|
14513
|
-
location: exports_external.object({
|
|
14514
|
-
file: exports_external.string(),
|
|
14515
|
-
line: exports_external.number().int(),
|
|
14516
|
-
column: exports_external.number().int().optional()
|
|
14517
|
-
}),
|
|
14518
|
-
remediation: exports_external.string().optional()
|
|
14519
|
-
})).default([]),
|
|
14520
|
+
findings: exports_external.array(SastFindingSchema).default([]),
|
|
14520
14521
|
engine: exports_external.enum(["tier_a", "tier_a+tier_b"]),
|
|
14521
14522
|
files_scanned: exports_external.number().int(),
|
|
14522
14523
|
findings_count: exports_external.number().int(),
|
|
@@ -14525,7 +14526,10 @@ var init_evidence_schema = __esm(() => {
|
|
|
14525
14526
|
high: exports_external.number().int(),
|
|
14526
14527
|
medium: exports_external.number().int(),
|
|
14527
14528
|
low: exports_external.number().int()
|
|
14528
|
-
})
|
|
14529
|
+
}),
|
|
14530
|
+
new_findings: exports_external.array(SastFindingSchema).optional(),
|
|
14531
|
+
pre_existing_findings: exports_external.array(SastFindingSchema).optional(),
|
|
14532
|
+
baseline_used: exports_external.boolean().optional()
|
|
14529
14533
|
});
|
|
14530
14534
|
SbomEvidenceSchema = BaseEvidenceSchema.extend({
|
|
14531
14535
|
type: exports_external.literal("sbom"),
|
|
@@ -54529,6 +54533,9 @@ All other gates: failure \u2192 return to coder. No self-fixes. No workarounds.
|
|
|
54529
54533
|
5a-bis. **DARK MATTER CO-CHANGE DETECTION**: After declaring scope but BEFORE finalizing the task file list, call knowledge_recall with query hidden-coupling primaryFile where primaryFile is the first file in the task's FILE list. Extract primaryFile from the task's FILE list (first file = primary). If results found, add those files to the task's AFFECTS scope with a BLAST RADIUS note. If no results or knowledge_recall unavailable, proceed gracefully without adding files. This is advisory \u2014 the architect may exclude files from scope if they are unrelated to the current task. Delegate to {{AGENT_PREFIX}}coder only after scope is declared.
|
|
54530
54534
|
|
|
54531
54535
|
5b-PRE (required): Call \`declare_scope({ taskId, files })\` with the EXACT file list for this task \u2014 including any co-change files surfaced by 5a-bis. Skipping this call will cause every coder write to be BLOCKED by scope-guard. No \`declare_scope\` \u2192 no 5b delegation. See Rule 1a.
|
|
54536
|
+
5b-BASE (required, once per task): Call \`sast_scan\` with \`{ capture_baseline: true, phase: <N>, changed_files: <files from 5b-PRE> }\` where \`<N>\` is the current phase number (extract from current task ID: task "3.2" \u2192 phase 3, task "1.5" \u2192 phase 1). The tool maintains \`.swarm/evidence/{phase}/sast-baseline.json\` as a phase-scoped, incrementally merged baseline of pre-existing SAST findings. Calling twice for the same files is safe (idempotent merge). Do NOT re-capture mid-task.
|
|
54537
|
+
\u2192 REQUIRED: Print "sast-baseline: [WRITTEN \u2014 N fingerprints | MERGED \u2014 N fingerprints | SKIPPED \u2014 gate disabled | ERROR \u2014 details]"
|
|
54538
|
+
\u2192 Subsequent \`pre_check_batch\` calls with \`phase: <N>\` will automatically diff against this baseline \u2014 only NEW findings (not in baseline) drive the fail verdict.
|
|
54532
54539
|
5b. {{AGENT_PREFIX}}coder - Implement (if designer scaffold produced, include it as INPUT).
|
|
54533
54540
|
5c. Run \`diff\` tool. If \`hasContractChanges\` \u2192 {{AGENT_PREFIX}}explorer integration analysis. If COMPATIBILITY SIGNALS=INCOMPATIBLE or MIGRATION_SURFACE=yes \u2192 coder retry. If COMPATIBILITY SIGNALS=COMPATIBLE and MIGRATION_SURFACE=no \u2192 proceed.
|
|
54534
54541
|
\u2192 REQUIRED: Print "diff: [PASS | CONTRACT CHANGE \u2014 details]"
|
|
@@ -54542,18 +54549,19 @@ All other gates: failure \u2192 return to coder. No self-fixes. No workarounds.
|
|
|
54542
54549
|
\u2192 REQUIRED: Print "lint: [PASS | FAIL \u2014 details]"
|
|
54543
54550
|
5h. Run \`build_check\` tool. BUILD FAILS \u2192 return to coder. SUCCESS \u2192 proceed to pre_check_batch.
|
|
54544
54551
|
\u2192 REQUIRED: Print "buildcheck: [PASS | FAIL | SKIPPED \u2014 no toolchain]"
|
|
54545
|
-
5i. Run \`pre_check_batch\` tool \u2192 runs four verification tools in parallel (max 4 concurrent):
|
|
54552
|
+
5i. Run \`pre_check_batch\` tool with \`phase: <N>\` (same phase number used in 5b-BASE) \u2192 runs four verification tools in parallel (max 4 concurrent):
|
|
54546
54553
|
- lint:check (code quality verification)
|
|
54547
54554
|
- secretscan (secret detection)
|
|
54548
|
-
- sast_scan (static security analysis)
|
|
54555
|
+
- sast_scan (static security analysis \u2014 diffs against phase baseline when phase provided)
|
|
54549
54556
|
- quality_budget (maintainability metrics)
|
|
54550
54557
|
\u2192 Returns { gates_passed, lint, secretscan, sast_scan, quality_budget, total_duration_ms }
|
|
54558
|
+
\u2192 sast_scan result may include { new_findings, pre_existing_findings, baseline_used } when baseline diff is active.
|
|
54551
54559
|
\u2192 If ALL FOUR tools have ran === false (lint.ran === false && secretscan.ran === false && sast_scan.ran === false && quality_budget.ran === false):
|
|
54552
54560
|
\u2192 This is a SKIP - no tools actually ran. Print "pre_check_batch: SKIP \u2014 all tools ran===false (no files to check or tools not available)" and proceed to {{AGENT_PREFIX}}reviewer.
|
|
54553
54561
|
\u2192 Else if gates_passed === false: read individual tool results, identify which tool(s) failed, return structured rejection to {{AGENT_PREFIX}}coder with specific tool failures. Do NOT call {{AGENT_PREFIX}}reviewer.
|
|
54554
|
-
\u2192 If gates_passed === true AND sast_preexisting_findings is present: proceed to {{AGENT_PREFIX}}reviewer. Include the pre-existing SAST findings in the reviewer delegation context with instruction: "SAST TRIAGE REQUIRED: The following
|
|
54562
|
+
\u2192 If gates_passed === true AND sast_preexisting_findings is present: proceed to {{AGENT_PREFIX}}reviewer. Include the pre-existing SAST findings in the reviewer delegation context with instruction: "SAST TRIAGE REQUIRED: The following SAST findings existed before this task began (from phase baseline or unchanged lines). Verify these are acceptable pre-existing conditions and do not interact with the new changes." Do NOT return to coder for pre-existing findings.
|
|
54555
54563
|
\u2192 If gates_passed === true (no sast_preexisting_findings): proceed to {{AGENT_PREFIX}}reviewer.
|
|
54556
|
-
\u2192 REQUIRED: Print "pre_check_batch: [PASS \u2014 all gates passed | PASS \u2014 pre-existing SAST findings
|
|
54564
|
+
\u2192 REQUIRED: Print "pre_check_batch: [PASS \u2014 all gates passed | PASS \u2014 pre-existing SAST findings (N findings, reviewer triage) | FAIL \u2014 [gate]: [details]]"
|
|
54557
54565
|
|
|
54558
54566
|
\u26A0\uFE0F pre_check_batch SCOPE BOUNDARY:
|
|
54559
54567
|
pre_check_batch runs FOUR automated tools: lint:check, secretscan, sast_scan, quality_budget.
|
|
@@ -61583,7 +61591,7 @@ var init_runtime = __esm(() => {
|
|
|
61583
61591
|
|
|
61584
61592
|
// src/index.ts
|
|
61585
61593
|
init_agents();
|
|
61586
|
-
import * as
|
|
61594
|
+
import * as path96 from "path";
|
|
61587
61595
|
|
|
61588
61596
|
// src/background/index.ts
|
|
61589
61597
|
init_event_bus();
|
|
@@ -64037,7 +64045,8 @@ function validateGraphNode(node) {
|
|
|
64037
64045
|
throw new Error("Invalid node: exports must be an array of strings");
|
|
64038
64046
|
}
|
|
64039
64047
|
if (containsControlChars(exp)) {
|
|
64040
|
-
|
|
64048
|
+
const preview = exp.slice(0, 120);
|
|
64049
|
+
throw new Error(`Invalid node: exports contains control characters (file=${node.filePath}, value="${preview}")`);
|
|
64041
64050
|
}
|
|
64042
64051
|
}
|
|
64043
64052
|
if (!Array.isArray(node.imports)) {
|
|
@@ -64048,7 +64057,8 @@ function validateGraphNode(node) {
|
|
|
64048
64057
|
throw new Error("Invalid node: imports must be an array of strings");
|
|
64049
64058
|
}
|
|
64050
64059
|
if (containsControlChars(imp)) {
|
|
64051
|
-
|
|
64060
|
+
const preview = imp.slice(0, 120);
|
|
64061
|
+
throw new Error(`Invalid node: imports contains control characters (file=${node.filePath}, value="${preview}")`);
|
|
64052
64062
|
}
|
|
64053
64063
|
}
|
|
64054
64064
|
}
|
|
@@ -64405,11 +64415,13 @@ var EXTENSION_TO_LANGUAGE = {
|
|
|
64405
64415
|
};
|
|
64406
64416
|
function parseFileImports(content) {
|
|
64407
64417
|
const imports = [];
|
|
64408
|
-
const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"
|
|
64418
|
+
const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`\0\t\r\n]+)['"`]|import\s+['"`]([^'"`\0\t\r\n]+)['"`]|require\s*\(\s*['"`]([^'"`\0\t\r\n]+)['"`]\s*\)|export\s*\{[^}]*\}\s*from\s+['"`]([^'"`\0\t\r\n]+)['"`]|export\s+\*(?:\s+as\s+\w+)?\s+from\s+['"`]([^'"`\0\t\r\n]+)['"`]|import\s*\(\s*['"`]([^'"`\0\t\r\n]+)['"`]\s*\)/g;
|
|
64409
64419
|
for (const match of content.matchAll(importRegex)) {
|
|
64410
64420
|
const modulePath = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
|
|
64411
64421
|
if (!modulePath)
|
|
64412
64422
|
continue;
|
|
64423
|
+
if (containsControlChars(modulePath))
|
|
64424
|
+
continue;
|
|
64413
64425
|
const matchedString = match[0];
|
|
64414
64426
|
let importType = "named";
|
|
64415
64427
|
if (matchedString.includes("* as")) {
|
|
@@ -76602,8 +76614,8 @@ var placeholder_scan = createSwarmTool({
|
|
|
76602
76614
|
});
|
|
76603
76615
|
// src/tools/pre-check-batch.ts
|
|
76604
76616
|
init_dist();
|
|
76605
|
-
import * as
|
|
76606
|
-
import * as
|
|
76617
|
+
import * as fs65 from "fs";
|
|
76618
|
+
import * as path79 from "path";
|
|
76607
76619
|
init_manager2();
|
|
76608
76620
|
init_utils();
|
|
76609
76621
|
init_create_tool();
|
|
@@ -76738,8 +76750,8 @@ var quality_budget = createSwarmTool({
|
|
|
76738
76750
|
init_dist();
|
|
76739
76751
|
init_manager2();
|
|
76740
76752
|
init_detector();
|
|
76741
|
-
import * as
|
|
76742
|
-
import * as
|
|
76753
|
+
import * as fs64 from "fs";
|
|
76754
|
+
import * as path78 from "path";
|
|
76743
76755
|
import { extname as extname18 } from "path";
|
|
76744
76756
|
|
|
76745
76757
|
// src/sast/rules/c.ts
|
|
@@ -77628,6 +77640,303 @@ async function runSemgrep(options) {
|
|
|
77628
77640
|
// src/tools/sast-scan.ts
|
|
77629
77641
|
init_utils();
|
|
77630
77642
|
init_create_tool();
|
|
77643
|
+
|
|
77644
|
+
// src/tools/sast-baseline.ts
|
|
77645
|
+
init_utils2();
|
|
77646
|
+
import * as crypto8 from "crypto";
|
|
77647
|
+
import * as fs63 from "fs";
|
|
77648
|
+
import * as path77 from "path";
|
|
77649
|
+
var BASELINE_SCHEMA_VERSION = "1.0.0";
|
|
77650
|
+
var MAX_BASELINE_FINDINGS = 2000;
|
|
77651
|
+
var MAX_BASELINE_BYTES = 2 * 1048576;
|
|
77652
|
+
var LOCK_RETRY_DELAYS_MS = [50, 100, 200, 400, 800];
|
|
77653
|
+
function normalizeFindingPath(directory, file3) {
|
|
77654
|
+
const resolved = path77.isAbsolute(file3) ? file3 : path77.resolve(directory, file3);
|
|
77655
|
+
const rel = path77.relative(path77.resolve(directory), resolved);
|
|
77656
|
+
return rel.replace(/\\/g, "/");
|
|
77657
|
+
}
|
|
77658
|
+
function baselineRelPath(phase) {
|
|
77659
|
+
return path77.join("evidence", String(phase), "sast-baseline.json");
|
|
77660
|
+
}
|
|
77661
|
+
function tempRelPath(phase) {
|
|
77662
|
+
return path77.join("evidence", String(phase), `sast-baseline.json.tmp.${Date.now()}.${process.pid}`);
|
|
77663
|
+
}
|
|
77664
|
+
function lockRelPath(phase) {
|
|
77665
|
+
return path77.join("evidence", String(phase), "sast-baseline.json.lock");
|
|
77666
|
+
}
|
|
77667
|
+
function getLine(lines, idx) {
|
|
77668
|
+
if (idx < 0 || idx >= lines.length)
|
|
77669
|
+
return "";
|
|
77670
|
+
return (lines[idx] ?? "").trim();
|
|
77671
|
+
}
|
|
77672
|
+
function fingerprintFinding(finding, directory, occurrenceIndex) {
|
|
77673
|
+
const relFile = normalizeFindingPath(directory, finding.location.file);
|
|
77674
|
+
if (relFile.startsWith("..")) {
|
|
77675
|
+
return {
|
|
77676
|
+
fingerprint: `${relFile}|${finding.rule_id}|L${finding.location.line}|UNSTABLE|#${occurrenceIndex}`,
|
|
77677
|
+
stable: false
|
|
77678
|
+
};
|
|
77679
|
+
}
|
|
77680
|
+
const lineNum = finding.location.line;
|
|
77681
|
+
try {
|
|
77682
|
+
const content = fs63.readFileSync(finding.location.file, "utf-8");
|
|
77683
|
+
const lines = content.split(`
|
|
77684
|
+
`);
|
|
77685
|
+
const idx = lineNum - 1;
|
|
77686
|
+
const window2 = [
|
|
77687
|
+
getLine(lines, idx - 1),
|
|
77688
|
+
getLine(lines, idx),
|
|
77689
|
+
getLine(lines, idx + 1)
|
|
77690
|
+
].join(`
|
|
77691
|
+
`);
|
|
77692
|
+
const hash3 = crypto8.createHash("sha256").update(window2).digest("hex").slice(0, 16);
|
|
77693
|
+
return {
|
|
77694
|
+
fingerprint: `${relFile}|${finding.rule_id}|${hash3}|#${occurrenceIndex}`,
|
|
77695
|
+
stable: true
|
|
77696
|
+
};
|
|
77697
|
+
} catch {
|
|
77698
|
+
return {
|
|
77699
|
+
fingerprint: `${relFile}|${finding.rule_id}|L${lineNum}|UNSTABLE|#${occurrenceIndex}`,
|
|
77700
|
+
stable: false
|
|
77701
|
+
};
|
|
77702
|
+
}
|
|
77703
|
+
}
|
|
77704
|
+
function assignOccurrenceIndices(findings, directory) {
|
|
77705
|
+
const countMap = new Map;
|
|
77706
|
+
return findings.map((finding) => {
|
|
77707
|
+
const relFile = normalizeFindingPath(directory, finding.location.file);
|
|
77708
|
+
const lineNum = finding.location.line;
|
|
77709
|
+
let baseKey;
|
|
77710
|
+
try {
|
|
77711
|
+
if (relFile.startsWith(".."))
|
|
77712
|
+
throw new Error("escapes workspace");
|
|
77713
|
+
const content = fs63.readFileSync(finding.location.file, "utf-8");
|
|
77714
|
+
const lines = content.split(`
|
|
77715
|
+
`);
|
|
77716
|
+
const idx = lineNum - 1;
|
|
77717
|
+
const window2 = [
|
|
77718
|
+
getLine(lines, idx - 1),
|
|
77719
|
+
getLine(lines, idx),
|
|
77720
|
+
getLine(lines, idx + 1)
|
|
77721
|
+
].join(`
|
|
77722
|
+
`);
|
|
77723
|
+
const hash3 = crypto8.createHash("sha256").update(window2).digest("hex").slice(0, 16);
|
|
77724
|
+
baseKey = `${relFile}|${finding.rule_id}|${hash3}`;
|
|
77725
|
+
} catch {
|
|
77726
|
+
baseKey = `${relFile}|${finding.rule_id}|L${lineNum}|UNSTABLE`;
|
|
77727
|
+
}
|
|
77728
|
+
const occIdx = countMap.get(baseKey) ?? 0;
|
|
77729
|
+
countMap.set(baseKey, occIdx + 1);
|
|
77730
|
+
const fp = fingerprintFinding(finding, directory, occIdx);
|
|
77731
|
+
return {
|
|
77732
|
+
finding,
|
|
77733
|
+
index: occIdx,
|
|
77734
|
+
stable: fp.stable,
|
|
77735
|
+
fingerprint: fp.fingerprint
|
|
77736
|
+
};
|
|
77737
|
+
});
|
|
77738
|
+
}
|
|
77739
|
+
async function acquireLock(lockPath) {
|
|
77740
|
+
for (let attempt = 0;attempt <= LOCK_RETRY_DELAYS_MS.length; attempt++) {
|
|
77741
|
+
try {
|
|
77742
|
+
const fd = fs63.openSync(lockPath, "wx");
|
|
77743
|
+
fs63.closeSync(fd);
|
|
77744
|
+
return () => {
|
|
77745
|
+
try {
|
|
77746
|
+
fs63.unlinkSync(lockPath);
|
|
77747
|
+
} catch {}
|
|
77748
|
+
};
|
|
77749
|
+
} catch {
|
|
77750
|
+
if (attempt < LOCK_RETRY_DELAYS_MS.length) {
|
|
77751
|
+
await new Promise((resolve31) => setTimeout(resolve31, LOCK_RETRY_DELAYS_MS[attempt]));
|
|
77752
|
+
}
|
|
77753
|
+
}
|
|
77754
|
+
}
|
|
77755
|
+
return () => {};
|
|
77756
|
+
}
|
|
77757
|
+
function validatePhase(phase) {
|
|
77758
|
+
if (!Number.isInteger(phase) || phase < 1) {
|
|
77759
|
+
return "Invalid phase: must be a positive integer";
|
|
77760
|
+
}
|
|
77761
|
+
return null;
|
|
77762
|
+
}
|
|
77763
|
+
async function captureOrMergeBaseline(directory, phase, findings, engine, scannedFiles, opts) {
|
|
77764
|
+
const phaseError = validatePhase(phase);
|
|
77765
|
+
if (phaseError)
|
|
77766
|
+
return { status: "error", message: phaseError };
|
|
77767
|
+
if (!scannedFiles || scannedFiles.length === 0) {
|
|
77768
|
+
return {
|
|
77769
|
+
status: "error",
|
|
77770
|
+
message: "capture_baseline requires non-empty changed_files"
|
|
77771
|
+
};
|
|
77772
|
+
}
|
|
77773
|
+
let baselinePath;
|
|
77774
|
+
let tempPath;
|
|
77775
|
+
let lockPath;
|
|
77776
|
+
try {
|
|
77777
|
+
baselinePath = validateSwarmPath(directory, baselineRelPath(phase));
|
|
77778
|
+
tempPath = validateSwarmPath(directory, tempRelPath(phase));
|
|
77779
|
+
lockPath = validateSwarmPath(directory, lockRelPath(phase));
|
|
77780
|
+
} catch (e) {
|
|
77781
|
+
return {
|
|
77782
|
+
status: "error",
|
|
77783
|
+
message: e instanceof Error ? e.message : "Path validation failed"
|
|
77784
|
+
};
|
|
77785
|
+
}
|
|
77786
|
+
fs63.mkdirSync(path77.dirname(baselinePath), { recursive: true });
|
|
77787
|
+
const releaseLock = await acquireLock(lockPath);
|
|
77788
|
+
try {
|
|
77789
|
+
let existing = null;
|
|
77790
|
+
try {
|
|
77791
|
+
const raw = fs63.readFileSync(baselinePath, "utf-8");
|
|
77792
|
+
const parsed = JSON.parse(raw);
|
|
77793
|
+
if (parsed.schema_version === BASELINE_SCHEMA_VERSION) {
|
|
77794
|
+
existing = parsed;
|
|
77795
|
+
}
|
|
77796
|
+
} catch {}
|
|
77797
|
+
const scannedRelFiles = new Set(scannedFiles.map((f) => normalizeFindingPath(directory, f)));
|
|
77798
|
+
const indexed = assignOccurrenceIndices(findings, directory);
|
|
77799
|
+
if (existing && !opts?.force) {
|
|
77800
|
+
const prunedFingerprints = existing.fingerprints.filter((fp) => {
|
|
77801
|
+
const relFile = fp.slice(0, fp.indexOf("|"));
|
|
77802
|
+
return !scannedRelFiles.has(relFile);
|
|
77803
|
+
});
|
|
77804
|
+
const prunedSnapshot = existing.findings_snapshot.filter((f) => {
|
|
77805
|
+
return !scannedRelFiles.has(normalizeFindingPath(directory, f.location.file));
|
|
77806
|
+
});
|
|
77807
|
+
const prunedFilesIndexed = existing.files_indexed.filter((f) => !scannedRelFiles.has(f));
|
|
77808
|
+
const mergedFingerprints = [
|
|
77809
|
+
...prunedFingerprints,
|
|
77810
|
+
...indexed.map((i2) => i2.fingerprint)
|
|
77811
|
+
];
|
|
77812
|
+
const mergedSnapshot = [
|
|
77813
|
+
...prunedSnapshot,
|
|
77814
|
+
...indexed.map((i2) => i2.finding)
|
|
77815
|
+
];
|
|
77816
|
+
const mergedFilesIndexed = [
|
|
77817
|
+
...prunedFilesIndexed,
|
|
77818
|
+
...Array.from(scannedRelFiles)
|
|
77819
|
+
];
|
|
77820
|
+
const truncated2 = mergedSnapshot.length > MAX_BASELINE_FINDINGS;
|
|
77821
|
+
const cappedSnapshot2 = truncated2 ? mergedSnapshot.slice(-MAX_BASELINE_FINDINGS) : mergedSnapshot;
|
|
77822
|
+
const cappedFingerprints2 = truncated2 ? mergedFingerprints.slice(-MAX_BASELINE_FINDINGS) : mergedFingerprints;
|
|
77823
|
+
let cappedFilesIndexed = mergedFilesIndexed;
|
|
77824
|
+
if (truncated2) {
|
|
77825
|
+
const survivingFiles = new Set;
|
|
77826
|
+
for (const finding of cappedSnapshot2) {
|
|
77827
|
+
const relFile = normalizeFindingPath(directory, finding.location.file);
|
|
77828
|
+
survivingFiles.add(relFile);
|
|
77829
|
+
}
|
|
77830
|
+
cappedFilesIndexed = Array.from(survivingFiles);
|
|
77831
|
+
}
|
|
77832
|
+
const now2 = new Date().toISOString();
|
|
77833
|
+
const bundle2 = {
|
|
77834
|
+
schema_version: BASELINE_SCHEMA_VERSION,
|
|
77835
|
+
phase,
|
|
77836
|
+
created_at: existing.created_at,
|
|
77837
|
+
updated_at: now2,
|
|
77838
|
+
engine,
|
|
77839
|
+
files_indexed: cappedFilesIndexed,
|
|
77840
|
+
fingerprints: cappedFingerprints2,
|
|
77841
|
+
findings_snapshot: cappedSnapshot2,
|
|
77842
|
+
truncated: truncated2
|
|
77843
|
+
};
|
|
77844
|
+
const json4 = JSON.stringify(bundle2, null, 2);
|
|
77845
|
+
if (json4.length > MAX_BASELINE_BYTES) {
|
|
77846
|
+
return {
|
|
77847
|
+
status: "error",
|
|
77848
|
+
message: `Baseline would exceed size cap (${json4.length} bytes > ${MAX_BASELINE_BYTES})`
|
|
77849
|
+
};
|
|
77850
|
+
}
|
|
77851
|
+
fs63.writeFileSync(tempPath, json4, "utf-8");
|
|
77852
|
+
fs63.renameSync(tempPath, baselinePath);
|
|
77853
|
+
return {
|
|
77854
|
+
status: "merged",
|
|
77855
|
+
path: baselinePath,
|
|
77856
|
+
fingerprint_count: cappedFingerprints2.length
|
|
77857
|
+
};
|
|
77858
|
+
}
|
|
77859
|
+
const newFingerprints = indexed.map((i2) => i2.fingerprint);
|
|
77860
|
+
const newSnapshot = indexed.map((i2) => i2.finding);
|
|
77861
|
+
const truncated = newSnapshot.length > MAX_BASELINE_FINDINGS;
|
|
77862
|
+
const cappedSnapshot = truncated ? newSnapshot.slice(0, MAX_BASELINE_FINDINGS) : newSnapshot;
|
|
77863
|
+
const cappedFingerprints = truncated ? newFingerprints.slice(0, MAX_BASELINE_FINDINGS) : newFingerprints;
|
|
77864
|
+
const now = new Date().toISOString();
|
|
77865
|
+
const bundle = {
|
|
77866
|
+
schema_version: BASELINE_SCHEMA_VERSION,
|
|
77867
|
+
phase,
|
|
77868
|
+
created_at: now,
|
|
77869
|
+
updated_at: now,
|
|
77870
|
+
engine,
|
|
77871
|
+
files_indexed: Array.from(scannedRelFiles),
|
|
77872
|
+
fingerprints: cappedFingerprints,
|
|
77873
|
+
findings_snapshot: cappedSnapshot,
|
|
77874
|
+
truncated
|
|
77875
|
+
};
|
|
77876
|
+
const json3 = JSON.stringify(bundle, null, 2);
|
|
77877
|
+
if (json3.length > MAX_BASELINE_BYTES) {
|
|
77878
|
+
return {
|
|
77879
|
+
status: "error",
|
|
77880
|
+
message: `Baseline would exceed size cap (${json3.length} bytes > ${MAX_BASELINE_BYTES})`
|
|
77881
|
+
};
|
|
77882
|
+
}
|
|
77883
|
+
fs63.writeFileSync(tempPath, json3, "utf-8");
|
|
77884
|
+
fs63.renameSync(tempPath, baselinePath);
|
|
77885
|
+
return {
|
|
77886
|
+
status: "written",
|
|
77887
|
+
path: baselinePath,
|
|
77888
|
+
fingerprint_count: cappedFingerprints.length
|
|
77889
|
+
};
|
|
77890
|
+
} finally {
|
|
77891
|
+
releaseLock();
|
|
77892
|
+
}
|
|
77893
|
+
}
|
|
77894
|
+
function loadBaseline(directory, phase) {
|
|
77895
|
+
const phaseError = validatePhase(phase);
|
|
77896
|
+
if (phaseError) {
|
|
77897
|
+
return { status: "invalid_schema", errors: [phaseError] };
|
|
77898
|
+
}
|
|
77899
|
+
let baselinePath;
|
|
77900
|
+
try {
|
|
77901
|
+
baselinePath = validateSwarmPath(directory, baselineRelPath(phase));
|
|
77902
|
+
} catch (e) {
|
|
77903
|
+
return {
|
|
77904
|
+
status: "invalid_schema",
|
|
77905
|
+
errors: [e instanceof Error ? e.message : "Path validation failed"]
|
|
77906
|
+
};
|
|
77907
|
+
}
|
|
77908
|
+
try {
|
|
77909
|
+
const raw = fs63.readFileSync(baselinePath, "utf-8");
|
|
77910
|
+
const parsed = JSON.parse(raw);
|
|
77911
|
+
if (parsed.schema_version !== BASELINE_SCHEMA_VERSION) {
|
|
77912
|
+
return {
|
|
77913
|
+
status: "invalid_schema",
|
|
77914
|
+
errors: [`Unknown schema version: ${String(parsed.schema_version)}`]
|
|
77915
|
+
};
|
|
77916
|
+
}
|
|
77917
|
+
if (!Array.isArray(parsed.fingerprints)) {
|
|
77918
|
+
return {
|
|
77919
|
+
status: "invalid_schema",
|
|
77920
|
+
errors: ["Missing or invalid fingerprints array"]
|
|
77921
|
+
};
|
|
77922
|
+
}
|
|
77923
|
+
return {
|
|
77924
|
+
status: "found",
|
|
77925
|
+
fingerprints: new Set(parsed.fingerprints),
|
|
77926
|
+
bundle: parsed
|
|
77927
|
+
};
|
|
77928
|
+
} catch (e) {
|
|
77929
|
+
if (e.code === "ENOENT") {
|
|
77930
|
+
return { status: "not_found" };
|
|
77931
|
+
}
|
|
77932
|
+
return {
|
|
77933
|
+
status: "invalid_schema",
|
|
77934
|
+
errors: [e instanceof Error ? e.message : "Failed to read baseline"]
|
|
77935
|
+
};
|
|
77936
|
+
}
|
|
77937
|
+
}
|
|
77938
|
+
|
|
77939
|
+
// src/tools/sast-scan.ts
|
|
77631
77940
|
var MAX_FILE_SIZE_BYTES8 = 512 * 1024;
|
|
77632
77941
|
var MAX_FILES_SCANNED2 = 1000;
|
|
77633
77942
|
var MAX_FINDINGS2 = 100;
|
|
@@ -77639,17 +77948,17 @@ var SEVERITY_ORDER = {
|
|
|
77639
77948
|
};
|
|
77640
77949
|
function shouldSkipFile(filePath) {
|
|
77641
77950
|
try {
|
|
77642
|
-
const stats =
|
|
77951
|
+
const stats = fs64.statSync(filePath);
|
|
77643
77952
|
if (stats.size > MAX_FILE_SIZE_BYTES8) {
|
|
77644
77953
|
return { skip: true, reason: "file too large" };
|
|
77645
77954
|
}
|
|
77646
77955
|
if (stats.size === 0) {
|
|
77647
77956
|
return { skip: true, reason: "empty file" };
|
|
77648
77957
|
}
|
|
77649
|
-
const fd =
|
|
77958
|
+
const fd = fs64.openSync(filePath, "r");
|
|
77650
77959
|
const buffer = Buffer.alloc(8192);
|
|
77651
|
-
const bytesRead =
|
|
77652
|
-
|
|
77960
|
+
const bytesRead = fs64.readSync(fd, buffer, 0, 8192, 0);
|
|
77961
|
+
fs64.closeSync(fd);
|
|
77653
77962
|
if (bytesRead > 0) {
|
|
77654
77963
|
let nullCount = 0;
|
|
77655
77964
|
for (let i2 = 0;i2 < bytesRead; i2++) {
|
|
@@ -77688,7 +77997,7 @@ function countBySeverity(findings) {
|
|
|
77688
77997
|
}
|
|
77689
77998
|
function scanFileWithTierA(filePath, language) {
|
|
77690
77999
|
try {
|
|
77691
|
-
const content =
|
|
78000
|
+
const content = fs64.readFileSync(filePath, "utf-8");
|
|
77692
78001
|
const findings = executeRulesSync(filePath, content, language);
|
|
77693
78002
|
return findings.map((f) => ({
|
|
77694
78003
|
rule_id: f.rule_id,
|
|
@@ -77706,7 +78015,12 @@ function scanFileWithTierA(filePath, language) {
|
|
|
77706
78015
|
}
|
|
77707
78016
|
}
|
|
77708
78017
|
async function sastScan(input, directory, config3) {
|
|
77709
|
-
const {
|
|
78018
|
+
const {
|
|
78019
|
+
changed_files,
|
|
78020
|
+
severity_threshold = "medium",
|
|
78021
|
+
capture_baseline = false,
|
|
78022
|
+
phase
|
|
78023
|
+
} = input;
|
|
77710
78024
|
if (config3?.gates?.sast_scan?.enabled === false) {
|
|
77711
78025
|
return {
|
|
77712
78026
|
verdict: "pass",
|
|
@@ -77727,6 +78041,7 @@ async function sastScan(input, directory, config3) {
|
|
|
77727
78041
|
const allFindings = [];
|
|
77728
78042
|
let filesScanned = 0;
|
|
77729
78043
|
let _filesSkipped = 0;
|
|
78044
|
+
const scannedFilePaths = [];
|
|
77730
78045
|
const semgrepAvailable = isSemgrepAvailable();
|
|
77731
78046
|
const engine = semgrepAvailable ? "tier_a+tier_b" : "tier_a";
|
|
77732
78047
|
const filesByLanguage = new Map;
|
|
@@ -77735,13 +78050,13 @@ async function sastScan(input, directory, config3) {
|
|
|
77735
78050
|
_filesSkipped++;
|
|
77736
78051
|
continue;
|
|
77737
78052
|
}
|
|
77738
|
-
const resolvedPath =
|
|
77739
|
-
const resolvedDirectory =
|
|
77740
|
-
if (!resolvedPath.startsWith(resolvedDirectory +
|
|
78053
|
+
const resolvedPath = path78.isAbsolute(filePath) ? filePath : path78.resolve(directory, filePath);
|
|
78054
|
+
const resolvedDirectory = path78.resolve(directory);
|
|
78055
|
+
if (!resolvedPath.startsWith(resolvedDirectory + path78.sep) && resolvedPath !== resolvedDirectory) {
|
|
77741
78056
|
_filesSkipped++;
|
|
77742
78057
|
continue;
|
|
77743
78058
|
}
|
|
77744
|
-
if (!
|
|
78059
|
+
if (!fs64.existsSync(resolvedPath)) {
|
|
77745
78060
|
_filesSkipped++;
|
|
77746
78061
|
continue;
|
|
77747
78062
|
}
|
|
@@ -77776,6 +78091,7 @@ async function sastScan(input, directory, config3) {
|
|
|
77776
78091
|
}
|
|
77777
78092
|
}
|
|
77778
78093
|
filesScanned++;
|
|
78094
|
+
scannedFilePaths.push(resolvedPath);
|
|
77779
78095
|
if (filesScanned >= MAX_FILES_SCANNED2) {
|
|
77780
78096
|
warn(`SAST Scan: Reached maximum files limit (${MAX_FILES_SCANNED2}), stopping`);
|
|
77781
78097
|
break;
|
|
@@ -77825,14 +78141,97 @@ async function sastScan(input, directory, config3) {
|
|
|
77825
78141
|
warn(`SAST Scan: Semgrep failed, falling back to Tier A: ${error93}`);
|
|
77826
78142
|
}
|
|
77827
78143
|
}
|
|
77828
|
-
|
|
77829
|
-
|
|
77830
|
-
|
|
77831
|
-
|
|
78144
|
+
if (capture_baseline) {
|
|
78145
|
+
if (phase === undefined || !Number.isInteger(phase) || phase < 1) {
|
|
78146
|
+
const errorResult = {
|
|
78147
|
+
verdict: "fail",
|
|
78148
|
+
findings: allFindings.slice(0, MAX_FINDINGS2),
|
|
78149
|
+
summary: {
|
|
78150
|
+
engine,
|
|
78151
|
+
files_scanned: filesScanned,
|
|
78152
|
+
findings_count: Math.min(allFindings.length, MAX_FINDINGS2),
|
|
78153
|
+
findings_by_severity: countBySeverity(allFindings.slice(0, MAX_FINDINGS2))
|
|
78154
|
+
}
|
|
78155
|
+
};
|
|
78156
|
+
return errorResult;
|
|
78157
|
+
}
|
|
78158
|
+
const captureFindings = allFindings.slice(0, MAX_BASELINE_FINDINGS);
|
|
78159
|
+
const captureResult = await captureOrMergeBaseline(directory, phase, captureFindings, engine, scannedFilePaths);
|
|
78160
|
+
const captureStatus = captureResult.status === "written" ? "baseline_captured" : captureResult.status === "merged" ? "baseline_merged" : undefined;
|
|
78161
|
+
if (captureResult.status === "error") {
|
|
78162
|
+
warn(`SAST Baseline: capture failed \u2014 ${captureResult.message}`);
|
|
78163
|
+
}
|
|
78164
|
+
const finalFindings2 = allFindings.slice(0, MAX_FINDINGS2);
|
|
78165
|
+
const summary2 = {
|
|
78166
|
+
engine,
|
|
78167
|
+
files_scanned: filesScanned,
|
|
78168
|
+
findings_count: finalFindings2.length,
|
|
78169
|
+
findings_by_severity: countBySeverity(finalFindings2)
|
|
78170
|
+
};
|
|
78171
|
+
await saveEvidence(directory, "sast_scan", {
|
|
78172
|
+
task_id: "sast_scan",
|
|
78173
|
+
type: "sast",
|
|
78174
|
+
timestamp: new Date().toISOString(),
|
|
78175
|
+
agent: "sast_scan",
|
|
78176
|
+
verdict: "pass",
|
|
78177
|
+
summary: `Baseline capture: scanned ${filesScanned} files, recorded ${captureFindings.length} finding(s)`,
|
|
78178
|
+
...summary2,
|
|
78179
|
+
findings: finalFindings2,
|
|
78180
|
+
baseline_used: false
|
|
78181
|
+
});
|
|
78182
|
+
return {
|
|
78183
|
+
verdict: "pass",
|
|
78184
|
+
findings: finalFindings2,
|
|
78185
|
+
summary: summary2,
|
|
78186
|
+
status: captureStatus,
|
|
78187
|
+
finding_count: captureResult.status !== "error" ? captureResult.fingerprint_count : undefined,
|
|
78188
|
+
baseline_used: false
|
|
78189
|
+
};
|
|
78190
|
+
}
|
|
78191
|
+
let newFindings;
|
|
78192
|
+
let preExistingFindings;
|
|
78193
|
+
let baselineUsed = false;
|
|
78194
|
+
let truncatedPreExisting = false;
|
|
78195
|
+
if (phase !== undefined && Number.isInteger(phase) && phase >= 1) {
|
|
78196
|
+
const baselineResult = loadBaseline(directory, phase);
|
|
78197
|
+
if (baselineResult.status === "found") {
|
|
78198
|
+
baselineUsed = true;
|
|
78199
|
+
const baselineSet = baselineResult.fingerprints;
|
|
78200
|
+
const indexed = assignOccurrenceIndices(allFindings, directory);
|
|
78201
|
+
const rawNew = [];
|
|
78202
|
+
const rawPreExisting = [];
|
|
78203
|
+
for (const { finding, stable, fingerprint } of indexed) {
|
|
78204
|
+
if (!stable || !baselineSet.has(fingerprint)) {
|
|
78205
|
+
rawNew.push(finding);
|
|
78206
|
+
} else {
|
|
78207
|
+
rawPreExisting.push(finding);
|
|
78208
|
+
}
|
|
78209
|
+
}
|
|
78210
|
+
newFindings = rawNew.slice(0, MAX_FINDINGS2);
|
|
78211
|
+
const preExistingBudget = Math.max(0, MAX_FINDINGS2 - newFindings.length);
|
|
78212
|
+
preExistingFindings = rawPreExisting.slice(0, preExistingBudget);
|
|
78213
|
+
truncatedPreExisting = rawPreExisting.length > preExistingBudget;
|
|
78214
|
+
} else if (baselineResult.status === "invalid_schema") {
|
|
78215
|
+
warn(`SAST Baseline: could not load baseline for phase ${phase} \u2014 ${baselineResult.errors.join(", ")}. Falling back to legacy behavior.`);
|
|
78216
|
+
}
|
|
78217
|
+
}
|
|
78218
|
+
let finalFindings;
|
|
78219
|
+
if (!baselineUsed) {
|
|
78220
|
+
finalFindings = allFindings;
|
|
78221
|
+
if (allFindings.length > MAX_FINDINGS2) {
|
|
78222
|
+
finalFindings = allFindings.slice(0, MAX_FINDINGS2);
|
|
78223
|
+
warn(`SAST Scan: Found ${allFindings.length} findings, limiting to ${MAX_FINDINGS2}`);
|
|
78224
|
+
}
|
|
78225
|
+
} else {
|
|
78226
|
+
finalFindings = [
|
|
78227
|
+
...newFindings ?? [],
|
|
78228
|
+
...preExistingFindings ?? []
|
|
78229
|
+
].slice(0, MAX_FINDINGS2);
|
|
77832
78230
|
}
|
|
77833
78231
|
const findingsBySeverity = countBySeverity(finalFindings);
|
|
78232
|
+
const verdictSource = baselineUsed ? newFindings ?? [] : finalFindings;
|
|
77834
78233
|
let verdict = "pass";
|
|
77835
|
-
for (const finding of
|
|
78234
|
+
for (const finding of verdictSource) {
|
|
77836
78235
|
if (meetsThreshold(finding.severity, severity_threshold)) {
|
|
77837
78236
|
verdict = "fail";
|
|
77838
78237
|
break;
|
|
@@ -77855,42 +78254,65 @@ async function sastScan(input, directory, config3) {
|
|
|
77855
78254
|
verdict,
|
|
77856
78255
|
summary: `Scanned ${filesScanned} files, found ${finalFindings.length} finding(s) using ${engine}`,
|
|
77857
78256
|
...summary,
|
|
77858
|
-
findings: finalFindings
|
|
78257
|
+
findings: finalFindings,
|
|
78258
|
+
...baselineUsed && {
|
|
78259
|
+
new_findings: newFindings,
|
|
78260
|
+
pre_existing_findings: preExistingFindings,
|
|
78261
|
+
baseline_used: true
|
|
78262
|
+
}
|
|
77859
78263
|
});
|
|
77860
|
-
|
|
78264
|
+
const result = {
|
|
77861
78265
|
verdict,
|
|
77862
78266
|
findings: finalFindings,
|
|
77863
78267
|
summary
|
|
77864
78268
|
};
|
|
78269
|
+
if (baselineUsed) {
|
|
78270
|
+
result.new_findings = newFindings;
|
|
78271
|
+
result.pre_existing_findings = preExistingFindings;
|
|
78272
|
+
result.baseline_used = true;
|
|
78273
|
+
if (truncatedPreExisting)
|
|
78274
|
+
result.truncated_pre_existing = true;
|
|
78275
|
+
}
|
|
78276
|
+
return result;
|
|
77865
78277
|
}
|
|
77866
78278
|
var sast_scan = createSwarmTool({
|
|
77867
|
-
description: "Static Application Security Testing (SAST) scan. Scans files for security vulnerabilities using built-in rules (Tier A) and optional Semgrep (Tier B).
|
|
78279
|
+
description: "Static Application Security Testing (SAST) scan. Scans files for security vulnerabilities using built-in rules (Tier A) and optional Semgrep (Tier B). Supports phase-scoped baseline diffing: set capture_baseline:true before first coder delegation to snapshot pre-existing findings; subsequent scans with the same phase only fail on NEW findings.",
|
|
77868
78280
|
args: {
|
|
77869
78281
|
directory: tool.schema.string().describe("Directory to scan for security vulnerabilities"),
|
|
77870
78282
|
changed_files: tool.schema.array(tool.schema.string()).optional().describe("List of files to scan (leave empty to scan none)"),
|
|
77871
|
-
severity_threshold: tool.schema.enum(["low", "medium", "high", "critical"]).optional().default("medium").describe("Minimum severity that causes failure")
|
|
78283
|
+
severity_threshold: tool.schema.enum(["low", "medium", "high", "critical"]).optional().default("medium").describe("Minimum severity that causes failure"),
|
|
78284
|
+
capture_baseline: tool.schema.boolean().optional().describe("When true, capture/merge a phase-scoped baseline of pre-existing findings. Requires phase. Subsequent scans with phase only fail on NEW findings."),
|
|
78285
|
+
phase: tool.schema.number().int().min(1).optional().describe("Current phase number (positive integer >= 1). Required with capture_baseline. Enables baseline diff when provided on non-capture scans.")
|
|
77872
78286
|
},
|
|
77873
78287
|
execute: async (args2, directory) => {
|
|
77874
78288
|
let safeArgs;
|
|
77875
78289
|
try {
|
|
77876
78290
|
if (args2 && typeof args2 === "object") {
|
|
78291
|
+
const rawPhase = args2.phase;
|
|
78292
|
+
const rawCapture = args2.capture_baseline;
|
|
77877
78293
|
safeArgs = {
|
|
77878
78294
|
directory: args2.directory,
|
|
77879
78295
|
changed_files: args2.changed_files,
|
|
77880
|
-
severity_threshold: args2.severity_threshold
|
|
78296
|
+
severity_threshold: args2.severity_threshold,
|
|
78297
|
+
phase: typeof rawPhase === "number" && Number.isInteger(rawPhase) && rawPhase >= 1 ? rawPhase : undefined,
|
|
78298
|
+
capture_baseline: typeof rawCapture === "boolean" ? rawCapture : undefined
|
|
77881
78299
|
};
|
|
77882
78300
|
} else {
|
|
77883
78301
|
safeArgs = {
|
|
77884
78302
|
directory: undefined,
|
|
77885
78303
|
changed_files: undefined,
|
|
77886
|
-
severity_threshold: undefined
|
|
78304
|
+
severity_threshold: undefined,
|
|
78305
|
+
capture_baseline: undefined,
|
|
78306
|
+
phase: undefined
|
|
77887
78307
|
};
|
|
77888
78308
|
}
|
|
77889
78309
|
} catch {
|
|
77890
78310
|
safeArgs = {
|
|
77891
78311
|
directory: undefined,
|
|
77892
78312
|
changed_files: undefined,
|
|
77893
|
-
severity_threshold: undefined
|
|
78313
|
+
severity_threshold: undefined,
|
|
78314
|
+
capture_baseline: undefined,
|
|
78315
|
+
phase: undefined
|
|
77894
78316
|
};
|
|
77895
78317
|
}
|
|
77896
78318
|
if (safeArgs.directory === undefined) {
|
|
@@ -77913,7 +78335,9 @@ var sast_scan = createSwarmTool({
|
|
|
77913
78335
|
}
|
|
77914
78336
|
const input = {
|
|
77915
78337
|
changed_files: safeArgs.changed_files ?? [],
|
|
77916
|
-
severity_threshold: safeArgs.severity_threshold ?? "medium"
|
|
78338
|
+
severity_threshold: safeArgs.severity_threshold ?? "medium",
|
|
78339
|
+
capture_baseline: safeArgs.capture_baseline,
|
|
78340
|
+
phase: safeArgs.phase
|
|
77917
78341
|
};
|
|
77918
78342
|
const result = await sastScan(input, directory);
|
|
77919
78343
|
return JSON.stringify(result, null, 2);
|
|
@@ -77939,20 +78363,20 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
77939
78363
|
let resolved;
|
|
77940
78364
|
const isWinAbs = isWindowsAbsolutePath(inputPath);
|
|
77941
78365
|
if (isWinAbs) {
|
|
77942
|
-
resolved =
|
|
77943
|
-
} else if (
|
|
77944
|
-
resolved =
|
|
78366
|
+
resolved = path79.win32.resolve(inputPath);
|
|
78367
|
+
} else if (path79.isAbsolute(inputPath)) {
|
|
78368
|
+
resolved = path79.resolve(inputPath);
|
|
77945
78369
|
} else {
|
|
77946
|
-
resolved =
|
|
78370
|
+
resolved = path79.resolve(baseDir, inputPath);
|
|
77947
78371
|
}
|
|
77948
|
-
const workspaceResolved =
|
|
77949
|
-
let
|
|
78372
|
+
const workspaceResolved = path79.resolve(workspaceDir);
|
|
78373
|
+
let relative19;
|
|
77950
78374
|
if (isWinAbs) {
|
|
77951
|
-
|
|
78375
|
+
relative19 = path79.win32.relative(workspaceResolved, resolved);
|
|
77952
78376
|
} else {
|
|
77953
|
-
|
|
78377
|
+
relative19 = path79.relative(workspaceResolved, resolved);
|
|
77954
78378
|
}
|
|
77955
|
-
if (
|
|
78379
|
+
if (relative19.startsWith("..")) {
|
|
77956
78380
|
return "path traversal detected";
|
|
77957
78381
|
}
|
|
77958
78382
|
return null;
|
|
@@ -78015,7 +78439,7 @@ async function runLintOnFiles(linter, files, workspaceDir) {
|
|
|
78015
78439
|
if (typeof file3 !== "string") {
|
|
78016
78440
|
continue;
|
|
78017
78441
|
}
|
|
78018
|
-
const resolvedPath =
|
|
78442
|
+
const resolvedPath = path79.resolve(file3);
|
|
78019
78443
|
const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
|
|
78020
78444
|
if (validationError) {
|
|
78021
78445
|
continue;
|
|
@@ -78172,7 +78596,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
78172
78596
|
skippedFiles++;
|
|
78173
78597
|
continue;
|
|
78174
78598
|
}
|
|
78175
|
-
const resolvedPath =
|
|
78599
|
+
const resolvedPath = path79.resolve(file3);
|
|
78176
78600
|
const validationError = validatePath(resolvedPath, directory, directory);
|
|
78177
78601
|
if (validationError) {
|
|
78178
78602
|
skippedFiles++;
|
|
@@ -78190,14 +78614,14 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
78190
78614
|
};
|
|
78191
78615
|
}
|
|
78192
78616
|
for (const file3 of validatedFiles) {
|
|
78193
|
-
const ext =
|
|
78617
|
+
const ext = path79.extname(file3).toLowerCase();
|
|
78194
78618
|
if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
|
|
78195
78619
|
skippedFiles++;
|
|
78196
78620
|
continue;
|
|
78197
78621
|
}
|
|
78198
78622
|
let stat3;
|
|
78199
78623
|
try {
|
|
78200
|
-
stat3 =
|
|
78624
|
+
stat3 = fs65.statSync(file3);
|
|
78201
78625
|
} catch {
|
|
78202
78626
|
skippedFiles++;
|
|
78203
78627
|
continue;
|
|
@@ -78208,7 +78632,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
78208
78632
|
}
|
|
78209
78633
|
let content;
|
|
78210
78634
|
try {
|
|
78211
|
-
const buffer =
|
|
78635
|
+
const buffer = fs65.readFileSync(file3);
|
|
78212
78636
|
if (buffer.includes(0)) {
|
|
78213
78637
|
skippedFiles++;
|
|
78214
78638
|
continue;
|
|
@@ -78274,10 +78698,14 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
78274
78698
|
return errorResult;
|
|
78275
78699
|
}
|
|
78276
78700
|
}
|
|
78277
|
-
async function runSastScanWrapped(changedFiles, directory, severityThreshold, config3) {
|
|
78701
|
+
async function runSastScanWrapped(changedFiles, directory, severityThreshold, config3, phase) {
|
|
78278
78702
|
const start2 = process.hrtime.bigint();
|
|
78279
78703
|
try {
|
|
78280
|
-
const result = await runWithTimeout2(sastScan({
|
|
78704
|
+
const result = await runWithTimeout2(sastScan({
|
|
78705
|
+
changed_files: changedFiles,
|
|
78706
|
+
severity_threshold: severityThreshold,
|
|
78707
|
+
phase
|
|
78708
|
+
}, directory, config3), TOOL_TIMEOUT_MS);
|
|
78281
78709
|
return {
|
|
78282
78710
|
ran: true,
|
|
78283
78711
|
result,
|
|
@@ -78309,6 +78737,15 @@ async function runQualityBudgetWrapped(changedFiles, directory, _config) {
|
|
|
78309
78737
|
}
|
|
78310
78738
|
}
|
|
78311
78739
|
var GATE_SEVERITIES = new Set(["high", "critical"]);
|
|
78740
|
+
var SEVERITY_ORDER_PCB = {
|
|
78741
|
+
low: 0,
|
|
78742
|
+
medium: 1,
|
|
78743
|
+
high: 2,
|
|
78744
|
+
critical: 3
|
|
78745
|
+
};
|
|
78746
|
+
function meetsThresholdForTriage(severity, threshold) {
|
|
78747
|
+
return (SEVERITY_ORDER_PCB[severity] ?? 0) >= (SEVERITY_ORDER_PCB[threshold] ?? 1);
|
|
78748
|
+
}
|
|
78312
78749
|
async function runGitDiff(args2, directory) {
|
|
78313
78750
|
try {
|
|
78314
78751
|
const proc = Bun.spawn(["git", "diff", ...args2], {
|
|
@@ -78396,7 +78833,7 @@ function classifySastFindings(findings, changedLineRanges, directory) {
|
|
|
78396
78833
|
const preexistingFindings = [];
|
|
78397
78834
|
for (const finding of findings) {
|
|
78398
78835
|
const filePath = finding.location.file;
|
|
78399
|
-
const normalised =
|
|
78836
|
+
const normalised = path79.relative(directory, filePath).replace(/\\/g, "/");
|
|
78400
78837
|
const changedLines = changedLineRanges.get(normalised);
|
|
78401
78838
|
if (changedLines?.has(finding.location.line)) {
|
|
78402
78839
|
newFindings.push(finding);
|
|
@@ -78408,7 +78845,7 @@ function classifySastFindings(findings, changedLineRanges, directory) {
|
|
|
78408
78845
|
}
|
|
78409
78846
|
async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
78410
78847
|
const effectiveWorkspaceDir = workspaceDir || input.directory || contextDir;
|
|
78411
|
-
const { files, directory, sast_threshold = "medium", config: config3 } = input;
|
|
78848
|
+
const { files, directory, sast_threshold = "medium", config: config3, phase } = input;
|
|
78412
78849
|
const dirError = validateDirectory2(directory, effectiveWorkspaceDir);
|
|
78413
78850
|
if (dirError) {
|
|
78414
78851
|
warn(`pre_check_batch: Invalid directory: ${dirError}`);
|
|
@@ -78447,7 +78884,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
|
78447
78884
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
78448
78885
|
continue;
|
|
78449
78886
|
}
|
|
78450
|
-
changedFiles.push(
|
|
78887
|
+
changedFiles.push(path79.resolve(directory, file3));
|
|
78451
78888
|
}
|
|
78452
78889
|
if (changedFiles.length === 0) {
|
|
78453
78890
|
warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
|
|
@@ -78471,7 +78908,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
|
78471
78908
|
const [lintResult, secretscanResult, sastScanResult, qualityBudgetResult] = await Promise.all([
|
|
78472
78909
|
limit(() => runLintWrapped(changedFiles, directory, config3)),
|
|
78473
78910
|
limit(() => runSecretscanWrapped(changedFiles, directory, config3)),
|
|
78474
|
-
limit(() => runSastScanWrapped(changedFiles, directory, sast_threshold, config3)),
|
|
78911
|
+
limit(() => runSastScanWrapped(changedFiles, directory, sast_threshold, config3, phase)),
|
|
78475
78912
|
limit(() => runQualityBudgetWrapped(changedFiles, directory, config3))
|
|
78476
78913
|
]);
|
|
78477
78914
|
const totalDuration = lintResult.duration_ms + secretscanResult.duration_ms + sastScanResult.duration_ms + qualityBudgetResult.duration_ms;
|
|
@@ -78516,8 +78953,20 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
|
78516
78953
|
}
|
|
78517
78954
|
let sastPreexistingFindings;
|
|
78518
78955
|
if (sastScanResult.ran && sastScanResult.result) {
|
|
78519
|
-
|
|
78520
|
-
|
|
78956
|
+
const sastResult = sastScanResult.result;
|
|
78957
|
+
if (sastResult.baseline_used) {
|
|
78958
|
+
if (sastResult.pre_existing_findings && sastResult.pre_existing_findings.length > 0) {
|
|
78959
|
+
sastPreexistingFindings = sastResult.pre_existing_findings.filter((f) => meetsThresholdForTriage(f.severity, sast_threshold));
|
|
78960
|
+
if (sastPreexistingFindings.length > 0) {
|
|
78961
|
+
warn(`pre_check_batch: SAST baseline diff found ${sastPreexistingFindings.length} pre-existing finding(s) - passing to reviewer for triage`);
|
|
78962
|
+
}
|
|
78963
|
+
}
|
|
78964
|
+
if (sastResult.verdict === "fail") {
|
|
78965
|
+
gatesPassed = false;
|
|
78966
|
+
warn(`pre_check_batch: SAST scan found new findings above threshold - GATE FAILED`);
|
|
78967
|
+
}
|
|
78968
|
+
} else if (sastResult.verdict === "fail") {
|
|
78969
|
+
const gateFindings = sastResult.findings.filter((f) => GATE_SEVERITIES.has(f.severity));
|
|
78521
78970
|
if (gateFindings.length > 0) {
|
|
78522
78971
|
const changedLineRanges = await getChangedLineRanges(directory);
|
|
78523
78972
|
const { newFindings, preexistingFindings } = classifySastFindings(gateFindings, changedLineRanges, directory);
|
|
@@ -78566,7 +79015,8 @@ var pre_check_batch = createSwarmTool({
|
|
|
78566
79015
|
args: {
|
|
78567
79016
|
files: tool.schema.array(tool.schema.string()).optional().describe("Specific files to check (optional, scans directory if not provided)"),
|
|
78568
79017
|
directory: tool.schema.string().describe('Directory to run checks in (e.g., "." or "./src")'),
|
|
78569
|
-
sast_threshold: tool.schema.enum(["low", "medium", "high", "critical"]).optional().describe("Minimum severity for SAST findings to cause failure (default: medium)")
|
|
79018
|
+
sast_threshold: tool.schema.enum(["low", "medium", "high", "critical"]).optional().describe("Minimum severity for SAST findings to cause failure (default: medium)"),
|
|
79019
|
+
phase: tool.schema.number().int().min(1).optional().describe("Current phase number (positive integer >= 1). When provided, enables SAST baseline diffing: only findings absent from the phase-scoped baseline fail the gate.")
|
|
78570
79020
|
},
|
|
78571
79021
|
async execute(args2, directory) {
|
|
78572
79022
|
if (!args2 || typeof args2 !== "object") {
|
|
@@ -78635,7 +79085,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
78635
79085
|
};
|
|
78636
79086
|
return JSON.stringify(errorResult, null, 2);
|
|
78637
79087
|
}
|
|
78638
|
-
const resolvedDirectory =
|
|
79088
|
+
const resolvedDirectory = path79.resolve(typedArgs.directory);
|
|
78639
79089
|
const workspaceAnchor = resolvedDirectory;
|
|
78640
79090
|
const dirError = validateDirectory2(resolvedDirectory, workspaceAnchor);
|
|
78641
79091
|
if (dirError) {
|
|
@@ -78650,11 +79100,14 @@ var pre_check_batch = createSwarmTool({
|
|
|
78650
79100
|
return JSON.stringify(errorResult, null, 2);
|
|
78651
79101
|
}
|
|
78652
79102
|
try {
|
|
79103
|
+
const rawPhase = typedArgs.phase;
|
|
79104
|
+
const safePhase = typeof rawPhase === "number" && Number.isInteger(rawPhase) && rawPhase >= 1 ? rawPhase : undefined;
|
|
78653
79105
|
const result = await runPreCheckBatch({
|
|
78654
79106
|
files: typedArgs.files,
|
|
78655
79107
|
directory: resolvedDirectory,
|
|
78656
79108
|
sast_threshold: typedArgs.sast_threshold,
|
|
78657
|
-
config: typedArgs.config
|
|
79109
|
+
config: typedArgs.config,
|
|
79110
|
+
phase: safePhase
|
|
78658
79111
|
}, workspaceAnchor, directory);
|
|
78659
79112
|
return JSON.stringify(result, null, 2);
|
|
78660
79113
|
} catch (error93) {
|
|
@@ -78673,7 +79126,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
78673
79126
|
});
|
|
78674
79127
|
// src/tools/repo-map.ts
|
|
78675
79128
|
init_dist();
|
|
78676
|
-
import * as
|
|
79129
|
+
import * as path80 from "path";
|
|
78677
79130
|
init_path_security();
|
|
78678
79131
|
init_create_tool();
|
|
78679
79132
|
var VALID_ACTIONS = [
|
|
@@ -78698,7 +79151,7 @@ function validateFile(p) {
|
|
|
78698
79151
|
return "file contains control characters";
|
|
78699
79152
|
if (containsPathTraversal(p))
|
|
78700
79153
|
return "file contains path traversal";
|
|
78701
|
-
if (
|
|
79154
|
+
if (path80.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p)) {
|
|
78702
79155
|
return "file must be a workspace-relative path, not absolute";
|
|
78703
79156
|
}
|
|
78704
79157
|
return null;
|
|
@@ -78721,8 +79174,8 @@ function ok(action, payload) {
|
|
|
78721
79174
|
}
|
|
78722
79175
|
function toRelativeGraphPath(input, workspaceRoot) {
|
|
78723
79176
|
const normalized = input.replace(/\\/g, "/");
|
|
78724
|
-
if (
|
|
78725
|
-
const rel =
|
|
79177
|
+
if (path80.isAbsolute(normalized)) {
|
|
79178
|
+
const rel = path80.relative(workspaceRoot, normalized).replace(/\\/g, "/");
|
|
78726
79179
|
return normalizeGraphPath2(rel);
|
|
78727
79180
|
}
|
|
78728
79181
|
return normalizeGraphPath2(normalized);
|
|
@@ -78866,8 +79319,8 @@ var repo_map = createSwarmTool({
|
|
|
78866
79319
|
// src/tools/req-coverage.ts
|
|
78867
79320
|
init_dist();
|
|
78868
79321
|
init_create_tool();
|
|
78869
|
-
import * as
|
|
78870
|
-
import * as
|
|
79322
|
+
import * as fs66 from "fs";
|
|
79323
|
+
import * as path81 from "path";
|
|
78871
79324
|
var SPEC_FILE = ".swarm/spec.md";
|
|
78872
79325
|
var EVIDENCE_DIR4 = ".swarm/evidence";
|
|
78873
79326
|
var OBLIGATION_KEYWORDS = ["MUST", "SHOULD", "SHALL"];
|
|
@@ -78926,19 +79379,19 @@ function extractObligationAndText(id, lineText) {
|
|
|
78926
79379
|
var PHASE_TASK_ID_REGEX = /^\d+\.\d+(\.\d+)*$/;
|
|
78927
79380
|
function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
78928
79381
|
const touchedFiles = new Set;
|
|
78929
|
-
if (!
|
|
79382
|
+
if (!fs66.existsSync(evidenceDir) || !fs66.statSync(evidenceDir).isDirectory()) {
|
|
78930
79383
|
return [];
|
|
78931
79384
|
}
|
|
78932
79385
|
let entries;
|
|
78933
79386
|
try {
|
|
78934
|
-
entries =
|
|
79387
|
+
entries = fs66.readdirSync(evidenceDir);
|
|
78935
79388
|
} catch {
|
|
78936
79389
|
return [];
|
|
78937
79390
|
}
|
|
78938
79391
|
for (const entry of entries) {
|
|
78939
|
-
const entryPath =
|
|
79392
|
+
const entryPath = path81.join(evidenceDir, entry);
|
|
78940
79393
|
try {
|
|
78941
|
-
const stat3 =
|
|
79394
|
+
const stat3 = fs66.statSync(entryPath);
|
|
78942
79395
|
if (!stat3.isDirectory()) {
|
|
78943
79396
|
continue;
|
|
78944
79397
|
}
|
|
@@ -78952,14 +79405,14 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
78952
79405
|
if (entryPhase !== String(phase)) {
|
|
78953
79406
|
continue;
|
|
78954
79407
|
}
|
|
78955
|
-
const evidenceFilePath =
|
|
79408
|
+
const evidenceFilePath = path81.join(entryPath, "evidence.json");
|
|
78956
79409
|
try {
|
|
78957
|
-
const resolvedPath =
|
|
78958
|
-
const evidenceDirResolved =
|
|
78959
|
-
if (!resolvedPath.startsWith(evidenceDirResolved +
|
|
79410
|
+
const resolvedPath = path81.resolve(evidenceFilePath);
|
|
79411
|
+
const evidenceDirResolved = path81.resolve(evidenceDir);
|
|
79412
|
+
if (!resolvedPath.startsWith(evidenceDirResolved + path81.sep)) {
|
|
78960
79413
|
continue;
|
|
78961
79414
|
}
|
|
78962
|
-
const stat3 =
|
|
79415
|
+
const stat3 = fs66.lstatSync(evidenceFilePath);
|
|
78963
79416
|
if (!stat3.isFile()) {
|
|
78964
79417
|
continue;
|
|
78965
79418
|
}
|
|
@@ -78971,7 +79424,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
78971
79424
|
}
|
|
78972
79425
|
let content;
|
|
78973
79426
|
try {
|
|
78974
|
-
content =
|
|
79427
|
+
content = fs66.readFileSync(evidenceFilePath, "utf-8");
|
|
78975
79428
|
} catch {
|
|
78976
79429
|
continue;
|
|
78977
79430
|
}
|
|
@@ -78990,7 +79443,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
78990
79443
|
if (Array.isArray(diffEntry.files_changed)) {
|
|
78991
79444
|
for (const file3 of diffEntry.files_changed) {
|
|
78992
79445
|
if (typeof file3 === "string") {
|
|
78993
|
-
touchedFiles.add(
|
|
79446
|
+
touchedFiles.add(path81.resolve(cwd, file3));
|
|
78994
79447
|
}
|
|
78995
79448
|
}
|
|
78996
79449
|
}
|
|
@@ -79003,12 +79456,12 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
79003
79456
|
}
|
|
79004
79457
|
function searchFileForKeywords(filePath, keywords, cwd) {
|
|
79005
79458
|
try {
|
|
79006
|
-
const resolvedPath =
|
|
79007
|
-
const cwdResolved =
|
|
79459
|
+
const resolvedPath = path81.resolve(filePath);
|
|
79460
|
+
const cwdResolved = path81.resolve(cwd);
|
|
79008
79461
|
if (!resolvedPath.startsWith(cwdResolved)) {
|
|
79009
79462
|
return false;
|
|
79010
79463
|
}
|
|
79011
|
-
const content =
|
|
79464
|
+
const content = fs66.readFileSync(resolvedPath, "utf-8");
|
|
79012
79465
|
for (const keyword of keywords) {
|
|
79013
79466
|
const regex = new RegExp(`\\b${keyword}\\b`, "i");
|
|
79014
79467
|
if (regex.test(content)) {
|
|
@@ -79138,10 +79591,10 @@ var req_coverage = createSwarmTool({
|
|
|
79138
79591
|
}, null, 2);
|
|
79139
79592
|
}
|
|
79140
79593
|
const cwd = inputDirectory || directory;
|
|
79141
|
-
const specPath =
|
|
79594
|
+
const specPath = path81.join(cwd, SPEC_FILE);
|
|
79142
79595
|
let specContent;
|
|
79143
79596
|
try {
|
|
79144
|
-
specContent =
|
|
79597
|
+
specContent = fs66.readFileSync(specPath, "utf-8");
|
|
79145
79598
|
} catch (readError) {
|
|
79146
79599
|
return JSON.stringify({
|
|
79147
79600
|
success: false,
|
|
@@ -79165,7 +79618,7 @@ var req_coverage = createSwarmTool({
|
|
|
79165
79618
|
message: "No FR requirements found in spec.md"
|
|
79166
79619
|
}, null, 2);
|
|
79167
79620
|
}
|
|
79168
|
-
const evidenceDir =
|
|
79621
|
+
const evidenceDir = path81.join(cwd, EVIDENCE_DIR4);
|
|
79169
79622
|
const touchedFiles = readTouchedFiles(evidenceDir, phase, cwd);
|
|
79170
79623
|
const analyzedRequirements = [];
|
|
79171
79624
|
let coveredCount = 0;
|
|
@@ -79191,12 +79644,12 @@ var req_coverage = createSwarmTool({
|
|
|
79191
79644
|
requirements: analyzedRequirements
|
|
79192
79645
|
};
|
|
79193
79646
|
const reportFilename = `req-coverage-phase-${phase}.json`;
|
|
79194
|
-
const reportPath =
|
|
79647
|
+
const reportPath = path81.join(evidenceDir, reportFilename);
|
|
79195
79648
|
try {
|
|
79196
|
-
if (!
|
|
79197
|
-
|
|
79649
|
+
if (!fs66.existsSync(evidenceDir)) {
|
|
79650
|
+
fs66.mkdirSync(evidenceDir, { recursive: true });
|
|
79198
79651
|
}
|
|
79199
|
-
|
|
79652
|
+
fs66.writeFileSync(reportPath, JSON.stringify(result, null, 2), "utf-8");
|
|
79200
79653
|
} catch (writeError) {
|
|
79201
79654
|
console.warn(`Failed to write coverage report: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
79202
79655
|
}
|
|
@@ -79274,9 +79727,9 @@ ${paginatedContent}`;
|
|
|
79274
79727
|
});
|
|
79275
79728
|
// src/tools/save-plan.ts
|
|
79276
79729
|
init_tool();
|
|
79277
|
-
import * as
|
|
79278
|
-
import * as
|
|
79279
|
-
import * as
|
|
79730
|
+
import * as crypto9 from "crypto";
|
|
79731
|
+
import * as fs67 from "fs";
|
|
79732
|
+
import * as path82 from "path";
|
|
79280
79733
|
init_checkpoint3();
|
|
79281
79734
|
init_ledger();
|
|
79282
79735
|
init_manager();
|
|
@@ -79357,12 +79810,12 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
79357
79810
|
let specMtime;
|
|
79358
79811
|
let specHash;
|
|
79359
79812
|
if (process.env.SWARM_SKIP_SPEC_GATE !== "1") {
|
|
79360
|
-
const specPath =
|
|
79813
|
+
const specPath = path82.join(targetWorkspace, ".swarm", "spec.md");
|
|
79361
79814
|
try {
|
|
79362
|
-
const stat3 = await
|
|
79815
|
+
const stat3 = await fs67.promises.stat(specPath);
|
|
79363
79816
|
specMtime = stat3.mtime.toISOString();
|
|
79364
|
-
const content = await
|
|
79365
|
-
specHash =
|
|
79817
|
+
const content = await fs67.promises.readFile(specPath, "utf8");
|
|
79818
|
+
specHash = crypto9.createHash("sha256").update(content).digest("hex");
|
|
79366
79819
|
} catch {
|
|
79367
79820
|
return {
|
|
79368
79821
|
success: false,
|
|
@@ -79439,14 +79892,14 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
79439
79892
|
}
|
|
79440
79893
|
await writeCheckpoint(dir).catch(() => {});
|
|
79441
79894
|
try {
|
|
79442
|
-
const markerPath =
|
|
79895
|
+
const markerPath = path82.join(dir, ".swarm", ".plan-write-marker");
|
|
79443
79896
|
const marker = JSON.stringify({
|
|
79444
79897
|
source: "save_plan",
|
|
79445
79898
|
timestamp: new Date().toISOString(),
|
|
79446
79899
|
phases_count: plan.phases.length,
|
|
79447
79900
|
tasks_count: tasksCount
|
|
79448
79901
|
});
|
|
79449
|
-
await
|
|
79902
|
+
await fs67.promises.writeFile(markerPath, marker, "utf8");
|
|
79450
79903
|
} catch {}
|
|
79451
79904
|
const warnings = [];
|
|
79452
79905
|
let criticReviewFound = false;
|
|
@@ -79462,7 +79915,7 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
79462
79915
|
return {
|
|
79463
79916
|
success: true,
|
|
79464
79917
|
message: "Plan saved successfully",
|
|
79465
|
-
plan_path:
|
|
79918
|
+
plan_path: path82.join(dir, ".swarm", "plan.json"),
|
|
79466
79919
|
phases_count: plan.phases.length,
|
|
79467
79920
|
tasks_count: tasksCount,
|
|
79468
79921
|
...warnings.length > 0 ? { warnings } : {}
|
|
@@ -79507,8 +79960,8 @@ var save_plan = createSwarmTool({
|
|
|
79507
79960
|
// src/tools/sbom-generate.ts
|
|
79508
79961
|
init_dist();
|
|
79509
79962
|
init_manager2();
|
|
79510
|
-
import * as
|
|
79511
|
-
import * as
|
|
79963
|
+
import * as fs68 from "fs";
|
|
79964
|
+
import * as path83 from "path";
|
|
79512
79965
|
|
|
79513
79966
|
// src/sbom/detectors/index.ts
|
|
79514
79967
|
init_utils();
|
|
@@ -80356,9 +80809,9 @@ function findManifestFiles(rootDir) {
|
|
|
80356
80809
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
80357
80810
|
function searchDir(dir) {
|
|
80358
80811
|
try {
|
|
80359
|
-
const entries =
|
|
80812
|
+
const entries = fs68.readdirSync(dir, { withFileTypes: true });
|
|
80360
80813
|
for (const entry of entries) {
|
|
80361
|
-
const fullPath =
|
|
80814
|
+
const fullPath = path83.join(dir, entry.name);
|
|
80362
80815
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
80363
80816
|
continue;
|
|
80364
80817
|
}
|
|
@@ -80367,7 +80820,7 @@ function findManifestFiles(rootDir) {
|
|
|
80367
80820
|
} else if (entry.isFile()) {
|
|
80368
80821
|
for (const pattern of patterns) {
|
|
80369
80822
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
80370
|
-
manifestFiles.push(
|
|
80823
|
+
manifestFiles.push(path83.relative(rootDir, fullPath));
|
|
80371
80824
|
break;
|
|
80372
80825
|
}
|
|
80373
80826
|
}
|
|
@@ -80383,13 +80836,13 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
80383
80836
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
80384
80837
|
for (const dir of directories) {
|
|
80385
80838
|
try {
|
|
80386
|
-
const entries =
|
|
80839
|
+
const entries = fs68.readdirSync(dir, { withFileTypes: true });
|
|
80387
80840
|
for (const entry of entries) {
|
|
80388
|
-
const fullPath =
|
|
80841
|
+
const fullPath = path83.join(dir, entry.name);
|
|
80389
80842
|
if (entry.isFile()) {
|
|
80390
80843
|
for (const pattern of patterns) {
|
|
80391
80844
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
80392
|
-
found.push(
|
|
80845
|
+
found.push(path83.relative(workingDir, fullPath));
|
|
80393
80846
|
break;
|
|
80394
80847
|
}
|
|
80395
80848
|
}
|
|
@@ -80402,11 +80855,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
80402
80855
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
80403
80856
|
const dirs = new Set;
|
|
80404
80857
|
for (const file3 of changedFiles) {
|
|
80405
|
-
let currentDir =
|
|
80858
|
+
let currentDir = path83.dirname(file3);
|
|
80406
80859
|
while (true) {
|
|
80407
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
80408
|
-
dirs.add(
|
|
80409
|
-
const parent =
|
|
80860
|
+
if (currentDir && currentDir !== "." && currentDir !== path83.sep) {
|
|
80861
|
+
dirs.add(path83.join(workingDir, currentDir));
|
|
80862
|
+
const parent = path83.dirname(currentDir);
|
|
80410
80863
|
if (parent === currentDir)
|
|
80411
80864
|
break;
|
|
80412
80865
|
currentDir = parent;
|
|
@@ -80420,7 +80873,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
|
80420
80873
|
}
|
|
80421
80874
|
function ensureOutputDir(outputDir) {
|
|
80422
80875
|
try {
|
|
80423
|
-
|
|
80876
|
+
fs68.mkdirSync(outputDir, { recursive: true });
|
|
80424
80877
|
} catch (error93) {
|
|
80425
80878
|
if (!error93 || error93.code !== "EEXIST") {
|
|
80426
80879
|
throw error93;
|
|
@@ -80490,7 +80943,7 @@ var sbom_generate = createSwarmTool({
|
|
|
80490
80943
|
const changedFiles = obj.changed_files;
|
|
80491
80944
|
const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
|
|
80492
80945
|
const workingDir = directory;
|
|
80493
|
-
const outputDir =
|
|
80946
|
+
const outputDir = path83.isAbsolute(relativeOutputDir) ? relativeOutputDir : path83.join(workingDir, relativeOutputDir);
|
|
80494
80947
|
let manifestFiles = [];
|
|
80495
80948
|
if (scope === "all") {
|
|
80496
80949
|
manifestFiles = findManifestFiles(workingDir);
|
|
@@ -80513,11 +80966,11 @@ var sbom_generate = createSwarmTool({
|
|
|
80513
80966
|
const processedFiles = [];
|
|
80514
80967
|
for (const manifestFile of manifestFiles) {
|
|
80515
80968
|
try {
|
|
80516
|
-
const fullPath =
|
|
80517
|
-
if (!
|
|
80969
|
+
const fullPath = path83.isAbsolute(manifestFile) ? manifestFile : path83.join(workingDir, manifestFile);
|
|
80970
|
+
if (!fs68.existsSync(fullPath)) {
|
|
80518
80971
|
continue;
|
|
80519
80972
|
}
|
|
80520
|
-
const content =
|
|
80973
|
+
const content = fs68.readFileSync(fullPath, "utf-8");
|
|
80521
80974
|
const components = detectComponents(manifestFile, content);
|
|
80522
80975
|
processedFiles.push(manifestFile);
|
|
80523
80976
|
if (components.length > 0) {
|
|
@@ -80530,8 +80983,8 @@ var sbom_generate = createSwarmTool({
|
|
|
80530
80983
|
const bom = generateCycloneDX(allComponents);
|
|
80531
80984
|
const bomJson = serializeCycloneDX(bom);
|
|
80532
80985
|
const filename = generateSbomFilename();
|
|
80533
|
-
const outputPath =
|
|
80534
|
-
|
|
80986
|
+
const outputPath = path83.join(outputDir, filename);
|
|
80987
|
+
fs68.writeFileSync(outputPath, bomJson, "utf-8");
|
|
80535
80988
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
80536
80989
|
try {
|
|
80537
80990
|
const timestamp = new Date().toISOString();
|
|
@@ -80573,8 +81026,8 @@ var sbom_generate = createSwarmTool({
|
|
|
80573
81026
|
// src/tools/schema-drift.ts
|
|
80574
81027
|
init_dist();
|
|
80575
81028
|
init_create_tool();
|
|
80576
|
-
import * as
|
|
80577
|
-
import * as
|
|
81029
|
+
import * as fs69 from "fs";
|
|
81030
|
+
import * as path84 from "path";
|
|
80578
81031
|
var SPEC_CANDIDATES = [
|
|
80579
81032
|
"openapi.json",
|
|
80580
81033
|
"openapi.yaml",
|
|
@@ -80606,28 +81059,28 @@ function normalizePath3(p) {
|
|
|
80606
81059
|
}
|
|
80607
81060
|
function discoverSpecFile(cwd, specFileArg) {
|
|
80608
81061
|
if (specFileArg) {
|
|
80609
|
-
const resolvedPath =
|
|
80610
|
-
const normalizedCwd = cwd.endsWith(
|
|
81062
|
+
const resolvedPath = path84.resolve(cwd, specFileArg);
|
|
81063
|
+
const normalizedCwd = cwd.endsWith(path84.sep) ? cwd : cwd + path84.sep;
|
|
80611
81064
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
80612
81065
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
80613
81066
|
}
|
|
80614
|
-
const ext =
|
|
81067
|
+
const ext = path84.extname(resolvedPath).toLowerCase();
|
|
80615
81068
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
80616
81069
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
80617
81070
|
}
|
|
80618
|
-
const stats =
|
|
81071
|
+
const stats = fs69.statSync(resolvedPath);
|
|
80619
81072
|
if (stats.size > MAX_SPEC_SIZE) {
|
|
80620
81073
|
throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
|
|
80621
81074
|
}
|
|
80622
|
-
if (!
|
|
81075
|
+
if (!fs69.existsSync(resolvedPath)) {
|
|
80623
81076
|
throw new Error(`Spec file not found: ${resolvedPath}`);
|
|
80624
81077
|
}
|
|
80625
81078
|
return resolvedPath;
|
|
80626
81079
|
}
|
|
80627
81080
|
for (const candidate of SPEC_CANDIDATES) {
|
|
80628
|
-
const candidatePath =
|
|
80629
|
-
if (
|
|
80630
|
-
const stats =
|
|
81081
|
+
const candidatePath = path84.resolve(cwd, candidate);
|
|
81082
|
+
if (fs69.existsSync(candidatePath)) {
|
|
81083
|
+
const stats = fs69.statSync(candidatePath);
|
|
80631
81084
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
80632
81085
|
return candidatePath;
|
|
80633
81086
|
}
|
|
@@ -80636,8 +81089,8 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
80636
81089
|
return null;
|
|
80637
81090
|
}
|
|
80638
81091
|
function parseSpec(specFile) {
|
|
80639
|
-
const content =
|
|
80640
|
-
const ext =
|
|
81092
|
+
const content = fs69.readFileSync(specFile, "utf-8");
|
|
81093
|
+
const ext = path84.extname(specFile).toLowerCase();
|
|
80641
81094
|
if (ext === ".json") {
|
|
80642
81095
|
return parseJsonSpec(content);
|
|
80643
81096
|
}
|
|
@@ -80708,12 +81161,12 @@ function extractRoutes(cwd) {
|
|
|
80708
81161
|
function walkDir(dir) {
|
|
80709
81162
|
let entries;
|
|
80710
81163
|
try {
|
|
80711
|
-
entries =
|
|
81164
|
+
entries = fs69.readdirSync(dir, { withFileTypes: true });
|
|
80712
81165
|
} catch {
|
|
80713
81166
|
return;
|
|
80714
81167
|
}
|
|
80715
81168
|
for (const entry of entries) {
|
|
80716
|
-
const fullPath =
|
|
81169
|
+
const fullPath = path84.join(dir, entry.name);
|
|
80717
81170
|
if (entry.isSymbolicLink()) {
|
|
80718
81171
|
continue;
|
|
80719
81172
|
}
|
|
@@ -80723,7 +81176,7 @@ function extractRoutes(cwd) {
|
|
|
80723
81176
|
}
|
|
80724
81177
|
walkDir(fullPath);
|
|
80725
81178
|
} else if (entry.isFile()) {
|
|
80726
|
-
const ext =
|
|
81179
|
+
const ext = path84.extname(entry.name).toLowerCase();
|
|
80727
81180
|
const baseName = entry.name.toLowerCase();
|
|
80728
81181
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
80729
81182
|
continue;
|
|
@@ -80741,7 +81194,7 @@ function extractRoutes(cwd) {
|
|
|
80741
81194
|
}
|
|
80742
81195
|
function extractRoutesFromFile(filePath) {
|
|
80743
81196
|
const routes = [];
|
|
80744
|
-
const content =
|
|
81197
|
+
const content = fs69.readFileSync(filePath, "utf-8");
|
|
80745
81198
|
const lines = content.split(/\r?\n/);
|
|
80746
81199
|
const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
80747
81200
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
@@ -80889,8 +81342,8 @@ var schema_drift = createSwarmTool({
|
|
|
80889
81342
|
init_tool();
|
|
80890
81343
|
init_path_security();
|
|
80891
81344
|
init_create_tool();
|
|
80892
|
-
import * as
|
|
80893
|
-
import * as
|
|
81345
|
+
import * as fs70 from "fs";
|
|
81346
|
+
import * as path85 from "path";
|
|
80894
81347
|
var DEFAULT_MAX_RESULTS = 100;
|
|
80895
81348
|
var DEFAULT_MAX_LINES = 200;
|
|
80896
81349
|
var REGEX_TIMEOUT_MS = 5000;
|
|
@@ -80926,11 +81379,11 @@ function containsWindowsAttacks3(str) {
|
|
|
80926
81379
|
}
|
|
80927
81380
|
function isPathInWorkspace3(filePath, workspace) {
|
|
80928
81381
|
try {
|
|
80929
|
-
const resolvedPath =
|
|
80930
|
-
const realWorkspace =
|
|
80931
|
-
const realResolvedPath =
|
|
80932
|
-
const relativePath =
|
|
80933
|
-
if (relativePath.startsWith("..") ||
|
|
81382
|
+
const resolvedPath = path85.resolve(workspace, filePath);
|
|
81383
|
+
const realWorkspace = fs70.realpathSync(workspace);
|
|
81384
|
+
const realResolvedPath = fs70.realpathSync(resolvedPath);
|
|
81385
|
+
const relativePath = path85.relative(realWorkspace, realResolvedPath);
|
|
81386
|
+
if (relativePath.startsWith("..") || path85.isAbsolute(relativePath)) {
|
|
80934
81387
|
return false;
|
|
80935
81388
|
}
|
|
80936
81389
|
return true;
|
|
@@ -80943,12 +81396,12 @@ function validatePathForRead2(filePath, workspace) {
|
|
|
80943
81396
|
}
|
|
80944
81397
|
function findRgInEnvPath() {
|
|
80945
81398
|
const searchPath = process.env.PATH ?? "";
|
|
80946
|
-
for (const dir of searchPath.split(
|
|
81399
|
+
for (const dir of searchPath.split(path85.delimiter)) {
|
|
80947
81400
|
if (!dir)
|
|
80948
81401
|
continue;
|
|
80949
81402
|
const isWindows = process.platform === "win32";
|
|
80950
|
-
const candidate =
|
|
80951
|
-
if (
|
|
81403
|
+
const candidate = path85.join(dir, isWindows ? "rg.exe" : "rg");
|
|
81404
|
+
if (fs70.existsSync(candidate))
|
|
80952
81405
|
return candidate;
|
|
80953
81406
|
}
|
|
80954
81407
|
return null;
|
|
@@ -81002,7 +81455,7 @@ async function ripgrepSearch(opts) {
|
|
|
81002
81455
|
stderr: "pipe",
|
|
81003
81456
|
cwd: opts.workspace
|
|
81004
81457
|
});
|
|
81005
|
-
const timeout = new Promise((
|
|
81458
|
+
const timeout = new Promise((resolve36) => setTimeout(() => resolve36("timeout"), REGEX_TIMEOUT_MS));
|
|
81006
81459
|
const exitPromise = proc.exited;
|
|
81007
81460
|
const result = await Promise.race([exitPromise, timeout]);
|
|
81008
81461
|
if (result === "timeout") {
|
|
@@ -81075,10 +81528,10 @@ function collectFiles(dir, workspace, includeGlobs, excludeGlobs) {
|
|
|
81075
81528
|
return files;
|
|
81076
81529
|
}
|
|
81077
81530
|
try {
|
|
81078
|
-
const entries =
|
|
81531
|
+
const entries = fs70.readdirSync(dir, { withFileTypes: true });
|
|
81079
81532
|
for (const entry of entries) {
|
|
81080
|
-
const fullPath =
|
|
81081
|
-
const relativePath =
|
|
81533
|
+
const fullPath = path85.join(dir, entry.name);
|
|
81534
|
+
const relativePath = path85.relative(workspace, fullPath);
|
|
81082
81535
|
if (!validatePathForRead2(fullPath, workspace)) {
|
|
81083
81536
|
continue;
|
|
81084
81537
|
}
|
|
@@ -81119,13 +81572,13 @@ async function fallbackSearch(opts) {
|
|
|
81119
81572
|
const matches = [];
|
|
81120
81573
|
let total = 0;
|
|
81121
81574
|
for (const file3 of files) {
|
|
81122
|
-
const fullPath =
|
|
81575
|
+
const fullPath = path85.join(opts.workspace, file3);
|
|
81123
81576
|
if (!validatePathForRead2(fullPath, opts.workspace)) {
|
|
81124
81577
|
continue;
|
|
81125
81578
|
}
|
|
81126
81579
|
let stats;
|
|
81127
81580
|
try {
|
|
81128
|
-
stats =
|
|
81581
|
+
stats = fs70.statSync(fullPath);
|
|
81129
81582
|
if (stats.size > MAX_FILE_SIZE_BYTES10) {
|
|
81130
81583
|
continue;
|
|
81131
81584
|
}
|
|
@@ -81134,7 +81587,7 @@ async function fallbackSearch(opts) {
|
|
|
81134
81587
|
}
|
|
81135
81588
|
let content;
|
|
81136
81589
|
try {
|
|
81137
|
-
content =
|
|
81590
|
+
content = fs70.readFileSync(fullPath, "utf-8");
|
|
81138
81591
|
} catch {
|
|
81139
81592
|
continue;
|
|
81140
81593
|
}
|
|
@@ -81246,7 +81699,7 @@ var search = createSwarmTool({
|
|
|
81246
81699
|
message: "Exclude pattern contains invalid Windows-specific sequence"
|
|
81247
81700
|
}, null, 2);
|
|
81248
81701
|
}
|
|
81249
|
-
if (!
|
|
81702
|
+
if (!fs70.existsSync(directory)) {
|
|
81250
81703
|
return JSON.stringify({
|
|
81251
81704
|
error: true,
|
|
81252
81705
|
type: "unknown",
|
|
@@ -81369,8 +81822,8 @@ var set_qa_gates = createSwarmTool({
|
|
|
81369
81822
|
init_tool();
|
|
81370
81823
|
init_path_security();
|
|
81371
81824
|
init_create_tool();
|
|
81372
|
-
import * as
|
|
81373
|
-
import * as
|
|
81825
|
+
import * as fs71 from "fs";
|
|
81826
|
+
import * as path86 from "path";
|
|
81374
81827
|
var WINDOWS_RESERVED_NAMES4 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
81375
81828
|
function containsWindowsAttacks4(str) {
|
|
81376
81829
|
if (/:[^\\/]/.test(str))
|
|
@@ -81384,14 +81837,14 @@ function containsWindowsAttacks4(str) {
|
|
|
81384
81837
|
}
|
|
81385
81838
|
function isPathInWorkspace4(filePath, workspace) {
|
|
81386
81839
|
try {
|
|
81387
|
-
const resolvedPath =
|
|
81388
|
-
if (!
|
|
81840
|
+
const resolvedPath = path86.resolve(workspace, filePath);
|
|
81841
|
+
if (!fs71.existsSync(resolvedPath)) {
|
|
81389
81842
|
return true;
|
|
81390
81843
|
}
|
|
81391
|
-
const realWorkspace =
|
|
81392
|
-
const realResolvedPath =
|
|
81393
|
-
const relativePath =
|
|
81394
|
-
if (relativePath.startsWith("..") ||
|
|
81844
|
+
const realWorkspace = fs71.realpathSync(workspace);
|
|
81845
|
+
const realResolvedPath = fs71.realpathSync(resolvedPath);
|
|
81846
|
+
const relativePath = path86.relative(realWorkspace, realResolvedPath);
|
|
81847
|
+
if (relativePath.startsWith("..") || path86.isAbsolute(relativePath)) {
|
|
81395
81848
|
return false;
|
|
81396
81849
|
}
|
|
81397
81850
|
return true;
|
|
@@ -81563,7 +82016,7 @@ var suggestPatch = createSwarmTool({
|
|
|
81563
82016
|
message: "changes cannot be empty"
|
|
81564
82017
|
}, null, 2);
|
|
81565
82018
|
}
|
|
81566
|
-
if (!
|
|
82019
|
+
if (!fs71.existsSync(directory)) {
|
|
81567
82020
|
return JSON.stringify({
|
|
81568
82021
|
success: false,
|
|
81569
82022
|
error: true,
|
|
@@ -81599,8 +82052,8 @@ var suggestPatch = createSwarmTool({
|
|
|
81599
82052
|
});
|
|
81600
82053
|
continue;
|
|
81601
82054
|
}
|
|
81602
|
-
const fullPath =
|
|
81603
|
-
if (!
|
|
82055
|
+
const fullPath = path86.resolve(directory, change.file);
|
|
82056
|
+
if (!fs71.existsSync(fullPath)) {
|
|
81604
82057
|
errors5.push({
|
|
81605
82058
|
success: false,
|
|
81606
82059
|
error: true,
|
|
@@ -81614,7 +82067,7 @@ var suggestPatch = createSwarmTool({
|
|
|
81614
82067
|
}
|
|
81615
82068
|
let content;
|
|
81616
82069
|
try {
|
|
81617
|
-
content =
|
|
82070
|
+
content = fs71.readFileSync(fullPath, "utf-8");
|
|
81618
82071
|
} catch (err3) {
|
|
81619
82072
|
errors5.push({
|
|
81620
82073
|
success: false,
|
|
@@ -81693,8 +82146,8 @@ var suggestPatch = createSwarmTool({
|
|
|
81693
82146
|
// src/tools/lint-spec.ts
|
|
81694
82147
|
init_spec_schema();
|
|
81695
82148
|
init_create_tool();
|
|
81696
|
-
import * as
|
|
81697
|
-
import * as
|
|
82149
|
+
import * as fs72 from "fs";
|
|
82150
|
+
import * as path87 from "path";
|
|
81698
82151
|
var SPEC_FILE_NAME = "spec.md";
|
|
81699
82152
|
var SWARM_DIR2 = ".swarm";
|
|
81700
82153
|
var OBLIGATION_KEYWORDS2 = ["MUST", "SHALL", "SHOULD", "MAY"];
|
|
@@ -81747,8 +82200,8 @@ var lint_spec = createSwarmTool({
|
|
|
81747
82200
|
async execute(_args, directory) {
|
|
81748
82201
|
const errors5 = [];
|
|
81749
82202
|
const warnings = [];
|
|
81750
|
-
const specPath =
|
|
81751
|
-
if (!
|
|
82203
|
+
const specPath = path87.join(directory, SWARM_DIR2, SPEC_FILE_NAME);
|
|
82204
|
+
if (!fs72.existsSync(specPath)) {
|
|
81752
82205
|
const result2 = {
|
|
81753
82206
|
valid: false,
|
|
81754
82207
|
specMtime: null,
|
|
@@ -81767,12 +82220,12 @@ var lint_spec = createSwarmTool({
|
|
|
81767
82220
|
}
|
|
81768
82221
|
let specMtime = null;
|
|
81769
82222
|
try {
|
|
81770
|
-
const stats =
|
|
82223
|
+
const stats = fs72.statSync(specPath);
|
|
81771
82224
|
specMtime = stats.mtime.toISOString();
|
|
81772
82225
|
} catch {}
|
|
81773
82226
|
let content;
|
|
81774
82227
|
try {
|
|
81775
|
-
content =
|
|
82228
|
+
content = fs72.readFileSync(specPath, "utf-8");
|
|
81776
82229
|
} catch (e) {
|
|
81777
82230
|
const result2 = {
|
|
81778
82231
|
valid: false,
|
|
@@ -81817,13 +82270,13 @@ var lint_spec = createSwarmTool({
|
|
|
81817
82270
|
});
|
|
81818
82271
|
// src/tools/mutation-test.ts
|
|
81819
82272
|
init_dist();
|
|
81820
|
-
import * as
|
|
81821
|
-
import * as
|
|
82273
|
+
import * as fs73 from "fs";
|
|
82274
|
+
import * as path89 from "path";
|
|
81822
82275
|
|
|
81823
82276
|
// src/mutation/engine.ts
|
|
81824
82277
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
81825
|
-
import { unlinkSync as
|
|
81826
|
-
import * as
|
|
82278
|
+
import { unlinkSync as unlinkSync12, writeFileSync as writeFileSync18 } from "fs";
|
|
82279
|
+
import * as path88 from "path";
|
|
81827
82280
|
|
|
81828
82281
|
// src/mutation/equivalence.ts
|
|
81829
82282
|
function isStaticallyEquivalent(originalCode, mutatedCode) {
|
|
@@ -81958,9 +82411,9 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
81958
82411
|
let patchFile;
|
|
81959
82412
|
try {
|
|
81960
82413
|
const safeId2 = patch.id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
81961
|
-
patchFile =
|
|
82414
|
+
patchFile = path88.join(workingDir, `.mutation_patch_${safeId2}.diff`);
|
|
81962
82415
|
try {
|
|
81963
|
-
|
|
82416
|
+
writeFileSync18(patchFile, patch.patch);
|
|
81964
82417
|
} catch (writeErr) {
|
|
81965
82418
|
error93 = `Failed to write patch file: ${writeErr}`;
|
|
81966
82419
|
outcome = "error";
|
|
@@ -82056,7 +82509,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
82056
82509
|
revertError = new Error(`Failed to revert mutation ${patch.id}: ${revertErr}. Working tree may be dirty.`);
|
|
82057
82510
|
}
|
|
82058
82511
|
try {
|
|
82059
|
-
|
|
82512
|
+
unlinkSync12(patchFile);
|
|
82060
82513
|
} catch (_unlinkErr) {}
|
|
82061
82514
|
}
|
|
82062
82515
|
}
|
|
@@ -82352,8 +82805,8 @@ var mutation_test = createSwarmTool({
|
|
|
82352
82805
|
];
|
|
82353
82806
|
for (const filePath of uniquePaths) {
|
|
82354
82807
|
try {
|
|
82355
|
-
const resolvedPath =
|
|
82356
|
-
sourceFiles.set(filePath,
|
|
82808
|
+
const resolvedPath = path89.resolve(cwd, filePath);
|
|
82809
|
+
sourceFiles.set(filePath, fs73.readFileSync(resolvedPath, "utf-8"));
|
|
82357
82810
|
} catch {}
|
|
82358
82811
|
}
|
|
82359
82812
|
const report = await executeMutationSuite(typedArgs.patches, typedArgs.test_command, typedArgs.files, cwd, undefined, undefined, sourceFiles.size > 0 ? sourceFiles : undefined);
|
|
@@ -82371,8 +82824,8 @@ var mutation_test = createSwarmTool({
|
|
|
82371
82824
|
init_dist();
|
|
82372
82825
|
init_manager2();
|
|
82373
82826
|
init_detector();
|
|
82374
|
-
import * as
|
|
82375
|
-
import * as
|
|
82827
|
+
import * as fs74 from "fs";
|
|
82828
|
+
import * as path90 from "path";
|
|
82376
82829
|
init_create_tool();
|
|
82377
82830
|
var MAX_FILE_SIZE2 = 2 * 1024 * 1024;
|
|
82378
82831
|
var BINARY_CHECK_BYTES = 8192;
|
|
@@ -82438,7 +82891,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
82438
82891
|
if (languages?.length) {
|
|
82439
82892
|
const lowerLangs = languages.map((l) => l.toLowerCase());
|
|
82440
82893
|
filesToCheck = filesToCheck.filter((file3) => {
|
|
82441
|
-
const ext =
|
|
82894
|
+
const ext = path90.extname(file3.path).toLowerCase();
|
|
82442
82895
|
const langDef = getLanguageForExtension(ext);
|
|
82443
82896
|
const fileProfile = getProfileForFile(file3.path);
|
|
82444
82897
|
const langId = fileProfile?.id || langDef?.id;
|
|
@@ -82451,7 +82904,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
82451
82904
|
let skippedCount = 0;
|
|
82452
82905
|
for (const fileInfo of filesToCheck) {
|
|
82453
82906
|
const { path: filePath } = fileInfo;
|
|
82454
|
-
const fullPath =
|
|
82907
|
+
const fullPath = path90.isAbsolute(filePath) ? filePath : path90.join(directory, filePath);
|
|
82455
82908
|
const result = {
|
|
82456
82909
|
path: filePath,
|
|
82457
82910
|
language: "",
|
|
@@ -82481,7 +82934,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
82481
82934
|
}
|
|
82482
82935
|
let content;
|
|
82483
82936
|
try {
|
|
82484
|
-
content =
|
|
82937
|
+
content = fs74.readFileSync(fullPath, "utf8");
|
|
82485
82938
|
} catch {
|
|
82486
82939
|
result.skipped_reason = "file_read_error";
|
|
82487
82940
|
skippedCount++;
|
|
@@ -82500,7 +82953,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
82500
82953
|
results.push(result);
|
|
82501
82954
|
continue;
|
|
82502
82955
|
}
|
|
82503
|
-
const ext =
|
|
82956
|
+
const ext = path90.extname(filePath).toLowerCase();
|
|
82504
82957
|
const langDef = getLanguageForExtension(ext);
|
|
82505
82958
|
result.language = profile?.id || langDef?.id || "unknown";
|
|
82506
82959
|
const errors5 = extractSyntaxErrors(parser, content);
|
|
@@ -82592,8 +83045,8 @@ init_dist();
|
|
|
82592
83045
|
init_utils();
|
|
82593
83046
|
init_create_tool();
|
|
82594
83047
|
init_path_security();
|
|
82595
|
-
import * as
|
|
82596
|
-
import * as
|
|
83048
|
+
import * as fs75 from "fs";
|
|
83049
|
+
import * as path91 from "path";
|
|
82597
83050
|
var MAX_TEXT_LENGTH = 200;
|
|
82598
83051
|
var MAX_FILE_SIZE_BYTES11 = 1024 * 1024;
|
|
82599
83052
|
var SUPPORTED_EXTENSIONS4 = new Set([
|
|
@@ -82659,9 +83112,9 @@ function validatePathsInput(paths, cwd) {
|
|
|
82659
83112
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
82660
83113
|
}
|
|
82661
83114
|
try {
|
|
82662
|
-
const resolvedPath =
|
|
82663
|
-
const normalizedCwd =
|
|
82664
|
-
const normalizedResolved =
|
|
83115
|
+
const resolvedPath = path91.resolve(paths);
|
|
83116
|
+
const normalizedCwd = path91.resolve(cwd);
|
|
83117
|
+
const normalizedResolved = path91.resolve(resolvedPath);
|
|
82665
83118
|
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
82666
83119
|
return {
|
|
82667
83120
|
error: "paths must be within the current working directory",
|
|
@@ -82677,13 +83130,13 @@ function validatePathsInput(paths, cwd) {
|
|
|
82677
83130
|
}
|
|
82678
83131
|
}
|
|
82679
83132
|
function isSupportedExtension(filePath) {
|
|
82680
|
-
const ext =
|
|
83133
|
+
const ext = path91.extname(filePath).toLowerCase();
|
|
82681
83134
|
return SUPPORTED_EXTENSIONS4.has(ext);
|
|
82682
83135
|
}
|
|
82683
83136
|
function findSourceFiles4(dir, files = []) {
|
|
82684
83137
|
let entries;
|
|
82685
83138
|
try {
|
|
82686
|
-
entries =
|
|
83139
|
+
entries = fs75.readdirSync(dir);
|
|
82687
83140
|
} catch {
|
|
82688
83141
|
return files;
|
|
82689
83142
|
}
|
|
@@ -82692,10 +83145,10 @@ function findSourceFiles4(dir, files = []) {
|
|
|
82692
83145
|
if (SKIP_DIRECTORIES5.has(entry)) {
|
|
82693
83146
|
continue;
|
|
82694
83147
|
}
|
|
82695
|
-
const fullPath =
|
|
83148
|
+
const fullPath = path91.join(dir, entry);
|
|
82696
83149
|
let stat3;
|
|
82697
83150
|
try {
|
|
82698
|
-
stat3 =
|
|
83151
|
+
stat3 = fs75.statSync(fullPath);
|
|
82699
83152
|
} catch {
|
|
82700
83153
|
continue;
|
|
82701
83154
|
}
|
|
@@ -82788,7 +83241,7 @@ var todo_extract = createSwarmTool({
|
|
|
82788
83241
|
return JSON.stringify(errorResult, null, 2);
|
|
82789
83242
|
}
|
|
82790
83243
|
const scanPath = resolvedPath;
|
|
82791
|
-
if (!
|
|
83244
|
+
if (!fs75.existsSync(scanPath)) {
|
|
82792
83245
|
const errorResult = {
|
|
82793
83246
|
error: `path not found: ${pathsInput}`,
|
|
82794
83247
|
total: 0,
|
|
@@ -82798,13 +83251,13 @@ var todo_extract = createSwarmTool({
|
|
|
82798
83251
|
return JSON.stringify(errorResult, null, 2);
|
|
82799
83252
|
}
|
|
82800
83253
|
const filesToScan = [];
|
|
82801
|
-
const stat3 =
|
|
83254
|
+
const stat3 = fs75.statSync(scanPath);
|
|
82802
83255
|
if (stat3.isFile()) {
|
|
82803
83256
|
if (isSupportedExtension(scanPath)) {
|
|
82804
83257
|
filesToScan.push(scanPath);
|
|
82805
83258
|
} else {
|
|
82806
83259
|
const errorResult = {
|
|
82807
|
-
error: `unsupported file extension: ${
|
|
83260
|
+
error: `unsupported file extension: ${path91.extname(scanPath)}`,
|
|
82808
83261
|
total: 0,
|
|
82809
83262
|
byPriority: { high: 0, medium: 0, low: 0 },
|
|
82810
83263
|
entries: []
|
|
@@ -82817,11 +83270,11 @@ var todo_extract = createSwarmTool({
|
|
|
82817
83270
|
const allEntries = [];
|
|
82818
83271
|
for (const filePath of filesToScan) {
|
|
82819
83272
|
try {
|
|
82820
|
-
const fileStat =
|
|
83273
|
+
const fileStat = fs75.statSync(filePath);
|
|
82821
83274
|
if (fileStat.size > MAX_FILE_SIZE_BYTES11) {
|
|
82822
83275
|
continue;
|
|
82823
83276
|
}
|
|
82824
|
-
const content =
|
|
83277
|
+
const content = fs75.readFileSync(filePath, "utf-8");
|
|
82825
83278
|
const entries = parseTodoComments(content, filePath, tagsSet);
|
|
82826
83279
|
allEntries.push(...entries);
|
|
82827
83280
|
} catch {}
|
|
@@ -82851,18 +83304,18 @@ init_tool();
|
|
|
82851
83304
|
init_loader();
|
|
82852
83305
|
init_schema();
|
|
82853
83306
|
init_gate_evidence();
|
|
82854
|
-
import * as
|
|
82855
|
-
import * as
|
|
83307
|
+
import * as fs77 from "fs";
|
|
83308
|
+
import * as path93 from "path";
|
|
82856
83309
|
|
|
82857
83310
|
// src/hooks/diff-scope.ts
|
|
82858
|
-
import * as
|
|
82859
|
-
import * as
|
|
83311
|
+
import * as fs76 from "fs";
|
|
83312
|
+
import * as path92 from "path";
|
|
82860
83313
|
function getDeclaredScope(taskId, directory) {
|
|
82861
83314
|
try {
|
|
82862
|
-
const planPath =
|
|
82863
|
-
if (!
|
|
83315
|
+
const planPath = path92.join(directory, ".swarm", "plan.json");
|
|
83316
|
+
if (!fs76.existsSync(planPath))
|
|
82864
83317
|
return null;
|
|
82865
|
-
const raw =
|
|
83318
|
+
const raw = fs76.readFileSync(planPath, "utf-8");
|
|
82866
83319
|
const plan = JSON.parse(raw);
|
|
82867
83320
|
for (const phase of plan.phases ?? []) {
|
|
82868
83321
|
for (const task of phase.tasks ?? []) {
|
|
@@ -82977,7 +83430,7 @@ var TIER_3_PATTERNS = [
|
|
|
82977
83430
|
];
|
|
82978
83431
|
function matchesTier3Pattern(files) {
|
|
82979
83432
|
for (const file3 of files) {
|
|
82980
|
-
const fileName =
|
|
83433
|
+
const fileName = path93.basename(file3);
|
|
82981
83434
|
for (const pattern of TIER_3_PATTERNS) {
|
|
82982
83435
|
if (pattern.test(fileName)) {
|
|
82983
83436
|
return true;
|
|
@@ -82991,8 +83444,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
82991
83444
|
if (hasActiveTurboMode()) {
|
|
82992
83445
|
const resolvedDir2 = workingDirectory;
|
|
82993
83446
|
try {
|
|
82994
|
-
const planPath =
|
|
82995
|
-
const planRaw =
|
|
83447
|
+
const planPath = path93.join(resolvedDir2, ".swarm", "plan.json");
|
|
83448
|
+
const planRaw = fs77.readFileSync(planPath, "utf-8");
|
|
82996
83449
|
const plan = JSON.parse(planRaw);
|
|
82997
83450
|
for (const planPhase of plan.phases ?? []) {
|
|
82998
83451
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -83058,8 +83511,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
83058
83511
|
}
|
|
83059
83512
|
try {
|
|
83060
83513
|
const resolvedDir2 = workingDirectory;
|
|
83061
|
-
const planPath =
|
|
83062
|
-
const planRaw =
|
|
83514
|
+
const planPath = path93.join(resolvedDir2, ".swarm", "plan.json");
|
|
83515
|
+
const planRaw = fs77.readFileSync(planPath, "utf-8");
|
|
83063
83516
|
const plan = JSON.parse(planRaw);
|
|
83064
83517
|
for (const planPhase of plan.phases ?? []) {
|
|
83065
83518
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -83276,8 +83729,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
83276
83729
|
};
|
|
83277
83730
|
}
|
|
83278
83731
|
}
|
|
83279
|
-
normalizedDir =
|
|
83280
|
-
const pathParts = normalizedDir.split(
|
|
83732
|
+
normalizedDir = path93.normalize(args2.working_directory);
|
|
83733
|
+
const pathParts = normalizedDir.split(path93.sep);
|
|
83281
83734
|
if (pathParts.includes("..")) {
|
|
83282
83735
|
return {
|
|
83283
83736
|
success: false,
|
|
@@ -83287,11 +83740,11 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
83287
83740
|
]
|
|
83288
83741
|
};
|
|
83289
83742
|
}
|
|
83290
|
-
const resolvedDir =
|
|
83743
|
+
const resolvedDir = path93.resolve(normalizedDir);
|
|
83291
83744
|
try {
|
|
83292
|
-
const realPath =
|
|
83293
|
-
const planPath =
|
|
83294
|
-
if (!
|
|
83745
|
+
const realPath = fs77.realpathSync(resolvedDir);
|
|
83746
|
+
const planPath = path93.join(realPath, ".swarm", "plan.json");
|
|
83747
|
+
if (!fs77.existsSync(planPath)) {
|
|
83295
83748
|
return {
|
|
83296
83749
|
success: false,
|
|
83297
83750
|
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
@@ -83322,12 +83775,12 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
83322
83775
|
}
|
|
83323
83776
|
if (args2.status === "in_progress") {
|
|
83324
83777
|
try {
|
|
83325
|
-
const evidencePath =
|
|
83326
|
-
|
|
83327
|
-
const fd =
|
|
83778
|
+
const evidencePath = path93.join(directory, ".swarm", "evidence", `${args2.task_id}.json`);
|
|
83779
|
+
fs77.mkdirSync(path93.dirname(evidencePath), { recursive: true });
|
|
83780
|
+
const fd = fs77.openSync(evidencePath, "wx");
|
|
83328
83781
|
let writeOk = false;
|
|
83329
83782
|
try {
|
|
83330
|
-
|
|
83783
|
+
fs77.writeSync(fd, JSON.stringify({
|
|
83331
83784
|
task_id: args2.task_id,
|
|
83332
83785
|
required_gates: ["reviewer", "test_engineer"],
|
|
83333
83786
|
gates: {},
|
|
@@ -83335,10 +83788,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
83335
83788
|
}, null, 2));
|
|
83336
83789
|
writeOk = true;
|
|
83337
83790
|
} finally {
|
|
83338
|
-
|
|
83791
|
+
fs77.closeSync(fd);
|
|
83339
83792
|
if (!writeOk) {
|
|
83340
83793
|
try {
|
|
83341
|
-
|
|
83794
|
+
fs77.unlinkSync(evidencePath);
|
|
83342
83795
|
} catch {}
|
|
83343
83796
|
}
|
|
83344
83797
|
}
|
|
@@ -83348,8 +83801,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
83348
83801
|
recoverTaskStateFromDelegations(args2.task_id);
|
|
83349
83802
|
let phaseRequiresReviewer = true;
|
|
83350
83803
|
try {
|
|
83351
|
-
const planPath =
|
|
83352
|
-
const planRaw =
|
|
83804
|
+
const planPath = path93.join(directory, ".swarm", "plan.json");
|
|
83805
|
+
const planRaw = fs77.readFileSync(planPath, "utf-8");
|
|
83353
83806
|
const plan = JSON.parse(planRaw);
|
|
83354
83807
|
const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
|
|
83355
83808
|
if (taskPhase?.required_agents && !taskPhase.required_agents.includes("reviewer")) {
|
|
@@ -83458,8 +83911,8 @@ init_utils2();
|
|
|
83458
83911
|
init_ledger();
|
|
83459
83912
|
init_manager();
|
|
83460
83913
|
init_create_tool();
|
|
83461
|
-
import
|
|
83462
|
-
import
|
|
83914
|
+
import fs78 from "fs";
|
|
83915
|
+
import path94 from "path";
|
|
83463
83916
|
function derivePlanId5(plan) {
|
|
83464
83917
|
return `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
83465
83918
|
}
|
|
@@ -83510,7 +83963,7 @@ async function executeWriteDriftEvidence(args2, directory) {
|
|
|
83510
83963
|
entries: [evidenceEntry]
|
|
83511
83964
|
};
|
|
83512
83965
|
const filename = "drift-verifier.json";
|
|
83513
|
-
const relativePath =
|
|
83966
|
+
const relativePath = path94.join("evidence", String(phase), filename);
|
|
83514
83967
|
let validatedPath;
|
|
83515
83968
|
try {
|
|
83516
83969
|
validatedPath = validateSwarmPath(directory, relativePath);
|
|
@@ -83521,12 +83974,12 @@ async function executeWriteDriftEvidence(args2, directory) {
|
|
|
83521
83974
|
message: error93 instanceof Error ? error93.message : "Failed to validate path"
|
|
83522
83975
|
}, null, 2);
|
|
83523
83976
|
}
|
|
83524
|
-
const evidenceDir =
|
|
83977
|
+
const evidenceDir = path94.dirname(validatedPath);
|
|
83525
83978
|
try {
|
|
83526
|
-
await
|
|
83527
|
-
const tempPath =
|
|
83528
|
-
await
|
|
83529
|
-
await
|
|
83979
|
+
await fs78.promises.mkdir(evidenceDir, { recursive: true });
|
|
83980
|
+
const tempPath = path94.join(evidenceDir, `.${filename}.tmp`);
|
|
83981
|
+
await fs78.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
|
|
83982
|
+
await fs78.promises.rename(tempPath, validatedPath);
|
|
83530
83983
|
let snapshotInfo;
|
|
83531
83984
|
let snapshotError;
|
|
83532
83985
|
let qaProfileLocked;
|
|
@@ -83620,8 +84073,8 @@ var write_drift_evidence = createSwarmTool({
|
|
|
83620
84073
|
init_tool();
|
|
83621
84074
|
init_utils2();
|
|
83622
84075
|
init_create_tool();
|
|
83623
|
-
import
|
|
83624
|
-
import
|
|
84076
|
+
import fs79 from "fs";
|
|
84077
|
+
import path95 from "path";
|
|
83625
84078
|
function normalizeVerdict2(verdict) {
|
|
83626
84079
|
switch (verdict) {
|
|
83627
84080
|
case "APPROVED":
|
|
@@ -83669,7 +84122,7 @@ async function executeWriteHallucinationEvidence(args2, directory) {
|
|
|
83669
84122
|
entries: [evidenceEntry]
|
|
83670
84123
|
};
|
|
83671
84124
|
const filename = "hallucination-guard.json";
|
|
83672
|
-
const relativePath =
|
|
84125
|
+
const relativePath = path95.join("evidence", String(phase), filename);
|
|
83673
84126
|
let validatedPath;
|
|
83674
84127
|
try {
|
|
83675
84128
|
validatedPath = validateSwarmPath(directory, relativePath);
|
|
@@ -83680,12 +84133,12 @@ async function executeWriteHallucinationEvidence(args2, directory) {
|
|
|
83680
84133
|
message: error93 instanceof Error ? error93.message : "Failed to validate path"
|
|
83681
84134
|
}, null, 2);
|
|
83682
84135
|
}
|
|
83683
|
-
const evidenceDir =
|
|
84136
|
+
const evidenceDir = path95.dirname(validatedPath);
|
|
83684
84137
|
try {
|
|
83685
|
-
await
|
|
83686
|
-
const tempPath =
|
|
83687
|
-
await
|
|
83688
|
-
await
|
|
84138
|
+
await fs79.promises.mkdir(evidenceDir, { recursive: true });
|
|
84139
|
+
const tempPath = path95.join(evidenceDir, `.${filename}.tmp`);
|
|
84140
|
+
await fs79.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
|
|
84141
|
+
await fs79.promises.rename(tempPath, validatedPath);
|
|
83689
84142
|
return JSON.stringify({
|
|
83690
84143
|
success: true,
|
|
83691
84144
|
phase,
|
|
@@ -83894,7 +84347,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
83894
84347
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
83895
84348
|
preflightTriggerManager = new PTM(automationConfig);
|
|
83896
84349
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
83897
|
-
const swarmDir =
|
|
84350
|
+
const swarmDir = path96.resolve(ctx.directory, ".swarm");
|
|
83898
84351
|
statusArtifact = new ASA(swarmDir);
|
|
83899
84352
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
83900
84353
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|