export interface RepoStatus { branch: string /** Number of commits ahead of upstream (undefined if no upstream) */ ahead?: number /** Number of commits behind upstream (undefined if no upstream) */ behind?: number /** True when there are uncommitted changes */ dirty: boolean } /** * Parses `git status --short --branch` output into a RepoStatus. * * First line format: ## main...origin/main [ahead N, behind M] * Remaining lines: XY path (presence means dirty) */ export function parseGitStatus(output: string): RepoStatus { const lines = output.trim().split('\n').filter(Boolean) let branch = 'unknown' let ahead: number | undefined let behind: number | undefined let dirty = false for (const line of lines) { if (line.startsWith('## ')) { const rest = line.slice(3) // "No commits yet on main" or "HEAD (no branch)" const trackMatch = rest.match(/^([^.]+)\.\.\.(\S+)/) if (trackMatch) { branch = trackMatch[1] } else { branch = rest.split(' ')[0] } const aheadMatch = rest.match(/ahead (\d+)/) const behindMatch = rest.match(/behind (\d+)/) if (aheadMatch) ahead = parseInt(aheadMatch[1], 10) if (behindMatch) behind = parseInt(behindMatch[1], 10) } else { // Any non-header line means there are changes dirty = true } } return { branch, ahead, behind, dirty } }