Why I built ViewWork — and how to use it
One day I lost track of which file my AI agent was reading and in what order. The end result compiled fine — but the process was buried in a text log somewhere. I wanted to see it. ViewWork is what came out of that.
Why I built it
If you've ever seen an AI agent — Claude Code, Cursor, Codex — touch fifty files in an hour, you know. The result works. But the path it took to get there is hidden in line-by-line transcripts somewhere.
I wanted two things to be visible at all times:
- "Who is touching this file right now?" — when I'm fixing one line, is the AI editing the same file? Did it read it twice? Is another agent in the same area?
- "Where did this change start?" — a click from a real visitor on my dev server traveled through which API route, which DB row got created, which file got modified. Was that flow traceable as a single moving line on a map?
So I built ViewWork — a desktop app that runs entirely on your PC and lays out your code directory + AI work + dev-server traffic on one screen, as nodes and living characters.
What ViewWork shows
Folder tree → 3D nodes + edges, mapped 1:1. Folders cluster on the left, files in the middle, and an outer ring carries external services / MCP servers / DB tables / URLs the agent is browsing — as virtual nodes.
Characters are alive. Every time an AI agent calls a tool (Read · Edit · WebFetch · …) its avatar lerps to the target node. When several characters land on the same node, they auto-arrange in a small ring so they don't overlap.
5-minute install
Grab the installer from the downloads section — pick a folder. That's it. AI hooks aren't required; ViewWork picks up the session logs automatically.
# Or build from source
git clone https://github.com/gugu9999gu/ViewWork.git
cd ViewWork
npm install
npm run dev # dev mode (hot reload)
# or
npm run dist:win # build a Windows installer
The viewport — node map + characters
The viewport supports two modes:
- 2D — based on React Flow. Crisp at any zoom, with click / pan / mini map / labels. Viewport culling keeps 5,000-node projects buttery.
- 3D — Three.js + react-three-fiber. Folders unfold as a volumetric tree; the camera can auto-follow a chosen agent.
Four layouts: horizontal · vertical · radial · volumetric tree. Each mode (2D / 3D) keeps its own settings — keep 2D readable with horizontal, keep 3D spacey with the volumetric tree, independently.
Auto AI agent tracking
The piece I'm proudest of. No hooks, no patches, no env vars. ViewWork watches these locations automatically:
~/.claude/projects/<encoded-cwd>/*.jsonl— Claude Code~/.codex/sessions/<YYYY/MM/DD>/rollout-*.jsonl— OpenAI Codex CLI- VSCode Copilot Chat — workspaceStorage chatSessions
- Cursor / Windsurf — their own session jsonl
- shell history (PowerShell / bash / zsh / fish)
ViewWork tails each line, parses tool-use calls, and maps them to a single action (read · edit · write · bash · task · …). The character then glides to the target file node.
"Other AI tools can join the same way — emit a single JSON line per tool call. The format + examples live inside the desktop app under Help → AI agent activity upload path."
Repeated identical events (same agent + path + action) collapse into a single row with a
×N badge. The sidebar never gets spammed by the same operation.
Off-project activity (URLs · thinking · answers)
I wanted to see what the AI does outside the repo too.
- WebFetch / WebSearch →
web:<host>virtual nodes appear in the graph; the URL or query shows up as the label. - Extended thinking →
mind:<agent>node, with a snippet of the chain-of-thought. - Final answer → an
answeraction that parks on the same mind node with a preview.
These virtual nodes self-clean after 30 minutes — the graph never becomes a wall of stale URLs.
MCP + DB schema
If your project has .mcp.json / .cursor/mcp.json /
claude_desktop_config.json, ViewWork enumerates every MCP server as a virtual
mcp:<name> node. When you have ≥2 nodes for the same vendor it auto-spawns
a platform hub — file → hub → individual MCP — so the routing becomes
obvious.
DB schema is even better. Prisma schema.prisma, Drizzle schema/,
and SQL migrations are all parsed. Tables, columns, and foreign keys
land as cloud nodes; supabase.from('users').select()-style calls in the code
draw an edge to the matching table.
Dev-server visitor tracking
A built-in reverse proxy sits in front of your dev server (localhost:3000,
etc.) and captures every page view, API call, and form submit. Visitors get stable IDs
via cookie (viewwork-visitor-id) — same person across pages, so flows are
accurate.
The fun part — flip the proxy to 0.0.0.0 LAN exposure and a phone or tablet on the same network can use the URL. Their traffic shows up as a character on the map. QA demos suddenly become visual.
Custom characters (image · GIF · GLB)
Default characters are pastel discs, but drop your own and they swap in. Settings → Characters → Open folder — drop PNG / SVG / GIF / WebP / AVIF / GLB / FBX. Naming convention:
claude.svg— applies to claude* (claude-code, claude-3.5-sonnet, …)_action-edit.gif— every agent's edit actionclaude/think.webp— claude's thinking action only (subfolder)claude.glb— 3D model in 3D mode + animation clips auto-matched per action (Mixamo-friendly)
Toggle "Show character image only" (Settings → Characters) to drop the circular badge / glow / border — your GIF avatar stays pure.
Local-first — no telemetry
All analysis runs on your PC.
- Code, AI activity logs, project paths, API keys → never leave your machine.
- The only outbound request: a once-per-24h anonymous GET to
viewwork.work/version.jsonfor update checks. - The feedback modal posts to your own Discord webhook URL — never a server we run.
What's next
The visualization core is solid. On top of it: live DB INSERT/UPDATE/DELETE visualization, direct local + cloud DB access, anomaly watch + alerts, time-scrub replay, team mode. Full list on the roadmap section.
Bug reports, ideas, questions — the desktop app's Help → Send feedback (F1) or the contact page. Thanks for reading. See you on the node map.
※ This article reflects v0.1.0. Latest changes: /en/release/.