* feat: add GitHub Copilot CLI integration
Add full Copilot CLI support via a new plugin (apps/copilot/) with:
- preToolUse hook intercepting exit_plan_mode for plan review
- copilot-plan subcommand with correct permissionDecision output format
- copilot-last subcommand parsing events.jsonl for last message annotation
- Copilot CLI session parser (copilot-session.ts)
- Plugin manifest, hooks.json, and slash commands
- UI origin branding (blue badge for Copilot CLI)
- Marketplace entry for plugin discovery
- README install section
Key design decisions:
- Intercepts exit_plan_mode (not create) to avoid double-review
- Uses sessionId (with fallback) to locate plan.md from session state
- Follows Codex integration pattern: same CLI binary, new plugin dir
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: handle actual Copilot CLI preToolUse input format (toolCalls array)
The Copilot CLI hook input uses { toolCalls: [{ name, args }] } format
instead of the documented { toolName, toolArgs } format. The filter was
checking event.toolName (always undefined), causing exit_plan_mode to
pass through unintercepted. Now extracts tool name from either format.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert "fix: handle actual Copilot CLI preToolUse input format (toolCalls array)"
This reverts commit b214bc02c58fc5d8f305b128457099afb2ea6ecc.
* fix: use absolute path in hooks.json for reliable binary discovery
The hook subprocess inherits the PATH from when Copilot CLI was started,
which may not include the plannotator binary directory. Using the full
absolute path via env vars ensures the hook always finds the binary.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Finishing the feature
* fix: remove Copilot CLI plugin from Claude Code marketplace.json
.claude-plugin/marketplace.json is for Claude Code's plugin marketplace.
The Copilot CLI plugin has its own install path and doesn't belong here.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: align Copilot CLI plugin with official spec
- Add .github/plugin/marketplace.json for Copilot CLI plugin discovery
- Change allowed-tools from Bash() to shell() syntax in commands
- Respect COPILOT_HOME env var in session path discovery
- Update plugin.json version to 0.14.5
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Yecats <Yecats@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: stahaff <222254525+stahaff@users.noreply.github.com>
Adds user-editable display names and persistent config via ~/.plannotator/config.json.
- ConfigStore singleton with precedence: server config file > cookie > default
- Editable identity input in Settings with "Use git name" and regenerate buttons
- POST /api/config endpoint for write-back across all 6 servers
- getServerConfig() reads config fresh per request (no stale cache)
- Eager constructor hydration so the store is safe to read before init()
- vendor.sh as single source of truth for Pi extension vendoring
- Vendor parity test to prevent missing generated modules
Closes#396
@plannotator/ai is a private workspace package unavailable on npm.
Pi's dynamic import silently failed, so AI features never loaded for
published package users. Now copies all AI files into generated/ai/
at build time, same pattern as the shared utils.
For provenance purposes, this commit was AI assisted.
* feat(pi): add missing endpoints to plan, review, and annotate servers
Phase 1-3 of Pi endpoint parity:
Plan server: image, upload, draft, editor-annotations, agents, favicon,
linked documents, Obsidian vaults/files/doc, file browser, VS Code diff
Annotate server: image, upload, draft, favicon, linked documents, file browser
Review server: extract shared handlers, add favicon
Shared utilities extracted from review server inline code into reusable
functions (handleImageRequest, handleUploadRequest, handleDraftRequest,
handleFavicon). Reference handlers (doc, Obsidian, file browser)
implemented using Node.js fs APIs replacing Bun.Glob/Bun.file.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(pi): add PR review endpoints and Node.js PR runtime adapter
Phase 4 of Pi endpoint parity:
- Node.js PRRuntime using child_process.spawn (matches Bun adapter pattern)
- GET /api/pr-context — fetch PR summary, comments, checks
- POST /api/pr-action — submit review to GitHub/GitLab
- PR mode guards on /api/diff/switch and /api/git-add
- /api/diff response includes prMetadata and platformUser in PR mode
- /api/file-content fetches from platform API in PR mode
- Build script copies pr-provider, pr-github, pr-gitlab from shared
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(pi): wire AI backbone with Node.js Pi SDK provider
Phase 5 of Pi endpoint parity:
- Create packages/ai/providers/pi-sdk-node.ts — PiProcessNode class
using child_process.spawn instead of Bun.spawn, same RPC protocol
- Register 4 AI providers in Pi review server (claude-agent-sdk,
codex-sdk, pi-sdk-node, opencode-sdk) with graceful degradation
- Route /api/ai/* endpoints through createAIEndpoints handlers
- Pipe Web Response → node:http response with ReadableStream support
for SSE streaming
- Dispose AI sessions and registry on server stop
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(pi): address parity audit findings across all three servers
Plan server:
- /api/plan: add repoInfo and projectRoot to response
- /api/approve: pass agentSwitch and permissionMode in decision
- Update decision promise type to include agentSwitch, permissionMode
Review server:
- /api/diff/switch: pass gitContext.cwd to runGitDiff
- /api/file-content: pass gitContext.cwd to getFileContentsForDiffCore
- /api/git-add: add fallback to gitContext.cwd when worktree parse fails
Annotate server:
- /api/plan: add repoInfo and projectRoot to response
- /api/feedback: capture annotations array (was silently dropped)
- Update decision promise type to include annotations
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(pi): complete parity — integrations, planSave, save-notes
Ports all remaining missing functionality:
- Node.js versions of saveToObsidian, saveToBear, saveToOctarine
(Bun.write → writeFileSync, Bun.$ → spawn)
- Node.js detectProjectNameSync (Bun.$ → execSync)
- extractTags, generateFrontmatter, generateFilename, extractTitle
- POST /api/save-notes — decoupled note saving
- POST /api/approve — full implementation: note integrations,
planSave snapshots, saveAnnotations, saveFinalSnapshot
- POST /api/deny — planSave snapshots on denial
- Import saveAnnotations, saveFinalSnapshot from storage.js
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(pi): wire domain module imports and fix type errors
- Add all missing imports from ./server/* domain modules to server.ts
- Export interfaces from integrations.ts (ObsidianConfig, BearConfig, etc.)
- Move toWebRequest to helpers.ts, remove duplicate from handlers.ts
- Add git() helper to project.ts (was in server.ts, needed by getRepoInfo)
- Fix os default import → named imports in handlers.ts and network.ts
- Fix readdirSync Dirent type in reference.ts
- Fix Headers.entries() → forEach for Node compat in AI endpoint piping
- Fix ReadableStream type cast in AI SSE streaming
- Fix matchAll iterator compat in integrations.ts (use while + exec)
- Cast pi-sdk provider config to any (PiSDKConfig not in base union)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(pi): move generated shared files to generated/ directory
Moves all build-time copied shared files (feedback-templates, review-core,
storage, draft, project, pr-provider, pr-github, pr-gitlab) from the
pi-extension root into generated/ subdirectory.
Updates build script to output there. Updates all imports in server.ts,
index.ts, and server/ domain modules to use ./generated/ paths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(pi): replace hand-maintained utils.ts with generated checklist
utils.ts was a manual copy of parseChecklist, extractDoneSteps, and
markCompletedSteps from packages/shared/checklist.ts. Add checklist
to the build-time copy list and import from generated/checklist.js.
Delete the redundant utils.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(pi): gitignore generated/ and built HTML files
These are build artifacts created by `bun run build:pi`. Untrack them
and add .gitignore to prevent re-adding.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(pi): split server.ts into domain-organized modules
- server.ts is now a barrel re-exporting from server/ modules
- server/serverPlan.ts — plan review server
- server/serverReview.ts — code review server
- server/serverAnnotate.ts — annotate server
- server/helpers.ts — add requestUrl() to eliminate non-null assertions
- server/project.ts — linter fix (sanitizeTag import path)
- packages/ai/package.json — add pi-sdk-node export entry
- index.ts — fix waitForDone non-null assertion with guard check,
update imports for generated/checklist.js
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(pi): parity audit fixes + shared code extraction
Systematic side-by-side audit of Pi vs Bun servers (A1-A22, B1-B2 complete).
Fixes found during audit:
- PlanServerResult.waitForDecision missing savedPath/agentSwitch/permissionMode
- Missing permissionMode option and /api/plan response field
- editorAnnotations created unnecessarily in archive mode
- repoInfo called per-request instead of cached at init
- Approve handler missing effectivePermissionMode fallback
- Deny handler missing savedPath in decision resolution
- Archive /api/plan response had extra pasteApiUrl
- Missing GET method guards on archive/plans, archive/plan, doc, obsidian/files, obsidian/doc, reference/files
- Review server had stray pasteApiUrl option/response field
- AI getCwd missing worktree support
Shared code extraction:
- packages/shared/favicon.ts — single source for favicon SVG
- packages/shared/integrations-common.ts — note app pure functions
- packages/shared/reference-common.ts — file tree building
- packages/shared/repo.ts — git remote parsing
- Updated all consumers to import from shared sources
For provenance purposes, this commit was AI assisted.
* fix: parity audit B3-C10 — review + annotate server fixes
Review server (B3-B17):
- diff/switch missing try/catch error handling
- git-add parseBody outside try/catch
- feedback missing try/catch error handling
- Unknown /api/ai/* paths now return 404 (both Bun and Pi)
Annotate server (C1-C10):
- Bun annotate server missing pasteApiUrl (short URL sharing broken)
- Added pasteApiUrl to Bun options, response, and both hook callers
- Pi repoInfo called per-request instead of cached at init
- Pi feedback missing try/catch error handling
- Missing GET method guards on doc and reference/files
For provenance purposes, this commit was AI assisted.
* fix: parity audit D3-D5 — draft error handling, editor annotations, resolve-file extraction
D3: Pi draft save handler missing error handling — added .catch() with 500 + console.error
D4: Pi editor annotation POST missing try/catch — added with "Invalid JSON" 400
D5: Extracted resolveMarkdownFile to packages/shared/resolve-file.ts
- Replaced Bun.Glob with runtime-agnostic walkMarkdownFiles (readdirSync)
- Made function sync (no longer async)
- Pi handleDocRequest now uses shared resolveMarkdownFile instead of inline resolution
- Gains Windows path normalization, isWithinProjectRoot security check
- Deleted packages/server/resolve-file.ts re-export, consumers import from shared
- Cleaned up stale await calls in hook entry, reference handler, and tests
- All 19 resolve-file tests pass
For provenance purposes, this commit was AI assisted.
* fix: parity audit D6-D10 — integrations, PR naming, shared modules
D6: Fixed broken detectProjectNameSync — was using require() for
non-existent exports. Now uses basename + sanitizeTag directly.
D7: Renamed checkAuth → checkPRAuth, getUser → getPRUser across
Bun server, hook, and OpenCode plugin to match Pi naming.
Also fixed stale resolve-file import in OpenCode plugin.
D8-D10: Verified clean — ide, project detection, network.
For provenance purposes, this commit was AI assisted.
* update openpackage.yml
* fix: bump Pi git-add test timeout to 15s for parallel suite stability
For provenance purposes, this commit was AI assisted.
* test: add route parity test — Bun ↔ Pi server route drift detection
For provenance purposes, this commit was AI assisted.
* fix(ci): update Pi generate step to use generated/ directory with full file list
The Pi extension was refactored to use generated/ subdirectory but the CI
generate step still used the old flat layout with a subset of files.
For provenance purposes, this commit was AI assisted.
* fix(ci): update release workflow Pi generate step to match new layout
Same stale generate step as test.yml — old flat layout, missing files.
For provenance purposes, this commit was AI assisted.
* fix(pi): update files array for modular server layout
The files array still referenced the old flat layout (server.ts monolith,
root-level generated files, deleted utils.ts). npm publish would have
produced a broken package missing server/ and generated/ directories.
For provenance purposes, this commit was AI assisted.
* feat: add TypeScript type-checking to CI pipeline
- Fix broken barrel export: buildFileTree/VaultNode re-exported from
@plannotator/shared instead of reference-handlers (P1 bug)
- Fix server.port type narrowing in all 3 servers
- Fix AI provider type errors (claude-agent-sdk, codex-sdk, opencode-sdk, pi-sdk)
- Extract mapPiEvent to pi-events.ts to break Bun→Node type chain
- Add tsconfig.json to packages/shared, packages/ai, packages/server, apps/pi-extension
- Add `typecheck` script to root package.json
- Add type-check step to test.yml and release.yml CI workflows
For provenance purposes, this commit was AI assisted.
* fix(ci): use bun-types instead of @types/node for typecheck
CI environment has bun-types (includes Node types) but not
@types/node as a standalone package.
For provenance purposes, this commit was AI assisted.
* fix(ci): add @types/node for Node-runtime type checks
Pi extension and packages/shared run on Node, not Bun — they should
type-check against @types/node, not bun-types. Added @types/node as
a dev dependency so CI resolves it.
For provenance purposes, this commit was AI assisted.
* fix: cast Uint8Array.buffer to ArrayBuffer for TS 5.9 compat
crypto.subtle.importKey expects BufferSource, but TS 5.9 is stricter
about Uint8Array.buffer being ArrayBufferLike (includes SharedArrayBuffer)
vs ArrayBuffer. Explicit cast resolves the overload mismatch.
Astro pulls in TS 5.9 transitively, so CI resolves a different
TypeScript version than local dev. This fix works on both 5.8 and 5.9.
For provenance purposes, this commit was AI assisted.
* fix(ci): add bun-types as explicit devDependency
CI's bun install doesn't hoist bun-types to root node_modules when
it's only a transitive dep of @types/bun. Adding it as a direct
devDependency guarantees tsc can resolve it.
For provenance purposes, this commit was AI assisted.
* fix(ci): remove Pi extension from typecheck
Pi extension depends on @mariozechner/pi-* peer dependencies that
aren't installed in CI. Type-checking it requires Pi's runtime
environment. The three packages we check (shared, ai, server) are
sufficient to catch barrel export bugs and type errors. Pi extension
coverage comes from route parity tests and bun test.
For provenance purposes, this commit was AI assisted.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: plan archive browser with in-session sidebar tab (#362)
Add `plannotator archive` subcommand and archive sidebar tab for browsing
saved plan decisions from ~/.plannotator/plans/. Plans show approved/denied
badges and timestamps. In-session mode uses the linked doc overlay pattern
so users can reference old plans while reviewing a current one.
- New archive server (packages/server/archive.ts) following annotate pattern
- New ArchiveBrowser sidebar component, reusable in both contexts
- Archive listing/parsing functions in storage.ts (reads decision snapshots)
- Archive endpoints on plan server for in-session use (/api/archive/plans)
- Remove dead "Other Plans" UI, projectPlans state, /api/plan/history endpoint
- Fix resize handle touch area covering scrollbars in sidebar/main content
- Fix sidebar tab bar overflow when narrow
For provenance purposes, this commit was AI assisted.
* fix: code quality sweep for plan archive
- Remove `as any` cast: add "archive" to SessionInfo.mode union
- Replace inline import() type with proper import for ArchivedPlan
- Replace any[] with ArchivedPlan[] in fetch response types
- Fix infinite re-fetch when archive is empty (use hasFetched ref)
- Cache archive plan list in plan server (avoid re-scanning filesystem)
- Document ResizeHandle side prop behavior
- Remove redundant comment on Viewer archiveInfo prop
For provenance purposes, this commit was AI assisted.
* chore: remove dead marketing components
Step.astro and Landing.astro are unused — landing page inlines
step markup and pages use Base.astro directly.
For provenance purposes, this commit was AI assisted.
* fix: address code review findings for plan archive
- Path traversal: use resolve() + trailing separator guard (matches reference-handlers.ts)
- Thread customPath into in-session archive endpoints via query param
- Sort same-day archive entries by mtime instead of title
- Clear selectedArchiveFile on linked doc back to prevent badge leak
- Hide archive tab in annotate mode (server doesn't serve those endpoints)
- Add targetTab param to useLinkedDoc.open() to preserve calling sidebar tab
- Replace mutable render variable with index-based date grouping
For provenance purposes, this commit was AI assisted.
* refactor: collapse standalone archive server into plan server
Delete packages/server/archive.ts (187 lines) — nearly all duplicated
from the plan server. Add mode:"archive" option to startPlannotatorServer
instead. Fixes two bugs from code review:
- handleArchiveCopy now splits on "# Plan Feedback" marker instead of
bare "---", preventing truncation at horizontal rules in plan content
- customPath support works in standalone archive mode (was only working
in-session because the standalone server never received it)
For provenance purposes, this commit was AI assisted.
* refactor: extract useArchive hook from App.tsx
Move archive state (archiveMode, plans, selectedFile, isLoading) and
handlers (select, fetchPlans, done, copy) into a dedicated useArchive
hook. Reduces App.tsx by ~75 lines and makes the archive feature
self-contained.
For provenance purposes, this commit was AI assisted.
* feat: Pi archive parity + eliminate server duplication
Move runtime-agnostic storage, draft, and project functions from
packages/server/ to packages/shared/ — eliminating ~250 lines of
duplicated code in Pi's server.ts. Server package becomes thin
re-exports, preserving all existing import paths.
Add archive mode to Pi's plan review server (mode, routes, waitForDone)
and register /plannotator-archive command in the Pi extension. Consolidate
ArchivedPlan type to single definition in shared/storage.ts.
Simplify archive copy to include full content with feedback.
For provenance purposes, this commit was AI assisted.
* fix: drop -core suffix from Pi shared copies
The -core suffix broke cross-file imports — storage.ts imports from
./project which didn't resolve to project-core.ts. Using the original
filenames (no collision) lets relative imports work naturally.
For provenance purposes, this commit was AI assisted.
* fix: archive custom path bugs, disable sharing, update docs
- Normalize planDir via resolve() in getPlanDir() to handle relative
paths and trailing slashes in the path traversal guard
- Re-fetch archive plans client-side with cookie-backed customPath
so standalone archive respects the user's configured save location
- Disable sharing in archive mode (read-only viewer, no need)
- Remove dead /api/plan/history endpoint and listProjectPlans import
from Pi extension
- Remove dead /api/plan/history mock from dev-mock-api
- Update CLAUDE.md and AGENTS.md: add archive flow, archive API
endpoints, shared package structure, correct storage location,
sidebar tab count, remove stale /api/plan/history references
- Update hook server docstring from four to five modes
For provenance purposes, this commit was AI assisted.
* fix: empty archive shows demo content, stale viewer after customPath fetch
- Clear demo markdown when archive opens with no plans (plan: "" was falsy,
so setMarkdown was never called)
- Remove redundant fetchPlans() from archive init — server already sends
archivePlans in initial response
- After fetchPlans() resolves with customPath results, auto-select and load
the first plan into the viewer
- Remove dead listProjectPlans re-export from server barrel
For provenance purposes, this commit was AI assisted.
* refactor: gitignore Pi shared copies, add @generated headers
Pi's copied .ts files (storage, draft, project, feedback-templates,
review-core) are build artifacts generated from packages/shared/. They
looked like editable source files, leading to confusion about which file
to edit. Now gitignored like the HTML copies, with @generated headers
prepended by the build script.
For provenance purposes, this commit was AI assisted.
* fix: generate Pi shared copies in CI before tests
The Pi .ts copies are now gitignored build artifacts. CI needs to
generate them before running tests since server.test.ts transitively
imports them via server.ts.
For provenance purposes, this commit was AI assisted.
* fix: generate Pi shared copies in release pipeline test job
Same fix as test.yml — the Pi .ts copies are gitignored, so the test
job in the release pipeline also needs to generate them before bun test.
For provenance purposes, this commit was AI assisted.
* fix: use block scalar in CI workflow to avoid YAML parse error
The inline `run:` had a colon in the printf string that YAML
interpreted as a mapping key. Switch to `run: |` block scalar.
For provenance purposes, this commit was AI assisted.
* feat: revamp marketing landing page
Combine hero (dot grid, noise texture, agent selector, install block) from
prototype 1 with middle content (plan/code review showcases, how-it-works
toggle, key properties, compatibility, final CTA) from prototype 2.
- Hero with animated fade-up, glassy agent buttons, per-agent install commands
- AnnoReplace component: shimmer + white glove hint → click → comment popover
- How It Works section with Plan/Code Review toggle swapping images + copy
- Accurate install instructions (binary + plugin for Claude Code/OpenCode)
- Three-column footer with brand stamp
- Nav with pipe-separated links
- Agent icons with CSS invert for light/dark mode
- Scroll reveal animations for showcase sections
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: interactive terminal demos, install UX, and copy refinements
- Plan review: terminal animation (ASCII art, /plan typing, agent steps,
submit_plan(), deny → feedback loop) with viewport-aware start/pause
- Code review: same pattern (/plannotator-review, send feedback loop)
- Install instructions: multi-step copyable rows for Claude Code and
OpenCode, accurate Pi install command, Codex plan mode caveat
- Remove redundant Get Started section (hero handles install)
- AnnoReplace component: shimmer + white glove hint → click → comment popover
- How It Works: centered toggle, notification dot, tighter spacing
- Footer: license line, tighter gaps, @plannotator handle
- Social proof bar: "Zero learning curve"
- Hero trust signal: "free & open source"
- CTA subline: "Shape what gets built. Own what ships."
- Nav: pipe-separated links from prototype 2
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up landing page dead code and fix mobile/perf issues
Remove ~160 lines of dead CSS/JS from removed Get Started section,
consolidate duplicate reveal animation system, fix copy buttons
invisible on touch devices, drop unused Source Serif 4 font load,
fix footer responsive breakpoint, and clean up duplicate data attrs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: mobile responsiveness, demo glove pointers, and capabilities grid
Mobile: hide install commands, promote video CTA, reorder terminal
demos above text, fix social proof bar stacking, footer 2-col layout,
glove jabs further on small screens.
Demo gloves: white glove pointer on plan deny button (flipped, right
side) and code review send button (left side) with jab animations.
Capabilities: replace icon cards with 3-column grid (Commands,
Features, Integrations) with shimmer on slash commands.
Also: send feedback button moved to bottom-left, matched deny button
color, title em-dash to hyphen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: optimize images and clean up dead assets
Convert How It Works screenshots to WebP (25MB → 470KB).
Add proper plan-review.webp and code-review.webp for terminal demos.
Remove 6 unused image assets (~27MB). Fix ASCII art alignment.
Update trust signal copy.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: contributor strip below footer with build-time GitHub fetch
GraphQL query (CI) fetches code contributors, issue authors, and
discussion participants in one request. REST fallback (local dev)
for code contributors only. Results cached in-process to avoid
blocking repeated renders. Deploy workflow passes GITHUB_TOKEN.
Also: bump brand stamp and avatar opacity, footer copy cleanup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update agent video links and Codex install instructions
OpenCode → dedicated YouTube demo, Codex → X post demo.
Codex detail steps now show !plannotator review and !plannotator last.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: agent install steps leaking across buttons
Step elements now fully cleared (innerHTML + display:none) on every
agent switch. Use inline style instead of class toggle to avoid
CSS specificity conflict with .hero-detail-step display:flex.
Add third step slot for Codex commands. Add !plannotator annotate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add restart instructions and noscript fallback
Add restart step to Claude Code and OpenCode hero install flows so
new users know hooks/plugins require a session restart. Add noscript
style so landing-reveal sections stay visible without JavaScript.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(annotate): support Windows markdown paths in CLI annotate flow
* fix tmp path
* fix resolve-file.ts
* Update command to use EXE_PATH variable
* Update command path for plugin hooks in install.ps1
* test: add core test suite for path resolution, storage, remote detection, and install scripts
- resolve-file: absolute paths, relative paths, case-insensitive search, ignored dirs, extension filtering, ambiguity, Windows separators
- storage: slug generation, tilde expansion, version history, deduplication
- remote: env var detection (PLANNOTATOR_REMOTE, SSH_TTY), port config and validation
- image: tmpdir usage, extension validation
- install scripts: JSON structure validation, checksum verification, arch detection, full exe path in hooks
79 tests, 35ms
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: add test workflow and gate release builds on tests
- New test.yml: runs `bun test` on PRs and pushes to main
- release.yml: tests must pass before build job runs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve pre-existing test failures for CI compatibility
- project.test.ts: make repo name assertion portable (works in CI where
checkout dir differs from local dev)
- vscode mock: add missing APIs needed by editor-annotations.ts
(comments, languages, Range, CodeActionKind, decorations)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: remove Viewer.test.tsx and happy-dom dependency
Tests didn't exercise any application code — they manually constructed
DOM elements inline and verified DOM API behavior, not Viewer.tsx logic.
The mock highlighter didn't simulate real hljs, and one test could never
fail due to its try/catch structure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use targeted markdown glob and restore clean lockfile
- Replace **/* glob with **/*.[mM][dD]{,[xX]} to only scan markdown
files during case-insensitive search (avoids iterating every file)
- Restore package.json key ordering from main, only removing happy-dom
- Regenerate bun.lock from main's base to eliminate version drift
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: drop redundant test and fix weak assertion
- Remove redundant PLANNOTATOR_REMOTE=TRUE test (already covered by true)
- Fix UPLOAD_DIR assertion that would pass even with hardcoded /tmp
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore: pin GitHub Actions to SHAs and add Renovate config
Pin all actions in deploy.yml to full commit SHAs to prevent tag
force-push supply chain attacks. Upgrade actions/checkout from v4
to v6.0.2 to match release.yml.
Add renovate.json scoped to GitHub Actions only — weekly grouped
PRs with automerge, zero noise from npm dependencies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: move renovate.json to .github/renovate.json
Reduces root directory clutter. Renovate supports this path natively.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Deploy paste service with end-to-end encryption (zero-knowledge)
Add AES-256-GCM client-side encryption to the short URL sharing flow.
Plans are encrypted in the browser before upload — the paste service
stores only ciphertext it cannot read. The decryption key lives only
in the URL fragment (#key=...) and never leaves the browser.
- New packages/shared/crypto.ts with encrypt/decrypt via Web Crypto API
- Encrypt before POST in createShortShareUrl, decrypt on load
- Parse #key= fragment in useSharing.ts for both direct and import URLs
- Deploy Cloudflare Worker with KV namespaces to workers.dev
- Add deploy-paste CI/CD job to deploy.yml
- Fix Cache-Control from public to private, no-store
- Update ExportModal strings with encryption messaging
- Update README, docs, and blog with zero-knowledge encryption details
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Address code review: fix short URL fallthrough and regex anchor
- Return false early when paste fetch fails on /p/<id> path instead of
falling through to parseShareHash() (which can never succeed since the
hash contains #key=, not plan data)
- Add end-anchor to importFromShareUrl regex to prevent false matches on
URLs with extra path segments (regression from PR #188)
- Remove demo plan NOTE from README
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add short URL sharing via paste service to fix URL truncation on Slack/messaging apps
Share URLs for large plans can be 10-40KB+ because the entire plan + annotations
are compressed into the URL hash. Services like Slack, WhatsApp, and Twitter
truncate these URLs, making shared plans unviewable (related: #142).
This adds an optional paste-service backend that stores compressed payloads and
returns short ~60-char URLs (e.g. share.plannotator.ai/p/aBcDeFgH).
Changes:
- Add Cloudflare Worker paste service (apps/paste-worker/) with KV storage
and 90-day TTL for stored plans
- Add createShortShareUrl() and loadFromPasteId() to sharing utils
- Update useSharing hook to auto-generate short URLs with 1s debounce
- Update ExportModal to show short URL as primary copy target with
full hash URL as backup
- Portal automatically supports /p/<id> paths via useSharing hook
- Fully backward compatible: hash-based URLs continue to work unchanged
- Graceful degradation: falls back to hash URLs if paste service is unavailable
* Fix critical bugs found during code review of short URL feature
- Worker: return only { id } so client constructs URL with its own
shareBaseUrl (fixes self-hosted deployments)
- importFromShareUrl: handle /p/<id> short URLs in addition to hash
URLs (fixes teammate import via short links)
- Anchor /p/<id> regex with ^ to prevent false matches on nested paths
- Pass shareBaseUrl to loadFromPasteId (was always defaulting)
- replaceState preserves base path instead of hardcoding /
- Clear stale shortShareUrl immediately on debounce to prevent showing
outdated link during the 1s delay
- Add shareBaseUrl to dependency arrays for loadFromHash and
importFromShareUrl callbacks
- Remove unused url field fallback from paste API response parsing
* Fix pasteApiUrl/shareBaseUrl confusion and add remote session share URLs
Bug fix (addresses @backnotprop's code review):
- useSharing hook now accepts separate `pasteApiUrl` parameter instead of
incorrectly passing `shareBaseUrl` to `loadFromPasteId`. These are
different domains (share portal vs paste backend). When omitted, the
default `https://paste.plannotator.ai` is used correctly.
- Fixed in all three call sites: loadFromHash, importFromShareUrl, and
generateShortUrl (via createShortShareUrl options).
Remote session share URLs (implements #192):
- When running on a remote instance (SSH, devcontainer), Plannotator now
generates a share.plannotator.ai URL and prints it to stderr so the
user can open the plan review in their local browser.
- Works for all three modes: plan review, code review, and annotate.
- Uses the same deflate-raw + base64url encoding as the client.
- Fails silently if URL generation fails — port forwarding still works.
- New server utility: packages/server/share-url.ts
* Address code review findings: fix spread limit, CORS, deps array
- share-url.ts: Replace btoa(String.fromCharCode(...compressed)) with
a loop to avoid RangeError on plans >65K compressed bytes
- share-url.ts: Add cross-reference comment noting the server-side
SharePayload is an intentional subset of the canonical UI type
- paste-worker: Only return CORS headers for allowed origins; disallowed
origins get no CORS headers instead of misleading partial headers
- useSharing.ts: Remove unused shareBaseUrl from loadFromHash dependency
array (loadFromHash only uses pasteApiUrl, not shareBaseUrl)
* Restructure paste service with pluggable storage, fix consent flow and security issues
Replaces monolithic paste-worker with multi-target paste-service architecture:
- Core logic (handler, storage interface, CORS) separated from deployment targets
- Filesystem store (self-hosted), KV store (Cloudflare), S3 stub (future)
- Bun binary target + Cloudflare Worker target
- Fix auto-upload removed: short URLs only created on explicit user click
- Clear stale short URL state when content changes
- Fix require('fs') in ESM module, add path-traversal guard in FsPasteStore
- Return 400 (not 500) for malformed JSON in both targets
- Fix compress() spread limit for large plans
- Updated docs: self-hosting guide, sharing guide, env vars, API endpoints
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Simplify paste service: extract shared router, remove TOCTOU patterns
- Extract duplicated HTTP routing from bun.ts and cloudflare.ts into
handleRequest() in core/handler.ts — targets are now thin wrappers
- Move ID_PATTERN regex to core (was duplicated in both targets)
- Remove existsSync guard before mkdirSync({recursive:true}) in fs.ts
- Remove file.exists() check before file.json() in fs.ts get() — the
try/catch already handles missing files
- Extract DEFAULT_SHARE_BASE constant in sharing.ts (was hardcoded twice)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Deduplicate compression, image parsing, and remote share URL logic
- Create packages/shared/ with compress/decompress (single source of truth)
— both @plannotator/server and @plannotator/ui import from here
- Export parseShareableImages from sharing.ts, remove duplicate
parseGlobalAttachments from useSharing.ts
- Extract writeRemoteShareLink() helper in share-url.ts, replace 3
near-identical blocks in apps/hook/server/index.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix modulo bias in paste ID generation and remove nonexistent doc artifacts
- Use rejection sampling in generateId() to eliminate modulo bias
(bytes >= 248 discarded, uniform distribution over 62 chars)
- Remove references to install-paste.sh and Docker image that don't
exist yet; point self-hosting docs to GitHub Releases binaries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds an npm-publish job to the release workflow that publishes
@plannotator/opencode and @plannotator/pi-extension to npm with
OIDC trusted publishing and provenance attestation.
- Add pull_request and workflow_dispatch triggers (with dry-run input)
- Derive DRY_RUN env var; publish only on tag pushes or explicit opt-in
- Pin all action refs to SHA for supply-chain security
- Gate the release job on tag refs to avoid spurious GH releases
- Add id-token: write permission at workflow and job level for OIDC
## Summary
Complete code review system for reviewing git diffs with annotations.
### Features
- Interactive diff viewer with split/unified views
- Line-level annotation system
- Diff type selector (uncommitted, last commit, vs main branch)
- Dynamic default branch detection
- Empty state handling
- Simplified UX with streamlined feedback flow
Closes#51Closes#56
The hook's server/index.ts imports from @plannotator/server, but the
package wasn't declared in dependencies. Bun only creates workspace
symlinks for declared dependencies, causing the build to fail in CI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Bun supports cross-compilation, so we build all 4 targets
(darwin-arm64, darwin-x64, linux-x64, win32-x64) from a single
Ubuntu runner. This avoids the retired macos-13 runner issue.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
npm distribution:
- Add npm/ directory with main package and platform-specific packages
- Add JS wrapper for cross-platform binary resolution
- Add GitHub Actions workflow for OIDC-based npm publishing
- Configure for darwin-arm64, darwin-x64, linux-x64, win32-x64
Plugin changes:
- Simplify apps/hook to config-only (calls `plannotator` from PATH)
- Add README with installation instructions
UI updates:
- Add theme toggle to Landing nav
- Update footer with author links and copyright
- Update copy: "How it works" step 3, Solution section
- Flip hero image horizontally
- Fix tater sprite z-index (behind dialogs)
- Change tater breakpoint from lg to md (show on tablets)
- Remove icon from editor header
Package metadata:
- Update root and npm package.json with full metadata
- Set version to 0.1.0
- Set license to BSL-1.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>