opencode-swarm 6.29.3 → 6.29.5

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
@@ -35193,7 +35193,8 @@ var init_test_runner = __esm(() => {
35193
35193
  scope: tool.schema.enum(["all", "convention", "graph"]).optional().describe('Test scope: "all" runs full suite, "convention" maps source files to test files by naming, "graph" finds related tests via imports'),
35194
35194
  files: tool.schema.array(tool.schema.string()).optional().describe("Specific files to test (used with convention or graph scope)"),
35195
35195
  coverage: tool.schema.boolean().optional().describe("Enable coverage reporting if supported"),
35196
- timeout_ms: tool.schema.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)")
35196
+ timeout_ms: tool.schema.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
35197
+ allow_full_suite: tool.schema.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.')
35197
35198
  },
35198
35199
  async execute(args2, directory) {
35199
35200
  const workingDir = directory.trim() || directory;
@@ -35245,14 +35246,16 @@ var init_test_runner = __esm(() => {
35245
35246
  }
35246
35247
  const scope = args2.scope || "all";
35247
35248
  if (scope === "all") {
35248
- const errorResult = {
35249
- success: false,
35250
- framework: "none",
35251
- scope: "all",
35252
- error: 'Full-suite test execution (scope: "all") is prohibited in interactive sessions',
35253
- message: 'Use scope "convention" or "graph" with explicit files to run targeted tests in interactive mode. Full-suite runs are restricted to prevent excessive resource consumption.'
35254
- };
35255
- return JSON.stringify(errorResult, null, 2);
35249
+ if (!args2.allow_full_suite) {
35250
+ const errorResult = {
35251
+ success: false,
35252
+ framework: "none",
35253
+ scope: "all",
35254
+ error: 'Full-suite test execution (scope: "all") requires allow_full_suite: true',
35255
+ message: 'Set allow_full_suite: true to confirm intentional full-suite execution. Use scope "convention" or "graph" for targeted tests. Full-suite output is large and may destabilize SSE streaming on some opencode versions.'
35256
+ };
35257
+ return JSON.stringify(errorResult, null, 2);
35258
+ }
35256
35259
  }
35257
35260
  if ((scope === "convention" || scope === "graph") && (!args2.files || args2.files.length === 0)) {
35258
35261
  const errorResult = {
@@ -35287,7 +35290,7 @@ var init_test_runner = __esm(() => {
35287
35290
  let testFiles = [];
35288
35291
  let graphFallbackReason;
35289
35292
  let effectiveScope = scope;
35290
- if (scope === "convention") {
35293
+ if (scope === "all") {} else if (scope === "convention") {
35291
35294
  const sourceFiles = args2.files.filter((f) => {
35292
35295
  const ext = path23.extname(f).toLowerCase();
35293
35296
  return SOURCE_EXTENSIONS.has(ext);
@@ -35327,7 +35330,7 @@ var init_test_runner = __esm(() => {
35327
35330
  testFiles = getTestFilesFromConvention(sourceFiles);
35328
35331
  }
35329
35332
  }
35330
- if (testFiles.length === 0) {
35333
+ if (scope !== "all" && testFiles.length === 0) {
35331
35334
  const errorResult = {
35332
35335
  success: false,
35333
35336
  framework,
@@ -35337,7 +35340,7 @@ var init_test_runner = __esm(() => {
35337
35340
  };
35338
35341
  return JSON.stringify(errorResult, null, 2);
35339
35342
  }
35340
- if (testFiles.length > MAX_SAFE_TEST_FILES) {
35343
+ if (scope !== "all" && testFiles.length > MAX_SAFE_TEST_FILES) {
35341
35344
  const sampleFiles = testFiles.slice(0, 5);
35342
35345
  const errorResult = {
35343
35346
  success: false,
@@ -37601,11 +37604,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
37601
37604
  throw toThrow;
37602
37605
  }, "quit_");
37603
37606
  var scriptDirectory = "";
37604
- function locateFile(path46) {
37607
+ function locateFile(path47) {
37605
37608
  if (Module["locateFile"]) {
37606
- return Module["locateFile"](path46, scriptDirectory);
37609
+ return Module["locateFile"](path47, scriptDirectory);
37607
37610
  }
37608
- return scriptDirectory + path46;
37611
+ return scriptDirectory + path47;
37609
37612
  }
37610
37613
  __name(locateFile, "locateFile");
37611
37614
  var readAsync, readBinary;
@@ -39353,7 +39356,7 @@ var init_runtime = __esm(() => {
39353
39356
  });
39354
39357
 
39355
39358
  // src/index.ts
39356
- import * as path56 from "path";
39359
+ import * as path57 from "path";
39357
39360
 
39358
39361
  // src/agents/index.ts
39359
39362
  init_config();
@@ -40412,6 +40415,22 @@ TASK GRANULARITY RULES:
40412
40415
  - Compound verbs are OK when they describe a single logical change: "add validation to handler and update its test" = 1 task. "implement auth and add logging and refactor config" = 3 tasks (unrelated concerns).
40413
40416
  - Coder receives ONE task. You make ALL scope decisions in the plan. Coder makes zero scope decisions.
40414
40417
 
40418
+ TEST TASK DEDUPLICATION:
40419
+ The QA gate (Stage B, step 5l) runs test_engineer-verification on EVERY implementation task.
40420
+ This means tests are written, run, and verified as part of the gate \u2014 NOT as separate plan tasks.
40421
+
40422
+ DO NOT create separate "write tests for X" or "add test coverage for X" tasks. They are redundant with the gate and waste execution budget.
40423
+
40424
+ Research confirms this: controlled experiments across 6 LLMs (arXiv:2602.07900) found that large shifts in test-writing volume yielded only 0\u20132.6% resolution change while consuming 20\u201349% more tokens. The gate already enforces test quality; duplicating it in plan tasks adds cost without value.
40425
+
40426
+ CREATE a dedicated test task ONLY when:
40427
+ - The work is PURE test infrastructure (new fixtures, test helpers, mock factories, CI config) with no implementation
40428
+ - Integration tests span multiple modules changed across different implementation tasks within the same phase
40429
+ - Coverage is explicitly below threshold and the user requests a dedicated coverage pass
40430
+
40431
+ If in doubt, do NOT create a test task. The gate handles it.
40432
+ Note: this is prompt-level guidance for the architect's planning behavior, not a hard gate \u2014 the behavioral enforcement is that test_engineer already writes tests at the QA gate level.
40433
+
40415
40434
  PHASE COUNT GUIDANCE:
40416
40435
  - Plans with 5+ tasks SHOULD be split into at least 2 phases.
40417
40436
  - Plans with 10+ tasks MUST be split into at least 3 phases.
@@ -40531,6 +40550,18 @@ Treating pre_check_batch as a substitute for {{AGENT_PREFIX}}reviewer is a PROCE
40531
40550
  \u2192 If TRIGGERED: Print "security-reviewer: [APPROVED | REJECTED \u2014 reason]"
40532
40551
  5l. {{AGENT_PREFIX}}test_engineer - Verification tests. FAIL \u2192 coder retry from 5g.
40533
40552
  \u2192 REQUIRED: Print "testengineer-verification: [PASS N/N | FAIL \u2014 details]"
40553
+ 5l-bis. REGRESSION SWEEP (automatic after test_engineer-verification PASS):
40554
+ Run test_runner with { scope: "graph", files: [<all source files changed by coder in this task>] }.
40555
+ scope:"graph" traces imports to discover test files beyond the task's own tests that may be affected by this change.
40556
+
40557
+ Outcomes:
40558
+ - If scope:"graph" returns ONLY the same test files test_engineer already ran \u2192 SKIP (no additional tests found). Print "regression-sweep: SKIPPED \u2014 no related tests beyond task scope"
40559
+ - If scope:"graph" returns additional test files AND all pass \u2192 PASS. Print "regression-sweep: PASS [N additional tests, M files]"
40560
+ - If scope:"graph" returns additional test files AND any FAIL \u2192 return to coder with: "REGRESSION DETECTED: Your changes in [files] broke [N] tests in [test files]. The failing tests are CORRECT \u2014 fix the source code, not the tests." Coder retry from 5g.
40561
+ - If test_runner fails to execute (error, timeout, no framework detected) \u2192 SKIP. Print "regression-sweep: SKIPPED \u2014 test_runner error" and continue pipeline. Do NOT block on test_runner infrastructure failures.
40562
+
40563
+ IMPORTANT: The regression sweep runs test_runner DIRECTLY (architect calls the tool). Do NOT delegate to test_engineer for this \u2014 the test_engineer's EXECUTION BOUNDARY restricts it to its own test files. The architect has unrestricted test_runner access.
40564
+ \u2192 REQUIRED: Print "regression-sweep: [PASS N additional tests | SKIPPED \u2014 no related tests beyond task scope | SKIPPED \u2014 test_runner error | FAIL \u2014 REGRESSION DETECTED in files]"
40534
40565
  {{ADVERSARIAL_TEST_STEP}}
40535
40566
  5n. COVERAGE CHECK: If {{AGENT_PREFIX}}test_engineer reports coverage < 70% \u2192 delegate {{AGENT_PREFIX}}test_engineer for an additional test pass targeting uncovered paths. This is a soft guideline; use judgment for trivial tasks.
40536
40567
 
@@ -40540,6 +40571,7 @@ PRE-COMMIT RULE \u2014 Before ANY commit or push:
40540
40571
  [ ] Did {{AGENT_PREFIX}}test_engineer run and return PASS? (not "the code looks correct" \u2014 the agent must have run)
40541
40572
  [ ] Did pre_check_batch run with gates_passed true?
40542
40573
  [ ] Did the diff step run?
40574
+ [ ] Did regression-sweep run (or SKIP with no related tests or test_runner error)?
40543
40575
 
40544
40576
  If ANY box is unchecked: DO NOT COMMIT. Return to step 5b.
40545
40577
  There is no override. A commit without a completed QA gate is a workflow violation.
@@ -40555,6 +40587,7 @@ PRE-COMMIT RULE \u2014 Before ANY commit or push:
40555
40587
  [GATE] reviewer: APPROVED \u2014 value: ___
40556
40588
  [GATE] security-reviewer: APPROVED / SKIPPED \u2014 value: ___
40557
40589
  [GATE] test_engineer-verification: PASS \u2014 value: ___
40590
+ [GATE] regression-sweep: PASS / SKIPPED \u2014 value: ___
40558
40591
  {{ADVERSARIAL_TEST_CHECKLIST}}
40559
40592
  [GATE] coverage: \u226570% / soft-skip \u2014 value: ___
40560
40593
 
@@ -41787,7 +41820,8 @@ WORKFLOW:
41787
41820
  EXECUTION BOUNDARY:
41788
41821
  - Blast radius is the FILE path(s) in input
41789
41822
  - When calling test_runner, use: { scope: "convention", files: ["<your-test-file-path>"] }
41790
- - Running the full test suite is PROHIBITED \u2014 it crashes the session
41823
+ - scope: "all" is PROHIBITED for test_engineer \u2014 full-suite output can destabilize opencode's SSE streaming, and the architect handles regression sweeps separately via scope: "graph"
41824
+ - If you need to verify tests beyond your assigned file, report the concern in your VERDICT and the architect will handle it
41791
41825
  - If you wrote tests/foo.test.ts for src/foo.ts, you MUST run only tests/foo.test.ts
41792
41826
 
41793
41827
  TOOL USAGE:
@@ -46351,6 +46385,7 @@ function serializeAgentSession(s) {
46351
46385
  lastCompletedPhaseAgentsDispatched,
46352
46386
  qaSkipCount: s.qaSkipCount ?? 0,
46353
46387
  qaSkipTaskIds: s.qaSkipTaskIds ?? [],
46388
+ pendingAdvisoryMessages: s.pendingAdvisoryMessages ?? [],
46354
46389
  taskWorkflowStates: Object.fromEntries(s.taskWorkflowStates ?? new Map),
46355
46390
  ...s.scopeViolationDetected !== undefined && {
46356
46391
  scopeViolationDetected: s.scopeViolationDetected
@@ -49837,7 +49872,7 @@ ${pending.message}
49837
49872
  }
49838
49873
  }
49839
49874
  if (isArchitectSession && (session?.pendingAdvisoryMessages?.length ?? 0) > 0) {
49840
- const advisories = session.pendingAdvisoryMessages;
49875
+ const advisories = session.pendingAdvisoryMessages ?? [];
49841
49876
  let targetMsg = systemMessages[0];
49842
49877
  if (!targetMsg) {
49843
49878
  const newMsg = {
@@ -50766,6 +50801,7 @@ function consolidateSystemMessages(messages) {
50766
50801
  // src/hooks/phase-monitor.ts
50767
50802
  init_schema();
50768
50803
  init_manager2();
50804
+ import * as path31 from "path";
50769
50805
  init_utils2();
50770
50806
  function createPhaseMonitorHook(directory, preflightManager, curatorRunner = runCuratorInit) {
50771
50807
  let lastKnownPhase = null;
@@ -50781,7 +50817,13 @@ function createPhaseMonitorHook(directory, preflightManager, curatorRunner = run
50781
50817
  const { config: config3 } = loadPluginConfigWithMeta2(directory);
50782
50818
  const curatorConfig = CuratorConfigSchema.parse(config3.curator ?? {});
50783
50819
  if (curatorConfig.enabled && curatorConfig.init_enabled) {
50784
- await curatorRunner(directory, curatorConfig);
50820
+ const initResult = await curatorRunner(directory, curatorConfig);
50821
+ if (initResult.briefing) {
50822
+ const briefingPath = path31.join(directory, ".swarm", "curator-briefing.md");
50823
+ const { mkdir: mkdir4, writeFile: writeFile4 } = await import("fs/promises");
50824
+ await mkdir4(path31.dirname(briefingPath), { recursive: true });
50825
+ await writeFile4(briefingPath, initResult.briefing, "utf-8");
50826
+ }
50785
50827
  }
50786
50828
  } catch {}
50787
50829
  return;
@@ -50897,7 +50939,7 @@ import * as fs19 from "fs";
50897
50939
  init_utils2();
50898
50940
  init_manager2();
50899
50941
  import * as fs18 from "fs";
50900
- import * as path31 from "path";
50942
+ import * as path32 from "path";
50901
50943
  var DEFAULT_DRIFT_CONFIG = {
50902
50944
  staleThresholdPhases: 1,
50903
50945
  detectContradictions: true,
@@ -51051,7 +51093,7 @@ async function analyzeDecisionDrift(directory, config3 = {}) {
51051
51093
  currentPhase = legacyPhase;
51052
51094
  }
51053
51095
  }
51054
- const contextPath = path31.join(directory, ".swarm", "context.md");
51096
+ const contextPath = path32.join(directory, ".swarm", "context.md");
51055
51097
  let contextContent = "";
51056
51098
  try {
51057
51099
  if (fs18.existsSync(contextPath)) {
@@ -52516,7 +52558,7 @@ function createDarkMatterDetectorHook(directory) {
52516
52558
 
52517
52559
  // src/hooks/incremental-verify.ts
52518
52560
  import * as fs20 from "fs";
52519
- import * as path32 from "path";
52561
+ import * as path33 from "path";
52520
52562
 
52521
52563
  // src/hooks/spawn-helper.ts
52522
52564
  import { spawn } from "child_process";
@@ -52572,7 +52614,7 @@ function spawnAsync(command, cwd, timeoutMs) {
52572
52614
  // src/hooks/incremental-verify.ts
52573
52615
  var emittedSkipAdvisories = new Set;
52574
52616
  function detectTypecheckCommand(projectDir) {
52575
- const pkgPath = path32.join(projectDir, "package.json");
52617
+ const pkgPath = path33.join(projectDir, "package.json");
52576
52618
  if (fs20.existsSync(pkgPath)) {
52577
52619
  try {
52578
52620
  const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf8"));
@@ -52588,8 +52630,8 @@ function detectTypecheckCommand(projectDir) {
52588
52630
  ...pkg.dependencies,
52589
52631
  ...pkg.devDependencies
52590
52632
  };
52591
- if (!deps?.typescript && !fs20.existsSync(path32.join(projectDir, "tsconfig.json"))) {}
52592
- const hasTSMarkers = deps?.typescript || fs20.existsSync(path32.join(projectDir, "tsconfig.json"));
52633
+ if (!deps?.typescript && !fs20.existsSync(path33.join(projectDir, "tsconfig.json"))) {}
52634
+ const hasTSMarkers = deps?.typescript || fs20.existsSync(path33.join(projectDir, "tsconfig.json"));
52593
52635
  if (hasTSMarkers) {
52594
52636
  return { command: ["npx", "tsc", "--noEmit"], language: "typescript" };
52595
52637
  }
@@ -52597,13 +52639,13 @@ function detectTypecheckCommand(projectDir) {
52597
52639
  return null;
52598
52640
  }
52599
52641
  }
52600
- if (fs20.existsSync(path32.join(projectDir, "go.mod"))) {
52642
+ if (fs20.existsSync(path33.join(projectDir, "go.mod"))) {
52601
52643
  return { command: ["go", "vet", "./..."], language: "go" };
52602
52644
  }
52603
- if (fs20.existsSync(path32.join(projectDir, "Cargo.toml"))) {
52645
+ if (fs20.existsSync(path33.join(projectDir, "Cargo.toml"))) {
52604
52646
  return { command: ["cargo", "check"], language: "rust" };
52605
52647
  }
52606
- if (fs20.existsSync(path32.join(projectDir, "pyproject.toml")) || fs20.existsSync(path32.join(projectDir, "requirements.txt")) || fs20.existsSync(path32.join(projectDir, "setup.py"))) {
52648
+ if (fs20.existsSync(path33.join(projectDir, "pyproject.toml")) || fs20.existsSync(path33.join(projectDir, "requirements.txt")) || fs20.existsSync(path33.join(projectDir, "setup.py"))) {
52607
52649
  return { command: null, language: "python" };
52608
52650
  }
52609
52651
  try {
@@ -52674,7 +52716,7 @@ ${errorSummary}`);
52674
52716
  // src/hooks/knowledge-reader.ts
52675
52717
  import { existsSync as existsSync19 } from "fs";
52676
52718
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
52677
- import * as path33 from "path";
52719
+ import * as path34 from "path";
52678
52720
  var JACCARD_THRESHOLD = 0.6;
52679
52721
  var HIVE_TIER_BOOST = 0.05;
52680
52722
  var SAME_PROJECT_PENALTY = -0.05;
@@ -52722,7 +52764,7 @@ function inferCategoriesFromPhase(phaseDescription) {
52722
52764
  return ["process", "tooling"];
52723
52765
  }
52724
52766
  async function recordLessonsShown(directory, lessonIds, currentPhase) {
52725
- const shownFile = path33.join(directory, ".swarm", ".knowledge-shown.json");
52767
+ const shownFile = path34.join(directory, ".swarm", ".knowledge-shown.json");
52726
52768
  try {
52727
52769
  let shownData = {};
52728
52770
  if (existsSync19(shownFile)) {
@@ -52730,7 +52772,7 @@ async function recordLessonsShown(directory, lessonIds, currentPhase) {
52730
52772
  shownData = JSON.parse(content);
52731
52773
  }
52732
52774
  shownData[currentPhase] = lessonIds;
52733
- await mkdir4(path33.dirname(shownFile), { recursive: true });
52775
+ await mkdir4(path34.dirname(shownFile), { recursive: true });
52734
52776
  await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
52735
52777
  } catch {
52736
52778
  console.warn("[swarm] Knowledge: failed to record shown lessons");
@@ -52825,7 +52867,7 @@ async function readMergedKnowledge(directory, config3, context) {
52825
52867
  return topN;
52826
52868
  }
52827
52869
  async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
52828
- const shownFile = path33.join(directory, ".swarm", ".knowledge-shown.json");
52870
+ const shownFile = path34.join(directory, ".swarm", ".knowledge-shown.json");
52829
52871
  try {
52830
52872
  if (!existsSync19(shownFile)) {
52831
52873
  return;
@@ -53298,10 +53340,10 @@ Use this data to avoid repeating known failure patterns.`;
53298
53340
  init_event_bus();
53299
53341
  init_utils2();
53300
53342
  import * as fs21 from "fs";
53301
- import * as path34 from "path";
53343
+ import * as path35 from "path";
53302
53344
  var DRIFT_REPORT_PREFIX = "drift-report-phase-";
53303
53345
  async function readPriorDriftReports(directory) {
53304
- const swarmDir = path34.join(directory, ".swarm");
53346
+ const swarmDir = path35.join(directory, ".swarm");
53305
53347
  const entries = await fs21.promises.readdir(swarmDir).catch(() => null);
53306
53348
  if (entries === null)
53307
53349
  return [];
@@ -53328,7 +53370,7 @@ async function readPriorDriftReports(directory) {
53328
53370
  async function writeDriftReport(directory, report) {
53329
53371
  const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
53330
53372
  const filePath = validateSwarmPath(directory, filename);
53331
- const swarmDir = path34.dirname(filePath);
53373
+ const swarmDir = path35.dirname(filePath);
53332
53374
  await fs21.promises.mkdir(swarmDir, { recursive: true });
53333
53375
  try {
53334
53376
  await fs21.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
@@ -53482,7 +53524,7 @@ function isOrchestratorAgent(agentName) {
53482
53524
  function injectKnowledgeMessage(output, text) {
53483
53525
  if (!output.messages)
53484
53526
  return;
53485
- const alreadyInjected = output.messages.some((m) => m.parts?.some((p) => p.text?.includes("\uD83D\uDCDA Knowledge")));
53527
+ const alreadyInjected = output.messages.some((m) => m.parts?.some((p) => p.text?.includes("\uD83D\uDCDA Knowledge") || p.text?.includes("<drift_report>") || p.text?.includes("<curator_briefing>")));
53486
53528
  if (alreadyInjected)
53487
53529
  return;
53488
53530
  const systemIdx = output.messages.findIndex((m) => m.info?.role === "system");
@@ -53528,8 +53570,33 @@ function createKnowledgeInjectorHook(directory, config3) {
53528
53570
  currentPhase: phaseDescription
53529
53571
  };
53530
53572
  const entries = await readMergedKnowledge(directory, config3, context);
53531
- if (entries.length === 0)
53573
+ let freshPreamble = null;
53574
+ try {
53575
+ const driftReports = await readPriorDriftReports(directory);
53576
+ if (driftReports.length > 0) {
53577
+ const latestReport = driftReports[driftReports.length - 1];
53578
+ const driftText = buildDriftInjectionText(latestReport, 500);
53579
+ if (driftText) {
53580
+ freshPreamble = driftText;
53581
+ }
53582
+ }
53583
+ } catch {}
53584
+ try {
53585
+ const briefingContent = await readSwarmFileAsync(directory, "curator-briefing.md");
53586
+ if (briefingContent) {
53587
+ const truncatedBriefing = briefingContent.slice(0, 500);
53588
+ freshPreamble = freshPreamble ? `<curator_briefing>${truncatedBriefing}</curator_briefing>
53589
+
53590
+ ${freshPreamble}` : `<curator_briefing>${truncatedBriefing}</curator_briefing>`;
53591
+ }
53592
+ } catch {}
53593
+ if (entries.length === 0) {
53594
+ if (freshPreamble === null)
53595
+ return;
53596
+ cachedInjectionText = freshPreamble;
53597
+ injectKnowledgeMessage(output, cachedInjectionText);
53532
53598
  return;
53599
+ }
53533
53600
  const runMemory = await getRunMemorySummary(directory);
53534
53601
  const lines = entries.map((entry) => {
53535
53602
  const stars = formatStars(entry.confidence);
@@ -53548,13 +53615,15 @@ function createKnowledgeInjectorHook(directory, config3) {
53548
53615
  "These are lessons learned from this project and past projects. Consider them as context but use your judgment \u2014 they may not all apply."
53549
53616
  ].join(`
53550
53617
  `);
53618
+ let injectionText = freshPreamble ? `${freshPreamble}
53619
+
53620
+ ${knowledgeSection}` : knowledgeSection;
53551
53621
  if (runMemory) {
53552
- cachedInjectionText = `${runMemory}
53622
+ injectionText = `${runMemory}
53553
53623
 
53554
- ${knowledgeSection}`;
53555
- } else {
53556
- cachedInjectionText = knowledgeSection;
53624
+ ${injectionText}`;
53557
53625
  }
53626
+ cachedInjectionText = injectionText;
53558
53627
  const rejected = await readRejectedLessons(directory);
53559
53628
  if (rejected.length > 0) {
53560
53629
  const recentRejected = rejected.slice(-3);
@@ -53565,25 +53634,13 @@ ${knowledgeSection}`;
53565
53634
  ` + rejectedLines.join(`
53566
53635
  `);
53567
53636
  }
53568
- try {
53569
- const driftReports = await readPriorDriftReports(directory);
53570
- if (driftReports.length > 0 && cachedInjectionText !== null) {
53571
- const latestReport = driftReports[driftReports.length - 1];
53572
- const driftText = buildDriftInjectionText(latestReport, 500);
53573
- if (driftText) {
53574
- cachedInjectionText = `${driftText}
53575
-
53576
- ${cachedInjectionText}`;
53577
- }
53578
- }
53579
- } catch {}
53580
53637
  injectKnowledgeMessage(output, cachedInjectionText);
53581
53638
  });
53582
53639
  }
53583
53640
 
53584
53641
  // src/hooks/slop-detector.ts
53585
53642
  import * as fs22 from "fs";
53586
- import * as path35 from "path";
53643
+ import * as path36 from "path";
53587
53644
  var WRITE_EDIT_TOOLS = new Set([
53588
53645
  "write",
53589
53646
  "edit",
@@ -53633,7 +53690,7 @@ function walkFiles(dir, exts, deadline) {
53633
53690
  break;
53634
53691
  if (entry.isSymbolicLink())
53635
53692
  continue;
53636
- const full = path35.join(dir, entry.name);
53693
+ const full = path36.join(dir, entry.name);
53637
53694
  if (entry.isDirectory()) {
53638
53695
  if (entry.name === "node_modules" || entry.name === ".git")
53639
53696
  continue;
@@ -53648,7 +53705,7 @@ function walkFiles(dir, exts, deadline) {
53648
53705
  return results;
53649
53706
  }
53650
53707
  function checkDeadExports(content, projectDir, startTime) {
53651
- const hasPackageJson = fs22.existsSync(path35.join(projectDir, "package.json"));
53708
+ const hasPackageJson = fs22.existsSync(path36.join(projectDir, "package.json"));
53652
53709
  if (!hasPackageJson)
53653
53710
  return null;
53654
53711
  const exportMatches = content.matchAll(/^\+(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
@@ -53798,7 +53855,7 @@ init_config_doctor();
53798
53855
 
53799
53856
  // src/session/snapshot-reader.ts
53800
53857
  init_utils2();
53801
- import path36 from "path";
53858
+ import path37 from "path";
53802
53859
  var VALID_TASK_WORKFLOW_STATES = [
53803
53860
  "idle",
53804
53861
  "coder_delegated",
@@ -53870,7 +53927,8 @@ function deserializeAgentSession(s) {
53870
53927
  declaredCoderScope: null,
53871
53928
  lastScopeViolation: null,
53872
53929
  scopeViolationDetected: s.scopeViolationDetected,
53873
- modifiedFilesThisCoderTask: []
53930
+ modifiedFilesThisCoderTask: [],
53931
+ pendingAdvisoryMessages: s.pendingAdvisoryMessages ?? []
53874
53932
  };
53875
53933
  }
53876
53934
  async function readSnapshot(directory) {
@@ -53923,7 +53981,7 @@ function rehydrateState(snapshot) {
53923
53981
  async function reconcileTaskStatesFromPlan(directory) {
53924
53982
  let raw;
53925
53983
  try {
53926
- raw = await Bun.file(path36.join(directory, ".swarm/plan.json")).text();
53984
+ raw = await Bun.file(path37.join(directory, ".swarm/plan.json")).text();
53927
53985
  } catch {
53928
53986
  return;
53929
53987
  }
@@ -54146,7 +54204,7 @@ var build_check = createSwarmTool({
54146
54204
  init_dist();
54147
54205
  init_create_tool();
54148
54206
  import * as fs24 from "fs";
54149
- import * as path37 from "path";
54207
+ import * as path38 from "path";
54150
54208
  var EVIDENCE_DIR = ".swarm/evidence";
54151
54209
  var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
54152
54210
  function isValidTaskId3(taskId) {
@@ -54163,9 +54221,9 @@ function isValidTaskId3(taskId) {
54163
54221
  return TASK_ID_PATTERN2.test(taskId);
54164
54222
  }
54165
54223
  function isPathWithinSwarm(filePath, workspaceRoot) {
54166
- const normalizedWorkspace = path37.resolve(workspaceRoot);
54167
- const swarmPath = path37.join(normalizedWorkspace, ".swarm", "evidence");
54168
- const normalizedPath = path37.resolve(filePath);
54224
+ const normalizedWorkspace = path38.resolve(workspaceRoot);
54225
+ const swarmPath = path38.join(normalizedWorkspace, ".swarm", "evidence");
54226
+ const normalizedPath = path38.resolve(filePath);
54169
54227
  return normalizedPath.startsWith(swarmPath);
54170
54228
  }
54171
54229
  function readEvidenceFile(evidencePath) {
@@ -54226,7 +54284,7 @@ var check_gate_status = createSwarmTool({
54226
54284
  };
54227
54285
  return JSON.stringify(errorResult, null, 2);
54228
54286
  }
54229
- const evidencePath = path37.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
54287
+ const evidencePath = path38.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
54230
54288
  if (!isPathWithinSwarm(evidencePath, directory)) {
54231
54289
  const errorResult = {
54232
54290
  taskId: taskIdInput,
@@ -54287,7 +54345,7 @@ init_tool();
54287
54345
  init_create_tool();
54288
54346
  import { spawnSync } from "child_process";
54289
54347
  import * as fs25 from "fs";
54290
- import * as path38 from "path";
54348
+ import * as path39 from "path";
54291
54349
  var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
54292
54350
  var MAX_LABEL_LENGTH = 100;
54293
54351
  var GIT_TIMEOUT_MS = 30000;
@@ -54338,7 +54396,7 @@ function validateLabel(label) {
54338
54396
  return null;
54339
54397
  }
54340
54398
  function getCheckpointLogPath(directory) {
54341
- return path38.join(directory, CHECKPOINT_LOG_PATH);
54399
+ return path39.join(directory, CHECKPOINT_LOG_PATH);
54342
54400
  }
54343
54401
  function readCheckpointLog(directory) {
54344
54402
  const logPath = getCheckpointLogPath(directory);
@@ -54356,7 +54414,7 @@ function readCheckpointLog(directory) {
54356
54414
  }
54357
54415
  function writeCheckpointLog(log2, directory) {
54358
54416
  const logPath = getCheckpointLogPath(directory);
54359
- const dir = path38.dirname(logPath);
54417
+ const dir = path39.dirname(logPath);
54360
54418
  if (!fs25.existsSync(dir)) {
54361
54419
  fs25.mkdirSync(dir, { recursive: true });
54362
54420
  }
@@ -54564,7 +54622,7 @@ var checkpoint = createSwarmTool({
54564
54622
  init_dist();
54565
54623
  init_create_tool();
54566
54624
  import * as fs26 from "fs";
54567
- import * as path39 from "path";
54625
+ import * as path40 from "path";
54568
54626
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
54569
54627
  var DEFAULT_DAYS = 90;
54570
54628
  var DEFAULT_TOP_N = 20;
@@ -54708,7 +54766,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
54708
54766
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
54709
54767
  const filteredChurn = new Map;
54710
54768
  for (const [file3, count] of churnMap) {
54711
- const ext = path39.extname(file3).toLowerCase();
54769
+ const ext = path40.extname(file3).toLowerCase();
54712
54770
  if (extSet.has(ext)) {
54713
54771
  filteredChurn.set(file3, count);
54714
54772
  }
@@ -54719,7 +54777,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
54719
54777
  for (const [file3, churnCount] of filteredChurn) {
54720
54778
  let fullPath = file3;
54721
54779
  if (!fs26.existsSync(fullPath)) {
54722
- fullPath = path39.join(cwd, file3);
54780
+ fullPath = path40.join(cwd, file3);
54723
54781
  }
54724
54782
  const complexity = getComplexityForFile(fullPath);
54725
54783
  if (complexity !== null) {
@@ -54867,7 +54925,7 @@ var complexity_hotspots = createSwarmTool({
54867
54925
  // src/tools/declare-scope.ts
54868
54926
  init_tool();
54869
54927
  import * as fs27 from "fs";
54870
- import * as path40 from "path";
54928
+ import * as path41 from "path";
54871
54929
  init_create_tool();
54872
54930
  function validateTaskIdFormat(taskId) {
54873
54931
  const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
@@ -54946,8 +55004,8 @@ async function executeDeclareScope(args2, fallbackDir) {
54946
55004
  };
54947
55005
  }
54948
55006
  }
54949
- normalizedDir = path40.normalize(args2.working_directory);
54950
- const pathParts = normalizedDir.split(path40.sep);
55007
+ normalizedDir = path41.normalize(args2.working_directory);
55008
+ const pathParts = normalizedDir.split(path41.sep);
54951
55009
  if (pathParts.includes("..")) {
54952
55010
  return {
54953
55011
  success: false,
@@ -54957,10 +55015,10 @@ async function executeDeclareScope(args2, fallbackDir) {
54957
55015
  ]
54958
55016
  };
54959
55017
  }
54960
- const resolvedDir = path40.resolve(normalizedDir);
55018
+ const resolvedDir = path41.resolve(normalizedDir);
54961
55019
  try {
54962
55020
  const realPath = fs27.realpathSync(resolvedDir);
54963
- const planPath2 = path40.join(realPath, ".swarm", "plan.json");
55021
+ const planPath2 = path41.join(realPath, ".swarm", "plan.json");
54964
55022
  if (!fs27.existsSync(planPath2)) {
54965
55023
  return {
54966
55024
  success: false,
@@ -54981,7 +55039,7 @@ async function executeDeclareScope(args2, fallbackDir) {
54981
55039
  }
54982
55040
  }
54983
55041
  const directory = normalizedDir ?? fallbackDir ?? process.cwd();
54984
- const planPath = path40.resolve(directory, ".swarm", "plan.json");
55042
+ const planPath = path41.resolve(directory, ".swarm", "plan.json");
54985
55043
  if (!fs27.existsSync(planPath)) {
54986
55044
  return {
54987
55045
  success: false,
@@ -55071,20 +55129,20 @@ function validateBase(base) {
55071
55129
  function validatePaths(paths) {
55072
55130
  if (!paths)
55073
55131
  return null;
55074
- for (const path41 of paths) {
55075
- if (!path41 || path41.length === 0) {
55132
+ for (const path42 of paths) {
55133
+ if (!path42 || path42.length === 0) {
55076
55134
  return "empty path not allowed";
55077
55135
  }
55078
- if (path41.length > MAX_PATH_LENGTH) {
55136
+ if (path42.length > MAX_PATH_LENGTH) {
55079
55137
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
55080
55138
  }
55081
- if (SHELL_METACHARACTERS2.test(path41)) {
55139
+ if (SHELL_METACHARACTERS2.test(path42)) {
55082
55140
  return "path contains shell metacharacters";
55083
55141
  }
55084
- if (path41.startsWith("-")) {
55142
+ if (path42.startsWith("-")) {
55085
55143
  return 'path cannot start with "-" (option-like arguments not allowed)';
55086
55144
  }
55087
- if (CONTROL_CHAR_PATTERN2.test(path41)) {
55145
+ if (CONTROL_CHAR_PATTERN2.test(path42)) {
55088
55146
  return "path contains control characters";
55089
55147
  }
55090
55148
  }
@@ -55164,8 +55222,8 @@ var diff = tool({
55164
55222
  if (parts2.length >= 3) {
55165
55223
  const additions = parseInt(parts2[0], 10) || 0;
55166
55224
  const deletions = parseInt(parts2[1], 10) || 0;
55167
- const path41 = parts2[2];
55168
- files.push({ path: path41, additions, deletions });
55225
+ const path42 = parts2[2];
55226
+ files.push({ path: path42, additions, deletions });
55169
55227
  }
55170
55228
  }
55171
55229
  const contractChanges = [];
@@ -55395,7 +55453,7 @@ Use these as DOMAIN values when delegating to @sme.`;
55395
55453
  init_dist();
55396
55454
  init_create_tool();
55397
55455
  import * as fs28 from "fs";
55398
- import * as path41 from "path";
55456
+ import * as path42 from "path";
55399
55457
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
55400
55458
  var MAX_EVIDENCE_FILES = 1000;
55401
55459
  var EVIDENCE_DIR2 = ".swarm/evidence";
@@ -55425,9 +55483,9 @@ function validateRequiredTypes(input) {
55425
55483
  return null;
55426
55484
  }
55427
55485
  function isPathWithinSwarm2(filePath, cwd) {
55428
- const normalizedCwd = path41.resolve(cwd);
55429
- const swarmPath = path41.join(normalizedCwd, ".swarm");
55430
- const normalizedPath = path41.resolve(filePath);
55486
+ const normalizedCwd = path42.resolve(cwd);
55487
+ const swarmPath = path42.join(normalizedCwd, ".swarm");
55488
+ const normalizedPath = path42.resolve(filePath);
55431
55489
  return normalizedPath.startsWith(swarmPath);
55432
55490
  }
55433
55491
  function parseCompletedTasks(planContent) {
@@ -55457,10 +55515,10 @@ function readEvidenceFiles(evidenceDir, _cwd) {
55457
55515
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
55458
55516
  continue;
55459
55517
  }
55460
- const filePath = path41.join(evidenceDir, filename);
55518
+ const filePath = path42.join(evidenceDir, filename);
55461
55519
  try {
55462
- const resolvedPath = path41.resolve(filePath);
55463
- const evidenceDirResolved = path41.resolve(evidenceDir);
55520
+ const resolvedPath = path42.resolve(filePath);
55521
+ const evidenceDirResolved = path42.resolve(evidenceDir);
55464
55522
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
55465
55523
  continue;
55466
55524
  }
@@ -55578,7 +55636,7 @@ var evidence_check = createSwarmTool({
55578
55636
  return JSON.stringify(errorResult, null, 2);
55579
55637
  }
55580
55638
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
55581
- const planPath = path41.join(cwd, PLAN_FILE);
55639
+ const planPath = path42.join(cwd, PLAN_FILE);
55582
55640
  if (!isPathWithinSwarm2(planPath, cwd)) {
55583
55641
  const errorResult = {
55584
55642
  error: "plan file path validation failed",
@@ -55610,7 +55668,7 @@ var evidence_check = createSwarmTool({
55610
55668
  };
55611
55669
  return JSON.stringify(result2, null, 2);
55612
55670
  }
55613
- const evidenceDir = path41.join(cwd, EVIDENCE_DIR2);
55671
+ const evidenceDir = path42.join(cwd, EVIDENCE_DIR2);
55614
55672
  const evidence = readEvidenceFiles(evidenceDir, cwd);
55615
55673
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
55616
55674
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -55628,7 +55686,7 @@ var evidence_check = createSwarmTool({
55628
55686
  init_tool();
55629
55687
  init_create_tool();
55630
55688
  import * as fs29 from "fs";
55631
- import * as path42 from "path";
55689
+ import * as path43 from "path";
55632
55690
  var EXT_MAP = {
55633
55691
  python: ".py",
55634
55692
  py: ".py",
@@ -55709,12 +55767,12 @@ var extract_code_blocks = createSwarmTool({
55709
55767
  if (prefix) {
55710
55768
  filename = `${prefix}_${filename}`;
55711
55769
  }
55712
- let filepath = path42.join(targetDir, filename);
55713
- const base = path42.basename(filepath, path42.extname(filepath));
55714
- const ext = path42.extname(filepath);
55770
+ let filepath = path43.join(targetDir, filename);
55771
+ const base = path43.basename(filepath, path43.extname(filepath));
55772
+ const ext = path43.extname(filepath);
55715
55773
  let counter = 1;
55716
55774
  while (fs29.existsSync(filepath)) {
55717
- filepath = path42.join(targetDir, `${base}_${counter}${ext}`);
55775
+ filepath = path43.join(targetDir, `${base}_${counter}${ext}`);
55718
55776
  counter++;
55719
55777
  }
55720
55778
  try {
@@ -55832,7 +55890,7 @@ var gitingest = tool({
55832
55890
  // src/tools/imports.ts
55833
55891
  init_dist();
55834
55892
  import * as fs30 from "fs";
55835
- import * as path43 from "path";
55893
+ import * as path44 from "path";
55836
55894
  var MAX_FILE_PATH_LENGTH2 = 500;
55837
55895
  var MAX_SYMBOL_LENGTH = 256;
55838
55896
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -55886,7 +55944,7 @@ function validateSymbolInput(symbol3) {
55886
55944
  return null;
55887
55945
  }
55888
55946
  function isBinaryFile2(filePath, buffer) {
55889
- const ext = path43.extname(filePath).toLowerCase();
55947
+ const ext = path44.extname(filePath).toLowerCase();
55890
55948
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
55891
55949
  return false;
55892
55950
  }
@@ -55910,15 +55968,15 @@ function parseImports(content, targetFile, targetSymbol) {
55910
55968
  const imports = [];
55911
55969
  let _resolvedTarget;
55912
55970
  try {
55913
- _resolvedTarget = path43.resolve(targetFile);
55971
+ _resolvedTarget = path44.resolve(targetFile);
55914
55972
  } catch {
55915
55973
  _resolvedTarget = targetFile;
55916
55974
  }
55917
- const targetBasename = path43.basename(targetFile, path43.extname(targetFile));
55975
+ const targetBasename = path44.basename(targetFile, path44.extname(targetFile));
55918
55976
  const targetWithExt = targetFile;
55919
55977
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
55920
- const normalizedTargetWithExt = path43.normalize(targetWithExt).replace(/\\/g, "/");
55921
- const normalizedTargetWithoutExt = path43.normalize(targetWithoutExt).replace(/\\/g, "/");
55978
+ const normalizedTargetWithExt = path44.normalize(targetWithExt).replace(/\\/g, "/");
55979
+ const normalizedTargetWithoutExt = path44.normalize(targetWithoutExt).replace(/\\/g, "/");
55922
55980
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
55923
55981
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
55924
55982
  const modulePath = match[1] || match[2] || match[3];
@@ -55941,9 +55999,9 @@ function parseImports(content, targetFile, targetSymbol) {
55941
55999
  }
55942
56000
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
55943
56001
  let isMatch = false;
55944
- const _targetDir = path43.dirname(targetFile);
55945
- const targetExt = path43.extname(targetFile);
55946
- const targetBasenameNoExt = path43.basename(targetFile, targetExt);
56002
+ const _targetDir = path44.dirname(targetFile);
56003
+ const targetExt = path44.extname(targetFile);
56004
+ const targetBasenameNoExt = path44.basename(targetFile, targetExt);
55947
56005
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
55948
56006
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
55949
56007
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -56011,10 +56069,10 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
56011
56069
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
56012
56070
  for (const entry of entries) {
56013
56071
  if (SKIP_DIRECTORIES2.has(entry)) {
56014
- stats.skippedDirs.push(path43.join(dir, entry));
56072
+ stats.skippedDirs.push(path44.join(dir, entry));
56015
56073
  continue;
56016
56074
  }
56017
- const fullPath = path43.join(dir, entry);
56075
+ const fullPath = path44.join(dir, entry);
56018
56076
  let stat2;
56019
56077
  try {
56020
56078
  stat2 = fs30.statSync(fullPath);
@@ -56028,7 +56086,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
56028
56086
  if (stat2.isDirectory()) {
56029
56087
  findSourceFiles(fullPath, files, stats);
56030
56088
  } else if (stat2.isFile()) {
56031
- const ext = path43.extname(fullPath).toLowerCase();
56089
+ const ext = path44.extname(fullPath).toLowerCase();
56032
56090
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
56033
56091
  files.push(fullPath);
56034
56092
  }
@@ -56084,7 +56142,7 @@ var imports = tool({
56084
56142
  return JSON.stringify(errorResult, null, 2);
56085
56143
  }
56086
56144
  try {
56087
- const targetFile = path43.resolve(file3);
56145
+ const targetFile = path44.resolve(file3);
56088
56146
  if (!fs30.existsSync(targetFile)) {
56089
56147
  const errorResult = {
56090
56148
  error: `target file not found: ${file3}`,
@@ -56106,7 +56164,7 @@ var imports = tool({
56106
56164
  };
56107
56165
  return JSON.stringify(errorResult, null, 2);
56108
56166
  }
56109
- const baseDir = path43.dirname(targetFile);
56167
+ const baseDir = path44.dirname(targetFile);
56110
56168
  const scanStats = {
56111
56169
  skippedDirs: [],
56112
56170
  skippedFiles: 0,
@@ -56428,7 +56486,7 @@ init_config();
56428
56486
  init_schema();
56429
56487
  init_manager();
56430
56488
  import * as fs31 from "fs";
56431
- import * as path44 from "path";
56489
+ import * as path45 from "path";
56432
56490
  init_utils2();
56433
56491
  init_create_tool();
56434
56492
  function safeWarn(message, error93) {
@@ -56623,7 +56681,7 @@ async function executePhaseComplete(args2, workingDirectory) {
56623
56681
  }
56624
56682
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
56625
56683
  try {
56626
- const projectName = path44.basename(dir);
56684
+ const projectName = path45.basename(dir);
56627
56685
  const knowledgeConfig = {
56628
56686
  enabled: true,
56629
56687
  swarm_max_entries: 100,
@@ -56652,12 +56710,17 @@ async function executePhaseComplete(args2, workingDirectory) {
56652
56710
  safeWarn("[phase_complete] Failed to curate lessons from retrospective:", error93);
56653
56711
  }
56654
56712
  }
56713
+ let complianceWarnings = [];
56655
56714
  try {
56656
56715
  const curatorConfig = CuratorConfigSchema.parse(config3.curator ?? {});
56657
56716
  if (curatorConfig.enabled && curatorConfig.phase_enabled) {
56658
56717
  const curatorResult = await runCuratorPhase(dir, phase, agentsDispatched, curatorConfig, {});
56659
56718
  await applyCuratorKnowledgeUpdates(dir, curatorResult.knowledge_recommendations, {});
56660
56719
  await runCriticDriftCheck(dir, phase, curatorResult, curatorConfig);
56720
+ if (curatorResult.compliance.length > 0 && !curatorConfig.suppress_warnings) {
56721
+ const complianceLines = curatorResult.compliance.map((obs) => `[${obs.severity.toUpperCase()}] ${obs.description}`).slice(0, 5);
56722
+ complianceWarnings = complianceLines;
56723
+ }
56661
56724
  }
56662
56725
  } catch (curatorError) {
56663
56726
  safeWarn("[phase_complete] Curator pipeline error (non-blocking):", curatorError);
@@ -56743,6 +56806,9 @@ async function executePhaseComplete(args2, workingDirectory) {
56743
56806
  warnings.push(`Warning: failed to update plan.json phase status: ${error93 instanceof Error ? error93.message : String(error93)}`);
56744
56807
  }
56745
56808
  }
56809
+ if (complianceWarnings.length > 0) {
56810
+ warnings.push(`Curator compliance: ${complianceWarnings.join("; ")}`);
56811
+ }
56746
56812
  const result = {
56747
56813
  success: success3,
56748
56814
  phase,
@@ -56787,7 +56853,7 @@ init_discovery();
56787
56853
  init_utils();
56788
56854
  init_create_tool();
56789
56855
  import * as fs32 from "fs";
56790
- import * as path45 from "path";
56856
+ import * as path46 from "path";
56791
56857
  var MAX_OUTPUT_BYTES5 = 52428800;
56792
56858
  var AUDIT_TIMEOUT_MS = 120000;
56793
56859
  function isValidEcosystem(value) {
@@ -56805,16 +56871,16 @@ function validateArgs3(args2) {
56805
56871
  function detectEcosystems(directory) {
56806
56872
  const ecosystems = [];
56807
56873
  const cwd = directory;
56808
- if (fs32.existsSync(path45.join(cwd, "package.json"))) {
56874
+ if (fs32.existsSync(path46.join(cwd, "package.json"))) {
56809
56875
  ecosystems.push("npm");
56810
56876
  }
56811
- if (fs32.existsSync(path45.join(cwd, "pyproject.toml")) || fs32.existsSync(path45.join(cwd, "requirements.txt"))) {
56877
+ if (fs32.existsSync(path46.join(cwd, "pyproject.toml")) || fs32.existsSync(path46.join(cwd, "requirements.txt"))) {
56812
56878
  ecosystems.push("pip");
56813
56879
  }
56814
- if (fs32.existsSync(path45.join(cwd, "Cargo.toml"))) {
56880
+ if (fs32.existsSync(path46.join(cwd, "Cargo.toml"))) {
56815
56881
  ecosystems.push("cargo");
56816
56882
  }
56817
- if (fs32.existsSync(path45.join(cwd, "go.mod"))) {
56883
+ if (fs32.existsSync(path46.join(cwd, "go.mod"))) {
56818
56884
  ecosystems.push("go");
56819
56885
  }
56820
56886
  try {
@@ -56823,10 +56889,10 @@ function detectEcosystems(directory) {
56823
56889
  ecosystems.push("dotnet");
56824
56890
  }
56825
56891
  } catch {}
56826
- if (fs32.existsSync(path45.join(cwd, "Gemfile")) || fs32.existsSync(path45.join(cwd, "Gemfile.lock"))) {
56892
+ if (fs32.existsSync(path46.join(cwd, "Gemfile")) || fs32.existsSync(path46.join(cwd, "Gemfile.lock"))) {
56827
56893
  ecosystems.push("ruby");
56828
56894
  }
56829
- if (fs32.existsSync(path45.join(cwd, "pubspec.yaml"))) {
56895
+ if (fs32.existsSync(path46.join(cwd, "pubspec.yaml"))) {
56830
56896
  ecosystems.push("dart");
56831
56897
  }
56832
56898
  return ecosystems;
@@ -57889,7 +57955,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
57889
57955
  // src/tools/pre-check-batch.ts
57890
57956
  init_dist();
57891
57957
  import * as fs35 from "fs";
57892
- import * as path48 from "path";
57958
+ import * as path49 from "path";
57893
57959
 
57894
57960
  // node_modules/yocto-queue/index.js
57895
57961
  class Node2 {
@@ -58057,7 +58123,7 @@ init_manager();
58057
58123
 
58058
58124
  // src/quality/metrics.ts
58059
58125
  import * as fs33 from "fs";
58060
- import * as path46 from "path";
58126
+ import * as path47 from "path";
58061
58127
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
58062
58128
  var MIN_DUPLICATION_LINES = 10;
58063
58129
  function estimateCyclomaticComplexity(content) {
@@ -58109,7 +58175,7 @@ async function computeComplexityDelta(files, workingDir) {
58109
58175
  let totalComplexity = 0;
58110
58176
  const analyzedFiles = [];
58111
58177
  for (const file3 of files) {
58112
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
58178
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58113
58179
  if (!fs33.existsSync(fullPath)) {
58114
58180
  continue;
58115
58181
  }
@@ -58232,7 +58298,7 @@ function countGoExports(content) {
58232
58298
  function getExportCountForFile(filePath) {
58233
58299
  try {
58234
58300
  const content = fs33.readFileSync(filePath, "utf-8");
58235
- const ext = path46.extname(filePath).toLowerCase();
58301
+ const ext = path47.extname(filePath).toLowerCase();
58236
58302
  switch (ext) {
58237
58303
  case ".ts":
58238
58304
  case ".tsx":
@@ -58258,7 +58324,7 @@ async function computePublicApiDelta(files, workingDir) {
58258
58324
  let totalExports = 0;
58259
58325
  const analyzedFiles = [];
58260
58326
  for (const file3 of files) {
58261
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
58327
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58262
58328
  if (!fs33.existsSync(fullPath)) {
58263
58329
  continue;
58264
58330
  }
@@ -58292,7 +58358,7 @@ async function computeDuplicationRatio(files, workingDir) {
58292
58358
  let duplicateLines = 0;
58293
58359
  const analyzedFiles = [];
58294
58360
  for (const file3 of files) {
58295
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
58361
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58296
58362
  if (!fs33.existsSync(fullPath)) {
58297
58363
  continue;
58298
58364
  }
@@ -58325,8 +58391,8 @@ function countCodeLines(content) {
58325
58391
  return lines.length;
58326
58392
  }
58327
58393
  function isTestFile(filePath) {
58328
- const basename8 = path46.basename(filePath);
58329
- const _ext = path46.extname(filePath).toLowerCase();
58394
+ const basename8 = path47.basename(filePath);
58395
+ const _ext = path47.extname(filePath).toLowerCase();
58330
58396
  const testPatterns = [
58331
58397
  ".test.",
58332
58398
  ".spec.",
@@ -58407,8 +58473,8 @@ function matchGlobSegment(globSegments, pathSegments) {
58407
58473
  }
58408
58474
  return gIndex === globSegments.length && pIndex === pathSegments.length;
58409
58475
  }
58410
- function matchesGlobSegment(path47, glob) {
58411
- const normalizedPath = path47.replace(/\\/g, "/");
58476
+ function matchesGlobSegment(path48, glob) {
58477
+ const normalizedPath = path48.replace(/\\/g, "/");
58412
58478
  const normalizedGlob = glob.replace(/\\/g, "/");
58413
58479
  if (normalizedPath.includes("//")) {
58414
58480
  return false;
@@ -58439,8 +58505,8 @@ function simpleGlobToRegex2(glob) {
58439
58505
  function hasGlobstar(glob) {
58440
58506
  return glob.includes("**");
58441
58507
  }
58442
- function globMatches(path47, glob) {
58443
- const normalizedPath = path47.replace(/\\/g, "/");
58508
+ function globMatches(path48, glob) {
58509
+ const normalizedPath = path48.replace(/\\/g, "/");
58444
58510
  if (!glob || glob === "") {
58445
58511
  if (normalizedPath.includes("//")) {
58446
58512
  return false;
@@ -58476,7 +58542,7 @@ function shouldExcludeFile(filePath, excludeGlobs) {
58476
58542
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58477
58543
  let testLines = 0;
58478
58544
  let codeLines = 0;
58479
- const srcDir = path46.join(workingDir, "src");
58545
+ const srcDir = path47.join(workingDir, "src");
58480
58546
  if (fs33.existsSync(srcDir)) {
58481
58547
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
58482
58548
  codeLines += lines;
@@ -58484,14 +58550,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58484
58550
  }
58485
58551
  const possibleSrcDirs = ["lib", "app", "source", "core"];
58486
58552
  for (const dir of possibleSrcDirs) {
58487
- const dirPath = path46.join(workingDir, dir);
58553
+ const dirPath = path47.join(workingDir, dir);
58488
58554
  if (fs33.existsSync(dirPath)) {
58489
58555
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
58490
58556
  codeLines += lines;
58491
58557
  });
58492
58558
  }
58493
58559
  }
58494
- const testsDir = path46.join(workingDir, "tests");
58560
+ const testsDir = path47.join(workingDir, "tests");
58495
58561
  if (fs33.existsSync(testsDir)) {
58496
58562
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
58497
58563
  testLines += lines;
@@ -58499,7 +58565,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58499
58565
  }
58500
58566
  const possibleTestDirs = ["test", "__tests__", "specs"];
58501
58567
  for (const dir of possibleTestDirs) {
58502
- const dirPath = path46.join(workingDir, dir);
58568
+ const dirPath = path47.join(workingDir, dir);
58503
58569
  if (fs33.existsSync(dirPath) && dirPath !== testsDir) {
58504
58570
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
58505
58571
  testLines += lines;
@@ -58514,7 +58580,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
58514
58580
  try {
58515
58581
  const entries = fs33.readdirSync(dirPath, { withFileTypes: true });
58516
58582
  for (const entry of entries) {
58517
- const fullPath = path46.join(dirPath, entry.name);
58583
+ const fullPath = path47.join(dirPath, entry.name);
58518
58584
  if (entry.isDirectory()) {
58519
58585
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
58520
58586
  continue;
@@ -58522,7 +58588,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
58522
58588
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
58523
58589
  } else if (entry.isFile()) {
58524
58590
  const relativePath = fullPath.replace(`${process.cwd()}/`, "");
58525
- const ext = path46.extname(entry.name).toLowerCase();
58591
+ const ext = path47.extname(entry.name).toLowerCase();
58526
58592
  const validExts = [
58527
58593
  ".ts",
58528
58594
  ".tsx",
@@ -58773,7 +58839,7 @@ init_dist();
58773
58839
  init_manager();
58774
58840
  init_detector();
58775
58841
  import * as fs34 from "fs";
58776
- import * as path47 from "path";
58842
+ import * as path48 from "path";
58777
58843
  import { extname as extname9 } from "path";
58778
58844
 
58779
58845
  // src/sast/rules/c.ts
@@ -59736,7 +59802,7 @@ async function sastScan(input, directory, config3) {
59736
59802
  _filesSkipped++;
59737
59803
  continue;
59738
59804
  }
59739
- const resolvedPath = path47.isAbsolute(filePath) ? filePath : path47.resolve(directory, filePath);
59805
+ const resolvedPath = path48.isAbsolute(filePath) ? filePath : path48.resolve(directory, filePath);
59740
59806
  if (!fs34.existsSync(resolvedPath)) {
59741
59807
  _filesSkipped++;
59742
59808
  continue;
@@ -59935,18 +60001,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
59935
60001
  let resolved;
59936
60002
  const isWinAbs = isWindowsAbsolutePath(inputPath);
59937
60003
  if (isWinAbs) {
59938
- resolved = path48.win32.resolve(inputPath);
59939
- } else if (path48.isAbsolute(inputPath)) {
59940
- resolved = path48.resolve(inputPath);
60004
+ resolved = path49.win32.resolve(inputPath);
60005
+ } else if (path49.isAbsolute(inputPath)) {
60006
+ resolved = path49.resolve(inputPath);
59941
60007
  } else {
59942
- resolved = path48.resolve(baseDir, inputPath);
60008
+ resolved = path49.resolve(baseDir, inputPath);
59943
60009
  }
59944
- const workspaceResolved = path48.resolve(workspaceDir);
60010
+ const workspaceResolved = path49.resolve(workspaceDir);
59945
60011
  let relative5;
59946
60012
  if (isWinAbs) {
59947
- relative5 = path48.win32.relative(workspaceResolved, resolved);
60013
+ relative5 = path49.win32.relative(workspaceResolved, resolved);
59948
60014
  } else {
59949
- relative5 = path48.relative(workspaceResolved, resolved);
60015
+ relative5 = path49.relative(workspaceResolved, resolved);
59950
60016
  }
59951
60017
  if (relative5.startsWith("..")) {
59952
60018
  return "path traversal detected";
@@ -60007,13 +60073,13 @@ async function runLintWrapped(files, directory, _config) {
60007
60073
  }
60008
60074
  async function runLintOnFiles(linter, files, workspaceDir) {
60009
60075
  const isWindows = process.platform === "win32";
60010
- const binDir = path48.join(workspaceDir, "node_modules", ".bin");
60076
+ const binDir = path49.join(workspaceDir, "node_modules", ".bin");
60011
60077
  const validatedFiles = [];
60012
60078
  for (const file3 of files) {
60013
60079
  if (typeof file3 !== "string") {
60014
60080
  continue;
60015
60081
  }
60016
- const resolvedPath = path48.resolve(file3);
60082
+ const resolvedPath = path49.resolve(file3);
60017
60083
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
60018
60084
  if (validationError) {
60019
60085
  continue;
@@ -60031,10 +60097,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
60031
60097
  }
60032
60098
  let command;
60033
60099
  if (linter === "biome") {
60034
- const biomeBin = isWindows ? path48.join(binDir, "biome.EXE") : path48.join(binDir, "biome");
60100
+ const biomeBin = isWindows ? path49.join(binDir, "biome.EXE") : path49.join(binDir, "biome");
60035
60101
  command = [biomeBin, "check", ...validatedFiles];
60036
60102
  } else {
60037
- const eslintBin = isWindows ? path48.join(binDir, "eslint.cmd") : path48.join(binDir, "eslint");
60103
+ const eslintBin = isWindows ? path49.join(binDir, "eslint.cmd") : path49.join(binDir, "eslint");
60038
60104
  command = [eslintBin, ...validatedFiles];
60039
60105
  }
60040
60106
  try {
@@ -60171,7 +60237,7 @@ async function runSecretscanWithFiles(files, directory) {
60171
60237
  skippedFiles++;
60172
60238
  continue;
60173
60239
  }
60174
- const resolvedPath = path48.resolve(file3);
60240
+ const resolvedPath = path49.resolve(file3);
60175
60241
  const validationError = validatePath(resolvedPath, directory, directory);
60176
60242
  if (validationError) {
60177
60243
  skippedFiles++;
@@ -60189,7 +60255,7 @@ async function runSecretscanWithFiles(files, directory) {
60189
60255
  };
60190
60256
  }
60191
60257
  for (const file3 of validatedFiles) {
60192
- const ext = path48.extname(file3).toLowerCase();
60258
+ const ext = path49.extname(file3).toLowerCase();
60193
60259
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
60194
60260
  skippedFiles++;
60195
60261
  continue;
@@ -60348,7 +60414,7 @@ async function runPreCheckBatch(input, workspaceDir) {
60348
60414
  warn(`pre_check_batch: Invalid file path: ${file3}`);
60349
60415
  continue;
60350
60416
  }
60351
- changedFiles.push(path48.resolve(directory, file3));
60417
+ changedFiles.push(path49.resolve(directory, file3));
60352
60418
  }
60353
60419
  if (changedFiles.length === 0) {
60354
60420
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -60499,7 +60565,7 @@ var pre_check_batch = createSwarmTool({
60499
60565
  };
60500
60566
  return JSON.stringify(errorResult, null, 2);
60501
60567
  }
60502
- const resolvedDirectory = path48.resolve(typedArgs.directory);
60568
+ const resolvedDirectory = path49.resolve(typedArgs.directory);
60503
60569
  const workspaceAnchor = resolvedDirectory;
60504
60570
  const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
60505
60571
  if (dirError) {
@@ -60607,7 +60673,7 @@ init_tool();
60607
60673
  init_manager2();
60608
60674
  init_create_tool();
60609
60675
  import * as fs36 from "fs";
60610
- import * as path49 from "path";
60676
+ import * as path50 from "path";
60611
60677
  function detectPlaceholderContent(args2) {
60612
60678
  const issues = [];
60613
60679
  const placeholderPattern = /^\[\w[\w\s]*\]$/;
@@ -60711,7 +60777,7 @@ async function executeSavePlan(args2, fallbackDir) {
60711
60777
  try {
60712
60778
  await savePlan(dir, plan);
60713
60779
  try {
60714
- const markerPath = path49.join(dir, ".swarm", ".plan-write-marker");
60780
+ const markerPath = path50.join(dir, ".swarm", ".plan-write-marker");
60715
60781
  const marker = JSON.stringify({
60716
60782
  source: "save_plan",
60717
60783
  timestamp: new Date().toISOString(),
@@ -60723,7 +60789,7 @@ async function executeSavePlan(args2, fallbackDir) {
60723
60789
  return {
60724
60790
  success: true,
60725
60791
  message: "Plan saved successfully",
60726
- plan_path: path49.join(dir, ".swarm", "plan.json"),
60792
+ plan_path: path50.join(dir, ".swarm", "plan.json"),
60727
60793
  phases_count: plan.phases.length,
60728
60794
  tasks_count: tasksCount
60729
60795
  };
@@ -60762,7 +60828,7 @@ var save_plan = createSwarmTool({
60762
60828
  init_dist();
60763
60829
  init_manager();
60764
60830
  import * as fs37 from "fs";
60765
- import * as path50 from "path";
60831
+ import * as path51 from "path";
60766
60832
 
60767
60833
  // src/sbom/detectors/index.ts
60768
60834
  init_utils();
@@ -61610,7 +61676,7 @@ function findManifestFiles(rootDir) {
61610
61676
  try {
61611
61677
  const entries = fs37.readdirSync(dir, { withFileTypes: true });
61612
61678
  for (const entry of entries) {
61613
- const fullPath = path50.join(dir, entry.name);
61679
+ const fullPath = path51.join(dir, entry.name);
61614
61680
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
61615
61681
  continue;
61616
61682
  }
@@ -61619,7 +61685,7 @@ function findManifestFiles(rootDir) {
61619
61685
  } else if (entry.isFile()) {
61620
61686
  for (const pattern of patterns) {
61621
61687
  if (simpleGlobToRegex(pattern).test(entry.name)) {
61622
- manifestFiles.push(path50.relative(rootDir, fullPath));
61688
+ manifestFiles.push(path51.relative(rootDir, fullPath));
61623
61689
  break;
61624
61690
  }
61625
61691
  }
@@ -61637,11 +61703,11 @@ function findManifestFilesInDirs(directories, workingDir) {
61637
61703
  try {
61638
61704
  const entries = fs37.readdirSync(dir, { withFileTypes: true });
61639
61705
  for (const entry of entries) {
61640
- const fullPath = path50.join(dir, entry.name);
61706
+ const fullPath = path51.join(dir, entry.name);
61641
61707
  if (entry.isFile()) {
61642
61708
  for (const pattern of patterns) {
61643
61709
  if (simpleGlobToRegex(pattern).test(entry.name)) {
61644
- found.push(path50.relative(workingDir, fullPath));
61710
+ found.push(path51.relative(workingDir, fullPath));
61645
61711
  break;
61646
61712
  }
61647
61713
  }
@@ -61654,11 +61720,11 @@ function findManifestFilesInDirs(directories, workingDir) {
61654
61720
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
61655
61721
  const dirs = new Set;
61656
61722
  for (const file3 of changedFiles) {
61657
- let currentDir = path50.dirname(file3);
61723
+ let currentDir = path51.dirname(file3);
61658
61724
  while (true) {
61659
- if (currentDir && currentDir !== "." && currentDir !== path50.sep) {
61660
- dirs.add(path50.join(workingDir, currentDir));
61661
- const parent = path50.dirname(currentDir);
61725
+ if (currentDir && currentDir !== "." && currentDir !== path51.sep) {
61726
+ dirs.add(path51.join(workingDir, currentDir));
61727
+ const parent = path51.dirname(currentDir);
61662
61728
  if (parent === currentDir)
61663
61729
  break;
61664
61730
  currentDir = parent;
@@ -61742,7 +61808,7 @@ var sbom_generate = createSwarmTool({
61742
61808
  const changedFiles = obj.changed_files;
61743
61809
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
61744
61810
  const workingDir = directory;
61745
- const outputDir = path50.isAbsolute(relativeOutputDir) ? relativeOutputDir : path50.join(workingDir, relativeOutputDir);
61811
+ const outputDir = path51.isAbsolute(relativeOutputDir) ? relativeOutputDir : path51.join(workingDir, relativeOutputDir);
61746
61812
  let manifestFiles = [];
61747
61813
  if (scope === "all") {
61748
61814
  manifestFiles = findManifestFiles(workingDir);
@@ -61765,7 +61831,7 @@ var sbom_generate = createSwarmTool({
61765
61831
  const processedFiles = [];
61766
61832
  for (const manifestFile of manifestFiles) {
61767
61833
  try {
61768
- const fullPath = path50.isAbsolute(manifestFile) ? manifestFile : path50.join(workingDir, manifestFile);
61834
+ const fullPath = path51.isAbsolute(manifestFile) ? manifestFile : path51.join(workingDir, manifestFile);
61769
61835
  if (!fs37.existsSync(fullPath)) {
61770
61836
  continue;
61771
61837
  }
@@ -61782,7 +61848,7 @@ var sbom_generate = createSwarmTool({
61782
61848
  const bom = generateCycloneDX(allComponents);
61783
61849
  const bomJson = serializeCycloneDX(bom);
61784
61850
  const filename = generateSbomFilename();
61785
- const outputPath = path50.join(outputDir, filename);
61851
+ const outputPath = path51.join(outputDir, filename);
61786
61852
  fs37.writeFileSync(outputPath, bomJson, "utf-8");
61787
61853
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
61788
61854
  try {
@@ -61826,7 +61892,7 @@ var sbom_generate = createSwarmTool({
61826
61892
  init_dist();
61827
61893
  init_create_tool();
61828
61894
  import * as fs38 from "fs";
61829
- import * as path51 from "path";
61895
+ import * as path52 from "path";
61830
61896
  var SPEC_CANDIDATES = [
61831
61897
  "openapi.json",
61832
61898
  "openapi.yaml",
@@ -61858,12 +61924,12 @@ function normalizePath2(p) {
61858
61924
  }
61859
61925
  function discoverSpecFile(cwd, specFileArg) {
61860
61926
  if (specFileArg) {
61861
- const resolvedPath = path51.resolve(cwd, specFileArg);
61862
- const normalizedCwd = cwd.endsWith(path51.sep) ? cwd : cwd + path51.sep;
61927
+ const resolvedPath = path52.resolve(cwd, specFileArg);
61928
+ const normalizedCwd = cwd.endsWith(path52.sep) ? cwd : cwd + path52.sep;
61863
61929
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
61864
61930
  throw new Error("Invalid spec_file: path traversal detected");
61865
61931
  }
61866
- const ext = path51.extname(resolvedPath).toLowerCase();
61932
+ const ext = path52.extname(resolvedPath).toLowerCase();
61867
61933
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
61868
61934
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
61869
61935
  }
@@ -61877,7 +61943,7 @@ function discoverSpecFile(cwd, specFileArg) {
61877
61943
  return resolvedPath;
61878
61944
  }
61879
61945
  for (const candidate of SPEC_CANDIDATES) {
61880
- const candidatePath = path51.resolve(cwd, candidate);
61946
+ const candidatePath = path52.resolve(cwd, candidate);
61881
61947
  if (fs38.existsSync(candidatePath)) {
61882
61948
  const stats = fs38.statSync(candidatePath);
61883
61949
  if (stats.size <= MAX_SPEC_SIZE) {
@@ -61889,7 +61955,7 @@ function discoverSpecFile(cwd, specFileArg) {
61889
61955
  }
61890
61956
  function parseSpec(specFile) {
61891
61957
  const content = fs38.readFileSync(specFile, "utf-8");
61892
- const ext = path51.extname(specFile).toLowerCase();
61958
+ const ext = path52.extname(specFile).toLowerCase();
61893
61959
  if (ext === ".json") {
61894
61960
  return parseJsonSpec(content);
61895
61961
  }
@@ -61965,7 +62031,7 @@ function extractRoutes(cwd) {
61965
62031
  return;
61966
62032
  }
61967
62033
  for (const entry of entries) {
61968
- const fullPath = path51.join(dir, entry.name);
62034
+ const fullPath = path52.join(dir, entry.name);
61969
62035
  if (entry.isSymbolicLink()) {
61970
62036
  continue;
61971
62037
  }
@@ -61975,7 +62041,7 @@ function extractRoutes(cwd) {
61975
62041
  }
61976
62042
  walkDir(fullPath);
61977
62043
  } else if (entry.isFile()) {
61978
- const ext = path51.extname(entry.name).toLowerCase();
62044
+ const ext = path52.extname(entry.name).toLowerCase();
61979
62045
  const baseName = entry.name.toLowerCase();
61980
62046
  if (![".ts", ".js", ".mjs"].includes(ext)) {
61981
62047
  continue;
@@ -62145,7 +62211,7 @@ init_secretscan();
62145
62211
  init_tool();
62146
62212
  init_create_tool();
62147
62213
  import * as fs39 from "fs";
62148
- import * as path52 from "path";
62214
+ import * as path53 from "path";
62149
62215
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
62150
62216
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
62151
62217
  function containsControlCharacters(str) {
@@ -62174,11 +62240,11 @@ function containsWindowsAttacks(str) {
62174
62240
  }
62175
62241
  function isPathInWorkspace(filePath, workspace) {
62176
62242
  try {
62177
- const resolvedPath = path52.resolve(workspace, filePath);
62243
+ const resolvedPath = path53.resolve(workspace, filePath);
62178
62244
  const realWorkspace = fs39.realpathSync(workspace);
62179
62245
  const realResolvedPath = fs39.realpathSync(resolvedPath);
62180
- const relativePath = path52.relative(realWorkspace, realResolvedPath);
62181
- if (relativePath.startsWith("..") || path52.isAbsolute(relativePath)) {
62246
+ const relativePath = path53.relative(realWorkspace, realResolvedPath);
62247
+ if (relativePath.startsWith("..") || path53.isAbsolute(relativePath)) {
62182
62248
  return false;
62183
62249
  }
62184
62250
  return true;
@@ -62190,7 +62256,7 @@ function validatePathForRead(filePath, workspace) {
62190
62256
  return isPathInWorkspace(filePath, workspace);
62191
62257
  }
62192
62258
  function extractTSSymbols(filePath, cwd) {
62193
- const fullPath = path52.join(cwd, filePath);
62259
+ const fullPath = path53.join(cwd, filePath);
62194
62260
  if (!validatePathForRead(fullPath, cwd)) {
62195
62261
  return [];
62196
62262
  }
@@ -62342,7 +62408,7 @@ function extractTSSymbols(filePath, cwd) {
62342
62408
  });
62343
62409
  }
62344
62410
  function extractPythonSymbols(filePath, cwd) {
62345
- const fullPath = path52.join(cwd, filePath);
62411
+ const fullPath = path53.join(cwd, filePath);
62346
62412
  if (!validatePathForRead(fullPath, cwd)) {
62347
62413
  return [];
62348
62414
  }
@@ -62425,7 +62491,7 @@ var symbols = createSwarmTool({
62425
62491
  }, null, 2);
62426
62492
  }
62427
62493
  const cwd = directory;
62428
- const ext = path52.extname(file3);
62494
+ const ext = path53.extname(file3);
62429
62495
  if (containsControlCharacters(file3)) {
62430
62496
  return JSON.stringify({
62431
62497
  file: file3,
@@ -62497,7 +62563,7 @@ init_dist();
62497
62563
  init_utils();
62498
62564
  init_create_tool();
62499
62565
  import * as fs40 from "fs";
62500
- import * as path53 from "path";
62566
+ import * as path54 from "path";
62501
62567
  var MAX_TEXT_LENGTH = 200;
62502
62568
  var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
62503
62569
  var SUPPORTED_EXTENSIONS2 = new Set([
@@ -62568,9 +62634,9 @@ function validatePathsInput(paths, cwd) {
62568
62634
  return { error: "paths contains path traversal", resolvedPath: null };
62569
62635
  }
62570
62636
  try {
62571
- const resolvedPath = path53.resolve(paths);
62572
- const normalizedCwd = path53.resolve(cwd);
62573
- const normalizedResolved = path53.resolve(resolvedPath);
62637
+ const resolvedPath = path54.resolve(paths);
62638
+ const normalizedCwd = path54.resolve(cwd);
62639
+ const normalizedResolved = path54.resolve(resolvedPath);
62574
62640
  if (!normalizedResolved.startsWith(normalizedCwd)) {
62575
62641
  return {
62576
62642
  error: "paths must be within the current working directory",
@@ -62586,7 +62652,7 @@ function validatePathsInput(paths, cwd) {
62586
62652
  }
62587
62653
  }
62588
62654
  function isSupportedExtension(filePath) {
62589
- const ext = path53.extname(filePath).toLowerCase();
62655
+ const ext = path54.extname(filePath).toLowerCase();
62590
62656
  return SUPPORTED_EXTENSIONS2.has(ext);
62591
62657
  }
62592
62658
  function findSourceFiles2(dir, files = []) {
@@ -62601,7 +62667,7 @@ function findSourceFiles2(dir, files = []) {
62601
62667
  if (SKIP_DIRECTORIES3.has(entry)) {
62602
62668
  continue;
62603
62669
  }
62604
- const fullPath = path53.join(dir, entry);
62670
+ const fullPath = path54.join(dir, entry);
62605
62671
  let stat2;
62606
62672
  try {
62607
62673
  stat2 = fs40.statSync(fullPath);
@@ -62713,7 +62779,7 @@ var todo_extract = createSwarmTool({
62713
62779
  filesToScan.push(scanPath);
62714
62780
  } else {
62715
62781
  const errorResult = {
62716
- error: `unsupported file extension: ${path53.extname(scanPath)}`,
62782
+ error: `unsupported file extension: ${path54.extname(scanPath)}`,
62717
62783
  total: 0,
62718
62784
  byPriority: { high: 0, medium: 0, low: 0 },
62719
62785
  entries: []
@@ -62759,14 +62825,14 @@ var todo_extract = createSwarmTool({
62759
62825
  init_tool();
62760
62826
  init_schema();
62761
62827
  import * as fs42 from "fs";
62762
- import * as path55 from "path";
62828
+ import * as path56 from "path";
62763
62829
 
62764
62830
  // src/hooks/diff-scope.ts
62765
62831
  import * as fs41 from "fs";
62766
- import * as path54 from "path";
62832
+ import * as path55 from "path";
62767
62833
  function getDeclaredScope(taskId, directory) {
62768
62834
  try {
62769
- const planPath = path54.join(directory, ".swarm", "plan.json");
62835
+ const planPath = path55.join(directory, ".swarm", "plan.json");
62770
62836
  if (!fs41.existsSync(planPath))
62771
62837
  return null;
62772
62838
  const raw = fs41.readFileSync(planPath, "utf-8");
@@ -62881,7 +62947,7 @@ var TIER_3_PATTERNS = [
62881
62947
  ];
62882
62948
  function matchesTier3Pattern(files) {
62883
62949
  for (const file3 of files) {
62884
- const fileName = path55.basename(file3);
62950
+ const fileName = path56.basename(file3);
62885
62951
  for (const pattern of TIER_3_PATTERNS) {
62886
62952
  if (pattern.test(fileName)) {
62887
62953
  return true;
@@ -62903,7 +62969,7 @@ function checkReviewerGate(taskId, workingDirectory) {
62903
62969
  if (hasActiveTurboMode2()) {
62904
62970
  const resolvedDir2 = workingDirectory ?? process.cwd();
62905
62971
  try {
62906
- const planPath = path55.join(resolvedDir2, ".swarm", "plan.json");
62972
+ const planPath = path56.join(resolvedDir2, ".swarm", "plan.json");
62907
62973
  const planRaw = fs42.readFileSync(planPath, "utf-8");
62908
62974
  const plan = JSON.parse(planRaw);
62909
62975
  for (const planPhase of plan.phases ?? []) {
@@ -62923,7 +62989,7 @@ function checkReviewerGate(taskId, workingDirectory) {
62923
62989
  }
62924
62990
  const resolvedDir = workingDirectory ?? process.cwd();
62925
62991
  try {
62926
- const evidencePath = path55.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
62992
+ const evidencePath = path56.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
62927
62993
  const raw = fs42.readFileSync(evidencePath, "utf-8");
62928
62994
  const evidence = JSON.parse(raw);
62929
62995
  if (evidence?.required_gates && Array.isArray(evidence.required_gates) && evidence?.gates) {
@@ -62964,7 +63030,7 @@ function checkReviewerGate(taskId, workingDirectory) {
62964
63030
  }
62965
63031
  try {
62966
63032
  const resolvedDir2 = workingDirectory ?? process.cwd();
62967
- const planPath = path55.join(resolvedDir2, ".swarm", "plan.json");
63033
+ const planPath = path56.join(resolvedDir2, ".swarm", "plan.json");
62968
63034
  const planRaw = fs42.readFileSync(planPath, "utf-8");
62969
63035
  const plan = JSON.parse(planRaw);
62970
63036
  for (const planPhase of plan.phases ?? []) {
@@ -63146,8 +63212,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
63146
63212
  };
63147
63213
  }
63148
63214
  }
63149
- normalizedDir = path55.normalize(args2.working_directory);
63150
- const pathParts = normalizedDir.split(path55.sep);
63215
+ normalizedDir = path56.normalize(args2.working_directory);
63216
+ const pathParts = normalizedDir.split(path56.sep);
63151
63217
  if (pathParts.includes("..")) {
63152
63218
  return {
63153
63219
  success: false,
@@ -63157,10 +63223,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
63157
63223
  ]
63158
63224
  };
63159
63225
  }
63160
- const resolvedDir = path55.resolve(normalizedDir);
63226
+ const resolvedDir = path56.resolve(normalizedDir);
63161
63227
  try {
63162
63228
  const realPath = fs42.realpathSync(resolvedDir);
63163
- const planPath = path55.join(realPath, ".swarm", "plan.json");
63229
+ const planPath = path56.join(realPath, ".swarm", "plan.json");
63164
63230
  if (!fs42.existsSync(planPath)) {
63165
63231
  return {
63166
63232
  success: false,
@@ -63355,7 +63421,7 @@ var OpenCodeSwarm = async (ctx) => {
63355
63421
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
63356
63422
  preflightTriggerManager = new PTM(automationConfig);
63357
63423
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
63358
- const swarmDir = path56.resolve(ctx.directory, ".swarm");
63424
+ const swarmDir = path57.resolve(ctx.directory, ".swarm");
63359
63425
  statusArtifact = new ASA(swarmDir);
63360
63426
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
63361
63427
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {