This skill should be used when the user asks to "analyze conversation history", "parse JSONL files", "read past sessions", "search conversation logs", "find what happened in a session", or needs to work with Claude Code's .jsonl conversation format. It provides the canonical parsing infrastructure for echo-sleuth agents.
From echo-sleuthnpx claudepluginhub xiaolai/claude-plugin-marketplace --plugin echo-sleuthThis skill uses the workspace's default tool permissions.
references/extraction-patterns.mdreferences/record-types.mdProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Calculates TAM/SAM/SOM using top-down, bottom-up, and value theory methodologies for market sizing, revenue estimation, and startup validation.
All parsing logic lives in ${CLAUDE_PLUGIN_ROOT}/scripts/echolib.py — a single Python module (stdlib only, Python 3.6+). The shell scripts are thin wrappers around it.
~/.claude/projects/<encoded-path>/sessions-index.json~/.claude/projects/<encoded-path>/.echo-sleuth-index.json~/.claude/projects/<encoded-path>/<uuid>.jsonl~/.claude/projects/<encoded-path>/<uuid>/subagents/agent-<id>.jsonl~/.claude/history.jsonlThe <encoded-path> is the project's absolute path with / replaced by - (e.g., -Users-joker-github-myproject).
Important: Only ~10% of projects have sessions-index.json. The scripts automatically build a fallback index from raw .jsonl files for the remaining 90%, cached in .echo-sleuth-index.json.
Always start with the index before opening any .jsonl file:
bash ${CLAUDE_PLUGIN_ROOT}/scripts/list-sessions.sh "current" --limit 20
bash ${CLAUDE_PLUGIN_ROOT}/scripts/list-sessions.sh "all" --grep "search term" --limit 10
bash ${CLAUDE_PLUGIN_ROOT}/scripts/list-sessions.sh "/path/to/project"
Output is tab-separated: SESSION_ID CREATED MODIFIED MSG_COUNT BRANCH SUMMARY FIRST_PROMPT PROJECT_PATH FULL_PATH
The FULL_PATH field (9th column) is the absolute path to the .jsonl file. Use this to pass to other scripts.
Only open the full .jsonl when you need message-level detail.
bash ${CLAUDE_PLUGIN_ROOT}/scripts/parse-jsonl.sh <file.jsonl> [options]
Key modes:
bash ${CLAUDE_PLUGIN_ROOT}/scripts/parse-jsonl.sh <file.jsonl> --detect-schema
bash ${CLAUDE_PLUGIN_ROOT}/scripts/parse-jsonl.sh <file.jsonl> --types user,assistant --skip-noise --limit 20
bash ${CLAUDE_PLUGIN_ROOT}/scripts/parse-jsonl.sh <file.jsonl> --types user --fields timestamp,message --format tsv
All scripts are at ${CLAUDE_PLUGIN_ROOT}/scripts/. They require only bash + python3 (stdlib only, no pip packages, minimum Python 3.6+). The git scripts additionally use git.
bash ${CLAUDE_PLUGIN_ROOT}/scripts/extract-messages.sh <file.jsonl> [--role user|assistant|both] [--no-tools] [--limit N] [--thinking [LIMIT]]
Note: --thinking without a number shows full thinking blocks. --thinking 500 truncates to 500 chars. Default: thinking blocks are hidden.
bash ${CLAUDE_PLUGIN_ROOT}/scripts/extract-tools.sh <file.jsonl> [--tool NAME] [--errors-only] [--limit N]
bash ${CLAUDE_PLUGIN_ROOT}/scripts/extract-files-changed.sh <file.jsonl> [--with-versions]
Uses reverse-read on large files (>50MB) to find the last snapshot efficiently.
bash ${CLAUDE_PLUGIN_ROOT}/scripts/session-stats.sh <file.jsonl>
bash ${CLAUDE_PLUGIN_ROOT}/scripts/build-index.sh [project-path|"all"]
Pre-warm the cache for projects without sessions-index.json.
Sessions with subagent work have a <session-uuid>/subagents/ directory. Check for it:
ls "$(dirname <full_path>)/$(basename <full_path> .jsonl)/subagents/" 2>/dev/null
Subagent files follow the same JSONL format and can be parsed with the same scripts.
--limit N enables early exit — near-instant for small N--skip-noise avoids json.loads on progress/queue-operation lines by string pre-filterjson.loads is the CPU bottleneck (63% of time), not I/Oextract-files-changed.sh uses reverse-read on files > 50MBsession-stats.sh counts errors in the same pass (no double-read)For targeted searches within large .jsonl files, use Grep directly:
# Find user messages containing a keyword (two-step workflow):
# Step 1: Find lines matching the record type
Grep pattern='"type":"user"' path="<file.jsonl>" output_mode="content"
# Step 2: From those results, visually scan or re-grep for your keyword.
# Alternatively, combine both conditions in one regex:
Grep pattern='"type":"user".*keyword' path="<file.jsonl>" output_mode="content"
# Find error results
Grep pattern='"is_error"\s*:\s*true'
# Find specific tool usage
Grep pattern='"name"\s*:\s*"ToolName"'
# Find decisions (AskUserQuestion usage)
Grep pattern='"name"\s*:\s*"AskUserQuestion"'
See references/record-types.md for the complete schema. The essential types:
| Type | What It Contains | When to Use |
|---|---|---|
user (string content) | Human's actual request | Understanding intent, finding topics |
assistant (text blocks) | Claude's responses and reasoning | Finding decisions, explanations |
assistant (tool_use blocks) | Tool invocations | Understanding what actions were taken |
file-history-snapshot | Files edited with version counts | Knowing which files were touched |
summary | AI-generated session title | Quick identification (also in sessions-index.json) |
system (compact_boundary) | Context compaction marker | Session was long enough to need compaction |
To map a project path to its Claude session directory:
/Users/joker/github/myproject)/ with - → Users-joker-github-myproject- → -Users-joker-github-myproject~/.claude/projects/-Users-joker-github-myproject/If unsure, use list-sessions.sh which handles the lookup automatically (including fuzzy matching via sessions-index.json originalPath).
When reading raw .jsonl, skip these:
type is progress or queue-operation (streaming/internal bookkeeping)isMeta: true (slash command injection)isCompactSummary: true (auto-generated context, not human input)model: "<synthetic>" (passthrough, not real inference)content is an array of tool_result blocks (tool outputs, not human messages)Use --skip-noise with parse-jsonl.sh for automatic noise filtering, or use the convenience scripts which handle this internally.
Claude Code evolves rapidly. The JSONL format has changed across versions:
progress at v2.1.14, pr-link later)summary, pr-link, file-history-snapshot) lack common fields like version or uuidsessions-index.json's originalPath field as ground truthWhen parsing results look unexpected, use the schema-scout agent or run:
bash ${CLAUDE_PLUGIN_ROOT}/scripts/parse-jsonl.sh <file.jsonl> --detect-schema
to check for unknown record types or field changes.