// @generated — DO NOT EDIT. Source: packages/shared/review-core.ts /** * Runtime-agnostic code-review core shared by Bun runtimes and Pi. * * Pi consumes a build-time copy of this file so its published package stays * self-contained while review diff logic remains sourced from one module. */ import { resolve as resolvePath } from "node:path"; export type DiffType = | "uncommitted" | "staged" | "unstaged" | "last-commit" | "branch" | "merge-base" | "all" | `worktree:${string}` | "p4-default" | `p4-changelist:${string}`; export interface DiffOption { id: string; label: string; } export interface WorktreeInfo { path: string; branch: string | null; head: string; } export interface AvailableBranches { local: string[]; remote: string[]; } export interface GitContext { currentBranch: string; defaultBranch: string; diffOptions: DiffOption[]; worktrees: WorktreeInfo[]; availableBranches: AvailableBranches; cwd?: string; vcsType?: "git" | "p4"; } export interface DiffResult { patch: string; label: string; error?: string; } export interface GitCommandResult { stdout: string; stderr: string; exitCode: number; } export interface ReviewGitRuntime { runGit: ( args: string[], options?: { cwd?: string; timeoutMs?: number }, ) => Promise; readTextFile: (path: string) => Promise; } export interface GitDiffOptions { hideWhitespace?: boolean; } export async function getCurrentBranch( runtime: ReviewGitRuntime, cwd?: string, ): Promise { const result = await runtime.runGit( ["rev-parse", "--abbrev-ref", "HEAD"], { cwd }, ); return result.exitCode === 0 ? result.stdout.trim() || "HEAD" : "HEAD"; } export async function getDefaultBranch( runtime: ReviewGitRuntime, cwd?: string, ): Promise { // Prefer the remote tracking ref (e.g. `origin/main`) so diffs run against // the upstream tip, not a potentially stale local copy. Only fall back to // a local ref when there's no remote configured at all. const remoteHead = await runtime.runGit( ["symbolic-ref", "refs/remotes/origin/HEAD"], { cwd }, ); if (remoteHead.exitCode === 0) { const ref = remoteHead.stdout.trim(); if (ref) { // `symbolic-ref` only tells us what origin/HEAD *points at* — it does // not guarantee that the target ref was actually fetched. In narrow // or partial clones the pointer can be set while the target is // missing, in which case a later `git diff origin/main..HEAD` would // error. Verify the target exists before trusting it. const verify = await runtime.runGit( ["show-ref", "--verify", "--quiet", ref], { cwd }, ); if (verify.exitCode === 0) return ref.replace("refs/remotes/", ""); } } const mainBranch = await runtime.runGit( ["show-ref", "--verify", "refs/heads/main"], { cwd }, ); if (mainBranch.exitCode === 0) return "main"; return "master"; } /** * Query the remote for its default branch via `ls-remote --symref`. Returns * `origin/` if the remote answers and the tracking ref exists locally, * otherwise `null`. Designed to run in the background at server startup — the * caller fires it with `.then()` and uses the result if/when it arrives. * * Timeout-guarded: if the network is slow or absent, the promise resolves * (with `null`) once the timeout fires. Never throws. */ export async function detectRemoteDefaultBranch( runtime: ReviewGitRuntime, cwd?: string, ): Promise { try { const lsRemote = await runtime.runGit( ["ls-remote", "--symref", "origin", "HEAD"], { cwd, timeoutMs: 5000 }, ); if (lsRemote.exitCode !== 0) return null; const match = lsRemote.stdout.match(/^ref:\s+refs\/heads\/(\S+)\s+HEAD/m); if (!match) return null; const remoteBranch = `origin/${match[1]}`; const refExists = await runtime.runGit( ["show-ref", "--verify", "--quiet", `refs/remotes/${remoteBranch}`], { cwd }, ); return refExists.exitCode === 0 ? remoteBranch : null; } catch { return null; } } export async function listBranches( runtime: ReviewGitRuntime, cwd?: string, ): Promise { // Emit `\t` so we can classify by ref prefix // without guessing from the short form — local branches can contain `/` // (e.g. `feature/foo`), so `name.includes("/")` would misclassify them. const result = await runtime.runGit( [ "for-each-ref", "--format=%(refname)\t%(refname:short)", "refs/heads", "refs/remotes", ], { cwd }, ); if (result.exitCode !== 0) return { local: [], remote: [] }; const local: string[] = []; const remote: string[] = []; for (const line of result.stdout.split("\n")) { const [fullRef, shortName] = line.split("\t"); if (!fullRef || !shortName) continue; if (shortName.endsWith("/HEAD")) continue; if (fullRef.startsWith("refs/heads/")) { local.push(shortName); } else if (fullRef.startsWith("refs/remotes/")) { remote.push(shortName); } } // Keep both local and remote refs — they can point to different commits // (stale local tracking branches are common) and users need to be able to // pick either explicitly. The picker groups them separately for clarity. local.sort(); remote.sort(); return { local, remote }; } /** * Pick a safe base branch. Trusts the caller verbatim if they supplied one, * otherwise falls back to the detected default. Shared by Bun (`review.ts`) * and Pi (`serverReview.ts`) so both runtimes behave identically. * * Why trust the caller: the UI picker only ever sends refs from the known * list, and external/programmatic callers may pass tags, SHAs, or refs under * non-`origin` remotes that we must not silently rewrite (a tag `release` is * not the same commit as a branch `origin/release`). Invalid refs surface as * git errors on the next diff call, which is better than silently producing * a patch against the wrong commit. */ export function resolveBaseBranch( requested: string | undefined, detected: string, ): string { return requested || detected; } export async function getWorktrees( runtime: ReviewGitRuntime, cwd?: string, ): Promise { const result = await runtime.runGit(["worktree", "list", "--porcelain"], { cwd }); if (result.exitCode !== 0) return []; const entries: WorktreeInfo[] = []; let current: Partial = {}; for (const line of result.stdout.split("\n")) { if (line.startsWith("worktree ")) { if (current.path) { entries.push({ path: current.path, head: current.head || "", branch: current.branch ?? null, }); } current = { path: line.slice("worktree ".length) }; } else if (line.startsWith("HEAD ")) { current.head = line.slice("HEAD ".length); } else if (line.startsWith("branch ")) { current.branch = line .slice("branch ".length) .replace("refs/heads/", ""); } else if (line === "detached") { current.branch = null; } } if (current.path) { entries.push({ path: current.path, head: current.head || "", branch: current.branch ?? null, }); } return entries; } export async function getGitContext( runtime: ReviewGitRuntime, cwd?: string, ): Promise { const [currentBranch, defaultBranch, availableBranches] = await Promise.all([ getCurrentBranch(runtime, cwd), getDefaultBranch(runtime, cwd), listBranches(runtime, cwd), ]); const diffOptions: DiffOption[] = [ { id: "uncommitted", label: "Uncommitted changes" }, { id: "staged", label: "Staged changes" }, { id: "unstaged", label: "Unstaged changes" }, { id: "last-commit", label: "Last commit" }, ]; // Always offer Branch diff / PR Diff when a default branch exists. The // older guard hid them when the reviewer was on the default branch (the // `vs ` diff from the default branch itself is always empty), but // the base picker now lets reviewers compare against any branch from any // branch, so there's no meaningless-by-construction option. Also: preserving // diff mode across worktree switches and Pi's `initialBase` can land the // reviewer on the default branch with branch/merge-base already active — the // old guard hid the active mode's option, trapping them. Unconditional // emission keeps the active option reachable in every flow. if (defaultBranch) { diffOptions.push({ id: "merge-base", label: "Committed changes" }); } diffOptions.push({ id: "all", label: "All files (HEAD)" }); const [worktrees, currentTreePathResult] = await Promise.all([ getWorktrees(runtime, cwd), runtime.runGit(["rev-parse", "--show-toplevel"], { cwd }), ]); const currentTreePath = currentTreePathResult.exitCode === 0 ? currentTreePathResult.stdout.trim() : null; return { currentBranch, defaultBranch, diffOptions, worktrees: worktrees.filter((wt) => wt.path !== currentTreePath), availableBranches, cwd, }; } async function getUntrackedFileDiffs( runtime: ReviewGitRuntime, srcPrefix = "a/", dstPrefix = "b/", cwd?: string, options?: GitDiffOptions, ): Promise { // git ls-files scopes to the CWD subtree and returns CWD-relative paths, // unlike git diff HEAD which always covers the full repo with root-relative // paths. Resolve the repo root so untracked files from the entire repo are // included and their paths match the tracked-diff output. const toplevelResult = await runtime.runGit( ["rev-parse", "--show-toplevel"], { cwd }, ); const rootCwd = toplevelResult.exitCode === 0 ? toplevelResult.stdout.trim() : cwd; const lsResult = await runtime.runGit( ["ls-files", "--others", "--exclude-standard"], { cwd: rootCwd }, ); if (lsResult.exitCode !== 0) return ""; const files = lsResult.stdout .trim() .split("\n") .filter((file) => file.length > 0); if (files.length === 0) return ""; const diffs = await Promise.all( files.map(async (file) => { const diffResult = await runtime.runGit( [ "diff", "--no-ext-diff", ...(options?.hideWhitespace ? ["-w"] : []), "--no-index", `--src-prefix=${srcPrefix}`, `--dst-prefix=${dstPrefix}`, "/dev/null", file, ], { cwd: rootCwd }, ); return diffResult.stdout; }), ); return diffs.join(""); } function assertGitSuccess( result: GitCommandResult, args: string[], ): GitCommandResult { if (result.exitCode === 0) return result; const command = `git ${args.join(" ")}`; const stderr = result.stderr.trim(); throw new Error( stderr ? `${command} failed: ${stderr}` : `${command} failed with exit code ${result.exitCode}`, ); } const WORKTREE_SUB_TYPES = new Set([ "uncommitted", "staged", "unstaged", "last-commit", "branch", "merge-base", "all", ]); export function parseWorktreeDiffType( diffType: string, ): { path: string; subType: string } | null { if (!diffType.startsWith("worktree:")) return null; const rest = diffType.slice("worktree:".length); const lastColon = rest.lastIndexOf(":"); if (lastColon !== -1) { const maybeSub = rest.slice(lastColon + 1); if (WORKTREE_SUB_TYPES.has(maybeSub)) { return { path: rest.slice(0, lastColon), subType: maybeSub }; } } return { path: rest, subType: "uncommitted" }; } export async function runGitDiff( runtime: ReviewGitRuntime, diffType: DiffType, defaultBranch: string = "main", externalCwd?: string, options?: GitDiffOptions, ): Promise { let patch = ""; let label = ""; let cwd: string | undefined = externalCwd; let effectiveDiffType = diffType as string; if (diffType.startsWith("worktree:")) { const parsed = parseWorktreeDiffType(diffType); if (!parsed) { return { patch: "", label: "Worktree error", error: "Could not parse worktree diff type", }; } cwd = parsed.path; effectiveDiffType = parsed.subType; } const wFlag = options?.hideWhitespace ? ["-w"] : []; try { switch (effectiveDiffType) { case "uncommitted": { const trackedDiffArgs = [ "diff", "--no-ext-diff", ...wFlag, "HEAD", "--src-prefix=a/", "--dst-prefix=b/", ]; const hasHead = (await runtime.runGit(["rev-parse", "--verify", "HEAD"], { cwd })) .exitCode === 0; const trackedPatch = hasHead ? assertGitSuccess( await runtime.runGit(trackedDiffArgs, { cwd }), trackedDiffArgs, ).stdout : ""; const untrackedDiff = await getUntrackedFileDiffs( runtime, "a/", "b/", cwd, options, ); patch = trackedPatch + untrackedDiff; label = "Uncommitted changes"; break; } case "staged": { const stagedDiffArgs = [ "diff", "--no-ext-diff", ...wFlag, "--staged", "--src-prefix=a/", "--dst-prefix=b/", ]; const stagedDiff = assertGitSuccess( await runtime.runGit(stagedDiffArgs, { cwd }), stagedDiffArgs, ); patch = stagedDiff.stdout; label = "Staged changes"; break; } case "unstaged": { const trackedDiffArgs = [ "diff", "--no-ext-diff", ...wFlag, "--src-prefix=a/", "--dst-prefix=b/", ]; const trackedDiff = assertGitSuccess( await runtime.runGit(trackedDiffArgs, { cwd }), trackedDiffArgs, ); const untrackedDiff = await getUntrackedFileDiffs( runtime, "a/", "b/", cwd, options, ); patch = trackedDiff.stdout + untrackedDiff; label = "Unstaged changes"; break; } case "last-commit": { const hasParent = await runtime.runGit( ["rev-parse", "--verify", "HEAD~1"], { cwd }, ); const args = hasParent.exitCode === 0 ? ["diff", "--no-ext-diff", ...wFlag, "HEAD~1..HEAD", "--src-prefix=a/", "--dst-prefix=b/"] : ["diff", "--no-ext-diff", ...wFlag, "--root", "HEAD", "--src-prefix=a/", "--dst-prefix=b/"]; const lastCommitDiff = assertGitSuccess( await runtime.runGit(args, { cwd }), args, ); patch = lastCommitDiff.stdout; label = "Last commit"; break; } case "branch": { // `--end-of-options` hardens against a caller-supplied `defaultBranch` // that starts with `-` being parsed as a git flag (e.g. `--output=...` // would redirect diff output to an attacker-chosen path). Same pattern // applied wherever user-controlled refs flow into a git argv. const branchDiffArgs = [ "diff", "--no-ext-diff", ...wFlag, "--src-prefix=a/", "--dst-prefix=b/", "--end-of-options", `${defaultBranch}..HEAD`, ]; const branchDiff = assertGitSuccess( await runtime.runGit(branchDiffArgs, { cwd }), branchDiffArgs, ); patch = branchDiff.stdout; label = `Changes vs ${defaultBranch}`; break; } case "merge-base": { const mergeBaseLookupArgs = ["merge-base", "--end-of-options", defaultBranch, "HEAD"]; const mergeBaseResult = assertGitSuccess( await runtime.runGit(mergeBaseLookupArgs, { cwd }), mergeBaseLookupArgs, ); const mergeBase = mergeBaseResult.stdout.trim(); const mergeBaseDiffArgs = [ "diff", "--no-ext-diff", ...wFlag, "--src-prefix=a/", "--dst-prefix=b/", "--end-of-options", `${mergeBase}..HEAD`, ]; const mergeBaseDiff = assertGitSuccess( await runtime.runGit(mergeBaseDiffArgs, { cwd }), mergeBaseDiffArgs, ); patch = mergeBaseDiff.stdout; label = `PR diff vs ${defaultBranch}`; break; } case "all": { // Diff from the empty tree to HEAD — shows every tracked file as an addition. const emptyTreeResult = await runtime.runGit(["hash-object", "-t", "tree", "/dev/null"], { cwd }); const emptyTree = emptyTreeResult.exitCode === 0 ? emptyTreeResult.stdout.trim() : "4b825dc642cb6eb9a060e54bf8d69288fbee4904"; const allDiffArgs = [ "diff", "--no-ext-diff", ...wFlag, "--src-prefix=a/", "--dst-prefix=b/", "--end-of-options", `${emptyTree}..HEAD`, ]; const allDiff = assertGitSuccess( await runtime.runGit(allDiffArgs, { cwd }), allDiffArgs, ); patch = allDiff.stdout; label = "All files"; break; } default: return { patch: "", label: "Unknown diff type" }; } } catch (error) { const raw = error instanceof Error ? error.message : String(error); // Git dumps its entire --help output on some failures; keep only the // first meaningful line so the UI doesn't vomit a wall of text. const firstLine = raw.split("\n").find((l) => l.trim().length > 0) ?? raw; const message = firstLine.length > 200 ? firstLine.slice(0, 200) + "…" : firstLine; return { patch: "", label: cwd ? "Worktree error" : `Error: ${diffType}`, error: message, }; } if (cwd) { const branch = await getCurrentBranch(runtime, cwd); label = branch && branch !== "HEAD" ? `${branch}: ${label}` : `${cwd.split("/").pop()}: ${label}`; } return { patch, label }; } export async function runGitDiffWithContext( runtime: ReviewGitRuntime, diffType: DiffType, gitContext: GitContext, options?: GitDiffOptions, ): Promise { return runGitDiff(runtime, diffType, gitContext.defaultBranch, gitContext.cwd, options); } export async function getFileContentsForDiff( runtime: ReviewGitRuntime, diffType: DiffType, defaultBranch: string, filePath: string, oldPath?: string, cwd?: string, ): Promise<{ oldContent: string | null; newContent: string | null }> { const oldFilePath = oldPath || filePath; let effectiveDiffType = diffType as string; if (diffType.startsWith("worktree:")) { const parsed = parseWorktreeDiffType(diffType); if (!parsed) return { oldContent: null, newContent: null }; cwd = parsed.path; effectiveDiffType = parsed.subType; } async function gitShow(ref: string, path: string): Promise { // `--end-of-options` hardens against user-supplied refs starting with `-`. const result = await runtime.runGit(["show", "--end-of-options", `${ref}:${path}`], { cwd }); return result.exitCode === 0 ? result.stdout : null; } async function readWorkingTree(path: string): Promise { const fullPath = cwd ? resolvePath(cwd, path) : path; return runtime.readTextFile(fullPath); } switch (effectiveDiffType) { case "uncommitted": return { oldContent: await gitShow("HEAD", oldFilePath), newContent: await readWorkingTree(filePath), }; case "staged": return { oldContent: await gitShow("HEAD", oldFilePath), newContent: await gitShow(":0", filePath), }; case "unstaged": return { oldContent: await gitShow(":0", oldFilePath), newContent: await readWorkingTree(filePath), }; case "last-commit": return { oldContent: await gitShow("HEAD~1", oldFilePath), newContent: await gitShow("HEAD", filePath), }; case "branch": return { oldContent: await gitShow(defaultBranch, oldFilePath), newContent: await gitShow("HEAD", filePath), }; case "merge-base": { const mbResult = await runtime.runGit(["merge-base", "--end-of-options", defaultBranch, "HEAD"], { cwd }); const mb = mbResult.exitCode === 0 ? mbResult.stdout.trim() : defaultBranch; return { oldContent: await gitShow(mb, oldFilePath), newContent: await gitShow("HEAD", filePath), }; } case "all": return { oldContent: null, newContent: await gitShow("HEAD", filePath), }; default: return { oldContent: null, newContent: null }; } } export function validateFilePath(filePath: string): void { if (filePath.includes("..") || filePath.startsWith("/")) { throw new Error("Invalid file path"); } } async function ensureGitSuccess( runtime: ReviewGitRuntime, args: string[], cwd?: string, ): Promise { const result = await runtime.runGit(args, { cwd }); if (result.exitCode !== 0) { throw new Error(result.stderr.trim() || `git ${args.join(" ")} failed`); } } export async function gitAddFile( runtime: ReviewGitRuntime, filePath: string, cwd?: string, ): Promise { validateFilePath(filePath); await ensureGitSuccess(runtime, ["add", "--", filePath], cwd); } export async function gitResetFile( runtime: ReviewGitRuntime, filePath: string, cwd?: string, ): Promise { validateFilePath(filePath); await ensureGitSuccess(runtime, ["reset", "HEAD", "--", filePath], cwd); } export function parseP4DiffType( diffType: string, ): { changelist: string | "default" } | null { if (diffType === "p4-default") return { changelist: "default" }; if (diffType.startsWith("p4-changelist:")) { return { changelist: diffType.slice("p4-changelist:".length) }; } return null; } export function isP4DiffType(diffType: string): boolean { return parseP4DiffType(diffType) !== null; }