612 Commits

Author SHA1 Message Date
Michael Ramos 12e3e2d35b chore: bump version to 0.19.16
For provenance purposes, this commit was AI assisted.
v0.19.16
2026-05-13 12:42:12 -07:00
Michael Ramos 95750ab657 feat: search-based code navigation with peek view (#711)
* feat: search-based code navigation with peek view (#694)

Add IDE-like code navigation to the review UI. Cmd/Ctrl+click a token
in a diff to find its definitions and references across the repo via
ripgrep, displayed in a VS Code-style peek panel below the diff.

Backend: bounded rg search with language-aware definition patterns
(TS/JS, Python, Go, Rust), ranked results (same file > changed files >
same directory), confidence labels, and graceful degradation when rg
is not installed. Both Bun and Pi servers implement the endpoints.

Frontend: Dockview peek panel with syntax-highlighted full-file preview
on the left and grouped reference list on the right. Clicking a
reference scrolls the preview; double-clicking an in-diff result
navigates to the file with a gold line flash.

Closes #694

* fix: resolve Pi server TypeScript errors for code-nav

Add missing spawn import, type the close callback parameter,
and use double-cast for parseBody → CodeNavRequest.

* fix: clear loading state on cached preview hits

Without this, clicking a cached file while a fetch is in-flight
leaves isLoading stuck true — the spinner hides the preview.

* feat: show pointer cursor on Cmd/Ctrl+hover for navigable tokens

Adds pn-token-nav class with thicker underline and pointer cursor
when hovering a token while holding the modifier key, signaling
the token is Cmd+clickable for code navigation.

* refactor: remove go-to-diff navigation from peek panel

Strip the in-diff badge, double-click-to-jump, highlightDiffLine
wiring, and onCodeNavGoToDiff from the peek panel. The peek view
is the primary interaction — jump-to-diff adds complexity without
clear value at this stage.

* chore: remove dead code from code-nav cleanup

Delete unused highlightDiffLine.ts, remove codeNavChangedFiles
and codeNavActiveSide from context and App.tsx, drop stale
extractChangedFiles import.

* chore: add code-nav endpoints to AGENTS.md, remove dead activeSide state

* fix: don't classify bare indented calls as definitions

Change the TS/JS method pattern from zero-or-more (*) to
one-or-more (+) declaration keywords, so plain calls like
startServer(config) are no longer misclassified as definitions.

* feat: show toast when code-nav is unavailable in platform-only PR mode

Instead of opening the peek panel and showing misleading "No results",
Cmd+click in non-local PR mode shows a brief toast explaining that
code navigation requires a local checkout.

* fix: strip hljs hardcoded background from code-nav preview

The highlight.js github-dark theme sets a fixed dark background
on all .hljs elements. Apply transparent override for the entire
peek preview so code inherits the active theme's background.
2026-05-13 12:24:37 -07:00
Michael Ramos 32e871415f chore: bump version to 0.19.15
For provenance purposes, this commit was AI assisted.
v0.19.15
2026-05-13 08:18:34 -07:00
Michael Ramos f2ab0f165d fix: Pi port=0 parity + update marketing docs
- Accept PLANNOTATOR_PORT=0 in Pi network.ts (matches Bun server)
- Add PLANNOTATOR_ORIGIN and PLANNOTATOR_VERIFY_ATTESTATION to env vars docs
- Fix port range from 1-65535 to 0-65535
- Add jj diff modes, commit-based base, and expanded API table to code review docs

For provenance purposes, this commit was AI assisted.
2026-05-13 07:24:36 -07:00
Michael Ramos 6db7ca9184 feat(review): pick a commit as the diff base (#709) (#723)
* feat(review): pick a commit as the diff base (#709)

Widens the base picker for `branch` and `merge-base` diff modes from
"branch only" to any commit-ish. `getGitContext` now returns the last
20 commits on HEAD; the picker shows them in a new "Recent commits"
group, and SHA-like or HEAD~N queries get a "Use … as base" affordance
for manual entry. Backend changes are minimal because git diff already
accepts any commit-ish on the left side of the range.

* feat(review): tabbed Branches/Commits in base picker (#709)

Replaces the three-section list (Local / Remote / Recent commits) with
a Branches | Commits tab strip — commits were below a scroll wall in
repos with many branches. SHA-like and HEAD~N queries auto-focus the
Commits tab so manual entry stays one keystroke away.
2026-05-13 07:09:17 -07:00
Cody Partington b04c9e1f30 fix(review): persist file comment drafts across close/reopen (#721)
* fix(review): persist file comment drafts across close/reopen

File comment popover lost in-progress text when dismissed via click-outside / Escape / X — local state died with the unmount. Line-comment toolbar already does this via a module-level draft map; mirror the pattern in `CommentPopover` behind an opt-in `draftKey` prop and wire both file-comment call sites (`AllFilesDiffView`, `DiffViewer`).

* fix(review): scope file-comment draft key by PR identity

File-path-only key leaked drafts across in-place PR switches.

* fix(review): remount all-files comment popover on context change

The all-files panel shares one CommentPopover instance across every
file. Switching files (or PR/scope) batches close+open into a single
state transition, so React reuses the instance with stale text in
state and the draft-sync effect writes it under the new draftKey.
Key the popover by the same expression as draftKey so any change in
file, PR, or scope forces a fresh mount that reads its own draft.
Also clear the anchor when the file list reloads so a popover
anchored to a removed DOM node doesn't survive a diff swap.

---------

Co-authored-by: Michael Ramos <mdramos8@gmail.com>
2026-05-13 06:54:33 -07:00
Leonardo Reis c0e36138fc Add Codex install guidance (#720) 2026-05-13 05:51:12 -07:00
Michael Ramos 927ca6d27e fix(gitlab): handle concatenated JSON pages from glab --paginate (#717)
glab api --paginate joins page responses as adjacent JSON arrays
(`[...][...]`), which JSON.parse rejects. This caused MR reviews to fail
with "JSON Parse error" for any MR with more than 100 changed files.

Replace the bare JSON.parse with a walker that splits the output into
top-level arrays (string- and depth-aware so `][` inside diff content
is not mistaken for a page boundary) and merges them.

Fixes #714
2026-05-13 05:33:07 -07:00
Michael Ramos 3fb0b9cf03 fix(gitlab): persist unposted inline comments + split pr-provider from browser-safe pr-types (#719)
Closes #680. Two changes that landed together because the persistence fix
exposed a hidden architectural constraint.

1. GitLab inline comments: when one or more discussion POSTs failed
   (e.g. transient `i/o timeout`), the failed comment bodies were lost.
   Now `submitGlMRReview` writes them to
   `~/.plannotator/failed-comments/{host}-{project}-mr{iid}-{ts}.json`
   in both the all-fail and partial-fail branches. The throw-vs-warn
   split is preserved deliberately: all-fail throws so the UI retries
   from a clean state, partial-fail warns so the UI doesn't resubmit
   already-posted content.

2. Split `packages/shared/pr-provider.ts` into `pr-types.ts`
   (browser-safe types + pure label/URL helpers) and `pr-provider.ts`
   (server-only dispatch that imports pr-github / pr-gitlab). The
   review-editor browser bundle previously dragged pr-gitlab.ts in as
   dead code via static imports, which silently constrained the file
   to never use Node built-ins. Adding `fs`/`os`/`path` for (1) broke
   the review build until we routed browser imports to pr-types and
   left server callers on the now server-only pr-provider facade.

Server-only `pr-provider.ts` re-exports `pr-types` so existing
server-side imports keep working unchanged.
2026-05-13 05:27:28 -07:00
Michael Ramos 1c40655e17 fix(opencode): intercept annotate/review/archive commands before LLM (#713) (#718)
OpenCode's command dispatcher appends `arguments` to the `.md` body and
runs `resolvePromptParts()` over the combined string, which auto-attaches
any file path it finds as a `FilePart`. With `/plannotator-annotate
/path/to/huge.md`, that meant the agent received the file's content as a
user message before the annotation UI even opened — blowing the context
on large files (GLM-5 auto-compact reported in #713).

Move `plannotator-annotate`, `plannotator-review`, and `plannotator-archive`
from the post-hoc `event` handler to `command.execute.before`, matching
the pattern `plannotator-last` already used. The hook clears `output.parts`
in place so the agent never receives the command turn; handlers then run
the UI and inject feedback via `client.session.prompt` as a separate turn.

Empty the bodies of the three `.md` files for defense in depth — only the
frontmatter is needed for OpenCode to register the slash command.

Also fixes a latent bug in the `plannotator-last` path: `output.parts = []`
reassigns the throwaway wrapper object's property but doesn't touch the
`parts` array the caller in `prompt.ts:1944` uses directly. Switched to
`output.parts.length = 0` to mutate in place. `plannotator-last` only
escaped notice because its parts array was always a single benign text
part.
2026-05-13 05:27:02 -07:00
Michael Ramos d21d0de074 fix: accept PLANNOTATOR_PORT=0 without spurious warning (#716)
Port 0 is the conventional "let OS pick a random port" value and is
already the local-mode default. Validation rejected it as invalid and
printed a warning, even though the fallback returned 0 anyway.

Fixes #715
2026-05-13 04:51:56 -07:00
Michael Ramos 4c8338f53f feat(ui): copyable hook path + guidance in Settings Hooks tab (#707)
* feat(ui): show copyable file path and guidance in Hooks settings tab

Always return the improvement hook file path from /api/hooks/status
(actual path when present, expected path when absent) so the UI can
display it in both states. Add CopyPathButton with tilde-shortened
display and full-path clipboard copy. When active, guide users to
edit directly or regenerate via /plannotator-compound. When absent,
show expected path and both creation options (auto-generate or manual).

* fix(ui): anchor displayPath on .plannotator instead of guessing homedir

The regex assumed home directories are always two segments deep
(/Users/x), which breaks for /root on Linux — it would capture
/root/.plannotator as the home prefix and display ~/hooks/... instead
of ~/.plannotator/hooks/..., leading users to create the file in the
wrong location.
2026-05-11 21:07:58 -07:00
Madhu C.S. 4272312eb0 Add jj evolog diff mode to code review UI (#702)
* review: add jj evolog diff mode to code review UI

Adds a new "Evolution diff" diff type for jj repos that shows what
changed between two evolutions (amendments) of the current change — i.e.
what was amended since a prior state of `@`.

Changes:
- Add `JjEvoLogEntry` type to `review-core.ts` and export from
  `shared/types.ts`
- Add `getJjEvoLogEntries()` to `jj-core.ts` — runs `jj evolog` with a
  tab-delimited template to collect commit ID, description, and age for
  each historical state of `@`
- Add `jj-evolog` to the `DiffType` union in `review-core.ts` and to the
  `JJ_DIFF_TYPES` set in `vcs-core.ts`
- Wire evolog mode into `runJjDiff()` and `getJjDiffArgs()` — defaults
  to the second evolog entry (the most recent prior state) when no
  explicit base is given; propagates `error` when there's no history
- Expose `jjEvologs` on `GitContext` and include the "Evolution diff"
  option in `diffOptions` only when two or more entries exist
- Add `EvoLogPicker.tsx`: a Radix Popover component that lists evolog
  entries and lets the user pick which prior state to compare against;
  shows commit ID, description, and age with a "default" badge on the
  auto-selected entry
- Update `FileTree.tsx` to render `EvoLogPicker` instead of
  `BaseBranchPicker` when `jj-evolog` is the active diff type
- Update `App.tsx` to thread `jjEvologs` and `detectedEvoBase` through
  to the file tree, auto-set the base to the second evolog entry on mode
  switch, and show an appropriate empty-state message
- Update `agent-review-message.ts` with an inspect hint for the evolog
  diff type
- Add unit tests covering: diff args, evolog output parsing, error
  cases, and the auto-base-selection fallback path

* review: fix evolog picker overflow and add evolog test sandbox

Fix the EvoLogPicker trigger and popover rows to handle narrow sidebar
widths — age strings now truncate instead of bleeding out of the container.

Add --with-evolog flag to the JJ manual test sandbox that amends the
working-copy change four times to create realistic evolution history.
Also writes a create-evolog.sh helper script into the sandbox for
on-demand evolog creation without the flag.

* fix: restore compare target when switching away from jj-evolog

When leaving Evolution diff mode, the evolog commit ID was left in
selectedBase and leaked into the next diff request. For jj-line this
caused jjLineBaseRevset() to treat the commit ID as a bookmark name,
producing a "didn't resolve to any revisions" error.

Now explicitly passes the restored default branch as baseOverride to
fetchDiffSwitch when leaving evolog, avoiding the React state batching
race that made the previous setSelectedBase-only approach insufficient.

---------

Co-authored-by: Michael Ramos <mdramos8@gmail.com>
2026-05-11 21:07:15 -07:00
Leonardo Reis 6e3efe8e7d Update Codex hooks feature flag (#708) 2026-05-11 21:02:19 -07:00
Michael Ramos d0e13d7b07 fix: scope id-token:write to only AWS OIDC deploy jobs (#706)
The workflow-level permissions block granted id-token:write to every
job, including detect-changes which only reads git diffs. Move the
OIDC capability to only deploy-marketing and deploy-portal (which
need it for aws-actions/configure-aws-credentials). deploy-paste
uses a Cloudflare API token and doesn't need OIDC at all.
2026-05-11 20:36:29 -07:00
Michael Ramos fcf2ba4cf5 fix: indent loose list continuation content under parent bullet (#705)
Closes #704
2026-05-11 16:41:26 -07:00
Michael Ramos c5b8535173 chore: bump version to 0.19.14
For provenance purposes, this commit was AI assisted.
v0.19.14
2026-05-11 12:44:14 -07:00
Michael Ramos c05ef85531 refactor: prescriptive plan/PR paths in visual-explainer skill (#701)
* refactor: split visual-explainer into three prescriptive paths

Plans and PR explainers now follow Plannotator's own prescriptive
approach with restored design-system.md, svg-patterns.md, and
pr-components.md references (Plannotator themed, html-effectiveness
inspired). General visual content delegates to Nico's visual-explainer
with theme-override.md.

Skill routes by content type:
- Plan/proposal → design-system + svg-patterns references
- PR explainer → design-system + pr-components references
- Everything else → Nico's visual-explainer + theme override

Remove redundant extended-patterns.md (content now in restored files).

* feat: restore prescriptive plan/PR references with Plannotator theming

Add timeline/milestone pattern to design-system.md (was missing).
Replace hand-rolled diff CSS in pr-components.md with Pierre diffs via CDN.
Add rollout plan pattern to pr-components.md.
Remove external reference links from SKILL.md (patterns baked into refs).
2026-05-11 12:33:18 -07:00
Michael Ramos f61bff03ed fix: update install.cmd test for visual-explainer skill check
For provenance purposes, this commit was AI assisted.
v0.19.13
2026-05-11 11:27:03 -07:00
Michael Ramos 071083e97d chore: bump version to 0.19.13
For provenance purposes, this commit was AI assisted.
2026-05-11 11:23:51 -07:00
skizophanes 67df7f5014 fix: resolve display-named opencode planning agents (#700)
Co-authored-by: thales <88134772+xenothales@users.noreply.github.com>
2026-05-11 11:17:13 -07:00
Michael Ramos d0bb3badda chore: bump version to 0.19.12
For provenance purposes, this commit was AI assisted.
v0.19.12
2026-05-11 09:38:27 -07:00
Michael Ramos 1eb561551c feat: standalone skills package + HTML render-annotate mode (#687)
Add --render-html flag to plannotator annotate that renders HTML files
as-is in an iframe instead of converting to markdown. Includes annotation
support via postMessage bridge, sharing via paste service, and theme
inheritance from Plannotator's 30+ themes.

New skill: plannotator-visual-explainer — wraps nicobailon/visual-explainer
with Plannotator theme tokens, extended patterns (timelines, SVG diagrams,
code blocks, risk tables, Pierre diffs via CDN), and plan/PR-specific guidance.

All three servers (Bun, Pi, OpenCode) support the new flag.
2026-05-11 10:30:49 -04:00
Andrey Skripalschikov 50191e6c1d Add diff line background intensity option (#691) 2026-05-11 10:18:27 -04:00
Michael Ramos 347663a4de feat(pfm): code line range references, hover preview, sketch Graphviz (#692)
Code file line range references with hover preview + Graphviz improvements.

Line ranges: `file.ts:42` and `file.ts:10-20` are fully supported with
syntax-highlighted hover preview popover (150ms delay, GitHub-style
persistence). New parseCodePath() utility, server-side line suffix
stripping on both Bun and Pi servers, ambiguous picker preserves line
suffix. useCodeFilePopout moved to hooks/pfm/.

Graphviz: responsive container height from SVG aspect ratio, white
background polygon removed, default colors (black, lightgrey) replaced
with theme tokens via SVG post-processing. User-specified colors
preserved.
2026-05-11 10:13:47 -04:00
Michael Ramos f4493fc6ff Optimize plan editor rendering (#696)
- Replace blocks/frontmatter useState + useEffect with useMemo (eliminates mount cascade)
- Migrate toast notifications from React state to Sonner (App no longer re-renders for toasts)
- Extract AppHeader component with React.memo (header no longer re-renders on unrelated state changes)
- Fix color transition flash on initial load (suppress global * transition until mount settles)
- Add Tailwind @source path for new editor components directory
2026-05-11 08:55:18 -04:00
Michael Ramos 579127e2c9 feat(ui): add Hooks visibility tab to Settings dialog (#695)
Add Hooks tab to plan review Settings dialog for visibility into the
improve-context system. PFM Reminder toggle card with config persistence,
Improvement Hook status card with collapsible content preview. New
GET /api/hooks/status endpoint and pfmReminder in POST /api/config
on both Bun and Pi servers. Dev mock API wired up.
2026-05-11 08:26:58 -04:00
Michael Ramos 13c667c044 feat(hook): PFM reminder & improvement hook support across all runtimes (#689)
PFM reminder & improvement hook support across Claude Code, OpenCode, and Pi.

- Add opt-in PFM reminder (pfmReminder config flag) injected on EnterPlanMode
- Wire composeImproveContext() into all three runtimes
- Fix OpenCode system.transform array reference bug (pushes were going to dead array)
- Fix install scripts silently stripping PreToolUse/EnterPlanMode hook entry
- Isolated Pi sandbox testing (--no-extensions -e)
2026-05-11 08:14:13 -04:00
Graeme Folk b5cd3d5120 fix(review): resolve jj default target from trunk (#686)
Resolve the default JJ compare target from trunk() so review UI and diff behavior use the same concrete bookmark when JJ can identify one.
2026-05-08 19:10:58 -07:00
Andrey Skripalschikov 7329efb980 Add diff tab size option (#685) 2026-05-08 11:45:47 -07:00
Michael Ramos 36948e8572 chore: bump version to 0.19.11
For provenance purposes, this commit was AI assisted.
v0.19.11
2026-05-07 23:24:00 -04:00
Graeme Folk 69ef11bdfb feat(review): add jj review workflows (#675)
* feat(review): add jj support for local diffs

* feat(review): add jj review workflows

* fix(review): tighten jj diff defaults

* test(review): add jj manual sandbox

* fix(review): share jj agent diff prompts

* fix(review): quote jj agent revsets

* feat(review): share jj vcs handling with pi

* fix(review): tighten jj bookmark and pi pr handling

* fix(review): tighten jj defaults and detection

* fix(review): harden jj diff and vcs detection

---------

Co-authored-by: Michael Ramos <mdramos8@gmail.com>
2026-05-07 19:57:33 -07:00
Michael Ramos 0e4d301925 Review UI refinements: separator styling and header layout (#683)
* Collapse viewed files in all-files review

* Style diff hunk separators to be slimmer and subtler

Reduce separator height from 32px to 24px, use theme-integrated
semi-transparent backgrounds via --diffs-bg-separator-override,
and fade text/buttons with opacity for a less visually heavy bar.

* Refine review header layout

Move sidebar toggles to far right, options menu to their left.
Add divider between file tree button and repo label.
Remove worktree pill from header (still accessible via menu).

* Fix gutter drag selecting only one line

Switch from deprecated enableHoverUtility + renderHoverUtility to
enableGutterUtility + onGutterUtilityClick. The old API never entered
Pierre's gutterSelecting mode, so dragging the gutter button always
reported a single line. The new API receives the full drag range.

Closes #679
2026-05-07 15:54:06 -07:00
Michael Ramos 71492ec880 chore: bump version to 0.19.10
For provenance purposes, this commit was AI assisted.
v0.19.10
2026-05-06 16:13:46 -07:00
Michael Ramos 49c55e6e25 Revert "Expose bypass clear reminder permission mode (#668)"
This reverts commit 3b88415aeb.
2026-05-06 16:10:31 -07:00
Michael Ramos cc57710df2 chore: bump version to 0.19.9
For provenance purposes, this commit was AI assisted.
v0.19.9
2026-05-06 15:57:39 -07:00
Michael Ramos 79b4832575 fix(pi): terminate agent loop on plan approval so model switch takes effect (#677)
Pi snapshots the model at the start of each agent.prompt() call, so
calling pi.setModel() mid-loop has no effect until the next user turn.
Return terminate: true from the approval tool to end the current loop,
then use an agent_end handler with deferred sendUserMessage to start a
fresh turn that picks up the executing model.

Closes #674
2026-05-06 15:16:20 -07:00
Michael Ramos f7b55e2e5f Avoid Pi bundled skill conflicts
## Summary
- configure Pi package entries to keep extension commands while disabling bundled package skills when shared Plannotator skills are installed globally
- preserve Pi bundled skills when global shared skills are unavailable
- write Pi settings without a UTF-8 BOM on Windows

## Tests
- bash -n scripts/install.sh
- bun test scripts/install.test.ts
- git diff --check
2026-05-06 15:12:47 -07:00
Michael Ramos a22ee3fbe5 fix(ui): remove shimmer animation from clickable file paths (#676)
The repeating shimmer animation on code file links was distracting and
stole focus while reading plans. Closes #672.
2026-05-06 13:51:30 -07:00
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