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/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
- test_failures: exports_external.number().int().min(0),
14225
- security_findings: exports_external.number().int().min(0),
14226
- integration_issues: exports_external.number().int().min(0),
14227
- task_count: exports_external.number().int().min(1),
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 || process.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(path43) {
37604
+ function locateFile(path45) {
37581
37605
  if (Module["locateFile"]) {
37582
- return Module["locateFile"](path43, scriptDirectory);
37606
+ return Module["locateFile"](path45, scriptDirectory);
37583
37607
  }
37584
- return scriptDirectory + path43;
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 path52 from "path";
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
- test_failures: tool.schema.number().int().min(0).describe("Number of test failures encountered"),
47795
- security_findings: tool.schema.number().int().min(0).describe("Number of security findings"),
47796
- integration_issues: tool.schema.number().int().min(0).describe("Number of integration issues"),
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 { isDelegation: true, targetAgent: stripKnownSwarmPrefix(subagentType) };
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 existsSync18 } from "fs";
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 path31 from "path";
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 = path31.join(directory, ".swarm", ".knowledge-shown.json");
52443
+ const shownFile = path32.join(directory, ".swarm", ".knowledge-shown.json");
52079
52444
  try {
52080
52445
  let shownData = {};
52081
- if (existsSync18(shownFile)) {
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(path31.dirname(shownFile), { recursive: true });
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 = path31.join(directory, ".swarm", ".knowledge-shown.json");
52546
+ const shownFile = path32.join(directory, ".swarm", ".knowledge-shown.json");
52182
52547
  try {
52183
- if (!existsSync18(shownFile)) {
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 fs19 from "fs";
52654
- import * as path32 from "path";
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 = path32.join(directory, ".swarm");
52658
- const entries = await fs19.promises.readdir(swarmDir).catch(() => null);
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 = path32.dirname(filePath);
52685
- await fs19.promises.mkdir(swarmDir, { recursive: true });
53049
+ const swarmDir = path33.dirname(filePath);
53050
+ await fs20.promises.mkdir(swarmDir, { recursive: true });
52686
53051
  try {
52687
- await fs19.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
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 fs20 from "fs";
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
- fs20.appendFileSync(eventsPath, `${JSON.stringify(event)}
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 path33 from "path";
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(path33.join(directory, ".swarm/plan.json")).text();
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 fs21 from "fs";
53341
- import * as path34 from "path";
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 = path34.resolve(workspaceRoot);
53359
- const swarmPath = path34.join(normalizedWorkspace, ".swarm", "evidence");
53360
- const normalizedPath = path34.resolve(filePath);
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 (!fs21.existsSync(evidencePath)) {
53944
+ if (!fs23.existsSync(evidencePath)) {
53365
53945
  return null;
53366
53946
  }
53367
53947
  let content;
53368
53948
  try {
53369
- content = fs21.readFileSync(evidencePath, "utf-8");
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 = path34.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
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 fs22 from "fs";
53482
- import * as path35 from "path";
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 path35.join(directory, CHECKPOINT_LOG_PATH);
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 (fs22.existsSync(logPath)) {
53539
- const content = fs22.readFileSync(logPath, "utf-8");
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 = path35.dirname(logPath);
53552
- if (!fs22.existsSync(dir)) {
53553
- fs22.mkdirSync(dir, { recursive: true });
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
- fs22.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
53557
- fs22.renameSync(tempPath, logPath);
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 fs23 from "fs";
53759
- import * as path36 from "path";
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 = fs23.statSync(filePath);
54468
+ const stat2 = fs25.statSync(filePath);
53889
54469
  if (stat2.size > MAX_FILE_SIZE_BYTES2) {
53890
54470
  return null;
53891
54471
  }
53892
- const content = fs23.readFileSync(filePath, "utf-8");
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 = path36.extname(file3).toLowerCase();
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 (!fs23.existsSync(fullPath)) {
53914
- fullPath = path36.join(cwd, file3);
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 fs24 from "fs";
54062
- import * as path37 from "path";
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 = path37.normalize(args2.working_directory);
54142
- const pathParts = normalizedDir.split(path37.sep);
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 = path37.resolve(normalizedDir);
54732
+ const resolvedDir = path39.resolve(normalizedDir);
54153
54733
  try {
54154
- const realPath = fs24.realpathSync(resolvedDir);
54155
- const planPath2 = path37.join(realPath, ".swarm", "plan.json");
54156
- if (!fs24.existsSync(planPath2)) {
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 = path37.resolve(directory, ".swarm", "plan.json");
54177
- if (!fs24.existsSync(planPath)) {
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(fs24.readFileSync(planPath, "utf-8"));
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 path38 of paths) {
54267
- if (!path38 || path38.length === 0) {
54846
+ for (const path40 of paths) {
54847
+ if (!path40 || path40.length === 0) {
54268
54848
  return "empty path not allowed";
54269
54849
  }
54270
- if (path38.length > MAX_PATH_LENGTH) {
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(path38)) {
54853
+ if (SHELL_METACHARACTERS2.test(path40)) {
54274
54854
  return "path contains shell metacharacters";
54275
54855
  }
54276
- if (path38.startsWith("-")) {
54856
+ if (path40.startsWith("-")) {
54277
54857
  return 'path cannot start with "-" (option-like arguments not allowed)';
54278
54858
  }
54279
- if (CONTROL_CHAR_PATTERN2.test(path38)) {
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 path38 = parts2[2];
54360
- files.push({ path: path38, additions, deletions });
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 fs25 from "fs";
54590
- import * as path38 from "path";
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 = path38.resolve(cwd);
54621
- const swarmPath = path38.join(normalizedCwd, ".swarm");
54622
- const normalizedPath = path38.resolve(filePath);
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 (!fs25.existsSync(evidenceDir) || !fs25.statSync(evidenceDir).isDirectory()) {
55218
+ if (!fs27.existsSync(evidenceDir) || !fs27.statSync(evidenceDir).isDirectory()) {
54639
55219
  return evidence;
54640
55220
  }
54641
55221
  let files;
54642
55222
  try {
54643
- files = fs25.readdirSync(evidenceDir);
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 = path38.join(evidenceDir, filename);
55232
+ const filePath = path40.join(evidenceDir, filename);
54653
55233
  try {
54654
- const resolvedPath = path38.resolve(filePath);
54655
- const evidenceDirResolved = path38.resolve(evidenceDir);
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 = fs25.lstatSync(filePath);
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 = fs25.statSync(filePath);
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 = fs25.readFileSync(filePath, "utf-8");
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 = path38.join(cwd, PLAN_FILE);
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 = fs25.readFileSync(planPath, "utf-8");
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 = path38.join(cwd, EVIDENCE_DIR2);
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 fs26 from "fs";
54823
- import * as path39 from "path";
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 (!fs26.existsSync(targetDir)) {
54886
- fs26.mkdirSync(targetDir, { recursive: true });
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 = path39.join(targetDir, filename);
54905
- const base = path39.basename(filepath, path39.extname(filepath));
54906
- const ext = path39.extname(filepath);
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 (fs26.existsSync(filepath)) {
54909
- filepath = path39.join(targetDir, `${base}_${counter}${ext}`);
55488
+ while (fs28.existsSync(filepath)) {
55489
+ filepath = path41.join(targetDir, `${base}_${counter}${ext}`);
54910
55490
  counter++;
54911
55491
  }
54912
55492
  try {
54913
- fs26.writeFileSync(filepath, code.trim(), "utf-8");
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 fs27 from "fs";
55027
- import * as path40 from "path";
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 = path40.extname(filePath).toLowerCase();
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 = path40.resolve(targetFile);
55685
+ _resolvedTarget = path42.resolve(targetFile);
55106
55686
  } catch {
55107
55687
  _resolvedTarget = targetFile;
55108
55688
  }
55109
- const targetBasename = path40.basename(targetFile, path40.extname(targetFile));
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 = path40.normalize(targetWithExt).replace(/\\/g, "/");
55113
- const normalizedTargetWithoutExt = path40.normalize(targetWithoutExt).replace(/\\/g, "/");
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 = path40.dirname(targetFile);
55137
- const targetExt = path40.extname(targetFile);
55138
- const targetBasenameNoExt = path40.basename(targetFile, targetExt);
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 = fs27.readdirSync(dir);
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(path40.join(dir, entry));
55786
+ stats.skippedDirs.push(path42.join(dir, entry));
55207
55787
  continue;
55208
55788
  }
55209
- const fullPath = path40.join(dir, entry);
55789
+ const fullPath = path42.join(dir, entry);
55210
55790
  let stat2;
55211
55791
  try {
55212
- stat2 = fs27.statSync(fullPath);
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 = path40.extname(fullPath).toLowerCase();
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 = path40.resolve(file3);
55280
- if (!fs27.existsSync(targetFile)) {
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 = fs27.statSync(targetFile);
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 = path40.dirname(targetFile);
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 = fs27.statSync(filePath);
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 = fs27.readFileSync(filePath);
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 existsSync26 } from "fs";
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 (!existsSync26(swarmPath)) {
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 (!existsSync26(hivePath)) {
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 fs28 from "fs";
55623
- import * as path41 from "path";
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 = path41.basename(dir);
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 = fs28.readFileSync(planPath, "utf-8");
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
- fs28.appendFileSync(eventsPath, `${JSON.stringify(event)}
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 = fs28.readFileSync(planPath, "utf-8");
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
- fs28.writeFileSync(planPath, `${JSON.stringify(plan, null, 2)}
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 fs29 from "fs";
55982
- import * as path42 from "path";
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 (fs29.existsSync(path42.join(cwd, "package.json"))) {
56580
+ if (fs31.existsSync(path44.join(cwd, "package.json"))) {
56001
56581
  ecosystems.push("npm");
56002
56582
  }
56003
- if (fs29.existsSync(path42.join(cwd, "pyproject.toml")) || fs29.existsSync(path42.join(cwd, "requirements.txt"))) {
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 (fs29.existsSync(path42.join(cwd, "Cargo.toml"))) {
56586
+ if (fs31.existsSync(path44.join(cwd, "Cargo.toml"))) {
56007
56587
  ecosystems.push("cargo");
56008
56588
  }
56009
- if (fs29.existsSync(path42.join(cwd, "go.mod"))) {
56589
+ if (fs31.existsSync(path44.join(cwd, "go.mod"))) {
56010
56590
  ecosystems.push("go");
56011
56591
  }
56012
56592
  try {
56013
- const files = fs29.readdirSync(cwd);
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 (fs29.existsSync(path42.join(cwd, "Gemfile")) || fs29.existsSync(path42.join(cwd, "Gemfile.lock"))) {
56598
+ if (fs31.existsSync(path44.join(cwd, "Gemfile")) || fs31.existsSync(path44.join(cwd, "Gemfile.lock"))) {
56019
56599
  ecosystems.push("ruby");
56020
56600
  }
56021
- if (fs29.existsSync(path42.join(cwd, "pubspec.yaml"))) {
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 fs32 from "fs";
57084
- import * as path45 from "path";
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 fs30 from "fs";
57252
- import * as path43 from "path";
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 = fs30.statSync(filePath);
57870
+ const stat2 = fs32.statSync(filePath);
57291
57871
  if (stat2.size > MAX_FILE_SIZE_BYTES5) {
57292
57872
  return null;
57293
57873
  }
57294
- const content = fs30.readFileSync(filePath, "utf-8");
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 = path43.isAbsolute(file3) ? file3 : path43.join(workingDir, file3);
57305
- if (!fs30.existsSync(fullPath)) {
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 = fs30.readFileSync(filePath, "utf-8");
57427
- const ext = path43.extname(filePath).toLowerCase();
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 = path43.isAbsolute(file3) ? file3 : path43.join(workingDir, file3);
57454
- if (!fs30.existsSync(fullPath)) {
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 = path43.isAbsolute(file3) ? file3 : path43.join(workingDir, file3);
57488
- if (!fs30.existsSync(fullPath)) {
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 = fs30.statSync(fullPath);
58072
+ const stat2 = fs32.statSync(fullPath);
57493
58073
  if (stat2.size > MAX_FILE_SIZE_BYTES5) {
57494
58074
  continue;
57495
58075
  }
57496
- const content = fs30.readFileSync(fullPath, "utf-8");
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 = path43.basename(filePath);
57521
- const _ext = path43.extname(filePath).toLowerCase();
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(path44, glob) {
57603
- const normalizedPath = path44.replace(/\\/g, "/");
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(path44, glob) {
57635
- const normalizedPath = path44.replace(/\\/g, "/");
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 = path43.join(workingDir, "src");
57672
- if (fs30.existsSync(srcDir)) {
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 = path43.join(workingDir, dir);
57680
- if (fs30.existsSync(dirPath)) {
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 = path43.join(workingDir, "tests");
57687
- if (fs30.existsSync(testsDir)) {
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 = path43.join(workingDir, dir);
57695
- if (fs30.existsSync(dirPath) && dirPath !== testsDir) {
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 = fs30.readdirSync(dirPath, { withFileTypes: true });
58287
+ const entries = fs32.readdirSync(dirPath, { withFileTypes: true });
57708
58288
  for (const entry of entries) {
57709
- const fullPath = path43.join(dirPath, entry.name);
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 = path43.extname(entry.name).toLowerCase();
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 = fs30.readFileSync(fullPath, "utf-8");
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 fs31 from "fs";
57968
- import * as path44 from "path";
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 = fs31.statSync(filePath);
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 = fs31.openSync(filePath, "r");
59422
+ const fd = fs33.openSync(filePath, "r");
58843
59423
  const buffer = Buffer.alloc(8192);
58844
- const bytesRead = fs31.readSync(fd, buffer, 0, 8192, 0);
58845
- fs31.closeSync(fd);
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 = fs31.readFileSync(filePath, "utf-8");
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 = path44.isAbsolute(filePath) ? filePath : path44.resolve(directory, filePath);
58932
- if (!fs31.existsSync(resolvedPath)) {
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 = path45.win32.resolve(inputPath);
59131
- } else if (path45.isAbsolute(inputPath)) {
59132
- resolved = path45.resolve(inputPath);
59710
+ resolved = path47.win32.resolve(inputPath);
59711
+ } else if (path47.isAbsolute(inputPath)) {
59712
+ resolved = path47.resolve(inputPath);
59133
59713
  } else {
59134
- resolved = path45.resolve(baseDir, inputPath);
59714
+ resolved = path47.resolve(baseDir, inputPath);
59135
59715
  }
59136
- const workspaceResolved = path45.resolve(workspaceDir);
59716
+ const workspaceResolved = path47.resolve(workspaceDir);
59137
59717
  let relative5;
59138
59718
  if (isWinAbs) {
59139
- relative5 = path45.win32.relative(workspaceResolved, resolved);
59719
+ relative5 = path47.win32.relative(workspaceResolved, resolved);
59140
59720
  } else {
59141
- relative5 = path45.relative(workspaceResolved, resolved);
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 runWithTimeout(promise3, timeoutMs) {
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 runWithTimeout(runLint(linter, "check", directory), TOOL_TIMEOUT_MS);
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 = path45.join(workspaceDir, "node_modules", ".bin");
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 = path45.resolve(file3);
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 ? path45.join(binDir, "biome.EXE") : path45.join(binDir, "biome");
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 ? path45.join(binDir, "eslint.cmd") : path45.join(binDir, "eslint");
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 runWithTimeout(runSecretscanWithFiles(files, directory), TOOL_TIMEOUT_MS);
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 runWithTimeout(runSecretscan(directory), TOOL_TIMEOUT_MS);
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 = path45.resolve(file3);
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 = path45.extname(file3).toLowerCase();
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 = fs32.statSync(file3);
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 = fs32.readFileSync(file3);
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 runWithTimeout(sastScan({ changed_files: changedFiles, severity_threshold: severityThreshold }, directory, config3), TOOL_TIMEOUT_MS);
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 runWithTimeout(qualityBudget({ changed_files: changedFiles }, directory), TOOL_TIMEOUT_MS);
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(path45.resolve(directory, file3));
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 = path45.resolve(typedArgs.directory);
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 fs33 from "fs";
59802
- import * as path46 from "path";
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 = path46.join(dir, ".swarm", ".plan-write-marker");
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 fs33.promises.writeFile(markerPath, marker, "utf8");
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: path46.join(dir, ".swarm", "plan.json"),
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 fs34 from "fs";
59957
- import * as path47 from "path";
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 = fs34.readdirSync(dir, { withFileTypes: true });
61383
+ const entries = fs36.readdirSync(dir, { withFileTypes: true });
60804
61384
  for (const entry of entries) {
60805
- const fullPath = path47.join(dir, entry.name);
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(path47.relative(rootDir, fullPath));
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 = fs34.readdirSync(dir, { withFileTypes: true });
61410
+ const entries = fs36.readdirSync(dir, { withFileTypes: true });
60831
61411
  for (const entry of entries) {
60832
- const fullPath = path47.join(dir, entry.name);
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(path47.relative(workingDir, fullPath));
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 = path47.dirname(file3);
61429
+ let currentDir = path49.dirname(file3);
60850
61430
  while (true) {
60851
- if (currentDir && currentDir !== "." && currentDir !== path47.sep) {
60852
- dirs.add(path47.join(workingDir, currentDir));
60853
- const parent = path47.dirname(currentDir);
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
- fs34.mkdirSync(outputDir, { recursive: true });
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 = path47.isAbsolute(relativeOutputDir) ? relativeOutputDir : path47.join(workingDir, relativeOutputDir);
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 = path47.isAbsolute(manifestFile) ? manifestFile : path47.join(workingDir, manifestFile);
60961
- if (!fs34.existsSync(fullPath)) {
61540
+ const fullPath = path49.isAbsolute(manifestFile) ? manifestFile : path49.join(workingDir, manifestFile);
61541
+ if (!fs36.existsSync(fullPath)) {
60962
61542
  continue;
60963
61543
  }
60964
- const content = fs34.readFileSync(fullPath, "utf-8");
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 = path47.join(outputDir, filename);
60978
- fs34.writeFileSync(outputPath, bomJson, "utf-8");
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 fs35 from "fs";
61021
- import * as path48 from "path";
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 = path48.resolve(cwd, specFileArg);
61054
- const normalizedCwd = cwd.endsWith(path48.sep) ? cwd : cwd + path48.sep;
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 = path48.extname(resolvedPath).toLowerCase();
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 = fs35.statSync(resolvedPath);
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 (!fs35.existsSync(resolvedPath)) {
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 = path48.resolve(cwd, candidate);
61073
- if (fs35.existsSync(candidatePath)) {
61074
- const stats = fs35.statSync(candidatePath);
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 = fs35.readFileSync(specFile, "utf-8");
61084
- const ext = path48.extname(specFile).toLowerCase();
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 = fs35.readdirSync(dir, { withFileTypes: true });
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 = path48.join(dir, entry.name);
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 = path48.extname(entry.name).toLowerCase();
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 = fs35.readFileSync(filePath, "utf-8");
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 fs36 from "fs";
61340
- import * as path49 from "path";
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 = path49.resolve(workspace, filePath);
61370
- const realWorkspace = fs36.realpathSync(workspace);
61371
- const realResolvedPath = fs36.realpathSync(resolvedPath);
61372
- const relativePath = path49.relative(realWorkspace, realResolvedPath);
61373
- if (relativePath.startsWith("..") || path49.isAbsolute(relativePath)) {
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 = path49.join(cwd, filePath);
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 = fs36.statSync(fullPath);
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 = fs36.readFileSync(fullPath, "utf-8");
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 = path49.join(cwd, filePath);
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 = fs36.statSync(fullPath);
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 = fs36.readFileSync(fullPath, "utf-8");
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 = path49.extname(file3);
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 fs37 from "fs";
61692
- import * as path50 from "path";
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 = path50.resolve(paths);
61764
- const normalizedCwd = path50.resolve(cwd);
61765
- const normalizedResolved = path50.resolve(resolvedPath);
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 = path50.extname(filePath).toLowerCase();
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 = fs37.readdirSync(dir);
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 = path50.join(dir, entry);
62376
+ const fullPath = path52.join(dir, entry);
61797
62377
  let stat2;
61798
62378
  try {
61799
- stat2 = fs37.statSync(fullPath);
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 (!fs37.existsSync(scanPath)) {
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 = fs37.statSync(scanPath);
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: ${path50.extname(scanPath)}`,
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 = fs37.statSync(filePath);
62501
+ const fileStat = fs39.statSync(filePath);
61922
62502
  if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
61923
62503
  continue;
61924
62504
  }
61925
- const content = fs37.readFileSync(filePath, "utf-8");
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 = path51.basename(file3);
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 = path51.join(resolvedDir2, ".swarm", "plan.json");
62013
- const planRaw = fs38.readFileSync(planPath, "utf-8");
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 = path51.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
62033
- const raw = fs38.readFileSync(evidencePath, "utf-8");
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 = path51.join(resolvedDir2, ".swarm", "plan.json");
62074
- const planRaw = fs38.readFileSync(planPath, "utf-8");
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 = path51.normalize(args2.working_directory);
62245
- const pathParts = normalizedDir.split(path51.sep);
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 = path51.resolve(normalizedDir);
62932
+ const resolvedDir = path54.resolve(normalizedDir);
62256
62933
  try {
62257
- const realPath = fs38.realpathSync(resolvedDir);
62258
- const planPath = path51.join(realPath, ".swarm", "plan.json");
62259
- if (!fs38.existsSync(planPath)) {
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 = checkReviewerGate(args2.task_id, directory);
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 = path52.resolve(ctx.directory, ".swarm");
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 = [