(args: z.infer<typeof FindReferencesArgs>, ctx: ToolContext)
| 264 | argsSchema = FindReferencesArgs; |
| 265 | |
| 266 | async execute(args: z.infer<typeof FindReferencesArgs>, ctx: ToolContext): Promise<ToolResult> { |
| 267 | const db = requireDB(); |
| 268 | if ('error' in db) return { content: db.error, isError: true }; |
| 269 | |
| 270 | const defs = db.findSymbolsByName(args.name); |
| 271 | const defKeys = args.include_definitions |
| 272 | ? new Set<string>() |
| 273 | : new Set(defs.map(d => `${d.file_path}:${d.start_line}`)); |
| 274 | |
| 275 | const escaped = args.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); |
| 276 | const limit = args.limit ?? 200; |
| 277 | |
| 278 | const rgArgs = ['-n', '--no-heading', '--word-regexp', '--max-count', String(limit + defs.length), '-e', escaped]; |
| 279 | if (args.language) rgArgs.push('--type', args.language); |
| 280 | rgArgs.push(ctx.cwd); |
| 281 | |
| 282 | const search = await runLineSearch(ctx.cwd, { |
| 283 | rgArgs, |
| 284 | regex: new RegExp(`\\b${escaped}\\b`), |
| 285 | language: args.language, |
| 286 | maxCount: limit + defs.length, |
| 287 | signal: ctx.signal, |
| 288 | }); |
| 289 | if ('error' in search) return { content: search.error, isError: true }; |
| 290 | |
| 291 | const lines = search.stdout.split('\n').filter(Boolean); |
| 292 | const refs: Array<{ file: string; line: number; preview: string }> = []; |
| 293 | for (const raw of lines) { |
| 294 | const firstColon = raw.indexOf(':'); |
| 295 | const secondColon = raw.indexOf(':', firstColon + 1); |
| 296 | if (firstColon === -1 || secondColon === -1) continue; |
| 297 | const file = raw.slice(0, firstColon); |
| 298 | const lineNum = parseInt(raw.slice(firstColon + 1, secondColon), 10); |
| 299 | if (!Number.isFinite(lineNum)) continue; |
| 300 | if (defKeys.has(`${file}:${lineNum}`)) continue; |
| 301 | refs.push({ file, line: lineNum, preview: raw.slice(secondColon + 1).trim().slice(0, 200) }); |
| 302 | if (refs.length >= limit) break; |
| 303 | } |
| 304 | if (refs.length === 0) { |
| 305 | const note = args.include_definitions |
| 306 | ? 'No references found at all' |
| 307 | : 'No references found (other than definitions)'; |
| 308 | return { content: `[NO_REFERENCES] ${note} for "${args.name}". The project may not be indexed (try /index).` }; |
| 309 | } |
| 310 | const byFile = new Map<string, typeof refs>(); |
| 311 | for (const r of refs) { |
| 312 | const rel = path.relative(ctx.cwd, r.file); |
| 313 | if (!byFile.has(rel)) byFile.set(rel, []); |
| 314 | byFile.get(rel)!.push(r); |
| 315 | } |
| 316 | const out: string[] = [`Found ${refs.length} reference${refs.length > 1 ? 's' : ''} to "${args.name}" across ${byFile.size} file${byFile.size > 1 ? 's' : ''}:`]; |
| 317 | for (const [rel, items] of byFile) { |
| 318 | out.push(`\n ${rel} (${items.length})`); |
| 319 | for (const it of items.slice(0, 30)) { |
| 320 | out.push(` :${it.line} ${it.preview}`); |
| 321 | } |
| 322 | if (items.length > 30) out.push(` ... [+${items.length - 30} more in this file]`); |
| 323 | } |
nothing calls this directly
no test coverage detected