claude-attribution 1.3.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -100
- package/package.json +1 -1
- package/src/attribution/commit.ts +16 -0
- package/src/attribution/differ.ts +12 -0
- package/src/cli.ts +4 -0
- package/src/export/pr-summary.ts +342 -109
- package/src/metrics/collect.ts +13 -0
- package/src/pricing.ts +136 -0
- package/src/setup/install.ts +76 -6
- package/src/setup/templates/claude-attribution-export.yml +78 -0
- package/src/setup/templates/claude-attribution-gha.yml +6 -1
- package/src/setup/uninstall.ts +19 -1
package/README.md
CHANGED
|
@@ -1,37 +1,45 @@
|
|
|
1
1
|
# claude-attribution
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
3
|
+
AI code attribution for Claude Code. After every `git commit`, a one-line summary appears in your terminal:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
[claude-attribution] a3f1b2c — 142 AI / 38 human / 4 mixed lines (77% AI)
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
When a PR is opened, full metrics — model usage, token counts, tool calls, and attribution percentages — are injected automatically into the PR body. No copy-paste, no manual tracking.
|
|
10
|
+
|
|
11
|
+
**Quick start:**
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g claude-attribution
|
|
15
|
+
claude-attribution install ~/Code/your-repo
|
|
16
|
+
claude-attribution init --ai # if repo was built with Claude Code; use --human otherwise
|
|
17
|
+
git add .claude/settings.json .github/workflows/claude-attribution-pr.yml .gitignore
|
|
18
|
+
git commit -m "chore: install claude-attribution hooks"
|
|
19
|
+
git push
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Using Copilot or `@claude` (claude-code-action)?** Bot commits are auto-detected and attributed as 100% AI — no extra steps needed. See [AI Actor Attribution](#ai-actor-attribution-copilot-bot-claude-gha).
|
|
23
|
+
|
|
24
|
+
**Requirements:** [Bun](https://bun.sh) (preferred) or Node 18+, and `gh` (GitHub CLI) authenticated for the `/pr` command.
|
|
17
25
|
|
|
18
26
|
---
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
Measures which lines in a commit were written by Claude vs. a human — using checkpoint snapshots and line-level SHA-256 comparison, not gross write-operation counts.
|
|
21
29
|
|
|
22
30
|
---
|
|
23
31
|
|
|
24
32
|
## GitHub Actions Requirements
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
Up to three workflows are installed into repos that use this tool — one always, two optionally. Each requires specific GitHub Actions to be allowed in your org. If your org enforces an action allowlist, you must approve the third-party action before the workflows will run.
|
|
27
35
|
|
|
28
36
|
### Workflows and the actions they use
|
|
29
37
|
|
|
30
|
-
| Workflow file | Trigger | GitHub-owned actions | Third-party actions |
|
|
31
|
-
|
|
32
|
-
| `claude-attribution-pr.yml` | PR opened / pushed | `actions/checkout@v4`, `actions/github-script@v7` | `oven-sh/setup-bun@v2` |
|
|
33
|
-
| `claude-attribution.yml` | PR merged | `actions/checkout@v4` | `oven-sh/setup-bun@v2` |
|
|
34
|
-
| `claude-attribution-gha.yml` | Every push (
|
|
38
|
+
| Workflow file | Trigger | Install | GitHub-owned actions | Third-party actions |
|
|
39
|
+
|---|---|---|---|---|
|
|
40
|
+
| `claude-attribution-pr.yml` | PR opened / pushed | Always | `actions/checkout@v4`, `actions/github-script@v7` | `oven-sh/setup-bun@v2` |
|
|
41
|
+
| `claude-attribution-export.yml` | PR merged | Optional (prompted) | `actions/checkout@v4` | `oven-sh/setup-bun@v2` |
|
|
42
|
+
| `claude-attribution-gha.yml` | Every push | Optional (claude-code-action detected) | `actions/checkout@v4` | `oven-sh/setup-bun@v2` |
|
|
35
43
|
|
|
36
44
|
**GitHub-owned actions** (`actions/*`) are pre-approved in all orgs by default.
|
|
37
45
|
|
|
@@ -44,7 +52,7 @@ Three workflows are installed into repos that use this tool. Each requires speci
|
|
|
44
52
|
| Workflow | `contents` | `pull-requests` | Why |
|
|
45
53
|
|---|---|---|---|
|
|
46
54
|
| `claude-attribution-pr.yml` | read | write | Reads git history; writes metrics into the PR body |
|
|
47
|
-
| `claude-attribution.yml` | read | — | Reads git notes and
|
|
55
|
+
| `claude-attribution-export.yml` | read | — | Reads git notes and exports metrics via OTLP/webhook |
|
|
48
56
|
| `claude-attribution-gha.yml` | write | — | Pushes attribution git notes back to origin |
|
|
49
57
|
|
|
50
58
|
### Required secrets
|
|
@@ -52,8 +60,11 @@ Three workflows are installed into repos that use this tool. Each requires speci
|
|
|
52
60
|
| Secret / Variable | Workflow | Required | Notes |
|
|
53
61
|
|---|---|---|---|
|
|
54
62
|
| `GITHUB_TOKEN` | All | Automatic | Provided by GitHub Actions; no setup needed |
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
63
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `claude-attribution-export.yml` | One of these | OTLP/HTTP base URL (e.g. `https://otlp.datadoghq.com`, `https://otlp-gateway-<zone>.grafana.net/otlp`, `http://localhost:4318`). Unset = dry-run mode. |
|
|
64
|
+
| `OTEL_EXPORTER_OTLP_HEADERS` | `claude-attribution-export.yml` | Depends on backend | Comma-separated `key=value` auth headers (e.g. `DD-Api-Key=xxx` for Datadog, `x-honeycomb-team=xxx` for Honeycomb) |
|
|
65
|
+
| `DATADOG_API_KEY` | `claude-attribution-export.yml` | Datadog shortcut | Org-level secret; auto-configures the Datadog OTLP endpoint without needing `OTEL_EXPORTER_OTLP_ENDPOINT`. |
|
|
66
|
+
| `DATADOG_SITE` | `claude-attribution-export.yml` | No | Org-level variable; used with `DATADOG_API_KEY`. Defaults to `datadoghq.com`. |
|
|
67
|
+
| `METRICS_WEBHOOK_URL` | `claude-attribution-export.yml` | No | Posts a flat JSON payload to any HTTP endpoint on each PR merge. Can be used alongside OTLP. |
|
|
57
68
|
|
|
58
69
|
---
|
|
59
70
|
|
|
@@ -67,30 +78,16 @@ Three workflows are installed into repos that use this tool. Each requires speci
|
|
|
67
78
|
|
|
68
79
|
### One-time setup (per developer machine)
|
|
69
80
|
|
|
70
|
-
**Option A — npm (recommended):**
|
|
71
|
-
|
|
72
81
|
```bash
|
|
73
82
|
npm install -g claude-attribution
|
|
74
83
|
```
|
|
75
84
|
|
|
76
|
-
**Option B — clone (if npm isn't available):**
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
git clone git@github.com:DTS-Productivity-Engineering/claude-attribution.git ~/Code/claude-attribution
|
|
80
|
-
cd ~/Code/claude-attribution
|
|
81
|
-
bun install
|
|
82
|
-
```
|
|
83
|
-
|
|
84
85
|
### Install into a repo (per repo, per developer)
|
|
85
86
|
|
|
86
87
|
**Step 1 — Run the installer:**
|
|
87
88
|
|
|
88
89
|
```bash
|
|
89
|
-
# npm install:
|
|
90
90
|
claude-attribution install ~/Code/your-repo
|
|
91
|
-
|
|
92
|
-
# clone install:
|
|
93
|
-
bun ~/Code/claude-attribution/src/setup/install.ts ~/Code/your-repo
|
|
94
91
|
```
|
|
95
92
|
|
|
96
93
|
**Step 2 — Declare your attribution baseline (`init`):**
|
|
@@ -106,6 +103,9 @@ claude-attribution init --human
|
|
|
106
103
|
|
|
107
104
|
# Not sure? Run with no flag — same as --human, prints a confirmation:
|
|
108
105
|
claude-attribution init
|
|
106
|
+
|
|
107
|
+
# Repo has mixed history — mark commits after a specific date as AI-written:
|
|
108
|
+
claude-attribution init --ai-since 2025-01-01
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
> **Why this matters:** Without `init`, the codebase-wide AI% starts at 0% and grows only from new commits. If your repo is all Claude Code, run `init --ai` now or the metrics will be misleading until the entire codebase has been re-committed line by line.
|
|
@@ -139,11 +139,14 @@ The installer makes the following changes to the target repo:
|
|
|
139
139
|
|
|
140
140
|
**`.github/workflows/claude-attribution-pr.yml`** — GitHub Actions workflow that fires on every PR open and push. Injects metrics into the PR body automatically for PRs created outside Claude (Copilot, manual `gh pr create`, GitHub UI). Skips injection if the local `post-bash` hook already injected metrics on `opened`; always updates on `synchronize` (new commits).
|
|
141
141
|
|
|
142
|
+
**`.github/workflows/claude-attribution-export.yml`** — fires on every PR merge. Exports AI attribution metrics via OTLP/HTTP or a generic webhook. Supports Datadog, Grafana Cloud, Splunk Observability, New Relic, Honeycomb, and any OpenTelemetry Collector. Runs in dry-run mode (prints payload to stdout, exits 0) when no export destination is configured. See [Metrics Export](#metrics-export).
|
|
143
|
+
|
|
142
144
|
**`.github/workflows/claude-attribution-gha.yml`** *(optional — installed when claude-code-action is detected)* — fires on every push. Writes a 100% AI git note for commits made by known AI actors (e.g. `@claude` via claude-code-action, Copilot coding agent). Silent no-op for human commits. See [AI Actor Attribution](#ai-actor-attribution-copilot-bot-claude-gha).
|
|
143
145
|
|
|
144
|
-
**`.claude/commands/`** — installs
|
|
146
|
+
**`.claude/commands/`** — installs three slash commands:
|
|
145
147
|
- `/metrics` — generate a PR metrics report
|
|
146
|
-
- `/start` — mark the start of a new
|
|
148
|
+
- `/start` — mark the start of a new ticket session
|
|
149
|
+
- `/pr` — create a PR with metrics embedded
|
|
147
150
|
|
|
148
151
|
**`.gitignore`** — adds `.claude/logs/` so tool usage logs don't end up in version control.
|
|
149
152
|
|
|
@@ -177,16 +180,12 @@ claude-attribution init --ai # only if repo was built 100% with Claude Code
|
|
|
177
180
|
git push origin refs/notes/claude-attribution-map
|
|
178
181
|
```
|
|
179
182
|
|
|
180
|
-
### Re-installing
|
|
183
|
+
### Re-installing
|
|
181
184
|
|
|
182
|
-
If you
|
|
185
|
+
If you reinstall `claude-attribution` globally (e.g. after upgrading), re-run the installer — it updates the absolute paths in `settings.json` and the git hook:
|
|
183
186
|
|
|
184
187
|
```bash
|
|
185
|
-
# npm install:
|
|
186
188
|
claude-attribution install ~/Code/your-repo
|
|
187
|
-
|
|
188
|
-
# clone install:
|
|
189
|
-
bun src/setup/install.ts ~/Code/your-repo
|
|
190
189
|
```
|
|
191
190
|
|
|
192
191
|
### Uninstalling
|
|
@@ -194,14 +193,10 @@ bun src/setup/install.ts ~/Code/your-repo
|
|
|
194
193
|
To remove claude-attribution from a repo:
|
|
195
194
|
|
|
196
195
|
```bash
|
|
197
|
-
# npm install:
|
|
198
196
|
claude-attribution uninstall ~/Code/your-repo
|
|
199
|
-
|
|
200
|
-
# clone install:
|
|
201
|
-
bun ~/Code/claude-attribution/src/setup/uninstall.ts ~/Code/your-repo
|
|
202
197
|
```
|
|
203
198
|
|
|
204
|
-
This removes hooks from `.claude/settings.json`, removes `.git/hooks/post-commit`, removes the slash commands, removes `.github/workflows/claude-attribution-pr.yml`, and removes any legacy `pre-push` hooks (for example `.husky/pre-push` or `.git/hooks/pre-push`) if present. Attribution state (`.claude/attribution-state/`) and logs (`.claude/logs/`) are left in place. The `remote.origin.push` refspec is also removed from git config.
|
|
199
|
+
This removes hooks from `.claude/settings.json`, removes `.git/hooks/post-commit`, removes the slash commands, removes `.github/workflows/claude-attribution-pr.yml`, `.github/workflows/claude-attribution-export.yml`, and `.github/workflows/claude-attribution-gha.yml` (if present), and removes any legacy `pre-push` hooks (for example `.husky/pre-push` or `.git/hooks/pre-push`) if present. Attribution state (`.claude/attribution-state/`) and logs (`.claude/logs/`) are left in place. The `remote.origin.push` refspec is also removed from git config.
|
|
205
200
|
|
|
206
201
|
---
|
|
207
202
|
|
|
@@ -237,33 +232,36 @@ Metrics are injected automatically — no command needed.
|
|
|
237
232
|
|
|
238
233
|
**On every new push to an open PR**: the workflow fires on `synchronize` and updates the attribution percentages to reflect new commits.
|
|
239
234
|
|
|
240
|
-
The metrics block looks like (when the cumulative minimap exists):
|
|
235
|
+
The metrics block injected into the PR body looks like (when the cumulative minimap exists):
|
|
241
236
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
**
|
|
246
|
-
|
|
237
|
+
> ## Claude Code Metrics
|
|
238
|
+
>
|
|
239
|
+
> **Codebase: ~77% AI** (3200 / 4150 lines)
|
|
240
|
+
> **This PR:** 184 lines changed (4% of codebase) · 77% Claude edits · 142 AI lines · Active: 8m
|
|
241
|
+
>
|
|
242
|
+
> | Model | Calls | Input | Output | Cache |
|
|
243
|
+
> |-------|-------|-------|--------|-------|
|
|
244
|
+
> | Sonnet | 45 | 120K | 35K | 10K |
|
|
245
|
+
> | **Total** | 45 | 120K | 35K | 10K |
|
|
246
|
+
>
|
|
247
|
+
> **Human prompts (steering effort):** 12
|
|
248
|
+
>
|
|
249
|
+
> <details><summary>Tools · Agents · Files</summary>
|
|
250
|
+
>
|
|
251
|
+
> **Tools:** Edit ×47, Read ×31, Bash ×12
|
|
252
|
+
>
|
|
253
|
+
> </details>
|
|
247
254
|
|
|
248
|
-
|
|
249
|
-
|-------|-------|-------|--------|-------|
|
|
250
|
-
| Sonnet | 45 | 120K | 35K | 10K |
|
|
251
|
-
| **Total** | 45 | 120K | 35K | 10K |
|
|
255
|
+
Before running `init --ai` (or on a fresh install with no minimap), the headline falls back to the session-only view:
|
|
252
256
|
|
|
253
|
-
**
|
|
257
|
+
> **AI contribution: ~77%** (142 of 184 committed lines) · Active: 8m
|
|
254
258
|
|
|
255
|
-
|
|
256
|
-
<summary>Tools · Agents · Files</summary>
|
|
259
|
+
The block is wrapped in HTML comments for idempotent updates — re-running replaces the existing block rather than appending:
|
|
257
260
|
|
|
258
|
-
**Tools:** Edit ×47, Read ×31, Bash ×12
|
|
259
|
-
...
|
|
260
|
-
</details>
|
|
261
261
|
```
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
```markdown
|
|
266
|
-
**AI contribution: ~77%** (142 of 184 committed lines) · Active: 8m
|
|
262
|
+
<!-- claude-attribution metrics -->
|
|
263
|
+
...metrics content...
|
|
264
|
+
<!-- /claude-attribution metrics -->
|
|
267
265
|
```
|
|
268
266
|
|
|
269
267
|
#### Manual option
|
|
@@ -271,7 +269,7 @@ Before running `init --ai` (or on a fresh install with no minimap), the headline
|
|
|
271
269
|
If you need to create a PR with metrics outside of Claude, use the `/pr` slash command or CLI directly:
|
|
272
270
|
|
|
273
271
|
```bash
|
|
274
|
-
claude-attribution pr "feat:
|
|
272
|
+
claude-attribution pr "feat: PROJ-1234 add user authentication"
|
|
275
273
|
claude-attribution pr "feat: my feature" --draft
|
|
276
274
|
claude-attribution pr "feat: my feature" --base develop
|
|
277
275
|
```
|
|
@@ -284,7 +282,6 @@ To see the metrics output without creating a PR, use `/metrics` or run directly:
|
|
|
284
282
|
|
|
285
283
|
```bash
|
|
286
284
|
claude-attribution metrics
|
|
287
|
-
# or: bun ~/Code/claude-attribution/src/metrics/calculate.ts
|
|
288
285
|
```
|
|
289
286
|
|
|
290
287
|
The output is markdown you paste into your PR description:
|
|
@@ -351,7 +348,7 @@ Example output:
|
|
|
351
348
|
{
|
|
352
349
|
"commit": "a3f1b2c",
|
|
353
350
|
"session": "abc-123-...",
|
|
354
|
-
"branch": "feature/
|
|
351
|
+
"branch": "feature/PROJ-1234",
|
|
355
352
|
"timestamp": "2026-03-26T15:32:00.000Z",
|
|
356
353
|
"files": [
|
|
357
354
|
{ "path": "src/components/Foo.tsx", "ai": 82, "human": 10, "mixed": 2, "total": 94, "pctAi": 87 }
|
|
@@ -466,36 +463,60 @@ To force re-detection of the TypeScript runtime (e.g., after installing Bun): `r
|
|
|
466
463
|
|
|
467
464
|
---
|
|
468
465
|
|
|
469
|
-
##
|
|
466
|
+
## Metrics Export
|
|
470
467
|
|
|
471
|
-
Attribution
|
|
468
|
+
Attribution metrics are exported automatically on every PR merge via GitHub Actions (`.github/workflows/claude-attribution-export.yml`). The workflow uses the OpenTelemetry OTLP/HTTP JSON format and supports any OTel-compliant backend.
|
|
472
469
|
|
|
473
|
-
**Metrics
|
|
470
|
+
**Metrics exported on each merged PR:**
|
|
474
471
|
|
|
475
|
-
| Metric | Description |
|
|
476
|
-
|
|
477
|
-
| `claude_attribution.ai_lines` | Lines written by Claude and committed unchanged |
|
|
478
|
-
| `claude_attribution.human_lines` | Lines written or left unchanged by the developer |
|
|
479
|
-
| `claude_attribution.total_lines` | Total committed lines in the PR |
|
|
480
|
-
| `claude_attribution.pct_ai` | Percentage of lines attributed to Claude (this PR) |
|
|
481
|
-
| `claude_attribution.codebase_pct_ai` | Cumulative codebase-wide AI% at PR merge time (requires minimap) |
|
|
482
|
-
| `claude_attribution.codebase_total_lines` | Total codebase lines tracked in the minimap |
|
|
483
|
-
| `
|
|
484
|
-
| `
|
|
485
|
-
| `
|
|
472
|
+
| Metric | Unit | Description |
|
|
473
|
+
|--------|------|-------------|
|
|
474
|
+
| `claude_attribution.ai_lines` | lines | Lines written by Claude and committed unchanged |
|
|
475
|
+
| `claude_attribution.human_lines` | lines | Lines written or left unchanged by the developer |
|
|
476
|
+
| `claude_attribution.total_lines` | lines | Total committed lines in the PR |
|
|
477
|
+
| `claude_attribution.pct_ai` | % | Percentage of lines attributed to Claude (this PR) |
|
|
478
|
+
| `claude_attribution.codebase_pct_ai` | % | Cumulative codebase-wide AI% at PR merge time (requires minimap) |
|
|
479
|
+
| `claude_attribution.codebase_total_lines` | lines | Total codebase lines tracked in the minimap |
|
|
480
|
+
| `claude_attribution.cost_usd` | $ | Estimated Claude API cost for this PR (requires v1.5.0+ notes) |
|
|
481
|
+
| `claude_attribution.input_tokens` | tokens | Total input tokens consumed by Claude in this PR |
|
|
482
|
+
| `claude_attribution.output_tokens` | tokens | Total output tokens generated by Claude in this PR |
|
|
483
|
+
| `claude_attribution.cache_read_tokens` | tokens | Cache read tokens in this PR |
|
|
484
|
+
| `claude_attribution.cache_creation_tokens` | tokens | Cache creation tokens in this PR |
|
|
486
485
|
|
|
487
|
-
|
|
486
|
+
Token and cost metrics are only emitted when the git notes contain token data (written by v1.5.0+ hooks). All metrics carry attributes `pr`, `branch`, `author`, `tool` — enabling per-PR trend analysis.
|
|
488
487
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
488
|
+
**Supported backends:**
|
|
489
|
+
|
|
490
|
+
| Backend | Configuration |
|
|
491
|
+
|---------|---------------|
|
|
492
|
+
| **Datadog** (shortcut) | Set `DATADOG_API_KEY` secret (and optionally `DATADOG_SITE` org variable, defaults to `datadoghq.com`). Endpoint is auto-configured. |
|
|
493
|
+
| **Datadog** (explicit) | `OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.datadoghq.com`, `OTEL_EXPORTER_OTLP_HEADERS=DD-Api-Key=<key>` |
|
|
494
|
+
| **Grafana Cloud** | `OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-<zone>.grafana.net/otlp`, `OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic <base64(user:token)>` |
|
|
495
|
+
| **Splunk Observability** | `OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.<realm>.signalfx.com/v2/datapoint/otlp`, `OTEL_EXPORTER_OTLP_HEADERS=X-SF-Token=<token>` |
|
|
496
|
+
| **New Relic** | `OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net`, `OTEL_EXPORTER_OTLP_HEADERS=api-key=<key>` |
|
|
497
|
+
| **Honeycomb** | `OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io`, `OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=<key>` |
|
|
498
|
+
| **OTel Collector** | `OTEL_EXPORTER_OTLP_ENDPOINT=http://your-collector:4318` |
|
|
499
|
+
| **Generic webhook** | `METRICS_WEBHOOK_URL=https://...` — POSTs a flat JSON payload with `pr`, `repo`, `author`, `branch`, `ai_lines`, `human_lines`, `total_lines`, `pct_ai`, and optionally `codebase_pct_ai` / `codebase_total_lines` / `cost_usd` / `input_tokens` / `output_tokens` |
|
|
493
500
|
|
|
494
|
-
**
|
|
495
|
-
|
|
496
|
-
|
|
501
|
+
When no destination is configured, the workflow runs in **dry-run mode** — it prints the OTLP payload to stdout and exits 0. This makes it safe to install and test before secrets are set.
|
|
502
|
+
|
|
503
|
+
Set secrets at the org level so they apply to all repos that use this tool. Multiple export destinations can be active simultaneously (e.g. both `OTEL_EXPORTER_OTLP_ENDPOINT` and `METRICS_WEBHOOK_URL`).
|
|
504
|
+
|
|
505
|
+
**Model pricing (org variables — update when Anthropic changes pricing):**
|
|
506
|
+
|
|
507
|
+
| Variable | Default | Description |
|
|
508
|
+
|----------|---------|-------------|
|
|
509
|
+
| `CLAUDE_PRICE_OPUS_INPUT` | `15.00` | $ per 1M input tokens (Claude Opus) |
|
|
510
|
+
| `CLAUDE_PRICE_OPUS_OUTPUT` | `75.00` | $ per 1M output tokens (Claude Opus) |
|
|
511
|
+
| `CLAUDE_PRICE_SONNET_INPUT` | `3.00` | $ per 1M input tokens (Claude Sonnet) |
|
|
512
|
+
| `CLAUDE_PRICE_SONNET_OUTPUT` | `15.00` | $ per 1M output tokens (Claude Sonnet) |
|
|
513
|
+
| `CLAUDE_PRICE_HAIKU_INPUT` | `0.80` | $ per 1M input tokens (Claude Haiku) |
|
|
514
|
+
| `CLAUDE_PRICE_HAIKU_OUTPUT` | `4.00` | $ per 1M output tokens (Claude Haiku) |
|
|
515
|
+
| `CLAUDE_PRICE_CACHE_READ_MULT` | `0.1` | Fraction of input price for cache reads |
|
|
516
|
+
| `CLAUDE_PRICE_CACHE_WRITE_MULT` | `1.25` | Fraction of input price for cache writes |
|
|
517
|
+
|
|
518
|
+
Unrecognized model names (new Claude releases) fall back to Opus pricing. Set these as org-level **variables** (not secrets) — they're not sensitive.
|
|
497
519
|
|
|
498
|
-
**Future:** When Faros is purchased, ADMPLAT-9609 will provide a Faros pipeline. Switching destinations is a one-line change to `src/export/pr-summary.ts` — the data collection, notes format, and GHA trigger all stay the same.
|
|
499
520
|
|
|
500
521
|
---
|
|
501
522
|
|
|
@@ -553,7 +574,7 @@ permissions:
|
|
|
553
574
|
jobs:
|
|
554
575
|
note-ai-commit:
|
|
555
576
|
name: Record AI actor commit attribution
|
|
556
|
-
runs-on: ubuntu-latest
|
|
577
|
+
runs-on: ubuntu-latest # replace with your self-hosted runner label if needed
|
|
557
578
|
steps:
|
|
558
579
|
- uses: actions/checkout@v4
|
|
559
580
|
with:
|
|
@@ -563,6 +584,11 @@ jobs:
|
|
|
563
584
|
- name: Fetch attribution notes
|
|
564
585
|
run: git fetch origin refs/notes/claude-attribution:refs/notes/claude-attribution || true
|
|
565
586
|
|
|
587
|
+
- name: Configure git identity for notes
|
|
588
|
+
run: |
|
|
589
|
+
git config user.email "claude-attribution[bot]@users.noreply.github.com"
|
|
590
|
+
git config user.name "claude-attribution[bot]"
|
|
591
|
+
|
|
566
592
|
- uses: oven-sh/setup-bun@v2
|
|
567
593
|
|
|
568
594
|
- name: Install claude-attribution
|
|
@@ -621,7 +647,7 @@ Each hook invocation is a short-lived process that exits immediately. The trace
|
|
|
621
647
|
|
|
622
648
|
The post-commit hook may not have run. Check:
|
|
623
649
|
1. Is `bun` (or `tsx`) on your PATH in a git hook context? Run `which bun` from your shell, then check if that path is in `.git/hooks/post-commit`.
|
|
624
|
-
2. Did you run `
|
|
650
|
+
2. Did you run `claude-attribution install <repo>` for this specific repo?
|
|
625
651
|
3. Check `.claude/logs/attribution.jsonl` — if it's empty, the hook isn't firing.
|
|
626
652
|
|
|
627
653
|
**Attribution is 0% AI even though Claude wrote everything**
|
package/package.json
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
type LineAttribution,
|
|
35
35
|
hashLine,
|
|
36
36
|
} from "./differ.ts";
|
|
37
|
+
import { parseTranscript } from "../metrics/transcript.ts";
|
|
37
38
|
import {
|
|
38
39
|
writeNote,
|
|
39
40
|
headSha,
|
|
@@ -146,6 +147,21 @@ async function main() {
|
|
|
146
147
|
totals: aggregateTotals(fileResults),
|
|
147
148
|
};
|
|
148
149
|
|
|
150
|
+
// Attach token usage from the Claude session transcript (non-fatal if unavailable)
|
|
151
|
+
if (sessionId) {
|
|
152
|
+
const tx = await parseTranscript(sessionId, repoRoot).catch(() => null);
|
|
153
|
+
if (tx && tx.byModel.length > 0) {
|
|
154
|
+
result.modelUsage = tx.byModel.map((m) => ({
|
|
155
|
+
modelFull: m.modelFull,
|
|
156
|
+
modelShort: m.modelShort,
|
|
157
|
+
inputTokens: m.inputTokens,
|
|
158
|
+
outputTokens: m.outputTokens,
|
|
159
|
+
cacheCreationTokens: m.cacheCreationTokens,
|
|
160
|
+
cacheReadTokens: m.cacheReadTokens,
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
149
165
|
// Write git note
|
|
150
166
|
await writeNote(result, repoRoot);
|
|
151
167
|
|
|
@@ -21,6 +21,16 @@ export interface FileAttribution {
|
|
|
21
21
|
pctAi: number;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/** Token usage captured from the Claude session transcript at commit time. */
|
|
25
|
+
export interface CommitModelUsage {
|
|
26
|
+
modelFull: string;
|
|
27
|
+
modelShort: "Opus" | "Sonnet" | "Haiku" | "Unknown";
|
|
28
|
+
inputTokens: number;
|
|
29
|
+
outputTokens: number;
|
|
30
|
+
cacheCreationTokens: number;
|
|
31
|
+
cacheReadTokens: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
export interface AttributionResult {
|
|
25
35
|
commit: string;
|
|
26
36
|
/** Session ID from .claude/attribution-state/current-session, or null if committed outside a Claude session. */
|
|
@@ -30,6 +40,8 @@ export interface AttributionResult {
|
|
|
30
40
|
timestamp: string;
|
|
31
41
|
files: FileAttribution[];
|
|
32
42
|
totals: Omit<FileAttribution, "path">;
|
|
43
|
+
/** Token usage from the Claude session that produced this commit. Absent for commits outside a Claude session or pre-v1.5.0 notes. */
|
|
44
|
+
modelUsage?: CommitModelUsage[];
|
|
33
45
|
}
|
|
34
46
|
|
|
35
47
|
/**
|
package/src/cli.ts
CHANGED
|
@@ -33,6 +33,9 @@ switch (cmd) {
|
|
|
33
33
|
case "note-ai-commit":
|
|
34
34
|
await import("./commands/note-ai-commit.ts");
|
|
35
35
|
break;
|
|
36
|
+
case "pr-summary":
|
|
37
|
+
await import("./export/pr-summary.ts");
|
|
38
|
+
break;
|
|
36
39
|
case "init":
|
|
37
40
|
await import("./commands/init.ts");
|
|
38
41
|
break;
|
|
@@ -97,6 +100,7 @@ Commands:
|
|
|
97
100
|
pr [title] Create PR with metrics embedded (--draft, --base <branch>)
|
|
98
101
|
init [--ai | --ai-since <YYYY-MM-DD>] Set attribution baseline in the cumulative minimap
|
|
99
102
|
note-ai-commit [sha] [--push] [--if-ai-actor] Write 100% AI git note for a commit (GHA use)
|
|
103
|
+
pr-summary Export PR attribution metrics via OTLP/webhook (GHA use)
|
|
100
104
|
start Mark session start for per-ticket scoping
|
|
101
105
|
hook <name> Run an internal hook (used by installed git hooks)
|
|
102
106
|
version Print version
|