Visualize your AI coding agent's review checklists as a Kanban board.
The markdown review files your agent writes to .agents/reviews/ render as a live
board β grouped by status, with priority, assignees, tags, and checklist progress.
Run it locally against the repo you're in (npx @phake/lanework, no auth, no
network), tick checkboxes back to disk, and dispatch a Claude Code agent on any
card in one click. A hosted, GitHub-backed read-only app also exists but is
currently paused while we focus on the local CLI + MCP server.
AI coding agents (Claude Code, Codex, Cursor, β¦) increasingly write review
checklists before changing behavior β a markdown file with - [ ] items you
approve before they implement. Lanework turns that convention into a board so
you can see, across a repo, what's awaiting review, in progress, done, or dropped β
and open any review to read and approve it.
The hosted app is read-only β it reads .agents/reviews/ and never writes back.
The local CLI reads the same way, but lets you tick checkboxes in a review and
Save changes back to the file on disk when you choose to.
The local CLI is the recommended path. The hosted app is paused (the code is untouched β see Develop the hosted app β but the public CTAs are gated off while we focus local-first).
Hosted (apps/webapp) |
Local CLI (apps/local) |
|
|---|---|---|
| Runs on | Cloudflare Workers (lanework.dev) | Your machine (npx @phake/lanework) |
| Source | A GitHub repo (any branch) | The current directory on disk |
| Auth | GitHub sign-in (Better Auth) | None |
| Network | Reads GitHub REST + GraphQL | Fully offline |
| Live updates | Manual refresh (KV-cached) | Auto β watches the folder |
| Checklists | Read-only | Tick boxes + Save changes to disk |
| Run agents | β | One-click Claude Code dispatch on a card |
| Cost view | β | Estimates Claude Code token spend from ~/.claude |
Both share the same UI and review-parsing logic (in packages/shared).
- ποΈ Board & List views β four columns from your
.agents/reviews/markdown (column from each card'sstatus:field, or fromtodo/processing/done/droppedfolders) - π·οΈ Rich cards β priority, assignee avatars, tags, date, and an
x/xchecklist-progress badge - π Filter by tag or My tasks, with a searchable repo switcher (hosted)
- π Full-screen review with a clean metadata panel + rendered markdown
- βοΈ Interactive checklists β tick items in a review with a live per-checklist progress ring (
x of x, solid checkmark at 100%); "pick one" groups render as radios - π€ Run agents on cards (local CLI) β dispatch a Claude Code agent on any card; pick the model Β· effort Β· mode per run, watch a "Claude is working" badge, then see the run's cost (tokens Β· runtime Β· ~$) written back onto the card
- πΎ Save to disk (local CLI) β persist your ticks back to the markdown file
- π° Cost view (local CLI) β estimates the project's Claude Code token spend from
~/.claudetranscripts, with a cache-aware per-model breakdown - π» Local CLI β
npx @phake/laneworkboards the current repo, no Cloudflare or auth, with live file-watch - π€ MCP server (AI-DLC) β
lanework mcplets an AI client drive the full review lifecycle (create β tick β advance status) via Model Context Protocol tools - π§© Claude Code plugin β
/lanework:create,/lanework:status,/lanework:advance, β¦ slash commands; install viaclaude plugin marketplace add fuongz/lanework(seeplugin/) - π GitHub sign-in (hosted) β pick any repo you can access
- β¨ Polished β Motion animations, Hugeicons, shadcn/Base UI, dark-mode tokens
- π Built-in guide (
/guide) explaining how to set up the convention in any repo
By default a card's column comes from its status: field, with files grouped by date:
<repo>/.agents/reviews/
βββ 2026-06-21/ β the card's date
βββ 01-ship-landing.md # ββ status: done
βββ 02-bulk-send-message.md # ββ status: processing
A card reads its column from the status: field, its date from the enclosing
YYYY-MM-DD/ folder (the leading NN- orders cards within the day), and priority Β·
assignees Β· tags Β· progress from the rest of the YAML frontmatter + its - [ ] /
- [x] checkboxes. Any other frontmatter key (e.g. your own task_id, phase) still
shows up as its own row in the card's detail view. You can instead let
todo/processing/done/dropped folders carry the status
({"status":{"from":"folder"}} in .agents/reviews/config.json makes folders
authoritative), or teach it a non-YYYY-MM-DD date shape with date.pattern. See the
in-app guide or the standardized
AGENTS.md template to set this up in your own repos.
No install, no config β point it at a repo that has an .agents/reviews/ folder:
npx @phake/lanework # boards the current directory
bunx @phake/lanework # same, with Bun
npx @phake/lanework path/to/repo # board a different directoryIt picks a free port (from 3662), serves on 127.0.0.1, and opens your browser.
Edit or add review files and the board updates live. Open a review to tick its
checkboxes β they're a working view until you hit Save changes, which writes the
updated markdown back to the file on disk. Hover a card and hit Run to dispatch a
Claude Code agent against that review without leaving the board.
lanework [dir] [--port N] [--no-open]
After a global install (npm i -g @phake/lanework) the commands lanework and
the short alias lw are available everywhere.
lanework mcp runs a Model Context Protocol
server over stdio, so an agent like Claude can drive the full review lifecycle
during its reasoning loop β an AI-Driven Development Lifecycle (AI-DLC): inception
(create_review) β review (toggle_item) β construction & shipping (set_status).
Install the Claude Code plugin β gets you the /lanework:* slash commands and
the MCP server in one step:
claude plugin marketplace add fuongz/lanework
claude plugin install lanework@laneworkRestart Claude Code, then type /lanework: for the commands (/lanework:create,
/lanework:status, /lanework:advance, β¦). The server runs headless by default
(tools only); set LANEWORK_DASHBOARD=1 in the server env to also auto-open the web
board (β :3662) when a session starts.
Just want the MCP tools (no slash commands)? Register the server on its own:
npx @phake/lanework setup claude-code(add--dashboardto auto-open the board).
Tools exposed:
| Tool | What it does |
|---|---|
lifecycle_status |
Phase view + suggested next actions (cards ready to advance) |
list_reviews |
List cards (filter by column / tag / assignee) |
get_review |
Read one review file's markdown by path |
board_summary |
Counts per column + aggregate checklist progress |
create_review |
Inception β create a new checklist card |
plan_review |
Fill a card's checklist in the canonical format (context + - [ ] **Dn.** decisions), frontmatter preserved |
toggle_item |
Check/uncheck a checklist item (by index or text) + note |
set_status |
Advance a card todo β processing β done (or dropped) |
update_review |
Patch priority / tags / assignees |
save_review |
Overwrite a review file (raw escape hatch) |
cost_estimate |
Per-model token usage from this project's Claude Code transcripts |
All tools read/write the current repo's .agents/reviews/ directly β no auth, no
network. stdout is reserved for the protocol; the dashboard (if enabled) runs as a
child process so it can't corrupt the stream. See plugin/ for the plugin.
| Area | Choice |
|---|---|
| Framework | TanStack Start (React 19, SSR, server functions) |
| Hosting | Cloudflare Workers (@cloudflare/vite-plugin + Wrangler) |
| Auth | Better Auth β GitHub OAuth, sessions in Cloudflare D1 via Drizzle |
| Data | GitHub REST + GraphQL (packages/shared/src/lib/github.ts) or the local filesystem (local-fs.ts) |
| UI | Tailwind CSS v4, shadcn over Base UI, Hugeicons, Motion |
| State | Zustand |
| Monorepo | Bun workspaces |
packages/shared/ all shared app code (one copy of the UI)
βββ src/
βββ routes/ TanStack routes (__root, index, guide, board.$owner.$repo)
βββ components/ ui/, kanban/, app-shell, app-sidebar, switchers
βββ server/reviews.ts server functions (branch on __LANEWORK_LOCAL__)
βββ lib/ github, reviews-core (shared parsing), local-fs,
β auth, db (Drizzle), utils, formatting
βββ stores/ Zustand UI state
βββ content/ the standardized AGENTS.md template
apps/webapp/ hosted target β lanework.dev
βββ vite.config.ts Cloudflare build (srcDirectory β ../../packages/shared/src)
βββ wrangler.jsonc, drizzle/ platform config + D1 migrations
βββ worker-configuration.d.ts
apps/local/ the `lanework` CLI (publishable to npm)
βββ cli.mjs port selection + auto-open browser
βββ server.mjs srvx server: static assets, SSR, live-watch SSE
βββ vite.config.local.ts Node build (no Cloudflare; cloudflare:workers shimmed)
βββ cloudflare-workers-shim.ts
Both apps point the route scanner at packages/shared/src. The filesystem code is
gated behind a build-time __LANEWORK_LOCAL__ flag, so it's dead-code-eliminated
from the Cloudflare Worker.
Requires Bun β₯ 1.2.
bun install
cp apps/webapp/.dev.vars.example apps/webapp/.dev.vars # fill in the secrets (below)
cd apps/webapp && bunx wrangler d1 create lanework-db # paste database_id into wrangler.jsonc
cd ../.. && bun run db:migrate:local
bun run dev # http://localhost:5173You'll need a GitHub OAuth App (dev) with callback
http://localhost:5173/api/auth/callback/github, then set in apps/webapp/.dev.vars:
BETTER_AUTH_SECRET=... # openssl rand -base64 32
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...The app requests the
reposcope to read private repos β change it topublic_repoinpackages/shared/src/lib/auth.tsif you only need public ones.
To run the local target during development (boards the current directory):
bun run local # builds apps/local and launches itbun run cf-typegen
bun run db:migrate:remote
cd apps/webapp
bunx wrangler secret put BETTER_AUTH_SECRET
bunx wrangler secret put GITHUB_CLIENT_ID
bunx wrangler secret put GITHUB_CLIENT_SECRET
cd ../.. && bun run deployCreate a separate prod OAuth App (a classic OAuth App matches the callback by
exact host, so dev localhost and prod can't share one) with your Worker's domain
in both URLs, set BETTER_AUTH_URL in apps/webapp/wrangler.jsonc, and redeploy.
Since the Worker URL isn't known until the first deploy: deploy once β note the URL
β create the prod app + secrets β redeploy.
The CLI is published as @phake/lanework from apps/local. It ships the
prebuilt app (dist-local), so it runs anywhere with no build step for consumers;
prepack rebuilds the bundle automatically before packing. Scoped packages are
private by default, so publishConfig.access is set to public.
# 1. From the repo root, install so the build toolchain is available
bun install
# 2. Log in to npm (needs an account that belongs to the `phake` org;
# prompts for an OTP if 2FA is on)
npm login
# 3. (For later releases) bump the version β npm versions are immutable
cd apps/local
npm version patch # 0.1.0 β 0.1.1 (skip for the very first publish)
# 4. Sanity-check what will ship, then publish (public, thanks to publishConfig)
npm publish --dry-run
npm publish
# 5. Verify
cd ~/any-repo && npx @phake/laneworkThe phake org must exist on npm with your account as a member
(create it here if needed). Only apps/local
is published β apps/webapp, packages/shared, and the workspace root are all
private.
- Design System β tokens, components, patterns, animation, layout
AGENTS.mdβ how AI agents should work in this repo (and the convention this app renders)- Contributing Β· Code of Conduct Β· Security
Contributions are welcome! Please read CONTRIBUTING.md and
the Design System docs before opening a PR.
Run bun run typecheck && bun run build before pushing.
MIT Β© fuongz