nexus-agents 2.165.1 → 2.166.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -125,6 +125,8 @@ ${lines.join("\n")}`;
125
125
  }
126
126
 
127
127
  // src/indexer/codebase-search.ts
128
+ var DEFAULT_INDEX_MAX_DEPTH = 24;
129
+ var MAX_INDEX_MAX_DEPTH = 64;
128
130
  var SCORE_EXACT = 20;
129
131
  var SCORE_PREFIX = 10;
130
132
  var SCORE_WORD = 5;
@@ -135,19 +137,22 @@ function isSourceFile(name) {
135
137
  return [".ts", ".tsx", ".js", ".jsx"].includes(ext) && !name.endsWith(".test.ts") && !name.endsWith(".test.tsx") && !name.endsWith(".d.ts");
136
138
  }
137
139
  async function findSourceFiles(dir, maxDepth) {
138
- if (maxDepth <= 0) return [];
140
+ if (maxDepth <= 0) return { files: [], skippedDirs: 1 };
139
141
  const files = [];
142
+ let skippedDirs = 0;
140
143
  const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
141
144
  for (const entry of entries) {
142
145
  const fullPath = resolve(dir, entry.name);
143
146
  if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist") {
144
- files.push(...await findSourceFiles(fullPath, maxDepth - 1));
147
+ const sub = await findSourceFiles(fullPath, maxDepth - 1);
148
+ files.push(...sub.files);
149
+ skippedDirs += sub.skippedDirs;
145
150
  }
146
151
  if (entry.isFile() && isSourceFile(entry.name)) {
147
152
  files.push(fullPath);
148
153
  }
149
154
  }
150
- return files;
155
+ return { files, skippedDirs };
151
156
  }
152
157
  function scoreMatch(symbolName, query) {
153
158
  const nameLower = symbolName.toLowerCase();
@@ -169,12 +174,20 @@ var CodebaseIndex = class {
169
174
  symbols = [];
170
175
  fileResults = /* @__PURE__ */ new Map();
171
176
  rootDir;
177
+ skippedDirs = 0;
172
178
  constructor(rootDir) {
173
179
  this.rootDir = rootDir;
174
180
  }
175
- /** Index all TS/JS source files in the directory. */
176
- async index(maxDepth = 4) {
177
- const files = await findSourceFiles(this.rootDir, maxDepth);
181
+ /**
182
+ * Index all TS/JS source files in the directory.
183
+ *
184
+ * `maxDepth` is clamped to `[1, MAX_INDEX_MAX_DEPTH]` so a caller-supplied
185
+ * value can't force an unbounded tree-walk (#4243).
186
+ */
187
+ async index(maxDepth = DEFAULT_INDEX_MAX_DEPTH) {
188
+ const clampedDepth = Math.min(Math.max(maxDepth, 1), MAX_INDEX_MAX_DEPTH);
189
+ const { files, skippedDirs } = await findSourceFiles(this.rootDir, clampedDepth);
190
+ this.skippedDirs = skippedDirs;
178
191
  for (const file of files) {
179
192
  const result = await extractSymbols(file);
180
193
  const relPath = relative(this.rootDir, file);
@@ -186,7 +199,8 @@ var CodebaseIndex = class {
186
199
  return {
187
200
  totalFiles: files.length,
188
201
  totalSymbols: this.symbols.length,
189
- indexedAt: (/* @__PURE__ */ new Date()).toISOString()
202
+ indexedAt: (/* @__PURE__ */ new Date()).toISOString(),
203
+ skippedDirs
190
204
  };
191
205
  }
192
206
  /** Search symbols by keyword. Returns top N results sorted by relevance. */
@@ -236,7 +250,8 @@ var CodebaseIndex = class {
236
250
  get stats() {
237
251
  return {
238
252
  files: this.fileResults.size,
239
- symbols: this.symbols.length
253
+ symbols: this.symbols.length,
254
+ skippedDirs: this.skippedDirs
240
255
  };
241
256
  }
242
257
  };
@@ -244,6 +259,8 @@ var CodebaseIndex = class {
244
259
  export {
245
260
  extractSymbols,
246
261
  extractSymbolIndex,
262
+ DEFAULT_INDEX_MAX_DEPTH,
263
+ MAX_INDEX_MAX_DEPTH,
247
264
  CodebaseIndex
248
265
  };
249
- //# sourceMappingURL=chunk-3ACDP4E6.js.map
266
+ //# sourceMappingURL=chunk-AO4X3BMP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/indexer/codebase-search.ts","../src/indexer/symbol-extractor.ts"],"sourcesContent":["/**\n * Codebase search — keyword search across symbol indices.\n *\n * Builds an in-memory symbol index for a directory of TS/JS files,\n * then supports keyword search, file summaries, and symbol lookup.\n *\n * Inspired by Augment Code's Context Engine. Uses the existing\n * extractSymbols() function for AST parsing.\n *\n * @module indexer/codebase-search\n */\n\nimport { readdir } from 'node:fs/promises';\nimport { resolve, extname, relative } from 'node:path';\nimport {\n extractSymbols,\n type CodeSymbol,\n type SymbolExtractionResult,\n} from './symbol-extractor.js';\n\n/** A symbol with its source file path. */\nexport interface IndexedSymbol extends CodeSymbol {\n /** Relative file path from the indexed root. */\n filePath: string;\n}\n\n/** Search result with relevance scoring. */\nexport interface SearchResult {\n symbol: IndexedSymbol;\n /** Relevance score (higher = better match). */\n score: number;\n /** How the query matched (exact, prefix, substring, word). */\n matchType: 'exact' | 'prefix' | 'substring' | 'word';\n}\n\n/** File summary — compact overview of a source file. */\nexport interface FileSummary {\n filePath: string;\n totalLines: number;\n exportedSymbols: number;\n privateSymbols: number;\n kinds: Record<string, number>;\n}\n\n/** Index statistics. */\nexport interface IndexStats {\n totalFiles: number;\n totalSymbols: number;\n indexedAt: string;\n /** Directories not descended into because `maxDepth` was exhausted (#4243). */\n skippedDirs: number;\n}\n\n/** Default recursion depth for `CodebaseIndex.index()` (#4243 — was hardcoded 4). */\nexport const DEFAULT_INDEX_MAX_DEPTH = 24;\n/** Upper clamp for caller-supplied `maxDepth` to bound worst-case tree-walk cost. */\nexport const MAX_INDEX_MAX_DEPTH = 64;\n\n// Score weights for different match types\nconst SCORE_EXACT = 20;\nconst SCORE_PREFIX = 10;\nconst SCORE_WORD = 5;\nconst SCORE_SUBSTRING = 2;\nconst SCORE_EXPORTED_BONUS = 3;\n\nfunction isSourceFile(name: string): boolean {\n const ext = extname(name).toLowerCase();\n return (\n ['.ts', '.tsx', '.js', '.jsx'].includes(ext) &&\n !name.endsWith('.test.ts') &&\n !name.endsWith('.test.tsx') &&\n !name.endsWith('.d.ts')\n );\n}\n\n/** Result of a recursive source-file walk: the files found plus a truncation signal. */\ninterface FindSourceFilesResult {\n files: string[];\n /** Count of directories that were NOT descended into because maxDepth hit 0. */\n skippedDirs: number;\n}\n\nasync function findSourceFiles(dir: string, maxDepth: number): Promise<FindSourceFilesResult> {\n if (maxDepth <= 0) return { files: [], skippedDirs: 1 };\n const files: string[] = [];\n let skippedDirs = 0;\n const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);\n for (const entry of entries) {\n const fullPath = resolve(dir, entry.name);\n if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== 'dist') {\n const sub = await findSourceFiles(fullPath, maxDepth - 1);\n files.push(...sub.files);\n skippedDirs += sub.skippedDirs;\n }\n if (entry.isFile() && isSourceFile(entry.name)) {\n files.push(fullPath);\n }\n }\n return { files, skippedDirs };\n}\n\nfunction scoreMatch(symbolName: string, query: string): SearchResult['score'] | null {\n const nameLower = symbolName.toLowerCase();\n const queryLower = query.toLowerCase();\n\n if (nameLower === queryLower) return SCORE_EXACT;\n if (nameLower.startsWith(queryLower)) return SCORE_PREFIX;\n\n // Word boundary match (camelCase splitting)\n const words = symbolName\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .toLowerCase()\n .split(/[\\s_-]+/);\n if (words.some((w) => w === queryLower)) return SCORE_WORD;\n\n if (nameLower.includes(queryLower)) return SCORE_SUBSTRING;\n\n return null;\n}\n\nfunction getMatchType(score: number): SearchResult['matchType'] {\n if (score >= SCORE_EXACT) return 'exact';\n if (score >= SCORE_PREFIX) return 'prefix';\n if (score >= SCORE_WORD) return 'word';\n return 'substring';\n}\n\n/** In-memory codebase symbol index. */\nexport class CodebaseIndex {\n private readonly symbols: IndexedSymbol[] = [];\n private readonly fileResults = new Map<string, SymbolExtractionResult>();\n private readonly rootDir: string;\n private skippedDirs = 0;\n\n constructor(rootDir: string) {\n this.rootDir = rootDir;\n }\n\n /**\n * Index all TS/JS source files in the directory.\n *\n * `maxDepth` is clamped to `[1, MAX_INDEX_MAX_DEPTH]` so a caller-supplied\n * value can't force an unbounded tree-walk (#4243).\n */\n async index(maxDepth: number = DEFAULT_INDEX_MAX_DEPTH): Promise<IndexStats> {\n const clampedDepth = Math.min(Math.max(maxDepth, 1), MAX_INDEX_MAX_DEPTH);\n const { files, skippedDirs } = await findSourceFiles(this.rootDir, clampedDepth);\n this.skippedDirs = skippedDirs;\n\n for (const file of files) {\n const result = await extractSymbols(file);\n const relPath = relative(this.rootDir, file);\n this.fileResults.set(relPath, result);\n\n for (const symbol of result.symbols) {\n this.symbols.push({ ...symbol, filePath: relPath });\n }\n }\n\n return {\n totalFiles: files.length,\n totalSymbols: this.symbols.length,\n indexedAt: new Date().toISOString(),\n skippedDirs,\n };\n }\n\n /** Search symbols by keyword. Returns top N results sorted by relevance. */\n search(query: string, limit = 20): SearchResult[] {\n const results: SearchResult[] = [];\n\n for (const symbol of this.symbols) {\n const baseScore = scoreMatch(symbol.name, query);\n if (baseScore === null) continue;\n\n const bonus = symbol.exported ? SCORE_EXPORTED_BONUS : 0;\n results.push({\n symbol,\n score: baseScore + bonus,\n matchType: getMatchType(baseScore),\n });\n }\n\n return results.sort((a, b) => b.score - a.score).slice(0, limit);\n }\n\n /** Get a compact summary of a file's symbols. */\n getFileSummary(filePath: string): FileSummary | undefined {\n const result = this.fileResults.get(filePath);\n if (result === undefined) return undefined;\n\n const kinds: Record<string, number> = {};\n let exported = 0;\n let priv = 0;\n\n for (const s of result.symbols) {\n kinds[s.kind] = (kinds[s.kind] ?? 0) + 1;\n if (s.exported) exported++;\n else priv++;\n }\n\n return {\n filePath,\n totalLines: result.totalLines,\n exportedSymbols: exported,\n privateSymbols: priv,\n kinds,\n };\n }\n\n /** List all indexed files with symbol counts. */\n listFiles(): Array<{ path: string; symbols: number; lines: number }> {\n return [...this.fileResults.entries()].map(([path, result]) => ({\n path,\n symbols: result.symbols.length,\n lines: result.totalLines,\n }));\n }\n\n /** Get index statistics. */\n get stats(): { files: number; symbols: number; skippedDirs: number } {\n return {\n files: this.fileResults.size,\n symbols: this.symbols.length,\n skippedDirs: this.skippedDirs,\n };\n }\n}\n","/**\n * AST symbol extraction for token-efficient code retrieval.\n *\n * Uses TypeScript's compiler API to extract function, class, method,\n * interface, and type definitions from source files.\n *\n * Token savings: ~80-99% vs reading full files.\n * No additional dependencies — uses TypeScript (already a project dep).\n *\n * @module indexer/symbol-extractor\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { extname } from 'node:path';\nimport ts from 'typescript';\n\n/** A symbol extracted from source code. */\nexport interface CodeSymbol {\n /** Symbol name */\n name: string;\n /** Symbol kind */\n kind: 'function' | 'class' | 'method' | 'interface' | 'type' | 'variable' | 'enum';\n /** Start line (1-based) */\n startLine: number;\n /** End line (1-based) */\n endLine: number;\n /** Full source text of the symbol */\n text: string;\n /** Whether the symbol is exported */\n exported: boolean;\n}\n\n/** Result of extracting symbols from a file. */\nexport interface SymbolExtractionResult {\n filePath: string;\n symbols: CodeSymbol[];\n totalLines: number;\n totalChars: number;\n symbolChars: number;\n savingsPercent: number;\n}\n\nfunction getKind(node: ts.Node): CodeSymbol['kind'] | null {\n if (ts.isFunctionDeclaration(node)) return 'function';\n if (ts.isClassDeclaration(node)) return 'class';\n if (ts.isInterfaceDeclaration(node)) return 'interface';\n if (ts.isTypeAliasDeclaration(node)) return 'type';\n if (ts.isEnumDeclaration(node)) return 'enum';\n if (ts.isMethodDeclaration(node)) return 'method';\n if (ts.isVariableStatement(node)) return 'variable';\n return null;\n}\n\nfunction getName(node: ts.Node): string {\n if (\n ts.isFunctionDeclaration(node) ||\n ts.isClassDeclaration(node) ||\n ts.isInterfaceDeclaration(node) ||\n ts.isTypeAliasDeclaration(node) ||\n ts.isEnumDeclaration(node) ||\n ts.isMethodDeclaration(node)\n ) {\n const nameNode = (node as ts.NamedDeclaration).name;\n return nameNode ? nameNode.getText() : '<anonymous>';\n }\n if (ts.isVariableStatement(node)) {\n const decls = node.declarationList.declarations;\n const firstDecl = decls[0];\n if (firstDecl !== undefined) {\n return firstDecl.name.getText();\n }\n }\n return '<anonymous>';\n}\n\nfunction isExported(node: ts.Node): boolean {\n const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;\n if (modifiers) {\n return modifiers.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);\n }\n return false;\n}\n\nfunction visitNode(node: ts.Node, sourceFile: ts.SourceFile, symbols: CodeSymbol[]): void {\n const kind = getKind(node);\n if (kind !== null) {\n const name = getName(node);\n if (name !== '<anonymous>') {\n const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());\n symbols.push({\n name,\n kind,\n startLine: start.line + 1,\n endLine: end.line + 1,\n text: node.getText(sourceFile),\n exported: isExported(node),\n });\n }\n }\n if (ts.isClassDeclaration(node)) {\n visitClassMembers(node, sourceFile, symbols);\n return;\n }\n ts.forEachChild(node, (child) => {\n visitNode(child, sourceFile, symbols);\n });\n}\n\nfunction visitClassMembers(\n node: ts.ClassDeclaration,\n sourceFile: ts.SourceFile,\n symbols: CodeSymbol[]\n): void {\n for (const member of node.members) {\n if (ts.isMethodDeclaration(member) || ts.isPropertyDeclaration(member)) {\n const memberName = member.name.getText();\n if (memberName !== '<anonymous>') {\n const start = sourceFile.getLineAndCharacterOfPosition(member.getStart());\n const end = sourceFile.getLineAndCharacterOfPosition(member.getEnd());\n symbols.push({\n name: memberName,\n kind: 'method',\n startLine: start.line + 1,\n endLine: end.line + 1,\n text: member.getText(sourceFile),\n exported: false,\n });\n }\n }\n }\n}\n\nfunction computeSavings(totalChars: number, symbolChars: number): number {\n return totalChars > 0 ? Math.round(100 * (1 - symbolChars / totalChars) * 10) / 10 : 0;\n}\n\n/**\n * Extract symbols from a TypeScript/JavaScript file.\n */\nexport async function extractSymbols(filePath: string): Promise<SymbolExtractionResult> {\n const ext = extname(filePath).toLowerCase();\n if (!['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {\n return {\n filePath,\n symbols: [],\n totalLines: 0,\n totalChars: 0,\n symbolChars: 0,\n savingsPercent: 0,\n };\n }\n\n const source = await readFile(filePath, 'utf-8');\n const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true);\n const symbols: CodeSymbol[] = [];\n\n ts.forEachChild(sourceFile, (node) => {\n visitNode(node, sourceFile, symbols);\n });\n\n const totalChars = source.length;\n const symbolChars = symbols.reduce((sum, s) => sum + s.text.length, 0);\n\n return {\n filePath,\n symbols,\n totalLines: source.split('\\n').length,\n totalChars,\n symbolChars,\n savingsPercent: computeSavings(totalChars, symbolChars),\n };\n}\n\n/**\n * Extract a compact symbol index (names + locations only, no source text).\n * This is the minimal representation for LLM context — ~95%+ token savings.\n */\nexport async function extractSymbolIndex(filePath: string): Promise<string> {\n const result = await extractSymbols(filePath);\n if (result.symbols.length === 0) return '';\n\n const lines = result.symbols.map((s) => {\n const exp = s.exported ? 'export ' : '';\n return `${exp}${s.kind} ${s.name} (L${String(s.startLine)}-${String(s.endLine)})`;\n });\n\n return `// ${filePath} — ${String(result.symbols.length)} symbols\\n${lines.join('\\n')}`;\n}\n"],"mappings":"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","names":["extname","extname"]}
@@ -8,7 +8,7 @@ import {
8
8
  checkSqlite,
9
9
  defaultConfig,
10
10
  initDataDirectories
11
- } from "./chunk-S6QSNP67.js";
11
+ } from "./chunk-YIEMNZUD.js";
12
12
  import {
13
13
  BUILT_IN_EXPERTS
14
14
  } from "./chunk-ZM4O442V.js";
