/cc-history
Reference documentation for analyzing Claude Code conversation history files
$ npx -y skills add solatis/claude-config --skill cc-history --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/cc-history
Context preview
The summary Claude sees to decide when to auto-load this skill.
Reference documentation for analyzing Claude Code conversation history files
SKILL.md
cc-history.SKILL.mdname: cc-history
description: Reference documentation for analyzing Claude Code conversation history files
Claude Code History Analysis
Reference documentation for querying and analyzing Claude Code's conversation history. Use shell commands and jq to extract information from JSONL conversation files.
Directory Structure
~/.claude/projects/{encoded-path}/
|-- {session-uuid}.jsonl # Main conversation
|-- {session-uuid}/
|-- subagents/
| |-- agent-{hash}.jsonl # Subagent conversations
|-- tool-results/ # Large tool outputsProject Path Resolution
Convert working directory to project directory:
PROJECT_DIR="~/.claude/projects/$(echo "$PWD" | sed 's|^/|-|; s|/\.|--|g; s|/|-|g')"
Encoding rules:
- Leading `/` becomes `-`
- Regular `/` becomes `-`
- `/.` (hidden directory) becomes `--`
Examples:
- `/Users/bill/.claude` -> `-Users-bill--claude`
- `/Users/bill/git/myproject` -> `-Users-bill-git-myproject`
Message Types
| Type | Description | | ----------------- | --------------------------------------------- | | `user` | User input messages | | `assistant` | Model responses (thinking, tool_use, text) | | `system` | System messages | | `queue-operation` | Background task notifications (subagent done) |
Message Structure
Each line in a JSONL file is a message object:
{
"type": "assistant",
"uuid": "abc123",
"parentUuid": "xyz789",
"timestamp": "2025-01-15T19:39:16.000Z",
"sessionId": "session-uuid",
"message": {
"role": "assistant",
"content": [...],
"usage": {
"input_tokens": 20000,
"output_tokens": 500,
"cache_read_input_tokens": 15000,
"cache_creation_input_tokens": 5000
}
}
}Assistant message content blocks:
- `type: "thinking"` - Model thinking (has `thinking` field)
- `type: "tool_use"` - Tool invocation (has `name`, `input` fields)
- `type: "text"` - Text response (has `text` field)
Common Queries
Find Conversations
# List by modification time (most recent first)
ls -lt "$PROJECT_DIR"/*.jsonl
# Find by date
ls -la "$PROJECT_DIR"/*.jsonl | grep "Jan 15"
# Find by content
grep -l "search term" "$PROJECT_DIR"/*.jsonl
Extract Messages
# Get message by line number (1-indexed)
sed -n '42p' file.jsonl | jq .
# Get message by uuid
jq -c 'select(.uuid=="abc123")' file.jsonl
# All user messages
jq -c 'select(.type=="user")' file.jsonl
# All assistant messages
jq -c 'select(.type=="assistant")' file.jsonl
Tool Call Analysis
# List all tool calls
jq -c 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use") | {name, input}' file.jsonl
# Count tool calls by name
jq -c 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use") | .name' file.jsonl | sort | uniq -c | sort -rn
# Find specific tool calls
jq -c 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use" and .name=="Bash")' file.jsonlSkill Invocation Detection
Pattern: `python3 -m skills\.([a-z_]+)\.`
# Find all skill invocations
grep -oE "python3 -m skills\.[a-z_]+" file.jsonl | sort -u
# Find conversations using a specific skill
grep -l "python3 -m skills\.planner\." "$PROJECT_DIR"/*.jsonl
Token Usage
# Total tokens in conversation
jq -s '[.[].message.usage? | select(.) | .input_tokens + .output_tokens] | add' file.jsonl
# Token breakdown
jq -s '[.[].message.usage? | select(.)] | {
input: (map(.input_tokens) | add),
output: (map(.output_tokens) | add),
cached: (map(.cache_read_input_tokens // 0) | add)
}' file.jsonl
# Token progression over time
jq -c 'select(.type=="assistant") | {ts: .timestamp[11:19], inp: .message.usage.input_tokens, out: .message.usage.output_tokens}' file.jsonlTaxonomy Aggregation
# Count messages by type
jq -s 'group_by(.type) | map({type: .[0].type, count: length})' file.jsonl
# Character count in user messages
jq -s '[.[] | select(.type=="user") | .message.content | length] | add' file.jsonl
# Thinking block character count
jq -s '[.[] | select(.type=="assistant") | .message.content[]? | select(.type=="thinking") | .thinking | length] | add' file.jsonlSubagent Analysis
# List subagents for a session
ls "${SESSION_DIR}/subagents/"
# Get subagent task description (first user message)
jq -c 'select(.type=="user") | .message.content' agent-*.jsonl | head -1
# Find Task tool calls in parent (these spawn subagents)
jq -c 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use" and .name=="Task") | .input' file.jsonlConversation Branching
Each `.jsonl` file contains the **entire conversation tree** (all branches), not separate files per branch. Branching is tracked via `parentUuid`:
- When user goes back in history and issues a new command, the new message gets the same `parentUuid` as where they branched from
- Multiple messages sharing the same `parentUuid` = sibling branches (fork point)
Detecting Branch Points
# Find all fork points (messages with multiple children)
jq -s 'group_by(.parentUuid) | map(select(length > 1)) | .[] | {
parentUuid: .[0].parentUuid,
branches: length,
timestamps: [.[].timestamp]
}' file.jsonl
# Show siblings at a known fork point
FORK_POINT="parent-uuid-here"
jq -c --arg fp "$FORK_POINT" 'select(.parentUuid==$fp) | {uuid, ts: .timestamp, preview: (.message.content | tostring)[:100]}' file.jsonlExtracting a Single Branch
To filter for exactly one branch, find a unique identifier in that branch, then walk the ancestor chain back to root.
**Step 1: Find target message uuid**
# By unique content
TARGET=$(jq -r 'select(.message.content | tostring | contains("unique-identifier")) | .uuid' file.jsonl | tail -1)
# By timestaRead more
name: cc-history description: Reference documentation for analyzing Claude Code conversation history files
Claude Code History Analysis
Reference documentation for querying and analyzing Claude Code's conversation history. Use shell commands and jq to extract information from JSONL conversation files.
Directory Structure
~/.claude/projects/{encoded-path}/
|-- {session-uuid}.jsonl # Main conversation
|-- {session-uuid}/
|-- subagents/
| |-- agent-{hash}.jsonl # Subagent conversations
|-- tool-results/ # Large tool outputsProject Path Resolution
Convert working directory to project directory:
PROJECT_DIR="~/.claude/projects/$(echo "$PWD" | sed 's|^/|-|; s|/\.|--|g; s|/|-|g')"
Encoding rules:
- Leading `/` becomes `-`
- Regular `/` becomes `-`
- `/.` (hidden directory) becomes `--`
Examples:
- `/Users/bill/.claude` -> `-Users-bill--claude`
- `/Users/bill/git/myproject` -> `-Users-bill-git-myproject`
Message Types
| Type | Description | | ----------------- | --------------------------------------------- | | `user` | User input messages | | `assistant` | Model responses (thinking, tool_use, text) | | `system` | System messages | | `queue-operation` | Background task notifications (subagent done) |
Message Structure
Each line in a JSONL file is a message object:
{
"type": "assistant",
"uuid": "abc123",
"parentUuid": "xyz789",
"timestamp": "2025-01-15T19:39:16.000Z",
"sessionId": "session-uuid",
"message": {
"role": "assistant",
"content": [...],
"usage": {
"input_tokens": 20000,
"output_tokens": 500,
"cache_read_input_tokens": 15000,
"cache_creation_input_tokens": 5000
}
}
}Assistant message content blocks:
- `type: "thinking"` - Model thinking (has `thinking` field)
- `type: "tool_use"` - Tool invocation (has `name`, `input` fields)
- `type: "text"` - Text response (has `text` field)
Common Queries
Find Conversations
# List by modification time (most recent first) ls -lt "$PROJECT_DIR"/*.jsonl # Find by date ls -la "$PROJECT_DIR"/*.jsonl | grep "Jan 15" # Find by content grep -l "search term" "$PROJECT_DIR"/*.jsonl
Extract Messages
# Get message by line number (1-indexed) sed -n '42p' file.jsonl | jq . # Get message by uuid jq -c 'select(.uuid=="abc123")' file.jsonl # All user messages jq -c 'select(.type=="user")' file.jsonl # All assistant messages jq -c 'select(.type=="assistant")' file.jsonl
Tool Call Analysis
# List all tool calls
jq -c 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use") | {name, input}' file.jsonl
# Count tool calls by name
jq -c 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use") | .name' file.jsonl | sort | uniq -c | sort -rn
# Find specific tool calls
jq -c 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use" and .name=="Bash")' file.jsonlSkill Invocation Detection
Pattern: `python3 -m skills\.([a-z_]+)\.`
# Find all skill invocations grep -oE "python3 -m skills\.[a-z_]+" file.jsonl | sort -u # Find conversations using a specific skill grep -l "python3 -m skills\.planner\." "$PROJECT_DIR"/*.jsonl
Token Usage
# Total tokens in conversation
jq -s '[.[].message.usage? | select(.) | .input_tokens + .output_tokens] | add' file.jsonl
# Token breakdown
jq -s '[.[].message.usage? | select(.)] | {
input: (map(.input_tokens) | add),
output: (map(.output_tokens) | add),
cached: (map(.cache_read_input_tokens // 0) | add)
}' file.jsonl
# Token progression over time
jq -c 'select(.type=="assistant") | {ts: .timestamp[11:19], inp: .message.usage.input_tokens, out: .message.usage.output_tokens}' file.jsonlTaxonomy Aggregation
# Count messages by type
jq -s 'group_by(.type) | map({type: .[0].type, count: length})' file.jsonl
# Character count in user messages
jq -s '[.[] | select(.type=="user") | .message.content | length] | add' file.jsonl
# Thinking block character count
jq -s '[.[] | select(.type=="assistant") | .message.content[]? | select(.type=="thinking") | .thinking | length] | add' file.jsonlSubagent Analysis
# List subagents for a session
ls "${SESSION_DIR}/subagents/"
# Get subagent task description (first user message)
jq -c 'select(.type=="user") | .message.content' agent-*.jsonl | head -1
# Find Task tool calls in parent (these spawn subagents)
jq -c 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use" and .name=="Task") | .input' file.jsonlConversation Branching
Each `.jsonl` file contains the **entire conversation tree** (all branches), not separate files per branch. Branching is tracked via `parentUuid`:
- When user goes back in history and issues a new command, the new message gets the same `parentUuid` as where they branched from
- Multiple messages sharing the same `parentUuid` = sibling branches (fork point)
Detecting Branch Points
# Find all fork points (messages with multiple children)
jq -s 'group_by(.parentUuid) | map(select(length > 1)) | .[] | {
parentUuid: .[0].parentUuid,
branches: length,
timestamps: [.[].timestamp]
}' file.jsonl
# Show siblings at a known fork point
FORK_POINT="parent-uuid-here"
jq -c --arg fp "$FORK_POINT" 'select(.parentUuid==$fp) | {uuid, ts: .timestamp, preview: (.message.content | tostring)[:100]}' file.jsonlExtracting a Single Branch
To filter for exactly one branch, find a unique identifier in that branch, then walk the ancestor chain back to root.
**Step 1: Find target message uuid**
# By unique content
TARGET=$(jq -r 'select(.message.content | tostring | contains("unique-identifier")) | .uuid' file.jsonl | tail -1)
# By timestaI use Claude Code for most of my work. After months of iteration, I noticed a pattern: LLM-assisted code rots faster than hand-written code.
Other skills on claude-config.
- /arxiv-to-md
Convert arXiv papers to LLM-consumable markdown. Invoke when user provides an arXiv ID or URL, or when syncing academic papers from a PDF folder to a markdown destination.
Open skill - /codebase-analysis
Invoke IMMEDIATELY via python script when user requests codebase understanding, architecture comprehension, or repository orientation. Do NOT explore first - the script orchestrates exploration.
Open skill - /decision-critic
Invoke IMMEDIATELY via python script to stress-test decisions and reasoning. Do NOT analyze first - the script orchestrates the critique workflow.
Open skill - /deepthink
Invoke IMMEDIATELY via python script when user requests structured reasoning for open-ended analytical questions. Do NOT explore first - the script orchestrates the thinking workflow.
Open skill - /doc-sync
Synchronizes docs across a repository. Use when user asks to sync docs.
Open skill - /incoherence
Detect and resolve incoherence in documentation, code, specs vs implementation.
Open skill

