feat: add pr diff and checkout (task 0014)
All checks were successful
CI / test (pull_request) Successful in 42s

Add the two PR commands that touch content and the local worktree:

- `pr diff <n>` fetches the raw diff from the `.diff` endpoint (forcing a
  text response, which the JSON-defaulting client would otherwise discard),
  truncates at 4000 chars with separate `truncated`/`original_length` fields
  and a prepended `--full` suggestion; `--full` returns the raw diff.
- `pr checkout <n>` reads the PR head branch from the PR fetch and fetches
  `refs/pull/{n}/head` from origin (uniform for same-repo and fork PRs, ADR
  0011), three-cased on local branch state so re-checkout is idempotent and
  divergent local commits fail with `GIT_ERROR` rather than being discarded.

Introduces the `GIT_ERROR` mapping (`runGit`) carrying git's first stderr
line plus a remediation help line, and widens the PR 404 classifier so a
`.diff` path still resolves to `PR_NOT_FOUND`.
This commit is contained in:
2026-07-13 22:15:03 -04:00
parent ba5171bb59
commit 3db6d421d4
8 changed files with 675 additions and 9 deletions

View File

@@ -13,6 +13,11 @@ export interface FixtureRoute {
body?: unknown;
/** Name of a JSON file in test/fixtures to serve as the body. */
fixture?: string;
/**
* A verbatim, non-JSON response body (e.g. a raw `.diff`). Served as-is with a
* `text/plain` content type, bypassing the JSON encoding the other fields get.
*/
raw?: string;
}
export interface RecordedRequest {
@@ -96,6 +101,14 @@ export async function startFixtureServer(routes: FixtureRoute[]): Promise<Fixtur
);
return;
}
if (route.raw !== undefined) {
res.writeHead(route.status ?? 200, {
"content-type": "text/plain",
...route.headers,
});
res.end(route.raw);
return;
}
res.writeHead(route.status ?? 200, {
"content-type": "application/json",
...route.headers,