(params: {
filePaths: string[]
cwd: string
fs: CodebuffFileSystem
/**
* Filter to classify files before reading.
* If provided, the caller takes full control of filtering (no gitignore check).
* If not provided, the SDK applies gitignore checking automatically.
*/
fileFilter?: FileFilter
})
| 12 | export type FileFilter = (filePath: string) => FileFilterResult |
| 13 | |
| 14 | export async function getFiles(params: { |
| 15 | filePaths: string[] |
| 16 | cwd: string |
| 17 | fs: CodebuffFileSystem |
| 18 | /** |
| 19 | * Filter to classify files before reading. |
| 20 | * If provided, the caller takes full control of filtering (no gitignore check). |
| 21 | * If not provided, the SDK applies gitignore checking automatically. |
| 22 | */ |
| 23 | fileFilter?: FileFilter |
| 24 | }) { |
| 25 | const { filePaths, cwd, fs, fileFilter } = params |
| 26 | // If caller provides a filter, they own all filtering decisions |
| 27 | // If not, SDK applies default gitignore checking |
| 28 | const hasCustomFilter = fileFilter !== undefined |
| 29 | |
| 30 | const result: Record<string, string | null> = {} |
| 31 | const MAX_FILE_BYTES = 10 * 1024 * 1024 // 10MB - skip reading entirely |
| 32 | const MAX_CHARS = 100_000 // 100k characters threshold |
| 33 | const numFmt = new Intl.NumberFormat('en-US') |
| 34 | const fmtNum = (n: number) => numFmt.format(n) |
| 35 | |
| 36 | for (const filePath of filePaths) { |
| 37 | if (!filePath) { |
| 38 | continue |
| 39 | } |
| 40 | |
| 41 | const resolvedPath = resolveFilePathWithinProject(cwd, filePath) |
| 42 | if (!resolvedPath) { |
| 43 | result[filePath] = FILE_READ_STATUS.OUTSIDE_PROJECT |
| 44 | continue |
| 45 | } |
| 46 | const { relativePath, fullPath } = resolvedPath |
| 47 | |
| 48 | // Apply file filter if provided |
| 49 | const filterResult = fileFilter?.(relativePath) |
| 50 | if (filterResult?.status === 'blocked') { |
| 51 | result[relativePath] = FILE_READ_STATUS.IGNORED |
| 52 | continue |
| 53 | } |
| 54 | const isExampleFile = filterResult?.status === 'allow-example' |
| 55 | |
| 56 | // If no custom filter provided, apply default gitignore checking |
| 57 | // (allow-example files skip gitignore since they need to bypass .env.* patterns) |
| 58 | if (!hasCustomFilter && !isExampleFile) { |
| 59 | const ignored = await isFileIgnored({ |
| 60 | filePath: relativePath, |
| 61 | projectRoot: cwd, |
| 62 | fs, |
| 63 | }) |
| 64 | if (ignored) { |
| 65 | result[relativePath] = FILE_READ_STATUS.IGNORED |
| 66 | continue |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | try { |
| 71 | // Safety check: skip reading files over 10MB to avoid OOM |
no test coverage detected