Desktop code-review tool. Claude generates story-driven reviews with inline annotations; each annotation is an interactive thread you can click to ask follow-ups, with answers streamed back into the diff viewer. Works for Bitbucket PRs and uncommitted local changes.
Product
- Story-driven AI code review — a narrative walkthrough with inline notes, not a one-shot lint comment
- Click any annotation to spawn a scoped Claude subagent that resumes its own session for follow-up questions
- Three modes: PR review (Bitbucket), self-review (local uncommitted changes), answer-review (respond to feedback)
- Thread replies post back to Bitbucket; suggested code fixes can be applied or rejected inline
Agent architecture
- Each user action spawns a purpose-built Claude subagent — annotation reply, file explainer, ship-commit, and a background reconciliation agent — coordinated by the server through
session.jsonstate, not a single chat loop - Sessions resume across turns via
claude -p --resume <sid>so context is reused without re-prompting (token + latency win) - Background reconciliation agent runs after any code-changing reply, returning a JSON-schema response that updates annotation
fixStatusand refreshes affected file explanations - Per-spawn cost/latency tuning:
--model sonnet --effort high+outputStyle: ultra-conciseoverride so subagents don't burn tokens emitting structured progressive payloads
Backend / infra
- Bun + Hono server with WebSocket file watcher broadcasting session changes to all connected clients
- Subprocess cancel registry with kill semantics; deterministic session IDs; stream-JSON parsing of Claude CLI output for incremental UI updates
- Per-session turn lock prevents races on
claude -p --resume <sid>writing the samejsonltranscript - Atomic JSON mutations via a single-writer mutex; pure merge helpers for disk↔memory reconciliation
- Custom
bb(Bitbucket) +jr(Jira) CLI wrappers with pagination + 429 backoff
Stack: TypeScript (strict), Bun, Hono, React 18, Vite, Zustand, Electron, Claude Code subagents, refractor, react-diff-view
The most interesting parts of the codebase, in order. Each file is small enough to read end-to-end.
-
server/subagent.ts(~195 lines) — the spawn primitive. WrapsBun.spawn("claude", ...), parses stream-JSON output, and registers each subprocess with a cancel registry keyed so a/api/reply/cancelroute can kill the right pid. Throws aCANCELLED_SENTINELso callers can distinguish user-cancellation from real failures. -
server/reply.ts(~395 lines) — the orchestrator.handleReplyroutes each user action (annotation reply, comment reply, ship, pr-comment, explain) to the right prompt builder, picks the right spawn options (resume? json-schema? bypass permissions?), parses structured JSON responses, and writes the result back tosession.jsonvia atomic mutation. -
server/reconcile.ts(~162 lines) — the second agent. After any code-changing reply, a background agent runs with a JSON-schema response describing which annotations to markapplied/resolvedand which file explanations to refresh. This is the multi-agent coordination piece: agent 1's output triggers agent 2's spawn. -
src/store/session-merge.ts(~310 lines) — pure helpers for disk↔memory state.mergeSessionDatareconciles incoming websocket-broadcast session.json updates against in-memory edits without clobbering.detectNewRepliesnotices when an asking-claude spinner should clear because a Claude reply just landed on disk. -
src/components/DiffView.tsx(~720 lines) — the diff renderer + widget map. Splices annotations, comments, and PR comments into the diff at correct change keys, with per-pane horizontal scroll sync and a custom gutter that hosts the inline "Ask Claude" UI. The widget-mapuseMemois the heart of the rendering loop.
If you want one file to understand the project: read server/reply.ts. Everything else exists to serve it.
Prerequisites
- macOS (Keychain used for credential storage)
- Bun —
curl -fsSL https://bun.sh/install | bash - Node.js ≥ 18
- Claude Code —
npm install -g @anthropic-ai/claude-code(required — powers all AI review features) - Git
Install + run
git clone <repo-url>
cd lens
./setup.sh
lenssetup.sh walks you through:
- Auto-installs Bun if missing; checks node / git / claude
- Bitbucket API token (scopes: Account/Workspace/Users/Repos Read, PR Read+Write, Pipelines Read)
- Jira API token (optional, for ticket context in reviews)
- Auto-fetches your Bitbucket identity, workspaces, and repos
- Installs skills, agents, and the
lenscommand on yourPATH
Open Lens (lens) and the home page lets you start a review directly — pick a repo, pick a PR or branch, click Start. The viewer opens when the review agent is ready.
You can also start from Claude Code via slash commands (/pr-review, /self-review, /review) if you prefer the terminal.
The reply flow. Click any annotation or comment in the viewer to ask Claude a follow-up about that specific code. Each click spawns a scoped subagent with the annotation's context + your question + the surrounding diff; subsequent questions resume the same session via claude -p --resume <sid> so context isn't re-prompted.
server/ Bun + Hono API server (port 3100) + WebSocket file watcher
├─ reply.ts orchestrator: routes each user action to a subagent
├─ subagent.ts spawn primitive + cancel registry
├─ reconcile.ts background agent that updates session.json after code changes
├─ prompts.ts prompt builders per item type
├─ review.ts session.json I/O (atomic, mutex-protected)
├─ git.ts diff / file-content / branch helpers
└─ routes/ Hono route modules (pr, review, session, git)
src/ React + Vite frontend (port 1421)
├─ components/ DiffView, AnnotationBox, CommentWidget, PrCommentBubble, ...
└─ store/ Zustand: session, ui, session-merge (pure helpers)
electron/ Electron shell
scripts/bb/ Bitbucket CLI (pr list, pr comment, etc.)
scripts/jr/ Jira CLI (view, edit, comment, etc.)
.claude/skills/ Review skill definitions (symlinked to ~/.claude/skills/)
.claude/agents/ Specialist agent definitions (symlinked to ~/.claude/agents/)
.reviews/ Session data (gitignored, per-repo hash directories)
- macOS only — uses Keychain for credential storage; no Linux/Windows port
- Requires Claude Code Pro/Max — every AI feature spawns a
claude -psubprocess against the user's installed CLI - Bitbucket only — no GitHub PR integration; self-review mode works against any git remote
- Single-user — no multi-tenant, no shared sessions across machines
- Self-review diff freezes to the pushed branch when you switch contexts — intentional, prevents the diff from silently changing under your feet when you check out a different branch mid-review
MIT — see LICENSE.
First step for any issue:
lens --checkValidates dependencies, credentials, configuration, and port availability.
- Port 3100 already in use: another Lens instance is running.
lsof -ti:3100 | xargs kill. lens: command not found: restart your shell (source ~/.zshrc) or re-run./setup.sh.- Bitbucket token issues:
security delete-generic-password -s "bitbucket-api-token"then re-run./setup.sh. - Jira token issues:
security delete-generic-password -s "jira-cli-token"then re-run./setup.sh.
Personal project I built to speed up my own review workflow. Public mainly as a writing sample.