| 234 | } |
| 235 | |
| 236 | function getChangedFiles(lastCommit) { |
| 237 | const files = new Set(); |
| 238 | |
| 239 | // Committed changes since last scan — lastCommit passed as array arg, never shell-interpolated |
| 240 | if (lastCommit) { |
| 241 | const committed = gitExec(['diff', '--name-only', lastCommit, 'HEAD']); |
| 242 | if (committed) { |
| 243 | for (const f of committed.split('\n')) { |
| 244 | if (f.trim()) files.add(f.trim()); |
| 245 | } |
| 246 | } |
| 247 | } else { |
| 248 | // First run: last commit only |
| 249 | const firstRun = gitExec(['diff', '--name-only', 'HEAD~1', 'HEAD']); |
| 250 | if (firstRun) { |
| 251 | for (const f of firstRun.split('\n')) { |
| 252 | if (f.trim()) files.add(f.trim()); |
| 253 | } |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | // Unstaged changes |
| 258 | const unstaged = gitExec(['diff', '--name-only']); |
| 259 | if (unstaged) { |
| 260 | for (const f of unstaged.split('\n')) { |
| 261 | if (f.trim()) files.add(f.trim()); |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | // Staged changes |
| 266 | const staged = gitExec(['diff', '--name-only', '--cached']); |
| 267 | if (staged) { |
| 268 | for (const f of staged.split('\n')) { |
| 269 | if (f.trim()) files.add(f.trim()); |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | // Untracked files (new files not yet committed) |
| 274 | const untracked = gitExec(['ls-files', '--others', '--exclude-standard']); |
| 275 | if (untracked) { |
| 276 | for (const f of untracked.split('\n')) { |
| 277 | if (f.trim()) files.add(f.trim()); |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | // Never scan the watcher's own state and intake output: intake items quote |
| 282 | // raw marker lines, so self-scanning would mint new markers every scan. |
| 283 | return Array.from(files).filter((f) => !/^\.planning([\\/]|$)/.test(f)); |
| 284 | } |
| 285 | |
| 286 | // -- Marker scanning ---------------------------------------------------------- |
| 287 | |