opencode-swarm 6.33.0 → 6.33.2

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
@@ -14146,7 +14146,7 @@ var init_zod = __esm(() => {
14146
14146
  });
14147
14147
 
14148
14148
  // src/config/evidence-schema.ts
14149
- var EVIDENCE_MAX_JSON_BYTES, EVIDENCE_MAX_PATCH_BYTES, EVIDENCE_MAX_TASK_BYTES, EvidenceTypeSchema, EvidenceVerdictSchema, BaseEvidenceSchema, ReviewEvidenceSchema, TestEvidenceSchema, DiffEvidenceSchema, ApprovalEvidenceSchema, NoteEvidenceSchema, RetrospectiveEvidenceSchema, SyntaxEvidenceSchema, PlaceholderEvidenceSchema, SastEvidenceSchema, SbomEvidenceSchema, BuildEvidenceSchema, QualityBudgetEvidenceSchema, EvidenceSchema, EvidenceBundleSchema;
14149
+ var EVIDENCE_MAX_JSON_BYTES, EVIDENCE_MAX_PATCH_BYTES, EVIDENCE_MAX_TASK_BYTES, EvidenceTypeSchema, EvidenceVerdictSchema, BaseEvidenceSchema, ReviewEvidenceSchema, TestEvidenceSchema, DiffEvidenceSchema, ApprovalEvidenceSchema, NoteEvidenceSchema, RetrospectiveEvidenceSchema, SyntaxEvidenceSchema, PlaceholderEvidenceSchema, SastEvidenceSchema, SbomEvidenceSchema, BuildEvidenceSchema, QualityBudgetEvidenceSchema, SecretscanEvidenceSchema, EvidenceSchema, EvidenceBundleSchema;
14150
14150
  var init_evidence_schema = __esm(() => {
14151
14151
  init_zod();
14152
14152
  EVIDENCE_MAX_JSON_BYTES = 500 * 1024;
@@ -14164,7 +14164,8 @@ var init_evidence_schema = __esm(() => {
14164
14164
  "sast",
14165
14165
  "sbom",
14166
14166
  "build",
14167
- "quality_budget"
14167
+ "quality_budget",
14168
+ "secretscan"
14168
14169
  ]);
14169
14170
  EvidenceVerdictSchema = exports_external.enum([
14170
14171
  "pass",
@@ -14245,7 +14246,14 @@ var init_evidence_schema = __esm(() => {
14245
14246
  approach: exports_external.string().min(1),
14246
14247
  result: exports_external.enum(["success", "failure", "partial"]),
14247
14248
  abandoned_reason: exports_external.string().optional()
14248
- })).max(10).default([])
14249
+ })).max(10).default([]),
14250
+ error_taxonomy: exports_external.array(exports_external.enum([
14251
+ "planning_error",
14252
+ "interface_mismatch",
14253
+ "logic_error",
14254
+ "scope_creep",
14255
+ "gate_evasion"
14256
+ ])).default([])
14249
14257
  });
14250
14258
  SyntaxEvidenceSchema = BaseEvidenceSchema.extend({
14251
14259
  type: exports_external.literal("syntax"),
@@ -14356,6 +14364,13 @@ var init_evidence_schema = __esm(() => {
14356
14364
  })).default([]),
14357
14365
  files_analyzed: exports_external.array(exports_external.string())
14358
14366
  });
