A local, read-only inspector for Claude Code transcripts, inspired by Kimi vis. Node.js 18+; no runtime dependencies, build step, or model calls.
Requires Node.js 18+. No install or build step — run server.js directly, or
npm link once to get a cc-vis command. Data source: CLAUDE_CONFIG_DIR
or ~/.claude (read-only; the server never writes to transcripts).
node server.js --port 8126 # opens a browser
node server.js --port 8126 --no-open # headless / remote serverOn a remote machine, prefer an SSH tunnel over exposing the port:
ssh -L 8126:127.0.0.1:8126 <host>Binding a non-loopback address requires VIS_AUTH_TOKEN. The helper script
takes care of it:
openssl rand -hex 24 > .vis-token # one-time: generate the access token
./start-intranet.sh # binds 0.0.0.0:8126 in the background.vis-token is gitignored; the token can also come from $VIS_AUTH_TOKEN
directly. The script refuses to start without a token, and exits quietly if
cc-vis is already running. VIS_PORT overrides the port. Equivalent one-liner
without the script:
VIS_AUTH_TOKEN=secret node server.js --host 0.0.0.0 --port 8126Either way, open http://<host>:8126/#token=<token> once — the UI saves the
token in the browser and removes it from the URL; afterwards the URL is just
http://<host>:8126/. Without a saved token the page prompts for one.
The server captures the bundled UI when it starts. After updating the frontend
or backend, restart cc-vis and reload the browser so both use the same API
contract. /api/meta exposes apiContract and uiRevision for diagnosis.
The startup log's CLAUDE_HOME label reports the resolved data directory; it
is not a separate setting.
Live mode is on by default. The header's 实时 button pauses/resumes it. The browser checks lightweight file revisions about every two seconds while visible and fetches data only for changed views. New sessions and child-agent writes are detected as well. The server waits for Claude Code to write its log; this does not create a separate model call or inspect unsaved token buffers.
Wire and Context follow new content when you are at the bottom. While reading earlier content, they preserve the visible row and show 跳到最新. Context is reprojected on compaction/clear rather than blindly appending obsolete messages. Search filters and expanded records survive refreshes.
/focuses the sidebar session search, unless you are typing or composing text.Esccloses the topmost drawer and restores focus. With no drawer open, it collapses expanded event/message details without leaving the session.- The shortcut hints on the start page can also be clicked.
The Claude mark is embedded in the single-file UI, so it needs no external image
request. After frontend source changes, build with npm run build:single inside
vendor/kimi-vis-web, copy dist-single/index.html to public/index.html, then
restart cc-vis and reload the browser. The runtime stays dependency-free.
- Shared dark/light/system themes, comfortable/compact density, adjustable session rail, responsive mobile navigation, accessible native controls.
- Project grouping, title/path/ID search, error and subagent filters.
- Stable session/agent/tab routes. Tab switches and refreshes retain the agent, filters, expanded content, page, and scroll position. New records prompt for an update when reading earlier content; automatic checks run every 10 seconds.
- Wire: lightweight pages of at most 250 semantic events; full event and raw record details on demand; tool input/result pairing; cross-page links; source-line links; full-content search; issue drawer and pairing highlight.
- Tool details: command/file summaries, Edit before/after content, AskUserQuestion questions/options, complete parameters and returned output.
- Timeline: turns and lazily loaded model steps; deduplicated usage; context trend and compaction markers; tool latency/error/output statistics; observed active span, wall span, idle gaps, and configuration changes.
- Context: paginated previews, complete messages on demand, Markdown subset, collapsed thinking/tool outputs, inline safe embedded images, per-model usage, full recorded history or records after the latest compaction.
- Agents: parent/child relationships from metadata and recorded spawn results; unknown parents remain separate; links to parent and originating call.
- Tasks: task board with owner/dependencies and operation history. State changes require a recorded successful tool result. Missing creation IDs are not invented. Legacy TodoWrite remains supported.
- Cron: CronCreate/CronList/CronDelete result replay and operation history. Historical state does not claim that a job is currently running.
- Logs / State: level-filtered transcript events, tool/API failures, and derived session metadata. Export downloads the main transcript as JSON.
A model message may appear in multiple content-block records carrying identical or progressively updated usage. The final available usage snapshot is counted once per session/agent/request/message key, without removing the content blocks. Records lacking identifiers retain independent usage. All analytical views use this aggregation rule. Token counts describe recorded provider usage, not billing.
The JSONL reader preserves physical line numbers and byte offsets, reports bad records, tolerates incomplete tails, caches bounded snapshots, and reads appended bytes incrementally. Changed inode or a shorter/rewritten file resets the snapshot. Derived caches include child transcript and metadata dependencies. Wire and Context return summaries first; full payloads are requested only when opened.
Conversation text is escaped. The small Markdown renderer does not execute HTML or load remote images. Inline images require an explicit full-message view and a supported base64 image MIME type. The server never writes to Claude transcripts.
GET /api/meta
GET /api/changes?sessionId=
GET /api/sessions
GET /api/sessions/:id
GET /api/sessions/:id/wire?agent=&offset=&limit=&type=&q=&issues=&around=&line=&tool=
GET /api/sessions/:id/wire/:eventId?agent=
GET /api/sessions/:id/timeline?agent=
GET /api/sessions/:id/timeline?agent=&turn=0
GET /api/sessions/:id/context?agent=&offset=&limit=&view=history|recent
GET /api/sessions/:id/context/:messageId?agent=
GET /api/sessions/:id/messages?agent=
GET /api/sessions/:id/agents/:agentId
GET /api/sessions/:id/tasks
GET /api/sessions/:id/cron
GET /api/sessions/:id/logs
GET /api/sessions/:id/state
GET /api/sessions/:id/export
Wire event IDs are physicalLine:blockIndex. Timeline's default response contains
turn summaries; turn= returns its steps. Context's default response is paginated;
message IDs address full content. These are intentional 0.2 API changes.
src/jsonl.js bounded incremental transcript snapshots
src/usage.js shared usage identity and aggregation
src/sessions.js discovery and summary statistics
src/wire.js semantic event index and full event details
src/timeline.js turn/step and tool analysis
src/context.js recorded-history projection and pagination
src/agents.js agent inventory and parent relationships
src/inspect.js tasks, cron, logs, derived state
src/cache.js dependency-aware cache
src/httpd.js read-only API and static serving
public/styles.css shared themes, components and responsive layouts
public/js/ui.js safe content rendering, source links, detail drawer
public/js/main.js navigation, view state, refresh, session rail
public/js/views/ feature views
Run npm test for aggregation, idle-time, scope, tool errors, task/Cron replay,
JSONL append/truncation, paging, cache invalidation and HTTP regression tests.
The Claude Code skill entry point remains skills/cc-vis/SKILL.md.
- Transcript formats vary by Claude Code/provider version. Unknown data remains inspectable in Wire; missing data is not presented as a measured zero.
- Context is recorded history, not an exact model request reconstruction. Selecting records after compaction does not implement a complete undo/branch projector or recover system prompts that were never recorded.
- Active duration is the observed span within each turn, not measured CPU/LLM time. Tool call-to-result duration can include scheduling and approval waits.
- Task/Cron states are historical. Independent task directories are not merged without a verified session relationship. Export includes the main transcript; debug ZIP import/export and persistent background-process capture are not included.
- Browser state uses local storage; current filters, expanded details and page positions are retained while navigating this app, not across a browser reload.
Thanks to Kimi vis from MoonshotAI/kimi-code —
the architecture cc-vis follows (read local transcripts, expose a small read-only
API, serve an embedded SPA) is modeled after it, and the bundled web UI is a
vendored, lightly modified copy of the kimi vis frontend, used under the MIT
License (see vendor/kimi-vis-web/PROVENANCE.md for the exact upstream commit
and local modifications).