neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
AI 에이전트 세션 기록을 SQL로 조회, DuckDB 기반
Query AI agent session transcripts with SQL. Built on DuckDB.
개발 도구Rust · ShellGitHub @technicalpickles직접 설치운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Hundreds of Claude Code sessions. Thousands of tool calls.
You've never looked at the logs.
1600 Bash calls. What am I even running?
TIGHT ON the commands.
Wait, I set up a git commit skill for that. Is it even firing? The commits would still go through Bash either way...
CUT TO Claude, investigating a hunch.
152 sessions. The skill was right there. Nobody called it.
TITLE CARD: SQL for your AI coding sessions.README 전문 보기 ↗INT. TERMINAL
Hundreds of Claude Code sessions. Thousands of tool calls. You've never looked at the logs.
$ cq tools
Read ██████████████████████████████ 1847
Bash ████████████████████████████ 1623
Edit ███████████████ 982
Write █████ 341
Grep ████ 298
1600 Bash calls. What am I even running?
TIGHT ON the commands.
$ cq tools Bash --fields command --limit 5
c82e9d4c Bash cargo test
c82e9d4c Bash git diff --stat
bfc27bd2 Bash docker compose up -d
bfc27bd2 Bash git commit -m "fix: resolve session timeout"
a1f3e890 Bash psql -c "SELECT count(*) FROM users"
Wait, I set up a git commit skill for that. Is it even firing? The commits would still go through Bash either way...
CUT TO Claude, investigating a hunch.
$ cq sql "
WITH commit_sessions AS (
SELECT DISTINCT session_id FROM tool_calls
WHERE name = 'Bash'
AND json_extract_string(input, '$.command') LIKE '%git commit%'
),
skill_sessions AS (
SELECT DISTINCT session_id FROM tool_calls
WHERE name = 'Skill'
AND json_extract_string(input, '$.skill') LIKE '%commit%'
)
SELECT
(SELECT count(*) FROM commit_sessions) as total_sessions,
(SELECT count(*) FROM skill_sessions) as used_skill,
(SELECT count(*) FROM commit_sessions)
- (SELECT count(*) FROM skill_sessions) as bypassed
" --since 7d
total_sessions used_skill bypassed
────────────── ────────── ────────
168 16 152
152 sessions. The skill was right there. Nobody called it.
TITLE CARD: SQL for your AI coding sessions.
cq indexes Claude Code and Codex JSONL session transcripts into a local DuckDB cache at ~/.cache/cq/ and gives you SQL views to query against. Incremental sync keeps it fresh on each run, so you only pay the full-parse cost once. Built-in commands handle the common stuff, and cq sql lets you run whatever you want.
If you have DotSlash installed, download the pointer file instead of a platform archive:
curl -fL https://github.com/technicalpickles/cq/releases/latest/download/cq -o cq
chmod +x cq
./cq --help
The downloaded cq is a small pointer file, not the binary itself: running it resolves your platform, fetches and caches the matching binary from the release (verifying it against a checksum baked into the pointer file), and execs it.
Grab the archive for your platform from the latest release, extract it, and put cq on your PATH. Builds are published for macOS (Apple Silicon and Intel) and Linux (x86_64 and arm64). The Linux builds link against glibc and need glibc 2.35 or newer (Ubuntu 22.04 and later, Debian 12 and later).
Requires Rust.
cargo install --git https://github.com/technicalpickles/cq
cq sessions # your recent sessions
cq search "dependency migration" # ranked full-text search across messages
cq tools # tool usage, ranked
cq messages --grep "docker" --since 7d # search your history
cq tools --errors --result-grep "ECONNREFUSED" # what failed, and why
cq hooks # hook events, ranked (SessionStart, PreToolUse, ...)
cq sql "SELECT count(*) FROM messages" # run anything
Run cq schema --examples for a query cookbook.
cq search uses BM25 relevance ranking with stemming, so a query such as
cq search "dependency migrations" can match messages containing “migrate a
dependency.” Results show the best-scoring message per session with a matches
count of how many messages in that session hit; --all-matches returns every
matching passage instead. This is lexical search rather than semantic search:
related ideas expressed with wholly different vocabulary still require
embeddings.
The first search downloads DuckDB's official fts extension into the cq cache
and builds a search index, which takes a while on a large corpus. After that the
index is allowed to lag up to five minutes behind your transcripts, because
rebuilding it costs several times a normal query and searches tend to come in
bursts. When cq serves a stale index it says so on stderr. Use --reindex to
rebuild on demand, CQ_FTS_MAX_AGE to change the window (0s refreshes on
every search, 1h is lazier), and --no-reindex to skip the refresh entirely.
| Flag | Short | Description |
|---|---|---|
--project <name> | -p | Scope to a project (substring match) |
--session <id> | -s | Scope to a session (UUID prefix match) |
--since <duration> | Time filter: 7d, 24h, 30m | |
--all | Remove inferred current-context scope (project, source, harness) | |
--harness <name> | Target one harness (claude or codex; cannot combine with --source) | |
--json | JSON output instead of tables; does not change scope | |
--table | Aligned table with headers | |
--no-color | Disable colored output | |
--limit <n> | Max results (default: 50, 0 for unlimited) | |
--offset <n> | Skip first N results | |
--type <type> | Filter message results (user or assistant; messages, search) | |
--all-matches | Every matching message instead of the best per session (search) | |
--version | -V | Print the cq version |
-A N | Show N messages after each match (messages, tools) | |
-B N | Show N messages before each match (messages, tools) | |
-C N | Shorthand for -A N -B N (messages, tools) |
DuckDB defaults to every core and 80% of RAM, and several cq processes at once (one per Claude session, say) can stack up and swamp a laptop. cq caps each process instead:
| Variable | Default | Description |
|---|---|---|
CQ_DUCKDB_THREADS | half the cores (at least 1) | Threads per cq process. auto leaves DuckDB's default. |
CQ_DUCKDB_MEMORY_LIMIT | 4GB | DuckDB's memory limit (512MB, 8GB, ...). auto leaves DuckDB's default. |
Work past the memory limit spills to <cache>/tmp/<pid> instead of failing where it can. Spill files can contain transcript content, are removed when the process exits, and can use a large share of free disk. Resident memory runs somewhat above the limit, since not everything goes through DuckDB's allocator. If a query hits the cap anyway, cq prints the effective limits and these variable names after the error.
cq indexes multiple transcript sources: the main config dir (~/.claude/projects, or $CQ_PROJECTS_DIR) plus every cenv env's projects/ (discovered under $CENV_BASE, default ~/.local/share/cenv). A cenv env is one kind of source; cq never shells out to cenv.
When Claude is the active harness, cq scopes to the active Claude source (the one matching $CLAUDE_CONFIG_DIR, else main), mirroring how it auto-scopes to the current directory. Use --all to span every source and --source <name> to target one. Every row carries a source column; compose with --since to weigh results by age.
| Flag | What it does |
|---|---|
| (default) | Scope to the active source |
--source <name> | Target one source by name (e.g. main, or a cenv env name) |
--all | Span all sources |
Codex transcripts are discovered recursively from $CODEX_HOME/sessions (default: ~/.codex/sessions/). Set CQ_CODEX_SESSIONS_DIR to override that location for cq only. Codex rows have harness = 'codex' and no source.
Built-in commands select the active harness by default: harness = 'codex' inside a Codex session and harness = 'claude' everywhere else. Codex selection skips Claude's automatic source scope. Use --all to span harnesses, or --harness claude / --harness codex to choose one explicitly. --source selects Claude rows only, so it cannot be combined with --harness. cq sql is raw SQL and ignores all scope flags.
Six SQL views, all queryable with cq sql:
subagent_counthook_additional_context recordsEvery view includes a harness column (claude or codex). Subagent activity is indexed for Claude Code: messages, tool_calls, and tool_results carry is_sidechain, agent_id, agent_type, and workflow_id so you can include, exclude, or focus subagents. cq sessions stays main-loop-only.
Run cq schema for full column details.
cq trace --session <id> renders a session as a terminal waterfall: one row per lane (main loop first, then subagents by first activity), duration bars scaled to your terminal width, and a header that always states the time-per-column scale. Tool execution is often a minority of wall clock: one real session ran 344.9 minutes with only 169.3 minutes (49%) inside tool spans, the rest spent waiting on you or on the model.
$ cq trace --session a1b2c3d4
7 spans 3 lanes 1.7 min wall [72 cols = 1.4 s/col]
1.0 min of tool work across 3 lanes blocked on you 1.0 min
main 4 ████████████████████████████ ██
sub1 2 ███████████████
sub2 1 █
Use --from/--to to zoom into a slice of the session, as an offset from session start (+12m, +90s, +2h) or an absolute ISO timestamp:
cq trace --session <id> --from +12m --to +17m
Add --format <FORMAT> to emit a trace file on stdout instead of the waterfall [valid: waterfall, perfetto, firefox-profiler]:
cq trace --session <id> --format firefox-profiler > profile.json
cq trace --session <id> --format perfetto > trace.json
--format firefox-profiler emits Firefox Profiler's native processed-profile JSON directly — open it at profiler.firefox.com for real per-category colors in the Marker Chart (Firefox Profiler's own Chrome Trace importer grays every marker out). --format perfetto emits Chrome Trace Event JSON, for opening in Perfetto or Speedscope; it also still loads in Firefox Profiler, just without native colors. The old --perfetto boolean flag is a deprecated alias for --format perfetto and still works, but new scripts should use --format.
Add --open to skip the file entirely and jump straight to the browser:
cq trace --session <id> --open # implies --format firefox-profiler
cq trace --session <id> --format perfetto --open
--open serves the trace from a local httpd and launches your OS browser straight to Firefox Profiler or Perfetto, instead of printing or saving JSON — the httpd only binds 127.0.0.1, and the hosted viewer's own network traffic is just its static JS/CSS, so the trace data itself never leaves your machine unless you explicitly click "Share" inside the viewer. It's not valid with --format waterfall or --json, since neither produces a file a browser can load. The server runs in the foreground and blocks the terminal until you hit Ctrl-C; that's intentional, not a hang. Without --open, running --format firefox-profiler/--format perfetto on an interactive terminal writes to a deterministic tmp file and prints its path instead of dumping raw JSON at you; piped or redirected output is unaffected.
The local server binds port 9001 by default (--port overrides it, and is only valid alongside --open) — that's not arbitrary: ui.perfetto.dev's own Content-Security-Policy only allows local connections to that exact port. Firefox Profiler has no such restriction, but both formats default to 9001 anyway so there's one port to remember. If something else already holds 9001, pass --port to pick a free one.
--open --format firefox-profiler opens your OS default browser, which needs to be Firefox or Chrome: Safari has its own limitation that blocks importing local profiles into Firefox Profiler entirely (a Safari restriction, not a cq bug), and refuses with an on-page error instead of loading the trace.
Tool spans and "blocked on you" gaps carry a compact args.detail string too (the tool's input, or the message that ended the gap, truncated at 200 chars) — Firefox Profiler renders it straight into the Marker Chart, Marker Table, and tooltip with no click needed; Perfetto shows the full args object regardless.
The global --json flag returns span rows instead of either renderer: one object per paired tool call, with lane, duration_ms, and is_error.
cq bundle --session <id> packages one session's raw transcript files -- the
main JSONL, any subagent JSONL (including nested workflow agents) and their
.meta.json sidecars, and best-effort copies of any persistedOutputPath
sidecars the records point to -- into a single zip with a manifest.json.
Useful for archiving a session before it rotates out of
~/.claude/projects/, attaching one to a bug report, or handing raw JSONL to
another tool without re-deriving cq's own file discovery by hand.
$ cq bundle --session a1b2c3d4
Wrote ./session-a1b2c3d4-0000-4000-8000-000000000001.zip (5 files, 0 sidecars, 3214 bytes)
cq bundle --session <id> -o ~/Desktop/session.zip # explicit output path
For deeper examples of what you can dig up, see docs/use-cases.md. Skill activation gaps, silent failures that look fine from the outside, context budget analysis across tool calls.
cq ships a Claude Code plugin that teaches Claude when and how to query your session history. Install it from the pickled-claude-plugins marketplace and Claude will reach for cq automatically when you ask about past sessions.
See claude-plugin/README.md for details.
cq schema and cq schema --examples are designed to be consumed by AI agents building their own queries. Pair with --json for machine-readable output.
MIT