Query Claude Code session history using jq to find past conversations. Use when users mention "JQ for Claude", "jq4clawd", "jq-for-clawd", "find my past sessions", "what did I ask before", "search conversation history", or "query session files".
From jq-for-clawdnpx claudepluginhub grailautomation/claude-plugins --plugin jq-for-clawdThis skill uses the workspace's default tool permissions.
Use this skill when users ask about past conversations, previous sessions, or conversation history.
Claude Code stores session history as JSONL files at:
~/.claude/projects/<encoded-project-path>/<session-id>.jsonl
Path encoding: Project paths use dashes instead of slashes.
/home/user/my-project becomes -home-user-my-projectTo find sessions for a specific project path:
# Encode project path (replace / with -)
PROJECT_PATH="/home/user/my-project"
ENCODED=$(echo "$PROJECT_PATH" | sed 's|^/|-|; s|/|-|g')
SESSION_DIR="$HOME/.claude/projects/$ENCODED"
# List sessions sorted by modification time
ls -lt "$SESSION_DIR"/*.jsonl 2>/dev/null | head -10
ls -lt ~/.claude/projects/ | head -20
Each session file contains one JSON object per line:
| Line Type | Key Fields | Description |
|---|---|---|
summary | type, summary, leafUuid | Session metadata (first line) |
user | type, timestamp, message | User messages |
assistant | type, timestamp, message | Claude responses |
system | type, message | System prompts |
file-history-snapshot | type, snapshot | File state snapshots |
User and assistant messages have this structure:
{
"type": "user",
"timestamp": "2026-01-10T22:32:07.522Z",
"message": {
"role": "user",
"content": "your message text here"
},
"sessionId": "uuid",
"uuid": "message-uuid"
}
Note: message.content can be either:
"content": "hello""content": [{"type": "text", "text": "hello"}]cat session.jsonl | jq -r '
select(.type == "user") |
.timestamp + " | " + (
if .message.content | type == "string"
then .message.content[0:300]
else (.message.content[0].text // "N/A")[0:300]
end
)
' | grep -v "N/A" | grep -v "command-name" | grep -v "local-command"
cat session.jsonl | jq -r '.type' | sort | uniq -c
head -5 session.jsonl | jq -r 'select(.type == "user") | .timestamp' | head -1
Search across all sessions in a project for a keyword:
SESSION_DIR="$HOME/.claude/projects/-home-user-my-project"
for f in "$SESSION_DIR"/*.jsonl; do
if grep -q "keyword" "$f" 2>/dev/null; then
echo "=== $f ==="
cat "$f" | jq -r 'select(.type == "user" and (.message.content | tostring | test("keyword"; "i"))) | .timestamp + " | " + (.message.content | tostring)[0:200]' 2>/dev/null
fi
done
SESSION_DIR="$HOME/.claude/projects/-home-user-my-project"
for f in $(ls -t "$SESSION_DIR"/*.jsonl 2>/dev/null | grep -v "agent-" | head -5); do
echo ""
echo "=== $(basename $f) ==="
cat "$f" | jq -r '
select(.type == "user") |
.timestamp + " | " + (
if .message.content | type == "string"
then .message.content[0:150]
else (.message.content[0].text // "N/A")[0:150]
end
)
' 2>/dev/null | grep -v "N/A" | grep -v "command-name" | head -3
done
Agent/subagent sessions have filenames starting with agent-:
ls -lt "$SESSION_DIR"/*.jsonl | grep -v "agent-"
Sessions include ISO timestamps. Filter with jq:
cat session.jsonl | jq -r '
select(.type == "user" and .timestamp > "2026-01-10") |
.timestamp + " | " + (.message.content | tostring)[0:100]
'
Filter out command invocations and system noise:
grep -v "command-name" | grep -v "local-command" | grep -v "N/A"
When a user asks "what did I ask you in our last few sessions?":
Identify the project path (current working directory or ask user)
Encode and locate sessions:
ENCODED=$(echo "$PWD" | sed 's|^/|-|; s|/|-|g')
SESSION_DIR="$HOME/.claude/projects/$ENCODED"
List recent non-agent sessions:
ls -lt "$SESSION_DIR"/*.jsonl | grep -v "agent-" | head -5
Extract user messages from each:
cat "$SESSION_DIR/<session-id>.jsonl" | jq -r '
select(.type == "user") |
.timestamp + " | " + (
if .message.content | type == "string"
then .message.content[0:300]
else (.message.content[0].text // "N/A")[0:300]
end
)
' | grep -v "N/A" | grep -v "command-name" | head -5
Present results in a clean, organized format grouped by session with timestamps.
No sessions found: Verify the project path encoding matches exactly. Check ls ~/.claude/projects/ for the correct directory name.
jq parse errors: Some lines may have malformed JSON. Add 2>/dev/null to suppress errors.
Empty content: Some user messages (like tool approvals) have array content without text. Filter with grep -v "N/A".