* Handle codegraph_files - get project file structure from the index
(args: Record<string, unknown>)
| 6506 | * Handle codegraph_files - get project file structure from the index |
| 6507 | */ |
| 6508 | private async handleFiles(args: Record<string, unknown>): Promise<ToolResult> { |
| 6509 | const cg = this.getCodeGraph(args.projectPath as string | undefined); |
| 6510 | const pathFilter = args.path as string | undefined; |
| 6511 | const pattern = args.pattern as string | undefined; |
| 6512 | const format = (args.format as 'tree' | 'flat' | 'grouped') || 'tree'; |
| 6513 | const includeMetadata = args.includeMetadata !== false; |
| 6514 | const maxDepth = args.maxDepth != null ? clamp(args.maxDepth as number, 1, 20) : undefined; |
| 6515 | |
| 6516 | // Get all files from the index |
| 6517 | const allFiles = cg.getFiles(); |
| 6518 | |
| 6519 | if (allFiles.length === 0) { |
| 6520 | return this.textResult('No files indexed. Run `codegraph index` first.'); |
| 6521 | } |
| 6522 | |
| 6523 | // Filter by path prefix. Stored paths are project-relative POSIX (e.g. |
| 6524 | // "src/foo.ts"), but agents commonly pass project-root variants like "/", |
| 6525 | // ".", "./", "" or Windows-style "src\foo" — and prefixes with leading |
| 6526 | // "/", "./" or "\". Normalize all of those before matching so the agent |
| 6527 | // gets results instead of falling back to Read/Glob (see #426). |
| 6528 | const normalizedFilter = pathFilter |
| 6529 | ? pathFilter |
| 6530 | .replace(/\\/g, '/') |
| 6531 | .replace(/^(?:\.?\/+)+/, '') |
| 6532 | .replace(/^\.$/, '') |
| 6533 | .replace(/\/+$/, '') |
| 6534 | : ''; |
| 6535 | let files = normalizedFilter |
| 6536 | ? allFiles.filter(f => f.path === normalizedFilter || f.path.startsWith(normalizedFilter + '/')) |
| 6537 | : allFiles; |
| 6538 | |
| 6539 | // Filter by glob pattern |
| 6540 | if (pattern) { |
| 6541 | const regex = this.globToRegex(pattern); |
| 6542 | files = files.filter(f => regex.test(f.path)); |
| 6543 | } |
| 6544 | |
| 6545 | if (files.length === 0) { |
| 6546 | return this.textResult(`No files found matching the criteria.`); |
| 6547 | } |
| 6548 | |
| 6549 | // Format output |
| 6550 | let output: string; |
| 6551 | switch (format) { |
| 6552 | case 'flat': |
| 6553 | output = this.formatFilesFlat(files, includeMetadata); |
| 6554 | break; |
| 6555 | case 'grouped': |
| 6556 | output = this.formatFilesGrouped(files, includeMetadata); |
| 6557 | break; |
| 6558 | case 'tree': |
| 6559 | default: |
| 6560 | output = this.formatFilesTree(files, includeMetadata, maxDepth); |
| 6561 | break; |
| 6562 | } |
| 6563 | |
| 6564 | return this.textResult(this.truncateOutput(output)); |
| 6565 | } |
no test coverage detected