Zero-copy SQL + semantic search + LLM analytics over ~/.claude/ transcripts.
Project description
claude-sql
Ask your Claude Code transcripts anything. Your sessions are already on disk.
claude-sqlturns them into a searchable, explorable, self-improving record of your work — in place, with zero copy.
What you get out of it
Remember what you worked on.
- "What was that thing I did last Tuesday with DuckDB and HNSW?"
- "Show me every conversation I've had about temporal workflows, ranked by relevance."
- "Which week did I finally figure out that memory bug?"
See where your time and money actually go.
- "Which sessions cost me more than $5 on Opus this month — and what was I trying to do?"
- "Which tools am I leaning on most? Which ones fail the most?"
- "Where am I spending hours on prose vs. on tool calls?"
Notice patterns in how you work.
- "When do I hand-hold the agent step-by-step vs. let it run on its own? Has that shifted over time?"
- "What kinds of work am I doing most — coding, strategy, admin, writing?"
- "Which session types actually finish successfully vs. trail off?"
- "Which todos do I create and never close out?"
Surface themes across hundreds of conversations.
- "Group my sessions by what they're about and tell me what moved this month."
- "Show me the biggest themes in my work and what's trending."
- "When I've wrestled with the same problem across multiple sessions, group them together so I can see the arc."
Catch yourself disagreeing with yourself.
- "Find sessions where I took two opposing positions on the same decision — and flag which ones got resolved vs. abandoned."
Spot where the agent left you hanging.
- "Which sessions had me pinging for status the most? What was the agent doing?"
- "Show me every time I asked a one-word question like screenshot? because the agent didn't proactively share one."
- "Rank sessions by how often I had to interrupt or correct the agent."
claude-sql turns every one of those into a SQL query that runs in under
a second on the live JSONL corpus — no export, no pipeline.
How it works
flowchart LR
J["~/.claude/projects/<br/>*.jsonl"] -->|read_json| R[raw views]
J2["subagents/<br/>agent-*.jsonl"] -->|read_json| R
R --> V[business views]
V --> Q[["claude-sql query<br/>claude-sql explain<br/>claude-sql schema"]]
V --> E["claude-sql embed<br/>(Cohere Embed v4 on Bedrock)"]
E --> P["LanceDB store<br/>(~/.claude/embeddings_lance/<br/>FLOAT[1024] + IVF_HNSW_SQ index)"]
P --> S[["claude-sql search"]]
V --> L["claude-sql classify / trajectory /<br/>conflicts / friction (Sonnet 4.6 +<br/>output_config.format)"]
L --> PA["session_classifications/, message_trajectory/,<br/>session_conflicts/, user_friction/<br/>(sharded part-*.parquet)"]
P --> C["claude-sql cluster + terms<br/>(UMAP + HDBSCAN + c-TF-IDF)"]
C --> PC["clusters + cluster_terms"]
P --> CM["claude-sql community<br/>(Leiden + CPM over mutual-kNN centroids)"]
CM --> PM["session_communities<br/>parquet"]
PA --> AV[analytics views + macros]
PC --> AV
PM --> AV
AV --> Q
Every parquet is cached and rebuilt only on explicit re-run. Views register over whichever parquets exist at connection open — missing ones warn and no-op, never crash.
Install
As a uv tool (recommended)
claude-sql is published to PyPI.
Install the CLI into an isolated, uv-managed venv on your PATH:
uv tool install claude-sql
claude-sql --version
uv tool upgrade claude-sql # pull the latest release
uv tool uninstall claude-sql # remove it
Or run it without installing anything persistent:
uvx claude-sql schema
From a local checkout (latest unreleased commits)
To run ahead of the last PyPI release, install from a clone.
mise run tool:install wraps uv tool install --from . claude-sql --force --reinstall:
git clone https://github.com/theagenticguy/claude-sql.git
cd claude-sql
mise run tool:install # → uv tool install --from . claude-sql --force --reinstall
claude-sql --version
Upgrade after pulling new commits by re-running the same task (the
tool:upgrade alias runs an identical command with clearer intent):
git pull
mise run tool:upgrade # reinstall from the checkout, not from PyPI
mise run tool:uninstall # → uv tool uninstall claude-sql
Project install (for development)
git clone https://github.com/theagenticguy/claude-sql.git
cd claude-sql
mise install # fetch pinned Python + uv
mise run install # uv sync --all-extras + install lefthook git hooks
mise run check # ruff + fmt + ty + pytest
mise auto-activates .venv on cd. Every command below is also
available as a mise task — run mise tasks for the full list.
mise run install also installs the lefthook git hooks. Re-run
mise run hooks:install any time lefthook.yml changes.
AWS credentials
Semantic search and Sonnet classification require Bedrock access.
export AWS_PROFILE=your-profile
The IAM policy needs bedrock:InvokeModel on:
inference-profile/global.cohere.embed-v4:0inference-profile/global.anthropic.claude-sonnet-4-6
Reading transcripts from S3
claude-sql reads the local JSONL corpus by default, but any transcript glob
can be an s3:// URI instead — point it at sessions mirrored to S3 by the
claude-agent-sdk S3SessionStore
(layout s3://{bucket}/{prefix}{project}/{session}/part-*.jsonl). DuckDB reads
the parts zero-copy over HTTP range requests — no download step — and every
view and macro works unchanged.
# Personal corpus on S3 instead of ~/.claude/projects.
export CLAUDE_SQL_DEFAULT_GLOB='s3://my-bucket/transcripts/*/*/part-*.jsonl'
export AWS_PROFILE=your-profile # credentials via the standard AWS chain
claude-sql schema
claude-sql query "SELECT session_id, started_at FROM sessions ORDER BY started_at DESC LIMIT 10"
claude-sql loads DuckDB's httpfs extension and creates a credential_chain
S3 secret automatically when it sees an s3:// glob — no keys are embedded
anywhere. For a non-AWS store (MinIO) or a local mock, set
CLAUDE_SQL_S3_ENDPOINT, CLAUDE_SQL_S3_URL_STYLE=path, and
CLAUDE_SQL_S3_USE_SSL=false. The IAM policy needs s3:GetObject +
s3:ListBucket on the prefix.
Quick tour
# Inspect every registered view + macro.
claude-sql schema
# Opus sessions over $5 in the last 30 days.
claude-sql query "
SELECT session_id, model_used(session_id) AS model,
cost_estimate(session_id) AS usd
FROM sessions
WHERE started_at >= current_timestamp - INTERVAL 30 DAY
AND model_used(session_id) LIKE '%opus%'
AND cost_estimate(session_id) > 5.0
ORDER BY usd DESC
"
# See the EXPLAIN plan (static by default — no execution).
claude-sql explain "SELECT * FROM messages WHERE session_id = '<uuid>' LIMIT 1"
# Drop into the DuckDB REPL with everything pre-registered.
claude-sql shell
# Backfill embeddings (Cohere Embed v4 via global CRIS).
claude-sql embed --since-days 30
# Semantic search.
claude-sql search "temporal workflow determinism" --k 10
# Classify every recent session (dry-run prints a cost estimate first).
claude-sql classify --dry-run --since-days 30
claude-sql classify --no-dry-run --since-days 30
# Friction classifier — status pings, unmet expectations, interruptions.
claude-sql friction --dry-run --since-days 14
claude-sql friction --no-dry-run --since-days 14
claude-sql query "SELECT * FROM friction_counts(14)"
claude-sql query "SELECT * FROM friction_examples('unmet_expectation', 10)"
# Seed the Skills catalog from ~/.claude/skills + ~/.claude/plugins/cache.
claude-sql skills sync
claude-sql skills ls --kind plugin-skill | head
claude-sql query "SELECT * FROM skill_rank(30) LIMIT 15"
claude-sql query "SELECT * FROM skill_source_mix(30) WHERE skill_id LIKE '%erpaval%'"
claude-sql query "SELECT * FROM unused_skills(30) LIMIT 20"
# Full analytics pipeline (includes a zero-cost `skills sync` at step 0).
claude-sql analyze --since-days 30 --no-dry-run
# Provenance: resolve a merged commit back to the transcript that wrote it.
claude-sql resolve "$(git rev-parse HEAD)"
claude-sql review-sheet "$(git rev-parse HEAD)" --no-dry-run
# Eval gym: pre-register a study, run the judge panel, gate on agreement.
claude-sql judges
claude-sql freeze rubric.yaml --panel kimi-k2.5,deepseek-v3.2,glm-5
claude-sql judge <manifest_sha> --sessions-parquet sessions.parquet \
--output-parquet scores.parquet --no-dry-run
claude-sql kappa scores.parquet --floor 0.6
More recipes in docs/cookbook.md (v1: sessions,
messages, tools, todos, subagents, semantic search) and
docs/analytics_cookbook.md (v2: clusters,
communities, classifications, trajectory, conflicts, friction).
CLI surface
Every subcommand shares the top-level flags: --verbose / --quiet,
--glob, --subagent-glob, and --format {auto,table,json,ndjson,csv}.
The LLM-classification commands (classify, trajectory, conflicts,
friction, analyze, judge, review-sheet) and the cache-rewriting
ones (cache compact, cache migrate, ingest) default to --dry-run.
embed and search are the exceptions — they spend Bedrock the moment
you call them (an embed backfill and a single query-vector call,
respectively), so they have no dry-run gate.
Query + introspection (zero cost)
| Command | Purpose |
|---|---|
schema |
List every view + its columns, plus every macro |
query <sql> |
Run a query, emit as table / JSON / NDJSON / CSV |
explain <sql> |
Static EXPLAIN by default; --analyze for EXPLAIN ANALYZE |
shell |
Launch the duckdb REPL with everything pre-registered |
list-cache |
Report freshness + row counts for every parquet cache |
peek <session_id> |
One-shot session summary — line count, role mix, top tools, samples |
Embeddings + structure
| Command | Purpose |
|---|---|
embed |
Backfill embeddings via Cohere Embed v4 on Bedrock (spends by default) |
search <text> |
IVF_HNSW_SQ cosine semantic search over the LanceDB store |
ingest |
Stamp messages with approx_tokens / simhash64 / canonical UUID (CPU only) |
cluster |
UMAP → HDBSCAN over message embeddings (CPU only; --force to rebuild) |
terms |
c-TF-IDF term labels per cluster (CPU only) |
community |
Leiden + CPM over mutual-kNN session centroids; emits medoid + coherence + resolution profile + --neighbors-of |
LLM analytics (Sonnet 4.6 — default --dry-run)
| Command | Purpose |
|---|---|
classify |
Session autonomy + work category + success + goal |
trajectory |
Per-window sentiment + transition kind |
conflicts |
Per-session stance-conflict detection |
friction |
Regex + Sonnet 4.6 → status pings, unmet expectations, confusion, etc. |
analyze |
Run the whole pipeline in dependency order (skills → ingest → embed → cluster → terms → community → classify → trajectory → conflicts → friction) |
Skills catalog + cache maintenance
| Command | Purpose |
|---|---|
skills sync |
Walk ~/.claude/skills/ + ~/.claude/plugins/cache/ → seedable skills catalog |
skills ls |
List catalog entries, filterable by --kind and --plugin |
cache compact |
Consolidate a sharded <cache>/part-*.parquet directory into one file |
cache migrate |
Move a legacy single-file cache into the sharded directory layout |
Eval gym (cross-provider judge panels + agreement gates)
| Command | Purpose |
|---|---|
judges |
List the cross-provider Bedrock judge catalog |
freeze <rubric> |
Pre-register a study — write an immutable manifest under ~/.claude/studies/<sha>/ |
replay <manifest_sha> |
Load + echo a frozen study manifest by SHA |
blind-handover <in> <out> |
Strip identity markers from a sessions parquet for grader-safe handover |
judge <manifest_sha> |
Dispatch a frozen study's judge panel over a sessions parquet |
ungrounded-claim <manifest_sha> |
Run the ungrounded-claim detector over a turns parquet |
kappa <scores_parquet> |
Cohen's + Fleiss' kappa with bootstrapped 95% CI; exit 66 below the floor |
Transcript ↔ PR provenance (RFC 0001)
| Command | Purpose |
|---|---|
bind |
Attach a transcript↔commit binding (trailers + git-notes) to a commit |
resolve <commit_sha> |
Resolve a commit's bound transcript (trailer → note precedence) |
review-sheet <commit_sha> |
Render a compressed PR review sheet (Sonnet 4.6) |
Agent-friendly defaults
--format autoemits a human table on a TTY and JSON when stdout is piped, so agents callingclaude-sqlvia subprocess get JSON for free.json,ndjson, andcsvare always available explicitly.- Classified exit codes for DuckDB errors —
64for invalid input (malformed flags or unparseable SQL),65for unknown view / column / macro,70for other runtime errors, and2whensearchis called beforeembedhas run (also reused whenresolve/review-sheetfind no binding). Two command-specific codes:kappaexits66when an agreement floor or delta-gate is tripped, andshellexits127when the systemduckdbbinary isn't onPATH. On non-TTY stdout the error also comes back as{"error": {"kind", "message", "hint"}}on stderr, so agents don't have to scrape tracebacks. list-cachereports every parquet (embeddings, classifications, trajectory, conflicts, clusters, cluster terms, communities, friction) with its{exists, bytes, mtime, rows}, so an agent can decide whether to run a prerequisite stage before issuing asearchorquery.explainis a static plan by default (no query execution); pass--analyzeforEXPLAIN ANALYZEwhen you want real timings.--quietdrops INFO / WARNING logs to ERROR-only. View registration happens at DEBUG level, so the defaultquerystderr is already empty unless something actually warrants attention.
Views
| View | Grain | Key columns |
|---|---|---|
sessions |
one per transcript file | session_id, started_at, ended_at |
messages |
one per chat message | uuid, session_id, role, model, token usage |
content_blocks |
flattened message.content[] |
block_type, tool_name |
messages_text |
text blocks aggregated per message (≥32 chars) | uuid, text_content |
turn_window |
adjacent-turn LAG window per session (compact-summary excluded) |
prev_uuid, curr_uuid, gap_ms, window_idx |
tool_calls |
content_blocks where type='tool_use' |
tool_name, tool_use_id |
tool_results |
content_blocks where type='tool_result' |
tool_use_id, content |
todo_events |
one row per todo per TodoWrite snapshot (legacy + --print/SDK) |
subject, status, snapshot_ix |
todo_state_current |
latest status per (session, subject) for TodoWrite |
status, written_at |
subagent_spawns |
Task / Agent launch sites (Claude Code v2.1.63 renamed Task→Agent) |
subagent_type, description, prompt |
task_creations |
TaskCreate / mcp__tasks__task_create (interactive task tracker, v2.1.16+) |
subject, description, active_form, metadata |
task_updates |
TaskUpdate / mcp__tasks__task_update lifecycle events |
task_id, status, add_blocked_by, owner |
tasks_state_current |
latest status per (session, task_id) for the v2.1.16+ family |
task_id, subject, status, last_updated_at |
skill_invocations |
every Skill tool call + <command-name>/foo</command-name> user slash |
source (tool / slash_command), skill_id, args |
subagent_sessions |
rolled-up subagent runs | parent_session_id, agent_hex, agent_type, description, started_at, ended_at, message_count, transcript_path |
subagent_messages |
user + assistant events from subagent transcripts | uuid, parent_session_id |
message_embeddings |
one row per embedded message (LanceDB-backed; empty stub before first embed) |
uuid, model, dim, embedding (FLOAT[1024]), embedded_at |
ingest_stamps |
per-message ingest stamps (seeded by claude-sql ingest) |
uuid, simhash64, canonical_uuid |
session_classifications |
one row per classified session | autonomy_tier, work_category, success, goal |
session_goals |
projection over classifications | session_id, goal |
message_trajectory |
one row per adjacent-turn window (pair-keyed) | prev_uuid, curr_uuid, curr_sentiment, delta, transition_kind, is_transition, confidence |
session_conflicts |
one row per conflicting turn pair (zero rows when none) | turn_a_uuid, turn_b_uuid, conflict_kind, severity, agent_position, user_position, confidence |
conflicts_summary |
per-session conflict count over session_conflicts |
session_id, conflict_count |
message_clusters |
cluster id + 2d viz coords | cluster_id, x, y, is_noise |
cluster_terms |
c-TF-IDF top terms per cluster | cluster_id, term, weight, rank |
session_communities |
Leiden+CPM community per session | community_id, size, is_medoid, coherence, gamma_used |
community_profile |
Resolution-profile sidecar (auto-γ runs only) | gamma, n_communities, quality, plateau_length |
user_friction |
one row per classified short user message | label (7-way), rationale, source (regex / llm / refused), confidence |
skills_catalog |
one row per known skill / slash command (seed by claude-sql skills sync) |
skill_id, name, plugin, plugin_version, source_kind (user-skill / plugin-skill / plugin-command / builtin), description |
skill_usage |
skill_invocations ⟕ skills_catalog |
source, skill_id, skill_name, plugin, is_builtin, description |
Macros
| Macro | Signature | What it does |
|---|---|---|
ago(interval_text) |
scalar → TIMESTAMP |
current_timestamp - INTERVAL <text> -- e.g. WHERE ts >= ago('30 days') |
model_used(sid) |
scalar → VARCHAR |
Latest model observed in the session |
cost_estimate(sid) |
scalar → DOUBLE |
USD spend (dated model IDs prefix-matched) |
tool_rank(last_n_days) |
table | Tool-use leaderboard over a window |
todo_velocity(sid) |
scalar → DOUBLE |
Completed / distinct todos ratio |
subagent_fanout(sid) |
scalar → INT |
Subagent runs for a session |
semantic_search(query_vec, k) |
table | HNSW top-k over embeddings |
autonomy_trend(window_days) |
table | Weekly autonomy-tier mix |
work_mix(since_days) |
table | Work-category distribution |
success_rate_by_work(since_days) |
table | Success / failure / partial rates per category |
cluster_top_terms(cid, n) |
table | Top-N terms for a cluster |
community_top_topics(cid, n) |
table | Dominant clusters within a community |
sentiment_arc(sid) |
table | Per-window sentiment timeline for one session |
friction_counts(since_days) |
table | Count + session breadth per friction label |
friction_rate(since_days) |
table | Per-session friction pressure vs. user message count |
friction_examples(label_name, n) |
table | Top-N example messages for a friction label |
skill_rank(last_n_days) |
table | Skill / slash leaderboard over a window (counts both tool and slash_command sources) |
skill_source_mix(last_n_days) |
table | Per skill n_tool vs. n_slash — how is each skill invoked? |
unused_skills(last_n_days) |
table | Catalog entries with zero invocations in the window (needs skills sync) |
canonical_uuid_resolve() |
table | SimHash near-duplicate UUID resolution over ingest_stamps (needs claude-sql ingest) |
Environment variables
Every setting in config.py is overridable via a CLAUDE_SQL_* env var
(prefix + the upper-cased field name) or a .env in the working
directory. The common knobs are below; the per-stage tuning fields
(CLAUDE_SQL_UMAP_*, CLAUDE_SQL_HDBSCAN_*, CLAUDE_SQL_TFIDF_*, the
*_THINKING modes, and each analytics parquet path) follow the same
prefix convention — see config.py for the full set.
| Variable | Default | Purpose |
|---|---|---|
CLAUDE_SQL_DEFAULT_GLOB |
~/.claude/projects/*/*.jsonl |
Main transcript glob |
CLAUDE_SQL_SUBAGENT_GLOB |
~/.claude/projects/*/*/subagents/agent-*.jsonl |
Subagent transcripts |
CLAUDE_SQL_TEAM_CORPUS_ROOT |
None |
Team-corpus root; when set, derives all three globs from <root>/<author>/projects/* (replaces the personal corpus) |
CLAUDE_SQL_S3_ENDPOINT |
None |
Custom S3 endpoint host[:port] for non-AWS stores (MinIO) or a local mock; unset uses default AWS S3. Only consulted when a glob is an s3:// URI |
CLAUDE_SQL_S3_URL_STYLE |
vhost |
S3 addressing style (vhost or path); set path for MinIO / moto |
CLAUDE_SQL_S3_USE_SSL |
true |
Toggle TLS for the S3 endpoint; set false for a local mock |
CLAUDE_SQL_REGION |
us-east-1 |
Bedrock region and the S3 secret region |
CLAUDE_SQL_MODEL_ID |
global.cohere.embed-v4:0 |
Embedding model |
CLAUDE_SQL_SONNET_MODEL_ID |
global.anthropic.claude-sonnet-4-6 |
Classification model |
CLAUDE_SQL_OUTPUT_DIMENSION |
1024 |
Matryoshka embedding dimension (256 / 512 / 1024 / 1536) |
CLAUDE_SQL_EMBEDDING_TYPE |
int8 |
Cohere output type (int8 / float / uint8 / binary / ubinary) |
CLAUDE_SQL_EMBED_CONCURRENCY |
8 |
Parallel Cohere Embed v4 calls (global CRIS) |
CLAUDE_SQL_LLM_CONCURRENCY |
16 |
Parallel Sonnet 4.6 calls (global CRIS) |
CLAUDE_SQL_BATCH_SIZE |
96 |
Cohere batch size |
CLAUDE_SQL_LANCE_URI |
~/.claude/embeddings_lance |
LanceDB embeddings store (FLOAT[1024] vectors + IVF_HNSW_SQ index) |
CLAUDE_SQL_HNSW_METRIC |
cosine |
Vector index distance metric (cosine / l2 / dot) |
CLAUDE_SQL_EMBEDDINGS_PARQUET_PATH |
~/.claude/embeddings/ |
Legacy parquet shards; kept only for one-time migration into LanceDB |
CLAUDE_SQL_USER_FRICTION_PARQUET_PATH |
~/.claude/user_friction/ |
Friction cache (sharded) |
CLAUDE_SQL_FRICTION_MAX_CHARS |
300 |
Short-message cutoff for the friction classifier |
CLAUDE_SQL_DUCKDB_THREADS |
os.cpu_count() |
DuckDB worker threads |
CLAUDE_SQL_DUCKDB_MEMORY_LIMIT |
'70%' |
DuckDB memory ceiling (percentage or absolute size) |
CLAUDE_SQL_DUCKDB_TEMP_DIR |
~/.claude/duckdb_tmp |
DuckDB spill directory (avoids /tmp tmpfs thrash) |
CLAUDE_SQL_SKILLS_CATALOG_PARQUET_PATH |
~/.claude/skills_catalog.parquet |
Skills catalog parquet |
CLAUDE_SQL_USER_SKILLS_DIR |
~/.claude/skills |
Root scanned for user-installed skills |
CLAUDE_SQL_PLUGINS_CACHE_DIR |
~/.claude/plugins/cache |
Root scanned for plugin skills + commands |
CLAUDE_SQL_SEED |
42 |
UMAP / HDBSCAN / Leiden determinism |
CLAUDE_SQL_LEIDEN_KNN_K |
15 |
Mutual-kNN k for the session-centroid graph |
CLAUDE_SQL_LEIDEN_EDGE_FLOOR |
0.3 |
Cosine floor below which edges are dropped |
CLAUDE_SQL_LEIDEN_MIN_COMMUNITY_SIZE |
3 |
Communities below this collapse to noise (-1) |
CLAUDE_SQL_LEIDEN_RESOLUTION |
unset (auto) | Explicit CPM γ; skips the resolution profile |
CLAUDE_SQL_LEIDEN_RESOLUTION_RANGE_LO |
0.05 |
Lower bound for Optimiser.resolution_profile bisection |
CLAUDE_SQL_LEIDEN_RESOLUTION_RANGE_HI |
0.95 |
Upper bound for the bisection |
CLAUDE_SQL_LEIDEN_N_ITERATIONS |
-1 |
Iterate to convergence; 2 is leidenalg's default |
CLAUDE_SQL_COMMUNITY_PROFILE_PARQUET_PATH |
~/.claude/community_profile.parquet |
Resolution-profile sidecar path |
Development
mise run check # lint + fmt-check + typecheck + tests
mise run fmt:write # auto-apply ruff formatting
mise run upgrade # uv lock --upgrade && uv sync
mise run build # uv build → dist/*.whl + *.tar.gz
mise run tool:install # install claude-sql as a uv tool (global)
mise run cli -- schema # run the CLI in the project venv
mise tasks # list every mise task
Git hooks + conventional commits
mise run install installs lefthook git hooks:
- pre-commit — parallel
ruff check --fix+ruff formaton staged Python files (auto-staged),ty check src/ tests/across the whole tree (strict mode), anduv lock --checkwhenpyproject.tomloruv.lockis staged. - commit-msg — validates the message via
cz check --allow-abortagainst the conventional-commits schema. - pre-push — runs the full pytest suite before the push lands.
Config lives in lefthook.yml. Reinstall any time that file changes:
mise run hooks:install
mise run hooks:uninstall # if you need to opt out
Version bumps + changelog (commitizen)
Commit messages follow
Conventional Commits. Supported
types: feat, fix, docs, style, refactor, perf, test,
build, ci, chore, revert. Use mise run commit for the
interactive wizard, or write the message yourself — either way the
commit-msg hook validates it.
Version bumps are driven by cz bump, which reads commit history and
decides MAJOR / MINOR / PATCH from the conventional-commits types:
mise run bump:dry-run # preview next version + tag
mise run bump # bump + changelog + annotated tag (vX.Y.Z)
mise run changelog # regenerate CHANGELOG.md without bumping
[tool.commitizen] is wired to version_provider = "uv", so every bump
keeps pyproject.toml[project.version] and uv.lock in sync
atomically.
Quality gates
Local mise run check (lint + fmt + typecheck + test) and GitHub
Actions run the same commands against the same pinned tool versions (via
jdx/mise-action), so contributors who pass the hooks locally can trust
CI agrees. On top of the local gate, CI layers in:
- Semgrep —
p/auto+p/owasp-top-tenrulesets, SARIF uploaded to GitHub code scanning. - Bandit — Python SAST as defense-in-depth alongside ruff's
flake8-bandit(S) selectors. Principled skips align 1:1 with the ruff S-ignores inpyproject.toml. SARIF to code scanning. - CodeQL —
security-and-qualityquery pack, weekly cron. - OSV-Scanner — known-CVE scan of
uv.lock, fails on findings. - Betterleaks — secrets sweep over full git history (gitleaks successor). SARIF to code scanning.
- OpenSSF Scorecard — weekly, SARIF to code scanning.
- Codecov — coverage.xml uploaded via tokenless OIDC.
- CycloneDX SBOM — generated + attached on every release.
Local security sweep
mise run security runs all four SAST/SCA/secrets scanners in parallel
against the working tree:
mise run security # bandit + semgrep + osv + leaks → .sarif/
mise run security:bandit # individual scanners also runnable
mise run security:semgrep
mise run security:osv
mise run security:leaks
Each scanner emits SARIF under .sarif/ (gitignored). The local sweep
mirrors what CI uploads to GitHub code scanning, so contributors can
reproduce findings without waiting on CI. All four use the
"report, don't gate" pattern (--exit-zero / --exit-code=0) — gating
happens through code-scanning branch protection, not the scanner exit.
See .erpaval/solutions/best-practices/sarif-scanner-report-vs-gate.md
for the rationale.
See docs/adr/0015-stack-modernization.md and
docs/adr/0016-ci-hardening.md for the full rationale.
Design notes
- Zero-copy reads.
read_json(..., filename=true, union_by_name=true, sample_size=-1, ignore_errors=true)so the corpus is queried in place. - Lazy content blocks. Nested
message.content[]stays as JSON and flattens viaUNNEST + json_extract_string, not eagerly shredded — resilient to new block types (thinking, MCP shapes, etc.). - Global CRIS for Cohere. The
global.cohere.embed-v4:0profile sustains the highest throughput with no throttling in testing; direct and US CRIS both saturate at low TPM. - LanceDB embeddings store. Vectors and the cosine-metric
IVF_HNSW_SQ(scalar-quantized HNSW) index live together in one versioned LanceDB dataset at~/.claude/embeddings_lance/; DuckDB reads it back via thelancecore extension (INSTALL lance; LOAD lance; ATTACH … (TYPE LANCE)). On a fresh install with no Lance table yet,register_vssbinds an emptymessage_embeddingsstub sosemantic_searchstill resolves. This replaces the oldembeddings/part-*.parquet+~/.claude/hnsw.duckdbrebuild-at-open combo; the legacy shards migrate over automatically on first connect. Recovery:rm -rf ~/.claude/embeddings_lance/thenclaude-sql embed. - Structured output, GA path. Sonnet 4.6 classification uses
Bedrock's GA
output_config.format(nottool_use/tool_choice) with adaptive thinking on. Pydantic v2 schemas are flattened (inline$ref, injectadditionalProperties: false, strip the numeric / string constraints the validator rejects from Draft 2020-12). - Determinism. UMAP, HDBSCAN, and Leiden all seed from
CLAUDE_SQL_SEED=42(default) so cluster IDs and community IDs are stable across reruns. The Leiden seed flows into bothleidenalg.find_partition(seed=...)andOptimiser.set_rng_seed(...)for the resolution-profile bisection — same seed + same input ⇒ byte-equal parquets across runs. - Communities =
leidenalg+ CPM. Reference Leiden implementation (leidenalg>=0.11.0) over anigraph>=1.0mutual-kNN cosine graph (k=15, edge floor 0.3) of session centroids. CPM γ has direct cosine semantics (internal density ≥ γ, external ≤ γ); auto-γ viaOptimiser.resolution_profile+ longest-plateau picker; warn-only connectivity check. Output is signal-rich:is_medoid,coherence,gamma_usedper row, plus acommunity_profilesidecar with one row per γ tested, so an LLM agent can ask "what γ would yield 50 communities?" without rerunning Leiden. Top terms per community come from the livecommunity_top_topics(cid, n)macro composed fromcluster_terms, not a frozen column. Seedocs/research_notes.mdfor the Louvain → Leiden+CPM swap rationale. - Hybrid friction pipeline. A hand-curated regex bank catches the
unambiguous
status_ping/interruption/correctioncases at zero Bedrock cost; the ambiguous class — especiallyunmet_expectation(one-word questions likescreenshot?that imply the agent missed a proactive step) — falls through to Sonnet 4.6 structured output. Scoped to user-role messages under 300 characters by default; longer turns are almost always genuine task instructions. - Pre-registered eval gym. The
judges/freeze/judge/kappafamily grades transcripts with a cross-provider Bedrock judge panel (8 non-Anthropic/non-Amazon primaries + a within-family Anthropic holdout to measure self-preference bias), then scores inter-rater agreement with Cohen's + Fleiss' kappa and bootstrapped 95% CIs.freezewrites an immutable study manifest under~/.claude/studies/<sha>/so a run is reproducible (replay);blind-handoverstrips identity markers before grading;kappaexits66below the agreement floor. No DuckDB views orlist-cacheparquets — output goes to caller-named parquets stamped with the freeze SHA. - Transcript ↔ PR provenance.
bindwrites a host-neutral binding between a merged commit and the transcript that produced it, encoded as threegit-interpret-trailerstrailers plus a JSON note underrefs/notes/transcripts.resolvereads it back (trailer-first, note-fallback, loud on digest disagreement);review-sheetcompresses the bound transcript into a Markdown/JSON PR review sheet via Sonnet 4.6. Pure-stdlib, no new git infrastructure — seedocs/rfc/0001-transcript-pr-binding.md.
See docs/research_notes.md for deeper design
rationale, and
docs/rfc/0002-vision-and-roadmap.md
for the data-model + roadmap direction.
Links
- Cookbook (v1) — sessions, messages, tools, todos, subagents, semantic search.
- Analytics cookbook (v2) — clusters, communities, classifications, trajectory, conflicts, friction.
- Research notes — design decisions and tuning knobs.
- JSONL schema reference — column listings for every registered view.
- RFC 0001 — transcript ↔ PR binding
— the
bind/resolve/review-sheetconvention. - RFC 0002 — vision + roadmap — the v2 data model and production direction.
License
Apache 2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claude_sql-1.1.7.tar.gz.
File metadata
- Download URL: claude_sql-1.1.7.tar.gz
- Upload date:
- Size: 233.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3412cefad646f0680e04b706c35c484043266444537875809990fb95c5cc7e9a
|
|
| MD5 |
69da4b7771ab6a93be60d2a529aa27e4
|
|
| BLAKE2b-256 |
dd0f95aed0f71cfd3d49a38367e3d45dfd21ec1c84bf4e3ad0064f3bae8747cf
|
File details
Details for the file claude_sql-1.1.7-py3-none-any.whl.
File metadata
- Download URL: claude_sql-1.1.7-py3-none-any.whl
- Upload date:
- Size: 243.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ff5d80ba873d9e25c7115cb37887b35e26ee195a2afd2ed483a8197dd0c723d
|
|
| MD5 |
8b22c1e1d29b7ea30f6006550215c2ae
|
|
| BLAKE2b-256 |
c913491b33ae4f5da7f95545f4ab141d64683da66a27f2635c75d9016e49b7c0
|