opencode-swarm 6.44.3 → 6.45.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
@@ -70,7 +70,10 @@ var init_tool_names = __esm(() => {
70
70
  "knowledgeAdd",
71
71
  "knowledgeRecall",
72
72
  "knowledgeRemove",
73
- "co_change_analyzer"
73
+ "co_change_analyzer",
74
+ "search",
75
+ "batch_symbols",
76
+ "suggest_patch"
74
77
  ];
75
78
  TOOL_NAME_SET = new Set(TOOL_NAMES);
76
79
  });
@@ -153,6 +156,8 @@ var init_constants = __esm(() => {
153
156
  "quality_budget",
154
157
  "retrieve_summary",
155
158
  "save_plan",
159
+ "search",
160
+ "batch_symbols",
156
161
  "schema_drift",
157
162
  "secretscan",
158
163
  "symbols",
@@ -174,7 +179,8 @@ var init_constants = __esm(() => {
174
179
  "knowledgeAdd",
175
180
  "knowledgeRecall",
176
181
  "knowledgeRemove",
177
- "co_change_analyzer"
182
+ "co_change_analyzer",
183
+ "suggest_patch"
178
184
  ],
179
185
  explorer: [
180
186
  "complexity_hotspots",
@@ -184,6 +190,8 @@ var init_constants = __esm(() => {
184
190
  "imports",
185
191
  "retrieve_summary",
186
192
  "schema_drift",
193
+ "search",
194
+ "batch_symbols",
187
195
  "symbols",
188
196
  "todo_extract",
189
197
  "doc_scan",
@@ -196,6 +204,7 @@ var init_constants = __esm(() => {
196
204
  "symbols",
197
205
  "extract_code_blocks",
198
206
  "retrieve_summary",
207
+ "search",
199
208
  "build_check",
200
209
  "syntax_check",
201
210
  "knowledgeAdd",
@@ -211,7 +220,8 @@ var init_constants = __esm(() => {
211
220
  "complexity_hotspots",
212
221
  "pkg_audit",
213
222
  "build_check",
214
- "syntax_check"
223
+ "syntax_check",
224
+ "search"
215
225
  ],
216
226
  sme: [
217
227
  "complexity_hotspots",
@@ -237,7 +247,10 @@ var init_constants = __esm(() => {
237
247
  "test_runner",
238
248
  "sast_scan",
239
249
  "placeholder_scan",
240
- "knowledgeRecall"
250
+ "knowledgeRecall",
251
+ "search",
252
+ "batch_symbols",
253
+ "suggest_patch"
241
254
  ],
242
255
  critic: [
243
256
  "complexity_hotspots",
@@ -333,7 +346,10 @@ var init_constants = __esm(() => {
333
346
  detect_domains: "detect which SME domains are relevant for a given text",
334
347
  extract_code_blocks: "extract code blocks from text content and save them to files",
335
348
  gitingest: "fetch a GitHub repository full content via gitingest.com",
336
- retrieve_summary: "retrieve the full content of a stored tool output summary"
349
+ retrieve_summary: "retrieve the full content of a stored tool output summary",
350
+ search: "Workspace-scoped ripgrep-style text search with structured JSON output. Supports literal and regex modes, glob filtering, and result limits. NOTE: This is text search, not structural AST search \u2014 use symbols and imports tools for structural queries.",
351
+ batch_symbols: "Batched symbol extraction across multiple files. Returns per-file symbol summaries with isolated error handling.",
352
+ suggest_patch: "Reviewer-safe structured patch suggestion tool. Produces context-anchored patch artifacts without file modification. Returns structured diagnostics on context mismatch."
337
353
  };
338
354
  for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
339
355
  const invalidTools = tools.filter((tool) => !TOOL_NAME_SET.has(tool));
@@ -39385,24 +39401,24 @@ var init_tree_sitter = __esm(() => {
39385
39401
  descendantsOfType(types, startPosition = ZERO_POINT, endPosition = ZERO_POINT) {
39386
39402
  if (!Array.isArray(types))
39387
39403
  types = [types];
39388
- const symbols = [];
39404
+ const symbols2 = [];
39389
39405
  const typesBySymbol = this.tree.language.types;
39390
39406
  for (const node_type of types) {
39391
39407
  if (node_type == "ERROR") {
39392
- symbols.push(65535);
39408
+ symbols2.push(65535);
39393
39409
  }
39394
39410
  }
39395
39411
  for (let i2 = 0, n = typesBySymbol.length;i2 < n; i2++) {
39396
39412
  if (types.includes(typesBySymbol[i2])) {
39397
- symbols.push(i2);
39413
+ symbols2.push(i2);
39398
39414
  }
39399
39415
  }
39400
- const symbolsAddress = C._malloc(SIZE_OF_INT * symbols.length);
39401
- for (let i2 = 0, n = symbols.length;i2 < n; i2++) {
39402
- C.setValue(symbolsAddress + i2 * SIZE_OF_INT, symbols[i2], "i32");
39416
+ const symbolsAddress = C._malloc(SIZE_OF_INT * symbols2.length);
39417
+ for (let i2 = 0, n = symbols2.length;i2 < n; i2++) {
39418
+ C.setValue(symbolsAddress + i2 * SIZE_OF_INT, symbols2[i2], "i32");
39403
39419
  }
39404
39420
  marshalNode(this);
39405
- C._ts_node_descendants_of_type_wasm(this.tree[0], symbolsAddress, symbols.length, startPosition.row, startPosition.column, endPosition.row, endPosition.column);
39421
+ C._ts_node_descendants_of_type_wasm(this.tree[0], symbolsAddress, symbols2.length, startPosition.row, startPosition.column, endPosition.row, endPosition.column);
39406
39422
  const descendantCount = C.getValue(TRANSFER_BUFFER, "i32");
39407
39423
  const descendantAddress = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, "i32");
39408
39424
  const result = new Array(descendantCount);
@@ -40018,8 +40034,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
40018
40034
  var moduleRtn;
40019
40035
  var Module = moduleArg;
40020
40036
  var readyPromiseResolve, readyPromiseReject;
40021
- var readyPromise = new Promise((resolve17, reject) => {
40022
- readyPromiseResolve = resolve17;
40037
+ var readyPromise = new Promise((resolve19, reject) => {
40038
+ readyPromiseResolve = resolve19;
40023
40039
  readyPromiseReject = reject;
40024
40040
  });
40025
40041
  var ENVIRONMENT_IS_WEB = typeof window == "object";
@@ -40041,11 +40057,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
40041
40057
  throw toThrow;
40042
40058
  }, "quit_");
40043
40059
  var scriptDirectory = "";
40044
- function locateFile(path53) {
40060
+ function locateFile(path55) {
40045
40061
  if (Module["locateFile"]) {
40046
- return Module["locateFile"](path53, scriptDirectory);
40062
+ return Module["locateFile"](path55, scriptDirectory);
40047
40063
  }
40048
- return scriptDirectory + path53;
40064
+ return scriptDirectory + path55;
40049
40065
  }
40050
40066
  __name(locateFile, "locateFile");
40051
40067
  var readAsync, readBinary;
@@ -40099,13 +40115,13 @@ ${JSON.stringify(symbolNames, null, 2)}`);
40099
40115
  }
40100
40116
  readAsync = /* @__PURE__ */ __name(async (url3) => {
40101
40117
  if (isFileURI(url3)) {
40102
- return new Promise((resolve17, reject) => {
40118
+ return new Promise((resolve19, reject) => {
40103
40119
  var xhr = new XMLHttpRequest;
40104
40120
  xhr.open("GET", url3, true);
40105
40121
  xhr.responseType = "arraybuffer";
40106
40122
  xhr.onload = () => {
40107
40123
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
40108
- resolve17(xhr.response);
40124
+ resolve19(xhr.response);
40109
40125
  return;
40110
40126
  }
40111
40127
  reject(xhr.status);
@@ -40325,10 +40341,10 @@ ${JSON.stringify(symbolNames, null, 2)}`);
40325
40341
  __name(receiveInstantiationResult, "receiveInstantiationResult");
40326
40342
  var info2 = getWasmImports();
40327
40343
  if (Module["instantiateWasm"]) {
40328
- return new Promise((resolve17, reject) => {
40344
+ return new Promise((resolve19, reject) => {
40329
40345
  Module["instantiateWasm"](info2, (mod, inst) => {
40330
40346
  receiveInstance(mod, inst);
40331
- resolve17(mod.exports);
40347
+ resolve19(mod.exports);
40332
40348
  });
40333
40349
  });
40334
40350
  }
@@ -41785,13 +41801,13 @@ ${JSON.stringify(symbolNames, null, 2)}`);
41785
41801
  });
41786
41802
 
41787
41803
  // src/lang/runtime.ts
41788
- import * as path53 from "path";
41804
+ import * as path55 from "path";
41789
41805
  import { fileURLToPath as fileURLToPath2 } from "url";
41790
41806
  async function initTreeSitter() {
41791
41807
  if (treeSitterInitialized) {
41792
41808
  return;
41793
41809
  }
41794
- const thisDir = path53.dirname(fileURLToPath2(import.meta.url));
41810
+ const thisDir = path55.dirname(fileURLToPath2(import.meta.url));
41795
41811
  const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/lang");
41796
41812
  if (isSource) {
41797
41813
  await Parser.init();
@@ -41799,7 +41815,7 @@ async function initTreeSitter() {
41799
41815
  const grammarsDir = getGrammarsDirAbsolute();
41800
41816
  await Parser.init({
41801
41817
  locateFile(scriptName) {
41802
- return path53.join(grammarsDir, scriptName);
41818
+ return path55.join(grammarsDir, scriptName);
41803
41819
  }
41804
41820
  });
41805
41821
  }
@@ -41820,9 +41836,9 @@ function getWasmFileName(languageId) {
41820
41836
  return `tree-sitter-${sanitized}.wasm`;
41821
41837
  }
41822
41838
  function getGrammarsDirAbsolute() {
41823
- const thisDir = path53.dirname(fileURLToPath2(import.meta.url));
41839
+ const thisDir = path55.dirname(fileURLToPath2(import.meta.url));
41824
41840
  const isSource = thisDir.replace(/\\/g, "/").endsWith("/src/lang");
41825
- return isSource ? path53.join(thisDir, "grammars") : path53.join(thisDir, "lang", "grammars");
41841
+ return isSource ? path55.join(thisDir, "grammars") : path55.join(thisDir, "lang", "grammars");
41826
41842
  }
41827
41843
  async function loadGrammar(languageId) {
41828
41844
  if (typeof languageId !== "string" || languageId.length > 100) {
@@ -41838,9 +41854,9 @@ async function loadGrammar(languageId) {
41838
41854
  await initTreeSitter();
41839
41855
  const parser = new Parser;
41840
41856
  const wasmFileName = getWasmFileName(normalizedId);
41841
- const wasmPath = path53.join(getGrammarsDirAbsolute(), wasmFileName);
41842
- const { existsSync: existsSync32 } = await import("fs");
41843
- if (!existsSync32(wasmPath)) {
41857
+ const wasmPath = path55.join(getGrammarsDirAbsolute(), wasmFileName);
41858
+ const { existsSync: existsSync33 } = await import("fs");
41859
+ if (!existsSync33(wasmPath)) {
41844
41860
  throw new Error(`Grammar file not found for ${languageId}: ${wasmPath}
41845
41861
  Make sure to run 'bun run build' to copy grammar files to dist/lang/grammars/`);
41846
41862
  }
@@ -41885,7 +41901,7 @@ var init_runtime = __esm(() => {
41885
41901
  });
41886
41902
 
41887
41903
  // src/index.ts
41888
- import * as path70 from "path";
41904
+ import * as path73 from "path";
41889
41905
 
41890
41906
  // src/agents/index.ts
41891
41907
  init_config();
@@ -42751,7 +42767,7 @@ OUTPUT: Test file + VERDICT: PASS/FAIL
42751
42767
  TASK: Integration impact analysis
42752
42768
  INPUT: Contract changes detected: [list from diff tool]
42753
42769
  OUTPUT: BREAKING_CHANGES + COMPATIBLE_CHANGES + CONSUMERS_AFFECTED + VERDICT: BREAKING/COMPATIBLE + MIGRATION_NEEDED
42754
- CONSTRAINT: Read-only. grep for imports/usages of changed exports.
42770
+ CONSTRAINT: Read-only. use search to find imports/usages of changed exports.
42755
42771
 
42756
42772
  {{AGENT_PREFIX}}docs
42757
42773
  TASK: Update documentation for Phase 2 changes
@@ -42904,6 +42920,7 @@ For complex tasks, make a second explorer call focused on risk/gap analysis:
42904
42920
  - Existing patterns that the implementation must follow
42905
42921
  After explorer returns:
42906
42922
  - Run \`symbols\` tool on key files identified by explorer to understand public API surfaces
42923
+ - For multi-file module surveys: prefer \`batch_symbols\` over sequential single-file symbols calls
42907
42924
  - Run \`complexity_hotspots\` if not already run in Phase 0 (check context.md for existing analysis). Note modules with recommendation "security_review" or "full_gates" in context.md.
42908
42925
  - Check for project governance files using the \`glob\` tool with patterns \`project-instructions.md\`, \`docs/project-instructions.md\`, \`CONTRIBUTING.md\`, and \`INSTRUCTIONS.md\` (checked in that priority order \u2014 first match wins). If a file is found: read it and extract all MUST (mandatory constraints) and SHOULD (recommended practices) rules. Write the extracted rules as a summary to \`.swarm/context.md\` under a \`## Project Governance\` section \u2014 append if the section already exists, create it if not. If no MUST or SHOULD rules are found in the file, skip writing. If no governance file is found: skip silently. Existing DISCOVER steps are unchanged.
42909
42926
 
@@ -43179,6 +43196,23 @@ Treating pre_check_batch as a substitute for {{AGENT_PREFIX}}reviewer is a PROCE
43179
43196
  IMPORTANT: The regression sweep runs test_runner DIRECTLY (architect calls the tool). Do NOT delegate to test_engineer for this \u2014 the test_engineer's EXECUTION BOUNDARY restricts it to its own test files. The architect has unrestricted test_runner access.
43180
43197
  \u2192 REQUIRED: Print "regression-sweep: [PASS N additional tests | SKIPPED \u2014 no related tests beyond task scope | SKIPPED \u2014 test_runner error | FAIL \u2014 REGRESSION DETECTED in files]"
43181
43198
 
43199
+ 5l-ter. TEST DRIFT CHECK (conditional): Run this step if the change involves any drift-prone area:
43200
+ - Command/CLI behavior changed (shell command wrappers, CLI interfaces)
43201
+ - Parsing or routing logic changed (argument parsing, route matching, file resolution)
43202
+ - User-visible output changed (formatted output, error messages, JSON response structure)
43203
+ - Public contracts or schemas changed (API types, tool argument schemas, return types)
43204
+ - Assertion-heavy areas where output strings are tested (command/help output tests, error message tests)
43205
+ - Helper behavior or lifecycle semantics changed (state machines, lifecycle hooks, initialization)
43206
+
43207
+ If NOT triggered: Print "test-drift: NOT TRIGGERED \u2014 no drift-prone change detected"
43208
+ If TRIGGERED:
43209
+ - Use grep/search to find test files that cover the affected functionality
43210
+ - Run those tests via test_runner with scope:"convention" on the related test files
43211
+ - If any FAIL \u2192 print "test-drift: DRIFT DETECTED in [N] tests" and escalate to reviewer/test_engineer
43212
+ - If all PASS \u2192 print "test-drift: [N] related tests verified"
43213
+ - If no related tests found \u2192 print "test-drift: NO RELATED TESTS FOUND" (not a failure)
43214
+ \u2192 REQUIRED: Print "test-drift: [TRIGGERED | NOT TRIGGERED \u2014 reason]" and "[DRIFT DETECTED in N tests | N related tests verified | NO RELATED TESTS FOUND | NOT TRIGGERED]"
43215
+
43182
43216
  5n. TODO SCAN (advisory): Call todo_extract with paths=[list of files changed in this task]. If any results have priority HIGH \u2192 print "todo-scan: WARN \u2014 N high-priority TODOs in changed files: [list of TODO texts]". If no high-priority results \u2192 print "todo-scan: CLEAN". This is advisory only and does NOT block the pipeline.
43183
43217
  \u2192 REQUIRED: Print "todo-scan: [WARN \u2014 N high-priority TODOs | CLEAN]"
43184
43218
 
@@ -43192,6 +43226,7 @@ PRE-COMMIT RULE \u2014 Before ANY commit or push:
43192
43226
  [ ] Did pre_check_batch run with gates_passed true?
43193
43227
  [ ] Did the diff step run?
43194
43228
  [ ] Did regression-sweep run (or SKIP with no related tests or test_runner error)?
43229
+ [ ] Did test-drift check run (or NOT TRIGGERED)?
43195
43230
 
43196
43231
  If ANY box is unchecked: DO NOT COMMIT. Return to step 5b.
43197
43232
  There is no override. A commit without a completed QA gate is a workflow violation.
@@ -43208,6 +43243,7 @@ PRE-COMMIT RULE \u2014 Before ANY commit or push:
43208
43243
  [GATE] security-reviewer: APPROVED / SKIPPED \u2014 value: ___
43209
43244
  [GATE] test_engineer-verification: PASS \u2014 value: ___
43210
43245
  [GATE] regression-sweep: PASS / SKIPPED \u2014 value: ___
43246
+ [GATE] test-drift: TRIGGERED / NOT TRIGGERED \u2014 value: ___
43211
43247
  {{ADVERSARIAL_TEST_CHECKLIST}}
43212
43248
  [GATE] coverage: \u226570% / soft-skip \u2014 value: ___
43213
43249
 
@@ -43421,22 +43457,22 @@ RULES:
43421
43457
  - Read target file before editing
43422
43458
  - Implement exactly what TASK specifies
43423
43459
  - Respect CONSTRAINT
43424
- - No web searches or documentation lookups \u2014 but DO search the codebase with grep/glob before using any function
43460
+ - No web searches or documentation lookups \u2014 but DO use the search tool for cross-codebase pattern lookup before using any function
43425
43461
  - Verify all import paths exist before using them
43426
43462
 
43427
43463
  ## ANTI-HALLUCINATION PROTOCOL (MANDATORY)
43428
43464
  Before importing ANY function, type, or class from an existing project module:
43429
- 1. Run grep to find the exact export: grep -rn "export.*functionName" src/
43465
+ 1. Run search to find the exact export using the search tool with appropriate query pattern
43430
43466
  2. Read the file that contains the export to verify its signature
43431
43467
  3. Use the EXACT function name and import path you found \u2014 do not guess or abbreviate
43432
43468
 
43433
- If grep returns zero results, the function does not exist. Do NOT:
43469
+ If search returns zero results, the function does not exist. Do NOT:
43434
43470
  - Import it anyway hoping it exists somewhere
43435
43471
  - Create a similar-sounding function name
43436
43472
  - Assume an export exists based on naming conventions
43437
43473
 
43438
43474
  WRONG: import { saveEvidence } from '../evidence/manager' (guessed path)
43439
- RIGHT: [grep first, then] import { saveEvidence } from '../evidence/manager' (verified path)
43475
+ RIGHT: [search first, then] import { saveEvidence } from '../evidence/manager' (verified path)
43440
43476
 
43441
43477
  If available_symbols was provided in your scope declaration, you MUST only call functions from that list when importing from existing project modules. Do not invent function names that are not in the list.
43442
43478
 
@@ -43906,7 +43942,7 @@ INPUT: [focus areas/paths]
43906
43942
  ACTIONS:
43907
43943
  - Scan structure (tree, ls, glob)
43908
43944
  - Read key files (README, configs, entry points)
43909
- - Search patterns (grep)
43945
+ - Search patterns using the search tool
43910
43946
 
43911
43947
  RULES:
43912
43948
  - Be fast: scan broadly, read selectively
@@ -43919,6 +43955,7 @@ When exploring a codebase area, systematically report all four dimensions:
43919
43955
  ### STRUCTURE
43920
43956
  - Entry points and their call chains (max 3 levels deep)
43921
43957
  - Public API surface: exported functions/classes/types with signatures
43958
+ - For multi-file symbol surveys: use batch_symbols to extract symbols from multiple files in one call
43922
43959
  - Internal dependencies: what this module imports and from where
43923
43960
  - External dependencies: third-party packages used
43924
43961
 
@@ -43966,7 +44003,7 @@ Activates when delegated with "Integration impact analysis" or INPUT lists contr
43966
44003
  INPUT: List of contract changes (from diff tool output \u2014 changed exports, signatures, types)
43967
44004
 
43968
44005
  STEPS:
43969
- 1. For each changed export: grep the codebase for imports and usages of that symbol
44006
+ 1. For each changed export: use search to find imports and usages of that symbol
43970
44007
  2. Classify each change: BREAKING (callers must update) or COMPATIBLE (callers unaffected)
43971
44008
  3. List all files that import or use the changed exports
43972
44009
 
@@ -44490,9 +44527,10 @@ Your unique value is catching LOGIC ERRORS, EDGE CASES, and SECURITY FLAWS that
44490
44527
 
44491
44528
  DO (explicitly):
44492
44529
  - READ the changed files yourself \u2014 do not rely on the coder's self-report
44493
- - VERIFY imports exist: if the coder added a new import, grep for the export in the source
44530
+ - VERIFY imports exist: if the coder added a new import, use search to verify the export exists in the source
44494
44531
  - CHECK test files were updated: if the coder changed a function signature, the tests should reflect it
44495
44532
  - VERIFY platform compatibility: path.join() used for all paths, no hardcoded separators
44533
+ - For confirmed issues requiring a concrete fix: use suggest_patch to produce a structured patch artifact for the coder
44496
44534
 
44497
44535
  ## REVIEW REASONING
44498
44536
  For each changed function or method, answer these before formulating issues:
@@ -53344,6 +53382,17 @@ function detectLoop(sessionId, toolName, args2) {
53344
53382
  };
53345
53383
  }
53346
53384
 
53385
+ // src/hooks/normalize-tool-name.ts
53386
+ var NAMESPACE_PREFIX_PATTERN = /^[^:]+[:.]/;
53387
+ function normalizeToolName(toolName) {
53388
+ if (!toolName)
53389
+ return;
53390
+ return toolName.replace(NAMESPACE_PREFIX_PATTERN, "");
53391
+ }
53392
+ function normalizeToolNameLowerCase(toolName) {
53393
+ return toolName.replace(NAMESPACE_PREFIX_PATTERN, "").toLowerCase();
53394
+ }
53395
+
53347
53396
  // src/hooks/guardrails.ts
53348
53397
  var storedInputArgs = new Map;
53349
53398
  var TRANSIENT_MODEL_ERROR_PATTERN = /rate.?limit|429|503|timeout|overloaded|model.?not.?found|temporarily unavailable|server error/i;
@@ -53366,7 +53415,7 @@ function extractPhaseNumber(phaseString) {
53366
53415
  return match ? parseInt(match[1], 10) : 1;
53367
53416
  }
53368
53417
  function isWriteTool(toolName) {
53369
- const normalized = toolName.replace(/^[^:]+[:.]/, "");
53418
+ const normalized = normalizeToolName(toolName);
53370
53419
  return WRITE_TOOL_NAMES.includes(normalized);
53371
53420
  }
53372
53421
  function isArchitect(sessionId) {
@@ -53421,7 +53470,7 @@ function hasTraversalSegments(filePath) {
53421
53470
  return normalized.startsWith("..") || normalized.includes("/../") || normalized.endsWith("/..");
53422
53471
  }
53423
53472
  function isGateTool(toolName) {
53424
- const normalized = toolName.replace(/^[^:]+[:.]/, "");
53473
+ const normalized = normalizeToolName(toolName);
53425
53474
  const gateTools = [
53426
53475
  "diff",
53427
53476
  "syntax_check",
@@ -53437,7 +53486,7 @@ function isGateTool(toolName) {
53437
53486
  return gateTools.includes(normalized);
53438
53487
  }
53439
53488
  function isAgentDelegation(toolName, args2) {
53440
- const normalized = toolName.replace(/^[^:]+[:.]/, "");
53489
+ const normalized = normalizeToolName(toolName);
53441
53490
  if (normalized !== "Task" && normalized !== "task") {
53442
53491
  return { isDelegation: false, targetAgent: null };
53443
53492
  }
@@ -53932,7 +53981,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
53932
53981
  }
53933
53982
  }
53934
53983
  const sessionId = input.sessionID;
53935
- const normalizedToolName = input.tool.replace(/^[^:]+[:.]/, "");
53984
+ const normalizedToolName = normalizeToolName(input.tool);
53936
53985
  if (isWriteTool(normalizedToolName)) {
53937
53986
  toolCallsSinceLastWrite.set(sessionId, 0);
53938
53987
  noOpWarningIssued.delete(sessionId);
@@ -54490,7 +54539,7 @@ function createDelegationGateHook(config3, directory) {
54490
54539
  const toolBefore = async (input, output) => {
54491
54540
  if (!input.sessionID)
54492
54541
  return;
54493
- const normalized = input.tool.replace(/^[^:]+[:.]/, "");
54542
+ const normalized = normalizeToolName(input.tool);
54494
54543
  if (normalized !== "Task" && normalized !== "task")
54495
54544
  return;
54496
54545
  const args2 = output.args;
@@ -54546,7 +54595,7 @@ function createDelegationGateHook(config3, directory) {
54546
54595
  const session = swarmState.agentSessions.get(input.sessionID);
54547
54596
  if (!session)
54548
54597
  return;
54549
- const normalized = input.tool.replace(/^[^:]+[:.]/, "");
54598
+ const normalized = normalizeToolName(input.tool);
54550
54599
  if (normalized === "Task" || normalized === "task") {
54551
54600
  const directArgs = input.args;
54552
54601
  const storedArgs = getStoredInputArgs(input.callID);
@@ -57354,12 +57403,12 @@ var WRITE_TOOL_PATTERNS = [
57354
57403
  "prepend"
57355
57404
  ];
57356
57405
  function isWriteTool2(toolName) {
57357
- const normalized = toolName.replace(/^[^:]+[:.]/, "").toLowerCase();
57406
+ const normalized = normalizeToolNameLowerCase(toolName);
57358
57407
  return WRITE_TOOL_PATTERNS.some((p) => normalized.includes(p));
57359
57408
  }
57360
57409
  var READ_TOOL_PATTERNS = ["read", "cat", "view", "fetch", "get"];
57361
57410
  function isReadTool(toolName) {
57362
- const normalized = toolName.replace(/^[^:]+[:.]/, "").toLowerCase();
57411
+ const normalized = normalizeToolNameLowerCase(toolName);
57363
57412
  return READ_TOOL_PATTERNS.some((p) => normalized.includes(p));
57364
57413
  }
57365
57414
 
@@ -57796,7 +57845,7 @@ function createScopeGuardHook(config3, directory, injectAdvisory) {
57796
57845
  toolBefore: async (input, output) => {
57797
57846
  if (!enabled)
57798
57847
  return;
57799
- const toolName = input.tool.replace(/^[^:]+[:.]/, "");
57848
+ const toolName = normalizeToolName(input.tool);
57800
57849
  if (!WRITE_TOOLS.has(toolName))
57801
57850
  return;
57802
57851
  const sessionId = input.sessionID;
@@ -57859,7 +57908,7 @@ function createSelfReviewHook(config3, injectAdvisory) {
57859
57908
  toolAfter: async (input, output) => {
57860
57909
  if (!enabled)
57861
57910
  return;
57862
- const toolName = input.tool.replace(/^[^:]+[:.]/, "");
57911
+ const toolName = normalizeToolName(input.tool);
57863
57912
  if (toolName !== "update_task_status")
57864
57913
  return;
57865
57914
  const args2 = output.args;
@@ -58359,6 +58408,520 @@ async function loadSnapshot(directory) {
58359
58408
  // src/index.ts
58360
58409
  init_telemetry();
58361
58410
 
58411
+ // src/tools/batch-symbols.ts
58412
+ init_tool();
58413
+ init_create_tool();
58414
+ import * as fs38 from "fs";
58415
+ import * as path49 from "path";
58416
+
58417
+ // src/tools/symbols.ts
58418
+ init_tool();
58419
+ init_create_tool();
58420
+ import * as fs37 from "fs";
58421
+ import * as path48 from "path";
58422
+ var MAX_FILE_SIZE_BYTES2 = 1024 * 1024;
58423
+ var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
58424
+ function containsWindowsAttacks(str) {
58425
+ if (/:[^\\/]/.test(str)) {
58426
+ return true;
58427
+ }
58428
+ const parts2 = str.split(/[/\\]/);
58429
+ for (const part of parts2) {
58430
+ if (WINDOWS_RESERVED_NAMES.test(part)) {
58431
+ return true;
58432
+ }
58433
+ }
58434
+ return false;
58435
+ }
58436
+ function isPathInWorkspace(filePath, workspace) {
58437
+ try {
58438
+ const resolvedPath = path48.resolve(workspace, filePath);
58439
+ const realWorkspace = fs37.realpathSync(workspace);
58440
+ const realResolvedPath = fs37.realpathSync(resolvedPath);
58441
+ const relativePath = path48.relative(realWorkspace, realResolvedPath);
58442
+ if (relativePath.startsWith("..") || path48.isAbsolute(relativePath)) {
58443
+ return false;
58444
+ }
58445
+ return true;
58446
+ } catch {
58447
+ return false;
58448
+ }
58449
+ }
58450
+ function validatePathForRead(filePath, workspace) {
58451
+ return isPathInWorkspace(filePath, workspace);
58452
+ }
58453
+ function extractTSSymbols(filePath, cwd) {
58454
+ const fullPath = path48.join(cwd, filePath);
58455
+ if (!validatePathForRead(fullPath, cwd)) {
58456
+ return [];
58457
+ }
58458
+ let content;
58459
+ try {
58460
+ const stats = fs37.statSync(fullPath);
58461
+ if (stats.size > MAX_FILE_SIZE_BYTES2) {
58462
+ throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES2})`);
58463
+ }
58464
+ content = fs37.readFileSync(fullPath, "utf-8");
58465
+ } catch {
58466
+ return [];
58467
+ }
58468
+ const lines = content.split(`
58469
+ `);
58470
+ const symbols = [];
58471
+ for (let i2 = 0;i2 < lines.length; i2++) {
58472
+ const line = lines[i2];
58473
+ const lineNum = i2 + 1;
58474
+ let jsdoc;
58475
+ if (i2 > 0 && lines[i2 - 1].trim().endsWith("*/")) {
58476
+ const jsdocLines = [];
58477
+ for (let j = i2 - 1;j >= 0; j--) {
58478
+ jsdocLines.unshift(lines[j]);
58479
+ if (lines[j].trim().startsWith("/**"))
58480
+ break;
58481
+ }
58482
+ jsdoc = jsdocLines.join(`
58483
+ `).trim();
58484
+ if (jsdoc.length > 300)
58485
+ jsdoc = `${jsdoc.substring(0, 300)}...`;
58486
+ }
58487
+ const fnMatch = line.match(/^export\s+(?:async\s+)?function\s+(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)(?:\s*:\s*(.+?))?(?:\s*\{|$)/);
58488
+ if (fnMatch) {
58489
+ symbols.push({
58490
+ name: fnMatch[1],
58491
+ kind: "function",
58492
+ exported: true,
58493
+ signature: `function ${fnMatch[1]}${fnMatch[2] || ""}(${fnMatch[3].trim()})${fnMatch[4] ? `: ${fnMatch[4].trim()}` : ""}`,
58494
+ line: lineNum,
58495
+ jsdoc
58496
+ });
58497
+ continue;
58498
+ }
58499
+ const constMatch = line.match(/^export\s+const\s+(\w+)(?:\s*:\s*(.+?))?\s*=/);
58500
+ if (constMatch) {
58501
+ const restOfLine = line.substring(line.indexOf("=") + 1).trim();
58502
+ const isArrow = restOfLine.startsWith("(") || restOfLine.startsWith("async (") || restOfLine.match(/^\w+\s*=>/);
58503
+ symbols.push({
58504
+ name: constMatch[1],
58505
+ kind: isArrow ? "function" : "const",
58506
+ exported: true,
58507
+ signature: `const ${constMatch[1]}${constMatch[2] ? `: ${constMatch[2].trim()}` : ""}`,
58508
+ line: lineNum,
58509
+ jsdoc
58510
+ });
58511
+ continue;
58512
+ }
58513
+ const classMatch = line.match(/^export\s+(?:abstract\s+)?class\s+(\w+)(?:\s+(?:extends|implements)\s+(.+?))?(?:\s*\{|$)/);
58514
+ if (classMatch) {
58515
+ symbols.push({
58516
+ name: classMatch[1],
58517
+ kind: "class",
58518
+ exported: true,
58519
+ signature: `class ${classMatch[1]}${classMatch[2] ? ` extends/implements ${classMatch[2].trim()}` : ""}`,
58520
+ line: lineNum,
58521
+ jsdoc
58522
+ });
58523
+ let braceDepth = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
58524
+ for (let j = i2 + 1;j < lines.length && braceDepth > 0; j++) {
58525
+ const memberLine = lines[j];
58526
+ braceDepth += (memberLine.match(/\{/g) || []).length - (memberLine.match(/\}/g) || []).length;
58527
+ const methodMatch = memberLine.match(/^\s+(?:public\s+)?(?:async\s+)?(\w+)\s*\(([^)]*)\)(?:\s*:\s*(.+?))?(?:\s*\{|;|$)/);
58528
+ if (methodMatch && !memberLine.includes("private") && !memberLine.includes("protected") && !memberLine.trim().startsWith("//")) {
58529
+ symbols.push({
58530
+ name: `${classMatch[1]}.${methodMatch[1]}`,
58531
+ kind: "method",
58532
+ exported: true,
58533
+ signature: `${methodMatch[1]}(${methodMatch[2].trim()})${methodMatch[3] ? `: ${methodMatch[3].trim()}` : ""}`,
58534
+ line: j + 1
58535
+ });
58536
+ }
58537
+ const propMatch = memberLine.match(/^\s+(?:public\s+)?(?:readonly\s+)?(\w+)(?:\?)?:\s*(.+?)(?:\s*[;=]|$)/);
58538
+ if (propMatch && !memberLine.includes("private") && !memberLine.includes("protected") && !memberLine.trim().startsWith("//")) {
58539
+ symbols.push({
58540
+ name: `${classMatch[1]}.${propMatch[1]}`,
58541
+ kind: "property",
58542
+ exported: true,
58543
+ signature: `${propMatch[1]}: ${propMatch[2].trim()}`,
58544
+ line: j + 1
58545
+ });
58546
+ }
58547
+ }
58548
+ continue;
58549
+ }
58550
+ const ifaceMatch = line.match(/^export\s+interface\s+(\w+)(?:\s*<([^>]+)>)?(?:\s+extends\s+(.+?))?(?:\s*\{|$)/);
58551
+ if (ifaceMatch) {
58552
+ symbols.push({
58553
+ name: ifaceMatch[1],
58554
+ kind: "interface",
58555
+ exported: true,
58556
+ signature: `interface ${ifaceMatch[1]}${ifaceMatch[2] ? `<${ifaceMatch[2]}>` : ""}${ifaceMatch[3] ? ` extends ${ifaceMatch[3].trim()}` : ""}`,
58557
+ line: lineNum,
58558
+ jsdoc
58559
+ });
58560
+ continue;
58561
+ }
58562
+ const typeMatch = line.match(/^export\s+type\s+(\w+)(?:\s*<([^>]+)>)?\s*=/);
58563
+ if (typeMatch) {
58564
+ const typeValue = line.substring(line.indexOf("=") + 1).trim().substring(0, 100);
58565
+ symbols.push({
58566
+ name: typeMatch[1],
58567
+ kind: "type",
58568
+ exported: true,
58569
+ signature: `type ${typeMatch[1]}${typeMatch[2] ? `<${typeMatch[2]}>` : ""} = ${typeValue}`,
58570
+ line: lineNum,
58571
+ jsdoc
58572
+ });
58573
+ continue;
58574
+ }
58575
+ const enumMatch = line.match(/^export\s+(?:const\s+)?enum\s+(\w+)/);
58576
+ if (enumMatch) {
58577
+ symbols.push({
58578
+ name: enumMatch[1],
58579
+ kind: "enum",
58580
+ exported: true,
58581
+ signature: `enum ${enumMatch[1]}`,
58582
+ line: lineNum,
58583
+ jsdoc
58584
+ });
58585
+ continue;
58586
+ }
58587
+ const defaultMatch = line.match(/^export\s+default\s+(?:function\s+)?(\w+)/);
58588
+ if (defaultMatch) {
58589
+ symbols.push({
58590
+ name: defaultMatch[1],
58591
+ kind: "function",
58592
+ exported: true,
58593
+ signature: `default ${defaultMatch[1]}`,
58594
+ line: lineNum,
58595
+ jsdoc
58596
+ });
58597
+ }
58598
+ }
58599
+ return symbols.sort((a, b) => {
58600
+ if (a.line !== b.line)
58601
+ return a.line - b.line;
58602
+ return a.name.localeCompare(b.name);
58603
+ });
58604
+ }
58605
+ function extractPythonSymbols(filePath, cwd) {
58606
+ const fullPath = path48.join(cwd, filePath);
58607
+ if (!validatePathForRead(fullPath, cwd)) {
58608
+ return [];
58609
+ }
58610
+ let content;
58611
+ try {
58612
+ const stats = fs37.statSync(fullPath);
58613
+ if (stats.size > MAX_FILE_SIZE_BYTES2) {
58614
+ throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES2})`);
58615
+ }
58616
+ content = fs37.readFileSync(fullPath, "utf-8");
58617
+ } catch {
58618
+ return [];
58619
+ }
58620
+ const lines = content.split(`
58621
+ `);
58622
+ const symbols = [];
58623
+ const allMatch = content.match(/__all__\s*=\s*\[([^\]]+)\]/);
58624
+ const explicitExports = allMatch ? allMatch[1].split(",").map((s) => s.trim().replace(/['"]/g, "")) : null;
58625
+ for (let i2 = 0;i2 < lines.length; i2++) {
58626
+ const line = lines[i2];
58627
+ if (line.startsWith(" ") || line.startsWith("\t"))
58628
+ continue;
58629
+ const fnMatch = line.match(/^(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*(.+?))?:/);
58630
+ if (fnMatch && !fnMatch[1].startsWith("_")) {
58631
+ const exported = !explicitExports || explicitExports.includes(fnMatch[1]);
58632
+ symbols.push({
58633
+ name: fnMatch[1],
58634
+ kind: "function",
58635
+ exported,
58636
+ signature: `def ${fnMatch[1]}(${fnMatch[2].trim()})${fnMatch[3] ? ` -> ${fnMatch[3].trim()}` : ""}`,
58637
+ line: i2 + 1
58638
+ });
58639
+ }
58640
+ const classMatch = line.match(/^class\s+(\w+)(?:\(([^)]*)\))?:/);
58641
+ if (classMatch && !classMatch[1].startsWith("_")) {
58642
+ const exported = !explicitExports || explicitExports.includes(classMatch[1]);
58643
+ symbols.push({
58644
+ name: classMatch[1],
58645
+ kind: "class",
58646
+ exported,
58647
+ signature: `class ${classMatch[1]}${classMatch[2] ? `(${classMatch[2].trim()})` : ""}`,
58648
+ line: i2 + 1
58649
+ });
58650
+ }
58651
+ const constMatch = line.match(/^([A-Z][A-Z0-9_]+)\s*[:=]/);
58652
+ if (constMatch) {
58653
+ symbols.push({
58654
+ name: constMatch[1],
58655
+ kind: "const",
58656
+ exported: true,
58657
+ signature: line.trim().substring(0, 100),
58658
+ line: i2 + 1
58659
+ });
58660
+ }
58661
+ }
58662
+ return symbols.sort((a, b) => {
58663
+ if (a.line !== b.line)
58664
+ return a.line - b.line;
58665
+ return a.name.localeCompare(b.name);
58666
+ });
58667
+ }
58668
+ var symbols = createSwarmTool({
58669
+ description: "Extract all exported symbols from a source file: functions with signatures, " + "classes with public members, interfaces, types, enums, constants. " + "Supports TypeScript/JavaScript and Python. Use for architect planning, " + "designer scaffolding, and understanding module public API surface.",
58670
+ args: {
58671
+ file: tool.schema.string().describe('File path to extract symbols from (e.g., "src/auth/login.ts")'),
58672
+ exported_only: tool.schema.boolean().default(true).describe("If true, only return exported/public symbols. If false, include all top-level symbols.")
58673
+ },
58674
+ execute: async (args2, directory) => {
58675
+ let file3;
58676
+ let exportedOnly = true;
58677
+ try {
58678
+ const obj = args2;
58679
+ file3 = String(obj.file);
58680
+ exportedOnly = obj.exported_only === true;
58681
+ } catch {
58682
+ return JSON.stringify({
58683
+ file: "<unknown>",
58684
+ error: "Invalid arguments: could not extract file path",
58685
+ symbols: []
58686
+ }, null, 2);
58687
+ }
58688
+ const cwd = directory;
58689
+ const ext = path48.extname(file3);
58690
+ if (containsControlChars(file3)) {
58691
+ return JSON.stringify({
58692
+ file: file3,
58693
+ error: "Path contains invalid control characters",
58694
+ symbols: []
58695
+ }, null, 2);
58696
+ }
58697
+ if (containsPathTraversal(file3)) {
58698
+ return JSON.stringify({
58699
+ file: file3,
58700
+ error: "Path contains path traversal sequence",
58701
+ symbols: []
58702
+ }, null, 2);
58703
+ }
58704
+ if (containsWindowsAttacks(file3)) {
58705
+ return JSON.stringify({
58706
+ file: file3,
58707
+ error: "Path contains invalid Windows-specific sequence",
58708
+ symbols: []
58709
+ }, null, 2);
58710
+ }
58711
+ if (!isPathInWorkspace(file3, cwd)) {
58712
+ return JSON.stringify({
58713
+ file: file3,
58714
+ error: "Path is outside workspace",
58715
+ symbols: []
58716
+ }, null, 2);
58717
+ }
58718
+ let syms;
58719
+ switch (ext) {
58720
+ case ".ts":
58721
+ case ".tsx":
58722
+ case ".js":
58723
+ case ".jsx":
58724
+ case ".mjs":
58725
+ case ".cjs":
58726
+ syms = extractTSSymbols(file3, cwd);
58727
+ break;
58728
+ case ".py":
58729
+ syms = extractPythonSymbols(file3, cwd);
58730
+ break;
58731
+ default:
58732
+ return JSON.stringify({
58733
+ file: file3,
58734
+ error: `Unsupported file extension: ${ext}. Supported: .ts, .tsx, .js, .jsx, .mjs, .cjs, .py`,
58735
+ symbols: []
58736
+ }, null, 2);
58737
+ }
58738
+ if (exportedOnly) {
58739
+ syms = syms.filter((s) => s.exported);
58740
+ }
58741
+ return JSON.stringify({
58742
+ file: file3,
58743
+ symbolCount: syms.length,
58744
+ symbols: syms
58745
+ }, null, 2);
58746
+ }
58747
+ });
58748
+
58749
+ // src/tools/batch-symbols.ts
58750
+ var WINDOWS_RESERVED_NAMES2 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
58751
+ function containsWindowsAttacks2(str) {
58752
+ if (/:[^\\/]/.test(str)) {
58753
+ return true;
58754
+ }
58755
+ const parts2 = str.split(/[/\\]/);
58756
+ for (const part of parts2) {
58757
+ if (WINDOWS_RESERVED_NAMES2.test(part)) {
58758
+ return true;
58759
+ }
58760
+ }
58761
+ return false;
58762
+ }
58763
+ function isPathInWorkspace2(filePath, workspace) {
58764
+ try {
58765
+ const resolvedPath = path49.resolve(workspace, filePath);
58766
+ if (!fs38.existsSync(resolvedPath)) {
58767
+ return true;
58768
+ }
58769
+ const realWorkspace = fs38.realpathSync(workspace);
58770
+ const realResolvedPath = fs38.realpathSync(resolvedPath);
58771
+ const relativePath = path49.relative(realWorkspace, realResolvedPath);
58772
+ if (relativePath.startsWith("..") || path49.isAbsolute(relativePath)) {
58773
+ return false;
58774
+ }
58775
+ return true;
58776
+ } catch {
58777
+ return false;
58778
+ }
58779
+ }
58780
+ function processFile(file3, cwd, exportedOnly) {
58781
+ const ext = path49.extname(file3);
58782
+ if (containsControlChars(file3)) {
58783
+ return {
58784
+ file: file3,
58785
+ success: false,
58786
+ error: "Path contains invalid control characters",
58787
+ errorType: "invalid-path"
58788
+ };
58789
+ }
58790
+ if (containsPathTraversal(file3)) {
58791
+ return {
58792
+ file: file3,
58793
+ success: false,
58794
+ error: "Path contains path traversal sequence",
58795
+ errorType: "path-traversal"
58796
+ };
58797
+ }
58798
+ if (containsWindowsAttacks2(file3)) {
58799
+ return {
58800
+ file: file3,
58801
+ success: false,
58802
+ error: "Path contains invalid Windows-specific sequence",
58803
+ errorType: "invalid-path"
58804
+ };
58805
+ }
58806
+ if (!isPathInWorkspace2(file3, cwd)) {
58807
+ return {
58808
+ file: file3,
58809
+ success: false,
58810
+ error: "Path is outside workspace",
58811
+ errorType: "path-outside-workspace"
58812
+ };
58813
+ }
58814
+ const fullPath = path49.join(cwd, file3);
58815
+ if (!fs38.existsSync(fullPath)) {
58816
+ return {
58817
+ file: file3,
58818
+ success: false,
58819
+ error: `File not found: ${file3}`,
58820
+ errorType: "file-not-found"
58821
+ };
58822
+ }
58823
+ let syms;
58824
+ switch (ext) {
58825
+ case ".ts":
58826
+ case ".tsx":
58827
+ case ".js":
58828
+ case ".jsx":
58829
+ case ".mjs":
58830
+ case ".cjs":
58831
+ syms = extractTSSymbols(file3, cwd);
58832
+ break;
58833
+ case ".py":
58834
+ syms = extractPythonSymbols(file3, cwd);
58835
+ break;
58836
+ default:
58837
+ return {
58838
+ file: file3,
58839
+ success: false,
58840
+ error: `Unsupported file extension: ${ext}. Supported: .ts, .tsx, .js, .jsx, .mjs, .cjs, .py`,
58841
+ errorType: "unsupported-language"
58842
+ };
58843
+ }
58844
+ let isEmptyFile = false;
58845
+ try {
58846
+ const stats = fs38.statSync(fullPath);
58847
+ if (stats.size === 0) {
58848
+ isEmptyFile = true;
58849
+ }
58850
+ } catch {}
58851
+ if (syms.length === 0) {
58852
+ try {
58853
+ const content = fs38.readFileSync(fullPath, "utf-8");
58854
+ if (content.trim().length === 0) {
58855
+ isEmptyFile = true;
58856
+ }
58857
+ } catch {}
58858
+ }
58859
+ if (isEmptyFile) {
58860
+ return {
58861
+ file: file3,
58862
+ success: true,
58863
+ symbols: [],
58864
+ error: "empty-file",
58865
+ errorType: "empty-file"
58866
+ };
58867
+ }
58868
+ if (exportedOnly) {
58869
+ syms = syms.filter((s) => s.exported);
58870
+ }
58871
+ return {
58872
+ file: file3,
58873
+ success: true,
58874
+ symbols: syms
58875
+ };
58876
+ }
58877
+ var batch_symbols = createSwarmTool({
58878
+ description: "Extract symbols from multiple files in a single batch call. " + "Accepts an array of file paths and returns per-file symbol summaries. " + "One bad file does not crash the batch. Use for surveying a module " + "when you need symbol information from multiple files at once.",
58879
+ args: {
58880
+ files: tool.schema.array(tool.schema.string()).describe("Array of file paths to extract symbols from"),
58881
+ exported_only: tool.schema.boolean().default(true).describe("If true, only return exported/public symbols. If false, include all top-level symbols.")
58882
+ },
58883
+ execute: async (args2, directory) => {
58884
+ let files;
58885
+ let exportedOnly = true;
58886
+ try {
58887
+ const obj = args2;
58888
+ if (!Array.isArray(obj.files)) {
58889
+ return JSON.stringify({
58890
+ results: [],
58891
+ totalFiles: 0,
58892
+ successCount: 0,
58893
+ failureCount: 0,
58894
+ error: "Invalid arguments: files must be an array"
58895
+ }, null, 2);
58896
+ }
58897
+ files = obj.files.map((f) => String(f));
58898
+ exportedOnly = obj.exported_only === true;
58899
+ } catch {
58900
+ return JSON.stringify({
58901
+ results: [],
58902
+ totalFiles: 0,
58903
+ successCount: 0,
58904
+ failureCount: 0,
58905
+ error: "Invalid arguments: could not extract files array"
58906
+ }, null, 2);
58907
+ }
58908
+ const cwd = directory;
58909
+ const results = [];
58910
+ for (const file3 of files) {
58911
+ const result = processFile(file3, cwd, exportedOnly);
58912
+ results.push(result);
58913
+ }
58914
+ const successCount = results.filter((r) => r.success).length;
58915
+ const failureCount = results.filter((r) => !r.success).length;
58916
+ const batchResult = {
58917
+ results,
58918
+ totalFiles: files.length,
58919
+ successCount,
58920
+ failureCount
58921
+ };
58922
+ return JSON.stringify(batchResult, null, 2);
58923
+ }
58924
+ });
58362
58925
  // src/tools/build-check.ts
58363
58926
  init_dist();
58364
58927
  init_discovery();
@@ -58538,8 +59101,8 @@ init_dist();
58538
59101
  init_manager();
58539
59102
  init_create_tool();
58540
59103
  init_resolve_working_directory();
58541
- import * as fs37 from "fs";
58542
- import * as path48 from "path";
59104
+ import * as fs39 from "fs";
59105
+ import * as path50 from "path";
58543
59106
  var EVIDENCE_DIR = ".swarm/evidence";
58544
59107
  var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
58545
59108
  function isValidTaskId3(taskId) {
@@ -58556,18 +59119,18 @@ function isValidTaskId3(taskId) {
58556
59119
  return TASK_ID_PATTERN2.test(taskId);
58557
59120
  }
58558
59121
  function isPathWithinSwarm(filePath, workspaceRoot) {
58559
- const normalizedWorkspace = path48.resolve(workspaceRoot);
58560
- const swarmPath = path48.join(normalizedWorkspace, ".swarm", "evidence");
58561
- const normalizedPath = path48.resolve(filePath);
59122
+ const normalizedWorkspace = path50.resolve(workspaceRoot);
59123
+ const swarmPath = path50.join(normalizedWorkspace, ".swarm", "evidence");
59124
+ const normalizedPath = path50.resolve(filePath);
58562
59125
  return normalizedPath.startsWith(swarmPath);
58563
59126
  }
58564
59127
  function readEvidenceFile(evidencePath) {
58565
- if (!fs37.existsSync(evidencePath)) {
59128
+ if (!fs39.existsSync(evidencePath)) {
58566
59129
  return null;
58567
59130
  }
58568
59131
  let content;
58569
59132
  try {
58570
- content = fs37.readFileSync(evidencePath, "utf-8");
59133
+ content = fs39.readFileSync(evidencePath, "utf-8");
58571
59134
  } catch {
58572
59135
  return null;
58573
59136
  }
@@ -58639,7 +59202,7 @@ var check_gate_status = createSwarmTool({
58639
59202
  };
58640
59203
  return JSON.stringify(errorResult, null, 2);
58641
59204
  }
58642
- const evidencePath = path48.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
59205
+ const evidencePath = path50.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
58643
59206
  if (!isPathWithinSwarm(evidencePath, directory)) {
58644
59207
  const errorResult = {
58645
59208
  taskId: taskIdInput,
@@ -58732,8 +59295,8 @@ init_co_change_analyzer();
58732
59295
  // src/tools/completion-verify.ts
58733
59296
  init_dist();
58734
59297
  init_utils2();
58735
- import * as fs38 from "fs";
58736
- import * as path49 from "path";
59298
+ import * as fs40 from "fs";
59299
+ import * as path51 from "path";
58737
59300
  init_create_tool();
58738
59301
  init_resolve_working_directory();
58739
59302
  function extractMatches(regex, text) {
@@ -58829,7 +59392,7 @@ async function executeCompletionVerify(args2, directory) {
58829
59392
  let plan;
58830
59393
  try {
58831
59394
  const planPath = validateSwarmPath(directory, "plan.json");
58832
- const planRaw = fs38.readFileSync(planPath, "utf-8");
59395
+ const planRaw = fs40.readFileSync(planPath, "utf-8");
58833
59396
  plan = JSON.parse(planRaw);
58834
59397
  } catch {
58835
59398
  const result2 = {
@@ -58887,10 +59450,10 @@ async function executeCompletionVerify(args2, directory) {
58887
59450
  let hasFileReadFailure = false;
58888
59451
  for (const filePath of fileTargets) {
58889
59452
  const normalizedPath = filePath.replace(/\\/g, "/");
58890
- const resolvedPath = path49.resolve(directory, normalizedPath);
58891
- const projectRoot = path49.resolve(directory);
58892
- const relative7 = path49.relative(projectRoot, resolvedPath);
58893
- const withinProject = relative7 === "" || !relative7.startsWith("..") && !path49.isAbsolute(relative7);
59453
+ const resolvedPath = path51.resolve(directory, normalizedPath);
59454
+ const projectRoot = path51.resolve(directory);
59455
+ const relative9 = path51.relative(projectRoot, resolvedPath);
59456
+ const withinProject = relative9 === "" || !relative9.startsWith("..") && !path51.isAbsolute(relative9);
58894
59457
  if (!withinProject) {
58895
59458
  blockedTasks.push({
58896
59459
  task_id: task.id,
@@ -58903,7 +59466,7 @@ async function executeCompletionVerify(args2, directory) {
58903
59466
  }
58904
59467
  let fileContent;
58905
59468
  try {
58906
- fileContent = fs38.readFileSync(resolvedPath, "utf-8");
59469
+ fileContent = fs40.readFileSync(resolvedPath, "utf-8");
58907
59470
  } catch {
58908
59471
  blockedTasks.push({
58909
59472
  task_id: task.id,
@@ -58945,9 +59508,9 @@ async function executeCompletionVerify(args2, directory) {
58945
59508
  blockedTasks
58946
59509
  };
58947
59510
  try {
58948
- const evidenceDir = path49.join(directory, ".swarm", "evidence", `${phase}`);
58949
- const evidencePath = path49.join(evidenceDir, "completion-verify.json");
58950
- fs38.mkdirSync(evidenceDir, { recursive: true });
59511
+ const evidenceDir = path51.join(directory, ".swarm", "evidence", `${phase}`);
59512
+ const evidencePath = path51.join(evidenceDir, "completion-verify.json");
59513
+ fs40.mkdirSync(evidenceDir, { recursive: true });
58951
59514
  const evidenceBundle = {
58952
59515
  schema_version: "1.0.0",
58953
59516
  task_id: "completion-verify",
@@ -58968,7 +59531,7 @@ async function executeCompletionVerify(args2, directory) {
58968
59531
  }
58969
59532
  ]
58970
59533
  };
58971
- fs38.writeFileSync(evidencePath, JSON.stringify(evidenceBundle, null, 2), "utf-8");
59534
+ fs40.writeFileSync(evidencePath, JSON.stringify(evidenceBundle, null, 2), "utf-8");
58972
59535
  } catch {}
58973
59536
  return JSON.stringify(result, null, 2);
58974
59537
  }
@@ -59022,13 +59585,13 @@ var completion_verify = createSwarmTool({
59022
59585
  });
59023
59586
  // src/tools/complexity-hotspots.ts
59024
59587
  init_dist();
59025
- import * as fs40 from "fs";
59026
- import * as path51 from "path";
59588
+ import * as fs42 from "fs";
59589
+ import * as path53 from "path";
59027
59590
 
59028
59591
  // src/quality/metrics.ts
59029
- import * as fs39 from "fs";
59030
- import * as path50 from "path";
59031
- var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
59592
+ import * as fs41 from "fs";
59593
+ import * as path52 from "path";
59594
+ var MAX_FILE_SIZE_BYTES3 = 256 * 1024;
59032
59595
  var MIN_DUPLICATION_LINES = 10;
59033
59596
  function estimateCyclomaticComplexity(content) {
59034
59597
  let processed = content.replace(/\/\*[\s\S]*?\*\//g, "");
@@ -59065,11 +59628,11 @@ function estimateCyclomaticComplexity(content) {
59065
59628
  }
59066
59629
  function getComplexityForFile(filePath) {
59067
59630
  try {
59068
- const stat2 = fs39.statSync(filePath);
59069
- if (stat2.size > MAX_FILE_SIZE_BYTES2) {
59631
+ const stat2 = fs41.statSync(filePath);
59632
+ if (stat2.size > MAX_FILE_SIZE_BYTES3) {
59070
59633
  return null;
59071
59634
  }
59072
- const content = fs39.readFileSync(filePath, "utf-8");
59635
+ const content = fs41.readFileSync(filePath, "utf-8");
59073
59636
  return estimateCyclomaticComplexity(content);
59074
59637
  } catch {
59075
59638
  return null;
@@ -59079,8 +59642,8 @@ async function computeComplexityDelta(files, workingDir) {
59079
59642
  let totalComplexity = 0;
59080
59643
  const analyzedFiles = [];
59081
59644
  for (const file3 of files) {
59082
- const fullPath = path50.isAbsolute(file3) ? file3 : path50.join(workingDir, file3);
59083
- if (!fs39.existsSync(fullPath)) {
59645
+ const fullPath = path52.isAbsolute(file3) ? file3 : path52.join(workingDir, file3);
59646
+ if (!fs41.existsSync(fullPath)) {
59084
59647
  continue;
59085
59648
  }
59086
59649
  const complexity = getComplexityForFile(fullPath);
@@ -59201,8 +59764,8 @@ function countGoExports(content) {
59201
59764
  }
59202
59765
  function getExportCountForFile(filePath) {
59203
59766
  try {
59204
- const content = fs39.readFileSync(filePath, "utf-8");
59205
- const ext = path50.extname(filePath).toLowerCase();
59767
+ const content = fs41.readFileSync(filePath, "utf-8");
59768
+ const ext = path52.extname(filePath).toLowerCase();
59206
59769
  switch (ext) {
59207
59770
  case ".ts":
59208
59771
  case ".tsx":
@@ -59228,8 +59791,8 @@ async function computePublicApiDelta(files, workingDir) {
59228
59791
  let totalExports = 0;
59229
59792
  const analyzedFiles = [];
59230
59793
  for (const file3 of files) {
59231
- const fullPath = path50.isAbsolute(file3) ? file3 : path50.join(workingDir, file3);
59232
- if (!fs39.existsSync(fullPath)) {
59794
+ const fullPath = path52.isAbsolute(file3) ? file3 : path52.join(workingDir, file3);
59795
+ if (!fs41.existsSync(fullPath)) {
59233
59796
  continue;
59234
59797
  }
59235
59798
  const exports = getExportCountForFile(fullPath);
@@ -59262,16 +59825,16 @@ async function computeDuplicationRatio(files, workingDir) {
59262
59825
  let duplicateLines = 0;
59263
59826
  const analyzedFiles = [];
59264
59827
  for (const file3 of files) {
59265
- const fullPath = path50.isAbsolute(file3) ? file3 : path50.join(workingDir, file3);
59266
- if (!fs39.existsSync(fullPath)) {
59828
+ const fullPath = path52.isAbsolute(file3) ? file3 : path52.join(workingDir, file3);
59829
+ if (!fs41.existsSync(fullPath)) {
59267
59830
  continue;
59268
59831
  }
59269
59832
  try {
59270
- const stat2 = fs39.statSync(fullPath);
59271
- if (stat2.size > MAX_FILE_SIZE_BYTES2) {
59833
+ const stat2 = fs41.statSync(fullPath);
59834
+ if (stat2.size > MAX_FILE_SIZE_BYTES3) {
59272
59835
  continue;
59273
59836
  }
59274
- const content = fs39.readFileSync(fullPath, "utf-8");
59837
+ const content = fs41.readFileSync(fullPath, "utf-8");
59275
59838
  const lines = content.split(`
59276
59839
  `).filter((line) => line.trim().length > 0);
59277
59840
  if (lines.length < MIN_DUPLICATION_LINES) {
@@ -59295,8 +59858,8 @@ function countCodeLines(content) {
59295
59858
  return lines.length;
59296
59859
  }
59297
59860
  function isTestFile(filePath) {
59298
- const basename7 = path50.basename(filePath);
59299
- const _ext = path50.extname(filePath).toLowerCase();
59861
+ const basename7 = path52.basename(filePath);
59862
+ const _ext = path52.extname(filePath).toLowerCase();
59300
59863
  const testPatterns = [
59301
59864
  ".test.",
59302
59865
  ".spec.",
@@ -59377,8 +59940,8 @@ function matchGlobSegment(globSegments, pathSegments) {
59377
59940
  }
59378
59941
  return gIndex === globSegments.length && pIndex === pathSegments.length;
59379
59942
  }
59380
- function matchesGlobSegment(path51, glob) {
59381
- const normalizedPath = path51.replace(/\\/g, "/");
59943
+ function matchesGlobSegment(path53, glob) {
59944
+ const normalizedPath = path53.replace(/\\/g, "/");
59382
59945
  const normalizedGlob = glob.replace(/\\/g, "/");
59383
59946
  if (normalizedPath.includes("//")) {
59384
59947
  return false;
@@ -59409,8 +59972,8 @@ function simpleGlobToRegex2(glob) {
59409
59972
  function hasGlobstar(glob) {
59410
59973
  return glob.includes("**");
59411
59974
  }
59412
- function globMatches(path51, glob) {
59413
- const normalizedPath = path51.replace(/\\/g, "/");
59975
+ function globMatches(path53, glob) {
59976
+ const normalizedPath = path53.replace(/\\/g, "/");
59414
59977
  if (!glob || glob === "") {
59415
59978
  if (normalizedPath.includes("//")) {
59416
59979
  return false;
@@ -59446,31 +60009,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
59446
60009
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
59447
60010
  let testLines = 0;
59448
60011
  let codeLines = 0;
59449
- const srcDir = path50.join(workingDir, "src");
59450
- if (fs39.existsSync(srcDir)) {
60012
+ const srcDir = path52.join(workingDir, "src");
60013
+ if (fs41.existsSync(srcDir)) {
59451
60014
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
59452
60015
  codeLines += lines;
59453
60016
  });
59454
60017
  }
59455
60018
  const possibleSrcDirs = ["lib", "app", "source", "core"];
59456
60019
  for (const dir of possibleSrcDirs) {
59457
- const dirPath = path50.join(workingDir, dir);
59458
- if (fs39.existsSync(dirPath)) {
60020
+ const dirPath = path52.join(workingDir, dir);
60021
+ if (fs41.existsSync(dirPath)) {
59459
60022
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
59460
60023
  codeLines += lines;
59461
60024
  });
59462
60025
  }
59463
60026
  }
59464
- const testsDir = path50.join(workingDir, "tests");
59465
- if (fs39.existsSync(testsDir)) {
60027
+ const testsDir = path52.join(workingDir, "tests");
60028
+ if (fs41.existsSync(testsDir)) {
59466
60029
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
59467
60030
  testLines += lines;
59468
60031
  });
59469
60032
  }
59470
60033
  const possibleTestDirs = ["test", "__tests__", "specs"];
59471
60034
  for (const dir of possibleTestDirs) {
59472
- const dirPath = path50.join(workingDir, dir);
59473
- if (fs39.existsSync(dirPath) && dirPath !== testsDir) {
60035
+ const dirPath = path52.join(workingDir, dir);
60036
+ if (fs41.existsSync(dirPath) && dirPath !== testsDir) {
59474
60037
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
59475
60038
  testLines += lines;
59476
60039
  });
@@ -59482,9 +60045,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
59482
60045
  }
59483
60046
  async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
59484
60047
  try {
59485
- const entries = fs39.readdirSync(dirPath, { withFileTypes: true });
60048
+ const entries = fs41.readdirSync(dirPath, { withFileTypes: true });
59486
60049
  for (const entry of entries) {
59487
- const fullPath = path50.join(dirPath, entry.name);
60050
+ const fullPath = path52.join(dirPath, entry.name);
59488
60051
  if (entry.isDirectory()) {
59489
60052
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
59490
60053
  continue;
@@ -59492,7 +60055,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
59492
60055
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
59493
60056
  } else if (entry.isFile()) {
59494
60057
  const relativePath = fullPath.replace(`${dirPath}/`, "");
59495
- const ext = path50.extname(entry.name).toLowerCase();
60058
+ const ext = path52.extname(entry.name).toLowerCase();
59496
60059
  const validExts = [
59497
60060
  ".ts",
59498
60061
  ".tsx",
@@ -59528,7 +60091,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
59528
60091
  continue;
59529
60092
  }
59530
60093
  try {
59531
- const content = fs39.readFileSync(fullPath, "utf-8");
60094
+ const content = fs41.readFileSync(fullPath, "utf-8");
59532
60095
  const lines = countCodeLines(content);
59533
60096
  callback(lines);
59534
60097
  } catch {}
@@ -59635,7 +60198,7 @@ async function computeQualityMetrics(changedFiles, thresholds, workingDir) {
59635
60198
 
59636
60199
  // src/tools/complexity-hotspots.ts
59637
60200
  init_create_tool();
59638
- var MAX_FILE_SIZE_BYTES3 = 256 * 1024;
60201
+ var MAX_FILE_SIZE_BYTES4 = 256 * 1024;
59639
60202
  var DEFAULT_DAYS = 90;
59640
60203
  var DEFAULT_TOP_N = 20;
59641
60204
  var DEFAULT_EXTENSIONS = "ts,tsx,js,jsx,py,rs,ps1";
@@ -59729,11 +60292,11 @@ async function getGitChurn(days, directory) {
59729
60292
  }
59730
60293
  function getComplexityForFile2(filePath) {
59731
60294
  try {
59732
- const stat2 = fs40.statSync(filePath);
59733
- if (stat2.size > MAX_FILE_SIZE_BYTES3) {
60295
+ const stat2 = fs42.statSync(filePath);
60296
+ if (stat2.size > MAX_FILE_SIZE_BYTES4) {
59734
60297
  return null;
59735
60298
  }
59736
- const content = fs40.readFileSync(filePath, "utf-8");
60299
+ const content = fs42.readFileSync(filePath, "utf-8");
59737
60300
  return estimateCyclomaticComplexity(content);
59738
60301
  } catch {
59739
60302
  return null;
@@ -59744,7 +60307,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
59744
60307
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
59745
60308
  const filteredChurn = new Map;
59746
60309
  for (const [file3, count] of churnMap) {
59747
- const ext = path51.extname(file3).toLowerCase();
60310
+ const ext = path53.extname(file3).toLowerCase();
59748
60311
  if (extSet.has(ext)) {
59749
60312
  filteredChurn.set(file3, count);
59750
60313
  }
@@ -59754,8 +60317,8 @@ async function analyzeHotspots(days, topN, extensions, directory) {
59754
60317
  let analyzedFiles = 0;
59755
60318
  for (const [file3, churnCount] of filteredChurn) {
59756
60319
  let fullPath = file3;
59757
- if (!fs40.existsSync(fullPath)) {
59758
- fullPath = path51.join(cwd, file3);
60320
+ if (!fs42.existsSync(fullPath)) {
60321
+ fullPath = path53.join(cwd, file3);
59759
60322
  }
59760
60323
  const complexity = getComplexityForFile2(fullPath);
59761
60324
  if (complexity !== null) {
@@ -59997,8 +60560,8 @@ var curator_analyze = createSwarmTool({
59997
60560
  });
59998
60561
  // src/tools/declare-scope.ts
59999
60562
  init_tool();
60000
- import * as fs41 from "fs";
60001
- import * as path52 from "path";
60563
+ import * as fs43 from "fs";
60564
+ import * as path54 from "path";
60002
60565
  init_create_tool();
60003
60566
  function validateTaskIdFormat(taskId) {
60004
60567
  const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
@@ -60077,8 +60640,8 @@ async function executeDeclareScope(args2, fallbackDir) {
60077
60640
  };
60078
60641
  }
60079
60642
  }
60080
- normalizedDir = path52.normalize(args2.working_directory);
60081
- const pathParts = normalizedDir.split(path52.sep);
60643
+ normalizedDir = path54.normalize(args2.working_directory);
60644
+ const pathParts = normalizedDir.split(path54.sep);
60082
60645
  if (pathParts.includes("..")) {
60083
60646
  return {
60084
60647
  success: false,
@@ -60088,11 +60651,11 @@ async function executeDeclareScope(args2, fallbackDir) {
60088
60651
  ]
60089
60652
  };
60090
60653
  }
60091
- const resolvedDir = path52.resolve(normalizedDir);
60654
+ const resolvedDir = path54.resolve(normalizedDir);
60092
60655
  try {
60093
- const realPath = fs41.realpathSync(resolvedDir);
60094
- const planPath2 = path52.join(realPath, ".swarm", "plan.json");
60095
- if (!fs41.existsSync(planPath2)) {
60656
+ const realPath = fs43.realpathSync(resolvedDir);
60657
+ const planPath2 = path54.join(realPath, ".swarm", "plan.json");
60658
+ if (!fs43.existsSync(planPath2)) {
60096
60659
  return {
60097
60660
  success: false,
60098
60661
  message: `Invalid working_directory: plan not found in "${realPath}"`,
@@ -60115,8 +60678,8 @@ async function executeDeclareScope(args2, fallbackDir) {
60115
60678
  console.warn("[declare-scope] fallbackDir is undefined, falling back to process.cwd()");
60116
60679
  }
60117
60680
  const directory = normalizedDir || fallbackDir;
60118
- const planPath = path52.resolve(directory, ".swarm", "plan.json");
60119
- if (!fs41.existsSync(planPath)) {
60681
+ const planPath = path54.resolve(directory, ".swarm", "plan.json");
60682
+ if (!fs43.existsSync(planPath)) {
60120
60683
  return {
60121
60684
  success: false,
60122
60685
  message: "No plan found",
@@ -60125,7 +60688,7 @@ async function executeDeclareScope(args2, fallbackDir) {
60125
60688
  }
60126
60689
  let planContent;
60127
60690
  try {
60128
- planContent = JSON.parse(fs41.readFileSync(planPath, "utf-8"));
60691
+ planContent = JSON.parse(fs43.readFileSync(planPath, "utf-8"));
60129
60692
  } catch {
60130
60693
  return {
60131
60694
  success: false,
@@ -60157,8 +60720,8 @@ async function executeDeclareScope(args2, fallbackDir) {
60157
60720
  const normalizeErrors = [];
60158
60721
  const dir = normalizedDir || fallbackDir || process.cwd();
60159
60722
  const mergedFiles = rawMergedFiles.map((file3) => {
60160
- if (path52.isAbsolute(file3)) {
60161
- const relativePath = path52.relative(dir, file3).replace(/\\/g, "/");
60723
+ if (path54.isAbsolute(file3)) {
60724
+ const relativePath = path54.relative(dir, file3).replace(/\\/g, "/");
60162
60725
  if (relativePath.startsWith("..")) {
60163
60726
  normalizeErrors.push(`Path '${file3}' resolves outside the project directory`);
60164
60727
  return file3;
@@ -60205,7 +60768,7 @@ import * as child_process5 from "child_process";
60205
60768
 
60206
60769
  // src/diff/ast-diff.ts
60207
60770
  init_tree_sitter();
60208
- import { extname as extname7 } from "path";
60771
+ import { extname as extname9 } from "path";
60209
60772
 
60210
60773
  // src/lang/registry.ts
60211
60774
  init_runtime();
@@ -60290,7 +60853,7 @@ var QUERIES = {
60290
60853
  var AST_TIMEOUT_MS = 500;
60291
60854
  async function computeASTDiff(filePath, oldContent, newContent) {
60292
60855
  const startTime = Date.now();
60293
- const extension = extname7(filePath).toLowerCase();
60856
+ const extension = extname9(filePath).toLowerCase();
60294
60857
  const language = getLanguageForExtension(extension);
60295
60858
  if (!language) {
60296
60859
  return {
@@ -60352,26 +60915,26 @@ async function computeASTDiff(filePath, oldContent, newContent) {
60352
60915
  }
60353
60916
  }
60354
60917
  function extractSymbols(tree, language) {
60355
- const symbols = [];
60918
+ const symbols2 = [];
60356
60919
  const queryStr = QUERIES[language.id];
60357
60920
  if (!queryStr) {
60358
- return symbols;
60921
+ return symbols2;
60359
60922
  }
60360
60923
  try {
60361
60924
  const lang = tree.language;
60362
60925
  if (!lang) {
60363
- return symbols;
60926
+ return symbols2;
60364
60927
  }
60365
60928
  const query = new Query(lang, queryStr);
60366
60929
  const matches = query.matches(tree.rootNode);
60367
60930
  for (const match of matches) {
60368
60931
  const symbol3 = parseMatch(match, language.id);
60369
60932
  if (symbol3) {
60370
- symbols.push(symbol3);
60933
+ symbols2.push(symbol3);
60371
60934
  }
60372
60935
  }
60373
60936
  } catch {}
60374
- return symbols;
60937
+ return symbols2;
60375
60938
  }
60376
60939
  function parseMatch(match, languageId) {
60377
60940
  const captures = match.captures;
@@ -60484,20 +61047,20 @@ function validateBase(base) {
60484
61047
  function validatePaths(paths) {
60485
61048
  if (!paths)
60486
61049
  return null;
60487
- for (const path54 of paths) {
60488
- if (!path54 || path54.length === 0) {
61050
+ for (const path56 of paths) {
61051
+ if (!path56 || path56.length === 0) {
60489
61052
  return "empty path not allowed";
60490
61053
  }
60491
- if (path54.length > MAX_PATH_LENGTH) {
61054
+ if (path56.length > MAX_PATH_LENGTH) {
60492
61055
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
60493
61056
  }
60494
- if (SHELL_METACHARACTERS2.test(path54)) {
61057
+ if (SHELL_METACHARACTERS2.test(path56)) {
60495
61058
  return "path contains shell metacharacters";
60496
61059
  }
60497
- if (path54.startsWith("-")) {
61060
+ if (path56.startsWith("-")) {
60498
61061
  return 'path cannot start with "-" (option-like arguments not allowed)';
60499
61062
  }
60500
- if (CONTROL_CHAR_PATTERN2.test(path54)) {
61063
+ if (CONTROL_CHAR_PATTERN2.test(path56)) {
60501
61064
  return "path contains control characters";
60502
61065
  }
60503
61066
  }
@@ -60578,8 +61141,8 @@ var diff = createSwarmTool({
60578
61141
  if (parts2.length >= 3) {
60579
61142
  const additions = parseInt(parts2[0], 10) || 0;
60580
61143
  const deletions = parseInt(parts2[1], 10) || 0;
60581
- const path54 = parts2[2];
60582
- files.push({ path: path54, additions, deletions });
61144
+ const path56 = parts2[2];
61145
+ files.push({ path: path56, additions, deletions });
60583
61146
  }
60584
61147
  }
60585
61148
  const contractChanges = [];
@@ -60861,9 +61424,9 @@ Use these as DOMAIN values when delegating to @sme.`;
60861
61424
  // src/tools/evidence-check.ts
60862
61425
  init_dist();
60863
61426
  init_create_tool();
60864
- import * as fs42 from "fs";
60865
- import * as path54 from "path";
60866
- var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
61427
+ import * as fs44 from "fs";
61428
+ import * as path56 from "path";
61429
+ var MAX_FILE_SIZE_BYTES5 = 1024 * 1024;
60867
61430
  var MAX_EVIDENCE_FILES = 1000;
60868
61431
  var EVIDENCE_DIR2 = ".swarm/evidence";
60869
61432
  var PLAN_FILE = ".swarm/plan.md";
@@ -60889,9 +61452,9 @@ function validateRequiredTypes(input) {
60889
61452
  return null;
60890
61453
  }
60891
61454
  function isPathWithinSwarm2(filePath, cwd) {
60892
- const normalizedCwd = path54.resolve(cwd);
60893
- const swarmPath = path54.join(normalizedCwd, ".swarm");
60894
- const normalizedPath = path54.resolve(filePath);
61455
+ const normalizedCwd = path56.resolve(cwd);
61456
+ const swarmPath = path56.join(normalizedCwd, ".swarm");
61457
+ const normalizedPath = path56.resolve(filePath);
60895
61458
  return normalizedPath.startsWith(swarmPath);
60896
61459
  }
60897
61460
  function parseCompletedTasks(planContent) {
@@ -60907,12 +61470,12 @@ function parseCompletedTasks(planContent) {
60907
61470
  }
60908
61471
  function readEvidenceFiles(evidenceDir, _cwd) {
60909
61472
  const evidence = [];
60910
- if (!fs42.existsSync(evidenceDir) || !fs42.statSync(evidenceDir).isDirectory()) {
61473
+ if (!fs44.existsSync(evidenceDir) || !fs44.statSync(evidenceDir).isDirectory()) {
60911
61474
  return evidence;
60912
61475
  }
60913
61476
  let files;
60914
61477
  try {
60915
- files = fs42.readdirSync(evidenceDir);
61478
+ files = fs44.readdirSync(evidenceDir);
60916
61479
  } catch {
60917
61480
  return evidence;
60918
61481
  }
@@ -60921,14 +61484,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
60921
61484
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
60922
61485
  continue;
60923
61486
  }
60924
- const filePath = path54.join(evidenceDir, filename);
61487
+ const filePath = path56.join(evidenceDir, filename);
60925
61488
  try {
60926
- const resolvedPath = path54.resolve(filePath);
60927
- const evidenceDirResolved = path54.resolve(evidenceDir);
61489
+ const resolvedPath = path56.resolve(filePath);
61490
+ const evidenceDirResolved = path56.resolve(evidenceDir);
60928
61491
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
60929
61492
  continue;
60930
61493
  }
60931
- const stat2 = fs42.lstatSync(filePath);
61494
+ const stat2 = fs44.lstatSync(filePath);
60932
61495
  if (!stat2.isFile()) {
60933
61496
  continue;
60934
61497
  }
@@ -60937,8 +61500,8 @@ function readEvidenceFiles(evidenceDir, _cwd) {
60937
61500
  }
60938
61501
  let fileStat;
60939
61502
  try {
60940
- fileStat = fs42.statSync(filePath);
60941
- if (fileStat.size > MAX_FILE_SIZE_BYTES4) {
61503
+ fileStat = fs44.statSync(filePath);
61504
+ if (fileStat.size > MAX_FILE_SIZE_BYTES5) {
60942
61505
  continue;
60943
61506
  }
60944
61507
  } catch {
@@ -60946,7 +61509,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
60946
61509
  }
60947
61510
  let content;
60948
61511
  try {
60949
- content = fs42.readFileSync(filePath, "utf-8");
61512
+ content = fs44.readFileSync(filePath, "utf-8");
60950
61513
  } catch {
60951
61514
  continue;
60952
61515
  }
@@ -61042,7 +61605,7 @@ var evidence_check = createSwarmTool({
61042
61605
  return JSON.stringify(errorResult, null, 2);
61043
61606
  }
61044
61607
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
61045
- const planPath = path54.join(cwd, PLAN_FILE);
61608
+ const planPath = path56.join(cwd, PLAN_FILE);
61046
61609
  if (!isPathWithinSwarm2(planPath, cwd)) {
61047
61610
  const errorResult = {
61048
61611
  error: "plan file path validation failed",
@@ -61056,7 +61619,7 @@ var evidence_check = createSwarmTool({
61056
61619
  }
61057
61620
  let planContent;
61058
61621
  try {
61059
- planContent = fs42.readFileSync(planPath, "utf-8");
61622
+ planContent = fs44.readFileSync(planPath, "utf-8");
61060
61623
  } catch {
61061
61624
  const result2 = {
61062
61625
  message: "No completed tasks found in plan.",
@@ -61074,7 +61637,7 @@ var evidence_check = createSwarmTool({
61074
61637
  };
61075
61638
  return JSON.stringify(result2, null, 2);
61076
61639
  }
61077
- const evidenceDir = path54.join(cwd, EVIDENCE_DIR2);
61640
+ const evidenceDir = path56.join(cwd, EVIDENCE_DIR2);
61078
61641
  const evidence = readEvidenceFiles(evidenceDir, cwd);
61079
61642
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
61080
61643
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -61091,8 +61654,8 @@ var evidence_check = createSwarmTool({
61091
61654
  // src/tools/file-extractor.ts
61092
61655
  init_tool();
61093
61656
  init_create_tool();
61094
- import * as fs43 from "fs";
61095
- import * as path55 from "path";
61657
+ import * as fs45 from "fs";
61658
+ import * as path57 from "path";
61096
61659
  var EXT_MAP = {
61097
61660
  python: ".py",
61098
61661
  py: ".py",
@@ -61154,8 +61717,8 @@ var extract_code_blocks = createSwarmTool({
61154
61717
  execute: async (args2, directory) => {
61155
61718
  const { content, output_dir, prefix } = args2;
61156
61719
  const targetDir = output_dir || directory;
61157
- if (!fs43.existsSync(targetDir)) {
61158
- fs43.mkdirSync(targetDir, { recursive: true });
61720
+ if (!fs45.existsSync(targetDir)) {
61721
+ fs45.mkdirSync(targetDir, { recursive: true });
61159
61722
  }
61160
61723
  if (!content) {
61161
61724
  return "Error: content is required";
@@ -61173,16 +61736,16 @@ var extract_code_blocks = createSwarmTool({
61173
61736
  if (prefix) {
61174
61737
  filename = `${prefix}_${filename}`;
61175
61738
  }
61176
- let filepath = path55.join(targetDir, filename);
61177
- const base = path55.basename(filepath, path55.extname(filepath));
61178
- const ext = path55.extname(filepath);
61739
+ let filepath = path57.join(targetDir, filename);
61740
+ const base = path57.basename(filepath, path57.extname(filepath));
61741
+ const ext = path57.extname(filepath);
61179
61742
  let counter = 1;
61180
- while (fs43.existsSync(filepath)) {
61181
- filepath = path55.join(targetDir, `${base}_${counter}${ext}`);
61743
+ while (fs45.existsSync(filepath)) {
61744
+ filepath = path57.join(targetDir, `${base}_${counter}${ext}`);
61182
61745
  counter++;
61183
61746
  }
61184
61747
  try {
61185
- fs43.writeFileSync(filepath, code.trim(), "utf-8");
61748
+ fs45.writeFileSync(filepath, code.trim(), "utf-8");
61186
61749
  savedFiles.push(filepath);
61187
61750
  } catch (error93) {
61188
61751
  errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
@@ -61212,7 +61775,7 @@ init_create_tool();
61212
61775
  var GITINGEST_TIMEOUT_MS = 1e4;
61213
61776
  var GITINGEST_MAX_RESPONSE_BYTES = 5242880;
61214
61777
  var GITINGEST_MAX_RETRIES = 2;
61215
- var delay = (ms) => new Promise((resolve18) => setTimeout(resolve18, ms));
61778
+ var delay = (ms) => new Promise((resolve20) => setTimeout(resolve20, ms));
61216
61779
  async function fetchGitingest(args2) {
61217
61780
  for (let attempt = 0;attempt <= GITINGEST_MAX_RETRIES; attempt++) {
61218
61781
  try {
@@ -61298,11 +61861,11 @@ var gitingest = createSwarmTool({
61298
61861
  // src/tools/imports.ts
61299
61862
  init_dist();
61300
61863
  init_create_tool();
61301
- import * as fs44 from "fs";
61302
- import * as path56 from "path";
61864
+ import * as fs46 from "fs";
61865
+ import * as path58 from "path";
61303
61866
  var MAX_FILE_PATH_LENGTH2 = 500;
61304
61867
  var MAX_SYMBOL_LENGTH = 256;
61305
- var MAX_FILE_SIZE_BYTES5 = 1024 * 1024;
61868
+ var MAX_FILE_SIZE_BYTES6 = 1024 * 1024;
61306
61869
  var MAX_CONSUMERS = 100;
61307
61870
  var SUPPORTED_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
61308
61871
  var BINARY_SIGNATURES2 = [
@@ -61347,7 +61910,7 @@ function validateSymbolInput(symbol3) {
61347
61910
  return null;
61348
61911
  }
61349
61912
  function isBinaryFile2(filePath, buffer) {
61350
- const ext = path56.extname(filePath).toLowerCase();
61913
+ const ext = path58.extname(filePath).toLowerCase();
61351
61914
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
61352
61915
  return false;
61353
61916
  }
@@ -61371,15 +61934,15 @@ function parseImports(content, targetFile, targetSymbol) {
61371
61934
  const imports = [];
61372
61935
  let _resolvedTarget;
61373
61936
  try {
61374
- _resolvedTarget = path56.resolve(targetFile);
61937
+ _resolvedTarget = path58.resolve(targetFile);
61375
61938
  } catch {
61376
61939
  _resolvedTarget = targetFile;
61377
61940
  }
61378
- const targetBasename = path56.basename(targetFile, path56.extname(targetFile));
61941
+ const targetBasename = path58.basename(targetFile, path58.extname(targetFile));
61379
61942
  const targetWithExt = targetFile;
61380
61943
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
61381
- const normalizedTargetWithExt = path56.normalize(targetWithExt).replace(/\\/g, "/");
61382
- const normalizedTargetWithoutExt = path56.normalize(targetWithoutExt).replace(/\\/g, "/");
61944
+ const normalizedTargetWithExt = path58.normalize(targetWithExt).replace(/\\/g, "/");
61945
+ const normalizedTargetWithoutExt = path58.normalize(targetWithoutExt).replace(/\\/g, "/");
61383
61946
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
61384
61947
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
61385
61948
  const modulePath = match[1] || match[2] || match[3];
@@ -61402,9 +61965,9 @@ function parseImports(content, targetFile, targetSymbol) {
61402
61965
  }
61403
61966
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
61404
61967
  let isMatch = false;
61405
- const _targetDir = path56.dirname(targetFile);
61406
- const targetExt = path56.extname(targetFile);
61407
- const targetBasenameNoExt = path56.basename(targetFile, targetExt);
61968
+ const _targetDir = path58.dirname(targetFile);
61969
+ const targetExt = path58.extname(targetFile);
61970
+ const targetBasenameNoExt = path58.basename(targetFile, targetExt);
61408
61971
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
61409
61972
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
61410
61973
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -61461,7 +62024,7 @@ var SKIP_DIRECTORIES3 = new Set([
61461
62024
  function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
61462
62025
  let entries;
61463
62026
  try {
61464
- entries = fs44.readdirSync(dir);
62027
+ entries = fs46.readdirSync(dir);
61465
62028
  } catch (e) {
61466
62029
  stats.fileErrors.push({
61467
62030
  path: dir,
@@ -61472,13 +62035,13 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
61472
62035
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
61473
62036
  for (const entry of entries) {
61474
62037
  if (SKIP_DIRECTORIES3.has(entry)) {
61475
- stats.skippedDirs.push(path56.join(dir, entry));
62038
+ stats.skippedDirs.push(path58.join(dir, entry));
61476
62039
  continue;
61477
62040
  }
61478
- const fullPath = path56.join(dir, entry);
62041
+ const fullPath = path58.join(dir, entry);
61479
62042
  let stat2;
61480
62043
  try {
61481
- stat2 = fs44.statSync(fullPath);
62044
+ stat2 = fs46.statSync(fullPath);
61482
62045
  } catch (e) {
61483
62046
  stats.fileErrors.push({
61484
62047
  path: fullPath,
@@ -61489,7 +62052,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
61489
62052
  if (stat2.isDirectory()) {
61490
62053
  findSourceFiles(fullPath, files, stats);
61491
62054
  } else if (stat2.isFile()) {
61492
- const ext = path56.extname(fullPath).toLowerCase();
62055
+ const ext = path58.extname(fullPath).toLowerCase();
61493
62056
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
61494
62057
  files.push(fullPath);
61495
62058
  }
@@ -61546,8 +62109,8 @@ var imports = createSwarmTool({
61546
62109
  return JSON.stringify(errorResult, null, 2);
61547
62110
  }
61548
62111
  try {
61549
- const targetFile = path56.resolve(file3);
61550
- if (!fs44.existsSync(targetFile)) {
62112
+ const targetFile = path58.resolve(file3);
62113
+ if (!fs46.existsSync(targetFile)) {
61551
62114
  const errorResult = {
61552
62115
  error: `target file not found: ${file3}`,
61553
62116
  target: file3,
@@ -61557,7 +62120,7 @@ var imports = createSwarmTool({
61557
62120
  };
61558
62121
  return JSON.stringify(errorResult, null, 2);
61559
62122
  }
61560
- const targetStat = fs44.statSync(targetFile);
62123
+ const targetStat = fs46.statSync(targetFile);
61561
62124
  if (!targetStat.isFile()) {
61562
62125
  const errorResult = {
61563
62126
  error: "target must be a file, not a directory",
@@ -61568,7 +62131,7 @@ var imports = createSwarmTool({
61568
62131
  };
61569
62132
  return JSON.stringify(errorResult, null, 2);
61570
62133
  }
61571
- const baseDir = path56.dirname(targetFile);
62134
+ const baseDir = path58.dirname(targetFile);
61572
62135
  const scanStats = {
61573
62136
  skippedDirs: [],
61574
62137
  skippedFiles: 0,
@@ -61583,12 +62146,12 @@ var imports = createSwarmTool({
61583
62146
  if (consumers.length >= MAX_CONSUMERS)
61584
62147
  break;
61585
62148
  try {
61586
- const stat2 = fs44.statSync(filePath);
61587
- if (stat2.size > MAX_FILE_SIZE_BYTES5) {
62149
+ const stat2 = fs46.statSync(filePath);
62150
+ if (stat2.size > MAX_FILE_SIZE_BYTES6) {
61588
62151
  skippedFileCount++;
61589
62152
  continue;
61590
62153
  }
61591
- const buffer = fs44.readFileSync(filePath);
62154
+ const buffer = fs46.readFileSync(filePath);
61592
62155
  if (isBinaryFile2(filePath, buffer)) {
61593
62156
  skippedFileCount++;
61594
62157
  continue;
@@ -61788,7 +62351,7 @@ init_dist();
61788
62351
  init_config();
61789
62352
  init_knowledge_store();
61790
62353
  init_create_tool();
61791
- import { existsSync as existsSync35 } from "fs";
62354
+ import { existsSync as existsSync36 } from "fs";
61792
62355
  var DEFAULT_LIMIT = 10;
61793
62356
  var MAX_LESSON_LENGTH = 200;
61794
62357
  var VALID_CATEGORIES3 = [
@@ -61857,14 +62420,14 @@ function validateLimit(limit) {
61857
62420
  }
61858
62421
  async function readSwarmKnowledge(directory) {
61859
62422
  const swarmPath = resolveSwarmKnowledgePath(directory);
61860
- if (!existsSync35(swarmPath)) {
62423
+ if (!existsSync36(swarmPath)) {
61861
62424
  return [];
61862
62425
  }
61863
62426
  return readKnowledge(swarmPath);
61864
62427
  }
61865
62428
  async function readHiveKnowledge() {
61866
62429
  const hivePath = resolveHiveKnowledgePath();
61867
- if (!existsSync35(hivePath)) {
62430
+ if (!existsSync36(hivePath)) {
61868
62431
  return [];
61869
62432
  }
61870
62433
  return readKnowledge(hivePath);
@@ -62177,8 +62740,8 @@ init_dist();
62177
62740
  init_config();
62178
62741
  init_schema();
62179
62742
  init_manager();
62180
- import * as fs45 from "fs";
62181
- import * as path57 from "path";
62743
+ import * as fs47 from "fs";
62744
+ import * as path59 from "path";
62182
62745
  init_review_receipt();
62183
62746
  init_utils2();
62184
62747
  init_ledger();
@@ -62402,11 +62965,11 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62402
62965
  safeWarn(`[phase_complete] Completion verify error (non-blocking):`, completionError);
62403
62966
  }
62404
62967
  try {
62405
- const driftEvidencePath = path57.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
62968
+ const driftEvidencePath = path59.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
62406
62969
  let driftVerdictFound = false;
62407
62970
  let driftVerdictApproved = false;
62408
62971
  try {
62409
- const driftEvidenceContent = fs45.readFileSync(driftEvidencePath, "utf-8");
62972
+ const driftEvidenceContent = fs47.readFileSync(driftEvidencePath, "utf-8");
62410
62973
  const driftEvidence = JSON.parse(driftEvidenceContent);
62411
62974
  const entries = driftEvidence.entries ?? [];
62412
62975
  for (const entry of entries) {
@@ -62436,14 +62999,14 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62436
62999
  driftVerdictFound = false;
62437
63000
  }
62438
63001
  if (!driftVerdictFound) {
62439
- const specPath = path57.join(dir, ".swarm", "spec.md");
62440
- const specExists = fs45.existsSync(specPath);
63002
+ const specPath = path59.join(dir, ".swarm", "spec.md");
63003
+ const specExists = fs47.existsSync(specPath);
62441
63004
  if (!specExists) {
62442
63005
  let incompleteTaskCount = 0;
62443
63006
  let planPhaseFound = false;
62444
63007
  try {
62445
63008
  const planPath = validateSwarmPath(dir, "plan.json");
62446
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63009
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62447
63010
  const plan = JSON.parse(planRaw);
62448
63011
  const targetPhase = plan.phases.find((p) => p.id === phase);
62449
63012
  if (targetPhase) {
@@ -62488,7 +63051,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62488
63051
  const knowledgeConfig = KnowledgeConfigSchema.parse(config3.knowledge ?? {});
62489
63052
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
62490
63053
  try {
62491
- const projectName = path57.basename(dir);
63054
+ const projectName = path59.basename(dir);
62492
63055
  const curationResult = await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
62493
63056
  if (curationResult) {
62494
63057
  const sessionState = swarmState.agentSessions.get(sessionID);
@@ -62568,7 +63131,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62568
63131
  let phaseRequiredAgents;
62569
63132
  try {
62570
63133
  const planPath = validateSwarmPath(dir, "plan.json");
62571
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63134
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62572
63135
  const plan = JSON.parse(planRaw);
62573
63136
  const phaseObj = plan.phases.find((p) => p.id === phase);
62574
63137
  phaseRequiredAgents = phaseObj?.required_agents;
@@ -62583,7 +63146,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62583
63146
  if (agentsMissing.length > 0) {
62584
63147
  try {
62585
63148
  const planPath = validateSwarmPath(dir, "plan.json");
62586
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63149
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62587
63150
  const plan = JSON.parse(planRaw);
62588
63151
  const targetPhase = plan.phases.find((p) => p.id === phase);
62589
63152
  if (targetPhase && targetPhase.tasks.length > 0 && targetPhase.tasks.every((t) => t.status === "completed")) {
@@ -62623,7 +63186,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62623
63186
  if (phaseCompleteConfig.regression_sweep?.enforce) {
62624
63187
  try {
62625
63188
  const planPath = validateSwarmPath(dir, "plan.json");
62626
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63189
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62627
63190
  const plan = JSON.parse(planRaw);
62628
63191
  const targetPhase = plan.phases.find((p) => p.id === phase);
62629
63192
  if (targetPhase) {
@@ -62661,7 +63224,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62661
63224
  };
62662
63225
  try {
62663
63226
  const eventsPath = validateSwarmPath(dir, "events.jsonl");
62664
- fs45.appendFileSync(eventsPath, `${JSON.stringify(event)}
63227
+ fs47.appendFileSync(eventsPath, `${JSON.stringify(event)}
62665
63228
  `, "utf-8");
62666
63229
  } catch (writeError) {
62667
63230
  warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
@@ -62703,12 +63266,12 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62703
63266
  warnings.push(`Warning: failed to update plan.json phase status`);
62704
63267
  try {
62705
63268
  const planPath = validateSwarmPath(dir, "plan.json");
62706
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63269
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62707
63270
  const plan2 = JSON.parse(planRaw);
62708
63271
  const phaseObj = plan2.phases.find((p) => p.id === phase);
62709
63272
  if (phaseObj) {
62710
63273
  phaseObj.status = "complete";
62711
- fs45.writeFileSync(planPath, JSON.stringify(plan2, null, 2), "utf-8");
63274
+ fs47.writeFileSync(planPath, JSON.stringify(plan2, null, 2), "utf-8");
62712
63275
  }
62713
63276
  } catch {}
62714
63277
  } else if (plan) {
@@ -62745,12 +63308,12 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62745
63308
  warnings.push(`Warning: failed to update plan.json phase status`);
62746
63309
  try {
62747
63310
  const planPath = validateSwarmPath(dir, "plan.json");
62748
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63311
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62749
63312
  const plan = JSON.parse(planRaw);
62750
63313
  const phaseObj = plan.phases.find((p) => p.id === phase);
62751
63314
  if (phaseObj) {
62752
63315
  phaseObj.status = "complete";
62753
- fs45.writeFileSync(planPath, JSON.stringify(plan, null, 2), "utf-8");
63316
+ fs47.writeFileSync(planPath, JSON.stringify(plan, null, 2), "utf-8");
62754
63317
  }
62755
63318
  } catch {}
62756
63319
  }
@@ -62807,8 +63370,8 @@ init_dist();
62807
63370
  init_discovery();
62808
63371
  init_utils();
62809
63372
  init_create_tool();
62810
- import * as fs46 from "fs";
62811
- import * as path58 from "path";
63373
+ import * as fs48 from "fs";
63374
+ import * as path60 from "path";
62812
63375
  var MAX_OUTPUT_BYTES5 = 52428800;
62813
63376
  var AUDIT_TIMEOUT_MS = 120000;
62814
63377
  function isValidEcosystem(value) {
@@ -62826,28 +63389,28 @@ function validateArgs3(args2) {
62826
63389
  function detectEcosystems(directory) {
62827
63390
  const ecosystems = [];
62828
63391
  const cwd = directory;
62829
- if (fs46.existsSync(path58.join(cwd, "package.json"))) {
63392
+ if (fs48.existsSync(path60.join(cwd, "package.json"))) {
62830
63393
  ecosystems.push("npm");
62831
63394
  }
62832
- if (fs46.existsSync(path58.join(cwd, "pyproject.toml")) || fs46.existsSync(path58.join(cwd, "requirements.txt"))) {
63395
+ if (fs48.existsSync(path60.join(cwd, "pyproject.toml")) || fs48.existsSync(path60.join(cwd, "requirements.txt"))) {
62833
63396
  ecosystems.push("pip");
62834
63397
  }
62835
- if (fs46.existsSync(path58.join(cwd, "Cargo.toml"))) {
63398
+ if (fs48.existsSync(path60.join(cwd, "Cargo.toml"))) {
62836
63399
  ecosystems.push("cargo");
62837
63400
  }
62838
- if (fs46.existsSync(path58.join(cwd, "go.mod"))) {
63401
+ if (fs48.existsSync(path60.join(cwd, "go.mod"))) {
62839
63402
  ecosystems.push("go");
62840
63403
  }
62841
63404
  try {
62842
- const files = fs46.readdirSync(cwd);
63405
+ const files = fs48.readdirSync(cwd);
62843
63406
  if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
62844
63407
  ecosystems.push("dotnet");
62845
63408
  }
62846
63409
  } catch {}
62847
- if (fs46.existsSync(path58.join(cwd, "Gemfile")) || fs46.existsSync(path58.join(cwd, "Gemfile.lock"))) {
63410
+ if (fs48.existsSync(path60.join(cwd, "Gemfile")) || fs48.existsSync(path60.join(cwd, "Gemfile.lock"))) {
62848
63411
  ecosystems.push("ruby");
62849
63412
  }
62850
- if (fs46.existsSync(path58.join(cwd, "pubspec.yaml"))) {
63413
+ if (fs48.existsSync(path60.join(cwd, "pubspec.yaml"))) {
62851
63414
  ecosystems.push("dart");
62852
63415
  }
62853
63416
  return ecosystems;
@@ -62860,7 +63423,7 @@ async function runNpmAudit(directory) {
62860
63423
  stderr: "pipe",
62861
63424
  cwd: directory
62862
63425
  });
62863
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
63426
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
62864
63427
  const result = await Promise.race([
62865
63428
  Promise.all([
62866
63429
  new Response(proc.stdout).text(),
@@ -62983,7 +63546,7 @@ async function runPipAudit(directory) {
62983
63546
  stderr: "pipe",
62984
63547
  cwd: directory
62985
63548
  });
62986
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
63549
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
62987
63550
  const result = await Promise.race([
62988
63551
  Promise.all([
62989
63552
  new Response(proc.stdout).text(),
@@ -63114,7 +63677,7 @@ async function runCargoAudit(directory) {
63114
63677
  stderr: "pipe",
63115
63678
  cwd: directory
63116
63679
  });
63117
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
63680
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
63118
63681
  const result = await Promise.race([
63119
63682
  Promise.all([
63120
63683
  new Response(proc.stdout).text(),
@@ -63241,7 +63804,7 @@ async function runGoAudit(directory) {
63241
63804
  stderr: "pipe",
63242
63805
  cwd: directory
63243
63806
  });
63244
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
63807
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
63245
63808
  const result = await Promise.race([
63246
63809
  Promise.all([
63247
63810
  new Response(proc.stdout).text(),
@@ -63377,7 +63940,7 @@ async function runDotnetAudit(directory) {
63377
63940
  stderr: "pipe",
63378
63941
  cwd: directory
63379
63942
  });
63380
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
63943
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
63381
63944
  const result = await Promise.race([
63382
63945
  Promise.all([
63383
63946
  new Response(proc.stdout).text(),
@@ -63496,7 +64059,7 @@ async function runBundleAudit(directory) {
63496
64059
  stderr: "pipe",
63497
64060
  cwd: directory
63498
64061
  });
63499
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
64062
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
63500
64063
  const result = await Promise.race([
63501
64064
  Promise.all([
63502
64065
  new Response(proc.stdout).text(),
@@ -63644,7 +64207,7 @@ async function runDartAudit(directory) {
63644
64207
  stderr: "pipe",
63645
64208
  cwd: directory
63646
64209
  });
63647
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
64210
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
63648
64211
  const result = await Promise.race([
63649
64212
  Promise.all([
63650
64213
  new Response(proc.stdout).text(),
@@ -63869,8 +64432,8 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
63869
64432
  ]);
63870
64433
  // src/tools/pre-check-batch.ts
63871
64434
  init_dist();
63872
- import * as fs48 from "fs";
63873
- import * as path60 from "path";
64435
+ import * as fs50 from "fs";
64436
+ import * as path62 from "path";
63874
64437
 
63875
64438
  // node_modules/yocto-queue/index.js
63876
64439
  class Node2 {
@@ -63961,26 +64524,26 @@ function pLimit(concurrency) {
63961
64524
  activeCount--;
63962
64525
  resumeNext();
63963
64526
  };
63964
- const run2 = async (function_, resolve19, arguments_2) => {
64527
+ const run2 = async (function_, resolve21, arguments_2) => {
63965
64528
  const result = (async () => function_(...arguments_2))();
63966
- resolve19(result);
64529
+ resolve21(result);
63967
64530
  try {
63968
64531
  await result;
63969
64532
  } catch {}
63970
64533
  next();
63971
64534
  };
63972
- const enqueue = (function_, resolve19, reject, arguments_2) => {
64535
+ const enqueue = (function_, resolve21, reject, arguments_2) => {
63973
64536
  const queueItem = { reject };
63974
64537
  new Promise((internalResolve) => {
63975
64538
  queueItem.run = internalResolve;
63976
64539
  queue.enqueue(queueItem);
63977
- }).then(run2.bind(undefined, function_, resolve19, arguments_2));
64540
+ }).then(run2.bind(undefined, function_, resolve21, arguments_2));
63978
64541
  if (activeCount < concurrency) {
63979
64542
  resumeNext();
63980
64543
  }
63981
64544
  };
63982
- const generator = (function_, ...arguments_2) => new Promise((resolve19, reject) => {
63983
- enqueue(function_, resolve19, reject, arguments_2);
64545
+ const generator = (function_, ...arguments_2) => new Promise((resolve21, reject) => {
64546
+ enqueue(function_, resolve21, reject, arguments_2);
63984
64547
  });
63985
64548
  Object.defineProperties(generator, {
63986
64549
  activeCount: {
@@ -64144,9 +64707,9 @@ async function qualityBudget(input, directory) {
64144
64707
  init_dist();
64145
64708
  init_manager();
64146
64709
  init_detector();
64147
- import * as fs47 from "fs";
64148
- import * as path59 from "path";
64149
- import { extname as extname10 } from "path";
64710
+ import * as fs49 from "fs";
64711
+ import * as path61 from "path";
64712
+ import { extname as extname12 } from "path";
64150
64713
 
64151
64714
  // src/sast/rules/c.ts
64152
64715
  var cRules = [
@@ -64896,7 +65459,7 @@ function mapSemgrepSeverity(severity) {
64896
65459
  }
64897
65460
  }
64898
65461
  async function executeWithTimeout(command, args2, options) {
64899
- return new Promise((resolve19) => {
65462
+ return new Promise((resolve21) => {
64900
65463
  const child = child_process6.spawn(command, args2, {
64901
65464
  shell: false,
64902
65465
  cwd: options.cwd
@@ -64905,7 +65468,7 @@ async function executeWithTimeout(command, args2, options) {
64905
65468
  let stderr = "";
64906
65469
  const timeout = setTimeout(() => {
64907
65470
  child.kill("SIGTERM");
64908
- resolve19({
65471
+ resolve21({
64909
65472
  stdout,
64910
65473
  stderr: "Process timed out",
64911
65474
  exitCode: 124
@@ -64919,7 +65482,7 @@ async function executeWithTimeout(command, args2, options) {
64919
65482
  });
64920
65483
  child.on("close", (code) => {
64921
65484
  clearTimeout(timeout);
64922
- resolve19({
65485
+ resolve21({
64923
65486
  stdout,
64924
65487
  stderr,
64925
65488
  exitCode: code ?? 0
@@ -64927,7 +65490,7 @@ async function executeWithTimeout(command, args2, options) {
64927
65490
  });
64928
65491
  child.on("error", (err2) => {
64929
65492
  clearTimeout(timeout);
64930
- resolve19({
65493
+ resolve21({
64931
65494
  stdout,
64932
65495
  stderr: err2.message,
64933
65496
  exitCode: 1
@@ -65004,7 +65567,7 @@ async function runSemgrep(options) {
65004
65567
  // src/tools/sast-scan.ts
65005
65568
  init_utils();
65006
65569
  init_create_tool();
65007
- var MAX_FILE_SIZE_BYTES6 = 512 * 1024;
65570
+ var MAX_FILE_SIZE_BYTES7 = 512 * 1024;
65008
65571
  var MAX_FILES_SCANNED2 = 1000;
65009
65572
  var MAX_FINDINGS2 = 100;
65010
65573
  var SEVERITY_ORDER = {
@@ -65015,17 +65578,17 @@ var SEVERITY_ORDER = {
65015
65578
  };
65016
65579
  function shouldSkipFile(filePath) {
65017
65580
  try {
65018
- const stats = fs47.statSync(filePath);
65019
- if (stats.size > MAX_FILE_SIZE_BYTES6) {
65581
+ const stats = fs49.statSync(filePath);
65582
+ if (stats.size > MAX_FILE_SIZE_BYTES7) {
65020
65583
  return { skip: true, reason: "file too large" };
65021
65584
  }
65022
65585
  if (stats.size === 0) {
65023
65586
  return { skip: true, reason: "empty file" };
65024
65587
  }
65025
- const fd = fs47.openSync(filePath, "r");
65588
+ const fd = fs49.openSync(filePath, "r");
65026
65589
  const buffer = Buffer.alloc(8192);
65027
- const bytesRead = fs47.readSync(fd, buffer, 0, 8192, 0);
65028
- fs47.closeSync(fd);
65590
+ const bytesRead = fs49.readSync(fd, buffer, 0, 8192, 0);
65591
+ fs49.closeSync(fd);
65029
65592
  if (bytesRead > 0) {
65030
65593
  let nullCount = 0;
65031
65594
  for (let i2 = 0;i2 < bytesRead; i2++) {
@@ -65064,7 +65627,7 @@ function countBySeverity(findings) {
65064
65627
  }
65065
65628
  function scanFileWithTierA(filePath, language) {
65066
65629
  try {
65067
- const content = fs47.readFileSync(filePath, "utf-8");
65630
+ const content = fs49.readFileSync(filePath, "utf-8");
65068
65631
  const findings = executeRulesSync(filePath, content, language);
65069
65632
  return findings.map((f) => ({
65070
65633
  rule_id: f.rule_id,
@@ -65111,13 +65674,13 @@ async function sastScan(input, directory, config3) {
65111
65674
  _filesSkipped++;
65112
65675
  continue;
65113
65676
  }
65114
- const resolvedPath = path59.isAbsolute(filePath) ? filePath : path59.resolve(directory, filePath);
65115
- const resolvedDirectory = path59.resolve(directory);
65116
- if (!resolvedPath.startsWith(resolvedDirectory + path59.sep) && resolvedPath !== resolvedDirectory) {
65677
+ const resolvedPath = path61.isAbsolute(filePath) ? filePath : path61.resolve(directory, filePath);
65678
+ const resolvedDirectory = path61.resolve(directory);
65679
+ if (!resolvedPath.startsWith(resolvedDirectory + path61.sep) && resolvedPath !== resolvedDirectory) {
65117
65680
  _filesSkipped++;
65118
65681
  continue;
65119
65682
  }
65120
- if (!fs47.existsSync(resolvedPath)) {
65683
+ if (!fs49.existsSync(resolvedPath)) {
65121
65684
  _filesSkipped++;
65122
65685
  continue;
65123
65686
  }
@@ -65126,7 +65689,7 @@ async function sastScan(input, directory, config3) {
65126
65689
  _filesSkipped++;
65127
65690
  continue;
65128
65691
  }
65129
- const ext = extname10(resolvedPath).toLowerCase();
65692
+ const ext = extname12(resolvedPath).toLowerCase();
65130
65693
  const profile = getProfileForFile(resolvedPath);
65131
65694
  const langDef = getLanguageForExtension(ext);
65132
65695
  if (!profile && !langDef) {
@@ -65315,20 +65878,20 @@ function validatePath(inputPath, baseDir, workspaceDir) {
65315
65878
  let resolved;
65316
65879
  const isWinAbs = isWindowsAbsolutePath(inputPath);
65317
65880
  if (isWinAbs) {
65318
- resolved = path60.win32.resolve(inputPath);
65319
- } else if (path60.isAbsolute(inputPath)) {
65320
- resolved = path60.resolve(inputPath);
65881
+ resolved = path62.win32.resolve(inputPath);
65882
+ } else if (path62.isAbsolute(inputPath)) {
65883
+ resolved = path62.resolve(inputPath);
65321
65884
  } else {
65322
- resolved = path60.resolve(baseDir, inputPath);
65885
+ resolved = path62.resolve(baseDir, inputPath);
65323
65886
  }
65324
- const workspaceResolved = path60.resolve(workspaceDir);
65325
- let relative9;
65887
+ const workspaceResolved = path62.resolve(workspaceDir);
65888
+ let relative11;
65326
65889
  if (isWinAbs) {
65327
- relative9 = path60.win32.relative(workspaceResolved, resolved);
65890
+ relative11 = path62.win32.relative(workspaceResolved, resolved);
65328
65891
  } else {
65329
- relative9 = path60.relative(workspaceResolved, resolved);
65892
+ relative11 = path62.relative(workspaceResolved, resolved);
65330
65893
  }
65331
- if (relative9.startsWith("..")) {
65894
+ if (relative11.startsWith("..")) {
65332
65895
  return "path traversal detected";
65333
65896
  }
65334
65897
  return null;
@@ -65391,7 +65954,7 @@ async function runLintOnFiles(linter, files, workspaceDir) {
65391
65954
  if (typeof file3 !== "string") {
65392
65955
  continue;
65393
65956
  }
65394
- const resolvedPath = path60.resolve(file3);
65957
+ const resolvedPath = path62.resolve(file3);
65395
65958
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
65396
65959
  if (validationError) {
65397
65960
  continue;
@@ -65484,7 +66047,7 @@ async function runSecretscanWrapped(files, directory, _config) {
65484
66047
  }
65485
66048
  }
65486
66049
  async function runSecretscanWithFiles(files, directory) {
65487
- const MAX_FILE_SIZE_BYTES7 = 512 * 1024;
66050
+ const MAX_FILE_SIZE_BYTES8 = 512 * 1024;
65488
66051
  const MAX_FINDINGS3 = 100;
65489
66052
  const DEFAULT_EXCLUDE_EXTENSIONS2 = new Set([
65490
66053
  ".png",
@@ -65548,7 +66111,7 @@ async function runSecretscanWithFiles(files, directory) {
65548
66111
  skippedFiles++;
65549
66112
  continue;
65550
66113
  }
65551
- const resolvedPath = path60.resolve(file3);
66114
+ const resolvedPath = path62.resolve(file3);
65552
66115
  const validationError = validatePath(resolvedPath, directory, directory);
65553
66116
  if (validationError) {
65554
66117
  skippedFiles++;
@@ -65566,25 +66129,25 @@ async function runSecretscanWithFiles(files, directory) {
65566
66129
  };
65567
66130
  }
65568
66131
  for (const file3 of validatedFiles) {
65569
- const ext = path60.extname(file3).toLowerCase();
66132
+ const ext = path62.extname(file3).toLowerCase();
65570
66133
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
65571
66134
  skippedFiles++;
65572
66135
  continue;
65573
66136
  }
65574
66137
  let stat2;
65575
66138
  try {
65576
- stat2 = fs48.statSync(file3);
66139
+ stat2 = fs50.statSync(file3);
65577
66140
  } catch {
65578
66141
  skippedFiles++;
65579
66142
  continue;
65580
66143
  }
65581
- if (stat2.size > MAX_FILE_SIZE_BYTES7) {
66144
+ if (stat2.size > MAX_FILE_SIZE_BYTES8) {
65582
66145
  skippedFiles++;
65583
66146
  continue;
65584
66147
  }
65585
66148
  let content;
65586
66149
  try {
65587
- const buffer = fs48.readFileSync(file3);
66150
+ const buffer = fs50.readFileSync(file3);
65588
66151
  if (buffer.includes(0)) {
65589
66152
  skippedFiles++;
65590
66153
  continue;
@@ -65772,7 +66335,7 @@ function classifySastFindings(findings, changedLineRanges, directory) {
65772
66335
  const preexistingFindings = [];
65773
66336
  for (const finding of findings) {
65774
66337
  const filePath = finding.location.file;
65775
- const normalised = path60.relative(directory, filePath).replace(/\\/g, "/");
66338
+ const normalised = path62.relative(directory, filePath).replace(/\\/g, "/");
65776
66339
  const changedLines = changedLineRanges.get(normalised);
65777
66340
  if (changedLines && changedLines.has(finding.location.line)) {
65778
66341
  newFindings.push(finding);
@@ -65823,7 +66386,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
65823
66386
  warn(`pre_check_batch: Invalid file path: ${file3}`);
65824
66387
  continue;
65825
66388
  }
65826
- changedFiles.push(path60.resolve(directory, file3));
66389
+ changedFiles.push(path62.resolve(directory, file3));
65827
66390
  }
65828
66391
  if (changedFiles.length === 0) {
65829
66392
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -66011,7 +66574,7 @@ var pre_check_batch = createSwarmTool({
66011
66574
  };
66012
66575
  return JSON.stringify(errorResult, null, 2);
66013
66576
  }
66014
- const resolvedDirectory = path60.resolve(typedArgs.directory);
66577
+ const resolvedDirectory = path62.resolve(typedArgs.directory);
66015
66578
  const workspaceAnchor = resolvedDirectory;
66016
66579
  const dirError = validateDirectory2(resolvedDirectory, workspaceAnchor);
66017
66580
  if (dirError) {
@@ -66117,31 +66680,31 @@ ${paginatedContent}`;
66117
66680
  });
66118
66681
  // src/tools/save-plan.ts
66119
66682
  init_tool();
66120
- import * as fs50 from "fs";
66121
- import * as path62 from "path";
66683
+ import * as fs52 from "fs";
66684
+ import * as path64 from "path";
66122
66685
 
66123
66686
  // src/parallel/file-locks.ts
66124
66687
  var import_proper_lockfile3 = __toESM(require_proper_lockfile(), 1);
66125
- import * as fs49 from "fs";
66126
- import * as path61 from "path";
66688
+ import * as fs51 from "fs";
66689
+ import * as path63 from "path";
66127
66690
  var LOCKS_DIR = ".swarm/locks";
66128
66691
  var LOCK_TIMEOUT_MS = 5 * 60 * 1000;
66129
66692
  function getLockFilePath(directory, filePath) {
66130
- const normalized = path61.resolve(directory, filePath);
66131
- if (!normalized.startsWith(path61.resolve(directory))) {
66693
+ const normalized = path63.resolve(directory, filePath);
66694
+ if (!normalized.startsWith(path63.resolve(directory))) {
66132
66695
  throw new Error("Invalid file path: path traversal not allowed");
66133
66696
  }
66134
66697
  const hash3 = Buffer.from(normalized).toString("base64").replace(/[/+=]/g, "_");
66135
- return path61.join(directory, LOCKS_DIR, `${hash3}.lock`);
66698
+ return path63.join(directory, LOCKS_DIR, `${hash3}.lock`);
66136
66699
  }
66137
66700
  async function tryAcquireLock(directory, filePath, agent, taskId) {
66138
66701
  const lockPath = getLockFilePath(directory, filePath);
66139
- const locksDir = path61.dirname(lockPath);
66140
- if (!fs49.existsSync(locksDir)) {
66141
- fs49.mkdirSync(locksDir, { recursive: true });
66702
+ const locksDir = path63.dirname(lockPath);
66703
+ if (!fs51.existsSync(locksDir)) {
66704
+ fs51.mkdirSync(locksDir, { recursive: true });
66142
66705
  }
66143
- if (!fs49.existsSync(lockPath)) {
66144
- fs49.writeFileSync(lockPath, "", "utf-8");
66706
+ if (!fs51.existsSync(lockPath)) {
66707
+ fs51.writeFileSync(lockPath, "", "utf-8");
66145
66708
  }
66146
66709
  let release;
66147
66710
  try {
@@ -66289,14 +66852,14 @@ async function executeSavePlan(args2, fallbackDir) {
66289
66852
  await savePlan(dir, plan);
66290
66853
  await writeCheckpoint(dir).catch(() => {});
66291
66854
  try {
66292
- const markerPath = path62.join(dir, ".swarm", ".plan-write-marker");
66855
+ const markerPath = path64.join(dir, ".swarm", ".plan-write-marker");
66293
66856
  const marker = JSON.stringify({
66294
66857
  source: "save_plan",
66295
66858
  timestamp: new Date().toISOString(),
66296
66859
  phases_count: plan.phases.length,
66297
66860
  tasks_count: tasksCount
66298
66861
  });
66299
- await fs50.promises.writeFile(markerPath, marker, "utf8");
66862
+ await fs52.promises.writeFile(markerPath, marker, "utf8");
66300
66863
  } catch {}
66301
66864
  const warnings = [];
66302
66865
  let criticReviewFound = false;
@@ -66312,7 +66875,7 @@ async function executeSavePlan(args2, fallbackDir) {
66312
66875
  return {
66313
66876
  success: true,
66314
66877
  message: "Plan saved successfully",
66315
- plan_path: path62.join(dir, ".swarm", "plan.json"),
66878
+ plan_path: path64.join(dir, ".swarm", "plan.json"),
66316
66879
  phases_count: plan.phases.length,
66317
66880
  tasks_count: tasksCount,
66318
66881
  ...warnings.length > 0 ? { warnings } : {}
@@ -66356,8 +66919,8 @@ var save_plan = createSwarmTool({
66356
66919
  // src/tools/sbom-generate.ts
66357
66920
  init_dist();
66358
66921
  init_manager();
66359
- import * as fs51 from "fs";
66360
- import * as path63 from "path";
66922
+ import * as fs53 from "fs";
66923
+ import * as path65 from "path";
66361
66924
 
66362
66925
  // src/sbom/detectors/index.ts
66363
66926
  init_utils();
@@ -67205,9 +67768,9 @@ function findManifestFiles(rootDir) {
67205
67768
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
67206
67769
  function searchDir(dir) {
67207
67770
  try {
67208
- const entries = fs51.readdirSync(dir, { withFileTypes: true });
67771
+ const entries = fs53.readdirSync(dir, { withFileTypes: true });
67209
67772
  for (const entry of entries) {
67210
- const fullPath = path63.join(dir, entry.name);
67773
+ const fullPath = path65.join(dir, entry.name);
67211
67774
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
67212
67775
  continue;
67213
67776
  }
@@ -67216,7 +67779,7 @@ function findManifestFiles(rootDir) {
67216
67779
  } else if (entry.isFile()) {
67217
67780
  for (const pattern of patterns) {
67218
67781
  if (simpleGlobToRegex(pattern).test(entry.name)) {
67219
- manifestFiles.push(path63.relative(rootDir, fullPath));
67782
+ manifestFiles.push(path65.relative(rootDir, fullPath));
67220
67783
  break;
67221
67784
  }
67222
67785
  }
@@ -67232,13 +67795,13 @@ function findManifestFilesInDirs(directories, workingDir) {
67232
67795
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
67233
67796
  for (const dir of directories) {
67234
67797
  try {
67235
- const entries = fs51.readdirSync(dir, { withFileTypes: true });
67798
+ const entries = fs53.readdirSync(dir, { withFileTypes: true });
67236
67799
  for (const entry of entries) {
67237
- const fullPath = path63.join(dir, entry.name);
67800
+ const fullPath = path65.join(dir, entry.name);
67238
67801
  if (entry.isFile()) {
67239
67802
  for (const pattern of patterns) {
67240
67803
  if (simpleGlobToRegex(pattern).test(entry.name)) {
67241
- found.push(path63.relative(workingDir, fullPath));
67804
+ found.push(path65.relative(workingDir, fullPath));
67242
67805
  break;
67243
67806
  }
67244
67807
  }
@@ -67251,11 +67814,11 @@ function findManifestFilesInDirs(directories, workingDir) {
67251
67814
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
67252
67815
  const dirs = new Set;
67253
67816
  for (const file3 of changedFiles) {
67254
- let currentDir = path63.dirname(file3);
67817
+ let currentDir = path65.dirname(file3);
67255
67818
  while (true) {
67256
- if (currentDir && currentDir !== "." && currentDir !== path63.sep) {
67257
- dirs.add(path63.join(workingDir, currentDir));
67258
- const parent = path63.dirname(currentDir);
67819
+ if (currentDir && currentDir !== "." && currentDir !== path65.sep) {
67820
+ dirs.add(path65.join(workingDir, currentDir));
67821
+ const parent = path65.dirname(currentDir);
67259
67822
  if (parent === currentDir)
67260
67823
  break;
67261
67824
  currentDir = parent;
@@ -67269,7 +67832,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
67269
67832
  }
67270
67833
  function ensureOutputDir(outputDir) {
67271
67834
  try {
67272
- fs51.mkdirSync(outputDir, { recursive: true });
67835
+ fs53.mkdirSync(outputDir, { recursive: true });
67273
67836
  } catch (error93) {
67274
67837
  if (!error93 || error93.code !== "EEXIST") {
67275
67838
  throw error93;
@@ -67339,7 +67902,7 @@ var sbom_generate = createSwarmTool({
67339
67902
  const changedFiles = obj.changed_files;
67340
67903
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
67341
67904
  const workingDir = directory;
67342
- const outputDir = path63.isAbsolute(relativeOutputDir) ? relativeOutputDir : path63.join(workingDir, relativeOutputDir);
67905
+ const outputDir = path65.isAbsolute(relativeOutputDir) ? relativeOutputDir : path65.join(workingDir, relativeOutputDir);
67343
67906
  let manifestFiles = [];
67344
67907
  if (scope === "all") {
67345
67908
  manifestFiles = findManifestFiles(workingDir);
@@ -67362,11 +67925,11 @@ var sbom_generate = createSwarmTool({
67362
67925
  const processedFiles = [];
67363
67926
  for (const manifestFile of manifestFiles) {
67364
67927
  try {
67365
- const fullPath = path63.isAbsolute(manifestFile) ? manifestFile : path63.join(workingDir, manifestFile);
67366
- if (!fs51.existsSync(fullPath)) {
67928
+ const fullPath = path65.isAbsolute(manifestFile) ? manifestFile : path65.join(workingDir, manifestFile);
67929
+ if (!fs53.existsSync(fullPath)) {
67367
67930
  continue;
67368
67931
  }
67369
- const content = fs51.readFileSync(fullPath, "utf-8");
67932
+ const content = fs53.readFileSync(fullPath, "utf-8");
67370
67933
  const components = detectComponents(manifestFile, content);
67371
67934
  processedFiles.push(manifestFile);
67372
67935
  if (components.length > 0) {
@@ -67379,8 +67942,8 @@ var sbom_generate = createSwarmTool({
67379
67942
  const bom = generateCycloneDX(allComponents);
67380
67943
  const bomJson = serializeCycloneDX(bom);
67381
67944
  const filename = generateSbomFilename();
67382
- const outputPath = path63.join(outputDir, filename);
67383
- fs51.writeFileSync(outputPath, bomJson, "utf-8");
67945
+ const outputPath = path65.join(outputDir, filename);
67946
+ fs53.writeFileSync(outputPath, bomJson, "utf-8");
67384
67947
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
67385
67948
  try {
67386
67949
  const timestamp = new Date().toISOString();
@@ -67422,8 +67985,8 @@ var sbom_generate = createSwarmTool({
67422
67985
  // src/tools/schema-drift.ts
67423
67986
  init_dist();
67424
67987
  init_create_tool();
67425
- import * as fs52 from "fs";
67426
- import * as path64 from "path";
67988
+ import * as fs54 from "fs";
67989
+ import * as path66 from "path";
67427
67990
  var SPEC_CANDIDATES = [
67428
67991
  "openapi.json",
67429
67992
  "openapi.yaml",
@@ -67455,28 +68018,28 @@ function normalizePath2(p) {
67455
68018
  }
67456
68019
  function discoverSpecFile(cwd, specFileArg) {
67457
68020
  if (specFileArg) {
67458
- const resolvedPath = path64.resolve(cwd, specFileArg);
67459
- const normalizedCwd = cwd.endsWith(path64.sep) ? cwd : cwd + path64.sep;
68021
+ const resolvedPath = path66.resolve(cwd, specFileArg);
68022
+ const normalizedCwd = cwd.endsWith(path66.sep) ? cwd : cwd + path66.sep;
67460
68023
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
67461
68024
  throw new Error("Invalid spec_file: path traversal detected");
67462
68025
  }
67463
- const ext = path64.extname(resolvedPath).toLowerCase();
68026
+ const ext = path66.extname(resolvedPath).toLowerCase();
67464
68027
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
67465
68028
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
67466
68029
  }
67467
- const stats = fs52.statSync(resolvedPath);
68030
+ const stats = fs54.statSync(resolvedPath);
67468
68031
  if (stats.size > MAX_SPEC_SIZE) {
67469
68032
  throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
67470
68033
  }
67471
- if (!fs52.existsSync(resolvedPath)) {
68034
+ if (!fs54.existsSync(resolvedPath)) {
67472
68035
  throw new Error(`Spec file not found: ${resolvedPath}`);
67473
68036
  }
67474
68037
  return resolvedPath;
67475
68038
  }
67476
68039
  for (const candidate of SPEC_CANDIDATES) {
67477
- const candidatePath = path64.resolve(cwd, candidate);
67478
- if (fs52.existsSync(candidatePath)) {
67479
- const stats = fs52.statSync(candidatePath);
68040
+ const candidatePath = path66.resolve(cwd, candidate);
68041
+ if (fs54.existsSync(candidatePath)) {
68042
+ const stats = fs54.statSync(candidatePath);
67480
68043
  if (stats.size <= MAX_SPEC_SIZE) {
67481
68044
  return candidatePath;
67482
68045
  }
@@ -67485,8 +68048,8 @@ function discoverSpecFile(cwd, specFileArg) {
67485
68048
  return null;
67486
68049
  }
67487
68050
  function parseSpec(specFile) {
67488
- const content = fs52.readFileSync(specFile, "utf-8");
67489
- const ext = path64.extname(specFile).toLowerCase();
68051
+ const content = fs54.readFileSync(specFile, "utf-8");
68052
+ const ext = path66.extname(specFile).toLowerCase();
67490
68053
  if (ext === ".json") {
67491
68054
  return parseJsonSpec(content);
67492
68055
  }
@@ -67557,12 +68120,12 @@ function extractRoutes(cwd) {
67557
68120
  function walkDir(dir) {
67558
68121
  let entries;
67559
68122
  try {
67560
- entries = fs52.readdirSync(dir, { withFileTypes: true });
68123
+ entries = fs54.readdirSync(dir, { withFileTypes: true });
67561
68124
  } catch {
67562
68125
  return;
67563
68126
  }
67564
68127
  for (const entry of entries) {
67565
- const fullPath = path64.join(dir, entry.name);
68128
+ const fullPath = path66.join(dir, entry.name);
67566
68129
  if (entry.isSymbolicLink()) {
67567
68130
  continue;
67568
68131
  }
@@ -67572,7 +68135,7 @@ function extractRoutes(cwd) {
67572
68135
  }
67573
68136
  walkDir(fullPath);
67574
68137
  } else if (entry.isFile()) {
67575
- const ext = path64.extname(entry.name).toLowerCase();
68138
+ const ext = path66.extname(entry.name).toLowerCase();
67576
68139
  const baseName = entry.name.toLowerCase();
67577
68140
  if (![".ts", ".js", ".mjs"].includes(ext)) {
67578
68141
  continue;
@@ -67590,7 +68153,7 @@ function extractRoutes(cwd) {
67590
68153
  }
67591
68154
  function extractRoutesFromFile(filePath) {
67592
68155
  const routes = [];
67593
- const content = fs52.readFileSync(filePath, "utf-8");
68156
+ const content = fs54.readFileSync(filePath, "utf-8");
67594
68157
  const lines = content.split(/\r?\n/);
67595
68158
  const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
67596
68159
  const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
@@ -67734,36 +68297,51 @@ var schema_drift = createSwarmTool({
67734
68297
  }
67735
68298
  }
67736
68299
  });
67737
-
67738
- // src/tools/index.ts
67739
- init_secretscan();
67740
-
67741
- // src/tools/symbols.ts
68300
+ // src/tools/search.ts
67742
68301
  init_tool();
67743
68302
  init_create_tool();
67744
- import * as fs53 from "fs";
67745
- import * as path65 from "path";
67746
- var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
67747
- var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
67748
- function containsWindowsAttacks(str) {
67749
- if (/:[^\\/]/.test(str)) {
67750
- return true;
68303
+ import * as fs55 from "fs";
68304
+ import * as path67 from "path";
68305
+ var DEFAULT_MAX_RESULTS = 100;
68306
+ var DEFAULT_MAX_LINES = 200;
68307
+ var REGEX_TIMEOUT_MS = 5000;
68308
+ var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
68309
+ var HARD_CAP_RESULTS = 1e4;
68310
+ var HARD_CAP_LINES = 1e4;
68311
+ function globMatch(pattern, filePath) {
68312
+ const normalizedPattern = pattern.replace(/\\/g, "/");
68313
+ const normalizedPath = filePath.replace(/\\/g, "/");
68314
+ const regexPattern = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{DOUBLESTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{\{DOUBLESTAR\}\}/g, ".*");
68315
+ try {
68316
+ const regex = new RegExp(`^${regexPattern}$`);
68317
+ return regex.test(normalizedPath);
68318
+ } catch {
68319
+ return false;
67751
68320
  }
68321
+ }
68322
+ function matchesGlobs(filePath, globs) {
68323
+ if (globs.length === 0)
68324
+ return true;
68325
+ return globs.some((glob) => globMatch(glob, filePath));
68326
+ }
68327
+ var WINDOWS_RESERVED_NAMES3 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
68328
+ function containsWindowsAttacks3(str) {
68329
+ if (/:[^\\/]/.test(str))
68330
+ return true;
67752
68331
  const parts2 = str.split(/[/\\]/);
67753
68332
  for (const part of parts2) {
67754
- if (WINDOWS_RESERVED_NAMES.test(part)) {
68333
+ if (WINDOWS_RESERVED_NAMES3.test(part))
67755
68334
  return true;
67756
- }
67757
68335
  }
67758
68336
  return false;
67759
68337
  }
67760
- function isPathInWorkspace(filePath, workspace) {
68338
+ function isPathInWorkspace3(filePath, workspace) {
67761
68339
  try {
67762
- const resolvedPath = path65.resolve(workspace, filePath);
67763
- const realWorkspace = fs53.realpathSync(workspace);
67764
- const realResolvedPath = fs53.realpathSync(resolvedPath);
67765
- const relativePath = path65.relative(realWorkspace, realResolvedPath);
67766
- if (relativePath.startsWith("..") || path65.isAbsolute(relativePath)) {
68340
+ const resolvedPath = path67.resolve(workspace, filePath);
68341
+ const realWorkspace = fs55.realpathSync(workspace);
68342
+ const realResolvedPath = fs55.realpathSync(resolvedPath);
68343
+ const relativePath = path67.relative(realWorkspace, realResolvedPath);
68344
+ if (relativePath.startsWith("..") || path67.isAbsolute(relativePath)) {
67767
68345
  return false;
67768
68346
  }
67769
68347
  return true;
@@ -67771,301 +68349,675 @@ function isPathInWorkspace(filePath, workspace) {
67771
68349
  return false;
67772
68350
  }
67773
68351
  }
67774
- function validatePathForRead(filePath, workspace) {
67775
- return isPathInWorkspace(filePath, workspace);
68352
+ function validatePathForRead2(filePath, workspace) {
68353
+ return isPathInWorkspace3(filePath, workspace);
67776
68354
  }
67777
- function extractTSSymbols(filePath, cwd) {
67778
- const fullPath = path65.join(cwd, filePath);
67779
- if (!validatePathForRead(fullPath, cwd)) {
67780
- return [];
68355
+ function findRgInEnvPath() {
68356
+ const searchPath = process.env.PATH ?? "";
68357
+ for (const dir of searchPath.split(path67.delimiter)) {
68358
+ if (!dir)
68359
+ continue;
68360
+ const isWindows = process.platform === "win32";
68361
+ const candidate = path67.join(dir, isWindows ? "rg.exe" : "rg");
68362
+ if (fs55.existsSync(candidate))
68363
+ return candidate;
67781
68364
  }
67782
- let content;
68365
+ return null;
68366
+ }
68367
+ async function isRipgrepAvailable() {
68368
+ const rgPath = findRgInEnvPath();
68369
+ if (!rgPath)
68370
+ return false;
67783
68371
  try {
67784
- const stats = fs53.statSync(fullPath);
67785
- if (stats.size > MAX_FILE_SIZE_BYTES7) {
67786
- throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
67787
- }
67788
- content = fs53.readFileSync(fullPath, "utf-8");
68372
+ const proc = Bun.spawn([rgPath, "--version"], {
68373
+ stdout: "pipe",
68374
+ stderr: "pipe"
68375
+ });
68376
+ const exitCode = await proc.exited;
68377
+ return exitCode === 0;
67789
68378
  } catch {
67790
- return [];
68379
+ return false;
67791
68380
  }
67792
- const lines = content.split(`
67793
- `);
67794
- const symbols = [];
67795
- for (let i2 = 0;i2 < lines.length; i2++) {
67796
- const line = lines[i2];
67797
- const lineNum = i2 + 1;
67798
- let jsdoc;
67799
- if (i2 > 0 && lines[i2 - 1].trim().endsWith("*/")) {
67800
- const jsdocLines = [];
67801
- for (let j = i2 - 1;j >= 0; j--) {
67802
- jsdocLines.unshift(lines[j]);
67803
- if (lines[j].trim().startsWith("/**"))
67804
- break;
67805
- }
67806
- jsdoc = jsdocLines.join(`
67807
- `).trim();
67808
- if (jsdoc.length > 300)
67809
- jsdoc = `${jsdoc.substring(0, 300)}...`;
68381
+ }
68382
+ async function ripgrepSearch(opts) {
68383
+ const rgPath = findRgInEnvPath();
68384
+ if (!rgPath) {
68385
+ return {
68386
+ error: true,
68387
+ type: "rg-not-found",
68388
+ message: "ripgrep (rg) not found in PATH"
68389
+ };
68390
+ }
68391
+ const args2 = [
68392
+ "--json",
68393
+ "-n"
68394
+ ];
68395
+ if (opts.include) {
68396
+ for (const pattern of opts.include.split(",")) {
68397
+ args2.push("--glob", pattern.trim());
67810
68398
  }
67811
- const fnMatch = line.match(/^export\s+(?:async\s+)?function\s+(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)(?:\s*:\s*(.+?))?(?:\s*\{|$)/);
67812
- if (fnMatch) {
67813
- symbols.push({
67814
- name: fnMatch[1],
67815
- kind: "function",
67816
- exported: true,
67817
- signature: `function ${fnMatch[1]}${fnMatch[2] || ""}(${fnMatch[3].trim()})${fnMatch[4] ? `: ${fnMatch[4].trim()}` : ""}`,
67818
- line: lineNum,
67819
- jsdoc
67820
- });
67821
- continue;
68399
+ }
68400
+ if (opts.exclude) {
68401
+ for (const pattern of opts.exclude.split(",")) {
68402
+ args2.push("--glob", `!${pattern.trim()}`);
67822
68403
  }
67823
- const constMatch = line.match(/^export\s+const\s+(\w+)(?:\s*:\s*(.+?))?\s*=/);
67824
- if (constMatch) {
67825
- const restOfLine = line.substring(line.indexOf("=") + 1).trim();
67826
- const isArrow = restOfLine.startsWith("(") || restOfLine.startsWith("async (") || restOfLine.match(/^\w+\s*=>/);
67827
- symbols.push({
67828
- name: constMatch[1],
67829
- kind: isArrow ? "function" : "const",
67830
- exported: true,
67831
- signature: `const ${constMatch[1]}${constMatch[2] ? `: ${constMatch[2].trim()}` : ""}`,
67832
- line: lineNum,
67833
- jsdoc
67834
- });
67835
- continue;
68404
+ }
68405
+ if (opts.mode !== "regex") {
68406
+ args2.push("--fixed-strings");
68407
+ }
68408
+ args2.push(opts.query);
68409
+ args2.push(opts.workspace);
68410
+ try {
68411
+ const proc = Bun.spawn([rgPath, ...args2], {
68412
+ stdout: "pipe",
68413
+ stderr: "pipe",
68414
+ cwd: opts.workspace
68415
+ });
68416
+ const timeout = new Promise((resolve26) => setTimeout(() => resolve26("timeout"), REGEX_TIMEOUT_MS));
68417
+ const exitPromise = proc.exited;
68418
+ const result = await Promise.race([exitPromise, timeout]);
68419
+ if (result === "timeout") {
68420
+ proc.kill();
68421
+ return {
68422
+ error: true,
68423
+ type: "regex-timeout",
68424
+ message: `Regex search timed out after ${REGEX_TIMEOUT_MS}ms`
68425
+ };
67836
68426
  }
67837
- const classMatch = line.match(/^export\s+(?:abstract\s+)?class\s+(\w+)(?:\s+(?:extends|implements)\s+(.+?))?(?:\s*\{|$)/);
67838
- if (classMatch) {
67839
- symbols.push({
67840
- name: classMatch[1],
67841
- kind: "class",
67842
- exported: true,
67843
- signature: `class ${classMatch[1]}${classMatch[2] ? ` extends/implements ${classMatch[2].trim()}` : ""}`,
67844
- line: lineNum,
67845
- jsdoc
67846
- });
67847
- let braceDepth = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
67848
- for (let j = i2 + 1;j < lines.length && braceDepth > 0; j++) {
67849
- const memberLine = lines[j];
67850
- braceDepth += (memberLine.match(/\{/g) || []).length - (memberLine.match(/\}/g) || []).length;
67851
- const methodMatch = memberLine.match(/^\s+(?:public\s+)?(?:async\s+)?(\w+)\s*\(([^)]*)\)(?:\s*:\s*(.+?))?(?:\s*\{|;|$)/);
67852
- if (methodMatch && !memberLine.includes("private") && !memberLine.includes("protected") && !memberLine.trim().startsWith("//")) {
67853
- symbols.push({
67854
- name: `${classMatch[1]}.${methodMatch[1]}`,
67855
- kind: "method",
67856
- exported: true,
67857
- signature: `${methodMatch[1]}(${methodMatch[2].trim()})${methodMatch[3] ? `: ${methodMatch[3].trim()}` : ""}`,
67858
- line: j + 1
67859
- });
68427
+ const stdout = await new Response(proc.stdout).text();
68428
+ const stderr = await new Response(proc.stderr).text();
68429
+ if (proc.exitCode !== 0 && stderr) {
68430
+ if (stderr.includes("Invalid regex") || stderr.includes("SyntaxError")) {
68431
+ return {
68432
+ error: true,
68433
+ type: "invalid-query",
68434
+ message: `Invalid query: ${stderr.split(`
68435
+ `)[0]}`
68436
+ };
68437
+ }
68438
+ }
68439
+ const matches = [];
68440
+ let total = 0;
68441
+ for (const line of stdout.split(`
68442
+ `)) {
68443
+ if (!line.trim())
68444
+ continue;
68445
+ try {
68446
+ const entry = JSON.parse(line);
68447
+ if (entry.type === "match") {
68448
+ total++;
68449
+ if (matches.length < opts.maxResults) {
68450
+ let lineText = entry.data.lines.text.trimEnd();
68451
+ if (lineText.length > opts.maxLines) {
68452
+ lineText = `${lineText.substring(0, opts.maxLines)}...`;
68453
+ }
68454
+ const match = {
68455
+ file: entry.data.path.text || entry.data.path,
68456
+ lineNumber: entry.data.line_number,
68457
+ lineText
68458
+ };
68459
+ matches.push(match);
68460
+ }
67860
68461
  }
67861
- const propMatch = memberLine.match(/^\s+(?:public\s+)?(?:readonly\s+)?(\w+)(?:\?)?:\s*(.+?)(?:\s*[;=]|$)/);
67862
- if (propMatch && !memberLine.includes("private") && !memberLine.includes("protected") && !memberLine.trim().startsWith("//")) {
67863
- symbols.push({
67864
- name: `${classMatch[1]}.${propMatch[1]}`,
67865
- kind: "property",
67866
- exported: true,
67867
- signature: `${propMatch[1]}: ${propMatch[2].trim()}`,
67868
- line: j + 1
67869
- });
68462
+ } catch {}
68463
+ }
68464
+ return {
68465
+ matches,
68466
+ truncated: total > opts.maxResults,
68467
+ total,
68468
+ query: opts.query,
68469
+ mode: opts.mode,
68470
+ maxResults: opts.maxResults
68471
+ };
68472
+ } catch (err2) {
68473
+ return {
68474
+ error: true,
68475
+ type: "unknown",
68476
+ message: err2 instanceof Error ? err2.message : String(err2)
68477
+ };
68478
+ }
68479
+ }
68480
+ function escapeRegex4(str) {
68481
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
68482
+ }
68483
+ function collectFiles(dir, workspace, includeGlobs, excludeGlobs) {
68484
+ const files = [];
68485
+ if (!validatePathForRead2(dir, workspace)) {
68486
+ return files;
68487
+ }
68488
+ try {
68489
+ const entries = fs55.readdirSync(dir, { withFileTypes: true });
68490
+ for (const entry of entries) {
68491
+ const fullPath = path67.join(dir, entry.name);
68492
+ const relativePath = path67.relative(workspace, fullPath);
68493
+ if (!validatePathForRead2(fullPath, workspace)) {
68494
+ continue;
68495
+ }
68496
+ if (entry.isDirectory()) {
68497
+ const subFiles = collectFiles(fullPath, workspace, includeGlobs, excludeGlobs);
68498
+ files.push(...subFiles);
68499
+ } else if (entry.isFile()) {
68500
+ if (includeGlobs.length > 0 && !matchesGlobs(relativePath, includeGlobs)) {
68501
+ continue;
67870
68502
  }
68503
+ if (excludeGlobs.length > 0 && matchesGlobs(relativePath, excludeGlobs)) {
68504
+ continue;
68505
+ }
68506
+ files.push(relativePath);
67871
68507
  }
67872
- continue;
67873
68508
  }
67874
- const ifaceMatch = line.match(/^export\s+interface\s+(\w+)(?:\s*<([^>]+)>)?(?:\s+extends\s+(.+?))?(?:\s*\{|$)/);
67875
- if (ifaceMatch) {
67876
- symbols.push({
67877
- name: ifaceMatch[1],
67878
- kind: "interface",
67879
- exported: true,
67880
- signature: `interface ${ifaceMatch[1]}${ifaceMatch[2] ? `<${ifaceMatch[2]}>` : ""}${ifaceMatch[3] ? ` extends ${ifaceMatch[3].trim()}` : ""}`,
67881
- line: lineNum,
67882
- jsdoc
67883
- });
68509
+ } catch {}
68510
+ return files;
68511
+ }
68512
+ async function fallbackSearch(opts) {
68513
+ const includeGlobs = opts.include ? opts.include.split(",").map((p) => p.trim()).filter(Boolean) : [];
68514
+ const excludeGlobs = opts.exclude ? opts.exclude.split(",").map((p) => p.trim()).filter(Boolean) : [];
68515
+ const files = collectFiles(opts.workspace, opts.workspace, includeGlobs, excludeGlobs);
68516
+ let regex;
68517
+ try {
68518
+ if (opts.mode === "regex") {
68519
+ regex = new RegExp(opts.query);
68520
+ } else {
68521
+ regex = new RegExp(escapeRegex4(opts.query));
68522
+ }
68523
+ } catch (err2) {
68524
+ return {
68525
+ error: true,
68526
+ type: "invalid-query",
68527
+ message: err2 instanceof Error ? err2.message : "Invalid regex pattern"
68528
+ };
68529
+ }
68530
+ const matches = [];
68531
+ let total = 0;
68532
+ for (const file3 of files) {
68533
+ const fullPath = path67.join(opts.workspace, file3);
68534
+ if (!validatePathForRead2(fullPath, opts.workspace)) {
67884
68535
  continue;
67885
68536
  }
67886
- const typeMatch = line.match(/^export\s+type\s+(\w+)(?:\s*<([^>]+)>)?\s*=/);
67887
- if (typeMatch) {
67888
- const typeValue = line.substring(line.indexOf("=") + 1).trim().substring(0, 100);
67889
- symbols.push({
67890
- name: typeMatch[1],
67891
- kind: "type",
67892
- exported: true,
67893
- signature: `type ${typeMatch[1]}${typeMatch[2] ? `<${typeMatch[2]}>` : ""} = ${typeValue}`,
67894
- line: lineNum,
67895
- jsdoc
67896
- });
68537
+ let stats;
68538
+ try {
68539
+ stats = fs55.statSync(fullPath);
68540
+ if (stats.size > MAX_FILE_SIZE_BYTES8) {
68541
+ continue;
68542
+ }
68543
+ } catch {
67897
68544
  continue;
67898
68545
  }
67899
- const enumMatch = line.match(/^export\s+(?:const\s+)?enum\s+(\w+)/);
67900
- if (enumMatch) {
67901
- symbols.push({
67902
- name: enumMatch[1],
67903
- kind: "enum",
67904
- exported: true,
67905
- signature: `enum ${enumMatch[1]}`,
67906
- line: lineNum,
67907
- jsdoc
67908
- });
68546
+ let content;
68547
+ try {
68548
+ content = fs55.readFileSync(fullPath, "utf-8");
68549
+ } catch {
67909
68550
  continue;
67910
68551
  }
67911
- const defaultMatch = line.match(/^export\s+default\s+(?:function\s+)?(\w+)/);
67912
- if (defaultMatch) {
67913
- symbols.push({
67914
- name: defaultMatch[1],
67915
- kind: "function",
67916
- exported: true,
67917
- signature: `default ${defaultMatch[1]}`,
67918
- line: lineNum,
67919
- jsdoc
67920
- });
68552
+ const lines = content.split(`
68553
+ `);
68554
+ for (let i2 = 0;i2 < lines.length; i2++) {
68555
+ const line = lines[i2];
68556
+ if (regex.test(line)) {
68557
+ total++;
68558
+ if (matches.length < opts.maxResults) {
68559
+ let lineText = line.trimEnd();
68560
+ if (lineText.length > opts.maxLines) {
68561
+ lineText = `${lineText.substring(0, opts.maxLines)}...`;
68562
+ }
68563
+ matches.push({
68564
+ file: file3,
68565
+ lineNumber: i2 + 1,
68566
+ lineText
68567
+ });
68568
+ }
68569
+ regex.lastIndex = 0;
68570
+ }
67921
68571
  }
67922
68572
  }
67923
- return symbols.sort((a, b) => {
67924
- if (a.line !== b.line)
67925
- return a.line - b.line;
67926
- return a.name.localeCompare(b.name);
67927
- });
68573
+ return {
68574
+ matches,
68575
+ truncated: total > opts.maxResults,
68576
+ total,
68577
+ query: opts.query,
68578
+ mode: opts.mode,
68579
+ maxResults: opts.maxResults
68580
+ };
67928
68581
  }
67929
- function extractPythonSymbols(filePath, cwd) {
67930
- const fullPath = path65.join(cwd, filePath);
67931
- if (!validatePathForRead(fullPath, cwd)) {
67932
- return [];
68582
+ var search = createSwarmTool({
68583
+ description: "Search for text within workspace files using ripgrep-style interface. " + "Supports literal and regex search modes with glob include/exclude filtering. " + "Returns structured JSON output with file paths, line numbers, and line content.",
68584
+ args: {
68585
+ query: tool.schema.string().describe("Search query string (literal or regex depending on mode)"),
68586
+ mode: tool.schema.enum(["literal", "regex"]).default("literal").describe("Search mode: literal for exact string match, regex for regular expression"),
68587
+ include: tool.schema.string().optional().describe('Glob pattern for files to include (e.g., "*.ts", "src/**/*.js")'),
68588
+ exclude: tool.schema.string().optional().describe('Glob pattern for files to exclude (e.g., "node_modules/**", "*.test.ts")'),
68589
+ max_results: tool.schema.number().default(DEFAULT_MAX_RESULTS).describe("Maximum number of matches to return"),
68590
+ max_lines: tool.schema.number().default(DEFAULT_MAX_LINES).describe("Maximum characters per line in results")
68591
+ },
68592
+ execute: async (args2, directory) => {
68593
+ let query;
68594
+ let mode = "literal";
68595
+ let include;
68596
+ let exclude;
68597
+ let maxResults = DEFAULT_MAX_RESULTS;
68598
+ let maxLines = DEFAULT_MAX_LINES;
68599
+ try {
68600
+ const obj = args2;
68601
+ query = String(obj.query ?? "");
68602
+ mode = obj.mode === "regex" ? "regex" : "literal";
68603
+ include = obj.include;
68604
+ exclude = obj.exclude;
68605
+ const rawMaxResults = typeof obj.max_results === "number" ? obj.max_results : DEFAULT_MAX_RESULTS;
68606
+ const sanitizedMaxResults = Number.isNaN(rawMaxResults) ? DEFAULT_MAX_RESULTS : rawMaxResults;
68607
+ maxResults = Math.min(Math.max(0, sanitizedMaxResults), HARD_CAP_RESULTS);
68608
+ const rawMaxLines = typeof obj.max_lines === "number" ? obj.max_lines : DEFAULT_MAX_LINES;
68609
+ const sanitizedMaxLines = Number.isNaN(rawMaxLines) ? DEFAULT_MAX_LINES : rawMaxLines;
68610
+ maxLines = Math.min(Math.max(0, sanitizedMaxLines), HARD_CAP_LINES);
68611
+ } catch {
68612
+ return JSON.stringify({
68613
+ error: true,
68614
+ type: "invalid-query",
68615
+ message: "Could not parse search arguments"
68616
+ }, null, 2);
68617
+ }
68618
+ if (!query || query.trim() === "") {
68619
+ return JSON.stringify({
68620
+ error: true,
68621
+ type: "invalid-query",
68622
+ message: "Query cannot be empty"
68623
+ }, null, 2);
68624
+ }
68625
+ if (containsControlChars(query)) {
68626
+ return JSON.stringify({
68627
+ error: true,
68628
+ type: "invalid-query",
68629
+ message: "Query contains invalid control characters"
68630
+ }, null, 2);
68631
+ }
68632
+ if (include && containsPathTraversal(include)) {
68633
+ return JSON.stringify({
68634
+ error: true,
68635
+ type: "path-escape",
68636
+ message: "Include pattern contains path traversal sequence"
68637
+ }, null, 2);
68638
+ }
68639
+ if (exclude && containsPathTraversal(exclude)) {
68640
+ return JSON.stringify({
68641
+ error: true,
68642
+ type: "path-escape",
68643
+ message: "Exclude pattern contains path traversal sequence"
68644
+ }, null, 2);
68645
+ }
68646
+ if (include && containsWindowsAttacks3(include)) {
68647
+ return JSON.stringify({
68648
+ error: true,
68649
+ type: "path-escape",
68650
+ message: "Include pattern contains invalid Windows-specific sequence"
68651
+ }, null, 2);
68652
+ }
68653
+ if (exclude && containsWindowsAttacks3(exclude)) {
68654
+ return JSON.stringify({
68655
+ error: true,
68656
+ type: "path-escape",
68657
+ message: "Exclude pattern contains invalid Windows-specific sequence"
68658
+ }, null, 2);
68659
+ }
68660
+ if (!fs55.existsSync(directory)) {
68661
+ return JSON.stringify({
68662
+ error: true,
68663
+ type: "unknown",
68664
+ message: "Workspace directory does not exist"
68665
+ }, null, 2);
68666
+ }
68667
+ const rgAvailable = await isRipgrepAvailable();
68668
+ let result;
68669
+ if (rgAvailable) {
68670
+ result = await ripgrepSearch({
68671
+ query,
68672
+ mode,
68673
+ include,
68674
+ exclude,
68675
+ maxResults,
68676
+ maxLines,
68677
+ workspace: directory
68678
+ });
68679
+ } else {
68680
+ result = await fallbackSearch({
68681
+ query,
68682
+ mode,
68683
+ include,
68684
+ exclude,
68685
+ maxResults,
68686
+ maxLines,
68687
+ workspace: directory
68688
+ });
68689
+ }
68690
+ if ("error" in result && result.error) {
68691
+ return JSON.stringify(result, null, 2);
68692
+ }
68693
+ return JSON.stringify(result, null, 2);
67933
68694
  }
67934
- let content;
68695
+ });
68696
+
68697
+ // src/tools/index.ts
68698
+ init_secretscan();
68699
+
68700
+ // src/tools/suggest-patch.ts
68701
+ init_tool();
68702
+ init_create_tool();
68703
+ import * as fs56 from "fs";
68704
+ import * as path68 from "path";
68705
+ var WINDOWS_RESERVED_NAMES4 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
68706
+ function containsWindowsAttacks4(str) {
68707
+ if (/:[^\\/]/.test(str))
68708
+ return true;
68709
+ const parts2 = str.split(/[/\\]/);
68710
+ for (const part of parts2) {
68711
+ if (WINDOWS_RESERVED_NAMES4.test(part))
68712
+ return true;
68713
+ }
68714
+ return false;
68715
+ }
68716
+ function isPathInWorkspace4(filePath, workspace) {
67935
68717
  try {
67936
- const stats = fs53.statSync(fullPath);
67937
- if (stats.size > MAX_FILE_SIZE_BYTES7) {
67938
- throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
68718
+ const resolvedPath = path68.resolve(workspace, filePath);
68719
+ if (!fs56.existsSync(resolvedPath)) {
68720
+ return true;
68721
+ }
68722
+ const realWorkspace = fs56.realpathSync(workspace);
68723
+ const realResolvedPath = fs56.realpathSync(resolvedPath);
68724
+ const relativePath = path68.relative(realWorkspace, realResolvedPath);
68725
+ if (relativePath.startsWith("..") || path68.isAbsolute(relativePath)) {
68726
+ return false;
67939
68727
  }
67940
- content = fs53.readFileSync(fullPath, "utf-8");
68728
+ return true;
67941
68729
  } catch {
67942
- return [];
68730
+ return false;
67943
68731
  }
68732
+ }
68733
+ function validateFilePath(filePath, workspace) {
68734
+ if (!filePath || filePath.trim() === "")
68735
+ return false;
68736
+ if (containsPathTraversal(filePath))
68737
+ return false;
68738
+ if (containsControlChars(filePath))
68739
+ return false;
68740
+ if (containsWindowsAttacks4(filePath))
68741
+ return false;
68742
+ return isPathInWorkspace4(filePath, workspace);
68743
+ }
68744
+ function findContextMatch(content, contextBefore, contextAfter, oldContent) {
67944
68745
  const lines = content.split(`
67945
68746
  `);
67946
- const symbols = [];
67947
- const allMatch = content.match(/__all__\s*=\s*\[([^\]]+)\]/);
67948
- const explicitExports = allMatch ? allMatch[1].split(",").map((s) => s.trim().replace(/['"]/g, "")) : null;
67949
- for (let i2 = 0;i2 < lines.length; i2++) {
67950
- const line = lines[i2];
67951
- if (line.startsWith(" ") || line.startsWith("\t"))
67952
- continue;
67953
- const fnMatch = line.match(/^(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*(.+?))?:/);
67954
- if (fnMatch && !fnMatch[1].startsWith("_")) {
67955
- const exported = !explicitExports || explicitExports.includes(fnMatch[1]);
67956
- symbols.push({
67957
- name: fnMatch[1],
67958
- kind: "function",
67959
- exported,
67960
- signature: `def ${fnMatch[1]}(${fnMatch[2].trim()})${fnMatch[3] ? ` -> ${fnMatch[3].trim()}` : ""}`,
67961
- line: i2 + 1
67962
- });
67963
- }
67964
- const classMatch = line.match(/^class\s+(\w+)(?:\(([^)]*)\))?:/);
67965
- if (classMatch && !classMatch[1].startsWith("_")) {
67966
- const exported = !explicitExports || explicitExports.includes(classMatch[1]);
67967
- symbols.push({
67968
- name: classMatch[1],
67969
- kind: "class",
67970
- exported,
67971
- signature: `class ${classMatch[1]}${classMatch[2] ? `(${classMatch[2].trim()})` : ""}`,
67972
- line: i2 + 1
67973
- });
68747
+ if ((!contextBefore || contextBefore.length === 0) && (!contextAfter || contextAfter.length === 0)) {
68748
+ return null;
68749
+ }
68750
+ if (contextBefore && contextBefore.length > 0) {
68751
+ for (let i2 = 0;i2 <= lines.length - contextBefore.length; i2++) {
68752
+ const slice = lines.slice(i2, i2 + contextBefore.length);
68753
+ if (arraysEqual(slice, contextBefore)) {
68754
+ const afterStart = i2 + contextBefore.length;
68755
+ if (contextAfter && contextAfter.length > 0) {
68756
+ for (let j = afterStart;j <= lines.length - contextAfter.length; j++) {
68757
+ const afterSlice = lines.slice(j, j + contextAfter.length);
68758
+ if (arraysEqual(afterSlice, contextAfter)) {
68759
+ if (j === afterStart) {
68760
+ return {
68761
+ startLineIndex: i2,
68762
+ endLineIndex: j + contextAfter.length - 1,
68763
+ matchedBefore: contextBefore,
68764
+ matchedAfter: contextAfter
68765
+ };
68766
+ } else {
68767
+ if (oldContent && oldContent.length > 0) {
68768
+ const oldContentLines = oldContent.split(`
68769
+ `);
68770
+ const betweenLines = lines.slice(afterStart, j);
68771
+ if (arraysEqual(betweenLines, oldContentLines)) {
68772
+ return {
68773
+ startLineIndex: i2,
68774
+ endLineIndex: j - 1,
68775
+ matchedBefore: contextBefore,
68776
+ matchedAfter: contextAfter
68777
+ };
68778
+ }
68779
+ } else {
68780
+ return {
68781
+ startLineIndex: i2,
68782
+ endLineIndex: j - 1,
68783
+ matchedBefore: contextBefore,
68784
+ matchedAfter: contextAfter
68785
+ };
68786
+ }
68787
+ }
68788
+ }
68789
+ }
68790
+ return null;
68791
+ } else {
68792
+ if (oldContent && oldContent.length > 0) {
68793
+ const oldContentLines = oldContent.split(`
68794
+ `);
68795
+ for (let k = afterStart;k <= lines.length - oldContentLines.length; k++) {
68796
+ const candidate = lines.slice(k, k + oldContentLines.length);
68797
+ if (arraysEqual(candidate, oldContentLines)) {
68798
+ return {
68799
+ startLineIndex: i2,
68800
+ endLineIndex: k + oldContentLines.length - 1,
68801
+ matchedBefore: contextBefore,
68802
+ matchedAfter: []
68803
+ };
68804
+ }
68805
+ }
68806
+ return null;
68807
+ } else {
68808
+ return {
68809
+ startLineIndex: i2,
68810
+ endLineIndex: afterStart - 1,
68811
+ matchedBefore: contextBefore,
68812
+ matchedAfter: []
68813
+ };
68814
+ }
68815
+ }
68816
+ }
67974
68817
  }
67975
- const constMatch = line.match(/^([A-Z][A-Z0-9_]+)\s*[:=]/);
67976
- if (constMatch) {
67977
- symbols.push({
67978
- name: constMatch[1],
67979
- kind: "const",
67980
- exported: true,
67981
- signature: line.trim().substring(0, 100),
67982
- line: i2 + 1
67983
- });
68818
+ } else if (contextAfter && contextAfter.length > 0) {
68819
+ for (let j = 0;j <= lines.length - contextAfter.length; j++) {
68820
+ const afterSlice = lines.slice(j, j + contextAfter.length);
68821
+ if (arraysEqual(afterSlice, contextAfter)) {
68822
+ return {
68823
+ startLineIndex: 0,
68824
+ endLineIndex: j - 1,
68825
+ matchedBefore: [],
68826
+ matchedAfter: contextAfter
68827
+ };
68828
+ }
67984
68829
  }
67985
68830
  }
67986
- return symbols.sort((a, b) => {
67987
- if (a.line !== b.line)
67988
- return a.line - b.line;
67989
- return a.name.localeCompare(b.name);
67990
- });
68831
+ return null;
67991
68832
  }
67992
- var symbols = createSwarmTool({
67993
- description: "Extract all exported symbols from a source file: functions with signatures, " + "classes with public members, interfaces, types, enums, constants. " + "Supports TypeScript/JavaScript and Python. Use for architect planning, " + "designer scaffolding, and understanding module public API surface.",
68833
+ function arraysEqual(a, b) {
68834
+ if (a.length !== b.length)
68835
+ return false;
68836
+ for (let i2 = 0;i2 < a.length; i2++) {
68837
+ if (a[i2] !== b[i2])
68838
+ return false;
68839
+ }
68840
+ return true;
68841
+ }
68842
+ function validateOldContent(lines, startIndex, endIndex, oldContent) {
68843
+ if (!oldContent) {
68844
+ return { valid: true };
68845
+ }
68846
+ const currentContent = lines.slice(startIndex, endIndex + 1).join(`
68847
+ `);
68848
+ if (currentContent !== oldContent) {
68849
+ return {
68850
+ valid: false,
68851
+ expected: oldContent,
68852
+ actual: currentContent
68853
+ };
68854
+ }
68855
+ return { valid: true };
68856
+ }
68857
+ var suggestPatch = createSwarmTool({
68858
+ description: "Suggest a structured patch for specified files without modifying them. " + "Returns context-based patch proposals with anchors for reviewer use. " + "This is a read-only tool \u2014 it does not modify any files.",
67994
68859
  args: {
67995
- file: tool.schema.string().describe('File path to extract symbols from (e.g., "src/auth/login.ts")'),
67996
- exported_only: tool.schema.boolean().default(true).describe("If true, only return exported/public symbols. If false, include all top-level symbols.")
68860
+ targetFiles: tool.schema.array(tool.schema.string()).describe("Array of file paths to patch").min(1),
68861
+ changes: tool.schema.array(tool.schema.object({
68862
+ file: tool.schema.string().describe("Path to the file this change applies to"),
68863
+ contextBefore: tool.schema.array(tool.schema.string()).optional().describe("Lines before the change region (anchor)"),
68864
+ contextAfter: tool.schema.array(tool.schema.string()).optional().describe("Lines after the change region (anchor)"),
68865
+ oldContent: tool.schema.string().optional().describe("Current content to be replaced"),
68866
+ newContent: tool.schema.string().describe("New content to replace with")
68867
+ })).describe("Array of change descriptions with context anchors").min(1)
67997
68868
  },
67998
68869
  execute: async (args2, directory) => {
67999
- let file3;
68000
- let exportedOnly = true;
68001
- try {
68002
- const obj = args2;
68003
- file3 = String(obj.file);
68004
- exportedOnly = obj.exported_only === true;
68005
- } catch {
68870
+ if (!args2 || typeof args2 !== "object") {
68006
68871
  return JSON.stringify({
68007
- file: "<unknown>",
68008
- error: "Invalid arguments: could not extract file path",
68009
- symbols: []
68872
+ success: false,
68873
+ error: true,
68874
+ type: "parse-error",
68875
+ message: "Could not parse suggest-patch arguments"
68010
68876
  }, null, 2);
68011
68877
  }
68012
- const cwd = directory;
68013
- const ext = path65.extname(file3);
68014
- if (containsControlChars(file3)) {
68878
+ const obj = args2;
68879
+ const targetFiles = obj.targetFiles ?? [];
68880
+ const changes = obj.changes ?? [];
68881
+ if (!targetFiles || targetFiles.length === 0) {
68015
68882
  return JSON.stringify({
68016
- file: file3,
68017
- error: "Path contains invalid control characters",
68018
- symbols: []
68883
+ success: false,
68884
+ error: true,
68885
+ type: "parse-error",
68886
+ message: "targetFiles cannot be empty"
68019
68887
  }, null, 2);
68020
68888
  }
68021
- if (containsPathTraversal(file3)) {
68889
+ if (!changes || changes.length === 0) {
68022
68890
  return JSON.stringify({
68023
- file: file3,
68024
- error: "Path contains path traversal sequence",
68025
- symbols: []
68891
+ success: false,
68892
+ error: true,
68893
+ type: "parse-error",
68894
+ message: "changes cannot be empty"
68026
68895
  }, null, 2);
68027
68896
  }
68028
- if (containsWindowsAttacks(file3)) {
68897
+ if (!fs56.existsSync(directory)) {
68029
68898
  return JSON.stringify({
68030
- file: file3,
68031
- error: "Path contains invalid Windows-specific sequence",
68032
- symbols: []
68899
+ success: false,
68900
+ error: true,
68901
+ type: "file-not-found",
68902
+ message: "Workspace directory does not exist"
68033
68903
  }, null, 2);
68034
68904
  }
68035
- if (!isPathInWorkspace(file3, cwd)) {
68905
+ const patches = [];
68906
+ const filesModifiedSet = new Set;
68907
+ const errors5 = [];
68908
+ const targetFileSet = new Set(targetFiles);
68909
+ for (let changeIndex = 0;changeIndex < changes.length; changeIndex++) {
68910
+ const change = changes[changeIndex];
68911
+ if (!targetFileSet.has(change.file)) {
68912
+ errors5.push({
68913
+ success: false,
68914
+ error: true,
68915
+ type: "parse-error",
68916
+ message: `File "${change.file}" is not in targetFiles`,
68917
+ details: { location: change.file }
68918
+ });
68919
+ continue;
68920
+ }
68921
+ if (!validateFilePath(change.file, directory)) {
68922
+ errors5.push({
68923
+ success: false,
68924
+ error: true,
68925
+ type: "parse-error",
68926
+ message: `Invalid file path: ${change.file}`,
68927
+ details: {
68928
+ location: change.file
68929
+ }
68930
+ });
68931
+ continue;
68932
+ }
68933
+ const fullPath = path68.resolve(directory, change.file);
68934
+ if (!fs56.existsSync(fullPath)) {
68935
+ errors5.push({
68936
+ success: false,
68937
+ error: true,
68938
+ type: "file-not-found",
68939
+ message: `File not found: ${change.file}`,
68940
+ details: {
68941
+ location: change.file
68942
+ }
68943
+ });
68944
+ continue;
68945
+ }
68946
+ let content;
68947
+ try {
68948
+ content = fs56.readFileSync(fullPath, "utf-8");
68949
+ } catch (err2) {
68950
+ errors5.push({
68951
+ success: false,
68952
+ error: true,
68953
+ type: "unknown",
68954
+ message: `Could not read file: ${err2 instanceof Error ? err2.message : String(err2)}`,
68955
+ details: {
68956
+ location: `${change.file}:0`
68957
+ }
68958
+ });
68959
+ continue;
68960
+ }
68961
+ const contextMatch = findContextMatch(content, change.contextBefore, change.contextAfter, change.oldContent);
68962
+ if (!contextMatch) {
68963
+ errors5.push({
68964
+ success: false,
68965
+ error: true,
68966
+ type: "context-mismatch",
68967
+ message: `Could not find context anchor in ${change.file}. Provide contextBefore/contextAfter lines to locate the change region.`,
68968
+ details: {
68969
+ location: change.file
68970
+ }
68971
+ });
68972
+ continue;
68973
+ }
68974
+ const lines = content.split(`
68975
+ `);
68976
+ const oldContentValidation = validateOldContent(lines, contextMatch.startLineIndex + contextMatch.matchedBefore.length, contextMatch.endLineIndex - contextMatch.matchedAfter.length + 1, change.oldContent);
68977
+ if (!oldContentValidation.valid) {
68978
+ errors5.push({
68979
+ success: false,
68980
+ error: true,
68981
+ type: "context-mismatch",
68982
+ message: `Content at the specified location does not match oldContent`,
68983
+ details: {
68984
+ expected: oldContentValidation.expected,
68985
+ actual: oldContentValidation.actual,
68986
+ location: `${change.file}:${contextMatch.startLineIndex + 1}-${contextMatch.endLineIndex + 1}`
68987
+ }
68988
+ });
68989
+ continue;
68990
+ }
68991
+ const originalContext = [
68992
+ ...contextMatch.matchedBefore,
68993
+ ...contextMatch.matchedAfter.length > 0 ? ["..."] : [],
68994
+ ...contextMatch.matchedAfter
68995
+ ];
68996
+ patches.push({
68997
+ file: change.file,
68998
+ originalContext,
68999
+ newContent: change.newContent,
69000
+ hunkIndex: changeIndex
69001
+ });
69002
+ filesModifiedSet.add(change.file);
69003
+ }
69004
+ if (patches.length > 0) {
68036
69005
  return JSON.stringify({
68037
- file: file3,
68038
- error: "Path is outside workspace",
68039
- symbols: []
69006
+ success: true,
69007
+ patches,
69008
+ filesModified: Array.from(filesModifiedSet),
69009
+ ...errors5.length > 0 && { errors: errors5 }
68040
69010
  }, null, 2);
68041
69011
  }
68042
- let syms;
68043
- switch (ext) {
68044
- case ".ts":
68045
- case ".tsx":
68046
- case ".js":
68047
- case ".jsx":
68048
- case ".mjs":
68049
- case ".cjs":
68050
- syms = extractTSSymbols(file3, cwd);
68051
- break;
68052
- case ".py":
68053
- syms = extractPythonSymbols(file3, cwd);
68054
- break;
68055
- default:
68056
- return JSON.stringify({
68057
- file: file3,
68058
- error: `Unsupported file extension: ${ext}. Supported: .ts, .tsx, .js, .jsx, .mjs, .cjs, .py`,
68059
- symbols: []
68060
- }, null, 2);
68061
- }
68062
- if (exportedOnly) {
68063
- syms = syms.filter((s) => s.exported);
69012
+ if (errors5.length === 1) {
69013
+ return JSON.stringify(errors5[0], null, 2);
68064
69014
  }
68065
69015
  return JSON.stringify({
68066
- file: file3,
68067
- symbolCount: syms.length,
68068
- symbols: syms
69016
+ success: false,
69017
+ error: true,
69018
+ type: "context-mismatch",
69019
+ message: `All ${errors5.length} patch suggestions failed`,
69020
+ errors: errors5
68069
69021
  }, null, 2);
68070
69022
  }
68071
69023
  });
@@ -68081,10 +69033,10 @@ init_test_runner();
68081
69033
  init_dist();
68082
69034
  init_utils();
68083
69035
  init_create_tool();
68084
- import * as fs54 from "fs";
68085
- import * as path66 from "path";
69036
+ import * as fs57 from "fs";
69037
+ import * as path69 from "path";
68086
69038
  var MAX_TEXT_LENGTH = 200;
68087
- var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
69039
+ var MAX_FILE_SIZE_BYTES9 = 1024 * 1024;
68088
69040
  var SUPPORTED_EXTENSIONS2 = new Set([
68089
69041
  ".ts",
68090
69042
  ".tsx",
@@ -68147,9 +69099,9 @@ function validatePathsInput(paths, cwd) {
68147
69099
  return { error: "paths contains path traversal", resolvedPath: null };
68148
69100
  }
68149
69101
  try {
68150
- const resolvedPath = path66.resolve(paths);
68151
- const normalizedCwd = path66.resolve(cwd);
68152
- const normalizedResolved = path66.resolve(resolvedPath);
69102
+ const resolvedPath = path69.resolve(paths);
69103
+ const normalizedCwd = path69.resolve(cwd);
69104
+ const normalizedResolved = path69.resolve(resolvedPath);
68153
69105
  if (!normalizedResolved.startsWith(normalizedCwd)) {
68154
69106
  return {
68155
69107
  error: "paths must be within the current working directory",
@@ -68165,13 +69117,13 @@ function validatePathsInput(paths, cwd) {
68165
69117
  }
68166
69118
  }
68167
69119
  function isSupportedExtension(filePath) {
68168
- const ext = path66.extname(filePath).toLowerCase();
69120
+ const ext = path69.extname(filePath).toLowerCase();
68169
69121
  return SUPPORTED_EXTENSIONS2.has(ext);
68170
69122
  }
68171
69123
  function findSourceFiles2(dir, files = []) {
68172
69124
  let entries;
68173
69125
  try {
68174
- entries = fs54.readdirSync(dir);
69126
+ entries = fs57.readdirSync(dir);
68175
69127
  } catch {
68176
69128
  return files;
68177
69129
  }
@@ -68180,10 +69132,10 @@ function findSourceFiles2(dir, files = []) {
68180
69132
  if (SKIP_DIRECTORIES4.has(entry)) {
68181
69133
  continue;
68182
69134
  }
68183
- const fullPath = path66.join(dir, entry);
69135
+ const fullPath = path69.join(dir, entry);
68184
69136
  let stat2;
68185
69137
  try {
68186
- stat2 = fs54.statSync(fullPath);
69138
+ stat2 = fs57.statSync(fullPath);
68187
69139
  } catch {
68188
69140
  continue;
68189
69141
  }
@@ -68276,7 +69228,7 @@ var todo_extract = createSwarmTool({
68276
69228
  return JSON.stringify(errorResult, null, 2);
68277
69229
  }
68278
69230
  const scanPath = resolvedPath;
68279
- if (!fs54.existsSync(scanPath)) {
69231
+ if (!fs57.existsSync(scanPath)) {
68280
69232
  const errorResult = {
68281
69233
  error: `path not found: ${pathsInput}`,
68282
69234
  total: 0,
@@ -68286,13 +69238,13 @@ var todo_extract = createSwarmTool({
68286
69238
  return JSON.stringify(errorResult, null, 2);
68287
69239
  }
68288
69240
  const filesToScan = [];
68289
- const stat2 = fs54.statSync(scanPath);
69241
+ const stat2 = fs57.statSync(scanPath);
68290
69242
  if (stat2.isFile()) {
68291
69243
  if (isSupportedExtension(scanPath)) {
68292
69244
  filesToScan.push(scanPath);
68293
69245
  } else {
68294
69246
  const errorResult = {
68295
- error: `unsupported file extension: ${path66.extname(scanPath)}`,
69247
+ error: `unsupported file extension: ${path69.extname(scanPath)}`,
68296
69248
  total: 0,
68297
69249
  byPriority: { high: 0, medium: 0, low: 0 },
68298
69250
  entries: []
@@ -68305,11 +69257,11 @@ var todo_extract = createSwarmTool({
68305
69257
  const allEntries = [];
68306
69258
  for (const filePath of filesToScan) {
68307
69259
  try {
68308
- const fileStat = fs54.statSync(filePath);
68309
- if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
69260
+ const fileStat = fs57.statSync(filePath);
69261
+ if (fileStat.size > MAX_FILE_SIZE_BYTES9) {
68310
69262
  continue;
68311
69263
  }
68312
- const content = fs54.readFileSync(filePath, "utf-8");
69264
+ const content = fs57.readFileSync(filePath, "utf-8");
68313
69265
  const entries = parseTodoComments(content, filePath, tagsSet);
68314
69266
  allEntries.push(...entries);
68315
69267
  } catch {}
@@ -68338,18 +69290,18 @@ var todo_extract = createSwarmTool({
68338
69290
  init_tool();
68339
69291
  init_schema();
68340
69292
  init_gate_evidence();
68341
- import * as fs56 from "fs";
68342
- import * as path68 from "path";
69293
+ import * as fs59 from "fs";
69294
+ import * as path71 from "path";
68343
69295
 
68344
69296
  // src/hooks/diff-scope.ts
68345
- import * as fs55 from "fs";
68346
- import * as path67 from "path";
69297
+ import * as fs58 from "fs";
69298
+ import * as path70 from "path";
68347
69299
  function getDeclaredScope(taskId, directory) {
68348
69300
  try {
68349
- const planPath = path67.join(directory, ".swarm", "plan.json");
68350
- if (!fs55.existsSync(planPath))
69301
+ const planPath = path70.join(directory, ".swarm", "plan.json");
69302
+ if (!fs58.existsSync(planPath))
68351
69303
  return null;
68352
- const raw = fs55.readFileSync(planPath, "utf-8");
69304
+ const raw = fs58.readFileSync(planPath, "utf-8");
68353
69305
  const plan = JSON.parse(raw);
68354
69306
  for (const phase of plan.phases ?? []) {
68355
69307
  for (const task of phase.tasks ?? []) {
@@ -68462,7 +69414,7 @@ var TIER_3_PATTERNS = [
68462
69414
  ];
68463
69415
  function matchesTier3Pattern(files) {
68464
69416
  for (const file3 of files) {
68465
- const fileName = path68.basename(file3);
69417
+ const fileName = path71.basename(file3);
68466
69418
  for (const pattern of TIER_3_PATTERNS) {
68467
69419
  if (pattern.test(fileName)) {
68468
69420
  return true;
@@ -68476,8 +69428,8 @@ function checkReviewerGate(taskId, workingDirectory) {
68476
69428
  if (hasActiveTurboMode()) {
68477
69429
  const resolvedDir2 = workingDirectory;
68478
69430
  try {
68479
- const planPath = path68.join(resolvedDir2, ".swarm", "plan.json");
68480
- const planRaw = fs56.readFileSync(planPath, "utf-8");
69431
+ const planPath = path71.join(resolvedDir2, ".swarm", "plan.json");
69432
+ const planRaw = fs59.readFileSync(planPath, "utf-8");
68481
69433
  const plan = JSON.parse(planRaw);
68482
69434
  for (const planPhase of plan.phases ?? []) {
68483
69435
  for (const task of planPhase.tasks ?? []) {
@@ -68543,8 +69495,8 @@ function checkReviewerGate(taskId, workingDirectory) {
68543
69495
  }
68544
69496
  try {
68545
69497
  const resolvedDir2 = workingDirectory;
68546
- const planPath = path68.join(resolvedDir2, ".swarm", "plan.json");
68547
- const planRaw = fs56.readFileSync(planPath, "utf-8");
69498
+ const planPath = path71.join(resolvedDir2, ".swarm", "plan.json");
69499
+ const planRaw = fs59.readFileSync(planPath, "utf-8");
68548
69500
  const plan = JSON.parse(planRaw);
68549
69501
  for (const planPhase of plan.phases ?? []) {
68550
69502
  for (const task of planPhase.tasks ?? []) {
@@ -68726,8 +69678,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
68726
69678
  };
68727
69679
  }
68728
69680
  }
68729
- normalizedDir = path68.normalize(args2.working_directory);
68730
- const pathParts = normalizedDir.split(path68.sep);
69681
+ normalizedDir = path71.normalize(args2.working_directory);
69682
+ const pathParts = normalizedDir.split(path71.sep);
68731
69683
  if (pathParts.includes("..")) {
68732
69684
  return {
68733
69685
  success: false,
@@ -68737,11 +69689,11 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
68737
69689
  ]
68738
69690
  };
68739
69691
  }
68740
- const resolvedDir = path68.resolve(normalizedDir);
69692
+ const resolvedDir = path71.resolve(normalizedDir);
68741
69693
  try {
68742
- const realPath = fs56.realpathSync(resolvedDir);
68743
- const planPath = path68.join(realPath, ".swarm", "plan.json");
68744
- if (!fs56.existsSync(planPath)) {
69694
+ const realPath = fs59.realpathSync(resolvedDir);
69695
+ const planPath = path71.join(realPath, ".swarm", "plan.json");
69696
+ if (!fs59.existsSync(planPath)) {
68745
69697
  return {
68746
69698
  success: false,
68747
69699
  message: `Invalid working_directory: plan not found in "${realPath}"`,
@@ -68774,8 +69726,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
68774
69726
  recoverTaskStateFromDelegations(args2.task_id);
68775
69727
  let phaseRequiresReviewer = true;
68776
69728
  try {
68777
- const planPath = path68.join(directory, ".swarm", "plan.json");
68778
- const planRaw = fs56.readFileSync(planPath, "utf-8");
69729
+ const planPath = path71.join(directory, ".swarm", "plan.json");
69730
+ const planRaw = fs59.readFileSync(planPath, "utf-8");
68779
69731
  const plan = JSON.parse(planRaw);
68780
69732
  const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
68781
69733
  if (taskPhase?.required_agents && !taskPhase.required_agents.includes("reviewer")) {
@@ -68838,8 +69790,8 @@ var update_task_status = createSwarmTool({
68838
69790
  init_tool();
68839
69791
  init_utils2();
68840
69792
  init_create_tool();
68841
- import fs57 from "fs";
68842
- import path69 from "path";
69793
+ import fs60 from "fs";
69794
+ import path72 from "path";
68843
69795
  function normalizeVerdict(verdict) {
68844
69796
  switch (verdict) {
68845
69797
  case "APPROVED":
@@ -68886,7 +69838,7 @@ async function executeWriteDriftEvidence(args2, directory) {
68886
69838
  entries: [evidenceEntry]
68887
69839
  };
68888
69840
  const filename = "drift-verifier.json";
68889
- const relativePath = path69.join("evidence", String(phase), filename);
69841
+ const relativePath = path72.join("evidence", String(phase), filename);
68890
69842
  let validatedPath;
68891
69843
  try {
68892
69844
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -68897,12 +69849,12 @@ async function executeWriteDriftEvidence(args2, directory) {
68897
69849
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
68898
69850
  }, null, 2);
68899
69851
  }
68900
- const evidenceDir = path69.dirname(validatedPath);
69852
+ const evidenceDir = path72.dirname(validatedPath);
68901
69853
  try {
68902
- await fs57.promises.mkdir(evidenceDir, { recursive: true });
68903
- const tempPath = path69.join(evidenceDir, `.${filename}.tmp`);
68904
- await fs57.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
68905
- await fs57.promises.rename(tempPath, validatedPath);
69854
+ await fs60.promises.mkdir(evidenceDir, { recursive: true });
69855
+ const tempPath = path72.join(evidenceDir, `.${filename}.tmp`);
69856
+ await fs60.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
69857
+ await fs60.promises.rename(tempPath, validatedPath);
68906
69858
  return JSON.stringify({
68907
69859
  success: true,
68908
69860
  phase,
@@ -69093,7 +70045,7 @@ var OpenCodeSwarm = async (ctx) => {
69093
70045
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
69094
70046
  preflightTriggerManager = new PTM(automationConfig);
69095
70047
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
69096
- const swarmDir = path70.resolve(ctx.directory, ".swarm");
70048
+ const swarmDir = path73.resolve(ctx.directory, ".swarm");
69097
70049
  statusArtifact = new ASA(swarmDir);
69098
70050
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
69099
70051
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -69223,6 +70175,9 @@ var OpenCodeSwarm = async (ctx) => {
69223
70175
  symbols,
69224
70176
  test_runner,
69225
70177
  todo_extract,
70178
+ search,
70179
+ batch_symbols,
70180
+ suggest_patch: suggestPatch,
69226
70181
  update_task_status,
69227
70182
  write_retro,
69228
70183
  write_drift_evidence,
@@ -69431,7 +70386,7 @@ var OpenCodeSwarm = async (ctx) => {
69431
70386
  "command.execute.before": safeHook(commandHandler),
69432
70387
  "tool.execute.before": async (input, output) => {
69433
70388
  if (process.env.DEBUG_SWARM)
69434
- console.error(`[DIAG] toolBefore tool=${input.tool?.replace?.(/^[^:]+[:.]/, "") ?? input.tool} session=${input.sessionID}`);
70389
+ console.error(`[DIAG] toolBefore tool=${normalizeToolName(input.tool) ?? input.tool} session=${input.sessionID}`);
69435
70390
  if (!swarmState.activeAgent.has(input.sessionID)) {
69436
70391
  swarmState.activeAgent.set(input.sessionID, ORCHESTRATOR_NAME);
69437
70392
  }
@@ -69462,10 +70417,10 @@ var OpenCodeSwarm = async (ctx) => {
69462
70417
  },
69463
70418
  "tool.execute.after": async (input, output) => {
69464
70419
  const _dbg = !!process.env.DEBUG_SWARM;
69465
- const _toolName = input.tool?.replace?.(/^[^:]+[:.]/, "") ?? input.tool;
70420
+ const _toolName = normalizeToolName(input.tool) ?? input.tool;
69466
70421
  if (_dbg)
69467
70422
  console.error(`[DIAG] toolAfter START tool=${_toolName} session=${input.sessionID}`);
69468
- const normalizedTool = input.tool.replace(/^[^:]+[:.]/, "");
70423
+ const normalizedTool = normalizeToolName(input.tool);
69469
70424
  const isTaskTool = normalizedTool === "Task" || normalizedTool === "task";
69470
70425
  const hookChain = async () => {
69471
70426
  await activityHooks.toolAfter(input, output);