@@ -2001,4 +2001,4 @@ export {
2001
2001
  setupCommand,
2002
2002
  setupCommandAsync
2003
2003
  };
2004
- //# sourceMappingURL=chunk-FFPFN4W2.js.map
2004
+ //# sourceMappingURL=chunk-MLURL552.js.map
@@ -6,9 +6,11 @@ import {
6
6
  } from "./chunk-HFOQKCD2.js";
7
7
  import {
8
8
  CodebaseIndex,
9
+ DEFAULT_INDEX_MAX_DEPTH,
10
+ MAX_INDEX_MAX_DEPTH,
9
11
  extractSymbolIndex,
10
12
  extractSymbols
11
- } from "./chunk-3ACDP4E6.js";
13
+ } from "./chunk-AO4X3BMP.js";
12
14
  import {
13
15
  FINDING_SEVERITY_LEVELS,
14
16
  SARIF_LEVEL_MAP,
@@ -18,7 +20,7 @@ import {
18
20
  DEFAULT_TASK_TTL_MS,
19
21
  DEFAULT_TOOL_RATE_LIMITS,
20
22
  clampTaskTtl
21
- } from "./chunk-S6QSNP67.js";
23
+ } from "./chunk-YIEMNZUD.js";
22
24
  import {
23
25
  executeExpert
24
26
  } from "./chunk-U43BUJSN.js";