14367
+ SecretscanEvidenceSchema = BaseEvidenceSchema.extend({
14368
+ type: exports_external.literal("secretscan"),
14369
+ findings_count: exports_external.number().int().min(0).default(0),
14370
+ scan_directory: exports_external.string().optional(),
14371
+ files_scanned: exports_external.number().int().min(0).default(0),
14372
+ skipped_files: exports_external.number().int().min(0).default(0)
14373
+ });
14359
14374
  EvidenceSchema = exports_external.discriminatedUnion("type", [
14360
14375
  ReviewEvidenceSchema,
14361
14376
  TestEvidenceSchema,
@@ -14368,7 +14383,8 @@ var init_evidence_schema = __esm(() => {
14368
14383
  SastEvidenceSchema,
14369
14384
  SbomEvidenceSchema,
14370
14385
  BuildEvidenceSchema,
14371
- QualityBudgetEvidenceSchema
14386
+ QualityBudgetEvidenceSchema,
14387
+ SecretscanEvidenceSchema
14372
14388
  ]);
14373
14389
  EvidenceBundleSchema = exports_external.object({
14374
14390
  schema_version: exports_external.literal("1.0.0"),
@@ -14458,7 +14474,8 @@ var init_schema = __esm(() => {
14458
14474
  AgentOverrideConfigSchema = exports_external.object({
14459
14475
  model: exports_external.string().optional(),
14460
14476
  temperature: exports_external.number().min(0).max(2).optional(),
14461
- disabled: exports_external.boolean().optional()
14477
+ disabled: exports_external.boolean().optional(),
14478
+ fallback_models: exports_external.array(exports_external.string()).max(3).optional()
14462
14479
  });
14463
14480
  SwarmConfigSchema = exports_external.object({
14464
14481
  name: exports_external.string().optional(),
@@ -14798,6 +14815,8 @@ var init_schema = __esm(() => {
14798
14815
  max_consecutive_errors: exports_external.number().min(2).max(20).default(5),
14799
14816
  warning_threshold: exports_external.number().min(0.1).max(0.9).default(0.75),
14800
14817
  idle_timeout_minutes: exports_external.number().min(5).max(240).default(60),
14818
+ no_op_warning_threshold: exports_external.number().min(1).max(100).default(15),
14819
+ max_coder_revisions: exports_external.number().int().min(1).max(20).default(5),
14801
14820
  qa_gates: exports_external.object({
14802
14821
  required_tools: exports_external.array(exports_external.string().min(1)).default([
14803
14822
  "diff",
@@ -14913,6 +14932,7 @@ var init_schema = __esm(() => {
14913
14932
  pipeline: PipelineConfigSchema.optional(),
14914
14933
  phase_complete: PhaseCompleteConfigSchema.optional(),
14915
14934
  qa_retry_limit: exports_external.number().min(1).max(10).default(3),
14935
+ execution_mode: exports_external.enum(["strict", "balanced", "fast"]).default("balanced"),
14916
14936
  inject_phase_reminders: exports_external.boolean().default(true),
14917
14937
  hooks: HooksConfigSchema.optional(),
14918
14938
  gates: GateConfigSchema.optional(),
@@ -15398,6 +15418,9 @@ import * as path4 from "path";
15398
15418
  function isValidEvidenceType(type) {
15399
15419
  return VALID_EVIDENCE_TYPES.includes(type);
15400
15420
  }
15421
+ function isSecretscanEvidence(evidence) {
15422
+ return evidence.type === "secretscan";
15423
+ }
15401
15424
  function sanitizeTaskId(taskId) {
15402
15425
  if (!taskId || taskId.length === 0) {
15403
15426
  throw new Error("Invalid task ID: empty string");
@@ -15650,11 +15673,12 @@ var init_manager = __esm(() => {
15650
15673
  "sast",
15651
15674
  "sbom",
15652
15675
  "build",
15653
- "quality_budget"
15676
+ "quality_budget",
15677
+ "secretscan"
15654
15678
  ];
15655
15679
  TASK_ID_REGEX = /^\d+\.\d+(\.\d+)*$/;
15656
15680
  RETRO_TASK_ID_REGEX = /^retro-\d+$/;
15657
- INTERNAL_TOOL_ID_REGEX = /^(?:sast_scan|quality_budget|syntax_check|placeholder_scan|sbom_generate|build)$/;
15681
+ INTERNAL_TOOL_ID_REGEX = /^(?:sast_scan|quality_budget|syntax_check|placeholder_scan|sbom_generate|build|secretscan)$/;
15658
15682
  LEGACY_TASK_COMPLEXITY_MAP = {
15659
15683
  low: "simple",
15660
15684
  medium: "moderate",
@@ -36248,7 +36272,7 @@ __export(exports_gate_evidence, {
36248
36272
  deriveRequiredGates: () => deriveRequiredGates,
36249
36273
  DEFAULT_REQUIRED_GATES: () => DEFAULT_REQUIRED_GATES
36250
36274
  });
36251
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync14, renameSync as renameSync8, unlinkSync as unlinkSync4 } from "fs";
36275
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync14, renameSync as renameSync9, unlinkSync as unlinkSync5 } from "fs";
36252
36276
  import * as path30 from "path";
36253
36277
  function isValidTaskId2(taskId) {
36254
36278
  if (!taskId)
@@ -36313,10 +36337,10 @@ async function atomicWrite(targetPath, content) {
36313
36337
  const tempPath = `${targetPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
36314
36338
  try {
36315
36339
  await Bun.write(tempPath, content);
36316
- renameSync8(tempPath, targetPath);
36340
+ renameSync9(tempPath, targetPath);
36317
36341
  } finally {
36318
36342
  try {
36319
- unlinkSync4(tempPath);
36343
+ unlinkSync5(tempPath);
36320
36344
  } catch {}
36321
36345
  }
36322
36346
  }
@@ -37908,11 +37932,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
37908
37932
  throw toThrow;
37909
37933
  }, "quit_");
37910
37934
  var scriptDirectory = "";
37911
- function locateFile(path47) {
37935
+ function locateFile(path48) {
37912
37936
  if (Module["locateFile"]) {
37913
- return Module["locateFile"](path47, scriptDirectory);
37937
+ return Module["locateFile"](path48, scriptDirectory);
37914
37938
  }
37915
- return scriptDirectory + path47;
37939
+ return scriptDirectory + path48;
37916
37940
  }
37917
37941
  __name(locateFile, "locateFile");
37918
37942
  var readAsync, readBinary;
@@ -39660,7 +39684,7 @@ var init_runtime = __esm(() => {
39660
39684
  });
39661
39685
 
39662
39686
  // src/index.ts
39663
- import * as path57 from "path";
39687
+ import * as path58 from "path";
39664
39688
 
39665
39689
  // src/agents/index.ts
39666
39690
  init_config();
@@ -39727,6 +39751,10 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
39727
39751
  scopeViolationDetected: false,
39728
39752
  modifiedFilesThisCoderTask: [],
39729
39753
  turboMode: false,
39754
+ model_fallback_index: 0,
39755
+ modelFallbackExhausted: false,
39756
+ coderRevisions: 0,
39757
+ revisionLimitHit: false,
39730
39758
  loopDetectionWindow: [],
39731
39759
  pendingAdvisoryMessages: []
39732
39760
  };
@@ -39835,6 +39863,12 @@ function ensureAgentSession(sessionId, agentName, directory) {
39835
39863
  if (session.turboMode === undefined) {
39836
39864
  session.turboMode = false;
39837
39865
  }
39866
+ if (session.model_fallback_index === undefined) {
39867
+ session.model_fallback_index = 0;
39868
+ }
39869
+ if (session.modelFallbackExhausted === undefined) {
39870
+ session.modelFallbackExhausted = false;
39871
+ }
39838
39872
  if (session.loopDetectionWindow === undefined) {
39839
39873
  session.loopDetectionWindow = [];
39840
39874
  }
@@ -44110,14 +44144,18 @@ async function readCuratorSummary(directory) {
44110
44144
  }
44111
44145
  return parsed;
44112
44146
  } catch {
44113
- console.warn("Failed to parse curator-summary.json: invalid JSON");
44147
+ if (process.env.DEBUG_SWARM) {
44148
+ console.warn("Failed to parse curator-summary.json: invalid JSON");
44149
+ }
44114
44150
  return null;
44115
44151
  }
44116
44152
  }
44117
44153
  async function writeCuratorSummary(directory, summary) {
44118
44154
  const resolvedPath = validateSwarmPath(directory, "curator-summary.json");
44119
44155
  fs7.mkdirSync(path12.dirname(resolvedPath), { recursive: true });
44120
- await Bun.write(resolvedPath, JSON.stringify(summary, null, 2));
44156
+ const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
44157
+ await Bun.write(tempPath, JSON.stringify(summary, null, 2));
44158
+ fs7.renameSync(tempPath, resolvedPath);
44121
44159
  }
44122
44160
  function normalizeAgentName(name2) {
44123
44161
  return name2.toLowerCase().replace(/^(mega|paid|local|lowtier|modelrelay)_/, "");
@@ -44141,7 +44179,9 @@ function filterPhaseEvents(eventsJsonl, phase, sinceTimestamp) {
44141
44179
  }
44142
44180
  }
44143
44181
  } catch {
44144
- console.warn("filterPhaseEvents: skipping malformed line");
44182
+ if (process.env.DEBUG_SWARM) {
44183
+ console.warn("filterPhaseEvents: skipping malformed line");
44184
+ }
44145
44185
  }
44146
44186
  }
44147
44187
  return filtered;
@@ -46471,7 +46511,7 @@ async function handleExportCommand(directory, _args) {
46471
46511
  // src/commands/handoff.ts
46472
46512
  init_utils2();
46473
46513
  import crypto3 from "crypto";
46474
- import { renameSync as renameSync5 } from "fs";
46514
+ import { renameSync as renameSync6 } from "fs";
46475
46515
 
46476
46516
  // src/services/handoff-service.ts
46477
46517
  init_utils2();
@@ -46772,8 +46812,10 @@ function formatHandoffMarkdown(data) {
46772
46812
 
46773
46813
  // src/session/snapshot-writer.ts
46774
46814
  init_utils2();
46775
- import { mkdirSync as mkdirSync7, renameSync as renameSync4 } from "fs";
46815
+ import { mkdirSync as mkdirSync7, renameSync as renameSync5 } from "fs";
46776
46816
  import * as path19 from "path";
46817
+ var pendingWrite = null;
46818
+ var lastWritePromise = Promise.resolve();
46777
46819
  function serializeAgentSession(s) {
46778
46820
  const gateLog = {};
46779
46821
  const rawGateLog = s.gateLog ?? new Map;
@@ -46835,7 +46877,11 @@ function serializeAgentSession(s) {
46835
46877
  taskWorkflowStates: Object.fromEntries(s.taskWorkflowStates ?? new Map),
46836
46878
  ...s.scopeViolationDetected !== undefined && {
46837
46879
  scopeViolationDetected: s.scopeViolationDetected
46838
- }
46880
+ },
46881
+ model_fallback_index: s.model_fallback_index ?? 0,
46882
+ modelFallbackExhausted: s.modelFallbackExhausted ?? false,
46883
+ coderRevisions: s.coderRevisions ?? 0,
46884
+ revisionLimitHit: s.revisionLimitHit ?? false
46839
46885
  };
46840
46886
  }
46841
46887
  async function writeSnapshot(directory, state) {
@@ -46857,15 +46903,31 @@ async function writeSnapshot(directory, state) {
46857
46903
  mkdirSync7(dir, { recursive: true });
46858
46904
  const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
46859
46905
  await Bun.write(tempPath, content);
46860
- renameSync4(tempPath, resolvedPath);
46861
- } catch {}
46906
+ renameSync5(tempPath, resolvedPath);
46907
+ } catch (error93) {
46908
+ if (process.env.DEBUG_SWARM) {
46909
+ console.warn("[snapshot-writer] write failed:", error93 instanceof Error ? error93.message : String(error93));
46910
+ }
46911
+ }
46862
46912
  }
46863
46913
  function createSnapshotWriterHook(directory) {
46864
46914
  return async (_input, _output) => {
46865
- try {
46866
- await writeSnapshot(directory, swarmState);
46867
- } catch {}
46868
- };
46915
+ if (pendingWrite)
46916
+ clearTimeout(pendingWrite);
46917
+ pendingWrite = setTimeout(() => {
46918
+ pendingWrite = null;
46919
+ lastWritePromise = writeSnapshot(directory, swarmState).catch(() => {});
46920
+ }, 2000);
46921
+ };
46922
+ }
46923
+ async function flushPendingSnapshot(directory) {
46924
+ if (pendingWrite) {
46925
+ clearTimeout(pendingWrite);
46926
+ pendingWrite = null;
46927
+ await writeSnapshot(directory, swarmState).catch(() => {});
46928
+ } else {
46929
+ await lastWritePromise;
46930
+ }
46869
46931
  }
46870
46932
 
46871
46933
  // src/commands/handoff.ts
@@ -46875,8 +46937,9 @@ async function handleHandoffCommand(directory, _args) {
46875
46937
  const resolvedPath = validateSwarmPath(directory, "handoff.md");
46876
46938
  const tempPath = `${resolvedPath}.tmp.${crypto3.randomUUID()}`;
46877
46939
  await Bun.write(tempPath, markdown);
46878
- renameSync5(tempPath, resolvedPath);
46940
+ renameSync6(tempPath, resolvedPath);
46879
46941
  await writeSnapshot(directory, swarmState);
46942
+ await flushPendingSnapshot(directory);
46880
46943
  return `## Handoff Brief Written
46881
46944
 
46882
46945
  Brief written to \`.swarm/handoff.md\`.
@@ -47586,10 +47649,41 @@ async function handleResetCommand(directory, args2) {
47586
47649
  `);
47587
47650
  }
47588
47651
 
47652
+ // src/commands/reset-session.ts
47653
+ init_utils2();
47654
+ import * as fs15 from "fs";
47655
+ async function handleResetSessionCommand(directory, _args) {
47656
+ const results = [];
47657
+ try {
47658
+ const statePath = validateSwarmPath(directory, "session/state.json");
47659
+ if (fs15.existsSync(statePath)) {
47660
+ fs15.unlinkSync(statePath);
47661
+ results.push("\u2705 Deleted .swarm/session/state.json");
47662
+ } else {
47663
+ results.push("\u23ED\uFE0F state.json not found (already clean)");
47664
+ }
47665
+ } catch {
47666
+ results.push("\u274C Failed to delete state.json");
47667
+ }
47668
+ const sessionCount = swarmState.agentSessions.size;
47669
+ swarmState.agentSessions.clear();
47670
+ results.push(`\u2705 Cleared ${sessionCount} in-memory agent session(s)`);
47671
+ return [
47672
+ "## Session State Reset",
47673
+ "",
47674
+ ...results,
47675
+ "",
47676
+ "Session state cleared. Plan, evidence, and knowledge preserved.",
47677
+ "",
47678
+ "**Next step:** Start a new OpenCode session. The plugin will initialize fresh session state on startup."
47679
+ ].join(`
47680
+ `);
47681
+ }
47682
+
47589
47683
  // src/summaries/manager.ts
47590
47684
  init_utils2();
47591
47685
  init_utils();
47592
- import { mkdirSync as mkdirSync8, readdirSync as readdirSync7, renameSync as renameSync6, rmSync as rmSync3, statSync as statSync7 } from "fs";
47686
+ import { mkdirSync as mkdirSync8, readdirSync as readdirSync7, renameSync as renameSync7, rmSync as rmSync3, statSync as statSync7 } from "fs";
47593
47687
  import * as path26 from "path";
47594
47688
  var SUMMARY_ID_REGEX = /^S\d+$/;
47595
47689
  function sanitizeSummaryId(id) {
@@ -47633,7 +47727,7 @@ async function storeSummary(directory, id, fullOutput, summaryText, maxStoredByt
47633
47727
  const tempPath = path26.join(summaryDir, `${sanitizedId}.json.tmp.${Date.now()}.${process.pid}`);
47634
47728
  try {
47635
47729
  await Bun.write(tempPath, entryJson);
47636
- renameSync6(tempPath, summaryPath);
47730
+ renameSync7(tempPath, summaryPath);
47637
47731
  } catch (error93) {
47638
47732
  try {
47639
47733
  rmSync3(tempPath, { force: true });
@@ -47696,18 +47790,18 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
47696
47790
 
47697
47791
  // src/commands/rollback.ts
47698
47792
  init_utils2();
47699
- import * as fs15 from "fs";
47793
+ import * as fs16 from "fs";
47700
47794
  import * as path27 from "path";
47701
47795
  async function handleRollbackCommand(directory, args2) {
47702
47796
  const phaseArg = args2[0];
47703
47797
  if (!phaseArg) {
47704
47798
  const manifestPath2 = validateSwarmPath(directory, "checkpoints/manifest.json");
47705
- if (!fs15.existsSync(manifestPath2)) {
47799
+ if (!fs16.existsSync(manifestPath2)) {
47706
47800
  return "No checkpoints found. Use `/swarm checkpoint` to create checkpoints.";
47707
47801
  }
47708
47802
  let manifest2;
47709
47803
  try {
47710
- manifest2 = JSON.parse(fs15.readFileSync(manifestPath2, "utf-8"));
47804
+ manifest2 = JSON.parse(fs16.readFileSync(manifestPath2, "utf-8"));
47711
47805
  } catch {
47712
47806
  return "Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.";
47713
47807
  }
@@ -47729,12 +47823,12 @@ async function handleRollbackCommand(directory, args2) {
47729
47823
  return "Error: Phase number must be a positive integer.";
47730
47824
  }
47731
47825
  const manifestPath = validateSwarmPath(directory, "checkpoints/manifest.json");
47732
- if (!fs15.existsSync(manifestPath)) {
47826
+ if (!fs16.existsSync(manifestPath)) {
47733
47827
  return `Error: No checkpoints found. Cannot rollback to phase ${targetPhase}.`;
47734
47828
  }
47735
47829
  let manifest;
47736
47830
  try {
47737
- manifest = JSON.parse(fs15.readFileSync(manifestPath, "utf-8"));
47831
+ manifest = JSON.parse(fs16.readFileSync(manifestPath, "utf-8"));
47738
47832
  } catch {
47739
47833
  return `Error: Checkpoint manifest is corrupted. Delete .swarm/checkpoints/manifest.json and re-checkpoint.`;
47740
47834
  }
@@ -47744,10 +47838,10 @@ async function handleRollbackCommand(directory, args2) {
47744
47838
  return `Error: Checkpoint for phase ${targetPhase} not found. Available phases: ${available}`;
47745
47839
  }
47746
47840
  const checkpointDir = validateSwarmPath(directory, `checkpoints/phase-${targetPhase}`);
47747
- if (!fs15.existsSync(checkpointDir)) {
47841
+ if (!fs16.existsSync(checkpointDir)) {
47748
47842
  return `Error: Checkpoint directory for phase ${targetPhase} does not exist.`;
47749
47843
  }
47750
- const checkpointFiles = fs15.readdirSync(checkpointDir);
47844
+ const checkpointFiles = fs16.readdirSync(checkpointDir);
47751
47845
  if (checkpointFiles.length === 0) {
47752
47846
  return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
47753
47847
  }
@@ -47758,7 +47852,7 @@ async function handleRollbackCommand(directory, args2) {
47758
47852
  const src = path27.join(checkpointDir, file3);
47759
47853
  const dest = path27.join(swarmDir, file3);
47760
47854
  try {
47761
- fs15.cpSync(src, dest, { recursive: true, force: true });
47855
+ fs16.cpSync(src, dest, { recursive: true, force: true });
47762
47856
  successes.push(file3);
47763
47857
  } catch (error93) {
47764
47858
  failures.push({ file: file3, error: error93.message });
@@ -47775,7 +47869,7 @@ async function handleRollbackCommand(directory, args2) {
47775
47869
  timestamp: new Date().toISOString()
47776
47870
  };
47777
47871
  try {
47778
- fs15.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
47872
+ fs16.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
47779
47873
  `);
47780
47874
  } catch (error93) {
47781
47875
  console.error("Failed to write rollback event:", error93);
@@ -47818,11 +47912,11 @@ async function handleSimulateCommand(directory, args2) {
47818
47912
  ];
47819
47913
  const report = reportLines.filter(Boolean).join(`
47820
47914
  `);
47821
- const fs16 = await import("fs/promises");
47915
+ const fs17 = await import("fs/promises");
47822
47916
  const path28 = await import("path");
47823
47917
  const reportPath = path28.join(directory, ".swarm", "simulate-report.md");
47824
- await fs16.mkdir(path28.dirname(reportPath), { recursive: true });
47825
- await fs16.writeFile(reportPath, report, "utf-8");
47918
+ await fs17.mkdir(path28.dirname(reportPath), { recursive: true });
47919
+ await fs17.writeFile(reportPath, report, "utf-8");
47826
47920
  return `${darkMatterPairs.length} hidden coupling pairs detected`;
47827
47921
  }
47828
47922
 
@@ -48179,7 +48273,7 @@ init_utils2();
48179
48273
  init_manager2();
48180
48274
 
48181
48275
  // src/services/compaction-service.ts
48182
- import * as fs16 from "fs";
48276
+ import * as fs17 from "fs";
48183
48277
  import * as path28 from "path";
48184
48278
  function makeInitialState() {
48185
48279
  return {
@@ -48201,7 +48295,7 @@ function appendSnapshot(directory, tier, budgetPct, message) {
48201
48295
  ## [${tier.toUpperCase()}] ${timestamp} \u2014 ${budgetPct.toFixed(1)}% used
48202
48296
  ${message}
48203
48297
  `;
48204
- fs16.appendFileSync(snapshotPath, entry, "utf-8");
48298
+ fs17.appendFileSync(snapshotPath, entry, "utf-8");
48205
48299
  } catch {}
48206
48300
  }
48207
48301
  function buildObservationMessage(budgetPct) {
@@ -48813,8 +48907,46 @@ async function executeWriteRetro(args2, directory) {
48813
48907
  top_rejection_reasons: args2.top_rejection_reasons ?? [],
48814
48908
  lessons_learned: (args2.lessons_learned ?? []).slice(0, 5),
48815
48909
  user_directives: [],
48816
- approaches_tried: []
48910
+ approaches_tried: [],
48911
+ error_taxonomy: []
48817
48912
  };
48913
+ const taxonomy = [];
48914
+ try {
48915
+ for (const taskSuffix of ["1", "2", "3", "4", "5"]) {
48916
+ const phaseTaskId = `${phase}.${taskSuffix}`;
48917
+ const result = await loadEvidence(directory, phaseTaskId);
48918
+ if (result.status !== "found")
48919
+ continue;
48920
+ const bundle = result.bundle;
48921
+ for (const entry of bundle.entries) {
48922
+ const e = entry;
48923
+ if (e.type === "review" && e.verdict === "fail") {
48924
+ const reasonParts = [];
48925
+ if (typeof e.summary === "string")
48926
+ reasonParts.push(e.summary);
48927
+ if (Array.isArray(e.issues)) {
48928
+ for (const iss of e.issues) {
48929
+ if (typeof iss.message === "string")
48930
+ reasonParts.push(iss.message);
48931
+ }
48932
+ }
48933
+ const reason = reasonParts.join(" ");
48934
+ if (/signature|type|contract|interface/i.test(reason)) {
48935
+ taxonomy.push("interface_mismatch");
48936
+ } else {
48937
+ taxonomy.push("logic_error");
48938
+ }
48939
+ } else if (e.type === "test" && e.verdict === "fail") {
48940
+ taxonomy.push("logic_error");
48941
+ } else if (e.agent === "scope_guard" && e.verdict === "fail") {
48942
+ taxonomy.push("scope_creep");
48943
+ } else if (e.agent === "loop_detector" && e.verdict === "fail") {
48944
+ taxonomy.push("gate_evasion");
48945
+ }
48946
+ }
48947
+ }
48948
+ } catch {}
48949
+ retroEntry.error_taxonomy = [...new Set(taxonomy)];
48818
48950
  try {
48819
48951
  await saveEvidence(directory, taskId, retroEntry);
48820
48952
  return JSON.stringify({
@@ -49029,6 +49161,10 @@ var COMMAND_REGISTRY = {
49029
49161
  handler: (ctx) => handleResetCommand(ctx.directory, ctx.args),
49030
49162
  description: "Clear swarm state files [--confirm]"
49031
49163
  },
49164
+ "reset-session": {
49165
+ handler: (ctx) => handleResetSessionCommand(ctx.directory, ctx.args),
49166
+ description: "Clear session state while preserving plan, evidence, and knowledge"
49167
+ },
49032
49168
  rollback: {
49033
49169
  handler: (ctx) => handleRollbackCommand(ctx.directory, ctx.args),
49034
49170
  description: "Restore swarm state to a checkpoint <phase>"
@@ -49133,7 +49269,7 @@ init_constants();
49133
49269
  init_schema();
49134
49270
 
49135
49271
  // src/hooks/agent-activity.ts
49136
- import { renameSync as renameSync7, unlinkSync as unlinkSync3 } from "fs";
49272
+ import { renameSync as renameSync8, unlinkSync as unlinkSync4 } from "fs";
49137
49273
  init_utils();
49138
49274
  init_utils2();
49139
49275
  function createAgentActivityHooks(config3, directory) {
@@ -49207,10 +49343,10 @@ async function doFlush(directory) {
49207
49343
  const tempPath = `${path29}.tmp`;
49208
49344
  try {
49209
49345
  await Bun.write(tempPath, updated);
49210
- renameSync7(tempPath, path29);
49346
+ renameSync8(tempPath, path29);
49211
49347
  } catch (writeError) {
49212
49348
  try {
49213
- unlinkSync3(tempPath);
49349
+ unlinkSync4(tempPath);
49214
49350
  } catch {}
49215
49351
  throw writeError;
49216
49352
  }
@@ -49256,7 +49392,7 @@ ${content.substring(endIndex + 1)}`;
49256
49392
  }
49257
49393
  // src/hooks/compaction-customizer.ts
49258
49394
  init_manager2();
49259
- import * as fs17 from "fs";
49395
+ import * as fs18 from "fs";
49260
49396
  import { join as join25 } from "path";
49261
49397
  init_utils2();
49262
49398
  function createCompactionCustomizerHook(config3, directory) {
@@ -49304,7 +49440,7 @@ function createCompactionCustomizerHook(config3, directory) {
49304
49440
  }
49305
49441
  try {
49306
49442
  const summariesDir = join25(directory, ".swarm", "summaries");
49307
- const files = await fs17.promises.readdir(summariesDir);
49443
+ const files = await fs18.promises.readdir(summariesDir);
49308
49444
  if (files.length > 0) {
49309
49445
  const count = files.length;
49310
49446
  output.context.push(`[CONTEXT OPTIMIZATION] Tool outputs from earlier in this session have been stored to disk. When compacting, replace any large tool output blocks (bash, test_runner, lint, diff results) with a one-line reference: "[Output stored \u2014 use /swarm retrieve to access full content]". Preserve the tool name, exit status, and any error messages. Discard raw output lines.`);
@@ -49789,7 +49925,7 @@ function maskToolOutput(msg, _threshold) {
49789
49925
  }
49790
49926
  // src/hooks/delegation-gate.ts
49791
49927
  init_schema();
49792
- import * as fs18 from "fs";
49928
+ import * as fs19 from "fs";
49793
49929
  import * as path31 from "path";
49794
49930
 
49795
49931
  // src/hooks/guardrails.ts
@@ -49841,6 +49977,7 @@ function detectLoop(sessionId, toolName, args2) {
49841
49977
 
49842
49978
  // src/hooks/guardrails.ts
49843
49979
  var storedInputArgs = new Map;
49980
+ var TRANSIENT_MODEL_ERROR_PATTERN = /rate.?limit|429|503|timeout|overloaded|model.?not.?found|temporarily unavailable|server error/i;
49844
49981
  function getStoredInputArgs(callID) {
49845
49982
  return storedInputArgs.get(callID);
49846
49983
  }
@@ -49850,6 +49987,8 @@ function setStoredInputArgs(callID, args2) {
49850
49987
  function deleteStoredInputArgs(callID) {
49851
49988
  storedInputArgs.delete(callID);
49852
49989
  }
49990
+ var toolCallsSinceLastWrite = new Map;
49991
+ var noOpWarningIssued = new Set;
49853
49992
  function extractPhaseNumber(phaseString) {
49854
49993
  if (!phaseString)
49855
49994
  return 1;
@@ -50016,6 +50155,9 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
50016
50155
  const coderSession = swarmState.agentSessions.get(input.sessionID);
50017
50156
  if (coderSession) {
50018
50157
  coderSession.modifiedFilesThisCoderTask = [];
50158
+ if (!coderSession.revisionLimitHit) {
50159
+ coderSession.coderRevisions = 0;
50160
+ }
50019
50161
  }
50020
50162
  }
50021
50163
  }
@@ -50024,7 +50166,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
50024
50166
  const loopResult = detectLoop(input.sessionID, input.tool, loopArgs);
50025
50167
  if (loopResult.count >= 5) {
50026
50168
  throw new Error(`CIRCUIT BREAKER: Delegation loop detected (${loopResult.count} identical patterns). Session paused. Ask the user for guidance.`);
50027
- } else if (loopResult.count === 3) {
50169
+ } else if (loopResult.count >= 3 && loopResult.count < 5) {
50028
50170
  const agentName2 = typeof loopArgs?.subagent_type === "string" ? loopArgs.subagent_type : "agent";
50029
50171
  const loopSession = swarmState.agentSessions.get(input.sessionID);
50030
50172
  if (loopSession) {
@@ -50355,6 +50497,16 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
50355
50497
  }
50356
50498
  if (delegation.isDelegation && delegation.targetAgent === "coder" && session.lastCoderDelegationTaskId) {
50357
50499
  session.currentTaskId = session.lastCoderDelegationTaskId;
50500
+ if (!session.revisionLimitHit) {
50501
+ session.coderRevisions++;
50502
+ const maxRevisions = cfg.max_coder_revisions ?? 5;
50503
+ if (session.coderRevisions >= maxRevisions) {
50504
+ session.revisionLimitHit = true;
50505
+ session.pendingAdvisoryMessages ??= [];
50506
+ session.pendingAdvisoryMessages.push(`CODER REVISION LIMIT: Agent has been revised ${session.coderRevisions} times ` + `(max: ${maxRevisions}) for task ${session.currentTaskId ?? "unknown"}. ` + `Escalate to user or consider a fundamentally different approach.`);
50507
+ swarmState.pendingEvents++;
50508
+ }
50509
+ }
50358
50510
  session.partialGateWarningsIssuedForTask?.delete(session.currentTaskId);
50359
50511
  if (session.declaredCoderScope !== null) {
50360
50512
  const undeclaredFiles = session.modifiedFilesThisCoderTask.map((f) => f.replace(/[\r\n\t]/g, "_")).filter((f) => !isInDeclaredScope(f, session.declaredCoderScope));
@@ -50367,15 +50519,45 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
50367
50519
  session.modifiedFilesThisCoderTask = [];
50368
50520
  }
50369
50521
  }
50522
+ const sessionId = input.sessionID;
50523
+ const normalizedToolName = input.tool.replace(/^[^:]+[:.]/, "");
50524
+ if (isWriteTool(normalizedToolName)) {
50525
+ toolCallsSinceLastWrite.set(sessionId, 0);
50526
+ } else {
50527
+ const count = (toolCallsSinceLastWrite.get(sessionId) ?? 0) + 1;
50528
+ toolCallsSinceLastWrite.set(sessionId, count);
50529
+ const threshold = cfg.no_op_warning_threshold ?? 15;
50530
+ if (count >= threshold && !noOpWarningIssued.has(sessionId) && session?.pendingAdvisoryMessages) {
50531
+ noOpWarningIssued.add(sessionId);
50532
+ session.pendingAdvisoryMessages.push(`WARNING: Agent has made ${count} tool calls with no file modifications. If you are stuck, use /swarm handoff to reset or /swarm turbo to reduce overhead.`);
50533
+ }
50534
+ }
50370
50535
  const window2 = getActiveWindow(input.sessionID);
50371
50536
  if (!window2)
50372
50537
  return;
50373
50538
  const hasError = output.output === null || output.output === undefined;
50374
50539
  if (hasError) {
50375
50540
  window2.consecutiveErrors++;
50541
+ if (session) {
50542
+ const outputStr = typeof output.output === "string" ? output.output : "";
50543
+ const errorContent = output.error ?? outputStr;
50544
+ if (typeof errorContent === "string" && TRANSIENT_MODEL_ERROR_PATTERN.test(errorContent) && !session.modelFallbackExhausted) {
50545
+ session.model_fallback_index++;
50546
+ session.modelFallbackExhausted = true;
50547
+ session.pendingAdvisoryMessages ??= [];
50548
+ session.pendingAdvisoryMessages.push(`MODEL FALLBACK: Transient model error detected (attempt ${session.model_fallback_index}). ` + `The agent model may be rate-limited, overloaded, or temporarily unavailable. ` + `Consider retrying with a fallback model or waiting before retrying.`);
50549
+ swarmState.pendingEvents++;
50550
+ }
50551
+ }
50376
50552
  } else {
50377
50553
  window2.consecutiveErrors = 0;
50378
50554
  window2.lastSuccessTimeMs = Date.now();
50555
+ if (session) {
50556
+ if (session.model_fallback_index > 0) {
50557
+ session.model_fallback_index = 0;
50558
+ session.modelFallbackExhausted = false;
50559
+ }
50560
+ }
50379
50561
  }
50380
50562
  },
50381
50563
  messagesTransform: async (_input, output) => {
@@ -50652,6 +50834,7 @@ function hashArgs(args2) {
50652
50834
 
50653
50835
  // src/hooks/delegation-gate.ts
50654
50836
  init_utils2();
50837
+ var pendingCoderScopeByTaskId = new Map;
50655
50838
  function extractTaskLine(text) {
50656
50839
  const match = text.match(/TASK:\s*(.+?)(?:\n|$)/i);
50657
50840
  return match ? match[1].trim() : null;
@@ -50670,7 +50853,7 @@ function extractPlanTaskId(text) {
50670
50853
  function getSeedTaskId(session) {
50671
50854
  return session.currentTaskId ?? session.lastCoderDelegationTaskId;
50672
50855
  }
50673
- function getEvidenceTaskId(session, directory) {
50856
+ async function getEvidenceTaskId(session, directory) {
50674
50857
  const primary = session.currentTaskId ?? session.lastCoderDelegationTaskId;
50675
50858
  if (primary)
50676
50859
  return primary;
@@ -50687,7 +50870,7 @@ function getEvidenceTaskId(session, directory) {
50687
50870
  if (!resolvedPlanPath.startsWith(resolvedDirectory + path31.sep) && resolvedPlanPath !== resolvedDirectory) {
50688
50871
  return null;
50689
50872
  }
50690
- const planContent = fs18.readFileSync(resolvedPlanPath, "utf-8");
50873
+ const planContent = await fs19.promises.readFile(resolvedPlanPath, "utf-8");
50691
50874
  const plan = JSON.parse(planContent);
50692
50875
  if (!plan || !Array.isArray(plan.phases)) {
50693
50876
  return null;
@@ -50807,7 +50990,7 @@ function createDelegationGateHook(config3, directory) {
50807
50990
  }
50808
50991
  if (typeof subagentType === "string") {
50809
50992
  const rawTaskId = directArgs?.task_id;
50810
- const evidenceTaskId = typeof rawTaskId === "string" && rawTaskId.length <= 20 && /^\d+\.\d+$/.test(rawTaskId.trim()) ? rawTaskId.trim() : getEvidenceTaskId(session, directory);
50993
+ const evidenceTaskId = typeof rawTaskId === "string" && rawTaskId.length <= 20 && /^\d+\.\d+$/.test(rawTaskId.trim()) ? rawTaskId.trim() : await getEvidenceTaskId(session, directory);
50811
50994
  if (evidenceTaskId) {
50812
50995
  try {
50813
50996
  const turbo = hasActiveTurboMode();
@@ -50927,7 +51110,7 @@ function createDelegationGateHook(config3, directory) {
50927
51110
  }
50928
51111
  {
50929
51112
  const rawTaskId = directArgs?.task_id;
50930
- const evidenceTaskId = typeof rawTaskId === "string" && rawTaskId.length <= 20 && /^\d+\.\d+$/.test(rawTaskId.trim()) ? rawTaskId.trim() : getEvidenceTaskId(session, directory);
51113
+ const evidenceTaskId = typeof rawTaskId === "string" && rawTaskId.length <= 20 && /^\d+\.\d+$/.test(rawTaskId.trim()) ? rawTaskId.trim() : await getEvidenceTaskId(session, directory);
50931
51114
  if (evidenceTaskId) {
50932
51115
  try {
50933
51116
  const turbo = hasActiveTurboMode();
@@ -51024,7 +51207,7 @@ ${trimComment}${after}`;
51024
51207
  const currentTaskId = planTaskId ?? taskIdFromLine;
51025
51208
  const coderDelegationPattern = /(?:^|\n)\s*(?:\w+_)?coder\s*\n\s*TASK:/i;
51026
51209
  const isCoderDelegation = coderDelegationPattern.test(text);
51027
- const priorCoderTaskId = sessionID ? ensureAgentSession(sessionID).lastCoderDelegationTaskId ?? null : null;
51210
+ const priorCoderTaskId = sessionID ? swarmState.agentSessions.get(sessionID)?.lastCoderDelegationTaskId ?? null : null;
51028
51211
  if (sessionID && isCoderDelegation && currentTaskId) {
51029
51212
  const session = ensureAgentSession(sessionID);
51030
51213
  session.lastCoderDelegationTaskId = currentTaskId;
@@ -51037,6 +51220,11 @@ ${trimComment}${after}`;
51037
51220
  }
51038
51221
  }
51039
51222
  session.declaredCoderScope = declaredFiles.length > 0 ? declaredFiles : null;
51223
+ if (declaredFiles.length > 0 && currentTaskId) {
51224
+ pendingCoderScopeByTaskId.set(currentTaskId, declaredFiles);
51225
+ } else {
51226
+ pendingCoderScopeByTaskId.delete(currentTaskId);
51227
+ }
51040
51228
  try {
51041
51229
  advanceTaskState(session, currentTaskId, "coder_delegated");
51042
51230
  } catch (err2) {
@@ -51164,7 +51352,7 @@ ${warningLines.join(`
51164
51352
  }
51165
51353
  // src/hooks/delegation-sanitizer.ts
51166
51354
  init_utils2();
51167
- import * as fs19 from "fs";
51355
+ import * as fs20 from "fs";
51168
51356
  var SANITIZATION_PATTERNS = [
51169
51357
  /\b\d+(st|nd|rd|th)\s+(attempt|try|time)\b/gi,
51170
51358
  /\b(5th|fifth|final|last)\s+attempt\b/gi,
@@ -51235,7 +51423,7 @@ function createDelegationSanitizerHook(directory) {
51235
51423
  stripped_patterns: result.stripped,
51236
51424
  timestamp: new Date().toISOString()
51237
51425
  };
51238
- fs19.appendFileSync(eventsPath, `${JSON.stringify(event)}
51426
+ fs20.appendFileSync(eventsPath, `${JSON.stringify(event)}
51239
51427
  `, "utf-8");
51240
51428
  } catch {}
51241
51429
  }
@@ -51492,12 +51680,12 @@ init_schema();
51492
51680
  init_manager();
51493
51681
  init_detector();
51494
51682
  init_manager2();
51495
- import * as fs21 from "fs";
51683
+ import * as fs22 from "fs";
51496
51684
 
51497
51685
  // src/services/decision-drift-analyzer.ts
51498
51686
  init_utils2();
51499
51687
  init_manager2();
51500
- import * as fs20 from "fs";
51688
+ import * as fs21 from "fs";
51501
51689
  import * as path33 from "path";
51502
51690
  var DEFAULT_DRIFT_CONFIG = {
51503
51691
  staleThresholdPhases: 1,
@@ -51655,8 +51843,8 @@ async function analyzeDecisionDrift(directory, config3 = {}) {
51655
51843
  const contextPath = path33.join(directory, ".swarm", "context.md");
51656
51844
  let contextContent = "";
51657
51845
  try {
51658
- if (fs20.existsSync(contextPath)) {
51659
- contextContent = fs20.readFileSync(contextPath, "utf-8");
51846
+ if (fs21.existsSync(contextPath)) {
51847
+ contextContent = fs21.readFileSync(contextPath, "utf-8");
51660
51848
  }
51661
51849
  } catch {
51662
51850
  return {
@@ -52150,11 +52338,11 @@ function createSystemEnhancerHook(config3, directory) {
52150
52338
  if (handoffContent) {
52151
52339
  const handoffPath = validateSwarmPath(directory, "handoff.md");
52152
52340
  const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
52153
- if (fs21.existsSync(consumedPath)) {
52341
+ if (fs22.existsSync(consumedPath)) {
52154
52342
  warn("Duplicate handoff detected: handoff-consumed.md already exists");
52155
- fs21.unlinkSync(consumedPath);
52343
+ fs22.unlinkSync(consumedPath);
52156
52344
  }
52157
- fs21.renameSync(handoffPath, consumedPath);
52345
+ fs22.renameSync(handoffPath, consumedPath);
52158
52346
  const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
52159
52347
  The previous model's session ended. Here is your starting context:
52160
52348
 
@@ -52434,11 +52622,11 @@ ${budgetWarning}`);
52434
52622
  if (handoffContent) {
52435
52623
  const handoffPath = validateSwarmPath(directory, "handoff.md");
52436
52624
  const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
52437
- if (fs21.existsSync(consumedPath)) {
52625
+ if (fs22.existsSync(consumedPath)) {
52438
52626
  warn("Duplicate handoff detected: handoff-consumed.md already exists");
52439
- fs21.unlinkSync(consumedPath);
52627
+ fs22.unlinkSync(consumedPath);
52440
52628
  }
52441
- fs21.renameSync(handoffPath, consumedPath);
52629
+ fs22.renameSync(handoffPath, consumedPath);
52442
52630
  const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
52443
52631
  The previous model's session ended. Here is your starting context:
52444
52632
 
@@ -53207,7 +53395,7 @@ function isReadTool(toolName) {
53207
53395
  }
53208
53396
 
53209
53397
  // src/hooks/incremental-verify.ts
53210
- import * as fs22 from "fs";
53398
+ import * as fs23 from "fs";
53211
53399
  import * as path34 from "path";
53212
53400
 
53213
53401
  // src/hooks/spawn-helper.ts
@@ -53283,21 +53471,21 @@ function spawnAsync(command, cwd, timeoutMs) {
53283
53471
  // src/hooks/incremental-verify.ts
53284
53472
  var emittedSkipAdvisories = new Set;
53285
53473
  function detectPackageManager(projectDir) {
53286
- if (fs22.existsSync(path34.join(projectDir, "bun.lockb")))
53474
+ if (fs23.existsSync(path34.join(projectDir, "bun.lockb")))
53287
53475
  return "bun";
53288
- if (fs22.existsSync(path34.join(projectDir, "pnpm-lock.yaml")))
53476
+ if (fs23.existsSync(path34.join(projectDir, "pnpm-lock.yaml")))
53289
53477
  return "pnpm";
53290
- if (fs22.existsSync(path34.join(projectDir, "yarn.lock")))
53478
+ if (fs23.existsSync(path34.join(projectDir, "yarn.lock")))
53291
53479
  return "yarn";
53292
- if (fs22.existsSync(path34.join(projectDir, "package-lock.json")))
53480
+ if (fs23.existsSync(path34.join(projectDir, "package-lock.json")))
53293
53481
  return "npm";
53294
53482
  return "bun";
53295
53483
  }
53296
53484
  function detectTypecheckCommand(projectDir) {
53297
53485
  const pkgPath = path34.join(projectDir, "package.json");
53298
- if (fs22.existsSync(pkgPath)) {
53486
+ if (fs23.existsSync(pkgPath)) {
53299
53487
  try {
53300
- const pkg = JSON.parse(fs22.readFileSync(pkgPath, "utf8"));
53488
+ const pkg = JSON.parse(fs23.readFileSync(pkgPath, "utf8"));
53301
53489
  const scripts = pkg.scripts;
53302
53490
  if (scripts?.typecheck) {
53303
53491
  const pm = detectPackageManager(projectDir);
@@ -53311,8 +53499,8 @@ function detectTypecheckCommand(projectDir) {
53311
53499
  ...pkg.dependencies,
53312
53500
  ...pkg.devDependencies
53313
53501
  };
53314
- if (!deps?.typescript && !fs22.existsSync(path34.join(projectDir, "tsconfig.json"))) {}
53315
- const hasTSMarkers = deps?.typescript || fs22.existsSync(path34.join(projectDir, "tsconfig.json"));
53502
+ if (!deps?.typescript && !fs23.existsSync(path34.join(projectDir, "tsconfig.json"))) {}
53503
+ const hasTSMarkers = deps?.typescript || fs23.existsSync(path34.join(projectDir, "tsconfig.json"));
53316
53504
  if (hasTSMarkers) {
53317
53505
  return { command: ["npx", "tsc", "--noEmit"], language: "typescript" };
53318
53506
  }
@@ -53320,17 +53508,17 @@ function detectTypecheckCommand(projectDir) {
53320
53508
  return null;
53321
53509
  }
53322
53510
  }
53323
- if (fs22.existsSync(path34.join(projectDir, "go.mod"))) {
53511
+ if (fs23.existsSync(path34.join(projectDir, "go.mod"))) {
53324
53512
  return { command: ["go", "vet", "./..."], language: "go" };
53325
53513
  }
53326
- if (fs22.existsSync(path34.join(projectDir, "Cargo.toml"))) {
53514
+ if (fs23.existsSync(path34.join(projectDir, "Cargo.toml"))) {
53327
53515
  return { command: ["cargo", "check"], language: "rust" };
53328
53516
  }
53329
- if (fs22.existsSync(path34.join(projectDir, "pyproject.toml")) || fs22.existsSync(path34.join(projectDir, "requirements.txt")) || fs22.existsSync(path34.join(projectDir, "setup.py"))) {
53517
+ if (fs23.existsSync(path34.join(projectDir, "pyproject.toml")) || fs23.existsSync(path34.join(projectDir, "requirements.txt")) || fs23.existsSync(path34.join(projectDir, "setup.py"))) {
53330
53518
  return { command: null, language: "python" };
53331
53519
  }
53332
53520
  try {
53333
- const entries = fs22.readdirSync(projectDir);
53521
+ const entries = fs23.readdirSync(projectDir);
53334
53522
  if (entries.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
53335
53523
  return {
53336
53524
  command: ["dotnet", "build", "--no-restore"],
@@ -53399,7 +53587,7 @@ ${errorSummary}`);
53399
53587
  }
53400
53588
 
53401
53589
  // src/hooks/knowledge-reader.ts
53402
- import { existsSync as existsSync20 } from "fs";
53590
+ import { existsSync as existsSync21 } from "fs";
53403
53591
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
53404
53592
  import * as path35 from "path";
53405
53593
  var JACCARD_THRESHOLD = 0.6;
@@ -53452,7 +53640,7 @@ async function recordLessonsShown(directory, lessonIds, currentPhase) {
53452
53640
  const shownFile = path35.join(directory, ".swarm", ".knowledge-shown.json");
53453
53641
  try {
53454
53642
  let shownData = {};
53455
- if (existsSync20(shownFile)) {
53643
+ if (existsSync21(shownFile)) {
53456
53644
  const content = await readFile5(shownFile, "utf-8");
53457
53645
  shownData = JSON.parse(content);
53458
53646
  }
@@ -53554,7 +53742,7 @@ async function readMergedKnowledge(directory, config3, context) {
53554
53742
  async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
53555
53743
  const shownFile = path35.join(directory, ".swarm", ".knowledge-shown.json");
53556
53744
  try {
53557
- if (!existsSync20(shownFile)) {
53745
+ if (!existsSync21(shownFile)) {
53558
53746
  return;
53559
53747
  }
53560
53748
  const content = await readFile5(shownFile, "utf-8");
@@ -54024,12 +54212,12 @@ Use this data to avoid repeating known failure patterns.`;
54024
54212
  // src/hooks/curator-drift.ts
54025
54213
  init_event_bus();
54026
54214
  init_utils2();
54027
- import * as fs23 from "fs";
54215
+ import * as fs24 from "fs";
54028
54216
  import * as path36 from "path";
54029
54217
  var DRIFT_REPORT_PREFIX = "drift-report-phase-";
54030
54218
  async function readPriorDriftReports(directory) {
54031
54219
  const swarmDir = path36.join(directory, ".swarm");
54032
- const entries = await fs23.promises.readdir(swarmDir).catch(() => null);
54220
+ const entries = await fs24.promises.readdir(swarmDir).catch(() => null);
54033
54221
  if (entries === null)
54034
54222
  return [];
54035
54223
  const reportFiles = entries.filter((name2) => name2.startsWith(DRIFT_REPORT_PREFIX) && name2.endsWith(".json")).sort();
@@ -54056,9 +54244,9 @@ async function writeDriftReport(directory, report) {
54056
54244
  const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
54057
54245
  const filePath = validateSwarmPath(directory, filename);
54058
54246
  const swarmDir = path36.dirname(filePath);
54059
- await fs23.promises.mkdir(swarmDir, { recursive: true });
54247
+ await fs24.promises.mkdir(swarmDir, { recursive: true });
54060
54248
  try {
54061
- await fs23.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
54249
+ await fs24.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
54062
54250
  } catch (err2) {
54063
54251
  throw new Error(`[curator-drift] Failed to write drift report to ${filePath}: ${String(err2)}`);
54064
54252
  }
@@ -54344,7 +54532,7 @@ var WRITE_TOOLS = new Set([
54344
54532
  "append",
54345
54533
  "prepend"
54346
54534
  ]);
54347
- function createScopeGuardHook(config3, _directory, injectAdvisory) {
54535
+ function createScopeGuardHook(config3, directory, injectAdvisory) {
54348
54536
  const enabled = config3.enabled ?? true;
54349
54537
  const _skipInTurbo = config3.skip_in_turbo ?? false;
54350
54538
  return {
@@ -54361,7 +54549,7 @@ function createScopeGuardHook(config3, _directory, injectAdvisory) {
54361
54549
  const isArchitect2 = stripKnownSwarmPrefix(agentName) === ORCHESTRATOR_NAME;
54362
54550
  if (isArchitect2)
54363
54551
  return;
54364
- const declaredScope = session?.declaredCoderScope;
54552
+ const declaredScope = session?.declaredCoderScope ?? (session?.currentTaskId ? pendingCoderScopeByTaskId.get(session.currentTaskId) : null);
54365
54553
  if (!declaredScope || declaredScope.length === 0)
54366
54554
  return;
54367
54555
  const argsObj = output.args;
@@ -54369,7 +54557,7 @@ function createScopeGuardHook(config3, _directory, injectAdvisory) {
54369
54557
  if (typeof rawFilePath !== "string" || !rawFilePath)
54370
54558
  return;
54371
54559
  const filePath = rawFilePath.replace(/[\r\n\t]/g, "_").split(String.fromCharCode(27)).join("_").replace(/\[[\d;]*m/g, "");
54372
- if (!isFileInScope(filePath, declaredScope)) {
54560
+ if (!isFileInScope(filePath, declaredScope, directory)) {
54373
54561
  const taskId = session?.currentTaskId ?? "unknown";
54374
54562
  const violationMessage = `SCOPE VIOLATION: ${agentName} attempted to modify '${filePath}' which is not in declared scope for task ${taskId}. Declared scope: [${declaredScope.slice(0, 3).join(", ")}${declaredScope.length > 3 ? "..." : ""}]`;
54375
54563
  if (session) {
@@ -54392,10 +54580,11 @@ function createScopeGuardHook(config3, _directory, injectAdvisory) {
54392
54580
  }
54393
54581
  };
54394
54582
  }
54395
- function isFileInScope(filePath, scopeEntries) {
54396
- const resolvedFile = path37.resolve(filePath);
54583
+ function isFileInScope(filePath, scopeEntries, directory) {
54584
+ const dir = directory ?? process.cwd();
54585
+ const resolvedFile = path37.resolve(dir, filePath);
54397
54586
  return scopeEntries.some((scope) => {
54398
- const resolvedScope = path37.resolve(scope);
54587
+ const resolvedScope = path37.resolve(dir, scope);
54399
54588
  if (resolvedFile === resolvedScope)
54400
54589
  return true;
54401
54590
  const rel = path37.relative(resolvedScope, resolvedFile);
@@ -54448,7 +54637,7 @@ function createSelfReviewHook(config3, injectAdvisory) {
54448
54637
  }
54449
54638
 
54450
54639
  // src/hooks/slop-detector.ts
54451
- import * as fs24 from "fs";
54640
+ import * as fs25 from "fs";
54452
54641
  import * as path38 from "path";
54453
54642
  var WRITE_EDIT_TOOLS = new Set([
54454
54643
  "write",
@@ -54494,7 +54683,7 @@ function checkBoilerplateExplosion(content, taskDescription, threshold) {
54494
54683
  function walkFiles(dir, exts, deadline) {
54495
54684
  const results = [];
54496
54685
  try {
54497
- for (const entry of fs24.readdirSync(dir, { withFileTypes: true })) {
54686
+ for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
54498
54687
  if (deadline !== undefined && Date.now() > deadline)
54499
54688
  break;
54500
54689
  if (entry.isSymbolicLink())
@@ -54514,7 +54703,7 @@ function walkFiles(dir, exts, deadline) {
54514
54703
  return results;
54515
54704
  }
54516
54705
  function checkDeadExports(content, projectDir, startTime) {
54517
- const hasPackageJson = fs24.existsSync(path38.join(projectDir, "package.json"));
54706
+ const hasPackageJson = fs25.existsSync(path38.join(projectDir, "package.json"));
54518
54707
  if (!hasPackageJson)
54519
54708
  return null;
54520
54709
  const exportMatches = content.matchAll(/^\+(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
@@ -54537,7 +54726,7 @@ function checkDeadExports(content, projectDir, startTime) {
54537
54726
  if (found || Date.now() - startTime > 480)
54538
54727
  break;
54539
54728
  try {
54540
- const text = fs24.readFileSync(file3, "utf-8");
54729
+ const text = fs25.readFileSync(file3, "utf-8");
54541
54730
  if (importPattern.test(text))
54542
54731
  found = true;
54543
54732
  importPattern.lastIndex = 0;
@@ -54670,7 +54859,7 @@ Review before proceeding.`;
54670
54859
 
54671
54860
  // src/hooks/steering-consumed.ts
54672
54861
  init_utils2();
54673
- import * as fs25 from "fs";
54862
+ import * as fs26 from "fs";
54674
54863
  function recordSteeringConsumed(directory, directiveId) {
54675
54864
  try {
54676
54865
  const eventsPath = validateSwarmPath(directory, "events.jsonl");
@@ -54679,7 +54868,7 @@ function recordSteeringConsumed(directory, directiveId) {
54679
54868
  directiveId,
54680
54869
  timestamp: new Date().toISOString()
54681
54870
  };
54682
- fs25.appendFileSync(eventsPath, `${JSON.stringify(event)}
54871
+ fs26.appendFileSync(eventsPath, `${JSON.stringify(event)}
54683
54872
  `, "utf-8");
54684
54873
  } catch {}
54685
54874
  }
@@ -54796,7 +54985,11 @@ function deserializeAgentSession(s) {
54796
54985
  lastScopeViolation: null,
54797
54986
  scopeViolationDetected: s.scopeViolationDetected,
54798
54987
  modifiedFilesThisCoderTask: [],
54799
- pendingAdvisoryMessages: s.pendingAdvisoryMessages ?? []
54988
+ pendingAdvisoryMessages: s.pendingAdvisoryMessages ?? [],
54989
+ model_fallback_index: s.model_fallback_index ?? 0,
54990
+ modelFallbackExhausted: s.modelFallbackExhausted ?? false,
54991
+ coderRevisions: s.coderRevisions ?? 0,
54992
+ revisionLimitHit: s.revisionLimitHit ?? false
54800
54993
  };
54801
54994
  }
54802
54995
  async function readSnapshot(directory) {
@@ -54845,6 +55038,10 @@ async function rehydrateState(snapshot) {
54845
55038
  }
54846
55039
  if (snapshot.agentSessions) {
54847
55040
  for (const [sessionId, serializedSession] of Object.entries(snapshot.agentSessions)) {
55041
+ if (!serializedSession || typeof serializedSession !== "object" || typeof serializedSession.agentName !== "string" || typeof serializedSession.lastToolCallTime !== "number" || typeof serializedSession.delegationActive !== "boolean") {
55042
+ console.warn("[snapshot-reader] Skipping malformed session %s: missing required fields (agentName, lastToolCallTime, delegationActive)", sessionId);
55043
+ continue;
55044
+ }
54848
55045
  swarmState.agentSessions.set(sessionId, deserializeAgentSession(serializedSession));
54849
55046
  }
54850
55047
  }
@@ -55035,8 +55232,9 @@ var build_check = createSwarmTool({
55035
55232
  });
55036
55233
  // src/tools/check-gate-status.ts
55037
55234
  init_dist();
55235
+ init_manager();
55038
55236
  init_create_tool();
55039
- import * as fs26 from "fs";
55237
+ import * as fs27 from "fs";
55040
55238
  import * as path39 from "path";
55041
55239
  var EVIDENCE_DIR = ".swarm/evidence";
55042
55240
  var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
@@ -55060,12 +55258,12 @@ function isPathWithinSwarm(filePath, workspaceRoot) {
55060
55258
  return normalizedPath.startsWith(swarmPath);
55061
55259
  }
55062
55260
  function readEvidenceFile(evidencePath) {
55063
- if (!fs26.existsSync(evidencePath)) {
55261
+ if (!fs27.existsSync(evidencePath)) {
55064
55262
  return null;
55065
55263
  }
55066
55264
  let content;
55067
55265
  try {
55068
- content = fs26.readFileSync(evidencePath, "utf-8");
55266
+ content = fs27.readFileSync(evidencePath, "utf-8");
55069
55267
  } catch {
55070
55268
  return null;
55071
55269
  }
@@ -55158,13 +55356,37 @@ var check_gate_status = createSwarmTool({
55158
55356
  missingGates.push(requiredGate);
55159
55357
  }
55160
55358
  }
55161
- const status = missingGates.length === 0 ? "all_passed" : "incomplete";
55359
+ let status = missingGates.length === 0 ? "all_passed" : "incomplete";
55162
55360
  let message;
55163
55361
  if (status === "all_passed") {
55164
55362
  message = `All required gates have passed for task "${taskIdInput}".`;
55165
55363
  } else {
55166
55364
  message = `Task "${taskIdInput}" is incomplete. Missing gates: ${missingGates.join(", ")}.`;
55167
55365
  }
55366
+ let secretscanVerdict = "not_run";
55367
+ try {
55368
+ const evidenceResult = await loadEvidence(directory, taskIdInput);
55369
+ if (evidenceResult.status === "found") {
55370
+ const secretscanEntries = evidenceResult.bundle.entries.filter((entry) => entry.type === "secretscan");
55371
+ if (secretscanEntries.length > 0) {
55372
+ const lastSecretscan = secretscanEntries[secretscanEntries.length - 1];
55373
+ if (isSecretscanEvidence(lastSecretscan)) {
55374
+ if (lastSecretscan.verdict === "fail" || lastSecretscan.verdict === "rejected") {
55375
+ secretscanVerdict = "fail";
55376
+ missingGates.push("secretscan (BLOCKED \u2014 secrets detected)");
55377
+ if (status === "all_passed") {
55378
+ status = "incomplete";
55379
+ }
55380
+ message = `BLOCKED: Secretscan found secrets in prior scan. ${message}`;
55381
+ } else if (lastSecretscan.verdict === "pass" || lastSecretscan.verdict === "approved" || lastSecretscan.verdict === "info") {
55382
+ secretscanVerdict = "pass";
55383
+ }
55384
+ }
55385
+ } else {
55386
+ message += " Advisory: No secretscan evidence found for this task. Consider running secretscan.";
55387
+ }
55388
+ }
55389
+ } catch {}
55168
55390
  const todoScan = evidenceData.todo_scan;
55169
55391
  const result = {
55170
55392
  taskId: taskIdInput,
@@ -55174,7 +55396,8 @@ var check_gate_status = createSwarmTool({
55174
55396
  missing_gates: missingGates,
55175
55397
  gates: gatesMap,
55176
55398
  message,
55177
- todo_scan: todoScan ?? null
55399
+ todo_scan: todoScan ?? null,
55400
+ secretscan_verdict: secretscanVerdict
55178
55401
  };
55179
55402
  return JSON.stringify(result, null, 2);
55180
55403
  }
@@ -55186,7 +55409,7 @@ init_checkpoint();
55186
55409
  // src/tools/complexity-hotspots.ts
55187
55410
  init_dist();
55188
55411
  init_create_tool();
55189
- import * as fs27 from "fs";
55412
+ import * as fs28 from "fs";
55190
55413
  import * as path40 from "path";
55191
55414
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
55192
55415
  var DEFAULT_DAYS = 90;
@@ -55316,11 +55539,11 @@ function estimateComplexity(content) {
55316
55539
  }
55317
55540
  function getComplexityForFile(filePath) {
55318
55541
  try {
55319
- const stat2 = fs27.statSync(filePath);
55542
+ const stat2 = fs28.statSync(filePath);
55320
55543
  if (stat2.size > MAX_FILE_SIZE_BYTES2) {
55321
55544
  return null;
55322
55545
  }
55323
- const content = fs27.readFileSync(filePath, "utf-8");
55546
+ const content = fs28.readFileSync(filePath, "utf-8");
55324
55547
  return estimateComplexity(content);
55325
55548
  } catch {
55326
55549
  return null;
@@ -55341,7 +55564,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
55341
55564
  let analyzedFiles = 0;
55342
55565
  for (const [file3, churnCount] of filteredChurn) {
55343
55566
  let fullPath = file3;
55344
- if (!fs27.existsSync(fullPath)) {
55567
+ if (!fs28.existsSync(fullPath)) {
55345
55568
  fullPath = path40.join(cwd, file3);
55346
55569
  }
55347
55570
  const complexity = getComplexityForFile(fullPath);
@@ -55550,7 +55773,7 @@ var curator_analyze = createSwarmTool({
55550
55773
  });
55551
55774
  // src/tools/declare-scope.ts
55552
55775
  init_tool();
55553
- import * as fs28 from "fs";
55776
+ import * as fs29 from "fs";
55554
55777
  import * as path41 from "path";
55555
55778
  init_create_tool();
55556
55779
  function validateTaskIdFormat(taskId) {
@@ -55643,9 +55866,9 @@ async function executeDeclareScope(args2, fallbackDir) {
55643
55866
  }
55644
55867
  const resolvedDir = path41.resolve(normalizedDir);
55645
55868
  try {
55646
- const realPath = fs28.realpathSync(resolvedDir);
55869
+ const realPath = fs29.realpathSync(resolvedDir);
55647
55870
  const planPath2 = path41.join(realPath, ".swarm", "plan.json");
55648
- if (!fs28.existsSync(planPath2)) {
55871
+ if (!fs29.existsSync(planPath2)) {
55649
55872
  return {
55650
55873
  success: false,
55651
55874
  message: `Invalid working_directory: plan not found in "${realPath}"`,
@@ -55669,7 +55892,7 @@ async function executeDeclareScope(args2, fallbackDir) {
55669
55892
  }
55670
55893
  const directory = normalizedDir || fallbackDir;
55671
55894
  const planPath = path41.resolve(directory, ".swarm", "plan.json");
55672
- if (!fs28.existsSync(planPath)) {
55895
+ if (!fs29.existsSync(planPath)) {
55673
55896
  return {
55674
55897
  success: false,
55675
55898
  message: "No plan found",
@@ -55678,7 +55901,7 @@ async function executeDeclareScope(args2, fallbackDir) {
55678
55901
  }
55679
55902
  let planContent;
55680
55903
  try {
55681
- planContent = JSON.parse(fs28.readFileSync(planPath, "utf-8"));
55904
+ planContent = JSON.parse(fs29.readFileSync(planPath, "utf-8"));
55682
55905
  } catch {
55683
55906
  return {
55684
55907
  success: false,
@@ -55900,6 +56123,391 @@ var diff = createSwarmTool({
55900
56123
  }
55901
56124
  }
55902
56125
  });
56126
+ // src/tools/doc-scan.ts
56127
+ init_dist();
56128
+ init_schema();
56129
+ import * as crypto4 from "crypto";
56130
+ import * as fs30 from "fs";
56131
+ import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
56132
+ import * as path42 from "path";
56133
+ init_create_tool();
56134
+ var SKIP_DIRECTORIES2 = new Set([
56135
+ "node_modules",
56136
+ ".git",
56137
+ ".swarm",
56138
+ "dist",
56139
+ "build",
56140
+ ".next",
56141
+ "vendor"
56142
+ ]);
56143
+ var SKIP_PATTERNS = [/\.test\./, /\.spec\./, /\.d\.ts$/];
56144
+ var MAX_SUMMARY_LENGTH = 200;
56145
+ var MAX_INDEXED_FILES = 100;
56146
+ var READ_LINES_LIMIT = 30;
56147
+ var MIN_LESSON_LENGTH = 15;
56148
+ var MAX_CONSTRAINTS_PER_DOC = 5;
56149
+ var MAX_CONSTRAINT_LENGTH = 200;
56150
+ var RELEVANCE_THRESHOLD = 0.1;
56151
+ var DEDUP_THRESHOLD = 0.6;
56152
+ function normalizeSeparators(filePath) {
56153
+ return filePath.replace(/\\/g, "/");
56154
+ }
56155
+ function matchesDocPattern(filePath, patterns) {
56156
+ const normalizedPath = normalizeSeparators(filePath);
56157
+ const basename5 = path42.basename(filePath);
56158
+ for (const pattern of patterns) {
56159
+ if (!pattern.includes("/") && !pattern.includes("\\")) {
56160
+ if (basename5 === pattern) {
56161
+ return true;
56162
+ }
56163
+ continue;
56164
+ }
56165
+ if (pattern.startsWith("**/")) {
56166
+ const filenamePattern = pattern.slice(3);
56167
+ if (basename5 === filenamePattern) {
56168
+ return true;
56169
+ }
56170
+ continue;
56171
+ }
56172
+ const patternNormalized = normalizeSeparators(pattern);
56173
+ const dirPrefix = patternNormalized.replace(/\/\*\*.*$/, "").replace(/\/\*.*$/, "");
56174
+ if (normalizedPath.startsWith(dirPrefix + "/") || normalizedPath === dirPrefix) {
56175
+ return true;
56176
+ }
56177
+ }
56178
+ return false;
56179
+ }
56180
+ function extractTitleAndSummary(content, filename) {
56181
+ const lines = content.split(`
56182
+ `);
56183
+ let title = filename;
56184
+ let summary = "";
56185
+ let foundTitle = false;
56186
+ const potentialSummaryLines = [];
56187
+ for (let i2 = 0;i2 < lines.length && i2 < READ_LINES_LIMIT; i2++) {
56188
+ const line = lines[i2].trim();
56189
+ if (!foundTitle && line.startsWith("# ")) {
56190
+ title = line.slice(2).trim();
56191
+ foundTitle = true;
56192
+ continue;
56193
+ }
56194
+ if (line && !line.startsWith("#")) {
56195
+ potentialSummaryLines.push(line);
56196
+ }
56197
+ }
56198
+ for (const line of potentialSummaryLines) {
56199
+ summary += (summary ? " " : "") + line;
56200
+ if (summary.length >= MAX_SUMMARY_LENGTH) {
56201
+ break;
56202
+ }
56203
+ }
56204
+ if (summary.length > MAX_SUMMARY_LENGTH) {
56205
+ summary = summary.slice(0, MAX_SUMMARY_LENGTH - 3) + "...";
56206
+ }
56207
+ return { title, summary };
56208
+ }
56209
+ function stripMarkdown(text) {
56210
+ return text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/^\s*[-*\u2022]\s+/gm, "").replace(/^\s*\d+\.\s+/gm, "").trim();
56211
+ }
56212
+ async function scanDocIndex(directory) {
56213
+ const manifestPath = path42.join(directory, ".swarm", "doc-manifest.json");
56214
+ const defaultPatterns = DocsConfigSchema.parse({}).doc_patterns;
56215
+ const extraPatterns = [
56216
+ "ARCHITECTURE.md",
56217
+ "CLAUDE.md",
56218
+ "AGENTS.md",
56219
+ ".github/*.md",
56220
+ "doc/**/*.md"
56221
+ ];
56222
+ const allPatterns = [...defaultPatterns, ...extraPatterns];
56223
+ try {
56224
+ const manifestContent = await readFile6(manifestPath, "utf-8");
56225
+ const existingManifest = JSON.parse(manifestContent);
56226
+ if (existingManifest.schema_version === 1 && existingManifest.files) {
56227
+ let cacheValid = true;
56228
+ for (const file3 of existingManifest.files) {
56229
+ try {
56230
+ const fullPath = path42.join(directory, file3.path);
56231
+ const stat2 = fs30.statSync(fullPath);
56232
+ if (stat2.mtimeMs > new Date(existingManifest.scanned_at).getTime()) {
56233
+ cacheValid = false;
56234
+ break;
56235
+ }
56236
+ } catch {
56237
+ cacheValid = false;
56238
+ break;
56239
+ }
56240
+ }
56241
+ if (cacheValid) {
56242
+ return { manifest: existingManifest, cached: true };
56243
+ }
56244
+ }
56245
+ } catch {}
56246
+ const discoveredFiles = [];
56247
+ let rawEntries;
56248
+ try {
56249
+ rawEntries = fs30.readdirSync(directory, { recursive: true });
56250
+ } catch {
56251
+ const manifest2 = {
56252
+ schema_version: 1,
56253
+ scanned_at: new Date().toISOString(),
56254
+ files: []
56255
+ };
56256
+ return { manifest: manifest2, cached: false };
56257
+ }
56258
+ const entries = rawEntries.filter((e) => typeof e === "string");
56259
+ for (const entry of entries) {
56260
+ const fullPath = path42.join(directory, entry);
56261
+ let stat2;
56262
+ try {
56263
+ stat2 = fs30.statSync(fullPath);
56264
+ } catch {
56265
+ continue;
56266
+ }
56267
+ if (!stat2.isFile())
56268
+ continue;
56269
+ const pathParts = normalizeSeparators(entry).split("/");
56270
+ let skipThisFile = false;
56271
+ for (const part of pathParts) {
56272
+ if (SKIP_DIRECTORIES2.has(part)) {
56273
+ skipThisFile = true;
56274
+ break;
56275
+ }
56276
+ }
56277
+ if (skipThisFile)
56278
+ continue;
56279
+ for (const pattern of SKIP_PATTERNS) {
56280
+ if (pattern.test(entry)) {
56281
+ skipThisFile = true;
56282
+ break;
56283
+ }
56284
+ }
56285
+ if (skipThisFile)
56286
+ continue;
56287
+ if (!matchesDocPattern(entry, allPatterns)) {
56288
+ continue;
56289
+ }
56290
+ let content;
56291
+ try {
56292
+ content = fs30.readFileSync(fullPath, "utf-8");
56293
+ } catch {
56294
+ continue;
56295
+ }
56296
+ const { title, summary } = extractTitleAndSummary(content, path42.basename(entry));
56297
+ const lineCount = content.split(`
56298
+ `).length;
56299
+ discoveredFiles.push({
56300
+ path: entry,
56301
+ title,
56302
+ summary,
56303
+ lines: lineCount,
56304
+ mtime: stat2.mtimeMs
56305
+ });
56306
+ }
56307
+ discoveredFiles.sort((a, b) => a.path.toLowerCase().localeCompare(b.path.toLowerCase()));
56308
+ let truncated = false;
56309
+ if (discoveredFiles.length > MAX_INDEXED_FILES) {
56310
+ discoveredFiles.splice(MAX_INDEXED_FILES);
56311
+ truncated = true;
56312
+ }
56313
+ if (truncated && discoveredFiles.length > 0) {
56314
+ discoveredFiles[0].summary = `[Warning: ${MAX_INDEXED_FILES}+ docs found, listing first ${MAX_INDEXED_FILES}] ` + discoveredFiles[0].summary;
56315
+ }
56316
+ const manifest = {
56317
+ schema_version: 1,
56318
+ scanned_at: new Date().toISOString(),
56319
+ files: discoveredFiles
56320
+ };
56321
+ try {
56322
+ await mkdir5(path42.dirname(manifestPath), { recursive: true });
56323
+ await writeFile5(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
56324
+ } catch {}
56325
+ return { manifest, cached: false };
56326
+ }
56327
+ var CONSTRAINT_PATTERNS = [
56328
+ /\bMUST\b/,
56329
+ /\bMUST NOT\b/,
56330
+ /\bSHOULD\b/,
56331
+ /\bSHOULD NOT\b/,
56332
+ /\bDO NOT\b/,
56333
+ /\bALWAYS\b/,
56334
+ /\bNEVER\b/,
56335
+ /\bREQUIRED\b/
56336
+ ];
56337
+ var ACTION_WORDS = /\b(must|should|don't|avoid|ensure|use|follow)\b/i;
56338
+ function isConstraintLine(line) {
56339
+ const upperLine = line.toUpperCase();
56340
+ for (const pattern of CONSTRAINT_PATTERNS) {
56341
+ if (pattern.test(upperLine)) {
56342
+ return true;
56343
+ }
56344
+ }
56345
+ if (/^\s*[-*\u2022]/.test(line) && ACTION_WORDS.test(line)) {
56346
+ return true;
56347
+ }
56348
+ return false;
56349
+ }
56350
+ function extractConstraintsFromContent(content) {
56351
+ const lines = content.split(`
56352
+ `);
56353
+ const constraints = [];
56354
+ for (const line of lines) {
56355
+ if (constraints.length >= MAX_CONSTRAINTS_PER_DOC) {
56356
+ break;
56357
+ }
56358
+ const trimmed = line.trim();
56359
+ if (!trimmed)
56360
+ continue;
56361
+ if (isConstraintLine(trimmed)) {
56362
+ const cleaned = stripMarkdown(trimmed);
56363
+ const len = cleaned.length;
56364
+ if (len >= MIN_LESSON_LENGTH && len <= MAX_CONSTRAINT_LENGTH) {
56365
+ constraints.push(cleaned);
56366
+ }
56367
+ }
56368
+ }
56369
+ return constraints;
56370
+ }
56371
+ async function extractDocConstraints(directory, taskFiles, taskDescription) {
56372
+ const manifestPath = path42.join(directory, ".swarm", "doc-manifest.json");
56373
+ let manifest;
56374
+ try {
56375
+ const content = await readFile6(manifestPath, "utf-8");
56376
+ manifest = JSON.parse(content);
56377
+ } catch {
56378
+ const result = await scanDocIndex(directory);
56379
+ manifest = result.manifest;
56380
+ }
56381
+ const knowledgePath = resolveSwarmKnowledgePath(directory);
56382
+ const existingEntries = await readKnowledge(knowledgePath);
56383
+ const taskContext = [...taskFiles, taskDescription].join(" ");
56384
+ const taskBigrams = wordBigrams(normalize2(taskContext));
56385
+ let extractedCount = 0;
56386
+ let skippedCount = 0;
56387
+ const details = [];
56388
+ for (const docFile of manifest.files) {
56389
+ const docContext = `${docFile.path} ${docFile.title} ${docFile.summary}`;
56390
+ const docBigrams = wordBigrams(normalize2(docContext));
56391
+ const score = jaccardBigram(taskBigrams, docBigrams);
56392
+ if (score <= RELEVANCE_THRESHOLD) {
56393
+ skippedCount++;
56394
+ continue;
56395
+ }
56396
+ let fullContent;
56397
+ try {
56398
+ fullContent = await readFile6(path42.join(directory, docFile.path), "utf-8");
56399
+ } catch {
56400
+ skippedCount++;
56401
+ continue;
56402
+ }
56403
+ const constraints = extractConstraintsFromContent(fullContent);
56404
+ if (constraints.length === 0) {
56405
+ skippedCount++;
56406
+ continue;
56407
+ }
56408
+ const docDetails = {
56409
+ path: docFile.path,
56410
+ score,
56411
+ constraints: []
56412
+ };
56413
+ for (const constraint of constraints) {
56414
+ const duplicate = findNearDuplicate(constraint, existingEntries, DEDUP_THRESHOLD);
56415
+ if (!duplicate) {
56416
+ const entry = {
56417
+ id: crypto4.randomUUID(),
56418
+ tier: "swarm",
56419
+ lesson: constraint,
56420
+ category: "architecture",
56421
+ tags: ["doc-scan", path42.basename(docFile.path)],
56422
+ scope: "global",
56423
+ confidence: 0.5,
56424
+ status: "candidate",
56425
+ confirmed_by: [],
56426
+ project_name: "",
56427
+ retrieval_outcomes: {
56428
+ applied_count: 0,
56429
+ succeeded_after_count: 0,
56430
+ failed_after_count: 0
56431
+ },
56432
+ schema_version: 1,
56433
+ created_at: new Date().toISOString(),
56434
+ updated_at: new Date().toISOString(),
56435
+ auto_generated: true,
56436
+ hive_eligible: false
56437
+ };
56438
+ await appendKnowledge(knowledgePath, entry);
56439
+ existingEntries.push(entry);
56440
+ extractedCount++;
56441
+ docDetails.constraints.push(constraint);
56442
+ }
56443
+ }
56444
+ if (docDetails.constraints.length > 0) {
56445
+ details.push(docDetails);
56446
+ } else {
56447
+ skippedCount++;
56448
+ }
56449
+ }
56450
+ return { extracted: extractedCount, skipped: skippedCount, details };
56451
+ }
56452
+ var doc_scan = createSwarmTool({
56453
+ description: "Scan project documentation files and build an index manifest. Caches results in .swarm/doc-manifest.json for fast subsequent scans.",
56454
+ args: {
56455
+ force: tool.schema.boolean().optional().describe("Force re-scan even if cache is valid")
56456
+ },
56457
+ execute: async (args2, directory) => {
56458
+ let force = false;
56459
+ try {
56460
+ if (args2 && typeof args2 === "object") {
56461
+ const obj = args2;
56462
+ if (obj.force === true)
56463
+ force = true;
56464
+ }
56465
+ } catch {}
56466
+ if (force) {
56467
+ const manifestPath = path42.join(directory, ".swarm", "doc-manifest.json");
56468
+ try {
56469
+ fs30.unlinkSync(manifestPath);
56470
+ } catch {}
56471
+ }
56472
+ const { manifest, cached: cached3 } = await scanDocIndex(directory);
56473
+ return JSON.stringify({
56474
+ success: true,
56475
+ files_count: manifest.files.length,
56476
+ cached: cached3,
56477
+ manifest
56478
+ }, null, 2);
56479
+ }
56480
+ });
56481
+ var doc_extract = createSwarmTool({
56482
+ description: "Extract actionable constraints from project documentation relevant to the current task. Scans docs via doc-manifest, scores relevance via Jaccard bigram similarity, and stores non-duplicate constraints in .swarm/knowledge.jsonl.",
56483
+ args: {
56484
+ task_files: tool.schema.array(tool.schema.string()).describe("List of file paths involved in the current task"),
56485
+ task_description: tool.schema.string().describe("Description of the current task")
56486
+ },
56487
+ execute: async (args2, directory) => {
56488
+ let taskFiles = [];
56489
+ let taskDescription = "";
56490
+ try {
56491
+ if (args2 && typeof args2 === "object") {
56492
+ const obj = args2;
56493
+ if (Array.isArray(obj.task_files)) {
56494
+ taskFiles = obj.task_files.filter((f) => typeof f === "string");
56495
+ }
56496
+ if (typeof obj.task_description === "string") {
56497
+ taskDescription = obj.task_description;
56498
+ }
56499
+ }
56500
+ } catch {}
56501
+ if (taskFiles.length === 0 && !taskDescription) {
56502
+ return JSON.stringify({
56503
+ success: false,
56504
+ error: "task_files or task_description is required"
56505
+ });
56506
+ }
56507
+ const result = await extractDocConstraints(directory, taskFiles, taskDescription);
56508
+ return JSON.stringify({ success: true, ...result }, null, 2);
56509
+ }
56510
+ });
55903
56511
  // src/tools/domain-detector.ts
55904
56512
  init_tool();
55905
56513
  var DOMAIN_PATTERNS = {
@@ -56083,8 +56691,8 @@ Use these as DOMAIN values when delegating to @sme.`;
56083
56691
  // src/tools/evidence-check.ts
56084
56692
  init_dist();
56085
56693
  init_create_tool();
56086
- import * as fs29 from "fs";
56087
- import * as path42 from "path";
56694
+ import * as fs31 from "fs";
56695
+ import * as path43 from "path";
56088
56696
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
56089
56697
  var MAX_EVIDENCE_FILES = 1000;
56090
56698
  var EVIDENCE_DIR2 = ".swarm/evidence";
@@ -56114,9 +56722,9 @@ function validateRequiredTypes(input) {
56114
56722
  return null;
56115
56723
  }
56116
56724
  function isPathWithinSwarm2(filePath, cwd) {
56117
- const normalizedCwd = path42.resolve(cwd);
56118
- const swarmPath = path42.join(normalizedCwd, ".swarm");
56119
- const normalizedPath = path42.resolve(filePath);
56725
+ const normalizedCwd = path43.resolve(cwd);
56726
+ const swarmPath = path43.join(normalizedCwd, ".swarm");
56727
+ const normalizedPath = path43.resolve(filePath);
56120
56728
  return normalizedPath.startsWith(swarmPath);
56121
56729
  }
56122
56730
  function parseCompletedTasks(planContent) {
@@ -56132,12 +56740,12 @@ function parseCompletedTasks(planContent) {
56132
56740
  }
56133
56741
  function readEvidenceFiles(evidenceDir, _cwd) {
56134
56742
  const evidence = [];
56135
- if (!fs29.existsSync(evidenceDir) || !fs29.statSync(evidenceDir).isDirectory()) {
56743
+ if (!fs31.existsSync(evidenceDir) || !fs31.statSync(evidenceDir).isDirectory()) {
56136
56744
  return evidence;
56137
56745
  }
56138
56746
  let files;
56139
56747
  try {
56140
- files = fs29.readdirSync(evidenceDir);
56748
+ files = fs31.readdirSync(evidenceDir);
56141
56749
  } catch {
56142
56750
  return evidence;
56143
56751
  }
@@ -56146,14 +56754,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
56146
56754
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
56147
56755
  continue;
56148
56756
  }
56149
- const filePath = path42.join(evidenceDir, filename);
56757
+ const filePath = path43.join(evidenceDir, filename);
56150
56758
  try {
56151
- const resolvedPath = path42.resolve(filePath);
56152
- const evidenceDirResolved = path42.resolve(evidenceDir);
56759
+ const resolvedPath = path43.resolve(filePath);
56760
+ const evidenceDirResolved = path43.resolve(evidenceDir);
56153
56761
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
56154
56762
  continue;
56155
56763
  }
56156
- const stat2 = fs29.lstatSync(filePath);
56764
+ const stat2 = fs31.lstatSync(filePath);
56157
56765
  if (!stat2.isFile()) {
56158
56766
  continue;
56159
56767
  }
@@ -56162,7 +56770,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
56162
56770
  }
56163
56771
  let fileStat;
56164
56772
  try {
56165
- fileStat = fs29.statSync(filePath);
56773
+ fileStat = fs31.statSync(filePath);
56166
56774
  if (fileStat.size > MAX_FILE_SIZE_BYTES3) {
56167
56775
  continue;
56168
56776
  }
@@ -56171,7 +56779,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
56171
56779
  }
56172
56780
  let content;
56173
56781
  try {
56174
- content = fs29.readFileSync(filePath, "utf-8");
56782
+ content = fs31.readFileSync(filePath, "utf-8");
56175
56783
  } catch {
56176
56784
  continue;
56177
56785
  }
@@ -56267,7 +56875,7 @@ var evidence_check = createSwarmTool({
56267
56875
  return JSON.stringify(errorResult, null, 2);
56268
56876
  }
56269
56877
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
56270
- const planPath = path42.join(cwd, PLAN_FILE);
56878
+ const planPath = path43.join(cwd, PLAN_FILE);
56271
56879
  if (!isPathWithinSwarm2(planPath, cwd)) {
56272
56880
  const errorResult = {
56273
56881
  error: "plan file path validation failed",
@@ -56281,7 +56889,7 @@ var evidence_check = createSwarmTool({
56281
56889
  }
56282
56890
  let planContent;
56283
56891
  try {
56284
- planContent = fs29.readFileSync(planPath, "utf-8");
56892
+ planContent = fs31.readFileSync(planPath, "utf-8");
56285
56893
  } catch {
56286
56894
  const result2 = {
56287
56895
  message: "No completed tasks found in plan.",
@@ -56299,7 +56907,7 @@ var evidence_check = createSwarmTool({
56299
56907
  };
56300
56908
  return JSON.stringify(result2, null, 2);
56301
56909
  }
56302
- const evidenceDir = path42.join(cwd, EVIDENCE_DIR2);
56910
+ const evidenceDir = path43.join(cwd, EVIDENCE_DIR2);
56303
56911
  const evidence = readEvidenceFiles(evidenceDir, cwd);
56304
56912
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
56305
56913
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -56316,8 +56924,8 @@ var evidence_check = createSwarmTool({
56316
56924
  // src/tools/file-extractor.ts
56317
56925
  init_tool();
56318
56926
  init_create_tool();
56319
- import * as fs30 from "fs";
56320
- import * as path43 from "path";
56927
+ import * as fs32 from "fs";
56928
+ import * as path44 from "path";
56321
56929
  var EXT_MAP = {
56322
56930
  python: ".py",
56323
56931
  py: ".py",
@@ -56379,8 +56987,8 @@ var extract_code_blocks = createSwarmTool({
56379
56987
  execute: async (args2, directory) => {
56380
56988
  const { content, output_dir, prefix } = args2;
56381
56989
  const targetDir = output_dir || directory;
56382
- if (!fs30.existsSync(targetDir)) {
56383
- fs30.mkdirSync(targetDir, { recursive: true });
56990
+ if (!fs32.existsSync(targetDir)) {
56991
+ fs32.mkdirSync(targetDir, { recursive: true });
56384
56992
  }
56385
56993
  if (!content) {
56386
56994
  return "Error: content is required";
@@ -56398,16 +57006,16 @@ var extract_code_blocks = createSwarmTool({
56398
57006
  if (prefix) {
56399
57007
  filename = `${prefix}_${filename}`;
56400
57008
  }
56401
- let filepath = path43.join(targetDir, filename);
56402
- const base = path43.basename(filepath, path43.extname(filepath));
56403
- const ext = path43.extname(filepath);
57009
+ let filepath = path44.join(targetDir, filename);
57010
+ const base = path44.basename(filepath, path44.extname(filepath));
57011
+ const ext = path44.extname(filepath);
56404
57012
  let counter = 1;
56405
- while (fs30.existsSync(filepath)) {
56406
- filepath = path43.join(targetDir, `${base}_${counter}${ext}`);
57013
+ while (fs32.existsSync(filepath)) {
57014
+ filepath = path44.join(targetDir, `${base}_${counter}${ext}`);
56407
57015
  counter++;
56408
57016
  }
56409
57017
  try {
56410
- fs30.writeFileSync(filepath, code.trim(), "utf-8");
57018
+ fs32.writeFileSync(filepath, code.trim(), "utf-8");
56411
57019
  savedFiles.push(filepath);
56412
57020
  } catch (error93) {
56413
57021
  errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
@@ -56523,8 +57131,8 @@ var gitingest = createSwarmTool({
56523
57131
  // src/tools/imports.ts
56524
57132
  init_dist();
56525
57133
  init_create_tool();
56526
- import * as fs31 from "fs";
56527
- import * as path44 from "path";
57134
+ import * as fs33 from "fs";
57135
+ import * as path45 from "path";
56528
57136
  var MAX_FILE_PATH_LENGTH2 = 500;
56529
57137
  var MAX_SYMBOL_LENGTH = 256;
56530
57138
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -56578,7 +57186,7 @@ function validateSymbolInput(symbol3) {
56578
57186
  return null;
56579
57187
  }
56580
57188
  function isBinaryFile2(filePath, buffer) {
56581
- const ext = path44.extname(filePath).toLowerCase();
57189
+ const ext = path45.extname(filePath).toLowerCase();
56582
57190
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
56583
57191
  return false;
56584
57192
  }
@@ -56602,15 +57210,15 @@ function parseImports(content, targetFile, targetSymbol) {
56602
57210
  const imports = [];
56603
57211
  let _resolvedTarget;
56604
57212
  try {
56605
- _resolvedTarget = path44.resolve(targetFile);
57213
+ _resolvedTarget = path45.resolve(targetFile);
56606
57214
  } catch {
56607
57215
  _resolvedTarget = targetFile;
56608
57216
  }
56609
- const targetBasename = path44.basename(targetFile, path44.extname(targetFile));
57217
+ const targetBasename = path45.basename(targetFile, path45.extname(targetFile));
56610
57218
  const targetWithExt = targetFile;
56611
57219
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
56612
- const normalizedTargetWithExt = path44.normalize(targetWithExt).replace(/\\/g, "/");
56613
- const normalizedTargetWithoutExt = path44.normalize(targetWithoutExt).replace(/\\/g, "/");
57220
+ const normalizedTargetWithExt = path45.normalize(targetWithExt).replace(/\\/g, "/");
57221
+ const normalizedTargetWithoutExt = path45.normalize(targetWithoutExt).replace(/\\/g, "/");
56614
57222
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
56615
57223
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
56616
57224
  const modulePath = match[1] || match[2] || match[3];
@@ -56633,9 +57241,9 @@ function parseImports(content, targetFile, targetSymbol) {
56633
57241
  }
56634
57242
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
56635
57243
  let isMatch = false;
56636
- const _targetDir = path44.dirname(targetFile);
56637
- const targetExt = path44.extname(targetFile);
56638
- const targetBasenameNoExt = path44.basename(targetFile, targetExt);
57244
+ const _targetDir = path45.dirname(targetFile);
57245
+ const targetExt = path45.extname(targetFile);
57246
+ const targetBasenameNoExt = path45.basename(targetFile, targetExt);
56639
57247
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
56640
57248
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
56641
57249
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -56675,7 +57283,7 @@ function parseImports(content, targetFile, targetSymbol) {
56675
57283
  }
56676
57284
  return imports;
56677
57285
  }
56678
- var SKIP_DIRECTORIES2 = new Set([
57286
+ var SKIP_DIRECTORIES3 = new Set([
56679
57287
  "node_modules",
56680
57288
  ".git",
56681
57289
  "dist",
@@ -56692,7 +57300,7 @@ var SKIP_DIRECTORIES2 = new Set([
56692
57300
  function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
56693
57301
  let entries;
56694
57302
  try {
56695
- entries = fs31.readdirSync(dir);
57303
+ entries = fs33.readdirSync(dir);
56696
57304
  } catch (e) {
56697
57305
  stats.fileErrors.push({
56698
57306
  path: dir,
@@ -56702,14 +57310,14 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
56702
57310
  }
56703
57311
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
56704
57312
  for (const entry of entries) {
56705
- if (SKIP_DIRECTORIES2.has(entry)) {
56706
- stats.skippedDirs.push(path44.join(dir, entry));
57313
+ if (SKIP_DIRECTORIES3.has(entry)) {
57314
+ stats.skippedDirs.push(path45.join(dir, entry));
56707
57315
  continue;
56708
57316
  }
56709
- const fullPath = path44.join(dir, entry);
57317
+ const fullPath = path45.join(dir, entry);
56710
57318
  let stat2;
56711
57319
  try {
56712
- stat2 = fs31.statSync(fullPath);
57320
+ stat2 = fs33.statSync(fullPath);
56713
57321
  } catch (e) {
56714
57322
  stats.fileErrors.push({
56715
57323
  path: fullPath,
@@ -56720,7 +57328,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
56720
57328
  if (stat2.isDirectory()) {
56721
57329
  findSourceFiles(fullPath, files, stats);
56722
57330
  } else if (stat2.isFile()) {
56723
- const ext = path44.extname(fullPath).toLowerCase();
57331
+ const ext = path45.extname(fullPath).toLowerCase();
56724
57332
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
56725
57333
  files.push(fullPath);
56726
57334
  }
@@ -56777,8 +57385,8 @@ var imports = createSwarmTool({
56777
57385
  return JSON.stringify(errorResult, null, 2);
56778
57386
  }
56779
57387
  try {
56780
- const targetFile = path44.resolve(file3);
56781
- if (!fs31.existsSync(targetFile)) {
57388
+ const targetFile = path45.resolve(file3);
57389
+ if (!fs33.existsSync(targetFile)) {
56782
57390
  const errorResult = {
56783
57391
  error: `target file not found: ${file3}`,
56784
57392
  target: file3,
@@ -56788,7 +57396,7 @@ var imports = createSwarmTool({
56788
57396
  };
56789
57397
  return JSON.stringify(errorResult, null, 2);
56790
57398
  }
56791
- const targetStat = fs31.statSync(targetFile);
57399
+ const targetStat = fs33.statSync(targetFile);
56792
57400
  if (!targetStat.isFile()) {
56793
57401
  const errorResult = {
56794
57402
  error: "target must be a file, not a directory",
@@ -56799,7 +57407,7 @@ var imports = createSwarmTool({
56799
57407
  };
56800
57408
  return JSON.stringify(errorResult, null, 2);
56801
57409
  }
56802
- const baseDir = path44.dirname(targetFile);
57410
+ const baseDir = path45.dirname(targetFile);
56803
57411
  const scanStats = {
56804
57412
  skippedDirs: [],
56805
57413
  skippedFiles: 0,
@@ -56814,12 +57422,12 @@ var imports = createSwarmTool({
56814
57422
  if (consumers.length >= MAX_CONSUMERS)
56815
57423
  break;
56816
57424
  try {
56817
- const stat2 = fs31.statSync(filePath);
57425
+ const stat2 = fs33.statSync(filePath);
56818
57426
  if (stat2.size > MAX_FILE_SIZE_BYTES4) {
56819
57427
  skippedFileCount++;
56820
57428
  continue;
56821
57429
  }
56822
- const buffer = fs31.readFileSync(filePath);
57430
+ const buffer = fs33.readFileSync(filePath);
56823
57431
  if (isBinaryFile2(filePath, buffer)) {
56824
57432
  skippedFileCount++;
56825
57433
  continue;
@@ -56991,7 +57599,7 @@ var knowledgeAdd = createSwarmTool({
56991
57599
  });
56992
57600
  // src/tools/knowledge-query.ts
56993
57601
  init_dist();
56994
- import { existsSync as existsSync28 } from "fs";
57602
+ import { existsSync as existsSync29 } from "fs";
56995
57603
  init_create_tool();
56996
57604
  var DEFAULT_LIMIT = 10;
56997
57605
  var MAX_LESSON_LENGTH = 200;
@@ -57061,14 +57669,14 @@ function validateLimit(limit) {
57061
57669
  }
57062
57670
  async function readSwarmKnowledge(directory) {
57063
57671
  const swarmPath = resolveSwarmKnowledgePath(directory);
57064
- if (!existsSync28(swarmPath)) {
57672
+ if (!existsSync29(swarmPath)) {
57065
57673
  return [];
57066
57674
  }
57067
57675
  return readKnowledge(swarmPath);
57068
57676
  }
57069
57677
  async function readHiveKnowledge() {
57070
57678
  const hivePath = resolveHiveKnowledgePath();
57071
- if (!existsSync28(hivePath)) {
57679
+ if (!existsSync29(hivePath)) {
57072
57680
  return [];
57073
57681
  }
57074
57682
  return readKnowledge(hivePath);
@@ -57368,8 +57976,8 @@ init_dist();
57368
57976
  init_config();
57369
57977
  init_schema();
57370
57978
  init_manager();
57371
- import * as fs32 from "fs";
57372
- import * as path45 from "path";
57979
+ import * as fs34 from "fs";
57980
+ import * as path46 from "path";
57373
57981
  init_utils2();
57374
57982
  init_create_tool();
57375
57983
  function safeWarn(message, error93) {
@@ -57588,7 +58196,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
57588
58196
  };
57589
58197
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
57590
58198
  try {
57591
- const projectName = path45.basename(dir);
58199
+ const projectName = path46.basename(dir);
57592
58200
  await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
57593
58201
  } catch (error93) {
57594
58202
  safeWarn("[phase_complete] Failed to curate lessons from retrospective:", error93);
@@ -57628,7 +58236,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
57628
58236
  if (agentsMissing.length > 0) {
57629
58237
  try {
57630
58238
  const planPath = validateSwarmPath(dir, "plan.json");
57631
- const planRaw = fs32.readFileSync(planPath, "utf-8");
58239
+ const planRaw = fs34.readFileSync(planPath, "utf-8");
57632
58240
  const plan = JSON.parse(planRaw);
57633
58241
  const targetPhase = plan.phases.find((p) => p.id === phase);
57634
58242
  if (targetPhase && targetPhase.tasks.length > 0 && targetPhase.tasks.every((t) => t.status === "completed")) {
@@ -57659,7 +58267,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
57659
58267
  if (phaseCompleteConfig.regression_sweep?.enforce) {
57660
58268
  try {
57661
58269
  const planPath = validateSwarmPath(dir, "plan.json");
57662
- const planRaw = fs32.readFileSync(planPath, "utf-8");
58270
+ const planRaw = fs34.readFileSync(planPath, "utf-8");
57663
58271
  const plan = JSON.parse(planRaw);
57664
58272
  const targetPhase = plan.phases.find((p) => p.id === phase);
57665
58273
  if (targetPhase) {
@@ -57697,7 +58305,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
57697
58305
  };
57698
58306
  try {
57699
58307
  const eventsPath = validateSwarmPath(dir, "events.jsonl");
57700
- fs32.appendFileSync(eventsPath, `${JSON.stringify(event)}
58308
+ fs34.appendFileSync(eventsPath, `${JSON.stringify(event)}
57701
58309
  `, "utf-8");
57702
58310
  } catch (writeError) {
57703
58311
  warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
@@ -57716,12 +58324,12 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
57716
58324
  }
57717
58325
  try {
57718
58326
  const planPath = validateSwarmPath(dir, "plan.json");
57719
- const planJson = fs32.readFileSync(planPath, "utf-8");
58327
+ const planJson = fs34.readFileSync(planPath, "utf-8");
57720
58328
  const plan = JSON.parse(planJson);
57721
58329
  const phaseObj = plan.phases.find((p) => p.id === phase);
57722
58330
  if (phaseObj) {
57723
58331
  phaseObj.status = "completed";
57724
- fs32.writeFileSync(planPath, `${JSON.stringify(plan, null, 2)}
58332
+ fs34.writeFileSync(planPath, `${JSON.stringify(plan, null, 2)}
57725
58333
  `, "utf-8");
57726
58334
  }
57727
58335
  } catch (error93) {
@@ -57740,6 +58348,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
57740
58348
  agentsMissing,
57741
58349
  warnings
57742
58350
  };
58351
+ await flushPendingSnapshot(dir);
57743
58352
  return JSON.stringify({ ...result, timestamp: event.timestamp, duration_ms: durationMs }, null, 2);
57744
58353
  }
57745
58354
  var phase_complete = createSwarmTool({
@@ -57774,8 +58383,8 @@ init_dist();
57774
58383
  init_discovery();
57775
58384
  init_utils();
57776
58385
  init_create_tool();
57777
- import * as fs33 from "fs";
57778
- import * as path46 from "path";
58386
+ import * as fs35 from "fs";
58387
+ import * as path47 from "path";
57779
58388
  var MAX_OUTPUT_BYTES5 = 52428800;
57780
58389
  var AUDIT_TIMEOUT_MS = 120000;
57781
58390
  function isValidEcosystem(value) {
@@ -57793,28 +58402,28 @@ function validateArgs3(args2) {
57793
58402
  function detectEcosystems(directory) {
57794
58403
  const ecosystems = [];
57795
58404
  const cwd = directory;
57796
- if (fs33.existsSync(path46.join(cwd, "package.json"))) {
58405
+ if (fs35.existsSync(path47.join(cwd, "package.json"))) {
57797
58406
  ecosystems.push("npm");
57798
58407
  }
57799
- if (fs33.existsSync(path46.join(cwd, "pyproject.toml")) || fs33.existsSync(path46.join(cwd, "requirements.txt"))) {
58408
+ if (fs35.existsSync(path47.join(cwd, "pyproject.toml")) || fs35.existsSync(path47.join(cwd, "requirements.txt"))) {
57800
58409
  ecosystems.push("pip");
57801
58410
  }
57802
- if (fs33.existsSync(path46.join(cwd, "Cargo.toml"))) {
58411
+ if (fs35.existsSync(path47.join(cwd, "Cargo.toml"))) {
57803
58412
  ecosystems.push("cargo");
57804
58413
  }
57805
- if (fs33.existsSync(path46.join(cwd, "go.mod"))) {
58414
+ if (fs35.existsSync(path47.join(cwd, "go.mod"))) {
57806
58415
  ecosystems.push("go");
57807
58416
  }
57808
58417
  try {
57809
- const files = fs33.readdirSync(cwd);
58418
+ const files = fs35.readdirSync(cwd);
57810
58419
  if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
57811
58420
  ecosystems.push("dotnet");
57812
58421
  }
57813
58422
  } catch {}
57814
- if (fs33.existsSync(path46.join(cwd, "Gemfile")) || fs33.existsSync(path46.join(cwd, "Gemfile.lock"))) {
58423
+ if (fs35.existsSync(path47.join(cwd, "Gemfile")) || fs35.existsSync(path47.join(cwd, "Gemfile.lock"))) {
57815
58424
  ecosystems.push("ruby");
57816
58425
  }
57817
- if (fs33.existsSync(path46.join(cwd, "pubspec.yaml"))) {
58426
+ if (fs35.existsSync(path47.join(cwd, "pubspec.yaml"))) {
57818
58427
  ecosystems.push("dart");
57819
58428
  }
57820
58429
  return ecosystems;
@@ -58876,8 +59485,8 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
58876
59485
  ]);
58877
59486
  // src/tools/pre-check-batch.ts
58878
59487
  init_dist();
58879
- import * as fs36 from "fs";
58880
- import * as path49 from "path";
59488
+ import * as fs38 from "fs";
59489
+ import * as path50 from "path";
58881
59490
 
58882
59491
  // node_modules/yocto-queue/index.js
58883
59492
  class Node2 {
@@ -59022,8 +59631,8 @@ function pLimit(concurrency) {
59022
59631
  },
59023
59632
  map: {
59024
59633
  async value(iterable, function_) {
59025
- const promises3 = Array.from(iterable, (value, index) => this(function_, value, index));
59026
- return Promise.all(promises3);
59634
+ const promises4 = Array.from(iterable, (value, index) => this(function_, value, index));
59635
+ return Promise.all(promises4);
59027
59636
  }
59028
59637
  }
59029
59638
  });
@@ -59036,6 +59645,7 @@ function validateConcurrency(concurrency) {
59036
59645
  }
59037
59646
 
59038
59647
  // src/tools/pre-check-batch.ts
59648
+ init_manager();
59039
59649
  init_utils();
59040
59650
  init_create_tool();
59041
59651
  init_lint();
@@ -59044,8 +59654,8 @@ init_lint();
59044
59654
  init_manager();
59045
59655
 
59046
59656
  // src/quality/metrics.ts
59047
- import * as fs34 from "fs";
59048
- import * as path47 from "path";
59657
+ import * as fs36 from "fs";
59658
+ import * as path48 from "path";
59049
59659
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
59050
59660
  var MIN_DUPLICATION_LINES = 10;
59051
59661
  function estimateCyclomaticComplexity(content) {
@@ -59083,11 +59693,11 @@ function estimateCyclomaticComplexity(content) {
59083
59693
  }
59084
59694
  function getComplexityForFile2(filePath) {
59085
59695
  try {
59086
- const stat2 = fs34.statSync(filePath);
59696
+ const stat2 = fs36.statSync(filePath);
59087
59697
  if (stat2.size > MAX_FILE_SIZE_BYTES5) {
59088
59698
  return null;
59089
59699
  }
59090
- const content = fs34.readFileSync(filePath, "utf-8");
59700
+ const content = fs36.readFileSync(filePath, "utf-8");
59091
59701
  return estimateCyclomaticComplexity(content);
59092
59702
  } catch {
59093
59703
  return null;
@@ -59097,8 +59707,8 @@ async function computeComplexityDelta(files, workingDir) {
59097
59707
  let totalComplexity = 0;
59098
59708
  const analyzedFiles = [];
59099
59709
  for (const file3 of files) {
59100
- const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
59101
- if (!fs34.existsSync(fullPath)) {
59710
+ const fullPath = path48.isAbsolute(file3) ? file3 : path48.join(workingDir, file3);
59711
+ if (!fs36.existsSync(fullPath)) {
59102
59712
  continue;
59103
59713
  }
59104
59714
  const complexity = getComplexityForFile2(fullPath);
@@ -59219,8 +59829,8 @@ function countGoExports(content) {
59219
59829
  }
59220
59830
  function getExportCountForFile(filePath) {
59221
59831
  try {
59222
- const content = fs34.readFileSync(filePath, "utf-8");
59223
- const ext = path47.extname(filePath).toLowerCase();
59832
+ const content = fs36.readFileSync(filePath, "utf-8");
59833
+ const ext = path48.extname(filePath).toLowerCase();
59224
59834
  switch (ext) {
59225
59835
  case ".ts":
59226
59836
  case ".tsx":
@@ -59246,8 +59856,8 @@ async function computePublicApiDelta(files, workingDir) {
59246
59856
  let totalExports = 0;
59247
59857
  const analyzedFiles = [];
59248
59858
  for (const file3 of files) {
59249
- const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
59250
- if (!fs34.existsSync(fullPath)) {
59859
+ const fullPath = path48.isAbsolute(file3) ? file3 : path48.join(workingDir, file3);
59860
+ if (!fs36.existsSync(fullPath)) {
59251
59861
  continue;
59252
59862
  }
59253
59863
  const exports = getExportCountForFile(fullPath);
@@ -59280,16 +59890,16 @@ async function computeDuplicationRatio(files, workingDir) {
59280
59890
  let duplicateLines = 0;
59281
59891
  const analyzedFiles = [];
59282
59892
  for (const file3 of files) {
59283
- const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
59284
- if (!fs34.existsSync(fullPath)) {
59893
+ const fullPath = path48.isAbsolute(file3) ? file3 : path48.join(workingDir, file3);
59894
+ if (!fs36.existsSync(fullPath)) {
59285
59895
  continue;
59286
59896
  }
59287
59897
  try {
59288
- const stat2 = fs34.statSync(fullPath);
59898
+ const stat2 = fs36.statSync(fullPath);
59289
59899
  if (stat2.size > MAX_FILE_SIZE_BYTES5) {
59290
59900
  continue;
59291
59901
  }
59292
- const content = fs34.readFileSync(fullPath, "utf-8");
59902
+ const content = fs36.readFileSync(fullPath, "utf-8");
59293
59903
  const lines = content.split(`
59294
59904
  `).filter((line) => line.trim().length > 0);
59295
59905
  if (lines.length < MIN_DUPLICATION_LINES) {
@@ -59313,8 +59923,8 @@ function countCodeLines(content) {
59313
59923
  return lines.length;
59314
59924
  }
59315
59925
  function isTestFile(filePath) {
59316
- const basename8 = path47.basename(filePath);
59317
- const _ext = path47.extname(filePath).toLowerCase();
59926
+ const basename9 = path48.basename(filePath);
59927
+ const _ext = path48.extname(filePath).toLowerCase();
59318
59928
  const testPatterns = [
59319
59929
  ".test.",
59320
59930
  ".spec.",
@@ -59329,7 +59939,7 @@ function isTestFile(filePath) {
59329
59939
  ".spec.jsx"
59330
59940
  ];
59331
59941
  for (const pattern of testPatterns) {
59332
- if (basename8.includes(pattern)) {
59942
+ if (basename9.includes(pattern)) {
59333
59943
  return true;
59334
59944
  }
59335
59945
  }
@@ -59395,8 +60005,8 @@ function matchGlobSegment(globSegments, pathSegments) {
59395
60005
  }
59396
60006
  return gIndex === globSegments.length && pIndex === pathSegments.length;
59397
60007
  }
59398
- function matchesGlobSegment(path48, glob) {
59399
- const normalizedPath = path48.replace(/\\/g, "/");
60008
+ function matchesGlobSegment(path49, glob) {
60009
+ const normalizedPath = path49.replace(/\\/g, "/");
59400
60010
  const normalizedGlob = glob.replace(/\\/g, "/");
59401
60011
  if (normalizedPath.includes("//")) {
59402
60012
  return false;
@@ -59427,8 +60037,8 @@ function simpleGlobToRegex2(glob) {
59427
60037
  function hasGlobstar(glob) {
59428
60038
  return glob.includes("**");
59429
60039
  }
59430
- function globMatches(path48, glob) {
59431
- const normalizedPath = path48.replace(/\\/g, "/");
60040
+ function globMatches(path49, glob) {
60041
+ const normalizedPath = path49.replace(/\\/g, "/");
59432
60042
  if (!glob || glob === "") {
59433
60043
  if (normalizedPath.includes("//")) {
59434
60044
  return false;
@@ -59464,31 +60074,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
59464
60074
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
59465
60075
  let testLines = 0;
59466
60076
  let codeLines = 0;
59467
- const srcDir = path47.join(workingDir, "src");
59468
- if (fs34.existsSync(srcDir)) {
60077
+ const srcDir = path48.join(workingDir, "src");
60078
+ if (fs36.existsSync(srcDir)) {
59469
60079
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
59470
60080
  codeLines += lines;
59471
60081
  });
59472
60082
  }
59473
60083
  const possibleSrcDirs = ["lib", "app", "source", "core"];
59474
60084
  for (const dir of possibleSrcDirs) {
59475
- const dirPath = path47.join(workingDir, dir);
59476
- if (fs34.existsSync(dirPath)) {
60085
+ const dirPath = path48.join(workingDir, dir);
60086
+ if (fs36.existsSync(dirPath)) {
59477
60087
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
59478
60088
  codeLines += lines;
59479
60089
  });
59480
60090
  }
59481
60091
  }
59482
- const testsDir = path47.join(workingDir, "tests");
59483
- if (fs34.existsSync(testsDir)) {
60092
+ const testsDir = path48.join(workingDir, "tests");
60093
+ if (fs36.existsSync(testsDir)) {
59484
60094
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
59485
60095
  testLines += lines;
59486
60096
  });
59487
60097
  }
59488
60098
  const possibleTestDirs = ["test", "__tests__", "specs"];
59489
60099
  for (const dir of possibleTestDirs) {
59490
- const dirPath = path47.join(workingDir, dir);
59491
- if (fs34.existsSync(dirPath) && dirPath !== testsDir) {
60100
+ const dirPath = path48.join(workingDir, dir);
60101
+ if (fs36.existsSync(dirPath) && dirPath !== testsDir) {
59492
60102
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
59493
60103
  testLines += lines;
59494
60104
  });
@@ -59500,9 +60110,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
59500
60110
  }
59501
60111
  async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
59502
60112
  try {
59503
- const entries = fs34.readdirSync(dirPath, { withFileTypes: true });
60113
+ const entries = fs36.readdirSync(dirPath, { withFileTypes: true });
59504
60114
  for (const entry of entries) {
59505
- const fullPath = path47.join(dirPath, entry.name);
60115
+ const fullPath = path48.join(dirPath, entry.name);
59506
60116
  if (entry.isDirectory()) {
59507
60117
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
59508
60118
  continue;
@@ -59510,7 +60120,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
59510
60120
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
59511
60121
  } else if (entry.isFile()) {
59512
60122
  const relativePath = fullPath.replace(`${dirPath}/`, "");
59513
- const ext = path47.extname(entry.name).toLowerCase();
60123
+ const ext = path48.extname(entry.name).toLowerCase();
59514
60124
  const validExts = [
59515
60125
  ".ts",
59516
60126
  ".tsx",
@@ -59546,7 +60156,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
59546
60156
  continue;
59547
60157
  }
59548
60158
  try {
59549
- const content = fs34.readFileSync(fullPath, "utf-8");
60159
+ const content = fs36.readFileSync(fullPath, "utf-8");
59550
60160
  const lines = countCodeLines(content);
59551
60161
  callback(lines);
59552
60162
  } catch {}
@@ -59760,8 +60370,8 @@ async function qualityBudget(input, directory) {
59760
60370
  init_dist();
59761
60371
  init_manager();
59762
60372
  init_detector();
59763
- import * as fs35 from "fs";
59764
- import * as path48 from "path";
60373
+ import * as fs37 from "fs";
60374
+ import * as path49 from "path";
59765
60375
  import { extname as extname9 } from "path";
59766
60376
 
59767
60377
  // src/sast/rules/c.ts
@@ -60628,17 +61238,17 @@ var SEVERITY_ORDER = {
60628
61238
  };
60629
61239
  function shouldSkipFile(filePath) {
60630
61240
  try {
60631
- const stats = fs35.statSync(filePath);
61241
+ const stats = fs37.statSync(filePath);
60632
61242
  if (stats.size > MAX_FILE_SIZE_BYTES6) {
60633
61243
  return { skip: true, reason: "file too large" };
60634
61244
  }
60635
61245
  if (stats.size === 0) {
60636
61246
  return { skip: true, reason: "empty file" };
60637
61247
  }
60638
- const fd = fs35.openSync(filePath, "r");
61248
+ const fd = fs37.openSync(filePath, "r");
60639
61249
  const buffer = Buffer.alloc(8192);
60640
- const bytesRead = fs35.readSync(fd, buffer, 0, 8192, 0);
60641
- fs35.closeSync(fd);
61250
+ const bytesRead = fs37.readSync(fd, buffer, 0, 8192, 0);
61251
+ fs37.closeSync(fd);
60642
61252
  if (bytesRead > 0) {
60643
61253
  let nullCount = 0;
60644
61254
  for (let i2 = 0;i2 < bytesRead; i2++) {
@@ -60677,7 +61287,7 @@ function countBySeverity(findings) {
60677
61287
  }
60678
61288
  function scanFileWithTierA(filePath, language) {
60679
61289
  try {
60680
- const content = fs35.readFileSync(filePath, "utf-8");
61290
+ const content = fs37.readFileSync(filePath, "utf-8");
60681
61291
  const findings = executeRulesSync(filePath, content, language);
60682
61292
  return findings.map((f) => ({
60683
61293
  rule_id: f.rule_id,
@@ -60724,8 +61334,8 @@ async function sastScan(input, directory, config3) {
60724
61334
  _filesSkipped++;
60725
61335
  continue;
60726
61336
  }
60727
- const resolvedPath = path48.isAbsolute(filePath) ? filePath : path48.resolve(directory, filePath);
60728
- if (!fs35.existsSync(resolvedPath)) {
61337
+ const resolvedPath = path49.isAbsolute(filePath) ? filePath : path49.resolve(directory, filePath);
61338
+ if (!fs37.existsSync(resolvedPath)) {
60729
61339
  _filesSkipped++;
60730
61340
  continue;
60731
61341
  }
@@ -60923,18 +61533,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
60923
61533
  let resolved;
60924
61534
  const isWinAbs = isWindowsAbsolutePath(inputPath);
60925
61535
  if (isWinAbs) {
60926
- resolved = path49.win32.resolve(inputPath);
60927
- } else if (path49.isAbsolute(inputPath)) {
60928
- resolved = path49.resolve(inputPath);
61536
+ resolved = path50.win32.resolve(inputPath);
61537
+ } else if (path50.isAbsolute(inputPath)) {
61538
+ resolved = path50.resolve(inputPath);
60929
61539
  } else {
60930
- resolved = path49.resolve(baseDir, inputPath);
61540
+ resolved = path50.resolve(baseDir, inputPath);
60931
61541
  }
60932
- const workspaceResolved = path49.resolve(workspaceDir);
61542
+ const workspaceResolved = path50.resolve(workspaceDir);
60933
61543
  let relative6;
60934
61544
  if (isWinAbs) {
60935
- relative6 = path49.win32.relative(workspaceResolved, resolved);
61545
+ relative6 = path50.win32.relative(workspaceResolved, resolved);
60936
61546
  } else {
60937
- relative6 = path49.relative(workspaceResolved, resolved);
61547
+ relative6 = path50.relative(workspaceResolved, resolved);
60938
61548
  }
60939
61549
  if (relative6.startsWith("..")) {
60940
61550
  return "path traversal detected";
@@ -60995,13 +61605,13 @@ async function runLintWrapped(files, directory, _config) {
60995
61605
  }
60996
61606
  async function runLintOnFiles(linter, files, workspaceDir) {
60997
61607
  const isWindows = process.platform === "win32";
60998
- const binDir = path49.join(workspaceDir, "node_modules", ".bin");
61608
+ const binDir = path50.join(workspaceDir, "node_modules", ".bin");
60999
61609
  const validatedFiles = [];
61000
61610
  for (const file3 of files) {
61001
61611
  if (typeof file3 !== "string") {
61002
61612
  continue;
61003
61613
  }
61004
- const resolvedPath = path49.resolve(file3);
61614
+ const resolvedPath = path50.resolve(file3);
61005
61615
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
61006
61616
  if (validationError) {
61007
61617
  continue;
@@ -61019,10 +61629,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
61019
61629
  }
61020
61630
  let command;
61021
61631
  if (linter === "biome") {
61022
- const biomeBin = isWindows ? path49.join(binDir, "biome.EXE") : path49.join(binDir, "biome");
61632
+ const biomeBin = isWindows ? path50.join(binDir, "biome.EXE") : path50.join(binDir, "biome");
61023
61633
  command = [biomeBin, "check", ...validatedFiles];
61024
61634
  } else {
61025
- const eslintBin = isWindows ? path49.join(binDir, "eslint.cmd") : path49.join(binDir, "eslint");
61635
+ const eslintBin = isWindows ? path50.join(binDir, "eslint.cmd") : path50.join(binDir, "eslint");
61026
61636
  command = [eslintBin, ...validatedFiles];
61027
61637
  }
61028
61638
  try {
@@ -61159,7 +61769,7 @@ async function runSecretscanWithFiles(files, directory) {
61159
61769
  skippedFiles++;
61160
61770
  continue;
61161
61771
  }
61162
- const resolvedPath = path49.resolve(file3);
61772
+ const resolvedPath = path50.resolve(file3);
61163
61773
  const validationError = validatePath(resolvedPath, directory, directory);
61164
61774
  if (validationError) {
61165
61775
  skippedFiles++;
@@ -61177,14 +61787,14 @@ async function runSecretscanWithFiles(files, directory) {
61177
61787
  };
61178
61788
  }
61179
61789
  for (const file3 of validatedFiles) {
61180
- const ext = path49.extname(file3).toLowerCase();
61790
+ const ext = path50.extname(file3).toLowerCase();
61181
61791
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
61182
61792
  skippedFiles++;
61183
61793
  continue;
61184
61794
  }
61185
61795
  let stat2;
61186
61796
  try {
61187
- stat2 = fs36.statSync(file3);
61797
+ stat2 = fs38.statSync(file3);
61188
61798
  } catch {
61189
61799
  skippedFiles++;
61190
61800
  continue;
@@ -61195,7 +61805,7 @@ async function runSecretscanWithFiles(files, directory) {
61195
61805
  }
61196
61806
  let content;
61197
61807
  try {
61198
- const buffer = fs36.readFileSync(file3);
61808
+ const buffer = fs38.readFileSync(file3);
61199
61809
  if (buffer.includes(0)) {
61200
61810
  skippedFiles++;
61201
61811
  continue;
@@ -61336,7 +61946,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
61336
61946
  warn(`pre_check_batch: Invalid file path: ${file3}`);
61337
61947
  continue;
61338
61948
  }
61339
- changedFiles.push(path49.resolve(directory, file3));
61949
+ changedFiles.push(path50.resolve(directory, file3));
61340
61950
  }
61341
61951
  if (changedFiles.length === 0) {
61342
61952
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -61383,6 +61993,26 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
61383
61993
  gatesPassed = false;
61384
61994
  warn(`pre_check_batch: Secretscan error - GATE FAILED: ${secretscanResult.error}`);
61385
61995
  }
61996
+ if (secretscanResult.ran && secretscanResult.result) {
61997
+ try {
61998
+ const scanResult = secretscanResult.result;
61999
+ const secretscanEvidence = {
62000
+ task_id: "secretscan",
62001
+ type: "secretscan",
62002
+ timestamp: new Date().toISOString(),
62003
+ agent: "pre_check_batch",
62004
+ verdict: scanResult.count > 0 ? "fail" : "pass",
62005
+ summary: `Secretscan: ${scanResult.count} finding(s), ${scanResult.files_scanned ?? 0} files scanned, ${scanResult.skipped_files ?? 0} skipped`,
62006
+ findings_count: scanResult.count,
62007
+ scan_directory: scanResult.scan_dir,
62008
+ files_scanned: scanResult.files_scanned,
62009
+ skipped_files: scanResult.skipped_files
62010
+ };
62011
+ await saveEvidence(directory, "secretscan", secretscanEvidence);
62012
+ } catch (e) {
62013
+ warn(`Failed to persist secretscan evidence: ${e instanceof Error ? e.message : String(e)}`);
62014
+ }
62015
+ }
61386
62016
  if (sastScanResult.ran && sastScanResult.result) {
61387
62017
  if (sastScanResult.result.verdict === "fail") {
61388
62018
  gatesPassed = false;
@@ -61487,7 +62117,7 @@ var pre_check_batch = createSwarmTool({
61487
62117
  };
61488
62118
  return JSON.stringify(errorResult, null, 2);
61489
62119
  }
61490
- const resolvedDirectory = path49.resolve(typedArgs.directory);
62120
+ const resolvedDirectory = path50.resolve(typedArgs.directory);
61491
62121
  const workspaceAnchor = resolvedDirectory;
61492
62122
  const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
61493
62123
  if (dirError) {
@@ -61595,8 +62225,8 @@ ${paginatedContent}`;
61595
62225
  init_tool();
61596
62226
  init_manager2();
61597
62227
  init_create_tool();
61598
- import * as fs37 from "fs";
61599
- import * as path50 from "path";
62228
+ import * as fs39 from "fs";
62229
+ import * as path51 from "path";
61600
62230
  function detectPlaceholderContent(args2) {
61601
62231
  const issues = [];
61602
62232
  const placeholderPattern = /^\[\w[\w\s]*\]$/;
@@ -61700,19 +62330,19 @@ async function executeSavePlan(args2, fallbackDir) {
61700
62330
  try {
61701
62331
  await savePlan(dir, plan);
61702
62332
  try {
61703
- const markerPath = path50.join(dir, ".swarm", ".plan-write-marker");
62333
+ const markerPath = path51.join(dir, ".swarm", ".plan-write-marker");
61704
62334
  const marker = JSON.stringify({
61705
62335
  source: "save_plan",
61706
62336
  timestamp: new Date().toISOString(),
61707
62337
  phases_count: plan.phases.length,
61708
62338
  tasks_count: tasksCount
61709
62339
  });
61710
- await fs37.promises.writeFile(markerPath, marker, "utf8");
62340
+ await fs39.promises.writeFile(markerPath, marker, "utf8");
61711
62341
  } catch {}
61712
62342
  return {
61713
62343
  success: true,
61714
62344
  message: "Plan saved successfully",
61715
- plan_path: path50.join(dir, ".swarm", "plan.json"),
62345
+ plan_path: path51.join(dir, ".swarm", "plan.json"),
61716
62346
  phases_count: plan.phases.length,
61717
62347
  tasks_count: tasksCount
61718
62348
  };
@@ -61750,8 +62380,8 @@ var save_plan = createSwarmTool({
61750
62380
  // src/tools/sbom-generate.ts
61751
62381
  init_dist();
61752
62382
  init_manager();
61753
- import * as fs38 from "fs";
61754
- import * as path51 from "path";
62383
+ import * as fs40 from "fs";
62384
+ import * as path52 from "path";
61755
62385
 
61756
62386
  // src/sbom/detectors/index.ts
61757
62387
  init_utils();
@@ -62597,9 +63227,9 @@ function findManifestFiles(rootDir) {
62597
63227
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
62598
63228
  function searchDir(dir) {
62599
63229
  try {
62600
- const entries = fs38.readdirSync(dir, { withFileTypes: true });
63230
+ const entries = fs40.readdirSync(dir, { withFileTypes: true });
62601
63231
  for (const entry of entries) {
62602
- const fullPath = path51.join(dir, entry.name);
63232
+ const fullPath = path52.join(dir, entry.name);
62603
63233
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
62604
63234
  continue;
62605
63235
  }
@@ -62608,7 +63238,7 @@ function findManifestFiles(rootDir) {
62608
63238
  } else if (entry.isFile()) {
62609
63239
  for (const pattern of patterns) {
62610
63240
  if (simpleGlobToRegex(pattern).test(entry.name)) {
62611
- manifestFiles.push(path51.relative(rootDir, fullPath));
63241
+ manifestFiles.push(path52.relative(rootDir, fullPath));
62612
63242
  break;
62613
63243
  }
62614
63244
  }
@@ -62624,13 +63254,13 @@ function findManifestFilesInDirs(directories, workingDir) {
62624
63254
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
62625
63255
  for (const dir of directories) {
62626
63256
  try {
62627
- const entries = fs38.readdirSync(dir, { withFileTypes: true });
63257
+ const entries = fs40.readdirSync(dir, { withFileTypes: true });
62628
63258
  for (const entry of entries) {
62629
- const fullPath = path51.join(dir, entry.name);
63259
+ const fullPath = path52.join(dir, entry.name);
62630
63260
  if (entry.isFile()) {
62631
63261
  for (const pattern of patterns) {
62632
63262
  if (simpleGlobToRegex(pattern).test(entry.name)) {
62633
- found.push(path51.relative(workingDir, fullPath));
63263
+ found.push(path52.relative(workingDir, fullPath));
62634
63264
  break;
62635
63265
  }
62636
63266
  }
@@ -62643,11 +63273,11 @@ function findManifestFilesInDirs(directories, workingDir) {
62643
63273
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
62644
63274
  const dirs = new Set;
62645
63275
  for (const file3 of changedFiles) {
62646
- let currentDir = path51.dirname(file3);
63276
+ let currentDir = path52.dirname(file3);
62647
63277
  while (true) {
62648
- if (currentDir && currentDir !== "." && currentDir !== path51.sep) {
62649
- dirs.add(path51.join(workingDir, currentDir));
62650
- const parent = path51.dirname(currentDir);
63278
+ if (currentDir && currentDir !== "." && currentDir !== path52.sep) {
63279
+ dirs.add(path52.join(workingDir, currentDir));
63280
+ const parent = path52.dirname(currentDir);
62651
63281
  if (parent === currentDir)
62652
63282
  break;
62653
63283
  currentDir = parent;
@@ -62661,7 +63291,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
62661
63291
  }
62662
63292
  function ensureOutputDir(outputDir) {
62663
63293
  try {
62664
- fs38.mkdirSync(outputDir, { recursive: true });
63294
+ fs40.mkdirSync(outputDir, { recursive: true });
62665
63295
  } catch (error93) {
62666
63296
  if (!error93 || error93.code !== "EEXIST") {
62667
63297
  throw error93;
@@ -62731,7 +63361,7 @@ var sbom_generate = createSwarmTool({
62731
63361
  const changedFiles = obj.changed_files;
62732
63362
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
62733
63363
  const workingDir = directory;
62734
- const outputDir = path51.isAbsolute(relativeOutputDir) ? relativeOutputDir : path51.join(workingDir, relativeOutputDir);
63364
+ const outputDir = path52.isAbsolute(relativeOutputDir) ? relativeOutputDir : path52.join(workingDir, relativeOutputDir);
62735
63365
  let manifestFiles = [];
62736
63366
  if (scope === "all") {
62737
63367
  manifestFiles = findManifestFiles(workingDir);
@@ -62754,11 +63384,11 @@ var sbom_generate = createSwarmTool({
62754
63384
  const processedFiles = [];
62755
63385
  for (const manifestFile of manifestFiles) {
62756
63386
  try {
62757
- const fullPath = path51.isAbsolute(manifestFile) ? manifestFile : path51.join(workingDir, manifestFile);
62758
- if (!fs38.existsSync(fullPath)) {
63387
+ const fullPath = path52.isAbsolute(manifestFile) ? manifestFile : path52.join(workingDir, manifestFile);
63388
+ if (!fs40.existsSync(fullPath)) {
62759
63389
  continue;
62760
63390
  }
62761
- const content = fs38.readFileSync(fullPath, "utf-8");
63391
+ const content = fs40.readFileSync(fullPath, "utf-8");
62762
63392
  const components = detectComponents(manifestFile, content);
62763
63393
  processedFiles.push(manifestFile);
62764
63394
  if (components.length > 0) {
@@ -62771,8 +63401,8 @@ var sbom_generate = createSwarmTool({
62771
63401
  const bom = generateCycloneDX(allComponents);
62772
63402
  const bomJson = serializeCycloneDX(bom);
62773
63403
  const filename = generateSbomFilename();
62774
- const outputPath = path51.join(outputDir, filename);
62775
- fs38.writeFileSync(outputPath, bomJson, "utf-8");
63404
+ const outputPath = path52.join(outputDir, filename);
63405
+ fs40.writeFileSync(outputPath, bomJson, "utf-8");
62776
63406
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
62777
63407
  try {
62778
63408
  const timestamp = new Date().toISOString();
@@ -62814,8 +63444,8 @@ var sbom_generate = createSwarmTool({
62814
63444
  // src/tools/schema-drift.ts
62815
63445
  init_dist();
62816
63446
  init_create_tool();
62817
- import * as fs39 from "fs";
62818
- import * as path52 from "path";
63447
+ import * as fs41 from "fs";
63448
+ import * as path53 from "path";
62819
63449
  var SPEC_CANDIDATES = [
62820
63450
  "openapi.json",
62821
63451
  "openapi.yaml",
@@ -62847,28 +63477,28 @@ function normalizePath2(p) {
62847
63477
  }
62848
63478
  function discoverSpecFile(cwd, specFileArg) {
62849
63479
  if (specFileArg) {
62850
- const resolvedPath = path52.resolve(cwd, specFileArg);
62851
- const normalizedCwd = cwd.endsWith(path52.sep) ? cwd : cwd + path52.sep;
63480
+ const resolvedPath = path53.resolve(cwd, specFileArg);
63481
+ const normalizedCwd = cwd.endsWith(path53.sep) ? cwd : cwd + path53.sep;
62852
63482
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
62853
63483
  throw new Error("Invalid spec_file: path traversal detected");
62854
63484
  }
62855
- const ext = path52.extname(resolvedPath).toLowerCase();
63485
+ const ext = path53.extname(resolvedPath).toLowerCase();
62856
63486
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
62857
63487
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
62858
63488
  }
62859
- const stats = fs39.statSync(resolvedPath);
63489
+ const stats = fs41.statSync(resolvedPath);
62860
63490
  if (stats.size > MAX_SPEC_SIZE) {
62861
63491
  throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
62862
63492
  }
62863
- if (!fs39.existsSync(resolvedPath)) {
63493
+ if (!fs41.existsSync(resolvedPath)) {
62864
63494
  throw new Error(`Spec file not found: ${resolvedPath}`);
62865
63495
  }
62866
63496
  return resolvedPath;
62867
63497
  }
62868
63498
  for (const candidate of SPEC_CANDIDATES) {
62869
- const candidatePath = path52.resolve(cwd, candidate);
62870
- if (fs39.existsSync(candidatePath)) {
62871
- const stats = fs39.statSync(candidatePath);
63499
+ const candidatePath = path53.resolve(cwd, candidate);
63500
+ if (fs41.existsSync(candidatePath)) {
63501
+ const stats = fs41.statSync(candidatePath);
62872
63502
  if (stats.size <= MAX_SPEC_SIZE) {
62873
63503
  return candidatePath;
62874
63504
  }
@@ -62877,8 +63507,8 @@ function discoverSpecFile(cwd, specFileArg) {
62877
63507
  return null;
62878
63508
  }
62879
63509
  function parseSpec(specFile) {
62880
- const content = fs39.readFileSync(specFile, "utf-8");
62881
- const ext = path52.extname(specFile).toLowerCase();
63510
+ const content = fs41.readFileSync(specFile, "utf-8");
63511
+ const ext = path53.extname(specFile).toLowerCase();
62882
63512
  if (ext === ".json") {
62883
63513
  return parseJsonSpec(content);
62884
63514
  }
@@ -62949,12 +63579,12 @@ function extractRoutes(cwd) {
62949
63579
  function walkDir(dir) {
62950
63580
  let entries;
62951
63581
  try {
62952
- entries = fs39.readdirSync(dir, { withFileTypes: true });
63582
+ entries = fs41.readdirSync(dir, { withFileTypes: true });
62953
63583
  } catch {
62954
63584
  return;
62955
63585
  }
62956
63586
  for (const entry of entries) {
62957
- const fullPath = path52.join(dir, entry.name);
63587
+ const fullPath = path53.join(dir, entry.name);
62958
63588
  if (entry.isSymbolicLink()) {
62959
63589
  continue;
62960
63590
  }
@@ -62964,7 +63594,7 @@ function extractRoutes(cwd) {
62964
63594
  }
62965
63595
  walkDir(fullPath);
62966
63596
  } else if (entry.isFile()) {
62967
- const ext = path52.extname(entry.name).toLowerCase();
63597
+ const ext = path53.extname(entry.name).toLowerCase();
62968
63598
  const baseName = entry.name.toLowerCase();
62969
63599
  if (![".ts", ".js", ".mjs"].includes(ext)) {
62970
63600
  continue;
@@ -62982,7 +63612,7 @@ function extractRoutes(cwd) {
62982
63612
  }
62983
63613
  function extractRoutesFromFile(filePath) {
62984
63614
  const routes = [];
62985
- const content = fs39.readFileSync(filePath, "utf-8");
63615
+ const content = fs41.readFileSync(filePath, "utf-8");
62986
63616
  const lines = content.split(/\r?\n/);
62987
63617
  const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
62988
63618
  const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
@@ -63133,8 +63763,8 @@ init_secretscan();
63133
63763
  // src/tools/symbols.ts
63134
63764
  init_tool();
63135
63765
  init_create_tool();
63136
- import * as fs40 from "fs";
63137
- import * as path53 from "path";
63766
+ import * as fs42 from "fs";
63767
+ import * as path54 from "path";
63138
63768
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
63139
63769
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
63140
63770
  function containsControlCharacters(str) {
@@ -63163,11 +63793,11 @@ function containsWindowsAttacks(str) {
63163
63793
  }
63164
63794
  function isPathInWorkspace(filePath, workspace) {
63165
63795
  try {
63166
- const resolvedPath = path53.resolve(workspace, filePath);
63167
- const realWorkspace = fs40.realpathSync(workspace);
63168
- const realResolvedPath = fs40.realpathSync(resolvedPath);
63169
- const relativePath = path53.relative(realWorkspace, realResolvedPath);
63170
- if (relativePath.startsWith("..") || path53.isAbsolute(relativePath)) {
63796
+ const resolvedPath = path54.resolve(workspace, filePath);
63797
+ const realWorkspace = fs42.realpathSync(workspace);
63798
+ const realResolvedPath = fs42.realpathSync(resolvedPath);
63799
+ const relativePath = path54.relative(realWorkspace, realResolvedPath);
63800
+ if (relativePath.startsWith("..") || path54.isAbsolute(relativePath)) {
63171
63801
  return false;
63172
63802
  }
63173
63803
  return true;
@@ -63179,17 +63809,17 @@ function validatePathForRead(filePath, workspace) {
63179
63809
  return isPathInWorkspace(filePath, workspace);
63180
63810
  }
63181
63811
  function extractTSSymbols(filePath, cwd) {
63182
- const fullPath = path53.join(cwd, filePath);
63812
+ const fullPath = path54.join(cwd, filePath);
63183
63813
  if (!validatePathForRead(fullPath, cwd)) {
63184
63814
  return [];
63185
63815
  }
63186
63816
  let content;
63187
63817
  try {
63188
- const stats = fs40.statSync(fullPath);
63818
+ const stats = fs42.statSync(fullPath);
63189
63819
  if (stats.size > MAX_FILE_SIZE_BYTES7) {
63190
63820
  throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
63191
63821
  }
63192
- content = fs40.readFileSync(fullPath, "utf-8");
63822
+ content = fs42.readFileSync(fullPath, "utf-8");
63193
63823
  } catch {
63194
63824
  return [];
63195
63825
  }
@@ -63331,17 +63961,17 @@ function extractTSSymbols(filePath, cwd) {
63331
63961
  });
63332
63962
  }
63333
63963
  function extractPythonSymbols(filePath, cwd) {
63334
- const fullPath = path53.join(cwd, filePath);
63964
+ const fullPath = path54.join(cwd, filePath);
63335
63965
  if (!validatePathForRead(fullPath, cwd)) {
63336
63966
  return [];
63337
63967
  }
63338
63968
  let content;
63339
63969
  try {
63340
- const stats = fs40.statSync(fullPath);
63970
+ const stats = fs42.statSync(fullPath);
63341
63971
  if (stats.size > MAX_FILE_SIZE_BYTES7) {
63342
63972
  throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
63343
63973
  }
63344
- content = fs40.readFileSync(fullPath, "utf-8");
63974
+ content = fs42.readFileSync(fullPath, "utf-8");
63345
63975
  } catch {
63346
63976
  return [];
63347
63977
  }
@@ -63414,7 +64044,7 @@ var symbols = createSwarmTool({
63414
64044
  }, null, 2);
63415
64045
  }
63416
64046
  const cwd = directory;
63417
- const ext = path53.extname(file3);
64047
+ const ext = path54.extname(file3);
63418
64048
  if (containsControlCharacters(file3)) {
63419
64049
  return JSON.stringify({
63420
64050
  file: file3,
@@ -63485,8 +64115,8 @@ init_test_runner();
63485
64115
  init_dist();
63486
64116
  init_utils();
63487
64117
  init_create_tool();
63488
- import * as fs41 from "fs";
63489
- import * as path54 from "path";
64118
+ import * as fs43 from "fs";
64119
+ import * as path55 from "path";
63490
64120
  var MAX_TEXT_LENGTH = 200;
63491
64121
  var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
63492
64122
  var SUPPORTED_EXTENSIONS2 = new Set([
@@ -63508,7 +64138,7 @@ var SUPPORTED_EXTENSIONS2 = new Set([
63508
64138
  ".swift",
63509
64139
  ".kt"
63510
64140
  ]);
63511
- var SKIP_DIRECTORIES3 = new Set([
64141
+ var SKIP_DIRECTORIES4 = new Set([
63512
64142
  "node_modules",
63513
64143
  "dist",
63514
64144
  "build",
@@ -63557,9 +64187,9 @@ function validatePathsInput(paths, cwd) {
63557
64187
  return { error: "paths contains path traversal", resolvedPath: null };
63558
64188
  }
63559
64189
  try {
63560
- const resolvedPath = path54.resolve(paths);
63561
- const normalizedCwd = path54.resolve(cwd);
63562
- const normalizedResolved = path54.resolve(resolvedPath);
64190
+ const resolvedPath = path55.resolve(paths);
64191
+ const normalizedCwd = path55.resolve(cwd);
64192
+ const normalizedResolved = path55.resolve(resolvedPath);
63563
64193
  if (!normalizedResolved.startsWith(normalizedCwd)) {
63564
64194
  return {
63565
64195
  error: "paths must be within the current working directory",
@@ -63575,25 +64205,25 @@ function validatePathsInput(paths, cwd) {
63575
64205
  }
63576
64206
  }
63577
64207
  function isSupportedExtension(filePath) {
63578
- const ext = path54.extname(filePath).toLowerCase();
64208
+ const ext = path55.extname(filePath).toLowerCase();
63579
64209
  return SUPPORTED_EXTENSIONS2.has(ext);
63580
64210
  }
63581
64211
  function findSourceFiles2(dir, files = []) {
63582
64212
  let entries;
63583
64213
  try {
63584
- entries = fs41.readdirSync(dir);
64214
+ entries = fs43.readdirSync(dir);
63585
64215
  } catch {
63586
64216
  return files;
63587
64217
  }
63588
64218
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
63589
64219
  for (const entry of entries) {
63590
- if (SKIP_DIRECTORIES3.has(entry)) {
64220
+ if (SKIP_DIRECTORIES4.has(entry)) {
63591
64221
  continue;
63592
64222
  }
63593
- const fullPath = path54.join(dir, entry);
64223
+ const fullPath = path55.join(dir, entry);
63594
64224
  let stat2;
63595
64225
  try {
63596
- stat2 = fs41.statSync(fullPath);
64226
+ stat2 = fs43.statSync(fullPath);
63597
64227
  } catch {
63598
64228
  continue;
63599
64229
  }
@@ -63686,7 +64316,7 @@ var todo_extract = createSwarmTool({
63686
64316
  return JSON.stringify(errorResult, null, 2);
63687
64317
  }
63688
64318
  const scanPath = resolvedPath;
63689
- if (!fs41.existsSync(scanPath)) {
64319
+ if (!fs43.existsSync(scanPath)) {
63690
64320
  const errorResult = {
63691
64321
  error: `path not found: ${pathsInput}`,
63692
64322
  total: 0,
@@ -63696,13 +64326,13 @@ var todo_extract = createSwarmTool({
63696
64326
  return JSON.stringify(errorResult, null, 2);
63697
64327
  }
63698
64328
  const filesToScan = [];
63699
- const stat2 = fs41.statSync(scanPath);
64329
+ const stat2 = fs43.statSync(scanPath);
63700
64330
  if (stat2.isFile()) {
63701
64331
  if (isSupportedExtension(scanPath)) {
63702
64332
  filesToScan.push(scanPath);
63703
64333
  } else {
63704
64334
  const errorResult = {
63705
- error: `unsupported file extension: ${path54.extname(scanPath)}`,
64335
+ error: `unsupported file extension: ${path55.extname(scanPath)}`,
63706
64336
  total: 0,
63707
64337
  byPriority: { high: 0, medium: 0, low: 0 },
63708
64338
  entries: []
@@ -63715,11 +64345,11 @@ var todo_extract = createSwarmTool({
63715
64345
  const allEntries = [];
63716
64346
  for (const filePath of filesToScan) {
63717
64347
  try {
63718
- const fileStat = fs41.statSync(filePath);
64348
+ const fileStat = fs43.statSync(filePath);
63719
64349
  if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
63720
64350
  continue;
63721
64351
  }
63722
- const content = fs41.readFileSync(filePath, "utf-8");
64352
+ const content = fs43.readFileSync(filePath, "utf-8");
63723
64353
  const entries = parseTodoComments(content, filePath, tagsSet);
63724
64354
  allEntries.push(...entries);
63725
64355
  } catch {}
@@ -63747,18 +64377,18 @@ var todo_extract = createSwarmTool({
63747
64377
  // src/tools/update-task-status.ts
63748
64378
  init_tool();
63749
64379
  init_schema();
63750
- import * as fs43 from "fs";
63751
- import * as path56 from "path";
64380
+ import * as fs45 from "fs";
64381
+ import * as path57 from "path";
63752
64382
 
63753
64383
  // src/hooks/diff-scope.ts
63754
- import * as fs42 from "fs";
63755
- import * as path55 from "path";
64384
+ import * as fs44 from "fs";
64385
+ import * as path56 from "path";
63756
64386
  function getDeclaredScope(taskId, directory) {
63757
64387
  try {
63758
- const planPath = path55.join(directory, ".swarm", "plan.json");
63759
- if (!fs42.existsSync(planPath))
64388
+ const planPath = path56.join(directory, ".swarm", "plan.json");
64389
+ if (!fs44.existsSync(planPath))
63760
64390
  return null;
63761
- const raw = fs42.readFileSync(planPath, "utf-8");
64391
+ const raw = fs44.readFileSync(planPath, "utf-8");
63762
64392
  const plan = JSON.parse(raw);
63763
64393
  for (const phase of plan.phases ?? []) {
63764
64394
  for (const task of phase.tasks ?? []) {
@@ -63870,7 +64500,7 @@ var TIER_3_PATTERNS = [
63870
64500
  ];
63871
64501
  function matchesTier3Pattern(files) {
63872
64502
  for (const file3 of files) {
63873
- const fileName = path56.basename(file3);
64503
+ const fileName = path57.basename(file3);
63874
64504
  for (const pattern of TIER_3_PATTERNS) {
63875
64505
  if (pattern.test(fileName)) {
63876
64506
  return true;
@@ -63892,8 +64522,8 @@ function checkReviewerGate(taskId, workingDirectory) {
63892
64522
  if (hasActiveTurboMode2()) {
63893
64523
  const resolvedDir2 = workingDirectory;
63894
64524
  try {
63895
- const planPath = path56.join(resolvedDir2, ".swarm", "plan.json");
63896
- const planRaw = fs43.readFileSync(planPath, "utf-8");
64525
+ const planPath = path57.join(resolvedDir2, ".swarm", "plan.json");
64526
+ const planRaw = fs45.readFileSync(planPath, "utf-8");
63897
64527
  const plan = JSON.parse(planRaw);
63898
64528
  for (const planPhase of plan.phases ?? []) {
63899
64529
  for (const task of planPhase.tasks ?? []) {
@@ -63912,8 +64542,8 @@ function checkReviewerGate(taskId, workingDirectory) {
63912
64542
  }
63913
64543
  const resolvedDir = workingDirectory;
63914
64544
  try {
63915
- const evidencePath = path56.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
63916
- const raw = fs43.readFileSync(evidencePath, "utf-8");
64545
+ const evidencePath = path57.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
64546
+ const raw = fs45.readFileSync(evidencePath, "utf-8");
63917
64547
  const evidence = JSON.parse(raw);
63918
64548
  if (evidence?.required_gates && Array.isArray(evidence.required_gates) && evidence?.gates) {
63919
64549
  const allGatesMet = evidence.required_gates.every((gate) => evidence.gates[gate] != null);
@@ -63953,8 +64583,8 @@ function checkReviewerGate(taskId, workingDirectory) {
63953
64583
  }
63954
64584
  try {
63955
64585
  const resolvedDir2 = workingDirectory;
63956
- const planPath = path56.join(resolvedDir2, ".swarm", "plan.json");
63957
- const planRaw = fs43.readFileSync(planPath, "utf-8");
64586
+ const planPath = path57.join(resolvedDir2, ".swarm", "plan.json");
64587
+ const planRaw = fs45.readFileSync(planPath, "utf-8");
63958
64588
  const plan = JSON.parse(planRaw);
63959
64589
  for (const planPhase of plan.phases ?? []) {
63960
64590
  for (const task of planPhase.tasks ?? []) {
@@ -64135,8 +64765,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
64135
64765
  };
64136
64766
  }
64137
64767
  }
64138
- normalizedDir = path56.normalize(args2.working_directory);
64139
- const pathParts = normalizedDir.split(path56.sep);
64768
+ normalizedDir = path57.normalize(args2.working_directory);
64769
+ const pathParts = normalizedDir.split(path57.sep);
64140
64770
  if (pathParts.includes("..")) {
64141
64771
  return {
64142
64772
  success: false,
@@ -64146,11 +64776,11 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
64146
64776
  ]
64147
64777
  };
64148
64778
  }
64149
- const resolvedDir = path56.resolve(normalizedDir);
64779
+ const resolvedDir = path57.resolve(normalizedDir);
64150
64780
  try {
64151
- const realPath = fs43.realpathSync(resolvedDir);
64152
- const planPath = path56.join(realPath, ".swarm", "plan.json");
64153
- if (!fs43.existsSync(planPath)) {
64781
+ const realPath = fs45.realpathSync(resolvedDir);
64782
+ const planPath = path57.join(realPath, ".swarm", "plan.json");
64783
+ if (!fs45.existsSync(planPath)) {
64154
64784
  return {
64155
64785
  success: false,
64156
64786
  message: `Invalid working_directory: plan not found in "${realPath}"`,
@@ -64372,7 +65002,7 @@ var OpenCodeSwarm = async (ctx) => {
64372
65002
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
64373
65003
  preflightTriggerManager = new PTM(automationConfig);
64374
65004
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
64375
- const swarmDir = path57.resolve(ctx.directory, ".swarm");
65005
+ const swarmDir = path58.resolve(ctx.directory, ".swarm");
64376
65006
  statusArtifact = new ASA(swarmDir);
64377
65007
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
64378
65008
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -64664,6 +65294,9 @@ var OpenCodeSwarm = async (ctx) => {
64664
65294
  await safeHook(activityHooks.toolBefore)(input, output);
64665
65295
  },
64666
65296
  "tool.execute.after": async (input, output) => {
65297
+ if (process.env.DEBUG_SWARM) {
65298
+ console.debug("[hook-chain] toolAfter start sessionID=%s agent=%s tool=%s", input.sessionID, input.agent, input.tool?.name);
65299
+ }
64667
65300
  await activityHooks.toolAfter(input, output);
64668
65301
  await guardrailsHooks.toolAfter(input, output);
64669
65302
  await safeHook(delegationLedgerHook.toolAfter)(input, output);
@@ -64678,12 +65311,15 @@ var OpenCodeSwarm = async (ctx) => {
64678
65311
  await safeHook(darkMatterDetectorHook)(input, output);
64679
65312
  await snapshotWriterHook(input, output);
64680
65313
  await toolSummarizerHook?.(input, output);
64681
- if (slopDetectorHook)
64682
- await slopDetectorHook.toolAfter(input, output);
64683
- if (incrementalVerifyHook)
64684
- await incrementalVerifyHook.toolAfter(input, output);
64685
- if (compactionServiceHook)
64686
- await compactionServiceHook.toolAfter(input, output);
65314
+ const execMode = config3.execution_mode ?? "balanced";
65315
+ if (execMode === "strict") {
65316
+ if (slopDetectorHook)
65317
+ await slopDetectorHook.toolAfter(input, output);
65318
+ if (incrementalVerifyHook)
65319
+ await incrementalVerifyHook.toolAfter(input, output);
65320
+ if (compactionServiceHook)
65321
+ await compactionServiceHook.toolAfter(input, output);
65322
+ }
64687
65323
  const toolOutputConfig = config3.tool_output;
64688
65324
  if (toolOutputConfig && toolOutputConfig.truncation_enabled !== false && typeof output.output === "string") {
64689
65325
  const defaultTruncatableTools = new Set([
@@ -64717,8 +65353,14 @@ var OpenCodeSwarm = async (ctx) => {
64717
65353
  session.lastAgentEventTime = Date.now();
64718
65354
  }
64719
65355
  }
65356
+ deleteStoredInputArgs(input.callID);
64720
65357
  },
64721
- "chat.message": safeHook(delegationHandler),
65358
+ "chat.message": safeHook(async (input, output) => {
65359
+ if (process.env.DEBUG_SWARM) {
65360
+ console.debug("[session] chat.message sessionID=%s agent=%s", input.sessionID, input.agent);
65361
+ }
65362
+ await delegationHandler(input, output);
65363
+ }),
64722
65364
  automation: automationManager
64723
65365
  };
64724
65366
  };