Analyze Claude Code session transcripts to debug plugins, understand context usage, and trace execution flow
Analyze Claude Code session transcripts to debug plugins, understand context usage, and trace execution flow. Use when investigating errors, finding context bloat sources, or examining tool call patterns in JSONL transcript files.
/plugin marketplace add bfreis/claude-mart/plugin install transcript-analyzer@claude-martThis skill inherits all available tools. When active, it can use any tool Claude has access to.
scripts/transcript-toolUse this skill when you need to analyze Claude Code session transcripts for:
Claude Code stores session transcripts at:
~/.claude/projects/-PATH-TO-PROJECT/*.jsonl
The path uses dashes instead of slashes. For example:
/Users/bfreis/dev/myproject~/.claude/projects/-Users-bfreis-dev-myproject/*.jsonlTranscripts use JSONL format (one JSON object per line).
| Type | Description |
|---|---|
summary | Session metadata |
file-history-snapshot | File change tracking |
user | User messages (includes tool results) |
assistant | Assistant messages (includes tool calls) |
{
"type": "user" | "assistant",
"uuid": "message-uuid",
"parentUuid": "parent-message-uuid",
"sessionId": "session-uuid",
"isSidechain": false,
"timestamp": "2025-12-06T...",
"message": {
"role": "user" | "assistant",
"content": [...],
"usage": { "input_tokens": N, "output_tokens": N }
}
}
In assistant messages:
text - Regular text responsetool_use - Tool invocation with .id, .name, .inputthinking - Extended thinking blocksIn user messages:
text - User inputtool_result - Tool output with .tool_use_id, .content.content can be string OR array - Handle both:
if .content | type == "array" then
(.content | map(.text // "") | add)
else
.content
end
[]? not [] - Handles missing fields gracefullyisSidechain: true - Filter these for main conversation onlyparentUuid links threads - Not line orderThe skill provides a CLI at scripts/transcript-tool:
# Quick overview
transcript-tool summary session.jsonl
# Find context bloat sources
transcript-tool bloat session.jsonl 15
# Tool usage breakdown
transcript-tool tools session.jsonl
# Trace specific tool
transcript-tool trace-tool session.jsonl Read
# Find errors
transcript-tool errors session.jsonl
# Custom jq query
transcript-tool extract session.jsonl '.type'
# Find skill invocations
transcript-tool trace-skill session.jsonl plan-generator
# See what tools were used
transcript-tool tools session.jsonl
# Check for errors
transcript-tool errors session.jsonl
# Find largest tool results
transcript-tool bloat session.jsonl 20
# Message size analysis
transcript-tool messages session.jsonl
# Identify specific large results
transcript-tool extract session.jsonl '
select(.type == "user") |
.message.content[]? |
select(.type == "tool_result") |
select((.content | tostring | length) > 10000) |
.tool_use_id
'
# All tool calls in order
transcript-tool extract session.jsonl '
select(.type == "assistant") |
.message.content[]? |
select(.type == "tool_use") |
"\(.name): \(.input | keys | join(", "))"
'
For complex analysis, use jq directly:
Count content block types:
jq -r '
select(.type == "user" or .type == "assistant") |
.message.content[]? |
.type
' session.jsonl | sort | uniq -c
Find tool call by ID:
jq -r --arg id "toolu_xxx" '
select(.type == "assistant") |
.message.content[]? |
select(.type == "tool_use" and .id == $id)
' session.jsonl
Get corresponding tool result:
jq -r --arg id "toolu_xxx" '
select(.type == "user") |
.message.content[]? |
select(.type == "tool_result" and .tool_use_id == $id) |
.content
' session.jsonl
Token usage per message:
jq -r '
select(.type == "assistant" and .message.usage) |
"\(.message.usage.input_tokens) in, \(.message.usage.output_tokens) out"
' session.jsonl