@@ -39537,40 +39539,47 @@ var SearchCodebaseInputSchema = z79.object({
39537
39539
  query: z79.string().min(1).max(200).describe("Search query (symbol name, keyword, or pattern)"),
39538
39540
  directory: z79.string().min(1).max(500).optional().describe("Directory to search (default: current working directory)"),
39539
39541
  limit: z79.number().min(1).max(50).optional().describe("Max results (default: 20)"),
39540
- mode: z79.enum(["search", "summary", "list"]).optional().describe("search: find symbols. summary: file overview. list: list indexed files.")
39542
+ mode: z79.enum(["search", "summary", "list"]).optional().describe("search: find symbols. summary: file overview. list: list indexed files."),
39543
+ maxDepth: z79.number().int().min(1).max(MAX_INDEX_MAX_DEPTH).optional().describe(
39544
+ `Max directory depth to index below "directory" (default: ${String(DEFAULT_INDEX_MAX_DEPTH)}, clamped to ${String(MAX_INDEX_MAX_DEPTH)}). Raise this if results seem incomplete for a deeply nested tree.`
39545
+ )
39541
39546
  });
39542
39547
  var MAX_CACHED_DIRS = 3;
39543
39548
  var INDEX_TTL_MS = 15 * 60 * 1e3;
39544
39549
  var indexCache = /* @__PURE__ */ new Map();
