Registers submit_plan tool and slash commands without modifying prompts
or agent permissions. Fills the gap between manual (commands only) and
plan-agent (full automation) for users who want to manage prompts and
permissions themselves.
* Preserve truthful approval semantics for Claude plan bypass
Thread a clear-context reminder flag through approval decisions, expose a Claude Code-only approval entry that requests bypass mode, and keep the hook response honest by emitting a reminder instead of claiming context was cleared.
Constraint: Claude Code PermissionRequest hooks have no documented clearContext response field and bypassPermissions is only a request when the mode is available.
Rejected: adding a permission-mode enum or undocumented clearContext field | those would misrepresent hook capabilities and broaden the contract.
Confidence: high
Scope-risk: moderate
Directive: Do not claim Plannotator clears context until Claude Code documents a hook field for that behavior; keep reminder copy truthful.
Tested: bun test packages/server apps/hook; bun test packages/editor/wideMode.test.ts packages/ui/hooks/useAgentSettings.test.ts; bun test; bun run build:review; bun run build:hook; bun run typecheck; git diff --check --cached.
Not-tested: Browser warning-dialog replay and interactive Claude Code smoke test.
Co-authored-by: OmX <omx@oh-my-codex.dev>
* Keep approval payloads truthful across Claude Code and Pi
Constraint: Claude Code hooks cannot clear context directly; the new action remains a truthful reminder plus bypass-mode approval.
Rejected: Sending OpenCode agent-switch state for Claude Code | it leaked build (?) UI state and misleading payload fields.
Confidence: high
Scope-risk: narrow
Directive: Keep OpenCode agent switching gated to opencode-origin approval payloads.
Tested: bun run typecheck; bun test; bun run build:review; bun run build:hook
Not-tested: Manual browser click-through of the Claude Code dropdown.
* Expose the clear-context reminder permission default
Constraint: Claude Code hooks can only emit a systemMessage nudge, not clear context directly.
Rejected: Server protocol changes | existing permissionMode plus clearContextNudge wire fields already support the behavior.
Confidence: high
Scope-risk: narrow
Directive: Keep bypassPermissionsClearReminder as a UI/storage-only synthetic mode that decomposes before /api/approve.
Tested: bun test packages/editor/approvalBody.test.ts; bun run --cwd apps/review build && bun run build:hook; bun run --cwd packages/ui typecheck; git diff --check
Not-tested: Root bun run typecheck could not run because tsc was not on PATH for the root script.
* Reject invalid persisted permission modes
Constraint: Stored browser values can be stale, corrupt, or from a future Plannotator build.
Rejected: Trusting the storage read with a type assertion | invalid values could flow into UI state and approval request construction.
Confidence: high
Scope-risk: narrow
Directive: Keep PermissionMode storage reads validated against PERMISSION_MODE_OPTIONS when adding or renaming modes.
Tested: bun test packages/editor/approvalBody.test.ts; bun run --cwd packages/ui typecheck; git diff --check
Not-tested: Full repo typecheck/build, outside this review-fix scope.
* Ensure approvals use the live permission setting
Settings persisted permission mode changes, but App kept the original mode in React state and used that stale value when building approval payloads. Push the Settings change back into App so the selected clear-reminder mode reaches the hook decision.
Constraint: Permission mode storage is cookie-backed while approval payload construction reads App state.\nRejected: Read permission cookies during approval | would couple approval construction to browser storage and duplicate Settings state.\nConfidence: high\nScope-risk: narrow\nDirective: Keep permission-mode writes synchronized with approval state when adding or changing modes.\nTested: bun test packages/editor/approvalBody.test.ts packages/ui/components/ApproveDropdown.test.tsx; bun x tsc --noEmit -p packages/ui/tsconfig.json; bun run build:hook; direct Playwright hook smoke verified bypassPermissionsClearReminder UI and hook payload.\nNot-tested: Live interactive Claude CLI session; direct hook/server simulation covered the wire output.
---------
Co-authored-by: OmX <omx@oh-my-codex.dev>
* chore: support bun link for local CLI development
* move Local Development section to end of README as Development
For provenance purposes, this commit was AI assisted.
---------
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
* feat(skills): add plannotator-setup-goal skill
Scaffolds reviewed Codex goal packages under goals/<slug>/ with
Plannotator annotation gates for each critical document.
* refactor(skills): merge acceptance into plan with narrative structure
Acceptance criteria now live inside plan.md. The plan template leads
with Solution Overview, Why This Approach, and How It Will Work before
slices and acceptance. Removes standalone acceptance.md from scaffold.
* feat(ui): expand theme system to 49 themes with matched syntax highlighting
Add 25 new themes sourced from shiki's bundled TextMate themes (Everforest,
Nord, Solarized, GitHub, One Dark Pro, Night Owl, Material, Vitesse, Vesper,
Poimandres, Ayu Dark, Houston, Laserwave, Andromeeda, Aurora X, Dark+/Light+,
Min, One Light, Plastic, Red, Slack, Snazzy Light, Vitesse Black). Each theme
was audited against the shiki source JSON to ensure correct color mapping.
Wire pierre/diffs syntax highlighting to match the active UI theme via a
SHIKI_THEME_MAP (35 of 49 themes get matched syntax token colors in diffs).
Add theme preview mode — a compact bottom-docked picker that lets users
browse themes while seeing the page update live behind it.
Settings panel enhancements:
- Syntax highlighting badge on themes with diff color matching
- Mode unavailability dimming (dark-only themes dim in light mode)
- Plannotator pinned first, rest alphabetical
* fix(themes): address review feedback — contrast, stale CSS, syntax mode safety
1. Material light: foreground #90A4AE → #546E7A (Blue Grey 600) for readable
UI text (~7:1 contrast on white). Primary-foreground #FAFAFA → #263238
for contrast on teal buttons.
2. DiffHunkPreview: add colorTheme to useEffect deps so unsafeCSS (bg/gutter
injection) recomputes when themes change, preventing stale mixed-theme
state in mounted tooltips during preview mode.
3. SHIKI_THEME_MAP: make mode-aware with null entries for modes lacking a
shiki counterpart. resolveSyntaxTheme() returns undefined when the current
mode has no match, so pierre falls back to its defaults rather than
applying dark token colors on a light background (fixes Tokyo Night light).
* fix(review): self-review cleanup — memo, double-call, escape key
- DiffHunkPreview: extract resolveSyntaxTheme to a variable instead of
calling it twice in the spread expression
- PierreDiffContent memo: compare syntaxTheme by dark/light string values
instead of reference equality (resolver returns new objects each call)
- Preview mode: add Escape key handler to return to full settings
* fix(ui): consume Escape event in theme preview to prevent leaking to page
* fix(remote): notify URL in remote mode for OpenCode and Pi (#551, #574)
PR #440 removed writeRemoteShareLink from OpenCode because the base64
share URL flooded the TUI — but no replacement was added. Remote users
got zero feedback about where the server was listening.
OpenCode: log the short localhost URL via client.app.log() in all onReady
callbacks (submit_plan, review, annotate, annotate-last, archive).
Pi: check isRemoteSession() directly instead of relying on the openBrowser
return value, which only sets isRemote when BROWSER env is unset. Users
running via Cursor (which sets BROWSER) now get the notification.
* fix(pi): use neutral URL notification for remote sessions
The previous wording ("Remote session. Open manually:") implied the
browser failed even when BROWSER env successfully opened it via port
forwarding. Use a neutral informational message instead.
* feat(marketing): include Co-authored-by contributors on homepage
Parse Co-authored-by trailers from commit messages and resolve emails
to GitHub profiles via /search/users, so co-authors of squash-merged
PRs appear in the contributors strip.
* feat(marketing): separate authors from community contributors
Split the contributors strip into two sections: "Built by" for commit
authors and co-authors, "With contributions from" for issue/discussion
participants. Also use REST /contributors for full commit history
coverage and parse GitHub noreply emails for co-author resolution.
* fix(marketing): paginate all commits and handle both noreply formats
Paginate through the full commit history via GraphQL so Co-authored-by
trailers in older commits are not missed. Also handle the login-only
noreply email format (login@users.noreply.github.com) in addition to
the id+login form.
* fix(session-log): detect ghost sessions from /clear to resolve correct log
After /clear, Claude Code creates a new session (new .jsonl file) but
never updates ~/.claude/sessions/<pid>.json — the metadata retains the
old sessionId. The ancestor-PID resolver would confidently return the
stale log, preventing fallthrough to mtime-based tiers.
Fix: after tier-1 matches a log that isn't the newest by mtime, check
whether the newer file's sessionId is registered in any metadata file.
If not, it's a "ghost" session from /clear — prefer it. If it IS
registered, it belongs to a concurrent session — keep the PID result.
Fixes#643
* test: fix flaky mtime ordering in ghost detection tests
Explicitly backdate the "older" file by 5 seconds instead of relying on
write order, which is non-deterministic when both files are created
within the same millisecond.
* fix(ui): smart resolution + existence-validation for code-file paths
The bare-prose / backtick path detector linkifies anything that looks
like a code path. Two failure modes regularly produce dead links: prose
abbreviations like `editor/App.tsx` (real file is
`packages/editor/App.tsx`) and references to files the plan proposes
but hasn't created yet. Both 404 on click with no UX cue.
Resolves abbreviated paths via a case-insensitive suffix-match against
a cached project walk (`resolveCodeFile` in `packages/shared/resolve-file.ts`),
mirroring what `resolveMarkdownFile` already does for markdown. The walk
is pre-warmed when the plan/annotate server boots and on every
`/api/doc` request, with a 30s TTL so newly-created files can resolve
mid-review. Storing the walk as a Promise makes the cache race-safe —
concurrent callers piggyback rather than starting a second walk.
A new `POST /api/doc/exists` endpoint takes a batch of candidate paths
and reports `found` / `ambiguous` / `missing` / `unavailable` per path.
On the frontend, `useValidatedCodePaths` extracts candidates from the
markdown on load and POSTs once. The renderer reads the result via
`CodePathValidationContext`: `found` opens directly with the resolved
absolute path, `ambiguous` opens a `CodeFilePicker` popover listing all
matches (common in monorepos where `App.tsx` exists in several
packages), `missing` demotes the link to plain code, and `unavailable`
falls back to the optimistic linkification we have today. While
validation is in flight, every detected path renders as a link, so
first paint is unchanged.
The detection itself gets a shape filter (`isPlausibleCodeFilePath`)
that hard-rejects shell brace expansion (`{a,b}`), glob wildcards, and
whitespace, while explicitly allowing `[` / `]` so Next.js dynamic
routes (`app/[slug]/page.tsx`) still resolve. The bare-prose regex moves
out of `InlineMarkdown.tsx` into `code-file.ts` so the renderer and the
new server-side extractor use the same source of truth, and the
extractor strips fenced code blocks, HTML comments, and URL ranges
before scanning so it only emits candidates the renderer would actually
paint.
Pi extension mirrors the Bun changes (handler upgrade, pre-warm,
`/api/doc/exists` route). When the popout's `/api/doc` request 404s the
dialog now surfaces "File not found in repo: <path>" instead of
silently swallowing the error.
Tests: `code-file.test.ts` extended with shape-filter and Next.js-route
cases; new `extract-code-paths.test.ts` covers extraction, dedup,
fenced/HTML/URL exclusion, and the URL-with-parens regression; new
`resolve-file.test.ts` covers the suffix-match strategy, leading `./`
handling, ambiguous results, and ignored-dir behavior.
* fix(ui): thread doc-base through code-path validator
Out-of-tree linked docs (and annotate-mode files outside cwd) reference
files relative to themselves. The validator was resolving against cwd
only, so those paths got marked missing and the renderer demoted them
to plain text — even though clicks still resolved correctly via base.
Also tightens the suffix-match's leading-segment strip so `../foo.ts`
no longer silently misresolves to an unrelated `foo.ts` in cwd.
Cleanup: delete unused extract-code-paths import in reference-handlers,
add the export entry to packages/shared so consumers don't rely on
Bun's lenient subpath resolution. Add TODO(security) comments at both
handleDocExists sites flagging that absolute paths bypass project-root
containment.
223 tests pass (3 new resolver cases for baseDir + ../ regression).
* refactor(editor): dedupe activeDocBaseDir; expand security TODO
Self-review fallout:
1. The doc-base expression `linkedDocHook.filepath ? dirname(...) :
imageBaseDir` lived in two places (click-time URL builder and Viewer
prop). If they drift, validator and click resolve against different
bases and we silently re-introduce the demote-correct-link bug.
Extract to a single useMemo.
2. The handleDocExists security TODO mentioned absolute paths in
`paths[]` but I just added `base` acceptance, which has the same
shape of leak (hostile sender supplies base=/secret/dir + relative
path). Both vectors flagged in one TODO, mirrored Bun + Pi.
223 tests pass; both builds clean.
* fix(ui): code-file popout shows real error; misc consistency
Review fallout:
- `CodeFilePopout` hardcoded "File not found in repo" regardless of
cause. The hook already captures the server's error string, so an
ambiguous-path 400 (which can happen if a user clicks an optimistic
link before validation completes) was surfacing as a misleading
not-found message. Render the actual `error` and only show the
planned/future-file caveat when the error matches "file not found".
- `InlineMarkdown` emitted demoted bare-prose paths as raw strings
while every other plain-text branch in `emitPlainTextWithBareUrls`
routes through `transformPlainText`. Cosmetic-only today since
paths rarely contain transformable content, but the divergence
invites copy-paste rot. Routed through the same helper.
- CLAUDE.md missed the new POST /api/doc/exists endpoint in both
Plan Server and Annotate Server tables. Added.
223 tests pass; both builds clean.
* fix(ui): demote paths the extractor excluded from validation
When the validator is ready but a candidate path has no entry in the
validated map, the extractor intentionally excluded it — e.g. inside
an HTML comment or fenced code block. The renderer was optimistically
linking these because gateCodePath returned 'link' for missing entries.
Found during manual testing: `<!-- packages/editor/App.tsx -->` inside
a paragraph (parser doesn't recognize HTML comments as block-level)
was rendered as a clickable link. The extractor correctly stripped the
comment, but the renderer's optimistic fallback overrode that.
Also adds manual test harness: tests/manual/path-detection/ with
sandbox setup + three launcher scripts (plan mode, annotate in-tree,
annotate out-of-tree) covering ~30 test cases.
223 tests pass; both builds clean.
* fix(ui): skip HTML comments in InlineMarkdown scanner
The parser doesn't recognize <!-- --> as block-level HTML, so comments
inside paragraphs fall through to InlineMarkdown. The scanner then
finds paths inside the comment text and linkifies them.
The previous gateCodePath fix (demote when not in validated map) didn't
help here because the same path appeared elsewhere in the document —
the map had an entry from the non-comment occurrence.
Fix: match <!-- ... --> at the top of the scanner loop and skip the
entire comment. HTML comments should be invisible per CommonMark spec.
* fix: handle unavailable variant in markdown resolve narrowing
The shared ResolveResult type gained an `unavailable` variant for code
files. The markdown resolver never returns it, but TS can't narrow
past it without an explicit guard. Both Bun and Pi handlers now guard
`not_found || unavailable` before accessing `result.path`.
* feat(ui): shortcut registry foundation
Salvages the foundation of #380 by @gjermundgaraba — the original PR sat
6 weeks waiting for review and was closed by the author. This lands the
engine, scope data, and auto-generated marketing docs page, deferring
per-component handler migration to follow-up PRs.
Layout:
- packages/ui/shortcuts/{core,runtime,index}.ts — engine + barrel
- packages/ui/shortcuts/plan-review/* — scopes for plan editor surfaces
- packages/ui/shortcuts/code-review/* — scopes for review editor surfaces
- packages/{editor,review-editor}/shortcuts.ts — per-app surface composition
- apps/marketing/src/{components,lib} — auto-generated docs page
App behavior is unchanged: existing ad-hoc keydown handlers keep running.
The registry is plumbing waiting for migration PRs to wire to it.
Co-authored-by: Gjermund Garaba <gjermund@garaba.net>
* feat(ui): add missing shortcuts; warn on hold-binding gap
Addresses code review on #652.
Adds five live shortcuts that were missing from the registry, so the
auto-generated /docs/reference/keyboard-shortcuts page is no longer
incomplete:
- Mod+P (print plan) — plan review + annotate
- Mod+B (toggle file tree) — code review (new "Layout" section)
- Mod+. (toggle review sidebar) — code review
- Mod+Shift+T (toggle demo tour, dev-only) — code review
- Mod+Shift+F (focus PR comments search) — new code-review/prComments scope
Also whitelists `.` as a valid normalized binding token in the registry
validator (keeps catching typos like Cmd/Ctrl while allowing legitimate
punctuation keys).
Hold-binding gap: the registry's `Alt hold` advertises a shortcut that
`useShortcutScope` cannot dispatch. Today this is harmless because the
existing `useInputMethodSwitch` hook handles it bespoke. Added warning
comments at the binding declaration and next to `useDoubleTapShortcuts`
so future migrators don't fall in. A `useHoldShortcuts` hook is the
proper fix and will land with the App.tsx migration that needs it.
* feat(ui): comprehensive shortcut registry coverage
Two-Sonnet inventory audit found 19 more user-facing shortcuts not in
the registry, on top of the 5 added in the previous commit. Adding all
of them so the auto-generated /docs/reference/keyboard-shortcuts page
ships honest and the in-app help modal (once migrated to read from the
registry) is comprehensive.
Plan / annotate surface (4 added):
- Escape closes image lightbox (Viewer)
- Mod+Enter saves annotation edit (AnnotationPanel)
- Escape cancels annotation edit (AnnotationPanel)
- Enter confirms image name (ImageAnnotator name field)
Code review surface (15 added):
- v / a in single-file diff mode (toggle viewed / stage)
- v, a, x, z, c, [, ] in all-files (new AllFilesDiff scope)
- Mod+Enter / Escape / Tab (new SuggestionModal scope)
- Mod+Enter / Escape (new AI Assistant scope: AITab + AskAIInput)
- Escape closes tour dialog (new TourDialog scope)
Engine: extended NAMED_TOKENS with `[` and `]` for the all-files
navigation bindings.
Tests updated for the new section ordering. App behavior unchanged —
this is pure declarative metadata. The existing ad-hoc keydown handlers
keep running; the registry is plumbing for the migration follow-up PRs.
* fix(ui): suggestion modal scope advertised non-existent submit shortcut
Self-review caught: SuggestionModal.tsx only has Escape (close) and Tab
(indent) handlers — no Mod+Enter. The Mod+Enter the inventory agent
referenced lives in AnnotationToolbar's suggested-code textarea, which
is already covered by reviewAnnotationToolbarShortcuts.submitComment.
Removed the bogus submit action from the scope. Renamed cancel→close
since the modal preserves suggestion state on Escape (it's not a
discard).
* fix(review): remove broken Cmd+Shift+C copy-diff shortcut
The handler at App.tsx:695 checked `e.key === 'c'` but Shift forces
`e.key` to uppercase `'C'`, so the shortcut never fired. The in-app
help modal also advertised it incorrectly as "Toggle comment mode".
Removing the broken shortcut entirely rather than fixing the case
mismatch — the toolbar's Copy button already does the same thing,
and there's no evidence anyone was relying on the keyboard shortcut.
Three deletions:
- packages/review-editor/App.tsx — handler block
- packages/review-editor/shortcuts.ts — copyDiff registry action
- packages/ui/components/KeyboardShortcuts.tsx — help modal entry
* chore(ui): address review nits
- F3: refresh AGENTS.md scope inventory (added scopes were missing)
- F5: getShortcutPlatform() now delegates to packages/ui/utils/platform
isMac instead of duplicating the regex+SSR guard
- F7: drop the stand-alone imageAnnotator.confirmName entry; the
behaviour is folded into the existing save action's hint so the
marketing docs page no longer renders two Enter rows with
different descriptions
- F8: remove the unused listenerOptions parameter from useShortcutScope
(no callers; inline objects in the dep array would have caused
re-render loops if anyone tried to use it)
TODOs left for the migration follow-up:
- F2: cross-scope arbitration — dispatchShortcutEvent should bail on
event.defaultPrevented; needs preventDefault to be claimed
consistently across scopes first
- F6: matchesKeyToken doesn't actually match the Space token; needs a
special case before any scope binds Space
App behaviour unchanged.
---------
Co-authored-by: Gjermund Garaba <gjermund@garaba.net>
* fix(review): stop diff list re-rendering on every comment keystroke (#659)
Move the useAnnotationToolbar hook + AnnotationToolbar/SuggestionModal
into a new ToolbarHost wrapper so per-keystroke form-state changes no
longer re-render AllFilesDiffView / DiffViewer (and therefore not every
LazyFileDiff and Pierre <FileDiff> beneath them). Parents talk to the
host through an imperative handle for selection/edit/token-click.
Verified with a console.count probe at the top of AllFilesDiffView:
typing 20+ chars no longer increments the render counter.
Out of scope (tracked in #659): React.memo on LazyFileDiff with stable
props, React Compiler, server-side prerendering of the review diff,
throttling capture-phase scroll listeners.
* fix(review): drop type cast on ToolbarHost mousemove handler
Widen useAnnotationToolbar's handleMouseMove signature to accept the
structural { clientX, clientY } shape — the implementation only reads
those two fields, and this lets ToolbarHost pass it directly to
window.addEventListener without an `as unknown as React.MouseEvent`
cast. Function parameters are contravariant, so any caller passing a
React.MouseEvent or native MouseEvent remains valid.
No runtime change.
planDenyFeedback was called but never imported — the import was removed
by #627 (configurable feedback pipeline) while #577 was in flight. Also
corrects the tool name from hardcoded "Stop" to getPlanToolName("codex").
For provenance purposes, this commit was AI assisted.
* feat(editor): auto-close left sidebar when TOC is empty
When the parsed document has no level 1-3 headings (common for annotate
and annotate-last sessions), keep the sidebar closed regardless of the
"Auto-open Sidebar" preference. The collapsed flag strip stays
available so users can still open it manually; manual re-open is not
overridden until the document changes again.
* fix(editor): hoist hasTocEntries above the effects that read it
Adding hasTocEntries to the tocEnabled sync effect's dep array while
the const itself was declared 300+ lines later threw a TDZ error at
render ("Cannot access 'Pe' before initialization" in the minified
bundle). Move the useMemo above the effects so dep-array evaluation
sees an initialized binding.
* feat(review): right-click file tree row to copy path or filename
* feat(review): add "Copy full path" to file tree context menu
Resolves to activeWorktreePath ?? agentCwd ?? gitContext.cwd; hidden in PR-review mode where files aren't on local disk.
Surface agent switching directly on the Approve button as a split
dropdown for OpenCode users, so they can pick which agent to switch
to (or disable switching) without hunting through Settings.
Also fix the "approved with notes" prompt to not say "Proceed with
implementation" when agent switching is disabled.
Closes#575, closes#114, closes#106, closes#159
* Make Pi browser review and annotation sessions async
* fix(pi-extension): harden async browser feedback
* fix(pi-extension): stop async browser sessions on failure
* fix(pi-extension): tighten async browser session helpers
* chore(pi-extension): remove stale await from message lookup
* fix(pi-extension): route stale browser feedback to active session
* fix(pi-extension): route feedback by active session identity
* fix(pi-extension): include helper modules in package
* fix(ui): code file and linked doc resolution in annotate-last mode
In annotate-last mode, imageBaseDir is set to "last-message" (a
non-path string), which gets sent as the base query param to /api/doc
and causes a 404. Guard both the code file popout and linked doc
URL builders to skip non-path imageBaseDir values and fall back to
projectRoot instead.
* fix(ui): drop projectRoot fallback to preserve server-side sandbox
Sending base=projectRoot bypasses the isWithinProjectRoot check in
/api/doc for code files (line 80) and skips containment entirely for
markdown (lines 40-57). Fall back to undefined instead, which omits
the base param and lets the server resolve paths against its own cwd
with sandbox checks intact — matching plan mode behavior.
Add file-scoped comment support to the all-files diff view via
onAddFileCommentForFile callback and CommentPopover. Remap keyboard
shortcuts: C now opens file comment, X collapses/expands files.
For provenance purposes, this commit was AI assisted.
* feat(review): all-files diff view with lazy mounting
Renders all diffs in a single scrollable pane using individual FileDiff
components with sticky file headers, collapse toggles, and viewport-based
lazy mounting via IntersectionObserver.
For provenance purposes, this commit was AI assisted.
* feat(review): convert all-files view to dock panel + fixes
- Register AllFilesDiffView as a proper dock panel so it coexists
with single-file tabs, AI chat, PR panels
- Remove viewMode toggle in favor of dock panel lifecycle
- Fix annotation toolbar positioning (mouse tracking)
- Fix annotation PR/scope filtering for stacked PRs
- Fix stale patch on file switch (deferred toolbar open)
- Fix conventional comments boolean comparison
- Fix SuggestionModal props to match DiffViewer pattern
- Wire reviewBase for correct hunk expansion context
- Fix pre-existing CSS bug on .has-annotations background
- Add Shift+V/A keyboard shortcuts for viewed/stage toggle
- Add [/] keyboard shortcuts for file-to-file scrolling
- Add keyboard shortcuts to settings panel
- Restore scroll-based file tree highlight via dock panel state
For provenance purposes, this commit was AI assisted.
* feat(review): sidebar toggles, keyboard shortcuts, click-drag selection
- Move sidebar tab buttons (annotations/AI/agents) to header bar
- Generalize useSidebar hook for both plan and review editors
- Remove internal tab state from ReviewSidebar, receive from parent
- Add file tree toggle button at far-left of header
- Add click-and-drag line selection in diff content area
- Change keyboard shortcuts from Shift+V/A to bare v/a/c
v = toggle viewed (+ collapse in all-files mode)
a = toggle git add/stage
c = collapse/expand file (all-files mode)
- Widen DiffOptionsPopover to prevent word-alt line break
- Fix Shift+V/A targeting wrong file in all-files mode
- Default to all-files panel on initial load
- Hide dock headers when all-files panel is the only panel
For provenance purposes, this commit was AI assisted.
* feat(review): click-and-drag line selection in diff content area
Extract shared selection helpers to utils/diffSelection.ts and add
mouseup handlers to both DiffViewer and AllFilesDiffView so users can
click and drag across code lines to open the annotation toolbar —
not just the gutter.
For provenance purposes, this commit was AI assisted.
* fix(review): address AI review findings
- Gate all-files keyboard shortcuts on isActive to prevent double-toggle
when a single-file tab is focused
- Fix getDiffSelection to iterate all diffs-container elements so
click-drag selection works on files beyond the first
- Clear needsInitialDiffPanel after opening all-files panel to prevent
forced reactivation on diff switches
- Clear activeFilePath and collapsedFiles when files change to prevent
stale state across PR/diff switches
- Add file.patch to LazyFileDiff fetch deps so file contents refetch
when the diff context changes
- Add toggleCollapse to keyboard effect dependency array
For provenance purposes, this commit was AI assisted.
* feat(review): add z to undo collapse with scroll-back
Press z to reopen the last collapsed file and scroll back to it.
Maintains a history stack so repeated z walks back through collapses.
Skips already-expanded entries. History clears on diff/PR switch.
For provenance purposes, this commit was AI assisted.
* fix(review): review findings + sidebar toggles + folder icon
- Fix stale context memo: add isAllFilesActive + handleAddAnnotationForFile
to useMemo deps
- Fix v handler: clear activeFilePath after collapse, only collapse when
marking viewed ON (not when toggling OFF)
- Fix click-drag copy regression: only trigger annotation toolbar for
multi-line selections, preserve single-line/word selection for Cmd+C
- Fix LazyFileDiff: clear stale file contents before refetch
- Clean up App.tsx v/a handler: early return when isAllFilesActive
- Add Cmd+B (file tree) and Cmd+. (sidebar) keyboard shortcuts
- Add toggle menu items with kbd badges to Options dropdown
- Replace file tree icon with simple folder
- Add z (undo collapse) to keyboard shortcuts panel
For provenance purposes, this commit was AI assisted.
* chore: remove dead code from iterations
- Remove unused FileTreeIcon import and component file
- Remove unused AnnotationsIcon component from ReviewHeaderMenu
- Remove unused onClose prop and close button from FileTree
For provenance purposes, this commit was AI assisted.
* fix(review): gate v/a on diff panel, keep annotation selection in all-files
- Track isDiffPanelActive so v/a shortcuts only fire when viewing a
single-file diff, not PR Comments/Summary/Agent panels
- Annotation selection stays in all-files mode instead of switching to
a single-file tab — lets the scroll-to-annotation effect handle it
For provenance purposes, this commit was AI assisted.
Inline code like `src/utils/foo.ts` and bare prose paths like
packages/server/index.ts are now clickable, opening the code file
popout viewer. Adds a subtle CSS shimmer and hover-to-primary
transition so users discover the interactivity without disrupting
the familiar inline-code look.
- Branch the backtick handler in InlineMarkdown to check isCodeFilePath()
- Extend emitPlainTextWithBareUrls() to also detect bare file paths
- Add isCodeFilePathStrict() requiring a / separator for bare prose
- Extract CodeFileIcon component to deduplicate the SVG across 3 sites
- Add code-file-link shimmer animation in theme.css with reduced-motion support
For provenance purposes, this commit was AI assisted.
The client-side approach from PR #635 normalized file contents before
diffing, which destroyed all indentation. Move whitespace handling to
the server by threading a `-w` flag through the git diff pipeline.
- Add `GitDiffOptions` to review-core.ts, inject `-w` in all 7 diff
type paths + untracked file diffs
- Thread options through git.ts → vcs.ts → review.ts / Pi server
- Read `hideWhitespace` from ~/.plannotator/config.json on startup so
the initial diff already respects the persisted preference
- Accept `hideWhitespace` in `/api/diff/switch`, echo in responses
- Client toggles trigger a lightweight server refetch that preserves
the active file (no panel reset)
- Handle edge case where current file disappears when `-w` removes
whitespace-only diffs
- Remove broken client-side parseDiffFromFile/normalize hack
- Update API docs in AGENTS.md
Closes the indentation bug reported by @zeroZshadow on PR #631.
For provenance purposes, this commit was AI assisted.
Pass `-c core.quotePath=false` to all git invocations so paths with
non-ASCII characters (Korean, Chinese, accented, etc.) and whitespace
are output as raw UTF-8 instead of octal escapes. Fixes the 0/0 files
issue when repos contain such paths.
Closes#628
For provenance purposes, this commit was AI assisted.
The diff library's ignoreWhitespace only trims leading/trailing whitespace,
so interior changes (alignment padding, extra spaces between tokens) still
showed as diffs. Normalize all whitespace before diffing to match git diff -w.
Also adds a real code change to the settings.ts demo so the file isn't fully
collapsed when hide-whitespace is on.
* feat(ui): extract reusable PopoutDialog, fix backdrop blur
The table popout lost its backdrop blur when we switched to modal={false}
to keep annotation toolbars interactive. Radix ignores Dialog.Overlay in
non-modal mode, so replace it with a plain div backdrop that works
regardless. Extract the dialog shell (backdrop, close button, portal,
annotation-aware dismiss) into a reusable PopoutDialog component for
upcoming use cases. Add a demo table to the dev plan content.
* Add read-only code file popout
* Add code file annotation support
* Fix code selection popover position
* fix(editor): restore global-attachment-only drafts
The save condition was broadened to persist drafts with only global
attachments, but the restore handler still skipped applying when both
annotation arrays were empty — silently dropping the attachments.
* fix(ui): import SelectedLineRange from @pierre/diffs base package
SelectedLineRange is not re-exported from @pierre/diffs/react —
import it from the base @pierre/diffs entry point instead.
* chore: add TODO for bot callback + code annotation limitation
* feat(review): add hide whitespace setting for diffs
Adds a "Hide Whitespace" toggle that suppresses whitespace-only changes
in diffs, matching GitHub's ?w=1 behavior. Uses parseDiffFromFile with
ignoreWhitespace when full file contents are available. Includes demo
data with a whitespace-heavy file (settings.ts) and a vite dev server
plugin to serve file contents in demo mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(review): add quick-settings popover on file header
Adds a gear icon to the file header that opens a compact Radix popover
with all diff display options (style, overflow, indicators, inline diff,
line numbers, background, hide whitespace). Provides quick access to
settings without opening the full settings dialog. Exports option
constants from Settings.tsx for reuse.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The renderHoverUtility callback was gating on getHoveredLine() during
React render, but Pierre updates hover state imperatively — so the
button was permanently null. Always render the button; call
getHoveredLine() only in the click handler. Also aligned button
sizing/colors with Pierre's own [data-utility-button] spec.
For provenance purposes, this commit was AI assisted.
* feat(review): add "All files" diff type for reviewing entire codebases
Adds an "all" diff type that diffs the empty tree against HEAD, showing
every tracked file as an addition. Solves the case where a repo has only
committed changes and no working tree diffs — previously there was no way
to launch a review at all.
For provenance purposes, this commit was AI assisted.
* fix(review): add "All files" to Settings panel diff type options
The Settings panel (gear icon) had its own DEFAULT_DIFF_TYPE_OPTIONS list
that was missing the new "all" diff type, so users couldn't select or see
it as the default from the settings UI.
For provenance purposes, this commit was AI assisted.
* fix(review): rename "All files" to "All files (HEAD)" for clarity
Makes it clear this mode shows committed state at HEAD, not the working
tree. Updated tooltip to distinguish from "Committed changes" (which
diffs against a base branch).
For provenance purposes, this commit was AI assisted.
Unified feedback pipeline for all plan approvals, plan denials, annotation
feedback, and review suffixes. Users customize messages via config.json with
{{variable}} template interpolation and per-runtime overrides.
Closes#624
Co-authored-by: Aviad Shiber <aviadshiber@users.noreply.github.com>
For provenance purposes, this commit was AI assisted.
* feat(review): add configurable approval prompts
Let users override the agent message Plannotator sends after approving a code review. Keep the existing behavior by default while supporting runtime-specific overrides in ~/.plannotator/config.json.
* fix(shared): export prompts helper
Expose the new shared prompts module through @plannotator/shared so Bun can resolve it during hook builds and CI.
* fix: add Gemini CLI to agent origin detection chain
Gemini CLI sets GEMINI_CLI=1 in the environment. Add it to the
detectedOrigin chain so runtime-specific prompt overrides work
on all paths (review, annotate, plan), not just plan review.
For provenance purposes, this commit was AI assisted.
---------
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
* feat(review): hide/de-emphasize merged PRs in stacked PR selector (#625)
Fetch PR state from GitHub GraphQL and surface it in the stack tree popover.
Adds a cookie-persisted "Hide merged" toggle: when on, merged nodes are removed
with a summary count; when off, they render dimmed with strikethrough and a
"merged" badge. Also reduces tree indentation to 2px/level to prevent overflow
on deep stacks.
Closes#625
For provenance purposes, this commit was AI assisted.
* feat(review): add hide-merged toggle to PR selector dropdown
Reuses the same micro-toggle pattern from the stack tree popover.
Adds a headerContent slot to SearchableSelect and filters merged PRs
from the list when the toggle is on. Separate cookie persistence from
the stack tree toggle.
For provenance purposes, this commit was AI assisted.
* fix(review): filter closed PRs alongside merged, improve count labels
Toggle ON now keeps only open PRs (filters both merged and closed).
Labels show "N open · N hidden" when filtering, "N open, N total" otherwise.
For provenance purposes, this commit was AI assisted.
Makes the image preview visible by splitting the dialog into options
(left) and a toolbar screenshot (right) with a CSS hover-to-zoom
effect so users can clearly see where the dropdown lives.
For provenance purposes, this commit was AI assisted.
Version-gates the cookie so all users see the dialog again. Reorders
options to match Settings, adds git command hints to descriptions, and
includes a screenshot showing the toolbar dropdown so users know they
can switch views at any time during a review.
For provenance purposes, this commit was AI assisted.
Each annotation in exported plan/annotate feedback now carries source
line numbers — single-line blocks show `(line N)`, multi-line blocks
show `(lines N–M)`. Diff-context and global comments stay lineless.
When the document was produced by Turndown/Jina (HTML file or URL),
the export carries a caveat that line numbers refer to the converted
markdown rather than the original source.
Key implementation details:
- extractFrontmatter() returns contentStartLine so block line numbers
account for stripped YAML headers
- blockEndLine() computes end lines per block type, with code blocks,
directives, and alerts accounting for stripped wrapper lines
- isConvertedSource() helper in url-to-markdown.ts centralizes the
source-type check across all entry points
- sourceConverted threaded from all CLIs through annotate servers
to the /api/plan payload; isConverted added to /api/doc responses
- Per-document conversion tracking in useLinkedDoc ensures the correct
flag is used when viewing linked HTML docs
Supersedes #621.
For provenance purposes, this commit was AI assisted.
The version-gated hint dot was permanently visible for users who never
matched the cookie version, with no way to dismiss it. Removes the
entire system: utility, state, prop plumbing, and pulsing dot JSX.
For provenance purposes, this commit was AI assisted.
* feat(shared): add isSameProject, PR stack types, and PR list provider
Extends PRRef/PRMetadata with defaultBranch, PRStackInfo, PRStackTree,
PRStackNode, PRDiffScope, and PRListItem types. Adds isSameProject()
for owner/repo validation on PR switching. Adds fetchPRStack() and
fetchPRList() dispatch functions (GitHub-only for now, GitLab stubs).
Includes 9 new tests for isSameProject covering GitHub, GitLab, and
cross-platform scenarios.
For provenance purposes, this commit was AI assisted.
* feat(shared): add GitHub PR stack tree walking and PR list fetching
Implements fetchGhPRStack() which walks up/down the PR stack via
GraphQL, resolving numbers and titles for each node in the chain.
Collapses queryPRsByHead/queryPRsByBase into a single queryPRsByRef
helper. Adds fetchGhPRList() using gh pr list. Fixes GHE support
by removing hostnameArgs from fetchGhPRList (--repo already handles
GHE). Filters jq "null" string from defaultBranch detection.
For provenance purposes, this commit was AI assisted.
* feat(shared): fetch defaultBranch for GitLab MRs
Queries the project's default_branch via glab API so getPRStackInfo
can detect stacked MRs on GitLab. Best-effort — caught errors fall
back to undefined.
For provenance purposes, this commit was AI assisted.
* feat(shared): add PR stack detection and full-stack diff module
New pr-stack module with:
- getPRStackInfo(): detects stacked PRs from baseBranch vs defaultBranch
- getPRDiffScopeOptions(): generates layer/full-stack scope options
- runPRFullStackDiff(): computes diff from default branch to HEAD
- resolvePRFullStackBaseRef(): resolves origin/main or local main
- checkoutPRHead(): fetches and checks out a PR head in a worktree
- buildMinimalStackTree(): builds UI tree from stack info
Includes 13 tests covering ref resolution, branch fallbacks, and
GitLab ref formats.
For provenance purposes, this commit was AI assisted.
* feat(shared): add worktree pool for per-PR agent isolation
Creates a session-scoped pool of git worktrees — each PR visited
during a stacked review gets its own isolated checkout. Agents run
in their PR's worktree undisturbed by PR switches. Handles
deduplication of concurrent ensure() calls for the same PR.
Includes 11 tests covering caching, cross-repo restrictions, GitLab
ref formats, and cleanup.
For provenance purposes, this commit was AI assisted.
* feat(shared): add diffScope/prUrl to agent jobs, branch diff type
Adds prUrl and diffScope optional fields to AgentJobInfo so agent
findings carry the PR and scope context they were launched under.
Exports new pr-stack and worktree-pool modules from package.json.
Adds 'branch' to DefaultDiffType union for branch diff as default.
For provenance purposes, this commit was AI assisted.
* feat(ui): add PR annotation fields, Popover, and SearchableSelect
Extends CodeAnnotation with prUrl, prNumber, prTitle, prRepo, and
diffScope fields for stacked PR attribution. Adds shared Popover
wrapper around radix-ui. Adds SearchableSelect for filterable
dropdown lists (used by PR selector).
For provenance purposes, this commit was AI assisted.
* feat(ui): add branch diff as default option, new Git settings tab
Adds 'Branch' as a fourth default diff type option in both the
first-run dialog and settings panel. Moves the default diff type
setting from the Display tab to a new Git tab in review mode.
Updates config store validators to accept 'branch'.
For provenance purposes, this commit was AI assisted.
* feat(server): add prUrl/diffScope plumbing to agent jobs and prompts
Threads prUrl and diffScope through the agent job lifecycle so
findings carry the PR and scope they were generated under. Adds
full-stack prompt branch to codex-review and tour-review — when
in full-stack mode, the diff is inlined in the prompt instead of
telling the agent to run git diff. Re-exports isSameProject and
new PR provider functions from server/pr.ts.
For provenance purposes, this commit was AI assisted.
* feat(server): add stacked PR support to Bun review server
Adds PR switching, layer/full-stack scope toggling, PR list caching,
worktree pool integration, and multi-PR platform posting to the Bun
review server. Key additions:
- /api/pr-diff-scope: switch between layer and full-stack diffs
- /api/pr-list: cached PR list for the current repo
- /api/pr-switch: in-place navigation between PRs in a stack
- /api/pr-action: targetPrUrl support for multi-PR posting
- /api/file-content: full-stack branch for hunk expansion
- prSwitchCache/prStackTreeCache for session-scoped caching
- diffScope tagging on agent job completion
- Scope guard: returns 400 on full-stack diff failure instead of
overwriting the working diff with empty content
For provenance purposes, this commit was AI assisted.
* feat(ai): pass cwd to Claude agent SDK for worktree support
Forwards the working directory to the Claude agent provider so
agents run in the correct worktree when reviewing stacked PRs.
For provenance purposes, this commit was AI assisted.
* feat(pi): add stacked PR support to Pi server (Bun parity)
Mirrors all stacked PR features from the Bun server:
- PR switching, scope toggling, PR list, multi-PR posting
- prSwitchCache/prStackTreeCache with initial PR seeding
- diffScope/prUrl plumbing in agent jobs
- Worktree pool creation and lifecycle
- Full-stack file-content resolution matching Bun's guard structure
- targetPrUrl support on /api/pr-action
Hoists worktreePool declaration to outer scope in plannotator-browser
to fix TS18004 scoping error. Updates vendor.sh for new shared modules.
For provenance purposes, this commit was AI assisted.
* feat(hook): create worktree pool for PR review sessions
Creates a worktree pool when opening a PR review with --local,
seeding it with the initial PR's checkout. Integrates pool cleanup
into server shutdown. Passes the pool to startReviewServer for
agent isolation during PR switching.
For provenance purposes, this commit was AI assisted.
* feat(review-editor): add hooks for PR stack, context, and annotations
- useAnnotationFactory: stamps prUrl/prNumber/prTitle/prRepo/diffScope
onto annotations, only when viewing a stacked PR
- usePRStack: handles scope selection and PR switching with loading state
- usePRContext: adds URL-change detection to prevent stale-fetch race
when switching PRs (discards in-flight responses for previous PR)
For provenance purposes, this commit was AI assisted.
* feat(review-editor): add stacked PR UI components
- PRSelector: searchable dropdown for switching between PRs in a repo
- PRSwitchOverlay: loading animation during PR switch
- StackedPRLabel: stack tree popover with scope selector and PR navigation
- ReviewSubmissionDialog: multi-PR submission dialog with per-target
status, orphaned findings section with copy-as-markdown, and
partial failure retry
For provenance purposes, this commit was AI assisted.
* feat(review-editor): multi-PR export with heading hierarchy
Updates exportReviewFeedback for multi-PR sessions:
- Groups annotations by prUrl, then by file within each PR
- Uses proper heading hierarchy (## for files, ### for annotations
in multi-PR mode)
- Detects single-PR mismatch (annotations from a different PR than
the current view) and uses annotation-level PR context
- Adds diffScope labels per PR group when present
Includes 5 new tests: multi-PR headings, single-PR mismatch,
diffScope labels, and non-stacked annotation handling.
For provenance purposes, this commit was AI assisted.
* feat(review-editor): integrate stacked PR into sidebar, diff panel, and agents
- ReviewSidebar: groups annotations by PR in multi-PR sessions,
shows PR headers with annotation counts
- ReviewDiffPanel: filters annotations by prUrl and diffScope so
only matching annotations appear in the diff gutter
- ReviewStateContext: adds prDiffScope to shared review state
- ReviewAgentJobDetailPanel: shows diffScope in job detail
- PRSummaryTab: shows stack info in PR summary
- index.css: PR switch shimmer and overlay animations
For provenance purposes, this commit was AI assisted.
* feat(review-editor): wire stacked PR into main review app
Integrates all stacked PR features into the review editor:
- PR stack state management (prStackInfo, prStackTree, prDiffScope)
- applyPRResponse: shared handler for PR switch and scope toggle,
preserves active file index on scope changes
- Multi-PR platform posting via Promise.allSettled with parallel
requests, partial failure retry, and per-target status tracking
- ReviewSubmissionDialog replaces inline dialog JSX
- useAnnotationFactory stamps PR context onto annotations
- keepalive on /api/feedback to survive tab closure
- Proper try/catch/finally on handlePlatformAction
For provenance purposes, this commit was AI assisted.
* docs: add stacked PR review documentation
Updates AGENTS.md, code-review command docs, and AI code review
guide with stacked PR review capabilities.
For provenance purposes, this commit was AI assisted.
* fix(server): stamp prNumber/prTitle/prRepo on agent findings
Agent annotations only had prUrl and diffScope, missing prNumber,
prTitle, and prRepo. When agent findings were the only annotations
for a PR target in the submission dialog, the target rendered as
#0 with no title. Now resolves full PR context from prSwitchCache
at job completion and stamps all five fields. Both Bun and Pi.
For provenance purposes, this commit was AI assisted.
* feat(ui): rename diff options — "Committed" replaces "Branch" / "Current PR Diff"
Consolidates two confusing committed-diff options into one:
- Settings/first-run: "Committed" — "Everything you've committed on this branch"
- Mid-session switcher: "Committed changes" (replaces both "vs main" and "Current PR Diff")
Uses merge-base under the hood (matches GitHub PR behavior). Removes
the two-dot branch diff from the UI — it stays in the runtime DiffType
union for backwards compat. Old "branch" values in config/cookies are
silently upgraded to merge-base.
Git settings tab now uses radio cards with descriptions instead of
a cramped segmented control. First-run dialog descriptions rewritten
in plain language — no git commands.
For provenance purposes, this commit was AI assisted.
* fix(review-editor): rename client-side "PR Diff" labels to "Committed changes"
DiffTypePicker.tsx had a hardcoded "PR Diff" override for merge-base
when the base picker is present. exportFeedback.ts also used "PR Diff"
in export labels. Both now say "Committed changes" to match the
server-side label and settings UI.
For provenance purposes, this commit was AI assisted.
* fix(server): discover stack UI for root PRs targeting the default branch
Root PRs (baseBranch === defaultBranch) were excluded from stack
detection because getPRStackInfo returned null. Now the server
always fetches the stack tree in PR mode. If the tree reveals
descendant PRs, prStackInfo is retroactively set with source
"tree-discovered", enabling the stack UI, scope selector, and
PR navigation from the root of a stack.
Both Bun and Pi servers updated. Adds "tree-discovered" to the
PRStackInfo source union.
For provenance purposes, this commit was AI assisted.
* fix(review-editor): derive diff scope from annotations, not UI state
The export function now reads diffScope from annotations instead of
the prReviewScope parameter. Fixes two issues:
1. Agent job "Copy All" showed the wrong scope when the user switched
between layer/full-stack after launching the agent
2. Mixed-scope annotations produced a confusing "layer, full-stack"
comma-joined label instead of grouping by scope
Extracts renderScopedGroups helper for scope-aware grouping — used
by both single-PR and multi-PR export paths. When annotations share
one scope, it appears in the header. When mixed, annotations are
grouped under ## Layer / ## Full-stack headings.
Includes 4 new tests: uniform scope derivation, mixed scope grouping,
single scope header, and prReviewScope override prevention.
For provenance purposes, this commit was AI assisted.
* fix(server): add tree-discovered stack fallback to pr-switch handler
The initial-load path upgrades prStackInfo for root PRs when the
stack tree reveals descendants, but the pr-switch handler was missing
this logic. The server now sends correct prStackInfo after switching
to a root-of-stack PR. Both Bun and Pi.
Also removes stale prReviewScope dependency from agent job panel's
copyAllText useMemo.
For provenance purposes, this commit was AI assisted.
* fix: extract resolveStackInfo helper, fix stack UI on non-stacked PRs
Extracts the tree-discovered stack fallback into resolveStackInfo()
in pr-stack.ts — eliminates 4 copies of the same logic across Bun
startup, Bun pr-switch, Pi startup, and Pi pr-switch.
Fixes StackedPRLabel showing on every PR: the check now counts
non-default-branch nodes (> 1) instead of all nodes (> 1). Without
this, every PR showed a "Stack (1 PR)" popover because the tree
always has at least [defaultBranch, currentPR].
For provenance purposes, this commit was AI assisted.
* fix(review-editor): don't re-open already-succeeded PR tabs on retry
On partial failure retry, openUrls was pre-seeded with URLs from
previously succeeded targets, causing those PR pages to re-open
in the browser alongside newly succeeded ones. Now starts empty —
only URLs from the current attempt are opened.
For provenance purposes, this commit was AI assisted.
* refactor(review-editor): extract PR session state into usePRSession hook
Consolidates 5 independent useState calls (prMetadata, prStackInfo,
prStackTree, prDiffScope, prDiffScopeOptions) into a single
usePRSession hook with atomic updatePRSession callback.
Replaces two identical 5-line setter blocks (initial load and
applyPRResponse) with single updatePRSession calls. All ~60 consumer
sites unchanged — same variable names via destructuring.
For provenance purposes, this commit was AI assisted.
* docs: scrub Insert/Replace from docs to match shipped UI
The annotation toolbar ships with Delete, Comment, Quick label (⚡),
Looks good (👍), and Copy. AnnotationType only has DELETION, COMMENT,
and GLOBAL_COMMENT. Insertion/Replacement were removed in #400 but
several docs still describe them as toolbar options.
Updates 9 files (the 7 listed in #617 plus two additional pages with
the same drift):
- apps/marketing/src/content/docs/getting-started/quickstart.md
- apps/marketing/src/content/docs/getting-started/ui-settings.md
- apps/marketing/src/content/docs/commands/plan-review.md
- apps/marketing/src/content/docs/commands/annotate.md
- apps/marketing/src/content/docs/commands/code-review.md
- apps/marketing/src/content/blog/welcome.md
- apps/marketing/src/content/blog/plannotator-meets-pi.md
- apps/marketing/src/content/blog/sharing-plans-with-your-team.md
- apps/portal/ANNOTATE.md
Where it made sense, added a note that users can ask for replacements
or insertions inside a Comment — the agent handles it during revision.
Closes#617
* docs: include Looks good (👍) in toolbar enumerations