opencode-swarm 6.40.4 → 6.40.6
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 +4 -2
- package/dist/index.js +259 -137
- package/dist/tools/pre-check-batch.d.ts +26 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -32825,6 +32825,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
32825
32825
|
import { execSync } from "child_process";
|
|
32826
32826
|
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
32827
32827
|
import path12 from "path";
|
|
32828
|
+
import { fileURLToPath } from "url";
|
|
32828
32829
|
init_manager();
|
|
32829
32830
|
init_utils2();
|
|
32830
32831
|
init_manager2();
|
|
@@ -33165,8 +33166,9 @@ async function checkGrammarWasmFiles() {
|
|
|
33165
33166
|
"tree-sitter-swift.wasm",
|
|
33166
33167
|
"tree-sitter-dart.wasm"
|
|
33167
33168
|
];
|
|
33168
|
-
const
|
|
33169
|
-
const
|
|
33169
|
+
const thisDir = path12.dirname(fileURLToPath(import.meta.url));
|
|
33170
|
+
const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/services");
|
|
33171
|
+
const grammarDir = isSource ? path12.join(thisDir, "..", "lang", "grammars") : path12.join(thisDir, "lang", "grammars");
|
|
33170
33172
|
const missing = [];
|
|
33171
33173
|
for (const file3 of grammarFiles) {
|
|
33172
33174
|
if (!existsSync4(path12.join(grammarDir, file3))) {
|
package/dist/index.js
CHANGED
|
@@ -40679,12 +40679,24 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
40679
40679
|
});
|
|
40680
40680
|
|
|
40681
40681
|
// src/lang/runtime.ts
|
|
40682
|
-
import
|
|
40682
|
+
import * as path47 from "path";
|
|
40683
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
40683
40684
|
async function initTreeSitter() {
|
|
40684
40685
|
if (treeSitterInitialized) {
|
|
40685
40686
|
return;
|
|
40686
40687
|
}
|
|
40687
|
-
|
|
40688
|
+
const thisDir = path47.dirname(fileURLToPath2(import.meta.url));
|
|
40689
|
+
const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/lang");
|
|
40690
|
+
if (isSource) {
|
|
40691
|
+
await Parser.init();
|
|
40692
|
+
} else {
|
|
40693
|
+
const grammarsDir = getGrammarsDirAbsolute();
|
|
40694
|
+
await Parser.init({
|
|
40695
|
+
locateFile(scriptName) {
|
|
40696
|
+
return path47.join(grammarsDir, scriptName);
|
|
40697
|
+
}
|
|
40698
|
+
});
|
|
40699
|
+
}
|
|
40688
40700
|
treeSitterInitialized = true;
|
|
40689
40701
|
}
|
|
40690
40702
|
function sanitizeLanguageId(languageId) {
|
|
@@ -40701,12 +40713,10 @@ function getWasmFileName(languageId) {
|
|
|
40701
40713
|
}
|
|
40702
40714
|
return `tree-sitter-${sanitized}.wasm`;
|
|
40703
40715
|
}
|
|
40704
|
-
function
|
|
40705
|
-
const
|
|
40706
|
-
|
|
40707
|
-
|
|
40708
|
-
}
|
|
40709
|
-
return "../../dist/lang/grammars/";
|
|
40716
|
+
function getGrammarsDirAbsolute() {
|
|
40717
|
+
const thisDir = path47.dirname(fileURLToPath2(import.meta.url));
|
|
40718
|
+
const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/lang");
|
|
40719
|
+
return isSource ? path47.join(thisDir, "grammars") : path47.join(thisDir, "lang", "grammars");
|
|
40710
40720
|
}
|
|
40711
40721
|
async function loadGrammar(languageId) {
|
|
40712
40722
|
if (typeof languageId !== "string" || languageId.length > 100) {
|
|
@@ -40722,8 +40732,7 @@ async function loadGrammar(languageId) {
|
|
|
40722
40732
|
await initTreeSitter();
|
|
40723
40733
|
const parser = new Parser;
|
|
40724
40734
|
const wasmFileName = getWasmFileName(normalizedId);
|
|
40725
|
-
const
|
|
40726
|
-
const wasmPath = fileURLToPath(new URL(`${grammarsPath}${wasmFileName}`, import.meta.url));
|
|
40735
|
+
const wasmPath = path47.join(getGrammarsDirAbsolute(), wasmFileName);
|
|
40727
40736
|
const { existsSync: existsSync29 } = await import("fs");
|
|
40728
40737
|
if (!existsSync29(wasmPath)) {
|
|
40729
40738
|
throw new Error(`Grammar file not found for ${languageId}: ${wasmPath}
|
|
@@ -40766,7 +40775,7 @@ var init_runtime = __esm(() => {
|
|
|
40766
40775
|
});
|
|
40767
40776
|
|
|
40768
40777
|
// src/index.ts
|
|
40769
|
-
import * as
|
|
40778
|
+
import * as path63 from "path";
|
|
40770
40779
|
|
|
40771
40780
|
// src/agents/index.ts
|
|
40772
40781
|
init_config();
|
|
@@ -42001,8 +42010,9 @@ All other gates: failure \u2192 return to coder. No self-fixes. No workarounds.
|
|
|
42001
42010
|
- quality_budget (maintainability metrics)
|
|
42002
42011
|
\u2192 Returns { gates_passed, lint, secretscan, sast_scan, quality_budget, total_duration_ms }
|
|
42003
42012
|
\u2192 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.
|
|
42004
|
-
\u2192 If gates_passed === true: proceed to {{AGENT_PREFIX}}reviewer.
|
|
42005
|
-
\u2192
|
|
42013
|
+
\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 HIGH/CRITICAL SAST findings exist on unchanged lines in this changeset. Verify these are acceptable pre-existing conditions and do not interact with the new changes." Do NOT return to coder for pre-existing findings on unchanged code.
|
|
42014
|
+
\u2192 If gates_passed === true (no sast_preexisting_findings): proceed to {{AGENT_PREFIX}}reviewer.
|
|
42015
|
+
\u2192 REQUIRED: Print "pre_check_batch: [PASS \u2014 all gates passed | PASS \u2014 pre-existing SAST findings on unchanged lines (N findings, reviewer triage) | FAIL \u2014 [gate]: [details]]"
|
|
42006
42016
|
|
|
42007
42017
|
\u26A0\uFE0F pre_check_batch SCOPE BOUNDARY:
|
|
42008
42018
|
pre_check_batch runs FOUR automated tools: lint:check, secretscan, sast_scan, quality_budget.
|
|
@@ -46948,6 +46958,7 @@ init_manager2();
|
|
|
46948
46958
|
import { execSync } from "child_process";
|
|
46949
46959
|
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync5 } from "fs";
|
|
46950
46960
|
import path18 from "path";
|
|
46961
|
+
import { fileURLToPath } from "url";
|
|
46951
46962
|
function validateTaskDag(plan) {
|
|
46952
46963
|
const allTaskIds = new Set;
|
|
46953
46964
|
for (const phase of plan.phases) {
|
|
@@ -47285,8 +47296,9 @@ async function checkGrammarWasmFiles() {
|
|
|
47285
47296
|
"tree-sitter-swift.wasm",
|
|
47286
47297
|
"tree-sitter-dart.wasm"
|
|
47287
47298
|
];
|
|
47288
|
-
const
|
|
47289
|
-
const
|
|
47299
|
+
const thisDir = path18.dirname(fileURLToPath(import.meta.url));
|
|
47300
|
+
const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/services");
|
|
47301
|
+
const grammarDir = isSource ? path18.join(thisDir, "..", "lang", "grammars") : path18.join(thisDir, "lang", "grammars");
|
|
47290
47302
|
const missing = [];
|
|
47291
47303
|
for (const file3 of grammarFiles) {
|
|
47292
47304
|
if (!existsSync8(path18.join(grammarDir, file3))) {
|
|
@@ -59020,20 +59032,20 @@ function validateBase(base) {
|
|
|
59020
59032
|
function validatePaths(paths) {
|
|
59021
59033
|
if (!paths)
|
|
59022
59034
|
return null;
|
|
59023
|
-
for (const
|
|
59024
|
-
if (!
|
|
59035
|
+
for (const path48 of paths) {
|
|
59036
|
+
if (!path48 || path48.length === 0) {
|
|
59025
59037
|
return "empty path not allowed";
|
|
59026
59038
|
}
|
|
59027
|
-
if (
|
|
59039
|
+
if (path48.length > MAX_PATH_LENGTH) {
|
|
59028
59040
|
return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
|
|
59029
59041
|
}
|
|
59030
|
-
if (SHELL_METACHARACTERS2.test(
|
|
59042
|
+
if (SHELL_METACHARACTERS2.test(path48)) {
|
|
59031
59043
|
return "path contains shell metacharacters";
|
|
59032
59044
|
}
|
|
59033
|
-
if (
|
|
59045
|
+
if (path48.startsWith("-")) {
|
|
59034
59046
|
return 'path cannot start with "-" (option-like arguments not allowed)';
|
|
59035
59047
|
}
|
|
59036
|
-
if (CONTROL_CHAR_PATTERN2.test(
|
|
59048
|
+
if (CONTROL_CHAR_PATTERN2.test(path48)) {
|
|
59037
59049
|
return "path contains control characters";
|
|
59038
59050
|
}
|
|
59039
59051
|
}
|
|
@@ -59114,8 +59126,8 @@ var diff = createSwarmTool({
|
|
|
59114
59126
|
if (parts2.length >= 3) {
|
|
59115
59127
|
const additions = parseInt(parts2[0], 10) || 0;
|
|
59116
59128
|
const deletions = parseInt(parts2[1], 10) || 0;
|
|
59117
|
-
const
|
|
59118
|
-
files.push({ path:
|
|
59129
|
+
const path48 = parts2[2];
|
|
59130
|
+
files.push({ path: path48, additions, deletions });
|
|
59119
59131
|
}
|
|
59120
59132
|
}
|
|
59121
59133
|
const contractChanges = [];
|
|
@@ -59398,7 +59410,7 @@ Use these as DOMAIN values when delegating to @sme.`;
|
|
|
59398
59410
|
init_dist();
|
|
59399
59411
|
init_create_tool();
|
|
59400
59412
|
import * as fs36 from "fs";
|
|
59401
|
-
import * as
|
|
59413
|
+
import * as path48 from "path";
|
|
59402
59414
|
var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
|
|
59403
59415
|
var MAX_EVIDENCE_FILES = 1000;
|
|
59404
59416
|
var EVIDENCE_DIR2 = ".swarm/evidence";
|
|
@@ -59425,9 +59437,9 @@ function validateRequiredTypes(input) {
|
|
|
59425
59437
|
return null;
|
|
59426
59438
|
}
|
|
59427
59439
|
function isPathWithinSwarm2(filePath, cwd) {
|
|
59428
|
-
const normalizedCwd =
|
|
59429
|
-
const swarmPath =
|
|
59430
|
-
const normalizedPath =
|
|
59440
|
+
const normalizedCwd = path48.resolve(cwd);
|
|
59441
|
+
const swarmPath = path48.join(normalizedCwd, ".swarm");
|
|
59442
|
+
const normalizedPath = path48.resolve(filePath);
|
|
59431
59443
|
return normalizedPath.startsWith(swarmPath);
|
|
59432
59444
|
}
|
|
59433
59445
|
function parseCompletedTasks(planContent) {
|
|
@@ -59457,10 +59469,10 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
59457
59469
|
if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
|
|
59458
59470
|
continue;
|
|
59459
59471
|
}
|
|
59460
|
-
const filePath =
|
|
59472
|
+
const filePath = path48.join(evidenceDir, filename);
|
|
59461
59473
|
try {
|
|
59462
|
-
const resolvedPath =
|
|
59463
|
-
const evidenceDirResolved =
|
|
59474
|
+
const resolvedPath = path48.resolve(filePath);
|
|
59475
|
+
const evidenceDirResolved = path48.resolve(evidenceDir);
|
|
59464
59476
|
if (!resolvedPath.startsWith(evidenceDirResolved)) {
|
|
59465
59477
|
continue;
|
|
59466
59478
|
}
|
|
@@ -59578,7 +59590,7 @@ var evidence_check = createSwarmTool({
|
|
|
59578
59590
|
return JSON.stringify(errorResult, null, 2);
|
|
59579
59591
|
}
|
|
59580
59592
|
const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
|
|
59581
|
-
const planPath =
|
|
59593
|
+
const planPath = path48.join(cwd, PLAN_FILE);
|
|
59582
59594
|
if (!isPathWithinSwarm2(planPath, cwd)) {
|
|
59583
59595
|
const errorResult = {
|
|
59584
59596
|
error: "plan file path validation failed",
|
|
@@ -59610,7 +59622,7 @@ var evidence_check = createSwarmTool({
|
|
|
59610
59622
|
};
|
|
59611
59623
|
return JSON.stringify(result2, null, 2);
|
|
59612
59624
|
}
|
|
59613
|
-
const evidenceDir =
|
|
59625
|
+
const evidenceDir = path48.join(cwd, EVIDENCE_DIR2);
|
|
59614
59626
|
const evidence = readEvidenceFiles(evidenceDir, cwd);
|
|
59615
59627
|
const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
|
|
59616
59628
|
const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
|
|
@@ -59628,7 +59640,7 @@ var evidence_check = createSwarmTool({
|
|
|
59628
59640
|
init_tool();
|
|
59629
59641
|
init_create_tool();
|
|
59630
59642
|
import * as fs37 from "fs";
|
|
59631
|
-
import * as
|
|
59643
|
+
import * as path49 from "path";
|
|
59632
59644
|
var EXT_MAP = {
|
|
59633
59645
|
python: ".py",
|
|
59634
59646
|
py: ".py",
|
|
@@ -59709,12 +59721,12 @@ var extract_code_blocks = createSwarmTool({
|
|
|
59709
59721
|
if (prefix) {
|
|
59710
59722
|
filename = `${prefix}_${filename}`;
|
|
59711
59723
|
}
|
|
59712
|
-
let filepath =
|
|
59713
|
-
const base =
|
|
59714
|
-
const ext =
|
|
59724
|
+
let filepath = path49.join(targetDir, filename);
|
|
59725
|
+
const base = path49.basename(filepath, path49.extname(filepath));
|
|
59726
|
+
const ext = path49.extname(filepath);
|
|
59715
59727
|
let counter = 1;
|
|
59716
59728
|
while (fs37.existsSync(filepath)) {
|
|
59717
|
-
filepath =
|
|
59729
|
+
filepath = path49.join(targetDir, `${base}_${counter}${ext}`);
|
|
59718
59730
|
counter++;
|
|
59719
59731
|
}
|
|
59720
59732
|
try {
|
|
@@ -59835,7 +59847,7 @@ var gitingest = createSwarmTool({
|
|
|
59835
59847
|
init_dist();
|
|
59836
59848
|
init_create_tool();
|
|
59837
59849
|
import * as fs38 from "fs";
|
|
59838
|
-
import * as
|
|
59850
|
+
import * as path50 from "path";
|
|
59839
59851
|
var MAX_FILE_PATH_LENGTH2 = 500;
|
|
59840
59852
|
var MAX_SYMBOL_LENGTH = 256;
|
|
59841
59853
|
var MAX_FILE_SIZE_BYTES5 = 1024 * 1024;
|
|
@@ -59883,7 +59895,7 @@ function validateSymbolInput(symbol3) {
|
|
|
59883
59895
|
return null;
|
|
59884
59896
|
}
|
|
59885
59897
|
function isBinaryFile2(filePath, buffer) {
|
|
59886
|
-
const ext =
|
|
59898
|
+
const ext = path50.extname(filePath).toLowerCase();
|
|
59887
59899
|
if (ext === ".json" || ext === ".md" || ext === ".txt") {
|
|
59888
59900
|
return false;
|
|
59889
59901
|
}
|
|
@@ -59907,15 +59919,15 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
59907
59919
|
const imports = [];
|
|
59908
59920
|
let _resolvedTarget;
|
|
59909
59921
|
try {
|
|
59910
|
-
_resolvedTarget =
|
|
59922
|
+
_resolvedTarget = path50.resolve(targetFile);
|
|
59911
59923
|
} catch {
|
|
59912
59924
|
_resolvedTarget = targetFile;
|
|
59913
59925
|
}
|
|
59914
|
-
const targetBasename =
|
|
59926
|
+
const targetBasename = path50.basename(targetFile, path50.extname(targetFile));
|
|
59915
59927
|
const targetWithExt = targetFile;
|
|
59916
59928
|
const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
59917
|
-
const normalizedTargetWithExt =
|
|
59918
|
-
const normalizedTargetWithoutExt =
|
|
59929
|
+
const normalizedTargetWithExt = path50.normalize(targetWithExt).replace(/\\/g, "/");
|
|
59930
|
+
const normalizedTargetWithoutExt = path50.normalize(targetWithoutExt).replace(/\\/g, "/");
|
|
59919
59931
|
const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
59920
59932
|
for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
|
|
59921
59933
|
const modulePath = match[1] || match[2] || match[3];
|
|
@@ -59938,9 +59950,9 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
59938
59950
|
}
|
|
59939
59951
|
const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
|
|
59940
59952
|
let isMatch = false;
|
|
59941
|
-
const _targetDir =
|
|
59942
|
-
const targetExt =
|
|
59943
|
-
const targetBasenameNoExt =
|
|
59953
|
+
const _targetDir = path50.dirname(targetFile);
|
|
59954
|
+
const targetExt = path50.extname(targetFile);
|
|
59955
|
+
const targetBasenameNoExt = path50.basename(targetFile, targetExt);
|
|
59944
59956
|
const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
59945
59957
|
const moduleName = modulePath.split(/[/\\]/).pop() || "";
|
|
59946
59958
|
const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
@@ -60008,10 +60020,10 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
|
|
|
60008
60020
|
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
60009
60021
|
for (const entry of entries) {
|
|
60010
60022
|
if (SKIP_DIRECTORIES3.has(entry)) {
|
|
60011
|
-
stats.skippedDirs.push(
|
|
60023
|
+
stats.skippedDirs.push(path50.join(dir, entry));
|
|
60012
60024
|
continue;
|
|
60013
60025
|
}
|
|
60014
|
-
const fullPath =
|
|
60026
|
+
const fullPath = path50.join(dir, entry);
|
|
60015
60027
|
let stat2;
|
|
60016
60028
|
try {
|
|
60017
60029
|
stat2 = fs38.statSync(fullPath);
|
|
@@ -60025,7 +60037,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
|
|
|
60025
60037
|
if (stat2.isDirectory()) {
|
|
60026
60038
|
findSourceFiles(fullPath, files, stats);
|
|
60027
60039
|
} else if (stat2.isFile()) {
|
|
60028
|
-
const ext =
|
|
60040
|
+
const ext = path50.extname(fullPath).toLowerCase();
|
|
60029
60041
|
if (SUPPORTED_EXTENSIONS.includes(ext)) {
|
|
60030
60042
|
files.push(fullPath);
|
|
60031
60043
|
}
|
|
@@ -60082,7 +60094,7 @@ var imports = createSwarmTool({
|
|
|
60082
60094
|
return JSON.stringify(errorResult, null, 2);
|
|
60083
60095
|
}
|
|
60084
60096
|
try {
|
|
60085
|
-
const targetFile =
|
|
60097
|
+
const targetFile = path50.resolve(file3);
|
|
60086
60098
|
if (!fs38.existsSync(targetFile)) {
|
|
60087
60099
|
const errorResult = {
|
|
60088
60100
|
error: `target file not found: ${file3}`,
|
|
@@ -60104,7 +60116,7 @@ var imports = createSwarmTool({
|
|
|
60104
60116
|
};
|
|
60105
60117
|
return JSON.stringify(errorResult, null, 2);
|
|
60106
60118
|
}
|
|
60107
|
-
const baseDir =
|
|
60119
|
+
const baseDir = path50.dirname(targetFile);
|
|
60108
60120
|
const scanStats = {
|
|
60109
60121
|
skippedDirs: [],
|
|
60110
60122
|
skippedFiles: 0,
|
|
@@ -60714,7 +60726,7 @@ init_config();
|
|
|
60714
60726
|
init_schema();
|
|
60715
60727
|
init_manager();
|
|
60716
60728
|
import * as fs39 from "fs";
|
|
60717
|
-
import * as
|
|
60729
|
+
import * as path51 from "path";
|
|
60718
60730
|
init_utils2();
|
|
60719
60731
|
init_telemetry();
|
|
60720
60732
|
init_create_tool();
|
|
@@ -60934,7 +60946,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
60934
60946
|
safeWarn(`[phase_complete] Completion verify error (non-blocking):`, completionError);
|
|
60935
60947
|
}
|
|
60936
60948
|
try {
|
|
60937
|
-
const driftEvidencePath =
|
|
60949
|
+
const driftEvidencePath = path51.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
|
|
60938
60950
|
let driftVerdictFound = false;
|
|
60939
60951
|
let driftVerdictApproved = false;
|
|
60940
60952
|
try {
|
|
@@ -60968,7 +60980,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
60968
60980
|
driftVerdictFound = false;
|
|
60969
60981
|
}
|
|
60970
60982
|
if (!driftVerdictFound) {
|
|
60971
|
-
const specPath =
|
|
60983
|
+
const specPath = path51.join(dir, ".swarm", "spec.md");
|
|
60972
60984
|
const specExists = fs39.existsSync(specPath);
|
|
60973
60985
|
if (!specExists) {
|
|
60974
60986
|
let incompleteTaskCount = 0;
|
|
@@ -61042,7 +61054,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
61042
61054
|
};
|
|
61043
61055
|
if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
|
|
61044
61056
|
try {
|
|
61045
|
-
const projectName =
|
|
61057
|
+
const projectName = path51.basename(dir);
|
|
61046
61058
|
const curationResult = await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
|
|
61047
61059
|
if (curationResult) {
|
|
61048
61060
|
const sessionState = swarmState.agentSessions.get(sessionID);
|
|
@@ -61252,7 +61264,7 @@ init_discovery();
|
|
|
61252
61264
|
init_utils();
|
|
61253
61265
|
init_create_tool();
|
|
61254
61266
|
import * as fs40 from "fs";
|
|
61255
|
-
import * as
|
|
61267
|
+
import * as path52 from "path";
|
|
61256
61268
|
var MAX_OUTPUT_BYTES5 = 52428800;
|
|
61257
61269
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
61258
61270
|
function isValidEcosystem(value) {
|
|
@@ -61270,16 +61282,16 @@ function validateArgs3(args2) {
|
|
|
61270
61282
|
function detectEcosystems(directory) {
|
|
61271
61283
|
const ecosystems = [];
|
|
61272
61284
|
const cwd = directory;
|
|
61273
|
-
if (fs40.existsSync(
|
|
61285
|
+
if (fs40.existsSync(path52.join(cwd, "package.json"))) {
|
|
61274
61286
|
ecosystems.push("npm");
|
|
61275
61287
|
}
|
|
61276
|
-
if (fs40.existsSync(
|
|
61288
|
+
if (fs40.existsSync(path52.join(cwd, "pyproject.toml")) || fs40.existsSync(path52.join(cwd, "requirements.txt"))) {
|
|
61277
61289
|
ecosystems.push("pip");
|
|
61278
61290
|
}
|
|
61279
|
-
if (fs40.existsSync(
|
|
61291
|
+
if (fs40.existsSync(path52.join(cwd, "Cargo.toml"))) {
|
|
61280
61292
|
ecosystems.push("cargo");
|
|
61281
61293
|
}
|
|
61282
|
-
if (fs40.existsSync(
|
|
61294
|
+
if (fs40.existsSync(path52.join(cwd, "go.mod"))) {
|
|
61283
61295
|
ecosystems.push("go");
|
|
61284
61296
|
}
|
|
61285
61297
|
try {
|
|
@@ -61288,10 +61300,10 @@ function detectEcosystems(directory) {
|
|
|
61288
61300
|
ecosystems.push("dotnet");
|
|
61289
61301
|
}
|
|
61290
61302
|
} catch {}
|
|
61291
|
-
if (fs40.existsSync(
|
|
61303
|
+
if (fs40.existsSync(path52.join(cwd, "Gemfile")) || fs40.existsSync(path52.join(cwd, "Gemfile.lock"))) {
|
|
61292
61304
|
ecosystems.push("ruby");
|
|
61293
61305
|
}
|
|
61294
|
-
if (fs40.existsSync(
|
|
61306
|
+
if (fs40.existsSync(path52.join(cwd, "pubspec.yaml"))) {
|
|
61295
61307
|
ecosystems.push("dart");
|
|
61296
61308
|
}
|
|
61297
61309
|
return ecosystems;
|
|
@@ -62313,7 +62325,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
|
|
|
62313
62325
|
// src/tools/pre-check-batch.ts
|
|
62314
62326
|
init_dist();
|
|
62315
62327
|
import * as fs42 from "fs";
|
|
62316
|
-
import * as
|
|
62328
|
+
import * as path54 from "path";
|
|
62317
62329
|
|
|
62318
62330
|
// node_modules/yocto-queue/index.js
|
|
62319
62331
|
class Node2 {
|
|
@@ -62588,7 +62600,7 @@ init_dist();
|
|
|
62588
62600
|
init_manager();
|
|
62589
62601
|
init_detector();
|
|
62590
62602
|
import * as fs41 from "fs";
|
|
62591
|
-
import * as
|
|
62603
|
+
import * as path53 from "path";
|
|
62592
62604
|
import { extname as extname10 } from "path";
|
|
62593
62605
|
|
|
62594
62606
|
// src/sast/rules/c.ts
|
|
@@ -63554,7 +63566,7 @@ async function sastScan(input, directory, config3) {
|
|
|
63554
63566
|
_filesSkipped++;
|
|
63555
63567
|
continue;
|
|
63556
63568
|
}
|
|
63557
|
-
const resolvedPath =
|
|
63569
|
+
const resolvedPath = path53.isAbsolute(filePath) ? filePath : path53.resolve(directory, filePath);
|
|
63558
63570
|
if (!fs41.existsSync(resolvedPath)) {
|
|
63559
63571
|
_filesSkipped++;
|
|
63560
63572
|
continue;
|
|
@@ -63753,18 +63765,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
63753
63765
|
let resolved;
|
|
63754
63766
|
const isWinAbs = isWindowsAbsolutePath(inputPath);
|
|
63755
63767
|
if (isWinAbs) {
|
|
63756
|
-
resolved =
|
|
63757
|
-
} else if (
|
|
63758
|
-
resolved =
|
|
63768
|
+
resolved = path54.win32.resolve(inputPath);
|
|
63769
|
+
} else if (path54.isAbsolute(inputPath)) {
|
|
63770
|
+
resolved = path54.resolve(inputPath);
|
|
63759
63771
|
} else {
|
|
63760
|
-
resolved =
|
|
63772
|
+
resolved = path54.resolve(baseDir, inputPath);
|
|
63761
63773
|
}
|
|
63762
|
-
const workspaceResolved =
|
|
63774
|
+
const workspaceResolved = path54.resolve(workspaceDir);
|
|
63763
63775
|
let relative6;
|
|
63764
63776
|
if (isWinAbs) {
|
|
63765
|
-
relative6 =
|
|
63777
|
+
relative6 = path54.win32.relative(workspaceResolved, resolved);
|
|
63766
63778
|
} else {
|
|
63767
|
-
relative6 =
|
|
63779
|
+
relative6 = path54.relative(workspaceResolved, resolved);
|
|
63768
63780
|
}
|
|
63769
63781
|
if (relative6.startsWith("..")) {
|
|
63770
63782
|
return "path traversal detected";
|
|
@@ -63825,13 +63837,13 @@ async function runLintWrapped(files, directory, _config) {
|
|
|
63825
63837
|
}
|
|
63826
63838
|
async function runLintOnFiles(linter, files, workspaceDir) {
|
|
63827
63839
|
const isWindows = process.platform === "win32";
|
|
63828
|
-
const binDir =
|
|
63840
|
+
const binDir = path54.join(workspaceDir, "node_modules", ".bin");
|
|
63829
63841
|
const validatedFiles = [];
|
|
63830
63842
|
for (const file3 of files) {
|
|
63831
63843
|
if (typeof file3 !== "string") {
|
|
63832
63844
|
continue;
|
|
63833
63845
|
}
|
|
63834
|
-
const resolvedPath =
|
|
63846
|
+
const resolvedPath = path54.resolve(file3);
|
|
63835
63847
|
const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
|
|
63836
63848
|
if (validationError) {
|
|
63837
63849
|
continue;
|
|
@@ -63849,10 +63861,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
|
|
|
63849
63861
|
}
|
|
63850
63862
|
let command;
|
|
63851
63863
|
if (linter === "biome") {
|
|
63852
|
-
const biomeBin = isWindows ?
|
|
63864
|
+
const biomeBin = isWindows ? path54.join(binDir, "biome.EXE") : path54.join(binDir, "biome");
|
|
63853
63865
|
command = [biomeBin, "check", ...validatedFiles];
|
|
63854
63866
|
} else {
|
|
63855
|
-
const eslintBin = isWindows ?
|
|
63867
|
+
const eslintBin = isWindows ? path54.join(binDir, "eslint.cmd") : path54.join(binDir, "eslint");
|
|
63856
63868
|
command = [eslintBin, ...validatedFiles];
|
|
63857
63869
|
}
|
|
63858
63870
|
try {
|
|
@@ -63989,7 +64001,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
63989
64001
|
skippedFiles++;
|
|
63990
64002
|
continue;
|
|
63991
64003
|
}
|
|
63992
|
-
const resolvedPath =
|
|
64004
|
+
const resolvedPath = path54.resolve(file3);
|
|
63993
64005
|
const validationError = validatePath(resolvedPath, directory, directory);
|
|
63994
64006
|
if (validationError) {
|
|
63995
64007
|
skippedFiles++;
|
|
@@ -64007,7 +64019,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
64007
64019
|
};
|
|
64008
64020
|
}
|
|
64009
64021
|
for (const file3 of validatedFiles) {
|
|
64010
|
-
const ext =
|
|
64022
|
+
const ext = path54.extname(file3).toLowerCase();
|
|
64011
64023
|
if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
|
|
64012
64024
|
skippedFiles++;
|
|
64013
64025
|
continue;
|
|
@@ -64125,6 +64137,99 @@ async function runQualityBudgetWrapped(changedFiles, directory, _config) {
|
|
|
64125
64137
|
};
|
|
64126
64138
|
}
|
|
64127
64139
|
}
|
|
64140
|
+
var GATE_SEVERITIES = new Set(["high", "critical"]);
|
|
64141
|
+
async function runGitDiff(args2, directory) {
|
|
64142
|
+
try {
|
|
64143
|
+
const proc = Bun.spawn(["git", "diff", ...args2], {
|
|
64144
|
+
cwd: directory,
|
|
64145
|
+
stdout: "pipe",
|
|
64146
|
+
stderr: "pipe"
|
|
64147
|
+
});
|
|
64148
|
+
const [exitCode, stdout] = await Promise.all([
|
|
64149
|
+
proc.exited,
|
|
64150
|
+
new Response(proc.stdout).text()
|
|
64151
|
+
]);
|
|
64152
|
+
if (exitCode !== 0)
|
|
64153
|
+
return null;
|
|
64154
|
+
const trimmed = stdout.trim();
|
|
64155
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
64156
|
+
} catch {
|
|
64157
|
+
return null;
|
|
64158
|
+
}
|
|
64159
|
+
}
|
|
64160
|
+
function parseDiffLineRanges(diffOutput) {
|
|
64161
|
+
const result = new Map;
|
|
64162
|
+
let currentFile = null;
|
|
64163
|
+
for (const line of diffOutput.split(`
|
|
64164
|
+
`)) {
|
|
64165
|
+
if (line.startsWith("+++ b/")) {
|
|
64166
|
+
currentFile = line.slice(6).trim();
|
|
64167
|
+
if (!result.has(currentFile)) {
|
|
64168
|
+
result.set(currentFile, new Set);
|
|
64169
|
+
}
|
|
64170
|
+
continue;
|
|
64171
|
+
}
|
|
64172
|
+
if (line.startsWith("@@") && currentFile) {
|
|
64173
|
+
const match = line.match(/^@@ [^+]*\+(\d+)(?:,(\d+))? @@/);
|
|
64174
|
+
if (match) {
|
|
64175
|
+
const start2 = parseInt(match[1], 10);
|
|
64176
|
+
const count = match[2] !== undefined ? parseInt(match[2], 10) : 1;
|
|
64177
|
+
const lines = result.get(currentFile);
|
|
64178
|
+
for (let i2 = start2;i2 < start2 + count; i2++) {
|
|
64179
|
+
lines.add(i2);
|
|
64180
|
+
}
|
|
64181
|
+
}
|
|
64182
|
+
}
|
|
64183
|
+
}
|
|
64184
|
+
return result;
|
|
64185
|
+
}
|
|
64186
|
+
async function getChangedLineRanges(directory) {
|
|
64187
|
+
try {
|
|
64188
|
+
for (const baseBranch of ["origin/main", "origin/master", "main", "master"]) {
|
|
64189
|
+
const mergeBaseProc = Bun.spawn(["git", "merge-base", baseBranch, "HEAD"], { cwd: directory, stdout: "pipe", stderr: "pipe" });
|
|
64190
|
+
const [mbExit, mbOut] = await Promise.all([
|
|
64191
|
+
mergeBaseProc.exited,
|
|
64192
|
+
new Response(mergeBaseProc.stdout).text()
|
|
64193
|
+
]);
|
|
64194
|
+
if (mbExit === 0 && mbOut.trim()) {
|
|
64195
|
+
const mergeBase = mbOut.trim();
|
|
64196
|
+
const diffOut = await runGitDiff(["-U0", `${mergeBase}..HEAD`], directory);
|
|
64197
|
+
if (diffOut) {
|
|
64198
|
+
return parseDiffLineRanges(diffOut);
|
|
64199
|
+
}
|
|
64200
|
+
}
|
|
64201
|
+
}
|
|
64202
|
+
const diffHead1 = await runGitDiff(["-U0", "HEAD~1"], directory);
|
|
64203
|
+
if (diffHead1) {
|
|
64204
|
+
return parseDiffLineRanges(diffHead1);
|
|
64205
|
+
}
|
|
64206
|
+
const diffHead = await runGitDiff(["-U0", "HEAD"], directory);
|
|
64207
|
+
if (diffHead) {
|
|
64208
|
+
return parseDiffLineRanges(diffHead);
|
|
64209
|
+
}
|
|
64210
|
+
return null;
|
|
64211
|
+
} catch {
|
|
64212
|
+
return null;
|
|
64213
|
+
}
|
|
64214
|
+
}
|
|
64215
|
+
function classifySastFindings(findings, changedLineRanges, directory) {
|
|
64216
|
+
if (!changedLineRanges || changedLineRanges.size === 0) {
|
|
64217
|
+
return { newFindings: findings, preexistingFindings: [] };
|
|
64218
|
+
}
|
|
64219
|
+
const newFindings = [];
|
|
64220
|
+
const preexistingFindings = [];
|
|
64221
|
+
for (const finding of findings) {
|
|
64222
|
+
const filePath = finding.location.file;
|
|
64223
|
+
const normalised = path54.relative(directory, filePath).replace(/\\/g, "/");
|
|
64224
|
+
const changedLines = changedLineRanges.get(normalised);
|
|
64225
|
+
if (changedLines && changedLines.has(finding.location.line)) {
|
|
64226
|
+
newFindings.push(finding);
|
|
64227
|
+
} else {
|
|
64228
|
+
preexistingFindings.push(finding);
|
|
64229
|
+
}
|
|
64230
|
+
}
|
|
64231
|
+
return { newFindings, preexistingFindings };
|
|
64232
|
+
}
|
|
64128
64233
|
async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
64129
64234
|
const effectiveWorkspaceDir = workspaceDir || input.directory || contextDir;
|
|
64130
64235
|
const { files, directory, sast_threshold = "medium", config: config3 } = input;
|
|
@@ -64166,7 +64271,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
|
64166
64271
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
64167
64272
|
continue;
|
|
64168
64273
|
}
|
|
64169
|
-
changedFiles.push(
|
|
64274
|
+
changedFiles.push(path54.resolve(directory, file3));
|
|
64170
64275
|
}
|
|
64171
64276
|
if (changedFiles.length === 0) {
|
|
64172
64277
|
warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
|
|
@@ -64233,10 +64338,24 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
|
64233
64338
|
warn(`Failed to persist secretscan evidence: ${e instanceof Error ? e.message : String(e)}`);
|
|
64234
64339
|
}
|
|
64235
64340
|
}
|
|
64341
|
+
let sastPreexistingFindings;
|
|
64236
64342
|
if (sastScanResult.ran && sastScanResult.result) {
|
|
64237
64343
|
if (sastScanResult.result.verdict === "fail") {
|
|
64238
|
-
|
|
64239
|
-
|
|
64344
|
+
const gateFindings = sastScanResult.result.findings.filter((f) => GATE_SEVERITIES.has(f.severity));
|
|
64345
|
+
if (gateFindings.length > 0) {
|
|
64346
|
+
const changedLineRanges = await getChangedLineRanges(directory);
|
|
64347
|
+
const { newFindings, preexistingFindings } = classifySastFindings(gateFindings, changedLineRanges, directory);
|
|
64348
|
+
if (newFindings.length > 0) {
|
|
64349
|
+
gatesPassed = false;
|
|
64350
|
+
warn(`pre_check_batch: SAST scan found ${newFindings.length} new HIGH/CRITICAL finding(s) on changed lines - GATE FAILED`);
|
|
64351
|
+
} else if (preexistingFindings.length > 0) {
|
|
64352
|
+
sastPreexistingFindings = preexistingFindings;
|
|
64353
|
+
warn(`pre_check_batch: SAST scan found ${preexistingFindings.length} pre-existing HIGH/CRITICAL finding(s) on unchanged lines - passing to reviewer for triage`);
|
|
64354
|
+
}
|
|
64355
|
+
} else {
|
|
64356
|
+
gatesPassed = false;
|
|
64357
|
+
warn("pre_check_batch: SAST scan found vulnerabilities - GATE FAILED");
|
|
64358
|
+
}
|
|
64240
64359
|
}
|
|
64241
64360
|
} else if (sastScanResult.error) {
|
|
64242
64361
|
gatesPassed = false;
|
|
@@ -64255,7 +64374,10 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
|
|
|
64255
64374
|
secretscan: secretscanResult,
|
|
64256
64375
|
sast_scan: sastScanResult,
|
|
64257
64376
|
quality_budget: qualityBudgetResult,
|
|
64258
|
-
total_duration_ms: Math.round(totalDuration)
|
|
64377
|
+
total_duration_ms: Math.round(totalDuration),
|
|
64378
|
+
...sastPreexistingFindings && sastPreexistingFindings.length > 0 && {
|
|
64379
|
+
sast_preexisting_findings: sastPreexistingFindings
|
|
64380
|
+
}
|
|
64259
64381
|
};
|
|
64260
64382
|
const outputSize = JSON.stringify(result).length;
|
|
64261
64383
|
if (outputSize > MAX_COMBINED_BYTES) {
|
|
@@ -64337,7 +64459,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
64337
64459
|
};
|
|
64338
64460
|
return JSON.stringify(errorResult, null, 2);
|
|
64339
64461
|
}
|
|
64340
|
-
const resolvedDirectory =
|
|
64462
|
+
const resolvedDirectory = path54.resolve(typedArgs.directory);
|
|
64341
64463
|
const workspaceAnchor = resolvedDirectory;
|
|
64342
64464
|
const dirError = validateDirectory2(resolvedDirectory, workspaceAnchor);
|
|
64343
64465
|
if (dirError) {
|
|
@@ -64444,24 +64566,24 @@ ${paginatedContent}`;
|
|
|
64444
64566
|
// src/tools/save-plan.ts
|
|
64445
64567
|
init_tool();
|
|
64446
64568
|
import * as fs44 from "fs";
|
|
64447
|
-
import * as
|
|
64569
|
+
import * as path56 from "path";
|
|
64448
64570
|
|
|
64449
64571
|
// src/parallel/file-locks.ts
|
|
64450
64572
|
import * as fs43 from "fs";
|
|
64451
|
-
import * as
|
|
64573
|
+
import * as path55 from "path";
|
|
64452
64574
|
var LOCKS_DIR = ".swarm/locks";
|
|
64453
64575
|
var LOCK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
64454
64576
|
function getLockFilePath(directory, filePath) {
|
|
64455
|
-
const normalized =
|
|
64456
|
-
if (!normalized.startsWith(
|
|
64577
|
+
const normalized = path55.resolve(directory, filePath);
|
|
64578
|
+
if (!normalized.startsWith(path55.resolve(directory))) {
|
|
64457
64579
|
throw new Error("Invalid file path: path traversal not allowed");
|
|
64458
64580
|
}
|
|
64459
64581
|
const hash3 = Buffer.from(normalized).toString("base64").replace(/[/+=]/g, "_");
|
|
64460
|
-
return
|
|
64582
|
+
return path55.join(directory, LOCKS_DIR, `${hash3}.lock`);
|
|
64461
64583
|
}
|
|
64462
64584
|
function tryAcquireLock(directory, filePath, agent, taskId) {
|
|
64463
64585
|
const lockPath = getLockFilePath(directory, filePath);
|
|
64464
|
-
const locksDir =
|
|
64586
|
+
const locksDir = path55.dirname(lockPath);
|
|
64465
64587
|
if (!fs43.existsSync(locksDir)) {
|
|
64466
64588
|
fs43.mkdirSync(locksDir, { recursive: true });
|
|
64467
64589
|
}
|
|
@@ -64627,7 +64749,7 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
64627
64749
|
try {
|
|
64628
64750
|
await savePlan(dir, plan);
|
|
64629
64751
|
try {
|
|
64630
|
-
const markerPath =
|
|
64752
|
+
const markerPath = path56.join(dir, ".swarm", ".plan-write-marker");
|
|
64631
64753
|
const marker = JSON.stringify({
|
|
64632
64754
|
source: "save_plan",
|
|
64633
64755
|
timestamp: new Date().toISOString(),
|
|
@@ -64650,7 +64772,7 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
64650
64772
|
return {
|
|
64651
64773
|
success: true,
|
|
64652
64774
|
message: "Plan saved successfully",
|
|
64653
|
-
plan_path:
|
|
64775
|
+
plan_path: path56.join(dir, ".swarm", "plan.json"),
|
|
64654
64776
|
phases_count: plan.phases.length,
|
|
64655
64777
|
tasks_count: tasksCount,
|
|
64656
64778
|
...warnings.length > 0 ? { warnings } : {}
|
|
@@ -64693,7 +64815,7 @@ var save_plan = createSwarmTool({
|
|
|
64693
64815
|
init_dist();
|
|
64694
64816
|
init_manager();
|
|
64695
64817
|
import * as fs45 from "fs";
|
|
64696
|
-
import * as
|
|
64818
|
+
import * as path57 from "path";
|
|
64697
64819
|
|
|
64698
64820
|
// src/sbom/detectors/index.ts
|
|
64699
64821
|
init_utils();
|
|
@@ -65543,7 +65665,7 @@ function findManifestFiles(rootDir) {
|
|
|
65543
65665
|
try {
|
|
65544
65666
|
const entries = fs45.readdirSync(dir, { withFileTypes: true });
|
|
65545
65667
|
for (const entry of entries) {
|
|
65546
|
-
const fullPath =
|
|
65668
|
+
const fullPath = path57.join(dir, entry.name);
|
|
65547
65669
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
65548
65670
|
continue;
|
|
65549
65671
|
}
|
|
@@ -65552,7 +65674,7 @@ function findManifestFiles(rootDir) {
|
|
|
65552
65674
|
} else if (entry.isFile()) {
|
|
65553
65675
|
for (const pattern of patterns) {
|
|
65554
65676
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
65555
|
-
manifestFiles.push(
|
|
65677
|
+
manifestFiles.push(path57.relative(rootDir, fullPath));
|
|
65556
65678
|
break;
|
|
65557
65679
|
}
|
|
65558
65680
|
}
|
|
@@ -65570,11 +65692,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
65570
65692
|
try {
|
|
65571
65693
|
const entries = fs45.readdirSync(dir, { withFileTypes: true });
|
|
65572
65694
|
for (const entry of entries) {
|
|
65573
|
-
const fullPath =
|
|
65695
|
+
const fullPath = path57.join(dir, entry.name);
|
|
65574
65696
|
if (entry.isFile()) {
|
|
65575
65697
|
for (const pattern of patterns) {
|
|
65576
65698
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
65577
|
-
found.push(
|
|
65699
|
+
found.push(path57.relative(workingDir, fullPath));
|
|
65578
65700
|
break;
|
|
65579
65701
|
}
|
|
65580
65702
|
}
|
|
@@ -65587,11 +65709,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
65587
65709
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
65588
65710
|
const dirs = new Set;
|
|
65589
65711
|
for (const file3 of changedFiles) {
|
|
65590
|
-
let currentDir =
|
|
65712
|
+
let currentDir = path57.dirname(file3);
|
|
65591
65713
|
while (true) {
|
|
65592
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
65593
|
-
dirs.add(
|
|
65594
|
-
const parent =
|
|
65714
|
+
if (currentDir && currentDir !== "." && currentDir !== path57.sep) {
|
|
65715
|
+
dirs.add(path57.join(workingDir, currentDir));
|
|
65716
|
+
const parent = path57.dirname(currentDir);
|
|
65595
65717
|
if (parent === currentDir)
|
|
65596
65718
|
break;
|
|
65597
65719
|
currentDir = parent;
|
|
@@ -65675,7 +65797,7 @@ var sbom_generate = createSwarmTool({
|
|
|
65675
65797
|
const changedFiles = obj.changed_files;
|
|
65676
65798
|
const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
|
|
65677
65799
|
const workingDir = directory;
|
|
65678
|
-
const outputDir =
|
|
65800
|
+
const outputDir = path57.isAbsolute(relativeOutputDir) ? relativeOutputDir : path57.join(workingDir, relativeOutputDir);
|
|
65679
65801
|
let manifestFiles = [];
|
|
65680
65802
|
if (scope === "all") {
|
|
65681
65803
|
manifestFiles = findManifestFiles(workingDir);
|
|
@@ -65698,7 +65820,7 @@ var sbom_generate = createSwarmTool({
|
|
|
65698
65820
|
const processedFiles = [];
|
|
65699
65821
|
for (const manifestFile of manifestFiles) {
|
|
65700
65822
|
try {
|
|
65701
|
-
const fullPath =
|
|
65823
|
+
const fullPath = path57.isAbsolute(manifestFile) ? manifestFile : path57.join(workingDir, manifestFile);
|
|
65702
65824
|
if (!fs45.existsSync(fullPath)) {
|
|
65703
65825
|
continue;
|
|
65704
65826
|
}
|
|
@@ -65715,7 +65837,7 @@ var sbom_generate = createSwarmTool({
|
|
|
65715
65837
|
const bom = generateCycloneDX(allComponents);
|
|
65716
65838
|
const bomJson = serializeCycloneDX(bom);
|
|
65717
65839
|
const filename = generateSbomFilename();
|
|
65718
|
-
const outputPath =
|
|
65840
|
+
const outputPath = path57.join(outputDir, filename);
|
|
65719
65841
|
fs45.writeFileSync(outputPath, bomJson, "utf-8");
|
|
65720
65842
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
65721
65843
|
try {
|
|
@@ -65759,7 +65881,7 @@ var sbom_generate = createSwarmTool({
|
|
|
65759
65881
|
init_dist();
|
|
65760
65882
|
init_create_tool();
|
|
65761
65883
|
import * as fs46 from "fs";
|
|
65762
|
-
import * as
|
|
65884
|
+
import * as path58 from "path";
|
|
65763
65885
|
var SPEC_CANDIDATES = [
|
|
65764
65886
|
"openapi.json",
|
|
65765
65887
|
"openapi.yaml",
|
|
@@ -65791,12 +65913,12 @@ function normalizePath2(p) {
|
|
|
65791
65913
|
}
|
|
65792
65914
|
function discoverSpecFile(cwd, specFileArg) {
|
|
65793
65915
|
if (specFileArg) {
|
|
65794
|
-
const resolvedPath =
|
|
65795
|
-
const normalizedCwd = cwd.endsWith(
|
|
65916
|
+
const resolvedPath = path58.resolve(cwd, specFileArg);
|
|
65917
|
+
const normalizedCwd = cwd.endsWith(path58.sep) ? cwd : cwd + path58.sep;
|
|
65796
65918
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
65797
65919
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
65798
65920
|
}
|
|
65799
|
-
const ext =
|
|
65921
|
+
const ext = path58.extname(resolvedPath).toLowerCase();
|
|
65800
65922
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
65801
65923
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
65802
65924
|
}
|
|
@@ -65810,7 +65932,7 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
65810
65932
|
return resolvedPath;
|
|
65811
65933
|
}
|
|
65812
65934
|
for (const candidate of SPEC_CANDIDATES) {
|
|
65813
|
-
const candidatePath =
|
|
65935
|
+
const candidatePath = path58.resolve(cwd, candidate);
|
|
65814
65936
|
if (fs46.existsSync(candidatePath)) {
|
|
65815
65937
|
const stats = fs46.statSync(candidatePath);
|
|
65816
65938
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
@@ -65822,7 +65944,7 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
65822
65944
|
}
|
|
65823
65945
|
function parseSpec(specFile) {
|
|
65824
65946
|
const content = fs46.readFileSync(specFile, "utf-8");
|
|
65825
|
-
const ext =
|
|
65947
|
+
const ext = path58.extname(specFile).toLowerCase();
|
|
65826
65948
|
if (ext === ".json") {
|
|
65827
65949
|
return parseJsonSpec(content);
|
|
65828
65950
|
}
|
|
@@ -65898,7 +66020,7 @@ function extractRoutes(cwd) {
|
|
|
65898
66020
|
return;
|
|
65899
66021
|
}
|
|
65900
66022
|
for (const entry of entries) {
|
|
65901
|
-
const fullPath =
|
|
66023
|
+
const fullPath = path58.join(dir, entry.name);
|
|
65902
66024
|
if (entry.isSymbolicLink()) {
|
|
65903
66025
|
continue;
|
|
65904
66026
|
}
|
|
@@ -65908,7 +66030,7 @@ function extractRoutes(cwd) {
|
|
|
65908
66030
|
}
|
|
65909
66031
|
walkDir(fullPath);
|
|
65910
66032
|
} else if (entry.isFile()) {
|
|
65911
|
-
const ext =
|
|
66033
|
+
const ext = path58.extname(entry.name).toLowerCase();
|
|
65912
66034
|
const baseName = entry.name.toLowerCase();
|
|
65913
66035
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
65914
66036
|
continue;
|
|
@@ -66078,7 +66200,7 @@ init_secretscan();
|
|
|
66078
66200
|
init_tool();
|
|
66079
66201
|
init_create_tool();
|
|
66080
66202
|
import * as fs47 from "fs";
|
|
66081
|
-
import * as
|
|
66203
|
+
import * as path59 from "path";
|
|
66082
66204
|
var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
|
|
66083
66205
|
var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
66084
66206
|
function containsWindowsAttacks(str) {
|
|
@@ -66095,11 +66217,11 @@ function containsWindowsAttacks(str) {
|
|
|
66095
66217
|
}
|
|
66096
66218
|
function isPathInWorkspace(filePath, workspace) {
|
|
66097
66219
|
try {
|
|
66098
|
-
const resolvedPath =
|
|
66220
|
+
const resolvedPath = path59.resolve(workspace, filePath);
|
|
66099
66221
|
const realWorkspace = fs47.realpathSync(workspace);
|
|
66100
66222
|
const realResolvedPath = fs47.realpathSync(resolvedPath);
|
|
66101
|
-
const relativePath =
|
|
66102
|
-
if (relativePath.startsWith("..") ||
|
|
66223
|
+
const relativePath = path59.relative(realWorkspace, realResolvedPath);
|
|
66224
|
+
if (relativePath.startsWith("..") || path59.isAbsolute(relativePath)) {
|
|
66103
66225
|
return false;
|
|
66104
66226
|
}
|
|
66105
66227
|
return true;
|
|
@@ -66111,7 +66233,7 @@ function validatePathForRead(filePath, workspace) {
|
|
|
66111
66233
|
return isPathInWorkspace(filePath, workspace);
|
|
66112
66234
|
}
|
|
66113
66235
|
function extractTSSymbols(filePath, cwd) {
|
|
66114
|
-
const fullPath =
|
|
66236
|
+
const fullPath = path59.join(cwd, filePath);
|
|
66115
66237
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
66116
66238
|
return [];
|
|
66117
66239
|
}
|
|
@@ -66263,7 +66385,7 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
66263
66385
|
});
|
|
66264
66386
|
}
|
|
66265
66387
|
function extractPythonSymbols(filePath, cwd) {
|
|
66266
|
-
const fullPath =
|
|
66388
|
+
const fullPath = path59.join(cwd, filePath);
|
|
66267
66389
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
66268
66390
|
return [];
|
|
66269
66391
|
}
|
|
@@ -66346,7 +66468,7 @@ var symbols = createSwarmTool({
|
|
|
66346
66468
|
}, null, 2);
|
|
66347
66469
|
}
|
|
66348
66470
|
const cwd = directory;
|
|
66349
|
-
const ext =
|
|
66471
|
+
const ext = path59.extname(file3);
|
|
66350
66472
|
if (containsControlChars(file3)) {
|
|
66351
66473
|
return JSON.stringify({
|
|
66352
66474
|
file: file3,
|
|
@@ -66418,7 +66540,7 @@ init_dist();
|
|
|
66418
66540
|
init_utils();
|
|
66419
66541
|
init_create_tool();
|
|
66420
66542
|
import * as fs48 from "fs";
|
|
66421
|
-
import * as
|
|
66543
|
+
import * as path60 from "path";
|
|
66422
66544
|
var MAX_TEXT_LENGTH = 200;
|
|
66423
66545
|
var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
|
|
66424
66546
|
var SUPPORTED_EXTENSIONS2 = new Set([
|
|
@@ -66483,9 +66605,9 @@ function validatePathsInput(paths, cwd) {
|
|
|
66483
66605
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
66484
66606
|
}
|
|
66485
66607
|
try {
|
|
66486
|
-
const resolvedPath =
|
|
66487
|
-
const normalizedCwd =
|
|
66488
|
-
const normalizedResolved =
|
|
66608
|
+
const resolvedPath = path60.resolve(paths);
|
|
66609
|
+
const normalizedCwd = path60.resolve(cwd);
|
|
66610
|
+
const normalizedResolved = path60.resolve(resolvedPath);
|
|
66489
66611
|
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
66490
66612
|
return {
|
|
66491
66613
|
error: "paths must be within the current working directory",
|
|
@@ -66501,7 +66623,7 @@ function validatePathsInput(paths, cwd) {
|
|
|
66501
66623
|
}
|
|
66502
66624
|
}
|
|
66503
66625
|
function isSupportedExtension(filePath) {
|
|
66504
|
-
const ext =
|
|
66626
|
+
const ext = path60.extname(filePath).toLowerCase();
|
|
66505
66627
|
return SUPPORTED_EXTENSIONS2.has(ext);
|
|
66506
66628
|
}
|
|
66507
66629
|
function findSourceFiles2(dir, files = []) {
|
|
@@ -66516,7 +66638,7 @@ function findSourceFiles2(dir, files = []) {
|
|
|
66516
66638
|
if (SKIP_DIRECTORIES4.has(entry)) {
|
|
66517
66639
|
continue;
|
|
66518
66640
|
}
|
|
66519
|
-
const fullPath =
|
|
66641
|
+
const fullPath = path60.join(dir, entry);
|
|
66520
66642
|
let stat2;
|
|
66521
66643
|
try {
|
|
66522
66644
|
stat2 = fs48.statSync(fullPath);
|
|
@@ -66628,7 +66750,7 @@ var todo_extract = createSwarmTool({
|
|
|
66628
66750
|
filesToScan.push(scanPath);
|
|
66629
66751
|
} else {
|
|
66630
66752
|
const errorResult = {
|
|
66631
|
-
error: `unsupported file extension: ${
|
|
66753
|
+
error: `unsupported file extension: ${path60.extname(scanPath)}`,
|
|
66632
66754
|
total: 0,
|
|
66633
66755
|
byPriority: { high: 0, medium: 0, low: 0 },
|
|
66634
66756
|
entries: []
|
|
@@ -66675,14 +66797,14 @@ init_tool();
|
|
|
66675
66797
|
init_schema();
|
|
66676
66798
|
init_gate_evidence();
|
|
66677
66799
|
import * as fs50 from "fs";
|
|
66678
|
-
import * as
|
|
66800
|
+
import * as path62 from "path";
|
|
66679
66801
|
|
|
66680
66802
|
// src/hooks/diff-scope.ts
|
|
66681
66803
|
import * as fs49 from "fs";
|
|
66682
|
-
import * as
|
|
66804
|
+
import * as path61 from "path";
|
|
66683
66805
|
function getDeclaredScope(taskId, directory) {
|
|
66684
66806
|
try {
|
|
66685
|
-
const planPath =
|
|
66807
|
+
const planPath = path61.join(directory, ".swarm", "plan.json");
|
|
66686
66808
|
if (!fs49.existsSync(planPath))
|
|
66687
66809
|
return null;
|
|
66688
66810
|
const raw = fs49.readFileSync(planPath, "utf-8");
|
|
@@ -66798,7 +66920,7 @@ var TIER_3_PATTERNS = [
|
|
|
66798
66920
|
];
|
|
66799
66921
|
function matchesTier3Pattern(files) {
|
|
66800
66922
|
for (const file3 of files) {
|
|
66801
|
-
const fileName =
|
|
66923
|
+
const fileName = path62.basename(file3);
|
|
66802
66924
|
for (const pattern of TIER_3_PATTERNS) {
|
|
66803
66925
|
if (pattern.test(fileName)) {
|
|
66804
66926
|
return true;
|
|
@@ -66812,7 +66934,7 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
66812
66934
|
if (hasActiveTurboMode()) {
|
|
66813
66935
|
const resolvedDir2 = workingDirectory;
|
|
66814
66936
|
try {
|
|
66815
|
-
const planPath =
|
|
66937
|
+
const planPath = path62.join(resolvedDir2, ".swarm", "plan.json");
|
|
66816
66938
|
const planRaw = fs50.readFileSync(planPath, "utf-8");
|
|
66817
66939
|
const plan = JSON.parse(planRaw);
|
|
66818
66940
|
for (const planPhase of plan.phases ?? []) {
|
|
@@ -66879,7 +67001,7 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
66879
67001
|
}
|
|
66880
67002
|
try {
|
|
66881
67003
|
const resolvedDir2 = workingDirectory;
|
|
66882
|
-
const planPath =
|
|
67004
|
+
const planPath = path62.join(resolvedDir2, ".swarm", "plan.json");
|
|
66883
67005
|
const planRaw = fs50.readFileSync(planPath, "utf-8");
|
|
66884
67006
|
const plan = JSON.parse(planRaw);
|
|
66885
67007
|
for (const planPhase of plan.phases ?? []) {
|
|
@@ -67062,8 +67184,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
67062
67184
|
};
|
|
67063
67185
|
}
|
|
67064
67186
|
}
|
|
67065
|
-
normalizedDir =
|
|
67066
|
-
const pathParts = normalizedDir.split(
|
|
67187
|
+
normalizedDir = path62.normalize(args2.working_directory);
|
|
67188
|
+
const pathParts = normalizedDir.split(path62.sep);
|
|
67067
67189
|
if (pathParts.includes("..")) {
|
|
67068
67190
|
return {
|
|
67069
67191
|
success: false,
|
|
@@ -67073,10 +67195,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
67073
67195
|
]
|
|
67074
67196
|
};
|
|
67075
67197
|
}
|
|
67076
|
-
const resolvedDir =
|
|
67198
|
+
const resolvedDir = path62.resolve(normalizedDir);
|
|
67077
67199
|
try {
|
|
67078
67200
|
const realPath = fs50.realpathSync(resolvedDir);
|
|
67079
|
-
const planPath =
|
|
67201
|
+
const planPath = path62.join(realPath, ".swarm", "plan.json");
|
|
67080
67202
|
if (!fs50.existsSync(planPath)) {
|
|
67081
67203
|
return {
|
|
67082
67204
|
success: false,
|
|
@@ -67110,7 +67232,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
67110
67232
|
recoverTaskStateFromDelegations(args2.task_id);
|
|
67111
67233
|
let phaseRequiresReviewer = true;
|
|
67112
67234
|
try {
|
|
67113
|
-
const planPath =
|
|
67235
|
+
const planPath = path62.join(directory, ".swarm", "plan.json");
|
|
67114
67236
|
const planRaw = fs50.readFileSync(planPath, "utf-8");
|
|
67115
67237
|
const plan = JSON.parse(planRaw);
|
|
67116
67238
|
const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
|
|
@@ -67318,7 +67440,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
67318
67440
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
67319
67441
|
preflightTriggerManager = new PTM(automationConfig);
|
|
67320
67442
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
67321
|
-
const swarmDir =
|
|
67443
|
+
const swarmDir = path63.resolve(ctx.directory, ".swarm");
|
|
67322
67444
|
statusArtifact = new ASA(swarmDir);
|
|
67323
67445
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
67324
67446
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|
|
@@ -7,7 +7,7 @@ import { tool } from '@opencode-ai/plugin';
|
|
|
7
7
|
import type { PluginConfig } from '../config';
|
|
8
8
|
import type { LintResult } from './lint';
|
|
9
9
|
import type { QualityBudgetResult } from './quality-budget';
|
|
10
|
-
import type { SastScanResult } from './sast-scan';
|
|
10
|
+
import type { SastScanFinding, SastScanResult } from './sast-scan';
|
|
11
11
|
import type { SecretscanErrorResult, SecretscanResult } from './secretscan';
|
|
12
12
|
export interface PreCheckBatchInput {
|
|
13
13
|
/** List of specific files to check (optional) */
|
|
@@ -42,7 +42,32 @@ export interface PreCheckBatchResult {
|
|
|
42
42
|
quality_budget: ToolResult<QualityBudgetResult>;
|
|
43
43
|
/** Total duration in milliseconds */
|
|
44
44
|
total_duration_ms: number;
|
|
45
|
+
/** Pre-existing SAST findings on unchanged lines, requiring reviewer triage */
|
|
46
|
+
sast_preexisting_findings?: SastScanFinding[];
|
|
45
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Parse unified diff output (with -U0) to extract added/modified line numbers per file.
|
|
50
|
+
* Returns a Map from normalised file path → Set of changed line numbers.
|
|
51
|
+
*/
|
|
52
|
+
export declare function parseDiffLineRanges(diffOutput: string): Map<string, Set<number>>;
|
|
53
|
+
/**
|
|
54
|
+
* Get changed line ranges for the current branch vs its base.
|
|
55
|
+
* Tries three strategies in order:
|
|
56
|
+
* 1. merge-base diff against main/master (captures all branch changes, works after commit)
|
|
57
|
+
* 2. HEAD~1 (single-commit diff, works after commit)
|
|
58
|
+
* 3. HEAD (unstaged/staged changes, works before commit)
|
|
59
|
+
* Returns null if git is unavailable or no changes found.
|
|
60
|
+
*/
|
|
61
|
+
export declare function getChangedLineRanges(directory: string): Promise<Map<string, Set<number>> | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Classify SAST findings as "new" (on changed lines) or "pre-existing" (unchanged lines).
|
|
64
|
+
* A finding is "new" if its file+line intersects the changed line ranges from git diff.
|
|
65
|
+
* If line ranges cannot be determined (git unavailable), all findings are treated as new (fail-closed).
|
|
66
|
+
*/
|
|
67
|
+
export declare function classifySastFindings(findings: SastScanFinding[], changedLineRanges: Map<string, Set<number>> | null, directory: string): {
|
|
68
|
+
newFindings: SastScanFinding[];
|
|
69
|
+
preexistingFindings: SastScanFinding[];
|
|
70
|
+
};
|
|
46
71
|
/**
|
|
47
72
|
* Run all 4 pre-check tools in parallel with concurrency limit
|
|
48
73
|
* @param input - The pre-check batch input
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.40.
|
|
3
|
+
"version": "6.40.6",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|