A Claude Code mod that draws a live pane of what Claude read, searched, changed and still holds in context.

<img src=".github/assets/header.svg" alt="cc-graph: see what Claude knows" width="100%">
<samp>install · the pane · privacy · roadmap · changelog</samp>
<samp>After a long turn, nobody can say without scrolling back what Claude actually has in context. cc-graph is a Claude Code mod that draws it in a live pane beside the conversation: every file Claude read, searched or changed, how those files import each other, and what compaction took away.</samp>
<a name="install"></a> <img src=".github/assets/h-install.svg" alt="install" width="100%">
/plugin install cc-graph --marketplace kuehn-lars/cc-graph
<samp>Then type /graph. Needs Claude Code v2.1.287 or later, in the terminal.</samp>
<a name="the-pane"></a> <img src=".github/assets/h-the-pane.svg" alt="the pane" width="100%">
<img src=".github/assets/showcase.svg" alt="The cc-graph pane in the terminal, annotated: the context window meter, partly read and compacted files, import edges, folder frames, size bars, the focus, search-only files, an edit and a new file, the now line and the legend" width="100%">
| <samp>control</samp> | <samp>what it does</samp> |
|---|---|
| <kbd>g</kbd> | <samp>graph view</samp> |
| <kbd>l</kbd> | <samp>list view</samp> |
| <kbd>1</kbd> … <kbd>9</kbd> | <samp>pick a row in the list</samp> |
maxNodes = 60 | <samp>most files in the graph, in /config</samp> |
importEdges = on | <samp>show file imports, in /config</samp> |
blindSpots = on | <samp>look for blind spots with git, in /config</samp> |
captureThinking = on | <samp>keep thinking excerpts, in /config</samp> |
<samp>Files a shell command created, deleted or changed show too, named with the command that did it, and so do files Claude read with cat, head or sed -n. Files that import a change but were never opened are flagged as blind spots while the turn is still running.</samp>
<samp>The list also shows Claude's newest thinking: a short excerpt per step, with its turn and the files that step read or changed. Claude Code passes thinking text to mods only when "showThinkingSummaries": true is in your settings (or with --thinking-display summarized). This holds for every 5.5 model: Opus, Sonnet and Haiku. Without it, the footer says thinking is hidden.</samp>
<a name="privacy"></a> <img src=".github/assets/h-privacy.svg" alt="privacy" width="100%">
<samp>cc-graph only watches. Its hooks pass every tool result on untouched and record what happened in session memory.</samp>
captureThinking is on. All of it is gone when the conversation ends or on /clear.</samp><a name="roadmap"></a> <img src=".github/assets/h-roadmap.svg" alt="roadmap" width="100%">
<img src=".github/assets/roadmap.svg" alt="Roadmap: M0 Foundations, v0.1 What Claude knows, v0.2 The graph and v0.3 Blind spots shipped; next v0.4 Why it did that, then v0.5 Desktop graph and pointer, v0.6 Decisions and timeline, v1.0 Hardening and listing" width="100%">
<samp>Features, exit gates and spikes per milestone are in ROADMAP.md.</samp>
<samp>The project is spec-driven: specs/ is the source of truth, and each feature goes from spec to failing tests to code. Instructions for Claude Code working on this repository are in CLAUDE.md.</samp>
claude --plugin-dir . # run with the mod loaded (use a copy of fixtures/sample-app)
npx tsc -p . && claude plugin test && node scripts/check-calls.mjs
node scripts/trace.mjs --check # every approved acceptance criterion has a test
<samp><a href="LICENSE">MIT</a> © 2026 Lars Kühn · built with and for <a href="https://claude.com/claude-code">Claude Code</a></samp>
hooks/register.tsx 21 lines1// Throwaway recording probe for fixtures/sessions/wide.ts and many-changes.ts (see ../../README.md). Not part of the mod.
2// It logs session.start, turn.start and every tool.call as the hooks see them, and changes nothing.
3import type { Register } from 'claude-code'
4
5const OUT = '/tmp/cc-graph-wide/log-' + Date.now() + '.json'
6const rows: any[] = []
7const log = async ($: any, event: string, e: any, next: any) => {
8 const row: any = { event, input: e }
9 rows.push(row)
10 const out = await next(e)
11 row.result = out
12 await $.fs.write(OUT, JSON.stringify(rows, null, 1))
13 return out
14}
15
16export const register: Register = on => {
17 on('session.start', ($, e: any, next: any) => log($, 'session.start', e, next)).catch(($, e, next) => next(e))
18 on('turn.start', ($, e: any, next: any) => log($, 'turn.start', e, next)).catch(($, e, next) => next(e))
19 on('tool.call', ($, e: any, next: any) => log($, 'tool.call', e, next)).catch(($, e, next) => next(e))
20}
21