opencode-swarm 6.44.3 → 6.45.1

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));
@@ -32503,7 +32519,7 @@ __export(exports_co_change_analyzer, {
32503
32519
  buildCoChangeMatrix: () => buildCoChangeMatrix
32504
32520
  });
32505
32521
  import * as child_process2 from "child_process";
32506
- import { randomUUID } from "crypto";
32522
+ import { randomUUID as randomUUID2 } from "crypto";
32507
32523
  import { readdir as readdir2, readFile as readFile4, stat } from "fs/promises";
32508
32524
  import * as path19 from "path";
32509
32525
  import { promisify } from "util";
@@ -32763,7 +32779,7 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
32763
32779
  }
32764
32780
  const confidence = Math.min(0.3 + 0.2 * Math.min(pair.coChangeCount / 10, 1), 0.5);
32765
32781
  entries.push({
32766
- id: randomUUID(),
32782
+ id: randomUUID2(),
32767
32783
  tier: "swarm",
32768
32784
  lesson,
32769
32785
  category: "architecture",
@@ -36198,17 +36214,17 @@ function getTestFilesFromConvention(sourceFiles) {
36198
36214
  const testFiles = [];
36199
36215
  for (const file3 of sourceFiles) {
36200
36216
  const normalizedPath = file3.replace(/\\/g, "/");
36201
- const basename4 = path28.basename(file3);
36217
+ const basename5 = path28.basename(file3);
36202
36218
  const dirname12 = path28.dirname(file3);
36203
- if (hasCompoundTestExtension(basename4) || basename4.includes(".spec.") || basename4.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
36219
+ if (hasCompoundTestExtension(basename5) || basename5.includes(".spec.") || basename5.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
36204
36220
  if (!testFiles.includes(file3)) {
36205
36221
  testFiles.push(file3);
36206
36222
  }
36207
36223
  continue;
36208
36224
  }
36209
36225
  for (const _pattern of TEST_PATTERNS) {
36210
- const nameWithoutExt = basename4.replace(/\.[^.]+$/, "");
36211
- const ext = path28.extname(basename4);
36226
+ const nameWithoutExt = basename5.replace(/\.[^.]+$/, "");
36227
+ const ext = path28.extname(basename5);
36212
36228
  const possibleTestFiles = [
36213
36229
  path28.join(dirname12, `${nameWithoutExt}.spec${ext}`),
36214
36230
  path28.join(dirname12, `${nameWithoutExt}.test${ext}`),
@@ -38038,17 +38054,17 @@ function normalizeSeparators(filePath) {
38038
38054
  }
38039
38055
  function matchesDocPattern(filePath, patterns) {
38040
38056
  const normalizedPath = normalizeSeparators(filePath);
38041
- const basename5 = path42.basename(filePath);
38057
+ const basename6 = path42.basename(filePath);
38042
38058
  for (const pattern of patterns) {
38043
38059
  if (!pattern.includes("/") && !pattern.includes("\\")) {
38044
- if (basename5 === pattern) {
38060
+ if (basename6 === pattern) {
38045
38061
  return true;
38046
38062
  }
38047
38063
  continue;
38048
38064
  }
38049
38065
  if (pattern.startsWith("**/")) {
38050
38066
  const filenamePattern = pattern.slice(3);
38051
- if (basename5 === filenamePattern) {
38067
+ if (basename6 === filenamePattern) {
38052
38068
  return true;
38053
38069
  }
38054
38070
  continue;
@@ -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
 
@@ -44081,7 +44118,8 @@ COMPLIANCE:
44081
44118
  - [type]: [description] (or "No deviations observed")
44082
44119
 
44083
44120
  KNOWLEDGE_UPDATES:
44084
- - [action] [entry_id or "new"]: [reason] (or "No recommendations")
44121
+ - [action] new: [reason] (or "No recommendations")
44122
+ NOTE: Always use "new" as the token \u2014 existing entry IDs (UUID v4) are not available in this context. Any non-UUID token is treated as "new" by the parser. Only "promote new:" creates a new entry; "archive new:" and "flag_contradiction new:" are silently skipped because those actions require an existing entry to operate on.
44085
44123
 
44086
44124
  EXTENDED_DIGEST:
44087
44125
  [the full running digest with this phase appended]
@@ -44490,9 +44528,10 @@ Your unique value is catching LOGIC ERRORS, EDGE CASES, and SECURITY FLAWS that
44490
44528
 
44491
44529
  DO (explicitly):
44492
44530
  - 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
44531
+ - VERIFY imports exist: if the coder added a new import, use search to verify the export exists in the source
44494
44532
  - CHECK test files were updated: if the coder changed a function signature, the tests should reflect it
44495
44533
  - VERIFY platform compatibility: path.join() used for all paths, no hardcoded separators
44534
+ - For confirmed issues requiring a concrete fix: use suggest_patch to produce a structured patch artifact for the coder
44496
44535
 
44497
44536
  ## REVIEW REASONING
44498
44537
  For each changed function or method, answer these before formulating issues:
@@ -48164,6 +48203,7 @@ async function handleConfigCommand(directory, _args) {
48164
48203
  init_schema();
48165
48204
 
48166
48205
  // src/hooks/curator.ts
48206
+ import { randomUUID } from "crypto";
48167
48207
  import * as fs12 from "fs";
48168
48208
  import * as path18 from "path";
48169
48209
  init_event_bus();
@@ -48185,7 +48225,8 @@ function parseKnowledgeRecommendations(llmOutput) {
48185
48225
  const match = trimmed.match(/^-\s+(promote|archive|flag_contradiction)\s+(\S+):\s+(.+)$/i);
48186
48226
  if (match) {
48187
48227
  const action = match[1].toLowerCase();
48188
- const entryId = match[2] === "new" ? undefined : match[2];
48228
+ const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
48229
+ const entryId = match[2] === "new" || !UUID_V4.test(match[2]) ? undefined : match[2];
48189
48230
  const reason = match[3].trim();
48190
48231
  recommendations.push({
48191
48232
  action,
@@ -48699,6 +48740,43 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, _knowled
48699
48740
  if (modified) {
48700
48741
  await rewriteKnowledge(knowledgePath, updatedEntries);
48701
48742
  }
48743
+ for (const rec of recommendations) {
48744
+ if (rec.entry_id !== undefined)
48745
+ continue;
48746
+ if (rec.action !== "promote") {
48747
+ skipped++;
48748
+ continue;
48749
+ }
48750
+ const lesson = rec.lesson?.trim() ?? "";
48751
+ if (lesson.length < 15) {
48752
+ skipped++;
48753
+ continue;
48754
+ }
48755
+ const now = new Date().toISOString();
48756
+ const newEntry = {
48757
+ id: randomUUID(),
48758
+ tier: "swarm",
48759
+ lesson: lesson.slice(0, 280),
48760
+ category: "other",
48761
+ tags: [],
48762
+ scope: "global",
48763
+ confidence: 0.5,
48764
+ status: "candidate",
48765
+ confirmed_by: [],
48766
+ retrieval_outcomes: {
48767
+ applied_count: 0,
48768
+ succeeded_after_count: 0,
48769
+ failed_after_count: 0
48770
+ },
48771
+ schema_version: 1,
48772
+ created_at: now,
48773
+ updated_at: now,
48774
+ auto_generated: true,
48775
+ project_name: path18.basename(directory)
48776
+ };
48777
+ await appendKnowledge(knowledgePath, newEntry);
48778
+ applied++;
48779
+ }
48702
48780
  return { applied, skipped };
48703
48781
  }
48704
48782
 
@@ -50378,7 +50456,7 @@ init_schema();
50378
50456
 
50379
50457
  // src/hooks/knowledge-migrator.ts
50380
50458
  init_knowledge_store();
50381
- import { randomUUID as randomUUID2 } from "crypto";
50459
+ import { randomUUID as randomUUID3 } from "crypto";
50382
50460
  import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
50383
50461
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
50384
50462
  import * as path23 from "path";
@@ -50448,7 +50526,7 @@ async function migrateContextToKnowledge(directory, config3) {
50448
50526
  }
50449
50527
  const inferredTags = inferTags(raw.text);
50450
50528
  const entry = {
50451
- id: randomUUID2(),
50529
+ id: randomUUID3(),
50452
50530
  tier: "swarm",
50453
50531
  lesson: truncateLesson(raw.text),
50454
50532
  category: raw.categoryHint ?? inferCategoryFromText(raw.text),
@@ -53344,6 +53422,17 @@ function detectLoop(sessionId, toolName, args2) {
53344
53422
  };
53345
53423
  }
53346
53424
 
53425
+ // src/hooks/normalize-tool-name.ts
53426
+ var NAMESPACE_PREFIX_PATTERN = /^[^:]+[:.]/;
53427
+ function normalizeToolName(toolName) {
53428
+ if (!toolName)
53429
+ return;
53430
+ return toolName.replace(NAMESPACE_PREFIX_PATTERN, "");
53431
+ }
53432
+ function normalizeToolNameLowerCase(toolName) {
53433
+ return toolName.replace(NAMESPACE_PREFIX_PATTERN, "").toLowerCase();
53434
+ }
53435
+
53347
53436
  // src/hooks/guardrails.ts
53348
53437
  var storedInputArgs = new Map;
53349
53438
  var TRANSIENT_MODEL_ERROR_PATTERN = /rate.?limit|429|503|timeout|overloaded|model.?not.?found|temporarily unavailable|server error/i;
@@ -53366,7 +53455,7 @@ function extractPhaseNumber(phaseString) {
53366
53455
  return match ? parseInt(match[1], 10) : 1;
53367
53456
  }
53368
53457
  function isWriteTool(toolName) {
53369
- const normalized = toolName.replace(/^[^:]+[:.]/, "");
53458
+ const normalized = normalizeToolName(toolName);
53370
53459
  return WRITE_TOOL_NAMES.includes(normalized);
53371
53460
  }
53372
53461
  function isArchitect(sessionId) {
@@ -53421,7 +53510,7 @@ function hasTraversalSegments(filePath) {
53421
53510
  return normalized.startsWith("..") || normalized.includes("/../") || normalized.endsWith("/..");
53422
53511
  }
53423
53512
  function isGateTool(toolName) {
53424
- const normalized = toolName.replace(/^[^:]+[:.]/, "");
53513
+ const normalized = normalizeToolName(toolName);
53425
53514
  const gateTools = [
53426
53515
  "diff",
53427
53516
  "syntax_check",
@@ -53437,7 +53526,7 @@ function isGateTool(toolName) {
53437
53526
  return gateTools.includes(normalized);
53438
53527
  }
53439
53528
  function isAgentDelegation(toolName, args2) {
53440
- const normalized = toolName.replace(/^[^:]+[:.]/, "");
53529
+ const normalized = normalizeToolName(toolName);
53441
53530
  if (normalized !== "Task" && normalized !== "task") {
53442
53531
  return { isDelegation: false, targetAgent: null };
53443
53532
  }
@@ -53932,7 +54021,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
53932
54021
  }
53933
54022
  }
53934
54023
  const sessionId = input.sessionID;
53935
- const normalizedToolName = input.tool.replace(/^[^:]+[:.]/, "");
54024
+ const normalizedToolName = normalizeToolName(input.tool);
53936
54025
  if (isWriteTool(normalizedToolName)) {
53937
54026
  toolCallsSinceLastWrite.set(sessionId, 0);
53938
54027
  noOpWarningIssued.delete(sessionId);
@@ -54490,7 +54579,7 @@ function createDelegationGateHook(config3, directory) {
54490
54579
  const toolBefore = async (input, output) => {
54491
54580
  if (!input.sessionID)
54492
54581
  return;
54493
- const normalized = input.tool.replace(/^[^:]+[:.]/, "");
54582
+ const normalized = normalizeToolName(input.tool);
54494
54583
  if (normalized !== "Task" && normalized !== "task")
54495
54584
  return;
54496
54585
  const args2 = output.args;
@@ -54546,7 +54635,7 @@ function createDelegationGateHook(config3, directory) {
54546
54635
  const session = swarmState.agentSessions.get(input.sessionID);
54547
54636
  if (!session)
54548
54637
  return;
54549
- const normalized = input.tool.replace(/^[^:]+[:.]/, "");
54638
+ const normalized = normalizeToolName(input.tool);
54550
54639
  if (normalized === "Task" || normalized === "task") {
54551
54640
  const directArgs = input.args;
54552
54641
  const storedArgs = getStoredInputArgs(input.callID);
@@ -57354,12 +57443,12 @@ var WRITE_TOOL_PATTERNS = [
57354
57443
  "prepend"
57355
57444
  ];
57356
57445
  function isWriteTool2(toolName) {
57357
- const normalized = toolName.replace(/^[^:]+[:.]/, "").toLowerCase();
57446
+ const normalized = normalizeToolNameLowerCase(toolName);
57358
57447
  return WRITE_TOOL_PATTERNS.some((p) => normalized.includes(p));
57359
57448
  }
57360
57449
  var READ_TOOL_PATTERNS = ["read", "cat", "view", "fetch", "get"];
57361
57450
  function isReadTool(toolName) {
57362
- const normalized = toolName.replace(/^[^:]+[:.]/, "").toLowerCase();
57451
+ const normalized = normalizeToolNameLowerCase(toolName);
57363
57452
  return READ_TOOL_PATTERNS.some((p) => normalized.includes(p));
57364
57453
  }
57365
57454
 
@@ -57796,7 +57885,7 @@ function createScopeGuardHook(config3, directory, injectAdvisory) {
57796
57885
  toolBefore: async (input, output) => {
57797
57886
  if (!enabled)
57798
57887
  return;
57799
- const toolName = input.tool.replace(/^[^:]+[:.]/, "");
57888
+ const toolName = normalizeToolName(input.tool);
57800
57889
  if (!WRITE_TOOLS.has(toolName))
57801
57890
  return;
57802
57891
  const sessionId = input.sessionID;
@@ -57859,7 +57948,7 @@ function createSelfReviewHook(config3, injectAdvisory) {
57859
57948
  toolAfter: async (input, output) => {
57860
57949
  if (!enabled)
57861
57950
  return;
57862
- const toolName = input.tool.replace(/^[^:]+[:.]/, "");
57951
+ const toolName = normalizeToolName(input.tool);
57863
57952
  if (toolName !== "update_task_status")
57864
57953
  return;
57865
57954
  const args2 = output.args;
@@ -58359,6 +58448,520 @@ async function loadSnapshot(directory) {
58359
58448
  // src/index.ts
58360
58449
  init_telemetry();
58361
58450
 
58451
+ // src/tools/batch-symbols.ts
58452
+ init_tool();
58453
+ init_create_tool();
58454
+ import * as fs38 from "fs";
58455
+ import * as path49 from "path";
58456
+
58457
+ // src/tools/symbols.ts
58458
+ init_tool();
58459
+ init_create_tool();
58460
+ import * as fs37 from "fs";
58461
+ import * as path48 from "path";
58462
+ var MAX_FILE_SIZE_BYTES2 = 1024 * 1024;
58463
+ var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
58464
+ function containsWindowsAttacks(str) {
58465
+ if (/:[^\\/]/.test(str)) {
58466
+ return true;
58467
+ }
58468
+ const parts2 = str.split(/[/\\]/);
58469
+ for (const part of parts2) {
58470
+ if (WINDOWS_RESERVED_NAMES.test(part)) {
58471
+ return true;
58472
+ }
58473
+ }
58474
+ return false;
58475
+ }
58476
+ function isPathInWorkspace(filePath, workspace) {
58477
+ try {
58478
+ const resolvedPath = path48.resolve(workspace, filePath);
58479
+ const realWorkspace = fs37.realpathSync(workspace);
58480
+ const realResolvedPath = fs37.realpathSync(resolvedPath);
58481
+ const relativePath = path48.relative(realWorkspace, realResolvedPath);
58482
+ if (relativePath.startsWith("..") || path48.isAbsolute(relativePath)) {
58483
+ return false;
58484
+ }
58485
+ return true;
58486
+ } catch {
58487
+ return false;
58488
+ }
58489
+ }
58490
+ function validatePathForRead(filePath, workspace) {
58491
+ return isPathInWorkspace(filePath, workspace);
58492
+ }
58493
+ function extractTSSymbols(filePath, cwd) {
58494
+ const fullPath = path48.join(cwd, filePath);
58495
+ if (!validatePathForRead(fullPath, cwd)) {
58496
+ return [];
58497
+ }
58498
+ let content;
58499
+ try {
58500
+ const stats = fs37.statSync(fullPath);
58501
+ if (stats.size > MAX_FILE_SIZE_BYTES2) {
58502
+ throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES2})`);
58503
+ }
58504
+ content = fs37.readFileSync(fullPath, "utf-8");
58505
+ } catch {
58506
+ return [];
58507
+ }
58508
+ const lines = content.split(`
58509
+ `);
58510
+ const symbols = [];
58511
+ for (let i2 = 0;i2 < lines.length; i2++) {
58512
+ const line = lines[i2];
58513
+ const lineNum = i2 + 1;
58514
+ let jsdoc;
58515
+ if (i2 > 0 && lines[i2 - 1].trim().endsWith("*/")) {
58516
+ const jsdocLines = [];
58517
+ for (let j = i2 - 1;j >= 0; j--) {
58518
+ jsdocLines.unshift(lines[j]);
58519
+ if (lines[j].trim().startsWith("/**"))
58520
+ break;
58521
+ }
58522
+ jsdoc = jsdocLines.join(`
58523
+ `).trim();
58524
+ if (jsdoc.length > 300)
58525
+ jsdoc = `${jsdoc.substring(0, 300)}...`;
58526
+ }
58527
+ const fnMatch = line.match(/^export\s+(?:async\s+)?function\s+(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)(?:\s*:\s*(.+?))?(?:\s*\{|$)/);
58528
+ if (fnMatch) {
58529
+ symbols.push({
58530
+ name: fnMatch[1],
58531
+ kind: "function",
58532
+ exported: true,
58533
+ signature: `function ${fnMatch[1]}${fnMatch[2] || ""}(${fnMatch[3].trim()})${fnMatch[4] ? `: ${fnMatch[4].trim()}` : ""}`,
58534
+ line: lineNum,
58535
+ jsdoc
58536
+ });
58537
+ continue;
58538
+ }
58539
+ const constMatch = line.match(/^export\s+const\s+(\w+)(?:\s*:\s*(.+?))?\s*=/);
58540
+ if (constMatch) {
58541
+ const restOfLine = line.substring(line.indexOf("=") + 1).trim();
58542
+ const isArrow = restOfLine.startsWith("(") || restOfLine.startsWith("async (") || restOfLine.match(/^\w+\s*=>/);
58543
+ symbols.push({
58544
+ name: constMatch[1],
58545
+ kind: isArrow ? "function" : "const",
58546
+ exported: true,
58547
+ signature: `const ${constMatch[1]}${constMatch[2] ? `: ${constMatch[2].trim()}` : ""}`,
58548
+ line: lineNum,
58549
+ jsdoc
58550
+ });
58551
+ continue;
58552
+ }
58553
+ const classMatch = line.match(/^export\s+(?:abstract\s+)?class\s+(\w+)(?:\s+(?:extends|implements)\s+(.+?))?(?:\s*\{|$)/);
58554
+ if (classMatch) {
58555
+ symbols.push({
58556
+ name: classMatch[1],
58557
+ kind: "class",
58558
+ exported: true,
58559
+ signature: `class ${classMatch[1]}${classMatch[2] ? ` extends/implements ${classMatch[2].trim()}` : ""}`,
58560
+ line: lineNum,
58561
+ jsdoc
58562
+ });
58563
+ let braceDepth = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
58564
+ for (let j = i2 + 1;j < lines.length && braceDepth > 0; j++) {
58565
+ const memberLine = lines[j];
58566
+ braceDepth += (memberLine.match(/\{/g) || []).length - (memberLine.match(/\}/g) || []).length;
58567
+ const methodMatch = memberLine.match(/^\s+(?:public\s+)?(?:async\s+)?(\w+)\s*\(([^)]*)\)(?:\s*:\s*(.+?))?(?:\s*\{|;|$)/);
58568
+ if (methodMatch && !memberLine.includes("private") && !memberLine.includes("protected") && !memberLine.trim().startsWith("//")) {
58569
+ symbols.push({
58570
+ name: `${classMatch[1]}.${methodMatch[1]}`,
58571
+ kind: "method",
58572
+ exported: true,
58573
+ signature: `${methodMatch[1]}(${methodMatch[2].trim()})${methodMatch[3] ? `: ${methodMatch[3].trim()}` : ""}`,
58574
+ line: j + 1
58575
+ });
58576
+ }
58577
+ const propMatch = memberLine.match(/^\s+(?:public\s+)?(?:readonly\s+)?(\w+)(?:\?)?:\s*(.+?)(?:\s*[;=]|$)/);
58578
+ if (propMatch && !memberLine.includes("private") && !memberLine.includes("protected") && !memberLine.trim().startsWith("//")) {
58579
+ symbols.push({
58580
+ name: `${classMatch[1]}.${propMatch[1]}`,
58581
+ kind: "property",
58582
+ exported: true,
58583
+ signature: `${propMatch[1]}: ${propMatch[2].trim()}`,
58584
+ line: j + 1
58585
+ });
58586
+ }
58587
+ }
58588
+ continue;
58589
+ }
58590
+ const ifaceMatch = line.match(/^export\s+interface\s+(\w+)(?:\s*<([^>]+)>)?(?:\s+extends\s+(.+?))?(?:\s*\{|$)/);
58591
+ if (ifaceMatch) {
58592
+ symbols.push({
58593
+ name: ifaceMatch[1],
58594
+ kind: "interface",
58595
+ exported: true,
58596
+ signature: `interface ${ifaceMatch[1]}${ifaceMatch[2] ? `<${ifaceMatch[2]}>` : ""}${ifaceMatch[3] ? ` extends ${ifaceMatch[3].trim()}` : ""}`,
58597
+ line: lineNum,
58598
+ jsdoc
58599
+ });
58600
+ continue;
58601
+ }
58602
+ const typeMatch = line.match(/^export\s+type\s+(\w+)(?:\s*<([^>]+)>)?\s*=/);
58603
+ if (typeMatch) {
58604
+ const typeValue = line.substring(line.indexOf("=") + 1).trim().substring(0, 100);
58605
+ symbols.push({
58606
+ name: typeMatch[1],
58607
+ kind: "type",
58608
+ exported: true,
58609
+ signature: `type ${typeMatch[1]}${typeMatch[2] ? `<${typeMatch[2]}>` : ""} = ${typeValue}`,
58610
+ line: lineNum,
58611
+ jsdoc
58612
+ });
58613
+ continue;
58614
+ }
58615
+ const enumMatch = line.match(/^export\s+(?:const\s+)?enum\s+(\w+)/);
58616
+ if (enumMatch) {
58617
+ symbols.push({
58618
+ name: enumMatch[1],
58619
+ kind: "enum",
58620
+ exported: true,
58621
+ signature: `enum ${enumMatch[1]}`,
58622
+ line: lineNum,
58623
+ jsdoc
58624
+ });
58625
+ continue;
58626
+ }
58627
+ const defaultMatch = line.match(/^export\s+default\s+(?:function\s+)?(\w+)/);
58628
+ if (defaultMatch) {
58629
+ symbols.push({
58630
+ name: defaultMatch[1],
58631
+ kind: "function",
58632
+ exported: true,
58633
+ signature: `default ${defaultMatch[1]}`,
58634
+ line: lineNum,
58635
+ jsdoc
58636
+ });
58637
+ }
58638
+ }
58639
+ return symbols.sort((a, b) => {
58640
+ if (a.line !== b.line)
58641
+ return a.line - b.line;
58642
+ return a.name.localeCompare(b.name);
58643
+ });
58644
+ }
58645
+ function extractPythonSymbols(filePath, cwd) {
58646
+ const fullPath = path48.join(cwd, filePath);
58647
+ if (!validatePathForRead(fullPath, cwd)) {
58648
+ return [];
58649
+ }
58650
+ let content;
58651
+ try {
58652
+ const stats = fs37.statSync(fullPath);
58653
+ if (stats.size > MAX_FILE_SIZE_BYTES2) {
58654
+ throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES2})`);
58655
+ }
58656
+ content = fs37.readFileSync(fullPath, "utf-8");
58657
+ } catch {
58658
+ return [];
58659
+ }
58660
+ const lines = content.split(`
58661
+ `);
58662
+ const symbols = [];
58663
+ const allMatch = content.match(/__all__\s*=\s*\[([^\]]+)\]/);
58664
+ const explicitExports = allMatch ? allMatch[1].split(",").map((s) => s.trim().replace(/['"]/g, "")) : null;
58665
+ for (let i2 = 0;i2 < lines.length; i2++) {
58666
+ const line = lines[i2];
58667
+ if (line.startsWith(" ") || line.startsWith("\t"))
58668
+ continue;
58669
+ const fnMatch = line.match(/^(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*(.+?))?:/);
58670
+ if (fnMatch && !fnMatch[1].startsWith("_")) {
58671
+ const exported = !explicitExports || explicitExports.includes(fnMatch[1]);
58672
+ symbols.push({
58673
+ name: fnMatch[1],
58674
+ kind: "function",
58675
+ exported,
58676
+ signature: `def ${fnMatch[1]}(${fnMatch[2].trim()})${fnMatch[3] ? ` -> ${fnMatch[3].trim()}` : ""}`,
58677
+ line: i2 + 1
58678
+ });
58679
+ }
58680
+ const classMatch = line.match(/^class\s+(\w+)(?:\(([^)]*)\))?:/);
58681
+ if (classMatch && !classMatch[1].startsWith("_")) {
58682
+ const exported = !explicitExports || explicitExports.includes(classMatch[1]);
58683
+ symbols.push({
58684
+ name: classMatch[1],
58685
+ kind: "class",
58686
+ exported,
58687
+ signature: `class ${classMatch[1]}${classMatch[2] ? `(${classMatch[2].trim()})` : ""}`,
58688
+ line: i2 + 1
58689
+ });
58690
+ }
58691
+ const constMatch = line.match(/^([A-Z][A-Z0-9_]+)\s*[:=]/);
58692
+ if (constMatch) {
58693
+ symbols.push({
58694
+ name: constMatch[1],
58695
+ kind: "const",
58696
+ exported: true,
58697
+ signature: line.trim().substring(0, 100),
58698
+ line: i2 + 1
58699
+ });
58700
+ }
58701
+ }
58702
+ return symbols.sort((a, b) => {
58703
+ if (a.line !== b.line)
58704
+ return a.line - b.line;
58705
+ return a.name.localeCompare(b.name);
58706
+ });
58707
+ }
58708
+ var symbols = createSwarmTool({
58709
+ 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.",
58710
+ args: {
58711
+ file: tool.schema.string().describe('File path to extract symbols from (e.g., "src/auth/login.ts")'),
58712
+ exported_only: tool.schema.boolean().default(true).describe("If true, only return exported/public symbols. If false, include all top-level symbols.")
58713
+ },
58714
+ execute: async (args2, directory) => {
58715
+ let file3;
58716
+ let exportedOnly = true;
58717
+ try {
58718
+ const obj = args2;
58719
+ file3 = String(obj.file);
58720
+ exportedOnly = obj.exported_only === true;
58721
+ } catch {
58722
+ return JSON.stringify({
58723
+ file: "<unknown>",
58724
+ error: "Invalid arguments: could not extract file path",
58725
+ symbols: []
58726
+ }, null, 2);
58727
+ }
58728
+ const cwd = directory;
58729
+ const ext = path48.extname(file3);
58730
+ if (containsControlChars(file3)) {
58731
+ return JSON.stringify({
58732
+ file: file3,
58733
+ error: "Path contains invalid control characters",
58734
+ symbols: []
58735
+ }, null, 2);
58736
+ }
58737
+ if (containsPathTraversal(file3)) {
58738
+ return JSON.stringify({
58739
+ file: file3,
58740
+ error: "Path contains path traversal sequence",
58741
+ symbols: []
58742
+ }, null, 2);
58743
+ }
58744
+ if (containsWindowsAttacks(file3)) {
58745
+ return JSON.stringify({
58746
+ file: file3,
58747
+ error: "Path contains invalid Windows-specific sequence",
58748
+ symbols: []
58749
+ }, null, 2);
58750
+ }
58751
+ if (!isPathInWorkspace(file3, cwd)) {
58752
+ return JSON.stringify({
58753
+ file: file3,
58754
+ error: "Path is outside workspace",
58755
+ symbols: []
58756
+ }, null, 2);
58757
+ }
58758
+ let syms;
58759
+ switch (ext) {
58760
+ case ".ts":
58761
+ case ".tsx":
58762
+ case ".js":
58763
+ case ".jsx":
58764
+ case ".mjs":
58765
+ case ".cjs":
58766
+ syms = extractTSSymbols(file3, cwd);
58767
+ break;
58768
+ case ".py":
58769
+ syms = extractPythonSymbols(file3, cwd);
58770
+ break;
58771
+ default:
58772
+ return JSON.stringify({
58773
+ file: file3,
58774
+ error: `Unsupported file extension: ${ext}. Supported: .ts, .tsx, .js, .jsx, .mjs, .cjs, .py`,
58775
+ symbols: []
58776
+ }, null, 2);
58777
+ }
58778
+ if (exportedOnly) {
58779
+ syms = syms.filter((s) => s.exported);
58780
+ }
58781
+ return JSON.stringify({
58782
+ file: file3,
58783
+ symbolCount: syms.length,
58784
+ symbols: syms
58785
+ }, null, 2);
58786
+ }
58787
+ });
58788
+
58789
+ // src/tools/batch-symbols.ts
58790
+ var WINDOWS_RESERVED_NAMES2 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
58791
+ function containsWindowsAttacks2(str) {
58792
+ if (/:[^\\/]/.test(str)) {
58793
+ return true;
58794
+ }
58795
+ const parts2 = str.split(/[/\\]/);
58796
+ for (const part of parts2) {
58797
+ if (WINDOWS_RESERVED_NAMES2.test(part)) {
58798
+ return true;
58799
+ }
58800
+ }
58801
+ return false;
58802
+ }
58803
+ function isPathInWorkspace2(filePath, workspace) {
58804
+ try {
58805
+ const resolvedPath = path49.resolve(workspace, filePath);
58806
+ if (!fs38.existsSync(resolvedPath)) {
58807
+ return true;
58808
+ }
58809
+ const realWorkspace = fs38.realpathSync(workspace);
58810
+ const realResolvedPath = fs38.realpathSync(resolvedPath);
58811
+ const relativePath = path49.relative(realWorkspace, realResolvedPath);
58812
+ if (relativePath.startsWith("..") || path49.isAbsolute(relativePath)) {
58813
+ return false;
58814
+ }
58815
+ return true;
58816
+ } catch {
58817
+ return false;
58818
+ }
58819
+ }
58820
+ function processFile(file3, cwd, exportedOnly) {
58821
+ const ext = path49.extname(file3);
58822
+ if (containsControlChars(file3)) {
58823
+ return {
58824
+ file: file3,
58825
+ success: false,
58826
+ error: "Path contains invalid control characters",
58827
+ errorType: "invalid-path"
58828
+ };
58829
+ }
58830
+ if (containsPathTraversal(file3)) {
58831
+ return {
58832
+ file: file3,
58833
+ success: false,
58834
+ error: "Path contains path traversal sequence",
58835
+ errorType: "path-traversal"
58836
+ };
58837
+ }
58838
+ if (containsWindowsAttacks2(file3)) {
58839
+ return {
58840
+ file: file3,
58841
+ success: false,
58842
+ error: "Path contains invalid Windows-specific sequence",
58843
+ errorType: "invalid-path"
58844
+ };
58845
+ }
58846
+ if (!isPathInWorkspace2(file3, cwd)) {
58847
+ return {
58848
+ file: file3,
58849
+ success: false,
58850
+ error: "Path is outside workspace",
58851
+ errorType: "path-outside-workspace"
58852
+ };
58853
+ }
58854
+ const fullPath = path49.join(cwd, file3);
58855
+ if (!fs38.existsSync(fullPath)) {
58856
+ return {
58857
+ file: file3,
58858
+ success: false,
58859
+ error: `File not found: ${file3}`,
58860
+ errorType: "file-not-found"
58861
+ };
58862
+ }
58863
+ let syms;
58864
+ switch (ext) {
58865
+ case ".ts":
58866
+ case ".tsx":
58867
+ case ".js":
58868
+ case ".jsx":
58869
+ case ".mjs":
58870
+ case ".cjs":
58871
+ syms = extractTSSymbols(file3, cwd);
58872
+ break;
58873
+ case ".py":
58874
+ syms = extractPythonSymbols(file3, cwd);
58875
+ break;
58876
+ default:
58877
+ return {
58878
+ file: file3,
58879
+ success: false,
58880
+ error: `Unsupported file extension: ${ext}. Supported: .ts, .tsx, .js, .jsx, .mjs, .cjs, .py`,
58881
+ errorType: "unsupported-language"
58882
+ };
58883
+ }
58884
+ let isEmptyFile = false;
58885
+ try {
58886
+ const stats = fs38.statSync(fullPath);
58887
+ if (stats.size === 0) {
58888
+ isEmptyFile = true;
58889
+ }
58890
+ } catch {}
58891
+ if (syms.length === 0) {
58892
+ try {
58893
+ const content = fs38.readFileSync(fullPath, "utf-8");
58894
+ if (content.trim().length === 0) {
58895
+ isEmptyFile = true;
58896
+ }
58897
+ } catch {}
58898
+ }
58899
+ if (isEmptyFile) {
58900
+ return {
58901
+ file: file3,
58902
+ success: true,
58903
+ symbols: [],
58904
+ error: "empty-file",
58905
+ errorType: "empty-file"
58906
+ };
58907
+ }
58908
+ if (exportedOnly) {
58909
+ syms = syms.filter((s) => s.exported);
58910
+ }
58911
+ return {
58912
+ file: file3,
58913
+ success: true,
58914
+ symbols: syms
58915
+ };
58916
+ }
58917
+ var batch_symbols = createSwarmTool({
58918
+ 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.",
58919
+ args: {
58920
+ files: tool.schema.array(tool.schema.string()).describe("Array of file paths to extract symbols from"),
58921
+ exported_only: tool.schema.boolean().default(true).describe("If true, only return exported/public symbols. If false, include all top-level symbols.")
58922
+ },
58923
+ execute: async (args2, directory) => {
58924
+ let files;
58925
+ let exportedOnly = true;
58926
+ try {
58927
+ const obj = args2;
58928
+ if (!Array.isArray(obj.files)) {
58929
+ return JSON.stringify({
58930
+ results: [],
58931
+ totalFiles: 0,
58932
+ successCount: 0,
58933
+ failureCount: 0,
58934
+ error: "Invalid arguments: files must be an array"
58935
+ }, null, 2);
58936
+ }
58937
+ files = obj.files.map((f) => String(f));
58938
+ exportedOnly = obj.exported_only === true;
58939
+ } catch {
58940
+ return JSON.stringify({
58941
+ results: [],
58942
+ totalFiles: 0,
58943
+ successCount: 0,
58944
+ failureCount: 0,
58945
+ error: "Invalid arguments: could not extract files array"
58946
+ }, null, 2);
58947
+ }
58948
+ const cwd = directory;
58949
+ const results = [];
58950
+ for (const file3 of files) {
58951
+ const result = processFile(file3, cwd, exportedOnly);
58952
+ results.push(result);
58953
+ }
58954
+ const successCount = results.filter((r) => r.success).length;
58955
+ const failureCount = results.filter((r) => !r.success).length;
58956
+ const batchResult = {
58957
+ results,
58958
+ totalFiles: files.length,
58959
+ successCount,
58960
+ failureCount
58961
+ };
58962
+ return JSON.stringify(batchResult, null, 2);
58963
+ }
58964
+ });
58362
58965
  // src/tools/build-check.ts
58363
58966
  init_dist();
58364
58967
  init_discovery();
@@ -58538,8 +59141,8 @@ init_dist();
58538
59141
  init_manager();
58539
59142
  init_create_tool();
58540
59143
  init_resolve_working_directory();
58541
- import * as fs37 from "fs";
58542
- import * as path48 from "path";
59144
+ import * as fs39 from "fs";
59145
+ import * as path50 from "path";
58543
59146
  var EVIDENCE_DIR = ".swarm/evidence";
58544
59147
  var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
58545
59148
  function isValidTaskId3(taskId) {
@@ -58556,18 +59159,18 @@ function isValidTaskId3(taskId) {
58556
59159
  return TASK_ID_PATTERN2.test(taskId);
58557
59160
  }
58558
59161
  function isPathWithinSwarm(filePath, workspaceRoot) {
58559
- const normalizedWorkspace = path48.resolve(workspaceRoot);
58560
- const swarmPath = path48.join(normalizedWorkspace, ".swarm", "evidence");
58561
- const normalizedPath = path48.resolve(filePath);
59162
+ const normalizedWorkspace = path50.resolve(workspaceRoot);
59163
+ const swarmPath = path50.join(normalizedWorkspace, ".swarm", "evidence");
59164
+ const normalizedPath = path50.resolve(filePath);
58562
59165
  return normalizedPath.startsWith(swarmPath);
58563
59166
  }
58564
59167
  function readEvidenceFile(evidencePath) {
58565
- if (!fs37.existsSync(evidencePath)) {
59168
+ if (!fs39.existsSync(evidencePath)) {
58566
59169
  return null;
58567
59170
  }
58568
59171
  let content;
58569
59172
  try {
58570
- content = fs37.readFileSync(evidencePath, "utf-8");
59173
+ content = fs39.readFileSync(evidencePath, "utf-8");
58571
59174
  } catch {
58572
59175
  return null;
58573
59176
  }
@@ -58639,7 +59242,7 @@ var check_gate_status = createSwarmTool({
58639
59242
  };
58640
59243
  return JSON.stringify(errorResult, null, 2);
58641
59244
  }
58642
- const evidencePath = path48.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
59245
+ const evidencePath = path50.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
58643
59246
  if (!isPathWithinSwarm(evidencePath, directory)) {
58644
59247
  const errorResult = {
58645
59248
  taskId: taskIdInput,
@@ -58732,8 +59335,8 @@ init_co_change_analyzer();
58732
59335
  // src/tools/completion-verify.ts
58733
59336
  init_dist();
58734
59337
  init_utils2();
58735
- import * as fs38 from "fs";
58736
- import * as path49 from "path";
59338
+ import * as fs40 from "fs";
59339
+ import * as path51 from "path";
58737
59340
  init_create_tool();
58738
59341
  init_resolve_working_directory();
58739
59342
  function extractMatches(regex, text) {
@@ -58829,7 +59432,7 @@ async function executeCompletionVerify(args2, directory) {
58829
59432
  let plan;
58830
59433
  try {
58831
59434
  const planPath = validateSwarmPath(directory, "plan.json");
58832
- const planRaw = fs38.readFileSync(planPath, "utf-8");
59435
+ const planRaw = fs40.readFileSync(planPath, "utf-8");
58833
59436
  plan = JSON.parse(planRaw);
58834
59437
  } catch {
58835
59438
  const result2 = {
@@ -58887,10 +59490,10 @@ async function executeCompletionVerify(args2, directory) {
58887
59490
  let hasFileReadFailure = false;
58888
59491
  for (const filePath of fileTargets) {
58889
59492
  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);
59493
+ const resolvedPath = path51.resolve(directory, normalizedPath);
59494
+ const projectRoot = path51.resolve(directory);
59495
+ const relative9 = path51.relative(projectRoot, resolvedPath);
59496
+ const withinProject = relative9 === "" || !relative9.startsWith("..") && !path51.isAbsolute(relative9);
58894
59497
  if (!withinProject) {
58895
59498
  blockedTasks.push({
58896
59499
  task_id: task.id,
@@ -58903,7 +59506,7 @@ async function executeCompletionVerify(args2, directory) {
58903
59506
  }
58904
59507
  let fileContent;
58905
59508
  try {
58906
- fileContent = fs38.readFileSync(resolvedPath, "utf-8");
59509
+ fileContent = fs40.readFileSync(resolvedPath, "utf-8");
58907
59510
  } catch {
58908
59511
  blockedTasks.push({
58909
59512
  task_id: task.id,
@@ -58945,9 +59548,9 @@ async function executeCompletionVerify(args2, directory) {
58945
59548
  blockedTasks
58946
59549
  };
58947
59550
  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 });
59551
+ const evidenceDir = path51.join(directory, ".swarm", "evidence", `${phase}`);
59552
+ const evidencePath = path51.join(evidenceDir, "completion-verify.json");
59553
+ fs40.mkdirSync(evidenceDir, { recursive: true });
58951
59554
  const evidenceBundle = {
58952
59555
  schema_version: "1.0.0",
58953
59556
  task_id: "completion-verify",
@@ -58968,7 +59571,7 @@ async function executeCompletionVerify(args2, directory) {
58968
59571
  }
58969
59572
  ]
58970
59573
  };
58971
- fs38.writeFileSync(evidencePath, JSON.stringify(evidenceBundle, null, 2), "utf-8");
59574
+ fs40.writeFileSync(evidencePath, JSON.stringify(evidenceBundle, null, 2), "utf-8");
58972
59575
  } catch {}
58973
59576
  return JSON.stringify(result, null, 2);
58974
59577
  }
@@ -59022,13 +59625,13 @@ var completion_verify = createSwarmTool({
59022
59625
  });
59023
59626
  // src/tools/complexity-hotspots.ts
59024
59627
  init_dist();
59025
- import * as fs40 from "fs";
59026
- import * as path51 from "path";
59628
+ import * as fs42 from "fs";
59629
+ import * as path53 from "path";
59027
59630
 
59028
59631
  // src/quality/metrics.ts
59029
- import * as fs39 from "fs";
59030
- import * as path50 from "path";
59031
- var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
59632
+ import * as fs41 from "fs";
59633
+ import * as path52 from "path";
59634
+ var MAX_FILE_SIZE_BYTES3 = 256 * 1024;
59032
59635
  var MIN_DUPLICATION_LINES = 10;
59033
59636
  function estimateCyclomaticComplexity(content) {
59034
59637
  let processed = content.replace(/\/\*[\s\S]*?\*\//g, "");
@@ -59065,11 +59668,11 @@ function estimateCyclomaticComplexity(content) {
59065
59668
  }
59066
59669
  function getComplexityForFile(filePath) {
59067
59670
  try {
59068
- const stat2 = fs39.statSync(filePath);
59069
- if (stat2.size > MAX_FILE_SIZE_BYTES2) {
59671
+ const stat2 = fs41.statSync(filePath);
59672
+ if (stat2.size > MAX_FILE_SIZE_BYTES3) {
59070
59673
  return null;
59071
59674
  }
59072
- const content = fs39.readFileSync(filePath, "utf-8");
59675
+ const content = fs41.readFileSync(filePath, "utf-8");
59073
59676
  return estimateCyclomaticComplexity(content);
59074
59677
  } catch {
59075
59678
  return null;
@@ -59079,8 +59682,8 @@ async function computeComplexityDelta(files, workingDir) {
59079
59682
  let totalComplexity = 0;
59080
59683
  const analyzedFiles = [];
59081
59684
  for (const file3 of files) {
59082
- const fullPath = path50.isAbsolute(file3) ? file3 : path50.join(workingDir, file3);
59083
- if (!fs39.existsSync(fullPath)) {
59685
+ const fullPath = path52.isAbsolute(file3) ? file3 : path52.join(workingDir, file3);
59686
+ if (!fs41.existsSync(fullPath)) {
59084
59687
  continue;
59085
59688
  }
59086
59689
  const complexity = getComplexityForFile(fullPath);
@@ -59201,8 +59804,8 @@ function countGoExports(content) {
59201
59804
  }
59202
59805
  function getExportCountForFile(filePath) {
59203
59806
  try {
59204
- const content = fs39.readFileSync(filePath, "utf-8");
59205
- const ext = path50.extname(filePath).toLowerCase();
59807
+ const content = fs41.readFileSync(filePath, "utf-8");
59808
+ const ext = path52.extname(filePath).toLowerCase();
59206
59809
  switch (ext) {
59207
59810
  case ".ts":
59208
59811
  case ".tsx":
@@ -59228,8 +59831,8 @@ async function computePublicApiDelta(files, workingDir) {
59228
59831
  let totalExports = 0;
59229
59832
  const analyzedFiles = [];
59230
59833
  for (const file3 of files) {
59231
- const fullPath = path50.isAbsolute(file3) ? file3 : path50.join(workingDir, file3);
59232
- if (!fs39.existsSync(fullPath)) {
59834
+ const fullPath = path52.isAbsolute(file3) ? file3 : path52.join(workingDir, file3);
59835
+ if (!fs41.existsSync(fullPath)) {
59233
59836
  continue;
59234
59837
  }
59235
59838
  const exports = getExportCountForFile(fullPath);
@@ -59262,16 +59865,16 @@ async function computeDuplicationRatio(files, workingDir) {
59262
59865
  let duplicateLines = 0;
59263
59866
  const analyzedFiles = [];
59264
59867
  for (const file3 of files) {
59265
- const fullPath = path50.isAbsolute(file3) ? file3 : path50.join(workingDir, file3);
59266
- if (!fs39.existsSync(fullPath)) {
59868
+ const fullPath = path52.isAbsolute(file3) ? file3 : path52.join(workingDir, file3);
59869
+ if (!fs41.existsSync(fullPath)) {
59267
59870
  continue;
59268
59871
  }
59269
59872
  try {
59270
- const stat2 = fs39.statSync(fullPath);
59271
- if (stat2.size > MAX_FILE_SIZE_BYTES2) {
59873
+ const stat2 = fs41.statSync(fullPath);
59874
+ if (stat2.size > MAX_FILE_SIZE_BYTES3) {
59272
59875
  continue;
59273
59876
  }
59274
- const content = fs39.readFileSync(fullPath, "utf-8");
59877
+ const content = fs41.readFileSync(fullPath, "utf-8");
59275
59878
  const lines = content.split(`
59276
59879
  `).filter((line) => line.trim().length > 0);
59277
59880
  if (lines.length < MIN_DUPLICATION_LINES) {
@@ -59295,8 +59898,8 @@ function countCodeLines(content) {
59295
59898
  return lines.length;
59296
59899
  }
59297
59900
  function isTestFile(filePath) {
59298
- const basename7 = path50.basename(filePath);
59299
- const _ext = path50.extname(filePath).toLowerCase();
59901
+ const basename8 = path52.basename(filePath);
59902
+ const _ext = path52.extname(filePath).toLowerCase();
59300
59903
  const testPatterns = [
59301
59904
  ".test.",
59302
59905
  ".spec.",
@@ -59311,7 +59914,7 @@ function isTestFile(filePath) {
59311
59914
  ".spec.jsx"
59312
59915
  ];
59313
59916
  for (const pattern of testPatterns) {
59314
- if (basename7.includes(pattern)) {
59917
+ if (basename8.includes(pattern)) {
59315
59918
  return true;
59316
59919
  }
59317
59920
  }
@@ -59377,8 +59980,8 @@ function matchGlobSegment(globSegments, pathSegments) {
59377
59980
  }
59378
59981
  return gIndex === globSegments.length && pIndex === pathSegments.length;
59379
59982
  }
59380
- function matchesGlobSegment(path51, glob) {
59381
- const normalizedPath = path51.replace(/\\/g, "/");
59983
+ function matchesGlobSegment(path53, glob) {
59984
+ const normalizedPath = path53.replace(/\\/g, "/");
59382
59985
  const normalizedGlob = glob.replace(/\\/g, "/");
59383
59986
  if (normalizedPath.includes("//")) {
59384
59987
  return false;
@@ -59409,8 +60012,8 @@ function simpleGlobToRegex2(glob) {
59409
60012
  function hasGlobstar(glob) {
59410
60013
  return glob.includes("**");
59411
60014
  }
59412
- function globMatches(path51, glob) {
59413
- const normalizedPath = path51.replace(/\\/g, "/");
60015
+ function globMatches(path53, glob) {
60016
+ const normalizedPath = path53.replace(/\\/g, "/");
59414
60017
  if (!glob || glob === "") {
59415
60018
  if (normalizedPath.includes("//")) {
59416
60019
  return false;
@@ -59446,31 +60049,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
59446
60049
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
59447
60050
  let testLines = 0;
59448
60051
  let codeLines = 0;
59449
- const srcDir = path50.join(workingDir, "src");
59450
- if (fs39.existsSync(srcDir)) {
60052
+ const srcDir = path52.join(workingDir, "src");
60053
+ if (fs41.existsSync(srcDir)) {
59451
60054
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
59452
60055
  codeLines += lines;
59453
60056
  });
59454
60057
  }
59455
60058
  const possibleSrcDirs = ["lib", "app", "source", "core"];
59456
60059
  for (const dir of possibleSrcDirs) {
59457
- const dirPath = path50.join(workingDir, dir);
59458
- if (fs39.existsSync(dirPath)) {
60060
+ const dirPath = path52.join(workingDir, dir);
60061
+ if (fs41.existsSync(dirPath)) {
59459
60062
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
59460
60063
  codeLines += lines;
59461
60064
  });
59462
60065
  }
59463
60066
  }
59464
- const testsDir = path50.join(workingDir, "tests");
59465
- if (fs39.existsSync(testsDir)) {
60067
+ const testsDir = path52.join(workingDir, "tests");
60068
+ if (fs41.existsSync(testsDir)) {
59466
60069
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
59467
60070
  testLines += lines;
59468
60071
  });
59469
60072
  }
59470
60073
  const possibleTestDirs = ["test", "__tests__", "specs"];
59471
60074
  for (const dir of possibleTestDirs) {
59472
- const dirPath = path50.join(workingDir, dir);
59473
- if (fs39.existsSync(dirPath) && dirPath !== testsDir) {
60075
+ const dirPath = path52.join(workingDir, dir);
60076
+ if (fs41.existsSync(dirPath) && dirPath !== testsDir) {
59474
60077
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
59475
60078
  testLines += lines;
59476
60079
  });
@@ -59482,9 +60085,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
59482
60085
  }
59483
60086
  async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
59484
60087
  try {
59485
- const entries = fs39.readdirSync(dirPath, { withFileTypes: true });
60088
+ const entries = fs41.readdirSync(dirPath, { withFileTypes: true });
59486
60089
  for (const entry of entries) {
59487
- const fullPath = path50.join(dirPath, entry.name);
60090
+ const fullPath = path52.join(dirPath, entry.name);
59488
60091
  if (entry.isDirectory()) {
59489
60092
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
59490
60093
  continue;
@@ -59492,7 +60095,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
59492
60095
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
59493
60096
  } else if (entry.isFile()) {
59494
60097
  const relativePath = fullPath.replace(`${dirPath}/`, "");
59495
- const ext = path50.extname(entry.name).toLowerCase();
60098
+ const ext = path52.extname(entry.name).toLowerCase();
59496
60099
  const validExts = [
59497
60100
  ".ts",
59498
60101
  ".tsx",
@@ -59528,7 +60131,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
59528
60131
  continue;
59529
60132
  }
59530
60133
  try {
59531
- const content = fs39.readFileSync(fullPath, "utf-8");
60134
+ const content = fs41.readFileSync(fullPath, "utf-8");
59532
60135
  const lines = countCodeLines(content);
59533
60136
  callback(lines);
59534
60137
  } catch {}
@@ -59635,7 +60238,7 @@ async function computeQualityMetrics(changedFiles, thresholds, workingDir) {
59635
60238
 
59636
60239
  // src/tools/complexity-hotspots.ts
59637
60240
  init_create_tool();
59638
- var MAX_FILE_SIZE_BYTES3 = 256 * 1024;
60241
+ var MAX_FILE_SIZE_BYTES4 = 256 * 1024;
59639
60242
  var DEFAULT_DAYS = 90;
59640
60243
  var DEFAULT_TOP_N = 20;
59641
60244
  var DEFAULT_EXTENSIONS = "ts,tsx,js,jsx,py,rs,ps1";
@@ -59729,11 +60332,11 @@ async function getGitChurn(days, directory) {
59729
60332
  }
59730
60333
  function getComplexityForFile2(filePath) {
59731
60334
  try {
59732
- const stat2 = fs40.statSync(filePath);
59733
- if (stat2.size > MAX_FILE_SIZE_BYTES3) {
60335
+ const stat2 = fs42.statSync(filePath);
60336
+ if (stat2.size > MAX_FILE_SIZE_BYTES4) {
59734
60337
  return null;
59735
60338
  }
59736
- const content = fs40.readFileSync(filePath, "utf-8");
60339
+ const content = fs42.readFileSync(filePath, "utf-8");
59737
60340
  return estimateCyclomaticComplexity(content);
59738
60341
  } catch {
59739
60342
  return null;
@@ -59744,7 +60347,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
59744
60347
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
59745
60348
  const filteredChurn = new Map;
59746
60349
  for (const [file3, count] of churnMap) {
59747
- const ext = path51.extname(file3).toLowerCase();
60350
+ const ext = path53.extname(file3).toLowerCase();
59748
60351
  if (extSet.has(ext)) {
59749
60352
  filteredChurn.set(file3, count);
59750
60353
  }
@@ -59754,8 +60357,8 @@ async function analyzeHotspots(days, topN, extensions, directory) {
59754
60357
  let analyzedFiles = 0;
59755
60358
  for (const [file3, churnCount] of filteredChurn) {
59756
60359
  let fullPath = file3;
59757
- if (!fs40.existsSync(fullPath)) {
59758
- fullPath = path51.join(cwd, file3);
60360
+ if (!fs42.existsSync(fullPath)) {
60361
+ fullPath = path53.join(cwd, file3);
59759
60362
  }
59760
60363
  const complexity = getComplexityForFile2(fullPath);
59761
60364
  if (complexity !== null) {
@@ -59977,7 +60580,12 @@ var curator_analyze = createSwarmTool({
59977
60580
  let applied = 0;
59978
60581
  let skipped = 0;
59979
60582
  if (typedArgs.recommendations && typedArgs.recommendations.length > 0) {
59980
- const result = await applyCuratorKnowledgeUpdates(directory, typedArgs.recommendations, knowledgeConfig);
60583
+ const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
60584
+ const sanitizedRecs = typedArgs.recommendations.map((rec) => ({
60585
+ ...rec,
60586
+ entry_id: rec.entry_id === undefined || UUID_V4.test(rec.entry_id) ? rec.entry_id : undefined
60587
+ }));
60588
+ const result = await applyCuratorKnowledgeUpdates(directory, sanitizedRecs, knowledgeConfig);
59981
60589
  applied = result.applied;
59982
60590
  skipped = result.skipped;
59983
60591
  }
@@ -59997,8 +60605,8 @@ var curator_analyze = createSwarmTool({
59997
60605
  });
59998
60606
  // src/tools/declare-scope.ts
59999
60607
  init_tool();
60000
- import * as fs41 from "fs";
60001
- import * as path52 from "path";
60608
+ import * as fs43 from "fs";
60609
+ import * as path54 from "path";
60002
60610
  init_create_tool();
60003
60611
  function validateTaskIdFormat(taskId) {
60004
60612
  const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
@@ -60077,8 +60685,8 @@ async function executeDeclareScope(args2, fallbackDir) {
60077
60685
  };
60078
60686
  }
60079
60687
  }
60080
- normalizedDir = path52.normalize(args2.working_directory);
60081
- const pathParts = normalizedDir.split(path52.sep);
60688
+ normalizedDir = path54.normalize(args2.working_directory);
60689
+ const pathParts = normalizedDir.split(path54.sep);
60082
60690
  if (pathParts.includes("..")) {
60083
60691
  return {
60084
60692
  success: false,
@@ -60088,11 +60696,11 @@ async function executeDeclareScope(args2, fallbackDir) {
60088
60696
  ]
60089
60697
  };
60090
60698
  }
60091
- const resolvedDir = path52.resolve(normalizedDir);
60699
+ const resolvedDir = path54.resolve(normalizedDir);
60092
60700
  try {
60093
- const realPath = fs41.realpathSync(resolvedDir);
60094
- const planPath2 = path52.join(realPath, ".swarm", "plan.json");
60095
- if (!fs41.existsSync(planPath2)) {
60701
+ const realPath = fs43.realpathSync(resolvedDir);
60702
+ const planPath2 = path54.join(realPath, ".swarm", "plan.json");
60703
+ if (!fs43.existsSync(planPath2)) {
60096
60704
  return {
60097
60705
  success: false,
60098
60706
  message: `Invalid working_directory: plan not found in "${realPath}"`,
@@ -60115,8 +60723,8 @@ async function executeDeclareScope(args2, fallbackDir) {
60115
60723
  console.warn("[declare-scope] fallbackDir is undefined, falling back to process.cwd()");
60116
60724
  }
60117
60725
  const directory = normalizedDir || fallbackDir;
60118
- const planPath = path52.resolve(directory, ".swarm", "plan.json");
60119
- if (!fs41.existsSync(planPath)) {
60726
+ const planPath = path54.resolve(directory, ".swarm", "plan.json");
60727
+ if (!fs43.existsSync(planPath)) {
60120
60728
  return {
60121
60729
  success: false,
60122
60730
  message: "No plan found",
@@ -60125,7 +60733,7 @@ async function executeDeclareScope(args2, fallbackDir) {
60125
60733
  }
60126
60734
  let planContent;
60127
60735
  try {
60128
- planContent = JSON.parse(fs41.readFileSync(planPath, "utf-8"));
60736
+ planContent = JSON.parse(fs43.readFileSync(planPath, "utf-8"));
60129
60737
  } catch {
60130
60738
  return {
60131
60739
  success: false,
@@ -60157,8 +60765,8 @@ async function executeDeclareScope(args2, fallbackDir) {
60157
60765
  const normalizeErrors = [];
60158
60766
  const dir = normalizedDir || fallbackDir || process.cwd();
60159
60767
  const mergedFiles = rawMergedFiles.map((file3) => {
60160
- if (path52.isAbsolute(file3)) {
60161
- const relativePath = path52.relative(dir, file3).replace(/\\/g, "/");
60768
+ if (path54.isAbsolute(file3)) {
60769
+ const relativePath = path54.relative(dir, file3).replace(/\\/g, "/");
60162
60770
  if (relativePath.startsWith("..")) {
60163
60771
  normalizeErrors.push(`Path '${file3}' resolves outside the project directory`);
60164
60772
  return file3;
@@ -60205,7 +60813,7 @@ import * as child_process5 from "child_process";
60205
60813
 
60206
60814
  // src/diff/ast-diff.ts
60207
60815
  init_tree_sitter();
60208
- import { extname as extname7 } from "path";
60816
+ import { extname as extname9 } from "path";
60209
60817
 
60210
60818
  // src/lang/registry.ts
60211
60819
  init_runtime();
@@ -60290,7 +60898,7 @@ var QUERIES = {
60290
60898
  var AST_TIMEOUT_MS = 500;
60291
60899
  async function computeASTDiff(filePath, oldContent, newContent) {
60292
60900
  const startTime = Date.now();
60293
- const extension = extname7(filePath).toLowerCase();
60901
+ const extension = extname9(filePath).toLowerCase();
60294
60902
  const language = getLanguageForExtension(extension);
60295
60903
  if (!language) {
60296
60904
  return {
@@ -60352,26 +60960,26 @@ async function computeASTDiff(filePath, oldContent, newContent) {
60352
60960
  }
60353
60961
  }
60354
60962
  function extractSymbols(tree, language) {
60355
- const symbols = [];
60963
+ const symbols2 = [];
60356
60964
  const queryStr = QUERIES[language.id];
60357
60965
  if (!queryStr) {
60358
- return symbols;
60966
+ return symbols2;
60359
60967
  }
60360
60968
  try {
60361
60969
  const lang = tree.language;
60362
60970
  if (!lang) {
60363
- return symbols;
60971
+ return symbols2;
60364
60972
  }
60365
60973
  const query = new Query(lang, queryStr);
60366
60974
  const matches = query.matches(tree.rootNode);
60367
60975
  for (const match of matches) {
60368
60976
  const symbol3 = parseMatch(match, language.id);
60369
60977
  if (symbol3) {
60370
- symbols.push(symbol3);
60978
+ symbols2.push(symbol3);
60371
60979
  }
60372
60980
  }
60373
60981
  } catch {}
60374
- return symbols;
60982
+ return symbols2;
60375
60983
  }
60376
60984
  function parseMatch(match, languageId) {
60377
60985
  const captures = match.captures;
@@ -60484,20 +61092,20 @@ function validateBase(base) {
60484
61092
  function validatePaths(paths) {
60485
61093
  if (!paths)
60486
61094
  return null;
60487
- for (const path54 of paths) {
60488
- if (!path54 || path54.length === 0) {
61095
+ for (const path56 of paths) {
61096
+ if (!path56 || path56.length === 0) {
60489
61097
  return "empty path not allowed";
60490
61098
  }
60491
- if (path54.length > MAX_PATH_LENGTH) {
61099
+ if (path56.length > MAX_PATH_LENGTH) {
60492
61100
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
60493
61101
  }
60494
- if (SHELL_METACHARACTERS2.test(path54)) {
61102
+ if (SHELL_METACHARACTERS2.test(path56)) {
60495
61103
  return "path contains shell metacharacters";
60496
61104
  }
60497
- if (path54.startsWith("-")) {
61105
+ if (path56.startsWith("-")) {
60498
61106
  return 'path cannot start with "-" (option-like arguments not allowed)';
60499
61107
  }
60500
- if (CONTROL_CHAR_PATTERN2.test(path54)) {
61108
+ if (CONTROL_CHAR_PATTERN2.test(path56)) {
60501
61109
  return "path contains control characters";
60502
61110
  }
60503
61111
  }
@@ -60578,8 +61186,8 @@ var diff = createSwarmTool({
60578
61186
  if (parts2.length >= 3) {
60579
61187
  const additions = parseInt(parts2[0], 10) || 0;
60580
61188
  const deletions = parseInt(parts2[1], 10) || 0;
60581
- const path54 = parts2[2];
60582
- files.push({ path: path54, additions, deletions });
61189
+ const path56 = parts2[2];
61190
+ files.push({ path: path56, additions, deletions });
60583
61191
  }
60584
61192
  }
60585
61193
  const contractChanges = [];
@@ -60861,9 +61469,9 @@ Use these as DOMAIN values when delegating to @sme.`;
60861
61469
  // src/tools/evidence-check.ts
60862
61470
  init_dist();
60863
61471
  init_create_tool();
60864
- import * as fs42 from "fs";
60865
- import * as path54 from "path";
60866
- var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
61472
+ import * as fs44 from "fs";
61473
+ import * as path56 from "path";
61474
+ var MAX_FILE_SIZE_BYTES5 = 1024 * 1024;
60867
61475
  var MAX_EVIDENCE_FILES = 1000;
60868
61476
  var EVIDENCE_DIR2 = ".swarm/evidence";
60869
61477
  var PLAN_FILE = ".swarm/plan.md";
@@ -60889,9 +61497,9 @@ function validateRequiredTypes(input) {
60889
61497
  return null;
60890
61498
  }
60891
61499
  function isPathWithinSwarm2(filePath, cwd) {
60892
- const normalizedCwd = path54.resolve(cwd);
60893
- const swarmPath = path54.join(normalizedCwd, ".swarm");
60894
- const normalizedPath = path54.resolve(filePath);
61500
+ const normalizedCwd = path56.resolve(cwd);
61501
+ const swarmPath = path56.join(normalizedCwd, ".swarm");
61502
+ const normalizedPath = path56.resolve(filePath);
60895
61503
  return normalizedPath.startsWith(swarmPath);
60896
61504
  }
60897
61505
  function parseCompletedTasks(planContent) {
@@ -60907,12 +61515,12 @@ function parseCompletedTasks(planContent) {
60907
61515
  }
60908
61516
  function readEvidenceFiles(evidenceDir, _cwd) {
60909
61517
  const evidence = [];
60910
- if (!fs42.existsSync(evidenceDir) || !fs42.statSync(evidenceDir).isDirectory()) {
61518
+ if (!fs44.existsSync(evidenceDir) || !fs44.statSync(evidenceDir).isDirectory()) {
60911
61519
  return evidence;
60912
61520
  }
60913
61521
  let files;
60914
61522
  try {
60915
- files = fs42.readdirSync(evidenceDir);
61523
+ files = fs44.readdirSync(evidenceDir);
60916
61524
  } catch {
60917
61525
  return evidence;
60918
61526
  }
@@ -60921,14 +61529,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
60921
61529
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
60922
61530
  continue;
60923
61531
  }
60924
- const filePath = path54.join(evidenceDir, filename);
61532
+ const filePath = path56.join(evidenceDir, filename);
60925
61533
  try {
60926
- const resolvedPath = path54.resolve(filePath);
60927
- const evidenceDirResolved = path54.resolve(evidenceDir);
61534
+ const resolvedPath = path56.resolve(filePath);
61535
+ const evidenceDirResolved = path56.resolve(evidenceDir);
60928
61536
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
60929
61537
  continue;
60930
61538
  }
60931
- const stat2 = fs42.lstatSync(filePath);
61539
+ const stat2 = fs44.lstatSync(filePath);
60932
61540
  if (!stat2.isFile()) {
60933
61541
  continue;
60934
61542
  }
@@ -60937,8 +61545,8 @@ function readEvidenceFiles(evidenceDir, _cwd) {
60937
61545
  }
60938
61546
  let fileStat;
60939
61547
  try {
60940
- fileStat = fs42.statSync(filePath);
60941
- if (fileStat.size > MAX_FILE_SIZE_BYTES4) {
61548
+ fileStat = fs44.statSync(filePath);
61549
+ if (fileStat.size > MAX_FILE_SIZE_BYTES5) {
60942
61550
  continue;
60943
61551
  }
60944
61552
  } catch {
@@ -60946,7 +61554,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
60946
61554
  }
60947
61555
  let content;
60948
61556
  try {
60949
- content = fs42.readFileSync(filePath, "utf-8");
61557
+ content = fs44.readFileSync(filePath, "utf-8");
60950
61558
  } catch {
60951
61559
  continue;
60952
61560
  }
@@ -61042,7 +61650,7 @@ var evidence_check = createSwarmTool({
61042
61650
  return JSON.stringify(errorResult, null, 2);
61043
61651
  }
61044
61652
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
61045
- const planPath = path54.join(cwd, PLAN_FILE);
61653
+ const planPath = path56.join(cwd, PLAN_FILE);
61046
61654
  if (!isPathWithinSwarm2(planPath, cwd)) {
61047
61655
  const errorResult = {
61048
61656
  error: "plan file path validation failed",
@@ -61056,7 +61664,7 @@ var evidence_check = createSwarmTool({
61056
61664
  }
61057
61665
  let planContent;
61058
61666
  try {
61059
- planContent = fs42.readFileSync(planPath, "utf-8");
61667
+ planContent = fs44.readFileSync(planPath, "utf-8");
61060
61668
  } catch {
61061
61669
  const result2 = {
61062
61670
  message: "No completed tasks found in plan.",
@@ -61074,7 +61682,7 @@ var evidence_check = createSwarmTool({
61074
61682
  };
61075
61683
  return JSON.stringify(result2, null, 2);
61076
61684
  }
61077
- const evidenceDir = path54.join(cwd, EVIDENCE_DIR2);
61685
+ const evidenceDir = path56.join(cwd, EVIDENCE_DIR2);
61078
61686
  const evidence = readEvidenceFiles(evidenceDir, cwd);
61079
61687
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
61080
61688
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -61091,8 +61699,8 @@ var evidence_check = createSwarmTool({
61091
61699
  // src/tools/file-extractor.ts
61092
61700
  init_tool();
61093
61701
  init_create_tool();
61094
- import * as fs43 from "fs";
61095
- import * as path55 from "path";
61702
+ import * as fs45 from "fs";
61703
+ import * as path57 from "path";
61096
61704
  var EXT_MAP = {
61097
61705
  python: ".py",
61098
61706
  py: ".py",
@@ -61154,8 +61762,8 @@ var extract_code_blocks = createSwarmTool({
61154
61762
  execute: async (args2, directory) => {
61155
61763
  const { content, output_dir, prefix } = args2;
61156
61764
  const targetDir = output_dir || directory;
61157
- if (!fs43.existsSync(targetDir)) {
61158
- fs43.mkdirSync(targetDir, { recursive: true });
61765
+ if (!fs45.existsSync(targetDir)) {
61766
+ fs45.mkdirSync(targetDir, { recursive: true });
61159
61767
  }
61160
61768
  if (!content) {
61161
61769
  return "Error: content is required";
@@ -61173,16 +61781,16 @@ var extract_code_blocks = createSwarmTool({
61173
61781
  if (prefix) {
61174
61782
  filename = `${prefix}_${filename}`;
61175
61783
  }
61176
- let filepath = path55.join(targetDir, filename);
61177
- const base = path55.basename(filepath, path55.extname(filepath));
61178
- const ext = path55.extname(filepath);
61784
+ let filepath = path57.join(targetDir, filename);
61785
+ const base = path57.basename(filepath, path57.extname(filepath));
61786
+ const ext = path57.extname(filepath);
61179
61787
  let counter = 1;
61180
- while (fs43.existsSync(filepath)) {
61181
- filepath = path55.join(targetDir, `${base}_${counter}${ext}`);
61788
+ while (fs45.existsSync(filepath)) {
61789
+ filepath = path57.join(targetDir, `${base}_${counter}${ext}`);
61182
61790
  counter++;
61183
61791
  }
61184
61792
  try {
61185
- fs43.writeFileSync(filepath, code.trim(), "utf-8");
61793
+ fs45.writeFileSync(filepath, code.trim(), "utf-8");
61186
61794
  savedFiles.push(filepath);
61187
61795
  } catch (error93) {
61188
61796
  errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
@@ -61212,7 +61820,7 @@ init_create_tool();
61212
61820
  var GITINGEST_TIMEOUT_MS = 1e4;
61213
61821
  var GITINGEST_MAX_RESPONSE_BYTES = 5242880;
61214
61822
  var GITINGEST_MAX_RETRIES = 2;
61215
- var delay = (ms) => new Promise((resolve18) => setTimeout(resolve18, ms));
61823
+ var delay = (ms) => new Promise((resolve20) => setTimeout(resolve20, ms));
61216
61824
  async function fetchGitingest(args2) {
61217
61825
  for (let attempt = 0;attempt <= GITINGEST_MAX_RETRIES; attempt++) {
61218
61826
  try {
@@ -61298,11 +61906,11 @@ var gitingest = createSwarmTool({
61298
61906
  // src/tools/imports.ts
61299
61907
  init_dist();
61300
61908
  init_create_tool();
61301
- import * as fs44 from "fs";
61302
- import * as path56 from "path";
61909
+ import * as fs46 from "fs";
61910
+ import * as path58 from "path";
61303
61911
  var MAX_FILE_PATH_LENGTH2 = 500;
61304
61912
  var MAX_SYMBOL_LENGTH = 256;
61305
- var MAX_FILE_SIZE_BYTES5 = 1024 * 1024;
61913
+ var MAX_FILE_SIZE_BYTES6 = 1024 * 1024;
61306
61914
  var MAX_CONSUMERS = 100;
61307
61915
  var SUPPORTED_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
61308
61916
  var BINARY_SIGNATURES2 = [
@@ -61347,7 +61955,7 @@ function validateSymbolInput(symbol3) {
61347
61955
  return null;
61348
61956
  }
61349
61957
  function isBinaryFile2(filePath, buffer) {
61350
- const ext = path56.extname(filePath).toLowerCase();
61958
+ const ext = path58.extname(filePath).toLowerCase();
61351
61959
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
61352
61960
  return false;
61353
61961
  }
@@ -61371,15 +61979,15 @@ function parseImports(content, targetFile, targetSymbol) {
61371
61979
  const imports = [];
61372
61980
  let _resolvedTarget;
61373
61981
  try {
61374
- _resolvedTarget = path56.resolve(targetFile);
61982
+ _resolvedTarget = path58.resolve(targetFile);
61375
61983
  } catch {
61376
61984
  _resolvedTarget = targetFile;
61377
61985
  }
61378
- const targetBasename = path56.basename(targetFile, path56.extname(targetFile));
61986
+ const targetBasename = path58.basename(targetFile, path58.extname(targetFile));
61379
61987
  const targetWithExt = targetFile;
61380
61988
  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, "/");
61989
+ const normalizedTargetWithExt = path58.normalize(targetWithExt).replace(/\\/g, "/");
61990
+ const normalizedTargetWithoutExt = path58.normalize(targetWithoutExt).replace(/\\/g, "/");
61383
61991
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
61384
61992
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
61385
61993
  const modulePath = match[1] || match[2] || match[3];
@@ -61402,9 +62010,9 @@ function parseImports(content, targetFile, targetSymbol) {
61402
62010
  }
61403
62011
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
61404
62012
  let isMatch = false;
61405
- const _targetDir = path56.dirname(targetFile);
61406
- const targetExt = path56.extname(targetFile);
61407
- const targetBasenameNoExt = path56.basename(targetFile, targetExt);
62013
+ const _targetDir = path58.dirname(targetFile);
62014
+ const targetExt = path58.extname(targetFile);
62015
+ const targetBasenameNoExt = path58.basename(targetFile, targetExt);
61408
62016
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
61409
62017
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
61410
62018
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -61461,7 +62069,7 @@ var SKIP_DIRECTORIES3 = new Set([
61461
62069
  function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
61462
62070
  let entries;
61463
62071
  try {
61464
- entries = fs44.readdirSync(dir);
62072
+ entries = fs46.readdirSync(dir);
61465
62073
  } catch (e) {
61466
62074
  stats.fileErrors.push({
61467
62075
  path: dir,
@@ -61472,13 +62080,13 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
61472
62080
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
61473
62081
  for (const entry of entries) {
61474
62082
  if (SKIP_DIRECTORIES3.has(entry)) {
61475
- stats.skippedDirs.push(path56.join(dir, entry));
62083
+ stats.skippedDirs.push(path58.join(dir, entry));
61476
62084
  continue;
61477
62085
  }
61478
- const fullPath = path56.join(dir, entry);
62086
+ const fullPath = path58.join(dir, entry);
61479
62087
  let stat2;
61480
62088
  try {
61481
- stat2 = fs44.statSync(fullPath);
62089
+ stat2 = fs46.statSync(fullPath);
61482
62090
  } catch (e) {
61483
62091
  stats.fileErrors.push({
61484
62092
  path: fullPath,
@@ -61489,7 +62097,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
61489
62097
  if (stat2.isDirectory()) {
61490
62098
  findSourceFiles(fullPath, files, stats);
61491
62099
  } else if (stat2.isFile()) {
61492
- const ext = path56.extname(fullPath).toLowerCase();
62100
+ const ext = path58.extname(fullPath).toLowerCase();
61493
62101
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
61494
62102
  files.push(fullPath);
61495
62103
  }
@@ -61546,8 +62154,8 @@ var imports = createSwarmTool({
61546
62154
  return JSON.stringify(errorResult, null, 2);
61547
62155
  }
61548
62156
  try {
61549
- const targetFile = path56.resolve(file3);
61550
- if (!fs44.existsSync(targetFile)) {
62157
+ const targetFile = path58.resolve(file3);
62158
+ if (!fs46.existsSync(targetFile)) {
61551
62159
  const errorResult = {
61552
62160
  error: `target file not found: ${file3}`,
61553
62161
  target: file3,
@@ -61557,7 +62165,7 @@ var imports = createSwarmTool({
61557
62165
  };
61558
62166
  return JSON.stringify(errorResult, null, 2);
61559
62167
  }
61560
- const targetStat = fs44.statSync(targetFile);
62168
+ const targetStat = fs46.statSync(targetFile);
61561
62169
  if (!targetStat.isFile()) {
61562
62170
  const errorResult = {
61563
62171
  error: "target must be a file, not a directory",
@@ -61568,7 +62176,7 @@ var imports = createSwarmTool({
61568
62176
  };
61569
62177
  return JSON.stringify(errorResult, null, 2);
61570
62178
  }
61571
- const baseDir = path56.dirname(targetFile);
62179
+ const baseDir = path58.dirname(targetFile);
61572
62180
  const scanStats = {
61573
62181
  skippedDirs: [],
61574
62182
  skippedFiles: 0,
@@ -61583,12 +62191,12 @@ var imports = createSwarmTool({
61583
62191
  if (consumers.length >= MAX_CONSUMERS)
61584
62192
  break;
61585
62193
  try {
61586
- const stat2 = fs44.statSync(filePath);
61587
- if (stat2.size > MAX_FILE_SIZE_BYTES5) {
62194
+ const stat2 = fs46.statSync(filePath);
62195
+ if (stat2.size > MAX_FILE_SIZE_BYTES6) {
61588
62196
  skippedFileCount++;
61589
62197
  continue;
61590
62198
  }
61591
- const buffer = fs44.readFileSync(filePath);
62199
+ const buffer = fs46.readFileSync(filePath);
61592
62200
  if (isBinaryFile2(filePath, buffer)) {
61593
62201
  skippedFileCount++;
61594
62202
  continue;
@@ -61655,7 +62263,7 @@ var imports = createSwarmTool({
61655
62263
  init_dist();
61656
62264
  init_config();
61657
62265
  init_knowledge_store();
61658
- import { randomUUID as randomUUID5 } from "crypto";
62266
+ import { randomUUID as randomUUID6 } from "crypto";
61659
62267
  init_manager2();
61660
62268
  init_create_tool();
61661
62269
  var VALID_CATEGORIES2 = [
@@ -61730,7 +62338,7 @@ var knowledgeAdd = createSwarmTool({
61730
62338
  project_name = plan?.title ?? "";
61731
62339
  } catch {}
61732
62340
  const entry = {
61733
- id: randomUUID5(),
62341
+ id: randomUUID6(),
61734
62342
  tier: "swarm",
61735
62343
  lesson,
61736
62344
  category,
@@ -61788,7 +62396,7 @@ init_dist();
61788
62396
  init_config();
61789
62397
  init_knowledge_store();
61790
62398
  init_create_tool();
61791
- import { existsSync as existsSync35 } from "fs";
62399
+ import { existsSync as existsSync36 } from "fs";
61792
62400
  var DEFAULT_LIMIT = 10;
61793
62401
  var MAX_LESSON_LENGTH = 200;
61794
62402
  var VALID_CATEGORIES3 = [
@@ -61857,14 +62465,14 @@ function validateLimit(limit) {
61857
62465
  }
61858
62466
  async function readSwarmKnowledge(directory) {
61859
62467
  const swarmPath = resolveSwarmKnowledgePath(directory);
61860
- if (!existsSync35(swarmPath)) {
62468
+ if (!existsSync36(swarmPath)) {
61861
62469
  return [];
61862
62470
  }
61863
62471
  return readKnowledge(swarmPath);
61864
62472
  }
61865
62473
  async function readHiveKnowledge() {
61866
62474
  const hivePath = resolveHiveKnowledgePath();
61867
- if (!existsSync35(hivePath)) {
62475
+ if (!existsSync36(hivePath)) {
61868
62476
  return [];
61869
62477
  }
61870
62478
  return readKnowledge(hivePath);
@@ -62177,8 +62785,8 @@ init_dist();
62177
62785
  init_config();
62178
62786
  init_schema();
62179
62787
  init_manager();
62180
- import * as fs45 from "fs";
62181
- import * as path57 from "path";
62788
+ import * as fs47 from "fs";
62789
+ import * as path59 from "path";
62182
62790
  init_review_receipt();
62183
62791
  init_utils2();
62184
62792
  init_ledger();
@@ -62402,11 +63010,11 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62402
63010
  safeWarn(`[phase_complete] Completion verify error (non-blocking):`, completionError);
62403
63011
  }
62404
63012
  try {
62405
- const driftEvidencePath = path57.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
63013
+ const driftEvidencePath = path59.join(dir, ".swarm", "evidence", String(phase), "drift-verifier.json");
62406
63014
  let driftVerdictFound = false;
62407
63015
  let driftVerdictApproved = false;
62408
63016
  try {
62409
- const driftEvidenceContent = fs45.readFileSync(driftEvidencePath, "utf-8");
63017
+ const driftEvidenceContent = fs47.readFileSync(driftEvidencePath, "utf-8");
62410
63018
  const driftEvidence = JSON.parse(driftEvidenceContent);
62411
63019
  const entries = driftEvidence.entries ?? [];
62412
63020
  for (const entry of entries) {
@@ -62436,14 +63044,14 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62436
63044
  driftVerdictFound = false;
62437
63045
  }
62438
63046
  if (!driftVerdictFound) {
62439
- const specPath = path57.join(dir, ".swarm", "spec.md");
62440
- const specExists = fs45.existsSync(specPath);
63047
+ const specPath = path59.join(dir, ".swarm", "spec.md");
63048
+ const specExists = fs47.existsSync(specPath);
62441
63049
  if (!specExists) {
62442
63050
  let incompleteTaskCount = 0;
62443
63051
  let planPhaseFound = false;
62444
63052
  try {
62445
63053
  const planPath = validateSwarmPath(dir, "plan.json");
62446
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63054
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62447
63055
  const plan = JSON.parse(planRaw);
62448
63056
  const targetPhase = plan.phases.find((p) => p.id === phase);
62449
63057
  if (targetPhase) {
@@ -62488,7 +63096,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62488
63096
  const knowledgeConfig = KnowledgeConfigSchema.parse(config3.knowledge ?? {});
62489
63097
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
62490
63098
  try {
62491
- const projectName = path57.basename(dir);
63099
+ const projectName = path59.basename(dir);
62492
63100
  const curationResult = await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
62493
63101
  if (curationResult) {
62494
63102
  const sessionState = swarmState.agentSessions.get(sessionID);
@@ -62568,7 +63176,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62568
63176
  let phaseRequiredAgents;
62569
63177
  try {
62570
63178
  const planPath = validateSwarmPath(dir, "plan.json");
62571
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63179
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62572
63180
  const plan = JSON.parse(planRaw);
62573
63181
  const phaseObj = plan.phases.find((p) => p.id === phase);
62574
63182
  phaseRequiredAgents = phaseObj?.required_agents;
@@ -62583,7 +63191,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62583
63191
  if (agentsMissing.length > 0) {
62584
63192
  try {
62585
63193
  const planPath = validateSwarmPath(dir, "plan.json");
62586
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63194
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62587
63195
  const plan = JSON.parse(planRaw);
62588
63196
  const targetPhase = plan.phases.find((p) => p.id === phase);
62589
63197
  if (targetPhase && targetPhase.tasks.length > 0 && targetPhase.tasks.every((t) => t.status === "completed")) {
@@ -62623,7 +63231,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62623
63231
  if (phaseCompleteConfig.regression_sweep?.enforce) {
62624
63232
  try {
62625
63233
  const planPath = validateSwarmPath(dir, "plan.json");
62626
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63234
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62627
63235
  const plan = JSON.parse(planRaw);
62628
63236
  const targetPhase = plan.phases.find((p) => p.id === phase);
62629
63237
  if (targetPhase) {
@@ -62661,7 +63269,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62661
63269
  };
62662
63270
  try {
62663
63271
  const eventsPath = validateSwarmPath(dir, "events.jsonl");
62664
- fs45.appendFileSync(eventsPath, `${JSON.stringify(event)}
63272
+ fs47.appendFileSync(eventsPath, `${JSON.stringify(event)}
62665
63273
  `, "utf-8");
62666
63274
  } catch (writeError) {
62667
63275
  warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
@@ -62703,12 +63311,12 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62703
63311
  warnings.push(`Warning: failed to update plan.json phase status`);
62704
63312
  try {
62705
63313
  const planPath = validateSwarmPath(dir, "plan.json");
62706
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63314
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62707
63315
  const plan2 = JSON.parse(planRaw);
62708
63316
  const phaseObj = plan2.phases.find((p) => p.id === phase);
62709
63317
  if (phaseObj) {
62710
63318
  phaseObj.status = "complete";
62711
- fs45.writeFileSync(planPath, JSON.stringify(plan2, null, 2), "utf-8");
63319
+ fs47.writeFileSync(planPath, JSON.stringify(plan2, null, 2), "utf-8");
62712
63320
  }
62713
63321
  } catch {}
62714
63322
  } else if (plan) {
@@ -62745,12 +63353,12 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
62745
63353
  warnings.push(`Warning: failed to update plan.json phase status`);
62746
63354
  try {
62747
63355
  const planPath = validateSwarmPath(dir, "plan.json");
62748
- const planRaw = fs45.readFileSync(planPath, "utf-8");
63356
+ const planRaw = fs47.readFileSync(planPath, "utf-8");
62749
63357
  const plan = JSON.parse(planRaw);
62750
63358
  const phaseObj = plan.phases.find((p) => p.id === phase);
62751
63359
  if (phaseObj) {
62752
63360
  phaseObj.status = "complete";
62753
- fs45.writeFileSync(planPath, JSON.stringify(plan, null, 2), "utf-8");
63361
+ fs47.writeFileSync(planPath, JSON.stringify(plan, null, 2), "utf-8");
62754
63362
  }
62755
63363
  } catch {}
62756
63364
  }
@@ -62807,8 +63415,8 @@ init_dist();
62807
63415
  init_discovery();
62808
63416
  init_utils();
62809
63417
  init_create_tool();
62810
- import * as fs46 from "fs";
62811
- import * as path58 from "path";
63418
+ import * as fs48 from "fs";
63419
+ import * as path60 from "path";
62812
63420
  var MAX_OUTPUT_BYTES5 = 52428800;
62813
63421
  var AUDIT_TIMEOUT_MS = 120000;
62814
63422
  function isValidEcosystem(value) {
@@ -62826,28 +63434,28 @@ function validateArgs3(args2) {
62826
63434
  function detectEcosystems(directory) {
62827
63435
  const ecosystems = [];
62828
63436
  const cwd = directory;
62829
- if (fs46.existsSync(path58.join(cwd, "package.json"))) {
63437
+ if (fs48.existsSync(path60.join(cwd, "package.json"))) {
62830
63438
  ecosystems.push("npm");
62831
63439
  }
62832
- if (fs46.existsSync(path58.join(cwd, "pyproject.toml")) || fs46.existsSync(path58.join(cwd, "requirements.txt"))) {
63440
+ if (fs48.existsSync(path60.join(cwd, "pyproject.toml")) || fs48.existsSync(path60.join(cwd, "requirements.txt"))) {
62833
63441
  ecosystems.push("pip");
62834
63442
  }
62835
- if (fs46.existsSync(path58.join(cwd, "Cargo.toml"))) {
63443
+ if (fs48.existsSync(path60.join(cwd, "Cargo.toml"))) {
62836
63444
  ecosystems.push("cargo");
62837
63445
  }
62838
- if (fs46.existsSync(path58.join(cwd, "go.mod"))) {
63446
+ if (fs48.existsSync(path60.join(cwd, "go.mod"))) {
62839
63447
  ecosystems.push("go");
62840
63448
  }
62841
63449
  try {
62842
- const files = fs46.readdirSync(cwd);
63450
+ const files = fs48.readdirSync(cwd);
62843
63451
  if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
62844
63452
  ecosystems.push("dotnet");
62845
63453
  }
62846
63454
  } catch {}
62847
- if (fs46.existsSync(path58.join(cwd, "Gemfile")) || fs46.existsSync(path58.join(cwd, "Gemfile.lock"))) {
63455
+ if (fs48.existsSync(path60.join(cwd, "Gemfile")) || fs48.existsSync(path60.join(cwd, "Gemfile.lock"))) {
62848
63456
  ecosystems.push("ruby");
62849
63457
  }
62850
- if (fs46.existsSync(path58.join(cwd, "pubspec.yaml"))) {
63458
+ if (fs48.existsSync(path60.join(cwd, "pubspec.yaml"))) {
62851
63459
  ecosystems.push("dart");
62852
63460
  }
62853
63461
  return ecosystems;
@@ -62860,7 +63468,7 @@ async function runNpmAudit(directory) {
62860
63468
  stderr: "pipe",
62861
63469
  cwd: directory
62862
63470
  });
62863
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
63471
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
62864
63472
  const result = await Promise.race([
62865
63473
  Promise.all([
62866
63474
  new Response(proc.stdout).text(),
@@ -62983,7 +63591,7 @@ async function runPipAudit(directory) {
62983
63591
  stderr: "pipe",
62984
63592
  cwd: directory
62985
63593
  });
62986
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
63594
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
62987
63595
  const result = await Promise.race([
62988
63596
  Promise.all([
62989
63597
  new Response(proc.stdout).text(),
@@ -63114,7 +63722,7 @@ async function runCargoAudit(directory) {
63114
63722
  stderr: "pipe",
63115
63723
  cwd: directory
63116
63724
  });
63117
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
63725
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
63118
63726
  const result = await Promise.race([
63119
63727
  Promise.all([
63120
63728
  new Response(proc.stdout).text(),
@@ -63241,7 +63849,7 @@ async function runGoAudit(directory) {
63241
63849
  stderr: "pipe",
63242
63850
  cwd: directory
63243
63851
  });
63244
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
63852
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
63245
63853
  const result = await Promise.race([
63246
63854
  Promise.all([
63247
63855
  new Response(proc.stdout).text(),
@@ -63377,7 +63985,7 @@ async function runDotnetAudit(directory) {
63377
63985
  stderr: "pipe",
63378
63986
  cwd: directory
63379
63987
  });
63380
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
63988
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
63381
63989
  const result = await Promise.race([
63382
63990
  Promise.all([
63383
63991
  new Response(proc.stdout).text(),
@@ -63496,7 +64104,7 @@ async function runBundleAudit(directory) {
63496
64104
  stderr: "pipe",
63497
64105
  cwd: directory
63498
64106
  });
63499
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
64107
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
63500
64108
  const result = await Promise.race([
63501
64109
  Promise.all([
63502
64110
  new Response(proc.stdout).text(),
@@ -63644,7 +64252,7 @@ async function runDartAudit(directory) {
63644
64252
  stderr: "pipe",
63645
64253
  cwd: directory
63646
64254
  });
63647
- const timeoutPromise = new Promise((resolve19) => setTimeout(() => resolve19("timeout"), AUDIT_TIMEOUT_MS));
64255
+ const timeoutPromise = new Promise((resolve21) => setTimeout(() => resolve21("timeout"), AUDIT_TIMEOUT_MS));
63648
64256
  const result = await Promise.race([
63649
64257
  Promise.all([
63650
64258
  new Response(proc.stdout).text(),
@@ -63869,8 +64477,8 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
63869
64477
  ]);
63870
64478
  // src/tools/pre-check-batch.ts
63871
64479
  init_dist();
63872
- import * as fs48 from "fs";
63873
- import * as path60 from "path";
64480
+ import * as fs50 from "fs";
64481
+ import * as path62 from "path";
63874
64482
 
63875
64483
  // node_modules/yocto-queue/index.js
63876
64484
  class Node2 {
@@ -63961,26 +64569,26 @@ function pLimit(concurrency) {
63961
64569
  activeCount--;
63962
64570
  resumeNext();
63963
64571
  };
63964
- const run2 = async (function_, resolve19, arguments_2) => {
64572
+ const run2 = async (function_, resolve21, arguments_2) => {
63965
64573
  const result = (async () => function_(...arguments_2))();
63966
- resolve19(result);
64574
+ resolve21(result);
63967
64575
  try {
63968
64576
  await result;
63969
64577
  } catch {}
63970
64578
  next();
63971
64579
  };
63972
- const enqueue = (function_, resolve19, reject, arguments_2) => {
64580
+ const enqueue = (function_, resolve21, reject, arguments_2) => {
63973
64581
  const queueItem = { reject };
63974
64582
  new Promise((internalResolve) => {
63975
64583
  queueItem.run = internalResolve;
63976
64584
  queue.enqueue(queueItem);
63977
- }).then(run2.bind(undefined, function_, resolve19, arguments_2));
64585
+ }).then(run2.bind(undefined, function_, resolve21, arguments_2));
63978
64586
  if (activeCount < concurrency) {
63979
64587
  resumeNext();
63980
64588
  }
63981
64589
  };
63982
- const generator = (function_, ...arguments_2) => new Promise((resolve19, reject) => {
63983
- enqueue(function_, resolve19, reject, arguments_2);
64590
+ const generator = (function_, ...arguments_2) => new Promise((resolve21, reject) => {
64591
+ enqueue(function_, resolve21, reject, arguments_2);
63984
64592
  });
63985
64593
  Object.defineProperties(generator, {
63986
64594
  activeCount: {
@@ -64144,9 +64752,9 @@ async function qualityBudget(input, directory) {
64144
64752
  init_dist();
64145
64753
  init_manager();
64146
64754
  init_detector();
64147
- import * as fs47 from "fs";
64148
- import * as path59 from "path";
64149
- import { extname as extname10 } from "path";
64755
+ import * as fs49 from "fs";
64756
+ import * as path61 from "path";
64757
+ import { extname as extname12 } from "path";
64150
64758
 
64151
64759
  // src/sast/rules/c.ts
64152
64760
  var cRules = [
@@ -64896,7 +65504,7 @@ function mapSemgrepSeverity(severity) {
64896
65504
  }
64897
65505
  }
64898
65506
  async function executeWithTimeout(command, args2, options) {
64899
- return new Promise((resolve19) => {
65507
+ return new Promise((resolve21) => {
64900
65508
  const child = child_process6.spawn(command, args2, {
64901
65509
  shell: false,
64902
65510
  cwd: options.cwd
@@ -64905,7 +65513,7 @@ async function executeWithTimeout(command, args2, options) {
64905
65513
  let stderr = "";
64906
65514
  const timeout = setTimeout(() => {
64907
65515
  child.kill("SIGTERM");
64908
- resolve19({
65516
+ resolve21({
64909
65517
  stdout,
64910
65518
  stderr: "Process timed out",
64911
65519
  exitCode: 124
@@ -64919,7 +65527,7 @@ async function executeWithTimeout(command, args2, options) {
64919
65527
  });
64920
65528
  child.on("close", (code) => {
64921
65529
  clearTimeout(timeout);
64922
- resolve19({
65530
+ resolve21({
64923
65531
  stdout,
64924
65532
  stderr,
64925
65533
  exitCode: code ?? 0
@@ -64927,7 +65535,7 @@ async function executeWithTimeout(command, args2, options) {
64927
65535
  });
64928
65536
  child.on("error", (err2) => {
64929
65537
  clearTimeout(timeout);
64930
- resolve19({
65538
+ resolve21({
64931
65539
  stdout,
64932
65540
  stderr: err2.message,
64933
65541
  exitCode: 1
@@ -65004,7 +65612,7 @@ async function runSemgrep(options) {
65004
65612
  // src/tools/sast-scan.ts
65005
65613
  init_utils();
65006
65614
  init_create_tool();
65007
- var MAX_FILE_SIZE_BYTES6 = 512 * 1024;
65615
+ var MAX_FILE_SIZE_BYTES7 = 512 * 1024;
65008
65616
  var MAX_FILES_SCANNED2 = 1000;
65009
65617
  var MAX_FINDINGS2 = 100;
65010
65618
  var SEVERITY_ORDER = {
@@ -65015,17 +65623,17 @@ var SEVERITY_ORDER = {
65015
65623
  };
65016
65624
  function shouldSkipFile(filePath) {
65017
65625
  try {
65018
- const stats = fs47.statSync(filePath);
65019
- if (stats.size > MAX_FILE_SIZE_BYTES6) {
65626
+ const stats = fs49.statSync(filePath);
65627
+ if (stats.size > MAX_FILE_SIZE_BYTES7) {
65020
65628
  return { skip: true, reason: "file too large" };
65021
65629
  }
65022
65630
  if (stats.size === 0) {
65023
65631
  return { skip: true, reason: "empty file" };
65024
65632
  }
65025
- const fd = fs47.openSync(filePath, "r");
65633
+ const fd = fs49.openSync(filePath, "r");
65026
65634
  const buffer = Buffer.alloc(8192);
65027
- const bytesRead = fs47.readSync(fd, buffer, 0, 8192, 0);
65028
- fs47.closeSync(fd);
65635
+ const bytesRead = fs49.readSync(fd, buffer, 0, 8192, 0);
65636
+ fs49.closeSync(fd);
65029
65637
  if (bytesRead > 0) {
65030
65638
  let nullCount = 0;
65031
65639
  for (let i2 = 0;i2 < bytesRead; i2++) {
@@ -65064,7 +65672,7 @@ function countBySeverity(findings) {
65064
65672
  }
65065
65673
  function scanFileWithTierA(filePath, language) {
65066
65674
  try {
65067
- const content = fs47.readFileSync(filePath, "utf-8");
65675
+ const content = fs49.readFileSync(filePath, "utf-8");
65068
65676
  const findings = executeRulesSync(filePath, content, language);
65069
65677
  return findings.map((f) => ({
65070
65678
  rule_id: f.rule_id,
@@ -65111,13 +65719,13 @@ async function sastScan(input, directory, config3) {
65111
65719
  _filesSkipped++;
65112
65720
  continue;
65113
65721
  }
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) {
65722
+ const resolvedPath = path61.isAbsolute(filePath) ? filePath : path61.resolve(directory, filePath);
65723
+ const resolvedDirectory = path61.resolve(directory);
65724
+ if (!resolvedPath.startsWith(resolvedDirectory + path61.sep) && resolvedPath !== resolvedDirectory) {
65117
65725
  _filesSkipped++;
65118
65726
  continue;
65119
65727
  }
65120
- if (!fs47.existsSync(resolvedPath)) {
65728
+ if (!fs49.existsSync(resolvedPath)) {
65121
65729
  _filesSkipped++;
65122
65730
  continue;
65123
65731
  }
@@ -65126,7 +65734,7 @@ async function sastScan(input, directory, config3) {
65126
65734
  _filesSkipped++;
65127
65735
  continue;
65128
65736
  }
65129
- const ext = extname10(resolvedPath).toLowerCase();
65737
+ const ext = extname12(resolvedPath).toLowerCase();
65130
65738
  const profile = getProfileForFile(resolvedPath);
65131
65739
  const langDef = getLanguageForExtension(ext);
65132
65740
  if (!profile && !langDef) {
@@ -65315,20 +65923,20 @@ function validatePath(inputPath, baseDir, workspaceDir) {
65315
65923
  let resolved;
65316
65924
  const isWinAbs = isWindowsAbsolutePath(inputPath);
65317
65925
  if (isWinAbs) {
65318
- resolved = path60.win32.resolve(inputPath);
65319
- } else if (path60.isAbsolute(inputPath)) {
65320
- resolved = path60.resolve(inputPath);
65926
+ resolved = path62.win32.resolve(inputPath);
65927
+ } else if (path62.isAbsolute(inputPath)) {
65928
+ resolved = path62.resolve(inputPath);
65321
65929
  } else {
65322
- resolved = path60.resolve(baseDir, inputPath);
65930
+ resolved = path62.resolve(baseDir, inputPath);
65323
65931
  }
65324
- const workspaceResolved = path60.resolve(workspaceDir);
65325
- let relative9;
65932
+ const workspaceResolved = path62.resolve(workspaceDir);
65933
+ let relative11;
65326
65934
  if (isWinAbs) {
65327
- relative9 = path60.win32.relative(workspaceResolved, resolved);
65935
+ relative11 = path62.win32.relative(workspaceResolved, resolved);
65328
65936
  } else {
65329
- relative9 = path60.relative(workspaceResolved, resolved);
65937
+ relative11 = path62.relative(workspaceResolved, resolved);
65330
65938
  }
65331
- if (relative9.startsWith("..")) {
65939
+ if (relative11.startsWith("..")) {
65332
65940
  return "path traversal detected";
65333
65941
  }
65334
65942
  return null;
@@ -65391,7 +65999,7 @@ async function runLintOnFiles(linter, files, workspaceDir) {
65391
65999
  if (typeof file3 !== "string") {
65392
66000
  continue;
65393
66001
  }
65394
- const resolvedPath = path60.resolve(file3);
66002
+ const resolvedPath = path62.resolve(file3);
65395
66003
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
65396
66004
  if (validationError) {
65397
66005
  continue;
@@ -65484,7 +66092,7 @@ async function runSecretscanWrapped(files, directory, _config) {
65484
66092
  }
65485
66093
  }
65486
66094
  async function runSecretscanWithFiles(files, directory) {
65487
- const MAX_FILE_SIZE_BYTES7 = 512 * 1024;
66095
+ const MAX_FILE_SIZE_BYTES8 = 512 * 1024;
65488
66096
  const MAX_FINDINGS3 = 100;
65489
66097
  const DEFAULT_EXCLUDE_EXTENSIONS2 = new Set([
65490
66098
  ".png",
@@ -65548,7 +66156,7 @@ async function runSecretscanWithFiles(files, directory) {
65548
66156
  skippedFiles++;
65549
66157
  continue;
65550
66158
  }
65551
- const resolvedPath = path60.resolve(file3);
66159
+ const resolvedPath = path62.resolve(file3);
65552
66160
  const validationError = validatePath(resolvedPath, directory, directory);
65553
66161
  if (validationError) {
65554
66162
  skippedFiles++;
@@ -65566,25 +66174,25 @@ async function runSecretscanWithFiles(files, directory) {
65566
66174
  };
65567
66175
  }
65568
66176
  for (const file3 of validatedFiles) {
65569
- const ext = path60.extname(file3).toLowerCase();
66177
+ const ext = path62.extname(file3).toLowerCase();
65570
66178
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
65571
66179
  skippedFiles++;
65572
66180
  continue;
65573
66181
  }
65574
66182
  let stat2;
65575
66183
  try {
65576
- stat2 = fs48.statSync(file3);
66184
+ stat2 = fs50.statSync(file3);
65577
66185
  } catch {
65578
66186
  skippedFiles++;
65579
66187
  continue;
65580
66188
  }
65581
- if (stat2.size > MAX_FILE_SIZE_BYTES7) {
66189
+ if (stat2.size > MAX_FILE_SIZE_BYTES8) {
65582
66190
  skippedFiles++;
65583
66191
  continue;
65584
66192
  }
65585
66193
  let content;
65586
66194
  try {
65587
- const buffer = fs48.readFileSync(file3);
66195
+ const buffer = fs50.readFileSync(file3);
65588
66196
  if (buffer.includes(0)) {
65589
66197
  skippedFiles++;
65590
66198
  continue;
@@ -65772,7 +66380,7 @@ function classifySastFindings(findings, changedLineRanges, directory) {
65772
66380
  const preexistingFindings = [];
65773
66381
  for (const finding of findings) {
65774
66382
  const filePath = finding.location.file;
65775
- const normalised = path60.relative(directory, filePath).replace(/\\/g, "/");
66383
+ const normalised = path62.relative(directory, filePath).replace(/\\/g, "/");
65776
66384
  const changedLines = changedLineRanges.get(normalised);
65777
66385
  if (changedLines && changedLines.has(finding.location.line)) {
65778
66386
  newFindings.push(finding);
@@ -65823,7 +66431,7 @@ async function runPreCheckBatch(input, workspaceDir, contextDir) {
65823
66431
  warn(`pre_check_batch: Invalid file path: ${file3}`);
65824
66432
  continue;
65825
66433
  }
65826
- changedFiles.push(path60.resolve(directory, file3));
66434
+ changedFiles.push(path62.resolve(directory, file3));
65827
66435
  }
65828
66436
  if (changedFiles.length === 0) {
65829
66437
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -66011,7 +66619,7 @@ var pre_check_batch = createSwarmTool({
66011
66619
  };
66012
66620
  return JSON.stringify(errorResult, null, 2);
66013
66621
  }
66014
- const resolvedDirectory = path60.resolve(typedArgs.directory);
66622
+ const resolvedDirectory = path62.resolve(typedArgs.directory);
66015
66623
  const workspaceAnchor = resolvedDirectory;
66016
66624
  const dirError = validateDirectory2(resolvedDirectory, workspaceAnchor);
66017
66625
  if (dirError) {
@@ -66117,31 +66725,31 @@ ${paginatedContent}`;
66117
66725
  });
66118
66726
  // src/tools/save-plan.ts
66119
66727
  init_tool();
66120
- import * as fs50 from "fs";
66121
- import * as path62 from "path";
66728
+ import * as fs52 from "fs";
66729
+ import * as path64 from "path";
66122
66730
 
66123
66731
  // src/parallel/file-locks.ts
66124
66732
  var import_proper_lockfile3 = __toESM(require_proper_lockfile(), 1);
66125
- import * as fs49 from "fs";
66126
- import * as path61 from "path";
66733
+ import * as fs51 from "fs";
66734
+ import * as path63 from "path";
66127
66735
  var LOCKS_DIR = ".swarm/locks";
66128
66736
  var LOCK_TIMEOUT_MS = 5 * 60 * 1000;
66129
66737
  function getLockFilePath(directory, filePath) {
66130
- const normalized = path61.resolve(directory, filePath);
66131
- if (!normalized.startsWith(path61.resolve(directory))) {
66738
+ const normalized = path63.resolve(directory, filePath);
66739
+ if (!normalized.startsWith(path63.resolve(directory))) {
66132
66740
  throw new Error("Invalid file path: path traversal not allowed");
66133
66741
  }
66134
66742
  const hash3 = Buffer.from(normalized).toString("base64").replace(/[/+=]/g, "_");
66135
- return path61.join(directory, LOCKS_DIR, `${hash3}.lock`);
66743
+ return path63.join(directory, LOCKS_DIR, `${hash3}.lock`);
66136
66744
  }
66137
66745
  async function tryAcquireLock(directory, filePath, agent, taskId) {
66138
66746
  const lockPath = getLockFilePath(directory, filePath);
66139
- const locksDir = path61.dirname(lockPath);
66140
- if (!fs49.existsSync(locksDir)) {
66141
- fs49.mkdirSync(locksDir, { recursive: true });
66747
+ const locksDir = path63.dirname(lockPath);
66748
+ if (!fs51.existsSync(locksDir)) {
66749
+ fs51.mkdirSync(locksDir, { recursive: true });
66142
66750
  }
66143
- if (!fs49.existsSync(lockPath)) {
66144
- fs49.writeFileSync(lockPath, "", "utf-8");
66751
+ if (!fs51.existsSync(lockPath)) {
66752
+ fs51.writeFileSync(lockPath, "", "utf-8");
66145
66753
  }
66146
66754
  let release;
66147
66755
  try {
@@ -66289,14 +66897,14 @@ async function executeSavePlan(args2, fallbackDir) {
66289
66897
  await savePlan(dir, plan);
66290
66898
  await writeCheckpoint(dir).catch(() => {});
66291
66899
  try {
66292
- const markerPath = path62.join(dir, ".swarm", ".plan-write-marker");
66900
+ const markerPath = path64.join(dir, ".swarm", ".plan-write-marker");
66293
66901
  const marker = JSON.stringify({
66294
66902
  source: "save_plan",
66295
66903
  timestamp: new Date().toISOString(),
66296
66904
  phases_count: plan.phases.length,
66297
66905
  tasks_count: tasksCount
66298
66906
  });
66299
- await fs50.promises.writeFile(markerPath, marker, "utf8");
66907
+ await fs52.promises.writeFile(markerPath, marker, "utf8");
66300
66908
  } catch {}
66301
66909
  const warnings = [];
66302
66910
  let criticReviewFound = false;
@@ -66312,7 +66920,7 @@ async function executeSavePlan(args2, fallbackDir) {
66312
66920
  return {
66313
66921
  success: true,
66314
66922
  message: "Plan saved successfully",
66315
- plan_path: path62.join(dir, ".swarm", "plan.json"),
66923
+ plan_path: path64.join(dir, ".swarm", "plan.json"),
66316
66924
  phases_count: plan.phases.length,
66317
66925
  tasks_count: tasksCount,
66318
66926
  ...warnings.length > 0 ? { warnings } : {}
@@ -66356,8 +66964,8 @@ var save_plan = createSwarmTool({
66356
66964
  // src/tools/sbom-generate.ts
66357
66965
  init_dist();
66358
66966
  init_manager();
66359
- import * as fs51 from "fs";
66360
- import * as path63 from "path";
66967
+ import * as fs53 from "fs";
66968
+ import * as path65 from "path";
66361
66969
 
66362
66970
  // src/sbom/detectors/index.ts
66363
66971
  init_utils();
@@ -67205,9 +67813,9 @@ function findManifestFiles(rootDir) {
67205
67813
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
67206
67814
  function searchDir(dir) {
67207
67815
  try {
67208
- const entries = fs51.readdirSync(dir, { withFileTypes: true });
67816
+ const entries = fs53.readdirSync(dir, { withFileTypes: true });
67209
67817
  for (const entry of entries) {
67210
- const fullPath = path63.join(dir, entry.name);
67818
+ const fullPath = path65.join(dir, entry.name);
67211
67819
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
67212
67820
  continue;
67213
67821
  }
@@ -67216,7 +67824,7 @@ function findManifestFiles(rootDir) {
67216
67824
  } else if (entry.isFile()) {
67217
67825
  for (const pattern of patterns) {
67218
67826
  if (simpleGlobToRegex(pattern).test(entry.name)) {
67219
- manifestFiles.push(path63.relative(rootDir, fullPath));
67827
+ manifestFiles.push(path65.relative(rootDir, fullPath));
67220
67828
  break;
67221
67829
  }
67222
67830
  }
@@ -67232,13 +67840,13 @@ function findManifestFilesInDirs(directories, workingDir) {
67232
67840
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
67233
67841
  for (const dir of directories) {
67234
67842
  try {
67235
- const entries = fs51.readdirSync(dir, { withFileTypes: true });
67843
+ const entries = fs53.readdirSync(dir, { withFileTypes: true });
67236
67844
  for (const entry of entries) {
67237
- const fullPath = path63.join(dir, entry.name);
67845
+ const fullPath = path65.join(dir, entry.name);
67238
67846
  if (entry.isFile()) {
67239
67847
  for (const pattern of patterns) {
67240
67848
  if (simpleGlobToRegex(pattern).test(entry.name)) {
67241
- found.push(path63.relative(workingDir, fullPath));
67849
+ found.push(path65.relative(workingDir, fullPath));
67242
67850
  break;
67243
67851
  }
67244
67852
  }
@@ -67251,11 +67859,11 @@ function findManifestFilesInDirs(directories, workingDir) {
67251
67859
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
67252
67860
  const dirs = new Set;
67253
67861
  for (const file3 of changedFiles) {
67254
- let currentDir = path63.dirname(file3);
67862
+ let currentDir = path65.dirname(file3);
67255
67863
  while (true) {
67256
- if (currentDir && currentDir !== "." && currentDir !== path63.sep) {
67257
- dirs.add(path63.join(workingDir, currentDir));
67258
- const parent = path63.dirname(currentDir);
67864
+ if (currentDir && currentDir !== "." && currentDir !== path65.sep) {
67865
+ dirs.add(path65.join(workingDir, currentDir));
67866
+ const parent = path65.dirname(currentDir);
67259
67867
  if (parent === currentDir)
67260
67868
  break;
67261
67869
  currentDir = parent;
@@ -67269,7 +67877,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
67269
67877
  }
67270
67878
  function ensureOutputDir(outputDir) {
67271
67879
  try {
67272
- fs51.mkdirSync(outputDir, { recursive: true });
67880
+ fs53.mkdirSync(outputDir, { recursive: true });
67273
67881
  } catch (error93) {
67274
67882
  if (!error93 || error93.code !== "EEXIST") {
67275
67883
  throw error93;
@@ -67339,7 +67947,7 @@ var sbom_generate = createSwarmTool({
67339
67947
  const changedFiles = obj.changed_files;
67340
67948
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
67341
67949
  const workingDir = directory;
67342
- const outputDir = path63.isAbsolute(relativeOutputDir) ? relativeOutputDir : path63.join(workingDir, relativeOutputDir);
67950
+ const outputDir = path65.isAbsolute(relativeOutputDir) ? relativeOutputDir : path65.join(workingDir, relativeOutputDir);
67343
67951
  let manifestFiles = [];
67344
67952
  if (scope === "all") {
67345
67953
  manifestFiles = findManifestFiles(workingDir);
@@ -67362,11 +67970,11 @@ var sbom_generate = createSwarmTool({
67362
67970
  const processedFiles = [];
67363
67971
  for (const manifestFile of manifestFiles) {
67364
67972
  try {
67365
- const fullPath = path63.isAbsolute(manifestFile) ? manifestFile : path63.join(workingDir, manifestFile);
67366
- if (!fs51.existsSync(fullPath)) {
67973
+ const fullPath = path65.isAbsolute(manifestFile) ? manifestFile : path65.join(workingDir, manifestFile);
67974
+ if (!fs53.existsSync(fullPath)) {
67367
67975
  continue;
67368
67976
  }
67369
- const content = fs51.readFileSync(fullPath, "utf-8");
67977
+ const content = fs53.readFileSync(fullPath, "utf-8");
67370
67978
  const components = detectComponents(manifestFile, content);
67371
67979
  processedFiles.push(manifestFile);
67372
67980
  if (components.length > 0) {
@@ -67379,8 +67987,8 @@ var sbom_generate = createSwarmTool({
67379
67987
  const bom = generateCycloneDX(allComponents);
67380
67988
  const bomJson = serializeCycloneDX(bom);
67381
67989
  const filename = generateSbomFilename();
67382
- const outputPath = path63.join(outputDir, filename);
67383
- fs51.writeFileSync(outputPath, bomJson, "utf-8");
67990
+ const outputPath = path65.join(outputDir, filename);
67991
+ fs53.writeFileSync(outputPath, bomJson, "utf-8");
67384
67992
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
67385
67993
  try {
67386
67994
  const timestamp = new Date().toISOString();
@@ -67422,8 +68030,8 @@ var sbom_generate = createSwarmTool({
67422
68030
  // src/tools/schema-drift.ts
67423
68031
  init_dist();
67424
68032
  init_create_tool();
67425
- import * as fs52 from "fs";
67426
- import * as path64 from "path";
68033
+ import * as fs54 from "fs";
68034
+ import * as path66 from "path";
67427
68035
  var SPEC_CANDIDATES = [
67428
68036
  "openapi.json",
67429
68037
  "openapi.yaml",
@@ -67455,28 +68063,28 @@ function normalizePath2(p) {
67455
68063
  }
67456
68064
  function discoverSpecFile(cwd, specFileArg) {
67457
68065
  if (specFileArg) {
67458
- const resolvedPath = path64.resolve(cwd, specFileArg);
67459
- const normalizedCwd = cwd.endsWith(path64.sep) ? cwd : cwd + path64.sep;
68066
+ const resolvedPath = path66.resolve(cwd, specFileArg);
68067
+ const normalizedCwd = cwd.endsWith(path66.sep) ? cwd : cwd + path66.sep;
67460
68068
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
67461
68069
  throw new Error("Invalid spec_file: path traversal detected");
67462
68070
  }
67463
- const ext = path64.extname(resolvedPath).toLowerCase();
68071
+ const ext = path66.extname(resolvedPath).toLowerCase();
67464
68072
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
67465
68073
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
67466
68074
  }
67467
- const stats = fs52.statSync(resolvedPath);
68075
+ const stats = fs54.statSync(resolvedPath);
67468
68076
  if (stats.size > MAX_SPEC_SIZE) {
67469
68077
  throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
67470
68078
  }
67471
- if (!fs52.existsSync(resolvedPath)) {
68079
+ if (!fs54.existsSync(resolvedPath)) {
67472
68080
  throw new Error(`Spec file not found: ${resolvedPath}`);
67473
68081
  }
67474
68082
  return resolvedPath;
67475
68083
  }
67476
68084
  for (const candidate of SPEC_CANDIDATES) {
67477
- const candidatePath = path64.resolve(cwd, candidate);
67478
- if (fs52.existsSync(candidatePath)) {
67479
- const stats = fs52.statSync(candidatePath);
68085
+ const candidatePath = path66.resolve(cwd, candidate);
68086
+ if (fs54.existsSync(candidatePath)) {
68087
+ const stats = fs54.statSync(candidatePath);
67480
68088
  if (stats.size <= MAX_SPEC_SIZE) {
67481
68089
  return candidatePath;
67482
68090
  }
@@ -67485,8 +68093,8 @@ function discoverSpecFile(cwd, specFileArg) {
67485
68093
  return null;
67486
68094
  }
67487
68095
  function parseSpec(specFile) {
67488
- const content = fs52.readFileSync(specFile, "utf-8");
67489
- const ext = path64.extname(specFile).toLowerCase();
68096
+ const content = fs54.readFileSync(specFile, "utf-8");
68097
+ const ext = path66.extname(specFile).toLowerCase();
67490
68098
  if (ext === ".json") {
67491
68099
  return parseJsonSpec(content);
67492
68100
  }
@@ -67557,12 +68165,12 @@ function extractRoutes(cwd) {
67557
68165
  function walkDir(dir) {
67558
68166
  let entries;
67559
68167
  try {
67560
- entries = fs52.readdirSync(dir, { withFileTypes: true });
68168
+ entries = fs54.readdirSync(dir, { withFileTypes: true });
67561
68169
  } catch {
67562
68170
  return;
67563
68171
  }
67564
68172
  for (const entry of entries) {
67565
- const fullPath = path64.join(dir, entry.name);
68173
+ const fullPath = path66.join(dir, entry.name);
67566
68174
  if (entry.isSymbolicLink()) {
67567
68175
  continue;
67568
68176
  }
@@ -67572,7 +68180,7 @@ function extractRoutes(cwd) {
67572
68180
  }
67573
68181
  walkDir(fullPath);
67574
68182
  } else if (entry.isFile()) {
67575
- const ext = path64.extname(entry.name).toLowerCase();
68183
+ const ext = path66.extname(entry.name).toLowerCase();
67576
68184
  const baseName = entry.name.toLowerCase();
67577
68185
  if (![".ts", ".js", ".mjs"].includes(ext)) {
67578
68186
  continue;
@@ -67590,7 +68198,7 @@ function extractRoutes(cwd) {
67590
68198
  }
67591
68199
  function extractRoutesFromFile(filePath) {
67592
68200
  const routes = [];
67593
- const content = fs52.readFileSync(filePath, "utf-8");
68201
+ const content = fs54.readFileSync(filePath, "utf-8");
67594
68202
  const lines = content.split(/\r?\n/);
67595
68203
  const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
67596
68204
  const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
@@ -67734,36 +68342,51 @@ var schema_drift = createSwarmTool({
67734
68342
  }
67735
68343
  }
67736
68344
  });
67737
-
67738
- // src/tools/index.ts
67739
- init_secretscan();
67740
-
67741
- // src/tools/symbols.ts
68345
+ // src/tools/search.ts
67742
68346
  init_tool();
67743
68347
  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;
68348
+ import * as fs55 from "fs";
68349
+ import * as path67 from "path";
68350
+ var DEFAULT_MAX_RESULTS = 100;
68351
+ var DEFAULT_MAX_LINES = 200;
68352
+ var REGEX_TIMEOUT_MS = 5000;
68353
+ var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
68354
+ var HARD_CAP_RESULTS = 1e4;
68355
+ var HARD_CAP_LINES = 1e4;
68356
+ function globMatch(pattern, filePath) {
68357
+ const normalizedPattern = pattern.replace(/\\/g, "/");
68358
+ const normalizedPath = filePath.replace(/\\/g, "/");
68359
+ const regexPattern = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{DOUBLESTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{\{DOUBLESTAR\}\}/g, ".*");
68360
+ try {
68361
+ const regex = new RegExp(`^${regexPattern}$`);
68362
+ return regex.test(normalizedPath);
68363
+ } catch {
68364
+ return false;
67751
68365
  }
68366
+ }
68367
+ function matchesGlobs(filePath, globs) {
68368
+ if (globs.length === 0)
68369
+ return true;
68370
+ return globs.some((glob) => globMatch(glob, filePath));
68371
+ }
68372
+ var WINDOWS_RESERVED_NAMES3 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
68373
+ function containsWindowsAttacks3(str) {
68374
+ if (/:[^\\/]/.test(str))
68375
+ return true;
67752
68376
  const parts2 = str.split(/[/\\]/);
67753
68377
  for (const part of parts2) {
67754
- if (WINDOWS_RESERVED_NAMES.test(part)) {
68378
+ if (WINDOWS_RESERVED_NAMES3.test(part))
67755
68379
  return true;
67756
- }
67757
68380
  }
67758
68381
  return false;
67759
68382
  }
67760
- function isPathInWorkspace(filePath, workspace) {
68383
+ function isPathInWorkspace3(filePath, workspace) {
67761
68384
  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)) {
68385
+ const resolvedPath = path67.resolve(workspace, filePath);
68386
+ const realWorkspace = fs55.realpathSync(workspace);
68387
+ const realResolvedPath = fs55.realpathSync(resolvedPath);
68388
+ const relativePath = path67.relative(realWorkspace, realResolvedPath);
68389
+ if (relativePath.startsWith("..") || path67.isAbsolute(relativePath)) {
67767
68390
  return false;
67768
68391
  }
67769
68392
  return true;
@@ -67771,301 +68394,675 @@ function isPathInWorkspace(filePath, workspace) {
67771
68394
  return false;
67772
68395
  }
67773
68396
  }
67774
- function validatePathForRead(filePath, workspace) {
67775
- return isPathInWorkspace(filePath, workspace);
68397
+ function validatePathForRead2(filePath, workspace) {
68398
+ return isPathInWorkspace3(filePath, workspace);
67776
68399
  }
67777
- function extractTSSymbols(filePath, cwd) {
67778
- const fullPath = path65.join(cwd, filePath);
67779
- if (!validatePathForRead(fullPath, cwd)) {
67780
- return [];
68400
+ function findRgInEnvPath() {
68401
+ const searchPath = process.env.PATH ?? "";
68402
+ for (const dir of searchPath.split(path67.delimiter)) {
68403
+ if (!dir)
68404
+ continue;
68405
+ const isWindows = process.platform === "win32";
68406
+ const candidate = path67.join(dir, isWindows ? "rg.exe" : "rg");
68407
+ if (fs55.existsSync(candidate))
68408
+ return candidate;
67781
68409
  }
67782
- let content;
68410
+ return null;
68411
+ }
68412
+ async function isRipgrepAvailable() {
68413
+ const rgPath = findRgInEnvPath();
68414
+ if (!rgPath)
68415
+ return false;
67783
68416
  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");
68417
+ const proc = Bun.spawn([rgPath, "--version"], {
68418
+ stdout: "pipe",
68419
+ stderr: "pipe"
68420
+ });
68421
+ const exitCode = await proc.exited;
68422
+ return exitCode === 0;
67789
68423
  } catch {
67790
- return [];
68424
+ return false;
67791
68425
  }
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)}...`;
68426
+ }
68427
+ async function ripgrepSearch(opts) {
68428
+ const rgPath = findRgInEnvPath();
68429
+ if (!rgPath) {
68430
+ return {
68431
+ error: true,
68432
+ type: "rg-not-found",
68433
+ message: "ripgrep (rg) not found in PATH"
68434
+ };
68435
+ }
68436
+ const args2 = [
68437
+ "--json",
68438
+ "-n"
68439
+ ];
68440
+ if (opts.include) {
68441
+ for (const pattern of opts.include.split(",")) {
68442
+ args2.push("--glob", pattern.trim());
67810
68443
  }
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;
68444
+ }
68445
+ if (opts.exclude) {
68446
+ for (const pattern of opts.exclude.split(",")) {
68447
+ args2.push("--glob", `!${pattern.trim()}`);
67822
68448
  }
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;
68449
+ }
68450
+ if (opts.mode !== "regex") {
68451
+ args2.push("--fixed-strings");
68452
+ }
68453
+ args2.push(opts.query);
68454
+ args2.push(opts.workspace);
68455
+ try {
68456
+ const proc = Bun.spawn([rgPath, ...args2], {
68457
+ stdout: "pipe",
68458
+ stderr: "pipe",
68459
+ cwd: opts.workspace
68460
+ });
68461
+ const timeout = new Promise((resolve26) => setTimeout(() => resolve26("timeout"), REGEX_TIMEOUT_MS));
68462
+ const exitPromise = proc.exited;
68463
+ const result = await Promise.race([exitPromise, timeout]);
68464
+ if (result === "timeout") {
68465
+ proc.kill();
68466
+ return {
68467
+ error: true,
68468
+ type: "regex-timeout",
68469
+ message: `Regex search timed out after ${REGEX_TIMEOUT_MS}ms`
68470
+ };
67836
68471
  }
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
- });
68472
+ const stdout = await new Response(proc.stdout).text();
68473
+ const stderr = await new Response(proc.stderr).text();
68474
+ if (proc.exitCode !== 0 && stderr) {
68475
+ if (stderr.includes("Invalid regex") || stderr.includes("SyntaxError")) {
68476
+ return {
68477
+ error: true,
68478
+ type: "invalid-query",
68479
+ message: `Invalid query: ${stderr.split(`
68480
+ `)[0]}`
68481
+ };
68482
+ }
68483
+ }
68484
+ const matches = [];
68485
+ let total = 0;
68486
+ for (const line of stdout.split(`
68487
+ `)) {
68488
+ if (!line.trim())
68489
+ continue;
68490
+ try {
68491
+ const entry = JSON.parse(line);
68492
+ if (entry.type === "match") {
68493
+ total++;
68494
+ if (matches.length < opts.maxResults) {
68495
+ let lineText = entry.data.lines.text.trimEnd();
68496
+ if (lineText.length > opts.maxLines) {
68497
+ lineText = `${lineText.substring(0, opts.maxLines)}...`;
68498
+ }
68499
+ const match = {
68500
+ file: entry.data.path.text || entry.data.path,
68501
+ lineNumber: entry.data.line_number,
68502
+ lineText
68503
+ };
68504
+ matches.push(match);
68505
+ }
67860
68506
  }
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
- });
68507
+ } catch {}
68508
+ }
68509
+ return {
68510
+ matches,
68511
+ truncated: total > opts.maxResults,
68512
+ total,
68513
+ query: opts.query,
68514
+ mode: opts.mode,
68515
+ maxResults: opts.maxResults
68516
+ };
68517
+ } catch (err2) {
68518
+ return {
68519
+ error: true,
68520
+ type: "unknown",
68521
+ message: err2 instanceof Error ? err2.message : String(err2)
68522
+ };
68523
+ }
68524
+ }
68525
+ function escapeRegex4(str) {
68526
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
68527
+ }
68528
+ function collectFiles(dir, workspace, includeGlobs, excludeGlobs) {
68529
+ const files = [];
68530
+ if (!validatePathForRead2(dir, workspace)) {
68531
+ return files;
68532
+ }
68533
+ try {
68534
+ const entries = fs55.readdirSync(dir, { withFileTypes: true });
68535
+ for (const entry of entries) {
68536
+ const fullPath = path67.join(dir, entry.name);
68537
+ const relativePath = path67.relative(workspace, fullPath);
68538
+ if (!validatePathForRead2(fullPath, workspace)) {
68539
+ continue;
68540
+ }
68541
+ if (entry.isDirectory()) {
68542
+ const subFiles = collectFiles(fullPath, workspace, includeGlobs, excludeGlobs);
68543
+ files.push(...subFiles);
68544
+ } else if (entry.isFile()) {
68545
+ if (includeGlobs.length > 0 && !matchesGlobs(relativePath, includeGlobs)) {
68546
+ continue;
67870
68547
  }
68548
+ if (excludeGlobs.length > 0 && matchesGlobs(relativePath, excludeGlobs)) {
68549
+ continue;
68550
+ }
68551
+ files.push(relativePath);
67871
68552
  }
67872
- continue;
67873
68553
  }
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
- });
68554
+ } catch {}
68555
+ return files;
68556
+ }
68557
+ async function fallbackSearch(opts) {
68558
+ const includeGlobs = opts.include ? opts.include.split(",").map((p) => p.trim()).filter(Boolean) : [];
68559
+ const excludeGlobs = opts.exclude ? opts.exclude.split(",").map((p) => p.trim()).filter(Boolean) : [];
68560
+ const files = collectFiles(opts.workspace, opts.workspace, includeGlobs, excludeGlobs);
68561
+ let regex;
68562
+ try {
68563
+ if (opts.mode === "regex") {
68564
+ regex = new RegExp(opts.query);
68565
+ } else {
68566
+ regex = new RegExp(escapeRegex4(opts.query));
68567
+ }
68568
+ } catch (err2) {
68569
+ return {
68570
+ error: true,
68571
+ type: "invalid-query",
68572
+ message: err2 instanceof Error ? err2.message : "Invalid regex pattern"
68573
+ };
68574
+ }
68575
+ const matches = [];
68576
+ let total = 0;
68577
+ for (const file3 of files) {
68578
+ const fullPath = path67.join(opts.workspace, file3);
68579
+ if (!validatePathForRead2(fullPath, opts.workspace)) {
67884
68580
  continue;
67885
68581
  }
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
- });
68582
+ let stats;
68583
+ try {
68584
+ stats = fs55.statSync(fullPath);
68585
+ if (stats.size > MAX_FILE_SIZE_BYTES8) {
68586
+ continue;
68587
+ }
68588
+ } catch {
67897
68589
  continue;
67898
68590
  }
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
- });
68591
+ let content;
68592
+ try {
68593
+ content = fs55.readFileSync(fullPath, "utf-8");
68594
+ } catch {
67909
68595
  continue;
67910
68596
  }
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
- });
68597
+ const lines = content.split(`
68598
+ `);
68599
+ for (let i2 = 0;i2 < lines.length; i2++) {
68600
+ const line = lines[i2];
68601
+ if (regex.test(line)) {
68602
+ total++;
68603
+ if (matches.length < opts.maxResults) {
68604
+ let lineText = line.trimEnd();
68605
+ if (lineText.length > opts.maxLines) {
68606
+ lineText = `${lineText.substring(0, opts.maxLines)}...`;
68607
+ }
68608
+ matches.push({
68609
+ file: file3,
68610
+ lineNumber: i2 + 1,
68611
+ lineText
68612
+ });
68613
+ }
68614
+ regex.lastIndex = 0;
68615
+ }
67921
68616
  }
67922
68617
  }
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
- });
68618
+ return {
68619
+ matches,
68620
+ truncated: total > opts.maxResults,
68621
+ total,
68622
+ query: opts.query,
68623
+ mode: opts.mode,
68624
+ maxResults: opts.maxResults
68625
+ };
67928
68626
  }
67929
- function extractPythonSymbols(filePath, cwd) {
67930
- const fullPath = path65.join(cwd, filePath);
67931
- if (!validatePathForRead(fullPath, cwd)) {
67932
- return [];
68627
+ var search = createSwarmTool({
68628
+ 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.",
68629
+ args: {
68630
+ query: tool.schema.string().describe("Search query string (literal or regex depending on mode)"),
68631
+ mode: tool.schema.enum(["literal", "regex"]).default("literal").describe("Search mode: literal for exact string match, regex for regular expression"),
68632
+ include: tool.schema.string().optional().describe('Glob pattern for files to include (e.g., "*.ts", "src/**/*.js")'),
68633
+ exclude: tool.schema.string().optional().describe('Glob pattern for files to exclude (e.g., "node_modules/**", "*.test.ts")'),
68634
+ max_results: tool.schema.number().default(DEFAULT_MAX_RESULTS).describe("Maximum number of matches to return"),
68635
+ max_lines: tool.schema.number().default(DEFAULT_MAX_LINES).describe("Maximum characters per line in results")
68636
+ },
68637
+ execute: async (args2, directory) => {
68638
+ let query;
68639
+ let mode = "literal";
68640
+ let include;
68641
+ let exclude;
68642
+ let maxResults = DEFAULT_MAX_RESULTS;
68643
+ let maxLines = DEFAULT_MAX_LINES;
68644
+ try {
68645
+ const obj = args2;
68646
+ query = String(obj.query ?? "");
68647
+ mode = obj.mode === "regex" ? "regex" : "literal";
68648
+ include = obj.include;
68649
+ exclude = obj.exclude;
68650
+ const rawMaxResults = typeof obj.max_results === "number" ? obj.max_results : DEFAULT_MAX_RESULTS;
68651
+ const sanitizedMaxResults = Number.isNaN(rawMaxResults) ? DEFAULT_MAX_RESULTS : rawMaxResults;
68652
+ maxResults = Math.min(Math.max(0, sanitizedMaxResults), HARD_CAP_RESULTS);
68653
+ const rawMaxLines = typeof obj.max_lines === "number" ? obj.max_lines : DEFAULT_MAX_LINES;
68654
+ const sanitizedMaxLines = Number.isNaN(rawMaxLines) ? DEFAULT_MAX_LINES : rawMaxLines;
68655
+ maxLines = Math.min(Math.max(0, sanitizedMaxLines), HARD_CAP_LINES);
68656
+ } catch {
68657
+ return JSON.stringify({
68658
+ error: true,
68659
+ type: "invalid-query",
68660
+ message: "Could not parse search arguments"
68661
+ }, null, 2);
68662
+ }
68663
+ if (!query || query.trim() === "") {
68664
+ return JSON.stringify({
68665
+ error: true,
68666
+ type: "invalid-query",
68667
+ message: "Query cannot be empty"
68668
+ }, null, 2);
68669
+ }
68670
+ if (containsControlChars(query)) {
68671
+ return JSON.stringify({
68672
+ error: true,
68673
+ type: "invalid-query",
68674
+ message: "Query contains invalid control characters"
68675
+ }, null, 2);
68676
+ }
68677
+ if (include && containsPathTraversal(include)) {
68678
+ return JSON.stringify({
68679
+ error: true,
68680
+ type: "path-escape",
68681
+ message: "Include pattern contains path traversal sequence"
68682
+ }, null, 2);
68683
+ }
68684
+ if (exclude && containsPathTraversal(exclude)) {
68685
+ return JSON.stringify({
68686
+ error: true,
68687
+ type: "path-escape",
68688
+ message: "Exclude pattern contains path traversal sequence"
68689
+ }, null, 2);
68690
+ }
68691
+ if (include && containsWindowsAttacks3(include)) {
68692
+ return JSON.stringify({
68693
+ error: true,
68694
+ type: "path-escape",
68695
+ message: "Include pattern contains invalid Windows-specific sequence"
68696
+ }, null, 2);
68697
+ }
68698
+ if (exclude && containsWindowsAttacks3(exclude)) {
68699
+ return JSON.stringify({
68700
+ error: true,
68701
+ type: "path-escape",
68702
+ message: "Exclude pattern contains invalid Windows-specific sequence"
68703
+ }, null, 2);
68704
+ }
68705
+ if (!fs55.existsSync(directory)) {
68706
+ return JSON.stringify({
68707
+ error: true,
68708
+ type: "unknown",
68709
+ message: "Workspace directory does not exist"
68710
+ }, null, 2);
68711
+ }
68712
+ const rgAvailable = await isRipgrepAvailable();
68713
+ let result;
68714
+ if (rgAvailable) {
68715
+ result = await ripgrepSearch({
68716
+ query,
68717
+ mode,
68718
+ include,
68719
+ exclude,
68720
+ maxResults,
68721
+ maxLines,
68722
+ workspace: directory
68723
+ });
68724
+ } else {
68725
+ result = await fallbackSearch({
68726
+ query,
68727
+ mode,
68728
+ include,
68729
+ exclude,
68730
+ maxResults,
68731
+ maxLines,
68732
+ workspace: directory
68733
+ });
68734
+ }
68735
+ if ("error" in result && result.error) {
68736
+ return JSON.stringify(result, null, 2);
68737
+ }
68738
+ return JSON.stringify(result, null, 2);
67933
68739
  }
67934
- let content;
68740
+ });
68741
+
68742
+ // src/tools/index.ts
68743
+ init_secretscan();
68744
+
68745
+ // src/tools/suggest-patch.ts
68746
+ init_tool();
68747
+ init_create_tool();
68748
+ import * as fs56 from "fs";
68749
+ import * as path68 from "path";
68750
+ var WINDOWS_RESERVED_NAMES4 = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
68751
+ function containsWindowsAttacks4(str) {
68752
+ if (/:[^\\/]/.test(str))
68753
+ return true;
68754
+ const parts2 = str.split(/[/\\]/);
68755
+ for (const part of parts2) {
68756
+ if (WINDOWS_RESERVED_NAMES4.test(part))
68757
+ return true;
68758
+ }
68759
+ return false;
68760
+ }
68761
+ function isPathInWorkspace4(filePath, workspace) {
67935
68762
  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})`);
68763
+ const resolvedPath = path68.resolve(workspace, filePath);
68764
+ if (!fs56.existsSync(resolvedPath)) {
68765
+ return true;
68766
+ }
68767
+ const realWorkspace = fs56.realpathSync(workspace);
68768
+ const realResolvedPath = fs56.realpathSync(resolvedPath);
68769
+ const relativePath = path68.relative(realWorkspace, realResolvedPath);
68770
+ if (relativePath.startsWith("..") || path68.isAbsolute(relativePath)) {
68771
+ return false;
67939
68772
  }
67940
- content = fs53.readFileSync(fullPath, "utf-8");
68773
+ return true;
67941
68774
  } catch {
67942
- return [];
68775
+ return false;
67943
68776
  }
68777
+ }
68778
+ function validateFilePath(filePath, workspace) {
68779
+ if (!filePath || filePath.trim() === "")
68780
+ return false;
68781
+ if (containsPathTraversal(filePath))
68782
+ return false;
68783
+ if (containsControlChars(filePath))
68784
+ return false;
68785
+ if (containsWindowsAttacks4(filePath))
68786
+ return false;
68787
+ return isPathInWorkspace4(filePath, workspace);
68788
+ }
68789
+ function findContextMatch(content, contextBefore, contextAfter, oldContent) {
67944
68790
  const lines = content.split(`
67945
68791
  `);
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
- });
68792
+ if ((!contextBefore || contextBefore.length === 0) && (!contextAfter || contextAfter.length === 0)) {
68793
+ return null;
68794
+ }
68795
+ if (contextBefore && contextBefore.length > 0) {
68796
+ for (let i2 = 0;i2 <= lines.length - contextBefore.length; i2++) {
68797
+ const slice = lines.slice(i2, i2 + contextBefore.length);
68798
+ if (arraysEqual(slice, contextBefore)) {
68799
+ const afterStart = i2 + contextBefore.length;
68800
+ if (contextAfter && contextAfter.length > 0) {
68801
+ for (let j = afterStart;j <= lines.length - contextAfter.length; j++) {
68802
+ const afterSlice = lines.slice(j, j + contextAfter.length);
68803
+ if (arraysEqual(afterSlice, contextAfter)) {
68804
+ if (j === afterStart) {
68805
+ return {
68806
+ startLineIndex: i2,
68807
+ endLineIndex: j + contextAfter.length - 1,
68808
+ matchedBefore: contextBefore,
68809
+ matchedAfter: contextAfter
68810
+ };
68811
+ } else {
68812
+ if (oldContent && oldContent.length > 0) {
68813
+ const oldContentLines = oldContent.split(`
68814
+ `);
68815
+ const betweenLines = lines.slice(afterStart, j);
68816
+ if (arraysEqual(betweenLines, oldContentLines)) {
68817
+ return {
68818
+ startLineIndex: i2,
68819
+ endLineIndex: j - 1,
68820
+ matchedBefore: contextBefore,
68821
+ matchedAfter: contextAfter
68822
+ };
68823
+ }
68824
+ } else {
68825
+ return {
68826
+ startLineIndex: i2,
68827
+ endLineIndex: j - 1,
68828
+ matchedBefore: contextBefore,
68829
+ matchedAfter: contextAfter
68830
+ };
68831
+ }
68832
+ }
68833
+ }
68834
+ }
68835
+ return null;
68836
+ } else {
68837
+ if (oldContent && oldContent.length > 0) {
68838
+ const oldContentLines = oldContent.split(`
68839
+ `);
68840
+ for (let k = afterStart;k <= lines.length - oldContentLines.length; k++) {
68841
+ const candidate = lines.slice(k, k + oldContentLines.length);
68842
+ if (arraysEqual(candidate, oldContentLines)) {
68843
+ return {
68844
+ startLineIndex: i2,
68845
+ endLineIndex: k + oldContentLines.length - 1,
68846
+ matchedBefore: contextBefore,
68847
+ matchedAfter: []
68848
+ };
68849
+ }
68850
+ }
68851
+ return null;
68852
+ } else {
68853
+ return {
68854
+ startLineIndex: i2,
68855
+ endLineIndex: afterStart - 1,
68856
+ matchedBefore: contextBefore,
68857
+ matchedAfter: []
68858
+ };
68859
+ }
68860
+ }
68861
+ }
67974
68862
  }
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
- });
68863
+ } else if (contextAfter && contextAfter.length > 0) {
68864
+ for (let j = 0;j <= lines.length - contextAfter.length; j++) {
68865
+ const afterSlice = lines.slice(j, j + contextAfter.length);
68866
+ if (arraysEqual(afterSlice, contextAfter)) {
68867
+ return {
68868
+ startLineIndex: 0,
68869
+ endLineIndex: j - 1,
68870
+ matchedBefore: [],
68871
+ matchedAfter: contextAfter
68872
+ };
68873
+ }
67984
68874
  }
67985
68875
  }
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
- });
68876
+ return null;
67991
68877
  }
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.",
68878
+ function arraysEqual(a, b) {
68879
+ if (a.length !== b.length)
68880
+ return false;
68881
+ for (let i2 = 0;i2 < a.length; i2++) {
68882
+ if (a[i2] !== b[i2])
68883
+ return false;
68884
+ }
68885
+ return true;
68886
+ }
68887
+ function validateOldContent(lines, startIndex, endIndex, oldContent) {
68888
+ if (!oldContent) {
68889
+ return { valid: true };
68890
+ }
68891
+ const currentContent = lines.slice(startIndex, endIndex + 1).join(`
68892
+ `);
68893
+ if (currentContent !== oldContent) {
68894
+ return {
68895
+ valid: false,
68896
+ expected: oldContent,
68897
+ actual: currentContent
68898
+ };
68899
+ }
68900
+ return { valid: true };
68901
+ }
68902
+ var suggestPatch = createSwarmTool({
68903
+ 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
68904
  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.")
68905
+ targetFiles: tool.schema.array(tool.schema.string()).describe("Array of file paths to patch").min(1),
68906
+ changes: tool.schema.array(tool.schema.object({
68907
+ file: tool.schema.string().describe("Path to the file this change applies to"),
68908
+ contextBefore: tool.schema.array(tool.schema.string()).optional().describe("Lines before the change region (anchor)"),
68909
+ contextAfter: tool.schema.array(tool.schema.string()).optional().describe("Lines after the change region (anchor)"),
68910
+ oldContent: tool.schema.string().optional().describe("Current content to be replaced"),
68911
+ newContent: tool.schema.string().describe("New content to replace with")
68912
+ })).describe("Array of change descriptions with context anchors").min(1)
67997
68913
  },
67998
68914
  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 {
68915
+ if (!args2 || typeof args2 !== "object") {
68006
68916
  return JSON.stringify({
68007
- file: "<unknown>",
68008
- error: "Invalid arguments: could not extract file path",
68009
- symbols: []
68917
+ success: false,
68918
+ error: true,
68919
+ type: "parse-error",
68920
+ message: "Could not parse suggest-patch arguments"
68010
68921
  }, null, 2);
68011
68922
  }
68012
- const cwd = directory;
68013
- const ext = path65.extname(file3);
68014
- if (containsControlChars(file3)) {
68923
+ const obj = args2;
68924
+ const targetFiles = obj.targetFiles ?? [];
68925
+ const changes = obj.changes ?? [];
68926
+ if (!targetFiles || targetFiles.length === 0) {
68015
68927
  return JSON.stringify({
68016
- file: file3,
68017
- error: "Path contains invalid control characters",
68018
- symbols: []
68928
+ success: false,
68929
+ error: true,
68930
+ type: "parse-error",
68931
+ message: "targetFiles cannot be empty"
68019
68932
  }, null, 2);
68020
68933
  }
68021
- if (containsPathTraversal(file3)) {
68934
+ if (!changes || changes.length === 0) {
68022
68935
  return JSON.stringify({
68023
- file: file3,
68024
- error: "Path contains path traversal sequence",
68025
- symbols: []
68936
+ success: false,
68937
+ error: true,
68938
+ type: "parse-error",
68939
+ message: "changes cannot be empty"
68026
68940
  }, null, 2);
68027
68941
  }
68028
- if (containsWindowsAttacks(file3)) {
68942
+ if (!fs56.existsSync(directory)) {
68029
68943
  return JSON.stringify({
68030
- file: file3,
68031
- error: "Path contains invalid Windows-specific sequence",
68032
- symbols: []
68944
+ success: false,
68945
+ error: true,
68946
+ type: "file-not-found",
68947
+ message: "Workspace directory does not exist"
68033
68948
  }, null, 2);
68034
68949
  }
68035
- if (!isPathInWorkspace(file3, cwd)) {
68950
+ const patches = [];
68951
+ const filesModifiedSet = new Set;
68952
+ const errors5 = [];
68953
+ const targetFileSet = new Set(targetFiles);
68954
+ for (let changeIndex = 0;changeIndex < changes.length; changeIndex++) {
68955
+ const change = changes[changeIndex];
68956
+ if (!targetFileSet.has(change.file)) {
68957
+ errors5.push({
68958
+ success: false,
68959
+ error: true,
68960
+ type: "parse-error",
68961
+ message: `File "${change.file}" is not in targetFiles`,
68962
+ details: { location: change.file }
68963
+ });
68964
+ continue;
68965
+ }
68966
+ if (!validateFilePath(change.file, directory)) {
68967
+ errors5.push({
68968
+ success: false,
68969
+ error: true,
68970
+ type: "parse-error",
68971
+ message: `Invalid file path: ${change.file}`,
68972
+ details: {
68973
+ location: change.file
68974
+ }
68975
+ });
68976
+ continue;
68977
+ }
68978
+ const fullPath = path68.resolve(directory, change.file);
68979
+ if (!fs56.existsSync(fullPath)) {
68980
+ errors5.push({
68981
+ success: false,
68982
+ error: true,
68983
+ type: "file-not-found",
68984
+ message: `File not found: ${change.file}`,
68985
+ details: {
68986
+ location: change.file
68987
+ }
68988
+ });
68989
+ continue;
68990
+ }
68991
+ let content;
68992
+ try {
68993
+ content = fs56.readFileSync(fullPath, "utf-8");
68994
+ } catch (err2) {
68995
+ errors5.push({
68996
+ success: false,
68997
+ error: true,
68998
+ type: "unknown",
68999
+ message: `Could not read file: ${err2 instanceof Error ? err2.message : String(err2)}`,
69000
+ details: {
69001
+ location: `${change.file}:0`
69002
+ }
69003
+ });
69004
+ continue;
69005
+ }
69006
+ const contextMatch = findContextMatch(content, change.contextBefore, change.contextAfter, change.oldContent);
69007
+ if (!contextMatch) {
69008
+ errors5.push({
69009
+ success: false,
69010
+ error: true,
69011
+ type: "context-mismatch",
69012
+ message: `Could not find context anchor in ${change.file}. Provide contextBefore/contextAfter lines to locate the change region.`,
69013
+ details: {
69014
+ location: change.file
69015
+ }
69016
+ });
69017
+ continue;
69018
+ }
69019
+ const lines = content.split(`
69020
+ `);
69021
+ const oldContentValidation = validateOldContent(lines, contextMatch.startLineIndex + contextMatch.matchedBefore.length, contextMatch.endLineIndex - contextMatch.matchedAfter.length + 1, change.oldContent);
69022
+ if (!oldContentValidation.valid) {
69023
+ errors5.push({
69024
+ success: false,
69025
+ error: true,
69026
+ type: "context-mismatch",
69027
+ message: `Content at the specified location does not match oldContent`,
69028
+ details: {
69029
+ expected: oldContentValidation.expected,
69030
+ actual: oldContentValidation.actual,
69031
+ location: `${change.file}:${contextMatch.startLineIndex + 1}-${contextMatch.endLineIndex + 1}`
69032
+ }
69033
+ });
69034
+ continue;
69035
+ }
69036
+ const originalContext = [
69037
+ ...contextMatch.matchedBefore,
69038
+ ...contextMatch.matchedAfter.length > 0 ? ["..."] : [],
69039
+ ...contextMatch.matchedAfter
69040
+ ];
69041
+ patches.push({
69042
+ file: change.file,
69043
+ originalContext,
69044
+ newContent: change.newContent,
69045
+ hunkIndex: changeIndex
69046
+ });
69047
+ filesModifiedSet.add(change.file);
69048
+ }
69049
+ if (patches.length > 0) {
68036
69050
  return JSON.stringify({
68037
- file: file3,
68038
- error: "Path is outside workspace",
68039
- symbols: []
69051
+ success: true,
69052
+ patches,
69053
+ filesModified: Array.from(filesModifiedSet),
69054
+ ...errors5.length > 0 && { errors: errors5 }
68040
69055
  }, null, 2);
68041
69056
  }
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);
69057
+ if (errors5.length === 1) {
69058
+ return JSON.stringify(errors5[0], null, 2);
68064
69059
  }
68065
69060
  return JSON.stringify({
68066
- file: file3,
68067
- symbolCount: syms.length,
68068
- symbols: syms
69061
+ success: false,
69062
+ error: true,
69063
+ type: "context-mismatch",
69064
+ message: `All ${errors5.length} patch suggestions failed`,
69065
+ errors: errors5
68069
69066
  }, null, 2);
68070
69067
  }
68071
69068
  });
@@ -68081,10 +69078,10 @@ init_test_runner();
68081
69078
  init_dist();
68082
69079
  init_utils();
68083
69080
  init_create_tool();
68084
- import * as fs54 from "fs";
68085
- import * as path66 from "path";
69081
+ import * as fs57 from "fs";
69082
+ import * as path69 from "path";
68086
69083
  var MAX_TEXT_LENGTH = 200;
68087
- var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
69084
+ var MAX_FILE_SIZE_BYTES9 = 1024 * 1024;
68088
69085
  var SUPPORTED_EXTENSIONS2 = new Set([
68089
69086
  ".ts",
68090
69087
  ".tsx",
@@ -68147,9 +69144,9 @@ function validatePathsInput(paths, cwd) {
68147
69144
  return { error: "paths contains path traversal", resolvedPath: null };
68148
69145
  }
68149
69146
  try {
68150
- const resolvedPath = path66.resolve(paths);
68151
- const normalizedCwd = path66.resolve(cwd);
68152
- const normalizedResolved = path66.resolve(resolvedPath);
69147
+ const resolvedPath = path69.resolve(paths);
69148
+ const normalizedCwd = path69.resolve(cwd);
69149
+ const normalizedResolved = path69.resolve(resolvedPath);
68153
69150
  if (!normalizedResolved.startsWith(normalizedCwd)) {
68154
69151
  return {
68155
69152
  error: "paths must be within the current working directory",
@@ -68165,13 +69162,13 @@ function validatePathsInput(paths, cwd) {
68165
69162
  }
68166
69163
  }
68167
69164
  function isSupportedExtension(filePath) {
68168
- const ext = path66.extname(filePath).toLowerCase();
69165
+ const ext = path69.extname(filePath).toLowerCase();
68169
69166
  return SUPPORTED_EXTENSIONS2.has(ext);
68170
69167
  }
68171
69168
  function findSourceFiles2(dir, files = []) {
68172
69169
  let entries;
68173
69170
  try {
68174
- entries = fs54.readdirSync(dir);
69171
+ entries = fs57.readdirSync(dir);
68175
69172
  } catch {
68176
69173
  return files;
68177
69174
  }
@@ -68180,10 +69177,10 @@ function findSourceFiles2(dir, files = []) {
68180
69177
  if (SKIP_DIRECTORIES4.has(entry)) {
68181
69178
  continue;
68182
69179
  }
68183
- const fullPath = path66.join(dir, entry);
69180
+ const fullPath = path69.join(dir, entry);
68184
69181
  let stat2;
68185
69182
  try {
68186
- stat2 = fs54.statSync(fullPath);
69183
+ stat2 = fs57.statSync(fullPath);
68187
69184
  } catch {
68188
69185
  continue;
68189
69186
  }
@@ -68276,7 +69273,7 @@ var todo_extract = createSwarmTool({
68276
69273
  return JSON.stringify(errorResult, null, 2);
68277
69274
  }
68278
69275
  const scanPath = resolvedPath;
68279
- if (!fs54.existsSync(scanPath)) {
69276
+ if (!fs57.existsSync(scanPath)) {
68280
69277
  const errorResult = {
68281
69278
  error: `path not found: ${pathsInput}`,
68282
69279
  total: 0,
@@ -68286,13 +69283,13 @@ var todo_extract = createSwarmTool({
68286
69283
  return JSON.stringify(errorResult, null, 2);
68287
69284
  }
68288
69285
  const filesToScan = [];
68289
- const stat2 = fs54.statSync(scanPath);
69286
+ const stat2 = fs57.statSync(scanPath);
68290
69287
  if (stat2.isFile()) {
68291
69288
  if (isSupportedExtension(scanPath)) {
68292
69289
  filesToScan.push(scanPath);
68293
69290
  } else {
68294
69291
  const errorResult = {
68295
- error: `unsupported file extension: ${path66.extname(scanPath)}`,
69292
+ error: `unsupported file extension: ${path69.extname(scanPath)}`,
68296
69293
  total: 0,
68297
69294
  byPriority: { high: 0, medium: 0, low: 0 },
68298
69295
  entries: []
@@ -68305,11 +69302,11 @@ var todo_extract = createSwarmTool({
68305
69302
  const allEntries = [];
68306
69303
  for (const filePath of filesToScan) {
68307
69304
  try {
68308
- const fileStat = fs54.statSync(filePath);
68309
- if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
69305
+ const fileStat = fs57.statSync(filePath);
69306
+ if (fileStat.size > MAX_FILE_SIZE_BYTES9) {
68310
69307
  continue;
68311
69308
  }
68312
- const content = fs54.readFileSync(filePath, "utf-8");
69309
+ const content = fs57.readFileSync(filePath, "utf-8");
68313
69310
  const entries = parseTodoComments(content, filePath, tagsSet);
68314
69311
  allEntries.push(...entries);
68315
69312
  } catch {}
@@ -68338,18 +69335,18 @@ var todo_extract = createSwarmTool({
68338
69335
  init_tool();
68339
69336
  init_schema();
68340
69337
  init_gate_evidence();
68341
- import * as fs56 from "fs";
68342
- import * as path68 from "path";
69338
+ import * as fs59 from "fs";
69339
+ import * as path71 from "path";
68343
69340
 
68344
69341
  // src/hooks/diff-scope.ts
68345
- import * as fs55 from "fs";
68346
- import * as path67 from "path";
69342
+ import * as fs58 from "fs";
69343
+ import * as path70 from "path";
68347
69344
  function getDeclaredScope(taskId, directory) {
68348
69345
  try {
68349
- const planPath = path67.join(directory, ".swarm", "plan.json");
68350
- if (!fs55.existsSync(planPath))
69346
+ const planPath = path70.join(directory, ".swarm", "plan.json");
69347
+ if (!fs58.existsSync(planPath))
68351
69348
  return null;
68352
- const raw = fs55.readFileSync(planPath, "utf-8");
69349
+ const raw = fs58.readFileSync(planPath, "utf-8");
68353
69350
  const plan = JSON.parse(raw);
68354
69351
  for (const phase of plan.phases ?? []) {
68355
69352
  for (const task of phase.tasks ?? []) {
@@ -68462,7 +69459,7 @@ var TIER_3_PATTERNS = [
68462
69459
  ];
68463
69460
  function matchesTier3Pattern(files) {
68464
69461
  for (const file3 of files) {
68465
- const fileName = path68.basename(file3);
69462
+ const fileName = path71.basename(file3);
68466
69463
  for (const pattern of TIER_3_PATTERNS) {
68467
69464
  if (pattern.test(fileName)) {
68468
69465
  return true;
@@ -68476,8 +69473,8 @@ function checkReviewerGate(taskId, workingDirectory) {
68476
69473
  if (hasActiveTurboMode()) {
68477
69474
  const resolvedDir2 = workingDirectory;
68478
69475
  try {
68479
- const planPath = path68.join(resolvedDir2, ".swarm", "plan.json");
68480
- const planRaw = fs56.readFileSync(planPath, "utf-8");
69476
+ const planPath = path71.join(resolvedDir2, ".swarm", "plan.json");
69477
+ const planRaw = fs59.readFileSync(planPath, "utf-8");
68481
69478
  const plan = JSON.parse(planRaw);
68482
69479
  for (const planPhase of plan.phases ?? []) {
68483
69480
  for (const task of planPhase.tasks ?? []) {
@@ -68543,8 +69540,8 @@ function checkReviewerGate(taskId, workingDirectory) {
68543
69540
  }
68544
69541
  try {
68545
69542
  const resolvedDir2 = workingDirectory;
68546
- const planPath = path68.join(resolvedDir2, ".swarm", "plan.json");
68547
- const planRaw = fs56.readFileSync(planPath, "utf-8");
69543
+ const planPath = path71.join(resolvedDir2, ".swarm", "plan.json");
69544
+ const planRaw = fs59.readFileSync(planPath, "utf-8");
68548
69545
  const plan = JSON.parse(planRaw);
68549
69546
  for (const planPhase of plan.phases ?? []) {
68550
69547
  for (const task of planPhase.tasks ?? []) {
@@ -68726,8 +69723,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
68726
69723
  };
68727
69724
  }
68728
69725
  }
68729
- normalizedDir = path68.normalize(args2.working_directory);
68730
- const pathParts = normalizedDir.split(path68.sep);
69726
+ normalizedDir = path71.normalize(args2.working_directory);
69727
+ const pathParts = normalizedDir.split(path71.sep);
68731
69728
  if (pathParts.includes("..")) {
68732
69729
  return {
68733
69730
  success: false,
@@ -68737,11 +69734,11 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
68737
69734
  ]
68738
69735
  };
68739
69736
  }
68740
- const resolvedDir = path68.resolve(normalizedDir);
69737
+ const resolvedDir = path71.resolve(normalizedDir);
68741
69738
  try {
68742
- const realPath = fs56.realpathSync(resolvedDir);
68743
- const planPath = path68.join(realPath, ".swarm", "plan.json");
68744
- if (!fs56.existsSync(planPath)) {
69739
+ const realPath = fs59.realpathSync(resolvedDir);
69740
+ const planPath = path71.join(realPath, ".swarm", "plan.json");
69741
+ if (!fs59.existsSync(planPath)) {
68745
69742
  return {
68746
69743
  success: false,
68747
69744
  message: `Invalid working_directory: plan not found in "${realPath}"`,
@@ -68774,8 +69771,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
68774
69771
  recoverTaskStateFromDelegations(args2.task_id);
68775
69772
  let phaseRequiresReviewer = true;
68776
69773
  try {
68777
- const planPath = path68.join(directory, ".swarm", "plan.json");
68778
- const planRaw = fs56.readFileSync(planPath, "utf-8");
69774
+ const planPath = path71.join(directory, ".swarm", "plan.json");
69775
+ const planRaw = fs59.readFileSync(planPath, "utf-8");
68779
69776
  const plan = JSON.parse(planRaw);
68780
69777
  const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
68781
69778
  if (taskPhase?.required_agents && !taskPhase.required_agents.includes("reviewer")) {
@@ -68838,8 +69835,8 @@ var update_task_status = createSwarmTool({
68838
69835
  init_tool();
68839
69836
  init_utils2();
68840
69837
  init_create_tool();
68841
- import fs57 from "fs";
68842
- import path69 from "path";
69838
+ import fs60 from "fs";
69839
+ import path72 from "path";
68843
69840
  function normalizeVerdict(verdict) {
68844
69841
  switch (verdict) {
68845
69842
  case "APPROVED":
@@ -68886,7 +69883,7 @@ async function executeWriteDriftEvidence(args2, directory) {
68886
69883
  entries: [evidenceEntry]
68887
69884
  };
68888
69885
  const filename = "drift-verifier.json";
68889
- const relativePath = path69.join("evidence", String(phase), filename);
69886
+ const relativePath = path72.join("evidence", String(phase), filename);
68890
69887
  let validatedPath;
68891
69888
  try {
68892
69889
  validatedPath = validateSwarmPath(directory, relativePath);
@@ -68897,12 +69894,12 @@ async function executeWriteDriftEvidence(args2, directory) {
68897
69894
  message: error93 instanceof Error ? error93.message : "Failed to validate path"
68898
69895
  }, null, 2);
68899
69896
  }
68900
- const evidenceDir = path69.dirname(validatedPath);
69897
+ const evidenceDir = path72.dirname(validatedPath);
68901
69898
  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);
69899
+ await fs60.promises.mkdir(evidenceDir, { recursive: true });
69900
+ const tempPath = path72.join(evidenceDir, `.${filename}.tmp`);
69901
+ await fs60.promises.writeFile(tempPath, JSON.stringify(evidenceContent, null, 2), "utf-8");
69902
+ await fs60.promises.rename(tempPath, validatedPath);
68906
69903
  return JSON.stringify({
68907
69904
  success: true,
68908
69905
  phase,
@@ -69093,7 +70090,7 @@ var OpenCodeSwarm = async (ctx) => {
69093
70090
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
69094
70091
  preflightTriggerManager = new PTM(automationConfig);
69095
70092
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
69096
- const swarmDir = path70.resolve(ctx.directory, ".swarm");
70093
+ const swarmDir = path73.resolve(ctx.directory, ".swarm");
69097
70094
  statusArtifact = new ASA(swarmDir);
69098
70095
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
69099
70096
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {
@@ -69223,6 +70220,9 @@ var OpenCodeSwarm = async (ctx) => {
69223
70220
  symbols,
69224
70221
  test_runner,
69225
70222
  todo_extract,
70223
+ search,
70224
+ batch_symbols,
70225
+ suggest_patch: suggestPatch,
69226
70226
  update_task_status,
69227
70227
  write_retro,
69228
70228
  write_drift_evidence,
@@ -69431,7 +70431,7 @@ var OpenCodeSwarm = async (ctx) => {
69431
70431
  "command.execute.before": safeHook(commandHandler),
69432
70432
  "tool.execute.before": async (input, output) => {
69433
70433
  if (process.env.DEBUG_SWARM)
69434
- console.error(`[DIAG] toolBefore tool=${input.tool?.replace?.(/^[^:]+[:.]/, "") ?? input.tool} session=${input.sessionID}`);
70434
+ console.error(`[DIAG] toolBefore tool=${normalizeToolName(input.tool) ?? input.tool} session=${input.sessionID}`);
69435
70435
  if (!swarmState.activeAgent.has(input.sessionID)) {
69436
70436
  swarmState.activeAgent.set(input.sessionID, ORCHESTRATOR_NAME);
69437
70437
  }
@@ -69462,10 +70462,10 @@ var OpenCodeSwarm = async (ctx) => {
69462
70462
  },
69463
70463
  "tool.execute.after": async (input, output) => {
69464
70464
  const _dbg = !!process.env.DEBUG_SWARM;
69465
- const _toolName = input.tool?.replace?.(/^[^:]+[:.]/, "") ?? input.tool;
70465
+ const _toolName = normalizeToolName(input.tool) ?? input.tool;
69466
70466
  if (_dbg)
69467
70467
  console.error(`[DIAG] toolAfter START tool=${_toolName} session=${input.sessionID}`);
69468
- const normalizedTool = input.tool.replace(/^[^:]+[:.]/, "");
70468
+ const normalizedTool = normalizeToolName(input.tool);
69469
70469
  const isTaskTool = normalizedTool === "Task" || normalizedTool === "task";
69470
70470
  const hookChain = async () => {
69471
70471
  await activityHooks.toolAfter(input, output);