export const GIT_SUMMARY_SCOPE = "current_project" as const; export interface GitSummaryCounts { changed: number; staged: number; dirty: number; untracked: number; conflicts: number; } export interface GitSummaryFile { path: string; repoPath: string; status: string; staged: boolean; dirty: boolean; untracked: boolean; conflict: boolean; } export interface GitSummaryProjectScope { scope: typeof GIT_SUMMARY_SCOPE; cwd: string; repoRoot: string | null; repoRelativePath: string | null; } export interface GitSummaryRepoResponse { kind: "repo"; project: GitSummaryProjectScope & { repoRoot: string; }; branch: string | null; mainBranch: string | null; hasChanges: boolean; hasConflicts: boolean; counts: GitSummaryCounts; changedFiles: GitSummaryFile[]; truncatedFileCount: number; } export interface GitSummaryNotRepoResponse { kind: "not_repo"; project: GitSummaryProjectScope; message: string; } export type GitSummaryResponse = | GitSummaryRepoResponse | GitSummaryNotRepoResponse; export function isGitSummaryResponse( value: unknown, ): value is GitSummaryResponse { if (!value || typeof value !== "object") return false; const response = value as Partial; if (response.kind !== "repo" && response.kind !== "not_repo") return false; if (!response.project || typeof response.project !== "object") return false; if (response.project.scope !== GIT_SUMMARY_SCOPE) return false; if (typeof response.project.cwd !== "string") return false; if (response.kind === "not_repo") { return typeof (response as GitSummaryNotRepoResponse).message === "string"; } const repo = response as Partial; if (typeof repo.project?.repoRoot !== "string") return false; if ( typeof repo.hasChanges !== "boolean" || typeof repo.hasConflicts !== "boolean" ) return false; if (!repo.counts || typeof repo.counts !== "object") return false; if (!Array.isArray(repo.changedFiles)) return false; if (typeof repo.truncatedFileCount !== "number") return false; return ( typeof repo.counts.changed === "number" && typeof repo.counts.staged === "number" && typeof repo.counts.dirty === "number" && typeof repo.counts.untracked === "number" && typeof repo.counts.conflicts === "number" ); }