opencode-swarm 6.28.1 → 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 +213 -25
- 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 +1186 -471
- 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/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
|
});
|
|
@@ -34189,10 +34216,7 @@ var init_secretscan = __esm(() => {
|
|
|
34189
34216
|
const excludeExact = new Set(DEFAULT_EXCLUDE_DIRS);
|
|
34190
34217
|
const excludeGlobs = [];
|
|
34191
34218
|
const ignoreFilePatterns = loadSecretScanIgnore(scanDir);
|
|
34192
|
-
const allUserPatterns = [
|
|
34193
|
-
...exclude ?? [],
|
|
34194
|
-
...ignoreFilePatterns
|
|
34195
|
-
];
|
|
34219
|
+
const allUserPatterns = [...exclude ?? [], ...ignoreFilePatterns];
|
|
34196
34220
|
for (const exc of allUserPatterns) {
|
|
34197
34221
|
if (exc.length === 0)
|
|
34198
34222
|
continue;
|
|
@@ -34435,7 +34459,7 @@ function detectMinitest(cwd) {
|
|
|
34435
34459
|
return fs11.existsSync(path23.join(cwd, "test")) && (fs11.existsSync(path23.join(cwd, "Gemfile")) || fs11.existsSync(path23.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
34436
34460
|
}
|
|
34437
34461
|
async function detectTestFramework(cwd) {
|
|
34438
|
-
const baseDir = cwd
|
|
34462
|
+
const baseDir = cwd;
|
|
34439
34463
|
try {
|
|
34440
34464
|
const packageJsonPath = path23.join(baseDir, "package.json");
|
|
34441
34465
|
if (fs11.existsSync(packageJsonPath)) {
|
|
@@ -37577,11 +37601,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
37577
37601
|
throw toThrow;
|
|
37578
37602
|
}, "quit_");
|
|
37579
37603
|
var scriptDirectory = "";
|
|
37580
|
-
function locateFile(
|
|
37604
|
+
function locateFile(path45) {
|
|
37581
37605
|
if (Module["locateFile"]) {
|
|
37582
|
-
return Module["locateFile"](
|
|
37606
|
+
return Module["locateFile"](path45, scriptDirectory);
|
|
37583
37607
|
}
|
|
37584
|
-
return scriptDirectory +
|
|
37608
|
+
return scriptDirectory + path45;
|
|
37585
37609
|
}
|
|
37586
37610
|
__name(locateFile, "locateFile");
|
|
37587
37611
|
var readAsync, readBinary;
|
|
@@ -39329,7 +39353,7 @@ var init_runtime = __esm(() => {
|
|
|
39329
39353
|
});
|
|
39330
39354
|
|
|
39331
39355
|
// src/index.ts
|
|
39332
|
-
import * as
|
|
39356
|
+
import * as path55 from "path";
|
|
39333
39357
|
|
|
39334
39358
|
// src/agents/index.ts
|
|
39335
39359
|
init_config();
|
|
@@ -39348,6 +39372,7 @@ var swarmState = {
|
|
|
39348
39372
|
activeAgent: new Map,
|
|
39349
39373
|
delegationChains: new Map,
|
|
39350
39374
|
pendingEvents: 0,
|
|
39375
|
+
lastBudgetPct: 0,
|
|
39351
39376
|
agentSessions: new Map
|
|
39352
39377
|
};
|
|
39353
39378
|
function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, directory) {
|
|
@@ -39392,7 +39417,8 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
|
|
|
39392
39417
|
lastScopeViolation: null,
|
|
39393
39418
|
scopeViolationDetected: false,
|
|
39394
39419
|
modifiedFilesThisCoderTask: [],
|
|
39395
|
-
turboMode: false
|
|
39420
|
+
turboMode: false,
|
|
39421
|
+
loopDetectionWindow: []
|
|
39396
39422
|
};
|
|
39397
39423
|
swarmState.agentSessions.set(sessionId, sessionState);
|
|
39398
39424
|
swarmState.activeAgent.set(sessionId, agentName);
|
|
@@ -39494,6 +39520,9 @@ function ensureAgentSession(sessionId, agentName, directory) {
|
|
|
39494
39520
|
if (session.turboMode === undefined) {
|
|
39495
39521
|
session.turboMode = false;
|
|
39496
39522
|
}
|
|
39523
|
+
if (session.loopDetectionWindow === undefined) {
|
|
39524
|
+
session.loopDetectionWindow = [];
|
|
39525
|
+
}
|
|
39497
39526
|
session.lastToolCallTime = now;
|
|
39498
39527
|
return session;
|
|
39499
39528
|
}
|
|
@@ -47578,6 +47607,173 @@ No plan content available. Start by creating a .swarm/plan.md file.
|
|
|
47578
47607
|
// src/services/status-service.ts
|
|
47579
47608
|
init_utils2();
|
|
47580
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
|
|
47581
47777
|
async function getStatusData(directory, agents) {
|
|
47582
47778
|
const plan = await loadPlan(directory);
|
|
47583
47779
|
if (plan && plan.migration_status !== "migration_failed") {
|
|
@@ -47599,7 +47795,10 @@ async function getStatusData(directory, agents) {
|
|
|
47599
47795
|
totalTasks: totalTasks2,
|
|
47600
47796
|
agentCount: agentCount2,
|
|
47601
47797
|
isLegacy: false,
|
|
47602
|
-
turboMode: hasActiveTurboMode()
|
|
47798
|
+
turboMode: hasActiveTurboMode(),
|
|
47799
|
+
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47800
|
+
compactionCount: 0,
|
|
47801
|
+
lastSnapshotAt: null
|
|
47603
47802
|
};
|
|
47604
47803
|
}
|
|
47605
47804
|
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
@@ -47611,7 +47810,10 @@ async function getStatusData(directory, agents) {
|
|
|
47611
47810
|
totalTasks: 0,
|
|
47612
47811
|
agentCount: Object.keys(agents).length,
|
|
47613
47812
|
isLegacy: true,
|
|
47614
|
-
turboMode: hasActiveTurboMode()
|
|
47813
|
+
turboMode: hasActiveTurboMode(),
|
|
47814
|
+
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47815
|
+
compactionCount: 0,
|
|
47816
|
+
lastSnapshotAt: null
|
|
47615
47817
|
};
|
|
47616
47818
|
}
|
|
47617
47819
|
const currentPhase = extractCurrentPhase(planContent) || "Unknown";
|
|
@@ -47626,7 +47828,10 @@ async function getStatusData(directory, agents) {
|
|
|
47626
47828
|
totalTasks,
|
|
47627
47829
|
agentCount,
|
|
47628
47830
|
isLegacy: true,
|
|
47629
|
-
turboMode: hasActiveTurboMode()
|
|
47831
|
+
turboMode: hasActiveTurboMode(),
|
|
47832
|
+
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47833
|
+
compactionCount: 0,
|
|
47834
|
+
lastSnapshotAt: null
|
|
47630
47835
|
};
|
|
47631
47836
|
}
|
|
47632
47837
|
function formatStatusMarkdown(status) {
|
|
@@ -47640,6 +47845,18 @@ function formatStatusMarkdown(status) {
|
|
|
47640
47845
|
if (status.turboMode) {
|
|
47641
47846
|
lines.push("", `**TURBO MODE**: active`);
|
|
47642
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
|
+
}
|
|
47643
47860
|
return lines.join(`
|
|
47644
47861
|
`);
|
|
47645
47862
|
}
|
|
@@ -47734,6 +47951,132 @@ async function executeWriteRetro(args2, directory) {
|
|
|
47734
47951
|
message: "Invalid task_count: must be a positive integer >= 1"
|
|
47735
47952
|
}, null, 2);
|
|
47736
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
|
+
}
|
|
47737
48080
|
const summary = args2.summary;
|
|
47738
48081
|
if (typeof summary !== "string" || summary.trim().length === 0) {
|
|
47739
48082
|
return JSON.stringify({
|
|
@@ -47755,6 +48098,8 @@ async function executeWriteRetro(args2, directory) {
|
|
|
47755
48098
|
total_tool_calls: args2.total_tool_calls,
|
|
47756
48099
|
coder_revisions: args2.coder_revisions,
|
|
47757
48100
|
reviewer_rejections: args2.reviewer_rejections,
|
|
48101
|
+
loop_detections: args2.loop_detections,
|
|
48102
|
+
circuit_breaker_trips: args2.circuit_breaker_trips,
|
|
47758
48103
|
test_failures: args2.test_failures,
|
|
47759
48104
|
security_findings: args2.security_findings,
|
|
47760
48105
|
integration_issues: args2.integration_issues,
|
|
@@ -47784,16 +48129,18 @@ async function executeWriteRetro(args2, directory) {
|
|
|
47784
48129
|
var write_retro = createSwarmTool({
|
|
47785
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.",
|
|
47786
48131
|
args: {
|
|
47787
|
-
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)"),
|
|
47788
48133
|
summary: tool.schema.string().describe("Human-readable summary of the phase"),
|
|
47789
|
-
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"),
|
|
47790
48135
|
task_complexity: tool.schema.enum(["trivial", "simple", "moderate", "complex"]).describe("Complexity level of the completed tasks"),
|
|
47791
|
-
total_tool_calls: tool.schema.number().int().min(0).describe("Total number of tool calls in this phase"),
|
|
47792
|
-
coder_revisions: tool.schema.number().int().min(0).describe("Number of coder revisions made"),
|
|
47793
|
-
reviewer_rejections: tool.schema.number().int().min(0).describe("Number of reviewer rejections received"),
|
|
47794
|
-
|
|
47795
|
-
|
|
47796
|
-
|
|
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"),
|
|
47797
48144
|
lessons_learned: tool.schema.array(tool.schema.string()).max(5).optional().describe("Key lessons learned from this phase (max 5)"),
|
|
47798
48145
|
top_rejection_reasons: tool.schema.array(tool.schema.string()).optional().describe("Top reasons for reviewer rejections"),
|
|
47799
48146
|
task_id: tool.schema.string().optional().describe("Optional custom task ID (defaults to retro-{phase})"),
|
|
@@ -47810,6 +48157,8 @@ var write_retro = createSwarmTool({
|
|
|
47810
48157
|
total_tool_calls: Number(args2.total_tool_calls),
|
|
47811
48158
|
coder_revisions: Number(args2.coder_revisions),
|
|
47812
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,
|
|
47813
48162
|
test_failures: Number(args2.test_failures),
|
|
47814
48163
|
security_findings: Number(args2.security_findings),
|
|
47815
48164
|
integration_issues: Number(args2.integration_issues),
|
|
@@ -48695,6 +49044,48 @@ init_schema();
|
|
|
48695
49044
|
init_manager2();
|
|
48696
49045
|
import * as path27 from "path";
|
|
48697
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
|
|
48698
49089
|
var storedInputArgs = new Map;
|
|
48699
49090
|
function getStoredInputArgs(callID) {
|
|
48700
49091
|
return storedInputArgs.get(callID);
|
|
@@ -48802,7 +49193,10 @@ function isAgentDelegation(toolName, args2) {
|
|
|
48802
49193
|
}
|
|
48803
49194
|
const subagentType = argsObj.subagent_type;
|
|
48804
49195
|
if (typeof subagentType === "string") {
|
|
48805
|
-
return {
|
|
49196
|
+
return {
|
|
49197
|
+
isDelegation: true,
|
|
49198
|
+
targetAgent: stripKnownSwarmPrefix(subagentType)
|
|
49199
|
+
};
|
|
48806
49200
|
}
|
|
48807
49201
|
return { isDelegation: false, targetAgent: null };
|
|
48808
49202
|
}
|
|
@@ -48869,6 +49263,37 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
48869
49263
|
}
|
|
48870
49264
|
}
|
|
48871
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
|
+
}
|
|
48872
49297
|
if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
|
|
48873
49298
|
const args2 = output.args;
|
|
48874
49299
|
const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
|
|
@@ -49213,6 +49638,21 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
49213
49638
|
const activeAgent = swarmState.activeAgent.get(sessionId);
|
|
49214
49639
|
const isArchitectSession = activeAgent ? stripKnownSwarmPrefix(activeAgent) === ORCHESTRATOR_NAME : session ? stripKnownSwarmPrefix(session.agentName) === ORCHESTRATOR_NAME : false;
|
|
49215
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
|
+
}
|
|
49216
49656
|
if (isArchitectSession && session && session.architectWriteCount > session.selfCodingWarnedAtCount) {
|
|
49217
49657
|
let targetSystemMessage = systemMessages[0];
|
|
49218
49658
|
if (!targetSystemMessage) {
|
|
@@ -50516,165 +50956,6 @@ function formatDriftForContext(result) {
|
|
|
50516
50956
|
|
|
50517
50957
|
// src/services/index.ts
|
|
50518
50958
|
init_config_doctor();
|
|
50519
|
-
|
|
50520
|
-
// src/services/context-budget-service.ts
|
|
50521
|
-
init_utils2();
|
|
50522
|
-
function validateDirectory(directory) {
|
|
50523
|
-
if (!directory || directory.trim() === "") {
|
|
50524
|
-
throw new Error("Invalid directory: empty");
|
|
50525
|
-
}
|
|
50526
|
-
if (/\.\.[/\\]/.test(directory)) {
|
|
50527
|
-
throw new Error("Invalid directory: path traversal detected");
|
|
50528
|
-
}
|
|
50529
|
-
if (directory.startsWith("/") || directory.startsWith("\\")) {
|
|
50530
|
-
throw new Error("Invalid directory: absolute path");
|
|
50531
|
-
}
|
|
50532
|
-
if (/^[A-Za-z]:[\\/]/.test(directory)) {
|
|
50533
|
-
throw new Error("Invalid directory: Windows absolute path");
|
|
50534
|
-
}
|
|
50535
|
-
}
|
|
50536
|
-
var COST_PER_1K_TOKENS = 0.003;
|
|
50537
|
-
function estimateTokens2(text) {
|
|
50538
|
-
if (!text || typeof text !== "string") {
|
|
50539
|
-
return 0;
|
|
50540
|
-
}
|
|
50541
|
-
return Math.ceil(text.length / 3.5);
|
|
50542
|
-
}
|
|
50543
|
-
async function readBudgetState(directory) {
|
|
50544
|
-
const content = await readSwarmFileAsync(directory, "session/budget-state.json");
|
|
50545
|
-
if (!content) {
|
|
50546
|
-
return null;
|
|
50547
|
-
}
|
|
50548
|
-
try {
|
|
50549
|
-
return JSON.parse(content);
|
|
50550
|
-
} catch {
|
|
50551
|
-
return null;
|
|
50552
|
-
}
|
|
50553
|
-
}
|
|
50554
|
-
async function writeBudgetState(directory, state) {
|
|
50555
|
-
const resolvedPath = validateSwarmPath(directory, "session/budget-state.json");
|
|
50556
|
-
const content = JSON.stringify(state, null, 2);
|
|
50557
|
-
await Bun.write(resolvedPath, content);
|
|
50558
|
-
}
|
|
50559
|
-
async function countEvents(directory) {
|
|
50560
|
-
const content = await readSwarmFileAsync(directory, "events.jsonl");
|
|
50561
|
-
if (!content) {
|
|
50562
|
-
return 0;
|
|
50563
|
-
}
|
|
50564
|
-
const lines = content.split(`
|
|
50565
|
-
`).filter((line) => line.trim().length > 0);
|
|
50566
|
-
return lines.length;
|
|
50567
|
-
}
|
|
50568
|
-
async function getPlanCursorContent(directory) {
|
|
50569
|
-
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
50570
|
-
if (!planContent) {
|
|
50571
|
-
return "";
|
|
50572
|
-
}
|
|
50573
|
-
const lines = planContent.split(`
|
|
50574
|
-
`);
|
|
50575
|
-
const cursorLines = [];
|
|
50576
|
-
let inCurrentSection = false;
|
|
50577
|
-
for (const line of lines) {
|
|
50578
|
-
if (line.includes("in_progress") || line.includes("**Current**")) {
|
|
50579
|
-
inCurrentSection = true;
|
|
50580
|
-
}
|
|
50581
|
-
if (inCurrentSection) {
|
|
50582
|
-
cursorLines.push(line);
|
|
50583
|
-
if (cursorLines.length > 30) {
|
|
50584
|
-
break;
|
|
50585
|
-
}
|
|
50586
|
-
}
|
|
50587
|
-
}
|
|
50588
|
-
return cursorLines.join(`
|
|
50589
|
-
`) || planContent.substring(0, 1000);
|
|
50590
|
-
}
|
|
50591
|
-
async function getContextBudgetReport(directory, assembledSystemPrompt, config3) {
|
|
50592
|
-
validateDirectory(directory);
|
|
50593
|
-
const timestamp = new Date().toISOString();
|
|
50594
|
-
const systemPromptTokens = estimateTokens2(assembledSystemPrompt);
|
|
50595
|
-
const planCursorContent = await getPlanCursorContent(directory);
|
|
50596
|
-
const planCursorTokens = estimateTokens2(planCursorContent);
|
|
50597
|
-
const knowledgeContent = await readSwarmFileAsync(directory, "knowledge.jsonl");
|
|
50598
|
-
const knowledgeTokens = estimateTokens2(knowledgeContent || "");
|
|
50599
|
-
const runMemoryContent = await readSwarmFileAsync(directory, "run-memory.jsonl");
|
|
50600
|
-
const runMemoryTokens = estimateTokens2(runMemoryContent || "");
|
|
50601
|
-
const handoffContent = await readSwarmFileAsync(directory, "handoff.md");
|
|
50602
|
-
const handoffTokens = estimateTokens2(handoffContent || "");
|
|
50603
|
-
const contextMdContent = await readSwarmFileAsync(directory, "context.md");
|
|
50604
|
-
const contextMdTokens = estimateTokens2(contextMdContent || "");
|
|
50605
|
-
const swarmTotalTokens = systemPromptTokens + planCursorTokens + knowledgeTokens + runMemoryTokens + handoffTokens + contextMdTokens;
|
|
50606
|
-
const estimatedTurnCount = await countEvents(directory);
|
|
50607
|
-
const budgetPct = swarmTotalTokens / config3.budgetTokens * 100;
|
|
50608
|
-
let status;
|
|
50609
|
-
let recommendation = null;
|
|
50610
|
-
if (budgetPct < config3.warningPct) {
|
|
50611
|
-
status = "ok";
|
|
50612
|
-
} else if (budgetPct < config3.criticalPct) {
|
|
50613
|
-
status = "warning";
|
|
50614
|
-
recommendation = "Consider wrapping up current phase and running /swarm handoff before starting new work.";
|
|
50615
|
-
} else {
|
|
50616
|
-
status = "critical";
|
|
50617
|
-
recommendation = "Run /swarm handoff and start a new session to avoid cost escalation.";
|
|
50618
|
-
}
|
|
50619
|
-
const estimatedSessionTokens = swarmTotalTokens * Math.max(1, estimatedTurnCount);
|
|
50620
|
-
return {
|
|
50621
|
-
timestamp,
|
|
50622
|
-
systemPromptTokens,
|
|
50623
|
-
planCursorTokens,
|
|
50624
|
-
knowledgeTokens,
|
|
50625
|
-
runMemoryTokens,
|
|
50626
|
-
handoffTokens,
|
|
50627
|
-
contextMdTokens,
|
|
50628
|
-
swarmTotalTokens,
|
|
50629
|
-
estimatedTurnCount,
|
|
50630
|
-
estimatedSessionTokens,
|
|
50631
|
-
budgetPct,
|
|
50632
|
-
status,
|
|
50633
|
-
recommendation
|
|
50634
|
-
};
|
|
50635
|
-
}
|
|
50636
|
-
async function formatBudgetWarning(report, directory, config3) {
|
|
50637
|
-
validateDirectory(directory);
|
|
50638
|
-
if (report.status === "ok") {
|
|
50639
|
-
return null;
|
|
50640
|
-
}
|
|
50641
|
-
if (!directory || directory.trim() === "") {
|
|
50642
|
-
return formatWarningMessage(report);
|
|
50643
|
-
}
|
|
50644
|
-
const budgetState = await readBudgetState(directory);
|
|
50645
|
-
const state = budgetState || {
|
|
50646
|
-
warningFiredAtTurn: null,
|
|
50647
|
-
criticalFiredAtTurn: null,
|
|
50648
|
-
lastInjectedAtTurn: null
|
|
50649
|
-
};
|
|
50650
|
-
const currentTurn = report.estimatedTurnCount;
|
|
50651
|
-
if (report.status === "warning") {
|
|
50652
|
-
if (config3.warningMode === "once" && state.warningFiredAtTurn !== null) {
|
|
50653
|
-
return null;
|
|
50654
|
-
}
|
|
50655
|
-
if (config3.warningMode === "interval" && state.warningFiredAtTurn !== null && currentTurn - state.warningFiredAtTurn < config3.warningIntervalTurns) {
|
|
50656
|
-
return null;
|
|
50657
|
-
}
|
|
50658
|
-
state.warningFiredAtTurn = currentTurn;
|
|
50659
|
-
state.lastInjectedAtTurn = currentTurn;
|
|
50660
|
-
await writeBudgetState(directory, state);
|
|
50661
|
-
} else if (report.status === "critical") {
|
|
50662
|
-
state.criticalFiredAtTurn = currentTurn;
|
|
50663
|
-
state.lastInjectedAtTurn = currentTurn;
|
|
50664
|
-
}
|
|
50665
|
-
return formatWarningMessage(report);
|
|
50666
|
-
}
|
|
50667
|
-
function formatWarningMessage(report) {
|
|
50668
|
-
const budgetPctStr = report.budgetPct.toFixed(1);
|
|
50669
|
-
const tokensPerTurn = report.swarmTotalTokens.toLocaleString();
|
|
50670
|
-
if (report.status === "warning") {
|
|
50671
|
-
return `[CONTEXT BUDGET: ${budgetPctStr}% \u2014 swarm injecting ~${tokensPerTurn} tokens/turn. Consider wrapping current phase and running /swarm handoff before starting new work.]`;
|
|
50672
|
-
}
|
|
50673
|
-
const costPerTurn = (report.swarmTotalTokens / 1000 * COST_PER_1K_TOKENS).toFixed(3);
|
|
50674
|
-
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.]`;
|
|
50675
|
-
}
|
|
50676
|
-
|
|
50677
|
-
// src/services/index.ts
|
|
50678
50959
|
init_evidence_summary_service();
|
|
50679
50960
|
init_preflight_integration();
|
|
50680
50961
|
init_preflight_service();
|
|
@@ -51261,6 +51542,7 @@ ${handoffBlock}`);
|
|
|
51261
51542
|
const assembledSystemPrompt = output.system.join(`
|
|
51262
51543
|
`);
|
|
51263
51544
|
const budgetReport = await getContextBudgetReport(directory, assembledSystemPrompt, contextBudgetConfig);
|
|
51545
|
+
swarmState.lastBudgetPct = budgetReport.budgetPct;
|
|
51264
51546
|
const budgetWarning = await formatBudgetWarning(budgetReport, directory, contextBudgetConfig);
|
|
51265
51547
|
if (budgetWarning) {
|
|
51266
51548
|
const sessionId_cb = _input.sessionID;
|
|
@@ -51662,6 +51944,7 @@ ${handoffBlock}`;
|
|
|
51662
51944
|
const assembledSystemPrompt_b = output.system.join(`
|
|
51663
51945
|
`);
|
|
51664
51946
|
const budgetReport_b = await getContextBudgetReport(directory, assembledSystemPrompt_b, contextBudgetConfig_b);
|
|
51947
|
+
swarmState.lastBudgetPct = budgetReport_b.budgetPct;
|
|
51665
51948
|
const budgetWarning_b = await formatBudgetWarning(budgetReport_b, directory, contextBudgetConfig_b);
|
|
51666
51949
|
if (budgetWarning_b) {
|
|
51667
51950
|
const sessionId_cb_b = _input.sessionID;
|
|
@@ -52024,10 +52307,92 @@ function createDarkMatterDetectorHook(directory) {
|
|
|
52024
52307
|
return safeHook(hook);
|
|
52025
52308
|
}
|
|
52026
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
|
+
|
|
52027
52392
|
// src/hooks/knowledge-reader.ts
|
|
52028
|
-
import { existsSync as
|
|
52393
|
+
import { existsSync as existsSync19 } from "fs";
|
|
52029
52394
|
import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
52030
|
-
import * as
|
|
52395
|
+
import * as path32 from "path";
|
|
52031
52396
|
var JACCARD_THRESHOLD = 0.6;
|
|
52032
52397
|
var HIVE_TIER_BOOST = 0.05;
|
|
52033
52398
|
var SAME_PROJECT_PENALTY = -0.05;
|
|
@@ -52075,15 +52440,15 @@ function inferCategoriesFromPhase(phaseDescription) {
|
|
|
52075
52440
|
return ["process", "tooling"];
|
|
52076
52441
|
}
|
|
52077
52442
|
async function recordLessonsShown(directory, lessonIds, currentPhase) {
|
|
52078
|
-
const shownFile =
|
|
52443
|
+
const shownFile = path32.join(directory, ".swarm", ".knowledge-shown.json");
|
|
52079
52444
|
try {
|
|
52080
52445
|
let shownData = {};
|
|
52081
|
-
if (
|
|
52446
|
+
if (existsSync19(shownFile)) {
|
|
52082
52447
|
const content = await readFile5(shownFile, "utf-8");
|
|
52083
52448
|
shownData = JSON.parse(content);
|
|
52084
52449
|
}
|
|
52085
52450
|
shownData[currentPhase] = lessonIds;
|
|
52086
|
-
await mkdir4(
|
|
52451
|
+
await mkdir4(path32.dirname(shownFile), { recursive: true });
|
|
52087
52452
|
await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
|
|
52088
52453
|
} catch {
|
|
52089
52454
|
console.warn("[swarm] Knowledge: failed to record shown lessons");
|
|
@@ -52178,9 +52543,9 @@ async function readMergedKnowledge(directory, config3, context) {
|
|
|
52178
52543
|
return topN;
|
|
52179
52544
|
}
|
|
52180
52545
|
async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
|
|
52181
|
-
const shownFile =
|
|
52546
|
+
const shownFile = path32.join(directory, ".swarm", ".knowledge-shown.json");
|
|
52182
52547
|
try {
|
|
52183
|
-
if (!
|
|
52548
|
+
if (!existsSync19(shownFile)) {
|
|
52184
52549
|
return;
|
|
52185
52550
|
}
|
|
52186
52551
|
const content = await readFile5(shownFile, "utf-8");
|
|
@@ -52650,12 +53015,12 @@ Use this data to avoid repeating known failure patterns.`;
|
|
|
52650
53015
|
// src/hooks/curator-drift.ts
|
|
52651
53016
|
init_event_bus();
|
|
52652
53017
|
init_utils2();
|
|
52653
|
-
import * as
|
|
52654
|
-
import * as
|
|
53018
|
+
import * as fs20 from "fs";
|
|
53019
|
+
import * as path33 from "path";
|
|
52655
53020
|
var DRIFT_REPORT_PREFIX = "drift-report-phase-";
|
|
52656
53021
|
async function readPriorDriftReports(directory) {
|
|
52657
|
-
const swarmDir =
|
|
52658
|
-
const entries = await
|
|
53022
|
+
const swarmDir = path33.join(directory, ".swarm");
|
|
53023
|
+
const entries = await fs20.promises.readdir(swarmDir).catch(() => null);
|
|
52659
53024
|
if (entries === null)
|
|
52660
53025
|
return [];
|
|
52661
53026
|
const reportFiles = entries.filter((name2) => name2.startsWith(DRIFT_REPORT_PREFIX) && name2.endsWith(".json")).sort();
|
|
@@ -52681,10 +53046,10 @@ async function readPriorDriftReports(directory) {
|
|
|
52681
53046
|
async function writeDriftReport(directory, report) {
|
|
52682
53047
|
const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
|
|
52683
53048
|
const filePath = validateSwarmPath(directory, filename);
|
|
52684
|
-
const swarmDir =
|
|
52685
|
-
await
|
|
53049
|
+
const swarmDir = path33.dirname(filePath);
|
|
53050
|
+
await fs20.promises.mkdir(swarmDir, { recursive: true });
|
|
52686
53051
|
try {
|
|
52687
|
-
await
|
|
53052
|
+
await fs20.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
|
|
52688
53053
|
} catch (err2) {
|
|
52689
53054
|
throw new Error(`[curator-drift] Failed to write drift report to ${filePath}: ${String(err2)}`);
|
|
52690
53055
|
}
|
|
@@ -52934,9 +53299,143 @@ ${cachedInjectionText}`;
|
|
|
52934
53299
|
});
|
|
52935
53300
|
}
|
|
52936
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
|
+
|
|
52937
53436
|
// src/hooks/steering-consumed.ts
|
|
52938
53437
|
init_utils2();
|
|
52939
|
-
import * as
|
|
53438
|
+
import * as fs21 from "fs";
|
|
52940
53439
|
function recordSteeringConsumed(directory, directiveId) {
|
|
52941
53440
|
try {
|
|
52942
53441
|
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
@@ -52945,7 +53444,7 @@ function recordSteeringConsumed(directory, directiveId) {
|
|
|
52945
53444
|
directiveId,
|
|
52946
53445
|
timestamp: new Date().toISOString()
|
|
52947
53446
|
};
|
|
52948
|
-
|
|
53447
|
+
fs21.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
52949
53448
|
`, "utf-8");
|
|
52950
53449
|
} catch {}
|
|
52951
53450
|
}
|
|
@@ -52985,12 +53484,93 @@ function createSteeringConsumedHook(directory) {
|
|
|
52985
53484
|
return safeHook(hook);
|
|
52986
53485
|
}
|
|
52987
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
|
+
|
|
52988
53568
|
// src/index.ts
|
|
52989
53569
|
init_config_doctor();
|
|
52990
53570
|
|
|
52991
53571
|
// src/session/snapshot-reader.ts
|
|
52992
53572
|
init_utils2();
|
|
52993
|
-
import
|
|
53573
|
+
import path35 from "path";
|
|
52994
53574
|
var VALID_TASK_WORKFLOW_STATES = [
|
|
52995
53575
|
"idle",
|
|
52996
53576
|
"coder_delegated",
|
|
@@ -53115,7 +53695,7 @@ function rehydrateState(snapshot) {
|
|
|
53115
53695
|
async function reconcileTaskStatesFromPlan(directory) {
|
|
53116
53696
|
let raw;
|
|
53117
53697
|
try {
|
|
53118
|
-
raw = await Bun.file(
|
|
53698
|
+
raw = await Bun.file(path35.join(directory, ".swarm/plan.json")).text();
|
|
53119
53699
|
} catch {
|
|
53120
53700
|
return;
|
|
53121
53701
|
}
|
|
@@ -53337,8 +53917,8 @@ var build_check = createSwarmTool({
|
|
|
53337
53917
|
// src/tools/check-gate-status.ts
|
|
53338
53918
|
init_dist();
|
|
53339
53919
|
init_create_tool();
|
|
53340
|
-
import * as
|
|
53341
|
-
import * as
|
|
53920
|
+
import * as fs23 from "fs";
|
|
53921
|
+
import * as path36 from "path";
|
|
53342
53922
|
var EVIDENCE_DIR = ".swarm/evidence";
|
|
53343
53923
|
var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
|
|
53344
53924
|
function isValidTaskId3(taskId) {
|
|
@@ -53355,18 +53935,18 @@ function isValidTaskId3(taskId) {
|
|
|
53355
53935
|
return TASK_ID_PATTERN2.test(taskId);
|
|
53356
53936
|
}
|
|
53357
53937
|
function isPathWithinSwarm(filePath, workspaceRoot) {
|
|
53358
|
-
const normalizedWorkspace =
|
|
53359
|
-
const swarmPath =
|
|
53360
|
-
const normalizedPath =
|
|
53938
|
+
const normalizedWorkspace = path36.resolve(workspaceRoot);
|
|
53939
|
+
const swarmPath = path36.join(normalizedWorkspace, ".swarm", "evidence");
|
|
53940
|
+
const normalizedPath = path36.resolve(filePath);
|
|
53361
53941
|
return normalizedPath.startsWith(swarmPath);
|
|
53362
53942
|
}
|
|
53363
53943
|
function readEvidenceFile(evidencePath) {
|
|
53364
|
-
if (!
|
|
53944
|
+
if (!fs23.existsSync(evidencePath)) {
|
|
53365
53945
|
return null;
|
|
53366
53946
|
}
|
|
53367
53947
|
let content;
|
|
53368
53948
|
try {
|
|
53369
|
-
content =
|
|
53949
|
+
content = fs23.readFileSync(evidencePath, "utf-8");
|
|
53370
53950
|
} catch {
|
|
53371
53951
|
return null;
|
|
53372
53952
|
}
|
|
@@ -53418,7 +53998,7 @@ var check_gate_status = createSwarmTool({
|
|
|
53418
53998
|
};
|
|
53419
53999
|
return JSON.stringify(errorResult, null, 2);
|
|
53420
54000
|
}
|
|
53421
|
-
const evidencePath =
|
|
54001
|
+
const evidencePath = path36.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
|
|
53422
54002
|
if (!isPathWithinSwarm(evidencePath, directory)) {
|
|
53423
54003
|
const errorResult = {
|
|
53424
54004
|
taskId: taskIdInput,
|
|
@@ -53478,8 +54058,8 @@ var check_gate_status = createSwarmTool({
|
|
|
53478
54058
|
init_tool();
|
|
53479
54059
|
init_create_tool();
|
|
53480
54060
|
import { spawnSync } from "child_process";
|
|
53481
|
-
import * as
|
|
53482
|
-
import * as
|
|
54061
|
+
import * as fs24 from "fs";
|
|
54062
|
+
import * as path37 from "path";
|
|
53483
54063
|
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
|
|
53484
54064
|
var MAX_LABEL_LENGTH = 100;
|
|
53485
54065
|
var GIT_TIMEOUT_MS = 30000;
|
|
@@ -53530,13 +54110,13 @@ function validateLabel(label) {
|
|
|
53530
54110
|
return null;
|
|
53531
54111
|
}
|
|
53532
54112
|
function getCheckpointLogPath(directory) {
|
|
53533
|
-
return
|
|
54113
|
+
return path37.join(directory, CHECKPOINT_LOG_PATH);
|
|
53534
54114
|
}
|
|
53535
54115
|
function readCheckpointLog(directory) {
|
|
53536
54116
|
const logPath = getCheckpointLogPath(directory);
|
|
53537
54117
|
try {
|
|
53538
|
-
if (
|
|
53539
|
-
const content =
|
|
54118
|
+
if (fs24.existsSync(logPath)) {
|
|
54119
|
+
const content = fs24.readFileSync(logPath, "utf-8");
|
|
53540
54120
|
const parsed = JSON.parse(content);
|
|
53541
54121
|
if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
|
|
53542
54122
|
return { version: 1, checkpoints: [] };
|
|
@@ -53548,13 +54128,13 @@ function readCheckpointLog(directory) {
|
|
|
53548
54128
|
}
|
|
53549
54129
|
function writeCheckpointLog(log2, directory) {
|
|
53550
54130
|
const logPath = getCheckpointLogPath(directory);
|
|
53551
|
-
const dir =
|
|
53552
|
-
if (!
|
|
53553
|
-
|
|
54131
|
+
const dir = path37.dirname(logPath);
|
|
54132
|
+
if (!fs24.existsSync(dir)) {
|
|
54133
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
53554
54134
|
}
|
|
53555
54135
|
const tempPath = `${logPath}.tmp`;
|
|
53556
|
-
|
|
53557
|
-
|
|
54136
|
+
fs24.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
|
|
54137
|
+
fs24.renameSync(tempPath, logPath);
|
|
53558
54138
|
}
|
|
53559
54139
|
function gitExec(args2) {
|
|
53560
54140
|
const result = spawnSync("git", args2, {
|
|
@@ -53755,8 +54335,8 @@ var checkpoint = createSwarmTool({
|
|
|
53755
54335
|
// src/tools/complexity-hotspots.ts
|
|
53756
54336
|
init_dist();
|
|
53757
54337
|
init_create_tool();
|
|
53758
|
-
import * as
|
|
53759
|
-
import * as
|
|
54338
|
+
import * as fs25 from "fs";
|
|
54339
|
+
import * as path38 from "path";
|
|
53760
54340
|
var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
|
|
53761
54341
|
var DEFAULT_DAYS = 90;
|
|
53762
54342
|
var DEFAULT_TOP_N = 20;
|
|
@@ -53885,11 +54465,11 @@ function estimateComplexity(content) {
|
|
|
53885
54465
|
}
|
|
53886
54466
|
function getComplexityForFile(filePath) {
|
|
53887
54467
|
try {
|
|
53888
|
-
const stat2 =
|
|
54468
|
+
const stat2 = fs25.statSync(filePath);
|
|
53889
54469
|
if (stat2.size > MAX_FILE_SIZE_BYTES2) {
|
|
53890
54470
|
return null;
|
|
53891
54471
|
}
|
|
53892
|
-
const content =
|
|
54472
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
53893
54473
|
return estimateComplexity(content);
|
|
53894
54474
|
} catch {
|
|
53895
54475
|
return null;
|
|
@@ -53900,7 +54480,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
|
|
|
53900
54480
|
const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
|
|
53901
54481
|
const filteredChurn = new Map;
|
|
53902
54482
|
for (const [file3, count] of churnMap) {
|
|
53903
|
-
const ext =
|
|
54483
|
+
const ext = path38.extname(file3).toLowerCase();
|
|
53904
54484
|
if (extSet.has(ext)) {
|
|
53905
54485
|
filteredChurn.set(file3, count);
|
|
53906
54486
|
}
|
|
@@ -53910,8 +54490,8 @@ async function analyzeHotspots(days, topN, extensions, directory) {
|
|
|
53910
54490
|
let analyzedFiles = 0;
|
|
53911
54491
|
for (const [file3, churnCount] of filteredChurn) {
|
|
53912
54492
|
let fullPath = file3;
|
|
53913
|
-
if (!
|
|
53914
|
-
fullPath =
|
|
54493
|
+
if (!fs25.existsSync(fullPath)) {
|
|
54494
|
+
fullPath = path38.join(cwd, file3);
|
|
53915
54495
|
}
|
|
53916
54496
|
const complexity = getComplexityForFile(fullPath);
|
|
53917
54497
|
if (complexity !== null) {
|
|
@@ -54058,8 +54638,8 @@ var complexity_hotspots = createSwarmTool({
|
|
|
54058
54638
|
});
|
|
54059
54639
|
// src/tools/declare-scope.ts
|
|
54060
54640
|
init_tool();
|
|
54061
|
-
import * as
|
|
54062
|
-
import * as
|
|
54641
|
+
import * as fs26 from "fs";
|
|
54642
|
+
import * as path39 from "path";
|
|
54063
54643
|
init_create_tool();
|
|
54064
54644
|
function validateTaskIdFormat(taskId) {
|
|
54065
54645
|
const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
|
|
@@ -54138,8 +54718,8 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54138
54718
|
};
|
|
54139
54719
|
}
|
|
54140
54720
|
}
|
|
54141
|
-
normalizedDir =
|
|
54142
|
-
const pathParts = normalizedDir.split(
|
|
54721
|
+
normalizedDir = path39.normalize(args2.working_directory);
|
|
54722
|
+
const pathParts = normalizedDir.split(path39.sep);
|
|
54143
54723
|
if (pathParts.includes("..")) {
|
|
54144
54724
|
return {
|
|
54145
54725
|
success: false,
|
|
@@ -54149,11 +54729,11 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54149
54729
|
]
|
|
54150
54730
|
};
|
|
54151
54731
|
}
|
|
54152
|
-
const resolvedDir =
|
|
54732
|
+
const resolvedDir = path39.resolve(normalizedDir);
|
|
54153
54733
|
try {
|
|
54154
|
-
const realPath =
|
|
54155
|
-
const planPath2 =
|
|
54156
|
-
if (!
|
|
54734
|
+
const realPath = fs26.realpathSync(resolvedDir);
|
|
54735
|
+
const planPath2 = path39.join(realPath, ".swarm", "plan.json");
|
|
54736
|
+
if (!fs26.existsSync(planPath2)) {
|
|
54157
54737
|
return {
|
|
54158
54738
|
success: false,
|
|
54159
54739
|
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
@@ -54173,8 +54753,8 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54173
54753
|
}
|
|
54174
54754
|
}
|
|
54175
54755
|
const directory = normalizedDir ?? fallbackDir ?? process.cwd();
|
|
54176
|
-
const planPath =
|
|
54177
|
-
if (!
|
|
54756
|
+
const planPath = path39.resolve(directory, ".swarm", "plan.json");
|
|
54757
|
+
if (!fs26.existsSync(planPath)) {
|
|
54178
54758
|
return {
|
|
54179
54759
|
success: false,
|
|
54180
54760
|
message: "No plan found",
|
|
@@ -54183,7 +54763,7 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54183
54763
|
}
|
|
54184
54764
|
let planContent;
|
|
54185
54765
|
try {
|
|
54186
|
-
planContent = JSON.parse(
|
|
54766
|
+
planContent = JSON.parse(fs26.readFileSync(planPath, "utf-8"));
|
|
54187
54767
|
} catch {
|
|
54188
54768
|
return {
|
|
54189
54769
|
success: false,
|
|
@@ -54263,20 +54843,20 @@ function validateBase(base) {
|
|
|
54263
54843
|
function validatePaths(paths) {
|
|
54264
54844
|
if (!paths)
|
|
54265
54845
|
return null;
|
|
54266
|
-
for (const
|
|
54267
|
-
if (!
|
|
54846
|
+
for (const path40 of paths) {
|
|
54847
|
+
if (!path40 || path40.length === 0) {
|
|
54268
54848
|
return "empty path not allowed";
|
|
54269
54849
|
}
|
|
54270
|
-
if (
|
|
54850
|
+
if (path40.length > MAX_PATH_LENGTH) {
|
|
54271
54851
|
return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
|
|
54272
54852
|
}
|
|
54273
|
-
if (SHELL_METACHARACTERS2.test(
|
|
54853
|
+
if (SHELL_METACHARACTERS2.test(path40)) {
|
|
54274
54854
|
return "path contains shell metacharacters";
|
|
54275
54855
|
}
|
|
54276
|
-
if (
|
|
54856
|
+
if (path40.startsWith("-")) {
|
|
54277
54857
|
return 'path cannot start with "-" (option-like arguments not allowed)';
|
|
54278
54858
|
}
|
|
54279
|
-
if (CONTROL_CHAR_PATTERN2.test(
|
|
54859
|
+
if (CONTROL_CHAR_PATTERN2.test(path40)) {
|
|
54280
54860
|
return "path contains control characters";
|
|
54281
54861
|
}
|
|
54282
54862
|
}
|
|
@@ -54356,8 +54936,8 @@ var diff = tool({
|
|
|
54356
54936
|
if (parts2.length >= 3) {
|
|
54357
54937
|
const additions = parseInt(parts2[0], 10) || 0;
|
|
54358
54938
|
const deletions = parseInt(parts2[1], 10) || 0;
|
|
54359
|
-
const
|
|
54360
|
-
files.push({ path:
|
|
54939
|
+
const path40 = parts2[2];
|
|
54940
|
+
files.push({ path: path40, additions, deletions });
|
|
54361
54941
|
}
|
|
54362
54942
|
}
|
|
54363
54943
|
const contractChanges = [];
|
|
@@ -54586,8 +55166,8 @@ Use these as DOMAIN values when delegating to @sme.`;
|
|
|
54586
55166
|
// src/tools/evidence-check.ts
|
|
54587
55167
|
init_dist();
|
|
54588
55168
|
init_create_tool();
|
|
54589
|
-
import * as
|
|
54590
|
-
import * as
|
|
55169
|
+
import * as fs27 from "fs";
|
|
55170
|
+
import * as path40 from "path";
|
|
54591
55171
|
var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
|
|
54592
55172
|
var MAX_EVIDENCE_FILES = 1000;
|
|
54593
55173
|
var EVIDENCE_DIR2 = ".swarm/evidence";
|
|
@@ -54617,9 +55197,9 @@ function validateRequiredTypes(input) {
|
|
|
54617
55197
|
return null;
|
|
54618
55198
|
}
|
|
54619
55199
|
function isPathWithinSwarm2(filePath, cwd) {
|
|
54620
|
-
const normalizedCwd =
|
|
54621
|
-
const swarmPath =
|
|
54622
|
-
const normalizedPath =
|
|
55200
|
+
const normalizedCwd = path40.resolve(cwd);
|
|
55201
|
+
const swarmPath = path40.join(normalizedCwd, ".swarm");
|
|
55202
|
+
const normalizedPath = path40.resolve(filePath);
|
|
54623
55203
|
return normalizedPath.startsWith(swarmPath);
|
|
54624
55204
|
}
|
|
54625
55205
|
function parseCompletedTasks(planContent) {
|
|
@@ -54635,12 +55215,12 @@ function parseCompletedTasks(planContent) {
|
|
|
54635
55215
|
}
|
|
54636
55216
|
function readEvidenceFiles(evidenceDir, _cwd) {
|
|
54637
55217
|
const evidence = [];
|
|
54638
|
-
if (!
|
|
55218
|
+
if (!fs27.existsSync(evidenceDir) || !fs27.statSync(evidenceDir).isDirectory()) {
|
|
54639
55219
|
return evidence;
|
|
54640
55220
|
}
|
|
54641
55221
|
let files;
|
|
54642
55222
|
try {
|
|
54643
|
-
files =
|
|
55223
|
+
files = fs27.readdirSync(evidenceDir);
|
|
54644
55224
|
} catch {
|
|
54645
55225
|
return evidence;
|
|
54646
55226
|
}
|
|
@@ -54649,14 +55229,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
54649
55229
|
if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
|
|
54650
55230
|
continue;
|
|
54651
55231
|
}
|
|
54652
|
-
const filePath =
|
|
55232
|
+
const filePath = path40.join(evidenceDir, filename);
|
|
54653
55233
|
try {
|
|
54654
|
-
const resolvedPath =
|
|
54655
|
-
const evidenceDirResolved =
|
|
55234
|
+
const resolvedPath = path40.resolve(filePath);
|
|
55235
|
+
const evidenceDirResolved = path40.resolve(evidenceDir);
|
|
54656
55236
|
if (!resolvedPath.startsWith(evidenceDirResolved)) {
|
|
54657
55237
|
continue;
|
|
54658
55238
|
}
|
|
54659
|
-
const stat2 =
|
|
55239
|
+
const stat2 = fs27.lstatSync(filePath);
|
|
54660
55240
|
if (!stat2.isFile()) {
|
|
54661
55241
|
continue;
|
|
54662
55242
|
}
|
|
@@ -54665,7 +55245,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
54665
55245
|
}
|
|
54666
55246
|
let fileStat;
|
|
54667
55247
|
try {
|
|
54668
|
-
fileStat =
|
|
55248
|
+
fileStat = fs27.statSync(filePath);
|
|
54669
55249
|
if (fileStat.size > MAX_FILE_SIZE_BYTES3) {
|
|
54670
55250
|
continue;
|
|
54671
55251
|
}
|
|
@@ -54674,7 +55254,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
54674
55254
|
}
|
|
54675
55255
|
let content;
|
|
54676
55256
|
try {
|
|
54677
|
-
content =
|
|
55257
|
+
content = fs27.readFileSync(filePath, "utf-8");
|
|
54678
55258
|
} catch {
|
|
54679
55259
|
continue;
|
|
54680
55260
|
}
|
|
@@ -54770,7 +55350,7 @@ var evidence_check = createSwarmTool({
|
|
|
54770
55350
|
return JSON.stringify(errorResult, null, 2);
|
|
54771
55351
|
}
|
|
54772
55352
|
const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
|
|
54773
|
-
const planPath =
|
|
55353
|
+
const planPath = path40.join(cwd, PLAN_FILE);
|
|
54774
55354
|
if (!isPathWithinSwarm2(planPath, cwd)) {
|
|
54775
55355
|
const errorResult = {
|
|
54776
55356
|
error: "plan file path validation failed",
|
|
@@ -54784,7 +55364,7 @@ var evidence_check = createSwarmTool({
|
|
|
54784
55364
|
}
|
|
54785
55365
|
let planContent;
|
|
54786
55366
|
try {
|
|
54787
|
-
planContent =
|
|
55367
|
+
planContent = fs27.readFileSync(planPath, "utf-8");
|
|
54788
55368
|
} catch {
|
|
54789
55369
|
const result2 = {
|
|
54790
55370
|
message: "No completed tasks found in plan.",
|
|
@@ -54802,7 +55382,7 @@ var evidence_check = createSwarmTool({
|
|
|
54802
55382
|
};
|
|
54803
55383
|
return JSON.stringify(result2, null, 2);
|
|
54804
55384
|
}
|
|
54805
|
-
const evidenceDir =
|
|
55385
|
+
const evidenceDir = path40.join(cwd, EVIDENCE_DIR2);
|
|
54806
55386
|
const evidence = readEvidenceFiles(evidenceDir, cwd);
|
|
54807
55387
|
const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
|
|
54808
55388
|
const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
|
|
@@ -54819,8 +55399,8 @@ var evidence_check = createSwarmTool({
|
|
|
54819
55399
|
// src/tools/file-extractor.ts
|
|
54820
55400
|
init_tool();
|
|
54821
55401
|
init_create_tool();
|
|
54822
|
-
import * as
|
|
54823
|
-
import * as
|
|
55402
|
+
import * as fs28 from "fs";
|
|
55403
|
+
import * as path41 from "path";
|
|
54824
55404
|
var EXT_MAP = {
|
|
54825
55405
|
python: ".py",
|
|
54826
55406
|
py: ".py",
|
|
@@ -54882,8 +55462,8 @@ var extract_code_blocks = createSwarmTool({
|
|
|
54882
55462
|
execute: async (args2, directory) => {
|
|
54883
55463
|
const { content, output_dir, prefix } = args2;
|
|
54884
55464
|
const targetDir = output_dir || directory;
|
|
54885
|
-
if (!
|
|
54886
|
-
|
|
55465
|
+
if (!fs28.existsSync(targetDir)) {
|
|
55466
|
+
fs28.mkdirSync(targetDir, { recursive: true });
|
|
54887
55467
|
}
|
|
54888
55468
|
if (!content) {
|
|
54889
55469
|
return "Error: content is required";
|
|
@@ -54901,16 +55481,16 @@ var extract_code_blocks = createSwarmTool({
|
|
|
54901
55481
|
if (prefix) {
|
|
54902
55482
|
filename = `${prefix}_${filename}`;
|
|
54903
55483
|
}
|
|
54904
|
-
let filepath =
|
|
54905
|
-
const base =
|
|
54906
|
-
const ext =
|
|
55484
|
+
let filepath = path41.join(targetDir, filename);
|
|
55485
|
+
const base = path41.basename(filepath, path41.extname(filepath));
|
|
55486
|
+
const ext = path41.extname(filepath);
|
|
54907
55487
|
let counter = 1;
|
|
54908
|
-
while (
|
|
54909
|
-
filepath =
|
|
55488
|
+
while (fs28.existsSync(filepath)) {
|
|
55489
|
+
filepath = path41.join(targetDir, `${base}_${counter}${ext}`);
|
|
54910
55490
|
counter++;
|
|
54911
55491
|
}
|
|
54912
55492
|
try {
|
|
54913
|
-
|
|
55493
|
+
fs28.writeFileSync(filepath, code.trim(), "utf-8");
|
|
54914
55494
|
savedFiles.push(filepath);
|
|
54915
55495
|
} catch (error93) {
|
|
54916
55496
|
errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
|
@@ -55023,8 +55603,8 @@ var gitingest = tool({
|
|
|
55023
55603
|
});
|
|
55024
55604
|
// src/tools/imports.ts
|
|
55025
55605
|
init_dist();
|
|
55026
|
-
import * as
|
|
55027
|
-
import * as
|
|
55606
|
+
import * as fs29 from "fs";
|
|
55607
|
+
import * as path42 from "path";
|
|
55028
55608
|
var MAX_FILE_PATH_LENGTH2 = 500;
|
|
55029
55609
|
var MAX_SYMBOL_LENGTH = 256;
|
|
55030
55610
|
var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
|
|
@@ -55078,7 +55658,7 @@ function validateSymbolInput(symbol3) {
|
|
|
55078
55658
|
return null;
|
|
55079
55659
|
}
|
|
55080
55660
|
function isBinaryFile2(filePath, buffer) {
|
|
55081
|
-
const ext =
|
|
55661
|
+
const ext = path42.extname(filePath).toLowerCase();
|
|
55082
55662
|
if (ext === ".json" || ext === ".md" || ext === ".txt") {
|
|
55083
55663
|
return false;
|
|
55084
55664
|
}
|
|
@@ -55102,15 +55682,15 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
55102
55682
|
const imports = [];
|
|
55103
55683
|
let _resolvedTarget;
|
|
55104
55684
|
try {
|
|
55105
|
-
_resolvedTarget =
|
|
55685
|
+
_resolvedTarget = path42.resolve(targetFile);
|
|
55106
55686
|
} catch {
|
|
55107
55687
|
_resolvedTarget = targetFile;
|
|
55108
55688
|
}
|
|
55109
|
-
const targetBasename =
|
|
55689
|
+
const targetBasename = path42.basename(targetFile, path42.extname(targetFile));
|
|
55110
55690
|
const targetWithExt = targetFile;
|
|
55111
55691
|
const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
55112
|
-
const normalizedTargetWithExt =
|
|
55113
|
-
const normalizedTargetWithoutExt =
|
|
55692
|
+
const normalizedTargetWithExt = path42.normalize(targetWithExt).replace(/\\/g, "/");
|
|
55693
|
+
const normalizedTargetWithoutExt = path42.normalize(targetWithoutExt).replace(/\\/g, "/");
|
|
55114
55694
|
const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
55115
55695
|
for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
|
|
55116
55696
|
const modulePath = match[1] || match[2] || match[3];
|
|
@@ -55133,9 +55713,9 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
55133
55713
|
}
|
|
55134
55714
|
const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
|
|
55135
55715
|
let isMatch = false;
|
|
55136
|
-
const _targetDir =
|
|
55137
|
-
const targetExt =
|
|
55138
|
-
const targetBasenameNoExt =
|
|
55716
|
+
const _targetDir = path42.dirname(targetFile);
|
|
55717
|
+
const targetExt = path42.extname(targetFile);
|
|
55718
|
+
const targetBasenameNoExt = path42.basename(targetFile, targetExt);
|
|
55139
55719
|
const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
55140
55720
|
const moduleName = modulePath.split(/[/\\]/).pop() || "";
|
|
55141
55721
|
const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
@@ -55192,7 +55772,7 @@ var SKIP_DIRECTORIES2 = new Set([
|
|
|
55192
55772
|
function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
|
|
55193
55773
|
let entries;
|
|
55194
55774
|
try {
|
|
55195
|
-
entries =
|
|
55775
|
+
entries = fs29.readdirSync(dir);
|
|
55196
55776
|
} catch (e) {
|
|
55197
55777
|
stats.fileErrors.push({
|
|
55198
55778
|
path: dir,
|
|
@@ -55203,13 +55783,13 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
|
|
|
55203
55783
|
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
55204
55784
|
for (const entry of entries) {
|
|
55205
55785
|
if (SKIP_DIRECTORIES2.has(entry)) {
|
|
55206
|
-
stats.skippedDirs.push(
|
|
55786
|
+
stats.skippedDirs.push(path42.join(dir, entry));
|
|
55207
55787
|
continue;
|
|
55208
55788
|
}
|
|
55209
|
-
const fullPath =
|
|
55789
|
+
const fullPath = path42.join(dir, entry);
|
|
55210
55790
|
let stat2;
|
|
55211
55791
|
try {
|
|
55212
|
-
stat2 =
|
|
55792
|
+
stat2 = fs29.statSync(fullPath);
|
|
55213
55793
|
} catch (e) {
|
|
55214
55794
|
stats.fileErrors.push({
|
|
55215
55795
|
path: fullPath,
|
|
@@ -55220,7 +55800,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
|
|
|
55220
55800
|
if (stat2.isDirectory()) {
|
|
55221
55801
|
findSourceFiles(fullPath, files, stats);
|
|
55222
55802
|
} else if (stat2.isFile()) {
|
|
55223
|
-
const ext =
|
|
55803
|
+
const ext = path42.extname(fullPath).toLowerCase();
|
|
55224
55804
|
if (SUPPORTED_EXTENSIONS.includes(ext)) {
|
|
55225
55805
|
files.push(fullPath);
|
|
55226
55806
|
}
|
|
@@ -55276,8 +55856,8 @@ var imports = tool({
|
|
|
55276
55856
|
return JSON.stringify(errorResult, null, 2);
|
|
55277
55857
|
}
|
|
55278
55858
|
try {
|
|
55279
|
-
const targetFile =
|
|
55280
|
-
if (!
|
|
55859
|
+
const targetFile = path42.resolve(file3);
|
|
55860
|
+
if (!fs29.existsSync(targetFile)) {
|
|
55281
55861
|
const errorResult = {
|
|
55282
55862
|
error: `target file not found: ${file3}`,
|
|
55283
55863
|
target: file3,
|
|
@@ -55287,7 +55867,7 @@ var imports = tool({
|
|
|
55287
55867
|
};
|
|
55288
55868
|
return JSON.stringify(errorResult, null, 2);
|
|
55289
55869
|
}
|
|
55290
|
-
const targetStat =
|
|
55870
|
+
const targetStat = fs29.statSync(targetFile);
|
|
55291
55871
|
if (!targetStat.isFile()) {
|
|
55292
55872
|
const errorResult = {
|
|
55293
55873
|
error: "target must be a file, not a directory",
|
|
@@ -55298,7 +55878,7 @@ var imports = tool({
|
|
|
55298
55878
|
};
|
|
55299
55879
|
return JSON.stringify(errorResult, null, 2);
|
|
55300
55880
|
}
|
|
55301
|
-
const baseDir =
|
|
55881
|
+
const baseDir = path42.dirname(targetFile);
|
|
55302
55882
|
const scanStats = {
|
|
55303
55883
|
skippedDirs: [],
|
|
55304
55884
|
skippedFiles: 0,
|
|
@@ -55313,12 +55893,12 @@ var imports = tool({
|
|
|
55313
55893
|
if (consumers.length >= MAX_CONSUMERS)
|
|
55314
55894
|
break;
|
|
55315
55895
|
try {
|
|
55316
|
-
const stat2 =
|
|
55896
|
+
const stat2 = fs29.statSync(filePath);
|
|
55317
55897
|
if (stat2.size > MAX_FILE_SIZE_BYTES4) {
|
|
55318
55898
|
skippedFileCount++;
|
|
55319
55899
|
continue;
|
|
55320
55900
|
}
|
|
55321
|
-
const buffer =
|
|
55901
|
+
const buffer = fs29.readFileSync(filePath);
|
|
55322
55902
|
if (isBinaryFile2(filePath, buffer)) {
|
|
55323
55903
|
skippedFileCount++;
|
|
55324
55904
|
continue;
|
|
@@ -55383,7 +55963,7 @@ var imports = tool({
|
|
|
55383
55963
|
});
|
|
55384
55964
|
// src/tools/knowledge-query.ts
|
|
55385
55965
|
init_dist();
|
|
55386
|
-
import { existsSync as
|
|
55966
|
+
import { existsSync as existsSync27 } from "fs";
|
|
55387
55967
|
init_create_tool();
|
|
55388
55968
|
var DEFAULT_LIMIT = 10;
|
|
55389
55969
|
var MAX_LESSON_LENGTH = 200;
|
|
@@ -55453,14 +56033,14 @@ function validateLimit(limit) {
|
|
|
55453
56033
|
}
|
|
55454
56034
|
async function readSwarmKnowledge(directory) {
|
|
55455
56035
|
const swarmPath = resolveSwarmKnowledgePath(directory);
|
|
55456
|
-
if (!
|
|
56036
|
+
if (!existsSync27(swarmPath)) {
|
|
55457
56037
|
return [];
|
|
55458
56038
|
}
|
|
55459
56039
|
return readKnowledge(swarmPath);
|
|
55460
56040
|
}
|
|
55461
56041
|
async function readHiveKnowledge() {
|
|
55462
56042
|
const hivePath = resolveHiveKnowledgePath();
|
|
55463
|
-
if (!
|
|
56043
|
+
if (!existsSync27(hivePath)) {
|
|
55464
56044
|
return [];
|
|
55465
56045
|
}
|
|
55466
56046
|
return readKnowledge(hivePath);
|
|
@@ -55619,8 +56199,8 @@ init_dist();
|
|
|
55619
56199
|
init_config();
|
|
55620
56200
|
init_schema();
|
|
55621
56201
|
init_manager();
|
|
55622
|
-
import * as
|
|
55623
|
-
import * as
|
|
56202
|
+
import * as fs30 from "fs";
|
|
56203
|
+
import * as path43 from "path";
|
|
55624
56204
|
init_utils2();
|
|
55625
56205
|
init_create_tool();
|
|
55626
56206
|
function safeWarn(message, error93) {
|
|
@@ -55815,7 +56395,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55815
56395
|
}
|
|
55816
56396
|
if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
|
|
55817
56397
|
try {
|
|
55818
|
-
const projectName =
|
|
56398
|
+
const projectName = path43.basename(dir);
|
|
55819
56399
|
const knowledgeConfig = {
|
|
55820
56400
|
enabled: true,
|
|
55821
56401
|
swarm_max_entries: 100,
|
|
@@ -55863,7 +56443,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55863
56443
|
if (agentsMissing.length > 0) {
|
|
55864
56444
|
try {
|
|
55865
56445
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
55866
|
-
const planRaw =
|
|
56446
|
+
const planRaw = fs30.readFileSync(planPath, "utf-8");
|
|
55867
56447
|
const plan = JSON.parse(planRaw);
|
|
55868
56448
|
const targetPhase = plan.phases.find((p) => p.id === phase);
|
|
55869
56449
|
if (targetPhase && targetPhase.tasks.length > 0 && targetPhase.tasks.every((t) => t.status === "completed")) {
|
|
@@ -55904,7 +56484,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55904
56484
|
};
|
|
55905
56485
|
try {
|
|
55906
56486
|
const eventsPath = validateSwarmPath(dir, "events.jsonl");
|
|
55907
|
-
|
|
56487
|
+
fs30.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
55908
56488
|
`, "utf-8");
|
|
55909
56489
|
} catch (writeError) {
|
|
55910
56490
|
warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
@@ -55923,12 +56503,12 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55923
56503
|
}
|
|
55924
56504
|
try {
|
|
55925
56505
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
55926
|
-
const planJson =
|
|
56506
|
+
const planJson = fs30.readFileSync(planPath, "utf-8");
|
|
55927
56507
|
const plan = JSON.parse(planJson);
|
|
55928
56508
|
const phaseObj = plan.phases.find((p) => p.id === phase);
|
|
55929
56509
|
if (phaseObj) {
|
|
55930
56510
|
phaseObj.status = "completed";
|
|
55931
|
-
|
|
56511
|
+
fs30.writeFileSync(planPath, `${JSON.stringify(plan, null, 2)}
|
|
55932
56512
|
`, "utf-8");
|
|
55933
56513
|
}
|
|
55934
56514
|
} catch (error93) {
|
|
@@ -55978,8 +56558,8 @@ init_dist();
|
|
|
55978
56558
|
init_discovery();
|
|
55979
56559
|
init_utils();
|
|
55980
56560
|
init_create_tool();
|
|
55981
|
-
import * as
|
|
55982
|
-
import * as
|
|
56561
|
+
import * as fs31 from "fs";
|
|
56562
|
+
import * as path44 from "path";
|
|
55983
56563
|
var MAX_OUTPUT_BYTES5 = 52428800;
|
|
55984
56564
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
55985
56565
|
function isValidEcosystem(value) {
|
|
@@ -55997,28 +56577,28 @@ function validateArgs3(args2) {
|
|
|
55997
56577
|
function detectEcosystems(directory) {
|
|
55998
56578
|
const ecosystems = [];
|
|
55999
56579
|
const cwd = directory;
|
|
56000
|
-
if (
|
|
56580
|
+
if (fs31.existsSync(path44.join(cwd, "package.json"))) {
|
|
56001
56581
|
ecosystems.push("npm");
|
|
56002
56582
|
}
|
|
56003
|
-
if (
|
|
56583
|
+
if (fs31.existsSync(path44.join(cwd, "pyproject.toml")) || fs31.existsSync(path44.join(cwd, "requirements.txt"))) {
|
|
56004
56584
|
ecosystems.push("pip");
|
|
56005
56585
|
}
|
|
56006
|
-
if (
|
|
56586
|
+
if (fs31.existsSync(path44.join(cwd, "Cargo.toml"))) {
|
|
56007
56587
|
ecosystems.push("cargo");
|
|
56008
56588
|
}
|
|
56009
|
-
if (
|
|
56589
|
+
if (fs31.existsSync(path44.join(cwd, "go.mod"))) {
|
|
56010
56590
|
ecosystems.push("go");
|
|
56011
56591
|
}
|
|
56012
56592
|
try {
|
|
56013
|
-
const files =
|
|
56593
|
+
const files = fs31.readdirSync(cwd);
|
|
56014
56594
|
if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
|
|
56015
56595
|
ecosystems.push("dotnet");
|
|
56016
56596
|
}
|
|
56017
56597
|
} catch {}
|
|
56018
|
-
if (
|
|
56598
|
+
if (fs31.existsSync(path44.join(cwd, "Gemfile")) || fs31.existsSync(path44.join(cwd, "Gemfile.lock"))) {
|
|
56019
56599
|
ecosystems.push("ruby");
|
|
56020
56600
|
}
|
|
56021
|
-
if (
|
|
56601
|
+
if (fs31.existsSync(path44.join(cwd, "pubspec.yaml"))) {
|
|
56022
56602
|
ecosystems.push("dart");
|
|
56023
56603
|
}
|
|
56024
56604
|
return ecosystems;
|
|
@@ -57080,8 +57660,8 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
|
|
|
57080
57660
|
]);
|
|
57081
57661
|
// src/tools/pre-check-batch.ts
|
|
57082
57662
|
init_dist();
|
|
57083
|
-
import * as
|
|
57084
|
-
import * as
|
|
57663
|
+
import * as fs34 from "fs";
|
|
57664
|
+
import * as path47 from "path";
|
|
57085
57665
|
|
|
57086
57666
|
// node_modules/yocto-queue/index.js
|
|
57087
57667
|
class Node2 {
|
|
@@ -57248,8 +57828,8 @@ init_lint();
|
|
|
57248
57828
|
init_manager();
|
|
57249
57829
|
|
|
57250
57830
|
// src/quality/metrics.ts
|
|
57251
|
-
import * as
|
|
57252
|
-
import * as
|
|
57831
|
+
import * as fs32 from "fs";
|
|
57832
|
+
import * as path45 from "path";
|
|
57253
57833
|
var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
|
|
57254
57834
|
var MIN_DUPLICATION_LINES = 10;
|
|
57255
57835
|
function estimateCyclomaticComplexity(content) {
|
|
@@ -57287,11 +57867,11 @@ function estimateCyclomaticComplexity(content) {
|
|
|
57287
57867
|
}
|
|
57288
57868
|
function getComplexityForFile2(filePath) {
|
|
57289
57869
|
try {
|
|
57290
|
-
const stat2 =
|
|
57870
|
+
const stat2 = fs32.statSync(filePath);
|
|
57291
57871
|
if (stat2.size > MAX_FILE_SIZE_BYTES5) {
|
|
57292
57872
|
return null;
|
|
57293
57873
|
}
|
|
57294
|
-
const content =
|
|
57874
|
+
const content = fs32.readFileSync(filePath, "utf-8");
|
|
57295
57875
|
return estimateCyclomaticComplexity(content);
|
|
57296
57876
|
} catch {
|
|
57297
57877
|
return null;
|
|
@@ -57301,8 +57881,8 @@ async function computeComplexityDelta(files, workingDir) {
|
|
|
57301
57881
|
let totalComplexity = 0;
|
|
57302
57882
|
const analyzedFiles = [];
|
|
57303
57883
|
for (const file3 of files) {
|
|
57304
|
-
const fullPath =
|
|
57305
|
-
if (!
|
|
57884
|
+
const fullPath = path45.isAbsolute(file3) ? file3 : path45.join(workingDir, file3);
|
|
57885
|
+
if (!fs32.existsSync(fullPath)) {
|
|
57306
57886
|
continue;
|
|
57307
57887
|
}
|
|
57308
57888
|
const complexity = getComplexityForFile2(fullPath);
|
|
@@ -57423,8 +58003,8 @@ function countGoExports(content) {
|
|
|
57423
58003
|
}
|
|
57424
58004
|
function getExportCountForFile(filePath) {
|
|
57425
58005
|
try {
|
|
57426
|
-
const content =
|
|
57427
|
-
const ext =
|
|
58006
|
+
const content = fs32.readFileSync(filePath, "utf-8");
|
|
58007
|
+
const ext = path45.extname(filePath).toLowerCase();
|
|
57428
58008
|
switch (ext) {
|
|
57429
58009
|
case ".ts":
|
|
57430
58010
|
case ".tsx":
|
|
@@ -57450,8 +58030,8 @@ async function computePublicApiDelta(files, workingDir) {
|
|
|
57450
58030
|
let totalExports = 0;
|
|
57451
58031
|
const analyzedFiles = [];
|
|
57452
58032
|
for (const file3 of files) {
|
|
57453
|
-
const fullPath =
|
|
57454
|
-
if (!
|
|
58033
|
+
const fullPath = path45.isAbsolute(file3) ? file3 : path45.join(workingDir, file3);
|
|
58034
|
+
if (!fs32.existsSync(fullPath)) {
|
|
57455
58035
|
continue;
|
|
57456
58036
|
}
|
|
57457
58037
|
const exports = getExportCountForFile(fullPath);
|
|
@@ -57484,16 +58064,16 @@ async function computeDuplicationRatio(files, workingDir) {
|
|
|
57484
58064
|
let duplicateLines = 0;
|
|
57485
58065
|
const analyzedFiles = [];
|
|
57486
58066
|
for (const file3 of files) {
|
|
57487
|
-
const fullPath =
|
|
57488
|
-
if (!
|
|
58067
|
+
const fullPath = path45.isAbsolute(file3) ? file3 : path45.join(workingDir, file3);
|
|
58068
|
+
if (!fs32.existsSync(fullPath)) {
|
|
57489
58069
|
continue;
|
|
57490
58070
|
}
|
|
57491
58071
|
try {
|
|
57492
|
-
const stat2 =
|
|
58072
|
+
const stat2 = fs32.statSync(fullPath);
|
|
57493
58073
|
if (stat2.size > MAX_FILE_SIZE_BYTES5) {
|
|
57494
58074
|
continue;
|
|
57495
58075
|
}
|
|
57496
|
-
const content =
|
|
58076
|
+
const content = fs32.readFileSync(fullPath, "utf-8");
|
|
57497
58077
|
const lines = content.split(`
|
|
57498
58078
|
`).filter((line) => line.trim().length > 0);
|
|
57499
58079
|
if (lines.length < MIN_DUPLICATION_LINES) {
|
|
@@ -57517,8 +58097,8 @@ function countCodeLines(content) {
|
|
|
57517
58097
|
return lines.length;
|
|
57518
58098
|
}
|
|
57519
58099
|
function isTestFile(filePath) {
|
|
57520
|
-
const basename8 =
|
|
57521
|
-
const _ext =
|
|
58100
|
+
const basename8 = path45.basename(filePath);
|
|
58101
|
+
const _ext = path45.extname(filePath).toLowerCase();
|
|
57522
58102
|
const testPatterns = [
|
|
57523
58103
|
".test.",
|
|
57524
58104
|
".spec.",
|
|
@@ -57599,8 +58179,8 @@ function matchGlobSegment(globSegments, pathSegments) {
|
|
|
57599
58179
|
}
|
|
57600
58180
|
return gIndex === globSegments.length && pIndex === pathSegments.length;
|
|
57601
58181
|
}
|
|
57602
|
-
function matchesGlobSegment(
|
|
57603
|
-
const normalizedPath =
|
|
58182
|
+
function matchesGlobSegment(path46, glob) {
|
|
58183
|
+
const normalizedPath = path46.replace(/\\/g, "/");
|
|
57604
58184
|
const normalizedGlob = glob.replace(/\\/g, "/");
|
|
57605
58185
|
if (normalizedPath.includes("//")) {
|
|
57606
58186
|
return false;
|
|
@@ -57631,8 +58211,8 @@ function simpleGlobToRegex2(glob) {
|
|
|
57631
58211
|
function hasGlobstar(glob) {
|
|
57632
58212
|
return glob.includes("**");
|
|
57633
58213
|
}
|
|
57634
|
-
function globMatches(
|
|
57635
|
-
const normalizedPath =
|
|
58214
|
+
function globMatches(path46, glob) {
|
|
58215
|
+
const normalizedPath = path46.replace(/\\/g, "/");
|
|
57636
58216
|
if (!glob || glob === "") {
|
|
57637
58217
|
if (normalizedPath.includes("//")) {
|
|
57638
58218
|
return false;
|
|
@@ -57668,31 +58248,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
|
|
|
57668
58248
|
async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
57669
58249
|
let testLines = 0;
|
|
57670
58250
|
let codeLines = 0;
|
|
57671
|
-
const srcDir =
|
|
57672
|
-
if (
|
|
58251
|
+
const srcDir = path45.join(workingDir, "src");
|
|
58252
|
+
if (fs32.existsSync(srcDir)) {
|
|
57673
58253
|
await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
57674
58254
|
codeLines += lines;
|
|
57675
58255
|
});
|
|
57676
58256
|
}
|
|
57677
58257
|
const possibleSrcDirs = ["lib", "app", "source", "core"];
|
|
57678
58258
|
for (const dir of possibleSrcDirs) {
|
|
57679
|
-
const dirPath =
|
|
57680
|
-
if (
|
|
58259
|
+
const dirPath = path45.join(workingDir, dir);
|
|
58260
|
+
if (fs32.existsSync(dirPath)) {
|
|
57681
58261
|
await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
57682
58262
|
codeLines += lines;
|
|
57683
58263
|
});
|
|
57684
58264
|
}
|
|
57685
58265
|
}
|
|
57686
|
-
const testsDir =
|
|
57687
|
-
if (
|
|
58266
|
+
const testsDir = path45.join(workingDir, "tests");
|
|
58267
|
+
if (fs32.existsSync(testsDir)) {
|
|
57688
58268
|
await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
57689
58269
|
testLines += lines;
|
|
57690
58270
|
});
|
|
57691
58271
|
}
|
|
57692
58272
|
const possibleTestDirs = ["test", "__tests__", "specs"];
|
|
57693
58273
|
for (const dir of possibleTestDirs) {
|
|
57694
|
-
const dirPath =
|
|
57695
|
-
if (
|
|
58274
|
+
const dirPath = path45.join(workingDir, dir);
|
|
58275
|
+
if (fs32.existsSync(dirPath) && dirPath !== testsDir) {
|
|
57696
58276
|
await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
57697
58277
|
testLines += lines;
|
|
57698
58278
|
});
|
|
@@ -57704,9 +58284,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
57704
58284
|
}
|
|
57705
58285
|
async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
|
|
57706
58286
|
try {
|
|
57707
|
-
const entries =
|
|
58287
|
+
const entries = fs32.readdirSync(dirPath, { withFileTypes: true });
|
|
57708
58288
|
for (const entry of entries) {
|
|
57709
|
-
const fullPath =
|
|
58289
|
+
const fullPath = path45.join(dirPath, entry.name);
|
|
57710
58290
|
if (entry.isDirectory()) {
|
|
57711
58291
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
|
|
57712
58292
|
continue;
|
|
@@ -57714,7 +58294,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
57714
58294
|
await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
|
|
57715
58295
|
} else if (entry.isFile()) {
|
|
57716
58296
|
const relativePath = fullPath.replace(`${process.cwd()}/`, "");
|
|
57717
|
-
const ext =
|
|
58297
|
+
const ext = path45.extname(entry.name).toLowerCase();
|
|
57718
58298
|
const validExts = [
|
|
57719
58299
|
".ts",
|
|
57720
58300
|
".tsx",
|
|
@@ -57750,7 +58330,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
57750
58330
|
continue;
|
|
57751
58331
|
}
|
|
57752
58332
|
try {
|
|
57753
|
-
const content =
|
|
58333
|
+
const content = fs32.readFileSync(fullPath, "utf-8");
|
|
57754
58334
|
const lines = countCodeLines(content);
|
|
57755
58335
|
callback(lines);
|
|
57756
58336
|
} catch {}
|
|
@@ -57964,8 +58544,8 @@ async function qualityBudget(input, directory) {
|
|
|
57964
58544
|
init_dist();
|
|
57965
58545
|
init_manager();
|
|
57966
58546
|
init_detector();
|
|
57967
|
-
import * as
|
|
57968
|
-
import * as
|
|
58547
|
+
import * as fs33 from "fs";
|
|
58548
|
+
import * as path46 from "path";
|
|
57969
58549
|
import { extname as extname9 } from "path";
|
|
57970
58550
|
|
|
57971
58551
|
// src/sast/rules/c.ts
|
|
@@ -58832,17 +59412,17 @@ var SEVERITY_ORDER = {
|
|
|
58832
59412
|
};
|
|
58833
59413
|
function shouldSkipFile(filePath) {
|
|
58834
59414
|
try {
|
|
58835
|
-
const stats =
|
|
59415
|
+
const stats = fs33.statSync(filePath);
|
|
58836
59416
|
if (stats.size > MAX_FILE_SIZE_BYTES6) {
|
|
58837
59417
|
return { skip: true, reason: "file too large" };
|
|
58838
59418
|
}
|
|
58839
59419
|
if (stats.size === 0) {
|
|
58840
59420
|
return { skip: true, reason: "empty file" };
|
|
58841
59421
|
}
|
|
58842
|
-
const fd =
|
|
59422
|
+
const fd = fs33.openSync(filePath, "r");
|
|
58843
59423
|
const buffer = Buffer.alloc(8192);
|
|
58844
|
-
const bytesRead =
|
|
58845
|
-
|
|
59424
|
+
const bytesRead = fs33.readSync(fd, buffer, 0, 8192, 0);
|
|
59425
|
+
fs33.closeSync(fd);
|
|
58846
59426
|
if (bytesRead > 0) {
|
|
58847
59427
|
let nullCount = 0;
|
|
58848
59428
|
for (let i2 = 0;i2 < bytesRead; i2++) {
|
|
@@ -58881,7 +59461,7 @@ function countBySeverity(findings) {
|
|
|
58881
59461
|
}
|
|
58882
59462
|
function scanFileWithTierA(filePath, language) {
|
|
58883
59463
|
try {
|
|
58884
|
-
const content =
|
|
59464
|
+
const content = fs33.readFileSync(filePath, "utf-8");
|
|
58885
59465
|
const findings = executeRulesSync(filePath, content, language);
|
|
58886
59466
|
return findings.map((f) => ({
|
|
58887
59467
|
rule_id: f.rule_id,
|
|
@@ -58928,8 +59508,8 @@ async function sastScan(input, directory, config3) {
|
|
|
58928
59508
|
_filesSkipped++;
|
|
58929
59509
|
continue;
|
|
58930
59510
|
}
|
|
58931
|
-
const resolvedPath =
|
|
58932
|
-
if (!
|
|
59511
|
+
const resolvedPath = path46.isAbsolute(filePath) ? filePath : path46.resolve(directory, filePath);
|
|
59512
|
+
if (!fs33.existsSync(resolvedPath)) {
|
|
58933
59513
|
_filesSkipped++;
|
|
58934
59514
|
continue;
|
|
58935
59515
|
}
|
|
@@ -59127,18 +59707,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
59127
59707
|
let resolved;
|
|
59128
59708
|
const isWinAbs = isWindowsAbsolutePath(inputPath);
|
|
59129
59709
|
if (isWinAbs) {
|
|
59130
|
-
resolved =
|
|
59131
|
-
} else if (
|
|
59132
|
-
resolved =
|
|
59710
|
+
resolved = path47.win32.resolve(inputPath);
|
|
59711
|
+
} else if (path47.isAbsolute(inputPath)) {
|
|
59712
|
+
resolved = path47.resolve(inputPath);
|
|
59133
59713
|
} else {
|
|
59134
|
-
resolved =
|
|
59714
|
+
resolved = path47.resolve(baseDir, inputPath);
|
|
59135
59715
|
}
|
|
59136
|
-
const workspaceResolved =
|
|
59716
|
+
const workspaceResolved = path47.resolve(workspaceDir);
|
|
59137
59717
|
let relative5;
|
|
59138
59718
|
if (isWinAbs) {
|
|
59139
|
-
relative5 =
|
|
59719
|
+
relative5 = path47.win32.relative(workspaceResolved, resolved);
|
|
59140
59720
|
} else {
|
|
59141
|
-
relative5 =
|
|
59721
|
+
relative5 = path47.relative(workspaceResolved, resolved);
|
|
59142
59722
|
}
|
|
59143
59723
|
if (relative5.startsWith("..")) {
|
|
59144
59724
|
return "path traversal detected";
|
|
@@ -59158,7 +59738,7 @@ function validateDirectory3(dir, workspaceDir) {
|
|
|
59158
59738
|
}
|
|
59159
59739
|
return null;
|
|
59160
59740
|
}
|
|
59161
|
-
async function
|
|
59741
|
+
async function runWithTimeout2(promise3, timeoutMs) {
|
|
59162
59742
|
const timeoutPromise = new Promise((_, reject) => {
|
|
59163
59743
|
setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs);
|
|
59164
59744
|
});
|
|
@@ -59183,7 +59763,7 @@ async function runLintWrapped(files, directory, _config) {
|
|
|
59183
59763
|
duration_ms: Number(process.hrtime.bigint() - start2) / 1e6
|
|
59184
59764
|
};
|
|
59185
59765
|
}
|
|
59186
|
-
const result = await
|
|
59766
|
+
const result = await runWithTimeout2(runLint(linter, "check", directory), TOOL_TIMEOUT_MS);
|
|
59187
59767
|
return {
|
|
59188
59768
|
ran: true,
|
|
59189
59769
|
result,
|
|
@@ -59199,13 +59779,13 @@ async function runLintWrapped(files, directory, _config) {
|
|
|
59199
59779
|
}
|
|
59200
59780
|
async function runLintOnFiles(linter, files, workspaceDir) {
|
|
59201
59781
|
const isWindows = process.platform === "win32";
|
|
59202
|
-
const binDir =
|
|
59782
|
+
const binDir = path47.join(workspaceDir, "node_modules", ".bin");
|
|
59203
59783
|
const validatedFiles = [];
|
|
59204
59784
|
for (const file3 of files) {
|
|
59205
59785
|
if (typeof file3 !== "string") {
|
|
59206
59786
|
continue;
|
|
59207
59787
|
}
|
|
59208
|
-
const resolvedPath =
|
|
59788
|
+
const resolvedPath = path47.resolve(file3);
|
|
59209
59789
|
const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
|
|
59210
59790
|
if (validationError) {
|
|
59211
59791
|
continue;
|
|
@@ -59223,10 +59803,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
|
|
|
59223
59803
|
}
|
|
59224
59804
|
let command;
|
|
59225
59805
|
if (linter === "biome") {
|
|
59226
|
-
const biomeBin = isWindows ?
|
|
59806
|
+
const biomeBin = isWindows ? path47.join(binDir, "biome.EXE") : path47.join(binDir, "biome");
|
|
59227
59807
|
command = [biomeBin, "check", ...validatedFiles];
|
|
59228
59808
|
} else {
|
|
59229
|
-
const eslintBin = isWindows ?
|
|
59809
|
+
const eslintBin = isWindows ? path47.join(binDir, "eslint.cmd") : path47.join(binDir, "eslint");
|
|
59230
59810
|
command = [eslintBin, ...validatedFiles];
|
|
59231
59811
|
}
|
|
59232
59812
|
try {
|
|
@@ -59277,14 +59857,14 @@ async function runSecretscanWrapped(files, directory, _config) {
|
|
|
59277
59857
|
const start2 = process.hrtime.bigint();
|
|
59278
59858
|
try {
|
|
59279
59859
|
if (files && files.length > 0) {
|
|
59280
|
-
const result2 = await
|
|
59860
|
+
const result2 = await runWithTimeout2(runSecretscanWithFiles(files, directory), TOOL_TIMEOUT_MS);
|
|
59281
59861
|
return {
|
|
59282
59862
|
ran: true,
|
|
59283
59863
|
result: result2,
|
|
59284
59864
|
duration_ms: Number(process.hrtime.bigint() - start2) / 1e6
|
|
59285
59865
|
};
|
|
59286
59866
|
}
|
|
59287
|
-
const result = await
|
|
59867
|
+
const result = await runWithTimeout2(runSecretscan(directory), TOOL_TIMEOUT_MS);
|
|
59288
59868
|
return {
|
|
59289
59869
|
ran: true,
|
|
59290
59870
|
result,
|
|
@@ -59363,7 +59943,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59363
59943
|
skippedFiles++;
|
|
59364
59944
|
continue;
|
|
59365
59945
|
}
|
|
59366
|
-
const resolvedPath =
|
|
59946
|
+
const resolvedPath = path47.resolve(file3);
|
|
59367
59947
|
const validationError = validatePath(resolvedPath, directory, directory);
|
|
59368
59948
|
if (validationError) {
|
|
59369
59949
|
skippedFiles++;
|
|
@@ -59381,14 +59961,14 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59381
59961
|
};
|
|
59382
59962
|
}
|
|
59383
59963
|
for (const file3 of validatedFiles) {
|
|
59384
|
-
const ext =
|
|
59964
|
+
const ext = path47.extname(file3).toLowerCase();
|
|
59385
59965
|
if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
|
|
59386
59966
|
skippedFiles++;
|
|
59387
59967
|
continue;
|
|
59388
59968
|
}
|
|
59389
59969
|
let stat2;
|
|
59390
59970
|
try {
|
|
59391
|
-
stat2 =
|
|
59971
|
+
stat2 = fs34.statSync(file3);
|
|
59392
59972
|
} catch {
|
|
59393
59973
|
skippedFiles++;
|
|
59394
59974
|
continue;
|
|
@@ -59399,7 +59979,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59399
59979
|
}
|
|
59400
59980
|
let content;
|
|
59401
59981
|
try {
|
|
59402
|
-
const buffer =
|
|
59982
|
+
const buffer = fs34.readFileSync(file3);
|
|
59403
59983
|
if (buffer.includes(0)) {
|
|
59404
59984
|
skippedFiles++;
|
|
59405
59985
|
continue;
|
|
@@ -59468,7 +60048,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59468
60048
|
async function runSastScanWrapped(changedFiles, directory, severityThreshold, config3) {
|
|
59469
60049
|
const start2 = process.hrtime.bigint();
|
|
59470
60050
|
try {
|
|
59471
|
-
const result = await
|
|
60051
|
+
const result = await runWithTimeout2(sastScan({ changed_files: changedFiles, severity_threshold: severityThreshold }, directory, config3), TOOL_TIMEOUT_MS);
|
|
59472
60052
|
return {
|
|
59473
60053
|
ran: true,
|
|
59474
60054
|
result,
|
|
@@ -59485,7 +60065,7 @@ async function runSastScanWrapped(changedFiles, directory, severityThreshold, co
|
|
|
59485
60065
|
async function runQualityBudgetWrapped(changedFiles, directory, _config) {
|
|
59486
60066
|
const start2 = process.hrtime.bigint();
|
|
59487
60067
|
try {
|
|
59488
|
-
const result = await
|
|
60068
|
+
const result = await runWithTimeout2(qualityBudget({ changed_files: changedFiles }, directory), TOOL_TIMEOUT_MS);
|
|
59489
60069
|
return {
|
|
59490
60070
|
ran: true,
|
|
59491
60071
|
result,
|
|
@@ -59540,7 +60120,7 @@ async function runPreCheckBatch(input, workspaceDir) {
|
|
|
59540
60120
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
59541
60121
|
continue;
|
|
59542
60122
|
}
|
|
59543
|
-
changedFiles.push(
|
|
60123
|
+
changedFiles.push(path47.resolve(directory, file3));
|
|
59544
60124
|
}
|
|
59545
60125
|
if (changedFiles.length === 0) {
|
|
59546
60126
|
warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
|
|
@@ -59691,7 +60271,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
59691
60271
|
};
|
|
59692
60272
|
return JSON.stringify(errorResult, null, 2);
|
|
59693
60273
|
}
|
|
59694
|
-
const resolvedDirectory =
|
|
60274
|
+
const resolvedDirectory = path47.resolve(typedArgs.directory);
|
|
59695
60275
|
const workspaceAnchor = resolvedDirectory;
|
|
59696
60276
|
const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
|
|
59697
60277
|
if (dirError) {
|
|
@@ -59798,8 +60378,8 @@ ${paginatedContent}`;
|
|
|
59798
60378
|
init_tool();
|
|
59799
60379
|
init_manager2();
|
|
59800
60380
|
init_create_tool();
|
|
59801
|
-
import * as
|
|
59802
|
-
import * as
|
|
60381
|
+
import * as fs35 from "fs";
|
|
60382
|
+
import * as path48 from "path";
|
|
59803
60383
|
function detectPlaceholderContent(args2) {
|
|
59804
60384
|
const issues = [];
|
|
59805
60385
|
const placeholderPattern = /^\[\w[\w\s]*\]$/;
|
|
@@ -59903,19 +60483,19 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
59903
60483
|
try {
|
|
59904
60484
|
await savePlan(dir, plan);
|
|
59905
60485
|
try {
|
|
59906
|
-
const markerPath =
|
|
60486
|
+
const markerPath = path48.join(dir, ".swarm", ".plan-write-marker");
|
|
59907
60487
|
const marker = JSON.stringify({
|
|
59908
60488
|
source: "save_plan",
|
|
59909
60489
|
timestamp: new Date().toISOString(),
|
|
59910
60490
|
phases_count: plan.phases.length,
|
|
59911
60491
|
tasks_count: tasksCount
|
|
59912
60492
|
});
|
|
59913
|
-
await
|
|
60493
|
+
await fs35.promises.writeFile(markerPath, marker, "utf8");
|
|
59914
60494
|
} catch {}
|
|
59915
60495
|
return {
|
|
59916
60496
|
success: true,
|
|
59917
60497
|
message: "Plan saved successfully",
|
|
59918
|
-
plan_path:
|
|
60498
|
+
plan_path: path48.join(dir, ".swarm", "plan.json"),
|
|
59919
60499
|
phases_count: plan.phases.length,
|
|
59920
60500
|
tasks_count: tasksCount
|
|
59921
60501
|
};
|
|
@@ -59953,8 +60533,8 @@ var save_plan = createSwarmTool({
|
|
|
59953
60533
|
// src/tools/sbom-generate.ts
|
|
59954
60534
|
init_dist();
|
|
59955
60535
|
init_manager();
|
|
59956
|
-
import * as
|
|
59957
|
-
import * as
|
|
60536
|
+
import * as fs36 from "fs";
|
|
60537
|
+
import * as path49 from "path";
|
|
59958
60538
|
|
|
59959
60539
|
// src/sbom/detectors/index.ts
|
|
59960
60540
|
init_utils();
|
|
@@ -60800,9 +61380,9 @@ function findManifestFiles(rootDir) {
|
|
|
60800
61380
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
60801
61381
|
function searchDir(dir) {
|
|
60802
61382
|
try {
|
|
60803
|
-
const entries =
|
|
61383
|
+
const entries = fs36.readdirSync(dir, { withFileTypes: true });
|
|
60804
61384
|
for (const entry of entries) {
|
|
60805
|
-
const fullPath =
|
|
61385
|
+
const fullPath = path49.join(dir, entry.name);
|
|
60806
61386
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
60807
61387
|
continue;
|
|
60808
61388
|
}
|
|
@@ -60811,7 +61391,7 @@ function findManifestFiles(rootDir) {
|
|
|
60811
61391
|
} else if (entry.isFile()) {
|
|
60812
61392
|
for (const pattern of patterns) {
|
|
60813
61393
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
60814
|
-
manifestFiles.push(
|
|
61394
|
+
manifestFiles.push(path49.relative(rootDir, fullPath));
|
|
60815
61395
|
break;
|
|
60816
61396
|
}
|
|
60817
61397
|
}
|
|
@@ -60827,13 +61407,13 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
60827
61407
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
60828
61408
|
for (const dir of directories) {
|
|
60829
61409
|
try {
|
|
60830
|
-
const entries =
|
|
61410
|
+
const entries = fs36.readdirSync(dir, { withFileTypes: true });
|
|
60831
61411
|
for (const entry of entries) {
|
|
60832
|
-
const fullPath =
|
|
61412
|
+
const fullPath = path49.join(dir, entry.name);
|
|
60833
61413
|
if (entry.isFile()) {
|
|
60834
61414
|
for (const pattern of patterns) {
|
|
60835
61415
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
60836
|
-
found.push(
|
|
61416
|
+
found.push(path49.relative(workingDir, fullPath));
|
|
60837
61417
|
break;
|
|
60838
61418
|
}
|
|
60839
61419
|
}
|
|
@@ -60846,11 +61426,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
60846
61426
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
60847
61427
|
const dirs = new Set;
|
|
60848
61428
|
for (const file3 of changedFiles) {
|
|
60849
|
-
let currentDir =
|
|
61429
|
+
let currentDir = path49.dirname(file3);
|
|
60850
61430
|
while (true) {
|
|
60851
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
60852
|
-
dirs.add(
|
|
60853
|
-
const parent =
|
|
61431
|
+
if (currentDir && currentDir !== "." && currentDir !== path49.sep) {
|
|
61432
|
+
dirs.add(path49.join(workingDir, currentDir));
|
|
61433
|
+
const parent = path49.dirname(currentDir);
|
|
60854
61434
|
if (parent === currentDir)
|
|
60855
61435
|
break;
|
|
60856
61436
|
currentDir = parent;
|
|
@@ -60864,7 +61444,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
|
60864
61444
|
}
|
|
60865
61445
|
function ensureOutputDir(outputDir) {
|
|
60866
61446
|
try {
|
|
60867
|
-
|
|
61447
|
+
fs36.mkdirSync(outputDir, { recursive: true });
|
|
60868
61448
|
} catch (error93) {
|
|
60869
61449
|
if (!error93 || error93.code !== "EEXIST") {
|
|
60870
61450
|
throw error93;
|
|
@@ -60934,7 +61514,7 @@ var sbom_generate = createSwarmTool({
|
|
|
60934
61514
|
const changedFiles = obj.changed_files;
|
|
60935
61515
|
const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
|
|
60936
61516
|
const workingDir = directory;
|
|
60937
|
-
const outputDir =
|
|
61517
|
+
const outputDir = path49.isAbsolute(relativeOutputDir) ? relativeOutputDir : path49.join(workingDir, relativeOutputDir);
|
|
60938
61518
|
let manifestFiles = [];
|
|
60939
61519
|
if (scope === "all") {
|
|
60940
61520
|
manifestFiles = findManifestFiles(workingDir);
|
|
@@ -60957,11 +61537,11 @@ var sbom_generate = createSwarmTool({
|
|
|
60957
61537
|
const processedFiles = [];
|
|
60958
61538
|
for (const manifestFile of manifestFiles) {
|
|
60959
61539
|
try {
|
|
60960
|
-
const fullPath =
|
|
60961
|
-
if (!
|
|
61540
|
+
const fullPath = path49.isAbsolute(manifestFile) ? manifestFile : path49.join(workingDir, manifestFile);
|
|
61541
|
+
if (!fs36.existsSync(fullPath)) {
|
|
60962
61542
|
continue;
|
|
60963
61543
|
}
|
|
60964
|
-
const content =
|
|
61544
|
+
const content = fs36.readFileSync(fullPath, "utf-8");
|
|
60965
61545
|
const components = detectComponents(manifestFile, content);
|
|
60966
61546
|
processedFiles.push(manifestFile);
|
|
60967
61547
|
if (components.length > 0) {
|
|
@@ -60974,8 +61554,8 @@ var sbom_generate = createSwarmTool({
|
|
|
60974
61554
|
const bom = generateCycloneDX(allComponents);
|
|
60975
61555
|
const bomJson = serializeCycloneDX(bom);
|
|
60976
61556
|
const filename = generateSbomFilename();
|
|
60977
|
-
const outputPath =
|
|
60978
|
-
|
|
61557
|
+
const outputPath = path49.join(outputDir, filename);
|
|
61558
|
+
fs36.writeFileSync(outputPath, bomJson, "utf-8");
|
|
60979
61559
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
60980
61560
|
try {
|
|
60981
61561
|
const timestamp = new Date().toISOString();
|
|
@@ -61017,8 +61597,8 @@ var sbom_generate = createSwarmTool({
|
|
|
61017
61597
|
// src/tools/schema-drift.ts
|
|
61018
61598
|
init_dist();
|
|
61019
61599
|
init_create_tool();
|
|
61020
|
-
import * as
|
|
61021
|
-
import * as
|
|
61600
|
+
import * as fs37 from "fs";
|
|
61601
|
+
import * as path50 from "path";
|
|
61022
61602
|
var SPEC_CANDIDATES = [
|
|
61023
61603
|
"openapi.json",
|
|
61024
61604
|
"openapi.yaml",
|
|
@@ -61050,28 +61630,28 @@ function normalizePath2(p) {
|
|
|
61050
61630
|
}
|
|
61051
61631
|
function discoverSpecFile(cwd, specFileArg) {
|
|
61052
61632
|
if (specFileArg) {
|
|
61053
|
-
const resolvedPath =
|
|
61054
|
-
const normalizedCwd = cwd.endsWith(
|
|
61633
|
+
const resolvedPath = path50.resolve(cwd, specFileArg);
|
|
61634
|
+
const normalizedCwd = cwd.endsWith(path50.sep) ? cwd : cwd + path50.sep;
|
|
61055
61635
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
61056
61636
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
61057
61637
|
}
|
|
61058
|
-
const ext =
|
|
61638
|
+
const ext = path50.extname(resolvedPath).toLowerCase();
|
|
61059
61639
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
61060
61640
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
61061
61641
|
}
|
|
61062
|
-
const stats =
|
|
61642
|
+
const stats = fs37.statSync(resolvedPath);
|
|
61063
61643
|
if (stats.size > MAX_SPEC_SIZE) {
|
|
61064
61644
|
throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
|
|
61065
61645
|
}
|
|
61066
|
-
if (!
|
|
61646
|
+
if (!fs37.existsSync(resolvedPath)) {
|
|
61067
61647
|
throw new Error(`Spec file not found: ${resolvedPath}`);
|
|
61068
61648
|
}
|
|
61069
61649
|
return resolvedPath;
|
|
61070
61650
|
}
|
|
61071
61651
|
for (const candidate of SPEC_CANDIDATES) {
|
|
61072
|
-
const candidatePath =
|
|
61073
|
-
if (
|
|
61074
|
-
const stats =
|
|
61652
|
+
const candidatePath = path50.resolve(cwd, candidate);
|
|
61653
|
+
if (fs37.existsSync(candidatePath)) {
|
|
61654
|
+
const stats = fs37.statSync(candidatePath);
|
|
61075
61655
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
61076
61656
|
return candidatePath;
|
|
61077
61657
|
}
|
|
@@ -61080,8 +61660,8 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
61080
61660
|
return null;
|
|
61081
61661
|
}
|
|
61082
61662
|
function parseSpec(specFile) {
|
|
61083
|
-
const content =
|
|
61084
|
-
const ext =
|
|
61663
|
+
const content = fs37.readFileSync(specFile, "utf-8");
|
|
61664
|
+
const ext = path50.extname(specFile).toLowerCase();
|
|
61085
61665
|
if (ext === ".json") {
|
|
61086
61666
|
return parseJsonSpec(content);
|
|
61087
61667
|
}
|
|
@@ -61152,12 +61732,12 @@ function extractRoutes(cwd) {
|
|
|
61152
61732
|
function walkDir(dir) {
|
|
61153
61733
|
let entries;
|
|
61154
61734
|
try {
|
|
61155
|
-
entries =
|
|
61735
|
+
entries = fs37.readdirSync(dir, { withFileTypes: true });
|
|
61156
61736
|
} catch {
|
|
61157
61737
|
return;
|
|
61158
61738
|
}
|
|
61159
61739
|
for (const entry of entries) {
|
|
61160
|
-
const fullPath =
|
|
61740
|
+
const fullPath = path50.join(dir, entry.name);
|
|
61161
61741
|
if (entry.isSymbolicLink()) {
|
|
61162
61742
|
continue;
|
|
61163
61743
|
}
|
|
@@ -61167,7 +61747,7 @@ function extractRoutes(cwd) {
|
|
|
61167
61747
|
}
|
|
61168
61748
|
walkDir(fullPath);
|
|
61169
61749
|
} else if (entry.isFile()) {
|
|
61170
|
-
const ext =
|
|
61750
|
+
const ext = path50.extname(entry.name).toLowerCase();
|
|
61171
61751
|
const baseName = entry.name.toLowerCase();
|
|
61172
61752
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
61173
61753
|
continue;
|
|
@@ -61185,7 +61765,7 @@ function extractRoutes(cwd) {
|
|
|
61185
61765
|
}
|
|
61186
61766
|
function extractRoutesFromFile(filePath) {
|
|
61187
61767
|
const routes = [];
|
|
61188
|
-
const content =
|
|
61768
|
+
const content = fs37.readFileSync(filePath, "utf-8");
|
|
61189
61769
|
const lines = content.split(/\r?\n/);
|
|
61190
61770
|
const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
61191
61771
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
@@ -61336,8 +61916,8 @@ init_secretscan();
|
|
|
61336
61916
|
// src/tools/symbols.ts
|
|
61337
61917
|
init_tool();
|
|
61338
61918
|
init_create_tool();
|
|
61339
|
-
import * as
|
|
61340
|
-
import * as
|
|
61919
|
+
import * as fs38 from "fs";
|
|
61920
|
+
import * as path51 from "path";
|
|
61341
61921
|
var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
|
|
61342
61922
|
var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
61343
61923
|
function containsControlCharacters(str) {
|
|
@@ -61366,11 +61946,11 @@ function containsWindowsAttacks(str) {
|
|
|
61366
61946
|
}
|
|
61367
61947
|
function isPathInWorkspace(filePath, workspace) {
|
|
61368
61948
|
try {
|
|
61369
|
-
const resolvedPath =
|
|
61370
|
-
const realWorkspace =
|
|
61371
|
-
const realResolvedPath =
|
|
61372
|
-
const relativePath =
|
|
61373
|
-
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)) {
|
|
61374
61954
|
return false;
|
|
61375
61955
|
}
|
|
61376
61956
|
return true;
|
|
@@ -61382,17 +61962,17 @@ function validatePathForRead(filePath, workspace) {
|
|
|
61382
61962
|
return isPathInWorkspace(filePath, workspace);
|
|
61383
61963
|
}
|
|
61384
61964
|
function extractTSSymbols(filePath, cwd) {
|
|
61385
|
-
const fullPath =
|
|
61965
|
+
const fullPath = path51.join(cwd, filePath);
|
|
61386
61966
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
61387
61967
|
return [];
|
|
61388
61968
|
}
|
|
61389
61969
|
let content;
|
|
61390
61970
|
try {
|
|
61391
|
-
const stats =
|
|
61971
|
+
const stats = fs38.statSync(fullPath);
|
|
61392
61972
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
61393
61973
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
61394
61974
|
}
|
|
61395
|
-
content =
|
|
61975
|
+
content = fs38.readFileSync(fullPath, "utf-8");
|
|
61396
61976
|
} catch {
|
|
61397
61977
|
return [];
|
|
61398
61978
|
}
|
|
@@ -61534,17 +62114,17 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
61534
62114
|
});
|
|
61535
62115
|
}
|
|
61536
62116
|
function extractPythonSymbols(filePath, cwd) {
|
|
61537
|
-
const fullPath =
|
|
62117
|
+
const fullPath = path51.join(cwd, filePath);
|
|
61538
62118
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
61539
62119
|
return [];
|
|
61540
62120
|
}
|
|
61541
62121
|
let content;
|
|
61542
62122
|
try {
|
|
61543
|
-
const stats =
|
|
62123
|
+
const stats = fs38.statSync(fullPath);
|
|
61544
62124
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
61545
62125
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
61546
62126
|
}
|
|
61547
|
-
content =
|
|
62127
|
+
content = fs38.readFileSync(fullPath, "utf-8");
|
|
61548
62128
|
} catch {
|
|
61549
62129
|
return [];
|
|
61550
62130
|
}
|
|
@@ -61617,7 +62197,7 @@ var symbols = createSwarmTool({
|
|
|
61617
62197
|
}, null, 2);
|
|
61618
62198
|
}
|
|
61619
62199
|
const cwd = directory;
|
|
61620
|
-
const ext =
|
|
62200
|
+
const ext = path51.extname(file3);
|
|
61621
62201
|
if (containsControlCharacters(file3)) {
|
|
61622
62202
|
return JSON.stringify({
|
|
61623
62203
|
file: file3,
|
|
@@ -61688,8 +62268,8 @@ init_test_runner();
|
|
|
61688
62268
|
init_dist();
|
|
61689
62269
|
init_utils();
|
|
61690
62270
|
init_create_tool();
|
|
61691
|
-
import * as
|
|
61692
|
-
import * as
|
|
62271
|
+
import * as fs39 from "fs";
|
|
62272
|
+
import * as path52 from "path";
|
|
61693
62273
|
var MAX_TEXT_LENGTH = 200;
|
|
61694
62274
|
var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
|
|
61695
62275
|
var SUPPORTED_EXTENSIONS2 = new Set([
|
|
@@ -61760,9 +62340,9 @@ function validatePathsInput(paths, cwd) {
|
|
|
61760
62340
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
61761
62341
|
}
|
|
61762
62342
|
try {
|
|
61763
|
-
const resolvedPath =
|
|
61764
|
-
const normalizedCwd =
|
|
61765
|
-
const normalizedResolved =
|
|
62343
|
+
const resolvedPath = path52.resolve(paths);
|
|
62344
|
+
const normalizedCwd = path52.resolve(cwd);
|
|
62345
|
+
const normalizedResolved = path52.resolve(resolvedPath);
|
|
61766
62346
|
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
61767
62347
|
return {
|
|
61768
62348
|
error: "paths must be within the current working directory",
|
|
@@ -61778,13 +62358,13 @@ function validatePathsInput(paths, cwd) {
|
|
|
61778
62358
|
}
|
|
61779
62359
|
}
|
|
61780
62360
|
function isSupportedExtension(filePath) {
|
|
61781
|
-
const ext =
|
|
62361
|
+
const ext = path52.extname(filePath).toLowerCase();
|
|
61782
62362
|
return SUPPORTED_EXTENSIONS2.has(ext);
|
|
61783
62363
|
}
|
|
61784
62364
|
function findSourceFiles2(dir, files = []) {
|
|
61785
62365
|
let entries;
|
|
61786
62366
|
try {
|
|
61787
|
-
entries =
|
|
62367
|
+
entries = fs39.readdirSync(dir);
|
|
61788
62368
|
} catch {
|
|
61789
62369
|
return files;
|
|
61790
62370
|
}
|
|
@@ -61793,10 +62373,10 @@ function findSourceFiles2(dir, files = []) {
|
|
|
61793
62373
|
if (SKIP_DIRECTORIES3.has(entry)) {
|
|
61794
62374
|
continue;
|
|
61795
62375
|
}
|
|
61796
|
-
const fullPath =
|
|
62376
|
+
const fullPath = path52.join(dir, entry);
|
|
61797
62377
|
let stat2;
|
|
61798
62378
|
try {
|
|
61799
|
-
stat2 =
|
|
62379
|
+
stat2 = fs39.statSync(fullPath);
|
|
61800
62380
|
} catch {
|
|
61801
62381
|
continue;
|
|
61802
62382
|
}
|
|
@@ -61889,7 +62469,7 @@ var todo_extract = createSwarmTool({
|
|
|
61889
62469
|
return JSON.stringify(errorResult, null, 2);
|
|
61890
62470
|
}
|
|
61891
62471
|
const scanPath = resolvedPath;
|
|
61892
|
-
if (!
|
|
62472
|
+
if (!fs39.existsSync(scanPath)) {
|
|
61893
62473
|
const errorResult = {
|
|
61894
62474
|
error: `path not found: ${pathsInput}`,
|
|
61895
62475
|
total: 0,
|
|
@@ -61899,13 +62479,13 @@ var todo_extract = createSwarmTool({
|
|
|
61899
62479
|
return JSON.stringify(errorResult, null, 2);
|
|
61900
62480
|
}
|
|
61901
62481
|
const filesToScan = [];
|
|
61902
|
-
const stat2 =
|
|
62482
|
+
const stat2 = fs39.statSync(scanPath);
|
|
61903
62483
|
if (stat2.isFile()) {
|
|
61904
62484
|
if (isSupportedExtension(scanPath)) {
|
|
61905
62485
|
filesToScan.push(scanPath);
|
|
61906
62486
|
} else {
|
|
61907
62487
|
const errorResult = {
|
|
61908
|
-
error: `unsupported file extension: ${
|
|
62488
|
+
error: `unsupported file extension: ${path52.extname(scanPath)}`,
|
|
61909
62489
|
total: 0,
|
|
61910
62490
|
byPriority: { high: 0, medium: 0, low: 0 },
|
|
61911
62491
|
entries: []
|
|
@@ -61918,11 +62498,11 @@ var todo_extract = createSwarmTool({
|
|
|
61918
62498
|
const allEntries = [];
|
|
61919
62499
|
for (const filePath of filesToScan) {
|
|
61920
62500
|
try {
|
|
61921
|
-
const fileStat =
|
|
62501
|
+
const fileStat = fs39.statSync(filePath);
|
|
61922
62502
|
if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
|
|
61923
62503
|
continue;
|
|
61924
62504
|
}
|
|
61925
|
-
const content =
|
|
62505
|
+
const content = fs39.readFileSync(filePath, "utf-8");
|
|
61926
62506
|
const entries = parseTodoComments(content, filePath, tagsSet);
|
|
61927
62507
|
allEntries.push(...entries);
|
|
61928
62508
|
} catch {}
|
|
@@ -61950,9 +62530,95 @@ var todo_extract = createSwarmTool({
|
|
|
61950
62530
|
// src/tools/update-task-status.ts
|
|
61951
62531
|
init_tool();
|
|
61952
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
|
|
61953
62621
|
init_manager2();
|
|
61954
|
-
import * as fs38 from "fs";
|
|
61955
|
-
import * as path51 from "path";
|
|
61956
62622
|
init_create_tool();
|
|
61957
62623
|
var VALID_STATUSES2 = [
|
|
61958
62624
|
"pending",
|
|
@@ -61987,7 +62653,7 @@ var TIER_3_PATTERNS = [
|
|
|
61987
62653
|
];
|
|
61988
62654
|
function matchesTier3Pattern(files) {
|
|
61989
62655
|
for (const file3 of files) {
|
|
61990
|
-
const fileName =
|
|
62656
|
+
const fileName = path54.basename(file3);
|
|
61991
62657
|
for (const pattern of TIER_3_PATTERNS) {
|
|
61992
62658
|
if (pattern.test(fileName)) {
|
|
61993
62659
|
return true;
|
|
@@ -62009,8 +62675,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62009
62675
|
if (hasActiveTurboMode2()) {
|
|
62010
62676
|
const resolvedDir2 = workingDirectory ?? process.cwd();
|
|
62011
62677
|
try {
|
|
62012
|
-
const planPath =
|
|
62013
|
-
const planRaw =
|
|
62678
|
+
const planPath = path54.join(resolvedDir2, ".swarm", "plan.json");
|
|
62679
|
+
const planRaw = fs41.readFileSync(planPath, "utf-8");
|
|
62014
62680
|
const plan = JSON.parse(planRaw);
|
|
62015
62681
|
for (const planPhase of plan.phases ?? []) {
|
|
62016
62682
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -62029,8 +62695,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62029
62695
|
}
|
|
62030
62696
|
const resolvedDir = workingDirectory ?? process.cwd();
|
|
62031
62697
|
try {
|
|
62032
|
-
const evidencePath =
|
|
62033
|
-
const raw =
|
|
62698
|
+
const evidencePath = path54.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
|
|
62699
|
+
const raw = fs41.readFileSync(evidencePath, "utf-8");
|
|
62034
62700
|
const evidence = JSON.parse(raw);
|
|
62035
62701
|
if (evidence?.required_gates && Array.isArray(evidence.required_gates) && evidence?.gates) {
|
|
62036
62702
|
const allGatesMet = evidence.required_gates.every((gate) => evidence.gates[gate] != null);
|
|
@@ -62070,8 +62736,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62070
62736
|
}
|
|
62071
62737
|
try {
|
|
62072
62738
|
const resolvedDir2 = workingDirectory ?? process.cwd();
|
|
62073
|
-
const planPath =
|
|
62074
|
-
const planRaw =
|
|
62739
|
+
const planPath = path54.join(resolvedDir2, ".swarm", "plan.json");
|
|
62740
|
+
const planRaw = fs41.readFileSync(planPath, "utf-8");
|
|
62075
62741
|
const plan = JSON.parse(planRaw);
|
|
62076
62742
|
for (const planPhase of plan.phases ?? []) {
|
|
62077
62743
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -62129,6 +62795,17 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62129
62795
|
return { blocked: false, reason: "" };
|
|
62130
62796
|
}
|
|
62131
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
|
+
}
|
|
62132
62809
|
function recoverTaskStateFromDelegations(taskId) {
|
|
62133
62810
|
let hasReviewer = false;
|
|
62134
62811
|
let hasTestEngineer = false;
|
|
@@ -62241,8 +62918,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
62241
62918
|
};
|
|
62242
62919
|
}
|
|
62243
62920
|
}
|
|
62244
|
-
normalizedDir =
|
|
62245
|
-
const pathParts = normalizedDir.split(
|
|
62921
|
+
normalizedDir = path54.normalize(args2.working_directory);
|
|
62922
|
+
const pathParts = normalizedDir.split(path54.sep);
|
|
62246
62923
|
if (pathParts.includes("..")) {
|
|
62247
62924
|
return {
|
|
62248
62925
|
success: false,
|
|
@@ -62252,11 +62929,11 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
62252
62929
|
]
|
|
62253
62930
|
};
|
|
62254
62931
|
}
|
|
62255
|
-
const resolvedDir =
|
|
62932
|
+
const resolvedDir = path54.resolve(normalizedDir);
|
|
62256
62933
|
try {
|
|
62257
|
-
const realPath =
|
|
62258
|
-
const planPath =
|
|
62259
|
-
if (!
|
|
62934
|
+
const realPath = fs41.realpathSync(resolvedDir);
|
|
62935
|
+
const planPath = path54.join(realPath, ".swarm", "plan.json");
|
|
62936
|
+
if (!fs41.existsSync(planPath)) {
|
|
62260
62937
|
return {
|
|
62261
62938
|
success: false,
|
|
62262
62939
|
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
@@ -62280,7 +62957,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
62280
62957
|
}
|
|
62281
62958
|
if (args2.status === "completed") {
|
|
62282
62959
|
recoverTaskStateFromDelegations(args2.task_id);
|
|
62283
|
-
const reviewerCheck =
|
|
62960
|
+
const reviewerCheck = await checkReviewerGateWithScope(args2.task_id, directory);
|
|
62284
62961
|
if (reviewerCheck.blocked) {
|
|
62285
62962
|
return {
|
|
62286
62963
|
success: false,
|
|
@@ -62403,6 +63080,31 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62403
63080
|
const steeringConsumedHook = createSteeringConsumedHook(ctx.directory);
|
|
62404
63081
|
const coChangeSuggesterHook = createCoChangeSuggesterHook(ctx.directory);
|
|
62405
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;
|
|
62406
63108
|
const snapshotWriterHook = createSnapshotWriterHook(ctx.directory);
|
|
62407
63109
|
const automationConfig = AutomationConfigSchema.parse(config3.automation ?? {});
|
|
62408
63110
|
let automationManager;
|
|
@@ -62413,7 +63115,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62413
63115
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
62414
63116
|
preflightTriggerManager = new PTM(automationConfig);
|
|
62415
63117
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
62416
|
-
const swarmDir =
|
|
63118
|
+
const swarmDir = path55.resolve(ctx.directory, ".swarm");
|
|
62417
63119
|
statusArtifact = new ASA(swarmDir);
|
|
62418
63120
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
62419
63121
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|
|
@@ -62675,6 +63377,13 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62675
63377
|
}
|
|
62676
63378
|
}
|
|
62677
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
|
+
}
|
|
62678
63387
|
await safeHook(activityHooks.toolBefore)(input, output);
|
|
62679
63388
|
},
|
|
62680
63389
|
"tool.execute.after": async (input, output) => {
|
|
@@ -62690,6 +63399,12 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62690
63399
|
await safeHook(darkMatterDetectorHook)(input, output);
|
|
62691
63400
|
await snapshotWriterHook(input, output);
|
|
62692
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);
|
|
62693
63408
|
const toolOutputConfig = config3.tool_output;
|
|
62694
63409
|
if (toolOutputConfig && toolOutputConfig.truncation_enabled !== false && typeof output.output === "string") {
|
|
62695
63410
|
const skipTools = [
|