opencode-swarm 6.73.1 → 6.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +17 -13
- package/dist/config/evidence-schema.d.ts +115 -0
- package/dist/index.js +699 -250
- 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();
|
|
@@ -76602,8 +76610,8 @@ var placeholder_scan = createSwarmTool({
|
|
|
76602
76610
|
});
|
|
76603
76611
|
// src/tools/pre-check-batch.ts
|
|
76604
76612
|
init_dist();
|
|
76605
|
-
import * as
|
|
76606
|
-
import * as
|
|
76613
|
+
import * as fs65 from "fs";
|
|
76614
|
+
import * as path79 from "path";
|
|
76607
76615
|
init_manager2();
|
|
76608
76616
|
init_utils();
|
|
76609
76617
|
init_create_tool();
|
|
@@ -76738,8 +76746,8 @@ var quality_budget = createSwarmTool({
|
|
|
76738
76746
|
init_dist();
|
|
76739
76747
|
init_manager2();
|
|
76740
76748
|
init_detector();
|
|
76741
|
-
import * as
|
|
76742
|
-
import * as
|
|
76749
|
+
import * as fs64 from "fs";
|
|
76750
|
+
import * as path78 from "path";
|
|
76743
76751
|
import { extname as extname18 } from "path";
|
|
76744
76752
|
|
|
76745
76753
|
// src/sast/rules/c.ts
|
|
@@ -77628,6 +77636,303 @@ async function runSemgrep(options) {
|
|
|
77628
77636
|
// src/tools/sast-scan.ts
|
|
77629
77637
|
init_utils();
|
|
77630
77638
|
init_create_tool();
|
|
77639
|
+
|
|
77640
|
+
// src/tools/sast-baseline.ts
|
|
77641
|
+
init_utils2();
|
|
77642
|
+
import * as crypto8 from "crypto";
|
|
77643
|
+
import * as fs63 from "fs";
|
|
77644
|
+
import * as path77 from "path";
|
|
77645
|
+
var BASELINE_SCHEMA_VERSION = "1.0.0";
|
|
77646
|
+
var MAX_BASELINE_FINDINGS = 2000;
|
|
77647
|
+
var MAX_BASELINE_BYTES = 2 * 1048576;
|
|
77648
|
+
var LOCK_RETRY_DELAYS_MS = [50, 100, 200, 400, 800];
|
|
77649
|
+
function normalizeFindingPath(directory, file3) {
|
|
77650
|
+
const resolved = path77.isAbsolute(file3) ? file3 : path77.resolve(directory, file3);
|
|
77651
|
+
const rel = path77.relative(path77.resolve(directory), resolved);
|
|
77652
|
+
return rel.replace(/\\/g, "/");
|
|
77653
|
+
}
|
|
77654
|
+
function baselineRelPath(phase) {
|
|
77655
|
+
return path77.join("evidence", String(phase), "sast-baseline.json");
|
|
77656
|
+
}
|
|
77657
|
+
function tempRelPath(phase) {
|
|
77658
|
+
return path77.join("evidence", String(phase), `sast-baseline.json.tmp.${Date.now()}.${process.pid}`);
|
|
77659
|
+
}
|
|
77660
|
+
function lockRelPath(phase) {
|
|
77661
|
+
return path77.join("evidence", String(phase), "sast-baseline.json.lock");
|
|
77662
|
+
}
|
|
77663
|
+
function getLine(lines, idx) {
|
|
77664
|
+
if (idx < 0 || idx >= lines.length)
|
|
77665
|
+
return "";
|
|
77666
|
+
return (lines[idx] ?? "").trim();
|
|
77667
|
+
}
|
|
77668
|
+
function fingerprintFinding(finding, directory, occurrenceIndex) {
|
|
77669
|
+
const relFile = normalizeFindingPath(directory, finding.location.file);
|
|
77670
|
+
if (relFile.startsWith("..")) {
|
|
77671
|
+
return {
|
|
77672
|
+
fingerprint: `${relFile}|${finding.rule_id}|L${finding.location.line}|UNSTABLE|#${occurrenceIndex}`,
|
|
77673
|
+
stable: false
|
|
77674
|
+
};
|
|
77675
|
+
}
|
|
77676
|
+
const lineNum = finding.location.line;
|
|
77677
|
+
try {
|
|
77678
|
+
const content = fs63.readFileSync(finding.location.file, "utf-8");
|
|
77679
|
+
const lines = content.split(`
|
|
77680
|
+
`);
|
|
77681
|
+
const idx = lineNum - 1;
|
|
77682
|
+
const window2 = [
|
|
77683
|
+
getLine(lines, idx - 1),
|
|
77684
|
+
getLine(lines, idx),
|
|
77685
|
+
getLine(lines, idx + 1)
|
|
77686
|
+
].join(`
|
|
77687
|
+
`);
|
|
77688
|
+
const hash3 = crypto8.createHash("sha256").update(window2).digest("hex").slice(0, 16);
|
|
77689
|
+
return {
|
|
77690
|
+
fingerprint: `${relFile}|${finding.rule_id}|${hash3}|#${occurrenceIndex}`,
|
|
77691
|
+
stable: true
|
|
77692
|
+
};
|
|
77693
|
+
} catch {
|
|
77694
|
+
return {
|
|
77695
|
+
fingerprint: `${relFile}|${finding.rule_id}|L${lineNum}|UNSTABLE|#${occurrenceIndex}`,
|
|
77696
|
+
stable: false
|
|
77697
|
+
};
|
|
77698
|
+
}
|
|
77699
|
+
}
|
|
77700
|
+
function assignOccurrenceIndices(findings, directory) {
|
|
77701
|
+
const countMap = new Map;
|
|
77702
|
+
return findings.map((finding) => {
|
|
77703
|
+
const relFile = normalizeFindingPath(directory, finding.location.file);
|
|
77704
|
+
const lineNum = finding.location.line;
|
|
77705
|
+
let baseKey;
|
|
77706
|
+
try {
|
|
77707
|
+
if (relFile.startsWith(".."))
|
|
77708
|
+
throw new Error("escapes workspace");
|
|
77709
|
+
const content = fs63.readFileSync(finding.location.file, "utf-8");
|
|
77710
|
+
const lines = content.split(`
|
|
77711
|
+
`);
|
|
77712
|
+
const idx = lineNum - 1;
|
|
77713
|
+
const window2 = [
|
|
77714
|
+
getLine(lines, idx - 1),
|
|
77715
|
+
getLine(lines, idx),
|
|
77716
|
+
getLine(lines, idx + 1)
|
|
77717
|
+
].join(`
|
|
77718
|
+
`);
|
|
77719
|
+
const hash3 = crypto8.createHash("sha256").update(window2).digest("hex").slice(0, 16);
|
|
77720
|
+
baseKey = `${relFile}|${finding.rule_id}|${hash3}`;
|
|
77721
|
+
} catch {
|
|
77722
|
+
baseKey = `${relFile}|${finding.rule_id}|L${lineNum}|UNSTABLE`;
|
|
77723
|
+
}
|
|
77724
|
+
const occIdx = countMap.get(baseKey) ?? 0;
|
|
77725
|
+
countMap.set(baseKey, occIdx + 1);
|
|
77726
|
+
const fp = fingerprintFinding(finding, directory, occIdx);
|
|
77727
|
+
return {
|
|
77728
|
+
finding,
|
|
77729
|
+
index: occIdx,
|
|
77730
|
+
stable: fp.stable,
|
|
77731
|
+
fingerprint: fp.fingerprint
|
|
77732
|
+
};
|
|
77733
|
+
});
|
|
77734
|
+
}
|
|
77735
|
+
async function acquireLock(lockPath) {
|
|
77736
|
+
for (let attempt = 0;attempt <= LOCK_RETRY_DELAYS_MS.length; attempt++) {
|
|
77737
|
+
try {
|
|
77738
|
+
const fd = fs63.openSync(lockPath, "wx");
|
|
77739
|
+
fs63.closeSync(fd);
|
|
77740
|
+
return () => {
|
|
77741
|
+
try {
|
|
77742
|
+
fs63.unlinkSync(lockPath);
|
|
77743
|
+
} catch {}
|
|
77744
|
+
};
|
|
77745
|
+
} catch {
|
|
77746
|
+
if (attempt < LOCK_RETRY_DELAYS_MS.length) {
|
|
77747
|
+
await new Promise((resolve31) => setTimeout(resolve31, LOCK_RETRY_DELAYS_MS[attempt]));
|
|
77748
|
+
}
|
|
77749
|
+
}
|
|
77750
|
+
}
|
|
77751
|
+
return () => {};
|
|
77752
|
+
}
|
|
77753
|
+
function validatePhase(phase) {
|
|
77754
|
+
if (!Number.isInteger(phase) || phase < 1) {
|
|
77755
|
+
return "Invalid phase: must be a positive integer";
|
|
77756
|
+
}
|
|
77757
|
+
return null;
|
|
77758
|
+
}
|
|
77759
|
+
async function captureOrMergeBaseline(directory, phase, findings, engine, scannedFiles, opts) {
|
|
77760
|
+
const phaseError = validatePhase(phase);
|
|
77761
|
+
if (phaseError)
|
|
77762
|
+
return { status: "error", message: phaseError };
|
|
77763
|
+
if (!scannedFiles || scannedFiles.length === 0) {
|
|
77764
|
+
return {
|
|
77765
|
+
status: "error",
|
|
77766
|
+
message: "capture_baseline requires non-empty changed_files"
|
|
77767
|
+
};
|
|
77768
|
+
}
|
|
77769
|
+
let baselinePath;
|
|
77770
|
+
let tempPath;
|
|
77771
|
+
let lockPath;
|
|
77772
|
+
try {
|
|
77773
|
+
baselinePath = validateSwarmPath(directory, baselineRelPath(phase));
|
|
77774
|
+
tempPath = validateSwarmPath(directory, tempRelPath(phase));
|
|
77775
|
+
lockPath = validateSwarmPath(directory, lockRelPath(phase));
|
|
77776
|
+
} catch (e) {
|
|
77777
|
+
return {
|
|
77778
|
+
status: "error",
|
|
77779
|
+
message: e instanceof Error ? e.message : "Path validation failed"
|
|
77780
|
+
};
|
|
77781
|
+
}
|
|
77782
|
+
fs63.mkdirSync(path77.dirname(baselinePath), { recursive: true });
|
|
77783
|
+
const releaseLock = await acquireLock(lockPath);
|
|
77784
|
+
try {
|
|
77785
|
+
let existing = null;
|
|
77786
|
+
try {
|
|
77787
|
+
const raw = fs63.readFileSync(baselinePath, "utf-8");
|
|
77788
|
+
const parsed = JSON.parse(raw);
|
|
77789
|
+
if (parsed.schema_version === BASELINE_SCHEMA_VERSION) {
|
|
77790
|
+
existing = parsed;
|
|
77791
|
+
}
|
|
77792
|
+
} catch {}
|
|
77793
|
+
const scannedRelFiles = new Set(scannedFiles.map((f) => normalizeFindingPath(directory, f)));
|
|
77794
|
+
const indexed = assignOccurrenceIndices(findings, directory);
|
|
77795
|
+
if (existing && !opts?.force) {
|
|
77796
|
+
const prunedFingerprints = existing.fingerprints.filter((fp) => {
|
|
77797
|
+
const relFile = fp.slice(0, fp.indexOf("|"));
|
|
77798
|
+
return !scannedRelFiles.has(relFile);
|
|
77799
|
+
});
|
|
77800
|
+
const prunedSnapshot = existing.findings_snapshot.filter((f) => {
|
|
77801
|
+
return !scannedRelFiles.has(normalizeFindingPath(directory, f.location.file));
|
|
77802
|
+
});
|
|
77803
|
+
const prunedFilesIndexed = existing.files_indexed.filter((f) => !scannedRelFiles.has(f));
|
|
77804
|
+
const mergedFingerprints = [
|
|
77805
|
+
...prunedFingerprints,
|
|
77806
|
+
...indexed.map((i2) => i2.fingerprint)
|
|
77807
|
+
];
|
|
77808
|
+
const mergedSnapshot = [
|
|
77809
|
+
...prunedSnapshot,
|
|
77810
|
+
...indexed.map((i2) => i2.finding)
|
|
77811
|
+
];
|
|
77812
|
+
const mergedFilesIndexed = [
|
|
77813
|
+
...prunedFilesIndexed,
|
|
77814
|
+
...Array.from(scannedRelFiles)
|
|
77815
|
+
];
|
|
77816
|
+
const truncated2 = mergedSnapshot.length > MAX_BASELINE_FINDINGS;
|
|
77817
|
+
const cappedSnapshot2 = truncated2 ? mergedSnapshot.slice(-MAX_BASELINE_FINDINGS) : mergedSnapshot;
|
|
77818
|
+
const cappedFingerprints2 = truncated2 ? mergedFingerprints.slice(-MAX_BASELINE_FINDINGS) : mergedFingerprints;
|
|
77819
|
+
let cappedFilesIndexed = mergedFilesIndexed;
|
|
77820
|
+
if (truncated2) {
|
|
77821
|
+
const survivingFiles = new Set;
|
|
77822
|
+
for (const finding of cappedSnapshot2) {
|
|
77823
|
+
const relFile = normalizeFindingPath(directory, finding.location.file);
|
|
77824
|
+
survivingFiles.add(relFile);
|
|
77825
|
+
}
|
|
77826
|
+
cappedFilesIndexed = Array.from(survivingFiles);
|
|
77827
|
+
}
|
|
77828
|
+
const now2 = new Date().toISOString();
|
|
77829
|
+
const bundle2 = {
|
|
77830
|
+
schema_version: BASELINE_SCHEMA_VERSION,
|
|
77831
|
+
phase,
|
|
77832
|
+
created_at: existing.created_at,
|
|
77833
|
+
updated_at: now2,
|
|
77834
|
+
engine,
|
|
77835
|
+
files_indexed: cappedFilesIndexed,
|
|
77836
|
+
fingerprints: cappedFingerprints2,
|
|
77837
|
+
findings_snapshot: cappedSnapshot2,
|
|
77838
|
+
truncated: truncated2
|
|
77839
|
+
};
|
|
77840
|
+
const json4 = JSON.stringify(bundle2, null, 2);
|
|
77841
|
+
if (json4.length > MAX_BASELINE_BYTES) {
|
|
77842
|
+
return {
|
|
77843
|
+
status: "error",
|
|
77844
|
+
message: `Baseline would exceed size cap (${json4.length} bytes > ${MAX_BASELINE_BYTES})`
|
|
77845
|
+
};
|
|
77846
|
+
}
|
|
77847
|
+
fs63.writeFileSync(tempPath, json4, "utf-8");
|
|
77848
|
+
fs63.renameSync(tempPath, baselinePath);
|
|
77849
|
+
return {
|
|
77850
|
+
status: "merged",
|
|
77851
|
+
path: baselinePath,
|
|
77852
|
+
fingerprint_count: cappedFingerprints2.length
|
|
77853
|
+
};
|
|
77854
|
+
}
|
|
77855
|
+
const newFingerprints = indexed.map((i2) => i2.fingerprint);
|
|
77856
|
+
const newSnapshot = indexed.map((i2) => i2.finding);
|
|
77857
|
+
const truncated = newSnapshot.length > MAX_BASELINE_FINDINGS;
|
|
77858
|
+
const cappedSnapshot = truncated ? newSnapshot.slice(0, MAX_BASELINE_FINDINGS) : newSnapshot;
|
|
77859
|
+
const cappedFingerprints = truncated ? newFingerprints.slice(0, MAX_BASELINE_FINDINGS) : newFingerprints;
|
|
77860
|
+
const now = new Date().toISOString();
|
|
77861
|
+
const bundle = {
|
|
77862
|
+
schema_version: BASELINE_SCHEMA_VERSION,
|
|
77863
|
+
phase,
|
|
77864
|
+
created_at: now,
|
|
77865
|
+
updated_at: now,
|
|
77866
|
+
engine,
|
|
77867
|
+
files_indexed: Array.from(scannedRelFiles),
|
|
77868
|
+
fingerprints: cappedFingerprints,
|
|
77869
|
+
findings_snapshot: cappedSnapshot,
|
|
77870
|
+
truncated
|
|
77871
|
+
};
|
|
77872
|
+
const json3 = JSON.stringify(bundle, null, 2);
|
|
77873
|
+
if (json3.length > MAX_BASELINE_BYTES) {
|
|
77874
|
+
return {
|
|
77875
|
+
status: "error",
|
|
77876
|
+
message: `Baseline would exceed size cap (${json3.length} bytes > ${MAX_BASELINE_BYTES})`
|
|
77877
|
+
};
|
|
77878
|
+
}
|
|
77879
|
+
fs63.writeFileSync(tempPath, json3, "utf-8");
|
|
77880
|
+
fs63.renameSync(tempPath, baselinePath);
|
|
77881
|
+
return {
|
|
77882
|
+
status: "written",
|
|
77883
|
+
path: baselinePath,
|
|
77884
|
+
fingerprint_count: cappedFingerprints.length
|
|
77885
|
+
};
|
|
77886
|
+
} finally {
|
|
77887
|
+
releaseLock();
|
|
77888
|
+
}
|
|
77889
|
+
}
|
|
77890
|
+
function loadBaseline(directory, phase) {
|
|
77891
|
+
const phaseError = validatePhase(phase);
|
|
77892
|
+
if (phaseError) {
|
|
77893
|
+
return { status: "invalid_schema", errors: [phaseError] };
|
|
77894
|
+
}
|
|
77895
|
+
let baselinePath;
|
|
77896
|
+
try {
|
|
77897
|
+
baselinePath = validateSwarmPath(directory, baselineRelPath(phase));
|
|
77898
|
+
} catch (e) {
|
|
77899
|
+
return {
|
|
77900
|
+
status: "invalid_schema",
|
|
77901
|
+
errors: [e instanceof Error ? e.message : "Path validation failed"]
|
|
77902
|
+
};
|
|
77903
|
+
}
|
|
77904
|
+
try {
|
|
77905
|
+
const raw = fs63.readFileSync(baselinePath, "utf-8");
|
|
77906
|
+
const parsed = JSON.parse(raw);
|
|
77907
|
+
if (parsed.schema_version !== BASELINE_SCHEMA_VERSION) {
|
|
77908
|
+
return {
|
|
77909
|
+
status: "invalid_schema",
|
|
77910
|
+
errors: [`Unknown schema version: ${String(parsed.schema_version)}`]
|
|
77911
|
+
};
|
|
77912
|
+
}
|
|
77913
|
+
if (!Array.isArray(parsed.fingerprints)) {
|
|
77914
|
+
return {
|
|
77915
|
+
status: "invalid_schema",
|
|
77916
|
+
errors: ["Missing or invalid fingerprints array"]
|
|
77917
|
+
};
|
|
77918
|
+
}
|
|
77919
|
+
return {
|
|
77920
|
+
status: "found",
|
|
77921
|
+
fingerprints: new Set(parsed.fingerprints),
|
|
77922
|
+
bundle: parsed
|
|
77923
|
+
};
|
|
77924
|
+
} catch (e) {
|
|
77925
|
+
if (e.code === "ENOENT") {
|
|
77926
|
+
return { status: "not_found" };
|
|
77927
|
+
}
|
|
77928
|
+
return {
|
|
77929
|
+
status: "invalid_schema",
|
|
77930
|
+
errors: [e instanceof Error ? e.message : "Failed to read baseline"]
|
|
77931
|
+
};
|
|
77932
|
+
}
|
|
77933
|
+
}
|
|
77934
|
+
|
|
77935
|
+
// src/tools/sast-scan.ts
|
|
77631
77936
|
var MAX_FILE_SIZE_BYTES8 = 512 * 1024;
|
|
77632
77937
|
var MAX_FILES_SCANNED2 = 1000;
|
|
77633
77938
|
var MAX_FINDINGS2 = 100;
|
|
@@ -77639,17 +77944,17 @@ var SEVERITY_ORDER = {
|
|
|
77639
77944
|
};
|
|
77640
77945
|
function shouldSkipFile(filePath) {
|
|
77641
77946
|
try {
|
|
77642
|
-
const stats =
|
|
77947
|
+
const stats = fs64.statSync(filePath);
|
|
77643
77948
|
if (stats.size > MAX_FILE_SIZE_BYTES8) {
|
|
77644
77949
|
return { skip: true, reason: "file too large" };
|
|
77645
77950
|
}
|
|
77646
77951
|
if (stats.size === 0) {
|
|
77647
77952
|
return { skip: true, reason: "empty file" };
|
|
77648
77953
|
}
|
|
77649
|
-
const fd =
|
|
77954
|
+
const fd = fs64.openSync(filePath, "r");
|
|
77650
77955
|
const buffer = Buffer.alloc(8192);
|
|
77651
|
-
const bytesRead =
|
|
77652
|
-
|
|
77956
|
+
const bytesRead = fs64.readSync(fd, buffer, 0, 8192, 0);
|
|
77957
|
+
fs64.closeSync(fd);
|
|
77653
77958
|
if (bytesRead > 0) {
|
|
77654
77959
|
let nullCount = 0;
|
|
77655
77960
|
for (let i2 = 0;i2 < bytesRead; i2++) {
|
|
@@ -77688,7 +77993,7 @@ function countBySeverity(findings) {
|
|
|
77688
77993
|
}
|
|
77689
77994
|
function scanFileWithTierA(filePath, language) {
|
|
77690
77995
|
try {
|
|
77691
|
-
const content =
|
|
77996
|
+
const content = fs64.readFileSync(filePath, "utf-8");
|
|
77692
77997
|
const findings = executeRulesSync(filePath, content, language);
|
|
77693
77998
|
return findings.map((f) => ({
|
|
77694
77999
|
rule_id: f.rule_id,
|
|
@@ -77706,7 +78011,12 @@ function scanFileWithTierA(filePath, language) {
|
|
|
77706
78011
|
}
|
|
77707
78012
|
}
|
|
77708
78013
|
async function sastScan(input, directory, config3) {
|
|
77709
|
-
const {
|
|
78014
|
+
const {
|
|
78015
|
+
changed_files,
|
|
78016
|
+
severity_threshold = "medium",
|
|
78017
|
+
capture_baseline = false,
|
|
78018
|
+
phase
|
|
78019
|
+
} = input;
|
|
77710
78020
|
if (config3?.gates?.sast_scan?.enabled === false) {
|
|
77711
78021
|
return {
|
|
77712
78022
|
verdict: "pass",
|
|
@@ -77727,6 +78037,7 @@ async function sastScan(input, directory, config3) {
|
|
|
77727
78037
|
const allFindings = [];
|
|
77728
78038
|
let filesScanned = 0;
|
|
77729
78039
|
let _filesSkipped = 0;
|
|
78040
|
+
const scannedFilePaths = [];
|
|
77730
78041
|
const semgrepAvailable = isSemgrepAvailable();
|
|
77731
78042
|
const engine = semgrepAvailable ? "tier_a+tier_b" : "tier_a";
|
|
77732
78043
|
const filesByLanguage = new Map;
|
|
@@ -77735,13 +78046,13 @@ async function sastScan(input, directory, config3) {
|
|
|
77735
78046
|
_filesSkipped++;
|
|
77736
78047
|
continue;
|
|
77737
78048
|
}
|
|
77738
|
-
const resolvedPath =
|
|
77739
|
-
const resolvedDirectory =
|
|
77740
|
-
if (!resolvedPath.startsWith(resolvedDirectory +
|
|
78049
|
+
const resolvedPath = path78.isAbsolute(filePath) ? filePath : path78.resolve(directory, filePath);
|
|
78050
|
+
const resolvedDirectory = path78.resolve(directory);
|
|
78051
|
+
if (!resolvedPath.startsWith(resolvedDirectory + path78.sep) && resolvedPath !== resolvedDirectory) {
|
|
77741
78052
|
_filesSkipped++;
|
|
77742
78053
|
continue;
|
|
77743
78054
|
}
|
|
77744
|
-
if (!
|
|
78055
|
+
if (!fs64.existsSync(resolvedPath)) {
|
|
77745
78056
|
_filesSkipped++;
|
|
77746
78057
|
continue;
|
|
77747
78058
|
}
|
|
@@ -77776,6 +78087,7 @@ async function sastScan(input, directory, config3) {
|
|
|
77776
78087
|
}
|
|
77777
78088
|
}
|
|
77778
78089
|
filesScanned++;
|
|
78090
|
+
scannedFilePaths.push(resolvedPath);
|
|
77779
78091
|
if (filesScanned >= MAX_FILES_SCANNED2) {
|
|
77780
78092
|
warn(`SAST Scan: Reached maximum files limit (${MAX_FILES_SCANNED2}), stopping`);
|
|
77781
78093
|
break;
|
|
@@ -77825,14 +78137,97 @@ async function sastScan(input, directory, config3) {
|
|
|
77825
78137
|
warn(`SAST Scan: Semgrep failed, falling back to Tier A: ${error93}`);
|
|
77826
78138
|
}
|
|
77827
78139
|
}
|
|
77828
|
-
|
|
77829
|
-
|
|
77830
|
-
|
|
77831
|
-
|
|
78140
|
+
if (capture_baseline) {
|
|
78141
|
+
if (phase === undefined || !Number.isInteger(phase) || phase < 1) {
|
|
78142
|
+
const errorResult = {
|
|
78143
|
+
verdict: "fail",
|
|
78144
|
+
findings: allFindings.slice(0, MAX_FINDINGS2),
|
|
78145
|
+
summary: {
|
|
78146
|
+
engine,
|
|
78147
|
+
files_scanned: filesScanned,
|
|
78148
|
+
findings_count: Math.min(allFindings.length, MAX_FINDINGS2),
|
|
78149
|
+
findings_by_severity: countBySeverity(allFindings.slice(0, MAX_FINDINGS2))
|
|
78150
|
+
}
|
|
78151
|
+
};
|
|
78152
|
+
return errorResult;
|
|
78153
|
+
}
|
|
78154
|
+
const captureFindings = allFindings.slice(0, MAX_BASELINE_FINDINGS);
|
|
78155
|
+
const captureResult = await captureOrMergeBaseline(directory, phase, captureFindings, engine, scannedFilePaths);
|
|
78156
|
+
const captureStatus = captureResult.status === "written" ? "baseline_captured" : captureResult.status === "merged" ? "baseline_merged" : undefined;
|
|
78157
|
+
if (captureResult.status === "error") {
|
|
78158
|
+
warn(`SAST Baseline: capture failed \u2014 ${captureResult.message}`);
|
|
78159
|
+
}
|
|
78160
|
+
const finalFindings2 = allFindings.slice(0, MAX_FINDINGS2);
|
|
78161
|
+
const summary2 = {
|
|
78162
|
+
engine,
|
|
78163
|
+
files_scanned: filesScanned,
|
|
78164
|
+
findings_count: finalFindings2.length,
|
|
78165
|
+
findings_by_severity: countBySeverity(finalFindings2)
|
|
78166
|
+
};
|
|
78167
|
+
await saveEvidence(directory, "sast_scan", {
|
|
78168
|
+
task_id: "sast_scan",
|
|
78169
|
+
type: "sast",
|
|
78170
|
+
timestamp: new Date().toISOString(),
|
|
78171
|
+
agent: "sast_scan",
|
|
78172
|
+
verdict: "pass",
|
|
78173
|
+
summary: `Baseline capture: scanned ${filesScanned} files, recorded ${captureFindings.length} finding(s)`,
|
|
78174
|
+
...summary2,
|
|
78175
|
+
findings: finalFindings2,
|
|
78176
|
+
baseline_used: false
|
|
78177
|
+
});
|
|
78178
|
+
return {
|
|
78179
|
+
verdict: "pass",
|
|
78180
|
+
findings: finalFindings2,
|
|
78181
|
+
summary: summary2,
|
|
78182
|
+
status: captureStatus,
|
|
78183
|
+
finding_count: captureResult.status !== "error" ? captureResult.fingerprint_count : undefined,
|
|
78184
|
+
baseline_used: false
|
|
78185
|
+
};
|
|
78186
|
+
}
|
|
78187
|
+
let newFindings;
|
|
78188
|
+
let preExistingFindings;
|
|
78189
|
+
let baselineUsed = false;
|
|
78190
|
+
let truncatedPreExisting = false;
|
|
78191
|
+
if (phase !== undefined && Number.isInteger(phase) && phase >= 1) {
|
|
78192
|
+
const baselineResult = loadBaseline(directory, phase);
|
|
78193
|
+
if (baselineResult.status === "found") {
|
|
78194
|
+
baselineUsed = true;
|
|
78195
|
+
const baselineSet = baselineResult.fingerprints;
|
|
78196
|
+
const indexed = assignOccurrenceIndices(allFindings, directory);
|
|
78197
|
+
const rawNew = [];
|
|
78198
|
+
const rawPreExisting = [];
|
|
78199
|
+
for (const { finding, stable, fingerprint } of indexed) {
|
|
78200
|
+
if (!stable || !baselineSet.has(fingerprint)) {
|
|
78201
|
+
rawNew.push(finding);
|
|
78202
|
+
} else {
|
|
78203
|
+
rawPreExisting.push(finding);
|
|
78204
|
+
}
|
|
78205
|
+
}
|
|
78206
|
+
newFindings = rawNew.slice(0, MAX_FINDINGS2);
|
|
78207
|
+
const preExistingBudget = Math.max(0, MAX_FINDINGS2 - newFindings.length);
|
|
78208
|
+
preExistingFindings = rawPreExisting.slice(0, preExistingBudget);
|
|
78209
|
+
truncatedPreExisting = rawPreExisting.length > preExistingBudget;
|
|
78210
|
+
} else if (baselineResult.status === "invalid_schema") {
|
|
78211
|
+
warn(`SAST Baseline: could not load baseline for phase ${phase} \u2014 ${baselineResult.errors.join(", ")}. Falling back to legacy behavior.`);
|
|
78212
|
+
}
|
|
78213
|
+
}
|
|
78214
|
+
let finalFindings;
|
|
78215
|
+
if (!baselineUsed) {
|
|
78216
|
+
finalFindings = allFindings;
|
|
78217
|
+
if (allFindings.length > MAX_FINDINGS2) {
|
|
78218
|
+
finalFindings = allFindings.slice(0, MAX_FINDINGS2);
|
|
78219
|
+
warn(`SAST Scan: Found ${allFindings.length} findings, limiting to ${MAX_FINDINGS2}`);
|
|
78220
|
+
}
|
|
78221
|
+
} else {
|
|
78222
|
+
finalFindings = [
|
|
78223
|
+
...newFindings ?? [],
|
|
78224
|
+
...preExistingFindings ?? []
|
|
78225
|
+
].slice(0, MAX_FINDINGS2);
|
|
77832
78226
|
}
|
|
77833
78227
|
const findingsBySeverity = countBySeverity(finalFindings);
|
|
78228
|
+
const verdictSource = baselineUsed ? newFindings ?? [] : finalFindings;
|
|
77834
78229
|
let verdict = "pass";
|
|
77835
|
-
for (const finding of
|
|
78230
|
+
for (const finding of verdictSource) {
|
|
77836
78231
|
if (meetsThreshold(finding.severity, severity_threshold)) {
|
|
77837
78232
|
verdict = "fail";
|
|
77838
78233
|
break;
|
|
@@ -77855,42 +78250,65 @@ async function sastScan(input, directory, config3) {
|
|
|
77855
78250
|
verdict,
|
|
77856
78251
|
summary: `Scanned ${filesScanned} files, found ${finalFindings.length} finding(s) using ${engine}`,
|
|
77857
78252
|
...summary,
|
|
77858
|
-
findings: finalFindings
|
|
78253
|
+
findings: finalFindings,
|
|
78254
|
+
...baselineUsed && {
|
|
78255
|
+
new_findings: newFindings,
|
|
78256
|
+
pre_existing_findings: preExistingFindings,
|
|
78257
|
+
baseline_used: true
|
|
78258
|
+
}
|
|
77859
78259
|
});
|
|
77860
|
-
|
|
78260
|
+
const result = {
|
|
77861
78261
|
verdict,
|
|
77862
78262
|
findings: finalFindings,
|
|
77863
78263
|
summary
|
|
77864
78264
|
};
|
|
78265
|
+
if (baselineUsed) {
|
|
78266
|
+
result.new_findings = newFindings;
|
|
78267
|
+
result.pre_existing_findings = preExistingFindings;
|
|
78268
|
+
result.baseline_used = true;
|
|
78269
|
+
if (truncatedPreExisting)
|
|
78270
|
+
result.truncated_pre_existing = true;
|
|
78271
|
+
}
|
|
78272
|
+
return result;
|
|
77865
78273
|
}
|
|
77866
78274
|
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).
|
|
78275
|
+
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
78276
|
args: {
|
|
77869
78277
|
directory: tool.schema.string().describe("Directory to scan for security vulnerabilities"),
|
|
77870
78278
|
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")
|
|
78279
|
+
severity_threshold: tool.schema.enum(["low", "medium", "high", "critical"]).optional().default("medium").describe("Minimum severity that causes failure"),
|
|
78280
|
+
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."),
|
|
78281
|
+
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
78282
|
},
|
|
77873
78283
|
execute: async (args2, directory) => {
|
|
77874
78284
|
let safeArgs;
|
|
77875
78285
|
try {
|
|
77876
78286
|
if (args2 && typeof args2 === "object") {
|
|
78287
|
+
const rawPhase = args2.phase;
|
|
78288
|
+
const rawCapture = args2.capture_baseline;
|
|
77877
78289
|
safeArgs = {
|
|
77878
78290
|
directory: args2.directory,
|
|
77879
78291
|
changed_files: args2.changed_files,
|
|
77880
|
-
severity_threshold: args2.severity_threshold
|
|
78292
|
+
severity_threshold: args2.severity_threshold,
|
|
78293
|
+
phase: typeof rawPhase === "number" && Number.isInteger(rawPhase) && rawPhase >= 1 ? rawPhase : undefined,
|
|
78294
|
+
capture_baseline: typeof rawCapture === "boolean" ? rawCapture : undefined
|
|
77881
78295
|
};
|
|
77882
78296
|
} else {
|
|
77883
78297
|
safeArgs = {
|
|
77884
78298
|
directory: undefined,
|
|
77885
78299
|
changed_files: undefined,
|
|
77886
|
-
severity_threshold: undefined
|
|
78300
|
+
severity_threshold: undefined,
|
|
78301
|
+
capture_baseline: undefined,
|
|
78302
|
+
phase: undefined
|
|
77887
78303
|
};
|
|
77888
78304
|
}
|
|
77889
78305
|
} catch {
|
|
77890
78306
|
safeArgs = {
|
|
77891
78307
|
directory: undefined,
|
|
77892
78308
|
changed_files: undefined,
|
|
77893
|
-
severity_threshold: undefined
|
|
78309
|
+
severity_threshold: undefined,
|
|
78310
|
+
capture_baseline: undefined,
|
|
78311
|
+
phase: undefined
|
|
77894
78312
|
};
|
|
77895
78313
|
}
|
|
77896
78314
|
if (safeArgs.directory === undefined) {
|
|
@@ -77913,7 +78331,9 @@ var sast_scan = createSwarmTool({
|
|
|
77913
78331
|
}
|
|
77914
78332
|
const input = {
|
|
77915
78333
|
changed_files: safeArgs.changed_files ?? [],
|
|
77916
|
-
severity_threshold: safeArgs.severity_threshold ?? "medium"
|
|
78334
|
+
severity_threshold: safeArgs.severity_threshold ?? "medium",
|
|
78335
|
+
capture_baseline: safeArgs.capture_baseline,
|
|
78336
|
+
phase: safeArgs.phase
|
|
77917
78337
|
};
|
|
77918
78338
|
const result = await sastScan(input, directory);
|
|
77919
78339
|
return JSON.stringify(result, null, 2);
|
|
@@ -77939,20 +78359,20 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
77939
78359
|
let resolved;
|
|
77940
78360
|
const isWinAbs = isWindowsAbsolutePath(inputPath);
|
|
77941
78361
|
if (isWinAbs) {
|
|
77942
|
-
resolved =
|
|
77943
|
-
} else if (
|
|
77944
|
-
resolved =
|
|
78362
|
+
resolved = path79.win32.resolve(inputPath);
|
|
78363
|
+
} else if (path79.isAbsolute(inputPath)) {
|
|
78364
|
+
resolved = path79.resolve(inputPath);
|
|
77945
78365
|
} else {
|
|
77946
|
-
resolved =
|
|
78366
|
+
resolved = path79.resolve(baseDir, inputPath);
|
|
77947
78367
|
}
|
|
77948
|
-
const workspaceResolved =
|
|
77949
|
-
let
|
|
78368
|
+
const workspaceResolved = path79.resolve(workspaceDir);
|
|
78369
|
+
let relative19;
|
|
77950
78370
|
if (isWinAbs) {
|
|
77951
|
-
|
|
78371
|
+
relative19 = path79.win32.relative(workspaceResolved, resolved);
|
|
77952
78372
|
} else {
|
|
77953
|
-
|
|
78373
|
+
relative19 = path79.relative(workspaceResolved, resolved);
|
|
77954
78374
|
}
|
|
77955
|
-
if (
|
|
78375
|
+
if (relative19.startsWith("..")) {
|
|
77956
78376
|
return "path traversal detected";
|
|
77957
78377
|
}
|
|
77958
78378
|
return null;
|
|
@@ -78015,7 +78435,7 @@ async function runLintOnFiles(linter, files, workspaceDir) {
|
|
|
78015
78435
|
if (typeof file3 !== "string") {
|
|
78016
78436
|
continue;
|
|
78017
78437
|
}
|
|
78018
|
-
const resolvedPath =
|
|
78438
|
+
const resolvedPath = path79.resolve(file3);
|
|
78019
78439
|
const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
|
|
78020
78440
|
if (validationError) {
|
|
78021
78441
|
continue;
|
|
@@ -78172,7 +78592,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
78172
78592
|
skippedFiles++;
|
|
78173
78593
|
continue;
|
|
78174
78594
|
}
|
|
78175
|
-
const resolvedPath =
|
|
78595
|
+
const resolvedPath = path79.resolve(file3);
|
|
78176
78596
|
const validationError = validatePath(resolvedPath, directory, directory);
|
|
78177
78597
|
if (validationError) {
|
|
78178
78598
|
skippedFiles++;
|
|
@@ -78190,14 +78610,14 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
78190
78610
|
};
|
|
78191
78611
|
}
|
|
78192
78612
|
for (const file3 of validatedFiles) {
|
|
78193
|
-
const ext =
|
|
78613
|
+
const ext = path79.extname(file3).toLowerCase();
|
|
78194
78614
|
if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
|
|
78195
78615
|
skippedFiles++;
|
|
78196
78616
|
continue;
|
|
78197
78617
|
}
|
|
78198
78618
|
let stat3;
|
|
78199
78619
|
try {
|
|
78200
|
-
stat3 =
|
|
78620
|
+
stat3 = fs65.statSync(file3);
|
|
78201
78621
|
} catch {
|
|
78202
78622
|
skippedFiles++;
|
|
78203
78623
|
continue;
|
|
@@ -78208,7 +78628,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
78208
78628
|
}
|
|
78209
78629
|
let content;
|
|
78210
78630
|
try {
|
|
78211
|
-
const buffer =
|
|
78631
|
+
const buffer = fs65.readFileSync(file3);
|
|
78212
78632
|
if (buffer.includes(0)) {
|
|
78213
78633
|
skippedFiles++;
|
|
78214
78634
|
continue;
|
|
@@ -78274,10 +78694,14 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
78274
78694
|
return errorResult;
|
|
78275
78695
|
}
|
|
78276
78696
|
}
|
|
78277
|
-
async function runSastScanWrapped(changedFiles, directory, severityThreshold, config3) {
|
|
78697
|
+
async function runSastScanWrapped(changedFiles, directory, severityThreshold, config3, phase) {
|
|
78278
78698
|
const start2 = process.hrtime.bigint();
|
|
78279
78699
|
try {
|
|
78280
|
-
const result = await runWithTimeout2(sastScan({
|
|
78700
|
+
const result = await runWithTimeout2(sastScan({
|
|
78701
|
+
changed_files: changedFiles,
|
|
78702
|
+
severity_threshold: severityThreshold,
|
|
78703
|
+
phase
|
|
78704
|
+
}, directory, config3), TOOL_TIMEOUT_MS);
|
|
78281
78705
|
return {
|
|
78282
78706
|
ran: true,
|
|
78283
78707
|
result,
|
|
@@ -78309,6 +78733,15 @@ async function runQualityBudgetWrapped(changedFiles, directory, _config) {
|
|
|
78309
78733
|
}
|
|
78310
78734
|
}
|
|
78311
78735
|
var GATE_SEVERITIES = new Set(["high", "critical"]);
|
|
78736
|
+
var SEVERITY_ORDER_PCB = {
|
|
78737
|
+
low: 0,
|
|
78738
|
+
medium: 1,
|
|
78739
|
+
high: 2,
|
|
78740
|
+
critical: 3
|
|
78741
|
+
};
|
|
78742
|
+
function meetsThresholdForTriage(severity, threshold) {
|
|
78743
|
+
return (SEVERITY_ORDER_PCB[severity] ?? 0) >= (SEVERITY_ORDER_PCB[threshold] ?? 1);
|
|
78744
|
+
}
|
|
78312
78745
|
async function runGitDiff(args2, directory) {
|
|
78313
78746
|
try {
|
|
78314
78747
|
const proc = Bun.spawn(["git", "diff", ...args2], {
|
|
@@ -78396,7 +78829,7 @@ function classifySastFindings(findings, changedLineRanges, directory) {
|
|
|
78396
78829
|
const preexistingFindings = [];
|
|
78397
78830
|
for (const finding of findings) {
|
|
78398
78831
|
const filePath = finding.location.file;
|
|
78399
|
-
const normalised =
|
|
78832
|
+
const normalised = path79.relative(directory, filePath).replace(/\\/g, "/");
|
|
78400
78833
|
const changedLines = changedLineRanges.get(normalised);
|
|
78401
78834
|
if (changedLines?.has(finding.location.line)) {
|
|
78402
78835
|
newFindings.push(finding);
|
|
@@ -78408,7 +78841,7 @@ function classifySastFindings(findings, changedLineRanges, directory) {
|
|
|
78408
78841
|
}
|
|
78409
78842
|
async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
78410
78843
|
const effectiveWorkspaceDir = workspaceDir || input.directory || contextDir;
|
|
78411
|
-
const { files, directory, sast_threshold = "medium", config: config3 } = input;
|
|
78844
|
+
const { files, directory, sast_threshold = "medium", config: config3, phase } = input;
|
|
78412
78845
|
const dirError = validateDirectory2(directory, effectiveWorkspaceDir);
|
|
78413
78846
|
if (dirError) {
|
|
78414
78847
|
warn(`pre_check_batch: Invalid directory: ${dirError}`);
|
|
@@ -78447,7 +78880,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
|
78447
78880
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
78448
78881
|
continue;
|
|
78449
78882
|
}
|
|
78450
|
-
changedFiles.push(
|
|
78883
|
+
changedFiles.push(path79.resolve(directory, file3));
|
|
78451
78884
|
}
|
|
78452
78885
|
if (changedFiles.length === 0) {
|
|
78453
78886
|
warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
|
|
@@ -78471,7 +78904,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
|
78471
78904
|
const [lintResult, secretscanResult, sastScanResult, qualityBudgetResult] = await Promise.all([
|
|
78472
78905
|
limit(() => runLintWrapped(changedFiles, directory, config3)),
|
|
78473
78906
|
limit(() => runSecretscanWrapped(changedFiles, directory, config3)),
|
|
78474
|
-
limit(() => runSastScanWrapped(changedFiles, directory, sast_threshold, config3)),
|
|
78907
|
+
limit(() => runSastScanWrapped(changedFiles, directory, sast_threshold, config3, phase)),
|
|
78475
78908
|
limit(() => runQualityBudgetWrapped(changedFiles, directory, config3))
|
|
78476
78909
|
]);
|
|
78477
78910
|
const totalDuration = lintResult.duration_ms + secretscanResult.duration_ms + sastScanResult.duration_ms + qualityBudgetResult.duration_ms;
|
|
@@ -78516,8 +78949,20 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
|
78516
78949
|
}
|
|
78517
78950
|
let sastPreexistingFindings;
|
|
78518
78951
|
if (sastScanResult.ran && sastScanResult.result) {
|
|
78519
|
-
|
|
78520
|
-
|
|
78952
|
+
const sastResult = sastScanResult.result;
|
|
78953
|
+
if (sastResult.baseline_used) {
|
|
78954
|
+
if (sastResult.pre_existing_findings && sastResult.pre_existing_findings.length > 0) {
|
|
78955
|
+
sastPreexistingFindings = sastResult.pre_existing_findings.filter((f) => meetsThresholdForTriage(f.severity, sast_threshold));
|
|
78956
|
+
if (sastPreexistingFindings.length > 0) {
|
|
78957
|
+
warn(`pre_check_batch: SAST baseline diff found ${sastPreexistingFindings.length} pre-existing finding(s) - passing to reviewer for triage`);
|
|
78958
|
+
}
|
|
78959
|
+
}
|
|
78960
|
+
if (sastResult.verdict === "fail") {
|
|
78961
|
+
gatesPassed = false;
|
|
78962
|
+
warn(`pre_check_batch: SAST scan found new findings above threshold - GATE FAILED`);
|
|
78963
|
+
}
|
|
78964
|
+
} else if (sastResult.verdict === "fail") {
|
|
78965
|
+
const gateFindings = sastResult.findings.filter((f) => GATE_SEVERITIES.has(f.severity));
|
|
78521
78966
|
if (gateFindings.length > 0) {
|
|
78522
78967
|
const changedLineRanges = await getChangedLineRanges(directory);
|
|
78523
78968
|
const { newFindings, preexistingFindings } = classifySastFindings(gateFindings, changedLineRanges, directory);
|
|
@@ -78566,7 +79011,8 @@ var pre_check_batch = createSwarmTool({
|
|
|
78566
79011
|
args: {
|
|
78567
79012
|
files: tool.schema.array(tool.schema.string()).optional().describe("Specific files to check (optional, scans directory if not provided)"),
|
|
78568
79013
|
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)")
|
|
79014
|
+
sast_threshold: tool.schema.enum(["low", "medium", "high", "critical"]).optional().describe("Minimum severity for SAST findings to cause failure (default: medium)"),
|
|
79015
|
+
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
79016
|
},
|
|
78571
79017
|
async execute(args2, directory) {
|
|
78572
79018
|
if (!args2 || typeof args2 !== "object") {
|
|
@@ -78635,7 +79081,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
78635
79081
|
};
|
|
78636
79082
|
return JSON.stringify(errorResult, null, 2);
|
|
78637
79083
|
}
|
|
78638
|
-
const resolvedDirectory =
|
|
79084
|
+
const resolvedDirectory = path79.resolve(typedArgs.directory);
|
|
78639
79085
|
const workspaceAnchor = resolvedDirectory;
|
|
78640
79086
|
const dirError = validateDirectory2(resolvedDirectory, workspaceAnchor);
|
|
78641
79087
|
if (dirError) {
|
|
@@ -78650,11 +79096,14 @@ var pre_check_batch = createSwarmTool({
|
|
|
78650
79096
|
return JSON.stringify(errorResult, null, 2);
|
|
78651
79097
|
}
|
|
78652
79098
|
try {
|
|
79099
|
+
const rawPhase = typedArgs.phase;
|
|
79100
|
+
const safePhase = typeof rawPhase === "number" && Number.isInteger(rawPhase) && rawPhase >= 1 ? rawPhase : undefined;
|
|
78653
79101
|
const result = await runPreCheckBatch({
|
|
78654
79102
|
files: typedArgs.files,
|
|
78655
79103
|
directory: resolvedDirectory,
|
|
78656
79104
|
sast_threshold: typedArgs.sast_threshold,
|
|
78657
|
-
config: typedArgs.config
|
|
79105
|
+
config: typedArgs.config,
|
|
79106
|
+
phase: safePhase
|
|
78658
79107
|
}, workspaceAnchor, directory);
|
|
78659
79108
|
return JSON.stringify(result, null, 2);
|
|
78660
79109
|
} catch (error93) {
|
|
@@ -78673,7 +79122,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
78673
79122
|
});
|
|
78674
79123
|
// src/tools/repo-map.ts
|
|
78675
79124
|
init_dist();
|
|
78676
|
-
import * as
|
|
79125
|
+
import * as path80 from "path";
|
|
78677
79126
|
init_path_security();
|
|
78678
79127
|
init_create_tool();
|
|
78679
79128
|
var VALID_ACTIONS = [
|
|
@@ -78698,7 +79147,7 @@ function validateFile(p) {
|
|
|
78698
79147
|
return "file contains control characters";
|
|
78699
79148
|
if (containsPathTraversal(p))
|
|
78700
79149
|
return "file contains path traversal";
|
|
78701
|
-
if (
|
|
79150
|
+
if (path80.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p)) {
|
|
78702
79151
|
return "file must be a workspace-relative path, not absolute";
|
|
78703
79152
|
}
|
|
78704
79153
|
return null;
|
|
@@ -78721,8 +79170,8 @@ function ok(action, payload) {
|
|
|
78721
79170
|
}
|
|
78722
79171
|
function toRelativeGraphPath(input, workspaceRoot) {
|
|
78723
79172
|
const normalized = input.replace(/\\/g, "/");
|
|
78724
|
-
if (
|
|
78725
|
-
const rel =
|
|
79173
|
+
if (path80.isAbsolute(normalized)) {
|
|
79174
|
+
const rel = path80.relative(workspaceRoot, normalized).replace(/\\/g, "/");
|
|
78726
79175
|
return normalizeGraphPath2(rel);
|
|
78727
79176
|
}
|
|
78728
79177
|
return normalizeGraphPath2(normalized);
|
|
@@ -78866,8 +79315,8 @@ var repo_map = createSwarmTool({
|
|
|
78866
79315
|
// src/tools/req-coverage.ts
|
|
78867
79316
|
init_dist();
|
|
78868
79317
|
init_create_tool();
|
|
78869
|
-
import * as
|
|
78870
|
-
import * as
|
|
79318
|
+
import * as fs66 from "fs";
|
|
79319
|
+
import * as path81 from "path";
|
|
78871
79320
|
var SPEC_FILE = ".swarm/spec.md";
|
|
78872
79321
|
var EVIDENCE_DIR4 = ".swarm/evidence";
|
|
78873
79322
|
var OBLIGATION_KEYWORDS = ["MUST", "SHOULD", "SHALL"];
|
|
@@ -78926,19 +79375,19 @@ function extractObligationAndText(id, lineText) {
|
|
|
78926
79375
|
var PHASE_TASK_ID_REGEX = /^\d+\.\d+(\.\d+)*$/;
|
|
78927
79376
|
function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
78928
79377
|
const touchedFiles = new Set;
|
|
78929
|
-
if (!
|
|
79378
|
+
if (!fs66.existsSync(evidenceDir) || !fs66.statSync(evidenceDir).isDirectory()) {
|
|
78930
79379
|
return [];
|
|
78931
79380
|
}
|
|
78932
79381
|
let entries;
|
|
78933
79382
|
try {
|
|
78934
|
-
entries =
|
|
79383
|
+
entries = fs66.readdirSync(evidenceDir);
|
|
78935
79384
|
} catch {
|
|
78936
79385
|
return [];
|
|
78937
79386
|
}
|
|
78938
79387
|
for (const entry of entries) {
|
|
78939
|
-
const entryPath =
|
|
79388
|
+
const entryPath = path81.join(evidenceDir, entry);
|
|
78940
79389
|
try {
|
|
78941
|
-
const stat3 =
|
|
79390
|
+
const stat3 = fs66.statSync(entryPath);
|
|
78942
79391
|
if (!stat3.isDirectory()) {
|
|
78943
79392
|
continue;
|
|
78944
79393
|
}
|
|
@@ -78952,14 +79401,14 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
78952
79401
|
if (entryPhase !== String(phase)) {
|
|
78953
79402
|
continue;
|
|
78954
79403
|
}
|
|
78955
|
-
const evidenceFilePath =
|
|
79404
|
+
const evidenceFilePath = path81.join(entryPath, "evidence.json");
|
|
78956
79405
|
try {
|
|
78957
|
-
const resolvedPath =
|
|
78958
|
-
const evidenceDirResolved =
|
|
78959
|
-
if (!resolvedPath.startsWith(evidenceDirResolved +
|
|
79406
|
+
const resolvedPath = path81.resolve(evidenceFilePath);
|
|
79407
|
+
const evidenceDirResolved = path81.resolve(evidenceDir);
|
|
79408
|
+
if (!resolvedPath.startsWith(evidenceDirResolved + path81.sep)) {
|
|
78960
79409
|
continue;
|
|
78961
79410
|
}
|
|
78962
|
-
const stat3 =
|
|
79411
|
+
const stat3 = fs66.lstatSync(evidenceFilePath);
|
|
78963
79412
|
if (!stat3.isFile()) {
|
|
78964
79413
|
continue;
|
|
78965
79414
|
}
|
|
@@ -78971,7 +79420,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
78971
79420
|
}
|
|
78972
79421
|
let content;
|
|
78973
79422
|
try {
|
|
78974
|
-
content =
|
|
79423
|
+
content = fs66.readFileSync(evidenceFilePath, "utf-8");
|
|
78975
79424
|
} catch {
|
|
78976
79425
|
continue;
|
|
78977
79426
|
}
|
|
@@ -78990,7 +79439,7 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
78990
79439
|
if (Array.isArray(diffEntry.files_changed)) {
|
|
78991
79440
|
for (const file3 of diffEntry.files_changed) {
|
|
78992
79441
|
if (typeof file3 === "string") {
|
|
78993
|
-
touchedFiles.add(
|
|
79442
|
+
touchedFiles.add(path81.resolve(cwd, file3));
|
|
78994
79443
|
}
|
|
78995
79444
|
}
|
|
78996
79445
|
}
|
|
@@ -79003,12 +79452,12 @@ function readTouchedFiles(evidenceDir, phase, cwd) {
|
|
|
79003
79452
|
}
|
|
79004
79453
|
function searchFileForKeywords(filePath, keywords, cwd) {
|
|
79005
79454
|
try {
|
|
79006
|
-
const resolvedPath =
|
|
79007
|
-
const cwdResolved =
|
|
79455
|
+
const resolvedPath = path81.resolve(filePath);
|
|
79456
|
+
const cwdResolved = path81.resolve(cwd);
|
|
79008
79457
|
if (!resolvedPath.startsWith(cwdResolved)) {
|
|
79009
79458
|
return false;
|
|
79010
79459
|
}
|
|
79011
|
-
const content =
|
|
79460
|
+
const content = fs66.readFileSync(resolvedPath, "utf-8");
|
|
79012
79461
|
for (const keyword of keywords) {
|
|
79013
79462
|
const regex = new RegExp(`\\b${keyword}\\b`, "i");
|
|
79014
79463
|
if (regex.test(content)) {
|
|
@@ -79138,10 +79587,10 @@ var req_coverage = createSwarmTool({
|
|
|
79138
79587
|
}, null, 2);
|
|
79139
79588
|
}
|
|
79140
79589
|
const cwd = inputDirectory || directory;
|
|
79141
|
-
const specPath =
|
|
79590
|
+
const specPath = path81.join(cwd, SPEC_FILE);
|
|
79142
79591
|
let specContent;
|
|
79143
79592
|
try {
|
|
79144
|
-
specContent =
|
|
79593
|
+
specContent = fs66.readFileSync(specPath, "utf-8");
|
|
79145
79594
|
} catch (readError) {
|
|
79146
79595
|
return JSON.stringify({
|
|
79147
79596
|
success: false,
|
|
@@ -79165,7 +79614,7 @@ var req_coverage = createSwarmTool({
|
|
|
79165
79614
|
message: "No FR requirements found in spec.md"
|
|
79166
79615
|
}, null, 2);
|
|
79167
79616
|
}
|
|
79168
|
-
const evidenceDir =
|
|
79617
|
+
const evidenceDir = path81.join(cwd, EVIDENCE_DIR4);
|
|
79169
79618
|
const touchedFiles = readTouchedFiles(evidenceDir, phase, cwd);
|
|
79170
79619
|
const analyzedRequirements = [];
|
|
79171
79620
|
let coveredCount = 0;
|
|
@@ -79191,12 +79640,12 @@ var req_coverage = createSwarmTool({
|
|
|
79191
79640
|
requirements: analyzedRequirements
|
|
79192
79641
|
};
|
|
79193
79642
|
const reportFilename = `req-coverage-phase-${phase}.json`;
|
|
79194
|
-
const reportPath =
|
|
79643
|
+
const reportPath = path81.join(evidenceDir, reportFilename);
|
|
79195
79644
|
try {
|
|
79196
|
-
if (!
|
|
79197
|
-
|
|
79645
|
+
if (!fs66.existsSync(evidenceDir)) {
|
|
79646
|
+
fs66.mkdirSync(evidenceDir, { recursive: true });
|
|
79198
79647
|
}
|
|
79199
|
-
|
|
79648
|
+
fs66.writeFileSync(reportPath, JSON.stringify(result, null, 2), "utf-8");
|
|
79200
79649
|
} catch (writeError) {
|
|
79201
79650
|
console.warn(`Failed to write coverage report: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
79202
79651
|
}
|
|
@@ -79274,9 +79723,9 @@ ${paginatedContent}`;
|
|
|
79274
79723
|
});
|
|
79275
79724
|
// src/tools/save-plan.ts
|
|
79276
79725
|
init_tool();
|
|
79277
|
-
import * as
|
|
79278
|
-
import * as
|
|
79279
|
-
import * as
|
|
79726
|
+
import * as crypto9 from "crypto";
|
|
79727
|
+
import * as fs67 from "fs";
|
|
79728
|
+
import * as path82 from "path";
|
|
79280
79729
|
init_checkpoint3();
|
|
79281
79730
|
init_ledger();
|
|
79282
79731
|
init_manager();
|
|
@@ -79357,12 +79806,12 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
79357
79806
|
let specMtime;
|
|
79358
79807
|
let specHash;
|
|
79359
79808
|
if (process.env.SWARM_SKIP_SPEC_GATE !== "1") {
|
|
79360
|
-
const specPath =
|
|
79809
|
+
const specPath = path82.join(targetWorkspace, ".swarm", "spec.md");
|
|
79361
79810
|
try {
|
|
79362
|
-
const stat3 = await
|
|
79811
|
+
const stat3 = await fs67.promises.stat(specPath);
|
|
79363
79812
|
specMtime = stat3.mtime.toISOString();
|
|
79364
|
-
const content = await
|
|
79365
|
-
specHash =
|
|
79813
|
+
const content = await fs67.promises.readFile(specPath, "utf8");
|
|
79814
|
+
specHash = crypto9.createHash("sha256").update(content).digest("hex");
|
|
79366
79815
|
} catch {
|
|
79367
79816
|
return {
|
|
79368
79817
|
success: false,
|
|
@@ -79439,14 +79888,14 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
79439
79888
|
}
|
|
79440
79889
|
await writeCheckpoint(dir).catch(() => {});
|
|
79441
79890
|
try {
|
|
79442
|
-
const markerPath =
|
|
79891
|
+
const markerPath = path82.join(dir, ".swarm", ".plan-write-marker");
|
|
79443
79892
|
const marker = JSON.stringify({
|
|
79444
79893
|
source: "save_plan",
|
|
79445
79894
|
timestamp: new Date().toISOString(),
|
|
79446
79895
|
phases_count: plan.phases.length,
|
|
79447
79896
|
tasks_count: tasksCount
|
|
79448
79897
|
});
|
|
79449
|
-
await
|
|
79898
|
+
await fs67.promises.writeFile(markerPath, marker, "utf8");
|
|
79450
79899
|
} catch {}
|
|
79451
79900
|
const warnings = [];
|
|
79452
79901
|
let criticReviewFound = false;
|
|
@@ -79462,7 +79911,7 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
79462
79911
|
return {
|
|
79463
79912
|
success: true,
|
|
79464
79913
|
message: "Plan saved successfully",
|
|
79465
|
-
plan_path:
|
|
79914
|
+
plan_path: path82.join(dir, ".swarm", "plan.json"),
|
|
79466
79915
|
phases_count: plan.phases.length,
|
|
79467
79916
|
tasks_count: tasksCount,
|
|
79468
79917
|
...warnings.length > 0 ? { warnings } : {}
|
|
@@ -79507,8 +79956,8 @@ var save_plan = createSwarmTool({
|
|
|
79507
79956
|
// src/tools/sbom-generate.ts
|
|
79508
79957
|
init_dist();
|
|
79509
79958
|
init_manager2();
|
|
79510
|
-
import * as
|
|
79511
|
-
import * as
|
|
79959
|
+
import * as fs68 from "fs";
|
|
79960
|
+
import * as path83 from "path";
|
|
79512
79961
|
|
|
79513
79962
|
// src/sbom/detectors/index.ts
|
|
79514
79963
|
init_utils();
|
|
@@ -80356,9 +80805,9 @@ function findManifestFiles(rootDir) {
|
|
|
80356
80805
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
80357
80806
|
function searchDir(dir) {
|
|
80358
80807
|
try {
|
|
80359
|
-
const entries =
|
|
80808
|
+
const entries = fs68.readdirSync(dir, { withFileTypes: true });
|
|
80360
80809
|
for (const entry of entries) {
|
|
80361
|
-
const fullPath =
|
|
80810
|
+
const fullPath = path83.join(dir, entry.name);
|
|
80362
80811
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
80363
80812
|
continue;
|
|
80364
80813
|
}
|
|
@@ -80367,7 +80816,7 @@ function findManifestFiles(rootDir) {
|
|
|
80367
80816
|
} else if (entry.isFile()) {
|
|
80368
80817
|
for (const pattern of patterns) {
|
|
80369
80818
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
80370
|
-
manifestFiles.push(
|
|
80819
|
+
manifestFiles.push(path83.relative(rootDir, fullPath));
|
|
80371
80820
|
break;
|
|
80372
80821
|
}
|
|
80373
80822
|
}
|
|
@@ -80383,13 +80832,13 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
80383
80832
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
80384
80833
|
for (const dir of directories) {
|
|
80385
80834
|
try {
|
|
80386
|
-
const entries =
|
|
80835
|
+
const entries = fs68.readdirSync(dir, { withFileTypes: true });
|
|
80387
80836
|
for (const entry of entries) {
|
|
80388
|
-
const fullPath =
|
|
80837
|
+
const fullPath = path83.join(dir, entry.name);
|
|
80389
80838
|
if (entry.isFile()) {
|
|
80390
80839
|
for (const pattern of patterns) {
|
|
80391
80840
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
80392
|
-
found.push(
|
|
80841
|
+
found.push(path83.relative(workingDir, fullPath));
|
|
80393
80842
|
break;
|
|
80394
80843
|
}
|
|
80395
80844
|
}
|
|
@@ -80402,11 +80851,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
80402
80851
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
80403
80852
|
const dirs = new Set;
|
|
80404
80853
|
for (const file3 of changedFiles) {
|
|
80405
|
-
let currentDir =
|
|
80854
|
+
let currentDir = path83.dirname(file3);
|
|
80406
80855
|
while (true) {
|
|
80407
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
80408
|
-
dirs.add(
|
|
80409
|
-
const parent =
|
|
80856
|
+
if (currentDir && currentDir !== "." && currentDir !== path83.sep) {
|
|
80857
|
+
dirs.add(path83.join(workingDir, currentDir));
|
|
80858
|
+
const parent = path83.dirname(currentDir);
|
|
80410
80859
|
if (parent === currentDir)
|
|
80411
80860
|
break;
|
|
80412
80861
|
currentDir = parent;
|
|
@@ -80420,7 +80869,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
|
80420
80869
|
}
|
|
80421
80870
|
function ensureOutputDir(outputDir) {
|
|
80422
80871
|
try {
|
|
80423
|
-
|
|
80872
|
+
fs68.mkdirSync(outputDir, { recursive: true });
|
|
80424
80873
|
} catch (error93) {
|
|
80425
80874
|
if (!error93 || error93.code !== "EEXIST") {
|
|
80426
80875
|
throw error93;
|
|
@@ -80490,7 +80939,7 @@ var sbom_generate = createSwarmTool({
|
|
|
80490
80939
|
const changedFiles = obj.changed_files;
|
|
80491
80940
|
const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
|
|
80492
80941
|
const workingDir = directory;
|
|
80493
|
-
const outputDir =
|
|
80942
|
+
const outputDir = path83.isAbsolute(relativeOutputDir) ? relativeOutputDir : path83.join(workingDir, relativeOutputDir);
|
|
80494
80943
|
let manifestFiles = [];
|
|
80495
80944
|
if (scope === "all") {
|
|
80496
80945
|
manifestFiles = findManifestFiles(workingDir);
|
|
@@ -80513,11 +80962,11 @@ var sbom_generate = createSwarmTool({
|
|
|
80513
80962
|
const processedFiles = [];
|
|
80514
80963
|
for (const manifestFile of manifestFiles) {
|
|
80515
80964
|
try {
|
|
80516
|
-
const fullPath =
|
|
80517
|
-
if (!
|
|
80965
|
+
const fullPath = path83.isAbsolute(manifestFile) ? manifestFile : path83.join(workingDir, manifestFile);
|
|
80966
|
+
if (!fs68.existsSync(fullPath)) {
|
|
80518
80967
|
continue;
|
|
80519
80968
|
}
|
|
80520
|
-
const content =
|
|
80969
|
+
const content = fs68.readFileSync(fullPath, "utf-8");
|
|
80521
80970
|
const components = detectComponents(manifestFile, content);
|
|
80522
80971
|
processedFiles.push(manifestFile);
|
|
80523
80972
|
if (components.length > 0) {
|
|
@@ -80530,8 +80979,8 @@ var sbom_generate = createSwarmTool({
|
|
|
80530
80979
|
const bom = generateCycloneDX(allComponents);
|
|
80531
80980
|
const bomJson = serializeCycloneDX(bom);
|
|
80532
80981
|
const filename = generateSbomFilename();
|
|
80533
|
-
const outputPath =
|
|
80534
|
-
|
|
80982
|
+
const outputPath = path83.join(outputDir, filename);
|
|
80983
|
+
fs68.writeFileSync(outputPath, bomJson, "utf-8");
|
|
80535
80984
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
80536
80985
|
try {
|
|
80537
80986
|
const timestamp = new Date().toISOString();
|
|
@@ -80573,8 +81022,8 @@ var sbom_generate = createSwarmTool({
|
|
|
80573
81022
|
// src/tools/schema-drift.ts
|
|
80574
81023
|
init_dist();
|
|
80575
81024
|
init_create_tool();
|
|
80576
|
-
import * as
|
|
80577
|
-
import * as
|
|
81025
|
+
import * as fs69 from "fs";
|
|
81026
|
+
import * as path84 from "path";
|
|
80578
81027
|
var SPEC_CANDIDATES = [
|
|
80579
81028
|
"openapi.json",
|
|
80580
81029
|
"openapi.yaml",
|
|
@@ -80606,28 +81055,28 @@ function normalizePath3(p) {
|
|
|
80606
81055
|
}
|
|
80607
81056
|
function discoverSpecFile(cwd, specFileArg) {
|
|
80608
81057
|
if (specFileArg) {
|
|
80609
|
-
const resolvedPath =
|
|
80610
|
-
const normalizedCwd = cwd.endsWith(
|
|
81058
|
+
const resolvedPath = path84.resolve(cwd, specFileArg);
|
|
81059
|
+
const normalizedCwd = cwd.endsWith(path84.sep) ? cwd : cwd + path84.sep;
|
|
80611
81060
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
80612
81061
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
80613
81062
|
}
|
|
80614
|
-
const ext =
|
|
81063
|
+
const ext = path84.extname(resolvedPath).toLowerCase();
|
|
80615
81064
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
80616
81065
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
80617
81066
|
}
|
|
80618
|
-
const stats =
|
|
81067
|
+
const stats = fs69.statSync(resolvedPath);
|
|
80619
81068
|
if (stats.size > MAX_SPEC_SIZE) {
|
|
80620
81069
|
throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
|
|
80621
81070
|
}
|
|
80622
|
-
if (!
|
|
81071
|
+
if (!fs69.existsSync(resolvedPath)) {
|
|
80623
81072
|
throw new Error(`Spec file not found: ${resolvedPath}`);
|
|
80624
81073
|
}
|
|
80625
81074
|
return resolvedPath;
|
|
80626
81075
|
}
|
|
80627
81076
|
for (const candidate of SPEC_CANDIDATES) {
|
|
80628
|
-
const candidatePath =
|
|
80629
|
-
if (
|
|
80630
|
-
const stats =
|
|
81077
|
+
const candidatePath = path84.resolve(cwd, candidate);
|
|
81078
|
+
if (fs69.existsSync(candidatePath)) {
|
|
81079
|
+
const stats = fs69.statSync(candidatePath);
|
|
80631
81080
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
80632
81081
|
return candidatePath;
|
|
80633
81082
|
}
|
|
@@ -80636,8 +81085,8 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
80636
81085
|
return null;
|
|
80637
81086
|
}
|
|
80638
81087
|
function parseSpec(specFile) {
|
|
80639
|
-
const content =
|
|
80640
|
-
const ext =
|
|
81088
|
+
const content = fs69.readFileSync(specFile, "utf-8");
|
|
81089
|
+
const ext = path84.extname(specFile).toLowerCase();
|
|
80641
81090
|
if (ext === ".json") {
|
|
80642
81091
|
return parseJsonSpec(content);
|
|
80643
81092
|
}
|
|
@@ -80708,12 +81157,12 @@ function extractRoutes(cwd) {
|
|
|
80708
81157
|
function walkDir(dir) {
|
|
80709
81158
|
let entries;
|
|
80710
81159
|
try {
|
|
80711
|
-
entries =
|
|
81160
|
+
entries = fs69.readdirSync(dir, { withFileTypes: true });
|
|
80712
81161
|
} catch {
|
|
80713
81162
|
return;
|
|
80714
81163
|
}
|
|
80715
81164
|
for (const entry of entries) {
|
|
80716
|
-
const fullPath =
|
|
81165
|
+
const fullPath = path84.join(dir, entry.name);
|
|
80717
81166
|
if (entry.isSymbolicLink()) {
|
|
80718
81167
|
continue;
|
|
80719
81168
|
}
|
|
@@ -80723,7 +81172,7 @@ function extractRoutes(cwd) {
|
|
|
80723
81172
|
}
|
|
80724
81173
|
walkDir(fullPath);
|
|
80725
81174
|
} else if (entry.isFile()) {
|
|
80726
|
-
const ext =
|
|
81175
|
+
const ext = path84.extname(entry.name).toLowerCase();
|
|
80727
81176
|
const baseName = entry.name.toLowerCase();
|
|
80728
81177
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
80729
81178
|
continue;
|
|
@@ -80741,7 +81190,7 @@ function extractRoutes(cwd) {
|
|
|
80741
81190
|
}
|
|
80742
81191
|
function extractRoutesFromFile(filePath) {
|
|
80743
81192
|
const routes = [];
|
|
80744
|
-
const content =
|
|
81193
|
+
const content = fs69.readFileSync(filePath, "utf-8");
|
|
80745
81194
|
const lines = content.split(/\r?\n/);
|
|
80746
81195
|
const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
80747
81196
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
@@ -80889,8 +81338,8 @@ var schema_drift = createSwarmTool({
|
|
|
80889
81338
|
init_tool();
|
|
80890
81339
|
init_path_security();
|
|
80891
81340
|
init_create_tool();
|
|
80892
|
-
import * as
|
|
80893
|
-
import * as
|
|
81341
|
+
import * as fs70 from "fs";
|
|
81342
|
+
import * as path85 from "path";
|
|
80894
81343
|
var DEFAULT_MAX_RESULTS = 100;
|
|
80895
81344
|
var DEFAULT_MAX_LINES = 200;
|
|
80896
81345
|
var REGEX_TIMEOUT_MS = 5000;
|
|
@@ -80926,11 +81375,11 @@ function containsWindowsAttacks3(str) {
|
|
|
80926
81375
|
}
|
|
80927
81376
|
function isPathInWorkspace3(filePath, workspace) {
|
|
80928
81377
|
try {
|
|
80929
|
-
const resolvedPath =
|
|
80930
|
-
const realWorkspace =
|
|
80931
|
-
const realResolvedPath =
|
|
80932
|
-
const relativePath =
|
|
80933
|
-
if (relativePath.startsWith("..") ||
|
|
81378
|
+
const resolvedPath = path85.resolve(workspace, filePath);
|
|
81379
|
+
const realWorkspace = fs70.realpathSync(workspace);
|
|
81380
|
+
const realResolvedPath = fs70.realpathSync(resolvedPath);
|
|
81381
|
+
const relativePath = path85.relative(realWorkspace, realResolvedPath);
|
|
81382
|
+
if (relativePath.startsWith("..") || path85.isAbsolute(relativePath)) {
|
|
80934
81383
|
return false;
|
|
80935
81384
|
}
|
|
80936
81385
|
return true;
|
|
@@ -80943,12 +81392,12 @@ function validatePathForRead2(filePath, workspace) {
|
|
|
80943
81392
|
}
|
|
80944
81393
|
function findRgInEnvPath() {
|
|
80945
81394
|
const searchPath = process.env.PATH ?? "";
|
|
80946
|
-
for (const dir of searchPath.split(
|
|
81395
|
+
for (const dir of searchPath.split(path85.delimiter)) {
|
|
80947
81396
|
if (!dir)
|
|
80948
81397
|
continue;
|
|
80949
81398
|
const isWindows = process.platform === "win32";
|
|
80950
|
-
const candidate =
|
|
80951
|
-
if (
|
|
81399
|
+
const candidate = path85.join(dir, isWindows ? "rg.exe" : "rg");
|
|
81400
|
+
if (fs70.existsSync(candidate))
|
|
80952
81401
|
return candidate;
|
|
80953
81402
|
}
|
|
80954
81403
|
return null;
|
|
@@ -81002,7 +81451,7 @@ async function ripgrepSearch(opts) {
|
|
|
81002
81451
|
stderr: "pipe",
|
|
81003
81452
|
cwd: opts.workspace
|
|
81004
81453
|
});
|
|
81005
|
-
const timeout = new Promise((
|
|
81454
|
+
const timeout = new Promise((resolve36) => setTimeout(() => resolve36("timeout"), REGEX_TIMEOUT_MS));
|
|
81006
81455
|
const exitPromise = proc.exited;
|
|
81007
81456
|
const result = await Promise.race([exitPromise, timeout]);
|
|
81008
81457
|
if (result === "timeout") {
|
|
@@ -81075,10 +81524,10 @@ function collectFiles(dir, workspace, includeGlobs, excludeGlobs) {
|
|
|
81075
81524
|
return files;
|
|
81076
81525
|
}
|
|
81077
81526
|
try {
|
|
81078
|
-
const entries =
|
|
81527
|
+
const entries = fs70.readdirSync(dir, { withFileTypes: true });
|
|
81079
81528
|
for (const entry of entries) {
|
|
81080
|
-
const fullPath =
|
|
81081
|
-
const relativePath =
|
|
81529
|
+
const fullPath = path85.join(dir, entry.name);
|
|
81530
|
+
const relativePath = path85.relative(workspace, fullPath);
|
|
81082
81531
|
if (!validatePathForRead2(fullPath, workspace)) {
|
|
81083
81532
|
continue;
|
|
81084
81533
|
}
|
|
@@ -81119,13 +81568,13 @@ async function fallbackSearch(opts) {
|
|
|
81119
81568
|
const matches = [];
|
|
81120
81569
|
let total = 0;
|
|
81121
81570
|
for (const file3 of files) {
|
|
81122
|
-
const fullPath =
|
|
81571
|
+
const fullPath = path85.join(opts.workspace, file3);
|
|
81123
81572
|
if (!validatePathForRead2(fullPath, opts.workspace)) {
|
|
81124
81573
|
continue;
|
|
81125
81574
|
}
|
|
81126
81575
|
let stats;
|
|
81127
81576
|
try {
|
|
81128
|
-
stats =
|
|
81577
|
+
stats = fs70.statSync(fullPath);
|
|
81129
81578
|
if (stats.size > MAX_FILE_SIZE_BYTES10) {
|
|
81130
81579
|
continue;
|
|
81131
81580
|
}
|
|
@@ -81134,7 +81583,7 @@ async function fallbackSearch(opts) {
|
|
|
81134
81583
|
}
|
|
81135
81584
|
let content;
|
|
81136
81585
|
try {
|
|
81137
|
-
content =
|
|
81586
|
+
content = fs70.readFileSync(fullPath, "utf-8");
|
|
81138
81587
|
} catch {
|
|
81139
81588
|
continue;
|
|
81140
81589
|
}
|
|
@@ -81246,7 +81695,7 @@ var search = createSwarmTool({
|
|
|
81246
81695
|
message: "Exclude pattern contains invalid Windows-specific sequence"
|
|
81247
81696
|
}, null, 2);
|
|
81248
81697
|
}
|
|
81249
|
-
if (!
|
|
81698
|
+
if (!fs70.existsSync(directory)) {
|
|
81250
81699
|
return JSON.stringify({
|
|
81251
81700
|
error: true,
|
|
81252
81701
|
type: "unknown",
|
|
@@ -81369,8 +81818,8 @@ var set_qa_gates = createSwarmTool({
|
|
|
81369
81818
|
init_tool();
|
|
81370
81819
|
init_path_security();
|
|
81371
81820
|
init_create_tool();
|
|
81372
|
-
import * as
|
|
81373
|
-
import * as
|
|
81821
|
+
import * as fs71 from "fs";
|
|
81822
|
+
import * as path86 from "path";
|
|
81374
81823
|
var WINDOWS_RESERVED_NAMES4 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
81375
81824
|
function containsWindowsAttacks4(str) {
|
|
81376
81825
|
if (/:[^\\/]/.test(str))
|
|
@@ -81384,14 +81833,14 @@ function containsWindowsAttacks4(str) {
|
|
|
81384
81833
|
}
|
|
81385
81834
|
function isPathInWorkspace4(filePath, workspace) {
|
|
81386
81835
|
try {
|
|
81387
|
-
const resolvedPath =
|
|
81388
|
-
if (!
|
|
81836
|
+
const resolvedPath = path86.resolve(workspace, filePath);
|
|
81837
|
+
if (!fs71.existsSync(resolvedPath)) {
|
|
81389
81838
|
return true;
|
|
81390
81839
|
}
|
|
81391
|
-
const realWorkspace =
|
|
81392
|
-
const realResolvedPath =
|
|
81393
|
-
const relativePath =
|
|
81394
|
-
if (relativePath.startsWith("..") ||
|
|
81840
|
+
const realWorkspace = fs71.realpathSync(workspace);
|
|
81841
|
+
const realResolvedPath = fs71.realpathSync(resolvedPath);
|
|
81842
|
+
const relativePath = path86.relative(realWorkspace, realResolvedPath);
|
|
81843
|
+
if (relativePath.startsWith("..") || path86.isAbsolute(relativePath)) {
|
|
81395
81844
|
return false;
|
|
81396
81845
|
}
|
|
81397
81846
|
return true;
|
|
@@ -81563,7 +82012,7 @@ var suggestPatch = createSwarmTool({
|
|
|
81563
82012
|
message: "changes cannot be empty"
|
|
81564
82013
|
}, null, 2);
|
|
81565
82014
|
}
|
|
81566
|
-
if (!
|
|
82015
|
+
if (!fs71.existsSync(directory)) {
|
|
81567
82016
|
return JSON.stringify({
|
|
81568
82017
|
success: false,
|
|
81569
82018
|
error: true,
|
|
@@ -81599,8 +82048,8 @@ var suggestPatch = createSwarmTool({
|
|
|
81599
82048
|
});
|
|
81600
82049
|
continue;
|
|
81601
82050
|
}
|
|
81602
|
-
const fullPath =
|
|
81603
|
-
if (!
|
|
82051
|
+
const fullPath = path86.resolve(directory, change.file);
|
|
82052
|
+
if (!fs71.existsSync(fullPath)) {
|
|
81604
82053
|
errors5.push({
|
|
81605
82054
|
success: false,
|
|
81606
82055
|
error: true,
|
|
@@ -81614,7 +82063,7 @@ var suggestPatch = createSwarmTool({
|
|
|
81614
82063
|
}
|
|
81615
82064
|
let content;
|
|
81616
82065
|
try {
|
|
81617
|
-
content =
|
|
82066
|
+
content = fs71.readFileSync(fullPath, "utf-8");
|
|
81618
82067
|
} catch (err3) {
|
|
81619
82068
|
errors5.push({
|
|
81620
82069
|
success: false,
|
|
@@ -81693,8 +82142,8 @@ var suggestPatch = createSwarmTool({
|
|
|
81693
82142
|
// src/tools/lint-spec.ts
|
|
81694
82143
|
init_spec_schema();
|
|
81695
82144
|
init_create_tool();
|
|
81696
|
-
import * as
|
|
81697
|
-
import * as
|
|
82145
|
+
import * as fs72 from "fs";
|
|
82146
|
+
import * as path87 from "path";
|
|
81698
82147
|
var SPEC_FILE_NAME = "spec.md";
|
|
81699
82148
|
var SWARM_DIR2 = ".swarm";
|
|
81700
82149
|
var OBLIGATION_KEYWORDS2 = ["MUST", "SHALL", "SHOULD", "MAY"];
|
|
@@ -81747,8 +82196,8 @@ var lint_spec = createSwarmTool({
|
|
|
81747
82196
|
async execute(_args, directory) {
|
|
81748
82197
|
const errors5 = [];
|
|
81749
82198
|
const warnings = [];
|
|
81750
|
-
const specPath =
|
|
81751
|
-
if (!
|
|
82199
|
+
const specPath = path87.join(directory, SWARM_DIR2, SPEC_FILE_NAME);
|
|
82200
|
+
if (!fs72.existsSync(specPath)) {
|
|
81752
82201
|
const result2 = {
|
|
81753
82202
|
valid: false,
|
|
81754
82203
|
specMtime: null,
|
|
@@ -81767,12 +82216,12 @@ var lint_spec = createSwarmTool({
|
|
|
81767
82216
|
}
|
|
81768
82217
|
let specMtime = null;
|
|
81769
82218
|
try {
|
|
81770
|
-
const stats =
|
|
82219
|
+
const stats = fs72.statSync(specPath);
|
|
81771
82220
|
specMtime = stats.mtime.toISOString();
|
|
81772
82221
|
} catch {}
|
|
81773
82222
|
let content;
|
|
81774
82223
|
try {
|
|
81775
|
-
content =
|
|
82224
|
+
content = fs72.readFileSync(specPath, "utf-8");
|
|
81776
82225
|
} catch (e) {
|
|
81777
82226
|
const result2 = {
|
|
81778
82227
|
valid: false,
|
|
@@ -81817,13 +82266,13 @@ var lint_spec = createSwarmTool({
|
|
|
81817
82266
|
});
|
|
81818
82267
|
// src/tools/mutation-test.ts
|
|
81819
82268
|
init_dist();
|
|
81820
|
-
import * as
|
|
81821
|
-
import * as
|
|
82269
|
+
import * as fs73 from "fs";
|
|
82270
|
+
import * as path89 from "path";
|
|
81822
82271
|
|
|
81823
82272
|
// src/mutation/engine.ts
|
|
81824
82273
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
81825
|
-
import { unlinkSync as
|
|
81826
|
-
import * as
|
|
82274
|
+
import { unlinkSync as unlinkSync12, writeFileSync as writeFileSync18 } from "fs";
|
|
82275
|
+
import * as path88 from "path";
|
|
81827
82276
|
|
|
81828
82277
|
// src/mutation/equivalence.ts
|
|
81829
82278
|
function isStaticallyEquivalent(originalCode, mutatedCode) {
|
|
@@ -81958,9 +82407,9 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
81958
82407
|
let patchFile;
|
|
81959
82408
|
try {
|
|
81960
82409
|
const safeId2 = patch.id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
81961
|
-
patchFile =
|
|
82410
|
+
patchFile = path88.join(workingDir, `.mutation_patch_${safeId2}.diff`);
|
|
81962
82411
|
try {
|
|
81963
|
-
|
|
82412
|
+
writeFileSync18(patchFile, patch.patch);
|
|
81964
82413
|
} catch (writeErr) {
|
|
81965
82414
|
error93 = `Failed to write patch file: ${writeErr}`;
|
|
81966
82415
|
outcome = "error";
|
|
@@ -82056,7 +82505,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
82056
82505
|
revertError = new Error(`Failed to revert mutation ${patch.id}: ${revertErr}. Working tree may be dirty.`);
|
|
82057
82506
|
}
|
|
82058
82507
|
try {
|
|
82059
|
-
|
|
82508
|
+
unlinkSync12(patchFile);
|
|
82060
82509
|
} catch (_unlinkErr) {}
|
|
82061
82510
|
}
|
|
82062
82511
|
}
|
|
@@ -82352,8 +82801,8 @@ var mutation_test = createSwarmTool({
|
|
|
82352
82801
|
];
|
|
82353
82802
|
for (const filePath of uniquePaths) {
|
|
82354
82803
|
try {
|
|
82355
|
-
const resolvedPath =
|
|
82356
|
-
sourceFiles.set(filePath,
|
|
82804
|
+
const resolvedPath = path89.resolve(cwd, filePath);
|
|
82805
|
+
sourceFiles.set(filePath, fs73.readFileSync(resolvedPath, "utf-8"));
|
|
82357
82806
|
} catch {}
|
|
82358
82807
|
}
|
|
82359
82808
|
const report = await executeMutationSuite(typedArgs.patches, typedArgs.test_command, typedArgs.files, cwd, undefined, undefined, sourceFiles.size > 0 ? sourceFiles : undefined);
|
|
@@ -82371,8 +82820,8 @@ var mutation_test = createSwarmTool({
|
|
|
82371
82820
|
init_dist();
|
|
82372
82821
|
init_manager2();
|
|
82373
82822
|
init_detector();
|
|
82374
|
-
import * as
|
|
82375
|
-
import * as
|
|
82823
|
+
import * as fs74 from "fs";
|
|
82824
|
+
import * as path90 from "path";
|
|
82376
82825
|
init_create_tool();
|
|
82377
82826
|
var MAX_FILE_SIZE2 = 2 * 1024 * 1024;
|
|
82378
82827
|
var BINARY_CHECK_BYTES = 8192;
|
|
@@ -82438,7 +82887,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
82438
82887
|
if (languages?.length) {
|
|
82439
82888
|
const lowerLangs = languages.map((l) => l.toLowerCase());
|
|
82440
82889
|
filesToCheck = filesToCheck.filter((file3) => {
|
|
82441
|
-
const ext =
|
|
82890
|
+
const ext = path90.extname(file3.path).toLowerCase();
|
|
82442
82891
|
const langDef = getLanguageForExtension(ext);
|
|
82443
82892
|
const fileProfile = getProfileForFile(file3.path);
|
|
82444
82893
|
const langId = fileProfile?.id || langDef?.id;
|
|
@@ -82451,7 +82900,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
82451
82900
|
let skippedCount = 0;
|
|
82452
82901
|
for (const fileInfo of filesToCheck) {
|
|
82453
82902
|
const { path: filePath } = fileInfo;
|
|
82454
|
-
const fullPath =
|
|
82903
|
+
const fullPath = path90.isAbsolute(filePath) ? filePath : path90.join(directory, filePath);
|
|
82455
82904
|
const result = {
|
|
82456
82905
|
path: filePath,
|
|
82457
82906
|
language: "",
|
|
@@ -82481,7 +82930,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
82481
82930
|
}
|
|
82482
82931
|
let content;
|
|
82483
82932
|
try {
|
|
82484
|
-
content =
|
|
82933
|
+
content = fs74.readFileSync(fullPath, "utf8");
|
|
82485
82934
|
} catch {
|
|
82486
82935
|
result.skipped_reason = "file_read_error";
|
|
82487
82936
|
skippedCount++;
|
|
@@ -82500,7 +82949,7 @@ async function syntaxCheck(input, directory, config3) {
|
|
|
82500
82949
|
results.push(result);
|
|
82501
82950
|
continue;
|
|
82502
82951
|
}
|
|
82503
|
-
const ext =
|
|
82952
|
+
const ext = path90.extname(filePath).toLowerCase();
|
|
82504
82953
|
const langDef = getLanguageForExtension(ext);
|
|
82505
82954
|
result.language = profile?.id || langDef?.id || "unknown";
|
|
82506
82955
|
const errors5 = extractSyntaxErrors(parser, content);
|
|
@@ -82592,8 +83041,8 @@ init_dist();
|
|
|
82592
83041
|
init_utils();
|
|
82593
83042
|
init_create_tool();
|
|
82594
83043
|
init_path_security();
|
|
82595
|
-
import * as
|
|
82596
|
-
import * as
|
|
83044
|
+
import * as fs75 from "fs";
|
|
83045
|
+
import * as path91 from "path";
|
|
82597
83046
|
var MAX_TEXT_LENGTH = 200;
|
|
82598
83047
|
var MAX_FILE_SIZE_BYTES11 = 1024 * 1024;
|
|
82599
83048
|
var SUPPORTED_EXTENSIONS4 = new Set([
|
|
@@ -82659,9 +83108,9 @@ function validatePathsInput(paths, cwd) {
|
|
|
82659
83108
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
82660
83109
|
}
|
|
82661
83110
|
try {
|
|
82662
|
-
const resolvedPath =
|
|
82663
|
-
const normalizedCwd =
|
|
82664
|
-
const normalizedResolved =
|
|
83111
|
+
const resolvedPath = path91.resolve(paths);
|
|
83112
|
+
const normalizedCwd = path91.resolve(cwd);
|
|
83113
|
+
const normalizedResolved = path91.resolve(resolvedPath);
|
|
82665
83114
|
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
82666
83115
|
return {
|
|
82667
83116
|
error: "paths must be within the current working directory",
|
|
@@ -82677,13 +83126,13 @@ function validatePathsInput(paths, cwd) {
|
|
|
82677
83126
|
}
|
|
82678
83127
|
}
|
|
82679
83128
|
function isSupportedExtension(filePath) {
|
|
82680
|
-
const ext =
|
|
83129
|
+
const ext = path91.extname(filePath).toLowerCase();
|
|
82681
83130
|
return SUPPORTED_EXTENSIONS4.has(ext);
|
|
82682
83131
|
}
|
|
82683
83132
|
function findSourceFiles4(dir, files = []) {
|
|
82684
83133
|
let entries;
|
|
82685
83134
|
try {
|
|
82686
|
-
entries =
|
|
83135
|
+
entries = fs75.readdirSync(dir);
|
|
82687
83136
|
} catch {
|
|
82688
83137
|
return files;
|
|
82689
83138
|
}
|
|
@@ -82692,10 +83141,10 @@ function findSourceFiles4(dir, files = []) {
|
|
|
82692
83141
|
if (SKIP_DIRECTORIES5.has(entry)) {
|
|
82693
83142
|
continue;
|
|
82694
83143
|
}
|
|
82695
|
-
const fullPath =
|
|
83144
|
+
const fullPath = path91.join(dir, entry);
|
|
82696
83145
|
let stat3;
|
|
82697
83146
|
try {
|
|
82698
|
-
stat3 =
|
|
83147
|
+
stat3 = fs75.statSync(fullPath);
|
|
82699
83148
|
} catch {
|
|
82700
83149
|
continue;
|
|
82701
83150
|
}
|
|
@@ -82788,7 +83237,7 @@ var todo_extract = createSwarmTool({
|
|
|
82788
83237
|
return JSON.stringify(errorResult, null, 2);
|
|
82789
83238
|
}
|
|
82790
83239
|
const scanPath = resolvedPath;
|
|
82791
|
-
if (!
|
|
83240
|
+
if (!fs75.existsSync(scanPath)) {
|
|
82792
83241
|
const errorResult = {
|
|
82793
83242
|
error: `path not found: ${pathsInput}`,
|
|
82794
83243
|
total: 0,
|
|
@@ -82798,13 +83247,13 @@ var todo_extract = createSwarmTool({
|
|
|
82798
83247
|
return JSON.stringify(errorResult, null, 2);
|
|
82799
83248
|
}
|
|
82800
83249
|
const filesToScan = [];
|
|
82801
|
-
const stat3 =
|
|
83250
|
+
const stat3 = fs75.statSync(scanPath);
|
|
82802
83251
|
if (stat3.isFile()) {
|
|
82803
83252
|
if (isSupportedExtension(scanPath)) {
|
|
82804
83253
|
filesToScan.push(scanPath);
|
|
82805
83254
|
} else {
|
|
82806
83255
|
const errorResult = {
|
|
82807
|
-
error: `unsupported file extension: ${
|
|
83256
|
+
error: `unsupported file extension: ${path91.extname(scanPath)}`,
|
|
82808
83257
|
total: 0,
|
|
82809
83258
|
byPriority: { high: 0, medium: 0, low: 0 },
|
|
82810
83259
|
entries: []
|
|
@@ -82817,11 +83266,11 @@ var todo_extract = createSwarmTool({
|
|
|
82817
83266
|
const allEntries = [];
|
|
82818
83267
|
for (const filePath of filesToScan) {
|
|
82819
83268
|
try {
|
|
82820
|
-
const fileStat =
|
|
83269
|
+
const fileStat = fs75.statSync(filePath);
|
|
82821
83270
|
if (fileStat.size > MAX_FILE_SIZE_BYTES11) {
|
|
82822
83271
|
continue;
|
|
82823
83272
|
}
|
|
82824
|
-
const content =
|
|
83273
|
+
const content = fs75.readFileSync(filePath, "utf-8");
|
|
82825
83274
|
const entries = parseTodoComments(content, filePath, tagsSet);
|
|
82826
83275
|
allEntries.push(...entries);
|
|
82827
83276
|
} catch {}
|
|
@@ -82851,18 +83300,18 @@ init_tool();
|
|
|
82851
83300
|
init_loader();
|
|
82852
83301
|
init_schema();
|
|
82853
83302
|
init_gate_evidence();
|
|
82854
|
-
import * as
|
|
82855
|
-
import * as
|
|
83303
|
+
import * as fs77 from "fs";
|
|
83304
|
+
import * as path93 from "path";
|
|
82856
83305
|
|
|
82857
83306
|
// src/hooks/diff-scope.ts
|
|
82858
|
-
import * as
|
|
82859
|
-
import * as
|
|
83307
|
+
import * as fs76 from "fs";
|
|
83308
|
+
import * as path92 from "path";
|
|
82860
83309
|
function getDeclaredScope(taskId, directory) {
|
|
82861
83310
|
try {
|
|
82862
|
-
const planPath =
|
|
82863
|
-
if (!
|
|
83311
|
+
const planPath = path92.join(directory, ".swarm", "plan.json");
|
|
83312
|
+
if (!fs76.existsSync(planPath))
|
|
82864
83313
|
return null;
|
|
82865
|
-
const raw =
|
|
83314
|
+
const raw = fs76.readFileSync(planPath, "utf-8");
|
|
82866
83315
|
const plan = JSON.parse(raw);
|
|
82867
83316
|
for (const phase of plan.phases ?? []) {
|
|
82868
83317
|
for (const task of phase.tasks ?? []) {
|
|
@@ -82977,7 +83426,7 @@ var TIER_3_PATTERNS = [
|
|
|
82977
83426
|
];
|
|
82978
83427
|
function matchesTier3Pattern(files) {
|
|
82979
83428
|
for (const file3 of files) {
|
|
82980
|
-
const fileName =
|
|
83429
|
+
const fileName = path93.basename(file3);
|
|
82981
83430
|
for (const pattern of TIER_3_PATTERNS) {
|
|
82982
83431
|
if (pattern.test(fileName)) {
|
|
82983
83432
|
return true;
|
|
@@ -82991,8 +83440,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
82991
83440
|
if (hasActiveTurboMode()) {
|
|
82992
83441
|
const resolvedDir2 = workingDirectory;
|
|
82993
83442
|
try {
|
|
82994
|
-
const planPath =
|
|
82995
|
-
const planRaw =
|
|
83443
|
+
const planPath = path93.join(resolvedDir2, ".swarm", "plan.json");
|
|
83444
|
+
const planRaw = fs77.readFileSync(planPath, "utf-8");
|
|
82996
83445
|
const plan = JSON.parse(planRaw);
|
|
82997
83446
|
for (const planPhase of plan.phases ?? []) {
|
|
82998
83447
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -83058,8 +83507,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
83058
83507
|
}
|
|
83059
83508
|
try {
|
|
83060
83509
|
const resolvedDir2 = workingDirectory;
|
|
83061
|
-
const planPath =
|
|
83062
|
-
const planRaw =
|
|
83510
|
+
const planPath = path93.join(resolvedDir2, ".swarm", "plan.json");
|
|
83511
|
+
const planRaw = fs77.readFileSync(planPath, "utf-8");
|
|
83063
83512
|
const plan = JSON.parse(planRaw);
|
|
83064
83513
|
for (const planPhase of plan.phases ?? []) {
|
|
83065
83514
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -83276,8 +83725,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
83276
83725
|
};
|
|
83277
83726
|
}
|
|
83278
83727
|
}
|
|
83279
|
-
normalizedDir =
|
|
83280
|
-
const pathParts = normalizedDir.split(
|
|
83728
|
+
normalizedDir = path93.normalize(args2.working_directory);
|
|
83729
|
+
const pathParts = normalizedDir.split(path93.sep);
|
|
83281
83730
|
if (pathParts.includes("..")) {
|
|
83282
83731
|
return {
|
|
83283
83732
|
success: false,
|
|
@@ -83287,11 +83736,11 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
83287
83736
|
]
|
|
83288
83737
|
};
|
|
83289
83738
|
}
|
|
83290
|
-
const resolvedDir =
|
|
83739
|
+
const resolvedDir = path93.resolve(normalizedDir);
|
|
83291
83740
|
try {
|
|
83292
|
-
const realPath =
|
|
83293
|
-
const planPath =
|
|
83294
|
-
if (!
|
|
83741
|
+
const realPath = fs77.realpathSync(resolvedDir);
|
|
83742
|
+
const planPath = path93.join(realPath, ".swarm", "plan.json");
|
|
83743
|
+
if (!fs77.existsSync(planPath)) {
|
|
83295
83744
|
return {
|
|
83296
83745
|
success: false,
|
|
83297
83746
|
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
@@ -83322,12 +83771,12 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
83322
83771
|
}
|
|
83323
83772
|
if (args2.status === "in_progress") {
|
|
83324
83773
|
try {
|
|
83325
|
-
const evidencePath =
|
|
83326
|
-
|
|
83327
|
-
const fd =
|
|
83774
|
+
const evidencePath = path93.join(directory, ".swarm", "evidence", `${args2.task_id}.json`);
|
|
83775
|
+
fs77.mkdirSync(path93.dirname(evidencePath), { recursive: true });
|
|
83776
|
+
const fd = fs77.openSync(evidencePath, "wx");
|
|
83328
83777
|
let writeOk = false;
|
|
83329
83778
|
try {
|
|
83330
|
-
|
|
83779
|
+
fs77.writeSync(fd, JSON.stringify({
|
|
83331
83780
|
task_id: args2.task_id,
|
|
83332
83781
|
required_gates: ["reviewer", "test_engineer"],
|
|
83333
83782
|
gates: {},
|
|
@@ -83335,10 +83784,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
83335
83784
|
}, null, 2));
|
|
83336
83785
|
writeOk = true;
|
|
83337
83786
|
} finally {
|
|
83338
|
-
|
|
83787
|
+
fs77.closeSync(fd);
|
|
83339
83788
|
if (!writeOk) {
|
|
83340
83789
|
try {
|
|
83341
|
-
|
|
83790
|
+
fs77.unlinkSync(evidencePath);
|
|
83342
83791
|
} catch {}
|
|
83343
83792
|
}
|
|
83344
83793
|
}
|
|
@@ -83348,8 +83797,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
83348
83797
|
recoverTaskStateFromDelegations(args2.task_id);
|
|
83349
83798
|
let phaseRequiresReviewer = true;
|
|
83350
83799
|
try {
|
|
83351
|
-
const planPath =
|
|
83352
|
-
const planRaw =
|
|
83800
|
+
const planPath = path93.join(directory, ".swarm", "plan.json");
|
|
83801
|
+
const planRaw = fs77.readFileSync(planPath, "utf-8");
|
|
83353
83802
|
const plan = JSON.parse(planRaw);
|
|
83354
83803
|
const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
|
|
83355
83804
|
if (taskPhase?.required_agents && !taskPhase.required_agents.includes("reviewer")) {
|
|
@@ -83458,8 +83907,8 @@ init_utils2();
|
|
|
83458
83907
|
init_ledger();
|
|
83459
83908
|
init_manager();
|
|
83460
83909
|
init_create_tool();
|
|
83461
|
-
import
|
|
83462
|
-
import
|
|
83910
|
+
import fs78 from "fs";
|
|
83911
|
+
import path94 from "path";
|
|
83463
83912
|
function derivePlanId5(plan) {
|
|
83464
83913
|
return `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
83465
83914
|
}
|
|
@@ -83510,7 +83959,7 @@ async function executeWriteDriftEvidence(args2, directory) {
|
|
|
83510
83959
|
entries: [evidenceEntry]
|
|
83511
83960
|
};
|
|
83512
83961
|
const filename = "drift-verifier.json";
|
|
83513
|
-
const relativePath =
|
|
83962
|
+
const relativePath = path94.join("evidence", String(phase), filename);
|
|
83514
83963
|
let validatedPath;
|
|
83515
83964
|
try {
|
|
83516
83965
|
validatedPath = validateSwarmPath(directory, relativePath);
|
|
@@ -83521,12 +83970,12 @@ async function executeWriteDriftEvidence(args2, directory) {
|
|
|
83521
83970
|
message: error93 instanceof Error ? error93.message : "Failed to validate path"
|
|
83522
83971
|
}, null, 2);
|
|
83523
83972
|
}
|
|
83524
|
-
const evidenceDir =
|
|
83973
|
+
const evidenceDir = path94.dirname(validatedPath);
|
|
83525
83974
|
try {
|
|
83526
|
-
await
|
|
83527
|
-
const tempPath =
|
|
83528
|
-
await
|
|
83529
|
-
await
|
|
83975
|
+
await fs78.promises.mkdir(evidenceDir, { recursive: true });
|
|
83976
|
+
const tempPath = path94.join(evidenceDir, `.${filename}.tmp`);
|
|
83977
|
+
await fs78.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
|
|
83978
|
+
await fs78.promises.rename(tempPath, validatedPath);
|
|
83530
83979
|
let snapshotInfo;
|
|
83531
83980
|
let snapshotError;
|
|
83532
83981
|
let qaProfileLocked;
|
|
@@ -83620,8 +84069,8 @@ var write_drift_evidence = createSwarmTool({
|
|
|
83620
84069
|
init_tool();
|
|
83621
84070
|
init_utils2();
|
|
83622
84071
|
init_create_tool();
|
|
83623
|
-
import
|
|
83624
|
-
import
|
|
84072
|
+
import fs79 from "fs";
|
|
84073
|
+
import path95 from "path";
|
|
83625
84074
|
function normalizeVerdict2(verdict) {
|
|
83626
84075
|
switch (verdict) {
|
|
83627
84076
|
case "APPROVED":
|
|
@@ -83669,7 +84118,7 @@ async function executeWriteHallucinationEvidence(args2, directory) {
|
|
|
83669
84118
|
entries: [evidenceEntry]
|
|
83670
84119
|
};
|
|
83671
84120
|
const filename = "hallucination-guard.json";
|
|
83672
|
-
const relativePath =
|
|
84121
|
+
const relativePath = path95.join("evidence", String(phase), filename);
|
|
83673
84122
|
let validatedPath;
|
|
83674
84123
|
try {
|
|
83675
84124
|
validatedPath = validateSwarmPath(directory, relativePath);
|
|
@@ -83680,12 +84129,12 @@ async function executeWriteHallucinationEvidence(args2, directory) {
|
|
|
83680
84129
|
message: error93 instanceof Error ? error93.message : "Failed to validate path"
|
|
83681
84130
|
}, null, 2);
|
|
83682
84131
|
}
|
|
83683
|
-
const evidenceDir =
|
|
84132
|
+
const evidenceDir = path95.dirname(validatedPath);
|
|
83684
84133
|
try {
|
|
83685
|
-
await
|
|
83686
|
-
const tempPath =
|
|
83687
|
-
await
|
|
83688
|
-
await
|
|
84134
|
+
await fs79.promises.mkdir(evidenceDir, { recursive: true });
|
|
84135
|
+
const tempPath = path95.join(evidenceDir, `.${filename}.tmp`);
|
|
84136
|
+
await fs79.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
|
|
84137
|
+
await fs79.promises.rename(tempPath, validatedPath);
|
|
83689
84138
|
return JSON.stringify({
|
|
83690
84139
|
success: true,
|
|
83691
84140
|
phase,
|
|
@@ -83894,7 +84343,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
83894
84343
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
83895
84344
|
preflightTriggerManager = new PTM(automationConfig);
|
|
83896
84345
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
83897
|
-
const swarmDir =
|
|
84346
|
+
const swarmDir = path96.resolve(ctx.directory, ".swarm");
|
|
83898
84347
|
statusArtifact = new ASA(swarmDir);
|
|
83899
84348
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
83900
84349
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|