jq-for-clawd/skills/jq-for-clawd/SKILL.md
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".
npx skillsauth add grailautomation/claude-plugins jq-for-clawdInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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".
development
Parse and analyze Workato recipe JSON exports. Use when the user asks about a Workato recipe's logic, data flow, field mappings, error handling, or control flow. Also use when the user references a .recipe.json file or asks to debug a Workato integration.
databases
Review implementation plans as a staff software engineer. Use when the user asks for staff engineer feedback, senior engineering review, plan review, architecture review, implementation-plan critique, approval before proceeding, risk review, or feedback on a proposed technical plan.
tools
Use when the user invokes $spec-kit:tasks or /spec-kit:tasks, or asks to generate dependency-ordered tasks from the plan and repo context.
testing
Use when the user invokes $spec-kit:specify or /spec-kit:specify, or asks to create a brownfield-aware feature specification from a natural language description.