Analyze Claude Code session logs - extract thinking blocks, tool usage stats, error patterns, debug trajectories. Triggers on: introspect, session logs, trajectory, analyze sessions, what went wrong, tool usage, thinking blocks, session history, my reasoning, past sessions, what did I do.
95
Quality
96%
Does it follow best practices?
Impact
90%
1.08xAverage score across 3 eval scenarios
Risky
Do not use without reviewing
Extract actionable intelligence from Claude Code session logs. Analyze tool usage, reasoning patterns, errors, and conversation flow to improve workflows and debug issues.
~/.claude/
├── history.jsonl # Global: all user inputs across projects
├── projects/
│ └── {project-path}/ # e.g., X--Dev-claude-mods/
│ ├── sessions-index.json # Session metadata index
│ ├── {session-uuid}.jsonl # Full session transcript
│ └── agent-{short-id}.jsonl # Subagent transcriptsProject paths use double-dash encoding: X:\Dev\claude-mods → X--Dev-claude-mods
# Find project directory for current path
project_dir=$(pwd | sed 's/[:\\\/]/-/g' | sed 's/--*/-/g')
ls ~/.claude/projects/ | grep -i "${project_dir##*-}"| Type | Contains | Key Fields |
|---|---|---|
user | User messages | message.content, uuid, timestamp |
assistant | Claude responses | message.content[], cwd, gitBranch |
thinking | Reasoning blocks | thinking, signature (in content array) |
tool_use | Tool invocations | name, input, id (in content array) |
tool_result | Tool outputs | tool_use_id, content |
summary | Conversation summaries | summary, leafUuid |
file-history-snapshot | File state checkpoints | File contents at point in time |
system | System context | Initial context, rules |
# Get sessions index
cat ~/.claude/projects/X--Dev-claude-mods/sessions-index.json | jq '.'
# List session files with sizes and dates
ls -lah ~/.claude/projects/X--Dev-claude-mods/*.jsonl | grep -v agentSESSION="417ce03a-6fc7-4906-b767-6428338f34c3"
PROJECT="X--Dev-claude-mods"
# Entry type distribution
jq -r '.type' ~/.claude/projects/$PROJECT/$SESSION.jsonl | sort | uniq -c
# Session duration (first to last timestamp)
jq -s '[.[].timestamp // .[].message.timestamp | select(.)] | [min, max] | map(. / 1000 | strftime("%Y-%m-%d %H:%M"))' \
~/.claude/projects/$PROJECT/$SESSION.jsonl
# Conversation summaries (quick overview)
jq -r 'select(.type == "summary") | .summary' ~/.claude/projects/$PROJECT/$SESSION.jsonlPROJECT="X--Dev-claude-mods"
# Tool frequency across all sessions
cat ~/.claude/projects/$PROJECT/*.jsonl | \
jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | .name' | \
sort | uniq -c | sort -rn
# Tool frequency for specific session
jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | .name' \
~/.claude/projects/$PROJECT/$SESSION.jsonl | sort | uniq -c | sort -rn
# Tools with their inputs (sampled)
jq -c 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | {tool: .name, input: .input}' \
~/.claude/projects/$PROJECT/$SESSION.jsonl | head -20SESSION="417ce03a-6fc7-4906-b767-6428338f34c3"
PROJECT="X--Dev-claude-mods"
# All thinking blocks (reasoning trace)
jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "thinking") | .thinking' \
~/.claude/projects/$PROJECT/$SESSION.jsonl
# Thinking blocks with context (which turn)
jq -r 'select(.type == "assistant") |
.message.content as $content |
($content | map(select(.type == "thinking")) | .[0].thinking) as $thinking |
($content | map(select(.type == "text")) | .[0].text | .[0:100]) as $response |
select($thinking) | "---\nThinking: \($thinking[0:500])...\nResponse: \($response)..."' \
~/.claude/projects/$PROJECT/$SESSION.jsonlPROJECT="X--Dev-claude-mods"
# Find tool errors across sessions
cat ~/.claude/projects/$PROJECT/*.jsonl | \
jq -r 'select(.type == "user") | .message.content[]? | select(.type == "tool_result") |
select(.content | test("error|Error|ERROR|failed|Failed|FAILED"; "i")) |
{tool_id: .tool_use_id, error: .content[0:200]}' 2>/dev/null | head -50
# Count errors by pattern
cat ~/.claude/projects/$PROJECT/*.jsonl | \
jq -r 'select(.type == "user") | .message.content[]? | select(.type == "tool_result") | .content' 2>/dev/null | \
grep -i "error\|failed\|exception" | \
sed 's/[0-9]\+//g' | sort | uniq -c | sort -rn | head -20PROJECT="X--Dev-claude-mods"
# Search user messages
cat ~/.claude/projects/$PROJECT/*.jsonl | \
jq -r 'select(.type == "user") | .message.content[]? | select(.type == "text") | .text' | \
grep -i "pattern"
# Search assistant responses
cat ~/.claude/projects/$PROJECT/*.jsonl | \
jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text' | \
grep -i "pattern"
# Find sessions mentioning a file
for f in ~/.claude/projects/$PROJECT/*.jsonl; do
if grep -q "specific-file.ts" "$f"; then
echo "Found in: $(basename $f)"
fi
doneSESSION="417ce03a-6fc7-4906-b767-6428338f34c3"
PROJECT="X--Dev-claude-mods"
# Reconstruct conversation (user/assistant turns)
jq -r '
if .type == "user" then
.message.content[]? | select(.type == "text") | "USER: \(.text[0:200])"
elif .type == "assistant" then
.message.content[]? | select(.type == "text") | "CLAUDE: \(.text[0:200])"
else empty end
' ~/.claude/projects/$PROJECT/$SESSION.jsonlPROJECT="X--Dev-claude-mods"
# List subagent sessions
ls ~/.claude/projects/$PROJECT/agent-*.jsonl 2>/dev/null
# Subagent tool usage
for f in ~/.claude/projects/$PROJECT/agent-*.jsonl; do
echo "=== $(basename $f) ==="
jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | .name' "$f" | \
sort | uniq -c | sort -rn | head -5
doneSESSION="417ce03a-6fc7-4906-b767-6428338f34c3"
PROJECT="X--Dev-claude-mods"
# Rough character count (tokens ≈ chars/4)
jq -r '[
(select(.type == "user") | .message.content[]? | select(.type == "text") | .text | length),
(select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text | length)
] | add' ~/.claude/projects/$PROJECT/$SESSION.jsonl | \
awk '{sum+=$1} END {print "Total chars:", sum, "Est tokens:", int(sum/4)}'SESSION="417ce03a-6fc7-4906-b767-6428338f34c3"
PROJECT="X--Dev-claude-mods"
# Files edited (Edit tool usage)
jq -r 'select(.type == "assistant") | .message.content[]? |
select(.type == "tool_use" and .name == "Edit") | .input.file_path' \
~/.claude/projects/$PROJECT/$SESSION.jsonl | sort | uniq -c | sort -rn
# Files written
jq -r 'select(.type == "assistant") | .message.content[]? |
select(.type == "tool_use" and .name == "Write") | .input.file_path' \
~/.claude/projects/$PROJECT/$SESSION.jsonl | sort | uniqPROJECT="X--Dev-claude-mods"
SESSION1="session-id-1"
SESSION2="session-id-2"
# Compare tool usage between sessions
echo "=== Session 1 ===" && \
jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | .name' \
~/.claude/projects/$PROJECT/$SESSION1.jsonl | sort | uniq -c | sort -rn
echo "=== Session 2 ===" && \
jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | .name' \
~/.claude/projects/$PROJECT/$SESSION2.jsonl | sort | uniq -c | sort -rn| Task | Command Pattern |
|---|---|
| List sessions | ls -lah ~/.claude/projects/$PROJECT/*.jsonl | grep -v agent |
| Entry types | jq -r '.type' $SESSION.jsonl | sort | uniq -c |
| Tool stats | jq -r '... | select(.type == "tool_use") | .name' | sort | uniq -c |
| Extract thinking | jq -r '... | select(.type == "thinking") | .thinking' |
| Find errors | grep -i "error|failed" $SESSION.jsonl |
| Session summaries | jq -r 'select(.type == "summary") | .summary' |
| User messages | jq -r 'select(.type == "user") | .message.content[]?.text' |
# Find yesterday's sessions by modification time
find ~/.claude/projects/X--Dev-claude-mods -name "*.jsonl" -mtime -1 ! -name "agent-*" | \
xargs -I{} jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | .name' {} | \
sort | uniq -c | sort -rn# Search for sessions mentioning auth, then extract thinking
for f in ~/.claude/projects/$PROJECT/*.jsonl; do
if grep -qi "auth" "$f"; then
echo "=== $(basename $f) ==="
jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "thinking") | .thinking' "$f" | \
grep -i -A5 -B5 "auth"
fi
donefind ~/.claude/projects/ -name "*.jsonl" -mtime -7 | \
xargs cat 2>/dev/null | \
jq -r 'select(.type == "user") | .message.content[]? | select(.type == "tool_result") | .content' 2>/dev/null | \
grep -i "error\|failed" | \
sed 's/[0-9]\+//g' | sed 's/\/[^ ]*//g' | \
sort | uniq -c | sort -rn | head -10Session logs contain:
Before sharing session exports:
SESSION="session-id"
PROJECT="X--Dev-claude-mods"
echo "# Session Report: $SESSION"
echo ""
echo "## Summary"
jq -r 'select(.type == "summary") | "- \(.summary)"' ~/.claude/projects/$PROJECT/$SESSION.jsonl
echo ""
echo "## Tool Usage"
jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | .name' \
~/.claude/projects/$PROJECT/$SESSION.jsonl | sort | uniq -c | sort -rn | \
awk '{print "| " $2 " | " $1 " |"}'jq -s '{
session_id: "'$SESSION'",
entries: length,
tools: [.[] | select(.type == "assistant") | .message.content[]? | select(.type == "tool_use") | .name] | group_by(.) | map({tool: .[0], count: length}),
summaries: [.[] | select(.type == "summary") | .summary]
}' ~/.claude/projects/$PROJECT/$SESSION.jsonl5342bca
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.