Claude Session Outline
This is an unofficial, community-made extension. It is not affiliated with,
endorsed by, or built by Anthropic. "Claude" and "Claude Code" are
Anthropic's product names, referenced here only to describe interoperability
with their CLI's local session files.
A read-only sidebar for VS Code that lists the prompts you typed in the current
Claude Code session for this workspace, so you can quickly scan or locate a past
question and Claude's answer to it — similar to ChatGPT's web sidebar message
outline. It does not send messages and cannot scroll/control the official
Claude Code panel (no such API exists across extensions); clicking an item
only expands its full text inside this panel.
Everything runs locally: this extension only reads .jsonl files that the
Claude Code CLI already writes to ~/.claude/projects on your own machine. It
makes no network requests, sends no telemetry, and does not modify or delete
anything in that directory.
How it works
- Reads
~/.claude/projects/<encoded-workspace-path>/<session-uuid>.jsonl directly
(same files the official extension writes, permission 0600, readable by the
same OS user).
- The workspace-path encoding (
replace(/[^a-zA-Z0-9]/g, "-"), with a hashed
suffix for names beyond 200 chars) was reverse-engineered from the official
extension's bundled extension.js. The 200+ char hash suffix isn't
reproduced exactly (the hash algorithm wasn't recoverable from the minified
bundle); as a fallback, the resolver prefix-scans ~/.claude/projects for a
directory matching the first 200 encoded characters.
- Parses JSONL records and keeps only real user-authored prompts:
type === "user", origin.kind === "human", isSidechain !== true, filtering out
tool_result / task-notification / attachment records that are also stored
with type: "user" but were never typed by a person.
- Each prompt is paired with Claude's answer: the ordered top-level
(non-sidechain) assistant
thinking/text content blocks between that
prompt and the next one (tool_use/tool_result blocks and sub-agent
chatter are skipped; adjacent same-type blocks are merged into one
segment). Since a reply can arrive in a later incremental tail-read than
the prompt it answers, segments are stitched onto the last known prompt
across reads rather than requiring the whole reply in one parse pass. The
filter box matches against the question, thinking, and answer text.
- In the sidebar, only the last text segment (the actual final answer) is
shown expanded by default; any
thinking content and any earlier text
segments (intermediate narration/tool commentary) render collapsed behind
"Thinking" / "Earlier steps" disclosure toggles. Question and answer text
render as markdown (via a vendored, hardened copy of marked — raw HTML
in the source is escaped rather than interpreted, and only http(s) links
are allowed through) rather than as plain text.
- Picks the most-recently-modified
.jsonl in the project directory as the
"current" session (there's no official API exposing which session the
official panel currently has open). Use the list-selection icon in the view
title bar to pin a specific session; a "Follow latest" button un-pins it.
- Watches the session file with
fs.watch and only re-reads bytes appended
since the last read (stopping at the last complete newline), so it stays
responsive even once a session file grows past tens of MB. The project
directory is also watched with a leading-edge debounce (reacts to the
first fs event almost immediately, then briefly cools down) to notice new
or newly-touched session files.
Known limitations
- Multi-root workspaces: only the first workspace folder is used. A warning
is logged (see "Diagnostics" below) if more than one folder is open.
- rewind/fork branches (
parentUuid tree) are not visualized; prompts are
simply listed in file order top-to-bottom.
- No message-level jump into the official Claude Code panel — VS Code
webviews are isolated from one another and the official extension exposes
no API for it.
- Confirmed blind spot: switching back to a session already opened once
in the current VS Code Extension Host run doesn't sync. Verified with
real diagnostic logs from a live repro (
fs.watch fires zero events — not
even an mtime touch — when the official panel re-opens an already-"warm"
session) and independently with a raw-inotify capture plus a pty-driven
claude --resume CLI test (attaching to view a session for 6s produces no
filesystem write at all). A first open of any session in a given run
does touch mtime and gets picked up correctly; only a repeat open within
the same run is invisible. There is no accessible external signal for
this (checked VS Code's state.vscdb ItemTable, the extension's
globalStorage folder, and webview mementos — none persist an "active
session" pointer), so this can't be fixed by watching harder or polling
more often. Workaround: use "Switch Session" (the list-selection icon) to
manually pin the session you want — it selects by id, not by mtime, so it
isn't affected by this at all.
Diagnostics
The extension logs its key decisions (project dir resolution, the full
session list with mtimes on every rescan, switch decisions, parse errors)
to an output channel. Open it via the output icon in the view's title bar,
or run the command "Claude Session Outline: Show Logs".
Development
npm install
npm run compile # or: npm run watch
Press F5 in VS Code (or run "Debug: Start Debugging") to launch an Extension
Development Host with this extension loaded.
Unit tests
test/unit/ covers SessionController (real fs.watch, real timers, a
temp ~/.claude/projects tree — switch latency, cross-chunk answer
stitching, burst-debounce correctness) and the webview (media/main.js +
media/marked.min.js loaded into jsdom — rendering, default-collapsed
Thinking/Earlier-steps sections, and the markdown XSS hardening). Both run
in plain Node via a minimal vscode module stub (test/unit/vscode-stub.js,
registered through node -r), so this needs no Electron download, no
display, and finishes in well under a second:
npm run test:unit
Integration tests
test/suite/index.js drives the real SessionController and webview
provider inside an actual VS Code Extension Development Host (real
fs.watch, real vscode.env.clipboard, real webview HTML building) against
a fake ~/.claude/projects tree under a temp CLAUDE_CONFIG_DIR, and checks
session-switch latency, prompt extraction, and clipboard support. activate()
returns { getController() } purely so this test can drive the controller
directly instead of through interactive UI like QuickPick.
npm run test:integration
This needs a display (or Xvfb) since it launches a real Electron-based VS
Code binary — @vscode/test-electron downloads that binary on first run
regardless of how you provide the display. If you don't have Xvfb installed
locally, run it in a container instead:
docker run --rm --init -v "$(pwd)":/workspace -w /workspace node:20-bookworm bash -c '
apt-get update -qq && apt-get install -y -qq xvfb libnss3 libatk1.0-0 \
libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 \
libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 libpango-1.0-0 \
libgtk-3-0 libx11-xcb1 >/dev/null &&
xvfb-run -a node test/runTest.js
'
Use --init (a real PID-1/init process) — without it, a Docker container's
PID 1 can miss SIGCHLD and hang forever once the wrapped process exits.
The downloaded VS Code binary is cached under .vscode-test/ (gitignored);
delete it if you want to reclaim the ~1GB, or leave it to skip the download
on the next run.
npm test runs both suites in order (unit, then integration).