()
| 47 | const REVIEW_REF_PREFIX = "refs/devspace/review"; |
| 48 | |
| 49 | export function createReviewCheckpointManager(): ReviewCheckpointManager { |
| 50 | const states = new Map<string, WorkspaceReviewState>(); |
| 51 | |
| 52 | return { |
| 53 | async initializeWorkspace({ workspaceId, root }) { |
| 54 | const refs = reviewRefs(workspaceId); |
| 55 | const state: WorkspaceReviewState = { root, ...refs }; |
| 56 | states.set(workspaceId, state); |
| 57 | |
| 58 | try { |
| 59 | const eligibility = await getGitEligibility(root); |
| 60 | if (!eligibility.ok || !eligibility.gitRoot) { |
| 61 | state.diagnostic = eligibility.message ?? "show_changes requires a Git workspace in this version."; |
| 62 | return; |
| 63 | } |
| 64 | |
| 65 | state.gitRoot = eligibility.gitRoot; |
| 66 | const commit = await createWorkingTreeSnapshot(eligibility.gitRoot); |
| 67 | await git(eligibility.gitRoot, ["update-ref", state.openRef, commit]); |
| 68 | await git(eligibility.gitRoot, ["update-ref", state.baselineRef, commit]); |
| 69 | } catch (error) { |
| 70 | state.diagnostic = error instanceof Error ? error.message : String(error); |
| 71 | } |
| 72 | }, |
| 73 | |
| 74 | async reviewChanges({ workspaceId, root, since = "last_shown", markReviewed = true }) { |
| 75 | let state = states.get(workspaceId); |
| 76 | if (!state) { |
| 77 | await this.initializeWorkspace({ workspaceId, root }); |
| 78 | state = states.get(workspaceId); |
| 79 | } |
| 80 | |
| 81 | if (!state?.gitRoot) { |
| 82 | throw new Error(state?.diagnostic ?? "show_changes requires a Git workspace in this version."); |
| 83 | } |
| 84 | |
| 85 | const baselineRef = since === "workspace_open" ? state.openRef : state.baselineRef; |
| 86 | const baseline = (await git(state.gitRoot, ["rev-parse", "--verify", `${baselineRef}^{commit}`])).stdout.trim(); |
| 87 | const current = await createWorkingTreeSnapshot(state.gitRoot); |
| 88 | const patch = (await git(state.gitRoot, ["diff", "--binary", "--no-color", baseline, current], { |
| 89 | maxBuffer: 50 * 1024 * 1024, |
| 90 | })).stdout; |
| 91 | const numstat = (await git(state.gitRoot, ["diff", "--numstat", "-z", baseline, current], { |
| 92 | maxBuffer: 50 * 1024 * 1024, |
| 93 | })).stdout; |
| 94 | const files = parseNumstat(numstat); |
| 95 | const summary = summarizeFiles(files); |
| 96 | |
| 97 | if (markReviewed) { |
| 98 | await git(state.gitRoot, ["update-ref", state.baselineRef, current]); |
| 99 | } |
| 100 | |
| 101 | return { |
| 102 | result: |
| 103 | summary.files === 0 |
| 104 | ? `No changes since ${since === "workspace_open" ? "workspace open" : "last shown changes"}.` |
| 105 | : `Changed ${summary.files} ${summary.files === 1 ? "file" : "files"} (+${summary.additions} -${summary.removals}).`, |
| 106 | summary, |
no outgoing calls
no test coverage detected