* Get recent git diff context (staged + unstaged changes). * Returns formatted string for injection, or empty string.
(cwd, maxLines = 100)
| 26 | * Returns formatted string for injection, or empty string. |
| 27 | */ |
| 28 | function getGitDiffContext(cwd, maxLines = 100) { |
| 29 | // Use execFileSync with arg arrays — never a shell — so the cwd path |
| 30 | // (which can contain spaces or unusual characters) cannot be misinterpreted. |
| 31 | const opts = { cwd, encoding: 'utf-8', timeout: 5000 }; |
| 32 | try { |
| 33 | execFileSync('git', ['rev-parse', '--git-dir'], { ...opts, timeout: 3000 }); |
| 34 | } catch { |
| 35 | return ''; |
| 36 | } |
| 37 | |
| 38 | let diff = ''; |
| 39 | try { |
| 40 | const unstaged = execFileSync('git', ['diff', '--stat', '--no-color'], opts).trim(); |
| 41 | if (unstaged) { |
| 42 | // Cap --stat output to 40 lines (large repos can have thousands of changed files) |
| 43 | const statLines = unstaged.split('\n'); |
| 44 | const cappedStat = statLines.length > 40 |
| 45 | ? statLines.slice(0, 40).join('\n') + `\n... (${statLines.length - 40} more files)` |
| 46 | : unstaged; |
| 47 | diff += `Unstaged changes:\n${sanitizeToolOutput(cappedStat)}\n\n`; |
| 48 | const fullDiff = execFileSync('git', ['diff', '--no-color'], opts); |
| 49 | const lines = fullDiff.split('\n').slice(0, maxLines); |
| 50 | diff += sanitizeToolOutput(lines.join('\n')); |
| 51 | if (fullDiff.split('\n').length > maxLines) { |
| 52 | diff += `\n... (${fullDiff.split('\n').length - maxLines} more lines)`; |
| 53 | } |
| 54 | } |
| 55 | } catch {} |
| 56 | |
| 57 | try { |
| 58 | const staged = execFileSync('git', ['diff', '--cached', '--stat', '--no-color'], opts).trim(); |
| 59 | if (staged && !diff.includes(staged)) { |
| 60 | diff += `\nStaged changes:\n${sanitizeToolOutput(staged)}\n`; |
| 61 | } |
| 62 | } catch {} |
| 63 | |
| 64 | try { |
| 65 | const lastCommit = execFileSync('git', ['log', '--oneline', '-1'], { ...opts, timeout: 3000 }).trim(); |
| 66 | if (lastCommit) { |
| 67 | diff += `\nLast commit: ${sanitizeToolOutput(lastCommit)}\n`; |
| 68 | } |
| 69 | } catch {} |
| 70 | |
| 71 | if (!diff.trim()) return ''; |
| 72 | return `\n\n--- Recent git changes ---\n${diff.trim()}\n`; |
| 73 | } |
| 74 | |
| 75 | module.exports = { shouldInjectGitContext, getGitDiffContext }; |
no test coverage detected