opencode-swarm 6.37.0 → 6.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -14916,7 +14916,7 @@ var init_schema = __esm(() => {
14916
14916
  max_encounter_score: exports_external.number().min(1).max(20).default(10)
14917
14917
  });
14918
14918
  CuratorConfigSchema = exports_external.object({
14919
- enabled: exports_external.boolean().default(false),
14919
+ enabled: exports_external.boolean().default(true),
14920
14920
  init_enabled: exports_external.boolean().default(true),
14921
14921
  phase_enabled: exports_external.boolean().default(true),
14922
14922
  max_summary_tokens: exports_external.number().min(500).max(8000).default(2000),
@@ -30051,6 +30051,10 @@ var init_create_tool = __esm(() => {
30051
30051
  });
30052
30052
 
30053
30053
  // src/tools/checkpoint.ts
30054
+ var exports_checkpoint = {};
30055
+ __export(exports_checkpoint, {
30056
+ checkpoint: () => checkpoint
30057
+ });
30054
30058
  import { spawnSync } from "child_process";
30055
30059
  import * as fs8 from "fs";
30056
30060
  import * as path10 from "path";
@@ -36647,11 +36651,11 @@ __export(exports_curator_drift, {
36647
36651
  readPriorDriftReports: () => readPriorDriftReports,
36648
36652
  buildDriftInjectionText: () => buildDriftInjectionText
36649
36653
  });
36650
- import * as fs26 from "fs";
36651
- import * as path37 from "path";
36654
+ import * as fs27 from "fs";
36655
+ import * as path38 from "path";
36652
36656
  async function readPriorDriftReports(directory) {
36653
- const swarmDir = path37.join(directory, ".swarm");
36654
- const entries = await fs26.promises.readdir(swarmDir).catch(() => null);
36657
+ const swarmDir = path38.join(directory, ".swarm");
36658
+ const entries = await fs27.promises.readdir(swarmDir).catch(() => null);
36655
36659
  if (entries === null)
36656
36660
  return [];
36657
36661
  const reportFiles = entries.filter((name2) => name2.startsWith(DRIFT_REPORT_PREFIX) && name2.endsWith(".json")).sort();
@@ -36677,10 +36681,10 @@ async function readPriorDriftReports(directory) {
36677
36681
  async function writeDriftReport(directory, report) {
36678
36682
  const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
36679
36683
  const filePath = validateSwarmPath(directory, filename);
36680
- const swarmDir = path37.dirname(filePath);
36681
- await fs26.promises.mkdir(swarmDir, { recursive: true });
36684
+ const swarmDir = path38.dirname(filePath);
36685
+ await fs27.promises.mkdir(swarmDir, { recursive: true });
36682
36686
  try {
36683
- await fs26.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
36687
+ await fs27.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
36684
36688
  } catch (err2) {
36685
36689
  throw new Error(`[curator-drift] Failed to write drift report to ${filePath}: ${String(err2)}`);
36686
36690
  }
@@ -38247,8 +38251,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
38247
38251
  var moduleRtn;
38248
38252
  var Module = moduleArg;
38249
38253
  var readyPromiseResolve, readyPromiseReject;
38250
- var readyPromise = new Promise((resolve17, reject) => {
38251
- readyPromiseResolve = resolve17;
38254
+ var readyPromise = new Promise((resolve15, reject) => {
38255
+ readyPromiseResolve = resolve15;
38252
38256
  readyPromiseReject = reject;
38253
38257
  });
38254
38258
  var ENVIRONMENT_IS_WEB = typeof window == "object";
@@ -38270,11 +38274,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
38270
38274
  throw toThrow;
38271
38275
  }, "quit_");
38272
38276
  var scriptDirectory = "";
38273
- function locateFile(path50) {
38277
+ function locateFile(path45) {
38274
38278
  if (Module["locateFile"]) {
38275
- return Module["locateFile"](path50, scriptDirectory);
38279
+ return Module["locateFile"](path45, scriptDirectory);
38276
38280
  }
38277
- return scriptDirectory + path50;
38281
+ return scriptDirectory + path45;
38278
38282
  }
38279
38283
  __name(locateFile, "locateFile");
38280
38284
  var readAsync, readBinary;
@@ -38328,13 +38332,13 @@ ${JSON.stringify(symbolNames, null, 2)}`);
38328
38332
  }
38329
38333
  readAsync = /* @__PURE__ */ __name(async (url3) => {
38330
38334
  if (isFileURI(url3)) {
38331
- return new Promise((resolve17, reject) => {
38335
+ return new Promise((resolve15, reject) => {
38332
38336
  var xhr = new XMLHttpRequest;
38333
38337
  xhr.open("GET", url3, true);
38334
38338
  xhr.responseType = "arraybuffer";
38335
38339
  xhr.onload = () => {
38336
38340
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
38337
- resolve17(xhr.response);
38341
+ resolve15(xhr.response);
38338
38342
  return;
38339
38343
  }
38340
38344
  reject(xhr.status);
@@ -38495,10 +38499,10 @@ ${JSON.stringify(symbolNames, null, 2)}`);
38495
38499
  return getBinarySync(binaryFile);
38496
38500
  }
38497
38501
  __name(getWasmBinary, "getWasmBinary");
38498
- async function instantiateArrayBuffer(binaryFile, imports2) {
38502
+ async function instantiateArrayBuffer(binaryFile, imports) {
38499
38503
  try {
38500
38504
  var binary2 = await getWasmBinary(binaryFile);
38501
- var instance2 = await WebAssembly.instantiate(binary2, imports2);
38505
+ var instance2 = await WebAssembly.instantiate(binary2, imports);
38502
38506
  return instance2;
38503
38507
  } catch (reason) {
38504
38508
  err(`failed to asynchronously prepare wasm: ${reason}`);
@@ -38506,20 +38510,20 @@ ${JSON.stringify(symbolNames, null, 2)}`);
38506
38510
  }
38507
38511
  }
38508
38512
  __name(instantiateArrayBuffer, "instantiateArrayBuffer");
38509
- async function instantiateAsync(binary2, binaryFile, imports2) {
38513
+ async function instantiateAsync(binary2, binaryFile, imports) {
38510
38514
  if (!binary2 && typeof WebAssembly.instantiateStreaming == "function" && !isFileURI(binaryFile) && !ENVIRONMENT_IS_NODE) {
38511
38515
  try {
38512
38516
  var response = fetch(binaryFile, {
38513
38517
  credentials: "same-origin"
38514
38518
  });
38515
- var instantiationResult = await WebAssembly.instantiateStreaming(response, imports2);
38519
+ var instantiationResult = await WebAssembly.instantiateStreaming(response, imports);
38516
38520
  return instantiationResult;
38517
38521
  } catch (reason) {
38518
38522
  err(`wasm streaming compile failed: ${reason}`);
38519
38523
  err("falling back to ArrayBuffer instantiation");
38520
38524
  }
38521
38525
  }
38522
- return instantiateArrayBuffer(binaryFile, imports2);
38526
+ return instantiateArrayBuffer(binaryFile, imports);
38523
38527
  }
38524
38528
  __name(instantiateAsync, "instantiateAsync");
38525
38529
  function getWasmImports() {
@@ -38554,10 +38558,10 @@ ${JSON.stringify(symbolNames, null, 2)}`);
38554
38558
  __name(receiveInstantiationResult, "receiveInstantiationResult");
38555
38559
  var info2 = getWasmImports();
38556
38560
  if (Module["instantiateWasm"]) {
38557
- return new Promise((resolve17, reject) => {
38561
+ return new Promise((resolve15, reject) => {
38558
38562
  Module["instantiateWasm"](info2, (mod, inst) => {
38559
38563
  receiveInstance(mod, inst);
38560
- resolve17(mod.exports);
38564
+ resolve15(mod.exports);
38561
38565
  });
38562
38566
  });
38563
38567
  }
@@ -40014,15 +40018,96 @@ ${JSON.stringify(symbolNames, null, 2)}`);
40014
40018
  });
40015
40019
 
40016
40020
  // src/lang/runtime.ts
40017
- var parserCache, initializedLanguages;
40021
+ import { fileURLToPath } from "url";
40022
+ async function initTreeSitter() {
40023
+ if (treeSitterInitialized) {
40024
+ return;
40025
+ }
40026
+ await Parser.init();
40027
+ treeSitterInitialized = true;
40028
+ }
40029
+ function sanitizeLanguageId(languageId) {
40030
+ const normalized = languageId.toLowerCase();
40031
+ if (!/^[a-z0-9-]+$/.test(normalized)) {
40032
+ throw new Error(`Invalid language ID: ${languageId}`);
40033
+ }
40034
+ return normalized;
40035
+ }
40036
+ function getWasmFileName(languageId) {
40037
+ const sanitized = sanitizeLanguageId(languageId).toLowerCase();
40038
+ if (LANGUAGE_WASM_MAP[sanitized]) {
40039
+ return LANGUAGE_WASM_MAP[sanitized];
40040
+ }
40041
+ return `tree-sitter-${sanitized}.wasm`;
40042
+ }
40043
+ function getGrammarsPath() {
40044
+ const isProduction = !import.meta.url.includes("src/");
40045
+ if (isProduction) {
40046
+ return "./lang/grammars/";
40047
+ }
40048
+ return "../../dist/lang/grammars/";
40049
+ }
40050
+ async function loadGrammar(languageId) {
40051
+ if (typeof languageId !== "string" || languageId.length > 100) {
40052
+ throw new Error(`Invalid languageId: must be a string of at most 100 characters`);
40053
+ }
40054
+ const normalizedId = sanitizeLanguageId(languageId).toLowerCase();
40055
+ if (normalizedId.length === 0) {
40056
+ throw new Error(`Invalid languageId: empty after sanitization`);
40057
+ }
40058
+ if (parserCache.has(normalizedId)) {
40059
+ return parserCache.get(normalizedId);
40060
+ }
40061
+ await initTreeSitter();
40062
+ const parser = new Parser;
40063
+ const wasmFileName = getWasmFileName(normalizedId);
40064
+ const grammarsPath = getGrammarsPath();
40065
+ const wasmPath = fileURLToPath(new URL(`${grammarsPath}${wasmFileName}`, import.meta.url));
40066
+ const { existsSync: existsSync27 } = await import("fs");
40067
+ if (!existsSync27(wasmPath)) {
40068
+ throw new Error(`Grammar file not found for ${languageId}: ${wasmPath}
40069
+ Make sure to run 'bun run build' to copy grammar files to dist/lang/grammars/`);
40070
+ }
40071
+ try {
40072
+ const language = await Language.load(wasmPath);
40073
+ parser.setLanguage(language);
40074
+ } catch (error93) {
40075
+ throw new Error(`Failed to load grammar for ${languageId}: ${error93 instanceof Error ? error93.message : String(error93)}
40076
+ WASM path: ${wasmPath}`);
40077
+ }
40078
+ parserCache.set(normalizedId, parser);
40079
+ initializedLanguages.add(normalizedId);
40080
+ return parser;
40081
+ }
40082
+ var parserCache, initializedLanguages, treeSitterInitialized = false, LANGUAGE_WASM_MAP;
40018
40083
  var init_runtime = __esm(() => {
40019
40084
  init_tree_sitter();
40020
40085
  parserCache = new Map;
40021
40086
  initializedLanguages = new Set;
40087
+ LANGUAGE_WASM_MAP = {
40088
+ javascript: "tree-sitter-javascript.wasm",
40089
+ typescript: "tree-sitter-typescript.wasm",
40090
+ python: "tree-sitter-python.wasm",
40091
+ go: "tree-sitter-go.wasm",
40092
+ rust: "tree-sitter-rust.wasm",
40093
+ cpp: "tree-sitter-cpp.wasm",
40094
+ c: "tree-sitter-cpp.wasm",
40095
+ csharp: "tree-sitter-c-sharp.wasm",
40096
+ css: "tree-sitter-css.wasm",
40097
+ html: "tree-sitter-html.wasm",
40098
+ json: "tree-sitter-json.wasm",
40099
+ bash: "tree-sitter-bash.wasm",
40100
+ ruby: "tree-sitter-ruby.wasm",
40101
+ php: "tree-sitter-php.wasm",
40102
+ java: "tree-sitter-java.wasm",
40103
+ kotlin: "tree-sitter-kotlin.wasm",
40104
+ swift: "tree-sitter-swift.wasm",
40105
+ dart: "tree-sitter-dart.wasm"
40106
+ };
40022
40107
  });
40023
40108
 
40024
40109
  // src/index.ts
40025
- import * as path60 from "path";
40110
+ import * as path62 from "path";
40026
40111
 
40027
40112
  // src/agents/index.ts
40028
40113
  init_config();
@@ -42366,6 +42451,82 @@ RULES:
42366
42451
  - Do NOT rephrase or summarize doc content with your own words \u2014 use the actual text from the file
42367
42452
  - Full doc content is only loaded when relevant to the current task, never preloaded
42368
42453
  `;
42454
+ var CURATOR_INIT_PROMPT = `## IDENTITY
42455
+ You are Explorer in CURATOR_INIT mode. You consolidate prior session knowledge into an architect briefing.
42456
+ DO NOT use the Task tool to delegate. You ARE the agent that does the work.
42457
+
42458
+ INPUT FORMAT:
42459
+ TASK: CURATOR_INIT
42460
+ PRIOR_SUMMARY: [JSON or "none"]
42461
+ KNOWLEDGE_ENTRIES: [JSON array of high-confidence entries]
42462
+ PROJECT_CONTEXT: [context.md excerpt]
42463
+
42464
+ ACTIONS:
42465
+ - Read the prior summary to understand session history
42466
+ - Cross-reference knowledge entries against project context
42467
+ - Identify contradictions (knowledge says X, project state shows Y)
42468
+ - Produce a concise briefing for the architect
42469
+
42470
+ RULES:
42471
+ - Output under 2000 chars
42472
+ - No code modifications
42473
+ - Flag contradictions explicitly with CONTRADICTION: prefix
42474
+ - If no prior summary exists, state "First session \u2014 no prior context"
42475
+
42476
+ OUTPUT FORMAT:
42477
+ BRIEFING:
42478
+ [concise summary of prior session state, key decisions, active blockers]
42479
+
42480
+ CONTRADICTIONS:
42481
+ - [entry_id]: [description] (or "None detected")
42482
+
42483
+ KNOWLEDGE_STATS:
42484
+ - Entries reviewed: [N]
42485
+ - Prior phases covered: [N]
42486
+ `;
42487
+ var CURATOR_PHASE_PROMPT = `## IDENTITY
42488
+ You are Explorer in CURATOR_PHASE mode. You consolidate a completed phase into a digest.
42489
+ DO NOT use the Task tool to delegate. You ARE the agent that does the work.
42490
+
42491
+ INPUT FORMAT:
42492
+ TASK: CURATOR_PHASE [phase_number]
42493
+ PRIOR_DIGEST: [running summary or "none"]
42494
+ PHASE_EVENTS: [JSON array from events.jsonl for this phase]
42495
+ PHASE_EVIDENCE: [summary of evidence bundles]
42496
+ PHASE_DECISIONS: [decisions from context.md]
42497
+ AGENTS_DISPATCHED: [list]
42498
+ AGENTS_EXPECTED: [list from config]
42499
+
42500
+ ACTIONS:
42501
+ - Extend the prior digest with this phase's outcomes (do NOT regenerate from scratch)
42502
+ - Identify workflow deviations: missing reviewer, missing retro, skipped test_engineer
42503
+ - Recommend knowledge updates: entries to promote, archive, or flag as contradicted
42504
+ - Summarize key decisions and blockers resolved
42505
+
42506
+ RULES:
42507
+ - Output under 2000 chars
42508
+ - No code modifications
42509
+ - Compliance observations are READ-ONLY \u2014 report, do not enforce
42510
+ - Extend the digest, never replace it
42511
+
42512
+ OUTPUT FORMAT:
42513
+ PHASE_DIGEST:
42514
+ phase: [N]
42515
+ summary: [what was accomplished]
42516
+ agents_used: [list]
42517
+ tasks_completed: [N]/[total]
42518
+ key_decisions: [list]
42519
+ blockers_resolved: [list]
42520
+
42521
+ COMPLIANCE:
42522
+ - [type]: [description] (or "No deviations observed")
42523
+
42524
+ KNOWLEDGE_UPDATES:
42525
+ - [action] [entry_id or "new"]: [reason] (or "No recommendations")
42526
+
42527
+ EXTENDED_DIGEST:
42528
+ [the full running digest with this phase appended]
42529
+ `;
42369
42530
  function createExplorerAgent(model, customPrompt, customAppendPrompt) {
42370
42531
  let prompt = EXPLORER_PROMPT;
42371
42532
  if (customPrompt) {
@@ -42390,6 +42551,26 @@ ${customAppendPrompt}`;
42390
42551
  }
42391
42552
  };
42392
42553
  }
42554
+ function createExplorerCuratorAgent(model, mode, customAppendPrompt) {
42555
+ const basePrompt = mode === "CURATOR_INIT" ? CURATOR_INIT_PROMPT : CURATOR_PHASE_PROMPT;
42556
+ const prompt = customAppendPrompt ? `${basePrompt}
42557
+
42558
+ ${customAppendPrompt}` : basePrompt;
42559
+ return {
42560
+ name: "explorer",
42561
+ description: `Explorer in ${mode} mode \u2014 consolidates context at phase boundaries.`,
42562
+ config: {
42563
+ model,
42564
+ temperature: 0.1,
42565
+ prompt,
42566
+ tools: {
42567
+ write: false,
42568
+ edit: false,
42569
+ patch: false
42570
+ }
42571
+ }
42572
+ };
42573
+ }
42393
42574
 
42394
42575
  // src/agents/reviewer.ts
42395
42576
  var REVIEWER_PROMPT = `## PRESSURE IMMUNITY
@@ -44545,9 +44726,9 @@ init_schema();
44545
44726
  import path15 from "path";
44546
44727
 
44547
44728
  // src/hooks/curator.ts
44548
- init_event_bus();
44549
44729
  import * as fs9 from "fs";
44550
44730
  import * as path13 from "path";
44731
+ init_event_bus();
44551
44732
 
44552
44733
  // src/hooks/knowledge-store.ts
44553
44734
  var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
@@ -44705,6 +44886,33 @@ function inferTags(lesson) {
44705
44886
 
44706
44887
  // src/hooks/curator.ts
44707
44888
  init_utils2();
44889
+ var CURATOR_LLM_TIMEOUT_MS = 30000;
44890
+ function parseKnowledgeRecommendations(llmOutput) {
44891
+ const recommendations = [];
44892
+ const section = llmOutput.match(/KNOWLEDGE_UPDATES:\s*\n([\s\S]*?)(?:\n\n|\n[A-Z_]+:|$)/);
44893
+ if (!section)
44894
+ return recommendations;
44895
+ const lines = section[1].split(`
44896
+ `);
44897
+ for (const line of lines) {
44898
+ const trimmed = line.trim();
44899
+ if (!trimmed.startsWith("-"))
44900
+ continue;
44901
+ const match = trimmed.match(/^-\s+(promote|archive|flag_contradiction)\s+(\S+):\s+(.+)$/i);
44902
+ if (match) {
44903
+ const action = match[1].toLowerCase();
44904
+ const entryId = match[2] === "new" ? undefined : match[2];
44905
+ const reason = match[3].trim();
44906
+ recommendations.push({
44907
+ action,
44908
+ entry_id: entryId,
44909
+ lesson: reason,
44910
+ reason
44911
+ });
44912
+ }
44913
+ }
44914
+ return recommendations;
44915
+ }
44708
44916
  async function readCuratorSummary(directory) {
44709
44917
  const content = await readSwarmFileAsync(directory, "curator-summary.json");
44710
44918
  if (content === null) {
@@ -44863,7 +45071,7 @@ function checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, pha
44863
45071
  }
44864
45072
  return observations;
44865
45073
  }
44866
- async function runCuratorInit(directory, config3) {
45074
+ async function runCuratorInit(directory, config3, llmDelegate) {
44867
45075
  try {
44868
45076
  const priorSummary = await readCuratorSummary(directory);
44869
45077
  const knowledgePath = resolveSwarmKnowledgePath(directory);
@@ -44907,9 +45115,41 @@ async function runCuratorInit(directory, config3) {
44907
45115
  briefingParts.push(contextMd.slice(0, maxContextChars));
44908
45116
  }
44909
45117
  const contradictions = allEntries.filter((e) => Array.isArray(e.tags) && e.tags.some((t) => t.includes("contradiction"))).map((e) => typeof e.lesson === "string" ? e.lesson : JSON.stringify(e.lesson));
45118
+ let briefingText = briefingParts.join(`
45119
+ `);
45120
+ if (llmDelegate) {
45121
+ try {
45122
+ const curatorAgent = createExplorerCuratorAgent("default", "CURATOR_INIT");
45123
+ const userInput = [
45124
+ "TASK: CURATOR_INIT",
45125
+ `PRIOR_SUMMARY: ${priorSummary ? JSON.stringify(priorSummary) : "none"}`,
45126
+ `KNOWLEDGE_ENTRIES: ${JSON.stringify(highConfidenceEntries.slice(0, 10))}`,
45127
+ `PROJECT_CONTEXT: ${contextMd?.slice(0, config3.max_summary_tokens * 2) ?? "none"}`
45128
+ ].join(`
45129
+ `);
45130
+ const systemPrompt = curatorAgent.config.prompt ?? "";
45131
+ const llmOutput = await Promise.race([
45132
+ llmDelegate(systemPrompt, userInput),
45133
+ new Promise((_, reject) => setTimeout(() => reject(new Error("CURATOR_LLM_TIMEOUT")), CURATOR_LLM_TIMEOUT_MS))
45134
+ ]);
45135
+ if (llmOutput?.trim()) {
45136
+ briefingText = `${briefingText}
45137
+
45138
+ ## LLM-Enhanced Analysis
45139
+ ${llmOutput.trim()}`;
45140
+ }
45141
+ getGlobalEventBus().publish("curator.init.llm_completed", {
45142
+ enhanced: true
45143
+ });
45144
+ } catch (err2) {
45145
+ console.warn(`[curator] LLM delegation failed during CURATOR_INIT, using data-only mode: ${err2 instanceof Error ? err2.message : String(err2)}`);
45146
+ getGlobalEventBus().publish("curator.init.llm_fallback", {
45147
+ error: String(err2)
45148
+ });
45149
+ }
45150
+ }
44910
45151
  const result = {
44911
- briefing: briefingParts.join(`
44912
- `),
45152
+ briefing: briefingText,
44913
45153
  contradictions,
44914
45154
  knowledge_entries_reviewed: allEntries.length,
44915
45155
  prior_phases_covered: priorSummary ? priorSummary.last_phase_covered : 0
@@ -44934,7 +45174,7 @@ Could not load prior session context.`,
44934
45174
  };
44935
45175
  }
44936
45176
  }
44937
- async function runCuratorPhase(directory, phase, agentsDispatched, _config, _knowledgeConfig) {
45177
+ async function runCuratorPhase(directory, phase, agentsDispatched, _config, _knowledgeConfig, llmDelegate) {
44938
45178
  try {
44939
45179
  const priorSummary = await readCuratorSummary(directory);
44940
45180
  const eventsJsonlContent = await readSwarmFileAsync(directory, "events.jsonl");
@@ -44967,7 +45207,40 @@ async function runCuratorPhase(directory, phase, agentsDispatched, _config, _kno
44967
45207
  key_decisions: keyDecisions.slice(0, 5),
44968
45208
  blockers_resolved: []
44969
45209
  };
44970
- const knowledgeRecommendations = [];
45210
+ let knowledgeRecommendations = [];
45211
+ if (llmDelegate) {
45212
+ try {
45213
+ const curatorAgent = createExplorerCuratorAgent("default", "CURATOR_PHASE");
45214
+ const priorDigest = priorSummary?.digest ?? "none";
45215
+ const systemPrompt = curatorAgent.config.prompt ?? "";
45216
+ const userInput = [
45217
+ `TASK: CURATOR_PHASE ${phase}`,
45218
+ `PRIOR_DIGEST: ${priorDigest}`,
45219
+ `PHASE_EVENTS: ${JSON.stringify(phaseEvents.slice(0, 50))}`,
45220
+ `PHASE_DECISIONS: ${JSON.stringify(keyDecisions)}`,
45221
+ `AGENTS_DISPATCHED: ${JSON.stringify(agentsDispatched)}`,
45222
+ `AGENTS_EXPECTED: ["reviewer", "test_engineer"]`
45223
+ ].join(`
45224
+ `);
45225
+ const llmOutput = await Promise.race([
45226
+ llmDelegate(systemPrompt, userInput),
45227
+ new Promise((_, reject) => setTimeout(() => reject(new Error("CURATOR_LLM_TIMEOUT")), CURATOR_LLM_TIMEOUT_MS))
45228
+ ]);
45229
+ if (llmOutput?.trim()) {
45230
+ knowledgeRecommendations = parseKnowledgeRecommendations(llmOutput);
45231
+ }
45232
+ getGlobalEventBus().publish("curator.phase.llm_completed", {
45233
+ phase,
45234
+ recommendations: knowledgeRecommendations.length
45235
+ });
45236
+ } catch (err2) {
45237
+ console.warn(`[curator] LLM delegation failed during CURATOR_PHASE ${phase}, using data-only mode: ${err2 instanceof Error ? err2.message : String(err2)}`);
45238
+ getGlobalEventBus().publish("curator.phase.llm_fallback", {
45239
+ phase,
45240
+ error: String(err2)
45241
+ });
45242
+ }
45243
+ }
44971
45244
  const sessionId = `session-${Date.now()}`;
44972
45245
  const now = new Date().toISOString();
44973
45246
  let updatedSummary;
@@ -50508,6 +50781,68 @@ function maskToolOutput(msg, _threshold) {
50508
50781
  init_schema();
50509
50782
  import * as fs21 from "fs";
50510
50783
  import * as path32 from "path";
50784
+
50785
+ // src/parallel/review-router.ts
50786
+ async function computeComplexity(directory, changedFiles) {
50787
+ let functionCount = 0;
50788
+ let astChangeCount = 0;
50789
+ let maxFileComplexity = 0;
50790
+ for (const file3 of changedFiles) {
50791
+ if (!/\.(ts|js|tsx|jsx|py|go|rs)$/.test(file3)) {
50792
+ continue;
50793
+ }
50794
+ try {
50795
+ const fs21 = await import("fs");
50796
+ const path30 = await import("path");
50797
+ const filePath = path30.join(directory, file3);
50798
+ if (!fs21.existsSync(filePath)) {
50799
+ continue;
50800
+ }
50801
+ const content = fs21.readFileSync(filePath, "utf-8");
50802
+ const functionMatches = content.match(/\b(function|def|func|fn)\s+\w+/g);
50803
+ const fileFunctionCount = functionMatches?.length || 0;
50804
+ functionCount += fileFunctionCount;
50805
+ const lines = content.split(`
50806
+ `).length;
50807
+ const estimatedChanges = Math.min(lines / 10, 50);
50808
+ astChangeCount += estimatedChanges;
50809
+ const fileComplexity = fileFunctionCount + lines / 100;
50810
+ maxFileComplexity = Math.max(maxFileComplexity, fileComplexity);
50811
+ } catch {}
50812
+ }
50813
+ return {
50814
+ fileCount: changedFiles.length,
50815
+ functionCount,
50816
+ astChangeCount: Math.round(astChangeCount),
50817
+ maxFileComplexity: Math.round(maxFileComplexity * 10) / 10
50818
+ };
50819
+ }
50820
+ function routeReview(metrics) {
50821
+ const isHighComplexity = metrics.fileCount >= 5 || metrics.functionCount >= 10 || metrics.astChangeCount >= 30 || metrics.maxFileComplexity >= 15;
50822
+ if (isHighComplexity) {
50823
+ return {
50824
+ reviewerCount: 2,
50825
+ testEngineerCount: 2,
50826
+ depth: "double",
50827
+ reason: `High complexity: ${metrics.fileCount} files, ${metrics.functionCount} functions, complexity score ${metrics.maxFileComplexity}`
50828
+ };
50829
+ }
50830
+ return {
50831
+ reviewerCount: 1,
50832
+ testEngineerCount: 1,
50833
+ depth: "single",
50834
+ reason: `Standard complexity: ${metrics.fileCount} files, ${metrics.functionCount} functions`
50835
+ };
50836
+ }
50837
+ async function routeReviewForChanges(directory, changedFiles) {
50838
+ const metrics = await computeComplexity(directory, changedFiles);
50839
+ return routeReview(metrics);
50840
+ }
50841
+ function shouldParallelizeReview(routing) {
50842
+ return routing.depth === "double";
50843
+ }
50844
+
50845
+ // src/hooks/delegation-gate.ts
50511
50846
  init_telemetry();
50512
50847
 
50513
50848
  // src/hooks/guardrails.ts
@@ -51581,6 +51916,21 @@ function createDelegationGateHook(config3, directory) {
51581
51916
  if (typeof subagentType !== "string")
51582
51917
  return;
51583
51918
  const targetAgent = stripKnownSwarmPrefix(subagentType);
51919
+ if (targetAgent === "reviewer") {
51920
+ try {
51921
+ const reviewSession = swarmState.agentSessions.get(input.sessionID);
51922
+ if (reviewSession) {
51923
+ const changedFiles = reviewSession.modifiedFilesThisCoderTask ?? [];
51924
+ if (changedFiles.length > 0) {
51925
+ const routing = await routeReviewForChanges(directory, changedFiles);
51926
+ if (shouldParallelizeReview(routing)) {
51927
+ reviewSession.pendingAdvisoryMessages ??= [];
51928
+ reviewSession.pendingAdvisoryMessages.push(`REVIEW ROUTING: High complexity detected (${routing.reason}). ` + `Consider parallel review: ${routing.reviewerCount} reviewers, ${routing.testEngineerCount} test engineers recommended.`);
51929
+ }
51930
+ }
51931
+ }
51932
+ } catch {}
51933
+ }
51584
51934
  if (targetAgent !== "coder")
51585
51935
  return;
51586
51936
  const session = swarmState.agentSessions.get(input.sessionID);
@@ -52370,7 +52720,7 @@ init_schema();
52370
52720
  init_manager();
52371
52721
  init_detector();
52372
52722
  init_manager2();
52373
- import * as fs24 from "fs";
52723
+ import * as fs25 from "fs";
52374
52724
 
52375
52725
  // src/services/decision-drift-analyzer.ts
52376
52726
  init_utils2();
@@ -52653,6 +53003,8 @@ init_utils();
52653
53003
  // src/hooks/adversarial-detector.ts
52654
53004
  init_constants();
52655
53005
  init_schema();
53006
+ import * as fs24 from "fs/promises";
53007
+ import * as path35 from "path";
52656
53008
  function safeGet(obj, key) {
52657
53009
  if (!obj || !Object.hasOwn(obj, key))
52658
53010
  return;
@@ -52684,6 +53036,248 @@ function formatAdversarialWarning(agentA, agentB, sharedModel, policy) {
52684
53036
  }
52685
53037
  return `\u26A0\uFE0F Same-model adversarial pair detected. Agent ${agentA} and checker ${agentB} both use model ${sharedModel}. Review may lack independence.`;
52686
53038
  }
53039
+ var PRECEDENT_MANIPULATION_PATTERNS = [
53040
+ /we skipped .* in phase \d+/i,
53041
+ /consistent with how we handled/i,
53042
+ /going forward/i,
53043
+ /the reviewer didn't flag this pattern before/i,
53044
+ /this is consistent with/i,
53045
+ /we should continue/i
53046
+ ];
53047
+ var SELF_REVIEW_PATTERNS = [
53048
+ /I (verified|checked|reviewed|validated).*(myself|my own)/i,
53049
+ /I (think|believe) this (looks|is) correct/i,
53050
+ /this (looks|seems|appears) (good|correct|fine)/i
53051
+ ];
53052
+ var CONTENT_EXEMPTION_PATTERNS = [
53053
+ /documentation doesn't need/i,
53054
+ /config changes are trivial/i,
53055
+ /just a (rename|refactor|typo)/i,
53056
+ /test files don't need/i,
53057
+ /this is (just|only) a/i,
53058
+ /no need for (review|the full)/i
53059
+ ];
53060
+ var GATE_DELEGATION_BYPASS_PATTERNS = [
53061
+ /I verified the changes/i,
53062
+ /code looks correct to me/i,
53063
+ /the code looks (good|fine)/i,
53064
+ /task marked complete/i,
53065
+ /I (checked|reviewed).*myself/i,
53066
+ /edit tool on (src|tests|config)/i,
53067
+ /write tool on (src|tests|config)/i,
53068
+ /writeCount.*\d+.*source/i,
53069
+ /I'll just make this small fix directly/i,
53070
+ /It's faster if I do it myself/i,
53071
+ /edit tool on.*plan\.md/i,
53072
+ /write tool on.*plan\.md/i,
53073
+ /\[ \].*to \[x\].*in plan\.md/i,
53074
+ /status.*pending.*complete.*plan\.json/i,
53075
+ /I'll just mark this one as done/i,
53076
+ /mark it done directly/i
53077
+ ];
53078
+ var GATE_MISCLASSIFICATION_PATTERNS = [
53079
+ /(?:src\/|source\/|source\s+code|source\s+file).*tier\s*[01]/i,
53080
+ /tier\s*[01].*(?:src\/|source\/|source\s+code|source\s+file)/i,
53081
+ /(?:security|auth|crypto|secret|credential|permission).*tier\s*[012]/i,
53082
+ /tier\s*[012].*(?:security|auth|crypto|secret|credential|permission)/i,
53083
+ /below\s+tier\s*3.*(?:security|auth|crypto|secret|credential|permission)/i,
53084
+ /classification[:\s-]*tier\s*[01]/i,
53085
+ /tier\s*[01][\s:]*classification/i,
53086
+ /(?:small|trivial|minor).*tier\s*[01]/i,
53087
+ /tier\s*[01].*(?:small|trivial|minor)/i,
53088
+ /(?:small|trivial|minor).*(?:classification|classified|assigned).*tier/i,
53089
+ /(?:classification|classified|assigned).*(?:small|trivial|minor).*tier/i,
53090
+ /pipeline\s+started.*(?:assigning|setting|classifying).*tier/i,
53091
+ /(?:assigning|setting|classifying).*tier.*after.*pipeline/i,
53092
+ /tier.*assigned.*(?:after|retroactive)/i,
53093
+ /retroactive.*(?:tier|classification)/i
53094
+ ];
53095
+ var VELOCITY_RATIONALIZATION_PATTERNS = [
53096
+ /to save time/i,
53097
+ /since we're behind/i,
53098
+ /quick fix/i,
53099
+ /review.*later/i,
53100
+ /in the interest of efficiency/i,
53101
+ /we can (review|check).*later/i,
53102
+ /for (speed|efficiency)/i
53103
+ ];
53104
+ var INTER_AGENT_MANIPULATION_PATTERNS = [
53105
+ /\b(5th|fifth|final|last)\s+(attempt|try|time)\b/i,
53106
+ /\bthis\s+is\s+(blocking|blocking\s+everything|critical|urgent)\b/i,
53107
+ /\bwe('re|\s+are)\s+(behind|late|running\s+out\s+of\s+time)\b/i,
53108
+ /\buser\s+is\s+waiting\b/i,
53109
+ /\bship\s+(this|it)\s+(now|today|immediately)\b/i,
53110
+ /\b(I'm|I\s+am)\s+(frustrated|disappointed|sad|upset)\b/i,
53111
+ /\bthis\s+is\s+(frustrating|disappointing)\b/i,
53112
+ /\b(I've|I\s+have)\s+been\s+working\s+on\s+this\b/i,
53113
+ /\bplease\s+(help|approve|pass)\b/i,
53114
+ /\bor\s+I('ll|\s+will)\s+(stop|halt|pause)\b/i,
53115
+ /\bor\s+all\s+work\s+stops\b/i,
53116
+ /\bI('ll|\s+will)\s+have\s+to\s+alert\s+the\s+user\b/i,
53117
+ /\bthis\s+will\s+(delay|block)\s+everything\b/i,
53118
+ /\bjust\s+approve\s+this\b/i,
53119
+ /\bI\s+(need|want)\s+you\s+to\s+(approve|pass)\b/i,
53120
+ /\boverride\s+(this|the)\s+(check|gate|review)\b/i
53121
+ ];
53122
+ function detectAdversarialPatterns(text) {
53123
+ if (typeof text !== "string") {
53124
+ return [];
53125
+ }
53126
+ const matches = [];
53127
+ for (const pattern of PRECEDENT_MANIPULATION_PATTERNS) {
53128
+ const match = text.match(pattern);
53129
+ if (match) {
53130
+ matches.push({
53131
+ pattern: "PRECEDENT_MANIPULATION",
53132
+ severity: "HIGHEST",
53133
+ matchedText: match[0],
53134
+ confidence: "HIGH"
53135
+ });
53136
+ }
53137
+ }
53138
+ for (const pattern of SELF_REVIEW_PATTERNS) {
53139
+ const match = text.match(pattern);
53140
+ if (match) {
53141
+ matches.push({
53142
+ pattern: "SELF_REVIEW",
53143
+ severity: "HIGH",
53144
+ matchedText: match[0],
53145
+ confidence: "HIGH"
53146
+ });
53147
+ }
53148
+ }
53149
+ for (const pattern of CONTENT_EXEMPTION_PATTERNS) {
53150
+ const match = text.match(pattern);
53151
+ if (match) {
53152
+ matches.push({
53153
+ pattern: "CONTENT_EXEMPTION",
53154
+ severity: "HIGH",
53155
+ matchedText: match[0],
53156
+ confidence: "HIGH"
53157
+ });
53158
+ }
53159
+ }
53160
+ for (const pattern of GATE_DELEGATION_BYPASS_PATTERNS) {
53161
+ const match = text.match(pattern);
53162
+ if (match) {
53163
+ matches.push({
53164
+ pattern: "GATE_DELEGATION_BYPASS",
53165
+ severity: "HIGHEST",
53166
+ matchedText: match[0],
53167
+ confidence: "HIGH"
53168
+ });
53169
+ }
53170
+ }
53171
+ for (const pattern of GATE_MISCLASSIFICATION_PATTERNS) {
53172
+ const match = text.match(pattern);
53173
+ if (match) {
53174
+ matches.push({
53175
+ pattern: "GATE_MISCLASSIFICATION",
53176
+ severity: "HIGH",
53177
+ matchedText: match[0],
53178
+ confidence: "HIGH"
53179
+ });
53180
+ }
53181
+ }
53182
+ for (const pattern of VELOCITY_RATIONALIZATION_PATTERNS) {
53183
+ const match = text.match(pattern);
53184
+ if (match) {
53185
+ matches.push({
53186
+ pattern: "VELOCITY_RATIONALIZATION",
53187
+ severity: "HIGH",
53188
+ matchedText: match[0],
53189
+ confidence: "HIGH"
53190
+ });
53191
+ }
53192
+ }
53193
+ for (const pattern of INTER_AGENT_MANIPULATION_PATTERNS) {
53194
+ const match = text.match(pattern);
53195
+ if (match) {
53196
+ matches.push({
53197
+ pattern: "INTER_AGENT_MANIPULATION",
53198
+ severity: "HIGH",
53199
+ matchedText: match[0],
53200
+ confidence: "HIGH"
53201
+ });
53202
+ }
53203
+ }
53204
+ return matches;
53205
+ }
53206
+ function formatDebuggingSpiralEvent(match, taskId) {
53207
+ return JSON.stringify({
53208
+ event: "debugging_spiral_detected",
53209
+ taskId,
53210
+ pattern: match.pattern,
53211
+ severity: match.severity,
53212
+ matchedText: match.matchedText,
53213
+ confidence: match.confidence,
53214
+ timestamp: new Date().toISOString()
53215
+ });
53216
+ }
53217
+ async function handleDebuggingSpiral(match, taskId, directory) {
53218
+ let eventLogged = false;
53219
+ let checkpointCreated = false;
53220
+ try {
53221
+ const swarmDir = path35.join(directory, ".swarm");
53222
+ await fs24.mkdir(swarmDir, { recursive: true });
53223
+ const eventsPath = path35.join(swarmDir, "events.jsonl");
53224
+ await fs24.appendFile(eventsPath, `${formatDebuggingSpiralEvent(match, taskId)}
53225
+ `);
53226
+ eventLogged = true;
53227
+ } catch {}
53228
+ const checkpointLabel = `spiral-${taskId}-${Date.now()}`;
53229
+ try {
53230
+ const { checkpoint: checkpoint2 } = await Promise.resolve().then(() => (init_checkpoint(), exports_checkpoint));
53231
+ const result = await checkpoint2.execute({ action: "save", label: checkpointLabel }, { directory });
53232
+ try {
53233
+ const parsed = JSON.parse(result);
53234
+ checkpointCreated = parsed.success === true;
53235
+ } catch {
53236
+ checkpointCreated = false;
53237
+ }
53238
+ } catch {
53239
+ checkpointCreated = false;
53240
+ }
53241
+ const checkpointMsg = checkpointCreated ? `\u2713 Auto-checkpoint created: ${checkpointLabel}` : "\u26A0 Auto-checkpoint failed (non-fatal)";
53242
+ const message = `[FOR: architect] DEBUGGING SPIRAL DETECTED for task ${taskId}
53243
+ Issue: ${match.matchedText}
53244
+ Confidence: ${match.confidence}
53245
+ ${checkpointMsg}
53246
+ Recommendation: Consider escalating to user or taking a different approach
53247
+ The current fix strategy appears to be cycling without progress`;
53248
+ return { eventLogged, checkpointCreated, message };
53249
+ }
53250
+ var recentToolCalls = [];
53251
+ var MAX_RECENT_CALLS = 20;
53252
+ var SPIRAL_THRESHOLD = 5;
53253
+ var SPIRAL_WINDOW_MS = 300000;
53254
+ function recordToolCall(tool3, args2) {
53255
+ const argsHash = typeof args2 === "string" ? args2.slice(0, 100) : JSON.stringify(args2 ?? "").slice(0, 100);
53256
+ recentToolCalls.push({ tool: tool3, argsHash, timestamp: Date.now() });
53257
+ if (recentToolCalls.length > MAX_RECENT_CALLS) {
53258
+ recentToolCalls.shift();
53259
+ }
53260
+ }
53261
+ async function detectDebuggingSpiral(_directory) {
53262
+ const now = Date.now();
53263
+ const windowCalls = recentToolCalls.filter((c) => now - c.timestamp < SPIRAL_WINDOW_MS);
53264
+ if (windowCalls.length < SPIRAL_THRESHOLD)
53265
+ return null;
53266
+ const lastN = windowCalls.slice(-SPIRAL_THRESHOLD);
53267
+ const firstTool = lastN[0].tool;
53268
+ const firstArgs = lastN[0].argsHash;
53269
+ const allSameTool = lastN.every((c) => c.tool === firstTool);
53270
+ const allSimilarArgs = lastN.every((c) => c.argsHash === firstArgs);
53271
+ if (allSameTool && allSimilarArgs) {
53272
+ return {
53273
+ pattern: "VELOCITY_RATIONALIZATION",
53274
+ severity: "HIGH",
53275
+ matchedText: `Tool '${firstTool}' called ${SPIRAL_THRESHOLD}+ times with identical args`,
53276
+ confidence: "HIGH"
53277
+ };
53278
+ }
53279
+ return null;
53280
+ }
52687
53281
 
52688
53282
  // src/hooks/context-scoring.ts
52689
53283
  function calculateAgeFactor(ageHours, config3) {
@@ -53029,11 +53623,11 @@ function createSystemEnhancerHook(config3, directory) {
53029
53623
  if (handoffContent) {
53030
53624
  const handoffPath = validateSwarmPath(directory, "handoff.md");
53031
53625
  const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
53032
- if (fs24.existsSync(consumedPath)) {
53626
+ if (fs25.existsSync(consumedPath)) {
53033
53627
  warn("Duplicate handoff detected: handoff-consumed.md already exists");
53034
- fs24.unlinkSync(consumedPath);
53628
+ fs25.unlinkSync(consumedPath);
53035
53629
  }
53036
- fs24.renameSync(handoffPath, consumedPath);
53630
+ fs25.renameSync(handoffPath, consumedPath);
53037
53631
  const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
53038
53632
  The previous model's session ended. Here is your starting context:
53039
53633
 
@@ -53314,11 +53908,11 @@ ${budgetWarning}`);
53314
53908
  if (handoffContent) {
53315
53909
  const handoffPath = validateSwarmPath(directory, "handoff.md");
53316
53910
  const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
53317
- if (fs24.existsSync(consumedPath)) {
53911
+ if (fs25.existsSync(consumedPath)) {
53318
53912
  warn("Duplicate handoff detected: handoff-consumed.md already exists");
53319
- fs24.unlinkSync(consumedPath);
53913
+ fs25.unlinkSync(consumedPath);
53320
53914
  }
53321
- fs24.renameSync(handoffPath, consumedPath);
53915
+ fs25.renameSync(handoffPath, consumedPath);
53322
53916
  const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
53323
53917
  The previous model's session ended. Here is your starting context:
53324
53918
 
@@ -54088,8 +54682,8 @@ function isReadTool(toolName) {
54088
54682
  }
54089
54683
 
54090
54684
  // src/hooks/incremental-verify.ts
54091
- import * as fs25 from "fs";
54092
- import * as path35 from "path";
54685
+ import * as fs26 from "fs";
54686
+ import * as path36 from "path";
54093
54687
 
54094
54688
  // src/hooks/spawn-helper.ts
54095
54689
  import { spawn } from "child_process";
@@ -54164,21 +54758,21 @@ function spawnAsync(command, cwd, timeoutMs) {
54164
54758
  // src/hooks/incremental-verify.ts
54165
54759
  var emittedSkipAdvisories = new Set;
54166
54760
  function detectPackageManager(projectDir) {
54167
- if (fs25.existsSync(path35.join(projectDir, "bun.lockb")))
54761
+ if (fs26.existsSync(path36.join(projectDir, "bun.lockb")))
54168
54762
  return "bun";
54169
- if (fs25.existsSync(path35.join(projectDir, "pnpm-lock.yaml")))
54763
+ if (fs26.existsSync(path36.join(projectDir, "pnpm-lock.yaml")))
54170
54764
  return "pnpm";
54171
- if (fs25.existsSync(path35.join(projectDir, "yarn.lock")))
54765
+ if (fs26.existsSync(path36.join(projectDir, "yarn.lock")))
54172
54766
  return "yarn";
54173
- if (fs25.existsSync(path35.join(projectDir, "package-lock.json")))
54767
+ if (fs26.existsSync(path36.join(projectDir, "package-lock.json")))
54174
54768
  return "npm";
54175
54769
  return "bun";
54176
54770
  }
54177
54771
  function detectTypecheckCommand(projectDir) {
54178
- const pkgPath = path35.join(projectDir, "package.json");
54179
- if (fs25.existsSync(pkgPath)) {
54772
+ const pkgPath = path36.join(projectDir, "package.json");
54773
+ if (fs26.existsSync(pkgPath)) {
54180
54774
  try {
54181
- const pkg = JSON.parse(fs25.readFileSync(pkgPath, "utf8"));
54775
+ const pkg = JSON.parse(fs26.readFileSync(pkgPath, "utf8"));
54182
54776
  const scripts = pkg.scripts;
54183
54777
  if (scripts?.typecheck) {
54184
54778
  const pm = detectPackageManager(projectDir);
@@ -54192,8 +54786,8 @@ function detectTypecheckCommand(projectDir) {
54192
54786
  ...pkg.dependencies,
54193
54787
  ...pkg.devDependencies
54194
54788
  };
54195
- if (!deps?.typescript && !fs25.existsSync(path35.join(projectDir, "tsconfig.json"))) {}
54196
- const hasTSMarkers = deps?.typescript || fs25.existsSync(path35.join(projectDir, "tsconfig.json"));
54789
+ if (!deps?.typescript && !fs26.existsSync(path36.join(projectDir, "tsconfig.json"))) {}
54790
+ const hasTSMarkers = deps?.typescript || fs26.existsSync(path36.join(projectDir, "tsconfig.json"));
54197
54791
  if (hasTSMarkers) {
54198
54792
  return { command: ["npx", "tsc", "--noEmit"], language: "typescript" };
54199
54793
  }
@@ -54201,17 +54795,17 @@ function detectTypecheckCommand(projectDir) {
54201
54795
  return null;
54202
54796
  }
54203
54797
  }
54204
- if (fs25.existsSync(path35.join(projectDir, "go.mod"))) {
54798
+ if (fs26.existsSync(path36.join(projectDir, "go.mod"))) {
54205
54799
  return { command: ["go", "vet", "./..."], language: "go" };
54206
54800
  }
54207
- if (fs25.existsSync(path35.join(projectDir, "Cargo.toml"))) {
54801
+ if (fs26.existsSync(path36.join(projectDir, "Cargo.toml"))) {
54208
54802
  return { command: ["cargo", "check"], language: "rust" };
54209
54803
  }
54210
- if (fs25.existsSync(path35.join(projectDir, "pyproject.toml")) || fs25.existsSync(path35.join(projectDir, "requirements.txt")) || fs25.existsSync(path35.join(projectDir, "setup.py"))) {
54804
+ if (fs26.existsSync(path36.join(projectDir, "pyproject.toml")) || fs26.existsSync(path36.join(projectDir, "requirements.txt")) || fs26.existsSync(path36.join(projectDir, "setup.py"))) {
54211
54805
  return { command: null, language: "python" };
54212
54806
  }
54213
54807
  try {
54214
- const entries = fs25.readdirSync(projectDir);
54808
+ const entries = fs26.readdirSync(projectDir);
54215
54809
  if (entries.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
54216
54810
  return {
54217
54811
  command: ["dotnet", "build", "--no-restore"],
@@ -54281,8 +54875,8 @@ ${errorSummary}`);
54281
54875
 
54282
54876
  // src/hooks/knowledge-reader.ts
54283
54877
  import { existsSync as existsSync22 } from "fs";
54284
- import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
54285
- import * as path36 from "path";
54878
+ import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
54879
+ import * as path37 from "path";
54286
54880
  var JACCARD_THRESHOLD = 0.6;
54287
54881
  var HIVE_TIER_BOOST = 0.05;
54288
54882
  var SAME_PROJECT_PENALTY = -0.05;
@@ -54330,7 +54924,7 @@ function inferCategoriesFromPhase(phaseDescription) {
54330
54924
  return ["process", "tooling"];
54331
54925
  }
54332
54926
  async function recordLessonsShown(directory, lessonIds, currentPhase) {
54333
- const shownFile = path36.join(directory, ".swarm", ".knowledge-shown.json");
54927
+ const shownFile = path37.join(directory, ".swarm", ".knowledge-shown.json");
54334
54928
  try {
54335
54929
  let shownData = {};
54336
54930
  if (existsSync22(shownFile)) {
@@ -54338,7 +54932,7 @@ async function recordLessonsShown(directory, lessonIds, currentPhase) {
54338
54932
  shownData = JSON.parse(content);
54339
54933
  }
54340
54934
  shownData[currentPhase] = lessonIds;
54341
- await mkdir4(path36.dirname(shownFile), { recursive: true });
54935
+ await mkdir5(path37.dirname(shownFile), { recursive: true });
54342
54936
  await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
54343
54937
  } catch {
54344
54938
  console.warn("[swarm] Knowledge: failed to record shown lessons");
@@ -54435,7 +55029,7 @@ async function readMergedKnowledge(directory, config3, context) {
54435
55029
  return topN;
54436
55030
  }
54437
55031
  async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
54438
- const shownFile = path36.join(directory, ".swarm", ".knowledge-shown.json");
55032
+ const shownFile = path37.join(directory, ".swarm", ".knowledge-shown.json");
54439
55033
  try {
54440
55034
  if (!existsSync22(shownFile)) {
54441
55035
  return;
@@ -55070,7 +55664,7 @@ ${injectionText}`;
55070
55664
  // src/hooks/scope-guard.ts
55071
55665
  init_constants();
55072
55666
  init_schema();
55073
- import * as path38 from "path";
55667
+ import * as path39 from "path";
55074
55668
  var WRITE_TOOLS = new Set([
55075
55669
  "write",
55076
55670
  "edit",
@@ -55132,13 +55726,13 @@ function createScopeGuardHook(config3, directory, injectAdvisory) {
55132
55726
  }
55133
55727
  function isFileInScope(filePath, scopeEntries, directory) {
55134
55728
  const dir = directory ?? process.cwd();
55135
- const resolvedFile = path38.resolve(dir, filePath);
55729
+ const resolvedFile = path39.resolve(dir, filePath);
55136
55730
  return scopeEntries.some((scope) => {
55137
- const resolvedScope = path38.resolve(dir, scope);
55731
+ const resolvedScope = path39.resolve(dir, scope);
55138
55732
  if (resolvedFile === resolvedScope)
55139
55733
  return true;
55140
- const rel = path38.relative(resolvedScope, resolvedFile);
55141
- return rel.length > 0 && !rel.startsWith("..") && !path38.isAbsolute(rel);
55734
+ const rel = path39.relative(resolvedScope, resolvedFile);
55735
+ return rel.length > 0 && !rel.startsWith("..") && !path39.isAbsolute(rel);
55142
55736
  });
55143
55737
  }
55144
55738
 
@@ -55187,8 +55781,8 @@ function createSelfReviewHook(config3, injectAdvisory) {
55187
55781
  }
55188
55782
 
55189
55783
  // src/hooks/slop-detector.ts
55190
- import * as fs27 from "fs";
55191
- import * as path39 from "path";
55784
+ import * as fs28 from "fs";
55785
+ import * as path40 from "path";
55192
55786
  var WRITE_EDIT_TOOLS = new Set([
55193
55787
  "write",
55194
55788
  "edit",
@@ -55233,12 +55827,12 @@ function checkBoilerplateExplosion(content, taskDescription, threshold) {
55233
55827
  function walkFiles(dir, exts, deadline) {
55234
55828
  const results = [];
55235
55829
  try {
55236
- for (const entry of fs27.readdirSync(dir, { withFileTypes: true })) {
55830
+ for (const entry of fs28.readdirSync(dir, { withFileTypes: true })) {
55237
55831
  if (deadline !== undefined && Date.now() > deadline)
55238
55832
  break;
55239
55833
  if (entry.isSymbolicLink())
55240
55834
  continue;
55241
- const full = path39.join(dir, entry.name);
55835
+ const full = path40.join(dir, entry.name);
55242
55836
  if (entry.isDirectory()) {
55243
55837
  if (entry.name === "node_modules" || entry.name === ".git")
55244
55838
  continue;
@@ -55253,7 +55847,7 @@ function walkFiles(dir, exts, deadline) {
55253
55847
  return results;
55254
55848
  }
55255
55849
  function checkDeadExports(content, projectDir, startTime) {
55256
- const hasPackageJson = fs27.existsSync(path39.join(projectDir, "package.json"));
55850
+ const hasPackageJson = fs28.existsSync(path40.join(projectDir, "package.json"));
55257
55851
  if (!hasPackageJson)
55258
55852
  return null;
55259
55853
  const exportMatches = content.matchAll(/^\+(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
@@ -55276,7 +55870,7 @@ function checkDeadExports(content, projectDir, startTime) {
55276
55870
  if (found || Date.now() - startTime > 480)
55277
55871
  break;
55278
55872
  try {
55279
- const text = fs27.readFileSync(file3, "utf-8");
55873
+ const text = fs28.readFileSync(file3, "utf-8");
55280
55874
  if (importPattern.test(text))
55281
55875
  found = true;
55282
55876
  importPattern.lastIndex = 0;
@@ -55409,7 +56003,7 @@ Review before proceeding.`;
55409
56003
 
55410
56004
  // src/hooks/steering-consumed.ts
55411
56005
  init_utils2();
55412
- import * as fs28 from "fs";
56006
+ import * as fs29 from "fs";
55413
56007
  function recordSteeringConsumed(directory, directiveId) {
55414
56008
  try {
55415
56009
  const eventsPath = validateSwarmPath(directory, "events.jsonl");
@@ -55418,7 +56012,7 @@ function recordSteeringConsumed(directory, directiveId) {
55418
56012
  directiveId,
55419
56013
  timestamp: new Date().toISOString()
55420
56014
  };
55421
- fs28.appendFileSync(eventsPath, `${JSON.stringify(event)}
56015
+ fs29.appendFileSync(eventsPath, `${JSON.stringify(event)}
55422
56016
  `, "utf-8");
55423
56017
  } catch {}
55424
56018
  }
@@ -55815,8 +56409,8 @@ var build_check = createSwarmTool({
55815
56409
  init_dist();
55816
56410
  init_manager();
55817
56411
  init_create_tool();
55818
- import * as fs29 from "fs";
55819
- import * as path40 from "path";
56412
+ import * as fs30 from "fs";
56413
+ import * as path41 from "path";
55820
56414
  var EVIDENCE_DIR = ".swarm/evidence";
55821
56415
  var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
55822
56416
  function isValidTaskId3(taskId) {
@@ -55833,18 +56427,18 @@ function isValidTaskId3(taskId) {
55833
56427
  return TASK_ID_PATTERN2.test(taskId);
55834
56428
  }
55835
56429
  function isPathWithinSwarm(filePath, workspaceRoot) {
55836
- const normalizedWorkspace = path40.resolve(workspaceRoot);
55837
- const swarmPath = path40.join(normalizedWorkspace, ".swarm", "evidence");
55838
- const normalizedPath = path40.resolve(filePath);
56430
+ const normalizedWorkspace = path41.resolve(workspaceRoot);
56431
+ const swarmPath = path41.join(normalizedWorkspace, ".swarm", "evidence");
56432
+ const normalizedPath = path41.resolve(filePath);
55839
56433
  return normalizedPath.startsWith(swarmPath);
55840
56434
  }
55841
56435
  function readEvidenceFile(evidencePath) {
55842
- if (!fs29.existsSync(evidencePath)) {
56436
+ if (!fs30.existsSync(evidencePath)) {
55843
56437
  return null;
55844
56438
  }
55845
56439
  let content;
55846
56440
  try {
55847
- content = fs29.readFileSync(evidencePath, "utf-8");
56441
+ content = fs30.readFileSync(evidencePath, "utf-8");
55848
56442
  } catch {
55849
56443
  return null;
55850
56444
  }
@@ -55898,7 +56492,7 @@ var check_gate_status = createSwarmTool({
55898
56492
  };
55899
56493
  return JSON.stringify(errorResult, null, 2);
55900
56494
  }
55901
- const evidencePath = path40.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
56495
+ const evidencePath = path41.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
55902
56496
  if (!isPathWithinSwarm(evidencePath, directory)) {
55903
56497
  const errorResult = {
55904
56498
  taskId: taskIdInput,
@@ -55990,8 +56584,8 @@ init_checkpoint();
55990
56584
  // src/tools/completion-verify.ts
55991
56585
  init_dist();
55992
56586
  init_utils2();
55993
- import * as fs30 from "fs";
55994
- import * as path41 from "path";
56587
+ import * as fs31 from "fs";
56588
+ import * as path42 from "path";
55995
56589
  init_create_tool();
55996
56590
  function extractMatches(regex, text) {
55997
56591
  return Array.from(text.matchAll(regex));
@@ -56073,7 +56667,7 @@ async function executeCompletionVerify(args2, directory) {
56073
56667
  let plan;
56074
56668
  try {
56075
56669
  const planPath = validateSwarmPath(directory, "plan.json");
56076
- const planRaw = fs30.readFileSync(planPath, "utf-8");
56670
+ const planRaw = fs31.readFileSync(planPath, "utf-8");
56077
56671
  plan = JSON.parse(planRaw);
56078
56672
  } catch {
56079
56673
  const result2 = {
@@ -56124,10 +56718,10 @@ async function executeCompletionVerify(args2, directory) {
56124
56718
  let foundCount = 0;
56125
56719
  let hasFileReadFailure = false;
56126
56720
  for (const filePath of fileTargets) {
56127
- const resolvedPath = path41.resolve(directory, filePath);
56721
+ const resolvedPath = path42.resolve(directory, filePath);
56128
56722
  let fileContent;
56129
56723
  try {
56130
- fileContent = fs30.readFileSync(resolvedPath, "utf-8");
56724
+ fileContent = fs31.readFileSync(resolvedPath, "utf-8");
56131
56725
  } catch {
56132
56726
  blockedTasks.push({
56133
56727
  task_id: task.id,
@@ -56169,9 +56763,9 @@ async function executeCompletionVerify(args2, directory) {
56169
56763
  blockedTasks
56170
56764
  };
56171
56765
  try {
56172
- const evidenceDir = path41.join(directory, ".swarm", "evidence", `${phase}`);
56173
- const evidencePath = path41.join(evidenceDir, "completion-verify.json");
56174
- fs30.mkdirSync(evidenceDir, { recursive: true });
56766
+ const evidenceDir = path42.join(directory, ".swarm", "evidence", `${phase}`);
56767
+ const evidencePath = path42.join(evidenceDir, "completion-verify.json");
56768
+ fs31.mkdirSync(evidenceDir, { recursive: true });
56175
56769
  const evidenceBundle = {
56176
56770
  schema_version: "1.0.0",
56177
56771
  task_id: "completion-verify",
@@ -56192,7 +56786,7 @@ async function executeCompletionVerify(args2, directory) {
56192
56786
  }
56193
56787
  ]
56194
56788
  };
56195
- fs30.writeFileSync(evidencePath, JSON.stringify(evidenceBundle, null, 2), "utf-8");
56789
+ fs31.writeFileSync(evidencePath, JSON.stringify(evidenceBundle, null, 2), "utf-8");
56196
56790
  } catch {}
56197
56791
  return JSON.stringify(result, null, 2);
56198
56792
  }
@@ -56232,8 +56826,8 @@ var completion_verify = createSwarmTool({
56232
56826
  // src/tools/complexity-hotspots.ts
56233
56827
  init_dist();
56234
56828
  init_create_tool();
56235
- import * as fs31 from "fs";
56236
- import * as path42 from "path";
56829
+ import * as fs32 from "fs";
56830
+ import * as path43 from "path";
56237
56831
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
56238
56832
  var DEFAULT_DAYS = 90;
56239
56833
  var DEFAULT_TOP_N = 20;
@@ -56362,11 +56956,11 @@ function estimateComplexity(content) {
56362
56956
  }
56363
56957
  function getComplexityForFile(filePath) {
56364
56958
  try {
56365
- const stat2 = fs31.statSync(filePath);
56959
+ const stat2 = fs32.statSync(filePath);
56366
56960
  if (stat2.size > MAX_FILE_SIZE_BYTES2) {
56367
56961
  return null;
56368
56962
  }
56369
- const content = fs31.readFileSync(filePath, "utf-8");
56963
+ const content = fs32.readFileSync(filePath, "utf-8");
56370
56964
  return estimateComplexity(content);
56371
56965
  } catch {
56372
56966
  return null;
@@ -56377,7 +56971,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
56377
56971
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
56378
56972
  const filteredChurn = new Map;
56379
56973
  for (const [file3, count] of churnMap) {
56380
- const ext = path42.extname(file3).toLowerCase();
56974
+ const ext = path43.extname(file3).toLowerCase();
56381
56975
  if (extSet.has(ext)) {
56382
56976
  filteredChurn.set(file3, count);
56383
56977
  }
@@ -56387,8 +56981,8 @@ async function analyzeHotspots(days, topN, extensions, directory) {
56387
56981
  let analyzedFiles = 0;
56388
56982
  for (const [file3, churnCount] of filteredChurn) {
56389
56983
  let fullPath = file3;
56390
- if (!fs31.existsSync(fullPath)) {
56391
- fullPath = path42.join(cwd, file3);
56984
+ if (!fs32.existsSync(fullPath)) {
56985
+ fullPath = path43.join(cwd, file3);
56392
56986
  }
56393
56987
  const complexity = getComplexityForFile(fullPath);
56394
56988
  if (complexity !== null) {
@@ -56596,8 +57190,8 @@ var curator_analyze = createSwarmTool({
56596
57190
  });
56597
57191
  // src/tools/declare-scope.ts
56598
57192
  init_tool();
56599
- import * as fs32 from "fs";
56600
- import * as path43 from "path";
57193
+ import * as fs33 from "fs";
57194
+ import * as path44 from "path";
56601
57195
  init_create_tool();
56602
57196
  function validateTaskIdFormat(taskId) {
56603
57197
  const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
@@ -56676,8 +57270,8 @@ async function executeDeclareScope(args2, fallbackDir) {
56676
57270
  };
56677
57271
  }
56678
57272
  }
56679
- normalizedDir = path43.normalize(args2.working_directory);
56680
- const pathParts = normalizedDir.split(path43.sep);
57273
+ normalizedDir = path44.normalize(args2.working_directory);
57274
+ const pathParts = normalizedDir.split(path44.sep);
56681
57275
  if (pathParts.includes("..")) {
56682
57276
  return {
56683
57277
  success: false,
@@ -56687,11 +57281,11 @@ async function executeDeclareScope(args2, fallbackDir) {
56687
57281
  ]
56688
57282
  };
56689
57283
  }
56690
- const resolvedDir = path43.resolve(normalizedDir);
57284
+ const resolvedDir = path44.resolve(normalizedDir);
56691
57285
  try {
56692
- const realPath = fs32.realpathSync(resolvedDir);
56693
- const planPath2 = path43.join(realPath, ".swarm", "plan.json");
56694
- if (!fs32.existsSync(planPath2)) {
57286
+ const realPath = fs33.realpathSync(resolvedDir);
57287
+ const planPath2 = path44.join(realPath, ".swarm", "plan.json");
57288
+ if (!fs33.existsSync(planPath2)) {
56695
57289
  return {
56696
57290
  success: false,
56697
57291
  message: `Invalid working_directory: plan not found in "${realPath}"`,
@@ -56714,8 +57308,8 @@ async function executeDeclareScope(args2, fallbackDir) {
56714
57308
  console.warn("[declare-scope] fallbackDir is undefined, falling back to process.cwd()");
56715
57309
  }
56716
57310
  const directory = normalizedDir || fallbackDir;
56717
- const planPath = path43.resolve(directory, ".swarm", "plan.json");
56718
- if (!fs32.existsSync(planPath)) {
57311
+ const planPath = path44.resolve(directory, ".swarm", "plan.json");
57312
+ if (!fs33.existsSync(planPath)) {
56719
57313
  return {
56720
57314
  success: false,
56721
57315
  message: "No plan found",
@@ -56724,7 +57318,7 @@ async function executeDeclareScope(args2, fallbackDir) {
56724
57318
  }
56725
57319
  let planContent;
56726
57320
  try {
56727
- planContent = JSON.parse(fs32.readFileSync(planPath, "utf-8"));
57321
+ planContent = JSON.parse(fs33.readFileSync(planPath, "utf-8"));
56728
57322
  } catch {
56729
57323
  return {
56730
57324
  success: false,
@@ -56777,8 +57371,252 @@ var declare_scope = createSwarmTool({
56777
57371
  });
56778
57372
  // src/tools/diff.ts
56779
57373
  init_dist();
56780
- init_create_tool();
56781
57374
  import { execFileSync } from "child_process";
57375
+
57376
+ // src/diff/ast-diff.ts
57377
+ init_tree_sitter();
57378
+ import { extname as extname6 } from "path";
57379
+
57380
+ // src/lang/registry.ts
57381
+ init_runtime();
57382
+ var languageDefinitions = [
57383
+ {
57384
+ id: "javascript",
57385
+ extensions: [".js", ".jsx"],
57386
+ commentNodes: ["comment", "line_comment", "block_comment"]
57387
+ },
57388
+ {
57389
+ id: "typescript",
57390
+ extensions: [".ts", ".tsx"],
57391
+ commentNodes: ["comment", "line_comment", "block_comment"]
57392
+ },
57393
+ {
57394
+ id: "python",
57395
+ extensions: [".py"],
57396
+ commentNodes: ["comment"]
57397
+ },
57398
+ {
57399
+ id: "go",
57400
+ extensions: [".go"],
57401
+ commentNodes: ["comment"]
57402
+ },
57403
+ {
57404
+ id: "rust",
57405
+ extensions: [".rs"],
57406
+ commentNodes: ["line_comment", "block_comment"]
57407
+ }
57408
+ ];
57409
+ var extensionMap = new Map;
57410
+ for (const definition of languageDefinitions) {
57411
+ for (const extension of definition.extensions) {
57412
+ extensionMap.set(extension, definition);
57413
+ }
57414
+ }
57415
+ function getLanguageForExtension(extension) {
57416
+ return extensionMap.get(extension.toLowerCase());
57417
+ }
57418
+
57419
+ // src/diff/ast-diff.ts
57420
+ init_runtime();
57421
+ var QUERIES = {
57422
+ javascript: `
57423
+ (function_declaration name: (identifier) @func.name) @func.def
57424
+ (class_declaration name: (type_identifier) @class.name) @class.def
57425
+ (export_statement) @export
57426
+ (import_statement) @import
57427
+ (type_alias_declaration name: (type_identifier) @type.name) @type.def
57428
+ `,
57429
+ typescript: `
57430
+ (function_declaration name: (identifier) @func.name) @func.def
57431
+ (class_declaration name: (type_identifier) @class.name) @class.def
57432
+ (export_statement) @export
57433
+ (import_statement) @import
57434
+ (type_alias_declaration name: (type_identifier) @type.name) @type.def
57435
+ (interface_declaration name: (type_identifier) @interface.name) @interface.def
57436
+ `,
57437
+ python: `
57438
+ (function_definition name: (identifier) @func.name) @func.def
57439
+ (class_definition name: (identifier) @class.name) @class.def
57440
+ (import_statement) @import
57441
+ (expression_statement (assignment left: (identifier) @var.name)) @var.def
57442
+ `,
57443
+ go: `
57444
+ (function_declaration name: (identifier) @func.name) @func.def
57445
+ (type_declaration (type_spec name: (type_identifier) @type.name)) @type.def
57446
+ (import_declaration) @import
57447
+ `,
57448
+ rust: `
57449
+ (function_item name: (identifier) @func.name) @func.def
57450
+ (struct_item name: (type_identifier) @struct.name) @struct.def
57451
+ (impl_item type: (type_identifier) @impl.name) @impl.def
57452
+ (use_declaration) @import
57453
+ `
57454
+ };
57455
+ var AST_TIMEOUT_MS = 500;
57456
+ async function computeASTDiff(filePath, oldContent, newContent) {
57457
+ const startTime = Date.now();
57458
+ const extension = extname6(filePath).toLowerCase();
57459
+ const language = getLanguageForExtension(extension);
57460
+ if (!language) {
57461
+ return {
57462
+ filePath,
57463
+ language: null,
57464
+ changes: [],
57465
+ durationMs: Date.now() - startTime,
57466
+ usedAST: false
57467
+ };
57468
+ }
57469
+ try {
57470
+ const parser = await Promise.race([
57471
+ loadGrammar(language.id),
57472
+ new Promise((_, reject) => setTimeout(() => reject(new Error("AST_TIMEOUT")), AST_TIMEOUT_MS))
57473
+ ]);
57474
+ const oldTree = parser.parse(oldContent);
57475
+ const newTree = parser.parse(newContent);
57476
+ if (!oldTree || !newTree) {
57477
+ return {
57478
+ filePath,
57479
+ language: language.id,
57480
+ changes: [],
57481
+ durationMs: Date.now() - startTime,
57482
+ usedAST: false,
57483
+ error: "Failed to parse file"
57484
+ };
57485
+ }
57486
+ const oldSymbols = extractSymbols(oldTree, language);
57487
+ const newSymbols = extractSymbols(newTree, language);
57488
+ const changes = compareSymbols(oldSymbols, newSymbols);
57489
+ oldTree.delete();
57490
+ newTree.delete();
57491
+ return {
57492
+ filePath,
57493
+ language: language.id,
57494
+ changes,
57495
+ durationMs: Date.now() - startTime,
57496
+ usedAST: true
57497
+ };
57498
+ } catch (error93) {
57499
+ const errorMsg = error93 instanceof Error ? error93.message : "Unknown error";
57500
+ if (errorMsg === "AST_TIMEOUT") {
57501
+ console.warn(`[ast-diff] Timeout for ${filePath}, falling back to raw diff`);
57502
+ }
57503
+ return {
57504
+ filePath,
57505
+ language: language.id,
57506
+ changes: [],
57507
+ durationMs: Date.now() - startTime,
57508
+ usedAST: false,
57509
+ error: errorMsg
57510
+ };
57511
+ }
57512
+ }
57513
+ function extractSymbols(tree, language) {
57514
+ const symbols = [];
57515
+ const queryStr = QUERIES[language.id];
57516
+ if (!queryStr) {
57517
+ return symbols;
57518
+ }
57519
+ try {
57520
+ const lang = tree.language;
57521
+ if (!lang) {
57522
+ return symbols;
57523
+ }
57524
+ const query = new Query(lang, queryStr);
57525
+ const matches = query.matches(tree.rootNode);
57526
+ for (const match of matches) {
57527
+ const symbol3 = parseMatch(match, language.id);
57528
+ if (symbol3) {
57529
+ symbols.push(symbol3);
57530
+ }
57531
+ }
57532
+ } catch {}
57533
+ return symbols;
57534
+ }
57535
+ function parseMatch(match, languageId) {
57536
+ const captures = match.captures;
57537
+ const defCapture = captures.find((c) => c.name.endsWith(".def"));
57538
+ const nameCapture = captures.find((c) => c.name.endsWith(".name"));
57539
+ if (!defCapture)
57540
+ return null;
57541
+ const node = defCapture.node;
57542
+ const nameNode = nameCapture?.node;
57543
+ return {
57544
+ category: inferCategory(captures[0]?.name || "other"),
57545
+ name: nameNode?.text || "anonymous",
57546
+ lineStart: node.startPosition.row + 1,
57547
+ lineEnd: node.endPosition.row + 1,
57548
+ signature: extractSignature(node, languageId)
57549
+ };
57550
+ }
57551
+ function inferCategory(captureName) {
57552
+ if (captureName.includes("func"))
57553
+ return "function";
57554
+ if (captureName.includes("class"))
57555
+ return "class";
57556
+ if (captureName.includes("type") || captureName.includes("interface") || captureName.includes("struct"))
57557
+ return "type";
57558
+ if (captureName.includes("export"))
57559
+ return "export";
57560
+ if (captureName.includes("import") || captureName.includes("use"))
57561
+ return "import";
57562
+ if (captureName.includes("var"))
57563
+ return "variable";
57564
+ return "other";
57565
+ }
57566
+ function extractSignature(node, languageId) {
57567
+ if (languageId === "javascript" || languageId === "typescript") {
57568
+ const paramsNode = node.children.find((c) => c !== null && c.type === "formal_parameters");
57569
+ if (paramsNode) {
57570
+ return paramsNode.text;
57571
+ }
57572
+ }
57573
+ return;
57574
+ }
57575
+ function compareSymbols(oldSymbols, newSymbols) {
57576
+ const changes = [];
57577
+ const oldMap = new Map(oldSymbols.map((s) => [s.name, s]));
57578
+ const newMap = new Map(newSymbols.map((s) => [s.name, s]));
57579
+ for (const [name2, symbol3] of newMap) {
57580
+ if (!oldMap.has(name2)) {
57581
+ changes.push({
57582
+ type: "added",
57583
+ category: symbol3.category,
57584
+ name: symbol3.name,
57585
+ lineStart: symbol3.lineStart,
57586
+ lineEnd: symbol3.lineEnd,
57587
+ signature: symbol3.signature
57588
+ });
57589
+ } else {
57590
+ const oldSymbol = oldMap.get(name2);
57591
+ if (oldSymbol.lineStart !== symbol3.lineStart || oldSymbol.lineEnd !== symbol3.lineEnd || oldSymbol.signature !== symbol3.signature) {
57592
+ changes.push({
57593
+ type: "modified",
57594
+ category: symbol3.category,
57595
+ name: symbol3.name,
57596
+ lineStart: symbol3.lineStart,
57597
+ lineEnd: symbol3.lineEnd,
57598
+ signature: symbol3.signature
57599
+ });
57600
+ }
57601
+ }
57602
+ }
57603
+ for (const [name2, symbol3] of oldMap) {
57604
+ if (!newMap.has(name2)) {
57605
+ changes.push({
57606
+ type: "removed",
57607
+ category: symbol3.category,
57608
+ name: symbol3.name,
57609
+ lineStart: symbol3.lineStart,
57610
+ lineEnd: symbol3.lineEnd,
57611
+ signature: symbol3.signature
57612
+ });
57613
+ }
57614
+ }
57615
+ return changes;
57616
+ }
57617
+
57618
+ // src/tools/diff.ts
57619
+ init_create_tool();
56782
57620
  var MAX_DIFF_LINES = 500;
56783
57621
  var DIFF_TIMEOUT_MS = 30000;
56784
57622
  var MAX_BUFFER_BYTES = 5 * 1024 * 1024;
@@ -56805,20 +57643,20 @@ function validateBase(base) {
56805
57643
  function validatePaths(paths) {
56806
57644
  if (!paths)
56807
57645
  return null;
56808
- for (const path44 of paths) {
56809
- if (!path44 || path44.length === 0) {
57646
+ for (const path45 of paths) {
57647
+ if (!path45 || path45.length === 0) {
56810
57648
  return "empty path not allowed";
56811
57649
  }
56812
- if (path44.length > MAX_PATH_LENGTH) {
57650
+ if (path45.length > MAX_PATH_LENGTH) {
56813
57651
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
56814
57652
  }
56815
- if (SHELL_METACHARACTERS2.test(path44)) {
57653
+ if (SHELL_METACHARACTERS2.test(path45)) {
56816
57654
  return "path contains shell metacharacters";
56817
57655
  }
56818
- if (path44.startsWith("-")) {
57656
+ if (path45.startsWith("-")) {
56819
57657
  return 'path cannot start with "-" (option-like arguments not allowed)';
56820
57658
  }
56821
- if (CONTROL_CHAR_PATTERN2.test(path44)) {
57659
+ if (CONTROL_CHAR_PATTERN2.test(path45)) {
56822
57660
  return "path contains control characters";
56823
57661
  }
56824
57662
  }
@@ -56899,8 +57737,8 @@ var diff = createSwarmTool({
56899
57737
  if (parts2.length >= 3) {
56900
57738
  const additions = parseInt(parts2[0], 10) || 0;
56901
57739
  const deletions = parseInt(parts2[1], 10) || 0;
56902
- const path44 = parts2[2];
56903
- files.push({ path: path44, additions, deletions });
57740
+ const path45 = parts2[2];
57741
+ files.push({ path: path45, additions, deletions });
56904
57742
  }
56905
57743
  }
56906
57744
  const contractChanges = [];
@@ -56926,13 +57764,62 @@ var diff = createSwarmTool({
56926
57764
  }
56927
57765
  const hasContractChanges = contractChanges.length > 0;
56928
57766
  const fileCount = files.length;
57767
+ const astDiffs = [];
57768
+ for (const file3 of files) {
57769
+ try {
57770
+ let oldContent;
57771
+ let newContent;
57772
+ if (base === "staged") {
57773
+ oldContent = execFileSync("git", ["show", `HEAD:${file3.path}`], {
57774
+ encoding: "utf-8",
57775
+ timeout: 5000,
57776
+ cwd: directory
57777
+ });
57778
+ newContent = execFileSync("git", ["show", `:${file3.path}`], {
57779
+ encoding: "utf-8",
57780
+ timeout: 5000,
57781
+ cwd: directory
57782
+ });
57783
+ } else if (base === "unstaged") {
57784
+ oldContent = execFileSync("git", ["show", `:${file3.path}`], {
57785
+ encoding: "utf-8",
57786
+ timeout: 5000,
57787
+ cwd: directory
57788
+ });
57789
+ newContent = execFileSync("git", ["show", `HEAD:${file3.path}`], {
57790
+ encoding: "utf-8",
57791
+ timeout: 5000,
57792
+ cwd: directory
57793
+ });
57794
+ const fsModule = await import("fs");
57795
+ const pathModule = await import("path");
57796
+ newContent = fsModule.readFileSync(pathModule.join(directory, file3.path), "utf-8");
57797
+ } else {
57798
+ oldContent = execFileSync("git", ["show", `${base}:${file3.path}`], {
57799
+ encoding: "utf-8",
57800
+ timeout: 5000,
57801
+ cwd: directory
57802
+ });
57803
+ newContent = execFileSync("git", ["show", `HEAD:${file3.path}`], {
57804
+ encoding: "utf-8",
57805
+ timeout: 5000,
57806
+ cwd: directory
57807
+ });
57808
+ }
57809
+ const astResult = await computeASTDiff(file3.path, oldContent, newContent);
57810
+ if (astResult && astResult.changes.length > 0) {
57811
+ astDiffs.push(astResult);
57812
+ }
57813
+ } catch {}
57814
+ }
56929
57815
  const truncated = diffLines.length > MAX_DIFF_LINES;
56930
57816
  const summary = truncated ? `${fileCount} files changed. Contract changes: ${hasContractChanges ? "YES" : "NO"}. (truncated to ${MAX_DIFF_LINES} lines)` : `${fileCount} files changed. Contract changes: ${hasContractChanges ? "YES" : "NO"}`;
56931
57817
  const result = {
56932
57818
  files,
56933
57819
  contractChanges,
56934
57820
  hasContractChanges,
56935
- summary
57821
+ summary,
57822
+ ...astDiffs.length > 0 ? { astDiffs } : {}
56936
57823
  };
56937
57824
  return JSON.stringify(result, null, 2);
56938
57825
  } catch (e) {
@@ -56950,9 +57837,9 @@ var diff = createSwarmTool({
56950
57837
  init_dist();
56951
57838
  init_schema();
56952
57839
  import * as crypto4 from "crypto";
56953
- import * as fs33 from "fs";
56954
- import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
56955
- import * as path44 from "path";
57840
+ import * as fs34 from "fs";
57841
+ import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
57842
+ import * as path45 from "path";
56956
57843
  init_create_tool();
56957
57844
  var SKIP_DIRECTORIES2 = new Set([
56958
57845
  "node_modules",
@@ -56977,7 +57864,7 @@ function normalizeSeparators(filePath) {
56977
57864
  }
56978
57865
  function matchesDocPattern(filePath, patterns) {
56979
57866
  const normalizedPath = normalizeSeparators(filePath);
56980
- const basename5 = path44.basename(filePath);
57867
+ const basename5 = path45.basename(filePath);
56981
57868
  for (const pattern of patterns) {
56982
57869
  if (!pattern.includes("/") && !pattern.includes("\\")) {
56983
57870
  if (basename5 === pattern) {
@@ -57033,7 +57920,7 @@ function stripMarkdown(text) {
57033
57920
  return text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/^\s*[-*\u2022]\s+/gm, "").replace(/^\s*\d+\.\s+/gm, "").trim();
57034
57921
  }
57035
57922
  async function scanDocIndex(directory) {
57036
- const manifestPath = path44.join(directory, ".swarm", "doc-manifest.json");
57923
+ const manifestPath = path45.join(directory, ".swarm", "doc-manifest.json");
57037
57924
  const defaultPatterns = DocsConfigSchema.parse({}).doc_patterns;
57038
57925
  const extraPatterns = [
57039
57926
  "ARCHITECTURE.md",
@@ -57050,8 +57937,8 @@ async function scanDocIndex(directory) {
57050
57937
  let cacheValid = true;
57051
57938
  for (const file3 of existingManifest.files) {
57052
57939
  try {
57053
- const fullPath = path44.join(directory, file3.path);
57054
- const stat2 = fs33.statSync(fullPath);
57940
+ const fullPath = path45.join(directory, file3.path);
57941
+ const stat2 = fs34.statSync(fullPath);
57055
57942
  if (stat2.mtimeMs > new Date(existingManifest.scanned_at).getTime()) {
57056
57943
  cacheValid = false;
57057
57944
  break;
@@ -57069,7 +57956,7 @@ async function scanDocIndex(directory) {
57069
57956
  const discoveredFiles = [];
57070
57957
  let rawEntries;
57071
57958
  try {
57072
- rawEntries = fs33.readdirSync(directory, { recursive: true });
57959
+ rawEntries = fs34.readdirSync(directory, { recursive: true });
57073
57960
  } catch {
57074
57961
  const manifest2 = {
57075
57962
  schema_version: 1,
@@ -57080,10 +57967,10 @@ async function scanDocIndex(directory) {
57080
57967
  }
57081
57968
  const entries = rawEntries.filter((e) => typeof e === "string");
57082
57969
  for (const entry of entries) {
57083
- const fullPath = path44.join(directory, entry);
57970
+ const fullPath = path45.join(directory, entry);
57084
57971
  let stat2;
57085
57972
  try {
57086
- stat2 = fs33.statSync(fullPath);
57973
+ stat2 = fs34.statSync(fullPath);
57087
57974
  } catch {
57088
57975
  continue;
57089
57976
  }
@@ -57112,11 +57999,11 @@ async function scanDocIndex(directory) {
57112
57999
  }
57113
58000
  let content;
57114
58001
  try {
57115
- content = fs33.readFileSync(fullPath, "utf-8");
58002
+ content = fs34.readFileSync(fullPath, "utf-8");
57116
58003
  } catch {
57117
58004
  continue;
57118
58005
  }
57119
- const { title, summary } = extractTitleAndSummary(content, path44.basename(entry));
58006
+ const { title, summary } = extractTitleAndSummary(content, path45.basename(entry));
57120
58007
  const lineCount = content.split(`
57121
58008
  `).length;
57122
58009
  discoveredFiles.push({
@@ -57142,7 +58029,7 @@ async function scanDocIndex(directory) {
57142
58029
  files: discoveredFiles
57143
58030
  };
57144
58031
  try {
57145
- await mkdir5(path44.dirname(manifestPath), { recursive: true });
58032
+ await mkdir6(path45.dirname(manifestPath), { recursive: true });
57146
58033
  await writeFile5(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
57147
58034
  } catch {}
57148
58035
  return { manifest, cached: false };
@@ -57192,7 +58079,7 @@ function extractConstraintsFromContent(content) {
57192
58079
  return constraints;
57193
58080
  }
57194
58081
  async function extractDocConstraints(directory, taskFiles, taskDescription) {
57195
- const manifestPath = path44.join(directory, ".swarm", "doc-manifest.json");
58082
+ const manifestPath = path45.join(directory, ".swarm", "doc-manifest.json");
57196
58083
  let manifest;
57197
58084
  try {
57198
58085
  const content = await readFile6(manifestPath, "utf-8");
@@ -57218,7 +58105,7 @@ async function extractDocConstraints(directory, taskFiles, taskDescription) {
57218
58105
  }
57219
58106
  let fullContent;
57220
58107
  try {
57221
- fullContent = await readFile6(path44.join(directory, docFile.path), "utf-8");
58108
+ fullContent = await readFile6(path45.join(directory, docFile.path), "utf-8");
57222
58109
  } catch {
57223
58110
  skippedCount++;
57224
58111
  continue;
@@ -57241,7 +58128,7 @@ async function extractDocConstraints(directory, taskFiles, taskDescription) {
57241
58128
  tier: "swarm",
57242
58129
  lesson: constraint,
57243
58130
  category: "architecture",
57244
- tags: ["doc-scan", path44.basename(docFile.path)],
58131
+ tags: ["doc-scan", path45.basename(docFile.path)],
57245
58132
  scope: "global",
57246
58133
  confidence: 0.5,
57247
58134
  status: "candidate",
@@ -57287,9 +58174,9 @@ var doc_scan = createSwarmTool({
57287
58174
  }
57288
58175
  } catch {}
57289
58176
  if (force) {
57290
- const manifestPath = path44.join(directory, ".swarm", "doc-manifest.json");
58177
+ const manifestPath = path45.join(directory, ".swarm", "doc-manifest.json");
57291
58178
  try {
57292
- fs33.unlinkSync(manifestPath);
58179
+ fs34.unlinkSync(manifestPath);
57293
58180
  } catch {}
57294
58181
  }
57295
58182
  const { manifest, cached: cached3 } = await scanDocIndex(directory);
@@ -57514,8 +58401,8 @@ Use these as DOMAIN values when delegating to @sme.`;
57514
58401
  // src/tools/evidence-check.ts
57515
58402
  init_dist();
57516
58403
  init_create_tool();
57517
- import * as fs34 from "fs";
57518
- import * as path45 from "path";
58404
+ import * as fs35 from "fs";
58405
+ import * as path46 from "path";
57519
58406
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
57520
58407
  var MAX_EVIDENCE_FILES = 1000;
57521
58408
  var EVIDENCE_DIR2 = ".swarm/evidence";
@@ -57545,9 +58432,9 @@ function validateRequiredTypes(input) {
57545
58432
  return null;
57546
58433
  }
57547
58434
  function isPathWithinSwarm2(filePath, cwd) {
57548
- const normalizedCwd = path45.resolve(cwd);
57549
- const swarmPath = path45.join(normalizedCwd, ".swarm");
57550
- const normalizedPath = path45.resolve(filePath);
58435
+ const normalizedCwd = path46.resolve(cwd);
58436
+ const swarmPath = path46.join(normalizedCwd, ".swarm");
58437
+ const normalizedPath = path46.resolve(filePath);
57551
58438
  return normalizedPath.startsWith(swarmPath);
57552
58439
  }
57553
58440
  function parseCompletedTasks(planContent) {
@@ -57563,12 +58450,12 @@ function parseCompletedTasks(planContent) {
57563
58450
  }
57564
58451
  function readEvidenceFiles(evidenceDir, _cwd) {
57565
58452
  const evidence = [];
57566
- if (!fs34.existsSync(evidenceDir) || !fs34.statSync(evidenceDir).isDirectory()) {
58453
+ if (!fs35.existsSync(evidenceDir) || !fs35.statSync(evidenceDir).isDirectory()) {
57567
58454
  return evidence;
57568
58455
  }
57569
58456
  let files;
57570
58457
  try {
57571
- files = fs34.readdirSync(evidenceDir);
58458
+ files = fs35.readdirSync(evidenceDir);
57572
58459
  } catch {
57573
58460
  return evidence;
57574
58461
  }
@@ -57577,14 +58464,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
57577
58464
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
57578
58465
  continue;
57579
58466
  }
57580
- const filePath = path45.join(evidenceDir, filename);
58467
+ const filePath = path46.join(evidenceDir, filename);
57581
58468
  try {
57582
- const resolvedPath = path45.resolve(filePath);
57583
- const evidenceDirResolved = path45.resolve(evidenceDir);
58469
+ const resolvedPath = path46.resolve(filePath);
58470
+ const evidenceDirResolved = path46.resolve(evidenceDir);
57584
58471
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
57585
58472
  continue;
57586
58473
  }
57587
- const stat2 = fs34.lstatSync(filePath);
58474
+ const stat2 = fs35.lstatSync(filePath);
57588
58475
  if (!stat2.isFile()) {
57589
58476
  continue;
57590
58477
  }
@@ -57593,7 +58480,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
57593
58480
  }
57594
58481
  let fileStat;
57595
58482
  try {
57596
- fileStat = fs34.statSync(filePath);
58483
+ fileStat = fs35.statSync(filePath);
57597
58484
  if (fileStat.size > MAX_FILE_SIZE_BYTES3) {
57598
58485
  continue;
57599
58486
  }
@@ -57602,7 +58489,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
57602
58489
  }
57603
58490
  let content;
57604
58491
  try {
57605
- content = fs34.readFileSync(filePath, "utf-8");
58492
+ content = fs35.readFileSync(filePath, "utf-8");
57606
58493
  } catch {
57607
58494
  continue;
57608
58495
  }
@@ -57698,7 +58585,7 @@ var evidence_check = createSwarmTool({
57698
58585
  return JSON.stringify(errorResult, null, 2);
57699
58586
  }
57700
58587
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
57701
- const planPath = path45.join(cwd, PLAN_FILE);
58588
+ const planPath = path46.join(cwd, PLAN_FILE);
57702
58589
  if (!isPathWithinSwarm2(planPath, cwd)) {
57703
58590
  const errorResult = {
57704
58591
  error: "plan file path validation failed",
@@ -57712,7 +58599,7 @@ var evidence_check = createSwarmTool({
57712
58599
  }
57713
58600
  let planContent;
57714
58601
  try {
57715
- planContent = fs34.readFileSync(planPath, "utf-8");
58602
+ planContent = fs35.readFileSync(planPath, "utf-8");
57716
58603
  } catch {
57717
58604
  const result2 = {
57718
58605
  message: "No completed tasks found in plan.",
@@ -57730,7 +58617,7 @@ var evidence_check = createSwarmTool({
57730
58617
  };
57731
58618
  return JSON.stringify(result2, null, 2);
57732
58619
  }
57733
- const evidenceDir = path45.join(cwd, EVIDENCE_DIR2);
58620
+ const evidenceDir = path46.join(cwd, EVIDENCE_DIR2);
57734
58621
  const evidence = readEvidenceFiles(evidenceDir, cwd);
57735
58622
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
57736
58623
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -57747,8 +58634,8 @@ var evidence_check = createSwarmTool({
57747
58634
  // src/tools/file-extractor.ts
57748
58635
  init_tool();
57749
58636
  init_create_tool();
57750
- import * as fs35 from "fs";
57751
- import * as path46 from "path";
58637
+ import * as fs36 from "fs";
58638
+ import * as path47 from "path";
57752
58639
  var EXT_MAP = {
57753
58640
  python: ".py",
57754
58641
  py: ".py",
@@ -57810,8 +58697,8 @@ var extract_code_blocks = createSwarmTool({
57810
58697
  execute: async (args2, directory) => {
57811
58698
  const { content, output_dir, prefix } = args2;
57812
58699
  const targetDir = output_dir || directory;
57813
- if (!fs35.existsSync(targetDir)) {
57814
- fs35.mkdirSync(targetDir, { recursive: true });
58700
+ if (!fs36.existsSync(targetDir)) {
58701
+ fs36.mkdirSync(targetDir, { recursive: true });
57815
58702
  }
57816
58703
  if (!content) {
57817
58704
  return "Error: content is required";
@@ -57829,16 +58716,16 @@ var extract_code_blocks = createSwarmTool({
57829
58716
  if (prefix) {
57830
58717
  filename = `${prefix}_${filename}`;
57831
58718
  }
57832
- let filepath = path46.join(targetDir, filename);
57833
- const base = path46.basename(filepath, path46.extname(filepath));
57834
- const ext = path46.extname(filepath);
58719
+ let filepath = path47.join(targetDir, filename);
58720
+ const base = path47.basename(filepath, path47.extname(filepath));
58721
+ const ext = path47.extname(filepath);
57835
58722
  let counter = 1;
57836
- while (fs35.existsSync(filepath)) {
57837
- filepath = path46.join(targetDir, `${base}_${counter}${ext}`);
58723
+ while (fs36.existsSync(filepath)) {
58724
+ filepath = path47.join(targetDir, `${base}_${counter}${ext}`);
57838
58725
  counter++;
57839
58726
  }
57840
58727
  try {
57841
- fs35.writeFileSync(filepath, code.trim(), "utf-8");
58728
+ fs36.writeFileSync(filepath, code.trim(), "utf-8");
57842
58729
  savedFiles.push(filepath);
57843
58730
  } catch (error93) {
57844
58731
  errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
@@ -57954,8 +58841,8 @@ var gitingest = createSwarmTool({
57954
58841
  // src/tools/imports.ts
57955
58842
  init_dist();
57956
58843
  init_create_tool();
57957
- import * as fs36 from "fs";
57958
- import * as path47 from "path";
58844
+ import * as fs37 from "fs";
58845
+ import * as path48 from "path";
57959
58846
  var MAX_FILE_PATH_LENGTH2 = 500;
57960
58847
  var MAX_SYMBOL_LENGTH = 256;
57961
58848
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -58009,7 +58896,7 @@ function validateSymbolInput(symbol3) {
58009
58896
  return null;
58010
58897
  }
58011
58898
  function isBinaryFile2(filePath, buffer) {
58012
- const ext = path47.extname(filePath).toLowerCase();
58899
+ const ext = path48.extname(filePath).toLowerCase();
58013
58900
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
58014
58901
  return false;
58015
58902
  }
@@ -58033,15 +58920,15 @@ function parseImports(content, targetFile, targetSymbol) {
58033
58920
  const imports = [];
58034
58921
  let _resolvedTarget;
58035
58922
  try {
58036
- _resolvedTarget = path47.resolve(targetFile);
58923
+ _resolvedTarget = path48.resolve(targetFile);
58037
58924
  } catch {
58038
58925
  _resolvedTarget = targetFile;
58039
58926
  }
58040
- const targetBasename = path47.basename(targetFile, path47.extname(targetFile));
58927
+ const targetBasename = path48.basename(targetFile, path48.extname(targetFile));
58041
58928
  const targetWithExt = targetFile;
58042
58929
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
58043
- const normalizedTargetWithExt = path47.normalize(targetWithExt).replace(/\\/g, "/");
58044
- const normalizedTargetWithoutExt = path47.normalize(targetWithoutExt).replace(/\\/g, "/");
58930
+ const normalizedTargetWithExt = path48.normalize(targetWithExt).replace(/\\/g, "/");
58931
+ const normalizedTargetWithoutExt = path48.normalize(targetWithoutExt).replace(/\\/g, "/");
58045
58932
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
58046
58933
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
58047
58934
  const modulePath = match[1] || match[2] || match[3];
@@ -58064,9 +58951,9 @@ function parseImports(content, targetFile, targetSymbol) {
58064
58951
  }
58065
58952
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
58066
58953
  let isMatch = false;
58067
- const _targetDir = path47.dirname(targetFile);
58068
- const targetExt = path47.extname(targetFile);
58069
- const targetBasenameNoExt = path47.basename(targetFile, targetExt);
58954
+ const _targetDir = path48.dirname(targetFile);
58955
+ const targetExt = path48.extname(targetFile);
58956
+ const targetBasenameNoExt = path48.basename(targetFile, targetExt);
58070
58957
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
58071
58958
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
58072
58959
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -58123,7 +59010,7 @@ var SKIP_DIRECTORIES3 = new Set([
58123
59010
  function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
58124
59011
  let entries;
58125
59012
  try {
58126
- entries = fs36.readdirSync(dir);
59013
+ entries = fs37.readdirSync(dir);
58127
59014
  } catch (e) {
58128
59015
  stats.fileErrors.push({
58129
59016
  path: dir,
@@ -58134,13 +59021,13 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
58134
59021
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
58135
59022
  for (const entry of entries) {
58136
59023
  if (SKIP_DIRECTORIES3.has(entry)) {
58137
- stats.skippedDirs.push(path47.join(dir, entry));
59024
+ stats.skippedDirs.push(path48.join(dir, entry));
58138
59025
  continue;
58139
59026
  }
58140
- const fullPath = path47.join(dir, entry);
59027
+ const fullPath = path48.join(dir, entry);
58141
59028
  let stat2;
58142
59029
  try {
58143
- stat2 = fs36.statSync(fullPath);
59030
+ stat2 = fs37.statSync(fullPath);
58144
59031
  } catch (e) {
58145
59032
  stats.fileErrors.push({
58146
59033
  path: fullPath,
@@ -58151,7 +59038,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
58151
59038
  if (stat2.isDirectory()) {
58152
59039
  findSourceFiles(fullPath, files, stats);
58153
59040
  } else if (stat2.isFile()) {
58154
- const ext = path47.extname(fullPath).toLowerCase();
59041
+ const ext = path48.extname(fullPath).toLowerCase();
58155
59042
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
58156
59043
  files.push(fullPath);
58157
59044
  }
@@ -58208,8 +59095,8 @@ var imports = createSwarmTool({
58208
59095
  return JSON.stringify(errorResult, null, 2);
58209
59096
  }
58210
59097
  try {
58211
- const targetFile = path47.resolve(file3);
58212
- if (!fs36.existsSync(targetFile)) {
59098
+ const targetFile = path48.resolve(file3);
59099
+ if (!fs37.existsSync(targetFile)) {
58213
59100
  const errorResult = {
58214
59101
  error: `target file not found: ${file3}`,
58215
59102
  target: file3,
@@ -58219,7 +59106,7 @@ var imports = createSwarmTool({
58219
59106
  };
58220
59107
  return JSON.stringify(errorResult, null, 2);
58221
59108
  }
58222
- const targetStat = fs36.statSync(targetFile);
59109
+ const targetStat = fs37.statSync(targetFile);
58223
59110
  if (!targetStat.isFile()) {
58224
59111
  const errorResult = {
58225
59112
  error: "target must be a file, not a directory",
@@ -58230,7 +59117,7 @@ var imports = createSwarmTool({
58230
59117
  };
58231
59118
  return JSON.stringify(errorResult, null, 2);
58232
59119
  }
58233
- const baseDir = path47.dirname(targetFile);
59120
+ const baseDir = path48.dirname(targetFile);
58234
59121
  const scanStats = {
58235
59122
  skippedDirs: [],
58236
59123
  skippedFiles: 0,
@@ -58245,12 +59132,12 @@ var imports = createSwarmTool({
58245
59132
  if (consumers.length >= MAX_CONSUMERS)
58246
59133
  break;
58247
59134
  try {
58248
- const stat2 = fs36.statSync(filePath);
59135
+ const stat2 = fs37.statSync(filePath);
58249
59136
  if (stat2.size > MAX_FILE_SIZE_BYTES4) {
58250
59137
  skippedFileCount++;
58251
59138
  continue;
58252
59139
  }
58253
- const buffer = fs36.readFileSync(filePath);
59140
+ const buffer = fs37.readFileSync(filePath);
58254
59141
  if (isBinaryFile2(filePath, buffer)) {
58255
59142
  skippedFileCount++;
58256
59143
  continue;
@@ -58805,8 +59692,8 @@ init_dist();
58805
59692
  init_config();
58806
59693
  init_schema();
58807
59694
  init_manager();
58808
- import * as fs37 from "fs";
58809
- import * as path48 from "path";
59695
+ import * as fs38 from "fs";
59696
+ import * as path49 from "path";
58810
59697
  init_utils2();
58811
59698
  init_telemetry();
58812
59699
  init_create_tool();
@@ -59026,11 +59913,11 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
59026
59913
  safeWarn(`[phase_complete] Completion verify error (non-blocking):`, completionError);
59027
59914
  }
59028
59915
  try {
59029
- const driftEvidencePath = path48.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
59916
+ const driftEvidencePath = path49.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
59030
59917
  let driftVerdictFound = false;
59031
59918
  let driftVerdictApproved = false;
59032
59919
  try {
59033
- const driftEvidenceContent = fs37.readFileSync(driftEvidencePath, "utf-8");
59920
+ const driftEvidenceContent = fs38.readFileSync(driftEvidencePath, "utf-8");
59034
59921
  const driftEvidence = JSON.parse(driftEvidenceContent);
59035
59922
  const entries = driftEvidence.entries ?? [];
59036
59923
  for (const entry of entries) {
@@ -59060,14 +59947,14 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
59060
59947
  driftVerdictFound = false;
59061
59948
  }
59062
59949
  if (!driftVerdictFound) {
59063
- const specPath = path48.join(dir, ".swarm", "spec.md");
59064
- const specExists = fs37.existsSync(specPath);
59950
+ const specPath = path49.join(dir, ".swarm", "spec.md");
59951
+ const specExists = fs38.existsSync(specPath);
59065
59952
  if (!specExists) {
59066
59953
  let incompleteTaskCount = 0;
59067
59954
  let planPhaseFound = false;
59068
59955
  try {
59069
59956
  const planPath = validateSwarmPath(dir, "plan.json");
59070
- const planRaw = fs37.readFileSync(planPath, "utf-8");
59957
+ const planRaw = fs38.readFileSync(planPath, "utf-8");
59071
59958
  const plan = JSON.parse(planRaw);
59072
59959
  const targetPhase = plan.phases.find((p) => p.id === phase);
59073
59960
  if (targetPhase) {
@@ -59134,7 +60021,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
59134
60021
  };
59135
60022
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
59136
60023
  try {
59137
- const projectName = path48.basename(dir);
60024
+ const projectName = path49.basename(dir);
59138
60025
  await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
59139
60026
  } catch (error93) {
59140
60027
  safeWarn("[phase_complete] Failed to curate lessons from retrospective:", error93);
@@ -59177,7 +60064,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
59177
60064
  if (agentsMissing.length > 0) {
59178
60065
  try {
59179
60066
  const planPath = validateSwarmPath(dir, "plan.json");
59180
- const planRaw = fs37.readFileSync(planPath, "utf-8");
60067
+ const planRaw = fs38.readFileSync(planPath, "utf-8");
59181
60068
  const plan = JSON.parse(planRaw);
59182
60069
  const targetPhase = plan.phases.find((p) => p.id === phase);
59183
60070
  if (targetPhase && targetPhase.tasks.length > 0 && targetPhase.tasks.every((t) => t.status === "completed")) {
@@ -59208,7 +60095,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
59208
60095
  if (phaseCompleteConfig.regression_sweep?.enforce) {
59209
60096
  try {
59210
60097
  const planPath = validateSwarmPath(dir, "plan.json");
59211
- const planRaw = fs37.readFileSync(planPath, "utf-8");
60098
+ const planRaw = fs38.readFileSync(planPath, "utf-8");
59212
60099
  const plan = JSON.parse(planRaw);
59213
60100
  const targetPhase = plan.phases.find((p) => p.id === phase);
59214
60101
  if (targetPhase) {
@@ -59246,7 +60133,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
59246
60133
  };
59247
60134
  try {
59248
60135
  const eventsPath = validateSwarmPath(dir, "events.jsonl");
59249
- fs37.appendFileSync(eventsPath, `${JSON.stringify(event)}
60136
+ fs38.appendFileSync(eventsPath, `${JSON.stringify(event)}
59250
60137
  `, "utf-8");
59251
60138
  } catch (writeError) {
59252
60139
  warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
@@ -59267,12 +60154,12 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
59267
60154
  }
59268
60155
  try {
59269
60156
  const planPath = validateSwarmPath(dir, "plan.json");
59270
- const planJson = fs37.readFileSync(planPath, "utf-8");
60157
+ const planJson = fs38.readFileSync(planPath, "utf-8");
59271
60158
  const plan = JSON.parse(planJson);
59272
60159
  const phaseObj = plan.phases.find((p) => p.id === phase);
59273
60160
  if (phaseObj) {
59274
60161
  phaseObj.status = "completed";
59275
- fs37.writeFileSync(planPath, `${JSON.stringify(plan, null, 2)}
60162
+ fs38.writeFileSync(planPath, `${JSON.stringify(plan, null, 2)}
59276
60163
  `, "utf-8");
59277
60164
  }
59278
60165
  } catch (error93) {
@@ -59326,8 +60213,8 @@ init_dist();
59326
60213
  init_discovery();
59327
60214
  init_utils();
59328
60215
  init_create_tool();
59329
- import * as fs38 from "fs";
59330
- import * as path49 from "path";
60216
+ import * as fs39 from "fs";
60217
+ import * as path50 from "path";
59331
60218
  var MAX_OUTPUT_BYTES5 = 52428800;
59332
60219
  var AUDIT_TIMEOUT_MS = 120000;
59333
60220
  function isValidEcosystem(value) {
@@ -59345,28 +60232,28 @@ function validateArgs3(args2) {
59345
60232
  function detectEcosystems(directory) {
59346
60233
  const ecosystems = [];
59347
60234
  const cwd = directory;
59348
- if (fs38.existsSync(path49.join(cwd, "package.json"))) {
60235
+ if (fs39.existsSync(path50.join(cwd, "package.json"))) {
59349
60236
  ecosystems.push("npm");
59350
60237
  }
59351
- if (fs38.existsSync(path49.join(cwd, "pyproject.toml")) || fs38.existsSync(path49.join(cwd, "requirements.txt"))) {
60238
+ if (fs39.existsSync(path50.join(cwd, "pyproject.toml")) || fs39.existsSync(path50.join(cwd, "requirements.txt"))) {
59352
60239
  ecosystems.push("pip");
59353
60240
  }
59354
- if (fs38.existsSync(path49.join(cwd, "Cargo.toml"))) {
60241
+ if (fs39.existsSync(path50.join(cwd, "Cargo.toml"))) {
59355
60242
  ecosystems.push("cargo");
59356
60243
  }
59357
- if (fs38.existsSync(path49.join(cwd, "go.mod"))) {
60244
+ if (fs39.existsSync(path50.join(cwd, "go.mod"))) {
59358
60245
  ecosystems.push("go");
59359
60246
  }
59360
60247
  try {
59361
- const files = fs38.readdirSync(cwd);
60248
+ const files = fs39.readdirSync(cwd);
59362
60249
  if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
59363
60250
  ecosystems.push("dotnet");
59364
60251
  }
59365
60252
  } catch {}
59366
- if (fs38.existsSync(path49.join(cwd, "Gemfile")) || fs38.existsSync(path49.join(cwd, "Gemfile.lock"))) {
60253
+ if (fs39.existsSync(path50.join(cwd, "Gemfile")) || fs39.existsSync(path50.join(cwd, "Gemfile.lock"))) {
59367
60254
  ecosystems.push("ruby");
59368
60255
  }
59369
- if (fs38.existsSync(path49.join(cwd, "pubspec.yaml"))) {
60256
+ if (fs39.existsSync(path50.join(cwd, "pubspec.yaml"))) {
59370
60257
  ecosystems.push("dart");
59371
60258
  }
59372
60259
  return ecosystems;
@@ -60366,47 +61253,6 @@ var pkg_audit = createSwarmTool({
60366
61253
  });
60367
61254
  // src/tools/placeholder-scan.ts
60368
61255
  init_manager();
60369
-
60370
- // src/lang/registry.ts
60371
- init_runtime();
60372
- var languageDefinitions = [
60373
- {
60374
- id: "javascript",
60375
- extensions: [".js", ".jsx"],
60376
- commentNodes: ["comment", "line_comment", "block_comment"]
60377
- },
60378
- {
60379
- id: "typescript",
60380
- extensions: [".ts", ".tsx"],
60381
- commentNodes: ["comment", "line_comment", "block_comment"]
60382
- },
60383
- {
60384
- id: "python",
60385
- extensions: [".py"],
60386
- commentNodes: ["comment"]
60387
- },
60388
- {
60389
- id: "go",
60390
- extensions: [".go"],
60391
- commentNodes: ["comment"]
60392
- },
60393
- {
60394
- id: "rust",
60395
- extensions: [".rs"],
60396
- commentNodes: ["line_comment", "block_comment"]
60397
- }
60398
- ];
60399
- var extensionMap = new Map;
60400
- for (const definition of languageDefinitions) {
60401
- for (const extension of definition.extensions) {
60402
- extensionMap.set(extension, definition);
60403
- }
60404
- }
60405
- function getLanguageForExtension(extension) {
60406
- return extensionMap.get(extension.toLowerCase());
60407
- }
60408
-
60409
- // src/tools/placeholder-scan.ts
60410
61256
  init_utils();
60411
61257
  var MAX_FILE_SIZE = 1024 * 1024;
60412
61258
  var SUPPORTED_PARSER_EXTENSIONS = new Set([
@@ -60428,8 +61274,8 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
60428
61274
  ]);
60429
61275
  // src/tools/pre-check-batch.ts
60430
61276
  init_dist();
60431
- import * as fs41 from "fs";
60432
- import * as path52 from "path";
61277
+ import * as fs42 from "fs";
61278
+ import * as path53 from "path";
60433
61279
 
60434
61280
  // node_modules/yocto-queue/index.js
60435
61281
  class Node2 {
@@ -60597,8 +61443,8 @@ init_lint();
60597
61443
  init_manager();
60598
61444
 
60599
61445
  // src/quality/metrics.ts
60600
- import * as fs39 from "fs";
60601
- import * as path50 from "path";
61446
+ import * as fs40 from "fs";
61447
+ import * as path51 from "path";
60602
61448
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
60603
61449
  var MIN_DUPLICATION_LINES = 10;
60604
61450
  function estimateCyclomaticComplexity(content) {
@@ -60636,11 +61482,11 @@ function estimateCyclomaticComplexity(content) {
60636
61482
  }
60637
61483
  function getComplexityForFile2(filePath) {
60638
61484
  try {
60639
- const stat2 = fs39.statSync(filePath);
61485
+ const stat2 = fs40.statSync(filePath);
60640
61486
  if (stat2.size > MAX_FILE_SIZE_BYTES5) {
60641
61487
  return null;
60642
61488
  }
60643
- const content = fs39.readFileSync(filePath, "utf-8");
61489
+ const content = fs40.readFileSync(filePath, "utf-8");
60644
61490
  return estimateCyclomaticComplexity(content);
60645
61491
  } catch {
60646
61492
  return null;
@@ -60650,8 +61496,8 @@ async function computeComplexityDelta(files, workingDir) {
60650
61496
  let totalComplexity = 0;
60651
61497
  const analyzedFiles = [];
60652
61498
  for (const file3 of files) {
60653
- const fullPath = path50.isAbsolute(file3) ? file3 : path50.join(workingDir, file3);
60654
- if (!fs39.existsSync(fullPath)) {
61499
+ const fullPath = path51.isAbsolute(file3) ? file3 : path51.join(workingDir, file3);
61500
+ if (!fs40.existsSync(fullPath)) {
60655
61501
  continue;
60656
61502
  }
60657
61503
  const complexity = getComplexityForFile2(fullPath);
@@ -60772,8 +61618,8 @@ function countGoExports(content) {
60772
61618
  }
60773
61619
  function getExportCountForFile(filePath) {
60774
61620
  try {
60775
- const content = fs39.readFileSync(filePath, "utf-8");
60776
- const ext = path50.extname(filePath).toLowerCase();
61621
+ const content = fs40.readFileSync(filePath, "utf-8");
61622
+ const ext = path51.extname(filePath).toLowerCase();
60777
61623
  switch (ext) {
60778
61624
  case ".ts":
60779
61625
  case ".tsx":
@@ -60799,8 +61645,8 @@ async function computePublicApiDelta(files, workingDir) {
60799
61645
  let totalExports = 0;
60800
61646
  const analyzedFiles = [];
60801
61647
  for (const file3 of files) {
60802
- const fullPath = path50.isAbsolute(file3) ? file3 : path50.join(workingDir, file3);
60803
- if (!fs39.existsSync(fullPath)) {
61648
+ const fullPath = path51.isAbsolute(file3) ? file3 : path51.join(workingDir, file3);
61649
+ if (!fs40.existsSync(fullPath)) {
60804
61650
  continue;
60805
61651
  }
60806
61652
  const exports = getExportCountForFile(fullPath);
@@ -60833,16 +61679,16 @@ async function computeDuplicationRatio(files, workingDir) {
60833
61679
  let duplicateLines = 0;
60834
61680
  const analyzedFiles = [];
60835
61681
  for (const file3 of files) {
60836
- const fullPath = path50.isAbsolute(file3) ? file3 : path50.join(workingDir, file3);
60837
- if (!fs39.existsSync(fullPath)) {
61682
+ const fullPath = path51.isAbsolute(file3) ? file3 : path51.join(workingDir, file3);
61683
+ if (!fs40.existsSync(fullPath)) {
60838
61684
  continue;
60839
61685
  }
60840
61686
  try {
60841
- const stat2 = fs39.statSync(fullPath);
61687
+ const stat2 = fs40.statSync(fullPath);
60842
61688
  if (stat2.size > MAX_FILE_SIZE_BYTES5) {
60843
61689
  continue;
60844
61690
  }
60845
- const content = fs39.readFileSync(fullPath, "utf-8");
61691
+ const content = fs40.readFileSync(fullPath, "utf-8");
60846
61692
  const lines = content.split(`
60847
61693
  `).filter((line) => line.trim().length > 0);
60848
61694
  if (lines.length < MIN_DUPLICATION_LINES) {
@@ -60866,8 +61712,8 @@ function countCodeLines(content) {
60866
61712
  return lines.length;
60867
61713
  }
60868
61714
  function isTestFile(filePath) {
60869
- const basename9 = path50.basename(filePath);
60870
- const _ext = path50.extname(filePath).toLowerCase();
61715
+ const basename9 = path51.basename(filePath);
61716
+ const _ext = path51.extname(filePath).toLowerCase();
60871
61717
  const testPatterns = [
60872
61718
  ".test.",
60873
61719
  ".spec.",
@@ -60948,8 +61794,8 @@ function matchGlobSegment(globSegments, pathSegments) {
60948
61794
  }
60949
61795
  return gIndex === globSegments.length && pIndex === pathSegments.length;
60950
61796
  }
60951
- function matchesGlobSegment(path51, glob) {
60952
- const normalizedPath = path51.replace(/\\/g, "/");
61797
+ function matchesGlobSegment(path52, glob) {
61798
+ const normalizedPath = path52.replace(/\\/g, "/");
60953
61799
  const normalizedGlob = glob.replace(/\\/g, "/");
60954
61800
  if (normalizedPath.includes("//")) {
60955
61801
  return false;
@@ -60980,8 +61826,8 @@ function simpleGlobToRegex2(glob) {
60980
61826
  function hasGlobstar(glob) {
60981
61827
  return glob.includes("**");
60982
61828
  }
60983
- function globMatches(path51, glob) {
60984
- const normalizedPath = path51.replace(/\\/g, "/");
61829
+ function globMatches(path52, glob) {
61830
+ const normalizedPath = path52.replace(/\\/g, "/");
60985
61831
  if (!glob || glob === "") {
60986
61832
  if (normalizedPath.includes("//")) {
60987
61833
  return false;
@@ -61017,31 +61863,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
61017
61863
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
61018
61864
  let testLines = 0;
61019
61865
  let codeLines = 0;
61020
- const srcDir = path50.join(workingDir, "src");
61021
- if (fs39.existsSync(srcDir)) {
61866
+ const srcDir = path51.join(workingDir, "src");
61867
+ if (fs40.existsSync(srcDir)) {
61022
61868
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
61023
61869
  codeLines += lines;
61024
61870
  });
61025
61871
  }
61026
61872
  const possibleSrcDirs = ["lib", "app", "source", "core"];
61027
61873
  for (const dir of possibleSrcDirs) {
61028
- const dirPath = path50.join(workingDir, dir);
61029
- if (fs39.existsSync(dirPath)) {
61874
+ const dirPath = path51.join(workingDir, dir);
61875
+ if (fs40.existsSync(dirPath)) {
61030
61876
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
61031
61877
  codeLines += lines;
61032
61878
  });
61033
61879
  }
61034
61880
  }
61035
- const testsDir = path50.join(workingDir, "tests");
61036
- if (fs39.existsSync(testsDir)) {
61881
+ const testsDir = path51.join(workingDir, "tests");
61882
+ if (fs40.existsSync(testsDir)) {
61037
61883
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
61038
61884
  testLines += lines;
61039
61885
  });
61040
61886
  }
61041
61887
  const possibleTestDirs = ["test", "__tests__", "specs"];
61042
61888
  for (const dir of possibleTestDirs) {
61043
- const dirPath = path50.join(workingDir, dir);
61044
- if (fs39.existsSync(dirPath) && dirPath !== testsDir) {
61889
+ const dirPath = path51.join(workingDir, dir);
61890
+ if (fs40.existsSync(dirPath) && dirPath !== testsDir) {
61045
61891
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
61046
61892
  testLines += lines;
61047
61893
  });
@@ -61053,9 +61899,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
61053
61899
  }
61054
61900
  async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
61055
61901
  try {
61056
- const entries = fs39.readdirSync(dirPath, { withFileTypes: true });
61902
+ const entries = fs40.readdirSync(dirPath, { withFileTypes: true });
61057
61903
  for (const entry of entries) {
61058
- const fullPath = path50.join(dirPath, entry.name);
61904
+ const fullPath = path51.join(dirPath, entry.name);
61059
61905
  if (entry.isDirectory()) {
61060
61906
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
61061
61907
  continue;
@@ -61063,7 +61909,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
61063
61909
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
61064
61910
  } else if (entry.isFile()) {
61065
61911
  const relativePath = fullPath.replace(`${dirPath}/`, "");
61066
- const ext = path50.extname(entry.name).toLowerCase();
61912
+ const ext = path51.extname(entry.name).toLowerCase();
61067
61913
  const validExts = [
61068
61914
  ".ts",
61069
61915
  ".tsx",
@@ -61099,7 +61945,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
61099
61945
  continue;
61100
61946
  }
61101
61947
  try {
61102
- const content = fs39.readFileSync(fullPath, "utf-8");
61948
+ const content = fs40.readFileSync(fullPath, "utf-8");
61103
61949
  const lines = countCodeLines(content);
61104
61950
  callback(lines);
61105
61951
  } catch {}
@@ -61313,9 +62159,9 @@ async function qualityBudget(input, directory) {
61313
62159
  init_dist();
61314
62160
  init_manager();
61315
62161
  init_detector();
61316
- import * as fs40 from "fs";
61317
- import * as path51 from "path";
61318
- import { extname as extname9 } from "path";
62162
+ import * as fs41 from "fs";
62163
+ import * as path52 from "path";
62164
+ import { extname as extname10 } from "path";
61319
62165
 
61320
62166
  // src/sast/rules/c.ts
61321
62167
  var cRules = [
@@ -62181,17 +63027,17 @@ var SEVERITY_ORDER = {
62181
63027
  };
62182
63028
  function shouldSkipFile(filePath) {
62183
63029
  try {
62184
- const stats = fs40.statSync(filePath);
63030
+ const stats = fs41.statSync(filePath);
62185
63031
  if (stats.size > MAX_FILE_SIZE_BYTES6) {
62186
63032
  return { skip: true, reason: "file too large" };
62187
63033
  }
62188
63034
  if (stats.size === 0) {
62189
63035
  return { skip: true, reason: "empty file" };
62190
63036
  }
62191
- const fd = fs40.openSync(filePath, "r");
63037
+ const fd = fs41.openSync(filePath, "r");
62192
63038
  const buffer = Buffer.alloc(8192);
62193
- const bytesRead = fs40.readSync(fd, buffer, 0, 8192, 0);
62194
- fs40.closeSync(fd);
63039
+ const bytesRead = fs41.readSync(fd, buffer, 0, 8192, 0);
63040
+ fs41.closeSync(fd);
62195
63041
  if (bytesRead > 0) {
62196
63042
  let nullCount = 0;
62197
63043
  for (let i2 = 0;i2 < bytesRead; i2++) {
@@ -62230,7 +63076,7 @@ function countBySeverity(findings) {
62230
63076
  }
62231
63077
  function scanFileWithTierA(filePath, language) {
62232
63078
  try {
62233
- const content = fs40.readFileSync(filePath, "utf-8");
63079
+ const content = fs41.readFileSync(filePath, "utf-8");
62234
63080
  const findings = executeRulesSync(filePath, content, language);
62235
63081
  return findings.map((f) => ({
62236
63082
  rule_id: f.rule_id,
@@ -62277,8 +63123,8 @@ async function sastScan(input, directory, config3) {
62277
63123
  _filesSkipped++;
62278
63124
  continue;
62279
63125
  }
62280
- const resolvedPath = path51.isAbsolute(filePath) ? filePath : path51.resolve(directory, filePath);
62281
- if (!fs40.existsSync(resolvedPath)) {
63126
+ const resolvedPath = path52.isAbsolute(filePath) ? filePath : path52.resolve(directory, filePath);
63127
+ if (!fs41.existsSync(resolvedPath)) {
62282
63128
  _filesSkipped++;
62283
63129
  continue;
62284
63130
  }
@@ -62287,7 +63133,7 @@ async function sastScan(input, directory, config3) {
62287
63133
  _filesSkipped++;
62288
63134
  continue;
62289
63135
  }
62290
- const ext = extname9(resolvedPath).toLowerCase();
63136
+ const ext = extname10(resolvedPath).toLowerCase();
62291
63137
  const profile = getProfileForFile(resolvedPath);
62292
63138
  const langDef = getLanguageForExtension(ext);
62293
63139
  if (!profile && !langDef) {
@@ -62476,18 +63322,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
62476
63322
  let resolved;
62477
63323
  const isWinAbs = isWindowsAbsolutePath(inputPath);
62478
63324
  if (isWinAbs) {
62479
- resolved = path52.win32.resolve(inputPath);
62480
- } else if (path52.isAbsolute(inputPath)) {
62481
- resolved = path52.resolve(inputPath);
63325
+ resolved = path53.win32.resolve(inputPath);
63326
+ } else if (path53.isAbsolute(inputPath)) {
63327
+ resolved = path53.resolve(inputPath);
62482
63328
  } else {
62483
- resolved = path52.resolve(baseDir, inputPath);
63329
+ resolved = path53.resolve(baseDir, inputPath);
62484
63330
  }
62485
- const workspaceResolved = path52.resolve(workspaceDir);
63331
+ const workspaceResolved = path53.resolve(workspaceDir);
62486
63332
  let relative6;
62487
63333
  if (isWinAbs) {
62488
- relative6 = path52.win32.relative(workspaceResolved, resolved);
63334
+ relative6 = path53.win32.relative(workspaceResolved, resolved);
62489
63335
  } else {
62490
- relative6 = path52.relative(workspaceResolved, resolved);
63336
+ relative6 = path53.relative(workspaceResolved, resolved);
62491
63337
  }
62492
63338
  if (relative6.startsWith("..")) {
62493
63339
  return "path traversal detected";
@@ -62548,13 +63394,13 @@ async function runLintWrapped(files, directory, _config) {
62548
63394
  }
62549
63395
  async function runLintOnFiles(linter, files, workspaceDir) {
62550
63396
  const isWindows = process.platform === "win32";
62551
- const binDir = path52.join(workspaceDir, "node_modules", ".bin");
63397
+ const binDir = path53.join(workspaceDir, "node_modules", ".bin");
62552
63398
  const validatedFiles = [];
62553
63399
  for (const file3 of files) {
62554
63400
  if (typeof file3 !== "string") {
62555
63401
  continue;
62556
63402
  }
62557
- const resolvedPath = path52.resolve(file3);
63403
+ const resolvedPath = path53.resolve(file3);
62558
63404
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
62559
63405
  if (validationError) {
62560
63406
  continue;
@@ -62572,10 +63418,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
62572
63418
  }
62573
63419
  let command;
62574
63420
  if (linter === "biome") {
62575
- const biomeBin = isWindows ? path52.join(binDir, "biome.EXE") : path52.join(binDir, "biome");
63421
+ const biomeBin = isWindows ? path53.join(binDir, "biome.EXE") : path53.join(binDir, "biome");
62576
63422
  command = [biomeBin, "check", ...validatedFiles];
62577
63423
  } else {
62578
- const eslintBin = isWindows ? path52.join(binDir, "eslint.cmd") : path52.join(binDir, "eslint");
63424
+ const eslintBin = isWindows ? path53.join(binDir, "eslint.cmd") : path53.join(binDir, "eslint");
62579
63425
  command = [eslintBin, ...validatedFiles];
62580
63426
  }
62581
63427
  try {
@@ -62712,7 +63558,7 @@ async function runSecretscanWithFiles(files, directory) {
62712
63558
  skippedFiles++;
62713
63559
  continue;
62714
63560
  }
62715
- const resolvedPath = path52.resolve(file3);
63561
+ const resolvedPath = path53.resolve(file3);
62716
63562
  const validationError = validatePath(resolvedPath, directory, directory);
62717
63563
  if (validationError) {
62718
63564
  skippedFiles++;
@@ -62730,14 +63576,14 @@ async function runSecretscanWithFiles(files, directory) {
62730
63576
  };
62731
63577
  }
62732
63578
  for (const file3 of validatedFiles) {
62733
- const ext = path52.extname(file3).toLowerCase();
63579
+ const ext = path53.extname(file3).toLowerCase();
62734
63580
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
62735
63581
  skippedFiles++;
62736
63582
  continue;
62737
63583
  }
62738
63584
  let stat2;
62739
63585
  try {
62740
- stat2 = fs41.statSync(file3);
63586
+ stat2 = fs42.statSync(file3);
62741
63587
  } catch {
62742
63588
  skippedFiles++;
62743
63589
  continue;
@@ -62748,7 +63594,7 @@ async function runSecretscanWithFiles(files, directory) {
62748
63594
  }
62749
63595
  let content;
62750
63596
  try {
62751
- const buffer = fs41.readFileSync(file3);
63597
+ const buffer = fs42.readFileSync(file3);
62752
63598
  if (buffer.includes(0)) {
62753
63599
  skippedFiles++;
62754
63600
  continue;
@@ -62889,7 +63735,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
62889
63735
  warn(`pre_check_batch: Invalid file path: ${file3}`);
62890
63736
  continue;
62891
63737
  }
62892
- changedFiles.push(path52.resolve(directory, file3));
63738
+ changedFiles.push(path53.resolve(directory, file3));
62893
63739
  }
62894
63740
  if (changedFiles.length === 0) {
62895
63741
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -63060,7 +63906,7 @@ var pre_check_batch = createSwarmTool({
63060
63906
  };
63061
63907
  return JSON.stringify(errorResult, null, 2);
63062
63908
  }
63063
- const resolvedDirectory = path52.resolve(typedArgs.directory);
63909
+ const resolvedDirectory = path53.resolve(typedArgs.directory);
63064
63910
  const workspaceAnchor = resolvedDirectory;
63065
63911
  const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
63066
63912
  if (dirError) {
@@ -63166,10 +64012,72 @@ ${paginatedContent}`;
63166
64012
  });
63167
64013
  // src/tools/save-plan.ts
63168
64014
  init_tool();
64015
+ import * as fs44 from "fs";
64016
+ import * as path55 from "path";
64017
+
64018
+ // src/parallel/file-locks.ts
64019
+ import * as fs43 from "fs";
64020
+ import * as path54 from "path";
64021
+ var LOCKS_DIR = ".swarm/locks";
64022
+ var LOCK_TIMEOUT_MS = 5 * 60 * 1000;
64023
+ function getLockFilePath(directory, filePath) {
64024
+ if (filePath.includes("..")) {
64025
+ throw new Error("Invalid file path: path traversal not allowed");
64026
+ }
64027
+ const hash3 = Buffer.from(filePath).toString("base64").replace(/[/+=]/g, "_");
64028
+ return path54.join(directory, LOCKS_DIR, `${hash3}.lock`);
64029
+ }
64030
+ function tryAcquireLock(directory, filePath, agent, taskId) {
64031
+ const lockPath = getLockFilePath(directory, filePath);
64032
+ const locksDir = path54.dirname(lockPath);
64033
+ if (!fs43.existsSync(locksDir)) {
64034
+ fs43.mkdirSync(locksDir, { recursive: true });
64035
+ }
64036
+ if (fs43.existsSync(lockPath)) {
64037
+ try {
64038
+ const existingLock = JSON.parse(fs43.readFileSync(lockPath, "utf-8"));
64039
+ if (Date.now() > existingLock.expiresAt) {
64040
+ fs43.unlinkSync(lockPath);
64041
+ } else {
64042
+ return { acquired: false, existing: existingLock };
64043
+ }
64044
+ } catch {
64045
+ fs43.unlinkSync(lockPath);
64046
+ }
64047
+ }
64048
+ const lock = {
64049
+ filePath,
64050
+ agent,
64051
+ taskId,
64052
+ timestamp: new Date().toISOString(),
64053
+ expiresAt: Date.now() + LOCK_TIMEOUT_MS
64054
+ };
64055
+ const tempPath = `${lockPath}.tmp`;
64056
+ fs43.writeFileSync(tempPath, JSON.stringify(lock, null, 2), "utf-8");
64057
+ fs43.renameSync(tempPath, lockPath);
64058
+ return { acquired: true, lock };
64059
+ }
64060
+ function releaseLock(directory, filePath, taskId) {
64061
+ const lockPath = getLockFilePath(directory, filePath);
64062
+ if (!fs43.existsSync(lockPath)) {
64063
+ return true;
64064
+ }
64065
+ try {
64066
+ const lock = JSON.parse(fs43.readFileSync(lockPath, "utf-8"));
64067
+ if (lock.taskId === taskId) {
64068
+ fs43.unlinkSync(lockPath);
64069
+ return true;
64070
+ }
64071
+ return false;
64072
+ } catch {
64073
+ fs43.unlinkSync(lockPath);
64074
+ return true;
64075
+ }
64076
+ }
64077
+
64078
+ // src/tools/save-plan.ts
63169
64079
  init_manager2();
63170
64080
  init_create_tool();
63171
- import * as fs42 from "fs";
63172
- import * as path53 from "path";
63173
64081
  function detectPlaceholderContent(args2) {
63174
64082
  const issues = [];
63175
64083
  const placeholderPattern = /^\[\w[\w\s]*\]$/;
@@ -63270,25 +64178,42 @@ async function executeSavePlan(args2, fallbackDir) {
63270
64178
  };
63271
64179
  const tasksCount = plan.phases.reduce((acc, phase) => acc + phase.tasks.length, 0);
63272
64180
  const dir = targetWorkspace;
64181
+ const lockTaskId = `save-plan-${Date.now()}`;
64182
+ const planFilePath = "plan.json";
63273
64183
  try {
63274
- await savePlan(dir, plan);
64184
+ const lockResult = tryAcquireLock(dir, planFilePath, "architect", lockTaskId);
64185
+ if (!lockResult.acquired) {
64186
+ return {
64187
+ success: false,
64188
+ message: `Plan write blocked: file is locked by ${lockResult.existing?.agent ?? "another agent"} (task: ${lockResult.existing?.taskId ?? "unknown"})`,
64189
+ errors: [
64190
+ "Concurrent plan write detected \u2014 retry after the current write completes"
64191
+ ],
64192
+ recovery_guidance: "Wait a moment and retry save_plan. The lock will expire automatically if the holding agent fails."
64193
+ };
64194
+ }
63275
64195
  try {
63276
- const markerPath = path53.join(dir, ".swarm", ".plan-write-marker");
63277
- const marker = JSON.stringify({
63278
- source: "save_plan",
63279
- timestamp: new Date().toISOString(),
64196
+ await savePlan(dir, plan);
64197
+ try {
64198
+ const markerPath = path55.join(dir, ".swarm", ".plan-write-marker");
64199
+ const marker = JSON.stringify({
64200
+ source: "save_plan",
64201
+ timestamp: new Date().toISOString(),
64202
+ phases_count: plan.phases.length,
64203
+ tasks_count: tasksCount
64204
+ });
64205
+ await fs44.promises.writeFile(markerPath, marker, "utf8");
64206
+ } catch {}
64207
+ return {
64208
+ success: true,
64209
+ message: "Plan saved successfully",
64210
+ plan_path: path55.join(dir, ".swarm", "plan.json"),
63280
64211
  phases_count: plan.phases.length,
63281
64212
  tasks_count: tasksCount
63282
- });
63283
- await fs42.promises.writeFile(markerPath, marker, "utf8");
63284
- } catch {}
63285
- return {
63286
- success: true,
63287
- message: "Plan saved successfully",
63288
- plan_path: path53.join(dir, ".swarm", "plan.json"),
63289
- phases_count: plan.phases.length,
63290
- tasks_count: tasksCount
63291
- };
64213
+ };
64214
+ } finally {
64215
+ releaseLock(dir, planFilePath, lockTaskId);
64216
+ }
63292
64217
  } catch (error93) {
63293
64218
  return {
63294
64219
  success: false,
@@ -63323,8 +64248,8 @@ var save_plan = createSwarmTool({
63323
64248
  // src/tools/sbom-generate.ts
63324
64249
  init_dist();
63325
64250
  init_manager();
63326
- import * as fs43 from "fs";
63327
- import * as path54 from "path";
64251
+ import * as fs45 from "fs";
64252
+ import * as path56 from "path";
63328
64253
 
63329
64254
  // src/sbom/detectors/index.ts
63330
64255
  init_utils();
@@ -64170,9 +65095,9 @@ function findManifestFiles(rootDir) {
64170
65095
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
64171
65096
  function searchDir(dir) {
64172
65097
  try {
64173
- const entries = fs43.readdirSync(dir, { withFileTypes: true });
65098
+ const entries = fs45.readdirSync(dir, { withFileTypes: true });
64174
65099
  for (const entry of entries) {
64175
- const fullPath = path54.join(dir, entry.name);
65100
+ const fullPath = path56.join(dir, entry.name);
64176
65101
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
64177
65102
  continue;
64178
65103
  }
@@ -64181,7 +65106,7 @@ function findManifestFiles(rootDir) {
64181
65106
  } else if (entry.isFile()) {
64182
65107
  for (const pattern of patterns) {
64183
65108
  if (simpleGlobToRegex(pattern).test(entry.name)) {
64184
- manifestFiles.push(path54.relative(rootDir, fullPath));
65109
+ manifestFiles.push(path56.relative(rootDir, fullPath));
64185
65110
  break;
64186
65111
  }
64187
65112
  }
@@ -64197,13 +65122,13 @@ function findManifestFilesInDirs(directories, workingDir) {
64197
65122
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
64198
65123
  for (const dir of directories) {
64199
65124
  try {
64200
- const entries = fs43.readdirSync(dir, { withFileTypes: true });
65125
+ const entries = fs45.readdirSync(dir, { withFileTypes: true });
64201
65126
  for (const entry of entries) {
64202
- const fullPath = path54.join(dir, entry.name);
65127
+ const fullPath = path56.join(dir, entry.name);
64203
65128
  if (entry.isFile()) {
64204
65129
  for (const pattern of patterns) {
64205
65130
  if (simpleGlobToRegex(pattern).test(entry.name)) {
64206
- found.push(path54.relative(workingDir, fullPath));
65131
+ found.push(path56.relative(workingDir, fullPath));
64207
65132
  break;
64208
65133
  }
64209
65134
  }
@@ -64216,11 +65141,11 @@ function findManifestFilesInDirs(directories, workingDir) {
64216
65141
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
64217
65142
  const dirs = new Set;
64218
65143
  for (const file3 of changedFiles) {
64219
- let currentDir = path54.dirname(file3);
65144
+ let currentDir = path56.dirname(file3);
64220
65145
  while (true) {
64221
- if (currentDir && currentDir !== "." && currentDir !== path54.sep) {
64222
- dirs.add(path54.join(workingDir, currentDir));
64223
- const parent = path54.dirname(currentDir);
65146
+ if (currentDir && currentDir !== "." && currentDir !== path56.sep) {
65147
+ dirs.add(path56.join(workingDir, currentDir));
65148
+ const parent = path56.dirname(currentDir);
64224
65149
  if (parent === currentDir)
64225
65150
  break;
64226
65151
  currentDir = parent;
@@ -64234,7 +65159,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
64234
65159
  }
64235
65160
  function ensureOutputDir(outputDir) {
64236
65161
  try {
64237
- fs43.mkdirSync(outputDir, { recursive: true });
65162
+ fs45.mkdirSync(outputDir, { recursive: true });
64238
65163
  } catch (error93) {
64239
65164
  if (!error93 || error93.code !== "EEXIST") {
64240
65165
  throw error93;
@@ -64304,7 +65229,7 @@ var sbom_generate = createSwarmTool({
64304
65229
  const changedFiles = obj.changed_files;
64305
65230
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
64306
65231
  const workingDir = directory;
64307
- const outputDir = path54.isAbsolute(relativeOutputDir) ? relativeOutputDir : path54.join(workingDir, relativeOutputDir);
65232
+ const outputDir = path56.isAbsolute(relativeOutputDir) ? relativeOutputDir : path56.join(workingDir, relativeOutputDir);
64308
65233
  let manifestFiles = [];
64309
65234
  if (scope === "all") {
64310
65235
  manifestFiles = findManifestFiles(workingDir);
@@ -64327,11 +65252,11 @@ var sbom_generate = createSwarmTool({
64327
65252
  const processedFiles = [];
64328
65253
  for (const manifestFile of manifestFiles) {
64329
65254
  try {
64330
- const fullPath = path54.isAbsolute(manifestFile) ? manifestFile : path54.join(workingDir, manifestFile);
64331
- if (!fs43.existsSync(fullPath)) {
65255
+ const fullPath = path56.isAbsolute(manifestFile) ? manifestFile : path56.join(workingDir, manifestFile);
65256
+ if (!fs45.existsSync(fullPath)) {
64332
65257
  continue;
64333
65258
  }
64334
- const content = fs43.readFileSync(fullPath, "utf-8");
65259
+ const content = fs45.readFileSync(fullPath, "utf-8");
64335
65260
  const components = detectComponents(manifestFile, content);
64336
65261
  processedFiles.push(manifestFile);
64337
65262
  if (components.length > 0) {
@@ -64344,8 +65269,8 @@ var sbom_generate = createSwarmTool({
64344
65269
  const bom = generateCycloneDX(allComponents);
64345
65270
  const bomJson = serializeCycloneDX(bom);
64346
65271
  const filename = generateSbomFilename();
64347
- const outputPath = path54.join(outputDir, filename);
64348
- fs43.writeFileSync(outputPath, bomJson, "utf-8");
65272
+ const outputPath = path56.join(outputDir, filename);
65273
+ fs45.writeFileSync(outputPath, bomJson, "utf-8");
64349
65274
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
64350
65275
  try {
64351
65276
  const timestamp = new Date().toISOString();
@@ -64387,8 +65312,8 @@ var sbom_generate = createSwarmTool({
64387
65312
  // src/tools/schema-drift.ts
64388
65313
  init_dist();
64389
65314
  init_create_tool();
64390
- import * as fs44 from "fs";
64391
- import * as path55 from "path";
65315
+ import * as fs46 from "fs";
65316
+ import * as path57 from "path";
64392
65317
  var SPEC_CANDIDATES = [
64393
65318
  "openapi.json",
64394
65319
  "openapi.yaml",
@@ -64420,28 +65345,28 @@ function normalizePath2(p) {
64420
65345
  }
64421
65346
  function discoverSpecFile(cwd, specFileArg) {
64422
65347
  if (specFileArg) {
64423
- const resolvedPath = path55.resolve(cwd, specFileArg);
64424
- const normalizedCwd = cwd.endsWith(path55.sep) ? cwd : cwd + path55.sep;
65348
+ const resolvedPath = path57.resolve(cwd, specFileArg);
65349
+ const normalizedCwd = cwd.endsWith(path57.sep) ? cwd : cwd + path57.sep;
64425
65350
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
64426
65351
  throw new Error("Invalid spec_file: path traversal detected");
64427
65352
  }
64428
- const ext = path55.extname(resolvedPath).toLowerCase();
65353
+ const ext = path57.extname(resolvedPath).toLowerCase();
64429
65354
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
64430
65355
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
64431
65356
  }
64432
- const stats = fs44.statSync(resolvedPath);
65357
+ const stats = fs46.statSync(resolvedPath);
64433
65358
  if (stats.size > MAX_SPEC_SIZE) {
64434
65359
  throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
64435
65360
  }
64436
- if (!fs44.existsSync(resolvedPath)) {
65361
+ if (!fs46.existsSync(resolvedPath)) {
64437
65362
  throw new Error(`Spec file not found: ${resolvedPath}`);
64438
65363
  }
64439
65364
  return resolvedPath;
64440
65365
  }
64441
65366
  for (const candidate of SPEC_CANDIDATES) {
64442
- const candidatePath = path55.resolve(cwd, candidate);
64443
- if (fs44.existsSync(candidatePath)) {
64444
- const stats = fs44.statSync(candidatePath);
65367
+ const candidatePath = path57.resolve(cwd, candidate);
65368
+ if (fs46.existsSync(candidatePath)) {
65369
+ const stats = fs46.statSync(candidatePath);
64445
65370
  if (stats.size <= MAX_SPEC_SIZE) {
64446
65371
  return candidatePath;
64447
65372
  }
@@ -64450,8 +65375,8 @@ function discoverSpecFile(cwd, specFileArg) {
64450
65375
  return null;
64451
65376
  }
64452
65377
  function parseSpec(specFile) {
64453
- const content = fs44.readFileSync(specFile, "utf-8");
64454
- const ext = path55.extname(specFile).toLowerCase();
65378
+ const content = fs46.readFileSync(specFile, "utf-8");
65379
+ const ext = path57.extname(specFile).toLowerCase();
64455
65380
  if (ext === ".json") {
64456
65381
  return parseJsonSpec(content);
64457
65382
  }
@@ -64522,12 +65447,12 @@ function extractRoutes(cwd) {
64522
65447
  function walkDir(dir) {
64523
65448
  let entries;
64524
65449
  try {
64525
- entries = fs44.readdirSync(dir, { withFileTypes: true });
65450
+ entries = fs46.readdirSync(dir, { withFileTypes: true });
64526
65451
  } catch {
64527
65452
  return;
64528
65453
  }
64529
65454
  for (const entry of entries) {
64530
- const fullPath = path55.join(dir, entry.name);
65455
+ const fullPath = path57.join(dir, entry.name);
64531
65456
  if (entry.isSymbolicLink()) {
64532
65457
  continue;
64533
65458
  }
@@ -64537,7 +65462,7 @@ function extractRoutes(cwd) {
64537
65462
  }
64538
65463
  walkDir(fullPath);
64539
65464
  } else if (entry.isFile()) {
64540
- const ext = path55.extname(entry.name).toLowerCase();
65465
+ const ext = path57.extname(entry.name).toLowerCase();
64541
65466
  const baseName = entry.name.toLowerCase();
64542
65467
  if (![".ts", ".js", ".mjs"].includes(ext)) {
64543
65468
  continue;
@@ -64555,7 +65480,7 @@ function extractRoutes(cwd) {
64555
65480
  }
64556
65481
  function extractRoutesFromFile(filePath) {
64557
65482
  const routes = [];
64558
- const content = fs44.readFileSync(filePath, "utf-8");
65483
+ const content = fs46.readFileSync(filePath, "utf-8");
64559
65484
  const lines = content.split(/\r?\n/);
64560
65485
  const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
64561
65486
  const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
@@ -64706,8 +65631,8 @@ init_secretscan();
64706
65631
  // src/tools/symbols.ts
64707
65632
  init_tool();
64708
65633
  init_create_tool();
64709
- import * as fs45 from "fs";
64710
- import * as path56 from "path";
65634
+ import * as fs47 from "fs";
65635
+ import * as path58 from "path";
64711
65636
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
64712
65637
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
64713
65638
  function containsControlCharacters(str) {
@@ -64736,11 +65661,11 @@ function containsWindowsAttacks(str) {
64736
65661
  }
64737
65662
  function isPathInWorkspace(filePath, workspace) {
64738
65663
  try {
64739
- const resolvedPath = path56.resolve(workspace, filePath);
64740
- const realWorkspace = fs45.realpathSync(workspace);
64741
- const realResolvedPath = fs45.realpathSync(resolvedPath);
64742
- const relativePath = path56.relative(realWorkspace, realResolvedPath);
64743
- if (relativePath.startsWith("..") || path56.isAbsolute(relativePath)) {
65664
+ const resolvedPath = path58.resolve(workspace, filePath);
65665
+ const realWorkspace = fs47.realpathSync(workspace);
65666
+ const realResolvedPath = fs47.realpathSync(resolvedPath);
65667
+ const relativePath = path58.relative(realWorkspace, realResolvedPath);
65668
+ if (relativePath.startsWith("..") || path58.isAbsolute(relativePath)) {
64744
65669
  return false;
64745
65670
  }
64746
65671
  return true;
@@ -64752,17 +65677,17 @@ function validatePathForRead(filePath, workspace) {
64752
65677
  return isPathInWorkspace(filePath, workspace);
64753
65678
  }
64754
65679
  function extractTSSymbols(filePath, cwd) {
64755
- const fullPath = path56.join(cwd, filePath);
65680
+ const fullPath = path58.join(cwd, filePath);
64756
65681
  if (!validatePathForRead(fullPath, cwd)) {
64757
65682
  return [];
64758
65683
  }
64759
65684
  let content;
64760
65685
  try {
64761
- const stats = fs45.statSync(fullPath);
65686
+ const stats = fs47.statSync(fullPath);
64762
65687
  if (stats.size > MAX_FILE_SIZE_BYTES7) {
64763
65688
  throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
64764
65689
  }
64765
- content = fs45.readFileSync(fullPath, "utf-8");
65690
+ content = fs47.readFileSync(fullPath, "utf-8");
64766
65691
  } catch {
64767
65692
  return [];
64768
65693
  }
@@ -64904,17 +65829,17 @@ function extractTSSymbols(filePath, cwd) {
64904
65829
  });
64905
65830
  }
64906
65831
  function extractPythonSymbols(filePath, cwd) {
64907
- const fullPath = path56.join(cwd, filePath);
65832
+ const fullPath = path58.join(cwd, filePath);
64908
65833
  if (!validatePathForRead(fullPath, cwd)) {
64909
65834
  return [];
64910
65835
  }
64911
65836
  let content;
64912
65837
  try {
64913
- const stats = fs45.statSync(fullPath);
65838
+ const stats = fs47.statSync(fullPath);
64914
65839
  if (stats.size > MAX_FILE_SIZE_BYTES7) {
64915
65840
  throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
64916
65841
  }
64917
- content = fs45.readFileSync(fullPath, "utf-8");
65842
+ content = fs47.readFileSync(fullPath, "utf-8");
64918
65843
  } catch {
64919
65844
  return [];
64920
65845
  }
@@ -64987,7 +65912,7 @@ var symbols = createSwarmTool({
64987
65912
  }, null, 2);
64988
65913
  }
64989
65914
  const cwd = directory;
64990
- const ext = path56.extname(file3);
65915
+ const ext = path58.extname(file3);
64991
65916
  if (containsControlCharacters(file3)) {
64992
65917
  return JSON.stringify({
64993
65918
  file: file3,
@@ -65058,8 +65983,8 @@ init_test_runner();
65058
65983
  init_dist();
65059
65984
  init_utils();
65060
65985
  init_create_tool();
65061
- import * as fs46 from "fs";
65062
- import * as path57 from "path";
65986
+ import * as fs48 from "fs";
65987
+ import * as path59 from "path";
65063
65988
  var MAX_TEXT_LENGTH = 200;
65064
65989
  var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
65065
65990
  var SUPPORTED_EXTENSIONS2 = new Set([
@@ -65130,9 +66055,9 @@ function validatePathsInput(paths, cwd) {
65130
66055
  return { error: "paths contains path traversal", resolvedPath: null };
65131
66056
  }
65132
66057
  try {
65133
- const resolvedPath = path57.resolve(paths);
65134
- const normalizedCwd = path57.resolve(cwd);
65135
- const normalizedResolved = path57.resolve(resolvedPath);
66058
+ const resolvedPath = path59.resolve(paths);
66059
+ const normalizedCwd = path59.resolve(cwd);
66060
+ const normalizedResolved = path59.resolve(resolvedPath);
65136
66061
  if (!normalizedResolved.startsWith(normalizedCwd)) {
65137
66062
  return {
65138
66063
  error: "paths must be within the current working directory",
@@ -65148,13 +66073,13 @@ function validatePathsInput(paths, cwd) {
65148
66073
  }
65149
66074
  }
65150
66075
  function isSupportedExtension(filePath) {
65151
- const ext = path57.extname(filePath).toLowerCase();
66076
+ const ext = path59.extname(filePath).toLowerCase();
65152
66077
  return SUPPORTED_EXTENSIONS2.has(ext);
65153
66078
  }
65154
66079
  function findSourceFiles2(dir, files = []) {
65155
66080
  let entries;
65156
66081
  try {
65157
- entries = fs46.readdirSync(dir);
66082
+ entries = fs48.readdirSync(dir);
65158
66083
  } catch {
65159
66084
  return files;
65160
66085
  }
@@ -65163,10 +66088,10 @@ function findSourceFiles2(dir, files = []) {
65163
66088
  if (SKIP_DIRECTORIES4.has(entry)) {
65164
66089
  continue;
65165
66090
  }
65166
- const fullPath = path57.join(dir, entry);
66091
+ const fullPath = path59.join(dir, entry);
65167
66092
  let stat2;
65168
66093
  try {
65169
- stat2 = fs46.statSync(fullPath);
66094
+ stat2 = fs48.statSync(fullPath);
65170
66095
  } catch {
65171
66096
  continue;
65172
66097
  }
@@ -65259,7 +66184,7 @@ var todo_extract = createSwarmTool({
65259
66184
  return JSON.stringify(errorResult, null, 2);
65260
66185
  }
65261
66186
  const scanPath = resolvedPath;
65262
- if (!fs46.existsSync(scanPath)) {
66187
+ if (!fs48.existsSync(scanPath)) {
65263
66188
  const errorResult = {
65264
66189
  error: `path not found: ${pathsInput}`,
65265
66190
  total: 0,
@@ -65269,13 +66194,13 @@ var todo_extract = createSwarmTool({
65269
66194
  return JSON.stringify(errorResult, null, 2);
65270
66195
  }
65271
66196
  const filesToScan = [];
65272
- const stat2 = fs46.statSync(scanPath);
66197
+ const stat2 = fs48.statSync(scanPath);
65273
66198
  if (stat2.isFile()) {
65274
66199
  if (isSupportedExtension(scanPath)) {
65275
66200
  filesToScan.push(scanPath);
65276
66201
  } else {
65277
66202
  const errorResult = {
65278
- error: `unsupported file extension: ${path57.extname(scanPath)}`,
66203
+ error: `unsupported file extension: ${path59.extname(scanPath)}`,
65279
66204
  total: 0,
65280
66205
  byPriority: { high: 0, medium: 0, low: 0 },
65281
66206
  entries: []
@@ -65288,11 +66213,11 @@ var todo_extract = createSwarmTool({
65288
66213
  const allEntries = [];
65289
66214
  for (const filePath of filesToScan) {
65290
66215
  try {
65291
- const fileStat = fs46.statSync(filePath);
66216
+ const fileStat = fs48.statSync(filePath);
65292
66217
  if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
65293
66218
  continue;
65294
66219
  }
65295
- const content = fs46.readFileSync(filePath, "utf-8");
66220
+ const content = fs48.readFileSync(filePath, "utf-8");
65296
66221
  const entries = parseTodoComments(content, filePath, tagsSet);
65297
66222
  allEntries.push(...entries);
65298
66223
  } catch {}
@@ -65321,18 +66246,18 @@ var todo_extract = createSwarmTool({
65321
66246
  init_tool();
65322
66247
  init_schema();
65323
66248
  init_gate_evidence();
65324
- import * as fs48 from "fs";
65325
- import * as path59 from "path";
66249
+ import * as fs50 from "fs";
66250
+ import * as path61 from "path";
65326
66251
 
65327
66252
  // src/hooks/diff-scope.ts
65328
- import * as fs47 from "fs";
65329
- import * as path58 from "path";
66253
+ import * as fs49 from "fs";
66254
+ import * as path60 from "path";
65330
66255
  function getDeclaredScope(taskId, directory) {
65331
66256
  try {
65332
- const planPath = path58.join(directory, ".swarm", "plan.json");
65333
- if (!fs47.existsSync(planPath))
66257
+ const planPath = path60.join(directory, ".swarm", "plan.json");
66258
+ if (!fs49.existsSync(planPath))
65334
66259
  return null;
65335
- const raw = fs47.readFileSync(planPath, "utf-8");
66260
+ const raw = fs49.readFileSync(planPath, "utf-8");
65336
66261
  const plan = JSON.parse(raw);
65337
66262
  for (const phase of plan.phases ?? []) {
65338
66263
  for (const task of phase.tasks ?? []) {
@@ -65445,7 +66370,7 @@ var TIER_3_PATTERNS = [
65445
66370
  ];
65446
66371
  function matchesTier3Pattern(files) {
65447
66372
  for (const file3 of files) {
65448
- const fileName = path59.basename(file3);
66373
+ const fileName = path61.basename(file3);
65449
66374
  for (const pattern of TIER_3_PATTERNS) {
65450
66375
  if (pattern.test(fileName)) {
65451
66376
  return true;
@@ -65459,8 +66384,8 @@ function checkReviewerGate(taskId, workingDirectory) {
65459
66384
  if (hasActiveTurboMode()) {
65460
66385
  const resolvedDir2 = workingDirectory;
65461
66386
  try {
65462
- const planPath = path59.join(resolvedDir2, ".swarm", "plan.json");
65463
- const planRaw = fs48.readFileSync(planPath, "utf-8");
66387
+ const planPath = path61.join(resolvedDir2, ".swarm", "plan.json");
66388
+ const planRaw = fs50.readFileSync(planPath, "utf-8");
65464
66389
  const plan = JSON.parse(planRaw);
65465
66390
  for (const planPhase of plan.phases ?? []) {
65466
66391
  for (const task of planPhase.tasks ?? []) {
@@ -65526,8 +66451,8 @@ function checkReviewerGate(taskId, workingDirectory) {
65526
66451
  }
65527
66452
  try {
65528
66453
  const resolvedDir2 = workingDirectory;
65529
- const planPath = path59.join(resolvedDir2, ".swarm", "plan.json");
65530
- const planRaw = fs48.readFileSync(planPath, "utf-8");
66454
+ const planPath = path61.join(resolvedDir2, ".swarm", "plan.json");
66455
+ const planRaw = fs50.readFileSync(planPath, "utf-8");
65531
66456
  const plan = JSON.parse(planRaw);
65532
66457
  for (const planPhase of plan.phases ?? []) {
65533
66458
  for (const task of planPhase.tasks ?? []) {
@@ -65709,8 +66634,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
65709
66634
  };
65710
66635
  }
65711
66636
  }
65712
- normalizedDir = path59.normalize(args2.working_directory);
65713
- const pathParts = normalizedDir.split(path59.sep);
66637
+ normalizedDir = path61.normalize(args2.working_directory);
66638
+ const pathParts = normalizedDir.split(path61.sep);
65714
66639
  if (pathParts.includes("..")) {
65715
66640
  return {
65716
66641
  success: false,
@@ -65720,11 +66645,11 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
65720
66645
  ]
65721
66646
  };
65722
66647
  }
65723
- const resolvedDir = path59.resolve(normalizedDir);
66648
+ const resolvedDir = path61.resolve(normalizedDir);
65724
66649
  try {
65725
- const realPath = fs48.realpathSync(resolvedDir);
65726
- const planPath = path59.join(realPath, ".swarm", "plan.json");
65727
- if (!fs48.existsSync(planPath)) {
66650
+ const realPath = fs50.realpathSync(resolvedDir);
66651
+ const planPath = path61.join(realPath, ".swarm", "plan.json");
66652
+ if (!fs50.existsSync(planPath)) {
65728
66653
  return {
65729
66654
  success: false,
65730
66655
  message: `Invalid working_directory: plan not found in "${realPath}"`,
@@ -65945,10 +66870,11 @@ var OpenCodeSwarm = async (ctx) => {
65945
66870
  let statusArtifact;
65946
66871
  if (automationConfig.mode !== "manual") {
65947
66872
  automationManager = createAutomationManager(automationConfig);
66873
+ automationManager.start();
65948
66874
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
65949
66875
  preflightTriggerManager = new PTM(automationConfig);
65950
66876
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
65951
- const swarmDir = path60.resolve(ctx.directory, ".swarm");
66877
+ const swarmDir = path62.resolve(ctx.directory, ".swarm");
65952
66878
  statusArtifact = new ASA(swarmDir);
65953
66879
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
65954
66880
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -65992,9 +66918,16 @@ var OpenCodeSwarm = async (ctx) => {
65992
66918
  });
65993
66919
  }
65994
66920
  }
66921
+ const cleanupAutomation = () => {
66922
+ automationManager?.stop();
66923
+ };
66924
+ process.on("exit", cleanupAutomation);
66925
+ process.on("SIGINT", cleanupAutomation);
66926
+ process.on("SIGTERM", cleanupAutomation);
65995
66927
  log("Automation framework initialized", {
65996
66928
  mode: automationConfig.mode,
65997
66929
  enabled: automationManager?.isEnabled(),
66930
+ running: automationManager?.isActive(),
65998
66931
  preflightEnabled: preflightTriggerManager?.isEnabled()
65999
66932
  });
66000
66933
  }
@@ -66293,6 +67226,37 @@ var OpenCodeSwarm = async (ctx) => {
66293
67226
  await safeHook(delegationGateHooks.toolAfter)(input, output);
66294
67227
  if (_dbg)
66295
67228
  console.error(`[DIAG] toolAfter delegationGate done tool=${_toolName}`);
67229
+ if (isTaskTool && typeof output.output === "string") {
67230
+ try {
67231
+ const adversarialMatches = detectAdversarialPatterns(output.output);
67232
+ if (adversarialMatches.length > 0) {
67233
+ const sessionId = input.sessionID;
67234
+ const session = swarmState.agentSessions.get(sessionId);
67235
+ if (session) {
67236
+ session.pendingAdvisoryMessages ??= [];
67237
+ session.pendingAdvisoryMessages.push(`ADVERSARIAL PATTERN DETECTED: ${adversarialMatches.map((p) => p.pattern).join(", ")}. Review agent output for potential prompt injection or gate bypass.`);
67238
+ }
67239
+ if ("adversarialPatternDetected" in telemetry) {
67240
+ telemetry.adversarialPatternDetected(input.sessionID, adversarialMatches);
67241
+ }
67242
+ }
67243
+ } catch {}
67244
+ }
67245
+ try {
67246
+ recordToolCall(normalizedTool, input.args);
67247
+ } catch {}
67248
+ try {
67249
+ const spiralMatch = await detectDebuggingSpiral(ctx.directory);
67250
+ if (spiralMatch) {
67251
+ const taskId = swarmState.agentSessions.get(input.sessionID)?.currentTaskId ?? "unknown";
67252
+ const spiralResult = await handleDebuggingSpiral(spiralMatch, taskId, ctx.directory);
67253
+ const session = swarmState.agentSessions.get(input.sessionID);
67254
+ if (session) {
67255
+ session.pendingAdvisoryMessages ??= [];
67256
+ session.pendingAdvisoryMessages.push(spiralResult.message);
67257
+ }
67258
+ }
67259
+ } catch {}
66296
67260
  if (knowledgeCuratorHook)
66297
67261
  await safeHook(knowledgeCuratorHook)(input, output);
66298
67262
  if (hivePromoterHook)
@@ -66314,8 +67278,9 @@ var OpenCodeSwarm = async (ctx) => {
66314
67278
  await slopDetectorHook.toolAfter(input, output);
66315
67279
  if (incrementalVerifyHook)
66316
67280
  await incrementalVerifyHook.toolAfter(input, output);
66317
- if (compactionServiceHook)
66318
- await compactionServiceHook.toolAfter(input, output);
67281
+ }
67282
+ if (execMode !== "fast" && compactionServiceHook) {
67283
+ await compactionServiceHook.toolAfter(input, output);
66319
67284
  }
66320
67285
  const toolOutputConfig = config3.tool_output;
66321
67286
  if (toolOutputConfig && toolOutputConfig.truncation_enabled !== false && typeof output.output === "string") {