Skip to content

feat(review): add --no-worktree flag for in-place PR checkout#992

Open
TovRudyy wants to merge 2 commits into
backnotprop:mainfrom
TovRudyy:olek.rudyy/plannotator-no-worktree-flag
Open

feat(review): add --no-worktree flag for in-place PR checkout#992
TovRudyy wants to merge 2 commits into
backnotprop:mainfrom
TovRudyy:olek.rudyy/plannotator-no-worktree-flag

Conversation

@TovRudyy

@TovRudyy TovRudyy commented Jul 3, 2026

Copy link
Copy Markdown

Motivation

plannotator review <PR_URL> defaults to --local, which builds a second working tree with git worktree add --detach. On large repositories that is slow and disk-heavy (it materializes a full second copy of the tree).

The existing escape hatch, --no-local, avoids the worktree but disables all local inspection — it reviews from the platform-API diff only, so full-stack diffs, context expansion, code navigation, and AI review agents are all unavailable.

--no-worktree fills the gap.

What --no-worktree does

For a same-repo PR it reuses the current repository instead of a worktree:

  1. Refuses to run if the working tree is dirty (never clobbers uncommitted work).
  2. Records the current branch (or detached SHA).
  3. Fetches the base refs (so origin/<base> resolves for full-stack diffs), then fetches + git checkouts the PR head in place (via the existing checkoutPRHead helper).
  4. Hands the current repo to the review server as agentCwd with no worktree pool.
  5. Restores the original branch/HEAD when the session exits — graceful close and hard exit (the existing SIGINT/SIGTERM handlers route through process.exit(), which fires the restore).

Because agentCwd is set and there's no pool, the server's existing resolvePRLocalCwd / ensurePRLocalCwd fallbacks resolve to the current repo — so full-stack diff, file-content expansion, and code-nav keep working, just without the cost of git worktree add.

Behavior / precedence

  • Cross-repo PR (or not in a git repo): warns and falls back to the platform diff. Does not clone.
  • --no-local + --no-worktree: --no-local wins (no local checkout at all).
  • On any failure it warns and falls back to the remote diff — the base fetch only touches refs, and a failed checkout aborts atomically, so nothing leaks.

Runtime scope

The in-place behavior is implemented in the Claude Code runtime (apps/hook/server/index.ts). Per CLAUDE.md's "both runtimes must be updated" rule, the flag is not silently swallowed elsewhere: OpenCode and Pi reject --no-worktree with an error rather than accepting-and-ignoring it. This is documented in the CLI help and the shared parser JSDoc. (Neither of those runtimes has an entry-level worktree path to mirror; full parity can be a follow-up.)

Changes

  • packages/shared/review-args.ts — parse --no-worktree into noWorktree.
  • apps/hook/server/index.ts — in-place checkout branch (dirty-tree guard + save/restore ref + exit hook). The existing --local worktree block is untouched (now an else if).
  • apps/opencode-plugin/commands.ts, apps/pi-extension/index.ts — reject --no-worktree with an error.
  • apps/hook/server/cli.ts — document the flag and its runtime scope.
  • packages/shared/review-args.test.ts — parsing, --git combo, --no-local/--no-worktree precedence.
  • packages/shared/no-worktree-checkout.test.ts — new git-mechanics integration test.

No changes were needed in packages/server/review.ts — the agentCwd fallbacks already cover the pool-less path.

Testing

  • bun test packages/shared/review-args.test.ts — parser/precedence (10 tests).
  • bun test packages/shared/no-worktree-checkout.test.tsnew: a real-git offline fixture (a bare origin carrying refs/pull/<N>/head) proving the in-place mechanics: HEAD moves to the PR head with no worktree added, the original ref restores, a dirty tree is detected, and an unreachable ref fails without mutating HEAD (4 tests, stable across repeated runs).
  • bun test packages/shared/worktree-pool.test.ts — no regressions (18 tests).
  • tsc --noEmit on packages/shared, packages/server, packages/ai, and apps/pi-extension — 0 errors.

Live verification

Ran plannotator review --no-worktree <this PR> in a clone whose origin is backnotprop/plannotator: confirmed the log Checked out PR head in … (no worktree), git worktree list showed no new worktree, HEAD moved to the PR head, and the branch was restored on session exit.

`plannotator review <PR_URL>` defaults to `--local`, which creates a second
working tree via `git worktree add`. On huge repos that is slow and disk-heavy.
`--no-local` avoids it but disables all local inspection (platform-API diff
only — no full-stack diff, context expansion, code-nav, or agents).

`--no-worktree` fills the gap: for a same-repo PR it reuses the current
repository by fetching and checking the PR head out in place, then hands that
repo to the review server as agentCwd (no worktree pool). The existing
resolvePRLocalCwd/ensurePRLocalCwd fallbacks already use agentCwd, so full-stack
diff, file expansion, and code navigation keep working — without the worktree.

Because an in-place checkout mutates the working tree it is guarded: it refuses
to run on a dirty tree, remembers the original branch/HEAD, and restores it when
the session exits (graceful close and process exit). Cross-repo PRs (or running
outside a git repo) warn and fall back to the platform diff; `--no-local` takes
precedence over `--no-worktree`.

- packages/shared/review-args.ts: parse --no-worktree into noWorktree
- apps/hook/server/index.ts: in-place checkout branch + guard + restore-on-exit
- apps/hook/server/cli.ts: document the flag
- packages/shared/review-args.test.ts: cover parsing + precedence

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@TovRudyy TovRudyy marked this pull request as draft July 3, 2026 13:08
…s test

Review feedback: trim the line-by-line narration comments in the in-place
block (kept the top doc comment and the two terse inline notes).

Runtime scope: --no-worktree is implemented only in the Claude Code runtime.
Instead of silently accepting-and-ignoring the flag, OpenCode and Pi now reject
it with an error (per CLAUDE.md's "both runtimes must be updated"), and the
limitation is documented in the CLI help and the shared parser JSDoc.

Testing: add packages/shared/no-worktree-checkout.test.ts — a real-git offline
fixture (a bare origin carrying refs/pull/<N>/head) proving the in-place
mechanics: HEAD moves to the PR head with no worktree added, the original ref
restores, a dirty tree is detectable, and an unreachable ref fails without
mutating HEAD. Generous timeouts avoid cold-run flakiness.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@TovRudyy TovRudyy marked this pull request as ready for review July 3, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant