* Add Amp plugin integration
* Use official Amp logo on landing page
* Tighten landing agent selector
* Default landing selector to Claude Code
* Stabilize server ready handoff test
* Create ready handoff directory before writing
* Stabilize server ready handoff tests
* Fix Amp command cancellation and cwd
* Preserve Plannotator browser handling for Amp
* Fix Amp review edge cases
* Add PowerShell installer smoke coverage
Replace the warm-hue (55) walnut dark palette with the Plandromeda
palette — Andromeda's soft darkness at oklch hue 260. Brighter
primaries, richer accents, consistent blue undertone across the page.
Light mode unchanged (already identical to Plandromeda light).
* feat: add PLANNOTATOR_DATA_DIR env var to customize data directory
* fix: update missed hardcoded paths to use PLANNOTATOR_DATA_DIR
OpenCode plugin and VS Code extension still used hardcoded
~/.plannotator paths, causing the IPC registry and plan backing
file to diverge from the server when PLANNOTATOR_DATA_DIR is set.
Also exports data-dir from @plannotator/shared and documents the
new env var in AGENTS.md.
Co-authored-by: Chris Werner Rau <14326070+cwrau@users.noreply.github.com>
Co-authored-by: João O. Santos <34689526+Joao-O-Santos@users.noreply.github.com>
* fix: vendor data-dir.ts into Pi extension and rewrite imports
The Pi extension copies shared/server modules into generated/ at
build time. Without vendoring data-dir.ts and rewriting the
parent-relative imports, typecheck fails on all generated files
that import getPlannotatorDataDir.
* refactor: eliminate duplicated data-dir logic and clean up call sites
- VS Code extension: replace inlined getPlannotatorDataDir() copy with
import from the canonical packages/shared/data-dir.ts (esbuild bundles
it, so no runtime dependency needed)
- storage.ts: hoist repeated getPlannotatorDataDir() calls to a
module-level DATA_DIR constant, matching the pattern config.ts uses
- data-dir.ts: remove inaccurate docstring claim about relative path
resolution (the code does not call resolve())
- improvement-hooks.ts: hoist to DATA_DIR constant, clarify comments
on the two-level hook lookup (hooks/ subdir vs root fallback)
* fix: resolve relative PLANNOTATOR_DATA_DIR to absolute path
A relative value like ./data would break readArchivedPlan's path
traversal guard, which compares a resolve()'d absolute path against
the still-relative planDir prefix. Always return an absolute path
so all callers get consistent path shapes.
* fix: use @plannotator/shared/data-dir imports in server package
Switch from relative ../shared/data-dir imports to the package
export, matching the convention every other server file follows.
Update Pi vendor script sed rules to match the new import style.
* fix: use package imports in server and respect data dir in compound skill
Server modules: switch from relative ../shared/data-dir imports to
@plannotator/shared/data-dir, matching the convention every other
server file follows. Update Pi vendor script sed rules to match.
Compound skill: update hardcoded ~/.plannotator paths to check
PLANNOTATOR_DATA_DIR first, so the skill reads plans and writes
the improvement hook to the correct location when users set a
custom data directory.
Co-authored-by: Chris Werner Rau <14326070+cwrau@users.noreply.github.com>
Co-authored-by: João O. Santos <34689526+Joao-O-Santos@users.noreply.github.com>
* fix: remove remaining hardcoded ~/.plannotator assumptions
- Settings UI: replace hardcoded path in label and placeholder with
generic text that doesn't assume a specific data directory
- quickLabels: update agent tip to reference PLANNOTATOR_DATA_DIR
so the agent checks the correct plans directory
- codex-review: hoist getPlannotatorDataDir() to module-level DATA_DIR
constant, eliminating redundant per-call resolution in debugLog()
- Tests: make submit-plan and storage tests resilient to
PLANNOTATOR_DATA_DIR being set in the environment
- Install scripts (sh, ps1, cmd): check PLANNOTATOR_DATA_DIR before
falling back to ~/.plannotator for config.json attestation lookup
* fix: expand tilde in install script and update test assertions
install.sh: PLANNOTATOR_DATA_DIR set to ~/... stays literal inside
double quotes, so the config file check silently failed. Add case
statement to expand ~ the same way the runtime data-dir.ts does.
install.test.ts: update three assertions that checked for hardcoded
~/.plannotator paths — now verify PLANNOTATOR_DATA_DIR awareness
instead.
* docs: add PLANNOTATOR_DATA_DIR to env var reference with VS Code note
Document the new env var on the marketing site's environment
variables reference page. Include a footnote about ensuring
VS Code inherits the variable when launched from the Dock.
---------
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
Co-authored-by: Chris Werner Rau <14326070+cwrau@users.noreply.github.com>
Co-authored-by: João O. Santos <34689526+Joao-O-Santos@users.noreply.github.com>
* Make /workspaces/ system-themed with a custom dark palette
Until now the page force-pinned the light tokens regardless of the
visitor's actual theme preference, which is rough at night.
Two changes:
1. The wrapper class is renamed `ws-force-light` → `ws-page-scope` and
now defines two token sets:
- default (no `.light` on <html>): a near-neutral, very-dark-gray
palette specific to this page
- `html.light .ws-page-scope`: the existing light palette, unchanged
The page now follows whatever theme the global Nav toggle has set,
the same way every other marketing page does.
2. The dark palette is intentionally NOT the global Plannotator dark
theme. It uses near-zero chroma (`oklch(0.X 0 0)`) for surfaces
(background 0.13, card 0.17, muted 0.22, border 0.27) so the
landing page reads as a clean dark-gray sheet — Linear/Vercel
energy rather than the warmer blue-tinted dark used in the docs
and the editor. Primary stays violet (oklch(0.75 0.18 280)) so the
OSS-commitment stamp and submit button still pop.
Every existing rule on the page already uses var(--background),
var(--card), var(--border), etc., plus relative-color tweaks like
`oklch(from var(--primary) l c h / 0.5)` — so they automatically
pick up the right values in both modes with no further changes.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Warm up the dark palette and tone down the green/violet
Three follow-ups based on visual review of the new dark mode:
- Bg/card/muted/border ladder lifted ~2 L-points and given a touch
of warm chroma (0.01 at hue 55) — page reads as dark walnut /
leather instead of stark neutral charcoal.
- --secondary (Today / Next / Compound labels) dropped from
oklch(0.70 0.15 180) to oklch(0.62 0.08 180) — same teal hue,
desaturated and slightly dimmer so the row recedes.
- --primary (Join button, OSS contributor stamp, focus ring)
dropped from oklch(0.75 0.18 280) to oklch(0.65 0.10 280) —
same violet hue, dialed down to a dusty plum.
Light mode is unchanged — still inheriting .theme-plannotator.light.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Resolve DEFAULT_SESSIONS_DIR and DEFAULT_PROJECTS_DIR from
CLAUDE_CONFIG_DIR when set, falling back to ~/.claude.
Add projectsDirOverride param to findSessionLogsByAncestorWalk
so all four resolution tiers respect custom config paths.
Closes#783
Three changes that reclaim horizontal room on narrow viewports so the
Docs link stops drifting into the logo:
- Nav side padding 24px -> 16px on mobile (px-4 sm:px-6)
- Link group gap 8px -> 6px on mobile (gap-1.5 sm:gap-2)
- Hide the theme toggle on mobile (the React island lives in a
hidden sm:flex wrapper); mode is still respected from cookies and
the toggle is back at sm+
Generated with [Devin](https://cli.devin.ai/docs)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Mobile screens were running out of horizontal room and the Docs link
was overlapping the Plannotator logo. Hiding Blog (and the separator
before it) on screens narrower than sm reclaims that space — Docs
shifts into where Blog used to be, no overlap.
Blog stays visible at sm+ where the full row fits comfortably.
Generated with [Devin](https://cli.devin.ai/docs)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Form labels Company 'optional', client validation no longer flags it
as Required when blank, and the submit handler stubs '(not provided)'
into the payload when the user leaves it empty so the worker's
existing 'company required' contract stays untouched.
Worker, schema, and DB are not changed.
Generated with [Devin](https://cli.devin.ai/docs)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The strong heading already says 'Collaborative knowledge base for
agent artifacts'; trailing 'Craft your shared engineering knowledge
base.' was just restating it.
Generated with [Devin](https://cli.devin.ai/docs)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add 'Versioned and accessible' to the Next phase copy
Tail clause notes that the standards extracted from artifacts and
reviews aren't a one-shot output — they're versioned over time and
visible to both your agents and your teammates.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Drop 'product' from Compound phase copy
'faster product verification' → 'faster verification'.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Removes the "show only when team-size = Just me" behavior added in
PR #773. The checkbox + lifetime-free-access note are now visible to
every visitor regardless of team size.
Reverts the wrapper div, the setContributorVisibility() helper + its
call site in the pill picker, and the .ws-contrib-block CSS.
Generated with [Devin](https://cli.devin.ai/docs)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Tighten the first sentence (drop "during generation, in review, and"
which was redundant with "before and after code lands"), then add
the gating + quality/speed payoff as the second beat.
Generated with [Devin](https://cli.devin.ai/docs)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
"From review surfaces to integrated verification systems" reads like a
slide title. Replacing with "From review surfaces to standards your
agents learn from" which still captures both ideas (verification +
agents that improve from the team's reviews) but drops the corporate
phrasing.
Generated with [Devin](https://cli.devin.ai/docs)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The OSS-contributor claim is personal-scope (one human attesting they
contributed), so it only makes sense for individual signups. Hide the
checkbox + helper note until the user picks "Just me" on team size.
Switching to any non-solo size hides it again and resets the state to
false so team signups can't accidentally carry is_contributor=true.
Small fade-in animation on reveal so the layout shift doesn't feel
abrupt.
Generated with [Devin](https://cli.devin.ai/docs)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The earlier force-light approach (adding the `.light` class to <html>
from a head script) gets undone the moment the React ModeToggleIsland
hydrates: its mount effect always calls `classList.remove('light')`
before re-applying based on the saved cookie. If the visitor's saved
theme is dark, the page flashes light then flips to dark.
Inline the light theme tokens inside `.ws-force-light` instead. The
toggle component can still flip the global `.light` class on/off, but
the workspaces page's tokens are pinned at the wrapper scope and
unaffected. Result: deterministic light mode regardless of the
visitor's saved preference.
Generated with [Devin](https://cli.devin.ai/docs)
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add Plannotator Workspaces waitlist (page + worker + CI)
Marketing
- New /workspaces/ page: hero+form sibling layout on desktop; mobile
reorders to hero -> form -> info -> banners so the CTA is reachable
on the first scroll. Uses the marketing site tokens/fonts and the
existing border-x editorial column. Theme-aware via the cookie.
- Adds a "head" slot to Base.astro for per-page <head> additions.
- Nav: new "Workspaces" link with a "soon" pill.
- Landing: small Workspaces CTA band before the final install CTA.
Waitlist worker (apps/waitlist-service)
- Cloudflare Worker + D1 (SQLite). Mirrors the paste-service layout.
- Endpoints: POST /signup (Turnstile-protected, rate-limited, honeypot),
GET /health, GET /admin/count, GET /admin/list gated by Bearer
ADMIN_TOKEN.
- Validation: required email + company + team_size; optional note +
is_contributor; honeypot field; freemail-aware company auto-inference.
- D1 schema includes a non-destructive 001 migration that adds note
and is_contributor columns. 19/19 validation unit tests pass; tsc
clean.
CI
- Adds a deploy-waitlist job to .github/workflows/deploy.yml mirroring
paste-service. Uses the existing CLOUDFLARE_API_TOKEN and
CLOUDFLARE_ACCOUNT_ID secrets, so no new repo config is required.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Force light mode on /workspaces/
The page is designed and signed off in light. Override the saved theme
cookie via a head-slot inline script that adds the `.light` class
before paint. Base.astro's cookie reader only adds the class (never
removes), so any saved-dark preference can't undo this.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add Ask AI to plan and annotate reviews
- mount shared AI runtime on plan, annotate, review, and Pi servers
- add shared document chat UI and comment-popover Ask AI entry point
- default providers from detected agent origin and document first-run announcement
- reuse shared code-review AI hook and document setup/docs
* Address Ask AI review followups
Stabilize AI chat session resets and callbacks, preserve per-origin provider defaults, share AI chat formatting utilities, and avoid blocking startup on model discovery.
* Fix AI permission details and Pi CLI lookup
Show formatted tool input in document AI permission cards and make Pi AI provider CLI detection work on Windows.
* Fix Pi model discovery in AI capabilities
Keep AI runtime startup non-blocking while making the capabilities endpoint await pending Pi/OpenCode model discovery before returning provider metadata.
The backing file used for edit-based plan submission was stored at
.opencode/plans/_active-plan.md inside the workspace, causing it to
appear in git status and editor file trees. It is now stored at
~/.plannotator/active/{project}/_active-plan.md alongside the version
history.
- Move getPlanBackingPath to derive path from project name under
~/.plannotator/active/
- Derive project name from ctx.directory basename via sanitizeTag at
call site
- Delete backing file on approval since it is no longer needed after the
session ends
- Update tests to reflect new path contract
When a file has no content (lineCount === 0), any edit is a pure insert
and the end field is semantically irrelevant. The previous check
rejected payloads where end was present on an empty file because end >
lineCount always evaluated true, breaking first-call submit_plan
invocations from agents or frameworks that include end unconditionally.
- Skip end > lineCount validation when lineCount === 0; applyEdits
handles it via splice clamping
- Add applyEdits test: edit on empty file with start=1, end=1 produces
correct output
- Add validateEdits test: passes for empty file with start=1 and end=1
Fixes#742
* feat(submit-plan): replace text/file-path mode with edit-based interface
Switches the OpenCode submit_plan tool from a dual-mode interface
(inline text or file path) to an edit-based one. The plugin now owns a
backing file at .opencode/plans/_active-plan.md; the agent never reads
or writes it directly. On denial, the response includes the current plan
with line numbers so the agent can apply surgical edits instead of
resubmitting the entire document, reducing token waste on iterative
revisions.
- Add applyEdits, validateEdits, formatWithLineNumbers, and
getPlanBackingPath helpers to the plugin
- Validate edit ranges (bounds, overlap, size limit) before mutating the
backing file
- Return line-numbered plan in denial responses to anchor targeted edits
- Remove getPlanDirectory, validatePlanPath, and file-path auto-
detection from plan-mode.ts
- Replace plan-mode.test.ts path-validation coverage with submit-
plan.test.ts for the edit engine
- Update custom-feedback.md and opencode.md docs for edit-based
semantics
Refs #365
* chore(opencode): drop unused buildPlanFileRule import
Removed in PR #730 deny path along with the only call site, but the
import was left behind.
---------
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
* fix(pi): move to earendil-works packages and links
* fix(docs): close markdown link for earendil pi URL
---------
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
Injects the version from package.json at compile time via Bun's --define
so compiled binaries report the correct version (e.g. `plannotator 0.19.16`).
Uncompiled dev runs fall back to `plannotator dev`.
* 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.
- 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.
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.
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.