DEV Community

Mason Delan
Mason Delan

Posted on

I rebuilt the install for my "git blame for AI" tool — six steps became one

A week ago I shipped Selvedge, an MCP server that captures why AI agents change code. The first dev.to post laid out the problem; this one is what's happened since.

Quick context if you missed the first one: 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. Then six months later you can ask selvedge blame users.email and 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:

  1. pip install selvedge
  2. cd your-project && selvedge init
  3. Open ~/.claude/config.json and add an mcpServers.selvedge block. Hope you got the JSON right.
  4. Open your project's CLAUDE.md (or create one). Paste in the system prompt that tells the agent to call log_change whenever it touches a column, function, env var, or dependency.
  5. selvedge install-hook so post-commit can link events to commits.
  6. 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_calls started arriving at the server, which is the only thing I actually care about. The JSON edit and the CLAUDE.md edit are where the cliff is.

Anyway. v0.3.4 collapses all of that to one command:

pip install selvedge
cd your-project
selvedge setup
Enter fullscreen mode Exit fullscreen mode

The wizard reads ~/.claude/config.json, ~/.cursor/mcp.json, and looks for .github/copilot-instructions.md — whichever of those you actually have, it configures. It writes the MCP entry. It drops the system-prompt block into your CLAUDE.md or .cursorrules, sentinel-bracketed so the next time I update the recommended block you can re-run selvedge prompt --install and it patches in place without disturbing the rest of your file. It runs selvedge init. It installs the hook. It writes a .bak next 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 people running it in CI or devcontainer.json's postCreateCommand: selvedge setup --non-interactive --yes.

That's it. The "interesting" parts of Selvedge — the live reasoning capture, the entity-level history, the changesets — are useless if nobody gets past the install. Six manual steps was burning everyone who would have otherwise tried it. One command isn't.

On not being alone in the category anymore

Other 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 of them backed by named teams. When I posted the first article a week ago I had basically one direct competitor I knew about. 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.

The thing that's actually different is when the reasoning gets captured.

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's not 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 doesn't have any of that. It has 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 explicit intent for that change, which is honestly the case you want to know about most.

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 "users.email" or "STRIPE_SECRET_KEY" or "/api/v1/checkout". So 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 variable
Enter fullscreen mode Exit fullscreen mode

selvedge diff users returns events for users, users.email, and anything else under users.. Same for any prefix.

I went back and forth on whether to also attribute lines (more granular, more like what the other tools do) 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

The other thing in Selvedge I haven't seen anywhere else 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.

The example I keep coming back to is Stripe billing. A real billing rollout in a real codebase touches new columns on users, 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 somebody has to audit which lines of code touch payment data — for compliance, for an incident, for 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 just gone.

If every event in that work was tagged changeset:add-stripe-billing, you run selvedge changeset add-stripe-billing and get every event from every entity that was part of it, with the agent's reasoning on each. It's the rollup view. Nobody else stores it as far as I can tell, because it requires capturing reasoning at the per-change level, which only really 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 is the wire format; Selvedge is one way to populate that wire format (live, from the agent, at the moment the change is made). The export design is 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 setup
Enter fullscreen mode Exit fullscreen mode

Then start an agent session in that directory, change something non-trivial, and in a separate terminal:

selvedge watch
Enter fullscreen mode Exit fullscreen mode

You should see events stream in as the agent calls log_change. Once a few have landed:

selvedge blame <whatever_you_just_changed>
Enter fullscreen mode Exit fullscreen mode

The reasoning was captured live. That's the whole product.

If nothing arrives in selvedge watch, run selvedge doctor. It'll tell you which step is silently broken. (There are still ways for it to 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. The local SQLite version is fully featured for solo devs and small teams who are fine sharing a file.

The thing I'd actually like feedback on, if 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, an issue is the most useful thing.

github.com/masondelan/selvedge — issues and DMs both work.

Top comments (0)