39545
39550
  var inflightIndex = /* @__PURE__ */ new Map();
39546
- function getFromCache(dir) {
39551
+ function getFromCache(dir, maxDepth) {
39547
39552
  const entry = indexCache.get(dir);
39548
39553
  if (entry === void 0) return void 0;
39549
39554
  if (entry.expiresAt <= getTimeProvider().now()) {
39550
39555
  indexCache.delete(dir);
39551
39556
  return void 0;
39552
39557
  }
39558
+ if (entry.maxDepth < maxDepth) {
39559
+ indexCache.delete(dir);
39560
+ return void 0;
39561
+ }
39553
39562
  indexCache.delete(dir);
39554
39563
  indexCache.set(dir, entry);
39555
39564
  return entry.index;
39556
39565
  }
39557
- function putInCache(dir, index) {
39558
- indexCache.set(dir, { index, expiresAt: getTimeProvider().now() + INDEX_TTL_MS });
39566
+ function putInCache(dir, index, maxDepth) {
39567
+ indexCache.set(dir, { index, expiresAt: getTimeProvider().now() + INDEX_TTL_MS, maxDepth });
39559
39568
  while (indexCache.size > MAX_CACHED_DIRS) {
39560
39569
  const lruKey = indexCache.keys().next().value;
39561
39570
  if (lruKey === void 0) break;
39562
39571
  indexCache.delete(lruKey);
39563
39572
  }
39564
39573
  }
39565
- async function getIndex(dir) {
39566
- const cached = getFromCache(dir);
39574
+ async function getIndex(dir, maxDepth) {
39575
+ const cached = getFromCache(dir, maxDepth);
39567
39576
  if (cached !== void 0) return cached;
39568
39577
  const inflight = inflightIndex.get(dir);
39569
39578
  if (inflight !== void 0) return inflight;
39570
39579
  const promise = (async () => {
39571
39580
  const index = new CodebaseIndex(dir);
39572
- await index.index(4);
39573
- putInCache(dir, index);
39581
+ await index.index(maxDepth);
39582
+ putInCache(dir, index, maxDepth);
39574
39583
  return index;
39575
39584
  })().finally(() => {
39576
39585
  inflightIndex.delete(dir);
@@ -39586,6 +39595,25 @@ function resolveSearchDir(directory) {
39586
39595
  }
39587
39596
  return { dir };
39588
39597
  }
39598
+ function skippedDirsNote(index) {
39599
+ const { skippedDirs } = index.stats;
39600
+ if (skippedDirs === 0) return "";
39601
+ return `
39602
+
39603
+ Note: ${String(skippedDirs)} subdirector${skippedDirs === 1 ? "y was" : "ies were"} not indexed because maxDepth was exhausted. Pass a larger maxDepth (up to ${String(MAX_INDEX_MAX_DEPTH)}) to include them.`;
39604
+ }
39605
+ function formatSearchOutput(index, query, results) {
39606
+ if (results.length === 0) {
39607
+ return `No symbols matching "${query}" found in ${String(index.stats.files)} indexed files.${skippedDirsNote(index)}`;
39608
+ }
39609
+ const output2 = results.map((r) => {
39610
+ const exp = r.symbol.exported ? "export " : "";
39611
+ return `[${r.matchType}] ${exp}${r.symbol.kind} ${r.symbol.name} (${r.symbol.filePath}:${String(r.symbol.startLine)})`;
39612
+ }).join("\n");
39613
+ return `${String(results.length)} results for "${query}":
39614
+
39615
+ ${output2}${skippedDirsNote(index)}`;
39616
+ }
39589
39617
  function formatListOutput(index) {
39590
39618
  const files = index.listFiles();
39591
39619
  const output2 = files.sort((a, b) => b.symbols - a.symbols).map(
@@ -39593,7 +39621,7 @@ function formatListOutput(index) {
39593
39621
  ).join("\n");
39594
39622
  return `${String(index.stats.files)} files, ${String(index.stats.symbols)} symbols indexed
39595
39623
 
39596
- ${output2}`;
39624
+ ${output2}${skippedDirsNote(index)}`;
39597
39625
  }
39598
39626
  async function searchCodebaseHandler(args, ctx) {
39599
39627
  const parsed = SearchCodebaseInputSchema.safeParse(args);
@@ -39603,13 +39631,13 @@ async function searchCodebaseHandler(args, ctx) {
39603
39631
  message: `Validation error: ${formatZodError(parsed.error)}`
39604
39632
  });
39605
39633
  }
39606
- const { query, directory, limit, mode } = parsed.data;
39634
+ const { query, directory, limit, mode, maxDepth } = parsed.data;
39607
39635
  const dirResult = resolveSearchDir(directory);
39608
39636
  if ("error" in dirResult) {
39609
39637
  return toolStructuredError({ errorCategory: "permission", message: dirResult.error });
39610
39638
  }
39611
39639
  try {
39612
- const index = await getIndex(dirResult.dir);
39640
+ const index = await getIndex(dirResult.dir, maxDepth ?? DEFAULT_INDEX_MAX_DEPTH);
39613
39641
  if (mode === "list") return toolSuccess(formatListOutput(index));
39614
39642
  if (mode === "summary") {
39615
39643
  const summary = index.getFileSummary(query);
@@ -39621,18 +39649,7 @@ async function searchCodebaseHandler(args, ctx) {
39621
39649
  return toolSuccess(JSON.stringify(summary, null, 2));
39622
39650
  }
39623
39651
  const results = index.search(query, limit ?? 20);
39624
- if (results.length === 0) {
39625
- return toolSuccess(
39626
- `No symbols matching "${query}" found in ${String(index.stats.files)} indexed files.`
39627
- );
39628
- }
39629
- const output2 = results.map((r) => {
39630
- const exp = r.symbol.exported ? "export " : "";
39631
- return `[${r.matchType}] ${exp}${r.symbol.kind} ${r.symbol.name} (${r.symbol.filePath}:${String(r.symbol.startLine)})`;
39632
- }).join("\n");
39633
- return toolSuccess(`${String(results.length)} results for "${query}":
39634
-
39635
- ${output2}`);
39652
+ return toolSuccess(formatSearchOutput(index, query, results));
39636
39653
  } catch (caught) {
39637
39654
  const e = caught instanceof Error ? caught : new Error(String(caught));
39638
39655
  ctx.logger.error("Codebase search failed", e);
@@ -39648,9 +39665,12 @@ function registerSearchCodebaseTool(server, deps) {
39648
39665
  query: z79.string().min(1).max(200).describe("Search query or file path"),
39649
39666
  directory: z79.string().max(500).optional().describe("Directory to index"),
39650
39667
  limit: z79.number().min(1).max(50).optional().describe("Max results"),
39651
- mode: z79.enum(["search", "summary", "list"]).optional().describe("search/summary/list")
39668
+ mode: z79.enum(["search", "summary", "list"]).optional().describe("search/summary/list"),
39669
+ maxDepth: z79.number().int().min(1).max(MAX_INDEX_MAX_DEPTH).optional().describe(
39670
+ `Max directory depth to index (default: ${String(DEFAULT_INDEX_MAX_DEPTH)}, clamped to ${String(MAX_INDEX_MAX_DEPTH)})`
39671
+ )
39652
39672
  };
39653
- const description = "Cross-file ripgrep-style search across the working directory. Builds an in-memory symbol index and ranks matches with relevance scoring. Use when you need usages of a symbol or pattern across MANY files. For the AST of a single file, use `extract_symbols` instead. Modes: search (find by keyword), summary (per-file overview), list (all indexed files).";
39673
+ const description = "Cross-file search across an index of declared symbol NAMES in the working directory \u2014 declarations only (functions, classes, methods, interfaces, types); NOT usages, call-sites, comments, or string/text content. Builds an in-memory symbol index and ranks matches with relevance scoring. Use when you need to find where a symbol is DECLARED across MANY files. For the AST of a single file, use `extract_symbols` instead. Modes: search (find by keyword), summary (per-file overview), list (all indexed files).";
39654
39674
  const secureHandler = createSecureHandler(searchCodebaseHandler, {
39655
39675
  toolName: "search_codebase",
39656
39676
  rateLimiter: deps.rateLimiter,
@@ -39735,7 +39755,7 @@ function registerExtractSymbolsTool(server, deps) {
39735
39755
  filePath: z80.string().min(1).max(500).describe("Path to TypeScript/JavaScript file"),
39736
39756
  mode: z80.enum(["index", "full"]).optional().describe("index (default): names+lines. full: includes source text")
39737
39757
  };
39738
- const description = "Parse a SINGLE TypeScript/JavaScript file with tree-sitter and return its structural AST symbols. Use when you need the structure of one file \u2014 function/class/method/interface/type definitions. Not a cross-file search; for that use `search_codebase`. Output is 80%+ smaller than reading the full file.";
39758
+ const description = "Parse a SINGLE TypeScript/JavaScript file with the TypeScript compiler API and return its structural AST symbols. Use when you need the structure of one file \u2014 function/class/method/interface/type definitions. Not a cross-file search; for that use `search_codebase`. Output is 80%+ smaller than reading the full file.";
39739
39759
  const secureHandler = createSecureHandler(extractSymbolsHandler, {
39740
39760
  toolName: "extract_symbols",
39741
39761
  rateLimiter: deps.rateLimiter,
@@ -43904,7 +43924,7 @@ function createScaffoldStageWrapper() {
43904
43924
  }
43905
43925
  async function searchCodebaseForTask(task) {
43906
43926
  try {
43907
- const { CodebaseIndex: CodebaseIndex2 } = await import("./codebase-search-PWMWLU7O.js");
43927
+ const { CodebaseIndex: CodebaseIndex2 } = await import("./codebase-search-DKLEA4PR.js");
43908
43928
  const index = new CodebaseIndex2(process.cwd());
43909
43929
  const terms = task.toLowerCase().split(/\s+/).filter((w) => w.length > 4).slice(0, 3);
43910
43930
  if (terms.length === 0) return null;
@@ -51826,4 +51846,4 @@ export {
51826
51846
  shutdownFeedbackSubscriber,
51827
51847
  createEventBusBridge
51828
51848
  };
51829
- //# sourceMappingURL=chunk-DPV7TCNP.js.map
51849
+ //# sourceMappingURL=chunk-RLRAUL43.js.map