{"id":51224865,"url":"https://github.com/saagpatel/ccq","last_synced_at":"2026-06-28T10:03:34.823Z","repository":{"id":366274392,"uuid":"1275698973","full_name":"saagpatel/ccq","owner":"saagpatel","description":"Query your own Claude Code agent history with DuckDB, read-only, over the JSONL transcripts.","archived":false,"fork":false,"pushed_at":"2026-06-21T04:19:32.000Z","size":55,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-21T05:11:58.606Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saagpatel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-21T03:09:34.000Z","updated_at":"2026-06-21T04:19:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/saagpatel/ccq","commit_stats":null,"previous_names":["saagpatel/ccq"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/saagpatel/ccq","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fccq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fccq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fccq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fccq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saagpatel","download_url":"https://codeload.github.com/saagpatel/ccq/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fccq/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34884278,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-28T10:03:34.342Z","updated_at":"2026-06-28T10:03:34.813Z","avatar_url":"https://github.com/saagpatel.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ccq: query your own Claude Code agent history\n\n[![CI](https://img.shields.io/github/actions/workflow/status/saagpatel/ccq/ci.yml?style=flat-square\u0026logo=githubactions\u0026logoColor=white\u0026label=CI)](https://github.com/saagpatel/ccq/actions/workflows/ci.yml)\n[![Python](https://img.shields.io/badge/python-3.12%2B-blue?style=flat-square\u0026logo=python\u0026logoColor=white)](https://www.python.org/)\n\n`ccq` makes your local Claude Code transcripts queryable. It runs **DuckDB directly\nover the JSONL** at `~/.claude/projects/\u003cproject\u003e/\u003csession\u003e.jsonl`. No copy, no ETL,\nno database to maintain. The transcripts are only ever **read**, never written.\n\nAsk it where your tokens go, which tools you lean on, where runs hit rate limits,\nhow much you delegate to subagents, and what happened inside any single session.\n\n```\n$ ccq cost --by model\nmodel                       turns          in_tok      out_tok   cost_usd\n-------------------------  ------  --------------  -----------  ---------\nclaude-opus-4-8            12,345   3,456,789,012   28,500,000   4,210.55\nclaude-sonnet-4-6           6,789     901,234,567    8,400,000      612.30\n...\n```\n\nA few more angles: your most-used tools, and the full timeline of any one session.\n\n```\n$ ccq tools\ntool_name    calls  sessions  projects\n----------  ------  --------  --------\nBash         4,210       210       48\nRead         2,980       198       45\nEdit         1,740       142       40\nAgent          410        88       24\n```\n\n```\n$ ccq session a1b2c3d4\nsession  a1b2c3d4-9f02-4c7e-bb31-0e5a6d7c8e90\nproject  demo-app   branch main\nspan     2026-06-01 10:00 -\u003e 10:42  (42 min, 88 messages)\nmodels   claude-opus-4-8, claude-sonnet-4-6\nest cost $4.21\n------------------------------------------------------------\nts          kind        detail\n10:00:05    prompt      refactor the duckdb loader\n10:01:12    tool:Bash   uv run pytest -q\n10:03:48    tool:Edit   src/ccq/db.py\n10:07:20    ERROR 429   claude-opus-4-8\n```\n\n## Install\n\n```bash\nuv sync          # create the venv + install deps\nuv run ccq --help\n```\n\n(Or `uv tool install .` to put `ccq` on your PATH.)\n\n## Commands\n\n| Command | What it answers |\n|---|---|\n| `ccq sessions` | List sessions: project, span, message count, tokens, estimated cost. `--sort cost\\|duration\\|messages\\|recent`, `--project`, `--since`, `-n`. |\n| `ccq cost` | Cost rollups. `--by project\\|model\\|day\\|session`. Main-loop only (see caveat). |\n| `ccq tools` | Tool-use frequency. `--bash` breaks Bash calls down by leading command. |\n| `ccq errors` | API errors / retries (429s, etc.) by project + status. `--list` for recent events. |\n| `ccq agents` | Subagent (Agent tool) dispatches + token totals. `--by type\\|model\\|session\\|project`. |\n| `ccq session \u003cid-prefix\u003e` | One session's decision timeline: prompts, tool calls, errors, in order. |\n| `ccq search \u003ctext\u003e` | Full-text over your typed prompts and session titles → matching sessions. |\n| `ccq sql \"\u003cSELECT…\u003e\"` | Run an arbitrary **read-only** query over the views (power surface). |\n| `ccq serve` | Launch a local web dashboard (localhost only) with a read-only SQL box. |\n| `ccq cache build\\|status\\|clear` | Manage the materialized snapshot that powers `--fast`. |\n\nEvery command takes `-f table|json|csv` and a global `--projects-dir` (defaults to\n`~/.claude/projects`, handy for pointing at a backup).\n\n## Fast mode\n\nA live query rescans ~1 GB of JSONL each time (~1-3 s). For instant repeat queries,\nmaterialize a snapshot once and pass `--fast` (`-F`):\n\n```bash\nccq cache build          # ~6 s, writes a ~100 MB snapshot to ~/.cache/ccq\nccq -F cost --by model   # now ~0.1 s\nccq cache status         # shows STALE once transcripts change; rebuild to refresh\n```\n\nThe snapshot lives under `$XDG_CACHE_HOME` (never in `~/.claude`), is opened\n**read-only** at the engine level, and `--fast` builds it automatically on first use.\n\n## Web viewer\n\n```bash\nccq -F serve             # http://127.0.0.1:8787  (Ctrl-C to stop)\n```\n\nA dashboard (cost by model, tools, errors, subagents, priciest sessions) plus a SQL\nbox that runs the same read-only-guarded queries. **Drill down:** click a project\nchip to filter to that project, or a session id to see its full decision timeline.\nStandard-library `http.server`, no extra dependencies, binds to localhost only.\n\n## The query surface (`ccq sql`)\n\n`sql` exposes these views. Compose your own:\n\n- **`sessions`** - one row per session: project, branch, span, `messages`, `models`, tokens, `cost_usd`.\n- **`message_usage`** - one row per assistant turn: token breakdown + `cost_usd`.\n- **`tool_calls`** - one row per tool invocation: `tool_name`, `tool_input` (JSON).\n- **`errors`** - API error events: `status`, project, session, model.\n- **`agents`** / **`agent_results`** - subagent dispatches joined to their `subagent_tokens`.\n- **`prompts`** - searchable typed prompts + session titles.\n- **`events`** - the raw per-line view everything else is built on.\n- **`model_pricing`** - the per-model rate table used for costing.\n\n```bash\nccq sql \"SELECT project, round(sum(cost_usd),2) usd\n         FROM message_usage WHERE ts \u003e= DATE '2026-06-01'\n         GROUP BY 1 ORDER BY usd DESC\"\n```\n\n`sql` accepts a **single read-only statement** (SELECT/WITH/EXPLAIN/…). Writes,\n`ATTACH`, `COPY`, `INSTALL`, and multi-statement input are refused. Tip: DuckDB\nreserves words like `day`, `first`, `last`, so quote them if used as aliases (`AS \"day\"`).\n\n## How it works\n\n`read_ndjson_objects('~/.claude/projects/*/*.jsonl')` loads each line as an opaque\n`JSON` value (zero schema inference, since the records are heterogeneous), and SQL views\nextract the entities. Two things the transcripts taught us are baked in:\n\n- Numeric fields use `TRY_CAST` (heterogeneous lines otherwise break a hard cast).\n- Token-casting views read from a type-filtered subquery so the optimizer can't\n  reorder a cast ahead of the `type = 'assistant'` filter.\n\n## Cost is estimated, and main-loop only\n\nTranscripts store **token counts, not dollars**. `ccq` prices them with published\nper-million-token rates (`src/ccq/pricing.py`) and the standard cache multipliers\n(cache write 1.25×, cache read 0.10×). Two honest caveats:\n\n1. **Estimates, not invoices**: an unlisted model falls back to its family rate\n   (`opus`/`sonnet`/`haiku`/`fable`, generation-aware where tiers differ); names with\n   no Claude family word (`\u003csynthetic\u003e`, non-Claude) price to `$0`.\n2. **Main-loop only**: a subagent's spend is **not** in the transcript. The only\n   signal that survives is `toolUseResult.totalTokens` (no input/output split, so it\n   can't be priced). `ccq agents` surfaces those token totals **separately**; they\n   are never folded into a dollar figure.\n\n## Develop\n\n```bash\nuv run pytest                                   # tests over synthetic fixtures\nuv run ruff check . \u0026\u0026 uv run ruff format .     # lint + format\nuv run ty check src/                            # type check\n```\n\nRead-only on `~/.claude/projects` by contract. The test suite uses synthetic\nfixtures and never reads your real history.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaagpatel%2Fccq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaagpatel%2Fccq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaagpatel%2Fccq/lists"}