Git blame for AI coding agents — record what every agent changed, why, and through which prompts, down to the line.
minspect runs as a local daemon that hooks into AI coding CLIs (Claude Code, Codex CLI, OpenCode) and captures every edit as it happens. It reconstructs a session → turn → prompt → reasoning → hunk → line chain so you can answer:
- "Which prompt introduced this line?"
- "What was the agent thinking when it made this change?"
- "What did the AI touch in the commit I'm about to push?"
Everything is local. No cloud, no account, no telemetry. Data lives in a single SQLite file under your state dir.
Working on Windows, macOS, and Linux with Node 20+. 351 tests across the workspace, lint + build clean.
| Agent | Method | Status |
|---|---|---|
| Claude Code | Native hooks | Full support (edits, reasoning, commit-link) |
| Codex CLI | Session-log import (rollout-*.jsonl) |
Full support (apply_patch → line blame) |
| OpenCode | Plugin (event / tool.execute.*) |
Full support (edits, reasoning, file path attribution) |
macOS / Linux:
curl -fsSL https://raw.githubusercontent.com/ivenlau/minspect/main/scripts/install.sh | shWindows (PowerShell):
iwr https://raw.githubusercontent.com/ivenlau/minspect/main/scripts/install.ps1 | iexBoth scripts check Node ≥ 20 and run npm install -g @ivenlau/minspect. Output is
limited to errors by default — pass --verbose (PowerShell: -Verbose) if you need
npm's full peerOptional / deprecated warnings for debugging. If you prefer npm
directly:
npm install -g @ivenlau/minspectOr from source:
pnpm install
pnpm -r build
pnpm -C packages/cli link --globalminspect initThat detects which agents you have, interactively installs their hooks
(Claude Code / OpenCode), optionally imports the last 30 days of Codex
sessions, installs the post-commit hook if you're in a git repo, asks once
whether hooks should auto-start the daemon, then starts serve and opens
the UI on http://127.0.0.1:21477. Pass --yes for non-interactive.
Re-running init is safe — anything already wired is skipped.
Chat with Claude Code / Codex / OpenCode as you always would. Every edit, tool call, prompt, and piece of agent reasoning is recorded in the background.
In the web UI:
- Dashboard — activity chart (today / week / 30 days / year), top workspaces, top agents, recent alerts
- Timeline — sessions in reverse chronological order across every workspace
- Session → Files — per-file edit counts, click through to blame
- Blame — every line coloured by the turn that wrote it; click a line to see the prompt, agent's thinking, and the final message
- Review — all edits in a session with inline diffs, filter by file / keyword / level, export to self-contained HTML for PR attachments
- Replay — step through tool calls one at a time with keyboard navigation (←/→, Home/End, Space for autoplay)
- Revert — "Revert this turn" button on Review cards and Replay steps; opens a preview modal and generates a copyable CLI command
- Command palette (⌘K / Ctrl+K) — cross-session FTS5 search over prompts, reasoning, tool-call explanations, and file paths
- EN / 中 toggle — full Chinese translation next to the theme toggle
minspect # default: status (daemon, queue, last event, hooks)
minspect init # one-shot setup (re-runnable)
minspect serve # start the daemon + UI (port 21477)
minspect stop # stop the daemon
minspect doctor # 8-point diagnostic
minspect uninstall --all --yes # symmetric to install (--purge also wipes state)
minspect import-codex --latest # manually import a Codex rolloutminspect keeps every before/after snapshot, so it can restore files to the state they were in before a specific turn or edit:
# Dry-run first — see what would change
minspect revert --turn <turn_id>
# Actually write to disk
minspect revert --turn <turn_id> --yes
# Revert a single edit (one file)
minspect revert --edit <edit_id> --yes
# Override drift detection (use only if you know the file has diverged since)
minspect revert --turn <turn_id> --yes --forceSafety guarantees:
- Dry-run by default — you must pass
--yesto actually write. - Drift detection — if the file on disk has been modified since
minspectlast recorded it, revert aborts (override with--force). - Codex hard-block — Codex-imported sessions are refused because their patch logs only capture hunk windows, not full files; restoring would clobber unrelated regions. Use
git checkoutfor Codex sessions. - Chain-break warnings — if user edits are detected between the target and the current state, they're listed before you confirm.
- Server-side stays read-only — the collector never writes to your workspace. The CLI is the only path to disk.
The UI Review / Replay pages also have "Revert this turn" buttons that open a preview modal and generate the exact CLI command to copy.
Codex has no hook API, so its logs are imported after the fact:
# Newest session
minspect import-codex --latest
# Specific session by path or UUID
minspect import-codex --session rollout-2026-02-26T16-22-56-019c990b-3d80-73a0-baa0-ebd4b1c3f87d.jsonl
minspect import-codex --session 019c990b
# Bulk import anything in the last 30 days (also what the sidebar refresh
# button does, plus a background hourly import with `--since 1d`)
minspect import-codex --all --since 30dRe-import is idempotent — Codex's own UUIDs become stable primary keys.
┌─ agent (Claude Code / Codex CLI / OpenCode)
│ │ hook fires ─or─ session log written
│ ▼
├─ `minspect capture` (short-lived; exits ≤100 ms; never blocks agent)
│ │ POST Event ─or─ enqueue to disk if daemon down
│ ▼
├─ Collector daemon (Fastify + SQLite + WAL, default port 21477)
│ │ single-transaction ingest
│ ▼
├─ Indexer
│ ├─ structuredPatch diff → hunks
│ ├─ line blame propagation (hash chain; break on user edits)
│ ├─ tree-sitter AST (TS/JS/Python/Go/Rust/Java) for method-level aggregation
│ └─ post-commit hook → commit_links
│ ▼
└─ Web UI (React + Vite SPA, bundled into the daemon)
Key design choices:
- Hooks never block the agent: failures write to stderr and
exit 0; network failures enqueue events to disk and drain on the next hook invocation. - Ingest is idempotent: every INSERT uses
ON CONFLICT DO NOTHINGand IDs are deterministic (edit_id = ${tool_call_id}:${idx}), so disk-queue replay is safe. - Agent reasoning comes from the agent itself: we extract the "I'll edit X because …" preamble Claude Code writes to its transcript (or Codex writes to
agent_reasoningevents). No extra LLM calls, no API key. A standalone LLM explainer remains as an opt-in fallback for agents without reasoning output. - Blame chains break cleanly on user edits: if
before_hashof edit N+1 doesn't matchafter_hashof edit N, the chain resets rather than mis-attributing your changes to the AI.
packages/
├── core/ — Event schema (zod), DB schema, migrations, git helpers
├── collector/ — Fastify server, SQLite store, blame + AST indexers,
│ LLM explainer (opt-in), Claude-Code transcript parser
├── cli/ — `minspect` binary: init, status, serve, stop, doctor,
│ capture, capture-opencode, install, uninstall,
│ import-codex, link-commit, revert, vacuum
├── ui/ — React + Vite SPA (dark/light theme, EN/zh i18n);
│ built output bundled into the collector
└── adapters/
├── claude-code/ — hook payload → Event; transcript reasoning extraction
├── codex/ — rollout-*.jsonl parser + apply_patch → file_edits
├── opencode/ — plugin envelope → Event (edit / write / reasoning)
└── aider/ — skeleton (reserved)
pnpm build # tsc across all packages
pnpm test # vitest across all packages (~351 tests)
pnpm lint # biome check .
pnpm format # biome format --write .Note: biome's format command does not reorganize imports. When adding/moving imports, run pnpm exec biome check --write . instead.
Publishing is automated via GitHub Actions. Push a v-prefixed tag and CI handles the rest:
# 1. Bump version in packages/cli/package.json
npm version 0.2.0 --no-git-tag-version
# 2. Commit and tag
git add -A && git commit -m "release: v0.2.0"
git tag v0.2.0
# 3. Push — CI takes over: build → bundle → test → npm publish → GitHub Release
git push && git push --tagsThe workflow (.github/workflows/publish.yml) validates that the tag version matches packages/cli/package.json before publishing. If they don't match, CI fails.
Prerequisite: the NPM_TOKEN secret must be configured in the repo's GitHub Actions settings (Settings → Secrets → Actions).
- SQLite file:
<state_dir>/history.sqlite(WAL mode) - State dir:
%LOCALAPPDATA%\minspect(Windows) or$XDG_STATE_HOME/minspect(Linux/macOS; defaults to~/.local/state/minspect) - Daemon state:
<state_dir>/state.json— port / pid / started_at / spawned_by - Per-session state:
<state_dir>/sessions/<session_id>.json— turn counter, pre-edit file snapshots - Disk queue (when daemon is offline):
<state_dir>/queue/<timestamp>-<uuid>.json - User config:
<state_dir>/config.json— currently justauto_spawn_daemon
To wipe all history: minspect uninstall --all --purge --yes (or stop the daemon and delete <state_dir>).
Nothing leaves your machine unless you explicitly opt into the LLM explainer (config.explainer.enabled = true), in which case hunks are sent to Anthropic's API using your own key.
If things aren't working (no sessions in the UI, hook not firing, etc.) run:
minspect doctorIt prints 8 checks (Node version, state dir, daemon, installed hooks, DB, recent activity) with ✓/⚠/✗ and a fix: hint next to anything that's not ok. Use --json for a machine-readable report. Exits non-zero only when there's a hard failure (✗), so it's CI-friendly.
# Preview what will be removed (dry-run)
minspect uninstall --all
# Apply: strip Claude Code + OpenCode hooks, stop daemon, remove cwd's
# post-commit hook if we installed one.
minspect uninstall --all --yes
# Also delete the SQLite DB and captured sessions (irreversible).
minspect uninstall --all --purge --yesuninstall is symmetric to install: it only touches the blocks our hooks wrote (__minspect_managed__: true for Claude Code, // >>> minspect managed >>> markers for OpenCode and post-commit). User-authored hooks around those blocks are preserved, and every file touched gets a .bak.<timestamp> copy.
- Codex patch line numbers are hunk-relative, not absolute: Codex's
apply_patchformat doesn't include full file contents, so line-level blame on Codex-edited files is relative to the changed region. Upgrade path documented inminispec/specs/adapters.md. - Merge commits aren't linked:
link-commitskips commits withparent_count > 1. - AST covers 6 languages: TypeScript, JavaScript, Python, Go, Rust, Java. Other extensions fall back to whole-file nodes.
- Inline diffs render as text-based
<pre>blocks (no Monaco / syntax highlighting yet).
Changes go through a lightweight spec workflow (minispec):
- Open a change card in
minispec/changes/ - Apply + test + tick acceptance checkboxes
- Close → merge rules into
minispec/specs/<domain>.md, archive card
See CLAUDE.md for the full contract. Canonical specs live in minispec/specs/:
- foundation.md — monorepo, tooling
- core.md — Event schema, DB schema
- collector.md — server, ingest pipeline, blame/AST
- adapters.md — per-agent parser rules
- cli.md — CLI commands and hook protocol
- ui.md — routes, API contract
The original product design doc: design.md.
MIT. See the repository at github.com/ivenlau/minspect.