opencode-swarm 6.22.20 → 6.23.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
CHANGED
|
@@ -33333,8 +33333,9 @@ function isHighEntropyString(str) {
|
|
|
33333
33333
|
function containsPathTraversal(str) {
|
|
33334
33334
|
if (/\.\.[/\\]/.test(str))
|
|
33335
33335
|
return true;
|
|
33336
|
-
|
|
33337
|
-
|
|
33336
|
+
if (/[/\\]\.\.$/.test(str) || str === "..")
|
|
33337
|
+
return true;
|
|
33338
|
+
if (/\.\.[/\\]/.test(path12.normalize(str.replace(/\*/g, "x"))))
|
|
33338
33339
|
return true;
|
|
33339
33340
|
if (str.includes("%2e%2e") || str.includes("%2E%2E"))
|
|
33340
33341
|
return true;
|
|
@@ -33342,6 +33343,58 @@ function containsPathTraversal(str) {
|
|
|
33342
33343
|
return true;
|
|
33343
33344
|
return false;
|
|
33344
33345
|
}
|
|
33346
|
+
function validateExcludePattern(exc) {
|
|
33347
|
+
if (exc.length === 0)
|
|
33348
|
+
return null;
|
|
33349
|
+
if (exc.length > MAX_FILE_PATH_LENGTH) {
|
|
33350
|
+
return `invalid exclude path: exceeds maximum length of ${MAX_FILE_PATH_LENGTH}`;
|
|
33351
|
+
}
|
|
33352
|
+
if (containsControlChars(exc)) {
|
|
33353
|
+
return "invalid exclude path: contains path traversal or control characters";
|
|
33354
|
+
}
|
|
33355
|
+
if (containsPathTraversal(exc)) {
|
|
33356
|
+
return "invalid exclude path: contains path traversal or control characters";
|
|
33357
|
+
}
|
|
33358
|
+
if (exc.startsWith("!")) {
|
|
33359
|
+
return "invalid exclude path: negation patterns are not supported";
|
|
33360
|
+
}
|
|
33361
|
+
if (exc.startsWith("/") || exc.startsWith("\\")) {
|
|
33362
|
+
return "invalid exclude path: absolute paths are not supported";
|
|
33363
|
+
}
|
|
33364
|
+
return null;
|
|
33365
|
+
}
|
|
33366
|
+
function isGlobOrPathPattern(pattern) {
|
|
33367
|
+
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
33368
|
+
}
|
|
33369
|
+
function loadSecretScanIgnore(scanDir) {
|
|
33370
|
+
const ignorePath = path12.join(scanDir, ".secretscanignore");
|
|
33371
|
+
try {
|
|
33372
|
+
if (!fs4.existsSync(ignorePath))
|
|
33373
|
+
return [];
|
|
33374
|
+
const content = fs4.readFileSync(ignorePath, "utf8");
|
|
33375
|
+
const patterns = [];
|
|
33376
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
33377
|
+
const line = rawLine.trim();
|
|
33378
|
+
if (!line || line.startsWith("#"))
|
|
33379
|
+
continue;
|
|
33380
|
+
if (validateExcludePattern(line) === null) {
|
|
33381
|
+
patterns.push(line);
|
|
33382
|
+
}
|
|
33383
|
+
}
|
|
33384
|
+
return patterns;
|
|
33385
|
+
} catch {
|
|
33386
|
+
return [];
|
|
33387
|
+
}
|
|
33388
|
+
}
|
|
33389
|
+
function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
33390
|
+
if (exactNames.has(entry))
|
|
33391
|
+
return true;
|
|
33392
|
+
for (const pattern of globPatterns) {
|
|
33393
|
+
if (path12.matchesGlob(relPath, pattern))
|
|
33394
|
+
return true;
|
|
33395
|
+
}
|
|
33396
|
+
return false;
|
|
33397
|
+
}
|
|
33345
33398
|
function containsControlChars(str) {
|
|
33346
33399
|
return /[\0\r]/.test(str);
|
|
33347
33400
|
}
|
|
@@ -33502,7 +33555,7 @@ function isPathWithinScope(realPath, scanDir) {
|
|
|
33502
33555
|
const resolvedRealPath = path12.resolve(realPath);
|
|
33503
33556
|
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path12.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
33504
33557
|
}
|
|
33505
|
-
function findScannableFiles(dir,
|
|
33558
|
+
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
33506
33559
|
skippedDirs: 0,
|
|
33507
33560
|
skippedFiles: 0,
|
|
33508
33561
|
fileErrors: 0,
|
|
@@ -33526,11 +33579,12 @@ function findScannableFiles(dir, excludeDirs, scanDir, visited, stats = {
|
|
|
33526
33579
|
return a.localeCompare(b);
|
|
33527
33580
|
});
|
|
33528
33581
|
for (const entry of entries) {
|
|
33529
|
-
|
|
33582
|
+
const fullPath = path12.join(dir, entry);
|
|
33583
|
+
const relPath = path12.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
33584
|
+
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
33530
33585
|
stats.skippedDirs++;
|
|
33531
33586
|
continue;
|
|
33532
33587
|
}
|
|
33533
|
-
const fullPath = path12.join(dir, entry);
|
|
33534
33588
|
let lstat;
|
|
33535
33589
|
try {
|
|
33536
33590
|
lstat = fs4.lstatSync(fullPath);
|
|
@@ -33558,7 +33612,7 @@ function findScannableFiles(dir, excludeDirs, scanDir, visited, stats = {
|
|
|
33558
33612
|
stats.symlinkSkipped++;
|
|
33559
33613
|
continue;
|
|
33560
33614
|
}
|
|
33561
|
-
const subFiles = findScannableFiles(fullPath,
|
|
33615
|
+
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
33562
33616
|
files.push(...subFiles);
|
|
33563
33617
|
} else if (lstat.isFile()) {
|
|
33564
33618
|
const ext = path12.extname(fullPath).toLowerCase();
|
|
@@ -33572,10 +33626,10 @@ function findScannableFiles(dir, excludeDirs, scanDir, visited, stats = {
|
|
|
33572
33626
|
return files;
|
|
33573
33627
|
}
|
|
33574
33628
|
var secretscan = tool({
|
|
33575
|
-
description: "Scan directory for potential secrets (API keys, tokens, passwords) using regex patterns and entropy heuristics. Returns metadata-only findings with redacted previews - NEVER returns raw secrets. Excludes common directories (node_modules, .git, dist, etc.) by default.",
|
|
33629
|
+
description: "Scan directory for potential secrets (API keys, tokens, passwords) using regex patterns and entropy heuristics. Returns metadata-only findings with redacted previews - NEVER returns raw secrets. Excludes common directories (node_modules, .git, dist, etc.) by default. Supports glob patterns (e.g. **/.svelte-kit/**, **/*.test.ts) and reads .secretscanignore at the scan root.",
|
|
33576
33630
|
args: {
|
|
33577
33631
|
directory: tool.schema.string().describe('Directory to scan for secrets (e.g., "." or "./src")'),
|
|
33578
|
-
exclude: tool.schema.array(tool.schema.string()).optional().describe("
|
|
33632
|
+
exclude: tool.schema.array(tool.schema.string()).optional().describe("Patterns to exclude: plain directory names (e.g. node_modules), relative paths, or globs (e.g. **/.svelte-kit/**, **/*.test.ts). Added to default exclusions.")
|
|
33579
33633
|
},
|
|
33580
33634
|
async execute(args, _context) {
|
|
33581
33635
|
let directory;
|
|
@@ -33611,20 +33665,10 @@ var secretscan = tool({
|
|
|
33611
33665
|
}
|
|
33612
33666
|
if (exclude) {
|
|
33613
33667
|
for (const exc of exclude) {
|
|
33614
|
-
|
|
33615
|
-
|
|
33616
|
-
error: `invalid exclude path: exceeds maximum length of ${MAX_FILE_PATH_LENGTH}`,
|
|
33617
|
-
scan_dir: directory,
|
|
33618
|
-
findings: [],
|
|
33619
|
-
count: 0,
|
|
33620
|
-
files_scanned: 0,
|
|
33621
|
-
skipped_files: 0
|
|
33622
|
-
};
|
|
33623
|
-
return JSON.stringify(errorResult, null, 2);
|
|
33624
|
-
}
|
|
33625
|
-
if (containsPathTraversal(exc) || containsControlChars(exc)) {
|
|
33668
|
+
const err = validateExcludePattern(exc);
|
|
33669
|
+
if (err) {
|
|
33626
33670
|
const errorResult = {
|
|
33627
|
-
error:
|
|
33671
|
+
error: err,
|
|
33628
33672
|
scan_dir: directory,
|
|
33629
33673
|
findings: [],
|
|
33630
33674
|
count: 0,
|
|
@@ -33660,10 +33704,20 @@ var secretscan = tool({
|
|
|
33660
33704
|
};
|
|
33661
33705
|
return JSON.stringify(errorResult, null, 2);
|
|
33662
33706
|
}
|
|
33663
|
-
const
|
|
33664
|
-
|
|
33665
|
-
|
|
33666
|
-
|
|
33707
|
+
const excludeExact = new Set(DEFAULT_EXCLUDE_DIRS);
|
|
33708
|
+
const excludeGlobs = [];
|
|
33709
|
+
const ignoreFilePatterns = loadSecretScanIgnore(scanDir);
|
|
33710
|
+
const allUserPatterns = [
|
|
33711
|
+
...exclude ?? [],
|
|
33712
|
+
...ignoreFilePatterns
|
|
33713
|
+
];
|
|
33714
|
+
for (const exc of allUserPatterns) {
|
|
33715
|
+
if (exc.length === 0)
|
|
33716
|
+
continue;
|
|
33717
|
+
if (isGlobOrPathPattern(exc)) {
|
|
33718
|
+
excludeGlobs.push(exc);
|
|
33719
|
+
} else {
|
|
33720
|
+
excludeExact.add(exc);
|
|
33667
33721
|
}
|
|
33668
33722
|
}
|
|
33669
33723
|
const stats = {
|
|
@@ -33673,7 +33727,7 @@ var secretscan = tool({
|
|
|
33673
33727
|
symlinkSkipped: 0
|
|
33674
33728
|
};
|
|
33675
33729
|
const visited = new Set;
|
|
33676
|
-
const files = findScannableFiles(scanDir,
|
|
33730
|
+
const files = findScannableFiles(scanDir, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
33677
33731
|
files.sort((a, b) => {
|
|
33678
33732
|
const aLower = a.toLowerCase();
|
|
33679
33733
|
const bLower = b.toLowerCase();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -15792,6 +15792,18 @@ async function updateTaskStatus(directory, taskId, status) {
|
|
|
15792
15792
|
throw new Error(`Plan not found in directory: ${directory}`);
|
|
15793
15793
|
}
|
|
15794
15794
|
let taskFound = false;
|
|
15795
|
+
const derivePhaseStatusFromTasks = (tasks) => {
|
|
15796
|
+
if (tasks.length > 0 && tasks.every((task) => task.status === "completed")) {
|
|
15797
|
+
return "complete";
|
|
15798
|
+
}
|
|
15799
|
+
if (tasks.some((task) => task.status === "in_progress")) {
|
|
15800
|
+
return "in_progress";
|
|
15801
|
+
}
|
|
15802
|
+
if (tasks.some((task) => task.status === "blocked")) {
|
|
15803
|
+
return "blocked";
|
|
15804
|
+
}
|
|
15805
|
+
return "pending";
|
|
15806
|
+
};
|
|
15795
15807
|
const updatedPhases = plan.phases.map((phase) => {
|
|
15796
15808
|
const updatedTasks = phase.tasks.map((task) => {
|
|
15797
15809
|
if (task.id === taskId) {
|
|
@@ -15800,7 +15812,11 @@ async function updateTaskStatus(directory, taskId, status) {
|
|
|
15800
15812
|
}
|
|
15801
15813
|
return task;
|
|
15802
15814
|
});
|
|
15803
|
-
return {
|
|
15815
|
+
return {
|
|
15816
|
+
...phase,
|
|
15817
|
+
status: derivePhaseStatusFromTasks(updatedTasks),
|
|
15818
|
+
tasks: updatedTasks
|
|
15819
|
+
};
|
|
15804
15820
|
});
|
|
15805
15821
|
if (!taskFound) {
|
|
15806
15822
|
throw new Error(`Task not found: ${taskId}`);
|
|
@@ -33558,8 +33574,9 @@ function isHighEntropyString(str) {
|
|
|
33558
33574
|
function containsPathTraversal(str) {
|
|
33559
33575
|
if (/\.\.[/\\]/.test(str))
|
|
33560
33576
|
return true;
|
|
33561
|
-
|
|
33562
|
-
|
|
33577
|
+
if (/[/\\]\.\.$/.test(str) || str === "..")
|
|
33578
|
+
return true;
|
|
33579
|
+
if (/\.\.[/\\]/.test(path21.normalize(str.replace(/\*/g, "x"))))
|
|
33563
33580
|
return true;
|
|
33564
33581
|
if (str.includes("%2e%2e") || str.includes("%2E%2E"))
|
|
33565
33582
|
return true;
|
|
@@ -33567,6 +33584,58 @@ function containsPathTraversal(str) {
|
|
|
33567
33584
|
return true;
|
|
33568
33585
|
return false;
|
|
33569
33586
|
}
|
|
33587
|
+
function validateExcludePattern(exc) {
|
|
33588
|
+
if (exc.length === 0)
|
|
33589
|
+
return null;
|
|
33590
|
+
if (exc.length > MAX_FILE_PATH_LENGTH) {
|
|
33591
|
+
return `invalid exclude path: exceeds maximum length of ${MAX_FILE_PATH_LENGTH}`;
|
|
33592
|
+
}
|
|
33593
|
+
if (containsControlChars(exc)) {
|
|
33594
|
+
return "invalid exclude path: contains path traversal or control characters";
|
|
33595
|
+
}
|
|
33596
|
+
if (containsPathTraversal(exc)) {
|
|
33597
|
+
return "invalid exclude path: contains path traversal or control characters";
|
|
33598
|
+
}
|
|
33599
|
+
if (exc.startsWith("!")) {
|
|
33600
|
+
return "invalid exclude path: negation patterns are not supported";
|
|
33601
|
+
}
|
|
33602
|
+
if (exc.startsWith("/") || exc.startsWith("\\")) {
|
|
33603
|
+
return "invalid exclude path: absolute paths are not supported";
|
|
33604
|
+
}
|
|
33605
|
+
return null;
|
|
33606
|
+
}
|
|
33607
|
+
function isGlobOrPathPattern(pattern) {
|
|
33608
|
+
return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
|
|
33609
|
+
}
|
|
33610
|
+
function loadSecretScanIgnore(scanDir) {
|
|
33611
|
+
const ignorePath = path21.join(scanDir, ".secretscanignore");
|
|
33612
|
+
try {
|
|
33613
|
+
if (!fs9.existsSync(ignorePath))
|
|
33614
|
+
return [];
|
|
33615
|
+
const content = fs9.readFileSync(ignorePath, "utf8");
|
|
33616
|
+
const patterns = [];
|
|
33617
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
33618
|
+
const line = rawLine.trim();
|
|
33619
|
+
if (!line || line.startsWith("#"))
|
|
33620
|
+
continue;
|
|
33621
|
+
if (validateExcludePattern(line) === null) {
|
|
33622
|
+
patterns.push(line);
|
|
33623
|
+
}
|
|
33624
|
+
}
|
|
33625
|
+
return patterns;
|
|
33626
|
+
} catch {
|
|
33627
|
+
return [];
|
|
33628
|
+
}
|
|
33629
|
+
}
|
|
33630
|
+
function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
33631
|
+
if (exactNames.has(entry))
|
|
33632
|
+
return true;
|
|
33633
|
+
for (const pattern of globPatterns) {
|
|
33634
|
+
if (path21.matchesGlob(relPath, pattern))
|
|
33635
|
+
return true;
|
|
33636
|
+
}
|
|
33637
|
+
return false;
|
|
33638
|
+
}
|
|
33570
33639
|
function containsControlChars(str) {
|
|
33571
33640
|
return /[\0\r]/.test(str);
|
|
33572
33641
|
}
|
|
@@ -33726,7 +33795,7 @@ function isPathWithinScope(realPath, scanDir) {
|
|
|
33726
33795
|
const resolvedRealPath = path21.resolve(realPath);
|
|
33727
33796
|
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path21.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
33728
33797
|
}
|
|
33729
|
-
function findScannableFiles(dir,
|
|
33798
|
+
function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
|
|
33730
33799
|
skippedDirs: 0,
|
|
33731
33800
|
skippedFiles: 0,
|
|
33732
33801
|
fileErrors: 0,
|
|
@@ -33750,11 +33819,12 @@ function findScannableFiles(dir, excludeDirs, scanDir, visited, stats = {
|
|
|
33750
33819
|
return a.localeCompare(b);
|
|
33751
33820
|
});
|
|
33752
33821
|
for (const entry of entries) {
|
|
33753
|
-
|
|
33822
|
+
const fullPath = path21.join(dir, entry);
|
|
33823
|
+
const relPath = path21.relative(scanDir, fullPath).replace(/\\/g, "/");
|
|
33824
|
+
if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
|
|
33754
33825
|
stats.skippedDirs++;
|
|
33755
33826
|
continue;
|
|
33756
33827
|
}
|
|
33757
|
-
const fullPath = path21.join(dir, entry);
|
|
33758
33828
|
let lstat;
|
|
33759
33829
|
try {
|
|
33760
33830
|
lstat = fs9.lstatSync(fullPath);
|
|
@@ -33782,7 +33852,7 @@ function findScannableFiles(dir, excludeDirs, scanDir, visited, stats = {
|
|
|
33782
33852
|
stats.symlinkSkipped++;
|
|
33783
33853
|
continue;
|
|
33784
33854
|
}
|
|
33785
|
-
const subFiles = findScannableFiles(fullPath,
|
|
33855
|
+
const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
33786
33856
|
files.push(...subFiles);
|
|
33787
33857
|
} else if (lstat.isFile()) {
|
|
33788
33858
|
const ext = path21.extname(fullPath).toLowerCase();
|
|
@@ -33987,10 +34057,10 @@ var init_secretscan = __esm(() => {
|
|
|
33987
34057
|
];
|
|
33988
34058
|
O_NOFOLLOW = process.platform !== "win32" ? fs9.constants.O_NOFOLLOW : undefined;
|
|
33989
34059
|
secretscan = tool({
|
|
33990
|
-
description: "Scan directory for potential secrets (API keys, tokens, passwords) using regex patterns and entropy heuristics. Returns metadata-only findings with redacted previews - NEVER returns raw secrets. Excludes common directories (node_modules, .git, dist, etc.) by default.",
|
|
34060
|
+
description: "Scan directory for potential secrets (API keys, tokens, passwords) using regex patterns and entropy heuristics. Returns metadata-only findings with redacted previews - NEVER returns raw secrets. Excludes common directories (node_modules, .git, dist, etc.) by default. Supports glob patterns (e.g. **/.svelte-kit/**, **/*.test.ts) and reads .secretscanignore at the scan root.",
|
|
33991
34061
|
args: {
|
|
33992
34062
|
directory: tool.schema.string().describe('Directory to scan for secrets (e.g., "." or "./src")'),
|
|
33993
|
-
exclude: tool.schema.array(tool.schema.string()).optional().describe("
|
|
34063
|
+
exclude: tool.schema.array(tool.schema.string()).optional().describe("Patterns to exclude: plain directory names (e.g. node_modules), relative paths, or globs (e.g. **/.svelte-kit/**, **/*.test.ts). Added to default exclusions.")
|
|
33994
34064
|
},
|
|
33995
34065
|
async execute(args2, _context) {
|
|
33996
34066
|
let directory;
|
|
@@ -34026,20 +34096,10 @@ var init_secretscan = __esm(() => {
|
|
|
34026
34096
|
}
|
|
34027
34097
|
if (exclude) {
|
|
34028
34098
|
for (const exc of exclude) {
|
|
34029
|
-
|
|
34030
|
-
|
|
34031
|
-
error: `invalid exclude path: exceeds maximum length of ${MAX_FILE_PATH_LENGTH}`,
|
|
34032
|
-
scan_dir: directory,
|
|
34033
|
-
findings: [],
|
|
34034
|
-
count: 0,
|
|
34035
|
-
files_scanned: 0,
|
|
34036
|
-
skipped_files: 0
|
|
34037
|
-
};
|
|
34038
|
-
return JSON.stringify(errorResult, null, 2);
|
|
34039
|
-
}
|
|
34040
|
-
if (containsPathTraversal(exc) || containsControlChars(exc)) {
|
|
34099
|
+
const err2 = validateExcludePattern(exc);
|
|
34100
|
+
if (err2) {
|
|
34041
34101
|
const errorResult = {
|
|
34042
|
-
error:
|
|
34102
|
+
error: err2,
|
|
34043
34103
|
scan_dir: directory,
|
|
34044
34104
|
findings: [],
|
|
34045
34105
|
count: 0,
|
|
@@ -34075,10 +34135,20 @@ var init_secretscan = __esm(() => {
|
|
|
34075
34135
|
};
|
|
34076
34136
|
return JSON.stringify(errorResult, null, 2);
|
|
34077
34137
|
}
|
|
34078
|
-
const
|
|
34079
|
-
|
|
34080
|
-
|
|
34081
|
-
|
|
34138
|
+
const excludeExact = new Set(DEFAULT_EXCLUDE_DIRS);
|
|
34139
|
+
const excludeGlobs = [];
|
|
34140
|
+
const ignoreFilePatterns = loadSecretScanIgnore(scanDir);
|
|
34141
|
+
const allUserPatterns = [
|
|
34142
|
+
...exclude ?? [],
|
|
34143
|
+
...ignoreFilePatterns
|
|
34144
|
+
];
|
|
34145
|
+
for (const exc of allUserPatterns) {
|
|
34146
|
+
if (exc.length === 0)
|
|
34147
|
+
continue;
|
|
34148
|
+
if (isGlobOrPathPattern(exc)) {
|
|
34149
|
+
excludeGlobs.push(exc);
|
|
34150
|
+
} else {
|
|
34151
|
+
excludeExact.add(exc);
|
|
34082
34152
|
}
|
|
34083
34153
|
}
|
|
34084
34154
|
const stats = {
|
|
@@ -34088,7 +34158,7 @@ var init_secretscan = __esm(() => {
|
|
|
34088
34158
|
symlinkSkipped: 0
|
|
34089
34159
|
};
|
|
34090
34160
|
const visited = new Set;
|
|
34091
|
-
const files = findScannableFiles(scanDir,
|
|
34161
|
+
const files = findScannableFiles(scanDir, excludeExact, excludeGlobs, scanDir, visited, stats);
|
|
34092
34162
|
files.sort((a, b) => {
|
|
34093
34163
|
const aLower = a.toLowerCase();
|
|
34094
34164
|
const bLower = b.toLowerCase();
|
|
@@ -47932,8 +48002,8 @@ function isOutsideSwarmDir(filePath, directory) {
|
|
|
47932
48002
|
return false;
|
|
47933
48003
|
const swarmDir = path26.resolve(directory, ".swarm");
|
|
47934
48004
|
const resolved = path26.resolve(directory, filePath);
|
|
47935
|
-
const
|
|
47936
|
-
return
|
|
48005
|
+
const relative4 = path26.relative(swarmDir, resolved);
|
|
48006
|
+
return relative4.startsWith("..") || path26.isAbsolute(relative4);
|
|
47937
48007
|
}
|
|
47938
48008
|
function isSourceCodePath(filePath) {
|
|
47939
48009
|
if (!filePath)
|
|
@@ -48612,6 +48682,9 @@ function extractPlanTaskId(text) {
|
|
|
48612
48682
|
}
|
|
48613
48683
|
return null;
|
|
48614
48684
|
}
|
|
48685
|
+
function getSeedTaskId(session) {
|
|
48686
|
+
return session.currentTaskId ?? session.lastCoderDelegationTaskId;
|
|
48687
|
+
}
|
|
48615
48688
|
function createDelegationGateHook(config3) {
|
|
48616
48689
|
const enabled = config3.hooks?.delegation_gate !== false;
|
|
48617
48690
|
const delegationMaxChars = config3.hooks?.delegation_max_chars ?? 4000;
|
|
@@ -48669,6 +48742,10 @@ function createDelegationGateHook(config3) {
|
|
|
48669
48742
|
if (!otherSession.taskWorkflowStates)
|
|
48670
48743
|
continue;
|
|
48671
48744
|
if (targetAgent === "reviewer") {
|
|
48745
|
+
const seedTaskId = getSeedTaskId(session);
|
|
48746
|
+
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
48747
|
+
otherSession.taskWorkflowStates.set(seedTaskId, "coder_delegated");
|
|
48748
|
+
}
|
|
48672
48749
|
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
48673
48750
|
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
48674
48751
|
try {
|
|
@@ -48680,6 +48757,10 @@ function createDelegationGateHook(config3) {
|
|
|
48680
48757
|
}
|
|
48681
48758
|
}
|
|
48682
48759
|
if (targetAgent === "test_engineer") {
|
|
48760
|
+
const seedTaskId = getSeedTaskId(session);
|
|
48761
|
+
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
48762
|
+
otherSession.taskWorkflowStates.set(seedTaskId, "reviewer_run");
|
|
48763
|
+
}
|
|
48683
48764
|
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
48684
48765
|
if (state === "reviewer_run") {
|
|
48685
48766
|
try {
|
|
@@ -48750,6 +48831,10 @@ function createDelegationGateHook(config3) {
|
|
|
48750
48831
|
continue;
|
|
48751
48832
|
if (!otherSession.taskWorkflowStates)
|
|
48752
48833
|
continue;
|
|
48834
|
+
const seedTaskId = getSeedTaskId(session);
|
|
48835
|
+
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
48836
|
+
otherSession.taskWorkflowStates.set(seedTaskId, "coder_delegated");
|
|
48837
|
+
}
|
|
48753
48838
|
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
48754
48839
|
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
48755
48840
|
try {
|
|
@@ -48767,6 +48852,10 @@ function createDelegationGateHook(config3) {
|
|
|
48767
48852
|
continue;
|
|
48768
48853
|
if (!otherSession.taskWorkflowStates)
|
|
48769
48854
|
continue;
|
|
48855
|
+
const seedTaskId = getSeedTaskId(session);
|
|
48856
|
+
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
48857
|
+
otherSession.taskWorkflowStates.set(seedTaskId, "reviewer_run");
|
|
48858
|
+
}
|
|
48770
48859
|
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
48771
48860
|
if (state === "reviewer_run") {
|
|
48772
48861
|
try {
|
|
@@ -58050,13 +58139,13 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
58050
58139
|
resolved = path41.resolve(baseDir, inputPath);
|
|
58051
58140
|
}
|
|
58052
58141
|
const workspaceResolved = path41.resolve(workspaceDir);
|
|
58053
|
-
let
|
|
58142
|
+
let relative5;
|
|
58054
58143
|
if (isWinAbs) {
|
|
58055
|
-
|
|
58144
|
+
relative5 = path41.win32.relative(workspaceResolved, resolved);
|
|
58056
58145
|
} else {
|
|
58057
|
-
|
|
58146
|
+
relative5 = path41.relative(workspaceResolved, resolved);
|
|
58058
58147
|
}
|
|
58059
|
-
if (
|
|
58148
|
+
if (relative5.startsWith("..")) {
|
|
58060
58149
|
return "path traversal detected";
|
|
58061
58150
|
}
|
|
58062
58151
|
return null;
|
|
@@ -60915,7 +61004,9 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
60915
61004
|
stateEntries.push(`${sessionId}: ${state}`);
|
|
60916
61005
|
}
|
|
60917
61006
|
const allIdle = stateEntries.length > 0 && stateEntries.every((e) => e.endsWith(": idle"));
|
|
60918
|
-
if (allIdle) {
|
|
61007
|
+
if (allIdle) {
|
|
61008
|
+
return { blocked: false, reason: "" };
|
|
61009
|
+
}
|
|
60919
61010
|
try {
|
|
60920
61011
|
const resolvedDir = workingDirectory ?? process.cwd();
|
|
60921
61012
|
const planPath = path47.join(resolvedDir, ".swarm", "plan.json");
|
|
@@ -60932,7 +61023,7 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
60932
61023
|
const currentStateStr = stateEntries.length > 0 ? stateEntries.join(", ") : "no active sessions";
|
|
60933
61024
|
return {
|
|
60934
61025
|
blocked: true,
|
|
60935
|
-
reason: `Task ${taskId} has not passed QA gates. Current state: [${currentStateStr}].
|
|
61026
|
+
reason: `Task ${taskId} has not passed QA gates. Current state by session: [${currentStateStr}]. Missing required state: tests_run or complete in at least one valid session. Do not write directly to plan files \u2014 use update_task_status after running the reviewer and test_engineer agents.`
|
|
60936
61027
|
};
|
|
60937
61028
|
} catch {
|
|
60938
61029
|
return { blocked: false, reason: "" };
|
|
@@ -61032,6 +61123,19 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
61032
61123
|
}
|
|
61033
61124
|
try {
|
|
61034
61125
|
const updatedPlan = await updateTaskStatus(directory, args2.task_id, args2.status);
|
|
61126
|
+
if (args2.status === "completed") {
|
|
61127
|
+
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
61128
|
+
if (!(session.taskWorkflowStates instanceof Map)) {
|
|
61129
|
+
continue;
|
|
61130
|
+
}
|
|
61131
|
+
const currentState = getTaskState(session, args2.task_id);
|
|
61132
|
+
if (currentState === "tests_run") {
|
|
61133
|
+
try {
|
|
61134
|
+
advanceTaskState(session, args2.task_id, "complete");
|
|
61135
|
+
} catch {}
|
|
61136
|
+
}
|
|
61137
|
+
}
|
|
61138
|
+
}
|
|
61035
61139
|
return {
|
|
61036
61140
|
success: true,
|
|
61037
61141
|
message: "Task status updated successfully",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.23.0",
|
|
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",
|