mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
feat/opencode2-native-commands
731 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bcddced148 |
perf(review): debounce search highlights and O(1) active match swap (#407)
* perf(review): debounce search highlights and O(1) active match swap Split the single search highlight effect into two paths: 1. Full rebuild (debounced 100ms) — fires on query/matches/file/style changes, collapsing rapid keystrokes into one DOM update instead of one per character typed. 2. Active swap (instant) — fires when stepping between matches (Enter/Shift+Enter), changing two elements' background color via querySelector instead of tearing down and rebuilding every <mark> in the shadow DOM. Also removes augmentedDiff from the effect deps (was triggering unnecessary rebuilds on context expansion) and exports clearSearchHighlights for the immediate-clear path on Escape. Closes #405 For provenance purposes, this commit was AI assisted. * perf(review): debounce search query and pre-index searchable lines The search input was laggy on large PRs because findReviewSearchMatches re-parsed every file's patch and scanned all lines synchronously on every keystroke, blocking React from re-rendering the input value. Two complementary fixes: 1. Pre-index: Build SearchableLine[] once when files change (useMemo), with pre-lowercased normalizedText, so matching skips repeated patch parsing and per-line toLowerCase() calls. 2. Debounced query: searchQuery updates instantly (input responsive), debouncedSearchQuery trails by 200ms and drives all expensive work. During the pending window, sidebar shows "Searching…", diff clears stale highlights, match count hides, and stepping is disabled. Also: refactors findReviewSearchMatches to delegate to the new findMatchesInIndex so there is a single matching implementation, removes the now-redundant 100ms DOM debounce in DiffViewer (query debounce handles it upstream), trims query in highlightQuery to prevent whitespace-only edits from breaking snippet highlighting, and restores augmentedDiff in the highlight effect deps. For provenance purposes, this commit was AI assisted. |
||
|
|
232cae265e |
feat(review): add expand/collapse all buttons to file tree sidebar (#403)
Add two icon buttons in the file tree header that allow expanding or collapsing all directory groups at once. |
||
|
|
401793e35f |
feat: custom display name + config file foundation (#399)
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 |
||
|
|
a8260eb389 | Sync github Viewed files (#393) | ||
|
|
81f29dfe6e | unix update command for wsl users on UpdateBanner (#395) | ||
|
|
d49fb49281 |
chore: bump version to 0.15.2
For provenance purposes, this commit was AI assisted. |
||
|
|
919f582e0c |
feat: support folder annotation via plannotator annotate <folder/> (#389)
* feat: add hasMarkdownFiles() and folderPath support for annotate servers Add hasMarkdownFiles() to resolve-file.ts — a shared validation function that checks if a directory contains at least one markdown file, skipping IGNORED_DIRS. Lives alongside the existing walkMarkdownFiles pattern. Both annotate servers (Bun + Pi) accept an optional folderPath that overrides projectRoot in the /api/plan response, and a new "annotate-folder" mode value. For provenance purposes, this commit was AI assisted. * feat: support folder annotation via plannotator annotate <folder/> Wire folder detection through both CLI entry points (Hook + Pi) and the plan editor UI. When a directory is passed instead of a file: - CLI validates markdown files exist via hasMarkdownFiles() - Server starts with empty markdown and mode "annotate-folder" - UI clears demo content, auto-opens sidebar to Files tab - Empty state prompts user to select a file from the tree - Linked doc system handles per-file annotation caching - Feedback exports with per-file headers via existing infrastructure Single-file annotation is unchanged. For provenance purposes, this commit was AI assisted. * fix: address review findings for folder annotation - Include .mdx files in file browser globs (reference-handlers.ts) - Fix imageBaseDir in folder mode — use folder path directly instead of stripping last segment, which produced the parent directory - Guard statSync in Pi extension with try/catch to match hook server pattern For provenance purposes, this commit was AI assisted. * fix: align folder validation exclusions with file browser hasMarkdownFiles() now accepts an excludedDirs parameter. Both CLI entry points pass FILE_BROWSER_EXCLUDED so the preflight check matches the file browser — no more empty sidebar after validation. For provenance purposes, this commit was AI assisted. |
||
|
|
5e36960698 |
feat: add /plannotator-archive slash command (#388)
* feat: add /plannotator-archive slash command for Claude Code and OpenCode The archive browser was only accessible via CLI (plannotator archive) and Pi (/plannotator-archive). This adds slash command parity so users can browse saved plan decisions from within Claude Code and OpenCode sessions. Relates to #362 For provenance purposes, this commit was AI assisted. * fix: re-fetch archive plans with custom path in standalone mode The server pre-loads plans from ~/.plannotator/plans/ (default) because no runtime passes customPlanPath. For users with a custom save directory, the plan list was wrong and clicking any plan 404'd. Calling fetchPlans() after init() re-fetches with the user's cookie-based custom path setting. For provenance purposes, this commit was AI assisted. |
||
|
|
5773f55952 | Fix Pi plan tool scoping (#387) | ||
|
|
afc476c310 |
chore: bump version to 0.15.0
For provenance purposes, this commit was AI assisted. |
||
|
|
3ac3d5bc17 |
fix(ui): sidebar resize handle touch area + linked doc border styling
ResizeHandle: the v0.14.5 archive browser PR simplified the touch area to treat both sides identically, making the left sidebar handle ungrabable. Now uses asymmetric positioning per side — extends slightly toward the panel, more toward the content, without covering scrollbars. Viewer: linked doc border reduced from 2px full-opacity primary to 1px at 40% opacity for a subtler visual distinction. For provenance purposes, this commit was AI assisted. |
||
|
|
f96758da0a |
feat(pi): complete Pi server rewrite — modular architecture, full Bun parity, shared code extraction (#382)
* 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> |
||
|
|
54a00e8552 |
feat: resizable split pane for code review diff viewer (#383)
Add a draggable divider between left/right panes in split diff view, allowing users to resize each side. Uses CSS custom properties that inherit into Pierre's shadow DOM for zero-rerender drag performance. Ratio persists across sessions via cookie storage. Also adds pierre-guard skill to protect the @pierre/diffs integration surface (shadow DOM selectors, CSS variables, grid layout assumptions). For provenance purposes, this commit was AI assisted. |
||
|
|
d4ff4a8df7 |
feat(ai): OpenCode provider via HTTP + SSE (#379)
* feat(ai): add OpenCode provider via HTTP + SSE Bundle @opencode-ai/sdk to spawn `opencode serve` and communicate via HTTP REST + SSE. Richest provider: fork, resume, streaming, tools, and runtime permission approvals all work natively. - OpenCodeProvider: lazy server spawn, shared HTTP client, dynamic model discovery via GET /provider - OpenCodeSession: SSE event drain loop, system prompt via API field, permission reply (once/reject), abort via HTTP - mapOpenCodeEvent: SSE → AIMessage mapping (text deltas, tool lifecycle, permissions, session status, errors) - 11 new event mapping tests (78 total, 157 expect calls) For provenance purposes, this commit was AI assisted. * docs(ai): add OpenCode to AI features guide and code review docs For provenance purposes, this commit was AI assisted. * fix(ai): prevent double server spawn in OpenCode ensureServer() Use a promise-mutex pattern so concurrent callers (e.g. fetchModels + createSession) share a single server spawn instead of racing to create two. Also remove empty finally block from SSE drain loop. For provenance purposes, this commit was AI assisted. * fix(ai): await SSE subscribe in OpenCode provider The SDK's event.subscribe() is async — must be awaited before destructuring the stream. For provenance purposes, this commit was AI assisted. * fix(ui): scrollable model dropdown with search filter Cap dropdown height at min(320px, 50vh) so it doesn't overflow the viewport when providers expose many models (e.g. OpenCode with 20+). Add a search/filter input when there are more than 8 models. For provenance purposes, this commit was AI assisted. * fix(ai): close SSE stream, rename to opencode-sdk, move dep to root - Close SSE stream in finally block to prevent connection leak on abort/early return - Rename provider from opencode to opencode-sdk for consistency with claude-agent-sdk, codex-sdk, pi-sdk - Move @opencode-ai/sdk dependency to root package.json alongside the other SDK dependencies For provenance purposes, this commit was AI assisted. * feat(ui): add first-run AI setup dialog for provider selection Show a one-time modal on first review when AI providers are available. Users pick a default provider and model from a 2-column grid, with a brief feature intro and auth disclaimer. Persists via cookie, can be changed later in Settings. For provenance purposes, this commit was AI assisted. * fix(ai): clear startPromise on failure so OpenCode provider can retry If doStart() fails (port conflict, timeout, binary crash), the cached rejected promise prevented all future ensureServer() calls from retrying. Now cleared on failure and on dispose(). Also updates stale "future" comment in types.ts. For provenance purposes, this commit was AI assisted. |
||
|
|
877b0c78ac |
feat(ai): Pi provider via RPC subprocess (#377)
* refactor(ai): extract BaseSession and buildEffectivePrompt Pull shared session lifecycle (~80 lines duplicated across Claude and Codex providers) into BaseSession: query guard, abort, ID resolution, generation counter. Extract first-query prompt prepending into buildEffectivePrompt() in context.ts. Both providers now extend BaseSession. Behavior-preserving — all 54 existing tests pass unchanged. For provenance purposes, this commit was AI assisted. * feat(ai): add Pi provider via RPC subprocess Spawns `pi --mode rpc` and communicates via JSONL over stdio. No Pi SDK bundled — user must have the pi CLI installed. Provider auto-discovers available models at startup via get_available_models. Includes: PiProcess (JSONL client), PiSDKProvider, PiSDKSession extending BaseSession, mapPiEvent for streaming, Pi icon in provider UI, and 12 new tests (9 event mapping + 3 buildEffectivePrompt). For provenance purposes, this commit was AI assisted. * fix(ai): harden Pi provider error paths - Surface startup errors when Pi process dies (bad config, missing API keys) instead of hanging the chat indefinitely - Map tool-call errors as non-terminal tool_result instead of AIErrorMessage, so Pi can continue reasoning after a failed tool call - Fix subprocess leak in fetchModels() by moving proc.kill() to finally block For provenance purposes, this commit was AI assisted. * fix(ai): detect Pi prompt rejections and process crashes - Use sendAndWait for prompt command so RPC-level rejections (expired credentials, invalid session) surface as errors instead of hanging - Emit process_exited instead of synthetic agent_end when Pi subprocess dies, so crashes show as errors instead of silent empty successes For provenance purposes, this commit was AI assisted. * docs(ai): add Pi to AI features guide, code review docs, and installation For provenance purposes, this commit was AI assisted. * style(ai): apply biome formatting to new files For provenance purposes, this commit was AI assisted. |
||
|
|
b675e8677c |
feat: markdown file browser sidebar tab (#378)
* feat: markdown file browser sidebar tab (#367) Add a Files sidebar tab that lets users browse and annotate markdown files from their project. The project root is shown automatically — no configuration needed. Users can add extra directories in Settings. - Server: new /api/reference/files endpoint scans directories for .md files with broad exclusion list (node_modules, .git, dist, etc.) - Server: projectRoot sent in /api/plan response (plan + annotate) - UI: FileBrowser component with per-directory collapsible sections - UI: useFileBrowser hook manages multi-directory tree state - UI: Files tab in sidebar (collapsed + expanded), between Versions and Vault - UI: Settings integration tab for additional directories - Wired into linked doc system for annotation support Closes #367 For provenance purposes, this commit was AI assisted. * fix: simplify file browser - compute activeDirPath, fix re-fetch bug, dedupe settings logic - Compute activeDirPath from activeFile + dirs instead of storing as separate state (removes state setter hack that abused setDirs as a read accessor) - Fix auto-fetch not detecting directory changes: compare loaded paths against configured dirs instead of checking dirs.length === 0 - Extract addDirectory() helper in Settings to eliminate copy-pasted trim-validate-add-clear logic - Remove "what" comments that described obvious code For provenance purposes, this commit was AI assisted. * fix: file browser prefix match, settings reactivity, and vault→files routing - Add trailing slash to activeDirPath startsWith check to prevent prefix collisions - Spread uiPrefs in handleFileBrowserChange so React sees a new reference on each settings edit - Clear vaultBrowser.activeFile when opening a file browser doc to prevent misrouted links For provenance purposes, this commit was AI assisted. |
||
|
|
787923182b |
fix: allow empty submit and count linked doc annotations in annotate mode (#376)
* fix: allow empty submit and count linked doc annotations in annotate mode (#371, #373) Annotate mode previously blocked submission when the main document had no annotations, even if linked documents did. This removes the validation gate in annotate mode entirely — the button always submits. The label dynamically shows "Done" when empty or "Send Annotations" when any annotations exist (including linked docs via a new reactive docAnnotationCount from useLinkedDoc). Also fixes plan mode's Send Feedback button to include linked doc annotations in its validation check, and updates the empty feedback message to "User reviewed the document and has no feedback." Closes #371, closes #373 For provenance purposes, this commit was AI assisted. * fix: check linked doc annotations in Cmd+Enter keyboard shortcut path The button click handler was updated to check getDocAnnotations() but the keyboard shortcut path was missed, causing Cmd+Enter to approve instead of sending feedback when only linked docs had annotations. For provenance purposes, this commit was AI assisted. |
||
|
|
4b097197a0 |
feat(ai): AI backbone + inline chat for code review (#363)
* feat(ai): add provider-agnostic AI backbone for inline chat Introduces @plannotator/ai — a pluggable provider layer that powers AI features (inline chat, plan Q&A, code review assistance) across all Plannotator surfaces. - ProviderRegistry: class-based, multi-instance provider management - SessionManager: in-memory session tracking with eviction, ID aliasing - Context builders: system prompts and fork preambles for all three modes - SSE streaming endpoints: session create/fork, query, abort, list - Claude Agent SDK provider: fork from parent sessions, read-only tools, per-query abort, cached SDK import, concurrent query guard - 34 passing tests covering all layers For provenance purposes, this commit was AI assisted. * fix(ai): address code review findings from PR #363 - Concurrent query race: reject with session_busy error instead of racing; add query generation counter so stale finally blocks don't clobber newer query state - Text dropped before tool_use: mapSDKMessage now returns AIMessage[] and flushes accumulated text before each tool_use block - defaultProvider returns instance ID: ProviderRegistry.getDefault() returns { id, provider } so capabilities endpoint sends the correct key for multi-instance setups - Fork preamble re-sent on error: use _firstQuerySent boolean instead of _resolvedId to guard preamble injection - Dead abortController params: remove unused abortController from CreateSessionOptions and AIProvider.resumeSession - Unused context in memory: remove context from SessionConfig and baseConfig — already consumed to build systemPrompt/forkPreamble - Fix tool_result mapping: SDK wraps tool results in SDKUserMessage (type "user"), not a "tool_result" type For provenance purposes, this commit was AI assisted. * feat(ai): inline AI chat on code review diffs Add "Ask AI" to the code review toolbar — select lines, ask questions about agent-written code, get streaming responses. Conversation persists across file switches with inline markers on the diff showing where questions were asked. - useAIChat hook: SSE streaming, lazy session creation, multi-turn - AskAIInput: question input with per-selection AI history - InlineAIMarker: compact sparkle markers on diff lines via @pierre - AITab: sidebar conversation grouped by file with collapsible sections - SparklesIcon: shared 3-star sparkle from automations branch - ReviewPanel: AI tab with message count badge, file group flash highlight - Server: mount AI endpoints in review server with graceful fallback - Support three question scopes: line, file, and general - Toolbar ergonomics: typed text submits as AI question, no double popover For provenance purposes, this commit was AI assisted. * fix(ai): toolbar popover ergonomics and visual consistency - X button closes entire toolbar in both modes (no ghost popover) - Comment/Ask AI toggle is inline in the action row, same position in both modes (left side, mirrored layout) - AI history appears below the input, not above - File group flash highlight uses --muted to match file tree hover For provenance purposes, this commit was AI assisted. * fix(ai): visual consistency, shared primitives, markdown rendering Visual consistency: - AI tab file group headers match FileTree pattern (sizing, hover, badges) - Q&A bubbles match annotation card padding and border-radius - Empty state uses circle-bg icon (same as annotations) - General input matches search input styling - Removed redundant CSS classes, moved to Tailwind Shared primitives: - Extract formatTimestamp to shared utility (was duplicated) - CountBadge component for consistent badge styling - CopyButton component with hover-reveal + "Copied" flash Affordances: - Copy button on AI responses (hover-reveal) - Send button on general input with keyboard shortcut hint - Timestamps on Q&A pairs - Keyboard shortcut title on Ask button Markdown rendering: - Add marked dependency for full block-level markdown in AI responses - renderMarkdown utility using marked + DOMPurify - Scoped .ai-markdown CSS for headings, lists, code blocks, blockquotes For provenance purposes, this commit was AI assisted. * feat(ai): permission inheritance + inline approval UI The inline AI agent now inherits the user's existing Claude Code permission rules via settingSources: ['user', 'project']. Tools already approved in ~/.claude/settings.json auto-approve silently. For tools not covered by existing rules, the SDK emits control_request messages. These are mapped to a new AIPermissionRequestMessage type and surfaced as inline approval cards in the AI tab — showing the tool name, command/input, and Allow/Deny buttons. Backend: - Map control_request to AIPermissionRequestMessage in provider - Store Query object on session for sending control_response back - respondToPermission() method on AISession sends allow/deny - POST /api/ai/permission endpoint for frontend → backend decisions - Add settingSources to ClaudeSDKQueryOptions Frontend: - PermissionCard component with Allow/Deny buttons + resolved state - useAIChat tracks permissionRequests array - respondToPermission() POSTs decision to server - AITab renders pending permission cards inline in conversation For provenance purposes, this commit was AI assisted. * fix(ai): streaming reactivity, maxTurns, popover→sidebar linking Streaming fixes: - State updates now create new object references via prev.map() instead of mutating objects inside the array — fixes React not re-rendering the sidebar during streaming - Complete assistant text messages only used as fallback when no streaming deltas received (prevents duplicate text) - Result text used as fallback when response is empty (agent spent all turns on tools) Config: - maxTurns raised to 99 (was 3 — agent ran out of turns reading files and never got to write its answer) Popover → sidebar linking: - Clicking AI history items in comment popover now passes questionId through to handleViewAIResponse, triggering the file group flash highlight in the sidebar (same behavior as clicking inline markers) Debug logging (temporary): - Server-side: [AI STREAM] logs every message the provider yields - Client-side: [AI SSE] logs every message received in the browser For provenance purposes, this commit was AI assisted. * refactor(ai): simplification pass — types, perf, cleanup From the three-agent code review analysis: Quality: - Rename AIMessage → AIChatEntry in review-editor to avoid collision with the wire-protocol AIMessage from packages/ai - Type permissionMode as union (not string) in SessionConfig + QueryOptions - Type _activeQuery with narrow interface instead of any - Make toolUseId optional on AIToolResultMessage (SDK doesn't populate it) - Remove redundant sessionIdRef double-write in useAIChat - Fix handleAskGeneral useCallback dep: [aiChat] → [aiChat.ask] Performance: - Wrap QAPair in React.memo to prevent re-renders on sibling updates - Memoize renderMarkdown output via useMemo (avoid re-parsing on every token) - Fix auto-scroll useEffect: depend on messages.length not messages ref (prevents querySelectorAll on every streaming token) Cleanup: - Remove debug console.log from SSE endpoint and browser parser - Extract isMac/submitHint to shared utils/platform.ts - Extract generateId to shared utils/generateId.ts Net: -2 lines. No behavior changes. For provenance purposes, this commit was AI assisted. * chore: biome lint fixes — import types, button a11y, unused import - Use `import type` where only types are consumed (endpoints, CountBadge, CopyButton, renderMarkdown) - Remove unused AIContext import from session-manager - Template literal instead of string concat in context.ts truncate() - Add type="button" + aria-hidden on CopyButton SVGs For provenance purposes, this commit was AI assisted. * feat(ai): Codex SDK provider + multi-provider backend - Add CodexSDKProvider implementing AIProvider interface - Maps Codex ThreadEvents to AIMessage types - Delta tracking for streaming text from cumulative item.updated events - Supports resume, abort, sandbox modes - Factory self-registration pattern matching Claude provider - Enhance capabilities endpoint with provider models + metadata - Register both providers in review server (graceful degradation) - Add CodexSDKConfig type, models list (GPT-5.4 family) - Add reasoningEffort to CreateSessionOptions (Codex only) - Extract DiffFile interface to shared types.ts - Update CLAUDE.md with build order documentation - 54 tests passing (20+ new for Codex event mapping) For provenance purposes, this commit was AI assisted. * feat(ai): provider settings UI + inline config bar - Settings → AI tab: provider cards with per-provider model dropdowns - Inline config bar in AI chat sidebar (provider, model, reasoning effort) - Provider icons: Claude SVG + Codex PNG from marketing assets - Cookie-based persistence for provider + per-provider model preferences - useAIChat accepts providerId, model, reasoningEffort from props - Session reset on provider/model switch (next question starts fresh) - Reasoning effort selector (Codex only: Low/Medium/High/Max) - Update code review docs with AI provider documentation For provenance purposes, this commit was AI assisted. * fix(ai): review fixes — reasoning props, input blocking, cwd resolution, codex-sdk dep - Add missing selectedReasoningEffort + onReasoningEffortChange props to populated-state AIConfigBar (was only on empty-state, causing crash) - Disable GeneralInput while streaming to prevent concurrent queries - Thread cwd through CreateSessionOptions so AI sessions use the correct working directory (worktree-aware via getCwd resolver) - Add @openai/codex-sdk as a real dependency (was only an optional peer) - Eagerly verify codex-sdk importability at startup to avoid advertising a broken provider For provenance purposes, this commit was AI assisted. * fix(ai): polish config bar, input, and sidebar header - Replace native <select> dropdowns with custom popover menus that show provider icons, model names, and checkmarks on active selection - Remove disabled gear placeholder button - Auto-grow textarea upward on multi-line input (capped at 6 lines) - Push tab icons to far right in sidebar header (w-full on flex container) - Add centered divider between file-scoped and general questions For provenance purposes, this commit was AI assisted. * fix(ai): honest fork capability + permissionMode doc accuracy - Endpoint now checks capabilities.fork before calling forkSession(), falling back to createSession() for providers that can't truly fork - Codex forkSession() now throws as the interface contract requires (was silently faking a fork with no history — misleading UX) - Fixed permissionMode JSDoc: said "plan" but code defaults to "default" For provenance purposes, this commit was AI assisted. * fix(review): move search result count inside input field Count now appears inline to the left of the X button instead of squeezing the input from outside. Dynamic right padding accommodates the count + clear button when a query is active. For provenance purposes, this commit was AI assisted. * fix(ai): use claude_code preset, pass selected code to queries - Claude provider now uses { preset: "claude_code", append } instead of a raw system prompt, so inline chat inherits Claude Code's full built-in capabilities - Stripped custom role/guidelines preamble from context builders — just emit the review context (diff, plan, document) - handleAskAI now extracts selected code via extractLinesFromPatch and sends it with the query, fixing hallucinations on large PRs where the file's diff was truncated out of the system prompt For provenance purposes, this commit was AI assisted. * fix(ai): bundle both SDKs, remove peer dependencies Both @anthropic-ai/claude-agent-sdk and @openai/codex-sdk are now direct imports (no string indirection) so bun build --compile bundles them into the binary. Users don't need to install SDKs separately. Removed peer dependency declarations from packages/ai/package.json — meaningless for a compiled binary. For provenance purposes, this commit was AI assisted. * refactor: canonical platform.ts, file renames, file-level comments - Created packages/ui/utils/platform.ts as the single source for isMac, modKey, altKey, submitHint, isWindows - Consolidated inline navigator.platform checks from 6 files - Renamed renderMarkdown → renderChatMarkdown (distinct from plan editor) - Renamed formatTimestamp → formatRelativeTime (clearer purpose) - Added file-level comments to InlineAIMarker, PermissionCard, generateId For provenance purposes, this commit was AI assisted. * docs(ai): add AI features guide + link from settings New docs page at guides/ai-features covering providers, models, configuration, session behavior, permissions, and available settings. AISettingsTab now links to the docs in both empty and populated states. For provenance purposes, this commit was AI assisted. |
||
|
|
a278fdaf77 |
feat: plan archive browser (#369)
* 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. |
||
|
|
b545969b00 |
chore: bump version to 0.14.5
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
0d13d208a8 |
fix: remove duplicate 'Code Review' header in pi extension review feedback (#370)
exportReviewFeedback() already includes a '# Code Review Feedback' header in its output. The pi extension was wrapping that output with another '# Code Review Feedback' prefix, causing the heading to appear twice when submitting review feedback. Remove the redundant header from sendUserMessage() so the pre-formatted feedback is sent directly. Add tests verifying exportReviewFeedback output contains exactly one top-level heading, preventing future regressions. |
||
|
|
40002e69dc |
feat: GitLab merge request review support (#364)
* feat: GitLab merge request review support Add full GitLab MR review parity with existing GitHub PR review: - Auto-detect platform from URL (github.com vs any GitLab host) - Extract GitHub logic into pr-github.ts, new pr-gitlab.ts implementation - Widen PRRef/PRMetadata to discriminated unions for type safety - Dispatch functions route to correct platform implementation - Platform-aware UI labels (PR/MR, #/!, GitHub/GitLab icons) - Self-hosted GitLab support via --hostname flag - Normalize glab diff output to standard git format - Handle glab CLI differences (no --jq, Content-Type header for --input) - Defensive JSON parsing for GitLab context API responses Tested against gitlab.com with inline comments, multi-line ranges, approval, and PR context tabs (summary, comments, checks). For provenance purposes, this commit was AI assisted. * fix: correct GitLab enum mappings and add shared file path encoding - Map GitLab job statuses to UI-expected enums (failed→FAILURE, canceled→NEUTRAL) - Map GitLab detailed_merge_status to CLEAN/BLOCKED/BEHIND/DIRTY/UNKNOWN - Fix false approval state on repos without required approvers - Add shared encodeApiFilePath helper used by both GitHub and GitLab For provenance purposes, this commit was AI assisted. * fix: align panel headers and refine file tree selection style - Use shared --panel-header-h CSS variable for consistent header heights across file tree search, file header, and annotations panel - Update GitLab icon to use official tanuki SVG paths with currentColor - Replace solid primary fill on active file tree items with 30% tinted background for better readability and semantic color preservation For provenance purposes, this commit was AI assisted. |
||
|
|
c5204770d2 |
chore: bump version to 0.14.4
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
181e23711a |
fix: eliminate gaps around file header in code review
Move FileHeader out of the scroll container into a flex-col wrapper so it spans the full panel width. Make resize handles zero-width overlays instead of layout-occupying flex children. Closes #354 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
c352465254 |
feat: Approve and review PR to github (#352)
* Approve and review PR to github
* fix: GitHub review improvements — multi-line ranges, own-PR detection, error handling
- Add start_line/start_side to review payload for multi-line annotations
- Detect current GitHub user and disable Approve on own PRs with tooltip
- Fix loading state: approve button no longer shows "Approving..." during feedback
- Fix empty submit: block Post Comments when no annotations and no comment
- Fix Cmd+Enter in GitHub comment dialog
- Fix completion screen text for GitHub mode ("submitted to GitHub")
- Fix agent label fallback to "Your agent" instead of "OpenCode"
- Improve gh-not-installed error message with install URL
- Fix stale useCallback dependency array on handleGitHubAction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(review): destination dropdown, icon components, and UI polish
Replace Agent/GitHub toggle with a compact destination dropdown.
Buttons stay consistent ("Send Feedback"/"Post Comments" + "Approve")
with behavior routed by destination. Double-tap Option to toggle.
- Add GitHubIcon, RepoIcon, PullRequestIcon components (currentColor)
- Add repo/PR icons to editor header
- Add keyboard hint in destination dropdown
- Add double-Option shortcut to settings
- Hide duplicate filename from Pierre diff header
- Refine FileHeader typography (text-xs font-semibold, no mono)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: include VS Code editor annotations in GitHub PR reviews
Editor annotations were silently dropped when submitting to GitHub.
Now converts them to inline PR comments with selected text as context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rename visibility, own-PR keyboard guard, and editor annotation filtering
- Keep data-prev-name visible in Pierre header for rename diffs
- Block Cmd+Enter approve path for own PRs (was only blocked via mouse)
- Filter editor annotations to diff files only to prevent GitHub API rejection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
||
|
|
33d5d6154c |
fix: prevent resize handle from covering scrollbar (#359)
* fix: prevent resize handle touch area from covering scrollbar (#354) The ResizeHandle touch target extended 8px in both directions (-inset-x-2), overlapping the 6px scrollbar on the adjacent content area. Users could see the scrollbar but couldn't grab it because the resize handle intercepted clicks. Add a `side` prop so the touch area only extends outward (away from the scroll container) — left handles extend left, right handles extend right. Closes #354 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove transform/opacity from global transition to fix choppy scroll The global `*` rule applied 150ms transitions on transform and opacity to every DOM element. During scroll, the compositor fights these transitions on sticky headers, annotation highlights, and layer compositing — causing visible choppiness. Narrowed to color, background-color, border-color, box-shadow only. These handle theme switching and hover states without affecting scroll performance. Components needing transform/opacity transitions already declare their own via Tailwind classes (205 usages across 45 files). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
7ed85dfe0b |
fix(parser): support nested markdown code fences (#355)
* fix(parser): support nested markdown code fences Count backticks in the opening fence and only close on a line with at least that many backticks. Previously any ``` line would prematurely close a 4- or 5-backtick fence, corrupting blocks that embed markdown examples. Follows CommonMark §4.5. Adds parser.test.ts with 6 tests covering the fix and baseline triple-backtick behaviour. Also adds devbox.json to pin bun for running tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(devbox): wire up test script to bun test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(parser): pass raw line to closingFence regex; add edge-case tests The regex already anchors trailing whitespace with \s*$ so calling .trim() before testing was redundant. Passing the raw line is also more correct — it lets the regex reject a closing fence indented more than three spaces (CommonMark §4.5 semantics), not that the rest of the parser handles indented blocks today, but the intent is clearer. Also adds three previously untested edge cases: - Unclosed fence at EOF (extends to end of document per spec) - Fence opener as the last line (zero-content block, no crash) - toHaveLength(1) guard on the language-tag preservation test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Overcomit * Overcomit --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
29f09d3288 |
feat: show repo identifier in tab title (#342) (#353)
* feat: show repo identifier in tab title (#342) * fix: fallback title when repoInfo is null |
||
|
|
d0ebca45eb |
chore: bump version to 0.14.3
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
1821943d06 |
feat(review): PR context panel — summary, comments, and checks (#350)
* feat(review): add PR context panel with summary, comments, and checks tabs When reviewing a GitHub PR, the ReviewPanel now shows icon tabs for Summary (PR body, labels, linked issues), Comments (chronological thread of comments + reviews), and Checks (CI status, merge readiness). Tabs only appear in PR mode; local review is unchanged. Data fetched lazily via new /api/pr-context endpoint on first tab click. Uses DOMPurify for safe HTML rendering of GitHub markdown content. Inline markdown renderer extended with link and bare URL support. Closes #348 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(review): reset fetch guard on error and detect inline HTML in PR bodies Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
0908e234e1 |
feat(review): VS Code-style diff search in file tree sidebar (#347)
* feat(review): add diff search engine and highlight utilities Search data layer for code review: patch parser, match finder, file grouping, snippet extraction, DOM highlight injection with shadow DOM traversal, and state management hook. Co-Authored-By: sercan tor <tor.sercan@gmail.com> * feat(review): wire search into file tree sidebar VS Code-style search UI in the file tree panel. Search input at the top of the sidebar replaces the tree with grouped results when active. Clicking a match navigates to the file and scrolls to the line. Keyboard: Cmd/Ctrl+F focuses input, Enter/F3 steps through matches, Escape clears. In-diff highlights via shadow DOM traversal. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(review): correct highlight assignment for duplicate matches and trimmed queries Group search matches by line before highlighting so each DOM occurrence gets the correct match ID. Also trim the query before building the highlight regex to stay consistent with the search engine. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: sercan tor <tor.sercan@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
ac4d03e069 |
chore: bump version to 0.14.2
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
b632131aac |
chore: bump version to 0.14.1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
1df5f40eb7 |
feat: persist viewed files in code review drafts (#332) (#335)
Extend the draft auto-save system to include viewed file state alongside annotations. Viewed files are saved atomically in the same draft file, restored together via the same banner, and backward compatible with existing drafts (optional field, defaults to empty). Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
d5b6bc702b |
fix: single submit_plan with text/path auto-detect for OpenCode (#333)
* fix: restore uniform submit_plan and add submit_plan_file for OpenCode PR #318 replaced the original text-based submit_plan with a file-path-only version and split prompt injection between plan/non-plan agents. This caused regressions: the agent couldn't figure out file paths from non-plan agents, and the 80+ line prompt with TodoWrite replacements was fragile against OpenCode upstream changes. This restores the original submit_plan(plan) that accepts markdown text directly — the contract that worked uniformly across all agents — and keeps the file-based workflow as submit_plan_file(path) for users who want persistent plan files. Key changes: - Two tools: submit_plan (text) + submit_plan_file (path) - Unified prompt for all primary agents (not just plan mode) - Removed aggressive TodoWrite string replacements and system-reminder - Kept adversarial stripping of OpenCode's STRICTLY FORBIDDEN rules - Extracted shared server helpers to reduce duplication Addresses #328 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: single submit_plan with auto-detect text/path for OpenCode Collapses submit_plan + submit_plan_file back into one tool that auto-detects whether the argument is plan text or a file path. First submission: agent passes markdown text (simple, works from any agent). On deny: response includes the history path where the plan was saved, so the agent can Edit the file for targeted revisions and resubmit with the path. Key changes: - One tool: submit_plan(plan) accepts text or absolute .md file path - Server surfaces historyPath through waitForDecision (already saved by saveToHistory, just not returned before) - Deny response includes file path hint for Edit-based revision workflow - Unified prompt for all primary agents (~25 lines, no TodoWrite warfare) - Still strips OpenCode's STRICTLY FORBIDDEN rules and suppresses plan_exit Addresses #328 and rcdailey's feedback on PR #333 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove historyPath leak, error on missing file paths, restore two-tier prompt - Remove historyPath from deny flow — internal history directory should not be exposed to agents. Text submissions get text feedback; file submissions get file feedback. No crossover. - Error when agent passes an absolute .md path that doesn't exist instead of silently treating it as plan text. - Restore two-tier prompt: plan agent gets full planning instructions, other primary agents get a minimal reminder (matching pre-v0.13.0 behavior). - Clean up stale prompt copy referencing historyPath-based revision flow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore todowrite override, strengthen planning prompt, remove noisy logs - Override todowrite description to defer to submit_plan during active planning - Tool description now instructs agent to explore and ask questions before submitting - Sequenced planning prompt: explore, ask, then write - Remove success logs from integration saves (stderr was bleeding through) - Append resubmit reminder to OpenCode deny feedback Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
9056e18af7 | fix: preserve slashes in Bear tag normalization for nested tags (#331) | ||
|
|
54d50f8beb |
chore: bump version to 0.14.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
216815c12e |
feat: PR review support via GitHub URL (#324)
* feat(review): add runtime-agnostic PR provider Introduces `packages/shared/pr-provider.ts` with a `PRRuntime` interface (same pattern as ReviewGitRuntime in review-core.ts) and GitHub PR operations: URL parsing, auth check, diff/metadata fetching, and file content retrieval via `gh` CLI. `packages/server/pr.ts` is the Bun wrapper that pre-binds the runtime, matching the git.ts pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(review): add PR mode to review server When `prMetadata` is provided to `startReviewServer`, the server enters PR mode: `/api/diff` includes PR metadata and omits gitContext, `/api/diff/switch` and `/api/git-add` return 400 (not applicable), and `/api/file-content` fetches from GitHub API using base/head SHAs instead of local git. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(review): PR review flow for Claude Code and OpenCode Detects URL argument in `/plannotator-review` command. When a GitHub PR URL is provided, fetches diff and metadata via `gh` CLI and starts the review server in PR mode. Local review mode is unchanged when no URL is passed. Updates slash command definitions to pass $ARGUMENTS through. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(review): PR metadata display in review UI Shows "PR Review" badge, PR title with link, and owner/repo in the header when reviewing a pull request. Diff switcher and staging controls auto-hide since gitContext is omitted in PR mode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(review): contextualized PR feedback for agent In PR mode, the feedback markdown now includes PR metadata (repo, number, title, branches, URL) so the agent has full context about the remote PR being reviewed. Removes the aggressive "address all of them" instruction in PR mode since the content is self-explanatory. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(review): extract exportReviewFeedback + add tests Moves the pure feedback construction function from App.tsx to utils/exportFeedback.ts so it can be unit tested. Drops the unused `files` parameter. 11 tests covering local/PR headers, annotation grouping, sorting, file-scope ordering, and suggested code rendering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(review): strengthen PR/local boundary tests Replaces shallow header checks with comprehensive boundary assertions: local mode must never contain PR-specific content (repo, URL, branches), PR mode must include all context fields and exclude the generic header. Covers null/undefined prMetadata edge cases. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(review): remove redundant empty-annotations test Covered by the "no annotations: returns generic empty regardless of prMetadata" test which checks all three cases (no arg, null, PR mode). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(review): guard fetchPR, fix UTF-8 decoding, correct useCallback deps - Wrap fetchPR() in try/catch in both hook and OpenCode entry points so network/auth errors show a clean message instead of a stack trace - Replace atob() with Buffer.from() for UTF-8 correct base64 decoding of PR file content from GitHub API - Fix stale useCallback deps in handleCopyFeedback (files → prMetadata) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(install): add $ARGUMENTS to review command for PR URL support Install scripts were writing the review slash command without $ARGUMENTS, so PR URLs passed to /plannotator-review were silently dropped. Also switches PS1 heredoc to single-quoted to prevent $ARGUMENTS from being expanded as a PowerShell variable. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add PR review support to docs and READMEs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
6b775ea1ed |
feat: /plannotator-last — annotate the last agent message (#325)
* feat: add /plannotator-last command to annotate last assistant message
Adds a new slash command that extracts the last rendered assistant message
from Claude Code's session log and opens it in the annotation UI.
Session log parser (apps/hook/server/session-log.ts):
- Parses Claude Code JSONL logs at ~/.claude/projects/{slug}/*.jsonl
- Finds the last assistant message.id with text content blocks
- Skips noise entries (progress, system, file-history-snapshot, queue-operation)
- Filters system-generated user messages by prefix to avoid false turn boundaries
- Walks backward through empty turns when back-to-back user messages exist
- No anchoring — reads from end of log since <command-message> isn't written
until after the binary completes
New files:
- apps/hook/commands/plannotator-last.md — slash command definition
- apps/hook/server/session-log.ts — Claude-Code-specific log parser
- apps/hook/server/session-log.test.ts — 30 tests covering streaming chunks,
tool call turns, sub-agent noise, stop hooks, thinking blocks, and edge cases
Modified:
- apps/hook/server/index.ts — annotate-last subcommand
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: remove 3 redundant real-world scenario tests
These duplicated coverage already provided by focused unit tests:
- "full conversation" → covered by "grabs last message.id in multi-tool turn"
- "stop hook interrupted" → covered by "skips progress and system noise"
- "long tool-only sequence" → covered by "skips tool-only assistant entries"
Kept the thinking block test (unique coverage). 27 tests remain.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add /plannotator-last command to Pi extension
Uses Pi's session manager API to find the last assistant message —
walks backward through ctx.sessionManager.getEntries(), finds the
last entry with role "assistant" and text content, opens it in the
annotation UI. Reuses existing isAssistantMessage(), getTextContent(),
startAnnotateServer(), and runBrowserReview() from the extension.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add /plannotator-last to OpenCode plugin + extract command handlers
Adds annotate-last command that fetches session messages via
client.session.messages(), finds the last assistant message with text
parts, and opens it in the annotation UI.
Refactors command handling: extracts review, annotate, and annotate-last
handlers from the inline event hook into commands.ts module. Reduces
index.ts by ~120 lines and makes adding future commands cleaner.
New files:
- apps/opencode-plugin/commands.ts — extracted command handlers
- apps/opencode-plugin/commands/plannotator-last.md — command metadata
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: context-aware UI labels for annotate-last mode
Adds "annotate-last" mode to the annotate server, passed through to the
UI via /api/plan response. The editor uses this to show "Copy message"
instead of "Copy plan", and "annotations on the message" in the
completion overlay.
- packages/server/annotate.ts: new `mode` option on AnnotateServerOptions
- packages/editor/App.tsx: annotateSource state derived from mode
- packages/ui/components/Viewer.tsx: copyLabel prop for button text
- All three harnesses pass mode: "annotate-last" in their callers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add Codex support to annotate-last command
Detects Codex via CODEX_THREAD_ID env var (injected by Codex into every
spawned process). Uses the thread ID to find the rollout file in
~/.codex/sessions/, parses the Codex rollout JSONL format to extract
the last assistant message.
Also adds `plannotator last` alias for shorter usage in Codex bang
commands (!plannotator last).
New files:
- apps/hook/server/codex-session.ts — Codex rollout parser
- apps/hook/server/codex-session.test.ts — 9 tests
Modified:
- apps/hook/server/index.ts — Codex detection + `last` alias
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: context-aware feedback title + top spacing for paragraph-first content
- exportAnnotations now accepts a title param: "Message Feedback" for
annotate-last, "File Feedback" for file annotation, "Plan Feedback"
for plan review (default)
- Adds top spacer when content starts with a paragraph (not a heading)
and has no frontmatter, fixing tight spacing in annotate-last mode
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: add sandbox scripts for Pi and Codex testing
- sandbox-pi.sh: builds extension, creates temp project, installs via
`pi install`, launches Pi with sample files
- sandbox-codex.sh: compiles binary, creates temp project, launches
Codex. Test with `!plannotator last`
Both follow the same pattern as sandbox-opencode.sh.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add hook build step to opencode sandbox script
The opencode build copies HTML from hook/dist/ — without building hook
first, the sandbox could use stale HTML. Pi and Codex sandboxes already
had this step.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove command body from plannotator-last to prevent agent response
The .md body was being sent to the agent as a prompt, causing it to
respond with "Opening annotation UI..." before the event handler could
fetch messages. That response became the "last message" instead of the
actual one. Empty body = agent stays silent, event handler intercepts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use command.execute.before hook for OpenCode annotate-last
Moves plannotator-last from the passive event hook to the
command.execute.before hook. This intercepts the command before the
agent sees it, clears output.parts so the agent stays silent, fetches
session messages, opens the annotation UI, then sends feedback via
client.session.prompt() — same pattern as review/annotate.
Previously the agent would respond to the command body before the
event handler could fetch messages, polluting the session history.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add Codex to origin type and agent name mapping
Origin "codex" was falling through to the default "Coding Agent" label.
Added "codex" to the origin union type across annotate server, editor,
and removed the `as any` cast in the hook.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remote share link, plan-specific prose, and codex type unions
- Add writeRemoteShareLink to annotate-last onReady callback so remote
sessions get a reachable URL
- Add subject parameter to exportAnnotations so feedback says "message"
or "file" instead of "plan" when appropriate
- Add 'codex' to origin type unions in useAgents, Settings, UpdateBanner,
and App.tsx fetch handler
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: correct JSDoc for projectSlugFromCwd (leading dash is kept, not stripped)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: use RenderedMessage type instead of inline structural type
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
||
|
|
9e760fb973 |
fix: opencode review cwd context flow (#323)
* fix opencode review cwd context flow * fix: sync pi-extension review-core with shared cwd changes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Michael Ramos <mdramos8@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
b4eb37f981 |
fix: proxy race condition + IPC discovery for VS Code SSH Remote (#321) (#322)
* fix: add retry + auto-reload to VS Code proxy for SSH race condition (#321) When opening plannotator over VS Code SSH Remote, the webview could show a "proxy error" because the cookie proxy connected before the upstream Bun server was ready. Two layers of defense: 1. Retry with exponential backoff (200/400/800ms) in the cookie proxy, buffering the request body so retries can replay it 2. Silent iframe auto-reload in the webview wrapper if no "ready" signal is received within 3 seconds (max 1 reload to prevent loops) Bump vscode extension to 0.5.2. * fix: file-based IPC registry for VS Code extension discovery (#321) VS Code's environmentVariableCollection only injects PLANNOTATOR_BROWSER into interactive terminal shells — background processes spawned by Claude Code hooks don't inherit it. In SSH Remote sessions this meant openBrowser() was never called (handleServerReady skipped it when isRemote=true and PLANNOTATOR_BROWSER was unset). - Extension writes IPC port to ~/.plannotator/vscode-ipc.json keyed by workspace path; cleans up on deactivate - openBrowser() falls back to IPC registry when shell-based open fails, using longest-prefix workspace match for multi-window support - handleServerReady() always calls openBrowser() since the IPC fallback handles the remote case |
||
|
|
f29dbbb1f8 | fix(review): disable external diff in git commands (#320) | ||
|
|
03390eb2c9 |
chore: bump version to 0.13.1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
fc655beb0e |
feat: Pi-style iterative planning for OpenCode plugin (#318)
* feat: extract checklist utilities to shared package Move ChecklistItem, parseChecklist, extractDoneSteps, and markCompletedSteps from Pi extension to @plannotator/shared for reuse by the OpenCode plugin. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: Pi-style iterative planning for OpenCode plugin Rewrite the OpenCode plugin to match Pi's planning methodology: - Inject rich iterative planning prompt when agent is "plan" (explore → update plan → ask user loop, structured plan format) - File-based submit_plan: reads plan from disk instead of requiring it as a string arg. Resolves path from OpenCode's system prompt, falls back to PLAN.md - Suppress plan_exit via tool.definition hook (directs to submit_plan) - Add PLANNOTATOR_ALLOW_SUBAGENTS env var for #289 - Pass planFilePath to denial feedback template - Keep existing subagent/build/title guards intact Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review findings in OpenCode plugin - Cache agents list (static per session, was fetched every LLM call) - Remove TOCTOU: drop redundant file.exists() before file.text() - Eliminate duplicate system.join() by reusing joined string - Resolve getSharingEnabled() once in submit_plan instead of twice - Use path.join() instead of string concatenation for file paths Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: session-scoped plan files and tool-neutral prompt Replace plugin-global resolvedPlanFilePath and prompt-path parsing with per-session plan files at ~/.plannotator/session-plans/opencode/{id}/plan.md. Remove resolvePlanFilePath() regex (fixes spaces-in-path and cross-session race). Make planning prompt tool-neutral (no write/edit references) so it works with apply_patch models. Strip OpenCode's native read-only and experimental-mode prompt lines before injecting Plannotator's planning prompt. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: directory-based plan storage with path validation Replace session-scoped plan files with a shared plan directory at ~/.plannotator/session-plans/opencode/. Agent picks the filename, submit_plan takes a path arg and validates it (absolute, inside plan dir, exists, readable, non-empty) with canonical path checks to defeat traversal and symlink escapes. Remove summary and plan string args. Planning prompt is now tool-neutral and directs the agent to reuse the same file on feedback, not create new ones. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use OpenCode's native plans directory for permission compatibility OpenCode's plan mode permission ruleset only allows edits to .opencode/plans/*.md and $XDG_DATA_HOME/opencode/plans/*.md. Our custom ~/.plannotator/session-plans/opencode/ path was blocked by PermissionDeniedError at the tool level regardless of prompt stripping. Switch to the XDG path that OpenCode already allows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: exploration-first planning prompt Restructure the planning prompt as a phased workflow: Explore → Ask → Write → Submit. The agent now explores the codebase before creating a plan file or asking questions, producing better-researched plans for existing codebases. Greenfield tasks can skip straight to questions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: fix stale path in JSDoc header comment Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
459a1f4946 |
fix: guard Obsidian mkdir against Bun EEXIST regression (#319)
Bun v1.1.43–1.1.45 throws EEXIST on mkdirSync with recursive:true when the directory already exists. Add existsSync guard to prevent save failures when the Obsidian folder already exists. Fixes #315 |
||
|
|
f31f6728f5 |
chore: bump version to 0.13.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
4a38c65bc6 |
fix: unify favicon background color to dark navy
The server-side inline SVG still had the original purple (#6B3FA0) while the marketing site favicon was updated to dark navy (#070b14). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
f2f713a655 |
fix: truncate git error messages in code review UI
Git dumps its entire --help output on some failures (e.g. running code review from a non-git directory). The error was passed raw to the UI, rendering hundreds of lines of usage text. Extract only the first meaningful line and cap at 200 chars. Also add line-clamp on the UI error display as a safety net. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
ea689d9745 |
Unify review core across Bun and Pi (#310)
* Unify review core across Bun and Pi
* Surface review diff failures
* fix: review core fixes from code review
- Handle unborn HEAD in runGitDiff("uncommitted") via rev-parse check
instead of assertGitSuccess, so fresh repos fall through to untracked diffs
- Bind Pi server to 0.0.0.0 for remote sessions (regression from e47eda0)
- Update CLAUDE.md: packages/shared/ description, PLANNOTATOR_SHARE env var,
/api/diff response shape
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update favicon background to dark navy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
||
|
|
01d807787b |
feat: add favicon (Purple P + gold highlight) (#312)
* feat: add favicon (option 2 — purple P with gold highlight) Adds the "Purple P + Highlight" favicon across all three Plannotator servers (plan, review, annotate). The icon is a rounded purple square with a bold white "P" and a semi-transparent gold stripe through the middle — typographic, on-brand, and legible at 16px. Implementation: - shared-handlers.ts: handleFavicon() serves the inline SVG with a 1-day cache header, shared by all servers - index.ts, review.ts, annotate.ts: /favicon.svg route added before the SPA catch-all - apps/hook/index.html, apps/review/index.html: <link rel="icon"> added pointing to /favicon.svg SVG favicons are the modern standard and render crisply at all sizes without a separate .ico file. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add favicon to marketing site Use the same purple P + gold highlight SVG favicon on plannotator.ai. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Michael Ramos <mdramos8@gmail.com> |