Download packages/agent-core-v2/src/app/git/gitParsers.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 4.45 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/agent-core-v2/src/app/git/gitParsers.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/packages/agent-core-v2/src/app/git/gitParsers.ts
-
curl -L -o gitParsers.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/agent-core-v2/src/app/git/gitParsers.ts
4.45 kB
| import type { FsGitStatus, FsGitStatusResponse, FsPullRequest } from './git'; | |
| export function parsePorcelain( | |
| stdout: string, | |
| filter: ReadonlySet<string> | undefined, | |
| ): FsGitStatusResponse { | |
| const records = stdout.split('\0'); | |
| let branch = ''; | |
| let ahead = 0; | |
| let behind = 0; | |
| const entries: Record<string, FsGitStatus> = {}; | |
| for (let i = 0; i < records.length; i++) { | |
| const record = records[i]!; | |
| if (record.length === 0) continue; | |
| if (record.startsWith('## ')) { | |
| const parsed = parseBranchHeader(record.slice(3)); | |
| branch = parsed.branch; | |
| ahead = parsed.ahead; | |
| behind = parsed.behind; | |
| continue; | |
| } | |
| if (record.length < 4) continue; | |
| const xy = record.slice(0, 2); | |
| const wirePath = record.slice(3); | |
| if (xy.startsWith('R') || xy.startsWith('C')) { | |
| i++; | |
| } | |
| if (filter !== undefined && !filter.has(wirePath)) continue; | |
| const status = collapseXY(xy); | |
| entries[wirePath] = status; | |
| } | |
| return { branch, ahead, behind, entries, additions: 0, deletions: 0, pullRequest: null }; | |
| } | |
| export function parseNumstat(stdout: string): { | |
| additions: number; | |
| deletions: number; | |
| } { | |
| let additions = 0; | |
| let deletions = 0; | |
| for (const line of stdout.split('\n')) { | |
| if (line.length === 0) continue; | |
| const [addedText, deletedText] = line.split('\t'); | |
| additions += parseNumstatCount(addedText); | |
| deletions += parseNumstatCount(deletedText); | |
| } | |
| return { additions, deletions }; | |
| } | |
| function parseNumstatCount(value: string | undefined): number { | |
| if (value === undefined || value === '-') return 0; | |
| const n = Number.parseInt(value, 10); | |
| return Number.isFinite(n) && n > 0 ? n : 0; | |
| } | |
| function parseBranchHeader(rest: string): { | |
| branch: string; | |
| ahead: number; | |
| behind: number; | |
| } { | |
| if (rest.startsWith('HEAD (no branch)')) { | |
| return { branch: '', ahead: 0, behind: 0 }; | |
| } | |
| if (rest.startsWith('No commits yet on ')) { | |
| return { branch: rest.slice('No commits yet on '.length), ahead: 0, behind: 0 }; | |
| } | |
| let branch = rest; | |
| let ahead = 0; | |
| let behind = 0; | |
| const bracket = rest.indexOf(' ['); | |
| if (bracket >= 0) { | |
| branch = rest.slice(0, bracket); | |
| const sliced = rest.slice(bracket + 2, rest.length - 1); | |
| const aheadMatch = sliced.match(/ahead (\d+)/); | |
| const behindMatch = sliced.match(/behind (\d+)/); | |
| if (aheadMatch !== null) ahead = Number.parseInt(aheadMatch[1] ?? '0', 10) || 0; | |
| if (behindMatch !== null) behind = Number.parseInt(behindMatch[1] ?? '0', 10) || 0; | |
| } | |
| const dots = branch.indexOf('...'); | |
| if (dots >= 0) branch = branch.slice(0, dots); | |
| return { branch, ahead, behind }; | |
| } | |
| function collapseXY(xy: string): FsGitStatus { | |
| if (xy === '??') return 'untracked'; | |
| if (xy === '!!') return 'ignored'; | |
| const x = xy.charAt(0); | |
| const y = xy.charAt(1); | |
| const set = new Set([x, y]); | |
| if ( | |
| xy === 'DD' || | |
| xy === 'AU' || | |
| xy === 'UD' || | |
| xy === 'UA' || | |
| xy === 'DU' || | |
| xy === 'AA' || | |
| xy === 'UU' | |
| ) { | |
| return 'conflicted'; | |
| } | |
| if (set.has('D')) return 'deleted'; | |
| if (set.has('M') || set.has('T')) return 'modified'; | |
| if (set.has('R')) return 'renamed'; | |
| if (set.has('C')) return 'renamed'; | |
| if (set.has('A')) return 'added'; | |
| return 'clean'; | |
| } | |
| export function parsePullRequest(stdout: string): FsPullRequest | null { | |
| let raw: unknown; | |
| try { | |
| raw = JSON.parse(stdout); | |
| } catch { | |
| return null; | |
| } | |
| if (typeof raw !== 'object' || raw === null) return null; | |
| const record = raw as Record<string, unknown>; | |
| const number = record['number']; | |
| const url = record['url']; | |
| const state = record['state']; | |
| if (typeof number !== 'number' || !Number.isInteger(number) || number <= 0) return null; | |
| if (typeof url !== 'string' || !isSafeHttpUrl(url)) return null; | |
| if (typeof state !== 'string') return null; | |
| const normalized = state.toLowerCase(); | |
| if (normalized !== 'open' && normalized !== 'merged' && normalized !== 'closed') return null; | |
| return { number, state: normalized, url }; | |
| } | |
| function isSafeHttpUrl(value: string): boolean { | |
| if (hasControlChars(value)) return false; | |
| try { | |
| const url = new URL(value); | |
| return url.protocol === 'https:' || url.protocol === 'http:'; | |
| } catch { | |
| return false; | |
| } | |
| } | |
| function hasControlChars(value: string): boolean { | |
| for (const char of value) { | |
| const code = char.codePointAt(0) ?? 0; | |
| if (code <= 0x1f || code === 0x7f) return true; | |
| } | |
| return false; | |
| } | |