opencode-swarm 6.11.0 → 6.13.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/README.md +427 -520
- package/dist/background/trigger.d.ts +4 -0
- package/dist/config/constants.d.ts +2 -0
- package/dist/config/evidence-schema.d.ts +4 -4
- package/dist/config/plan-schema.d.ts +28 -1
- package/dist/config/schema.d.ts +53 -22
- package/dist/hooks/extractors.d.ts +14 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/messages-transform.d.ts +13 -0
- package/dist/hooks/pipeline-tracker.d.ts +5 -0
- package/dist/hooks/system-enhancer.d.ts +11 -0
- package/dist/index.js +1414 -417
- package/dist/sast/rules/index.d.ts +2 -2
- package/dist/state.d.ts +20 -0
- package/dist/tools/test-runner.d.ts +2 -2
- package/dist/tools/tool-names.d.ts +10 -0
- package/dist/utils/tool-output.d.ts +10 -0
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -14150,6 +14150,7 @@ var init_plan_schema = __esm(() => {
|
|
|
14150
14150
|
"pending",
|
|
14151
14151
|
"in_progress",
|
|
14152
14152
|
"complete",
|
|
14153
|
+
"completed",
|
|
14153
14154
|
"blocked"
|
|
14154
14155
|
]);
|
|
14155
14156
|
MigrationStatusSchema = exports_external.enum([
|
|
@@ -14179,7 +14180,7 @@ var init_plan_schema = __esm(() => {
|
|
|
14179
14180
|
schema_version: exports_external.literal("1.0.0"),
|
|
14180
14181
|
title: exports_external.string().min(1),
|
|
14181
14182
|
swarm: exports_external.string().min(1),
|
|
14182
|
-
current_phase: exports_external.number().int().min(1),
|
|
14183
|
+
current_phase: exports_external.number().int().min(1).optional(),
|
|
14183
14184
|
phases: exports_external.array(PhaseSchema).min(1),
|
|
14184
14185
|
migration_status: MigrationStatusSchema.optional()
|
|
14185
14186
|
});
|
|
@@ -14576,6 +14577,10 @@ import * as path4 from "path";
|
|
|
14576
14577
|
async function loadPlanJsonOnly(directory) {
|
|
14577
14578
|
const planJsonContent = await readSwarmFileAsync(directory, "plan.json");
|
|
14578
14579
|
if (planJsonContent !== null) {
|
|
14580
|
+
if (planJsonContent.includes("\x00") || planJsonContent.includes("\uFFFD")) {
|
|
14581
|
+
warn("Plan rejected: .swarm/plan.json contains null bytes or invalid encoding");
|
|
14582
|
+
return null;
|
|
14583
|
+
}
|
|
14579
14584
|
try {
|
|
14580
14585
|
const parsed = JSON.parse(planJsonContent);
|
|
14581
14586
|
const validated = PlanSchema.parse(parsed);
|
|
@@ -14660,25 +14665,29 @@ ${markdown}`;
|
|
|
14660
14665
|
async function loadPlan(directory) {
|
|
14661
14666
|
const planJsonContent = await readSwarmFileAsync(directory, "plan.json");
|
|
14662
14667
|
if (planJsonContent !== null) {
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
|
|
14668
|
-
|
|
14669
|
-
|
|
14670
|
-
|
|
14671
|
-
|
|
14668
|
+
if (planJsonContent.includes("\x00") || planJsonContent.includes("\uFFFD")) {
|
|
14669
|
+
warn("Plan rejected: .swarm/plan.json contains null bytes or invalid encoding");
|
|
14670
|
+
} else {
|
|
14671
|
+
try {
|
|
14672
|
+
const parsed = JSON.parse(planJsonContent);
|
|
14673
|
+
const validated = PlanSchema.parse(parsed);
|
|
14674
|
+
const inSync = await isPlanMdInSync(directory, validated);
|
|
14675
|
+
if (!inSync) {
|
|
14676
|
+
try {
|
|
14677
|
+
await regeneratePlanMarkdown(directory, validated);
|
|
14678
|
+
} catch (regenError) {
|
|
14679
|
+
warn(`Failed to regenerate plan.md: ${regenError instanceof Error ? regenError.message : String(regenError)}. Proceeding with plan.json only.`);
|
|
14680
|
+
}
|
|
14681
|
+
}
|
|
14682
|
+
return validated;
|
|
14683
|
+
} catch (error49) {
|
|
14684
|
+
warn(`Plan validation failed for .swarm/plan.json: ${error49 instanceof Error ? error49.message : String(error49)}`);
|
|
14685
|
+
const planMdContent2 = await readSwarmFileAsync(directory, "plan.md");
|
|
14686
|
+
if (planMdContent2 !== null) {
|
|
14687
|
+
const migrated = migrateLegacyPlan(planMdContent2);
|
|
14688
|
+
await savePlan(directory, migrated);
|
|
14689
|
+
return migrated;
|
|
14672
14690
|
}
|
|
14673
|
-
}
|
|
14674
|
-
return validated;
|
|
14675
|
-
} catch (error49) {
|
|
14676
|
-
warn(`Plan validation failed for .swarm/plan.json: ${error49 instanceof Error ? error49.message : String(error49)}`);
|
|
14677
|
-
const planMdContent2 = await readSwarmFileAsync(directory, "plan.md");
|
|
14678
|
-
if (planMdContent2 !== null) {
|
|
14679
|
-
const migrated = migrateLegacyPlan(planMdContent2);
|
|
14680
|
-
await savePlan(directory, migrated);
|
|
14681
|
-
return migrated;
|
|
14682
14691
|
}
|
|
14683
14692
|
}
|
|
14684
14693
|
}
|
|
@@ -14712,10 +14721,11 @@ function derivePlanMarkdown(plan) {
|
|
|
14712
14721
|
blocked: "BLOCKED"
|
|
14713
14722
|
};
|
|
14714
14723
|
const now = new Date().toISOString();
|
|
14715
|
-
const
|
|
14724
|
+
const currentPhase = plan.current_phase ?? 1;
|
|
14725
|
+
const phaseStatus = statusMap[plan.phases[currentPhase - 1]?.status] || "PENDING";
|
|
14716
14726
|
let markdown = `# ${plan.title}
|
|
14717
14727
|
Swarm: ${plan.swarm}
|
|
14718
|
-
Phase: ${
|
|
14728
|
+
Phase: ${currentPhase} [${phaseStatus}] | Updated: ${now}
|
|
14719
14729
|
`;
|
|
14720
14730
|
const sortedPhases = [...plan.phases].sort((a, b) => a.id - b.id);
|
|
14721
14731
|
for (const phase of sortedPhases) {
|
|
@@ -15014,7 +15024,7 @@ async function buildPhaseSummary(phase) {
|
|
|
15014
15024
|
const taskIds = await listEvidenceTaskIds(".");
|
|
15015
15025
|
const phaseTaskIds = new Set(phase.tasks.map((t) => t.id));
|
|
15016
15026
|
const taskSummaries = [];
|
|
15017
|
-
const
|
|
15027
|
+
const _taskMap = new Map(phase.tasks.map((t) => [t.id, t]));
|
|
15018
15028
|
for (const task of phase.tasks) {
|
|
15019
15029
|
const summary = await buildTaskSummary(task, task.id);
|
|
15020
15030
|
taskSummaries.push(summary);
|
|
@@ -15138,7 +15148,7 @@ async function buildEvidenceSummary(directory, currentPhase) {
|
|
|
15138
15148
|
schema_version: EVIDENCE_SUMMARY_VERSION,
|
|
15139
15149
|
generated_at: new Date().toISOString(),
|
|
15140
15150
|
planTitle: plan.title,
|
|
15141
|
-
currentPhase: currentPhase ?? plan.current_phase,
|
|
15151
|
+
currentPhase: currentPhase ?? plan.current_phase ?? 1,
|
|
15142
15152
|
phaseSummaries,
|
|
15143
15153
|
overallCompletionRatio,
|
|
15144
15154
|
overallBlockers,
|
|
@@ -15670,6 +15680,9 @@ class PhaseBoundaryTrigger {
|
|
|
15670
15680
|
getCurrentPhase() {
|
|
15671
15681
|
return this.lastKnownPhase;
|
|
15672
15682
|
}
|
|
15683
|
+
get lastTriggeredPhaseValue() {
|
|
15684
|
+
return this.lastTriggeredPhase;
|
|
15685
|
+
}
|
|
15673
15686
|
detectBoundary(newPhase, completedTasks, totalTasks) {
|
|
15674
15687
|
if (newPhase === this.lastKnownPhase) {
|
|
15675
15688
|
return {
|
|
@@ -15847,7 +15860,7 @@ class PreflightTriggerManager {
|
|
|
15847
15860
|
enabled: this.isEnabled(),
|
|
15848
15861
|
mode,
|
|
15849
15862
|
currentPhase: this.trigger.getCurrentPhase(),
|
|
15850
|
-
lastTriggeredPhase: this.trigger
|
|
15863
|
+
lastTriggeredPhase: this.trigger.lastTriggeredPhaseValue,
|
|
15851
15864
|
pendingRequests: this.getQueueSize()
|
|
15852
15865
|
};
|
|
15853
15866
|
}
|
|
@@ -16053,7 +16066,7 @@ function readConfigFromFile(directory) {
|
|
|
16053
16066
|
return null;
|
|
16054
16067
|
}
|
|
16055
16068
|
}
|
|
16056
|
-
function validateConfigKey(path10, value,
|
|
16069
|
+
function validateConfigKey(path10, value, _config) {
|
|
16057
16070
|
const findings = [];
|
|
16058
16071
|
switch (path10) {
|
|
16059
16072
|
case "agents": {
|
|
@@ -29285,7 +29298,7 @@ async function runLint(linter, mode) {
|
|
|
29285
29298
|
` : "") + stderr;
|
|
29286
29299
|
}
|
|
29287
29300
|
if (output.length > MAX_OUTPUT_BYTES) {
|
|
29288
|
-
output = output.slice(0, MAX_OUTPUT_BYTES)
|
|
29301
|
+
output = `${output.slice(0, MAX_OUTPUT_BYTES)}
|
|
29289
29302
|
... (output truncated)`;
|
|
29290
29303
|
}
|
|
29291
29304
|
const result = {
|
|
@@ -29425,15 +29438,14 @@ function isBinaryFile(filePath, buffer) {
|
|
|
29425
29438
|
}
|
|
29426
29439
|
return nullCount > checkLen * BINARY_NULL_THRESHOLD;
|
|
29427
29440
|
}
|
|
29428
|
-
function scanLineForSecrets(line,
|
|
29441
|
+
function scanLineForSecrets(line, _lineNum) {
|
|
29429
29442
|
const results = [];
|
|
29430
29443
|
if (line.length > MAX_LINE_LENGTH) {
|
|
29431
29444
|
return results;
|
|
29432
29445
|
}
|
|
29433
29446
|
for (const pattern of SECRET_PATTERNS) {
|
|
29434
29447
|
pattern.regex.lastIndex = 0;
|
|
29435
|
-
let match;
|
|
29436
|
-
while ((match = pattern.regex.exec(line)) !== null) {
|
|
29448
|
+
for (let match = pattern.regex.exec(line);match !== null; match = pattern.regex.exec(line)) {
|
|
29437
29449
|
const fullMatch = match[0];
|
|
29438
29450
|
const redacted = pattern.redactTemplate(fullMatch);
|
|
29439
29451
|
results.push({
|
|
@@ -29544,7 +29556,7 @@ function isSymlinkLoop(realPath, visited) {
|
|
|
29544
29556
|
function isPathWithinScope(realPath, scanDir) {
|
|
29545
29557
|
const resolvedScanDir = path11.resolve(scanDir);
|
|
29546
29558
|
const resolvedRealPath = path11.resolve(realPath);
|
|
29547
|
-
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path11.sep) || resolvedRealPath.startsWith(resolvedScanDir
|
|
29559
|
+
return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path11.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
|
|
29548
29560
|
}
|
|
29549
29561
|
function findScannableFiles(dir, excludeDirs, scanDir, visited, stats = {
|
|
29550
29562
|
skippedDirs: 0,
|
|
@@ -29971,7 +29983,7 @@ var init_secretscan = __esm(() => {
|
|
|
29971
29983
|
parts2.push(`${skippedFiles + stats.fileErrors + stats.symlinkSkipped} files skipped (binary/oversized/symlinks/errors)`);
|
|
29972
29984
|
}
|
|
29973
29985
|
if (parts2.length > 0) {
|
|
29974
|
-
result.message = parts2.join("; ")
|
|
29986
|
+
result.message = `${parts2.join("; ")}.`;
|
|
29975
29987
|
}
|
|
29976
29988
|
let jsonOutput = JSON.stringify(result, null, 2);
|
|
29977
29989
|
if (jsonOutput.length > MAX_OUTPUT_BYTES2) {
|
|
@@ -30092,13 +30104,14 @@ function hasDevDependency(devDeps, ...patterns) {
|
|
|
30092
30104
|
return false;
|
|
30093
30105
|
return hasPackageJsonDependency(devDeps, ...patterns);
|
|
30094
30106
|
}
|
|
30095
|
-
async function detectTestFramework() {
|
|
30107
|
+
async function detectTestFramework(cwd) {
|
|
30108
|
+
const baseDir = cwd || process.cwd();
|
|
30096
30109
|
try {
|
|
30097
|
-
const packageJsonPath = path12.join(
|
|
30110
|
+
const packageJsonPath = path12.join(baseDir, "package.json");
|
|
30098
30111
|
if (fs7.existsSync(packageJsonPath)) {
|
|
30099
30112
|
const content = fs7.readFileSync(packageJsonPath, "utf-8");
|
|
30100
30113
|
const pkg = JSON.parse(content);
|
|
30101
|
-
const
|
|
30114
|
+
const _deps = pkg.dependencies || {};
|
|
30102
30115
|
const devDeps = pkg.devDependencies || {};
|
|
30103
30116
|
const scripts = pkg.scripts || {};
|
|
30104
30117
|
if (scripts.test?.includes("vitest"))
|
|
@@ -30115,16 +30128,16 @@ async function detectTestFramework() {
|
|
|
30115
30128
|
return "jest";
|
|
30116
30129
|
if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
|
|
30117
30130
|
return "mocha";
|
|
30118
|
-
if (fs7.existsSync(path12.join(
|
|
30131
|
+
if (fs7.existsSync(path12.join(baseDir, "bun.lockb")) || fs7.existsSync(path12.join(baseDir, "bun.lock"))) {
|
|
30119
30132
|
if (scripts.test?.includes("bun"))
|
|
30120
30133
|
return "bun";
|
|
30121
30134
|
}
|
|
30122
30135
|
}
|
|
30123
30136
|
} catch {}
|
|
30124
30137
|
try {
|
|
30125
|
-
const pyprojectTomlPath = path12.join(
|
|
30126
|
-
const setupCfgPath = path12.join(
|
|
30127
|
-
const requirementsTxtPath = path12.join(
|
|
30138
|
+
const pyprojectTomlPath = path12.join(baseDir, "pyproject.toml");
|
|
30139
|
+
const setupCfgPath = path12.join(baseDir, "setup.cfg");
|
|
30140
|
+
const requirementsTxtPath = path12.join(baseDir, "requirements.txt");
|
|
30128
30141
|
if (fs7.existsSync(pyprojectTomlPath)) {
|
|
30129
30142
|
const content = fs7.readFileSync(pyprojectTomlPath, "utf-8");
|
|
30130
30143
|
if (content.includes("[tool.pytest"))
|
|
@@ -30144,7 +30157,7 @@ async function detectTestFramework() {
|
|
|
30144
30157
|
}
|
|
30145
30158
|
} catch {}
|
|
30146
30159
|
try {
|
|
30147
|
-
const cargoTomlPath = path12.join(
|
|
30160
|
+
const cargoTomlPath = path12.join(baseDir, "Cargo.toml");
|
|
30148
30161
|
if (fs7.existsSync(cargoTomlPath)) {
|
|
30149
30162
|
const content = fs7.readFileSync(cargoTomlPath, "utf-8");
|
|
30150
30163
|
if (content.includes("[dev-dependencies]")) {
|
|
@@ -30155,9 +30168,9 @@ async function detectTestFramework() {
|
|
|
30155
30168
|
}
|
|
30156
30169
|
} catch {}
|
|
30157
30170
|
try {
|
|
30158
|
-
const pesterConfigPath = path12.join(
|
|
30159
|
-
const pesterConfigJsonPath = path12.join(
|
|
30160
|
-
const pesterPs1Path = path12.join(
|
|
30171
|
+
const pesterConfigPath = path12.join(baseDir, "pester.config.ps1");
|
|
30172
|
+
const pesterConfigJsonPath = path12.join(baseDir, "pester.config.ps1.json");
|
|
30173
|
+
const pesterPs1Path = path12.join(baseDir, "tests.ps1");
|
|
30161
30174
|
if (fs7.existsSync(pesterConfigPath) || fs7.existsSync(pesterConfigJsonPath) || fs7.existsSync(pesterPs1Path)) {
|
|
30162
30175
|
return "pester";
|
|
30163
30176
|
}
|
|
@@ -30179,7 +30192,7 @@ function getTestFilesFromConvention(sourceFiles) {
|
|
|
30179
30192
|
}
|
|
30180
30193
|
continue;
|
|
30181
30194
|
}
|
|
30182
|
-
for (const
|
|
30195
|
+
for (const _pattern of TEST_PATTERNS) {
|
|
30183
30196
|
const nameWithoutExt = basename2.replace(/\.[^.]+$/, "");
|
|
30184
30197
|
const ext = path12.extname(basename2);
|
|
30185
30198
|
const possibleTestFiles = [
|
|
@@ -30210,7 +30223,8 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
30210
30223
|
const testDir = path12.dirname(testFile);
|
|
30211
30224
|
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
30212
30225
|
let match;
|
|
30213
|
-
|
|
30226
|
+
match = importRegex.exec(content);
|
|
30227
|
+
while (match !== null) {
|
|
30214
30228
|
const importPath = match[1];
|
|
30215
30229
|
let resolvedImport;
|
|
30216
30230
|
if (importPath.startsWith(".")) {
|
|
@@ -30248,9 +30262,11 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
30248
30262
|
break;
|
|
30249
30263
|
}
|
|
30250
30264
|
}
|
|
30265
|
+
match = importRegex.exec(content);
|
|
30251
30266
|
}
|
|
30252
30267
|
const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
30253
|
-
|
|
30268
|
+
match = requireRegex.exec(content);
|
|
30269
|
+
while (match !== null) {
|
|
30254
30270
|
const importPath = match[1];
|
|
30255
30271
|
if (importPath.startsWith(".")) {
|
|
30256
30272
|
let resolvedImport = path12.resolve(testDir, importPath);
|
|
@@ -30285,6 +30301,7 @@ async function getTestFilesFromGraph(sourceFiles) {
|
|
|
30285
30301
|
}
|
|
30286
30302
|
}
|
|
30287
30303
|
}
|
|
30304
|
+
match = requireRegex.exec(content);
|
|
30288
30305
|
}
|
|
30289
30306
|
} catch {}
|
|
30290
30307
|
}
|
|
@@ -30463,7 +30480,7 @@ function parseTestOutput(framework, output) {
|
|
|
30463
30480
|
}
|
|
30464
30481
|
return { totals, coveragePercent };
|
|
30465
30482
|
}
|
|
30466
|
-
async function runTests(framework, scope, files, coverage, timeout_ms) {
|
|
30483
|
+
async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
30467
30484
|
const command = buildTestCommand(framework, scope, files, coverage);
|
|
30468
30485
|
if (!command) {
|
|
30469
30486
|
return {
|
|
@@ -30488,7 +30505,8 @@ async function runTests(framework, scope, files, coverage, timeout_ms) {
|
|
|
30488
30505
|
try {
|
|
30489
30506
|
const proc = Bun.spawn(command, {
|
|
30490
30507
|
stdout: "pipe",
|
|
30491
|
-
stderr: "pipe"
|
|
30508
|
+
stderr: "pipe",
|
|
30509
|
+
cwd: cwd || process.cwd()
|
|
30492
30510
|
});
|
|
30493
30511
|
const exitPromise = proc.exited;
|
|
30494
30512
|
const timeoutPromise = new Promise((resolve7) => setTimeout(() => {
|
|
@@ -30516,7 +30534,7 @@ async function runTests(framework, scope, files, coverage, timeout_ms) {
|
|
|
30516
30534
|
}
|
|
30517
30535
|
truncIndex--;
|
|
30518
30536
|
}
|
|
30519
|
-
output = output.slice(0, truncIndex)
|
|
30537
|
+
output = `${output.slice(0, truncIndex)}
|
|
30520
30538
|
... (output truncated)`;
|
|
30521
30539
|
}
|
|
30522
30540
|
const { totals, coveragePercent } = parseTestOutput(framework, output);
|
|
@@ -30660,7 +30678,46 @@ var init_test_runner = __esm(() => {
|
|
|
30660
30678
|
coverage: tool.schema.boolean().optional().describe("Enable coverage reporting if supported"),
|
|
30661
30679
|
timeout_ms: tool.schema.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)")
|
|
30662
30680
|
},
|
|
30663
|
-
async execute(args2,
|
|
30681
|
+
async execute(args2, context) {
|
|
30682
|
+
const ctx = context;
|
|
30683
|
+
const rawDir = ctx?.directory || ctx?.worktree || process.cwd();
|
|
30684
|
+
const workingDir = rawDir.trim() || process.cwd();
|
|
30685
|
+
if (workingDir.length > 4096) {
|
|
30686
|
+
const errorResult = {
|
|
30687
|
+
success: false,
|
|
30688
|
+
framework: "none",
|
|
30689
|
+
scope: "all",
|
|
30690
|
+
error: "Invalid working directory"
|
|
30691
|
+
};
|
|
30692
|
+
return JSON.stringify(errorResult, null, 2);
|
|
30693
|
+
}
|
|
30694
|
+
if (/^[/\\]{2}/.test(workingDir)) {
|
|
30695
|
+
const errorResult = {
|
|
30696
|
+
success: false,
|
|
30697
|
+
framework: "none",
|
|
30698
|
+
scope: "all",
|
|
30699
|
+
error: "Invalid working directory"
|
|
30700
|
+
};
|
|
30701
|
+
return JSON.stringify(errorResult, null, 2);
|
|
30702
|
+
}
|
|
30703
|
+
if (containsControlChars2(workingDir)) {
|
|
30704
|
+
const errorResult = {
|
|
30705
|
+
success: false,
|
|
30706
|
+
framework: "none",
|
|
30707
|
+
scope: "all",
|
|
30708
|
+
error: "Invalid working directory"
|
|
30709
|
+
};
|
|
30710
|
+
return JSON.stringify(errorResult, null, 2);
|
|
30711
|
+
}
|
|
30712
|
+
if (containsPathTraversal2(workingDir)) {
|
|
30713
|
+
const errorResult = {
|
|
30714
|
+
success: false,
|
|
30715
|
+
framework: "none",
|
|
30716
|
+
scope: "all",
|
|
30717
|
+
error: "Invalid working directory"
|
|
30718
|
+
};
|
|
30719
|
+
return JSON.stringify(errorResult, null, 2);
|
|
30720
|
+
}
|
|
30664
30721
|
if (!validateArgs2(args2)) {
|
|
30665
30722
|
const errorResult = {
|
|
30666
30723
|
success: false,
|
|
@@ -30672,10 +30729,10 @@ var init_test_runner = __esm(() => {
|
|
|
30672
30729
|
return JSON.stringify(errorResult, null, 2);
|
|
30673
30730
|
}
|
|
30674
30731
|
const scope = args2.scope || "all";
|
|
30675
|
-
const
|
|
30732
|
+
const _files = args2.files || [];
|
|
30676
30733
|
const coverage = args2.coverage || false;
|
|
30677
30734
|
const timeout_ms = Math.min(args2.timeout_ms || DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
|
|
30678
|
-
const framework = await detectTestFramework();
|
|
30735
|
+
const framework = await detectTestFramework(workingDir);
|
|
30679
30736
|
if (framework === "none") {
|
|
30680
30737
|
const result2 = {
|
|
30681
30738
|
success: false,
|
|
@@ -30701,13 +30758,13 @@ var init_test_runner = __esm(() => {
|
|
|
30701
30758
|
const sourceFiles = args2.files && args2.files.length > 0 ? args2.files.filter((f) => {
|
|
30702
30759
|
const ext = path12.extname(f).toLowerCase();
|
|
30703
30760
|
return SOURCE_EXTENSIONS.has(ext);
|
|
30704
|
-
}) : findSourceFiles(
|
|
30761
|
+
}) : findSourceFiles(workingDir);
|
|
30705
30762
|
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
30706
30763
|
} else if (scope === "graph") {
|
|
30707
30764
|
const sourceFiles = args2.files && args2.files.length > 0 ? args2.files.filter((f) => {
|
|
30708
30765
|
const ext = path12.extname(f).toLowerCase();
|
|
30709
30766
|
return SOURCE_EXTENSIONS.has(ext);
|
|
30710
|
-
}) : findSourceFiles(
|
|
30767
|
+
}) : findSourceFiles(workingDir);
|
|
30711
30768
|
const graphTestFiles = await getTestFilesFromGraph(sourceFiles);
|
|
30712
30769
|
if (graphTestFiles.length > 0) {
|
|
30713
30770
|
testFiles = graphTestFiles;
|
|
@@ -30717,7 +30774,7 @@ var init_test_runner = __esm(() => {
|
|
|
30717
30774
|
testFiles = getTestFilesFromConvention(sourceFiles);
|
|
30718
30775
|
}
|
|
30719
30776
|
}
|
|
30720
|
-
const result = await runTests(framework, effectiveScope, testFiles, coverage, timeout_ms);
|
|
30777
|
+
const result = await runTests(framework, effectiveScope, testFiles, coverage, timeout_ms, workingDir);
|
|
30721
30778
|
if (graphFallbackReason && result.message) {
|
|
30722
30779
|
result.message = `${result.message} (${graphFallbackReason})`;
|
|
30723
30780
|
}
|
|
@@ -30798,7 +30855,7 @@ function getVersionFileVersion(dir) {
|
|
|
30798
30855
|
}
|
|
30799
30856
|
return null;
|
|
30800
30857
|
}
|
|
30801
|
-
async function runVersionCheck(dir,
|
|
30858
|
+
async function runVersionCheck(dir, _timeoutMs) {
|
|
30802
30859
|
const startTime = Date.now();
|
|
30803
30860
|
try {
|
|
30804
30861
|
const packageVersion = getPackageVersion(dir);
|
|
@@ -30854,7 +30911,7 @@ async function runVersionCheck(dir, timeoutMs) {
|
|
|
30854
30911
|
};
|
|
30855
30912
|
}
|
|
30856
30913
|
}
|
|
30857
|
-
async function runLintCheck(
|
|
30914
|
+
async function runLintCheck(_dir, linter, timeoutMs) {
|
|
30858
30915
|
const startTime = Date.now();
|
|
30859
30916
|
try {
|
|
30860
30917
|
const lintPromise = runLint(linter, "check");
|
|
@@ -30921,7 +30978,7 @@ async function runLintCheck(dir, linter, timeoutMs) {
|
|
|
30921
30978
|
};
|
|
30922
30979
|
}
|
|
30923
30980
|
}
|
|
30924
|
-
async function runTestsCheck(
|
|
30981
|
+
async function runTestsCheck(_dir, scope, timeoutMs) {
|
|
30925
30982
|
const startTime = Date.now();
|
|
30926
30983
|
try {
|
|
30927
30984
|
const result = await runTests("none", scope, [], false, timeoutMs);
|
|
@@ -31366,7 +31423,35 @@ var init_preflight_integration = __esm(() => {
|
|
|
31366
31423
|
});
|
|
31367
31424
|
|
|
31368
31425
|
// src/index.ts
|
|
31369
|
-
import * as
|
|
31426
|
+
import * as path32 from "path";
|
|
31427
|
+
|
|
31428
|
+
// src/tools/tool-names.ts
|
|
31429
|
+
var TOOL_NAMES = [
|
|
31430
|
+
"diff",
|
|
31431
|
+
"syntax_check",
|
|
31432
|
+
"placeholder_scan",
|
|
31433
|
+
"imports",
|
|
31434
|
+
"lint",
|
|
31435
|
+
"secretscan",
|
|
31436
|
+
"sast_scan",
|
|
31437
|
+
"build_check",
|
|
31438
|
+
"pre_check_batch",
|
|
31439
|
+
"quality_budget",
|
|
31440
|
+
"symbols",
|
|
31441
|
+
"complexity_hotspots",
|
|
31442
|
+
"schema_drift",
|
|
31443
|
+
"todo_extract",
|
|
31444
|
+
"evidence_check",
|
|
31445
|
+
"sbom_generate",
|
|
31446
|
+
"checkpoint",
|
|
31447
|
+
"pkg_audit",
|
|
31448
|
+
"test_runner",
|
|
31449
|
+
"detect_domains",
|
|
31450
|
+
"gitingest",
|
|
31451
|
+
"retrieve_summary",
|
|
31452
|
+
"extract_code_blocks"
|
|
31453
|
+
];
|
|
31454
|
+
var TOOL_NAME_SET = new Set(TOOL_NAMES);
|
|
31370
31455
|
|
|
31371
31456
|
// src/config/constants.ts
|
|
31372
31457
|
var QA_AGENTS = ["reviewer", "critic"];
|
|
@@ -31383,6 +31468,102 @@ var ALL_AGENT_NAMES = [
|
|
|
31383
31468
|
ORCHESTRATOR_NAME,
|
|
31384
31469
|
...ALL_SUBAGENT_NAMES
|
|
31385
31470
|
];
|
|
31471
|
+
var AGENT_TOOL_MAP = {
|
|
31472
|
+
architect: [
|
|
31473
|
+
"checkpoint",
|
|
31474
|
+
"complexity_hotspots",
|
|
31475
|
+
"detect_domains",
|
|
31476
|
+
"evidence_check",
|
|
31477
|
+
"extract_code_blocks",
|
|
31478
|
+
"gitingest",
|
|
31479
|
+
"imports",
|
|
31480
|
+
"lint",
|
|
31481
|
+
"diff",
|
|
31482
|
+
"pkg_audit",
|
|
31483
|
+
"pre_check_batch",
|
|
31484
|
+
"retrieve_summary",
|
|
31485
|
+
"schema_drift",
|
|
31486
|
+
"secretscan",
|
|
31487
|
+
"symbols",
|
|
31488
|
+
"test_runner",
|
|
31489
|
+
"todo_extract"
|
|
31490
|
+
],
|
|
31491
|
+
explorer: [
|
|
31492
|
+
"complexity_hotspots",
|
|
31493
|
+
"detect_domains",
|
|
31494
|
+
"extract_code_blocks",
|
|
31495
|
+
"gitingest",
|
|
31496
|
+
"imports",
|
|
31497
|
+
"retrieve_summary",
|
|
31498
|
+
"schema_drift",
|
|
31499
|
+
"symbols",
|
|
31500
|
+
"todo_extract"
|
|
31501
|
+
],
|
|
31502
|
+
coder: [
|
|
31503
|
+
"diff",
|
|
31504
|
+
"imports",
|
|
31505
|
+
"lint",
|
|
31506
|
+
"symbols",
|
|
31507
|
+
"extract_code_blocks",
|
|
31508
|
+
"retrieve_summary"
|
|
31509
|
+
],
|
|
31510
|
+
test_engineer: [
|
|
31511
|
+
"test_runner",
|
|
31512
|
+
"diff",
|
|
31513
|
+
"symbols",
|
|
31514
|
+
"extract_code_blocks",
|
|
31515
|
+
"retrieve_summary",
|
|
31516
|
+
"imports",
|
|
31517
|
+
"complexity_hotspots",
|
|
31518
|
+
"pkg_audit"
|
|
31519
|
+
],
|
|
31520
|
+
sme: [
|
|
31521
|
+
"complexity_hotspots",
|
|
31522
|
+
"detect_domains",
|
|
31523
|
+
"extract_code_blocks",
|
|
31524
|
+
"imports",
|
|
31525
|
+
"retrieve_summary",
|
|
31526
|
+
"schema_drift",
|
|
31527
|
+
"symbols"
|
|
31528
|
+
],
|
|
31529
|
+
reviewer: [
|
|
31530
|
+
"diff",
|
|
31531
|
+
"imports",
|
|
31532
|
+
"lint",
|
|
31533
|
+
"pkg_audit",
|
|
31534
|
+
"pre_check_batch",
|
|
31535
|
+
"secretscan",
|
|
31536
|
+
"symbols",
|
|
31537
|
+
"complexity_hotspots",
|
|
31538
|
+
"retrieve_summary",
|
|
31539
|
+
"extract_code_blocks",
|
|
31540
|
+
"test_runner"
|
|
31541
|
+
],
|
|
31542
|
+
critic: [
|
|
31543
|
+
"complexity_hotspots",
|
|
31544
|
+
"detect_domains",
|
|
31545
|
+
"imports",
|
|
31546
|
+
"retrieve_summary",
|
|
31547
|
+
"symbols"
|
|
31548
|
+
],
|
|
31549
|
+
docs: [
|
|
31550
|
+
"detect_domains",
|
|
31551
|
+
"extract_code_blocks",
|
|
31552
|
+
"gitingest",
|
|
31553
|
+
"imports",
|
|
31554
|
+
"retrieve_summary",
|
|
31555
|
+
"schema_drift",
|
|
31556
|
+
"symbols",
|
|
31557
|
+
"todo_extract"
|
|
31558
|
+
],
|
|
31559
|
+
designer: ["extract_code_blocks", "retrieve_summary", "symbols"]
|
|
31560
|
+
};
|
|
31561
|
+
for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
|
|
31562
|
+
const invalidTools = tools.filter((tool) => !TOOL_NAME_SET.has(tool));
|
|
31563
|
+
if (invalidTools.length > 0) {
|
|
31564
|
+
throw new Error(`Agent '${agentName}' has invalid tool names: [${invalidTools.join(", ")}]. ` + `All tools must be registered in TOOL_NAME_SET.`);
|
|
31565
|
+
}
|
|
31566
|
+
}
|
|
31386
31567
|
var DEFAULT_MODELS = {
|
|
31387
31568
|
architect: "anthropic/claude-sonnet-4-5",
|
|
31388
31569
|
explorer: "google/gemini-2.0-flash",
|
|
@@ -31430,6 +31611,55 @@ import * as path from "path";
|
|
|
31430
31611
|
|
|
31431
31612
|
// src/config/schema.ts
|
|
31432
31613
|
init_zod();
|
|
31614
|
+
var KNOWN_SWARM_PREFIXES = [
|
|
31615
|
+
"paid",
|
|
31616
|
+
"local",
|
|
31617
|
+
"cloud",
|
|
31618
|
+
"enterprise",
|
|
31619
|
+
"mega",
|
|
31620
|
+
"default",
|
|
31621
|
+
"custom",
|
|
31622
|
+
"team",
|
|
31623
|
+
"project",
|
|
31624
|
+
"swarm"
|
|
31625
|
+
];
|
|
31626
|
+
var SEPARATORS = ["_", "-", " "];
|
|
31627
|
+
function stripKnownSwarmPrefix(agentName) {
|
|
31628
|
+
if (!agentName)
|
|
31629
|
+
return agentName;
|
|
31630
|
+
const normalized = agentName.toLowerCase();
|
|
31631
|
+
let stripped = normalized;
|
|
31632
|
+
let previous = "";
|
|
31633
|
+
while (stripped !== previous) {
|
|
31634
|
+
previous = stripped;
|
|
31635
|
+
for (const prefix of KNOWN_SWARM_PREFIXES) {
|
|
31636
|
+
for (const sep of SEPARATORS) {
|
|
31637
|
+
const prefixWithSep = prefix + sep;
|
|
31638
|
+
if (stripped.startsWith(prefixWithSep)) {
|
|
31639
|
+
stripped = stripped.slice(prefixWithSep.length);
|
|
31640
|
+
break;
|
|
31641
|
+
}
|
|
31642
|
+
}
|
|
31643
|
+
if (stripped !== previous)
|
|
31644
|
+
break;
|
|
31645
|
+
}
|
|
31646
|
+
}
|
|
31647
|
+
if (ALL_AGENT_NAMES.includes(stripped)) {
|
|
31648
|
+
return stripped;
|
|
31649
|
+
}
|
|
31650
|
+
for (const agent of ALL_AGENT_NAMES) {
|
|
31651
|
+
for (const sep of SEPARATORS) {
|
|
31652
|
+
const suffix = sep + agent;
|
|
31653
|
+
if (normalized.endsWith(suffix)) {
|
|
31654
|
+
return agent;
|
|
31655
|
+
}
|
|
31656
|
+
}
|
|
31657
|
+
if (normalized === agent) {
|
|
31658
|
+
return agent;
|
|
31659
|
+
}
|
|
31660
|
+
}
|
|
31661
|
+
return agentName;
|
|
31662
|
+
}
|
|
31433
31663
|
var AgentOverrideConfigSchema = exports_external.object({
|
|
31434
31664
|
model: exports_external.string().optional(),
|
|
31435
31665
|
temperature: exports_external.number().min(0).max(2).optional(),
|
|
@@ -31745,38 +31975,36 @@ var GuardrailsConfigSchema = exports_external.object({
|
|
|
31745
31975
|
idle_timeout_minutes: exports_external.number().min(5).max(240).default(60),
|
|
31746
31976
|
profiles: exports_external.record(exports_external.string(), GuardrailsProfileSchema).optional()
|
|
31747
31977
|
});
|
|
31748
|
-
function
|
|
31749
|
-
return name2.toLowerCase().replace(/[-\s]+/g, "_");
|
|
31750
|
-
}
|
|
31751
|
-
function stripKnownSwarmPrefix(name2) {
|
|
31752
|
-
if (!name2)
|
|
31753
|
-
return name2;
|
|
31754
|
-
if (ALL_AGENT_NAMES.includes(name2))
|
|
31755
|
-
return name2;
|
|
31756
|
-
const normalized = normalizeAgentName(name2);
|
|
31757
|
-
for (const agentName of ALL_AGENT_NAMES) {
|
|
31758
|
-
const normalizedAgent = normalizeAgentName(agentName);
|
|
31759
|
-
if (normalized === normalizedAgent)
|
|
31760
|
-
return agentName;
|
|
31761
|
-
if (normalized.endsWith(`_${normalizedAgent}`)) {
|
|
31762
|
-
return agentName;
|
|
31763
|
-
}
|
|
31764
|
-
}
|
|
31765
|
-
return name2;
|
|
31766
|
-
}
|
|
31767
|
-
function resolveGuardrailsConfig(base, agentName) {
|
|
31978
|
+
function resolveGuardrailsConfig(config2, agentName) {
|
|
31768
31979
|
if (!agentName) {
|
|
31769
|
-
return
|
|
31980
|
+
return config2;
|
|
31770
31981
|
}
|
|
31771
|
-
const
|
|
31772
|
-
const
|
|
31773
|
-
const
|
|
31774
|
-
|
|
31775
|
-
|
|
31776
|
-
|
|
31982
|
+
const canonicalName = stripKnownSwarmPrefix(agentName);
|
|
31983
|
+
const hasBuiltInProfile = canonicalName in DEFAULT_AGENT_PROFILES;
|
|
31984
|
+
const userProfile = config2.profiles?.[agentName] ?? config2.profiles?.[canonicalName];
|
|
31985
|
+
if (!hasBuiltInProfile) {
|
|
31986
|
+
if (userProfile) {
|
|
31987
|
+
return { ...config2, ...userProfile };
|
|
31988
|
+
}
|
|
31989
|
+
return config2;
|
|
31777
31990
|
}
|
|
31778
|
-
|
|
31991
|
+
const builtInProfile = DEFAULT_AGENT_PROFILES[canonicalName];
|
|
31992
|
+
const resolved = {
|
|
31993
|
+
...config2,
|
|
31994
|
+
...builtInProfile,
|
|
31995
|
+
...userProfile || {}
|
|
31996
|
+
};
|
|
31997
|
+
return resolved;
|
|
31779
31998
|
}
|
|
31999
|
+
var ToolFilterConfigSchema = exports_external.object({
|
|
32000
|
+
enabled: exports_external.boolean().default(true),
|
|
32001
|
+
overrides: exports_external.record(exports_external.string(), exports_external.array(exports_external.string())).default({})
|
|
32002
|
+
});
|
|
32003
|
+
var PlanCursorConfigSchema = exports_external.object({
|
|
32004
|
+
enabled: exports_external.boolean().default(true),
|
|
32005
|
+
max_tokens: exports_external.number().min(500).max(4000).default(1500),
|
|
32006
|
+
lookahead_tasks: exports_external.number().min(0).max(5).default(2)
|
|
32007
|
+
});
|
|
31780
32008
|
var CheckpointConfigSchema = exports_external.object({
|
|
31781
32009
|
enabled: exports_external.boolean().default(true),
|
|
31782
32010
|
auto_checkpoint_threshold: exports_external.number().min(1).max(20).default(3)
|
|
@@ -31813,6 +32041,8 @@ var PluginConfigSchema = exports_external.object({
|
|
|
31813
32041
|
gates: GateConfigSchema.optional(),
|
|
31814
32042
|
context_budget: ContextBudgetConfigSchema.optional(),
|
|
31815
32043
|
guardrails: GuardrailsConfigSchema.optional(),
|
|
32044
|
+
tool_filter: ToolFilterConfigSchema.optional(),
|
|
32045
|
+
plan_cursor: PlanCursorConfigSchema.optional(),
|
|
31816
32046
|
evidence: EvidenceConfigSchema.optional(),
|
|
31817
32047
|
summaries: SummaryConfigSchema.optional(),
|
|
31818
32048
|
review_passes: ReviewPassesConfigSchema.optional(),
|
|
@@ -31823,7 +32053,12 @@ var PluginConfigSchema = exports_external.object({
|
|
|
31823
32053
|
lint: LintConfigSchema.optional(),
|
|
31824
32054
|
secretscan: SecretscanConfigSchema.optional(),
|
|
31825
32055
|
checkpoint: CheckpointConfigSchema.optional(),
|
|
31826
|
-
automation: AutomationConfigSchema.optional()
|
|
32056
|
+
automation: AutomationConfigSchema.optional(),
|
|
32057
|
+
tool_output: exports_external.object({
|
|
32058
|
+
truncation_enabled: exports_external.boolean().default(true),
|
|
32059
|
+
max_lines: exports_external.number().min(10).max(500).default(150),
|
|
32060
|
+
per_tool: exports_external.record(exports_external.string(), exports_external.number()).optional()
|
|
32061
|
+
}).optional()
|
|
31827
32062
|
});
|
|
31828
32063
|
|
|
31829
32064
|
// src/config/loader.ts
|
|
@@ -31847,7 +32082,11 @@ function loadRawConfigFromPath(configPath) {
|
|
|
31847
32082
|
console.warn("[opencode-swarm] \u26A0\uFE0F SECURITY: Config file exceeds size limit. Falling back to safe defaults with guardrails ENABLED.");
|
|
31848
32083
|
return { config: null, fileExisted: true, hadError: true };
|
|
31849
32084
|
}
|
|
31850
|
-
|
|
32085
|
+
let sanitizedContent = content;
|
|
32086
|
+
if (content.charCodeAt(0) === 65279) {
|
|
32087
|
+
sanitizedContent = content.slice(1);
|
|
32088
|
+
}
|
|
32089
|
+
const rawConfig = JSON.parse(sanitizedContent);
|
|
31851
32090
|
if (typeof rawConfig !== "object" || rawConfig === null || Array.isArray(rawConfig)) {
|
|
31852
32091
|
console.warn(`[opencode-swarm] Invalid config at ${configPath}: expected an object`);
|
|
31853
32092
|
console.warn("[opencode-swarm] \u26A0\uFE0F SECURITY: Config format invalid. Falling back to safe defaults with guardrails ENABLED.");
|
|
@@ -31966,14 +32205,41 @@ Do not re-trigger DISCOVER or CONSULT because you noticed a project phase bounda
|
|
|
31966
32205
|
Output to .swarm/plan.md MUST use "## Phase N" headers. Do not write MODE labels into plan.md.
|
|
31967
32206
|
|
|
31968
32207
|
1. DELEGATE all coding to {{AGENT_PREFIX}}coder. You do NOT write code.
|
|
32208
|
+
YOUR TOOLS: Task (delegation), diff, syntax_check, placeholder_scan, imports, lint, secretscan, sast_scan, build_check, pre_check_batch, quality_budget, symbols, complexity_hotspots, schema_drift, todo_extract, evidence_check, sbom_generate, checkpoint, pkg_audit, test_runner.
|
|
32209
|
+
CODER'S TOOLS: write, edit, patch, apply_patch, create_file, insert, replace \u2014 any tool that modifies file contents.
|
|
32210
|
+
If a tool modifies a file, it is a CODER tool. Delegate.
|
|
31969
32211
|
2. ONE agent per message. Send, STOP, wait for response.
|
|
31970
32212
|
3. ONE task per {{AGENT_PREFIX}}coder call. Never batch.
|
|
31971
|
-
|
|
31972
|
-
|
|
31973
|
-
|
|
31974
|
-
|
|
31975
|
-
|
|
31976
|
-
|
|
32213
|
+
BATCHING DETECTION \u2014 you are batching if your coder delegation contains ANY of:
|
|
32214
|
+
- The word "and" connecting two actions ("update X AND add Y")
|
|
32215
|
+
- Multiple FILE paths ("FILE: src/a.ts, src/b.ts, src/c.ts")
|
|
32216
|
+
- Multiple TASK objectives ("TASK: Refactor the processor and update the config")
|
|
32217
|
+
- Phrases like "also", "while you're at it", "additionally", "as well"
|
|
32218
|
+
|
|
32219
|
+
WHY: Each coder task goes through the FULL QA gate (Stage A + Stage B).
|
|
32220
|
+
If you batch 3 tasks into 1 coder call, the QA gate runs once on the combined diff.
|
|
32221
|
+
The reviewer cannot distinguish which changes belong to which requirement.
|
|
32222
|
+
The test_engineer cannot write targeted tests for each behavior.
|
|
32223
|
+
A failure in one part blocks the entire batch, wasting all the work.
|
|
32224
|
+
|
|
32225
|
+
SPLIT RULE: If your delegation draft has "and" in the TASK line, split it.
|
|
32226
|
+
Two small delegations with two QA gates > one large delegation with one QA gate.
|
|
32227
|
+
4. ARCHITECT CODING BOUNDARIES \u2014 Only code yourself after {{QA_RETRY_LIMIT}} {{AGENT_PREFIX}}coder failures on same task.
|
|
32228
|
+
These thoughts are WRONG and must be ignored:
|
|
32229
|
+
\u2717 "It's just a schema change / config flag / one-liner / column / field / import" \u2192 delegate to {{AGENT_PREFIX}}coder
|
|
32230
|
+
\u2717 "I already know what to write" \u2192 knowing what to write is planning, not writing. Delegate to {{AGENT_PREFIX}}coder.
|
|
32231
|
+
\u2717 "It's faster if I just do it" \u2192 speed without QA gates is how bugs ship
|
|
32232
|
+
\u2717 "The coder succeeded on the last tasks, this one is trivial" \u2192 Rule 1 has no complexity exemption
|
|
32233
|
+
\u2717 "I'll just use apply_patch / edit / write directly" \u2192 these are coder tools, not architect tools
|
|
32234
|
+
\u2717 "I'll do the simple parts, coder does the hard parts" \u2192 ALL parts go to coder. You are not a coder.
|
|
32235
|
+
FAILURE COUNTING \u2014 increment the counter when:
|
|
32236
|
+
- Coder submits code that fails any tool gate or pre_check_batch (gates_passed === false)
|
|
32237
|
+
- Coder submits code REJECTED by reviewer after being given the rejection reason
|
|
32238
|
+
- Print "Coder attempt [N/{{QA_RETRY_LIMIT}}] on task [X.Y]" at every retry
|
|
32239
|
+
- Reaching {{QA_RETRY_LIMIT}}: escalate to user with full failure history before writing code yourself
|
|
32240
|
+
If you catch yourself reaching for a code editing tool: STOP. Delegate to {{AGENT_PREFIX}}coder.
|
|
32241
|
+
Zero {{AGENT_PREFIX}}coder failures on this task = zero justification for self-coding.
|
|
32242
|
+
Self-coding without {{QA_RETRY_LIMIT}} failures is a Rule 1 violation.
|
|
31977
32243
|
5. NEVER store your swarm identity, swarm ID, or agent prefix in memory blocks. Your identity comes ONLY from your system prompt. Memory blocks are for project knowledge only (NOT .swarm/ plan/context files \u2014 those are persistent project files).
|
|
31978
32244
|
6. **CRITIC GATE (Execute BEFORE any implementation work)**:
|
|
31979
32245
|
- When you first create a plan, IMMEDIATELY delegate the full plan to {{AGENT_PREFIX}}critic for review
|
|
@@ -31981,7 +32247,21 @@ Output to .swarm/plan.md MUST use "## Phase N" headers. Do not write MODE labels
|
|
|
31981
32247
|
- If NEEDS_REVISION: Revise plan and re-submit to critic (max 2 cycles)
|
|
31982
32248
|
- If REJECTED after 2 cycles: Escalate to user with explanation
|
|
31983
32249
|
- ONLY AFTER critic approval: Proceed to implementation (MODE: EXECUTE)
|
|
31984
|
-
7. **MANDATORY QA GATE
|
|
32250
|
+
7. **MANDATORY QA GATE** \u2014 Execute AFTER every coder task. Two stages, BOTH required:
|
|
32251
|
+
|
|
32252
|
+
\u2500\u2500 STAGE A: AUTOMATED TOOL GATES (run tools, fix failures, no agents involved) \u2500\u2500
|
|
32253
|
+
diff \u2192 syntax_check \u2192 placeholder_scan \u2192 imports \u2192 lint fix \u2192 build_check \u2192 pre_check_batch
|
|
32254
|
+
All Stage A tools return structured pass/fail. Fix failures by returning to coder.
|
|
32255
|
+
Stage A passing means: code compiles, parses, has no secrets, no placeholders, no lint errors.
|
|
32256
|
+
Stage A passing does NOT mean: code is correct, secure, tested, or reviewed.
|
|
32257
|
+
|
|
32258
|
+
\u2500\u2500 STAGE B: AGENT REVIEW GATES (delegate to agents, wait for verdicts) \u2500\u2500
|
|
32259
|
+
{{AGENT_PREFIX}}reviewer \u2192 security reviewer (conditional) \u2192 {{AGENT_PREFIX}}test_engineer verification \u2192 {{AGENT_PREFIX}}test_engineer adversarial \u2192 coverage check
|
|
32260
|
+
Stage B CANNOT be skipped. Stage A passing does not satisfy Stage B.
|
|
32261
|
+
Stage B is where logic errors, security flaws, edge cases, and behavioral bugs are caught.
|
|
32262
|
+
You MUST delegate to each Stage B agent and wait for their response.
|
|
32263
|
+
|
|
32264
|
+
A task is complete ONLY when BOTH stages pass.
|
|
31985
32265
|
ANTI-EXEMPTION RULES \u2014 these thoughts are WRONG and must be ignored:
|
|
31986
32266
|
\u2717 "It's a simple change" \u2192 gates are mandatory for ALL changes regardless of perceived complexity
|
|
31987
32267
|
\u2717 "It's just a rename / refactor / config tweak" \u2192 same
|
|
@@ -31994,20 +32274,25 @@ ANTI-EXEMPTION RULES \u2014 these thoughts are WRONG and must be ignored:
|
|
|
31994
32274
|
|
|
31995
32275
|
There are NO simple changes. There are NO exceptions to the QA gate sequence.
|
|
31996
32276
|
The gates exist because the author cannot objectively evaluate their own work.
|
|
32277
|
+
|
|
32278
|
+
PARTIAL GATE RATIONALIZATIONS \u2014 automated gates \u2260 agent review. Running SOME gates is NOT compliance:
|
|
32279
|
+
\u2717 "I ran pre_check_batch so the code is verified" \u2192 pre_check_batch does NOT replace {{AGENT_PREFIX}}reviewer or {{AGENT_PREFIX}}test_engineer
|
|
32280
|
+
\u2717 "syntax_check passed, good enough" \u2192 syntax_check catches syntax. Reviewer catches logic. Test_engineer catches behavior. All three are required.
|
|
32281
|
+
\u2717 "The mechanical gates passed, skip the agent gates" \u2192 automated tools miss logic errors, security flaws, and edge cases that agent review catches
|
|
32282
|
+
\u2717 "It's Phase 6+, the codebase is stable now" \u2192 complacency after successful phases is the #1 predictor of shipped bugs. Phase 6 needs MORE review, not less.
|
|
32283
|
+
\u2717 "I'll just run the fast gates" \u2192 speed of a gate does not determine whether it is required
|
|
32284
|
+
\u2717 "5 phases passed clean, this one will be fine" \u2192 past success does not predict future correctness
|
|
32285
|
+
|
|
32286
|
+
Running syntax_check + pre_check_batch without reviewer + test_engineer is a PARTIAL GATE VIOLATION.
|
|
32287
|
+
It is the same severity as skipping all gates. The QA gate is ALL steps or NONE.
|
|
32288
|
+
|
|
31997
32289
|
- After coder completes: run \`diff\` tool. If \`hasContractChanges\` is true \u2192 delegate {{AGENT_PREFIX}}explorer for integration impact analysis. BREAKING \u2192 return to coder. COMPATIBLE \u2192 proceed.
|
|
31998
32290
|
- Run \`syntax_check\` tool. SYNTACTIC ERRORS \u2192 return to coder. NO ERRORS \u2192 proceed to placeholder_scan.
|
|
31999
32291
|
- Run \`placeholder_scan\` tool. PLACEHOLDER FINDINGS \u2192 return to coder. NO FINDINGS \u2192 proceed to imports check.
|
|
32000
32292
|
- Run \`imports\` tool. Record results for dependency audit. Proceed to lint fix.
|
|
32001
32293
|
- Run \`lint\` tool (mode: fix) \u2192 allow auto-corrections. LINT FIX FAILS \u2192 return to coder. SUCCESS \u2192 proceed to build_check.
|
|
32002
32294
|
- Run \`build_check\` tool. BUILD FAILS \u2192 return to coder. SUCCESS \u2192 proceed to pre_check_batch.
|
|
32003
|
-
- Run \`pre_check_batch\` tool
|
|
32004
|
-
- lint:check (code quality verification)
|
|
32005
|
-
- secretscan (secret detection)
|
|
32006
|
-
- sast_scan (static security analysis)
|
|
32007
|
-
- quality_budget (maintainability metrics)
|
|
32008
|
-
\u2192 Returns { gates_passed, lint, secretscan, sast_scan, quality_budget, total_duration_ms }
|
|
32009
|
-
\u2192 If gates_passed === false: read individual tool results, identify which tool(s) failed, return structured rejection to @coder with specific tool failures. Do NOT call @reviewer.
|
|
32010
|
-
\u2192 If gates_passed === true: proceed to @reviewer.
|
|
32295
|
+
- Run \`pre_check_batch\` tool. If gates_passed === false: return to coder. If gates_passed === true: proceed to @reviewer.
|
|
32011
32296
|
- Delegate {{AGENT_PREFIX}}reviewer with CHECK dimensions. REJECTED \u2192 return to coder (max {{QA_RETRY_LIMIT}} attempts). APPROVED \u2192 continue.
|
|
32012
32297
|
- If file matches security globs (auth, api, crypto, security, middleware, session, token, config/, env, credentials, authorization, roles, permissions, access) OR content has security keywords (see SECURITY_KEYWORDS list) OR secretscan has ANY findings OR sast_scan has ANY findings at or above threshold \u2192 MUST delegate {{AGENT_PREFIX}}reviewer AGAIN with security-only CHECK review. REJECTED \u2192 return to coder (max {{QA_RETRY_LIMIT}} attempts). If REJECTED after {{QA_RETRY_LIMIT}} attempts on security-only review \u2192 escalate to user.
|
|
32013
32298
|
- Delegate {{AGENT_PREFIX}}test_engineer for verification tests. FAIL \u2192 return to coder.
|
|
@@ -32213,6 +32498,29 @@ RETRY PROTOCOL \u2014 when returning to coder after any gate failure:
|
|
|
32213
32498
|
4. Gates already PASSED may be skipped on retry if their input files are unchanged
|
|
32214
32499
|
5. Print "Resuming at step [5X] after coder retry [N/{{QA_RETRY_LIMIT}}]" before re-executing
|
|
32215
32500
|
|
|
32501
|
+
GATE FAILURE RESPONSE RULES \u2014 when ANY gate returns a failure:
|
|
32502
|
+
You MUST return to {{AGENT_PREFIX}}coder. You MUST NOT fix the code yourself.
|
|
32503
|
+
|
|
32504
|
+
WRONG responses to gate failure:
|
|
32505
|
+
\u2717 Editing the file yourself to fix the syntax error
|
|
32506
|
+
\u2717 Running a tool to auto-fix and moving on without coder
|
|
32507
|
+
\u2717 "Installing" or "configuring" tools to work around the failure
|
|
32508
|
+
\u2717 Treating the failure as an environment issue and proceeding
|
|
32509
|
+
\u2717 Deciding the failure is a false positive and skipping the gate
|
|
32510
|
+
|
|
32511
|
+
RIGHT response to gate failure:
|
|
32512
|
+
\u2713 Print "GATE FAILED: [gate name] | REASON: [details]"
|
|
32513
|
+
\u2713 Delegate to {{AGENT_PREFIX}}coder with:
|
|
32514
|
+
TASK: Fix [gate name] failure
|
|
32515
|
+
FILE: [affected file(s)]
|
|
32516
|
+
INPUT: [exact error output from the gate]
|
|
32517
|
+
CONSTRAINT: Fix ONLY the reported issue, do not modify other code
|
|
32518
|
+
\u2713 After coder returns, re-run the failed gate from the step that failed
|
|
32519
|
+
\u2713 Print "Coder attempt [N/{{QA_RETRY_LIMIT}}] on task [X.Y]"
|
|
32520
|
+
|
|
32521
|
+
The ONLY exception: lint tool in fix mode (step 5g) auto-corrects by design.
|
|
32522
|
+
All other gates: failure \u2192 return to coder. No self-fixes. No workarounds.
|
|
32523
|
+
|
|
32216
32524
|
5a. **UI DESIGN GATE** (conditional \u2014 Rule 9): If task matches UI trigger \u2192 {{AGENT_PREFIX}}designer produces scaffold \u2192 pass scaffold to coder as INPUT. If no match \u2192 skip.
|
|
32217
32525
|
5b. {{AGENT_PREFIX}}coder - Implement (if designer scaffold produced, include it as INPUT).
|
|
32218
32526
|
5c. Run \`diff\` tool. If \`hasContractChanges\` \u2192 {{AGENT_PREFIX}}explorer integration analysis. BREAKING \u2192 coder retry.
|
|
@@ -32236,6 +32544,22 @@ RETRY PROTOCOL \u2014 when returning to coder after any gate failure:
|
|
|
32236
32544
|
\u2192 If gates_passed === false: read individual tool results, identify which tool(s) failed, return structured rejection to @coder with specific tool failures. Do NOT call @reviewer.
|
|
32237
32545
|
\u2192 If gates_passed === true: proceed to @reviewer.
|
|
32238
32546
|
\u2192 REQUIRED: Print "pre_check_batch: [PASS \u2014 all gates passed | FAIL \u2014 [gate]: [details]]"
|
|
32547
|
+
|
|
32548
|
+
\u26A0\uFE0F pre_check_batch SCOPE BOUNDARY:
|
|
32549
|
+
pre_check_batch runs FOUR automated tools: lint:check, secretscan, sast_scan, quality_budget.
|
|
32550
|
+
pre_check_batch does NOT run and does NOT replace:
|
|
32551
|
+
- {{AGENT_PREFIX}}reviewer (logic review, correctness, edge cases, maintainability)
|
|
32552
|
+
- {{AGENT_PREFIX}}reviewer security-only pass (OWASP evaluation, auth/crypto review)
|
|
32553
|
+
- {{AGENT_PREFIX}}test_engineer verification tests (functional correctness)
|
|
32554
|
+
- {{AGENT_PREFIX}}test_engineer adversarial tests (attack vectors, boundary violations)
|
|
32555
|
+
- diff tool (contract change detection)
|
|
32556
|
+
- placeholder_scan (TODO/stub detection)
|
|
32557
|
+
- imports (dependency audit)
|
|
32558
|
+
gates_passed: true means "automated static checks passed."
|
|
32559
|
+
It does NOT mean "code is reviewed." It does NOT mean "code is tested."
|
|
32560
|
+
After pre_check_batch passes, you MUST STILL delegate to {{AGENT_PREFIX}}reviewer.
|
|
32561
|
+
Treating pre_check_batch as a substitute for reviewer is a PROCESS VIOLATION.
|
|
32562
|
+
|
|
32239
32563
|
5j. {{AGENT_PREFIX}}reviewer - General review. REJECTED (< {{QA_RETRY_LIMIT}}) \u2192 coder retry. REJECTED ({{QA_RETRY_LIMIT}}) \u2192 escalate.
|
|
32240
32564
|
\u2192 REQUIRED: Print "reviewer: [APPROVED | REJECTED \u2014 reason]"
|
|
32241
32565
|
5k. Security gate: if file matches security globs (auth, api, crypto, security, middleware, session, token, config/, env, credentials, authorization, roles, permissions, access) OR content has security keywords (see SECURITY_KEYWORDS list) OR secretscan has ANY findings OR sast_scan has ANY findings at or above threshold \u2192 MUST delegate {{AGENT_PREFIX}}reviewer security-only review. REJECTED (< {{QA_RETRY_LIMIT}}) \u2192 coder retry. REJECTED ({{QA_RETRY_LIMIT}}) \u2192 escalate to user.
|
|
@@ -32257,20 +32581,24 @@ PRE-COMMIT RULE \u2014 Before ANY commit or push:
|
|
|
32257
32581
|
If ANY box is unchecked: DO NOT COMMIT. Return to step 5b.
|
|
32258
32582
|
There is no override. A commit without a completed QA gate is a workflow violation.
|
|
32259
32583
|
|
|
32260
|
-
TASK COMPLETION
|
|
32261
|
-
[TOOL] diff: PASS / SKIP
|
|
32262
|
-
[TOOL]
|
|
32263
|
-
[
|
|
32264
|
-
[
|
|
32265
|
-
[
|
|
32266
|
-
[
|
|
32267
|
-
[
|
|
32268
|
-
[
|
|
32269
|
-
[
|
|
32270
|
-
[
|
|
32271
|
-
[
|
|
32272
|
-
[
|
|
32273
|
-
|
|
32584
|
+
5o. \u26D4 TASK COMPLETION GATE \u2014 You MUST print this checklist with filled values before marking \u2713 in .swarm/plan.md:
|
|
32585
|
+
[TOOL] diff: PASS / SKIP \u2014 value: ___
|
|
32586
|
+
[TOOL] syntax_check: PASS \u2014 value: ___
|
|
32587
|
+
[TOOL] placeholder_scan: PASS \u2014 value: ___
|
|
32588
|
+
[TOOL] imports: PASS \u2014 value: ___
|
|
32589
|
+
[TOOL] lint: PASS \u2014 value: ___
|
|
32590
|
+
[TOOL] build_check: PASS / SKIPPED \u2014 value: ___
|
|
32591
|
+
[TOOL] pre_check_batch: PASS (lint:check \u2713 secretscan \u2713 sast_scan \u2713 quality_budget \u2713) \u2014 value: ___
|
|
32592
|
+
[GATE] reviewer: APPROVED \u2014 value: ___
|
|
32593
|
+
[GATE] security-reviewer: APPROVED / SKIPPED \u2014 value: ___
|
|
32594
|
+
[GATE] test_engineer-verification: PASS \u2014 value: ___
|
|
32595
|
+
[GATE] test_engineer-adversarial: PASS \u2014 value: ___
|
|
32596
|
+
[GATE] coverage: \u226570% / soft-skip \u2014 value: ___
|
|
32597
|
+
|
|
32598
|
+
You MUST NOT mark a task complete without printing this checklist with filled values.
|
|
32599
|
+
You MUST NOT fill "PASS" or "APPROVED" for a gate you did not actually run \u2014 that is fabrication.
|
|
32600
|
+
Any blank "value: ___" field = gate was not run = task is NOT complete.
|
|
32601
|
+
Filling this checklist from memory ("I think I ran it") is INVALID. Each value must come from actual tool/agent output in this session.
|
|
32274
32602
|
|
|
32275
32603
|
5o. Update plan.md [x], proceed to next task.
|
|
32276
32604
|
|
|
@@ -32287,6 +32615,15 @@ TASK COMPLETION CHECKLIST \u2014 emit before marking \u2713 in plan.md:
|
|
|
32287
32615
|
6. Summarize to user
|
|
32288
32616
|
7. Ask: "Ready for Phase [N+1]?"
|
|
32289
32617
|
|
|
32618
|
+
CATASTROPHIC VIOLATION CHECK \u2014 ask yourself at EVERY phase boundary (MODE: PHASE-WRAP):
|
|
32619
|
+
"Have I delegated to {{AGENT_PREFIX}}reviewer at least once this phase?"
|
|
32620
|
+
If the answer is NO: you have a catastrophic process violation.
|
|
32621
|
+
STOP. Do not proceed to the next phase. Inform the user:
|
|
32622
|
+
"\u26D4 PROCESS VIOLATION: Phase [N] completed with zero reviewer delegations.
|
|
32623
|
+
All code changes in this phase are unreviewed. Recommend retrospective review before proceeding."
|
|
32624
|
+
This is not optional. Zero reviewer calls in a phase is always a violation.
|
|
32625
|
+
There is no project where code ships without review.
|
|
32626
|
+
|
|
32290
32627
|
### Blockers
|
|
32291
32628
|
Mark [BLOCKED] in plan.md, skip to next unblocked task, inform user.
|
|
32292
32629
|
|
|
@@ -33079,6 +33416,9 @@ function createAgents(config2) {
|
|
|
33079
33416
|
}
|
|
33080
33417
|
function getAgentConfigs(config2) {
|
|
33081
33418
|
const agents = createAgents(config2);
|
|
33419
|
+
const toolFilterEnabled = config2?.tool_filter?.enabled ?? true;
|
|
33420
|
+
const toolFilterOverrides = config2?.tool_filter?.overrides ?? {};
|
|
33421
|
+
const warnedMissingWhitelist = new Set;
|
|
33082
33422
|
return Object.fromEntries(agents.map((agent) => {
|
|
33083
33423
|
const sdkConfig = {
|
|
33084
33424
|
...agent.config,
|
|
@@ -33089,6 +33429,41 @@ function getAgentConfigs(config2) {
|
|
|
33089
33429
|
} else {
|
|
33090
33430
|
sdkConfig.mode = "subagent";
|
|
33091
33431
|
}
|
|
33432
|
+
const baseAgentName = stripKnownSwarmPrefix(agent.name);
|
|
33433
|
+
if (!toolFilterEnabled) {
|
|
33434
|
+
sdkConfig.tools = agent.config.tools ?? {};
|
|
33435
|
+
return [agent.name, sdkConfig];
|
|
33436
|
+
}
|
|
33437
|
+
let allowedTools;
|
|
33438
|
+
const override = toolFilterOverrides[baseAgentName];
|
|
33439
|
+
if (override !== undefined) {
|
|
33440
|
+
allowedTools = override;
|
|
33441
|
+
} else {
|
|
33442
|
+
allowedTools = AGENT_TOOL_MAP[baseAgentName];
|
|
33443
|
+
}
|
|
33444
|
+
if (!allowedTools && !Object.hasOwn(toolFilterOverrides, baseAgentName)) {
|
|
33445
|
+
if (!warnedMissingWhitelist.has(baseAgentName)) {
|
|
33446
|
+
console.warn(`[getAgentConfigs] Unknown agent '${baseAgentName}', defaulting to minimal toolset.`);
|
|
33447
|
+
warnedMissingWhitelist.add(baseAgentName);
|
|
33448
|
+
}
|
|
33449
|
+
}
|
|
33450
|
+
const originalTools = agent.config.tools ? { ...agent.config.tools } : undefined;
|
|
33451
|
+
if (allowedTools) {
|
|
33452
|
+
const baseTools = originalTools ?? {};
|
|
33453
|
+
const disabledTools = Object.fromEntries(Object.entries(baseTools).filter(([, value]) => value === false));
|
|
33454
|
+
const filteredTools = { ...disabledTools };
|
|
33455
|
+
for (const tool of allowedTools) {
|
|
33456
|
+
if (filteredTools[tool] === false)
|
|
33457
|
+
continue;
|
|
33458
|
+
filteredTools[tool] = true;
|
|
33459
|
+
}
|
|
33460
|
+
sdkConfig.tools = filteredTools;
|
|
33461
|
+
} else {
|
|
33462
|
+
sdkConfig.tools = {
|
|
33463
|
+
write: false,
|
|
33464
|
+
edit: false
|
|
33465
|
+
};
|
|
33466
|
+
}
|
|
33092
33467
|
return [agent.name, sdkConfig];
|
|
33093
33468
|
}));
|
|
33094
33469
|
}
|
|
@@ -33743,7 +34118,7 @@ class PlanSyncWorker {
|
|
|
33743
34118
|
log("[PlanSyncWorker] Swarm directory does not exist yet");
|
|
33744
34119
|
return false;
|
|
33745
34120
|
}
|
|
33746
|
-
this.watcher = fs3.watch(swarmDir, { persistent: false }, (
|
|
34121
|
+
this.watcher = fs3.watch(swarmDir, { persistent: false }, (_eventType, filename) => {
|
|
33747
34122
|
if (this.disposed || this.status !== "running") {
|
|
33748
34123
|
return;
|
|
33749
34124
|
}
|
|
@@ -33886,7 +34261,7 @@ class PlanSyncWorker {
|
|
|
33886
34261
|
withTimeout(promise2, ms, timeoutMessage) {
|
|
33887
34262
|
return new Promise((resolve4, reject) => {
|
|
33888
34263
|
const timer = setTimeout(() => {
|
|
33889
|
-
reject(new Error(timeoutMessage
|
|
34264
|
+
reject(new Error(`${timeoutMessage} (${ms}ms)`));
|
|
33890
34265
|
}, ms);
|
|
33891
34266
|
promise2.then((result) => {
|
|
33892
34267
|
clearTimeout(timer);
|
|
@@ -34051,7 +34426,15 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000) {
|
|
|
34051
34426
|
activeInvocationId: 0,
|
|
34052
34427
|
lastInvocationIdByAgent: {},
|
|
34053
34428
|
windows: {},
|
|
34054
|
-
lastCompactionHint: 0
|
|
34429
|
+
lastCompactionHint: 0,
|
|
34430
|
+
architectWriteCount: 0,
|
|
34431
|
+
lastCoderDelegationTaskId: null,
|
|
34432
|
+
gateLog: new Map,
|
|
34433
|
+
reviewerCallCount: new Map,
|
|
34434
|
+
lastGateFailure: null,
|
|
34435
|
+
partialGateWarningIssued: false,
|
|
34436
|
+
selfFixAttempted: false,
|
|
34437
|
+
catastrophicPhaseWarnings: new Set
|
|
34055
34438
|
};
|
|
34056
34439
|
swarmState.agentSessions.set(sessionId, sessionState);
|
|
34057
34440
|
swarmState.activeAgent.set(sessionId, agentName);
|
|
@@ -34078,6 +34461,30 @@ function ensureAgentSession(sessionId, agentName) {
|
|
|
34078
34461
|
if (session.lastCompactionHint === undefined) {
|
|
34079
34462
|
session.lastCompactionHint = 0;
|
|
34080
34463
|
}
|
|
34464
|
+
if (session.architectWriteCount === undefined) {
|
|
34465
|
+
session.architectWriteCount = 0;
|
|
34466
|
+
}
|
|
34467
|
+
if (session.lastCoderDelegationTaskId === undefined) {
|
|
34468
|
+
session.lastCoderDelegationTaskId = null;
|
|
34469
|
+
}
|
|
34470
|
+
if (!session.gateLog) {
|
|
34471
|
+
session.gateLog = new Map;
|
|
34472
|
+
}
|
|
34473
|
+
if (!session.reviewerCallCount) {
|
|
34474
|
+
session.reviewerCallCount = new Map;
|
|
34475
|
+
}
|
|
34476
|
+
if (session.lastGateFailure === undefined) {
|
|
34477
|
+
session.lastGateFailure = null;
|
|
34478
|
+
}
|
|
34479
|
+
if (session.partialGateWarningIssued === undefined) {
|
|
34480
|
+
session.partialGateWarningIssued = false;
|
|
34481
|
+
}
|
|
34482
|
+
if (session.selfFixAttempted === undefined) {
|
|
34483
|
+
session.selfFixAttempted = false;
|
|
34484
|
+
}
|
|
34485
|
+
if (!session.catastrophicPhaseWarnings) {
|
|
34486
|
+
session.catastrophicPhaseWarnings = new Set;
|
|
34487
|
+
}
|
|
34081
34488
|
session.lastToolCallTime = now;
|
|
34082
34489
|
return session;
|
|
34083
34490
|
}
|
|
@@ -34392,7 +34799,7 @@ async function handleBenchmarkCommand(directory, args2) {
|
|
|
34392
34799
|
}
|
|
34393
34800
|
lines.push("");
|
|
34394
34801
|
}
|
|
34395
|
-
if (qualityMetrics
|
|
34802
|
+
if (qualityMetrics?.hasEvidence) {
|
|
34396
34803
|
lines.push("### Quality Metrics");
|
|
34397
34804
|
lines.push(`- Complexity Delta: ${qualityMetrics.complexityDelta} (max: ${qualityMetrics.thresholds.maxComplexityDelta}) ${qualityMetrics.complexityDelta <= qualityMetrics.thresholds.maxComplexityDelta ? "\u2705" : "\u274C"}`);
|
|
34398
34805
|
lines.push(`- Public API Delta: ${qualityMetrics.publicApiDelta} (max: ${qualityMetrics.thresholds.maxPublicApiDelta}) ${qualityMetrics.publicApiDelta <= qualityMetrics.thresholds.maxPublicApiDelta ? "\u2705" : "\u274C"}`);
|
|
@@ -34734,12 +35141,12 @@ function formatEvidenceEntry(index, entry) {
|
|
|
34734
35141
|
const details = {};
|
|
34735
35142
|
if (entry.type === "review") {
|
|
34736
35143
|
const reviewEntry = entry;
|
|
34737
|
-
details
|
|
34738
|
-
details
|
|
35144
|
+
details.risk = reviewEntry.risk;
|
|
35145
|
+
details.issues = reviewEntry.issues?.length;
|
|
34739
35146
|
} else if (entry.type === "test") {
|
|
34740
35147
|
const testEntry = entry;
|
|
34741
|
-
details
|
|
34742
|
-
details
|
|
35148
|
+
details.tests_passed = testEntry.tests_passed;
|
|
35149
|
+
details.tests_failed = testEntry.tests_failed;
|
|
34743
35150
|
}
|
|
34744
35151
|
return {
|
|
34745
35152
|
index,
|
|
@@ -34965,7 +35372,7 @@ function extractFromPlan(plan) {
|
|
|
34965
35372
|
phases.push({
|
|
34966
35373
|
id: phase.id,
|
|
34967
35374
|
name: phase.name,
|
|
34968
|
-
status: phase.status,
|
|
35375
|
+
status: phase.status === "completed" ? "complete" : phase.status,
|
|
34969
35376
|
statusText: getStatusText(phase.status),
|
|
34970
35377
|
statusIcon: getStatusIcon(phase.status),
|
|
34971
35378
|
completedTasks: completed,
|
|
@@ -35554,6 +35961,153 @@ function extractIncompleteTasksFromPlan(plan, maxChars = 500) {
|
|
|
35554
35961
|
return text;
|
|
35555
35962
|
return `${text.slice(0, maxChars)}...`;
|
|
35556
35963
|
}
|
|
35964
|
+
function extractPlanCursor(planContent, options) {
|
|
35965
|
+
const maxTokens = options?.maxTokens ?? 1500;
|
|
35966
|
+
const maxChars = maxTokens * 4;
|
|
35967
|
+
const lookaheadCount = options?.lookaheadTasks ?? 2;
|
|
35968
|
+
if (!planContent || typeof planContent !== "string") {
|
|
35969
|
+
return `[SWARM PLAN CURSOR]
|
|
35970
|
+
No plan content available. Start by creating a .swarm/plan.md file.
|
|
35971
|
+
[/SWARM PLAN CURSOR]`;
|
|
35972
|
+
}
|
|
35973
|
+
const lines = planContent.split(`
|
|
35974
|
+
`);
|
|
35975
|
+
const result = [];
|
|
35976
|
+
result.push("[SWARM PLAN CURSOR]");
|
|
35977
|
+
const phases = [];
|
|
35978
|
+
let currentPhase = null;
|
|
35979
|
+
let inPhase = false;
|
|
35980
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
35981
|
+
const line = lines[i2];
|
|
35982
|
+
const trimmed = line.trim();
|
|
35983
|
+
const phaseMatch = trimmed.match(/^## Phase (\d+):?\s*(.*?)\s*\[(COMPLETE|IN PROGRESS|PENDING|BLOCKED)\]/i);
|
|
35984
|
+
if (phaseMatch) {
|
|
35985
|
+
if (currentPhase) {
|
|
35986
|
+
phases.push(currentPhase);
|
|
35987
|
+
}
|
|
35988
|
+
const phaseNum = parseInt(phaseMatch[1], 10);
|
|
35989
|
+
const phaseTitle = phaseMatch[2]?.trim() || "";
|
|
35990
|
+
const status = phaseMatch[3].toUpperCase();
|
|
35991
|
+
currentPhase = {
|
|
35992
|
+
number: phaseNum,
|
|
35993
|
+
title: phaseTitle,
|
|
35994
|
+
status,
|
|
35995
|
+
contentLines: []
|
|
35996
|
+
};
|
|
35997
|
+
inPhase = true;
|
|
35998
|
+
continue;
|
|
35999
|
+
}
|
|
36000
|
+
if (inPhase && (line.startsWith("## ") || trimmed === "---")) {
|
|
36001
|
+
if (currentPhase) {
|
|
36002
|
+
phases.push(currentPhase);
|
|
36003
|
+
}
|
|
36004
|
+
currentPhase = null;
|
|
36005
|
+
inPhase = false;
|
|
36006
|
+
continue;
|
|
36007
|
+
}
|
|
36008
|
+
if (currentPhase && inPhase && trimmed) {
|
|
36009
|
+
currentPhase.contentLines.push(line);
|
|
36010
|
+
}
|
|
36011
|
+
}
|
|
36012
|
+
if (currentPhase) {
|
|
36013
|
+
phases.push(currentPhase);
|
|
36014
|
+
}
|
|
36015
|
+
if (phases.length === 0) {
|
|
36016
|
+
result.push("No phases found in plan.");
|
|
36017
|
+
result.push("[/SWARM PLAN CURSOR]");
|
|
36018
|
+
return result.join(`
|
|
36019
|
+
`);
|
|
36020
|
+
}
|
|
36021
|
+
const inProgressPhase = phases.find((p) => p.status === "IN PROGRESS");
|
|
36022
|
+
const completePhases = phases.filter((p) => p.status === "COMPLETE");
|
|
36023
|
+
const pendingPhases = phases.filter((p) => p.status === "PENDING");
|
|
36024
|
+
if (completePhases.length > 0) {
|
|
36025
|
+
const recentComplete = completePhases.slice(-5);
|
|
36026
|
+
if (completePhases.length > 5) {
|
|
36027
|
+
result.push("");
|
|
36028
|
+
result.push(`## Earlier Phases (${completePhases.length - 5} more)`);
|
|
36029
|
+
result.push(`- Phase 1-${completePhases.length - 5}: Complete`);
|
|
36030
|
+
}
|
|
36031
|
+
result.push("");
|
|
36032
|
+
result.push("## Completed Phases");
|
|
36033
|
+
for (const phase of recentComplete) {
|
|
36034
|
+
const taskLines = phase.contentLines.filter((l) => l.trim().startsWith("- [")).map((l) => l.replace(/^- \[[ xX]\]\s*/, "").replace(/\s*\[.*?\]/g, "").trim()).slice(0, 3);
|
|
36035
|
+
const taskSummary = taskLines.length > 0 ? taskLines.join(", ") : "All tasks complete";
|
|
36036
|
+
result.push(`- Phase ${phase.number}: ${phase.title}`);
|
|
36037
|
+
result.push(` - ${taskSummary}`);
|
|
36038
|
+
}
|
|
36039
|
+
}
|
|
36040
|
+
const incompleteTasks = inProgressPhase ? inProgressPhase.contentLines.filter((l) => l.trim().startsWith("- [ ]")).map((l) => l.trim()) : [];
|
|
36041
|
+
if (inProgressPhase) {
|
|
36042
|
+
result.push("");
|
|
36043
|
+
result.push(`## Phase ${inProgressPhase.number} [IN PROGRESS]`);
|
|
36044
|
+
result.push(`- ${inProgressPhase.title}`);
|
|
36045
|
+
if (incompleteTasks.length > 0) {
|
|
36046
|
+
const currentTask = incompleteTasks[0];
|
|
36047
|
+
result.push("");
|
|
36048
|
+
result.push(`- Current: ${currentTask.replace("- [ ] ", "")}`);
|
|
36049
|
+
const lookahead = incompleteTasks.slice(1, 1 + lookaheadCount);
|
|
36050
|
+
for (let i2 = 0;i2 < lookahead.length; i2++) {
|
|
36051
|
+
result.push(`- Next: ${lookahead[i2].replace("- [ ] ", "")}`);
|
|
36052
|
+
}
|
|
36053
|
+
} else {
|
|
36054
|
+
result.push("- (No pending tasks)");
|
|
36055
|
+
}
|
|
36056
|
+
}
|
|
36057
|
+
const nextPending = pendingPhases[0];
|
|
36058
|
+
if (nextPending) {
|
|
36059
|
+
result.push("");
|
|
36060
|
+
result.push(`## Phase ${nextPending.number} [PENDING]`);
|
|
36061
|
+
result.push(`- ${nextPending.title}`);
|
|
36062
|
+
}
|
|
36063
|
+
let output = result.join(`
|
|
36064
|
+
`);
|
|
36065
|
+
output += `
|
|
36066
|
+
[/SWARM PLAN CURSOR]`;
|
|
36067
|
+
if (output.length > maxChars) {
|
|
36068
|
+
const compactResult = [];
|
|
36069
|
+
compactResult.push("[SWARM PLAN CURSOR]");
|
|
36070
|
+
if (completePhases.length > 0) {
|
|
36071
|
+
compactResult.push("## Completed Phases");
|
|
36072
|
+
const recentCompact = completePhases.slice(-3);
|
|
36073
|
+
for (const phase of recentCompact) {
|
|
36074
|
+
const taskLines = phase.contentLines.filter((l) => l.trim().startsWith("- [")).map((l) => l.replace(/^- \[[ xX]\]\s*/, "").replace(/\s*\[.*?\]/g, "").trim()).slice(0, 1);
|
|
36075
|
+
const taskSummary = taskLines.length > 0 ? taskLines[0] : "Complete";
|
|
36076
|
+
compactResult.push(`- Phase ${phase.number}: ${taskSummary}`);
|
|
36077
|
+
}
|
|
36078
|
+
if (completePhases.length > 3) {
|
|
36079
|
+
compactResult.push(`- Earlier: Phase 1-${completePhases.length - 3} complete`);
|
|
36080
|
+
}
|
|
36081
|
+
}
|
|
36082
|
+
if (inProgressPhase) {
|
|
36083
|
+
compactResult.push("");
|
|
36084
|
+
compactResult.push(`## Phase ${inProgressPhase.number} [IN PROGRESS]`);
|
|
36085
|
+
compactResult.push(`- ${inProgressPhase.title}`);
|
|
36086
|
+
if (incompleteTasks.length > 0) {
|
|
36087
|
+
const truncateTask = (task) => {
|
|
36088
|
+
const text = task.replace("- [ ] ", "");
|
|
36089
|
+
return text.length > 60 ? text.slice(0, 57) + "..." : text;
|
|
36090
|
+
};
|
|
36091
|
+
compactResult.push(`- Current: ${truncateTask(incompleteTasks[0])}`);
|
|
36092
|
+
const lookahead = incompleteTasks.slice(1, 1 + Math.min(lookaheadCount, 1));
|
|
36093
|
+
for (const task of lookahead) {
|
|
36094
|
+
compactResult.push(`- Next: ${truncateTask(task)}`);
|
|
36095
|
+
}
|
|
36096
|
+
} else {
|
|
36097
|
+
compactResult.push("- (No pending tasks)");
|
|
36098
|
+
}
|
|
36099
|
+
}
|
|
36100
|
+
if (nextPending) {
|
|
36101
|
+
compactResult.push("");
|
|
36102
|
+
compactResult.push(`## Phase ${nextPending.number} [PENDING]`);
|
|
36103
|
+
compactResult.push(`- ${nextPending.title}`);
|
|
36104
|
+
}
|
|
36105
|
+
compactResult.push("[/SWARM PLAN CURSOR]");
|
|
36106
|
+
output = compactResult.join(`
|
|
36107
|
+
`);
|
|
36108
|
+
}
|
|
36109
|
+
return output;
|
|
36110
|
+
}
|
|
35557
36111
|
|
|
35558
36112
|
// src/services/status-service.ts
|
|
35559
36113
|
init_utils2();
|
|
@@ -35971,6 +36525,10 @@ function createContextBudgetHandler(config3) {
|
|
|
35971
36525
|
};
|
|
35972
36526
|
}
|
|
35973
36527
|
// src/hooks/delegation-gate.ts
|
|
36528
|
+
function extractTaskLine(text) {
|
|
36529
|
+
const match = text.match(/TASK:\s*(.+?)(?:\n|$)/i);
|
|
36530
|
+
return match ? match[1].trim() : null;
|
|
36531
|
+
}
|
|
35974
36532
|
function createDelegationGateHook(config3) {
|
|
35975
36533
|
const enabled = config3.hooks?.delegation_gate !== false;
|
|
35976
36534
|
const delegationMaxChars = config3.hooks?.delegation_max_chars ?? 4000;
|
|
@@ -36002,8 +36560,26 @@ function createDelegationGateHook(config3) {
|
|
|
36002
36560
|
return;
|
|
36003
36561
|
const textPart = lastUserMessage.parts[textPartIndex];
|
|
36004
36562
|
const text = textPart.text ?? "";
|
|
36563
|
+
const sessionID = lastUserMessage.info?.sessionID;
|
|
36564
|
+
const taskIdMatch = text.match(/TASK:\s*(.+?)(?:\n|$)/i);
|
|
36565
|
+
const currentTaskId = taskIdMatch ? taskIdMatch[1].trim() : null;
|
|
36005
36566
|
const coderDelegationPattern = /(?:^|\n)\s*(?:\w+_)?coder\s*\n\s*TASK:/i;
|
|
36006
|
-
|
|
36567
|
+
const isCoderDelegation = coderDelegationPattern.test(text);
|
|
36568
|
+
if (sessionID && isCoderDelegation && currentTaskId) {
|
|
36569
|
+
const session = ensureAgentSession(sessionID);
|
|
36570
|
+
session.lastCoderDelegationTaskId = currentTaskId;
|
|
36571
|
+
}
|
|
36572
|
+
if (sessionID && !isCoderDelegation && currentTaskId) {
|
|
36573
|
+
const session = ensureAgentSession(sessionID);
|
|
36574
|
+
if (session.architectWriteCount > 0 && session.lastCoderDelegationTaskId !== currentTaskId) {
|
|
36575
|
+
const warningText2 = `\u26A0\uFE0F DELEGATION VIOLATION: Code modifications detected for task ${currentTaskId} with zero coder delegations.
|
|
36576
|
+
Rule 1: DELEGATE all coding to coder. You do NOT write code.`;
|
|
36577
|
+
textPart.text = `${warningText2}
|
|
36578
|
+
|
|
36579
|
+
${text}`;
|
|
36580
|
+
}
|
|
36581
|
+
}
|
|
36582
|
+
if (!isCoderDelegation)
|
|
36007
36583
|
return;
|
|
36008
36584
|
const warnings = [];
|
|
36009
36585
|
if (text.length > delegationMaxChars) {
|
|
@@ -36017,12 +36593,18 @@ function createDelegationGateHook(config3) {
|
|
|
36017
36593
|
if (taskMatches && taskMatches.length > 1) {
|
|
36018
36594
|
warnings.push(`Multiple TASK: sections detected (${taskMatches.length}). Send ONE task per coder call.`);
|
|
36019
36595
|
}
|
|
36020
|
-
const batchingPattern = /\b(?:and also|then also|additionally|as well as|along with)
|
|
36596
|
+
const batchingPattern = /\b(?:and also|then also|additionally|as well as|along with|while you'?re at it)[.,]?\b/gi;
|
|
36021
36597
|
const batchingMatches = text.match(batchingPattern);
|
|
36022
36598
|
if (batchingMatches && batchingMatches.length > 0) {
|
|
36023
|
-
warnings.push(
|
|
36599
|
+
warnings.push(`Batching language detected (${batchingMatches.join(", ")}). Break compound objectives into separate coder calls.`);
|
|
36600
|
+
}
|
|
36601
|
+
const taskLine = extractTaskLine(text);
|
|
36602
|
+
if (taskLine) {
|
|
36603
|
+
const andPattern = /\s+and\s+(update|add|remove|modify|refactor|implement|create|delete|fix|change|build|deploy|write|test|move|rename|extend|extract|convert|migrate|upgrade|replace)\b/i;
|
|
36604
|
+
if (andPattern.test(taskLine)) {
|
|
36605
|
+
warnings.push('TASK line contains "and" connecting separate actions');
|
|
36606
|
+
}
|
|
36024
36607
|
}
|
|
36025
|
-
const sessionID = lastUserMessage.info?.sessionID;
|
|
36026
36608
|
if (sessionID) {
|
|
36027
36609
|
const delegationChain = swarmState.delegationChains.get(sessionID);
|
|
36028
36610
|
if (delegationChain && delegationChain.length >= 2) {
|
|
@@ -36047,10 +36629,11 @@ function createDelegationGateHook(config3) {
|
|
|
36047
36629
|
}
|
|
36048
36630
|
if (warnings.length === 0)
|
|
36049
36631
|
return;
|
|
36050
|
-
const
|
|
36051
|
-
|
|
36052
|
-
|
|
36053
|
-
|
|
36632
|
+
const warningLines = warnings.map((w) => `Detected signal: ${w}`);
|
|
36633
|
+
const warningText = `\u26A0\uFE0F BATCH DETECTED: Your coder delegation appears to contain multiple tasks.
|
|
36634
|
+
Rule 3: ONE task per coder call. Split this into separate delegations.
|
|
36635
|
+
${warningLines.join(`
|
|
36636
|
+
`)}`;
|
|
36054
36637
|
const originalText = textPart.text ?? "";
|
|
36055
36638
|
textPart.text = `${warningText}
|
|
36056
36639
|
|
|
@@ -36063,7 +36646,7 @@ function createDelegationTrackerHook(config3, guardrailsEnabled = true) {
|
|
|
36063
36646
|
const now = Date.now();
|
|
36064
36647
|
if (!input.agent || input.agent === "") {
|
|
36065
36648
|
const session2 = swarmState.agentSessions.get(input.sessionID);
|
|
36066
|
-
if (session2
|
|
36649
|
+
if (session2?.delegationActive) {
|
|
36067
36650
|
session2.delegationActive = false;
|
|
36068
36651
|
swarmState.activeAgent.set(input.sessionID, ORCHESTRATOR_NAME);
|
|
36069
36652
|
ensureAgentSession(input.sessionID, ORCHESTRATOR_NAME);
|
|
@@ -36099,7 +36682,85 @@ function createDelegationTrackerHook(config3, guardrailsEnabled = true) {
|
|
|
36099
36682
|
};
|
|
36100
36683
|
}
|
|
36101
36684
|
// src/hooks/guardrails.ts
|
|
36685
|
+
import * as path15 from "path";
|
|
36686
|
+
init_manager2();
|
|
36102
36687
|
init_utils();
|
|
36688
|
+
function extractPhaseNumber(phaseString) {
|
|
36689
|
+
if (!phaseString)
|
|
36690
|
+
return 1;
|
|
36691
|
+
const match = phaseString.match(/^Phase (\d+):/);
|
|
36692
|
+
return match ? parseInt(match[1], 10) : 1;
|
|
36693
|
+
}
|
|
36694
|
+
function isWriteTool(toolName) {
|
|
36695
|
+
const normalized = toolName.replace(/^[^:]+[:.]/, "");
|
|
36696
|
+
const writeTools = [
|
|
36697
|
+
"write",
|
|
36698
|
+
"edit",
|
|
36699
|
+
"patch",
|
|
36700
|
+
"apply_patch",
|
|
36701
|
+
"create_file",
|
|
36702
|
+
"insert",
|
|
36703
|
+
"replace"
|
|
36704
|
+
];
|
|
36705
|
+
return writeTools.includes(normalized);
|
|
36706
|
+
}
|
|
36707
|
+
function isArchitect(sessionId) {
|
|
36708
|
+
const activeAgent = swarmState.activeAgent.get(sessionId);
|
|
36709
|
+
if (activeAgent) {
|
|
36710
|
+
const stripped = stripKnownSwarmPrefix(activeAgent);
|
|
36711
|
+
if (stripped === ORCHESTRATOR_NAME) {
|
|
36712
|
+
return true;
|
|
36713
|
+
}
|
|
36714
|
+
}
|
|
36715
|
+
const session = swarmState.agentSessions.get(sessionId);
|
|
36716
|
+
if (session) {
|
|
36717
|
+
const stripped = stripKnownSwarmPrefix(session.agentName);
|
|
36718
|
+
if (stripped === ORCHESTRATOR_NAME) {
|
|
36719
|
+
return true;
|
|
36720
|
+
}
|
|
36721
|
+
}
|
|
36722
|
+
return false;
|
|
36723
|
+
}
|
|
36724
|
+
function isOutsideSwarmDir(filePath) {
|
|
36725
|
+
if (!filePath)
|
|
36726
|
+
return false;
|
|
36727
|
+
const cwd = process.cwd();
|
|
36728
|
+
const swarmDir = path15.resolve(cwd, ".swarm");
|
|
36729
|
+
const resolved = path15.resolve(cwd, filePath);
|
|
36730
|
+
const relative2 = path15.relative(swarmDir, resolved);
|
|
36731
|
+
return relative2.startsWith("..") || path15.isAbsolute(relative2);
|
|
36732
|
+
}
|
|
36733
|
+
function isGateTool(toolName) {
|
|
36734
|
+
const normalized = toolName.replace(/^[^:]+[:.]/, "");
|
|
36735
|
+
const gateTools = [
|
|
36736
|
+
"diff",
|
|
36737
|
+
"syntax_check",
|
|
36738
|
+
"placeholder_scan",
|
|
36739
|
+
"imports",
|
|
36740
|
+
"lint",
|
|
36741
|
+
"build_check",
|
|
36742
|
+
"pre_check_batch",
|
|
36743
|
+
"secretscan",
|
|
36744
|
+
"sast_scan",
|
|
36745
|
+
"quality_budget"
|
|
36746
|
+
];
|
|
36747
|
+
return gateTools.includes(normalized);
|
|
36748
|
+
}
|
|
36749
|
+
function isAgentDelegation(toolName, args2) {
|
|
36750
|
+
const normalized = toolName.replace(/^[^:]+[:.]/, "");
|
|
36751
|
+
if (normalized !== "Task" && normalized !== "task") {
|
|
36752
|
+
return { isDelegation: false, targetAgent: null };
|
|
36753
|
+
}
|
|
36754
|
+
const argsObj = args2;
|
|
36755
|
+
if (!argsObj) {
|
|
36756
|
+
return { isDelegation: false, targetAgent: null };
|
|
36757
|
+
}
|
|
36758
|
+
const subagentType = argsObj.subagent_type;
|
|
36759
|
+
if (typeof subagentType === "string") {
|
|
36760
|
+
return { isDelegation: true, targetAgent: subagentType };
|
|
36761
|
+
}
|
|
36762
|
+
return { isDelegation: false, targetAgent: null };
|
|
36763
|
+
}
|
|
36103
36764
|
function createGuardrailsHooks(config3) {
|
|
36104
36765
|
if (config3.enabled === false) {
|
|
36105
36766
|
return {
|
|
@@ -36108,8 +36769,36 @@ function createGuardrailsHooks(config3) {
|
|
|
36108
36769
|
messagesTransform: async () => {}
|
|
36109
36770
|
};
|
|
36110
36771
|
}
|
|
36772
|
+
const inputArgsByCallID = new Map;
|
|
36111
36773
|
return {
|
|
36112
36774
|
toolBefore: async (input, output) => {
|
|
36775
|
+
if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
|
|
36776
|
+
const args2 = output.args;
|
|
36777
|
+
const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
|
|
36778
|
+
if (typeof targetPath === "string" && isOutsideSwarmDir(targetPath)) {
|
|
36779
|
+
const session2 = swarmState.agentSessions.get(input.sessionID);
|
|
36780
|
+
if (session2) {
|
|
36781
|
+
session2.architectWriteCount++;
|
|
36782
|
+
warn("Architect direct code edit detected", {
|
|
36783
|
+
tool: input.tool,
|
|
36784
|
+
sessionID: input.sessionID,
|
|
36785
|
+
targetPath,
|
|
36786
|
+
writeCount: session2.architectWriteCount
|
|
36787
|
+
});
|
|
36788
|
+
if (session2.lastGateFailure && Date.now() - session2.lastGateFailure.timestamp < 120000) {
|
|
36789
|
+
const failedGate = session2.lastGateFailure.tool;
|
|
36790
|
+
const failedTaskId = session2.lastGateFailure.taskId;
|
|
36791
|
+
warn("Self-fix after gate failure detected", {
|
|
36792
|
+
failedGate,
|
|
36793
|
+
failedTaskId,
|
|
36794
|
+
currentTool: input.tool,
|
|
36795
|
+
sessionID: input.sessionID
|
|
36796
|
+
});
|
|
36797
|
+
session2.selfFixAttempted = true;
|
|
36798
|
+
}
|
|
36799
|
+
}
|
|
36800
|
+
}
|
|
36801
|
+
}
|
|
36113
36802
|
const rawActiveAgent = swarmState.activeAgent.get(input.sessionID);
|
|
36114
36803
|
const strippedAgent = rawActiveAgent ? stripKnownSwarmPrefix(rawActiveAgent) : undefined;
|
|
36115
36804
|
if (strippedAgent === ORCHESTRATOR_NAME) {
|
|
@@ -36237,8 +36926,45 @@ function createGuardrailsHooks(config3) {
|
|
|
36237
36926
|
window2.warningReason = reasons.join(", ");
|
|
36238
36927
|
}
|
|
36239
36928
|
}
|
|
36929
|
+
inputArgsByCallID.set(input.callID, output.args);
|
|
36240
36930
|
},
|
|
36241
36931
|
toolAfter: async (input, output) => {
|
|
36932
|
+
const session = swarmState.agentSessions.get(input.sessionID);
|
|
36933
|
+
if (session) {
|
|
36934
|
+
if (isGateTool(input.tool)) {
|
|
36935
|
+
const taskId = `${input.sessionID}:current`;
|
|
36936
|
+
if (!session.gateLog.has(taskId)) {
|
|
36937
|
+
session.gateLog.set(taskId, new Set);
|
|
36938
|
+
}
|
|
36939
|
+
session.gateLog.get(taskId)?.add(input.tool);
|
|
36940
|
+
const outputStr = typeof output.output === "string" ? output.output : "";
|
|
36941
|
+
const hasFailure = output.output === null || output.output === undefined || outputStr.includes("FAIL") || outputStr.includes("error") || outputStr.toLowerCase().includes("gates_passed: false");
|
|
36942
|
+
if (hasFailure) {
|
|
36943
|
+
session.lastGateFailure = {
|
|
36944
|
+
tool: input.tool,
|
|
36945
|
+
taskId,
|
|
36946
|
+
timestamp: Date.now()
|
|
36947
|
+
};
|
|
36948
|
+
} else {
|
|
36949
|
+
session.lastGateFailure = null;
|
|
36950
|
+
}
|
|
36951
|
+
}
|
|
36952
|
+
const inputArgs = inputArgsByCallID.get(input.callID);
|
|
36953
|
+
inputArgsByCallID.delete(input.callID);
|
|
36954
|
+
const delegation = isAgentDelegation(input.tool, inputArgs);
|
|
36955
|
+
if (delegation.isDelegation && (delegation.targetAgent === "reviewer" || delegation.targetAgent === "test_engineer")) {
|
|
36956
|
+
let currentPhase = 1;
|
|
36957
|
+
try {
|
|
36958
|
+
const plan = await loadPlan(process.cwd());
|
|
36959
|
+
if (plan) {
|
|
36960
|
+
const phaseString = extractCurrentPhaseFromPlan(plan);
|
|
36961
|
+
currentPhase = extractPhaseNumber(phaseString);
|
|
36962
|
+
}
|
|
36963
|
+
} catch {}
|
|
36964
|
+
const count = session.reviewerCallCount.get(currentPhase) ?? 0;
|
|
36965
|
+
session.reviewerCallCount.set(currentPhase, count + 1);
|
|
36966
|
+
}
|
|
36967
|
+
}
|
|
36242
36968
|
const window2 = getActiveWindow(input.sessionID);
|
|
36243
36969
|
if (!window2)
|
|
36244
36970
|
return;
|
|
@@ -36260,6 +36986,99 @@ function createGuardrailsHooks(config3) {
|
|
|
36260
36986
|
if (!sessionId) {
|
|
36261
36987
|
return;
|
|
36262
36988
|
}
|
|
36989
|
+
const session = swarmState.agentSessions.get(sessionId);
|
|
36990
|
+
const activeAgent = swarmState.activeAgent.get(sessionId);
|
|
36991
|
+
const isArchitectSession = activeAgent ? stripKnownSwarmPrefix(activeAgent) === ORCHESTRATOR_NAME : session ? stripKnownSwarmPrefix(session.agentName) === ORCHESTRATOR_NAME : false;
|
|
36992
|
+
if (isArchitectSession && session && session.architectWriteCount > 0) {
|
|
36993
|
+
const textPart2 = lastMessage.parts.find((part) => part.type === "text" && typeof part.text === "string");
|
|
36994
|
+
if (textPart2) {
|
|
36995
|
+
textPart2.text = `\u26A0\uFE0F SELF-CODING DETECTED: You have used ${session.architectWriteCount} write-class tool(s) directly on non-.swarm/ files.
|
|
36996
|
+
` + `Rule 1 requires ALL coding to be delegated to @coder.
|
|
36997
|
+
` + `If you have not exhausted QA_RETRY_LIMIT coder failures on this task, STOP and delegate.
|
|
36998
|
+
|
|
36999
|
+
` + textPart2.text;
|
|
37000
|
+
}
|
|
37001
|
+
}
|
|
37002
|
+
if (isArchitectSession && session && session.selfFixAttempted && session.lastGateFailure && Date.now() - session.lastGateFailure.timestamp < 120000) {
|
|
37003
|
+
const textPart2 = lastMessage.parts.find((part) => part.type === "text" && typeof part.text === "string");
|
|
37004
|
+
if (textPart2 && !textPart2.text.includes("SELF-FIX DETECTED")) {
|
|
37005
|
+
textPart2.text = `\u26A0\uFE0F SELF-FIX DETECTED: Gate '${session.lastGateFailure.tool}' failed on task ${session.lastGateFailure.taskId}.
|
|
37006
|
+
` + `You are now using a write tool instead of delegating to @coder.
|
|
37007
|
+
` + `GATE FAILURE RESPONSE RULES require: return to coder with structured rejection.
|
|
37008
|
+
` + `Do NOT fix gate failures yourself.
|
|
37009
|
+
|
|
37010
|
+
` + textPart2.text;
|
|
37011
|
+
session.selfFixAttempted = false;
|
|
37012
|
+
}
|
|
37013
|
+
}
|
|
37014
|
+
const isArchitectSessionForGates = activeAgent ? stripKnownSwarmPrefix(activeAgent) === ORCHESTRATOR_NAME : session ? stripKnownSwarmPrefix(session.agentName) === ORCHESTRATOR_NAME : false;
|
|
37015
|
+
if (isArchitectSessionForGates && session && session.gateLog.size > 0 && !session.partialGateWarningIssued) {
|
|
37016
|
+
const taskId = `${sessionId}:current`;
|
|
37017
|
+
const gates = session.gateLog.get(taskId);
|
|
37018
|
+
if (gates) {
|
|
37019
|
+
const REQUIRED_GATES = [
|
|
37020
|
+
"diff",
|
|
37021
|
+
"syntax_check",
|
|
37022
|
+
"placeholder_scan",
|
|
37023
|
+
"lint",
|
|
37024
|
+
"pre_check_batch"
|
|
37025
|
+
];
|
|
37026
|
+
const missingGates = [];
|
|
37027
|
+
for (const gate of REQUIRED_GATES) {
|
|
37028
|
+
if (!gates.has(gate)) {
|
|
37029
|
+
missingGates.push(gate);
|
|
37030
|
+
}
|
|
37031
|
+
}
|
|
37032
|
+
let currentPhaseForCheck = 1;
|
|
37033
|
+
try {
|
|
37034
|
+
const plan = await loadPlan(process.cwd());
|
|
37035
|
+
if (plan) {
|
|
37036
|
+
const phaseString = extractCurrentPhaseFromPlan(plan);
|
|
37037
|
+
currentPhaseForCheck = extractPhaseNumber(phaseString);
|
|
37038
|
+
}
|
|
37039
|
+
} catch {}
|
|
37040
|
+
const hasReviewerDelegation = (session.reviewerCallCount.get(currentPhaseForCheck) ?? 0) > 0;
|
|
37041
|
+
if (missingGates.length > 0 || !hasReviewerDelegation) {
|
|
37042
|
+
const textPart2 = lastMessage.parts.find((part) => part.type === "text" && typeof part.text === "string");
|
|
37043
|
+
if (textPart2 && !textPart2.text.includes("PARTIAL GATE VIOLATION")) {
|
|
37044
|
+
const missing = [...missingGates];
|
|
37045
|
+
if (!hasReviewerDelegation) {
|
|
37046
|
+
missing.push("reviewer/test_engineer (no delegations this phase)");
|
|
37047
|
+
}
|
|
37048
|
+
session.partialGateWarningIssued = true;
|
|
37049
|
+
textPart2.text = `\u26A0\uFE0F PARTIAL GATE VIOLATION: Task may be marked complete but missing gates: [${missing.join(", ")}].
|
|
37050
|
+
` + `The QA gate is ALL steps or NONE. Revert any \u2713 marks and run the missing gates.
|
|
37051
|
+
|
|
37052
|
+
` + textPart2.text;
|
|
37053
|
+
}
|
|
37054
|
+
}
|
|
37055
|
+
}
|
|
37056
|
+
}
|
|
37057
|
+
if (isArchitectSessionForGates && session && session.catastrophicPhaseWarnings) {
|
|
37058
|
+
try {
|
|
37059
|
+
const plan = await loadPlan(process.cwd());
|
|
37060
|
+
if (plan?.phases) {
|
|
37061
|
+
for (const phase of plan.phases) {
|
|
37062
|
+
if (phase.status === "complete") {
|
|
37063
|
+
const phaseNum = phase.id;
|
|
37064
|
+
if (!session.catastrophicPhaseWarnings.has(phaseNum)) {
|
|
37065
|
+
const reviewerCount = session.reviewerCallCount.get(phaseNum) ?? 0;
|
|
37066
|
+
if (reviewerCount === 0) {
|
|
37067
|
+
session.catastrophicPhaseWarnings.add(phaseNum);
|
|
37068
|
+
const textPart2 = lastMessage.parts.find((part) => part.type === "text" && typeof part.text === "string");
|
|
37069
|
+
if (textPart2 && !textPart2.text.includes("CATASTROPHIC VIOLATION")) {
|
|
37070
|
+
textPart2.text = `[CATASTROPHIC VIOLATION: Phase ${phaseNum} completed with ZERO reviewer delegations.` + ` Every coder task requires reviewer approval. Recommend retrospective review of all Phase ${phaseNum} tasks.]
|
|
37071
|
+
|
|
37072
|
+
` + textPart2.text;
|
|
37073
|
+
}
|
|
37074
|
+
break;
|
|
37075
|
+
}
|
|
37076
|
+
}
|
|
37077
|
+
}
|
|
37078
|
+
}
|
|
37079
|
+
}
|
|
37080
|
+
} catch {}
|
|
37081
|
+
}
|
|
36263
37082
|
const targetWindow = getActiveWindow(sessionId);
|
|
36264
37083
|
if (!targetWindow || !targetWindow.warningIssued && !targetWindow.hardLimitHit) {
|
|
36265
37084
|
return;
|
|
@@ -36292,6 +37111,54 @@ function hashArgs(args2) {
|
|
|
36292
37111
|
return 0;
|
|
36293
37112
|
}
|
|
36294
37113
|
}
|
|
37114
|
+
// src/hooks/messages-transform.ts
|
|
37115
|
+
function consolidateSystemMessages(messages) {
|
|
37116
|
+
if (messages.length > 0 && messages[0].role === "system" && messages[0].content !== undefined && typeof messages[0].content === "string" && messages[0].content.trim().length > 0) {
|
|
37117
|
+
const systemMessageCount = messages.filter((m) => m.role === "system" && typeof m.content === "string" && m.content.trim().length > 0 && m.tool_call_id === undefined && m.name === undefined).length;
|
|
37118
|
+
if (systemMessageCount === 1) {
|
|
37119
|
+
return [...messages];
|
|
37120
|
+
}
|
|
37121
|
+
}
|
|
37122
|
+
const systemMessageIndices = [];
|
|
37123
|
+
const systemContents = [];
|
|
37124
|
+
for (let i2 = 0;i2 < messages.length; i2++) {
|
|
37125
|
+
const message = messages[i2];
|
|
37126
|
+
if (message.role !== "system") {
|
|
37127
|
+
continue;
|
|
37128
|
+
}
|
|
37129
|
+
if (message.tool_call_id !== undefined || message.name !== undefined) {
|
|
37130
|
+
continue;
|
|
37131
|
+
}
|
|
37132
|
+
if (typeof message.content !== "string") {
|
|
37133
|
+
continue;
|
|
37134
|
+
}
|
|
37135
|
+
const trimmedContent = message.content.trim();
|
|
37136
|
+
if (trimmedContent.length === 0) {
|
|
37137
|
+
continue;
|
|
37138
|
+
}
|
|
37139
|
+
systemMessageIndices.push(i2);
|
|
37140
|
+
systemContents.push(trimmedContent);
|
|
37141
|
+
}
|
|
37142
|
+
if (systemContents.length === 0) {
|
|
37143
|
+
return [...messages];
|
|
37144
|
+
}
|
|
37145
|
+
const mergedSystemContent = systemContents.join(`
|
|
37146
|
+
|
|
37147
|
+
`);
|
|
37148
|
+
const result = [];
|
|
37149
|
+
const firstSystemMessage = messages[systemMessageIndices[0]];
|
|
37150
|
+
result.push({
|
|
37151
|
+
role: "system",
|
|
37152
|
+
content: mergedSystemContent,
|
|
37153
|
+
...Object.fromEntries(Object.entries(firstSystemMessage).filter(([key]) => key !== "role" && key !== "content"))
|
|
37154
|
+
});
|
|
37155
|
+
for (let i2 = 0;i2 < messages.length; i2++) {
|
|
37156
|
+
if (!systemMessageIndices.includes(i2)) {
|
|
37157
|
+
result.push({ ...messages[i2] });
|
|
37158
|
+
}
|
|
37159
|
+
}
|
|
37160
|
+
return result;
|
|
37161
|
+
}
|
|
36295
37162
|
// src/hooks/phase-monitor.ts
|
|
36296
37163
|
init_manager2();
|
|
36297
37164
|
init_utils2();
|
|
@@ -36301,7 +37168,7 @@ function createPhaseMonitorHook(directory, preflightManager) {
|
|
|
36301
37168
|
const plan = await loadPlan(directory);
|
|
36302
37169
|
if (!plan)
|
|
36303
37170
|
return;
|
|
36304
|
-
const currentPhase = plan.current_phase;
|
|
37171
|
+
const currentPhase = plan.current_phase ?? 1;
|
|
36305
37172
|
if (lastKnownPhase === null) {
|
|
36306
37173
|
lastKnownPhase = currentPhase;
|
|
36307
37174
|
return;
|
|
@@ -36318,9 +37185,22 @@ function createPhaseMonitorHook(directory, preflightManager) {
|
|
|
36318
37185
|
return safeHook(handler);
|
|
36319
37186
|
}
|
|
36320
37187
|
// src/hooks/pipeline-tracker.ts
|
|
37188
|
+
init_manager2();
|
|
36321
37189
|
init_utils2();
|
|
36322
|
-
|
|
36323
|
-
|
|
37190
|
+
function parsePhaseNumber(phaseString) {
|
|
37191
|
+
if (!phaseString)
|
|
37192
|
+
return null;
|
|
37193
|
+
const match = phaseString.match(/^Phase (\d+):/);
|
|
37194
|
+
if (match) {
|
|
37195
|
+
return parseInt(match[1], 10);
|
|
37196
|
+
}
|
|
37197
|
+
return null;
|
|
37198
|
+
}
|
|
37199
|
+
function buildPhaseReminder(phaseNumber) {
|
|
37200
|
+
const phaseHeader = phaseNumber !== null ? ` (Phase ${phaseNumber})` : "";
|
|
37201
|
+
const complianceHeader = phaseNumber !== null ? `COMPLIANCE CHECK (Phase ${phaseNumber}):` : "COMPLIANCE CHECK:";
|
|
37202
|
+
return `<swarm_reminder>
|
|
37203
|
+
\u26A0\uFE0F ARCHITECT WORKFLOW REMINDER${phaseHeader}:
|
|
36324
37204
|
1. ANALYZE \u2192 Identify domains, create initial spec
|
|
36325
37205
|
2. SME_CONSULTATION \u2192 Delegate to @sme (one domain per call, max 3 calls)
|
|
36326
37206
|
3. COLLATE \u2192 Synthesize SME outputs into unified spec
|
|
@@ -36333,7 +37213,15 @@ DELEGATION RULES:
|
|
|
36333
37213
|
- SME: ONE domain per call (serial), max 3 per phase
|
|
36334
37214
|
- Reviewer: Specify CHECK dimensions relevant to the change
|
|
36335
37215
|
- Always wait for response before next delegation
|
|
37216
|
+
|
|
37217
|
+
${complianceHeader}
|
|
37218
|
+
- Reviewer delegation is MANDATORY for every coder task.
|
|
37219
|
+
- pre_check_batch is NOT a substitute for reviewer.
|
|
37220
|
+
- Stage A (tools) + Stage B (agents) = BOTH required.
|
|
37221
|
+
${phaseNumber !== null && phaseNumber >= 4 ? `
|
|
37222
|
+
\u26A0\uFE0F You are in Phase ${phaseNumber}. Compliance degrades with time. Do not skip reviewer or test_engineer.` : ""}
|
|
36336
37223
|
</swarm_reminder>`;
|
|
37224
|
+
}
|
|
36337
37225
|
function createPipelineTrackerHook(config3) {
|
|
36338
37226
|
const enabled = config3.inject_phase_reminders !== false;
|
|
36339
37227
|
if (!enabled) {
|
|
@@ -36362,8 +37250,19 @@ function createPipelineTrackerHook(config3) {
|
|
|
36362
37250
|
const textPartIndex = lastUserMessage.parts.findIndex((p) => p?.type === "text" && p.text !== undefined);
|
|
36363
37251
|
if (textPartIndex === -1)
|
|
36364
37252
|
return;
|
|
37253
|
+
let phaseNumber = null;
|
|
37254
|
+
try {
|
|
37255
|
+
const plan = await loadPlan(process.cwd());
|
|
37256
|
+
if (plan) {
|
|
37257
|
+
const phaseString = extractCurrentPhaseFromPlan(plan);
|
|
37258
|
+
phaseNumber = parsePhaseNumber(phaseString);
|
|
37259
|
+
}
|
|
37260
|
+
} catch {
|
|
37261
|
+
phaseNumber = null;
|
|
37262
|
+
}
|
|
37263
|
+
const phaseReminder = buildPhaseReminder(phaseNumber);
|
|
36365
37264
|
const originalText = lastUserMessage.parts[textPartIndex].text ?? "";
|
|
36366
|
-
lastUserMessage.parts[textPartIndex].text = `${
|
|
37265
|
+
lastUserMessage.parts[textPartIndex].text = `${phaseReminder}
|
|
36367
37266
|
|
|
36368
37267
|
---
|
|
36369
37268
|
|
|
@@ -36373,14 +37272,14 @@ ${originalText}`;
|
|
|
36373
37272
|
}
|
|
36374
37273
|
// src/hooks/system-enhancer.ts
|
|
36375
37274
|
import * as fs11 from "fs";
|
|
36376
|
-
import * as
|
|
37275
|
+
import * as path17 from "path";
|
|
36377
37276
|
init_manager2();
|
|
36378
37277
|
|
|
36379
37278
|
// src/services/decision-drift-analyzer.ts
|
|
36380
37279
|
init_utils2();
|
|
36381
37280
|
init_manager2();
|
|
36382
37281
|
import * as fs10 from "fs";
|
|
36383
|
-
import * as
|
|
37282
|
+
import * as path16 from "path";
|
|
36384
37283
|
var DEFAULT_DRIFT_CONFIG = {
|
|
36385
37284
|
staleThresholdPhases: 1,
|
|
36386
37285
|
detectContradictions: true,
|
|
@@ -36534,7 +37433,7 @@ async function analyzeDecisionDrift(directory, config3 = {}) {
|
|
|
36534
37433
|
currentPhase = legacyPhase;
|
|
36535
37434
|
}
|
|
36536
37435
|
}
|
|
36537
|
-
const contextPath =
|
|
37436
|
+
const contextPath = path16.join(directory, ".swarm", "context.md");
|
|
36538
37437
|
let contextContent = "";
|
|
36539
37438
|
try {
|
|
36540
37439
|
if (fs10.existsSync(contextPath)) {
|
|
@@ -36639,7 +37538,7 @@ function formatDriftForContext(result) {
|
|
|
36639
37538
|
const maxLength = 600;
|
|
36640
37539
|
let summary = result.summary;
|
|
36641
37540
|
if (summary.length > maxLength) {
|
|
36642
|
-
summary = summary.substring(0, maxLength - 3)
|
|
37541
|
+
summary = `${summary.substring(0, maxLength - 3)}...`;
|
|
36643
37542
|
}
|
|
36644
37543
|
return summary;
|
|
36645
37544
|
}
|
|
@@ -36738,30 +37637,30 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
36738
37637
|
const contextContent = await readSwarmFileAsync(directory, "context.md");
|
|
36739
37638
|
const scoringEnabled = config3.context_budget?.scoring?.enabled === true;
|
|
36740
37639
|
if (!scoringEnabled) {
|
|
36741
|
-
|
|
37640
|
+
let plan2 = null;
|
|
37641
|
+
try {
|
|
37642
|
+
plan2 = await loadPlan(directory);
|
|
37643
|
+
} catch (error93) {
|
|
37644
|
+
warn(`Failed to load plan: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
|
37645
|
+
}
|
|
37646
|
+
const mode = await detectArchitectMode(directory);
|
|
37647
|
+
let planContent = null;
|
|
37648
|
+
let phaseHeader = "";
|
|
36742
37649
|
if (plan2 && plan2.migration_status !== "migration_failed") {
|
|
36743
|
-
|
|
36744
|
-
|
|
36745
|
-
tryInject(`[SWARM CONTEXT] Current phase: ${currentPhase2}`);
|
|
36746
|
-
}
|
|
36747
|
-
const currentTask2 = extractCurrentTaskFromPlan(plan2);
|
|
36748
|
-
if (currentTask2) {
|
|
36749
|
-
tryInject(`[SWARM CONTEXT] Current task: ${currentTask2}`);
|
|
36750
|
-
}
|
|
37650
|
+
phaseHeader = extractCurrentPhaseFromPlan(plan2) || "";
|
|
37651
|
+
planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
36751
37652
|
} else {
|
|
36752
|
-
|
|
36753
|
-
|
|
36754
|
-
|
|
36755
|
-
|
|
36756
|
-
|
|
36757
|
-
}
|
|
36758
|
-
const currentTask2 = extractCurrentTask(planContent);
|
|
36759
|
-
if (currentTask2) {
|
|
36760
|
-
tryInject(`[SWARM CONTEXT] Current task: ${currentTask2}`);
|
|
36761
|
-
}
|
|
36762
|
-
}
|
|
37653
|
+
planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
37654
|
+
phaseHeader = planContent ? extractCurrentPhase(planContent) || "" : "";
|
|
37655
|
+
}
|
|
37656
|
+
if (phaseHeader) {
|
|
37657
|
+
tryInject(`[SWARM CONTEXT] Phase: ${phaseHeader}`);
|
|
36763
37658
|
}
|
|
36764
|
-
if (
|
|
37659
|
+
if (mode !== "DISCOVER" && planContent) {
|
|
37660
|
+
const planCursor = extractPlanCursor(planContent);
|
|
37661
|
+
tryInject(planCursor);
|
|
37662
|
+
}
|
|
37663
|
+
if (mode !== "DISCOVER" && contextContent) {
|
|
36765
37664
|
const decisions = extractDecisions(contextContent, 200);
|
|
36766
37665
|
if (decisions) {
|
|
36767
37666
|
tryInject(`[SWARM CONTEXT] Key decisions: ${decisions}`);
|
|
@@ -36795,28 +37694,30 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
36795
37694
|
if (config3.secretscan?.enabled === false) {
|
|
36796
37695
|
tryInject("[SWARM CONFIG] Secretscan gate is DISABLED. Skip secretscan in QA sequence.");
|
|
36797
37696
|
}
|
|
36798
|
-
|
|
36799
|
-
|
|
36800
|
-
|
|
36801
|
-
|
|
36802
|
-
if (
|
|
36803
|
-
|
|
36804
|
-
|
|
36805
|
-
|
|
37697
|
+
if (mode !== "DISCOVER") {
|
|
37698
|
+
const sessionId_preflight = _input.sessionID;
|
|
37699
|
+
const activeAgent_preflight = swarmState.activeAgent.get(sessionId_preflight ?? "");
|
|
37700
|
+
const isArchitectForPreflight = !activeAgent_preflight || stripKnownSwarmPrefix(activeAgent_preflight) === "architect";
|
|
37701
|
+
if (isArchitectForPreflight) {
|
|
37702
|
+
if (config3.pipeline?.parallel_precheck !== false) {
|
|
37703
|
+
tryInject("[SWARM HINT] Parallel pre-check enabled: call pre_check_batch(files, directory) after lint --fix and build_check to run lint:check + secretscan + sast_scan + quality_budget concurrently (max 4 parallel). Check gates_passed before calling @reviewer.");
|
|
37704
|
+
} else {
|
|
37705
|
+
tryInject("[SWARM HINT] Parallel pre-check disabled: run lint:check \u2192 secretscan \u2192 sast_scan \u2192 quality_budget sequentially.");
|
|
37706
|
+
}
|
|
36806
37707
|
}
|
|
36807
37708
|
}
|
|
36808
37709
|
const sessionId_retro = _input.sessionID;
|
|
36809
37710
|
const activeAgent_retro = swarmState.activeAgent.get(sessionId_retro ?? "");
|
|
36810
|
-
const
|
|
36811
|
-
if (
|
|
37711
|
+
const isArchitect2 = !activeAgent_retro || stripKnownSwarmPrefix(activeAgent_retro) === "architect";
|
|
37712
|
+
if (isArchitect2) {
|
|
36812
37713
|
try {
|
|
36813
|
-
const evidenceDir =
|
|
37714
|
+
const evidenceDir = path17.join(directory, ".swarm", "evidence");
|
|
36814
37715
|
if (fs11.existsSync(evidenceDir)) {
|
|
36815
37716
|
const files = fs11.readdirSync(evidenceDir).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
36816
37717
|
for (const file3 of files.slice(0, 5)) {
|
|
36817
37718
|
let content;
|
|
36818
37719
|
try {
|
|
36819
|
-
content = JSON.parse(fs11.readFileSync(
|
|
37720
|
+
content = JSON.parse(fs11.readFileSync(path17.join(evidenceDir, file3), "utf-8"));
|
|
36820
37721
|
} catch {
|
|
36821
37722
|
continue;
|
|
36822
37723
|
}
|
|
@@ -36837,7 +37738,7 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
36837
37738
|
if (retroHint.length <= 800) {
|
|
36838
37739
|
tryInject(retroHint);
|
|
36839
37740
|
} else {
|
|
36840
|
-
tryInject(retroHint.substring(0, 800)
|
|
37741
|
+
tryInject(`${retroHint.substring(0, 800)}...`);
|
|
36841
37742
|
}
|
|
36842
37743
|
}
|
|
36843
37744
|
break;
|
|
@@ -36845,68 +37746,80 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
36845
37746
|
}
|
|
36846
37747
|
}
|
|
36847
37748
|
} catch {}
|
|
36848
|
-
|
|
36849
|
-
|
|
36850
|
-
|
|
36851
|
-
|
|
36852
|
-
|
|
36853
|
-
|
|
36854
|
-
|
|
36855
|
-
|
|
36856
|
-
|
|
36857
|
-
|
|
36858
|
-
|
|
36859
|
-
|
|
36860
|
-
|
|
36861
|
-
|
|
36862
|
-
|
|
36863
|
-
|
|
36864
|
-
|
|
36865
|
-
|
|
36866
|
-
|
|
36867
|
-
|
|
37749
|
+
if (mode !== "DISCOVER") {
|
|
37750
|
+
const compactionConfig = config3.compaction_advisory;
|
|
37751
|
+
if (compactionConfig?.enabled !== false && sessionId_retro) {
|
|
37752
|
+
const session = swarmState.agentSessions.get(sessionId_retro);
|
|
37753
|
+
if (session) {
|
|
37754
|
+
const totalToolCalls = Array.from(swarmState.toolAggregates.values()).reduce((sum, agg) => sum + agg.count, 0);
|
|
37755
|
+
const thresholds = compactionConfig?.thresholds ?? [
|
|
37756
|
+
50,
|
|
37757
|
+
75,
|
|
37758
|
+
100,
|
|
37759
|
+
125,
|
|
37760
|
+
150
|
|
37761
|
+
];
|
|
37762
|
+
const lastHint = session.lastCompactionHint || 0;
|
|
37763
|
+
for (const threshold of thresholds) {
|
|
37764
|
+
if (totalToolCalls >= threshold && lastHint < threshold) {
|
|
37765
|
+
const totalToolCallsPlaceholder = "$" + "{totalToolCalls}";
|
|
37766
|
+
const messageTemplate = compactionConfig?.message ?? `[SWARM HINT] Session has ${totalToolCallsPlaceholder} tool calls. Consider compacting at next phase boundary to maintain context quality.`;
|
|
37767
|
+
const message = messageTemplate.replace(totalToolCallsPlaceholder, String(totalToolCalls));
|
|
37768
|
+
tryInject(message);
|
|
37769
|
+
session.lastCompactionHint = threshold;
|
|
37770
|
+
break;
|
|
37771
|
+
}
|
|
36868
37772
|
}
|
|
36869
37773
|
}
|
|
36870
37774
|
}
|
|
36871
37775
|
}
|
|
36872
37776
|
}
|
|
36873
|
-
|
|
36874
|
-
|
|
36875
|
-
|
|
36876
|
-
|
|
36877
|
-
|
|
36878
|
-
|
|
36879
|
-
|
|
36880
|
-
|
|
36881
|
-
|
|
36882
|
-
|
|
36883
|
-
|
|
37777
|
+
if (mode !== "DISCOVER") {
|
|
37778
|
+
const automationCapabilities = config3.automation?.capabilities;
|
|
37779
|
+
if (automationCapabilities?.decision_drift_detection === true && _input.sessionID) {
|
|
37780
|
+
const activeAgentForDrift = swarmState.activeAgent.get(_input.sessionID);
|
|
37781
|
+
const isArchitectForDrift = !activeAgentForDrift || stripKnownSwarmPrefix(activeAgentForDrift) === "architect";
|
|
37782
|
+
if (isArchitectForDrift) {
|
|
37783
|
+
try {
|
|
37784
|
+
const driftResult = await analyzeDecisionDrift(directory);
|
|
37785
|
+
if (driftResult.hasDrift) {
|
|
37786
|
+
const driftText = formatDriftForContext(driftResult);
|
|
37787
|
+
if (driftText) {
|
|
37788
|
+
tryInject(driftText);
|
|
37789
|
+
}
|
|
36884
37790
|
}
|
|
36885
|
-
}
|
|
36886
|
-
}
|
|
37791
|
+
} catch {}
|
|
37792
|
+
}
|
|
36887
37793
|
}
|
|
36888
37794
|
}
|
|
36889
37795
|
return;
|
|
36890
37796
|
}
|
|
37797
|
+
const mode_b = await detectArchitectMode(directory);
|
|
36891
37798
|
const userScoringConfig = config3.context_budget?.scoring;
|
|
36892
37799
|
const candidates = [];
|
|
36893
37800
|
let idCounter = 0;
|
|
37801
|
+
let planContentForCursor = null;
|
|
36894
37802
|
const effectiveConfig = userScoringConfig?.weights ? {
|
|
36895
37803
|
...DEFAULT_SCORING_CONFIG,
|
|
36896
37804
|
...userScoringConfig,
|
|
36897
37805
|
weights: userScoringConfig.weights
|
|
36898
37806
|
} : DEFAULT_SCORING_CONFIG;
|
|
36899
|
-
|
|
37807
|
+
let plan = null;
|
|
37808
|
+
try {
|
|
37809
|
+
plan = await loadPlan(directory);
|
|
37810
|
+
} catch (error93) {
|
|
37811
|
+
warn(`Failed to load plan: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
|
37812
|
+
}
|
|
36900
37813
|
let currentPhase = null;
|
|
36901
37814
|
let currentTask = null;
|
|
36902
37815
|
if (plan && plan.migration_status !== "migration_failed") {
|
|
36903
37816
|
currentPhase = extractCurrentPhaseFromPlan(plan);
|
|
36904
37817
|
currentTask = extractCurrentTaskFromPlan(plan);
|
|
36905
37818
|
} else {
|
|
36906
|
-
|
|
36907
|
-
if (
|
|
36908
|
-
currentPhase = extractCurrentPhase(
|
|
36909
|
-
currentTask = extractCurrentTask(
|
|
37819
|
+
planContentForCursor = await readSwarmFileAsync(directory, "plan.md");
|
|
37820
|
+
if (planContentForCursor) {
|
|
37821
|
+
currentPhase = extractCurrentPhase(planContentForCursor);
|
|
37822
|
+
currentTask = extractCurrentTask(planContentForCursor);
|
|
36910
37823
|
}
|
|
36911
37824
|
}
|
|
36912
37825
|
if (currentPhase) {
|
|
@@ -36934,6 +37847,17 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
36934
37847
|
}
|
|
36935
37848
|
});
|
|
36936
37849
|
}
|
|
37850
|
+
if (planContentForCursor) {
|
|
37851
|
+
const planCursor = extractPlanCursor(planContentForCursor);
|
|
37852
|
+
candidates.push({
|
|
37853
|
+
id: `candidate-${idCounter++}`,
|
|
37854
|
+
kind: "phase",
|
|
37855
|
+
text: planCursor,
|
|
37856
|
+
tokens: estimateTokens(planCursor),
|
|
37857
|
+
priority: 1,
|
|
37858
|
+
metadata: { contentType: "markdown" }
|
|
37859
|
+
});
|
|
37860
|
+
}
|
|
36937
37861
|
if (contextContent) {
|
|
36938
37862
|
const decisions = extractDecisions(contextContent, 200);
|
|
36939
37863
|
if (decisions) {
|
|
@@ -37050,13 +37974,13 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
37050
37974
|
const isArchitect_b = !activeAgent_retro_b || stripKnownSwarmPrefix(activeAgent_retro_b) === "architect";
|
|
37051
37975
|
if (isArchitect_b) {
|
|
37052
37976
|
try {
|
|
37053
|
-
const evidenceDir_b =
|
|
37977
|
+
const evidenceDir_b = path17.join(directory, ".swarm", "evidence");
|
|
37054
37978
|
if (fs11.existsSync(evidenceDir_b)) {
|
|
37055
37979
|
const files_b = fs11.readdirSync(evidenceDir_b).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
37056
37980
|
for (const file3 of files_b.slice(0, 5)) {
|
|
37057
37981
|
let content_b;
|
|
37058
37982
|
try {
|
|
37059
|
-
content_b = JSON.parse(fs11.readFileSync(
|
|
37983
|
+
content_b = JSON.parse(fs11.readFileSync(path17.join(evidenceDir_b, file3), "utf-8"));
|
|
37060
37984
|
} catch {
|
|
37061
37985
|
continue;
|
|
37062
37986
|
}
|
|
@@ -37074,7 +37998,7 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
37074
37998
|
}
|
|
37075
37999
|
if (hints_b.length > 0) {
|
|
37076
38000
|
const retroHint_b = `[SWARM RETROSPECTIVE] From Phase ${retro_b.phase_number}: ${hints_b.join(" ")}`;
|
|
37077
|
-
const retroText = retroHint_b.length <= 800 ? retroHint_b : retroHint_b.substring(0, 800)
|
|
38001
|
+
const retroText = retroHint_b.length <= 800 ? retroHint_b : `${retroHint_b.substring(0, 800)}...`;
|
|
37078
38002
|
candidates.push({
|
|
37079
38003
|
id: `candidate-${idCounter++}`,
|
|
37080
38004
|
kind: "phase",
|
|
@@ -37089,33 +38013,36 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
37089
38013
|
}
|
|
37090
38014
|
}
|
|
37091
38015
|
} catch {}
|
|
37092
|
-
|
|
37093
|
-
|
|
37094
|
-
|
|
37095
|
-
|
|
37096
|
-
|
|
37097
|
-
|
|
37098
|
-
|
|
37099
|
-
|
|
37100
|
-
|
|
37101
|
-
|
|
37102
|
-
|
|
37103
|
-
|
|
37104
|
-
|
|
37105
|
-
|
|
37106
|
-
|
|
37107
|
-
|
|
37108
|
-
|
|
37109
|
-
|
|
37110
|
-
|
|
37111
|
-
|
|
37112
|
-
|
|
37113
|
-
|
|
37114
|
-
|
|
37115
|
-
|
|
37116
|
-
|
|
37117
|
-
|
|
37118
|
-
|
|
38016
|
+
if (mode_b !== "DISCOVER") {
|
|
38017
|
+
const compactionConfig_b = config3.compaction_advisory;
|
|
38018
|
+
if (compactionConfig_b?.enabled !== false && sessionId_retro_b) {
|
|
38019
|
+
const session_b = swarmState.agentSessions.get(sessionId_retro_b);
|
|
38020
|
+
if (session_b) {
|
|
38021
|
+
const totalToolCalls_b = Array.from(swarmState.toolAggregates.values()).reduce((sum, agg) => sum + agg.count, 0);
|
|
38022
|
+
const thresholds_b = compactionConfig_b?.thresholds ?? [
|
|
38023
|
+
50,
|
|
38024
|
+
75,
|
|
38025
|
+
100,
|
|
38026
|
+
125,
|
|
38027
|
+
150
|
|
38028
|
+
];
|
|
38029
|
+
const lastHint_b = session_b.lastCompactionHint || 0;
|
|
38030
|
+
for (const threshold of thresholds_b) {
|
|
38031
|
+
if (totalToolCalls_b >= threshold && lastHint_b < threshold) {
|
|
38032
|
+
const totalToolCallsPlaceholder_b = "$" + "{totalToolCalls}";
|
|
38033
|
+
const messageTemplate_b = compactionConfig_b?.message ?? `[SWARM HINT] Session has ${totalToolCallsPlaceholder_b} tool calls. Consider compacting at next phase boundary to maintain context quality.`;
|
|
38034
|
+
const compactionText = messageTemplate_b.replace(totalToolCallsPlaceholder_b, String(totalToolCalls_b));
|
|
38035
|
+
candidates.push({
|
|
38036
|
+
id: `candidate-${idCounter++}`,
|
|
38037
|
+
kind: "phase",
|
|
38038
|
+
text: compactionText,
|
|
38039
|
+
tokens: estimateTokens(compactionText),
|
|
38040
|
+
priority: 1,
|
|
38041
|
+
metadata: { contentType: "prose" }
|
|
38042
|
+
});
|
|
38043
|
+
session_b.lastCompactionHint = threshold;
|
|
38044
|
+
break;
|
|
38045
|
+
}
|
|
37119
38046
|
}
|
|
37120
38047
|
}
|
|
37121
38048
|
}
|
|
@@ -37190,6 +38117,26 @@ ${activitySection}`;
|
|
|
37190
38117
|
}
|
|
37191
38118
|
return contextSummary;
|
|
37192
38119
|
}
|
|
38120
|
+
async function detectArchitectMode(directory) {
|
|
38121
|
+
try {
|
|
38122
|
+
const plan = await loadPlan(directory);
|
|
38123
|
+
if (!plan) {
|
|
38124
|
+
return "DISCOVER";
|
|
38125
|
+
}
|
|
38126
|
+
const hasActiveTask = plan.phases?.some((phase) => phase.tasks?.some((task) => task.status === "in_progress")) ?? false;
|
|
38127
|
+
if (hasActiveTask) {
|
|
38128
|
+
return "EXECUTE";
|
|
38129
|
+
}
|
|
38130
|
+
const hasPendingTask = plan.phases?.some((phase) => phase.tasks?.some((task) => task.status === "pending")) ?? false;
|
|
38131
|
+
if (!hasPendingTask) {
|
|
38132
|
+
return "PHASE-WRAP";
|
|
38133
|
+
}
|
|
38134
|
+
return "PLAN";
|
|
38135
|
+
} catch (error93) {
|
|
38136
|
+
warn(`Failed to detect architect mode: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
|
38137
|
+
return "UNKNOWN";
|
|
38138
|
+
}
|
|
38139
|
+
}
|
|
37193
38140
|
// src/summaries/summarizer.ts
|
|
37194
38141
|
var HYSTERESIS_FACTOR = 1.25;
|
|
37195
38142
|
function detectContentType(output, toolName) {
|
|
@@ -37308,7 +38255,7 @@ function createSummary(output, toolName, summaryId, maxSummaryChars) {
|
|
|
37308
38255
|
}
|
|
37309
38256
|
}
|
|
37310
38257
|
if (preview.length > maxPreviewChars) {
|
|
37311
|
-
preview = preview.substring(0, maxPreviewChars - 3)
|
|
38258
|
+
preview = `${preview.substring(0, maxPreviewChars - 3)}...`;
|
|
37312
38259
|
}
|
|
37313
38260
|
return `${headerLine}
|
|
37314
38261
|
${preview}
|
|
@@ -37352,7 +38299,7 @@ init_dist();
|
|
|
37352
38299
|
// src/build/discovery.ts
|
|
37353
38300
|
init_dist();
|
|
37354
38301
|
import * as fs12 from "fs";
|
|
37355
|
-
import * as
|
|
38302
|
+
import * as path18 from "path";
|
|
37356
38303
|
var ECOSYSTEMS = [
|
|
37357
38304
|
{
|
|
37358
38305
|
ecosystem: "node",
|
|
@@ -37466,15 +38413,15 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
37466
38413
|
try {
|
|
37467
38414
|
const files = fs12.readdirSync(dir);
|
|
37468
38415
|
const matches = files.filter((f) => {
|
|
37469
|
-
const regex = new RegExp(
|
|
38416
|
+
const regex = new RegExp(`^${pattern.replace(/\*/g, ".*")}$`);
|
|
37470
38417
|
return regex.test(f);
|
|
37471
38418
|
});
|
|
37472
38419
|
if (matches.length > 0) {
|
|
37473
|
-
return
|
|
38420
|
+
return path18.join(dir, matches[0]);
|
|
37474
38421
|
}
|
|
37475
38422
|
} catch {}
|
|
37476
38423
|
} else {
|
|
37477
|
-
const filePath =
|
|
38424
|
+
const filePath = path18.join(workingDir, pattern);
|
|
37478
38425
|
if (fs12.existsSync(filePath)) {
|
|
37479
38426
|
return filePath;
|
|
37480
38427
|
}
|
|
@@ -37483,7 +38430,7 @@ function findBuildFiles(workingDir, patterns) {
|
|
|
37483
38430
|
return null;
|
|
37484
38431
|
}
|
|
37485
38432
|
function getRepoDefinedScripts(workingDir, scripts) {
|
|
37486
|
-
const packageJsonPath =
|
|
38433
|
+
const packageJsonPath = path18.join(workingDir, "package.json");
|
|
37487
38434
|
if (!fs12.existsSync(packageJsonPath)) {
|
|
37488
38435
|
return [];
|
|
37489
38436
|
}
|
|
@@ -37521,10 +38468,10 @@ function findAllBuildFiles(workingDir) {
|
|
|
37521
38468
|
for (const ecosystem of ECOSYSTEMS) {
|
|
37522
38469
|
for (const pattern of ecosystem.buildFiles) {
|
|
37523
38470
|
if (pattern.includes("*")) {
|
|
37524
|
-
const regex = new RegExp(
|
|
38471
|
+
const regex = new RegExp(`^${pattern.replace(/\*/g, ".*")}$`);
|
|
37525
38472
|
findFilesRecursive(workingDir, regex, allBuildFiles);
|
|
37526
38473
|
} else {
|
|
37527
|
-
const filePath =
|
|
38474
|
+
const filePath = path18.join(workingDir, pattern);
|
|
37528
38475
|
if (fs12.existsSync(filePath)) {
|
|
37529
38476
|
allBuildFiles.add(filePath);
|
|
37530
38477
|
}
|
|
@@ -37537,7 +38484,7 @@ function findFilesRecursive(dir, regex, results) {
|
|
|
37537
38484
|
try {
|
|
37538
38485
|
const entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
37539
38486
|
for (const entry of entries) {
|
|
37540
|
-
const fullPath =
|
|
38487
|
+
const fullPath = path18.join(dir, entry.name);
|
|
37541
38488
|
if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
|
|
37542
38489
|
findFilesRecursive(fullPath, regex, results);
|
|
37543
38490
|
} else if (entry.isFile() && regex.test(entry.name)) {
|
|
@@ -37549,7 +38496,7 @@ function findFilesRecursive(dir, regex, results) {
|
|
|
37549
38496
|
async function discoverBuildCommands(workingDir, options) {
|
|
37550
38497
|
const scope = options?.scope ?? "all";
|
|
37551
38498
|
const changedFiles = options?.changedFiles ?? [];
|
|
37552
|
-
const
|
|
38499
|
+
const _filesToCheck = filterByScope(workingDir, scope, changedFiles);
|
|
37553
38500
|
const commands = [];
|
|
37554
38501
|
const skipped = [];
|
|
37555
38502
|
for (const ecosystem of ECOSYSTEMS) {
|
|
@@ -37779,7 +38726,7 @@ var build_check = tool({
|
|
|
37779
38726
|
init_tool();
|
|
37780
38727
|
import { spawnSync } from "child_process";
|
|
37781
38728
|
import * as fs13 from "fs";
|
|
37782
|
-
import * as
|
|
38729
|
+
import * as path19 from "path";
|
|
37783
38730
|
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
|
|
37784
38731
|
var MAX_LABEL_LENGTH = 100;
|
|
37785
38732
|
var GIT_TIMEOUT_MS = 30000;
|
|
@@ -37830,7 +38777,7 @@ function validateLabel(label) {
|
|
|
37830
38777
|
return null;
|
|
37831
38778
|
}
|
|
37832
38779
|
function getCheckpointLogPath() {
|
|
37833
|
-
return
|
|
38780
|
+
return path19.join(process.cwd(), CHECKPOINT_LOG_PATH);
|
|
37834
38781
|
}
|
|
37835
38782
|
function readCheckpointLog() {
|
|
37836
38783
|
const logPath = getCheckpointLogPath();
|
|
@@ -37848,7 +38795,7 @@ function readCheckpointLog() {
|
|
|
37848
38795
|
}
|
|
37849
38796
|
function writeCheckpointLog(log2) {
|
|
37850
38797
|
const logPath = getCheckpointLogPath();
|
|
37851
|
-
const dir =
|
|
38798
|
+
const dir = path19.dirname(logPath);
|
|
37852
38799
|
if (!fs13.existsSync(dir)) {
|
|
37853
38800
|
fs13.mkdirSync(dir, { recursive: true });
|
|
37854
38801
|
}
|
|
@@ -38055,7 +39002,7 @@ var checkpoint = tool({
|
|
|
38055
39002
|
// src/tools/complexity-hotspots.ts
|
|
38056
39003
|
init_dist();
|
|
38057
39004
|
import * as fs14 from "fs";
|
|
38058
|
-
import * as
|
|
39005
|
+
import * as path20 from "path";
|
|
38059
39006
|
var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
|
|
38060
39007
|
var DEFAULT_DAYS = 90;
|
|
38061
39008
|
var DEFAULT_TOP_N = 20;
|
|
@@ -38198,7 +39145,7 @@ async function analyzeHotspots(days, topN, extensions) {
|
|
|
38198
39145
|
const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
|
|
38199
39146
|
const filteredChurn = new Map;
|
|
38200
39147
|
for (const [file3, count] of churnMap) {
|
|
38201
|
-
const ext =
|
|
39148
|
+
const ext = path20.extname(file3).toLowerCase();
|
|
38202
39149
|
if (extSet.has(ext)) {
|
|
38203
39150
|
filteredChurn.set(file3, count);
|
|
38204
39151
|
}
|
|
@@ -38209,7 +39156,7 @@ async function analyzeHotspots(days, topN, extensions) {
|
|
|
38209
39156
|
for (const [file3, churnCount] of filteredChurn) {
|
|
38210
39157
|
let fullPath = file3;
|
|
38211
39158
|
if (!fs14.existsSync(fullPath)) {
|
|
38212
|
-
fullPath =
|
|
39159
|
+
fullPath = path20.join(cwd, file3);
|
|
38213
39160
|
}
|
|
38214
39161
|
const complexity = getComplexityForFile(fullPath);
|
|
38215
39162
|
if (complexity !== null) {
|
|
@@ -38367,14 +39314,14 @@ function validateBase(base) {
|
|
|
38367
39314
|
function validatePaths(paths) {
|
|
38368
39315
|
if (!paths)
|
|
38369
39316
|
return null;
|
|
38370
|
-
for (const
|
|
38371
|
-
if (!
|
|
39317
|
+
for (const path21 of paths) {
|
|
39318
|
+
if (!path21 || path21.length === 0) {
|
|
38372
39319
|
return "empty path not allowed";
|
|
38373
39320
|
}
|
|
38374
|
-
if (
|
|
39321
|
+
if (path21.length > MAX_PATH_LENGTH) {
|
|
38375
39322
|
return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
|
|
38376
39323
|
}
|
|
38377
|
-
if (SHELL_METACHARACTERS2.test(
|
|
39324
|
+
if (SHELL_METACHARACTERS2.test(path21)) {
|
|
38378
39325
|
return "path contains shell metacharacters";
|
|
38379
39326
|
}
|
|
38380
39327
|
}
|
|
@@ -38389,7 +39336,7 @@ var diff = tool({
|
|
|
38389
39336
|
async execute(args2, _context) {
|
|
38390
39337
|
try {
|
|
38391
39338
|
const base = args2.base ?? "HEAD";
|
|
38392
|
-
const pathSpec = args2.paths?.length ?
|
|
39339
|
+
const pathSpec = args2.paths?.length ? `-- ${args2.paths.join(" ")}` : "";
|
|
38393
39340
|
const baseValidationError = validateBase(base);
|
|
38394
39341
|
if (baseValidationError) {
|
|
38395
39342
|
const errorResult = {
|
|
@@ -38418,11 +39365,11 @@ var diff = tool({
|
|
|
38418
39365
|
} else {
|
|
38419
39366
|
gitCmd = `git --no-pager diff ${base}`;
|
|
38420
39367
|
}
|
|
38421
|
-
const numstatOutput = execSync(gitCmd
|
|
39368
|
+
const numstatOutput = execSync(`${gitCmd} --numstat ${pathSpec}`, {
|
|
38422
39369
|
encoding: "utf-8",
|
|
38423
39370
|
timeout: DIFF_TIMEOUT_MS
|
|
38424
39371
|
});
|
|
38425
|
-
const fullDiffOutput = execSync(gitCmd
|
|
39372
|
+
const fullDiffOutput = execSync(`${gitCmd} -U3 ${pathSpec}`, {
|
|
38426
39373
|
encoding: "utf-8",
|
|
38427
39374
|
timeout: DIFF_TIMEOUT_MS,
|
|
38428
39375
|
maxBuffer: MAX_BUFFER_BYTES
|
|
@@ -38435,10 +39382,10 @@ var diff = tool({
|
|
|
38435
39382
|
continue;
|
|
38436
39383
|
const parts2 = line.split("\t");
|
|
38437
39384
|
if (parts2.length >= 3) {
|
|
38438
|
-
const additions = parseInt(parts2[0]) || 0;
|
|
38439
|
-
const deletions = parseInt(parts2[1]) || 0;
|
|
38440
|
-
const
|
|
38441
|
-
files.push({ path:
|
|
39385
|
+
const additions = parseInt(parts2[0], 10) || 0;
|
|
39386
|
+
const deletions = parseInt(parts2[1], 10) || 0;
|
|
39387
|
+
const path21 = parts2[2];
|
|
39388
|
+
files.push({ path: path21, additions, deletions });
|
|
38442
39389
|
}
|
|
38443
39390
|
}
|
|
38444
39391
|
const contractChanges = [];
|
|
@@ -38667,7 +39614,7 @@ Use these as DOMAIN values when delegating to @sme.`;
|
|
|
38667
39614
|
// src/tools/evidence-check.ts
|
|
38668
39615
|
init_dist();
|
|
38669
39616
|
import * as fs15 from "fs";
|
|
38670
|
-
import * as
|
|
39617
|
+
import * as path21 from "path";
|
|
38671
39618
|
var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
|
|
38672
39619
|
var MAX_EVIDENCE_FILES = 1000;
|
|
38673
39620
|
var EVIDENCE_DIR = ".swarm/evidence";
|
|
@@ -38690,16 +39637,15 @@ function validateRequiredTypes(input) {
|
|
|
38690
39637
|
return null;
|
|
38691
39638
|
}
|
|
38692
39639
|
function isPathWithinSwarm(filePath, cwd) {
|
|
38693
|
-
const normalizedCwd =
|
|
38694
|
-
const swarmPath =
|
|
38695
|
-
const normalizedPath =
|
|
39640
|
+
const normalizedCwd = path21.resolve(cwd);
|
|
39641
|
+
const swarmPath = path21.join(normalizedCwd, ".swarm");
|
|
39642
|
+
const normalizedPath = path21.resolve(filePath);
|
|
38696
39643
|
return normalizedPath.startsWith(swarmPath);
|
|
38697
39644
|
}
|
|
38698
39645
|
function parseCompletedTasks(planContent) {
|
|
38699
39646
|
const tasks = [];
|
|
38700
39647
|
const regex = /^-\s+\[x\]\s+(\d+\.\d+):\s+(.+)/gm;
|
|
38701
|
-
let match;
|
|
38702
|
-
while ((match = regex.exec(planContent)) !== null) {
|
|
39648
|
+
for (let match = regex.exec(planContent);match !== null; match = regex.exec(planContent)) {
|
|
38703
39649
|
const taskId = match[1];
|
|
38704
39650
|
let taskName = match[2].trim();
|
|
38705
39651
|
taskName = taskName.replace(/\s*\[(SMALL|MEDIUM|LARGE)\]\s*$/i, "").trim();
|
|
@@ -38707,7 +39653,7 @@ function parseCompletedTasks(planContent) {
|
|
|
38707
39653
|
}
|
|
38708
39654
|
return tasks;
|
|
38709
39655
|
}
|
|
38710
|
-
function readEvidenceFiles(evidenceDir,
|
|
39656
|
+
function readEvidenceFiles(evidenceDir, _cwd) {
|
|
38711
39657
|
const evidence = [];
|
|
38712
39658
|
if (!fs15.existsSync(evidenceDir) || !fs15.statSync(evidenceDir).isDirectory()) {
|
|
38713
39659
|
return evidence;
|
|
@@ -38723,10 +39669,10 @@ function readEvidenceFiles(evidenceDir, cwd) {
|
|
|
38723
39669
|
if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
|
|
38724
39670
|
continue;
|
|
38725
39671
|
}
|
|
38726
|
-
const filePath =
|
|
39672
|
+
const filePath = path21.join(evidenceDir, filename);
|
|
38727
39673
|
try {
|
|
38728
|
-
const resolvedPath =
|
|
38729
|
-
const evidenceDirResolved =
|
|
39674
|
+
const resolvedPath = path21.resolve(filePath);
|
|
39675
|
+
const evidenceDirResolved = path21.resolve(evidenceDir);
|
|
38730
39676
|
if (!resolvedPath.startsWith(evidenceDirResolved)) {
|
|
38731
39677
|
continue;
|
|
38732
39678
|
}
|
|
@@ -38780,7 +39726,7 @@ function analyzeGaps(completedTasks, evidence, requiredTypes) {
|
|
|
38780
39726
|
for (const task of completedTasks) {
|
|
38781
39727
|
const taskEvidence = evidenceByTask.get(task.taskId) || new Set;
|
|
38782
39728
|
const requiredSet = new Set(requiredTypes.map((t) => t.toLowerCase()));
|
|
38783
|
-
const
|
|
39729
|
+
const _presentSet = new Set([...taskEvidence].filter((t) => requiredSet.has(t.toLowerCase())));
|
|
38784
39730
|
const missing = [];
|
|
38785
39731
|
const present = [];
|
|
38786
39732
|
for (const reqType of requiredTypes) {
|
|
@@ -38833,7 +39779,7 @@ var evidence_check = tool({
|
|
|
38833
39779
|
return JSON.stringify(errorResult, null, 2);
|
|
38834
39780
|
}
|
|
38835
39781
|
const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
38836
|
-
const planPath =
|
|
39782
|
+
const planPath = path21.join(cwd, PLAN_FILE);
|
|
38837
39783
|
if (!isPathWithinSwarm(planPath, cwd)) {
|
|
38838
39784
|
const errorResult = {
|
|
38839
39785
|
error: "plan file path validation failed",
|
|
@@ -38865,7 +39811,7 @@ var evidence_check = tool({
|
|
|
38865
39811
|
};
|
|
38866
39812
|
return JSON.stringify(result2, null, 2);
|
|
38867
39813
|
}
|
|
38868
|
-
const evidenceDir =
|
|
39814
|
+
const evidenceDir = path21.join(cwd, EVIDENCE_DIR);
|
|
38869
39815
|
const evidence = readEvidenceFiles(evidenceDir, cwd);
|
|
38870
39816
|
const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
|
|
38871
39817
|
const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
|
|
@@ -38882,7 +39828,7 @@ var evidence_check = tool({
|
|
|
38882
39828
|
// src/tools/file-extractor.ts
|
|
38883
39829
|
init_tool();
|
|
38884
39830
|
import * as fs16 from "fs";
|
|
38885
|
-
import * as
|
|
39831
|
+
import * as path22 from "path";
|
|
38886
39832
|
var EXT_MAP = {
|
|
38887
39833
|
python: ".py",
|
|
38888
39834
|
py: ".py",
|
|
@@ -38960,12 +39906,12 @@ var extract_code_blocks = tool({
|
|
|
38960
39906
|
if (prefix) {
|
|
38961
39907
|
filename = `${prefix}_${filename}`;
|
|
38962
39908
|
}
|
|
38963
|
-
let filepath =
|
|
38964
|
-
const base =
|
|
38965
|
-
const ext =
|
|
39909
|
+
let filepath = path22.join(targetDir, filename);
|
|
39910
|
+
const base = path22.basename(filepath, path22.extname(filepath));
|
|
39911
|
+
const ext = path22.extname(filepath);
|
|
38966
39912
|
let counter = 1;
|
|
38967
39913
|
while (fs16.existsSync(filepath)) {
|
|
38968
|
-
filepath =
|
|
39914
|
+
filepath = path22.join(targetDir, `${base}_${counter}${ext}`);
|
|
38969
39915
|
counter++;
|
|
38970
39916
|
}
|
|
38971
39917
|
try {
|
|
@@ -38998,7 +39944,7 @@ init_dist();
|
|
|
38998
39944
|
var GITINGEST_TIMEOUT_MS = 1e4;
|
|
38999
39945
|
var GITINGEST_MAX_RESPONSE_BYTES = 5242880;
|
|
39000
39946
|
var GITINGEST_MAX_RETRIES = 2;
|
|
39001
|
-
var delay = (ms) => new Promise((
|
|
39947
|
+
var delay = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
39002
39948
|
async function fetchGitingest(args2) {
|
|
39003
39949
|
for (let attempt = 0;attempt <= GITINGEST_MAX_RETRIES; attempt++) {
|
|
39004
39950
|
try {
|
|
@@ -39078,7 +40024,7 @@ var gitingest = tool({
|
|
|
39078
40024
|
// src/tools/imports.ts
|
|
39079
40025
|
init_dist();
|
|
39080
40026
|
import * as fs17 from "fs";
|
|
39081
|
-
import * as
|
|
40027
|
+
import * as path23 from "path";
|
|
39082
40028
|
var MAX_FILE_PATH_LENGTH2 = 500;
|
|
39083
40029
|
var MAX_SYMBOL_LENGTH = 256;
|
|
39084
40030
|
var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
|
|
@@ -39132,7 +40078,7 @@ function validateSymbolInput(symbol3) {
|
|
|
39132
40078
|
return null;
|
|
39133
40079
|
}
|
|
39134
40080
|
function isBinaryFile2(filePath, buffer) {
|
|
39135
|
-
const ext =
|
|
40081
|
+
const ext = path23.extname(filePath).toLowerCase();
|
|
39136
40082
|
if (ext === ".json" || ext === ".md" || ext === ".txt") {
|
|
39137
40083
|
return false;
|
|
39138
40084
|
}
|
|
@@ -39154,20 +40100,19 @@ function isBinaryFile2(filePath, buffer) {
|
|
|
39154
40100
|
}
|
|
39155
40101
|
function parseImports(content, targetFile, targetSymbol) {
|
|
39156
40102
|
const imports = [];
|
|
39157
|
-
let
|
|
40103
|
+
let _resolvedTarget;
|
|
39158
40104
|
try {
|
|
39159
|
-
|
|
40105
|
+
_resolvedTarget = path23.resolve(targetFile);
|
|
39160
40106
|
} catch {
|
|
39161
|
-
|
|
40107
|
+
_resolvedTarget = targetFile;
|
|
39162
40108
|
}
|
|
39163
|
-
const targetBasename =
|
|
40109
|
+
const targetBasename = path23.basename(targetFile, path23.extname(targetFile));
|
|
39164
40110
|
const targetWithExt = targetFile;
|
|
39165
40111
|
const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
39166
|
-
const normalizedTargetWithExt =
|
|
39167
|
-
const normalizedTargetWithoutExt =
|
|
40112
|
+
const normalizedTargetWithExt = path23.normalize(targetWithExt).replace(/\\/g, "/");
|
|
40113
|
+
const normalizedTargetWithoutExt = path23.normalize(targetWithoutExt).replace(/\\/g, "/");
|
|
39168
40114
|
const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
39169
|
-
let match;
|
|
39170
|
-
while ((match = importRegex.exec(content)) !== null) {
|
|
40115
|
+
for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
|
|
39171
40116
|
const modulePath = match[1] || match[2] || match[3];
|
|
39172
40117
|
if (!modulePath)
|
|
39173
40118
|
continue;
|
|
@@ -39186,15 +40131,15 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
39186
40131
|
} else if (matchedString.includes("require(")) {
|
|
39187
40132
|
importType = "require";
|
|
39188
40133
|
}
|
|
39189
|
-
const
|
|
40134
|
+
const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
|
|
39190
40135
|
let isMatch = false;
|
|
39191
|
-
const
|
|
39192
|
-
const targetExt =
|
|
39193
|
-
const targetBasenameNoExt =
|
|
40136
|
+
const _targetDir = path23.dirname(targetFile);
|
|
40137
|
+
const targetExt = path23.extname(targetFile);
|
|
40138
|
+
const targetBasenameNoExt = path23.basename(targetFile, targetExt);
|
|
39194
40139
|
const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
39195
40140
|
const moduleName = modulePath.split(/[/\\]/).pop() || "";
|
|
39196
40141
|
const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
39197
|
-
if (modulePath === targetBasename || modulePath === targetBasenameNoExt || modulePath ===
|
|
40142
|
+
if (modulePath === targetBasename || modulePath === targetBasenameNoExt || modulePath === `./${targetBasename}` || modulePath === `./${targetBasenameNoExt}` || modulePath === `../${targetBasename}` || modulePath === `../${targetBasenameNoExt}` || moduleNormalized === normalizedTargetWithExt || moduleNormalized === normalizedTargetWithoutExt || modulePath.endsWith(`/${targetBasename}`) || modulePath.endsWith(`\\${targetBasename}`) || modulePath.endsWith(`/${targetBasenameNoExt}`) || modulePath.endsWith(`\\${targetBasenameNoExt}`) || moduleNameNoExt === targetBasenameNoExt || `./${moduleNameNoExt}` === targetBasenameNoExt || moduleName === targetBasename || moduleName === targetBasenameNoExt) {
|
|
39198
40143
|
isMatch = true;
|
|
39199
40144
|
}
|
|
39200
40145
|
if (isMatch && targetSymbol) {
|
|
@@ -39258,10 +40203,10 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
|
|
|
39258
40203
|
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
39259
40204
|
for (const entry of entries) {
|
|
39260
40205
|
if (SKIP_DIRECTORIES2.has(entry)) {
|
|
39261
|
-
stats.skippedDirs.push(
|
|
40206
|
+
stats.skippedDirs.push(path23.join(dir, entry));
|
|
39262
40207
|
continue;
|
|
39263
40208
|
}
|
|
39264
|
-
const fullPath =
|
|
40209
|
+
const fullPath = path23.join(dir, entry);
|
|
39265
40210
|
let stat;
|
|
39266
40211
|
try {
|
|
39267
40212
|
stat = fs17.statSync(fullPath);
|
|
@@ -39275,7 +40220,7 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
|
|
|
39275
40220
|
if (stat.isDirectory()) {
|
|
39276
40221
|
findSourceFiles2(fullPath, files, stats);
|
|
39277
40222
|
} else if (stat.isFile()) {
|
|
39278
|
-
const ext =
|
|
40223
|
+
const ext = path23.extname(fullPath).toLowerCase();
|
|
39279
40224
|
if (SUPPORTED_EXTENSIONS.includes(ext)) {
|
|
39280
40225
|
files.push(fullPath);
|
|
39281
40226
|
}
|
|
@@ -39331,7 +40276,7 @@ var imports = tool({
|
|
|
39331
40276
|
return JSON.stringify(errorResult, null, 2);
|
|
39332
40277
|
}
|
|
39333
40278
|
try {
|
|
39334
|
-
const targetFile =
|
|
40279
|
+
const targetFile = path23.resolve(file3);
|
|
39335
40280
|
if (!fs17.existsSync(targetFile)) {
|
|
39336
40281
|
const errorResult = {
|
|
39337
40282
|
error: `target file not found: ${file3}`,
|
|
@@ -39353,7 +40298,7 @@ var imports = tool({
|
|
|
39353
40298
|
};
|
|
39354
40299
|
return JSON.stringify(errorResult, null, 2);
|
|
39355
40300
|
}
|
|
39356
|
-
const baseDir =
|
|
40301
|
+
const baseDir = path23.dirname(targetFile);
|
|
39357
40302
|
const scanStats = {
|
|
39358
40303
|
skippedDirs: [],
|
|
39359
40304
|
skippedFiles: 0,
|
|
@@ -39395,7 +40340,7 @@ var imports = tool({
|
|
|
39395
40340
|
raw: imp.raw
|
|
39396
40341
|
});
|
|
39397
40342
|
}
|
|
39398
|
-
} catch (
|
|
40343
|
+
} catch (_e) {
|
|
39399
40344
|
skippedFileCount++;
|
|
39400
40345
|
}
|
|
39401
40346
|
}
|
|
@@ -39421,7 +40366,7 @@ var imports = tool({
|
|
|
39421
40366
|
}
|
|
39422
40367
|
}
|
|
39423
40368
|
if (parts2.length > 0) {
|
|
39424
|
-
result.message = parts2.join("; ")
|
|
40369
|
+
result.message = `${parts2.join("; ")}.`;
|
|
39425
40370
|
}
|
|
39426
40371
|
return JSON.stringify(result, null, 2);
|
|
39427
40372
|
} catch (e) {
|
|
@@ -39443,7 +40388,7 @@ init_lint();
|
|
|
39443
40388
|
// src/tools/pkg-audit.ts
|
|
39444
40389
|
init_dist();
|
|
39445
40390
|
import * as fs18 from "fs";
|
|
39446
|
-
import * as
|
|
40391
|
+
import * as path24 from "path";
|
|
39447
40392
|
var MAX_OUTPUT_BYTES5 = 52428800;
|
|
39448
40393
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
39449
40394
|
function isValidEcosystem(value) {
|
|
@@ -39461,13 +40406,13 @@ function validateArgs3(args2) {
|
|
|
39461
40406
|
function detectEcosystems() {
|
|
39462
40407
|
const ecosystems = [];
|
|
39463
40408
|
const cwd = process.cwd();
|
|
39464
|
-
if (fs18.existsSync(
|
|
40409
|
+
if (fs18.existsSync(path24.join(cwd, "package.json"))) {
|
|
39465
40410
|
ecosystems.push("npm");
|
|
39466
40411
|
}
|
|
39467
|
-
if (fs18.existsSync(
|
|
40412
|
+
if (fs18.existsSync(path24.join(cwd, "pyproject.toml")) || fs18.existsSync(path24.join(cwd, "requirements.txt"))) {
|
|
39468
40413
|
ecosystems.push("pip");
|
|
39469
40414
|
}
|
|
39470
|
-
if (fs18.existsSync(
|
|
40415
|
+
if (fs18.existsSync(path24.join(cwd, "Cargo.toml"))) {
|
|
39471
40416
|
ecosystems.push("cargo");
|
|
39472
40417
|
}
|
|
39473
40418
|
return ecosystems;
|
|
@@ -39480,7 +40425,7 @@ async function runNpmAudit() {
|
|
|
39480
40425
|
stderr: "pipe",
|
|
39481
40426
|
cwd: process.cwd()
|
|
39482
40427
|
});
|
|
39483
|
-
const timeoutPromise = new Promise((
|
|
40428
|
+
const timeoutPromise = new Promise((resolve11) => setTimeout(() => resolve11("timeout"), AUDIT_TIMEOUT_MS));
|
|
39484
40429
|
const result = await Promise.race([
|
|
39485
40430
|
Promise.all([
|
|
39486
40431
|
new Response(proc.stdout).text(),
|
|
@@ -39603,7 +40548,7 @@ async function runPipAudit() {
|
|
|
39603
40548
|
stderr: "pipe",
|
|
39604
40549
|
cwd: process.cwd()
|
|
39605
40550
|
});
|
|
39606
|
-
const timeoutPromise = new Promise((
|
|
40551
|
+
const timeoutPromise = new Promise((resolve11) => setTimeout(() => resolve11("timeout"), AUDIT_TIMEOUT_MS));
|
|
39607
40552
|
const result = await Promise.race([
|
|
39608
40553
|
Promise.all([
|
|
39609
40554
|
new Response(proc.stdout).text(),
|
|
@@ -39734,12 +40679,12 @@ async function runCargoAudit() {
|
|
|
39734
40679
|
stderr: "pipe",
|
|
39735
40680
|
cwd: process.cwd()
|
|
39736
40681
|
});
|
|
39737
|
-
const timeoutPromise = new Promise((
|
|
40682
|
+
const timeoutPromise = new Promise((resolve11) => setTimeout(() => resolve11("timeout"), AUDIT_TIMEOUT_MS));
|
|
39738
40683
|
const result = await Promise.race([
|
|
39739
40684
|
Promise.all([
|
|
39740
40685
|
new Response(proc.stdout).text(),
|
|
39741
40686
|
new Response(proc.stderr).text()
|
|
39742
|
-
]).then(([stdout2,
|
|
40687
|
+
]).then(([stdout2, stderr]) => ({ stdout: stdout2, stderr })),
|
|
39743
40688
|
timeoutPromise
|
|
39744
40689
|
]);
|
|
39745
40690
|
if (result === "timeout") {
|
|
@@ -39755,7 +40700,7 @@ async function runCargoAudit() {
|
|
|
39755
40700
|
note: `cargo audit timed out after ${AUDIT_TIMEOUT_MS / 1000}s`
|
|
39756
40701
|
};
|
|
39757
40702
|
}
|
|
39758
|
-
let { stdout, stderr } = result;
|
|
40703
|
+
let { stdout, stderr: _stderr } = result;
|
|
39759
40704
|
if (stdout.length > MAX_OUTPUT_BYTES5) {
|
|
39760
40705
|
stdout = stdout.slice(0, MAX_OUTPUT_BYTES5);
|
|
39761
40706
|
}
|
|
@@ -39777,7 +40722,7 @@ async function runCargoAudit() {
|
|
|
39777
40722
|
for (const line of lines) {
|
|
39778
40723
|
try {
|
|
39779
40724
|
const obj = JSON.parse(line);
|
|
39780
|
-
if (obj.vulnerabilities
|
|
40725
|
+
if (obj.vulnerabilities?.list) {
|
|
39781
40726
|
for (const item of obj.vulnerabilities.list) {
|
|
39782
40727
|
const cvss = item.advisory.cvss || 0;
|
|
39783
40728
|
const severity = mapCargoSeverity(cvss);
|
|
@@ -41352,8 +42297,8 @@ var Module2 = (() => {
|
|
|
41352
42297
|
var moduleRtn;
|
|
41353
42298
|
var Module = moduleArg;
|
|
41354
42299
|
var readyPromiseResolve, readyPromiseReject;
|
|
41355
|
-
var readyPromise = new Promise((
|
|
41356
|
-
readyPromiseResolve =
|
|
42300
|
+
var readyPromise = new Promise((resolve11, reject) => {
|
|
42301
|
+
readyPromiseResolve = resolve11;
|
|
41357
42302
|
readyPromiseReject = reject;
|
|
41358
42303
|
});
|
|
41359
42304
|
var ENVIRONMENT_IS_WEB = typeof window == "object";
|
|
@@ -41375,11 +42320,11 @@ var Module2 = (() => {
|
|
|
41375
42320
|
throw toThrow;
|
|
41376
42321
|
}, "quit_");
|
|
41377
42322
|
var scriptDirectory = "";
|
|
41378
|
-
function locateFile(
|
|
42323
|
+
function locateFile(path25) {
|
|
41379
42324
|
if (Module["locateFile"]) {
|
|
41380
|
-
return Module["locateFile"](
|
|
42325
|
+
return Module["locateFile"](path25, scriptDirectory);
|
|
41381
42326
|
}
|
|
41382
|
-
return scriptDirectory +
|
|
42327
|
+
return scriptDirectory + path25;
|
|
41383
42328
|
}
|
|
41384
42329
|
__name(locateFile, "locateFile");
|
|
41385
42330
|
var readAsync, readBinary;
|
|
@@ -41433,13 +42378,13 @@ var Module2 = (() => {
|
|
|
41433
42378
|
}
|
|
41434
42379
|
readAsync = /* @__PURE__ */ __name(async (url3) => {
|
|
41435
42380
|
if (isFileURI(url3)) {
|
|
41436
|
-
return new Promise((
|
|
42381
|
+
return new Promise((resolve11, reject) => {
|
|
41437
42382
|
var xhr = new XMLHttpRequest;
|
|
41438
42383
|
xhr.open("GET", url3, true);
|
|
41439
42384
|
xhr.responseType = "arraybuffer";
|
|
41440
42385
|
xhr.onload = () => {
|
|
41441
42386
|
if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
|
|
41442
|
-
|
|
42387
|
+
resolve11(xhr.response);
|
|
41443
42388
|
return;
|
|
41444
42389
|
}
|
|
41445
42390
|
reject(xhr.status);
|
|
@@ -41659,10 +42604,10 @@ var Module2 = (() => {
|
|
|
41659
42604
|
__name(receiveInstantiationResult, "receiveInstantiationResult");
|
|
41660
42605
|
var info2 = getWasmImports();
|
|
41661
42606
|
if (Module["instantiateWasm"]) {
|
|
41662
|
-
return new Promise((
|
|
42607
|
+
return new Promise((resolve11, reject) => {
|
|
41663
42608
|
Module["instantiateWasm"](info2, (mod, inst) => {
|
|
41664
42609
|
receiveInstance(mod, inst);
|
|
41665
|
-
|
|
42610
|
+
resolve11(mod.exports);
|
|
41666
42611
|
});
|
|
41667
42612
|
});
|
|
41668
42613
|
}
|
|
@@ -43193,7 +44138,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
|
|
|
43193
44138
|
]);
|
|
43194
44139
|
// src/tools/pre-check-batch.ts
|
|
43195
44140
|
init_dist();
|
|
43196
|
-
import * as
|
|
44141
|
+
import * as path27 from "path";
|
|
43197
44142
|
|
|
43198
44143
|
// node_modules/yocto-queue/index.js
|
|
43199
44144
|
class Node2 {
|
|
@@ -43284,26 +44229,26 @@ function pLimit(concurrency) {
|
|
|
43284
44229
|
activeCount--;
|
|
43285
44230
|
resumeNext();
|
|
43286
44231
|
};
|
|
43287
|
-
const run2 = async (function_,
|
|
44232
|
+
const run2 = async (function_, resolve11, arguments_2) => {
|
|
43288
44233
|
const result = (async () => function_(...arguments_2))();
|
|
43289
|
-
|
|
44234
|
+
resolve11(result);
|
|
43290
44235
|
try {
|
|
43291
44236
|
await result;
|
|
43292
44237
|
} catch {}
|
|
43293
44238
|
next();
|
|
43294
44239
|
};
|
|
43295
|
-
const enqueue = (function_,
|
|
44240
|
+
const enqueue = (function_, resolve11, reject, arguments_2) => {
|
|
43296
44241
|
const queueItem = { reject };
|
|
43297
44242
|
new Promise((internalResolve) => {
|
|
43298
44243
|
queueItem.run = internalResolve;
|
|
43299
44244
|
queue.enqueue(queueItem);
|
|
43300
|
-
}).then(run2.bind(undefined, function_,
|
|
44245
|
+
}).then(run2.bind(undefined, function_, resolve11, arguments_2));
|
|
43301
44246
|
if (activeCount < concurrency) {
|
|
43302
44247
|
resumeNext();
|
|
43303
44248
|
}
|
|
43304
44249
|
};
|
|
43305
|
-
const generator = (function_, ...arguments_2) => new Promise((
|
|
43306
|
-
enqueue(function_,
|
|
44250
|
+
const generator = (function_, ...arguments_2) => new Promise((resolve11, reject) => {
|
|
44251
|
+
enqueue(function_, resolve11, reject, arguments_2);
|
|
43307
44252
|
});
|
|
43308
44253
|
Object.defineProperties(generator, {
|
|
43309
44254
|
activeCount: {
|
|
@@ -43360,7 +44305,7 @@ init_manager();
|
|
|
43360
44305
|
|
|
43361
44306
|
// src/quality/metrics.ts
|
|
43362
44307
|
import * as fs19 from "fs";
|
|
43363
|
-
import * as
|
|
44308
|
+
import * as path25 from "path";
|
|
43364
44309
|
var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
|
|
43365
44310
|
var MIN_DUPLICATION_LINES = 10;
|
|
43366
44311
|
function estimateCyclomaticComplexity(content) {
|
|
@@ -43412,7 +44357,7 @@ async function computeComplexityDelta(files, workingDir) {
|
|
|
43412
44357
|
let totalComplexity = 0;
|
|
43413
44358
|
const analyzedFiles = [];
|
|
43414
44359
|
for (const file3 of files) {
|
|
43415
|
-
const fullPath =
|
|
44360
|
+
const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
|
|
43416
44361
|
if (!fs19.existsSync(fullPath)) {
|
|
43417
44362
|
continue;
|
|
43418
44363
|
}
|
|
@@ -43535,7 +44480,7 @@ function countGoExports(content) {
|
|
|
43535
44480
|
function getExportCountForFile(filePath) {
|
|
43536
44481
|
try {
|
|
43537
44482
|
const content = fs19.readFileSync(filePath, "utf-8");
|
|
43538
|
-
const ext =
|
|
44483
|
+
const ext = path25.extname(filePath).toLowerCase();
|
|
43539
44484
|
switch (ext) {
|
|
43540
44485
|
case ".ts":
|
|
43541
44486
|
case ".tsx":
|
|
@@ -43561,7 +44506,7 @@ async function computePublicApiDelta(files, workingDir) {
|
|
|
43561
44506
|
let totalExports = 0;
|
|
43562
44507
|
const analyzedFiles = [];
|
|
43563
44508
|
for (const file3 of files) {
|
|
43564
|
-
const fullPath =
|
|
44509
|
+
const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
|
|
43565
44510
|
if (!fs19.existsSync(fullPath)) {
|
|
43566
44511
|
continue;
|
|
43567
44512
|
}
|
|
@@ -43595,7 +44540,7 @@ async function computeDuplicationRatio(files, workingDir) {
|
|
|
43595
44540
|
let duplicateLines = 0;
|
|
43596
44541
|
const analyzedFiles = [];
|
|
43597
44542
|
for (const file3 of files) {
|
|
43598
|
-
const fullPath =
|
|
44543
|
+
const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
|
|
43599
44544
|
if (!fs19.existsSync(fullPath)) {
|
|
43600
44545
|
continue;
|
|
43601
44546
|
}
|
|
@@ -43628,8 +44573,8 @@ function countCodeLines(content) {
|
|
|
43628
44573
|
return lines.length;
|
|
43629
44574
|
}
|
|
43630
44575
|
function isTestFile(filePath) {
|
|
43631
|
-
const basename5 =
|
|
43632
|
-
const _ext =
|
|
44576
|
+
const basename5 = path25.basename(filePath);
|
|
44577
|
+
const _ext = path25.extname(filePath).toLowerCase();
|
|
43633
44578
|
const testPatterns = [
|
|
43634
44579
|
".test.",
|
|
43635
44580
|
".spec.",
|
|
@@ -43671,7 +44616,7 @@ function shouldExcludeFile(filePath, excludeGlobs) {
|
|
|
43671
44616
|
async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
43672
44617
|
let testLines = 0;
|
|
43673
44618
|
let codeLines = 0;
|
|
43674
|
-
const srcDir =
|
|
44619
|
+
const srcDir = path25.join(workingDir, "src");
|
|
43675
44620
|
if (fs19.existsSync(srcDir)) {
|
|
43676
44621
|
await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
43677
44622
|
codeLines += lines;
|
|
@@ -43679,14 +44624,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
43679
44624
|
}
|
|
43680
44625
|
const possibleSrcDirs = ["lib", "app", "source", "core"];
|
|
43681
44626
|
for (const dir of possibleSrcDirs) {
|
|
43682
|
-
const dirPath =
|
|
44627
|
+
const dirPath = path25.join(workingDir, dir);
|
|
43683
44628
|
if (fs19.existsSync(dirPath)) {
|
|
43684
44629
|
await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
43685
44630
|
codeLines += lines;
|
|
43686
44631
|
});
|
|
43687
44632
|
}
|
|
43688
44633
|
}
|
|
43689
|
-
const testsDir =
|
|
44634
|
+
const testsDir = path25.join(workingDir, "tests");
|
|
43690
44635
|
if (fs19.existsSync(testsDir)) {
|
|
43691
44636
|
await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
43692
44637
|
testLines += lines;
|
|
@@ -43694,7 +44639,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
43694
44639
|
}
|
|
43695
44640
|
const possibleTestDirs = ["test", "__tests__", "specs"];
|
|
43696
44641
|
for (const dir of possibleTestDirs) {
|
|
43697
|
-
const dirPath =
|
|
44642
|
+
const dirPath = path25.join(workingDir, dir);
|
|
43698
44643
|
if (fs19.existsSync(dirPath) && dirPath !== testsDir) {
|
|
43699
44644
|
await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
43700
44645
|
testLines += lines;
|
|
@@ -43709,7 +44654,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
43709
44654
|
try {
|
|
43710
44655
|
const entries = fs19.readdirSync(dirPath, { withFileTypes: true });
|
|
43711
44656
|
for (const entry of entries) {
|
|
43712
|
-
const fullPath =
|
|
44657
|
+
const fullPath = path25.join(dirPath, entry.name);
|
|
43713
44658
|
if (entry.isDirectory()) {
|
|
43714
44659
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
|
|
43715
44660
|
continue;
|
|
@@ -43717,7 +44662,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
43717
44662
|
await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
|
|
43718
44663
|
} else if (entry.isFile()) {
|
|
43719
44664
|
const relativePath = fullPath.replace(`${process.cwd()}/`, "");
|
|
43720
|
-
const ext =
|
|
44665
|
+
const ext = path25.extname(entry.name).toLowerCase();
|
|
43721
44666
|
const validExts = [
|
|
43722
44667
|
".ts",
|
|
43723
44668
|
".tsx",
|
|
@@ -43972,7 +44917,7 @@ async function qualityBudget(input, directory) {
|
|
|
43972
44917
|
// src/tools/sast-scan.ts
|
|
43973
44918
|
init_manager();
|
|
43974
44919
|
import * as fs20 from "fs";
|
|
43975
|
-
import * as
|
|
44920
|
+
import * as path26 from "path";
|
|
43976
44921
|
import { extname as extname7 } from "path";
|
|
43977
44922
|
|
|
43978
44923
|
// src/sast/rules/c.ts
|
|
@@ -44600,9 +45545,9 @@ function findPatternMatches(content, pattern) {
|
|
|
44600
45545
|
`);
|
|
44601
45546
|
for (let lineNum = 0;lineNum < lines.length; lineNum++) {
|
|
44602
45547
|
const line = lines[lineNum];
|
|
44603
|
-
const workPattern = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : pattern.flags
|
|
44604
|
-
let match;
|
|
44605
|
-
while (
|
|
45548
|
+
const workPattern = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
|
|
45549
|
+
let match = workPattern.exec(line);
|
|
45550
|
+
while (match !== null) {
|
|
44606
45551
|
matches.push({
|
|
44607
45552
|
text: match[0],
|
|
44608
45553
|
line: lineNum + 1,
|
|
@@ -44611,6 +45556,7 @@ function findPatternMatches(content, pattern) {
|
|
|
44611
45556
|
if (match[0].length === 0) {
|
|
44612
45557
|
workPattern.lastIndex++;
|
|
44613
45558
|
}
|
|
45559
|
+
match = workPattern.exec(line);
|
|
44614
45560
|
}
|
|
44615
45561
|
}
|
|
44616
45562
|
return matches;
|
|
@@ -44657,7 +45603,7 @@ function executeRulesSync(filePath, content, language) {
|
|
|
44657
45603
|
// src/sast/semgrep.ts
|
|
44658
45604
|
import { execFile, execFileSync, spawn } from "child_process";
|
|
44659
45605
|
import { promisify } from "util";
|
|
44660
|
-
var
|
|
45606
|
+
var _execFileAsync = promisify(execFile);
|
|
44661
45607
|
var semgrepAvailableCache = null;
|
|
44662
45608
|
var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
|
|
44663
45609
|
var DEFAULT_TIMEOUT_MS3 = 30000;
|
|
@@ -44719,7 +45665,7 @@ function mapSemgrepSeverity(severity) {
|
|
|
44719
45665
|
}
|
|
44720
45666
|
}
|
|
44721
45667
|
async function executeWithTimeout(command, args2, options) {
|
|
44722
|
-
return new Promise((
|
|
45668
|
+
return new Promise((resolve11) => {
|
|
44723
45669
|
const child = spawn(command, args2, {
|
|
44724
45670
|
shell: false,
|
|
44725
45671
|
cwd: options.cwd
|
|
@@ -44728,7 +45674,7 @@ async function executeWithTimeout(command, args2, options) {
|
|
|
44728
45674
|
let stderr = "";
|
|
44729
45675
|
const timeout = setTimeout(() => {
|
|
44730
45676
|
child.kill("SIGTERM");
|
|
44731
|
-
|
|
45677
|
+
resolve11({
|
|
44732
45678
|
stdout,
|
|
44733
45679
|
stderr: "Process timed out",
|
|
44734
45680
|
exitCode: 124
|
|
@@ -44742,7 +45688,7 @@ async function executeWithTimeout(command, args2, options) {
|
|
|
44742
45688
|
});
|
|
44743
45689
|
child.on("close", (code) => {
|
|
44744
45690
|
clearTimeout(timeout);
|
|
44745
|
-
|
|
45691
|
+
resolve11({
|
|
44746
45692
|
stdout,
|
|
44747
45693
|
stderr,
|
|
44748
45694
|
exitCode: code ?? 0
|
|
@@ -44750,7 +45696,7 @@ async function executeWithTimeout(command, args2, options) {
|
|
|
44750
45696
|
});
|
|
44751
45697
|
child.on("error", (err2) => {
|
|
44752
45698
|
clearTimeout(timeout);
|
|
44753
|
-
|
|
45699
|
+
resolve11({
|
|
44754
45700
|
stdout,
|
|
44755
45701
|
stderr: err2.message,
|
|
44756
45702
|
exitCode: 1
|
|
@@ -44778,7 +45724,7 @@ async function runSemgrep(options) {
|
|
|
44778
45724
|
};
|
|
44779
45725
|
}
|
|
44780
45726
|
const args2 = [
|
|
44781
|
-
|
|
45727
|
+
`--config=./${rulesDir}`,
|
|
44782
45728
|
"--json",
|
|
44783
45729
|
"--quiet",
|
|
44784
45730
|
...files
|
|
@@ -44921,25 +45867,25 @@ async function sastScan(input, directory, config3) {
|
|
|
44921
45867
|
}
|
|
44922
45868
|
const allFindings = [];
|
|
44923
45869
|
let filesScanned = 0;
|
|
44924
|
-
let
|
|
45870
|
+
let _filesSkipped = 0;
|
|
44925
45871
|
const semgrepAvailable = isSemgrepAvailable();
|
|
44926
45872
|
const engine = semgrepAvailable ? "tier_a+tier_b" : "tier_a";
|
|
44927
45873
|
const filesByLanguage = new Map;
|
|
44928
45874
|
for (const filePath of changed_files) {
|
|
44929
|
-
const resolvedPath =
|
|
45875
|
+
const resolvedPath = path26.isAbsolute(filePath) ? filePath : path26.resolve(directory, filePath);
|
|
44930
45876
|
if (!fs20.existsSync(resolvedPath)) {
|
|
44931
|
-
|
|
45877
|
+
_filesSkipped++;
|
|
44932
45878
|
continue;
|
|
44933
45879
|
}
|
|
44934
45880
|
const skipResult = shouldSkipFile(resolvedPath);
|
|
44935
45881
|
if (skipResult.skip) {
|
|
44936
|
-
|
|
45882
|
+
_filesSkipped++;
|
|
44937
45883
|
continue;
|
|
44938
45884
|
}
|
|
44939
45885
|
const ext = extname7(resolvedPath).toLowerCase();
|
|
44940
45886
|
const langDef = getLanguageForExtension(ext);
|
|
44941
45887
|
if (!langDef) {
|
|
44942
|
-
|
|
45888
|
+
_filesSkipped++;
|
|
44943
45889
|
continue;
|
|
44944
45890
|
}
|
|
44945
45891
|
const language = langDef.id;
|
|
@@ -45035,10 +45981,10 @@ function validatePath(inputPath, baseDir) {
|
|
|
45035
45981
|
if (!inputPath || inputPath.length === 0) {
|
|
45036
45982
|
return "path is required";
|
|
45037
45983
|
}
|
|
45038
|
-
const resolved =
|
|
45039
|
-
const baseResolved =
|
|
45040
|
-
const
|
|
45041
|
-
if (
|
|
45984
|
+
const resolved = path27.resolve(baseDir, inputPath);
|
|
45985
|
+
const baseResolved = path27.resolve(baseDir);
|
|
45986
|
+
const relative3 = path27.relative(baseResolved, resolved);
|
|
45987
|
+
if (relative3.startsWith("..") || path27.isAbsolute(relative3)) {
|
|
45042
45988
|
return "path traversal detected";
|
|
45043
45989
|
}
|
|
45044
45990
|
return null;
|
|
@@ -45160,7 +46106,7 @@ async function runPreCheckBatch(input) {
|
|
|
45160
46106
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
45161
46107
|
continue;
|
|
45162
46108
|
}
|
|
45163
|
-
changedFiles.push(
|
|
46109
|
+
changedFiles.push(path27.resolve(directory, file3));
|
|
45164
46110
|
}
|
|
45165
46111
|
} else {
|
|
45166
46112
|
changedFiles = [];
|
|
@@ -45351,7 +46297,7 @@ var retrieve_summary = tool({
|
|
|
45351
46297
|
init_dist();
|
|
45352
46298
|
init_manager();
|
|
45353
46299
|
import * as fs21 from "fs";
|
|
45354
|
-
import * as
|
|
46300
|
+
import * as path28 from "path";
|
|
45355
46301
|
|
|
45356
46302
|
// src/sbom/detectors/dart.ts
|
|
45357
46303
|
function parsePubspecLock(content) {
|
|
@@ -46198,7 +47144,7 @@ function findManifestFiles(rootDir) {
|
|
|
46198
47144
|
try {
|
|
46199
47145
|
const entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
46200
47146
|
for (const entry of entries) {
|
|
46201
|
-
const fullPath =
|
|
47147
|
+
const fullPath = path28.join(dir, entry.name);
|
|
46202
47148
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
46203
47149
|
continue;
|
|
46204
47150
|
}
|
|
@@ -46208,7 +47154,7 @@ function findManifestFiles(rootDir) {
|
|
|
46208
47154
|
for (const pattern of patterns) {
|
|
46209
47155
|
const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
46210
47156
|
if (new RegExp(regex, "i").test(entry.name)) {
|
|
46211
|
-
manifestFiles.push(
|
|
47157
|
+
manifestFiles.push(path28.relative(cwd, fullPath));
|
|
46212
47158
|
break;
|
|
46213
47159
|
}
|
|
46214
47160
|
}
|
|
@@ -46221,18 +47167,18 @@ function findManifestFiles(rootDir) {
|
|
|
46221
47167
|
}
|
|
46222
47168
|
function findManifestFilesInDirs(directories, workingDir) {
|
|
46223
47169
|
const found = [];
|
|
46224
|
-
const
|
|
47170
|
+
const _cwd = process.cwd();
|
|
46225
47171
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
46226
47172
|
for (const dir of directories) {
|
|
46227
47173
|
try {
|
|
46228
47174
|
const entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
46229
47175
|
for (const entry of entries) {
|
|
46230
|
-
const fullPath =
|
|
47176
|
+
const fullPath = path28.join(dir, entry.name);
|
|
46231
47177
|
if (entry.isFile()) {
|
|
46232
47178
|
for (const pattern of patterns) {
|
|
46233
47179
|
const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
46234
47180
|
if (new RegExp(regex, "i").test(entry.name)) {
|
|
46235
|
-
found.push(
|
|
47181
|
+
found.push(path28.relative(workingDir, fullPath));
|
|
46236
47182
|
break;
|
|
46237
47183
|
}
|
|
46238
47184
|
}
|
|
@@ -46245,11 +47191,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
46245
47191
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
46246
47192
|
const dirs = new Set;
|
|
46247
47193
|
for (const file3 of changedFiles) {
|
|
46248
|
-
let currentDir =
|
|
47194
|
+
let currentDir = path28.dirname(file3);
|
|
46249
47195
|
while (true) {
|
|
46250
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
46251
|
-
dirs.add(
|
|
46252
|
-
const parent =
|
|
47196
|
+
if (currentDir && currentDir !== "." && currentDir !== path28.sep) {
|
|
47197
|
+
dirs.add(path28.join(workingDir, currentDir));
|
|
47198
|
+
const parent = path28.dirname(currentDir);
|
|
46253
47199
|
if (parent === currentDir)
|
|
46254
47200
|
break;
|
|
46255
47201
|
currentDir = parent;
|
|
@@ -46356,7 +47302,7 @@ var sbom_generate = tool({
|
|
|
46356
47302
|
const processedFiles = [];
|
|
46357
47303
|
for (const manifestFile of manifestFiles) {
|
|
46358
47304
|
try {
|
|
46359
|
-
const fullPath =
|
|
47305
|
+
const fullPath = path28.isAbsolute(manifestFile) ? manifestFile : path28.join(workingDir, manifestFile);
|
|
46360
47306
|
if (!fs21.existsSync(fullPath)) {
|
|
46361
47307
|
continue;
|
|
46362
47308
|
}
|
|
@@ -46373,7 +47319,7 @@ var sbom_generate = tool({
|
|
|
46373
47319
|
const bom = generateCycloneDX(allComponents);
|
|
46374
47320
|
const bomJson = serializeCycloneDX(bom);
|
|
46375
47321
|
const filename = generateSbomFilename();
|
|
46376
|
-
const outputPath =
|
|
47322
|
+
const outputPath = path28.join(outputDir, filename);
|
|
46377
47323
|
fs21.writeFileSync(outputPath, bomJson, "utf-8");
|
|
46378
47324
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
46379
47325
|
try {
|
|
@@ -46416,7 +47362,7 @@ var sbom_generate = tool({
|
|
|
46416
47362
|
// src/tools/schema-drift.ts
|
|
46417
47363
|
init_dist();
|
|
46418
47364
|
import * as fs22 from "fs";
|
|
46419
|
-
import * as
|
|
47365
|
+
import * as path29 from "path";
|
|
46420
47366
|
var SPEC_CANDIDATES = [
|
|
46421
47367
|
"openapi.json",
|
|
46422
47368
|
"openapi.yaml",
|
|
@@ -46448,12 +47394,12 @@ function normalizePath(p) {
|
|
|
46448
47394
|
}
|
|
46449
47395
|
function discoverSpecFile(cwd, specFileArg) {
|
|
46450
47396
|
if (specFileArg) {
|
|
46451
|
-
const resolvedPath =
|
|
46452
|
-
const normalizedCwd = cwd.endsWith(
|
|
47397
|
+
const resolvedPath = path29.resolve(cwd, specFileArg);
|
|
47398
|
+
const normalizedCwd = cwd.endsWith(path29.sep) ? cwd : cwd + path29.sep;
|
|
46453
47399
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
46454
47400
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
46455
47401
|
}
|
|
46456
|
-
const ext =
|
|
47402
|
+
const ext = path29.extname(resolvedPath).toLowerCase();
|
|
46457
47403
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
46458
47404
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
46459
47405
|
}
|
|
@@ -46467,7 +47413,7 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
46467
47413
|
return resolvedPath;
|
|
46468
47414
|
}
|
|
46469
47415
|
for (const candidate of SPEC_CANDIDATES) {
|
|
46470
|
-
const candidatePath =
|
|
47416
|
+
const candidatePath = path29.resolve(cwd, candidate);
|
|
46471
47417
|
if (fs22.existsSync(candidatePath)) {
|
|
46472
47418
|
const stats = fs22.statSync(candidatePath);
|
|
46473
47419
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
@@ -46479,7 +47425,7 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
46479
47425
|
}
|
|
46480
47426
|
function parseSpec(specFile) {
|
|
46481
47427
|
const content = fs22.readFileSync(specFile, "utf-8");
|
|
46482
|
-
const ext =
|
|
47428
|
+
const ext = path29.extname(specFile).toLowerCase();
|
|
46483
47429
|
if (ext === ".json") {
|
|
46484
47430
|
return parseJsonSpec(content);
|
|
46485
47431
|
}
|
|
@@ -46521,18 +47467,18 @@ function parseYamlSpec(content) {
|
|
|
46521
47467
|
return paths;
|
|
46522
47468
|
}
|
|
46523
47469
|
const pathRegex = /^\s{2}(\/[^\s:]+):/gm;
|
|
46524
|
-
let match;
|
|
46525
|
-
while ((match = pathRegex.exec(content)) !== null) {
|
|
47470
|
+
for (let match = pathRegex.exec(content);match !== null; match = pathRegex.exec(content)) {
|
|
46526
47471
|
const pathKey = match[1];
|
|
46527
47472
|
const methodRegex = /^\s{4}(get|post|put|patch|delete|options|head):/gm;
|
|
46528
47473
|
const methods = [];
|
|
46529
|
-
let methodMatch;
|
|
46530
47474
|
const pathStart = match.index;
|
|
46531
47475
|
const nextPathMatch = content.substring(pathStart + 1).match(/^\s{2}\//m);
|
|
46532
47476
|
const pathEnd = nextPathMatch && nextPathMatch.index !== undefined ? pathStart + 1 + nextPathMatch.index : content.length;
|
|
46533
47477
|
const pathSection = content.substring(pathStart, pathEnd);
|
|
46534
|
-
|
|
47478
|
+
let methodMatch = methodRegex.exec(pathSection);
|
|
47479
|
+
while (methodMatch !== null) {
|
|
46535
47480
|
methods.push(methodMatch[1]);
|
|
47481
|
+
methodMatch = methodRegex.exec(pathSection);
|
|
46536
47482
|
}
|
|
46537
47483
|
if (methods.length > 0) {
|
|
46538
47484
|
paths.push({ path: pathKey, methods });
|
|
@@ -46550,7 +47496,7 @@ function extractRoutes(cwd) {
|
|
|
46550
47496
|
return;
|
|
46551
47497
|
}
|
|
46552
47498
|
for (const entry of entries) {
|
|
46553
|
-
const fullPath =
|
|
47499
|
+
const fullPath = path29.join(dir, entry.name);
|
|
46554
47500
|
if (entry.isSymbolicLink()) {
|
|
46555
47501
|
continue;
|
|
46556
47502
|
}
|
|
@@ -46560,7 +47506,7 @@ function extractRoutes(cwd) {
|
|
|
46560
47506
|
}
|
|
46561
47507
|
walkDir(fullPath);
|
|
46562
47508
|
} else if (entry.isFile()) {
|
|
46563
|
-
const ext =
|
|
47509
|
+
const ext = path29.extname(entry.name).toLowerCase();
|
|
46564
47510
|
const baseName = entry.name.toLowerCase();
|
|
46565
47511
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
46566
47512
|
continue;
|
|
@@ -46584,10 +47530,10 @@ function extractRoutesFromFile(filePath) {
|
|
|
46584
47530
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
46585
47531
|
for (let lineNum = 0;lineNum < lines.length; lineNum++) {
|
|
46586
47532
|
const line = lines[lineNum];
|
|
46587
|
-
let match;
|
|
47533
|
+
let match = expressRegex.exec(line);
|
|
46588
47534
|
expressRegex.lastIndex = 0;
|
|
46589
47535
|
flaskRegex.lastIndex = 0;
|
|
46590
|
-
while (
|
|
47536
|
+
while (match !== null) {
|
|
46591
47537
|
const method = match[1].toLowerCase();
|
|
46592
47538
|
const routePath = match[2];
|
|
46593
47539
|
routes.push({
|
|
@@ -46596,8 +47542,10 @@ function extractRoutesFromFile(filePath) {
|
|
|
46596
47542
|
file: filePath,
|
|
46597
47543
|
line: lineNum + 1
|
|
46598
47544
|
});
|
|
47545
|
+
match = expressRegex.exec(line);
|
|
46599
47546
|
}
|
|
46600
|
-
|
|
47547
|
+
match = flaskRegex.exec(line);
|
|
47548
|
+
while (match !== null) {
|
|
46601
47549
|
const routePath = match[1];
|
|
46602
47550
|
routes.push({
|
|
46603
47551
|
path: routePath,
|
|
@@ -46605,6 +47553,7 @@ function extractRoutesFromFile(filePath) {
|
|
|
46605
47553
|
file: filePath,
|
|
46606
47554
|
line: lineNum + 1
|
|
46607
47555
|
});
|
|
47556
|
+
match = flaskRegex.exec(line);
|
|
46608
47557
|
}
|
|
46609
47558
|
}
|
|
46610
47559
|
return routes;
|
|
@@ -46726,7 +47675,7 @@ init_secretscan();
|
|
|
46726
47675
|
// src/tools/symbols.ts
|
|
46727
47676
|
init_tool();
|
|
46728
47677
|
import * as fs23 from "fs";
|
|
46729
|
-
import * as
|
|
47678
|
+
import * as path30 from "path";
|
|
46730
47679
|
var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
|
|
46731
47680
|
var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
46732
47681
|
function containsControlCharacters(str) {
|
|
@@ -46755,11 +47704,11 @@ function containsWindowsAttacks(str) {
|
|
|
46755
47704
|
}
|
|
46756
47705
|
function isPathInWorkspace(filePath, workspace) {
|
|
46757
47706
|
try {
|
|
46758
|
-
const resolvedPath =
|
|
47707
|
+
const resolvedPath = path30.resolve(workspace, filePath);
|
|
46759
47708
|
const realWorkspace = fs23.realpathSync(workspace);
|
|
46760
47709
|
const realResolvedPath = fs23.realpathSync(resolvedPath);
|
|
46761
|
-
const relativePath =
|
|
46762
|
-
if (relativePath.startsWith("..") ||
|
|
47710
|
+
const relativePath = path30.relative(realWorkspace, realResolvedPath);
|
|
47711
|
+
if (relativePath.startsWith("..") || path30.isAbsolute(relativePath)) {
|
|
46763
47712
|
return false;
|
|
46764
47713
|
}
|
|
46765
47714
|
return true;
|
|
@@ -46771,7 +47720,7 @@ function validatePathForRead(filePath, workspace) {
|
|
|
46771
47720
|
return isPathInWorkspace(filePath, workspace);
|
|
46772
47721
|
}
|
|
46773
47722
|
function extractTSSymbols(filePath, cwd) {
|
|
46774
|
-
const fullPath =
|
|
47723
|
+
const fullPath = path30.join(cwd, filePath);
|
|
46775
47724
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
46776
47725
|
return [];
|
|
46777
47726
|
}
|
|
@@ -46802,7 +47751,7 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
46802
47751
|
jsdoc = jsdocLines.join(`
|
|
46803
47752
|
`).trim();
|
|
46804
47753
|
if (jsdoc.length > 300)
|
|
46805
|
-
jsdoc = jsdoc.substring(0, 300)
|
|
47754
|
+
jsdoc = `${jsdoc.substring(0, 300)}...`;
|
|
46806
47755
|
}
|
|
46807
47756
|
const fnMatch = line.match(/^export\s+(?:async\s+)?function\s+(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)(?:\s*:\s*(.+?))?(?:\s*\{|$)/);
|
|
46808
47757
|
if (fnMatch) {
|
|
@@ -46923,7 +47872,7 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
46923
47872
|
});
|
|
46924
47873
|
}
|
|
46925
47874
|
function extractPythonSymbols(filePath, cwd) {
|
|
46926
|
-
const fullPath =
|
|
47875
|
+
const fullPath = path30.join(cwd, filePath);
|
|
46927
47876
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
46928
47877
|
return [];
|
|
46929
47878
|
}
|
|
@@ -47005,7 +47954,7 @@ var symbols = tool({
|
|
|
47005
47954
|
}, null, 2);
|
|
47006
47955
|
}
|
|
47007
47956
|
const cwd = process.cwd();
|
|
47008
|
-
const ext =
|
|
47957
|
+
const ext = path30.extname(file3);
|
|
47009
47958
|
if (containsControlCharacters(file3)) {
|
|
47010
47959
|
return JSON.stringify({
|
|
47011
47960
|
file: file3,
|
|
@@ -47074,7 +48023,7 @@ init_test_runner();
|
|
|
47074
48023
|
// src/tools/todo-extract.ts
|
|
47075
48024
|
init_dist();
|
|
47076
48025
|
import * as fs24 from "fs";
|
|
47077
|
-
import * as
|
|
48026
|
+
import * as path31 from "path";
|
|
47078
48027
|
var MAX_TEXT_LENGTH = 200;
|
|
47079
48028
|
var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
|
|
47080
48029
|
var SUPPORTED_EXTENSIONS2 = new Set([
|
|
@@ -47145,9 +48094,9 @@ function validatePathsInput(paths, cwd) {
|
|
|
47145
48094
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
47146
48095
|
}
|
|
47147
48096
|
try {
|
|
47148
|
-
const resolvedPath =
|
|
47149
|
-
const normalizedCwd =
|
|
47150
|
-
const normalizedResolved =
|
|
48097
|
+
const resolvedPath = path31.resolve(paths);
|
|
48098
|
+
const normalizedCwd = path31.resolve(cwd);
|
|
48099
|
+
const normalizedResolved = path31.resolve(resolvedPath);
|
|
47151
48100
|
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
47152
48101
|
return {
|
|
47153
48102
|
error: "paths must be within the current working directory",
|
|
@@ -47163,7 +48112,7 @@ function validatePathsInput(paths, cwd) {
|
|
|
47163
48112
|
}
|
|
47164
48113
|
}
|
|
47165
48114
|
function isSupportedExtension(filePath) {
|
|
47166
|
-
const ext =
|
|
48115
|
+
const ext = path31.extname(filePath).toLowerCase();
|
|
47167
48116
|
return SUPPORTED_EXTENSIONS2.has(ext);
|
|
47168
48117
|
}
|
|
47169
48118
|
function findSourceFiles3(dir, files = []) {
|
|
@@ -47178,7 +48127,7 @@ function findSourceFiles3(dir, files = []) {
|
|
|
47178
48127
|
if (SKIP_DIRECTORIES3.has(entry)) {
|
|
47179
48128
|
continue;
|
|
47180
48129
|
}
|
|
47181
|
-
const fullPath =
|
|
48130
|
+
const fullPath = path31.join(dir, entry);
|
|
47182
48131
|
let stat;
|
|
47183
48132
|
try {
|
|
47184
48133
|
stat = fs24.statSync(fullPath);
|
|
@@ -47210,7 +48159,7 @@ function parseTodoComments(content, filePath, tagsSet) {
|
|
|
47210
48159
|
let text = line.substring(match.index + match[0].length).trim();
|
|
47211
48160
|
text = text.replace(/^[/*\-\s]+/, "");
|
|
47212
48161
|
if (text.length > MAX_TEXT_LENGTH) {
|
|
47213
|
-
text = text.substring(0, MAX_TEXT_LENGTH - 3)
|
|
48162
|
+
text = `${text.substring(0, MAX_TEXT_LENGTH - 3)}...`;
|
|
47214
48163
|
}
|
|
47215
48164
|
entries.push({
|
|
47216
48165
|
file: filePath,
|
|
@@ -47290,7 +48239,7 @@ var todo_extract = tool({
|
|
|
47290
48239
|
filesToScan.push(scanPath);
|
|
47291
48240
|
} else {
|
|
47292
48241
|
const errorResult = {
|
|
47293
|
-
error: `unsupported file extension: ${
|
|
48242
|
+
error: `unsupported file extension: ${path31.extname(scanPath)}`,
|
|
47294
48243
|
total: 0,
|
|
47295
48244
|
byPriority: { high: 0, medium: 0, low: 0 },
|
|
47296
48245
|
entries: []
|
|
@@ -47334,6 +48283,33 @@ var todo_extract = tool({
|
|
|
47334
48283
|
});
|
|
47335
48284
|
// src/index.ts
|
|
47336
48285
|
init_utils();
|
|
48286
|
+
|
|
48287
|
+
// src/utils/tool-output.ts
|
|
48288
|
+
function truncateToolOutput(output, maxLines, toolName) {
|
|
48289
|
+
if (!output) {
|
|
48290
|
+
return output;
|
|
48291
|
+
}
|
|
48292
|
+
const lines = output.split(`
|
|
48293
|
+
`);
|
|
48294
|
+
if (lines.length <= maxLines) {
|
|
48295
|
+
return output;
|
|
48296
|
+
}
|
|
48297
|
+
const omittedCount = lines.length - maxLines;
|
|
48298
|
+
const truncated = lines.slice(0, maxLines);
|
|
48299
|
+
const footerLines = [];
|
|
48300
|
+
footerLines.push("");
|
|
48301
|
+
footerLines.push(`[... ${omittedCount} line${omittedCount === 1 ? "" : "s"} omitted ...]`);
|
|
48302
|
+
if (toolName) {
|
|
48303
|
+
footerLines.push(`Tool: ${toolName}`);
|
|
48304
|
+
}
|
|
48305
|
+
footerLines.push("Use /swarm retrieve <id> to get the full content");
|
|
48306
|
+
return truncated.join(`
|
|
48307
|
+
`) + `
|
|
48308
|
+
` + footerLines.join(`
|
|
48309
|
+
`);
|
|
48310
|
+
}
|
|
48311
|
+
|
|
48312
|
+
// src/index.ts
|
|
47337
48313
|
var OpenCodeSwarm = async (ctx) => {
|
|
47338
48314
|
const { config: config3, loadedFromFile } = loadPluginConfigWithMeta(ctx.directory);
|
|
47339
48315
|
const agents = getAgentConfigs(config3);
|
|
@@ -47378,7 +48354,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
47378
48354
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
47379
48355
|
preflightTriggerManager = new PTM(automationConfig);
|
|
47380
48356
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
47381
|
-
const swarmDir =
|
|
48357
|
+
const swarmDir = path32.resolve(ctx.directory, ".swarm");
|
|
47382
48358
|
statusArtifact = new ASA(swarmDir);
|
|
47383
48359
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
47384
48360
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|
|
@@ -47511,7 +48487,13 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
47511
48487
|
pipelineHook["experimental.chat.messages.transform"],
|
|
47512
48488
|
contextBudgetHandler,
|
|
47513
48489
|
guardrailsHooks.messagesTransform,
|
|
47514
|
-
delegationGateHandler
|
|
48490
|
+
delegationGateHandler,
|
|
48491
|
+
(_input, output) => {
|
|
48492
|
+
if (output.messages) {
|
|
48493
|
+
output.messages = consolidateSystemMessages(output.messages);
|
|
48494
|
+
}
|
|
48495
|
+
return Promise.resolve();
|
|
48496
|
+
}
|
|
47515
48497
|
].filter((fn) => Boolean(fn))),
|
|
47516
48498
|
"experimental.chat.system.transform": composeHandlers(...[
|
|
47517
48499
|
systemEnhancerHook["experimental.chat.system.transform"],
|
|
@@ -47542,6 +48524,21 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
47542
48524
|
await activityHooks.toolAfter(input, output);
|
|
47543
48525
|
await guardrailsHooks.toolAfter(input, output);
|
|
47544
48526
|
await toolSummarizerHook?.(input, output);
|
|
48527
|
+
const toolOutputConfig = config3.tool_output;
|
|
48528
|
+
if (toolOutputConfig && toolOutputConfig.truncation_enabled !== false && typeof output.output === "string") {
|
|
48529
|
+
const skipTools = [
|
|
48530
|
+
"pre_check_batch",
|
|
48531
|
+
"pkg_audit",
|
|
48532
|
+
"schema_drift",
|
|
48533
|
+
"sbom_generate"
|
|
48534
|
+
];
|
|
48535
|
+
if (!skipTools.includes(input.tool)) {
|
|
48536
|
+
const maxLines = toolOutputConfig.per_tool?.[input.tool] ?? toolOutputConfig.max_lines ?? 150;
|
|
48537
|
+
if (input.tool === "diff" || input.tool === "symbols") {
|
|
48538
|
+
output.output = truncateToolOutput(output.output, maxLines, input.tool);
|
|
48539
|
+
}
|
|
48540
|
+
}
|
|
48541
|
+
}
|
|
47545
48542
|
if (input.tool === "task") {
|
|
47546
48543
|
const sessionId = input.sessionID;
|
|
47547
48544
|
swarmState.activeAgent.set(sessionId, ORCHESTRATOR_NAME);
|