inspector#
A terminal UI for inspecting Claude Code session transcripts by session. Browse the sessions recorded for a project, open one, and read the full conversation — user turns, assistant prose, hidden reasoning, tool calls, and tool results — in a scrollable pager. A live follow mode tails the active session as Claude writes to it.
Every session is scanned for prompt-injection compromise via
response; affected turns are flagged inline with a
severity banner, and the session list shows a ⚠N / ✓ badge per session.
Transcripts are read from ~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl.
The inspector is read-only and never modifies them.
Run#
Requires Node ≥ 22.6 (the source is TypeScript, run directly via type stripping — no build step).
When installed in a project:
# inspect the project for the current working directory
./node_modules/.bin/badger-inspector
# or: pnpm exec badger-inspector
# or: npx badger-inspector
# inspect OpenCode sessions for the current project
badger-inspector --opencode
# list OpenCode sessions across all projects
badger-inspector --opencode --list
# jump straight into live follow of the active session
badger-inspector --follow
# list projects that have transcripts
badger-inspector --list
When developing in this workspace:
node packages/inspector/src/index.ts --opencode
node packages/inspector/src/index.ts --opencode --list
Needs a real interactive terminal. Launching through a non-TTY pipe (including Claude Code's
!command runner) prints a "not a TTY" notice and exits.
Keys#
Session list
| key | action |
|---|---|
↑ / ↓, k/j |
move selection |
enter / → |
open session |
/ |
filter by title/id |
g / G |
top / bottom |
q |
quit |
Reader / Follow
| key | action |
|---|---|
↑ / ↓, k/j |
scroll line |
space / b |
page down / up |
g / G |
top / bottom |
q / esc / ← |
back to list |
In follow mode the view auto-scrolls to the newest turn as the transcript
grows; scrolling up pauses the auto-follow (paused), and G or f re-pins to
the bottom (following). Press f from the list to start following the active
session.
Layout#
| file | primary export | responsibility |
|---|---|---|
parse-session.ts |
parseSession |
one .jsonl → ordered, normalized turns |
list-sessions.ts |
listSessions |
a project dir → newest-first session summaries |
scan-session.ts |
scanAppend / scanChatMessages |
host-agnostic scan core: ChatMessage[] → response compromise findings (takes a ToChatMessages adapter) |
claude/scan-session.ts |
scanSession |
binds the Claude adapter: a .jsonl file → findings |
render-thread.ts |
renderThread |
a session (+ findings) → colored, wrapped display lines |
inspect-sessions.ts |
inspectSessions |
the one interactive list / reader / follow TUI, driven by a SessionBackend |
claude/backend.ts |
claudeBackend |
SessionBackend over JSONL files (fs.watch follow) |
opencode/backend.ts |
opencodeBackend |
SessionBackend over SQLite storage (timer-polled follow) |
index.ts |
— | argv → project dir / backend resolution, entry point |
The host adapter (claude/to-chat-messages.ts) maps a transcript into the
OpenRouter-shaped ChatMessage[] that response expects, and
scan-session.ts scans that — it never reads a transcript itself. Assistant
tool_use blocks become toolCalls (with a toolUseId → name map), and
tool_result blocks — which Claude nests inside the following user record —
become standalone role: "tool" messages, which is where detectCompromise()
looks for echoed injection payloads. Today echoed_injection_payload is the live category;
literal_injection_marker activates once response's marker list is
eval-derived.
Security note#
Transcript content (especially fetched web pages in tool results) can contain
raw terminal escape sequences. render-thread.ts strips control characters and
ANSI escapes from all content before drawing, so untrusted data can't move the
cursor or recolor the UI.