Commit Graph

1173 Commits

Author SHA1 Message Date
Kirill Saksin 5ecf5be51e feat(opencode-plugin): add user-managed workflow mode (#667)
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.
2026-05-05 16:05:19 -07:00
Nehr 3b88415aeb Expose bypass clear reminder permission mode (#668)
* 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>
2026-05-05 12:27:16 -07:00
Michael Ramos 7c6dc142a4 Install Plannotator command skills under Codex home (#669)
* Install Plannotator skills under Codex home

* Keep shared Plannotator skills in agent scope

* Harden scoped skill migration
2026-05-05 12:18:18 -07:00
Michael Ramos ff2759b553 chore: bump version to 0.19.8
For provenance purposes, this commit was AI assisted.
v0.19.8
2026-05-05 00:26:16 -07:00
Cody Partington 87e92b9d86 Add bun link support for local CLI development (#656)
* 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>
2026-05-05 00:07:54 -07:00
Michael Ramos b47a4c58b3 feat(skills): add plannotator-setup-goal skill (#665)
* 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.
2026-05-04 23:54:41 -07:00
Michael Ramos 308e272b10 feat(ui): 49 themes with matched syntax highlighting + preview mode (#664)
* 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
2026-05-04 23:25:30 -07:00
Michael Ramos 0b4c499988 fix(remote): notify URL in remote mode for OpenCode and Pi (#663)
* 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.
2026-05-04 22:17:53 -07:00
Michael Ramos 61e44ef5d3 feat(marketing): include co-authors and community contributors on homepage (#662)
* 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.
2026-05-04 22:10:28 -07:00
Michael Ramos e0fb690039 fix(session-log): detect ghost sessions from /clear to resolve correct log (#661)
* 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.
2026-05-04 20:35:08 -07:00
Michael Ramos 84a0b434f9 fix(ui): smart resolution + existence-validation for code-file paths (#654)
* 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`.
2026-05-04 14:21:16 -07:00
Michael Ramos 5bc57fc5bd feat(ui): shortcut registry foundation (#652)
* 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>
2026-05-04 10:15:31 -07:00
Michael Ramos 525c8b05c1 fix(review): stop diff list re-rendering on every comment keystroke (#660)
* 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.
2026-05-04 09:50:47 -07:00
Michael Ramos 6fc60a6a84 chore: bump version to 0.19.7
For provenance purposes, this commit was AI assisted.
v0.19.7
2026-05-02 14:01:25 -07:00
Michael Ramos 86f95e13e3 fix(codex): use getPlanDeniedPrompt for Stop-hook deny path
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.
2026-05-02 13:59:46 -07:00
Andrei Ivanov a22a744749 Add Codex Stop-hook plan review (#577)
* feat: add codex stop hook plan review

* Install Codex plan review hooks

* Remove Codex manual test screenshots

* Update Codex plan mode docs

* Tighten Codex release readiness

* Preserve custom Codex hook wrappers

* ci: smoke test release artifacts

* ci: reduce release smoke flake risk

* fix: keep Codex last-message extraction to output text

* ci: poll release smoke servers on loopback

* ci: skip macOS release smoke jobs

---------

Co-authored-by: Michael Ramos <mdramos8@gmail.com>
2026-05-02 13:21:42 -07:00
Michael Ramos 2b144cbd2e feat(editor): auto-close left sidebar when TOC is empty (#651)
* 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.
2026-05-02 12:32:36 -07:00
Leonardo Reis a95db504a2 feat: add codex plannotator skills (#644) 2026-05-02 12:30:44 -07:00
Michael Ramos b0ed4be063 feat(review): right-click file tree row to copy path or filename (#650)
* 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.
2026-05-02 12:28:57 -07:00
Michael Ramos 6324a0c859 chore: bump version to 0.19.6
Co-authored-by: Eric Clemmons <15182+ericclemmons@users.noreply.github.com>

For provenance purposes, this commit was AI assisted.
v0.19.6
2026-05-01 18:38:10 -07:00
Michael Ramos 5aabace2ee feat(opencode): agent picker dropdown on Approve button (#648)
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
2026-05-01 18:05:50 -07:00
Michael Ramos a96e74c2f8 Make Pi browser review and annotation sessions async (#645)
* 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
2026-05-01 16:46:41 -07:00
Michael Ramos 4b8ffcf0f2 fix(ui): code file and linked doc resolution in annotate-last mode (#647)
* 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.
2026-05-01 16:29:28 -07:00
Michael Ramos 5aaac6bc88 chore: bump version to 0.19.5
For provenance purposes, this commit was AI assisted.
v0.19.5
2026-05-01 00:14:59 -07:00
Michael Ramos ddcca6e0ee feat(review): file comment button in all-files view + remap C/X shortcuts
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.
2026-05-01 00:04:14 -07:00
Michael Ramos 0fd9826c2c feat(review): all-files diff view with unified scrolling (#640)
* 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.
2026-04-30 23:39:43 -07:00
Michael Ramos 108a61b7d8 feat(ui): detect code file paths in backtick spans and bare prose (#639)
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.
2026-04-30 18:08:52 -07:00
Michael Ramos 9b7c39d2a4 fix(review): move hide-whitespace to server-side git diff -w (#635 follow-up) (#638)
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.
2026-04-30 17:23:55 -07:00
Michael Ramos 657cccaee0 fix(review): support non-ASCII and whitespace file paths in code review (#637)
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.
2026-04-30 13:01:27 -07:00
Michael Ramos 3636e9ed76 chore: bump version to 0.19.4
For provenance purposes, this commit was AI assisted.
v0.19.4
2026-04-30 11:09:41 -07:00
Michael Ramos c1f5a77d98 fix(review): hide-whitespace now ignores interior whitespace to match GitHub (#635)
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.
2026-04-30 11:04:11 -07:00
Michael Ramos a11bf802bd feat(ui): code file viewer with syntax highlighting and annotations (#634)
* 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
2026-04-30 10:42:36 -07:00
Michael Ramos c007d1c6a2 Use single file tree expand toggle (#633) 2026-04-30 07:27:33 -07:00
Michael Ramos b4b1d432c8 feat(review): diff display options — hide whitespace + quick-settings popover (#631)
* 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>
2026-04-29 23:51:17 -07:00
Michael Ramos b017b2992e fix(review): gutter hover button not rendering + update @pierre/diffs to 1.1.20 (#630)
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.
2026-04-29 21:25:03 -07:00
Michael Ramos a68463b6a4 feat(review): add "All files" diff type (#629)
* 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.
2026-04-29 16:43:06 -07:00
Michael Ramos 117c7c8ad1 chore: bump version to 0.19.3
For provenance purposes, this commit was AI assisted.
v0.19.3
2026-04-29 00:30:37 -07:00
Michael Ramos 64c845fd2e feat(feedback): configurable plan, annotation, and review feedback (#627)
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.
2026-04-29 00:06:05 -07:00
Orestis Ioannou b2eae468d7 feat(review): add configurable approval prompts (#561)
* 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>
2026-04-28 15:59:19 -07:00
Michael Ramos 93c844021d feat(review): hide/de-emphasize merged PRs in stacked PR selector (#626)
* 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.
2026-04-28 13:18:28 -07:00
Michael Ramos 8a5082d3ed chore: bump version to 0.19.2
For provenance purposes, this commit was AI assisted.
v0.19.2
2026-04-27 23:48:41 -07:00
Michael Ramos 03d4e8b214 fix: widen diff type dialog with 60/40 split layout and hover-to-zoom image
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.
2026-04-27 23:25:58 -07:00
Michael Ramos aaad89ee3f fix: re-show diff type setup dialog with clearer options and toolbar hint
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.
2026-04-27 23:15:19 -07:00
Michael Ramos fdc4bc4656 feat(plan,annotate): include source line numbers in exported feedback (#623)
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.
2026-04-27 23:13:52 -07:00
Michael Ramos 7ab2d8f868 fix: remove ghost dot on Options menu (new-settings-hint system)
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.
2026-04-27 22:47:02 -07:00
Michael Ramos bb404f8d14 feat: stacked PR review — PR switching, scope toggling, multi-PR posting (#620)
* 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.
2026-04-27 22:07:55 -07:00
Vincent Xiao 9a320220c4 docs: scrub Insert/Replace from docs to match shipped UI (#618)
* 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
2026-04-27 13:28:56 -07:00
Michael Ramos 33f409adc1 docs: clarify OpenCode plugin configuration
Clarify how to attach Plannotator options when OpenCode has multiple plugins, and link the landing page OpenCode tab to setup and migration docs.
2026-04-27 10:33:49 -07:00
Michael Ramos ef3172e339 Update README.md 2026-04-24 08:51:09 -07:00
Michael Ramos 88320a0d47 Update README.md 2026-04-24 08:49:54 -07:00