opencode-swarm 6.28.0 → 6.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +225 -29
- package/dist/config/constants.d.ts +19 -0
- package/dist/config/evidence-schema.d.ts +6 -0
- package/dist/config/schema.d.ts +41 -0
- package/dist/hooks/diff-scope.d.ts +12 -0
- package/dist/hooks/diff-scope.test.d.ts +1 -0
- package/dist/hooks/incremental-verify.d.ts +18 -0
- package/dist/hooks/incremental-verify.test.d.ts +1 -0
- package/dist/hooks/loop-detector.d.ts +17 -0
- package/dist/hooks/loop-detector.test.d.ts +1 -0
- package/dist/hooks/slop-detector.d.ts +17 -0
- package/dist/hooks/slop-detector.test.d.ts +1 -0
- package/dist/index.js +1199 -476
- package/dist/services/compaction-service.d.ts +23 -0
- package/dist/services/compaction-service.test.d.ts +1 -0
- package/dist/services/status-service.d.ts +6 -0
- package/dist/state.d.ts +15 -0
- package/dist/tools/lint.d.ts +7 -1
- package/dist/tools/test-runner.d.ts +1 -1
- package/dist/tools/update-task-status.d.ts +8 -0
- package/dist/tools/write-retro.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14217,14 +14217,16 @@ var init_evidence_schema = __esm(() => {
|
|
|
14217
14217
|
});
|
|
14218
14218
|
RetrospectiveEvidenceSchema = BaseEvidenceSchema.extend({
|
|
14219
14219
|
type: exports_external.literal("retrospective"),
|
|
14220
|
-
phase_number: exports_external.number().int().min(0),
|
|
14221
|
-
total_tool_calls: exports_external.number().int().min(0),
|
|
14222
|
-
coder_revisions: exports_external.number().int().min(0),
|
|
14223
|
-
reviewer_rejections: exports_external.number().int().min(0),
|
|
14224
|
-
|
|
14225
|
-
|
|
14226
|
-
|
|
14227
|
-
|
|
14220
|
+
phase_number: exports_external.number().int().min(0).max(99),
|
|
14221
|
+
total_tool_calls: exports_external.number().int().min(0).max(9999),
|
|
14222
|
+
coder_revisions: exports_external.number().int().min(0).max(999),
|
|
14223
|
+
reviewer_rejections: exports_external.number().int().min(0).max(999),
|
|
14224
|
+
loop_detections: exports_external.number().int().min(0).max(9999).optional(),
|
|
14225
|
+
circuit_breaker_trips: exports_external.number().int().min(0).max(9999).optional(),
|
|
14226
|
+
test_failures: exports_external.number().int().min(0).max(9999),
|
|
14227
|
+
security_findings: exports_external.number().int().min(0).max(999),
|
|
14228
|
+
integration_issues: exports_external.number().int().min(0).max(999),
|
|
14229
|
+
task_count: exports_external.number().int().min(1).max(9999),
|
|
14228
14230
|
task_complexity: exports_external.enum(["trivial", "simple", "moderate", "complex"]),
|
|
14229
14231
|
top_rejection_reasons: exports_external.array(exports_external.string()).default([]),
|
|
14230
14232
|
lessons_learned: exports_external.array(exports_external.string()).max(5).default([]),
|
|
@@ -14435,7 +14437,7 @@ function resolveGuardrailsConfig(config2, agentName) {
|
|
|
14435
14437
|
};
|
|
14436
14438
|
return resolved;
|
|
14437
14439
|
}
|
|
14438
|
-
var KNOWN_SWARM_PREFIXES, SEPARATORS, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, CuratorConfigSchema, PluginConfigSchema;
|
|
14440
|
+
var KNOWN_SWARM_PREFIXES, SEPARATORS, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, CuratorConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PluginConfigSchema;
|
|
14439
14441
|
var init_schema = __esm(() => {
|
|
14440
14442
|
init_zod();
|
|
14441
14443
|
init_constants();
|
|
@@ -14872,6 +14874,25 @@ var init_schema = __esm(() => {
|
|
|
14872
14874
|
suppress_warnings: exports_external.boolean().default(true),
|
|
14873
14875
|
drift_inject_max_chars: exports_external.number().min(100).max(2000).default(500)
|
|
14874
14876
|
});
|
|
14877
|
+
SlopDetectorConfigSchema = exports_external.object({
|
|
14878
|
+
enabled: exports_external.boolean().default(true),
|
|
14879
|
+
classThreshold: exports_external.number().int().min(1).default(3),
|
|
14880
|
+
commentStripThreshold: exports_external.number().int().min(1).default(5),
|
|
14881
|
+
diffLineThreshold: exports_external.number().int().min(10).default(200)
|
|
14882
|
+
});
|
|
14883
|
+
IncrementalVerifyConfigSchema = exports_external.object({
|
|
14884
|
+
enabled: exports_external.boolean().default(true),
|
|
14885
|
+
command: exports_external.string().nullable().default(null),
|
|
14886
|
+
timeoutMs: exports_external.number().int().min(1000).max(300000).default(30000),
|
|
14887
|
+
triggerAgents: exports_external.array(exports_external.string()).default(["coder"])
|
|
14888
|
+
});
|
|
14889
|
+
CompactionConfigSchema = exports_external.object({
|
|
14890
|
+
enabled: exports_external.boolean().default(true),
|
|
14891
|
+
observationThreshold: exports_external.number().min(1).max(99).default(40),
|
|
14892
|
+
reflectionThreshold: exports_external.number().min(1).max(99).default(60),
|
|
14893
|
+
emergencyThreshold: exports_external.number().min(1).max(99).default(80),
|
|
14894
|
+
preserveLastNTurns: exports_external.number().int().min(1).default(5)
|
|
14895
|
+
});
|
|
14875
14896
|
PluginConfigSchema = exports_external.object({
|
|
14876
14897
|
agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
|
|
14877
14898
|
swarms: exports_external.record(exports_external.string(), SwarmConfigSchema).optional(),
|
|
@@ -14905,7 +14926,10 @@ var init_schema = __esm(() => {
|
|
|
14905
14926
|
truncation_enabled: exports_external.boolean().default(true),
|
|
14906
14927
|
max_lines: exports_external.number().min(10).max(500).default(150),
|
|
14907
14928
|
per_tool: exports_external.record(exports_external.string(), exports_external.number()).optional()
|
|
14908
|
-
}).optional()
|
|
14929
|
+
}).optional(),
|
|
14930
|
+
slop_detector: SlopDetectorConfigSchema.optional(),
|
|
14931
|
+
incremental_verify: IncrementalVerifyConfigSchema.optional(),
|
|
14932
|
+
compaction_service: CompactionConfigSchema.optional()
|
|
14909
14933
|
});
|
|
14910
14934
|
});
|
|
14911
14935
|
|
|
@@ -17112,11 +17136,12 @@ class PreflightTriggerManager {
|
|
|
17112
17136
|
this.unsubscribe = this.eventBus.subscribe("preflight.requested", async (event) => {
|
|
17113
17137
|
if (this.preflightHandler) {
|
|
17114
17138
|
const request = event.payload;
|
|
17139
|
+
let timeoutHandle;
|
|
17115
17140
|
try {
|
|
17116
17141
|
await Promise.race([
|
|
17117
17142
|
this.preflightHandler(request),
|
|
17118
17143
|
new Promise((_, reject) => {
|
|
17119
|
-
setTimeout(() => {
|
|
17144
|
+
timeoutHandle = setTimeout(() => {
|
|
17120
17145
|
reject(new Error(`Preflight handler timed out after ${HANDLER_TIMEOUT_MS}ms`));
|
|
17121
17146
|
}, HANDLER_TIMEOUT_MS);
|
|
17122
17147
|
})
|
|
@@ -17127,6 +17152,8 @@ class PreflightTriggerManager {
|
|
|
17127
17152
|
phase: request.currentPhase,
|
|
17128
17153
|
errorType: error49 instanceof Error ? error49.name : "unknown"
|
|
17129
17154
|
});
|
|
17155
|
+
} finally {
|
|
17156
|
+
clearTimeout(timeoutHandle);
|
|
17130
17157
|
}
|
|
17131
17158
|
}
|
|
17132
17159
|
});
|
|
@@ -33386,7 +33413,15 @@ function detectAdditionalLinter(cwd) {
|
|
|
33386
33413
|
return "rubocop";
|
|
33387
33414
|
return null;
|
|
33388
33415
|
}
|
|
33389
|
-
async function detectAvailableLinter() {
|
|
33416
|
+
async function detectAvailableLinter(directory) {
|
|
33417
|
+
const DETECT_TIMEOUT = 2000;
|
|
33418
|
+
const projectDir = directory ?? process.cwd();
|
|
33419
|
+
const isWindows = process.platform === "win32";
|
|
33420
|
+
const biomeBin = isWindows ? path21.join(projectDir, "node_modules", ".bin", "biome.EXE") : path21.join(projectDir, "node_modules", ".bin", "biome");
|
|
33421
|
+
const eslintBin = isWindows ? path21.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path21.join(projectDir, "node_modules", ".bin", "eslint");
|
|
33422
|
+
return _detectAvailableLinter(projectDir, biomeBin, eslintBin);
|
|
33423
|
+
}
|
|
33424
|
+
async function _detectAvailableLinter(projectDir, biomeBin, eslintBin) {
|
|
33390
33425
|
const DETECT_TIMEOUT = 2000;
|
|
33391
33426
|
try {
|
|
33392
33427
|
const biomeProc = Bun.spawn(["npx", "biome", "--version"], {
|
|
@@ -33398,7 +33433,7 @@ async function detectAvailableLinter() {
|
|
|
33398
33433
|
const result = await Promise.race([biomeExit, timeout]);
|
|
33399
33434
|
if (result === "timeout") {
|
|
33400
33435
|
biomeProc.kill();
|
|
33401
|
-
} else if (biomeProc.exitCode === 0) {
|
|
33436
|
+
} else if (biomeProc.exitCode === 0 && fs9.existsSync(biomeBin)) {
|
|
33402
33437
|
return "biome";
|
|
33403
33438
|
}
|
|
33404
33439
|
} catch {}
|
|
@@ -33412,7 +33447,7 @@ async function detectAvailableLinter() {
|
|
|
33412
33447
|
const result = await Promise.race([eslintExit, timeout]);
|
|
33413
33448
|
if (result === "timeout") {
|
|
33414
33449
|
eslintProc.kill();
|
|
33415
|
-
} else if (eslintProc.exitCode === 0) {
|
|
33450
|
+
} else if (eslintProc.exitCode === 0 && fs9.existsSync(eslintBin)) {
|
|
33416
33451
|
return "eslint";
|
|
33417
33452
|
}
|
|
33418
33453
|
} catch {}
|
|
@@ -33564,7 +33599,7 @@ var init_lint = __esm(() => {
|
|
|
33564
33599
|
}
|
|
33565
33600
|
const { mode } = args2;
|
|
33566
33601
|
const cwd = directory;
|
|
33567
|
-
const linter = await detectAvailableLinter();
|
|
33602
|
+
const linter = await detectAvailableLinter(directory);
|
|
33568
33603
|
if (linter) {
|
|
33569
33604
|
const result = await runLint(linter, mode, directory);
|
|
33570
33605
|
return JSON.stringify(result, null, 2);
|
|
@@ -34181,10 +34216,7 @@ var init_secretscan = __esm(() => {
|
|
|
34181
34216
|
const excludeExact = new Set(DEFAULT_EXCLUDE_DIRS);
|
|
34182
34217
|
const excludeGlobs = [];
|
|
34183
34218
|
const ignoreFilePatterns = loadSecretScanIgnore(scanDir);
|
|
34184
|
-
const allUserPatterns = [
|
|
34185
|
-
...exclude ?? [],
|
|
34186
|
-
...ignoreFilePatterns
|
|
34187
|
-
];
|
|
34219
|
+
const allUserPatterns = [...exclude ?? [], ...ignoreFilePatterns];
|
|
34188
34220
|
for (const exc of allUserPatterns) {
|
|
34189
34221
|
if (exc.length === 0)
|
|
34190
34222
|
continue;
|
|
@@ -34427,7 +34459,7 @@ function detectMinitest(cwd) {
|
|
|
34427
34459
|
return fs11.existsSync(path23.join(cwd, "test")) && (fs11.existsSync(path23.join(cwd, "Gemfile")) || fs11.existsSync(path23.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
34428
34460
|
}
|
|
34429
34461
|
async function detectTestFramework(cwd) {
|
|
34430
|
-
const baseDir = cwd
|
|
34462
|
+
const baseDir = cwd;
|
|
34431
34463
|
try {
|
|
34432
34464
|
const packageJsonPath = path23.join(baseDir, "package.json");
|
|
34433
34465
|
if (fs11.existsSync(packageJsonPath)) {
|
|
@@ -37569,11 +37601,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
37569
37601
|
throw toThrow;
|
|
37570
37602
|
}, "quit_");
|
|
37571
37603
|
var scriptDirectory = "";
|
|
37572
|
-
function locateFile(
|
|
37604
|
+
function locateFile(path45) {
|
|
37573
37605
|
if (Module["locateFile"]) {
|
|
37574
|
-
return Module["locateFile"](
|
|
37606
|
+
return Module["locateFile"](path45, scriptDirectory);
|
|
37575
37607
|
}
|
|
37576
|
-
return scriptDirectory +
|
|
37608
|
+
return scriptDirectory + path45;
|
|
37577
37609
|
}
|
|
37578
37610
|
__name(locateFile, "locateFile");
|
|
37579
37611
|
var readAsync, readBinary;
|
|
@@ -39321,7 +39353,7 @@ var init_runtime = __esm(() => {
|
|
|
39321
39353
|
});
|
|
39322
39354
|
|
|
39323
39355
|
// src/index.ts
|
|
39324
|
-
import * as
|
|
39356
|
+
import * as path55 from "path";
|
|
39325
39357
|
|
|
39326
39358
|
// src/agents/index.ts
|
|
39327
39359
|
init_config();
|
|
@@ -39340,6 +39372,7 @@ var swarmState = {
|
|
|
39340
39372
|
activeAgent: new Map,
|
|
39341
39373
|
delegationChains: new Map,
|
|
39342
39374
|
pendingEvents: 0,
|
|
39375
|
+
lastBudgetPct: 0,
|
|
39343
39376
|
agentSessions: new Map
|
|
39344
39377
|
};
|
|
39345
39378
|
function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, directory) {
|
|
@@ -39384,7 +39417,8 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
|
|
|
39384
39417
|
lastScopeViolation: null,
|
|
39385
39418
|
scopeViolationDetected: false,
|
|
39386
39419
|
modifiedFilesThisCoderTask: [],
|
|
39387
|
-
turboMode: false
|
|
39420
|
+
turboMode: false,
|
|
39421
|
+
loopDetectionWindow: []
|
|
39388
39422
|
};
|
|
39389
39423
|
swarmState.agentSessions.set(sessionId, sessionState);
|
|
39390
39424
|
swarmState.activeAgent.set(sessionId, agentName);
|
|
@@ -39486,6 +39520,9 @@ function ensureAgentSession(sessionId, agentName, directory) {
|
|
|
39486
39520
|
if (session.turboMode === undefined) {
|
|
39487
39521
|
session.turboMode = false;
|
|
39488
39522
|
}
|
|
39523
|
+
if (session.loopDetectionWindow === undefined) {
|
|
39524
|
+
session.loopDetectionWindow = [];
|
|
39525
|
+
}
|
|
39489
39526
|
session.lastToolCallTime = now;
|
|
39490
39527
|
return session;
|
|
39491
39528
|
}
|
|
@@ -47570,6 +47607,173 @@ No plan content available. Start by creating a .swarm/plan.md file.
|
|
|
47570
47607
|
// src/services/status-service.ts
|
|
47571
47608
|
init_utils2();
|
|
47572
47609
|
init_manager2();
|
|
47610
|
+
|
|
47611
|
+
// src/services/context-budget-service.ts
|
|
47612
|
+
init_utils2();
|
|
47613
|
+
function validateDirectory(directory) {
|
|
47614
|
+
if (!directory || directory.trim() === "") {
|
|
47615
|
+
throw new Error("Invalid directory: empty");
|
|
47616
|
+
}
|
|
47617
|
+
if (/\.\.[/\\]/.test(directory)) {
|
|
47618
|
+
throw new Error("Invalid directory: path traversal detected");
|
|
47619
|
+
}
|
|
47620
|
+
if (directory.startsWith("/") || directory.startsWith("\\")) {
|
|
47621
|
+
throw new Error("Invalid directory: absolute path");
|
|
47622
|
+
}
|
|
47623
|
+
if (/^[A-Za-z]:[\\/]/.test(directory)) {
|
|
47624
|
+
throw new Error("Invalid directory: Windows absolute path");
|
|
47625
|
+
}
|
|
47626
|
+
}
|
|
47627
|
+
var DEFAULT_CONTEXT_BUDGET_CONFIG = {
|
|
47628
|
+
enabled: true,
|
|
47629
|
+
budgetTokens: 40000,
|
|
47630
|
+
warningPct: 70,
|
|
47631
|
+
criticalPct: 90,
|
|
47632
|
+
warningMode: "once",
|
|
47633
|
+
warningIntervalTurns: 20
|
|
47634
|
+
};
|
|
47635
|
+
var COST_PER_1K_TOKENS = 0.003;
|
|
47636
|
+
function estimateTokens2(text) {
|
|
47637
|
+
if (!text || typeof text !== "string") {
|
|
47638
|
+
return 0;
|
|
47639
|
+
}
|
|
47640
|
+
return Math.ceil(text.length / 3.5);
|
|
47641
|
+
}
|
|
47642
|
+
async function readBudgetState(directory) {
|
|
47643
|
+
const content = await readSwarmFileAsync(directory, "session/budget-state.json");
|
|
47644
|
+
if (!content) {
|
|
47645
|
+
return null;
|
|
47646
|
+
}
|
|
47647
|
+
try {
|
|
47648
|
+
return JSON.parse(content);
|
|
47649
|
+
} catch {
|
|
47650
|
+
return null;
|
|
47651
|
+
}
|
|
47652
|
+
}
|
|
47653
|
+
async function writeBudgetState(directory, state) {
|
|
47654
|
+
const resolvedPath = validateSwarmPath(directory, "session/budget-state.json");
|
|
47655
|
+
const content = JSON.stringify(state, null, 2);
|
|
47656
|
+
await Bun.write(resolvedPath, content);
|
|
47657
|
+
}
|
|
47658
|
+
async function countEvents(directory) {
|
|
47659
|
+
const content = await readSwarmFileAsync(directory, "events.jsonl");
|
|
47660
|
+
if (!content) {
|
|
47661
|
+
return 0;
|
|
47662
|
+
}
|
|
47663
|
+
const lines = content.split(`
|
|
47664
|
+
`).filter((line) => line.trim().length > 0);
|
|
47665
|
+
return lines.length;
|
|
47666
|
+
}
|
|
47667
|
+
async function getPlanCursorContent(directory) {
|
|
47668
|
+
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
47669
|
+
if (!planContent) {
|
|
47670
|
+
return "";
|
|
47671
|
+
}
|
|
47672
|
+
const lines = planContent.split(`
|
|
47673
|
+
`);
|
|
47674
|
+
const cursorLines = [];
|
|
47675
|
+
let inCurrentSection = false;
|
|
47676
|
+
for (const line of lines) {
|
|
47677
|
+
if (line.includes("in_progress") || line.includes("**Current**")) {
|
|
47678
|
+
inCurrentSection = true;
|
|
47679
|
+
}
|
|
47680
|
+
if (inCurrentSection) {
|
|
47681
|
+
cursorLines.push(line);
|
|
47682
|
+
if (cursorLines.length > 30) {
|
|
47683
|
+
break;
|
|
47684
|
+
}
|
|
47685
|
+
}
|
|
47686
|
+
}
|
|
47687
|
+
return cursorLines.join(`
|
|
47688
|
+
`) || planContent.substring(0, 1000);
|
|
47689
|
+
}
|
|
47690
|
+
async function getContextBudgetReport(directory, assembledSystemPrompt, config3) {
|
|
47691
|
+
validateDirectory(directory);
|
|
47692
|
+
const timestamp = new Date().toISOString();
|
|
47693
|
+
const systemPromptTokens = estimateTokens2(assembledSystemPrompt);
|
|
47694
|
+
const planCursorContent = await getPlanCursorContent(directory);
|
|
47695
|
+
const planCursorTokens = estimateTokens2(planCursorContent);
|
|
47696
|
+
const knowledgeContent = await readSwarmFileAsync(directory, "knowledge.jsonl");
|
|
47697
|
+
const knowledgeTokens = estimateTokens2(knowledgeContent || "");
|
|
47698
|
+
const runMemoryContent = await readSwarmFileAsync(directory, "run-memory.jsonl");
|
|
47699
|
+
const runMemoryTokens = estimateTokens2(runMemoryContent || "");
|
|
47700
|
+
const handoffContent = await readSwarmFileAsync(directory, "handoff.md");
|
|
47701
|
+
const handoffTokens = estimateTokens2(handoffContent || "");
|
|
47702
|
+
const contextMdContent = await readSwarmFileAsync(directory, "context.md");
|
|
47703
|
+
const contextMdTokens = estimateTokens2(contextMdContent || "");
|
|
47704
|
+
const swarmTotalTokens = systemPromptTokens + planCursorTokens + knowledgeTokens + runMemoryTokens + handoffTokens + contextMdTokens;
|
|
47705
|
+
const estimatedTurnCount = await countEvents(directory);
|
|
47706
|
+
const budgetPct = swarmTotalTokens / config3.budgetTokens * 100;
|
|
47707
|
+
let status;
|
|
47708
|
+
let recommendation = null;
|
|
47709
|
+
if (budgetPct < config3.warningPct) {
|
|
47710
|
+
status = "ok";
|
|
47711
|
+
} else if (budgetPct < config3.criticalPct) {
|
|
47712
|
+
status = "warning";
|
|
47713
|
+
recommendation = "Consider wrapping up current phase and running /swarm handoff before starting new work.";
|
|
47714
|
+
} else {
|
|
47715
|
+
status = "critical";
|
|
47716
|
+
recommendation = "Run /swarm handoff and start a new session to avoid cost escalation.";
|
|
47717
|
+
}
|
|
47718
|
+
const estimatedSessionTokens = swarmTotalTokens * Math.max(1, estimatedTurnCount);
|
|
47719
|
+
return {
|
|
47720
|
+
timestamp,
|
|
47721
|
+
systemPromptTokens,
|
|
47722
|
+
planCursorTokens,
|
|
47723
|
+
knowledgeTokens,
|
|
47724
|
+
runMemoryTokens,
|
|
47725
|
+
handoffTokens,
|
|
47726
|
+
contextMdTokens,
|
|
47727
|
+
swarmTotalTokens,
|
|
47728
|
+
estimatedTurnCount,
|
|
47729
|
+
estimatedSessionTokens,
|
|
47730
|
+
budgetPct,
|
|
47731
|
+
status,
|
|
47732
|
+
recommendation
|
|
47733
|
+
};
|
|
47734
|
+
}
|
|
47735
|
+
async function formatBudgetWarning(report, directory, config3) {
|
|
47736
|
+
validateDirectory(directory);
|
|
47737
|
+
if (report.status === "ok") {
|
|
47738
|
+
return null;
|
|
47739
|
+
}
|
|
47740
|
+
if (!directory || directory.trim() === "") {
|
|
47741
|
+
return formatWarningMessage(report);
|
|
47742
|
+
}
|
|
47743
|
+
const budgetState = await readBudgetState(directory);
|
|
47744
|
+
const state = budgetState || {
|
|
47745
|
+
warningFiredAtTurn: null,
|
|
47746
|
+
criticalFiredAtTurn: null,
|
|
47747
|
+
lastInjectedAtTurn: null
|
|
47748
|
+
};
|
|
47749
|
+
const currentTurn = report.estimatedTurnCount;
|
|
47750
|
+
if (report.status === "warning") {
|
|
47751
|
+
if (config3.warningMode === "once" && state.warningFiredAtTurn !== null) {
|
|
47752
|
+
return null;
|
|
47753
|
+
}
|
|
47754
|
+
if (config3.warningMode === "interval" && state.warningFiredAtTurn !== null && currentTurn - state.warningFiredAtTurn < config3.warningIntervalTurns) {
|
|
47755
|
+
return null;
|
|
47756
|
+
}
|
|
47757
|
+
state.warningFiredAtTurn = currentTurn;
|
|
47758
|
+
state.lastInjectedAtTurn = currentTurn;
|
|
47759
|
+
await writeBudgetState(directory, state);
|
|
47760
|
+
} else if (report.status === "critical") {
|
|
47761
|
+
state.criticalFiredAtTurn = currentTurn;
|
|
47762
|
+
state.lastInjectedAtTurn = currentTurn;
|
|
47763
|
+
}
|
|
47764
|
+
return formatWarningMessage(report);
|
|
47765
|
+
}
|
|
47766
|
+
function formatWarningMessage(report) {
|
|
47767
|
+
const budgetPctStr = report.budgetPct.toFixed(1);
|
|
47768
|
+
const tokensPerTurn = report.swarmTotalTokens.toLocaleString();
|
|
47769
|
+
if (report.status === "warning") {
|
|
47770
|
+
return `[CONTEXT BUDGET: ${budgetPctStr}% \u2014 swarm injecting ~${tokensPerTurn} tokens/turn. Consider wrapping current phase and running /swarm handoff before starting new work.]`;
|
|
47771
|
+
}
|
|
47772
|
+
const costPerTurn = (report.swarmTotalTokens / 1000 * COST_PER_1K_TOKENS).toFixed(3);
|
|
47773
|
+
return `[CONTEXT BUDGET: ${budgetPctStr}% CRITICAL \u2014 swarm injecting ~${tokensPerTurn} tokens/turn. Run /swarm handoff and start a new session to avoid cost escalation. Estimated session cost scaling: ~$${costPerTurn}/turn at current context size.]`;
|
|
47774
|
+
}
|
|
47775
|
+
|
|
47776
|
+
// src/services/status-service.ts
|
|
47573
47777
|
async function getStatusData(directory, agents) {
|
|
47574
47778
|
const plan = await loadPlan(directory);
|
|
47575
47779
|
if (plan && plan.migration_status !== "migration_failed") {
|
|
@@ -47591,7 +47795,10 @@ async function getStatusData(directory, agents) {
|
|
|
47591
47795
|
totalTasks: totalTasks2,
|
|
47592
47796
|
agentCount: agentCount2,
|
|
47593
47797
|
isLegacy: false,
|
|
47594
|
-
turboMode: hasActiveTurboMode()
|
|
47798
|
+
turboMode: hasActiveTurboMode(),
|
|
47799
|
+
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47800
|
+
compactionCount: 0,
|
|
47801
|
+
lastSnapshotAt: null
|
|
47595
47802
|
};
|
|
47596
47803
|
}
|
|
47597
47804
|
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
@@ -47603,7 +47810,10 @@ async function getStatusData(directory, agents) {
|
|
|
47603
47810
|
totalTasks: 0,
|
|
47604
47811
|
agentCount: Object.keys(agents).length,
|
|
47605
47812
|
isLegacy: true,
|
|
47606
|
-
turboMode: hasActiveTurboMode()
|
|
47813
|
+
turboMode: hasActiveTurboMode(),
|
|
47814
|
+
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47815
|
+
compactionCount: 0,
|
|
47816
|
+
lastSnapshotAt: null
|
|
47607
47817
|
};
|
|
47608
47818
|
}
|
|
47609
47819
|
const currentPhase = extractCurrentPhase(planContent) || "Unknown";
|
|
@@ -47618,7 +47828,10 @@ async function getStatusData(directory, agents) {
|
|
|
47618
47828
|
totalTasks,
|
|
47619
47829
|
agentCount,
|
|
47620
47830
|
isLegacy: true,
|
|
47621
|
-
turboMode: hasActiveTurboMode()
|
|
47831
|
+
turboMode: hasActiveTurboMode(),
|
|
47832
|
+
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47833
|
+
compactionCount: 0,
|
|
47834
|
+
lastSnapshotAt: null
|
|
47622
47835
|
};
|
|
47623
47836
|
}
|
|
47624
47837
|
function formatStatusMarkdown(status) {
|
|
@@ -47632,6 +47845,18 @@ function formatStatusMarkdown(status) {
|
|
|
47632
47845
|
if (status.turboMode) {
|
|
47633
47846
|
lines.push("", `**TURBO MODE**: active`);
|
|
47634
47847
|
}
|
|
47848
|
+
if (status.contextBudgetPct !== null && status.contextBudgetPct > 0) {
|
|
47849
|
+
const pct = status.contextBudgetPct.toFixed(1);
|
|
47850
|
+
const budgetTokens = DEFAULT_CONTEXT_BUDGET_CONFIG.budgetTokens;
|
|
47851
|
+
const est = Math.round(status.contextBudgetPct / 100 * budgetTokens);
|
|
47852
|
+
lines.push("", `**Context**: ${pct}% used (est. ${est.toLocaleString()} / ${budgetTokens.toLocaleString()} tokens)`);
|
|
47853
|
+
if (status.compactionCount > 0) {
|
|
47854
|
+
lines.push(`**Compaction events**: ${status.compactionCount} triggered`);
|
|
47855
|
+
}
|
|
47856
|
+
if (status.lastSnapshotAt) {
|
|
47857
|
+
lines.push(`**Last snapshot**: ${status.lastSnapshotAt}`);
|
|
47858
|
+
}
|
|
47859
|
+
}
|
|
47635
47860
|
return lines.join(`
|
|
47636
47861
|
`);
|
|
47637
47862
|
}
|
|
@@ -47726,6 +47951,132 @@ async function executeWriteRetro(args2, directory) {
|
|
|
47726
47951
|
message: "Invalid task_count: must be a positive integer >= 1"
|
|
47727
47952
|
}, null, 2);
|
|
47728
47953
|
}
|
|
47954
|
+
if (!Number.isInteger(args2.total_tool_calls) || args2.total_tool_calls < 0) {
|
|
47955
|
+
return JSON.stringify({
|
|
47956
|
+
success: false,
|
|
47957
|
+
phase,
|
|
47958
|
+
message: "Invalid total_tool_calls: must be a non-negative integer"
|
|
47959
|
+
}, null, 2);
|
|
47960
|
+
}
|
|
47961
|
+
if (!Number.isInteger(args2.coder_revisions) || args2.coder_revisions < 0) {
|
|
47962
|
+
return JSON.stringify({
|
|
47963
|
+
success: false,
|
|
47964
|
+
phase,
|
|
47965
|
+
message: "Invalid coder_revisions: must be a non-negative integer"
|
|
47966
|
+
}, null, 2);
|
|
47967
|
+
}
|
|
47968
|
+
if (!Number.isInteger(args2.reviewer_rejections) || args2.reviewer_rejections < 0) {
|
|
47969
|
+
return JSON.stringify({
|
|
47970
|
+
success: false,
|
|
47971
|
+
phase,
|
|
47972
|
+
message: "Invalid reviewer_rejections: must be a non-negative integer"
|
|
47973
|
+
}, null, 2);
|
|
47974
|
+
}
|
|
47975
|
+
if (!Number.isInteger(args2.test_failures) || args2.test_failures < 0) {
|
|
47976
|
+
return JSON.stringify({
|
|
47977
|
+
success: false,
|
|
47978
|
+
phase,
|
|
47979
|
+
message: "Invalid test_failures: must be a non-negative integer"
|
|
47980
|
+
}, null, 2);
|
|
47981
|
+
}
|
|
47982
|
+
if (!Number.isInteger(args2.security_findings) || args2.security_findings < 0) {
|
|
47983
|
+
return JSON.stringify({
|
|
47984
|
+
success: false,
|
|
47985
|
+
phase,
|
|
47986
|
+
message: "Invalid security_findings: must be a non-negative integer"
|
|
47987
|
+
}, null, 2);
|
|
47988
|
+
}
|
|
47989
|
+
if (!Number.isInteger(args2.integration_issues) || args2.integration_issues < 0) {
|
|
47990
|
+
return JSON.stringify({
|
|
47991
|
+
success: false,
|
|
47992
|
+
phase,
|
|
47993
|
+
message: "Invalid integration_issues: must be a non-negative integer"
|
|
47994
|
+
}, null, 2);
|
|
47995
|
+
}
|
|
47996
|
+
if (args2.loop_detections !== undefined && (!Number.isInteger(args2.loop_detections) || args2.loop_detections < 0)) {
|
|
47997
|
+
return JSON.stringify({
|
|
47998
|
+
success: false,
|
|
47999
|
+
phase,
|
|
48000
|
+
message: "Invalid loop_detections: must be a non-negative integer"
|
|
48001
|
+
}, null, 2);
|
|
48002
|
+
}
|
|
48003
|
+
if (args2.circuit_breaker_trips !== undefined && (!Number.isInteger(args2.circuit_breaker_trips) || args2.circuit_breaker_trips < 0)) {
|
|
48004
|
+
return JSON.stringify({
|
|
48005
|
+
success: false,
|
|
48006
|
+
phase,
|
|
48007
|
+
message: "Invalid circuit_breaker_trips: must be a non-negative integer"
|
|
48008
|
+
}, null, 2);
|
|
48009
|
+
}
|
|
48010
|
+
if (args2.phase > 99) {
|
|
48011
|
+
return JSON.stringify({
|
|
48012
|
+
success: false,
|
|
48013
|
+
phase,
|
|
48014
|
+
message: "Invalid phase: must be <= 99"
|
|
48015
|
+
}, null, 2);
|
|
48016
|
+
}
|
|
48017
|
+
if (args2.task_count > 9999) {
|
|
48018
|
+
return JSON.stringify({
|
|
48019
|
+
success: false,
|
|
48020
|
+
phase,
|
|
48021
|
+
message: "Invalid task_count: must be <= 9999"
|
|
48022
|
+
}, null, 2);
|
|
48023
|
+
}
|
|
48024
|
+
if (args2.total_tool_calls > 9999) {
|
|
48025
|
+
return JSON.stringify({
|
|
48026
|
+
success: false,
|
|
48027
|
+
phase,
|
|
48028
|
+
message: "Invalid total_tool_calls: must be <= 9999"
|
|
48029
|
+
}, null, 2);
|
|
48030
|
+
}
|
|
48031
|
+
if (args2.coder_revisions > 999) {
|
|
48032
|
+
return JSON.stringify({
|
|
48033
|
+
success: false,
|
|
48034
|
+
phase,
|
|
48035
|
+
message: "Invalid coder_revisions: must be <= 999"
|
|
48036
|
+
}, null, 2);
|
|
48037
|
+
}
|
|
48038
|
+
if (args2.reviewer_rejections > 999) {
|
|
48039
|
+
return JSON.stringify({
|
|
48040
|
+
success: false,
|
|
48041
|
+
phase,
|
|
48042
|
+
message: "Invalid reviewer_rejections: must be <= 999"
|
|
48043
|
+
}, null, 2);
|
|
48044
|
+
}
|
|
48045
|
+
if (args2.loop_detections !== undefined && args2.loop_detections > 9999) {
|
|
48046
|
+
return JSON.stringify({
|
|
48047
|
+
success: false,
|
|
48048
|
+
phase,
|
|
48049
|
+
message: "Invalid loop_detections: must be <= 9999"
|
|
48050
|
+
}, null, 2);
|
|
48051
|
+
}
|
|
48052
|
+
if (args2.circuit_breaker_trips !== undefined && args2.circuit_breaker_trips > 9999) {
|
|
48053
|
+
return JSON.stringify({
|
|
48054
|
+
success: false,
|
|
48055
|
+
phase,
|
|
48056
|
+
message: "Invalid circuit_breaker_trips: must be <= 9999"
|
|
48057
|
+
}, null, 2);
|
|
48058
|
+
}
|
|
48059
|
+
if (args2.test_failures > 9999) {
|
|
48060
|
+
return JSON.stringify({
|
|
48061
|
+
success: false,
|
|
48062
|
+
phase,
|
|
48063
|
+
message: "Invalid test_failures: must be <= 9999"
|
|
48064
|
+
}, null, 2);
|
|
48065
|
+
}
|
|
48066
|
+
if (args2.security_findings > 999) {
|
|
48067
|
+
return JSON.stringify({
|
|
48068
|
+
success: false,
|
|
48069
|
+
phase,
|
|
48070
|
+
message: "Invalid security_findings: must be <= 999"
|
|
48071
|
+
}, null, 2);
|
|
48072
|
+
}
|
|
48073
|
+
if (args2.integration_issues > 999) {
|
|
48074
|
+
return JSON.stringify({
|
|
48075
|
+
success: false,
|
|
48076
|
+
phase,
|
|
48077
|
+
message: "Invalid integration_issues: must be <= 999"
|
|
48078
|
+
}, null, 2);
|
|
48079
|
+
}
|
|
47729
48080
|
const summary = args2.summary;
|
|
47730
48081
|
if (typeof summary !== "string" || summary.trim().length === 0) {
|
|
47731
48082
|
return JSON.stringify({
|
|
@@ -47747,6 +48098,8 @@ async function executeWriteRetro(args2, directory) {
|
|
|
47747
48098
|
total_tool_calls: args2.total_tool_calls,
|
|
47748
48099
|
coder_revisions: args2.coder_revisions,
|
|
47749
48100
|
reviewer_rejections: args2.reviewer_rejections,
|
|
48101
|
+
loop_detections: args2.loop_detections,
|
|
48102
|
+
circuit_breaker_trips: args2.circuit_breaker_trips,
|
|
47750
48103
|
test_failures: args2.test_failures,
|
|
47751
48104
|
security_findings: args2.security_findings,
|
|
47752
48105
|
integration_issues: args2.integration_issues,
|
|
@@ -47776,16 +48129,18 @@ async function executeWriteRetro(args2, directory) {
|
|
|
47776
48129
|
var write_retro = createSwarmTool({
|
|
47777
48130
|
description: "Write a retrospective evidence bundle for a completed phase. " + "Accepts flat retro fields and writes a correctly-wrapped EvidenceBundle to " + ".swarm/evidence/retro-{phase}/evidence.json. " + "Use this instead of manually writing retro JSON to avoid schema validation failures in phase_complete.",
|
|
47778
48131
|
args: {
|
|
47779
|
-
phase: tool.schema.number().int().positive().describe("The phase number being completed (e.g., 1, 2, 3)"),
|
|
48132
|
+
phase: tool.schema.number().int().positive().max(99).describe("The phase number being completed (e.g., 1, 2, 3)"),
|
|
47780
48133
|
summary: tool.schema.string().describe("Human-readable summary of the phase"),
|
|
47781
|
-
task_count: tool.schema.number().int().min(1).describe("Count of tasks completed in this phase"),
|
|
48134
|
+
task_count: tool.schema.number().int().min(1).max(9999).describe("Count of tasks completed in this phase"),
|
|
47782
48135
|
task_complexity: tool.schema.enum(["trivial", "simple", "moderate", "complex"]).describe("Complexity level of the completed tasks"),
|
|
47783
|
-
total_tool_calls: tool.schema.number().int().min(0).describe("Total number of tool calls in this phase"),
|
|
47784
|
-
coder_revisions: tool.schema.number().int().min(0).describe("Number of coder revisions made"),
|
|
47785
|
-
reviewer_rejections: tool.schema.number().int().min(0).describe("Number of reviewer rejections received"),
|
|
47786
|
-
|
|
47787
|
-
|
|
47788
|
-
|
|
48136
|
+
total_tool_calls: tool.schema.number().int().min(0).max(9999).describe("Total number of tool calls in this phase"),
|
|
48137
|
+
coder_revisions: tool.schema.number().int().min(0).max(999).describe("Number of coder revisions made"),
|
|
48138
|
+
reviewer_rejections: tool.schema.number().int().min(0).max(999).describe("Number of reviewer rejections received"),
|
|
48139
|
+
loop_detections: tool.schema.number().int().min(0).max(9999).optional().describe("Number of loop detection events in this phase"),
|
|
48140
|
+
circuit_breaker_trips: tool.schema.number().int().min(0).max(9999).optional().describe("Number of circuit breaker trips in this phase"),
|
|
48141
|
+
test_failures: tool.schema.number().int().min(0).max(9999).describe("Number of test failures encountered"),
|
|
48142
|
+
security_findings: tool.schema.number().int().min(0).max(999).describe("Number of security findings"),
|
|
48143
|
+
integration_issues: tool.schema.number().int().min(0).max(999).describe("Number of integration issues"),
|
|
47789
48144
|
lessons_learned: tool.schema.array(tool.schema.string()).max(5).optional().describe("Key lessons learned from this phase (max 5)"),
|
|
47790
48145
|
top_rejection_reasons: tool.schema.array(tool.schema.string()).optional().describe("Top reasons for reviewer rejections"),
|
|
47791
48146
|
task_id: tool.schema.string().optional().describe("Optional custom task ID (defaults to retro-{phase})"),
|
|
@@ -47802,6 +48157,8 @@ var write_retro = createSwarmTool({
|
|
|
47802
48157
|
total_tool_calls: Number(args2.total_tool_calls),
|
|
47803
48158
|
coder_revisions: Number(args2.coder_revisions),
|
|
47804
48159
|
reviewer_rejections: Number(args2.reviewer_rejections),
|
|
48160
|
+
loop_detections: args2.loop_detections != null ? Number(args2.loop_detections) : undefined,
|
|
48161
|
+
circuit_breaker_trips: args2.circuit_breaker_trips != null ? Number(args2.circuit_breaker_trips) : undefined,
|
|
47805
48162
|
test_failures: Number(args2.test_failures),
|
|
47806
48163
|
security_findings: Number(args2.security_findings),
|
|
47807
48164
|
integration_issues: Number(args2.integration_issues),
|
|
@@ -48687,6 +49044,48 @@ init_schema();
|
|
|
48687
49044
|
init_manager2();
|
|
48688
49045
|
import * as path27 from "path";
|
|
48689
49046
|
init_utils();
|
|
49047
|
+
|
|
49048
|
+
// src/hooks/loop-detector.ts
|
|
49049
|
+
function hashDelegation(toolName, args2) {
|
|
49050
|
+
const targetAgent = typeof args2?.subagent_type === "string" ? args2.subagent_type : "unknown";
|
|
49051
|
+
const firstArgKey = args2 != null ? Object.keys(args2)[0] ?? "noargs" : "noargs";
|
|
49052
|
+
return `${toolName}:${targetAgent}:${firstArgKey}`;
|
|
49053
|
+
}
|
|
49054
|
+
function detectLoop(sessionId, toolName, args2) {
|
|
49055
|
+
if (toolName !== "Task") {
|
|
49056
|
+
return { looping: false, count: 0, pattern: "" };
|
|
49057
|
+
}
|
|
49058
|
+
const session = swarmState.agentSessions.get(sessionId);
|
|
49059
|
+
if (!session) {
|
|
49060
|
+
return { looping: false, count: 0, pattern: "" };
|
|
49061
|
+
}
|
|
49062
|
+
if (!session.loopDetectionWindow) {
|
|
49063
|
+
session.loopDetectionWindow = [];
|
|
49064
|
+
}
|
|
49065
|
+
const argsRecord = args2 != null && typeof args2 === "object" && !Array.isArray(args2) ? args2 : undefined;
|
|
49066
|
+
const hash3 = hashDelegation(toolName, argsRecord);
|
|
49067
|
+
const now = Date.now();
|
|
49068
|
+
session.loopDetectionWindow.push({ hash: hash3, timestamp: now });
|
|
49069
|
+
if (session.loopDetectionWindow.length > 10) {
|
|
49070
|
+
session.loopDetectionWindow.shift();
|
|
49071
|
+
}
|
|
49072
|
+
const window2 = session.loopDetectionWindow;
|
|
49073
|
+
let consecutiveCount = 0;
|
|
49074
|
+
for (let i2 = window2.length - 1;i2 >= 0; i2--) {
|
|
49075
|
+
if (window2[i2].hash === hash3) {
|
|
49076
|
+
consecutiveCount++;
|
|
49077
|
+
} else {
|
|
49078
|
+
break;
|
|
49079
|
+
}
|
|
49080
|
+
}
|
|
49081
|
+
return {
|
|
49082
|
+
looping: consecutiveCount >= 3,
|
|
49083
|
+
count: consecutiveCount,
|
|
49084
|
+
pattern: hash3
|
|
49085
|
+
};
|
|
49086
|
+
}
|
|
49087
|
+
|
|
49088
|
+
// src/hooks/guardrails.ts
|
|
48690
49089
|
var storedInputArgs = new Map;
|
|
48691
49090
|
function getStoredInputArgs(callID) {
|
|
48692
49091
|
return storedInputArgs.get(callID);
|
|
@@ -48794,7 +49193,10 @@ function isAgentDelegation(toolName, args2) {
|
|
|
48794
49193
|
}
|
|
48795
49194
|
const subagentType = argsObj.subagent_type;
|
|
48796
49195
|
if (typeof subagentType === "string") {
|
|
48797
|
-
return {
|
|
49196
|
+
return {
|
|
49197
|
+
isDelegation: true,
|
|
49198
|
+
targetAgent: stripKnownSwarmPrefix(subagentType)
|
|
49199
|
+
};
|
|
48798
49200
|
}
|
|
48799
49201
|
return { isDelegation: false, targetAgent: null };
|
|
48800
49202
|
}
|
|
@@ -48861,6 +49263,37 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
48861
49263
|
}
|
|
48862
49264
|
}
|
|
48863
49265
|
}
|
|
49266
|
+
if (input.tool === "Task") {
|
|
49267
|
+
const loopArgs = output.args;
|
|
49268
|
+
const loopResult = detectLoop(input.sessionID, input.tool, loopArgs);
|
|
49269
|
+
if (loopResult.count >= 5) {
|
|
49270
|
+
throw new Error(`CIRCUIT BREAKER: Delegation loop detected (${loopResult.count} identical patterns). Session paused. Ask the user for guidance.`);
|
|
49271
|
+
} else if (loopResult.count === 3) {
|
|
49272
|
+
const agentName2 = typeof loopArgs?.subagent_type === "string" ? loopArgs.subagent_type : "agent";
|
|
49273
|
+
const loopSession = swarmState.agentSessions.get(input.sessionID);
|
|
49274
|
+
if (loopSession) {
|
|
49275
|
+
loopSession.loopWarningPending = {
|
|
49276
|
+
agent: agentName2,
|
|
49277
|
+
message: `LOOP DETECTED: You have delegated to ${agentName2} with the same pattern 3 times. Change your approach \u2014 try a different agent, different instructions, or escalate to the user.`,
|
|
49278
|
+
timestamp: Date.now()
|
|
49279
|
+
};
|
|
49280
|
+
}
|
|
49281
|
+
}
|
|
49282
|
+
}
|
|
49283
|
+
if (input.tool === "bash" || input.tool === "shell") {
|
|
49284
|
+
const bashArgs = output.args;
|
|
49285
|
+
const cmd = (typeof bashArgs?.command === "string" ? bashArgs.command : "").trim();
|
|
49286
|
+
const testRunnerPrefixPattern = /^(bun\s+test|npm\s+test|npx\s+vitest|bunx\s+vitest)\b/;
|
|
49287
|
+
if (testRunnerPrefixPattern.test(cmd)) {
|
|
49288
|
+
const tokens = cmd.split(/\s+/);
|
|
49289
|
+
const runnerTokenCount = tokens[0] === "npx" || tokens[0] === "bunx" ? 3 : 2;
|
|
49290
|
+
const remainingTokens = tokens.slice(runnerTokenCount);
|
|
49291
|
+
const hasFileArg = remainingTokens.some((token) => token.length > 0 && !token.startsWith("-") && (token.includes("/") || token.includes("\\") || token.endsWith(".ts") || token.endsWith(".js") || token.endsWith(".tsx") || token.endsWith(".jsx") || token.endsWith(".mts") || token.endsWith(".mjs")));
|
|
49292
|
+
if (!hasFileArg) {
|
|
49293
|
+
throw new Error("BLOCKED: Full test suite execution is not allowed in-session. Run a specific test file instead: bun test path/to/file.test.ts");
|
|
49294
|
+
}
|
|
49295
|
+
}
|
|
49296
|
+
}
|
|
48864
49297
|
if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
|
|
48865
49298
|
const args2 = output.args;
|
|
48866
49299
|
const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
|
|
@@ -49205,6 +49638,21 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
49205
49638
|
const activeAgent = swarmState.activeAgent.get(sessionId);
|
|
49206
49639
|
const isArchitectSession = activeAgent ? stripKnownSwarmPrefix(activeAgent) === ORCHESTRATOR_NAME : session ? stripKnownSwarmPrefix(session.agentName) === ORCHESTRATOR_NAME : false;
|
|
49207
49640
|
const systemMessages = messages.filter((msg) => msg.info?.role === "system");
|
|
49641
|
+
if (isArchitectSession && session?.loopWarningPending) {
|
|
49642
|
+
const pending = session.loopWarningPending;
|
|
49643
|
+
session.loopWarningPending = undefined;
|
|
49644
|
+
const loopSystemMsg = systemMessages[0];
|
|
49645
|
+
if (loopSystemMsg) {
|
|
49646
|
+
const loopTextPart = (loopSystemMsg.parts ?? []).find((part) => part.type === "text" && typeof part.text === "string");
|
|
49647
|
+
if (loopTextPart && !loopTextPart.text.includes("LOOP DETECTED")) {
|
|
49648
|
+
loopTextPart.text = `[LOOP WARNING]
|
|
49649
|
+
${pending.message}
|
|
49650
|
+
[/LOOP WARNING]
|
|
49651
|
+
|
|
49652
|
+
` + loopTextPart.text;
|
|
49653
|
+
}
|
|
49654
|
+
}
|
|
49655
|
+
}
|
|
49208
49656
|
if (isArchitectSession && session && session.architectWriteCount > session.selfCodingWarnedAtCount) {
|
|
49209
49657
|
let targetSystemMessage = systemMessages[0];
|
|
49210
49658
|
if (!targetSystemMessage) {
|
|
@@ -50508,165 +50956,6 @@ function formatDriftForContext(result) {
|
|
|
50508
50956
|
|
|
50509
50957
|
// src/services/index.ts
|
|
50510
50958
|
init_config_doctor();
|
|
50511
|
-
|
|
50512
|
-
// src/services/context-budget-service.ts
|
|
50513
|
-
init_utils2();
|
|
50514
|
-
function validateDirectory(directory) {
|
|
50515
|
-
if (!directory || directory.trim() === "") {
|
|
50516
|
-
throw new Error("Invalid directory: empty");
|
|
50517
|
-
}
|
|
50518
|
-
if (/\.\.[/\\]/.test(directory)) {
|
|
50519
|
-
throw new Error("Invalid directory: path traversal detected");
|
|
50520
|
-
}
|
|
50521
|
-
if (directory.startsWith("/") || directory.startsWith("\\")) {
|
|
50522
|
-
throw new Error("Invalid directory: absolute path");
|
|
50523
|
-
}
|
|
50524
|
-
if (/^[A-Za-z]:[\\/]/.test(directory)) {
|
|
50525
|
-
throw new Error("Invalid directory: Windows absolute path");
|
|
50526
|
-
}
|
|
50527
|
-
}
|
|
50528
|
-
var COST_PER_1K_TOKENS = 0.003;
|
|
50529
|
-
function estimateTokens2(text) {
|
|
50530
|
-
if (!text || typeof text !== "string") {
|
|
50531
|
-
return 0;
|
|
50532
|
-
}
|
|
50533
|
-
return Math.ceil(text.length / 3.5);
|
|
50534
|
-
}
|
|
50535
|
-
async function readBudgetState(directory) {
|
|
50536
|
-
const content = await readSwarmFileAsync(directory, "session/budget-state.json");
|
|
50537
|
-
if (!content) {
|
|
50538
|
-
return null;
|
|
50539
|
-
}
|
|
50540
|
-
try {
|
|
50541
|
-
return JSON.parse(content);
|
|
50542
|
-
} catch {
|
|
50543
|
-
return null;
|
|
50544
|
-
}
|
|
50545
|
-
}
|
|
50546
|
-
async function writeBudgetState(directory, state) {
|
|
50547
|
-
const resolvedPath = validateSwarmPath(directory, "session/budget-state.json");
|
|
50548
|
-
const content = JSON.stringify(state, null, 2);
|
|
50549
|
-
await Bun.write(resolvedPath, content);
|
|
50550
|
-
}
|
|
50551
|
-
async function countEvents(directory) {
|
|
50552
|
-
const content = await readSwarmFileAsync(directory, "events.jsonl");
|
|
50553
|
-
if (!content) {
|
|
50554
|
-
return 0;
|
|
50555
|
-
}
|
|
50556
|
-
const lines = content.split(`
|
|
50557
|
-
`).filter((line) => line.trim().length > 0);
|
|
50558
|
-
return lines.length;
|
|
50559
|
-
}
|
|
50560
|
-
async function getPlanCursorContent(directory) {
|
|
50561
|
-
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
50562
|
-
if (!planContent) {
|
|
50563
|
-
return "";
|
|
50564
|
-
}
|
|
50565
|
-
const lines = planContent.split(`
|
|
50566
|
-
`);
|
|
50567
|
-
const cursorLines = [];
|
|
50568
|
-
let inCurrentSection = false;
|
|
50569
|
-
for (const line of lines) {
|
|
50570
|
-
if (line.includes("in_progress") || line.includes("**Current**")) {
|
|
50571
|
-
inCurrentSection = true;
|
|
50572
|
-
}
|
|
50573
|
-
if (inCurrentSection) {
|
|
50574
|
-
cursorLines.push(line);
|
|
50575
|
-
if (cursorLines.length > 30) {
|
|
50576
|
-
break;
|
|
50577
|
-
}
|
|
50578
|
-
}
|
|
50579
|
-
}
|
|
50580
|
-
return cursorLines.join(`
|
|
50581
|
-
`) || planContent.substring(0, 1000);
|
|
50582
|
-
}
|
|
50583
|
-
async function getContextBudgetReport(directory, assembledSystemPrompt, config3) {
|
|
50584
|
-
validateDirectory(directory);
|
|
50585
|
-
const timestamp = new Date().toISOString();
|
|
50586
|
-
const systemPromptTokens = estimateTokens2(assembledSystemPrompt);
|
|
50587
|
-
const planCursorContent = await getPlanCursorContent(directory);
|
|
50588
|
-
const planCursorTokens = estimateTokens2(planCursorContent);
|
|
50589
|
-
const knowledgeContent = await readSwarmFileAsync(directory, "knowledge.jsonl");
|
|
50590
|
-
const knowledgeTokens = estimateTokens2(knowledgeContent || "");
|
|
50591
|
-
const runMemoryContent = await readSwarmFileAsync(directory, "run-memory.jsonl");
|
|
50592
|
-
const runMemoryTokens = estimateTokens2(runMemoryContent || "");
|
|
50593
|
-
const handoffContent = await readSwarmFileAsync(directory, "handoff.md");
|
|
50594
|
-
const handoffTokens = estimateTokens2(handoffContent || "");
|
|
50595
|
-
const contextMdContent = await readSwarmFileAsync(directory, "context.md");
|
|
50596
|
-
const contextMdTokens = estimateTokens2(contextMdContent || "");
|
|
50597
|
-
const swarmTotalTokens = systemPromptTokens + planCursorTokens + knowledgeTokens + runMemoryTokens + handoffTokens + contextMdTokens;
|
|
50598
|
-
const estimatedTurnCount = await countEvents(directory);
|
|
50599
|
-
const budgetPct = swarmTotalTokens / config3.budgetTokens * 100;
|
|
50600
|
-
let status;
|
|
50601
|
-
let recommendation = null;
|
|
50602
|
-
if (budgetPct < config3.warningPct) {
|
|
50603
|
-
status = "ok";
|
|
50604
|
-
} else if (budgetPct < config3.criticalPct) {
|
|
50605
|
-
status = "warning";
|
|
50606
|
-
recommendation = "Consider wrapping up current phase and running /swarm handoff before starting new work.";
|
|
50607
|
-
} else {
|
|
50608
|
-
status = "critical";
|
|
50609
|
-
recommendation = "Run /swarm handoff and start a new session to avoid cost escalation.";
|
|
50610
|
-
}
|
|
50611
|
-
const estimatedSessionTokens = swarmTotalTokens * Math.max(1, estimatedTurnCount);
|
|
50612
|
-
return {
|
|
50613
|
-
timestamp,
|
|
50614
|
-
systemPromptTokens,
|
|
50615
|
-
planCursorTokens,
|
|
50616
|
-
knowledgeTokens,
|
|
50617
|
-
runMemoryTokens,
|
|
50618
|
-
handoffTokens,
|
|
50619
|
-
contextMdTokens,
|
|
50620
|
-
swarmTotalTokens,
|
|
50621
|
-
estimatedTurnCount,
|
|
50622
|
-
estimatedSessionTokens,
|
|
50623
|
-
budgetPct,
|
|
50624
|
-
status,
|
|
50625
|
-
recommendation
|
|
50626
|
-
};
|
|
50627
|
-
}
|
|
50628
|
-
async function formatBudgetWarning(report, directory, config3) {
|
|
50629
|
-
validateDirectory(directory);
|
|
50630
|
-
if (report.status === "ok") {
|
|
50631
|
-
return null;
|
|
50632
|
-
}
|
|
50633
|
-
if (!directory || directory.trim() === "") {
|
|
50634
|
-
return formatWarningMessage(report);
|
|
50635
|
-
}
|
|
50636
|
-
const budgetState = await readBudgetState(directory);
|
|
50637
|
-
const state = budgetState || {
|
|
50638
|
-
warningFiredAtTurn: null,
|
|
50639
|
-
criticalFiredAtTurn: null,
|
|
50640
|
-
lastInjectedAtTurn: null
|
|
50641
|
-
};
|
|
50642
|
-
const currentTurn = report.estimatedTurnCount;
|
|
50643
|
-
if (report.status === "warning") {
|
|
50644
|
-
if (config3.warningMode === "once" && state.warningFiredAtTurn !== null) {
|
|
50645
|
-
return null;
|
|
50646
|
-
}
|
|
50647
|
-
if (config3.warningMode === "interval" && state.warningFiredAtTurn !== null && currentTurn - state.warningFiredAtTurn < config3.warningIntervalTurns) {
|
|
50648
|
-
return null;
|
|
50649
|
-
}
|
|
50650
|
-
state.warningFiredAtTurn = currentTurn;
|
|
50651
|
-
state.lastInjectedAtTurn = currentTurn;
|
|
50652
|
-
await writeBudgetState(directory, state);
|
|
50653
|
-
} else if (report.status === "critical") {
|
|
50654
|
-
state.criticalFiredAtTurn = currentTurn;
|
|
50655
|
-
state.lastInjectedAtTurn = currentTurn;
|
|
50656
|
-
}
|
|
50657
|
-
return formatWarningMessage(report);
|
|
50658
|
-
}
|
|
50659
|
-
function formatWarningMessage(report) {
|
|
50660
|
-
const budgetPctStr = report.budgetPct.toFixed(1);
|
|
50661
|
-
const tokensPerTurn = report.swarmTotalTokens.toLocaleString();
|
|
50662
|
-
if (report.status === "warning") {
|
|
50663
|
-
return `[CONTEXT BUDGET: ${budgetPctStr}% \u2014 swarm injecting ~${tokensPerTurn} tokens/turn. Consider wrapping current phase and running /swarm handoff before starting new work.]`;
|
|
50664
|
-
}
|
|
50665
|
-
const costPerTurn = (report.swarmTotalTokens / 1000 * COST_PER_1K_TOKENS).toFixed(3);
|
|
50666
|
-
return `[CONTEXT BUDGET: ${budgetPctStr}% CRITICAL \u2014 swarm injecting ~${tokensPerTurn} tokens/turn. Run /swarm handoff and start a new session to avoid cost escalation. Estimated session cost scaling: ~$${costPerTurn}/turn at current context size.]`;
|
|
50667
|
-
}
|
|
50668
|
-
|
|
50669
|
-
// src/services/index.ts
|
|
50670
50959
|
init_evidence_summary_service();
|
|
50671
50960
|
init_preflight_integration();
|
|
50672
50961
|
init_preflight_service();
|
|
@@ -51253,6 +51542,7 @@ ${handoffBlock}`);
|
|
|
51253
51542
|
const assembledSystemPrompt = output.system.join(`
|
|
51254
51543
|
`);
|
|
51255
51544
|
const budgetReport = await getContextBudgetReport(directory, assembledSystemPrompt, contextBudgetConfig);
|
|
51545
|
+
swarmState.lastBudgetPct = budgetReport.budgetPct;
|
|
51256
51546
|
const budgetWarning = await formatBudgetWarning(budgetReport, directory, contextBudgetConfig);
|
|
51257
51547
|
if (budgetWarning) {
|
|
51258
51548
|
const sessionId_cb = _input.sessionID;
|
|
@@ -51654,6 +51944,7 @@ ${handoffBlock}`;
|
|
|
51654
51944
|
const assembledSystemPrompt_b = output.system.join(`
|
|
51655
51945
|
`);
|
|
51656
51946
|
const budgetReport_b = await getContextBudgetReport(directory, assembledSystemPrompt_b, contextBudgetConfig_b);
|
|
51947
|
+
swarmState.lastBudgetPct = budgetReport_b.budgetPct;
|
|
51657
51948
|
const budgetWarning_b = await formatBudgetWarning(budgetReport_b, directory, contextBudgetConfig_b);
|
|
51658
51949
|
if (budgetWarning_b) {
|
|
51659
51950
|
const sessionId_cb_b = _input.sessionID;
|
|
@@ -52016,10 +52307,92 @@ function createDarkMatterDetectorHook(directory) {
|
|
|
52016
52307
|
return safeHook(hook);
|
|
52017
52308
|
}
|
|
52018
52309
|
|
|
52310
|
+
// src/hooks/incremental-verify.ts
|
|
52311
|
+
import * as fs19 from "fs";
|
|
52312
|
+
import * as path31 from "path";
|
|
52313
|
+
function detectTypecheckCommand(projectDir) {
|
|
52314
|
+
const pkgPath = path31.join(projectDir, "package.json");
|
|
52315
|
+
if (!fs19.existsSync(pkgPath))
|
|
52316
|
+
return null;
|
|
52317
|
+
try {
|
|
52318
|
+
const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf8"));
|
|
52319
|
+
const scripts = pkg.scripts;
|
|
52320
|
+
if (scripts?.typecheck)
|
|
52321
|
+
return ["bun", "run", "typecheck"];
|
|
52322
|
+
if (scripts?.["type-check"])
|
|
52323
|
+
return ["bun", "run", "type-check"];
|
|
52324
|
+
const deps = {
|
|
52325
|
+
...pkg.dependencies,
|
|
52326
|
+
...pkg.devDependencies
|
|
52327
|
+
};
|
|
52328
|
+
if (!deps?.typescript && !fs19.existsSync(path31.join(projectDir, "tsconfig.json"))) {
|
|
52329
|
+
return null;
|
|
52330
|
+
}
|
|
52331
|
+
return ["npx", "tsc", "--noEmit"];
|
|
52332
|
+
} catch {
|
|
52333
|
+
return null;
|
|
52334
|
+
}
|
|
52335
|
+
}
|
|
52336
|
+
async function runWithTimeout(command, cwd, timeoutMs) {
|
|
52337
|
+
try {
|
|
52338
|
+
const proc = Bun.spawn(command, {
|
|
52339
|
+
cwd,
|
|
52340
|
+
stdout: "pipe",
|
|
52341
|
+
stderr: "pipe"
|
|
52342
|
+
});
|
|
52343
|
+
const timeoutHandle = setTimeout(() => {
|
|
52344
|
+
try {
|
|
52345
|
+
proc.kill();
|
|
52346
|
+
} catch {}
|
|
52347
|
+
}, timeoutMs);
|
|
52348
|
+
try {
|
|
52349
|
+
const [exitCode, stderr] = await Promise.all([
|
|
52350
|
+
proc.exited,
|
|
52351
|
+
new Response(proc.stderr).text()
|
|
52352
|
+
]);
|
|
52353
|
+
return { exitCode, stderr };
|
|
52354
|
+
} finally {
|
|
52355
|
+
clearTimeout(timeoutHandle);
|
|
52356
|
+
}
|
|
52357
|
+
} catch {
|
|
52358
|
+
return null;
|
|
52359
|
+
}
|
|
52360
|
+
}
|
|
52361
|
+
function createIncrementalVerifyHook(config3, projectDir, injectMessage) {
|
|
52362
|
+
return {
|
|
52363
|
+
toolAfter: async (input, output) => {
|
|
52364
|
+
if (!config3.enabled)
|
|
52365
|
+
return;
|
|
52366
|
+
if (input.tool !== "Task")
|
|
52367
|
+
return;
|
|
52368
|
+
const args2 = input.args ?? output.args;
|
|
52369
|
+
const subagentType = typeof args2?.subagent_type === "string" ? args2.subagent_type : "";
|
|
52370
|
+
const agentName = subagentType.replace(/^[^_]+_/, "");
|
|
52371
|
+
if (!config3.triggerAgents.includes(agentName) && !config3.triggerAgents.includes(subagentType)) {
|
|
52372
|
+
return;
|
|
52373
|
+
}
|
|
52374
|
+
const command = config3.command != null ? config3.command.split(" ") : detectTypecheckCommand(projectDir);
|
|
52375
|
+
if (!command)
|
|
52376
|
+
return;
|
|
52377
|
+
const result = await runWithTimeout(command, projectDir, config3.timeoutMs);
|
|
52378
|
+
if (result === null) {
|
|
52379
|
+
return;
|
|
52380
|
+
}
|
|
52381
|
+
if (result.exitCode === 0) {
|
|
52382
|
+
injectMessage(input.sessionID, "POST-CODER CHECK PASSED: No type errors.");
|
|
52383
|
+
} else {
|
|
52384
|
+
const errorSummary = result.stderr.slice(0, 800);
|
|
52385
|
+
injectMessage(input.sessionID, `POST-CODER CHECK FAILED: Type errors detected after coder delegation. Address these before proceeding.
|
|
52386
|
+
${errorSummary}`);
|
|
52387
|
+
}
|
|
52388
|
+
}
|
|
52389
|
+
};
|
|
52390
|
+
}
|
|
52391
|
+
|
|
52019
52392
|
// src/hooks/knowledge-reader.ts
|
|
52020
|
-
import { existsSync as
|
|
52393
|
+
import { existsSync as existsSync19 } from "fs";
|
|
52021
52394
|
import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
52022
|
-
import * as
|
|
52395
|
+
import * as path32 from "path";
|
|
52023
52396
|
var JACCARD_THRESHOLD = 0.6;
|
|
52024
52397
|
var HIVE_TIER_BOOST = 0.05;
|
|
52025
52398
|
var SAME_PROJECT_PENALTY = -0.05;
|
|
@@ -52067,15 +52440,15 @@ function inferCategoriesFromPhase(phaseDescription) {
|
|
|
52067
52440
|
return ["process", "tooling"];
|
|
52068
52441
|
}
|
|
52069
52442
|
async function recordLessonsShown(directory, lessonIds, currentPhase) {
|
|
52070
|
-
const shownFile =
|
|
52443
|
+
const shownFile = path32.join(directory, ".swarm", ".knowledge-shown.json");
|
|
52071
52444
|
try {
|
|
52072
52445
|
let shownData = {};
|
|
52073
|
-
if (
|
|
52446
|
+
if (existsSync19(shownFile)) {
|
|
52074
52447
|
const content = await readFile5(shownFile, "utf-8");
|
|
52075
52448
|
shownData = JSON.parse(content);
|
|
52076
52449
|
}
|
|
52077
52450
|
shownData[currentPhase] = lessonIds;
|
|
52078
|
-
await mkdir4(
|
|
52451
|
+
await mkdir4(path32.dirname(shownFile), { recursive: true });
|
|
52079
52452
|
await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
|
|
52080
52453
|
} catch {
|
|
52081
52454
|
console.warn("[swarm] Knowledge: failed to record shown lessons");
|
|
@@ -52170,9 +52543,9 @@ async function readMergedKnowledge(directory, config3, context) {
|
|
|
52170
52543
|
return topN;
|
|
52171
52544
|
}
|
|
52172
52545
|
async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
|
|
52173
|
-
const shownFile =
|
|
52546
|
+
const shownFile = path32.join(directory, ".swarm", ".knowledge-shown.json");
|
|
52174
52547
|
try {
|
|
52175
|
-
if (!
|
|
52548
|
+
if (!existsSync19(shownFile)) {
|
|
52176
52549
|
return;
|
|
52177
52550
|
}
|
|
52178
52551
|
const content = await readFile5(shownFile, "utf-8");
|
|
@@ -52642,12 +53015,12 @@ Use this data to avoid repeating known failure patterns.`;
|
|
|
52642
53015
|
// src/hooks/curator-drift.ts
|
|
52643
53016
|
init_event_bus();
|
|
52644
53017
|
init_utils2();
|
|
52645
|
-
import * as
|
|
52646
|
-
import * as
|
|
53018
|
+
import * as fs20 from "fs";
|
|
53019
|
+
import * as path33 from "path";
|
|
52647
53020
|
var DRIFT_REPORT_PREFIX = "drift-report-phase-";
|
|
52648
53021
|
async function readPriorDriftReports(directory) {
|
|
52649
|
-
const swarmDir =
|
|
52650
|
-
const entries = await
|
|
53022
|
+
const swarmDir = path33.join(directory, ".swarm");
|
|
53023
|
+
const entries = await fs20.promises.readdir(swarmDir).catch(() => null);
|
|
52651
53024
|
if (entries === null)
|
|
52652
53025
|
return [];
|
|
52653
53026
|
const reportFiles = entries.filter((name2) => name2.startsWith(DRIFT_REPORT_PREFIX) && name2.endsWith(".json")).sort();
|
|
@@ -52673,10 +53046,10 @@ async function readPriorDriftReports(directory) {
|
|
|
52673
53046
|
async function writeDriftReport(directory, report) {
|
|
52674
53047
|
const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
|
|
52675
53048
|
const filePath = validateSwarmPath(directory, filename);
|
|
52676
|
-
const swarmDir =
|
|
52677
|
-
await
|
|
53049
|
+
const swarmDir = path33.dirname(filePath);
|
|
53050
|
+
await fs20.promises.mkdir(swarmDir, { recursive: true });
|
|
52678
53051
|
try {
|
|
52679
|
-
await
|
|
53052
|
+
await fs20.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
|
|
52680
53053
|
} catch (err2) {
|
|
52681
53054
|
throw new Error(`[curator-drift] Failed to write drift report to ${filePath}: ${String(err2)}`);
|
|
52682
53055
|
}
|
|
@@ -52926,9 +53299,143 @@ ${cachedInjectionText}`;
|
|
|
52926
53299
|
});
|
|
52927
53300
|
}
|
|
52928
53301
|
|
|
53302
|
+
// src/hooks/slop-detector.ts
|
|
53303
|
+
var WRITE_EDIT_TOOLS = new Set([
|
|
53304
|
+
"write",
|
|
53305
|
+
"edit",
|
|
53306
|
+
"apply_patch",
|
|
53307
|
+
"create_file"
|
|
53308
|
+
]);
|
|
53309
|
+
function countMatches(text, pattern) {
|
|
53310
|
+
return (text.match(pattern) ?? []).length;
|
|
53311
|
+
}
|
|
53312
|
+
function checkAbstractionBloat(content, threshold) {
|
|
53313
|
+
const newClasses = countMatches(content, /^\+.*\bclass\s+\w+/gm);
|
|
53314
|
+
if (newClasses >= threshold) {
|
|
53315
|
+
return {
|
|
53316
|
+
type: "abstraction_bloat",
|
|
53317
|
+
detail: `${newClasses} new class declarations added (threshold: ${threshold}). Consider whether all abstractions are necessary.`
|
|
53318
|
+
};
|
|
53319
|
+
}
|
|
53320
|
+
return null;
|
|
53321
|
+
}
|
|
53322
|
+
function checkCommentStrip(content, threshold) {
|
|
53323
|
+
const removedComments = countMatches(content, /^-\s*\/[/*]/gm);
|
|
53324
|
+
const addedComments = countMatches(content, /^\+\s*\/[/*]/gm);
|
|
53325
|
+
if (removedComments >= threshold && addedComments === 0) {
|
|
53326
|
+
return {
|
|
53327
|
+
type: "comment_strip",
|
|
53328
|
+
detail: `${removedComments} comment lines removed and 0 added. Verify comments were not documenting important behaviour.`
|
|
53329
|
+
};
|
|
53330
|
+
}
|
|
53331
|
+
return null;
|
|
53332
|
+
}
|
|
53333
|
+
function checkBoilerplateExplosion(content, taskDescription, threshold) {
|
|
53334
|
+
const addedLines = countMatches(content, /^\+[^+]/gm);
|
|
53335
|
+
const isSmallTask = /\b(fix|patch|update|tweak|adjust|correct|remove|rename|change)\b/i.test(taskDescription);
|
|
53336
|
+
if (isSmallTask && addedLines >= threshold) {
|
|
53337
|
+
return {
|
|
53338
|
+
type: "boilerplate_explosion",
|
|
53339
|
+
detail: `${addedLines} lines added for a "${taskDescription.slice(0, 40)}" task (threshold: ${threshold}). Review for scope creep.`
|
|
53340
|
+
};
|
|
53341
|
+
}
|
|
53342
|
+
return null;
|
|
53343
|
+
}
|
|
53344
|
+
async function checkDeadExports(content, projectDir, startTime) {
|
|
53345
|
+
const exportMatches = content.matchAll(/^(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
|
|
53346
|
+
const newExports = [];
|
|
53347
|
+
for (const match of exportMatches) {
|
|
53348
|
+
if (match[1])
|
|
53349
|
+
newExports.push(match[1]);
|
|
53350
|
+
}
|
|
53351
|
+
if (newExports.length === 0)
|
|
53352
|
+
return null;
|
|
53353
|
+
const deadExports = [];
|
|
53354
|
+
for (const name2 of newExports) {
|
|
53355
|
+
if (Date.now() - startTime > 480)
|
|
53356
|
+
break;
|
|
53357
|
+
try {
|
|
53358
|
+
const importPattern = new RegExp(`\\bimport\\b[^;]*\\b${name2}\\b`, "g");
|
|
53359
|
+
const glob = new Bun.Glob(`src/**/*.ts`);
|
|
53360
|
+
let found = false;
|
|
53361
|
+
for await (const file3 of glob.scan(projectDir)) {
|
|
53362
|
+
if (found || Date.now() - startTime > 480)
|
|
53363
|
+
break;
|
|
53364
|
+
try {
|
|
53365
|
+
const text = await Bun.file(`${projectDir}/${file3}`).text();
|
|
53366
|
+
if (importPattern.test(text))
|
|
53367
|
+
found = true;
|
|
53368
|
+
importPattern.lastIndex = 0;
|
|
53369
|
+
} catch {}
|
|
53370
|
+
}
|
|
53371
|
+
if (!found)
|
|
53372
|
+
deadExports.push(name2);
|
|
53373
|
+
} catch {}
|
|
53374
|
+
}
|
|
53375
|
+
if (deadExports.length === 0)
|
|
53376
|
+
return null;
|
|
53377
|
+
return {
|
|
53378
|
+
type: "dead_export",
|
|
53379
|
+
detail: `New exports not found in any import: ${deadExports.slice(0, 3).join(", ")}. Verify these are intentionally exported.`
|
|
53380
|
+
};
|
|
53381
|
+
}
|
|
53382
|
+
function createSlopDetectorHook(config3, projectDir, injectSystemMessage) {
|
|
53383
|
+
return {
|
|
53384
|
+
toolAfter: async (input, output) => {
|
|
53385
|
+
if (!config3.enabled)
|
|
53386
|
+
return;
|
|
53387
|
+
if (!WRITE_EDIT_TOOLS.has(input.tool.toLowerCase()))
|
|
53388
|
+
return;
|
|
53389
|
+
const args2 = output.args;
|
|
53390
|
+
const content = (() => {
|
|
53391
|
+
if (typeof args2?.content === "string")
|
|
53392
|
+
return args2.content;
|
|
53393
|
+
if (typeof args2?.newString === "string")
|
|
53394
|
+
return args2.newString;
|
|
53395
|
+
if (typeof args2?.patch === "string")
|
|
53396
|
+
return args2.patch;
|
|
53397
|
+
if (typeof args2?.file_text === "string")
|
|
53398
|
+
return args2.file_text;
|
|
53399
|
+
return "";
|
|
53400
|
+
})();
|
|
53401
|
+
if (!content || content.length < 10)
|
|
53402
|
+
return;
|
|
53403
|
+
const taskDescription = typeof args2?.description === "string" ? args2.description : typeof args2?.task === "string" ? args2.task : "";
|
|
53404
|
+
const startTime = Date.now();
|
|
53405
|
+
const findings = [];
|
|
53406
|
+
try {
|
|
53407
|
+
const bloat = checkAbstractionBloat(content, config3.classThreshold);
|
|
53408
|
+
if (bloat)
|
|
53409
|
+
findings.push(bloat);
|
|
53410
|
+
const strip = checkCommentStrip(content, config3.commentStripThreshold);
|
|
53411
|
+
if (strip)
|
|
53412
|
+
findings.push(strip);
|
|
53413
|
+
const explosion = checkBoilerplateExplosion(content, taskDescription, config3.diffLineThreshold);
|
|
53414
|
+
if (explosion)
|
|
53415
|
+
findings.push(explosion);
|
|
53416
|
+
} catch {}
|
|
53417
|
+
if (Date.now() - startTime < 400) {
|
|
53418
|
+
try {
|
|
53419
|
+
const dead = await checkDeadExports(content, projectDir, startTime);
|
|
53420
|
+
if (dead)
|
|
53421
|
+
findings.push(dead);
|
|
53422
|
+
} catch {}
|
|
53423
|
+
}
|
|
53424
|
+
if (findings.length === 0)
|
|
53425
|
+
return;
|
|
53426
|
+
const findingText = findings.map((f) => ` \u2022 ${f.type}: ${f.detail}`).join(`
|
|
53427
|
+
`);
|
|
53428
|
+
const message = `SLOP CHECK: ${findings.length} potential issue(s) detected after ${input.tool}:
|
|
53429
|
+
${findingText}
|
|
53430
|
+
Review before proceeding.`;
|
|
53431
|
+
injectSystemMessage(input.sessionID, message);
|
|
53432
|
+
}
|
|
53433
|
+
};
|
|
53434
|
+
}
|
|
53435
|
+
|
|
52929
53436
|
// src/hooks/steering-consumed.ts
|
|
52930
53437
|
init_utils2();
|
|
52931
|
-
import * as
|
|
53438
|
+
import * as fs21 from "fs";
|
|
52932
53439
|
function recordSteeringConsumed(directory, directiveId) {
|
|
52933
53440
|
try {
|
|
52934
53441
|
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
@@ -52937,7 +53444,7 @@ function recordSteeringConsumed(directory, directiveId) {
|
|
|
52937
53444
|
directiveId,
|
|
52938
53445
|
timestamp: new Date().toISOString()
|
|
52939
53446
|
};
|
|
52940
|
-
|
|
53447
|
+
fs21.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
52941
53448
|
`, "utf-8");
|
|
52942
53449
|
} catch {}
|
|
52943
53450
|
}
|
|
@@ -52977,12 +53484,93 @@ function createSteeringConsumedHook(directory) {
|
|
|
52977
53484
|
return safeHook(hook);
|
|
52978
53485
|
}
|
|
52979
53486
|
|
|
53487
|
+
// src/services/compaction-service.ts
|
|
53488
|
+
import * as fs22 from "fs";
|
|
53489
|
+
import * as path34 from "path";
|
|
53490
|
+
function makeInitialState() {
|
|
53491
|
+
return {
|
|
53492
|
+
lastObservationAt: 0,
|
|
53493
|
+
lastReflectionAt: 0,
|
|
53494
|
+
lastEmergencyAt: 0,
|
|
53495
|
+
observationCount: 0,
|
|
53496
|
+
reflectionCount: 0,
|
|
53497
|
+
emergencyCount: 0
|
|
53498
|
+
};
|
|
53499
|
+
}
|
|
53500
|
+
function appendSnapshot(directory, tier, budgetPct, message) {
|
|
53501
|
+
try {
|
|
53502
|
+
const snapshotPath = path34.join(directory, ".swarm", "context-snapshot.md");
|
|
53503
|
+
const timestamp = new Date().toISOString();
|
|
53504
|
+
const entry = `
|
|
53505
|
+
## [${tier.toUpperCase()}] ${timestamp} \u2014 ${budgetPct.toFixed(1)}% used
|
|
53506
|
+
${message}
|
|
53507
|
+
`;
|
|
53508
|
+
fs22.appendFileSync(snapshotPath, entry, "utf-8");
|
|
53509
|
+
} catch {}
|
|
53510
|
+
}
|
|
53511
|
+
function buildObservationMessage(budgetPct) {
|
|
53512
|
+
return `[CONTEXT COMPACTION \u2014 OBSERVATION TIER]
|
|
53513
|
+
` + `Context window is ${budgetPct.toFixed(1)}% used. Initiating observation compaction.
|
|
53514
|
+
` + `INSTRUCTIONS: Summarise the key decisions made so far, files changed, errors resolved, ` + `and the current task state. Discard verbose tool outputs and raw file reads. ` + `Preserve: plan task ID, agent verdicts, file paths touched, unresolved blockers.
|
|
53515
|
+
` + `[/CONTEXT COMPACTION]`;
|
|
53516
|
+
}
|
|
53517
|
+
function buildReflectionMessage(budgetPct) {
|
|
53518
|
+
return `[CONTEXT COMPACTION \u2014 REFLECTION TIER]
|
|
53519
|
+
` + `Context window is ${budgetPct.toFixed(1)}% used. Initiating reflection compaction.
|
|
53520
|
+
` + `INSTRUCTIONS: Re-summarise into a tighter format. Discard completed task details ` + `and resolved errors. Retain ONLY: current phase tasks remaining, open blockers, ` + `last 3 reviewer/test verdicts, and active file scope.
|
|
53521
|
+
` + `[/CONTEXT COMPACTION]`;
|
|
53522
|
+
}
|
|
53523
|
+
function buildEmergencyMessage(budgetPct, preserveLastN) {
|
|
53524
|
+
return `[CONTEXT COMPACTION \u2014 EMERGENCY TIER]
|
|
53525
|
+
` + `Context window is ${budgetPct.toFixed(1)}% used. EMERGENCY compaction required.
|
|
53526
|
+
` + `INSTRUCTIONS: Retain ONLY the system prompt, the current task context, and the ` + `last ${preserveLastN} conversation turns. Discard everything else. ` + `If you cannot complete the current task in the remaining context, escalate to the user.
|
|
53527
|
+
` + `[/CONTEXT COMPACTION]`;
|
|
53528
|
+
}
|
|
53529
|
+
function createCompactionService(config3, directory, injectMessage) {
|
|
53530
|
+
const state = makeInitialState();
|
|
53531
|
+
return {
|
|
53532
|
+
toolAfter: async (_input, _output) => {
|
|
53533
|
+
if (!config3.enabled)
|
|
53534
|
+
return;
|
|
53535
|
+
const budgetPct = swarmState.lastBudgetPct ?? 0;
|
|
53536
|
+
if (budgetPct <= 0)
|
|
53537
|
+
return;
|
|
53538
|
+
const sessionId = _input.sessionID;
|
|
53539
|
+
try {
|
|
53540
|
+
if (budgetPct >= config3.emergencyThreshold && budgetPct > state.lastEmergencyAt + 5) {
|
|
53541
|
+
state.lastEmergencyAt = budgetPct;
|
|
53542
|
+
state.emergencyCount++;
|
|
53543
|
+
const msg = buildEmergencyMessage(budgetPct, config3.preserveLastNTurns);
|
|
53544
|
+
appendSnapshot(directory, "emergency", budgetPct, msg);
|
|
53545
|
+
injectMessage(sessionId, msg);
|
|
53546
|
+
return;
|
|
53547
|
+
}
|
|
53548
|
+
if (budgetPct >= config3.reflectionThreshold && budgetPct > state.lastReflectionAt + 5) {
|
|
53549
|
+
state.lastReflectionAt = budgetPct;
|
|
53550
|
+
state.reflectionCount++;
|
|
53551
|
+
const msg = buildReflectionMessage(budgetPct);
|
|
53552
|
+
appendSnapshot(directory, "reflection", budgetPct, msg);
|
|
53553
|
+
injectMessage(sessionId, msg);
|
|
53554
|
+
return;
|
|
53555
|
+
}
|
|
53556
|
+
if (budgetPct >= config3.observationThreshold && budgetPct > state.lastObservationAt + 5) {
|
|
53557
|
+
state.lastObservationAt = budgetPct;
|
|
53558
|
+
state.observationCount++;
|
|
53559
|
+
const msg = buildObservationMessage(budgetPct);
|
|
53560
|
+
appendSnapshot(directory, "observation", budgetPct, msg);
|
|
53561
|
+
injectMessage(sessionId, msg);
|
|
53562
|
+
}
|
|
53563
|
+
} catch {}
|
|
53564
|
+
}
|
|
53565
|
+
};
|
|
53566
|
+
}
|
|
53567
|
+
|
|
52980
53568
|
// src/index.ts
|
|
52981
53569
|
init_config_doctor();
|
|
52982
53570
|
|
|
52983
53571
|
// src/session/snapshot-reader.ts
|
|
52984
53572
|
init_utils2();
|
|
52985
|
-
import
|
|
53573
|
+
import path35 from "path";
|
|
52986
53574
|
var VALID_TASK_WORKFLOW_STATES = [
|
|
52987
53575
|
"idle",
|
|
52988
53576
|
"coder_delegated",
|
|
@@ -53107,7 +53695,7 @@ function rehydrateState(snapshot) {
|
|
|
53107
53695
|
async function reconcileTaskStatesFromPlan(directory) {
|
|
53108
53696
|
let raw;
|
|
53109
53697
|
try {
|
|
53110
|
-
raw = await Bun.file(
|
|
53698
|
+
raw = await Bun.file(path35.join(directory, ".swarm/plan.json")).text();
|
|
53111
53699
|
} catch {
|
|
53112
53700
|
return;
|
|
53113
53701
|
}
|
|
@@ -53329,8 +53917,8 @@ var build_check = createSwarmTool({
|
|
|
53329
53917
|
// src/tools/check-gate-status.ts
|
|
53330
53918
|
init_dist();
|
|
53331
53919
|
init_create_tool();
|
|
53332
|
-
import * as
|
|
53333
|
-
import * as
|
|
53920
|
+
import * as fs23 from "fs";
|
|
53921
|
+
import * as path36 from "path";
|
|
53334
53922
|
var EVIDENCE_DIR = ".swarm/evidence";
|
|
53335
53923
|
var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
|
|
53336
53924
|
function isValidTaskId3(taskId) {
|
|
@@ -53347,18 +53935,18 @@ function isValidTaskId3(taskId) {
|
|
|
53347
53935
|
return TASK_ID_PATTERN2.test(taskId);
|
|
53348
53936
|
}
|
|
53349
53937
|
function isPathWithinSwarm(filePath, workspaceRoot) {
|
|
53350
|
-
const normalizedWorkspace =
|
|
53351
|
-
const swarmPath =
|
|
53352
|
-
const normalizedPath =
|
|
53938
|
+
const normalizedWorkspace = path36.resolve(workspaceRoot);
|
|
53939
|
+
const swarmPath = path36.join(normalizedWorkspace, ".swarm", "evidence");
|
|
53940
|
+
const normalizedPath = path36.resolve(filePath);
|
|
53353
53941
|
return normalizedPath.startsWith(swarmPath);
|
|
53354
53942
|
}
|
|
53355
53943
|
function readEvidenceFile(evidencePath) {
|
|
53356
|
-
if (!
|
|
53944
|
+
if (!fs23.existsSync(evidencePath)) {
|
|
53357
53945
|
return null;
|
|
53358
53946
|
}
|
|
53359
53947
|
let content;
|
|
53360
53948
|
try {
|
|
53361
|
-
content =
|
|
53949
|
+
content = fs23.readFileSync(evidencePath, "utf-8");
|
|
53362
53950
|
} catch {
|
|
53363
53951
|
return null;
|
|
53364
53952
|
}
|
|
@@ -53410,7 +53998,7 @@ var check_gate_status = createSwarmTool({
|
|
|
53410
53998
|
};
|
|
53411
53999
|
return JSON.stringify(errorResult, null, 2);
|
|
53412
54000
|
}
|
|
53413
|
-
const evidencePath =
|
|
54001
|
+
const evidencePath = path36.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
|
|
53414
54002
|
if (!isPathWithinSwarm(evidencePath, directory)) {
|
|
53415
54003
|
const errorResult = {
|
|
53416
54004
|
taskId: taskIdInput,
|
|
@@ -53470,8 +54058,8 @@ var check_gate_status = createSwarmTool({
|
|
|
53470
54058
|
init_tool();
|
|
53471
54059
|
init_create_tool();
|
|
53472
54060
|
import { spawnSync } from "child_process";
|
|
53473
|
-
import * as
|
|
53474
|
-
import * as
|
|
54061
|
+
import * as fs24 from "fs";
|
|
54062
|
+
import * as path37 from "path";
|
|
53475
54063
|
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
|
|
53476
54064
|
var MAX_LABEL_LENGTH = 100;
|
|
53477
54065
|
var GIT_TIMEOUT_MS = 30000;
|
|
@@ -53522,13 +54110,13 @@ function validateLabel(label) {
|
|
|
53522
54110
|
return null;
|
|
53523
54111
|
}
|
|
53524
54112
|
function getCheckpointLogPath(directory) {
|
|
53525
|
-
return
|
|
54113
|
+
return path37.join(directory, CHECKPOINT_LOG_PATH);
|
|
53526
54114
|
}
|
|
53527
54115
|
function readCheckpointLog(directory) {
|
|
53528
54116
|
const logPath = getCheckpointLogPath(directory);
|
|
53529
54117
|
try {
|
|
53530
|
-
if (
|
|
53531
|
-
const content =
|
|
54118
|
+
if (fs24.existsSync(logPath)) {
|
|
54119
|
+
const content = fs24.readFileSync(logPath, "utf-8");
|
|
53532
54120
|
const parsed = JSON.parse(content);
|
|
53533
54121
|
if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
|
|
53534
54122
|
return { version: 1, checkpoints: [] };
|
|
@@ -53540,13 +54128,13 @@ function readCheckpointLog(directory) {
|
|
|
53540
54128
|
}
|
|
53541
54129
|
function writeCheckpointLog(log2, directory) {
|
|
53542
54130
|
const logPath = getCheckpointLogPath(directory);
|
|
53543
|
-
const dir =
|
|
53544
|
-
if (!
|
|
53545
|
-
|
|
54131
|
+
const dir = path37.dirname(logPath);
|
|
54132
|
+
if (!fs24.existsSync(dir)) {
|
|
54133
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
53546
54134
|
}
|
|
53547
54135
|
const tempPath = `${logPath}.tmp`;
|
|
53548
|
-
|
|
53549
|
-
|
|
54136
|
+
fs24.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
|
|
54137
|
+
fs24.renameSync(tempPath, logPath);
|
|
53550
54138
|
}
|
|
53551
54139
|
function gitExec(args2) {
|
|
53552
54140
|
const result = spawnSync("git", args2, {
|
|
@@ -53747,8 +54335,8 @@ var checkpoint = createSwarmTool({
|
|
|
53747
54335
|
// src/tools/complexity-hotspots.ts
|
|
53748
54336
|
init_dist();
|
|
53749
54337
|
init_create_tool();
|
|
53750
|
-
import * as
|
|
53751
|
-
import * as
|
|
54338
|
+
import * as fs25 from "fs";
|
|
54339
|
+
import * as path38 from "path";
|
|
53752
54340
|
var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
|
|
53753
54341
|
var DEFAULT_DAYS = 90;
|
|
53754
54342
|
var DEFAULT_TOP_N = 20;
|
|
@@ -53877,11 +54465,11 @@ function estimateComplexity(content) {
|
|
|
53877
54465
|
}
|
|
53878
54466
|
function getComplexityForFile(filePath) {
|
|
53879
54467
|
try {
|
|
53880
|
-
const stat2 =
|
|
54468
|
+
const stat2 = fs25.statSync(filePath);
|
|
53881
54469
|
if (stat2.size > MAX_FILE_SIZE_BYTES2) {
|
|
53882
54470
|
return null;
|
|
53883
54471
|
}
|
|
53884
|
-
const content =
|
|
54472
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
53885
54473
|
return estimateComplexity(content);
|
|
53886
54474
|
} catch {
|
|
53887
54475
|
return null;
|
|
@@ -53892,7 +54480,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
|
|
|
53892
54480
|
const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
|
|
53893
54481
|
const filteredChurn = new Map;
|
|
53894
54482
|
for (const [file3, count] of churnMap) {
|
|
53895
|
-
const ext =
|
|
54483
|
+
const ext = path38.extname(file3).toLowerCase();
|
|
53896
54484
|
if (extSet.has(ext)) {
|
|
53897
54485
|
filteredChurn.set(file3, count);
|
|
53898
54486
|
}
|
|
@@ -53902,8 +54490,8 @@ async function analyzeHotspots(days, topN, extensions, directory) {
|
|
|
53902
54490
|
let analyzedFiles = 0;
|
|
53903
54491
|
for (const [file3, churnCount] of filteredChurn) {
|
|
53904
54492
|
let fullPath = file3;
|
|
53905
|
-
if (!
|
|
53906
|
-
fullPath =
|
|
54493
|
+
if (!fs25.existsSync(fullPath)) {
|
|
54494
|
+
fullPath = path38.join(cwd, file3);
|
|
53907
54495
|
}
|
|
53908
54496
|
const complexity = getComplexityForFile(fullPath);
|
|
53909
54497
|
if (complexity !== null) {
|
|
@@ -54050,8 +54638,8 @@ var complexity_hotspots = createSwarmTool({
|
|
|
54050
54638
|
});
|
|
54051
54639
|
// src/tools/declare-scope.ts
|
|
54052
54640
|
init_tool();
|
|
54053
|
-
import * as
|
|
54054
|
-
import * as
|
|
54641
|
+
import * as fs26 from "fs";
|
|
54642
|
+
import * as path39 from "path";
|
|
54055
54643
|
init_create_tool();
|
|
54056
54644
|
function validateTaskIdFormat(taskId) {
|
|
54057
54645
|
const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
|
|
@@ -54130,8 +54718,8 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54130
54718
|
};
|
|
54131
54719
|
}
|
|
54132
54720
|
}
|
|
54133
|
-
normalizedDir =
|
|
54134
|
-
const pathParts = normalizedDir.split(
|
|
54721
|
+
normalizedDir = path39.normalize(args2.working_directory);
|
|
54722
|
+
const pathParts = normalizedDir.split(path39.sep);
|
|
54135
54723
|
if (pathParts.includes("..")) {
|
|
54136
54724
|
return {
|
|
54137
54725
|
success: false,
|
|
@@ -54141,11 +54729,11 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54141
54729
|
]
|
|
54142
54730
|
};
|
|
54143
54731
|
}
|
|
54144
|
-
const resolvedDir =
|
|
54732
|
+
const resolvedDir = path39.resolve(normalizedDir);
|
|
54145
54733
|
try {
|
|
54146
|
-
const realPath =
|
|
54147
|
-
const planPath2 =
|
|
54148
|
-
if (!
|
|
54734
|
+
const realPath = fs26.realpathSync(resolvedDir);
|
|
54735
|
+
const planPath2 = path39.join(realPath, ".swarm", "plan.json");
|
|
54736
|
+
if (!fs26.existsSync(planPath2)) {
|
|
54149
54737
|
return {
|
|
54150
54738
|
success: false,
|
|
54151
54739
|
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
@@ -54165,8 +54753,8 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54165
54753
|
}
|
|
54166
54754
|
}
|
|
54167
54755
|
const directory = normalizedDir ?? fallbackDir ?? process.cwd();
|
|
54168
|
-
const planPath =
|
|
54169
|
-
if (!
|
|
54756
|
+
const planPath = path39.resolve(directory, ".swarm", "plan.json");
|
|
54757
|
+
if (!fs26.existsSync(planPath)) {
|
|
54170
54758
|
return {
|
|
54171
54759
|
success: false,
|
|
54172
54760
|
message: "No plan found",
|
|
@@ -54175,7 +54763,7 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54175
54763
|
}
|
|
54176
54764
|
let planContent;
|
|
54177
54765
|
try {
|
|
54178
|
-
planContent = JSON.parse(
|
|
54766
|
+
planContent = JSON.parse(fs26.readFileSync(planPath, "utf-8"));
|
|
54179
54767
|
} catch {
|
|
54180
54768
|
return {
|
|
54181
54769
|
success: false,
|
|
@@ -54255,20 +54843,20 @@ function validateBase(base) {
|
|
|
54255
54843
|
function validatePaths(paths) {
|
|
54256
54844
|
if (!paths)
|
|
54257
54845
|
return null;
|
|
54258
|
-
for (const
|
|
54259
|
-
if (!
|
|
54846
|
+
for (const path40 of paths) {
|
|
54847
|
+
if (!path40 || path40.length === 0) {
|
|
54260
54848
|
return "empty path not allowed";
|
|
54261
54849
|
}
|
|
54262
|
-
if (
|
|
54850
|
+
if (path40.length > MAX_PATH_LENGTH) {
|
|
54263
54851
|
return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
|
|
54264
54852
|
}
|
|
54265
|
-
if (SHELL_METACHARACTERS2.test(
|
|
54853
|
+
if (SHELL_METACHARACTERS2.test(path40)) {
|
|
54266
54854
|
return "path contains shell metacharacters";
|
|
54267
54855
|
}
|
|
54268
|
-
if (
|
|
54856
|
+
if (path40.startsWith("-")) {
|
|
54269
54857
|
return 'path cannot start with "-" (option-like arguments not allowed)';
|
|
54270
54858
|
}
|
|
54271
|
-
if (CONTROL_CHAR_PATTERN2.test(
|
|
54859
|
+
if (CONTROL_CHAR_PATTERN2.test(path40)) {
|
|
54272
54860
|
return "path contains control characters";
|
|
54273
54861
|
}
|
|
54274
54862
|
}
|
|
@@ -54348,8 +54936,8 @@ var diff = tool({
|
|
|
54348
54936
|
if (parts2.length >= 3) {
|
|
54349
54937
|
const additions = parseInt(parts2[0], 10) || 0;
|
|
54350
54938
|
const deletions = parseInt(parts2[1], 10) || 0;
|
|
54351
|
-
const
|
|
54352
|
-
files.push({ path:
|
|
54939
|
+
const path40 = parts2[2];
|
|
54940
|
+
files.push({ path: path40, additions, deletions });
|
|
54353
54941
|
}
|
|
54354
54942
|
}
|
|
54355
54943
|
const contractChanges = [];
|
|
@@ -54578,8 +55166,8 @@ Use these as DOMAIN values when delegating to @sme.`;
|
|
|
54578
55166
|
// src/tools/evidence-check.ts
|
|
54579
55167
|
init_dist();
|
|
54580
55168
|
init_create_tool();
|
|
54581
|
-
import * as
|
|
54582
|
-
import * as
|
|
55169
|
+
import * as fs27 from "fs";
|
|
55170
|
+
import * as path40 from "path";
|
|
54583
55171
|
var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
|
|
54584
55172
|
var MAX_EVIDENCE_FILES = 1000;
|
|
54585
55173
|
var EVIDENCE_DIR2 = ".swarm/evidence";
|
|
@@ -54609,9 +55197,9 @@ function validateRequiredTypes(input) {
|
|
|
54609
55197
|
return null;
|
|
54610
55198
|
}
|
|
54611
55199
|
function isPathWithinSwarm2(filePath, cwd) {
|
|
54612
|
-
const normalizedCwd =
|
|
54613
|
-
const swarmPath =
|
|
54614
|
-
const normalizedPath =
|
|
55200
|
+
const normalizedCwd = path40.resolve(cwd);
|
|
55201
|
+
const swarmPath = path40.join(normalizedCwd, ".swarm");
|
|
55202
|
+
const normalizedPath = path40.resolve(filePath);
|
|
54615
55203
|
return normalizedPath.startsWith(swarmPath);
|
|
54616
55204
|
}
|
|
54617
55205
|
function parseCompletedTasks(planContent) {
|
|
@@ -54627,12 +55215,12 @@ function parseCompletedTasks(planContent) {
|
|
|
54627
55215
|
}
|
|
54628
55216
|
function readEvidenceFiles(evidenceDir, _cwd) {
|
|
54629
55217
|
const evidence = [];
|
|
54630
|
-
if (!
|
|
55218
|
+
if (!fs27.existsSync(evidenceDir) || !fs27.statSync(evidenceDir).isDirectory()) {
|
|
54631
55219
|
return evidence;
|
|
54632
55220
|
}
|
|
54633
55221
|
let files;
|
|
54634
55222
|
try {
|
|
54635
|
-
files =
|
|
55223
|
+
files = fs27.readdirSync(evidenceDir);
|
|
54636
55224
|
} catch {
|
|
54637
55225
|
return evidence;
|
|
54638
55226
|
}
|
|
@@ -54641,14 +55229,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
54641
55229
|
if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
|
|
54642
55230
|
continue;
|
|
54643
55231
|
}
|
|
54644
|
-
const filePath =
|
|
55232
|
+
const filePath = path40.join(evidenceDir, filename);
|
|
54645
55233
|
try {
|
|
54646
|
-
const resolvedPath =
|
|
54647
|
-
const evidenceDirResolved =
|
|
55234
|
+
const resolvedPath = path40.resolve(filePath);
|
|
55235
|
+
const evidenceDirResolved = path40.resolve(evidenceDir);
|
|
54648
55236
|
if (!resolvedPath.startsWith(evidenceDirResolved)) {
|
|
54649
55237
|
continue;
|
|
54650
55238
|
}
|
|
54651
|
-
const stat2 =
|
|
55239
|
+
const stat2 = fs27.lstatSync(filePath);
|
|
54652
55240
|
if (!stat2.isFile()) {
|
|
54653
55241
|
continue;
|
|
54654
55242
|
}
|
|
@@ -54657,7 +55245,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
54657
55245
|
}
|
|
54658
55246
|
let fileStat;
|
|
54659
55247
|
try {
|
|
54660
|
-
fileStat =
|
|
55248
|
+
fileStat = fs27.statSync(filePath);
|
|
54661
55249
|
if (fileStat.size > MAX_FILE_SIZE_BYTES3) {
|
|
54662
55250
|
continue;
|
|
54663
55251
|
}
|
|
@@ -54666,7 +55254,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
54666
55254
|
}
|
|
54667
55255
|
let content;
|
|
54668
55256
|
try {
|
|
54669
|
-
content =
|
|
55257
|
+
content = fs27.readFileSync(filePath, "utf-8");
|
|
54670
55258
|
} catch {
|
|
54671
55259
|
continue;
|
|
54672
55260
|
}
|
|
@@ -54762,7 +55350,7 @@ var evidence_check = createSwarmTool({
|
|
|
54762
55350
|
return JSON.stringify(errorResult, null, 2);
|
|
54763
55351
|
}
|
|
54764
55352
|
const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
|
|
54765
|
-
const planPath =
|
|
55353
|
+
const planPath = path40.join(cwd, PLAN_FILE);
|
|
54766
55354
|
if (!isPathWithinSwarm2(planPath, cwd)) {
|
|
54767
55355
|
const errorResult = {
|
|
54768
55356
|
error: "plan file path validation failed",
|
|
@@ -54776,7 +55364,7 @@ var evidence_check = createSwarmTool({
|
|
|
54776
55364
|
}
|
|
54777
55365
|
let planContent;
|
|
54778
55366
|
try {
|
|
54779
|
-
planContent =
|
|
55367
|
+
planContent = fs27.readFileSync(planPath, "utf-8");
|
|
54780
55368
|
} catch {
|
|
54781
55369
|
const result2 = {
|
|
54782
55370
|
message: "No completed tasks found in plan.",
|
|
@@ -54794,7 +55382,7 @@ var evidence_check = createSwarmTool({
|
|
|
54794
55382
|
};
|
|
54795
55383
|
return JSON.stringify(result2, null, 2);
|
|
54796
55384
|
}
|
|
54797
|
-
const evidenceDir =
|
|
55385
|
+
const evidenceDir = path40.join(cwd, EVIDENCE_DIR2);
|
|
54798
55386
|
const evidence = readEvidenceFiles(evidenceDir, cwd);
|
|
54799
55387
|
const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
|
|
54800
55388
|
const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
|
|
@@ -54811,8 +55399,8 @@ var evidence_check = createSwarmTool({
|
|
|
54811
55399
|
// src/tools/file-extractor.ts
|
|
54812
55400
|
init_tool();
|
|
54813
55401
|
init_create_tool();
|
|
54814
|
-
import * as
|
|
54815
|
-
import * as
|
|
55402
|
+
import * as fs28 from "fs";
|
|
55403
|
+
import * as path41 from "path";
|
|
54816
55404
|
var EXT_MAP = {
|
|
54817
55405
|
python: ".py",
|
|
54818
55406
|
py: ".py",
|
|
@@ -54874,8 +55462,8 @@ var extract_code_blocks = createSwarmTool({
|
|
|
54874
55462
|
execute: async (args2, directory) => {
|
|
54875
55463
|
const { content, output_dir, prefix } = args2;
|
|
54876
55464
|
const targetDir = output_dir || directory;
|
|
54877
|
-
if (!
|
|
54878
|
-
|
|
55465
|
+
if (!fs28.existsSync(targetDir)) {
|
|
55466
|
+
fs28.mkdirSync(targetDir, { recursive: true });
|
|
54879
55467
|
}
|
|
54880
55468
|
if (!content) {
|
|
54881
55469
|
return "Error: content is required";
|
|
@@ -54893,16 +55481,16 @@ var extract_code_blocks = createSwarmTool({
|
|
|
54893
55481
|
if (prefix) {
|
|
54894
55482
|
filename = `${prefix}_${filename}`;
|
|
54895
55483
|
}
|
|
54896
|
-
let filepath =
|
|
54897
|
-
const base =
|
|
54898
|
-
const ext =
|
|
55484
|
+
let filepath = path41.join(targetDir, filename);
|
|
55485
|
+
const base = path41.basename(filepath, path41.extname(filepath));
|
|
55486
|
+
const ext = path41.extname(filepath);
|
|
54899
55487
|
let counter = 1;
|
|
54900
|
-
while (
|
|
54901
|
-
filepath =
|
|
55488
|
+
while (fs28.existsSync(filepath)) {
|
|
55489
|
+
filepath = path41.join(targetDir, `${base}_${counter}${ext}`);
|
|
54902
55490
|
counter++;
|
|
54903
55491
|
}
|
|
54904
55492
|
try {
|
|
54905
|
-
|
|
55493
|
+
fs28.writeFileSync(filepath, code.trim(), "utf-8");
|
|
54906
55494
|
savedFiles.push(filepath);
|
|
54907
55495
|
} catch (error93) {
|
|
54908
55496
|
errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
|
@@ -55015,8 +55603,8 @@ var gitingest = tool({
|
|
|
55015
55603
|
});
|
|
55016
55604
|
// src/tools/imports.ts
|
|
55017
55605
|
init_dist();
|
|
55018
|
-
import * as
|
|
55019
|
-
import * as
|
|
55606
|
+
import * as fs29 from "fs";
|
|
55607
|
+
import * as path42 from "path";
|
|
55020
55608
|
var MAX_FILE_PATH_LENGTH2 = 500;
|
|
55021
55609
|
var MAX_SYMBOL_LENGTH = 256;
|
|
55022
55610
|
var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
|
|
@@ -55070,7 +55658,7 @@ function validateSymbolInput(symbol3) {
|
|
|
55070
55658
|
return null;
|
|
55071
55659
|
}
|
|
55072
55660
|
function isBinaryFile2(filePath, buffer) {
|
|
55073
|
-
const ext =
|
|
55661
|
+
const ext = path42.extname(filePath).toLowerCase();
|
|
55074
55662
|
if (ext === ".json" || ext === ".md" || ext === ".txt") {
|
|
55075
55663
|
return false;
|
|
55076
55664
|
}
|
|
@@ -55094,15 +55682,15 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
55094
55682
|
const imports = [];
|
|
55095
55683
|
let _resolvedTarget;
|
|
55096
55684
|
try {
|
|
55097
|
-
_resolvedTarget =
|
|
55685
|
+
_resolvedTarget = path42.resolve(targetFile);
|
|
55098
55686
|
} catch {
|
|
55099
55687
|
_resolvedTarget = targetFile;
|
|
55100
55688
|
}
|
|
55101
|
-
const targetBasename =
|
|
55689
|
+
const targetBasename = path42.basename(targetFile, path42.extname(targetFile));
|
|
55102
55690
|
const targetWithExt = targetFile;
|
|
55103
55691
|
const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
55104
|
-
const normalizedTargetWithExt =
|
|
55105
|
-
const normalizedTargetWithoutExt =
|
|
55692
|
+
const normalizedTargetWithExt = path42.normalize(targetWithExt).replace(/\\/g, "/");
|
|
55693
|
+
const normalizedTargetWithoutExt = path42.normalize(targetWithoutExt).replace(/\\/g, "/");
|
|
55106
55694
|
const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
55107
55695
|
for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
|
|
55108
55696
|
const modulePath = match[1] || match[2] || match[3];
|
|
@@ -55125,9 +55713,9 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
55125
55713
|
}
|
|
55126
55714
|
const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
|
|
55127
55715
|
let isMatch = false;
|
|
55128
|
-
const _targetDir =
|
|
55129
|
-
const targetExt =
|
|
55130
|
-
const targetBasenameNoExt =
|
|
55716
|
+
const _targetDir = path42.dirname(targetFile);
|
|
55717
|
+
const targetExt = path42.extname(targetFile);
|
|
55718
|
+
const targetBasenameNoExt = path42.basename(targetFile, targetExt);
|
|
55131
55719
|
const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
55132
55720
|
const moduleName = modulePath.split(/[/\\]/).pop() || "";
|
|
55133
55721
|
const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
@@ -55184,7 +55772,7 @@ var SKIP_DIRECTORIES2 = new Set([
|
|
|
55184
55772
|
function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
|
|
55185
55773
|
let entries;
|
|
55186
55774
|
try {
|
|
55187
|
-
entries =
|
|
55775
|
+
entries = fs29.readdirSync(dir);
|
|
55188
55776
|
} catch (e) {
|
|
55189
55777
|
stats.fileErrors.push({
|
|
55190
55778
|
path: dir,
|
|
@@ -55195,13 +55783,13 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
|
|
|
55195
55783
|
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
55196
55784
|
for (const entry of entries) {
|
|
55197
55785
|
if (SKIP_DIRECTORIES2.has(entry)) {
|
|
55198
|
-
stats.skippedDirs.push(
|
|
55786
|
+
stats.skippedDirs.push(path42.join(dir, entry));
|
|
55199
55787
|
continue;
|
|
55200
55788
|
}
|
|
55201
|
-
const fullPath =
|
|
55789
|
+
const fullPath = path42.join(dir, entry);
|
|
55202
55790
|
let stat2;
|
|
55203
55791
|
try {
|
|
55204
|
-
stat2 =
|
|
55792
|
+
stat2 = fs29.statSync(fullPath);
|
|
55205
55793
|
} catch (e) {
|
|
55206
55794
|
stats.fileErrors.push({
|
|
55207
55795
|
path: fullPath,
|
|
@@ -55212,7 +55800,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
|
|
|
55212
55800
|
if (stat2.isDirectory()) {
|
|
55213
55801
|
findSourceFiles(fullPath, files, stats);
|
|
55214
55802
|
} else if (stat2.isFile()) {
|
|
55215
|
-
const ext =
|
|
55803
|
+
const ext = path42.extname(fullPath).toLowerCase();
|
|
55216
55804
|
if (SUPPORTED_EXTENSIONS.includes(ext)) {
|
|
55217
55805
|
files.push(fullPath);
|
|
55218
55806
|
}
|
|
@@ -55268,8 +55856,8 @@ var imports = tool({
|
|
|
55268
55856
|
return JSON.stringify(errorResult, null, 2);
|
|
55269
55857
|
}
|
|
55270
55858
|
try {
|
|
55271
|
-
const targetFile =
|
|
55272
|
-
if (!
|
|
55859
|
+
const targetFile = path42.resolve(file3);
|
|
55860
|
+
if (!fs29.existsSync(targetFile)) {
|
|
55273
55861
|
const errorResult = {
|
|
55274
55862
|
error: `target file not found: ${file3}`,
|
|
55275
55863
|
target: file3,
|
|
@@ -55279,7 +55867,7 @@ var imports = tool({
|
|
|
55279
55867
|
};
|
|
55280
55868
|
return JSON.stringify(errorResult, null, 2);
|
|
55281
55869
|
}
|
|
55282
|
-
const targetStat =
|
|
55870
|
+
const targetStat = fs29.statSync(targetFile);
|
|
55283
55871
|
if (!targetStat.isFile()) {
|
|
55284
55872
|
const errorResult = {
|
|
55285
55873
|
error: "target must be a file, not a directory",
|
|
@@ -55290,7 +55878,7 @@ var imports = tool({
|
|
|
55290
55878
|
};
|
|
55291
55879
|
return JSON.stringify(errorResult, null, 2);
|
|
55292
55880
|
}
|
|
55293
|
-
const baseDir =
|
|
55881
|
+
const baseDir = path42.dirname(targetFile);
|
|
55294
55882
|
const scanStats = {
|
|
55295
55883
|
skippedDirs: [],
|
|
55296
55884
|
skippedFiles: 0,
|
|
@@ -55305,12 +55893,12 @@ var imports = tool({
|
|
|
55305
55893
|
if (consumers.length >= MAX_CONSUMERS)
|
|
55306
55894
|
break;
|
|
55307
55895
|
try {
|
|
55308
|
-
const stat2 =
|
|
55896
|
+
const stat2 = fs29.statSync(filePath);
|
|
55309
55897
|
if (stat2.size > MAX_FILE_SIZE_BYTES4) {
|
|
55310
55898
|
skippedFileCount++;
|
|
55311
55899
|
continue;
|
|
55312
55900
|
}
|
|
55313
|
-
const buffer =
|
|
55901
|
+
const buffer = fs29.readFileSync(filePath);
|
|
55314
55902
|
if (isBinaryFile2(filePath, buffer)) {
|
|
55315
55903
|
skippedFileCount++;
|
|
55316
55904
|
continue;
|
|
@@ -55375,7 +55963,7 @@ var imports = tool({
|
|
|
55375
55963
|
});
|
|
55376
55964
|
// src/tools/knowledge-query.ts
|
|
55377
55965
|
init_dist();
|
|
55378
|
-
import { existsSync as
|
|
55966
|
+
import { existsSync as existsSync27 } from "fs";
|
|
55379
55967
|
init_create_tool();
|
|
55380
55968
|
var DEFAULT_LIMIT = 10;
|
|
55381
55969
|
var MAX_LESSON_LENGTH = 200;
|
|
@@ -55445,14 +56033,14 @@ function validateLimit(limit) {
|
|
|
55445
56033
|
}
|
|
55446
56034
|
async function readSwarmKnowledge(directory) {
|
|
55447
56035
|
const swarmPath = resolveSwarmKnowledgePath(directory);
|
|
55448
|
-
if (!
|
|
56036
|
+
if (!existsSync27(swarmPath)) {
|
|
55449
56037
|
return [];
|
|
55450
56038
|
}
|
|
55451
56039
|
return readKnowledge(swarmPath);
|
|
55452
56040
|
}
|
|
55453
56041
|
async function readHiveKnowledge() {
|
|
55454
56042
|
const hivePath = resolveHiveKnowledgePath();
|
|
55455
|
-
if (!
|
|
56043
|
+
if (!existsSync27(hivePath)) {
|
|
55456
56044
|
return [];
|
|
55457
56045
|
}
|
|
55458
56046
|
return readKnowledge(hivePath);
|
|
@@ -55611,8 +56199,8 @@ init_dist();
|
|
|
55611
56199
|
init_config();
|
|
55612
56200
|
init_schema();
|
|
55613
56201
|
init_manager();
|
|
55614
|
-
import * as
|
|
55615
|
-
import * as
|
|
56202
|
+
import * as fs30 from "fs";
|
|
56203
|
+
import * as path43 from "path";
|
|
55616
56204
|
init_utils2();
|
|
55617
56205
|
init_create_tool();
|
|
55618
56206
|
function safeWarn(message, error93) {
|
|
@@ -55807,7 +56395,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55807
56395
|
}
|
|
55808
56396
|
if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
|
|
55809
56397
|
try {
|
|
55810
|
-
const projectName =
|
|
56398
|
+
const projectName = path43.basename(dir);
|
|
55811
56399
|
const knowledgeConfig = {
|
|
55812
56400
|
enabled: true,
|
|
55813
56401
|
swarm_max_entries: 100,
|
|
@@ -55855,7 +56443,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55855
56443
|
if (agentsMissing.length > 0) {
|
|
55856
56444
|
try {
|
|
55857
56445
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
55858
|
-
const planRaw =
|
|
56446
|
+
const planRaw = fs30.readFileSync(planPath, "utf-8");
|
|
55859
56447
|
const plan = JSON.parse(planRaw);
|
|
55860
56448
|
const targetPhase = plan.phases.find((p) => p.id === phase);
|
|
55861
56449
|
if (targetPhase && targetPhase.tasks.length > 0 && targetPhase.tasks.every((t) => t.status === "completed")) {
|
|
@@ -55896,7 +56484,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55896
56484
|
};
|
|
55897
56485
|
try {
|
|
55898
56486
|
const eventsPath = validateSwarmPath(dir, "events.jsonl");
|
|
55899
|
-
|
|
56487
|
+
fs30.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
55900
56488
|
`, "utf-8");
|
|
55901
56489
|
} catch (writeError) {
|
|
55902
56490
|
warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
@@ -55915,12 +56503,12 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55915
56503
|
}
|
|
55916
56504
|
try {
|
|
55917
56505
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
55918
|
-
const planJson =
|
|
56506
|
+
const planJson = fs30.readFileSync(planPath, "utf-8");
|
|
55919
56507
|
const plan = JSON.parse(planJson);
|
|
55920
56508
|
const phaseObj = plan.phases.find((p) => p.id === phase);
|
|
55921
56509
|
if (phaseObj) {
|
|
55922
56510
|
phaseObj.status = "completed";
|
|
55923
|
-
|
|
56511
|
+
fs30.writeFileSync(planPath, `${JSON.stringify(plan, null, 2)}
|
|
55924
56512
|
`, "utf-8");
|
|
55925
56513
|
}
|
|
55926
56514
|
} catch (error93) {
|
|
@@ -55970,8 +56558,8 @@ init_dist();
|
|
|
55970
56558
|
init_discovery();
|
|
55971
56559
|
init_utils();
|
|
55972
56560
|
init_create_tool();
|
|
55973
|
-
import * as
|
|
55974
|
-
import * as
|
|
56561
|
+
import * as fs31 from "fs";
|
|
56562
|
+
import * as path44 from "path";
|
|
55975
56563
|
var MAX_OUTPUT_BYTES5 = 52428800;
|
|
55976
56564
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
55977
56565
|
function isValidEcosystem(value) {
|
|
@@ -55989,28 +56577,28 @@ function validateArgs3(args2) {
|
|
|
55989
56577
|
function detectEcosystems(directory) {
|
|
55990
56578
|
const ecosystems = [];
|
|
55991
56579
|
const cwd = directory;
|
|
55992
|
-
if (
|
|
56580
|
+
if (fs31.existsSync(path44.join(cwd, "package.json"))) {
|
|
55993
56581
|
ecosystems.push("npm");
|
|
55994
56582
|
}
|
|
55995
|
-
if (
|
|
56583
|
+
if (fs31.existsSync(path44.join(cwd, "pyproject.toml")) || fs31.existsSync(path44.join(cwd, "requirements.txt"))) {
|
|
55996
56584
|
ecosystems.push("pip");
|
|
55997
56585
|
}
|
|
55998
|
-
if (
|
|
56586
|
+
if (fs31.existsSync(path44.join(cwd, "Cargo.toml"))) {
|
|
55999
56587
|
ecosystems.push("cargo");
|
|
56000
56588
|
}
|
|
56001
|
-
if (
|
|
56589
|
+
if (fs31.existsSync(path44.join(cwd, "go.mod"))) {
|
|
56002
56590
|
ecosystems.push("go");
|
|
56003
56591
|
}
|
|
56004
56592
|
try {
|
|
56005
|
-
const files =
|
|
56593
|
+
const files = fs31.readdirSync(cwd);
|
|
56006
56594
|
if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
|
|
56007
56595
|
ecosystems.push("dotnet");
|
|
56008
56596
|
}
|
|
56009
56597
|
} catch {}
|
|
56010
|
-
if (
|
|
56598
|
+
if (fs31.existsSync(path44.join(cwd, "Gemfile")) || fs31.existsSync(path44.join(cwd, "Gemfile.lock"))) {
|
|
56011
56599
|
ecosystems.push("ruby");
|
|
56012
56600
|
}
|
|
56013
|
-
if (
|
|
56601
|
+
if (fs31.existsSync(path44.join(cwd, "pubspec.yaml"))) {
|
|
56014
56602
|
ecosystems.push("dart");
|
|
56015
56603
|
}
|
|
56016
56604
|
return ecosystems;
|
|
@@ -57072,8 +57660,8 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
|
|
|
57072
57660
|
]);
|
|
57073
57661
|
// src/tools/pre-check-batch.ts
|
|
57074
57662
|
init_dist();
|
|
57075
|
-
import * as
|
|
57076
|
-
import * as
|
|
57663
|
+
import * as fs34 from "fs";
|
|
57664
|
+
import * as path47 from "path";
|
|
57077
57665
|
|
|
57078
57666
|
// node_modules/yocto-queue/index.js
|
|
57079
57667
|
class Node2 {
|
|
@@ -57240,8 +57828,8 @@ init_lint();
|
|
|
57240
57828
|
init_manager();
|
|
57241
57829
|
|
|
57242
57830
|
// src/quality/metrics.ts
|
|
57243
|
-
import * as
|
|
57244
|
-
import * as
|
|
57831
|
+
import * as fs32 from "fs";
|
|
57832
|
+
import * as path45 from "path";
|
|
57245
57833
|
var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
|
|
57246
57834
|
var MIN_DUPLICATION_LINES = 10;
|
|
57247
57835
|
function estimateCyclomaticComplexity(content) {
|
|
@@ -57279,11 +57867,11 @@ function estimateCyclomaticComplexity(content) {
|
|
|
57279
57867
|
}
|
|
57280
57868
|
function getComplexityForFile2(filePath) {
|
|
57281
57869
|
try {
|
|
57282
|
-
const stat2 =
|
|
57870
|
+
const stat2 = fs32.statSync(filePath);
|
|
57283
57871
|
if (stat2.size > MAX_FILE_SIZE_BYTES5) {
|
|
57284
57872
|
return null;
|
|
57285
57873
|
}
|
|
57286
|
-
const content =
|
|
57874
|
+
const content = fs32.readFileSync(filePath, "utf-8");
|
|
57287
57875
|
return estimateCyclomaticComplexity(content);
|
|
57288
57876
|
} catch {
|
|
57289
57877
|
return null;
|
|
@@ -57293,8 +57881,8 @@ async function computeComplexityDelta(files, workingDir) {
|
|
|
57293
57881
|
let totalComplexity = 0;
|
|
57294
57882
|
const analyzedFiles = [];
|
|
57295
57883
|
for (const file3 of files) {
|
|
57296
|
-
const fullPath =
|
|
57297
|
-
if (!
|
|
57884
|
+
const fullPath = path45.isAbsolute(file3) ? file3 : path45.join(workingDir, file3);
|
|
57885
|
+
if (!fs32.existsSync(fullPath)) {
|
|
57298
57886
|
continue;
|
|
57299
57887
|
}
|
|
57300
57888
|
const complexity = getComplexityForFile2(fullPath);
|
|
@@ -57415,8 +58003,8 @@ function countGoExports(content) {
|
|
|
57415
58003
|
}
|
|
57416
58004
|
function getExportCountForFile(filePath) {
|
|
57417
58005
|
try {
|
|
57418
|
-
const content =
|
|
57419
|
-
const ext =
|
|
58006
|
+
const content = fs32.readFileSync(filePath, "utf-8");
|
|
58007
|
+
const ext = path45.extname(filePath).toLowerCase();
|
|
57420
58008
|
switch (ext) {
|
|
57421
58009
|
case ".ts":
|
|
57422
58010
|
case ".tsx":
|
|
@@ -57442,8 +58030,8 @@ async function computePublicApiDelta(files, workingDir) {
|
|
|
57442
58030
|
let totalExports = 0;
|
|
57443
58031
|
const analyzedFiles = [];
|
|
57444
58032
|
for (const file3 of files) {
|
|
57445
|
-
const fullPath =
|
|
57446
|
-
if (!
|
|
58033
|
+
const fullPath = path45.isAbsolute(file3) ? file3 : path45.join(workingDir, file3);
|
|
58034
|
+
if (!fs32.existsSync(fullPath)) {
|
|
57447
58035
|
continue;
|
|
57448
58036
|
}
|
|
57449
58037
|
const exports = getExportCountForFile(fullPath);
|
|
@@ -57476,16 +58064,16 @@ async function computeDuplicationRatio(files, workingDir) {
|
|
|
57476
58064
|
let duplicateLines = 0;
|
|
57477
58065
|
const analyzedFiles = [];
|
|
57478
58066
|
for (const file3 of files) {
|
|
57479
|
-
const fullPath =
|
|
57480
|
-
if (!
|
|
58067
|
+
const fullPath = path45.isAbsolute(file3) ? file3 : path45.join(workingDir, file3);
|
|
58068
|
+
if (!fs32.existsSync(fullPath)) {
|
|
57481
58069
|
continue;
|
|
57482
58070
|
}
|
|
57483
58071
|
try {
|
|
57484
|
-
const stat2 =
|
|
58072
|
+
const stat2 = fs32.statSync(fullPath);
|
|
57485
58073
|
if (stat2.size > MAX_FILE_SIZE_BYTES5) {
|
|
57486
58074
|
continue;
|
|
57487
58075
|
}
|
|
57488
|
-
const content =
|
|
58076
|
+
const content = fs32.readFileSync(fullPath, "utf-8");
|
|
57489
58077
|
const lines = content.split(`
|
|
57490
58078
|
`).filter((line) => line.trim().length > 0);
|
|
57491
58079
|
if (lines.length < MIN_DUPLICATION_LINES) {
|
|
@@ -57509,8 +58097,8 @@ function countCodeLines(content) {
|
|
|
57509
58097
|
return lines.length;
|
|
57510
58098
|
}
|
|
57511
58099
|
function isTestFile(filePath) {
|
|
57512
|
-
const basename8 =
|
|
57513
|
-
const _ext =
|
|
58100
|
+
const basename8 = path45.basename(filePath);
|
|
58101
|
+
const _ext = path45.extname(filePath).toLowerCase();
|
|
57514
58102
|
const testPatterns = [
|
|
57515
58103
|
".test.",
|
|
57516
58104
|
".spec.",
|
|
@@ -57591,8 +58179,8 @@ function matchGlobSegment(globSegments, pathSegments) {
|
|
|
57591
58179
|
}
|
|
57592
58180
|
return gIndex === globSegments.length && pIndex === pathSegments.length;
|
|
57593
58181
|
}
|
|
57594
|
-
function matchesGlobSegment(
|
|
57595
|
-
const normalizedPath =
|
|
58182
|
+
function matchesGlobSegment(path46, glob) {
|
|
58183
|
+
const normalizedPath = path46.replace(/\\/g, "/");
|
|
57596
58184
|
const normalizedGlob = glob.replace(/\\/g, "/");
|
|
57597
58185
|
if (normalizedPath.includes("//")) {
|
|
57598
58186
|
return false;
|
|
@@ -57623,8 +58211,8 @@ function simpleGlobToRegex2(glob) {
|
|
|
57623
58211
|
function hasGlobstar(glob) {
|
|
57624
58212
|
return glob.includes("**");
|
|
57625
58213
|
}
|
|
57626
|
-
function globMatches(
|
|
57627
|
-
const normalizedPath =
|
|
58214
|
+
function globMatches(path46, glob) {
|
|
58215
|
+
const normalizedPath = path46.replace(/\\/g, "/");
|
|
57628
58216
|
if (!glob || glob === "") {
|
|
57629
58217
|
if (normalizedPath.includes("//")) {
|
|
57630
58218
|
return false;
|
|
@@ -57660,31 +58248,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
|
|
|
57660
58248
|
async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
57661
58249
|
let testLines = 0;
|
|
57662
58250
|
let codeLines = 0;
|
|
57663
|
-
const srcDir =
|
|
57664
|
-
if (
|
|
58251
|
+
const srcDir = path45.join(workingDir, "src");
|
|
58252
|
+
if (fs32.existsSync(srcDir)) {
|
|
57665
58253
|
await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
57666
58254
|
codeLines += lines;
|
|
57667
58255
|
});
|
|
57668
58256
|
}
|
|
57669
58257
|
const possibleSrcDirs = ["lib", "app", "source", "core"];
|
|
57670
58258
|
for (const dir of possibleSrcDirs) {
|
|
57671
|
-
const dirPath =
|
|
57672
|
-
if (
|
|
58259
|
+
const dirPath = path45.join(workingDir, dir);
|
|
58260
|
+
if (fs32.existsSync(dirPath)) {
|
|
57673
58261
|
await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
57674
58262
|
codeLines += lines;
|
|
57675
58263
|
});
|
|
57676
58264
|
}
|
|
57677
58265
|
}
|
|
57678
|
-
const testsDir =
|
|
57679
|
-
if (
|
|
58266
|
+
const testsDir = path45.join(workingDir, "tests");
|
|
58267
|
+
if (fs32.existsSync(testsDir)) {
|
|
57680
58268
|
await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
57681
58269
|
testLines += lines;
|
|
57682
58270
|
});
|
|
57683
58271
|
}
|
|
57684
58272
|
const possibleTestDirs = ["test", "__tests__", "specs"];
|
|
57685
58273
|
for (const dir of possibleTestDirs) {
|
|
57686
|
-
const dirPath =
|
|
57687
|
-
if (
|
|
58274
|
+
const dirPath = path45.join(workingDir, dir);
|
|
58275
|
+
if (fs32.existsSync(dirPath) && dirPath !== testsDir) {
|
|
57688
58276
|
await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
57689
58277
|
testLines += lines;
|
|
57690
58278
|
});
|
|
@@ -57696,9 +58284,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
57696
58284
|
}
|
|
57697
58285
|
async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
|
|
57698
58286
|
try {
|
|
57699
|
-
const entries =
|
|
58287
|
+
const entries = fs32.readdirSync(dirPath, { withFileTypes: true });
|
|
57700
58288
|
for (const entry of entries) {
|
|
57701
|
-
const fullPath =
|
|
58289
|
+
const fullPath = path45.join(dirPath, entry.name);
|
|
57702
58290
|
if (entry.isDirectory()) {
|
|
57703
58291
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
|
|
57704
58292
|
continue;
|
|
@@ -57706,7 +58294,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
57706
58294
|
await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
|
|
57707
58295
|
} else if (entry.isFile()) {
|
|
57708
58296
|
const relativePath = fullPath.replace(`${process.cwd()}/`, "");
|
|
57709
|
-
const ext =
|
|
58297
|
+
const ext = path45.extname(entry.name).toLowerCase();
|
|
57710
58298
|
const validExts = [
|
|
57711
58299
|
".ts",
|
|
57712
58300
|
".tsx",
|
|
@@ -57742,7 +58330,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
57742
58330
|
continue;
|
|
57743
58331
|
}
|
|
57744
58332
|
try {
|
|
57745
|
-
const content =
|
|
58333
|
+
const content = fs32.readFileSync(fullPath, "utf-8");
|
|
57746
58334
|
const lines = countCodeLines(content);
|
|
57747
58335
|
callback(lines);
|
|
57748
58336
|
} catch {}
|
|
@@ -57956,8 +58544,8 @@ async function qualityBudget(input, directory) {
|
|
|
57956
58544
|
init_dist();
|
|
57957
58545
|
init_manager();
|
|
57958
58546
|
init_detector();
|
|
57959
|
-
import * as
|
|
57960
|
-
import * as
|
|
58547
|
+
import * as fs33 from "fs";
|
|
58548
|
+
import * as path46 from "path";
|
|
57961
58549
|
import { extname as extname9 } from "path";
|
|
57962
58550
|
|
|
57963
58551
|
// src/sast/rules/c.ts
|
|
@@ -58824,17 +59412,17 @@ var SEVERITY_ORDER = {
|
|
|
58824
59412
|
};
|
|
58825
59413
|
function shouldSkipFile(filePath) {
|
|
58826
59414
|
try {
|
|
58827
|
-
const stats =
|
|
59415
|
+
const stats = fs33.statSync(filePath);
|
|
58828
59416
|
if (stats.size > MAX_FILE_SIZE_BYTES6) {
|
|
58829
59417
|
return { skip: true, reason: "file too large" };
|
|
58830
59418
|
}
|
|
58831
59419
|
if (stats.size === 0) {
|
|
58832
59420
|
return { skip: true, reason: "empty file" };
|
|
58833
59421
|
}
|
|
58834
|
-
const fd =
|
|
59422
|
+
const fd = fs33.openSync(filePath, "r");
|
|
58835
59423
|
const buffer = Buffer.alloc(8192);
|
|
58836
|
-
const bytesRead =
|
|
58837
|
-
|
|
59424
|
+
const bytesRead = fs33.readSync(fd, buffer, 0, 8192, 0);
|
|
59425
|
+
fs33.closeSync(fd);
|
|
58838
59426
|
if (bytesRead > 0) {
|
|
58839
59427
|
let nullCount = 0;
|
|
58840
59428
|
for (let i2 = 0;i2 < bytesRead; i2++) {
|
|
@@ -58873,7 +59461,7 @@ function countBySeverity(findings) {
|
|
|
58873
59461
|
}
|
|
58874
59462
|
function scanFileWithTierA(filePath, language) {
|
|
58875
59463
|
try {
|
|
58876
|
-
const content =
|
|
59464
|
+
const content = fs33.readFileSync(filePath, "utf-8");
|
|
58877
59465
|
const findings = executeRulesSync(filePath, content, language);
|
|
58878
59466
|
return findings.map((f) => ({
|
|
58879
59467
|
rule_id: f.rule_id,
|
|
@@ -58920,8 +59508,8 @@ async function sastScan(input, directory, config3) {
|
|
|
58920
59508
|
_filesSkipped++;
|
|
58921
59509
|
continue;
|
|
58922
59510
|
}
|
|
58923
|
-
const resolvedPath =
|
|
58924
|
-
if (!
|
|
59511
|
+
const resolvedPath = path46.isAbsolute(filePath) ? filePath : path46.resolve(directory, filePath);
|
|
59512
|
+
if (!fs33.existsSync(resolvedPath)) {
|
|
58925
59513
|
_filesSkipped++;
|
|
58926
59514
|
continue;
|
|
58927
59515
|
}
|
|
@@ -59119,18 +59707,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
59119
59707
|
let resolved;
|
|
59120
59708
|
const isWinAbs = isWindowsAbsolutePath(inputPath);
|
|
59121
59709
|
if (isWinAbs) {
|
|
59122
|
-
resolved =
|
|
59123
|
-
} else if (
|
|
59124
|
-
resolved =
|
|
59710
|
+
resolved = path47.win32.resolve(inputPath);
|
|
59711
|
+
} else if (path47.isAbsolute(inputPath)) {
|
|
59712
|
+
resolved = path47.resolve(inputPath);
|
|
59125
59713
|
} else {
|
|
59126
|
-
resolved =
|
|
59714
|
+
resolved = path47.resolve(baseDir, inputPath);
|
|
59127
59715
|
}
|
|
59128
|
-
const workspaceResolved =
|
|
59716
|
+
const workspaceResolved = path47.resolve(workspaceDir);
|
|
59129
59717
|
let relative5;
|
|
59130
59718
|
if (isWinAbs) {
|
|
59131
|
-
relative5 =
|
|
59719
|
+
relative5 = path47.win32.relative(workspaceResolved, resolved);
|
|
59132
59720
|
} else {
|
|
59133
|
-
relative5 =
|
|
59721
|
+
relative5 = path47.relative(workspaceResolved, resolved);
|
|
59134
59722
|
}
|
|
59135
59723
|
if (relative5.startsWith("..")) {
|
|
59136
59724
|
return "path traversal detected";
|
|
@@ -59150,7 +59738,7 @@ function validateDirectory3(dir, workspaceDir) {
|
|
|
59150
59738
|
}
|
|
59151
59739
|
return null;
|
|
59152
59740
|
}
|
|
59153
|
-
async function
|
|
59741
|
+
async function runWithTimeout2(promise3, timeoutMs) {
|
|
59154
59742
|
const timeoutPromise = new Promise((_, reject) => {
|
|
59155
59743
|
setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs);
|
|
59156
59744
|
});
|
|
@@ -59159,7 +59747,7 @@ async function runWithTimeout(promise3, timeoutMs) {
|
|
|
59159
59747
|
async function runLintWrapped(files, directory, _config) {
|
|
59160
59748
|
const start2 = process.hrtime.bigint();
|
|
59161
59749
|
try {
|
|
59162
|
-
const linter = await detectAvailableLinter();
|
|
59750
|
+
const linter = await detectAvailableLinter(directory);
|
|
59163
59751
|
if (!linter) {
|
|
59164
59752
|
return {
|
|
59165
59753
|
ran: false,
|
|
@@ -59175,7 +59763,7 @@ async function runLintWrapped(files, directory, _config) {
|
|
|
59175
59763
|
duration_ms: Number(process.hrtime.bigint() - start2) / 1e6
|
|
59176
59764
|
};
|
|
59177
59765
|
}
|
|
59178
|
-
const result = await
|
|
59766
|
+
const result = await runWithTimeout2(runLint(linter, "check", directory), TOOL_TIMEOUT_MS);
|
|
59179
59767
|
return {
|
|
59180
59768
|
ran: true,
|
|
59181
59769
|
result,
|
|
@@ -59191,13 +59779,13 @@ async function runLintWrapped(files, directory, _config) {
|
|
|
59191
59779
|
}
|
|
59192
59780
|
async function runLintOnFiles(linter, files, workspaceDir) {
|
|
59193
59781
|
const isWindows = process.platform === "win32";
|
|
59194
|
-
const binDir =
|
|
59782
|
+
const binDir = path47.join(workspaceDir, "node_modules", ".bin");
|
|
59195
59783
|
const validatedFiles = [];
|
|
59196
59784
|
for (const file3 of files) {
|
|
59197
59785
|
if (typeof file3 !== "string") {
|
|
59198
59786
|
continue;
|
|
59199
59787
|
}
|
|
59200
|
-
const resolvedPath =
|
|
59788
|
+
const resolvedPath = path47.resolve(file3);
|
|
59201
59789
|
const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
|
|
59202
59790
|
if (validationError) {
|
|
59203
59791
|
continue;
|
|
@@ -59215,10 +59803,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
|
|
|
59215
59803
|
}
|
|
59216
59804
|
let command;
|
|
59217
59805
|
if (linter === "biome") {
|
|
59218
|
-
const biomeBin = isWindows ?
|
|
59806
|
+
const biomeBin = isWindows ? path47.join(binDir, "biome.EXE") : path47.join(binDir, "biome");
|
|
59219
59807
|
command = [biomeBin, "check", ...validatedFiles];
|
|
59220
59808
|
} else {
|
|
59221
|
-
const eslintBin = isWindows ?
|
|
59809
|
+
const eslintBin = isWindows ? path47.join(binDir, "eslint.cmd") : path47.join(binDir, "eslint");
|
|
59222
59810
|
command = [eslintBin, ...validatedFiles];
|
|
59223
59811
|
}
|
|
59224
59812
|
try {
|
|
@@ -59269,14 +59857,14 @@ async function runSecretscanWrapped(files, directory, _config) {
|
|
|
59269
59857
|
const start2 = process.hrtime.bigint();
|
|
59270
59858
|
try {
|
|
59271
59859
|
if (files && files.length > 0) {
|
|
59272
|
-
const result2 = await
|
|
59860
|
+
const result2 = await runWithTimeout2(runSecretscanWithFiles(files, directory), TOOL_TIMEOUT_MS);
|
|
59273
59861
|
return {
|
|
59274
59862
|
ran: true,
|
|
59275
59863
|
result: result2,
|
|
59276
59864
|
duration_ms: Number(process.hrtime.bigint() - start2) / 1e6
|
|
59277
59865
|
};
|
|
59278
59866
|
}
|
|
59279
|
-
const result = await
|
|
59867
|
+
const result = await runWithTimeout2(runSecretscan(directory), TOOL_TIMEOUT_MS);
|
|
59280
59868
|
return {
|
|
59281
59869
|
ran: true,
|
|
59282
59870
|
result,
|
|
@@ -59355,7 +59943,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59355
59943
|
skippedFiles++;
|
|
59356
59944
|
continue;
|
|
59357
59945
|
}
|
|
59358
|
-
const resolvedPath =
|
|
59946
|
+
const resolvedPath = path47.resolve(file3);
|
|
59359
59947
|
const validationError = validatePath(resolvedPath, directory, directory);
|
|
59360
59948
|
if (validationError) {
|
|
59361
59949
|
skippedFiles++;
|
|
@@ -59373,14 +59961,14 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59373
59961
|
};
|
|
59374
59962
|
}
|
|
59375
59963
|
for (const file3 of validatedFiles) {
|
|
59376
|
-
const ext =
|
|
59964
|
+
const ext = path47.extname(file3).toLowerCase();
|
|
59377
59965
|
if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
|
|
59378
59966
|
skippedFiles++;
|
|
59379
59967
|
continue;
|
|
59380
59968
|
}
|
|
59381
59969
|
let stat2;
|
|
59382
59970
|
try {
|
|
59383
|
-
stat2 =
|
|
59971
|
+
stat2 = fs34.statSync(file3);
|
|
59384
59972
|
} catch {
|
|
59385
59973
|
skippedFiles++;
|
|
59386
59974
|
continue;
|
|
@@ -59391,7 +59979,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59391
59979
|
}
|
|
59392
59980
|
let content;
|
|
59393
59981
|
try {
|
|
59394
|
-
const buffer =
|
|
59982
|
+
const buffer = fs34.readFileSync(file3);
|
|
59395
59983
|
if (buffer.includes(0)) {
|
|
59396
59984
|
skippedFiles++;
|
|
59397
59985
|
continue;
|
|
@@ -59460,7 +60048,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59460
60048
|
async function runSastScanWrapped(changedFiles, directory, severityThreshold, config3) {
|
|
59461
60049
|
const start2 = process.hrtime.bigint();
|
|
59462
60050
|
try {
|
|
59463
|
-
const result = await
|
|
60051
|
+
const result = await runWithTimeout2(sastScan({ changed_files: changedFiles, severity_threshold: severityThreshold }, directory, config3), TOOL_TIMEOUT_MS);
|
|
59464
60052
|
return {
|
|
59465
60053
|
ran: true,
|
|
59466
60054
|
result,
|
|
@@ -59477,7 +60065,7 @@ async function runSastScanWrapped(changedFiles, directory, severityThreshold, co
|
|
|
59477
60065
|
async function runQualityBudgetWrapped(changedFiles, directory, _config) {
|
|
59478
60066
|
const start2 = process.hrtime.bigint();
|
|
59479
60067
|
try {
|
|
59480
|
-
const result = await
|
|
60068
|
+
const result = await runWithTimeout2(qualityBudget({ changed_files: changedFiles }, directory), TOOL_TIMEOUT_MS);
|
|
59481
60069
|
return {
|
|
59482
60070
|
ran: true,
|
|
59483
60071
|
result,
|
|
@@ -59532,7 +60120,7 @@ async function runPreCheckBatch(input, workspaceDir) {
|
|
|
59532
60120
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
59533
60121
|
continue;
|
|
59534
60122
|
}
|
|
59535
|
-
changedFiles.push(
|
|
60123
|
+
changedFiles.push(path47.resolve(directory, file3));
|
|
59536
60124
|
}
|
|
59537
60125
|
if (changedFiles.length === 0) {
|
|
59538
60126
|
warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
|
|
@@ -59683,7 +60271,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
59683
60271
|
};
|
|
59684
60272
|
return JSON.stringify(errorResult, null, 2);
|
|
59685
60273
|
}
|
|
59686
|
-
const resolvedDirectory =
|
|
60274
|
+
const resolvedDirectory = path47.resolve(typedArgs.directory);
|
|
59687
60275
|
const workspaceAnchor = resolvedDirectory;
|
|
59688
60276
|
const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
|
|
59689
60277
|
if (dirError) {
|
|
@@ -59790,8 +60378,8 @@ ${paginatedContent}`;
|
|
|
59790
60378
|
init_tool();
|
|
59791
60379
|
init_manager2();
|
|
59792
60380
|
init_create_tool();
|
|
59793
|
-
import * as
|
|
59794
|
-
import * as
|
|
60381
|
+
import * as fs35 from "fs";
|
|
60382
|
+
import * as path48 from "path";
|
|
59795
60383
|
function detectPlaceholderContent(args2) {
|
|
59796
60384
|
const issues = [];
|
|
59797
60385
|
const placeholderPattern = /^\[\w[\w\s]*\]$/;
|
|
@@ -59895,19 +60483,19 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
59895
60483
|
try {
|
|
59896
60484
|
await savePlan(dir, plan);
|
|
59897
60485
|
try {
|
|
59898
|
-
const markerPath =
|
|
60486
|
+
const markerPath = path48.join(dir, ".swarm", ".plan-write-marker");
|
|
59899
60487
|
const marker = JSON.stringify({
|
|
59900
60488
|
source: "save_plan",
|
|
59901
60489
|
timestamp: new Date().toISOString(),
|
|
59902
60490
|
phases_count: plan.phases.length,
|
|
59903
60491
|
tasks_count: tasksCount
|
|
59904
60492
|
});
|
|
59905
|
-
await
|
|
60493
|
+
await fs35.promises.writeFile(markerPath, marker, "utf8");
|
|
59906
60494
|
} catch {}
|
|
59907
60495
|
return {
|
|
59908
60496
|
success: true,
|
|
59909
60497
|
message: "Plan saved successfully",
|
|
59910
|
-
plan_path:
|
|
60498
|
+
plan_path: path48.join(dir, ".swarm", "plan.json"),
|
|
59911
60499
|
phases_count: plan.phases.length,
|
|
59912
60500
|
tasks_count: tasksCount
|
|
59913
60501
|
};
|
|
@@ -59945,8 +60533,8 @@ var save_plan = createSwarmTool({
|
|
|
59945
60533
|
// src/tools/sbom-generate.ts
|
|
59946
60534
|
init_dist();
|
|
59947
60535
|
init_manager();
|
|
59948
|
-
import * as
|
|
59949
|
-
import * as
|
|
60536
|
+
import * as fs36 from "fs";
|
|
60537
|
+
import * as path49 from "path";
|
|
59950
60538
|
|
|
59951
60539
|
// src/sbom/detectors/index.ts
|
|
59952
60540
|
init_utils();
|
|
@@ -60792,9 +61380,9 @@ function findManifestFiles(rootDir) {
|
|
|
60792
61380
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
60793
61381
|
function searchDir(dir) {
|
|
60794
61382
|
try {
|
|
60795
|
-
const entries =
|
|
61383
|
+
const entries = fs36.readdirSync(dir, { withFileTypes: true });
|
|
60796
61384
|
for (const entry of entries) {
|
|
60797
|
-
const fullPath =
|
|
61385
|
+
const fullPath = path49.join(dir, entry.name);
|
|
60798
61386
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
60799
61387
|
continue;
|
|
60800
61388
|
}
|
|
@@ -60803,7 +61391,7 @@ function findManifestFiles(rootDir) {
|
|
|
60803
61391
|
} else if (entry.isFile()) {
|
|
60804
61392
|
for (const pattern of patterns) {
|
|
60805
61393
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
60806
|
-
manifestFiles.push(
|
|
61394
|
+
manifestFiles.push(path49.relative(rootDir, fullPath));
|
|
60807
61395
|
break;
|
|
60808
61396
|
}
|
|
60809
61397
|
}
|
|
@@ -60819,13 +61407,13 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
60819
61407
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
60820
61408
|
for (const dir of directories) {
|
|
60821
61409
|
try {
|
|
60822
|
-
const entries =
|
|
61410
|
+
const entries = fs36.readdirSync(dir, { withFileTypes: true });
|
|
60823
61411
|
for (const entry of entries) {
|
|
60824
|
-
const fullPath =
|
|
61412
|
+
const fullPath = path49.join(dir, entry.name);
|
|
60825
61413
|
if (entry.isFile()) {
|
|
60826
61414
|
for (const pattern of patterns) {
|
|
60827
61415
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
60828
|
-
found.push(
|
|
61416
|
+
found.push(path49.relative(workingDir, fullPath));
|
|
60829
61417
|
break;
|
|
60830
61418
|
}
|
|
60831
61419
|
}
|
|
@@ -60838,11 +61426,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
60838
61426
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
60839
61427
|
const dirs = new Set;
|
|
60840
61428
|
for (const file3 of changedFiles) {
|
|
60841
|
-
let currentDir =
|
|
61429
|
+
let currentDir = path49.dirname(file3);
|
|
60842
61430
|
while (true) {
|
|
60843
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
60844
|
-
dirs.add(
|
|
60845
|
-
const parent =
|
|
61431
|
+
if (currentDir && currentDir !== "." && currentDir !== path49.sep) {
|
|
61432
|
+
dirs.add(path49.join(workingDir, currentDir));
|
|
61433
|
+
const parent = path49.dirname(currentDir);
|
|
60846
61434
|
if (parent === currentDir)
|
|
60847
61435
|
break;
|
|
60848
61436
|
currentDir = parent;
|
|
@@ -60856,7 +61444,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
|
60856
61444
|
}
|
|
60857
61445
|
function ensureOutputDir(outputDir) {
|
|
60858
61446
|
try {
|
|
60859
|
-
|
|
61447
|
+
fs36.mkdirSync(outputDir, { recursive: true });
|
|
60860
61448
|
} catch (error93) {
|
|
60861
61449
|
if (!error93 || error93.code !== "EEXIST") {
|
|
60862
61450
|
throw error93;
|
|
@@ -60926,7 +61514,7 @@ var sbom_generate = createSwarmTool({
|
|
|
60926
61514
|
const changedFiles = obj.changed_files;
|
|
60927
61515
|
const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
|
|
60928
61516
|
const workingDir = directory;
|
|
60929
|
-
const outputDir =
|
|
61517
|
+
const outputDir = path49.isAbsolute(relativeOutputDir) ? relativeOutputDir : path49.join(workingDir, relativeOutputDir);
|
|
60930
61518
|
let manifestFiles = [];
|
|
60931
61519
|
if (scope === "all") {
|
|
60932
61520
|
manifestFiles = findManifestFiles(workingDir);
|
|
@@ -60949,11 +61537,11 @@ var sbom_generate = createSwarmTool({
|
|
|
60949
61537
|
const processedFiles = [];
|
|
60950
61538
|
for (const manifestFile of manifestFiles) {
|
|
60951
61539
|
try {
|
|
60952
|
-
const fullPath =
|
|
60953
|
-
if (!
|
|
61540
|
+
const fullPath = path49.isAbsolute(manifestFile) ? manifestFile : path49.join(workingDir, manifestFile);
|
|
61541
|
+
if (!fs36.existsSync(fullPath)) {
|
|
60954
61542
|
continue;
|
|
60955
61543
|
}
|
|
60956
|
-
const content =
|
|
61544
|
+
const content = fs36.readFileSync(fullPath, "utf-8");
|
|
60957
61545
|
const components = detectComponents(manifestFile, content);
|
|
60958
61546
|
processedFiles.push(manifestFile);
|
|
60959
61547
|
if (components.length > 0) {
|
|
@@ -60966,8 +61554,8 @@ var sbom_generate = createSwarmTool({
|
|
|
60966
61554
|
const bom = generateCycloneDX(allComponents);
|
|
60967
61555
|
const bomJson = serializeCycloneDX(bom);
|
|
60968
61556
|
const filename = generateSbomFilename();
|
|
60969
|
-
const outputPath =
|
|
60970
|
-
|
|
61557
|
+
const outputPath = path49.join(outputDir, filename);
|
|
61558
|
+
fs36.writeFileSync(outputPath, bomJson, "utf-8");
|
|
60971
61559
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
60972
61560
|
try {
|
|
60973
61561
|
const timestamp = new Date().toISOString();
|
|
@@ -61009,8 +61597,8 @@ var sbom_generate = createSwarmTool({
|
|
|
61009
61597
|
// src/tools/schema-drift.ts
|
|
61010
61598
|
init_dist();
|
|
61011
61599
|
init_create_tool();
|
|
61012
|
-
import * as
|
|
61013
|
-
import * as
|
|
61600
|
+
import * as fs37 from "fs";
|
|
61601
|
+
import * as path50 from "path";
|
|
61014
61602
|
var SPEC_CANDIDATES = [
|
|
61015
61603
|
"openapi.json",
|
|
61016
61604
|
"openapi.yaml",
|
|
@@ -61042,28 +61630,28 @@ function normalizePath2(p) {
|
|
|
61042
61630
|
}
|
|
61043
61631
|
function discoverSpecFile(cwd, specFileArg) {
|
|
61044
61632
|
if (specFileArg) {
|
|
61045
|
-
const resolvedPath =
|
|
61046
|
-
const normalizedCwd = cwd.endsWith(
|
|
61633
|
+
const resolvedPath = path50.resolve(cwd, specFileArg);
|
|
61634
|
+
const normalizedCwd = cwd.endsWith(path50.sep) ? cwd : cwd + path50.sep;
|
|
61047
61635
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
61048
61636
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
61049
61637
|
}
|
|
61050
|
-
const ext =
|
|
61638
|
+
const ext = path50.extname(resolvedPath).toLowerCase();
|
|
61051
61639
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
61052
61640
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
61053
61641
|
}
|
|
61054
|
-
const stats =
|
|
61642
|
+
const stats = fs37.statSync(resolvedPath);
|
|
61055
61643
|
if (stats.size > MAX_SPEC_SIZE) {
|
|
61056
61644
|
throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
|
|
61057
61645
|
}
|
|
61058
|
-
if (!
|
|
61646
|
+
if (!fs37.existsSync(resolvedPath)) {
|
|
61059
61647
|
throw new Error(`Spec file not found: ${resolvedPath}`);
|
|
61060
61648
|
}
|
|
61061
61649
|
return resolvedPath;
|
|
61062
61650
|
}
|
|
61063
61651
|
for (const candidate of SPEC_CANDIDATES) {
|
|
61064
|
-
const candidatePath =
|
|
61065
|
-
if (
|
|
61066
|
-
const stats =
|
|
61652
|
+
const candidatePath = path50.resolve(cwd, candidate);
|
|
61653
|
+
if (fs37.existsSync(candidatePath)) {
|
|
61654
|
+
const stats = fs37.statSync(candidatePath);
|
|
61067
61655
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
61068
61656
|
return candidatePath;
|
|
61069
61657
|
}
|
|
@@ -61072,8 +61660,8 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
61072
61660
|
return null;
|
|
61073
61661
|
}
|
|
61074
61662
|
function parseSpec(specFile) {
|
|
61075
|
-
const content =
|
|
61076
|
-
const ext =
|
|
61663
|
+
const content = fs37.readFileSync(specFile, "utf-8");
|
|
61664
|
+
const ext = path50.extname(specFile).toLowerCase();
|
|
61077
61665
|
if (ext === ".json") {
|
|
61078
61666
|
return parseJsonSpec(content);
|
|
61079
61667
|
}
|
|
@@ -61144,12 +61732,12 @@ function extractRoutes(cwd) {
|
|
|
61144
61732
|
function walkDir(dir) {
|
|
61145
61733
|
let entries;
|
|
61146
61734
|
try {
|
|
61147
|
-
entries =
|
|
61735
|
+
entries = fs37.readdirSync(dir, { withFileTypes: true });
|
|
61148
61736
|
} catch {
|
|
61149
61737
|
return;
|
|
61150
61738
|
}
|
|
61151
61739
|
for (const entry of entries) {
|
|
61152
|
-
const fullPath =
|
|
61740
|
+
const fullPath = path50.join(dir, entry.name);
|
|
61153
61741
|
if (entry.isSymbolicLink()) {
|
|
61154
61742
|
continue;
|
|
61155
61743
|
}
|
|
@@ -61159,7 +61747,7 @@ function extractRoutes(cwd) {
|
|
|
61159
61747
|
}
|
|
61160
61748
|
walkDir(fullPath);
|
|
61161
61749
|
} else if (entry.isFile()) {
|
|
61162
|
-
const ext =
|
|
61750
|
+
const ext = path50.extname(entry.name).toLowerCase();
|
|
61163
61751
|
const baseName = entry.name.toLowerCase();
|
|
61164
61752
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
61165
61753
|
continue;
|
|
@@ -61177,7 +61765,7 @@ function extractRoutes(cwd) {
|
|
|
61177
61765
|
}
|
|
61178
61766
|
function extractRoutesFromFile(filePath) {
|
|
61179
61767
|
const routes = [];
|
|
61180
|
-
const content =
|
|
61768
|
+
const content = fs37.readFileSync(filePath, "utf-8");
|
|
61181
61769
|
const lines = content.split(/\r?\n/);
|
|
61182
61770
|
const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
61183
61771
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
@@ -61328,8 +61916,8 @@ init_secretscan();
|
|
|
61328
61916
|
// src/tools/symbols.ts
|
|
61329
61917
|
init_tool();
|
|
61330
61918
|
init_create_tool();
|
|
61331
|
-
import * as
|
|
61332
|
-
import * as
|
|
61919
|
+
import * as fs38 from "fs";
|
|
61920
|
+
import * as path51 from "path";
|
|
61333
61921
|
var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
|
|
61334
61922
|
var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
61335
61923
|
function containsControlCharacters(str) {
|
|
@@ -61358,11 +61946,11 @@ function containsWindowsAttacks(str) {
|
|
|
61358
61946
|
}
|
|
61359
61947
|
function isPathInWorkspace(filePath, workspace) {
|
|
61360
61948
|
try {
|
|
61361
|
-
const resolvedPath =
|
|
61362
|
-
const realWorkspace =
|
|
61363
|
-
const realResolvedPath =
|
|
61364
|
-
const relativePath =
|
|
61365
|
-
if (relativePath.startsWith("..") ||
|
|
61949
|
+
const resolvedPath = path51.resolve(workspace, filePath);
|
|
61950
|
+
const realWorkspace = fs38.realpathSync(workspace);
|
|
61951
|
+
const realResolvedPath = fs38.realpathSync(resolvedPath);
|
|
61952
|
+
const relativePath = path51.relative(realWorkspace, realResolvedPath);
|
|
61953
|
+
if (relativePath.startsWith("..") || path51.isAbsolute(relativePath)) {
|
|
61366
61954
|
return false;
|
|
61367
61955
|
}
|
|
61368
61956
|
return true;
|
|
@@ -61374,17 +61962,17 @@ function validatePathForRead(filePath, workspace) {
|
|
|
61374
61962
|
return isPathInWorkspace(filePath, workspace);
|
|
61375
61963
|
}
|
|
61376
61964
|
function extractTSSymbols(filePath, cwd) {
|
|
61377
|
-
const fullPath =
|
|
61965
|
+
const fullPath = path51.join(cwd, filePath);
|
|
61378
61966
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
61379
61967
|
return [];
|
|
61380
61968
|
}
|
|
61381
61969
|
let content;
|
|
61382
61970
|
try {
|
|
61383
|
-
const stats =
|
|
61971
|
+
const stats = fs38.statSync(fullPath);
|
|
61384
61972
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
61385
61973
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
61386
61974
|
}
|
|
61387
|
-
content =
|
|
61975
|
+
content = fs38.readFileSync(fullPath, "utf-8");
|
|
61388
61976
|
} catch {
|
|
61389
61977
|
return [];
|
|
61390
61978
|
}
|
|
@@ -61526,17 +62114,17 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
61526
62114
|
});
|
|
61527
62115
|
}
|
|
61528
62116
|
function extractPythonSymbols(filePath, cwd) {
|
|
61529
|
-
const fullPath =
|
|
62117
|
+
const fullPath = path51.join(cwd, filePath);
|
|
61530
62118
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
61531
62119
|
return [];
|
|
61532
62120
|
}
|
|
61533
62121
|
let content;
|
|
61534
62122
|
try {
|
|
61535
|
-
const stats =
|
|
62123
|
+
const stats = fs38.statSync(fullPath);
|
|
61536
62124
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
61537
62125
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
61538
62126
|
}
|
|
61539
|
-
content =
|
|
62127
|
+
content = fs38.readFileSync(fullPath, "utf-8");
|
|
61540
62128
|
} catch {
|
|
61541
62129
|
return [];
|
|
61542
62130
|
}
|
|
@@ -61609,7 +62197,7 @@ var symbols = createSwarmTool({
|
|
|
61609
62197
|
}, null, 2);
|
|
61610
62198
|
}
|
|
61611
62199
|
const cwd = directory;
|
|
61612
|
-
const ext =
|
|
62200
|
+
const ext = path51.extname(file3);
|
|
61613
62201
|
if (containsControlCharacters(file3)) {
|
|
61614
62202
|
return JSON.stringify({
|
|
61615
62203
|
file: file3,
|
|
@@ -61680,8 +62268,8 @@ init_test_runner();
|
|
|
61680
62268
|
init_dist();
|
|
61681
62269
|
init_utils();
|
|
61682
62270
|
init_create_tool();
|
|
61683
|
-
import * as
|
|
61684
|
-
import * as
|
|
62271
|
+
import * as fs39 from "fs";
|
|
62272
|
+
import * as path52 from "path";
|
|
61685
62273
|
var MAX_TEXT_LENGTH = 200;
|
|
61686
62274
|
var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
|
|
61687
62275
|
var SUPPORTED_EXTENSIONS2 = new Set([
|
|
@@ -61752,9 +62340,9 @@ function validatePathsInput(paths, cwd) {
|
|
|
61752
62340
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
61753
62341
|
}
|
|
61754
62342
|
try {
|
|
61755
|
-
const resolvedPath =
|
|
61756
|
-
const normalizedCwd =
|
|
61757
|
-
const normalizedResolved =
|
|
62343
|
+
const resolvedPath = path52.resolve(paths);
|
|
62344
|
+
const normalizedCwd = path52.resolve(cwd);
|
|
62345
|
+
const normalizedResolved = path52.resolve(resolvedPath);
|
|
61758
62346
|
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
61759
62347
|
return {
|
|
61760
62348
|
error: "paths must be within the current working directory",
|
|
@@ -61770,13 +62358,13 @@ function validatePathsInput(paths, cwd) {
|
|
|
61770
62358
|
}
|
|
61771
62359
|
}
|
|
61772
62360
|
function isSupportedExtension(filePath) {
|
|
61773
|
-
const ext =
|
|
62361
|
+
const ext = path52.extname(filePath).toLowerCase();
|
|
61774
62362
|
return SUPPORTED_EXTENSIONS2.has(ext);
|
|
61775
62363
|
}
|
|
61776
62364
|
function findSourceFiles2(dir, files = []) {
|
|
61777
62365
|
let entries;
|
|
61778
62366
|
try {
|
|
61779
|
-
entries =
|
|
62367
|
+
entries = fs39.readdirSync(dir);
|
|
61780
62368
|
} catch {
|
|
61781
62369
|
return files;
|
|
61782
62370
|
}
|
|
@@ -61785,10 +62373,10 @@ function findSourceFiles2(dir, files = []) {
|
|
|
61785
62373
|
if (SKIP_DIRECTORIES3.has(entry)) {
|
|
61786
62374
|
continue;
|
|
61787
62375
|
}
|
|
61788
|
-
const fullPath =
|
|
62376
|
+
const fullPath = path52.join(dir, entry);
|
|
61789
62377
|
let stat2;
|
|
61790
62378
|
try {
|
|
61791
|
-
stat2 =
|
|
62379
|
+
stat2 = fs39.statSync(fullPath);
|
|
61792
62380
|
} catch {
|
|
61793
62381
|
continue;
|
|
61794
62382
|
}
|
|
@@ -61881,7 +62469,7 @@ var todo_extract = createSwarmTool({
|
|
|
61881
62469
|
return JSON.stringify(errorResult, null, 2);
|
|
61882
62470
|
}
|
|
61883
62471
|
const scanPath = resolvedPath;
|
|
61884
|
-
if (!
|
|
62472
|
+
if (!fs39.existsSync(scanPath)) {
|
|
61885
62473
|
const errorResult = {
|
|
61886
62474
|
error: `path not found: ${pathsInput}`,
|
|
61887
62475
|
total: 0,
|
|
@@ -61891,13 +62479,13 @@ var todo_extract = createSwarmTool({
|
|
|
61891
62479
|
return JSON.stringify(errorResult, null, 2);
|
|
61892
62480
|
}
|
|
61893
62481
|
const filesToScan = [];
|
|
61894
|
-
const stat2 =
|
|
62482
|
+
const stat2 = fs39.statSync(scanPath);
|
|
61895
62483
|
if (stat2.isFile()) {
|
|
61896
62484
|
if (isSupportedExtension(scanPath)) {
|
|
61897
62485
|
filesToScan.push(scanPath);
|
|
61898
62486
|
} else {
|
|
61899
62487
|
const errorResult = {
|
|
61900
|
-
error: `unsupported file extension: ${
|
|
62488
|
+
error: `unsupported file extension: ${path52.extname(scanPath)}`,
|
|
61901
62489
|
total: 0,
|
|
61902
62490
|
byPriority: { high: 0, medium: 0, low: 0 },
|
|
61903
62491
|
entries: []
|
|
@@ -61910,11 +62498,11 @@ var todo_extract = createSwarmTool({
|
|
|
61910
62498
|
const allEntries = [];
|
|
61911
62499
|
for (const filePath of filesToScan) {
|
|
61912
62500
|
try {
|
|
61913
|
-
const fileStat =
|
|
62501
|
+
const fileStat = fs39.statSync(filePath);
|
|
61914
62502
|
if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
|
|
61915
62503
|
continue;
|
|
61916
62504
|
}
|
|
61917
|
-
const content =
|
|
62505
|
+
const content = fs39.readFileSync(filePath, "utf-8");
|
|
61918
62506
|
const entries = parseTodoComments(content, filePath, tagsSet);
|
|
61919
62507
|
allEntries.push(...entries);
|
|
61920
62508
|
} catch {}
|
|
@@ -61942,9 +62530,95 @@ var todo_extract = createSwarmTool({
|
|
|
61942
62530
|
// src/tools/update-task-status.ts
|
|
61943
62531
|
init_tool();
|
|
61944
62532
|
init_schema();
|
|
62533
|
+
import * as fs41 from "fs";
|
|
62534
|
+
import * as path54 from "path";
|
|
62535
|
+
|
|
62536
|
+
// src/hooks/diff-scope.ts
|
|
62537
|
+
import * as fs40 from "fs";
|
|
62538
|
+
import * as path53 from "path";
|
|
62539
|
+
function getDeclaredScope(taskId, directory) {
|
|
62540
|
+
try {
|
|
62541
|
+
const planPath = path53.join(directory, ".swarm", "plan.json");
|
|
62542
|
+
if (!fs40.existsSync(planPath))
|
|
62543
|
+
return null;
|
|
62544
|
+
const raw = fs40.readFileSync(planPath, "utf-8");
|
|
62545
|
+
const plan = JSON.parse(raw);
|
|
62546
|
+
for (const phase of plan.phases ?? []) {
|
|
62547
|
+
for (const task of phase.tasks ?? []) {
|
|
62548
|
+
if (task.id !== taskId)
|
|
62549
|
+
continue;
|
|
62550
|
+
const ft = task.files_touched;
|
|
62551
|
+
if (Array.isArray(ft) && ft.length > 0) {
|
|
62552
|
+
return ft;
|
|
62553
|
+
}
|
|
62554
|
+
if (typeof ft === "string" && ft.length > 0) {
|
|
62555
|
+
return [ft];
|
|
62556
|
+
}
|
|
62557
|
+
return null;
|
|
62558
|
+
}
|
|
62559
|
+
}
|
|
62560
|
+
return null;
|
|
62561
|
+
} catch {
|
|
62562
|
+
return null;
|
|
62563
|
+
}
|
|
62564
|
+
}
|
|
62565
|
+
async function getChangedFiles(directory) {
|
|
62566
|
+
try {
|
|
62567
|
+
const proc = Bun.spawn(["git", "diff", "--name-only", "HEAD~1"], {
|
|
62568
|
+
cwd: directory,
|
|
62569
|
+
stdout: "pipe",
|
|
62570
|
+
stderr: "pipe"
|
|
62571
|
+
});
|
|
62572
|
+
const [exitCode, stdout] = await Promise.all([
|
|
62573
|
+
proc.exited,
|
|
62574
|
+
new Response(proc.stdout).text()
|
|
62575
|
+
]);
|
|
62576
|
+
if (exitCode === 0) {
|
|
62577
|
+
return stdout.trim().split(`
|
|
62578
|
+
`).map((f) => f.trim()).filter((f) => f.length > 0);
|
|
62579
|
+
}
|
|
62580
|
+
const proc2 = Bun.spawn(["git", "diff", "--name-only", "HEAD"], {
|
|
62581
|
+
cwd: directory,
|
|
62582
|
+
stdout: "pipe",
|
|
62583
|
+
stderr: "pipe"
|
|
62584
|
+
});
|
|
62585
|
+
const [exitCode2, stdout2] = await Promise.all([
|
|
62586
|
+
proc2.exited,
|
|
62587
|
+
new Response(proc2.stdout).text()
|
|
62588
|
+
]);
|
|
62589
|
+
if (exitCode2 === 0) {
|
|
62590
|
+
return stdout2.trim().split(`
|
|
62591
|
+
`).map((f) => f.trim()).filter((f) => f.length > 0);
|
|
62592
|
+
}
|
|
62593
|
+
return null;
|
|
62594
|
+
} catch {
|
|
62595
|
+
return null;
|
|
62596
|
+
}
|
|
62597
|
+
}
|
|
62598
|
+
async function validateDiffScope(taskId, directory) {
|
|
62599
|
+
try {
|
|
62600
|
+
const declaredScope = getDeclaredScope(taskId, directory);
|
|
62601
|
+
if (!declaredScope)
|
|
62602
|
+
return null;
|
|
62603
|
+
const changedFiles = await getChangedFiles(directory);
|
|
62604
|
+
if (!changedFiles)
|
|
62605
|
+
return null;
|
|
62606
|
+
const normalise = (p) => p.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
62607
|
+
const normScope = new Set(declaredScope.map(normalise));
|
|
62608
|
+
const undeclared = changedFiles.map(normalise).filter((f) => !normScope.has(f));
|
|
62609
|
+
if (undeclared.length === 0)
|
|
62610
|
+
return null;
|
|
62611
|
+
const scopeStr = declaredScope.join(", ");
|
|
62612
|
+
const undeclaredStr = undeclared.slice(0, 5).join(", ");
|
|
62613
|
+
const extra = undeclared.length > 5 ? ` (+${undeclared.length - 5} more)` : "";
|
|
62614
|
+
return `SCOPE WARNING: Task ${taskId} declared scope [${scopeStr}] but also modified [${undeclaredStr}${extra}]. Reviewer should verify these changes are intentional.`;
|
|
62615
|
+
} catch {
|
|
62616
|
+
return null;
|
|
62617
|
+
}
|
|
62618
|
+
}
|
|
62619
|
+
|
|
62620
|
+
// src/tools/update-task-status.ts
|
|
61945
62621
|
init_manager2();
|
|
61946
|
-
import * as fs38 from "fs";
|
|
61947
|
-
import * as path51 from "path";
|
|
61948
62622
|
init_create_tool();
|
|
61949
62623
|
var VALID_STATUSES2 = [
|
|
61950
62624
|
"pending",
|
|
@@ -61979,7 +62653,7 @@ var TIER_3_PATTERNS = [
|
|
|
61979
62653
|
];
|
|
61980
62654
|
function matchesTier3Pattern(files) {
|
|
61981
62655
|
for (const file3 of files) {
|
|
61982
|
-
const fileName =
|
|
62656
|
+
const fileName = path54.basename(file3);
|
|
61983
62657
|
for (const pattern of TIER_3_PATTERNS) {
|
|
61984
62658
|
if (pattern.test(fileName)) {
|
|
61985
62659
|
return true;
|
|
@@ -62001,8 +62675,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62001
62675
|
if (hasActiveTurboMode2()) {
|
|
62002
62676
|
const resolvedDir2 = workingDirectory ?? process.cwd();
|
|
62003
62677
|
try {
|
|
62004
|
-
const planPath =
|
|
62005
|
-
const planRaw =
|
|
62678
|
+
const planPath = path54.join(resolvedDir2, ".swarm", "plan.json");
|
|
62679
|
+
const planRaw = fs41.readFileSync(planPath, "utf-8");
|
|
62006
62680
|
const plan = JSON.parse(planRaw);
|
|
62007
62681
|
for (const planPhase of plan.phases ?? []) {
|
|
62008
62682
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -62021,8 +62695,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62021
62695
|
}
|
|
62022
62696
|
const resolvedDir = workingDirectory ?? process.cwd();
|
|
62023
62697
|
try {
|
|
62024
|
-
const evidencePath =
|
|
62025
|
-
const raw =
|
|
62698
|
+
const evidencePath = path54.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
|
|
62699
|
+
const raw = fs41.readFileSync(evidencePath, "utf-8");
|
|
62026
62700
|
const evidence = JSON.parse(raw);
|
|
62027
62701
|
if (evidence?.required_gates && Array.isArray(evidence.required_gates) && evidence?.gates) {
|
|
62028
62702
|
const allGatesMet = evidence.required_gates.every((gate) => evidence.gates[gate] != null);
|
|
@@ -62062,8 +62736,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62062
62736
|
}
|
|
62063
62737
|
try {
|
|
62064
62738
|
const resolvedDir2 = workingDirectory ?? process.cwd();
|
|
62065
|
-
const planPath =
|
|
62066
|
-
const planRaw =
|
|
62739
|
+
const planPath = path54.join(resolvedDir2, ".swarm", "plan.json");
|
|
62740
|
+
const planRaw = fs41.readFileSync(planPath, "utf-8");
|
|
62067
62741
|
const plan = JSON.parse(planRaw);
|
|
62068
62742
|
for (const planPhase of plan.phases ?? []) {
|
|
62069
62743
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -62121,6 +62795,17 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62121
62795
|
return { blocked: false, reason: "" };
|
|
62122
62796
|
}
|
|
62123
62797
|
}
|
|
62798
|
+
async function checkReviewerGateWithScope(taskId, workingDirectory) {
|
|
62799
|
+
const result = checkReviewerGate(taskId, workingDirectory);
|
|
62800
|
+
const scopeWarning = await validateDiffScope(taskId, workingDirectory ?? process.cwd()).catch(() => null);
|
|
62801
|
+
if (!scopeWarning)
|
|
62802
|
+
return result;
|
|
62803
|
+
return {
|
|
62804
|
+
...result,
|
|
62805
|
+
reason: result.reason ? `${result.reason}
|
|
62806
|
+
${scopeWarning}` : scopeWarning
|
|
62807
|
+
};
|
|
62808
|
+
}
|
|
62124
62809
|
function recoverTaskStateFromDelegations(taskId) {
|
|
62125
62810
|
let hasReviewer = false;
|
|
62126
62811
|
let hasTestEngineer = false;
|
|
@@ -62233,8 +62918,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
62233
62918
|
};
|
|
62234
62919
|
}
|
|
62235
62920
|
}
|
|
62236
|
-
normalizedDir =
|
|
62237
|
-
const pathParts = normalizedDir.split(
|
|
62921
|
+
normalizedDir = path54.normalize(args2.working_directory);
|
|
62922
|
+
const pathParts = normalizedDir.split(path54.sep);
|
|
62238
62923
|
if (pathParts.includes("..")) {
|
|
62239
62924
|
return {
|
|
62240
62925
|
success: false,
|
|
@@ -62244,11 +62929,11 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
62244
62929
|
]
|
|
62245
62930
|
};
|
|
62246
62931
|
}
|
|
62247
|
-
const resolvedDir =
|
|
62932
|
+
const resolvedDir = path54.resolve(normalizedDir);
|
|
62248
62933
|
try {
|
|
62249
|
-
const realPath =
|
|
62250
|
-
const planPath =
|
|
62251
|
-
if (!
|
|
62934
|
+
const realPath = fs41.realpathSync(resolvedDir);
|
|
62935
|
+
const planPath = path54.join(realPath, ".swarm", "plan.json");
|
|
62936
|
+
if (!fs41.existsSync(planPath)) {
|
|
62252
62937
|
return {
|
|
62253
62938
|
success: false,
|
|
62254
62939
|
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
@@ -62272,7 +62957,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
62272
62957
|
}
|
|
62273
62958
|
if (args2.status === "completed") {
|
|
62274
62959
|
recoverTaskStateFromDelegations(args2.task_id);
|
|
62275
|
-
const reviewerCheck =
|
|
62960
|
+
const reviewerCheck = await checkReviewerGateWithScope(args2.task_id, directory);
|
|
62276
62961
|
if (reviewerCheck.blocked) {
|
|
62277
62962
|
return {
|
|
62278
62963
|
success: false,
|
|
@@ -62395,6 +63080,31 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62395
63080
|
const steeringConsumedHook = createSteeringConsumedHook(ctx.directory);
|
|
62396
63081
|
const coChangeSuggesterHook = createCoChangeSuggesterHook(ctx.directory);
|
|
62397
63082
|
const darkMatterDetectorHook = createDarkMatterDetectorHook(ctx.directory);
|
|
63083
|
+
const slopDetectorHook = config3.slop_detector?.enabled !== false ? createSlopDetectorHook(config3.slop_detector ?? {
|
|
63084
|
+
enabled: true,
|
|
63085
|
+
classThreshold: 3,
|
|
63086
|
+
commentStripThreshold: 5,
|
|
63087
|
+
diffLineThreshold: 200
|
|
63088
|
+
}, ctx.directory, (_sessionId, message) => {
|
|
63089
|
+
console.warn(`[slop-detector] ${message}`);
|
|
63090
|
+
}) : null;
|
|
63091
|
+
const incrementalVerifyHook = config3.incremental_verify?.enabled !== false ? createIncrementalVerifyHook(config3.incremental_verify ?? {
|
|
63092
|
+
enabled: true,
|
|
63093
|
+
command: null,
|
|
63094
|
+
timeoutMs: 30000,
|
|
63095
|
+
triggerAgents: ["coder"]
|
|
63096
|
+
}, ctx.directory, (_sessionId, message) => {
|
|
63097
|
+
console.warn(`[incremental-verify] ${message}`);
|
|
63098
|
+
}) : null;
|
|
63099
|
+
const compactionServiceHook = config3.compaction_service?.enabled !== false ? createCompactionService(config3.compaction_service ?? {
|
|
63100
|
+
enabled: true,
|
|
63101
|
+
observationThreshold: 40,
|
|
63102
|
+
reflectionThreshold: 60,
|
|
63103
|
+
emergencyThreshold: 80,
|
|
63104
|
+
preserveLastNTurns: 5
|
|
63105
|
+
}, ctx.directory, (_sessionId, message) => {
|
|
63106
|
+
console.warn(`[compaction-service] ${message}`);
|
|
63107
|
+
}) : null;
|
|
62398
63108
|
const snapshotWriterHook = createSnapshotWriterHook(ctx.directory);
|
|
62399
63109
|
const automationConfig = AutomationConfigSchema.parse(config3.automation ?? {});
|
|
62400
63110
|
let automationManager;
|
|
@@ -62405,7 +63115,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62405
63115
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
62406
63116
|
preflightTriggerManager = new PTM(automationConfig);
|
|
62407
63117
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
62408
|
-
const swarmDir =
|
|
63118
|
+
const swarmDir = path55.resolve(ctx.directory, ".swarm");
|
|
62409
63119
|
statusArtifact = new ASA(swarmDir);
|
|
62410
63120
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
62411
63121
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|
|
@@ -62667,6 +63377,13 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62667
63377
|
}
|
|
62668
63378
|
}
|
|
62669
63379
|
await guardrailsHooks.toolBefore(input, output);
|
|
63380
|
+
if (swarmState.lastBudgetPct >= 50) {
|
|
63381
|
+
const pressureSession = ensureAgentSession(input.sessionID, swarmState.activeAgent.get(input.sessionID) ?? ORCHESTRATOR_NAME);
|
|
63382
|
+
if (!pressureSession.contextPressureWarningSent) {
|
|
63383
|
+
pressureSession.contextPressureWarningSent = true;
|
|
63384
|
+
console.warn(`[context-pressure] CONTEXT PRESSURE: ${swarmState.lastBudgetPct.toFixed(1)}% of context window estimated used. Prioritize completing the current task.`);
|
|
63385
|
+
}
|
|
63386
|
+
}
|
|
62670
63387
|
await safeHook(activityHooks.toolBefore)(input, output);
|
|
62671
63388
|
},
|
|
62672
63389
|
"tool.execute.after": async (input, output) => {
|
|
@@ -62682,6 +63399,12 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62682
63399
|
await safeHook(darkMatterDetectorHook)(input, output);
|
|
62683
63400
|
await snapshotWriterHook(input, output);
|
|
62684
63401
|
await toolSummarizerHook?.(input, output);
|
|
63402
|
+
if (slopDetectorHook)
|
|
63403
|
+
await slopDetectorHook.toolAfter(input, output);
|
|
63404
|
+
if (incrementalVerifyHook)
|
|
63405
|
+
await incrementalVerifyHook.toolAfter(input, output);
|
|
63406
|
+
if (compactionServiceHook)
|
|
63407
|
+
await compactionServiceHook.toolAfter(input, output);
|
|
62685
63408
|
const toolOutputConfig = config3.tool_output;
|
|
62686
63409
|
if (toolOutputConfig && toolOutputConfig.truncation_enabled !== false && typeof output.output === "string") {
|
|
62687
63410
|
const skipTools = [
|