Unified SQL interface across AI coding history, Git repositories, and source code.
DevSQL loads data from Claude Code, Codex CLI, Git, and your source tree into an in-memory SQLite database so you can join, filter, and aggregate across all of them with standard SQL.
~/.claude/ ─┐
~/.codex/ ─┤
.git/ ─┼──▶ SQLite (in-memory) ──▶ SQL queries / JSON / CSV
src/**/* ─┘
Three standalone tools, one unified interface:
| Tool | Data Source |
|---|---|
ccql |
Claude Code + Codex CLI data (~/.claude/, ~/.codex/) |
vcsql |
Git repositories (commits, branches, diffs) |
devsql |
All of the above, plus source code analysis |
DevSQL auto-detects which tables your query references and only loads the data it needs.
brew install douglance/tap/devsql
/plugin marketplace add douglance/devsql
/plugin install devsql@devsql
The plugin auto-installs the binary on first session start.
Prebuilt binaries for macOS and Linux are available from GitHub Releases.
git clone https://github.com/douglance/devsql.git
cd devsql && cargo install --path crates/devsql
To enable tree-sitter-based AST analysis (richer symbol extraction and import parsing):
cargo install --path crates/devsql --features tree-sitter-ast
devsql "<SQL>" # Default table output
devsql -f json "<SQL>" # JSON output
devsql -f csv "<SQL>" # CSV output
Structured commands that return JSON, designed for use by AI agents and scripts:
| Command | Description |
|---|---|
devsql search <query> |
Find symbols by name across the codebase |
devsql context <file> |
File metadata and symbols for a given path |
devsql history <file> |
Git commit history for a specific file |
devsql diff <base> <head> |
Compare two Git refs with file and symbol-level stats |
devsql impact <file> |
Analyze exports and find potential dependents |
Common options: --repo / -r (default .), --data-dir / -d (default ~/.claude).
| Table | Source | Description |
|---|---|---|
history |
~/.claude/history.jsonl |
Claude Code prompts (timestamp, display, project) |
transcripts |
~/.claude/projects/<slug>/**/*.jsonl (+ legacy ~/.claude/transcripts/*.jsonl) |
Full conversations incl. subagents (type, content, tool_name, session_id, _project, _agent_id, timestamp, model, usage_* token columns) |
sessions |
Same files as transcripts |
One row per session: title, cwd, git_branch, first/last_timestamp, message counts, subagent_count, total_*_tokens, pr_url, pr_number |
todos |
~/.claude/todos/*.json |
Task items (content, status) |
jhistory |
~/.codex/history.jsonl |
Codex CLI prompts (session_id, text, display, timestamp) |
codex_history |
— | Alias for jhistory |
| Table | Description |
|---|---|
commits |
id, message, summary, author_name, authored_at, short_id |
branches |
name, is_head, commit_id |
diffs |
Commit-level stats: commit_id, files_changed, insertions, deletions |
diff_files |
Per-file stats: commit_id, path, status (A/D/M/R/C), insertions, deletions |
| Table | Description |
|---|---|
source_files |
File inventory: path, name, extension, directory, size_bytes, line_count, modified_at, language |
source_lines |
Line content: file_path, line_number, content, is_blank |
symbols |
Definitions: file_path, name, kind, line_start, line_end, signature, visibility, parameters, return_type, language |
imports* |
Import statements: file_path, line_number, module, name, alias, kind, is_default, is_wildcard |
ast_nodes* |
Raw AST nodes |
* Requires the tree-sitter-ast feature for full extraction. Without it, symbols falls back to regex-based extraction (Rust, TypeScript, JavaScript, Python, Go) and imports/ast_nodes are empty.
SELECT
date(c.authored_at) as day,
COUNT(DISTINCT h.timestamp) as prompts,
COUNT(DISTINCT c.id) as commits
FROM commits c
LEFT JOIN history h
ON date(c.authored_at) = date(datetime(h.timestamp/1000, 'unixepoch'))
GROUP BY day
ORDER BY day DESC
LIMIT 14;
SELECT h.display as prompt, COUNT(c.id) as commits_after
FROM history h
JOIN commits c ON date(datetime(h.timestamp/1000, 'unixepoch')) = date(c.authored_at)
GROUP BY h.display
HAVING commits_after > 0
ORDER BY commits_after DESC
LIMIT 20;
SELECT language, COUNT(*) as files, SUM(line_count) as total_lines
FROM source_files
GROUP BY language
ORDER BY total_lines DESC;
SELECT df.path,
COUNT(DISTINCT df.commit_id) as commits,
SUM(df.insertions) as lines_added,
(SELECT COUNT(*) FROM symbols s WHERE s.file_path = df.path) as symbols
FROM diff_files df
GROUP BY df.path
ORDER BY commits DESC
LIMIT 10;
devsql search "parse"
devsql search "Error" --kind struct
devsql diff main~5 HEAD
devsql context src/engine.rs
devsql impact src/lib.rs
devsql history src/engine.rs
history.timestamp is in epoch milliseconds. Use datetime(timestamp/1000, 'unixepoch') to convert.DATE() function normalizes epoch ms, epoch seconds, and ISO strings.symbols table extracts functions, structs, enums, traits, types, classes, interfaces, and more depending on language.MIT
$ claude mcp add devsql \
-- python -m otcore.mcp_server <graph>