I rebuilt the install for my 'git blame for AI' tool — six steps became one
Source: Dev.to
A week ago I shipped Selvedge, an MCP server that captures why AI agents change code
Quick context (if you missed the first post)
AI agents write code with full intent in the moment, and then the session ends and the intent is gone. Selvedge captures that intent live, by the agent, while it’s still in context. Six months later you can run:
selvedge blame users.emailand get an actual sentence back about why the column exists.
The original launch did fine: a few hundred installs, a handful of stars, one post on each of dev.to and HN that converted better than I expected. Nothing dramatic, but the shape of where people dropped off was instructive, and that’s what the v0.3.4 release I shipped this weekend is mostly about.
The install was the bottleneck and I was kidding myself
I knew the install wasn’t great. The first version of the README walked you through six things:
pip install selvedgecd your-project && selvedge init- Open
~/.claude/config.jsonand add anmcpServers.selvedgeblock. Hope you got the JSON right. - Open your project’s
CLAUDE.md(or create one). Paste in the system prompt that tells the agent to calllog_changewhenever it touches a column, function, env var, or dependency. selvedge install-hookso post‑commit can link events to commits.- Restart Claude Code, because the MCP config doesn’t hot‑reload.
Four flips between the README and your editor. One of those steps — number three — is “edit a JSON file by hand,” which kills curiosity in about eight seconds. And number five, the post‑commit hook, silently failed on macOS when you committed through a GUI client like Tower or GitKraken, because git launches with a stripped PATH and selvedge isn’t on it. I wrote selvedge doctor in v0.3.2 specifically because of that bug. The bug existed because the install didn’t surface that the hook was even there to fail.
What actually happened in the install metrics is roughly what you’d expect:
- People
pip install‑ed. - Some ran
selvedge init. - A much smaller fraction ever made it to the point where
tool_callsstarted arriving at the server — the only thing I actually care about.
The JSON edit and the CLAUDE.md edit are where the cliff is.
v0.3.4: One‑command setup
v0.3.4 collapses all of that to one command:
pip install selvedge
cd your-project
selvedge setupThe wizard:
- Reads
~/.claude/config.json,~/.cursor/mcp.json, and looks for.github/copilot-instructions.md— whichever of those you actually have. - Writes the MCP entry.
- Drops the system‑prompt block into your
CLAUDE.mdor.cursorrules, sentinel‑bracketed so the next time I update the recommended block you can re‑runselvedge prompt --installand it patches in place without disturbing the rest of your file. - Runs
selvedge init. - Installs the hook.
- Writes a
.baknext to every file it touches before any change reaches disk, so if it makes a wrong call you can revert.
Re‑running on an already‑set‑up project is a no‑op. For CI or devcontainer.json’s postCreateCommand you can use:
selvedge setup --non-interactive --yesThat’s it. The “interesting” parts of Selvedge — live reasoning capture, entity‑level history, changesets — are useless if nobody gets past the install. Six manual steps were burning everyone who would have otherwise tried it. One command isn’t.
On not being alone in the category anymore
Another thing that changed since the launch post: the “git blame for AI” space has gotten crowded fast. AgentDiff, Origin, Git AI, BlamePrompt — all real tools, all active, several backed by named teams. When I posted the first article a week ago I knew about basically one direct competitor. Now I have four or five.
That’s actually fine. It means the problem is real enough that more than one person decided to work on it. But it does mean I should be specific about what’s different, because if you read every tool’s README in isolation you’d reasonably conclude they’re all doing the same thing.
What’s actually different? When the reasoning gets captured
| Tool | Reasoning source | Granularity | Mechanism |
|---|---|---|---|
| Selvedge | Captured live, by the agent in the same context that produced the change | Entity (DB column, table, env var, dep, API route, function) | MCP server — agent calls it as work happens |
| AgentDiff | Inferred post‑hoc by Claude Haiku from the diff at session end | Line | Git pre/post‑commit hook |
| Origin | Captured at commit time | Line | Git hook |
| Git AI | Attribution metadata | Line | Git hook + Agent Trace alliance |
| BlamePrompt | Prompt‑only | Line | Git hook |
Most of these tools wait until commit time and then ask a second LLM to look at the diff and explain what changed. That’s a reasonable approach and the output looks fine on first read, but it isn’t the actual reasoning the agent had — the agent had the prompt, the conversation history, the user’s actual ask, and a model’s interpretation of all of it. The post‑hoc explainer only sees the diff, so it generates something plausible.
Plausible isn’t always right.
If you migrated from INTEGER cents to DECIMAL(10,2) because a user reported rounding errors on international transactions, that’s the actual reason. If a second LLM looks at the diff months later, it’ll write something like “improved precision for monetary values” — true in a generic sense, useless when you’re trying to figure out which currencies you should regression‑test next time.
Selvedge’s reasoning is whatever the agent that made the change wrote. It comes from the same context window that produced the change. There’s no second model and no inference. And because it’s whatever the agent had, an empty reasoning field is itself useful information — it means the agent didn’t have a reason to record.
Entity Attribution, Briefly
The other thing that’s a bit different: most tools attribute lines. Selvedge attributes things.
When you investigate something in your codebase you don’t search “lines 40 through 48 of users.py”; you search for users.email, STRIPE_SECRET_KEY, or /api/v1/checkout. That’s what Selvedge stores:
users.email DB column
users DB table
src/auth.py::login Function in a file
api/v1/users API route
deps/stripe Dependency
env/STRIPE_SECRET_KEY Environment variableselvedge diff users returns events for users, users.email, and anything else under users.. The same works for any prefix.
I debated whether to also attribute lines (more granular, like other tools) and decided not to. Lines drift. Renames break attribution. Refactoring a function into three files invalidates everything that referenced the original location. Entities are sticky in a way that lines aren’t.
Changesets
Another thing Selvedge offers that I haven’t seen elsewhere is changesets—a slug you can attach to any event, so later you can pull every event for a feature back, regardless of how many entities it touched or how many PRs it spanned.
Example: Stripe billing.
A real billing rollout in a real codebase touches new columns onusers, two new env vars, three new API routes, a new dependency, four functions across the codebase, and lands as eight smaller PRs over a month because nobody wants to review one mega‑diff.
Six months later someone has to audit which lines of code touch payment data— for compliance, an incident, or a refactor. git log is useless for this. Each PR is generic (“update billing”, “wire webhook”, “fix typo in migration”) and the cross‑cutting nature is gone.
If every event in that work was tagged changeset:add-stripe-billing, you can run:
selvedge changeset add-stripe-billingand get every event from every entity that was part of it, with the agent’s reasoning on each. It’s a roll‑up view. Nobody else stores it (as far as I can tell) because it requires capturing reasoning at the per‑change level, which only works if you’re capturing live.
Agent Trace
A few people DM’d me about Agent Trace after the first article. Cursor and Cognition put out an RFC in January for an open standard for AI code‑attribution traces: JSON records, file/line granularity, deliberately storage‑agnostic. Cloudflare, Vercel, Google Jules, Amp, OpenCode, and git‑ai are all signed on.
Selvedge isn’t competing with that; it’s a producer for it.
- Agent Trace = the wire format.
- Selvedge = one way to populate that wire format (live, from the agent, at the moment the change is made).
The export design lives at docs/agent-trace-interop.md. I’ll ship the actual selvedge export --format agent-trace flag in v0.3.5 or v0.4.
Try It
pip install selvedge
cd your-project
selvedge setupThen start an agent session in that directory, make a non‑trivial change, and in a separate terminal run:
selvedge watchYou should see events stream in as the agent calls log_change. Once a few have landed:
selvedge blameThe reasoning was captured live—that’s the whole product.
If nothing appears in selvedge watch, run:
selvedge doctorIt will tell you which step is silently broken (there are still edge‑cases that can be silently broken; I’m working on them).
What’s Next, and What I Want Feedback On
Phase 3 of the roadmap is the team‑sync story—Postgres backend, an HTTP API, multiple developers sharing history without sharing a SQLite file. I don’t have a date on it yet. The local SQLite version is fully featured for solo devs and small teams who are fine sharing a file.
Feedback request: When you try this, where does selvedge setup pick the wrong default? The Cursor and Copilot paths are newer than the Claude Code one—I’ve used them less, and the test coverage exists but isn’t from real‑world usage. If something in the wizard surprises you, opening an issue is the most useful thing you can do.
github.com/masondelan/selvedge — issues and DMs both work.