Which commits an AI agent wrote, read from evidence a repository already carries, with optional edit-time capture for the agents you choose. Every commit gets one of three labels, and every share is shown with the coverage behind it.
| Label | Meaning |
|---|---|
agent |
A marker or a captured edit ties the commit to an agent |
human_attested |
An explicit human record: a git-ai note naming only human authors, or an Agent Trace record whose contributors are all human |
unknown |
No marker either way. Unknown is never counted as human |
With no configuration, indexing reads public markers from local git history. No network calls, no model, no prompts.
| Marker | Example |
|---|---|
| Bot author or committer | copilot-swe-agent[bot], devin-ai-integration[bot], cursoragent@cursor.com |
| Message footer | Generated with Claude Code, the aider (aider) author suffix |
| Trailers | Co-authored-by: naming an agent service identity, Assisted-by:, Generated-by: |
| git-ai notes | Authorship notes on refs/notes/ai (git-ai standard) |
| Agent Trace | .agent-trace/traces.jsonl records committed to the repository |
Every pattern is anchored to a service identity (a bot login, a service email, an exact footer phrase), never a bare name, so a contributor called Devin stays unlabelled.
To recognise your own bot or footer, add it under agent_provenance: in
.repowise/config.yaml:
agent_provenance:
service_emails:
mybot@example.com: {agent: mybot, tier: 1}
footer_patterns:
- {pattern: "Generated by MyBot", agent: mybot}Capture records which lines an agent wrote as it writes them, so commits carry a label even when nothing in the message says so. It is off until both the repository and your user config turn it on.
| Agent | Capture |
|---|---|
| Claude Code | PostToolUse hook installed with the agent hooks |
| Codex CLI | PostToolUse hook installed with the agent hooks |
| Cursor | afterFileEdit hook: repowise agents add --target=cursor --scope=user |
Other agents are read through public markers only. The Attribution column of
INTEGRATIONS.md is the live list.
The hook stores line hashes, the repo-relative path, an opaque session id, the tool and
the model in .repowise/attribution/events.jsonl, never prompts or code. repowise update (which the
post-commit hook runs) matches those hashes to the lines new commits added and labels a
matched commit with the agent. An unmatched commit stays unknown.
repowise attribution backfill --agent claude-code,codex binds past commits from the
transcripts these agents keep on this machine, under the same consent. It keeps line
hashes only, binds an edit to a commit made within 14 days after it, and needs a run of 6
substantive lines. When it binds new commits, the next repowise update re-reads git
history once to label them.
| Level | Writes | Leaves the machine |
|---|---|---|
off (default) |
Nothing | Nothing |
local |
The sidecar under .repowise/attribution/ (gitignored) and labels in the local index |
Nothing |
notes |
Everything local writes, plus a git-ai v3 note per matched commit on refs/notes/ai, and notes.rewriteRef so the note follows an amend or rebase |
Only when you run git push origin refs/notes/ai. Repowise never pushes |
A note holds file paths, line ranges, the tool name, the model and a hashed session id. It never holds prompts, code, conversation ids or a person's identity. A commit that already has a note keeps it; Repowise only writes notes on commits without one.
# .repowise/config.yaml and ~/.repowise/config.yaml
attribution:
enabled: true # read public markers
capture: off # off, local or notes
agents: [] # agent target ids, e.g. claude-code, codex, cursorThe stricter value wins per key across the repo and user files:
enabled: falsein either file means nothing about AI is stored or shown on any surface.capturetakes the lower of the two levels, and an unset key counts asoff.agentsis the set both files list.DO_NOT_TRACK=1keeps capture off.- An invalid value counts as the strictest value and is reported by
status.
Key reference: CONFIG.md.
| Command | What it does |
|---|---|
repowise attribution status [--format json] |
Effective, repo and user values, why any differ, capture per agent, sidecar counts, and commits by label with coverage |
repowise attribution backfill --agent claude-code,codex [--format json] |
Binds past commits from local transcripts; prints commits scanned, bound and coverage before and after |
repowise attribution purge [--yes] |
Deletes the index labels and file rollups, the .repowise/attribution/ sidecar and the notes Repowise wrote locally. Git history, trailers and notes written by anything else stay |
repowise doctor |
At capture: notes, checks the fetch refspec, the push refspec and notes.rewriteRef, and names the command for anything missing |
| Surface | What you see |
|---|---|
| CLI | repowise attribution status |
| REST | GET /api/repos/{id}/commits?authorship=agent|human|unknown, GET /api/repos/{id}/commits/agent-trend (monthly counts per label, coverage, share) |
| Web | Commits page: agent badges, an authorship filter and a 12-month trend strip |
| MCP | get_context file cards: agent share and coverage, when the file has agent commits |
| Change Records | A three-state provenance block per change. See CHANGES.md |
Coverage is the fraction of commits with a known source (agent plus
human_attested). The AI share is the agent fraction of those covered commits, always
printed beside its coverage, for example "at least 40% of covered commits, coverage
62%". Below 30% coverage the share is withheld and only the count is shown.
- Squash merges that drop trailers lose those markers. PR-level signals (bot PR author, branch names, PR body) need forge APIs and are outside the local reader.
- Capture labels a commit only when a run of at least two consecutive substantive lines from one agent matches. One-line edits, lines repeated three or more times in a file, and lines two agents both claim stay unmatched.
- Pending capture events expire after 14 days. Each
updatebinds at most 50 new commits and resumes from where it stopped. - Capture sees edits made on this machine. Teammates' work reaches you through their own capture or through notes they push.