* Handle codegraph_files - get project file structure from the index
(args: Record<string, unknown>)
| 4049 | * Handle codegraph_files - get project file structure from the index |
| 4050 | */ |
| 4051 | private async handleFiles(args: Record<string, unknown>): Promise<ToolResult> { |
| 4052 | const cg = this.getCodeGraph(args.projectPath as string | undefined); |
| 4053 | const pathFilter = args.path as string | undefined; |
| 4054 | const pattern = args.pattern as string | undefined; |
| 4055 | const format = (args.format as 'tree' | 'flat' | 'grouped') || 'tree'; |
| 4056 | const includeMetadata = args.includeMetadata !== false; |
| 4057 | const maxDepth = args.maxDepth != null ? clamp(args.maxDepth as number, 1, 20) : undefined; |
| 4058 | |
| 4059 | // Get all files from the index |
| 4060 | const allFiles = cg.getFiles(); |
| 4061 | |
| 4062 | if (allFiles.length === 0) { |
| 4063 | return this.textResult('No files indexed. Run `codegraph index` first.'); |
| 4064 | } |
| 4065 | |
| 4066 | // Filter by path prefix. Stored paths are project-relative POSIX (e.g. |
| 4067 | // "src/foo.ts"), but agents commonly pass project-root variants like "/", |
| 4068 | // ".", "./", "" or Windows-style "src\foo" — and prefixes with leading |
| 4069 | // "/", "./" or "\". Normalize all of those before matching so the agent |
| 4070 | // gets results instead of falling back to Read/Glob (see #426). |
| 4071 | const normalizedFilter = pathFilter |
| 4072 | ? pathFilter |
| 4073 | .replace(/\\/g, '/') |
| 4074 | .replace(/^(?:\.?\/+)+/, '') |
| 4075 | .replace(/^\.$/, '') |
| 4076 | .replace(/\/+$/, '') |
| 4077 | : ''; |
| 4078 | let files = normalizedFilter |
| 4079 | ? allFiles.filter(f => f.path === normalizedFilter || f.path.startsWith(normalizedFilter + '/')) |
| 4080 | : allFiles; |
| 4081 | |
| 4082 | // Filter by glob pattern |
| 4083 | if (pattern) { |
| 4084 | const regex = this.globToRegex(pattern); |
| 4085 | files = files.filter(f => regex.test(f.path)); |
| 4086 | } |
| 4087 | |
| 4088 | if (files.length === 0) { |
| 4089 | return this.textResult(`No files found matching the criteria.`); |
| 4090 | } |
| 4091 | |
| 4092 | // Format output |
| 4093 | let output: string; |
| 4094 | switch (format) { |
| 4095 | case 'flat': |
| 4096 | output = this.formatFilesFlat(files, includeMetadata); |
| 4097 | break; |
| 4098 | case 'grouped': |
| 4099 | output = this.formatFilesGrouped(files, includeMetadata); |
| 4100 | break; |
| 4101 | case 'tree': |
| 4102 | default: |
| 4103 | output = this.formatFilesTree(files, includeMetadata, maxDepth); |
| 4104 | break; |
| 4105 | } |
| 4106 | |
| 4107 | return this.textResult(this.truncateOutput(output)); |
| 4108 | } |
no test coverage detected