* Fetch untracked file names (no content reading). * Returns file paths only - they'll be displayed with a note to stage them. * * @param maxFiles Maximum number of untracked files to include
( maxFiles: number, )
| 332 | * @param maxFiles Maximum number of untracked files to include |
| 333 | */ |
| 334 | async function fetchUntrackedFiles( |
| 335 | maxFiles: number, |
| 336 | ): Promise<Map<string, PerFileStats> | null> { |
| 337 | // Get list of untracked files (excludes gitignored) |
| 338 | const { stdout, code } = await execFileNoThrow( |
| 339 | gitExe(), |
| 340 | ['--no-optional-locks', 'ls-files', '--others', '--exclude-standard'], |
| 341 | { timeout: GIT_TIMEOUT_MS, preserveOutputOnError: false }, |
| 342 | ) |
| 343 | |
| 344 | if (code !== 0 || !stdout.trim()) return null |
| 345 | |
| 346 | const untrackedPaths = stdout.trim().split('\n').filter(Boolean) |
| 347 | if (untrackedPaths.length === 0) return null |
| 348 | |
| 349 | const perFileStats = new Map<string, PerFileStats>() |
| 350 | |
| 351 | // Just record filenames, no content reading |
| 352 | for (const filePath of untrackedPaths.slice(0, maxFiles)) { |
| 353 | perFileStats.set(filePath, { |
| 354 | added: 0, |
| 355 | removed: 0, |
| 356 | isBinary: false, |
| 357 | isUntracked: true, |
| 358 | }) |
| 359 | } |
| 360 | |
| 361 | return perFileStats |
| 362 | } |
| 363 | |
| 364 | /** |
| 365 | * Parse git diff --shortstat output into stats. |
no test coverage detected