costgrep-cli 0.0.0-stage → 0.7.3

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/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 Paraphern and costgrep contributors
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/README.md CHANGED
@@ -1,3 +1,300 @@
1
- # Temporary Holding Version
1
+ # costgrep
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
4
+ [![test](https://github.com/Paraphern/costgrep/actions/workflows/test.yml/badge.svg)](https://github.com/Paraphern/costgrep/actions/workflows/test.yml)
5
+ [![self-audit](https://github.com/Paraphern/costgrep/actions/workflows/audit.yml/badge.svg)](https://github.com/Paraphern/costgrep/actions/workflows/audit.yml)
6
+
7
+ **GitHub Actions cost breakdown by who triggered the run: human vs AI agent vs bot.**
8
+
9
+ **See it on real data → [live benchmark, 6 public repos](benchmark/2026-10-02/README.md)**
10
+ and **[3 verifiable cases with links to the actual runs & PRs](benchmark/cases.md):**
11
+ in GitHub's own agentic-workflows repo, AI agents triggered **53.5% of runs and 28.8% of
12
+ CI spend in a single day**; a solo maintainer's repo is now **agent-majority (46% of runs,
13
+ 57% of spend)**; in microsoft/vscode, **89% of a one-day sample's compute value ($123.61)
14
+ sits in runner classes GitHub bills even for public repos**.
15
+ Every figure ships with per-job evidence you can recompute in a spreadsheet.
16
+
17
+ Copilot coding agents, Copilot code review, Claude Code, Codex, Cursor — since June 2026
18
+ they all burn your Actions minutes *on top of* AI credits ([#192948], 958 downvotes).
19
+ GitHub's own usage views don't tell you **who** spent it. This does:
20
+
21
+ ```
22
+ $ node scripts/costgrep.mjs --repo actions/stale --days 30 --max-runs 50 --quiet
23
+ costgrep — actions/stale (last 30 days, 50 runs, 47 jobs)
24
+ ==============================================================================
25
+ class runs run% minutes cost cost%
26
+ ------------------------------------------------------------------------------
27
+ agent 0 0.0% 0 $0.00 0.0%
28
+ agent-assisted 14 28.0% 26 $0.28 36.8%
29
+ human 25 50.0% 24 $0.33 42.5%
30
+ bot 11 22.0% 16 $0.16 20.7%
31
+ unattributed 0 0.0% 0 $0.00 0.0%
32
+ ------------------------------------------------------------------------------
33
+ total 50 66 $0.77
34
+
35
+ >>> Agents cost $0.00 (0.0% of CI spend, 0 runs / 0.0%).
36
+ >>> Bot runs: 11 (22.0% of all runs), bot spend $0.16.
37
+ >>> Top workflows by total cost:
38
+ $0.50 65.0% Basic validation
39
+ $0.10 12.4% Code scanning
40
+ $0.06 7.8% Licensed
41
+ >>> public repo: standard Linux/Windows minutes are FREE — the $ above is the list-price VALUE of this compute (what it would cost in a private repo), not money owed. NOTE: 5 macOS/larger-runner jobs ARE billed even for public repos (~$0.37).
42
+ >>> List-price model: NOT your invoice. Included plan minutes are consumed first; hosted phase reconciles against the billing API.
43
+ ```
44
+
45
+ *(Real snapshot taken 2026-10-04 with the exact command shown; rerun it and you get
46
+ current numbers. Note the `agent-assisted` row: 14 of 50 runs in this repo were
47
+ pushed by humans with an AI `Co-Authored-By` trailer — that's the class doing its job.)*
48
+
49
+ Runs **on your infrastructure** (your Actions runner, your terminal). Zero npm
50
+ dependencies (single-file CLI core + a plain `agents.json` for the classifier
51
+ lists — the core works standalone if the data file is missing), no telemetry,
52
+ no code/log access — workflow-run metadata only.
53
+
54
+ ## Quick start (composite action)
55
+
56
+ ```yaml
57
+ # .github/workflows/agent-ci-audit.yml
58
+ name: costgrep audit
59
+ on:
60
+ schedule: [{cron: '0 6 * * 1'}] # weekly
61
+ workflow_dispatch:
62
+ permissions:
63
+ actions: read # the only permission needed
64
+ jobs:
65
+ audit:
66
+ runs-on: ubuntu-latest
67
+ steps:
68
+ - uses: actions/checkout@v4 # only needed if you run a *local* copy / want the JSON in an artifact
69
+ - uses: YOUR_LOGIN/costgrep@v1
70
+ with:
71
+ days: '30'
72
+ - uses: actions/upload-artifact@v4
73
+ if: always()
74
+ with: {name: costgrep, path: costgrep.json}
75
+ ```
76
+
77
+ No org-admin rights, no billing access, no webhook — the repo-scoped
78
+ `GITHUB_TOKEN` with `actions: read` is enough.
79
+
80
+ ## CLI
81
+
82
+ ```bash
83
+ node scripts/costgrep.mjs --repo owner/name [--days 30] [--token $GITHUB_TOKEN] \
84
+ [--max-runs 1000] [--config my-rules.json] [--json-file report.json] \
85
+ [--csv-file evidence.csv] [--md-file report.md] [--gh-output $GITHUB_OUTPUT] \
86
+ [--no-coab] [--pr-comment N|auto] [--slack-webhook URL] [--step-summary] [--quiet] [--help]
87
+ # org-wide audit (still no org-admin rights — a PAT with actions:read is enough):
88
+ node scripts/costgrep.mjs --org my-org [--repos 20] [--max-runs 100]
89
+ # offline demo on a bundled fixture:
90
+ node scripts/costgrep.mjs --fixture-dir test/fixtures/demo-repo --days 4000
91
+ ```
92
+
93
+ Requires Node >= 18 (`fetch`, `node:test`, ESM — no dependencies). Note on caps:
94
+ `--max-runs` defaults to **1000** in the CLI and **500** in the composite action
95
+ (safety cap for shared runners).
96
+
97
+ `--config` (JSON) extends the classifier and overrides rates:
98
+
99
+ ```json
100
+ {
101
+ "agents": ["my-internal-agent-bot"],
102
+ "bots": ["my-ci-bot"],
103
+ "rates": { "actions_linux": 0.006 }
104
+ }
105
+ ```
106
+
107
+ ## Outputs & budget gate
108
+
109
+ The action exposes machine-readable outputs, so CI can *act* on the split —
110
+ not just display it (per-actor budget enforcement GitHub's budgets API doesn't have):
111
+
112
+ ```yaml
113
+ - id: costgrep
114
+ uses: Paraphern/costgrep@v1
115
+ - name: Agent budget gate (fail when agents exceed 20% of CI spend)
116
+ if: fromJSON(steps.costgrep.outputs['agent-share-pct']) > 20
117
+ run: |
118
+ echo "::error::AI agents burned ${{ steps.costgrep.outputs['agent-cost'] }} \
119
+ (${{ steps.costgrep.outputs['agent-share-pct'] }}% of CI spend) — above the 20% budget"
120
+ exit 1
121
+ ```
122
+
123
+ | output | meaning |
124
+ |---|---|
125
+ | `total-cost` / `total-minutes` | whole-window list-price spend / billable minutes |
126
+ | `agent-cost` / `agent-share-pct` / `agent-runs` | the AI-agent slice |
127
+ | `agent-assisted-cost` | human-triggered runs with an AI `Co-Authored-By` trailer |
128
+ | `bot-runs` / `bot-runs-pct` | automation share of runs |
129
+
130
+ ### Ready-made gates (copy-paste)
131
+
132
+ ```yaml
133
+ # 1) Budget gate: fail CI when agents exceed 20% of spend
134
+ - id: costgrep
135
+ uses: Paraphern/costgrep@v1
136
+ - run: |
137
+ if (( $(echo "${{ steps.costgrep.outputs['agent-share-pct'] }} > 20" | bc -l) )); then
138
+ echo "::error::Agents at ${{ steps.costgrep.outputs['agent-cost'] }} (${{ steps.costgrep.outputs['agent-share-pct'] }}%) — above budget"
139
+ exit 1
140
+ fi
141
+
142
+ # 2) Weekly org audit to Slack (secrets.SLACK_WEBHOOK; PAT with actions:read as CG_PAT)
143
+ - uses: Paraphern/costgrep@v1
144
+ env:
145
+ CG_TOKEN: ${{ secrets.CG_PAT }}
146
+ with:
147
+ token: ${{ secrets.CG_PAT }}
148
+ slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
149
+ days: '7'
150
+ # (--org mode: run the CLI directly, see above)
151
+
152
+ # 3) Cost stamp on every PR (needs pull-requests:write)
153
+ - uses: Paraphern/costgrep@v1
154
+ with:
155
+ pr-comment: auto
156
+ ```
157
+
158
+ ## Reconciliation & AI credits (experimental subcommands)
159
+
160
+ For organizations that *can* provide an org token with billing rights (the one
161
+ place costgrep ever asks for more than `actions:read`):
162
+
163
+ ```bash
164
+ # our recomputation vs the billing API, per SKU, for a calendar month;
165
+ # the delta lands in an explicit "unattributed" bucket with coverage warnings
166
+ node scripts/costgrep.mjs reconcile --org my-org --month 2026-09 --billing-token $ORG_TOKEN
167
+
168
+ # AI credits for the same month (by model; per-user breakdown is not exposed by the API)
169
+ node scripts/costgrep.mjs credits --org my-org --month 2026-09 --billing-token $ORG_TOKEN
170
+ ```
171
+
172
+ **Status, stated plainly: EXPERIMENTAL.** This code path is not yet validated
173
+ against a live billing account — treat every delta as a hypothesis until
174
+ calibrated on a real invoice (that calibration program is the 1.0 gate). Every
175
+ output of these subcommands carries this label; the coverage warnings say
176
+ exactly which repos/runs the comparison did and did not see.
177
+
178
+ ## Evidence: every number is traceable
179
+
180
+ Reports are self-describing — a human can check any figure against real API data:
181
+
182
+ - **JSON** (`--json-file`): aggregates + `provenance` (which endpoints were called, the
183
+ exact window, fetched-vs-counted jobs, rates version, honesty flags) + `evidence[]` —
184
+ one row per billable job.
185
+ - **CSV** (`--csv-file`): the same per-job rows for any spreadsheet: run, actor, class,
186
+ timestamps, minutes, SKU, rate, cost.
187
+ - **Markdown** (`--md-file`): a shareable report with the provenance block baked in.
188
+
189
+ Verify it yourself (that's the point):
190
+
191
+ 1. open the CSV and recompute `minutes × rate_usd_per_min` on any row — it matches `cost_usd`;
192
+ 2. sum `cost_usd` — it equals the headline totals;
193
+ 3. compare `minutes` against the GitHub usage UI for the same window (same per-job round-up);
194
+ 4. rerun costgrep on the same repo + window — the result is deterministic (latest job attempt only);
195
+ 5. spot-check any `run_id` via `gh api /repos/{repo}/actions/runs/{run_id}/jobs` — timestamps,
196
+ actor and labels in the CSV are the API's own values, unmodified.
197
+
198
+ Scope note: the CSV proves the **spend and minutes** figures. The *run-share* percentages
199
+ (e.g. "agents = 53.5% of runs") count all runs in the window, including ones whose jobs
200
+ never started — those are visible in the JSON report's `runsAnalyzed`/class totals, and
201
+ re-derivable directly from `GET /repos/{repo}/actions/runs`.
202
+
203
+ ## How it attributes (summary — full methodology: [docs/methodology.md](docs/methodology.md))
204
+
205
+ 1. **Identity = `triggering_actor`**, falling back to `actor`. Re-runs are
206
+ attributed to whoever re-ran them. For `workflow_run` chains `github.actor`
207
+ can resolve to a generic bot ([gh-aw#20586]) — `triggering_actor` is the
208
+ honest identity, so that's what we use.
209
+ 2. **Classes:**
210
+ - `agent` — login matches an AI-agent slug list (Copilot / `copilot-swe-agent`,
211
+ Claude, Codex, Cursor, Gemini, Devin, Aider, Windsurf, CodeRabbit, Qodo, …
212
+ the list lives in [`agents.json`](agents.json) — extend it by PR, no code);
213
+ - `agent-assisted` — human-triggered run whose head commit carries an AI
214
+ `Co-Authored-By:` trailer (checked from the run's own metadata, zero extra
215
+ API calls; disable with `--no-coab`). Known limit, stated plainly: some
216
+ editors (VS Code) auto-insert the Copilot trailer — that's why this is a
217
+ separate class and never silently merged into `agent`;
218
+ - `bot` — automation (`github-actions[bot]`, Dependabot, Renovate, …) and any
219
+ other `type: Bot` / `[bot]` account not claimed by the agent list;
220
+ - `human` — `type: User`;
221
+ - `unattributed` — anything we can't classify. We show the bucket; we never
222
+ quietly re-assign it.
223
+ 3. **Minutes:** `ceil((completed_at − started_at) / 60s)` per job, minimum 1 —
224
+ same per-job round-up GitHub bills by. Jobs of the latest attempt only
225
+ (the API default), so re-run attempts aren't double-counted. Jobs that never
226
+ reached a runner (no timestamps) count 0; in-progress runs are excluded and
227
+ reported. Jobs that hold runner timestamps but were `skipped` or had
228
+ zero/negative duration are billed at the 1-minute minimum and **flagged in
229
+ every report with their exact total** — whether GitHub bills such executions
230
+ identically is unverifiable without invoice access, so we surface the
231
+ subtrahend instead of silently deciding.
232
+ 4. **Rates:** the published GitHub-hosted runner list prices effective
233
+ 2026-01-01 ([docs]), keyed by the same SKU ids the billing API uses — the
234
+ foundation for invoice reconciliation in the hosted phase. Self-hosted = $0
235
+ (the $0.002/min platform fee was [postponed]); minutes still counted.
236
+ 5. **Known limits (stated, not hidden):** list-price model — included plan
237
+ minutes are consumed first, so this is *gross spend at list prices*, not
238
+ your invoice; runner-class inference comes from job labels and falls back
239
+ to the standard Linux rate (flagged in every report as `rate fallback` jobs);
240
+ per-user AI *credits* are not visible at repo level (org billing API only —
241
+ hosted phase). Expected accuracy vs a real invoice: **±5–10%**, which is
242
+ exactly why the hosted product leads with reconciliation, not estimates.
243
+
244
+ ## Why not just…? *(checked 2026-10)*
245
+
246
+ | tool | human/agent/bot split | cost numbers it gives you | what you pay | needs org admin |
247
+ |---|---|---|---|---|
248
+ | GitHub native usage views | ✗ — no actor slice; org metrics [frozen since 21.07.2026](https://github.com/orgs/community/discussions/202838) | invoice-grade billing view | included in plan | view perms on billing |
249
+ | Datadog CI Visibility | partial — git-author facet only, not trigger actor | span-based estimates | $8–12 / committer / mo | yes |
250
+ | Blacksmith Analytics | ✗ (jobs / workflows / repos) | runner minutes | bundled with runners | no |
251
+ | TrimCI | partial — bots excluded from *their* billing math; no AI-agent class | cost of CI failures | usage-based, per active contributor | no |
252
+ | **costgrep** | **✓ human / AI agent / bot, re-runs attributed** | **list-price per 2026 SKU rates (invoice reconciliation = planned hosted phase, not yet)** | **nothing — free, open source (Apache-2.0)** | **no — repo token, `actions:read`** |
253
+
254
+ **Money, stated plainly:** costgrep costs nothing and no paid tier exists today.
255
+ Every `$` figure it prints is the *list-price value of compute* (see the notice in
256
+ every report) — not money you owe GitHub and not money you pay us.
257
+
258
+ ## Calibration case (do this first)
259
+
260
+ Run it on a repo where you can see the Actions usage chart for the same window:
261
+
262
+ 1. `node scripts/costgrep.mjs --repo your-org/your-repo --days 30 --json-file r.json`
263
+ 2. Compare `totalMinutes` per workflow against the GitHub UI "Usage" breakdown
264
+ for the same 30 days (billable minutes, per-job rounded).
265
+ 3. Compare per-class cost shares against what you *know* about the repo
266
+ (scheduled jobs → bot; PRs from `copilot-swe-agent` → agent).
267
+ 4. If your runners use unusual labels, add a `--config` rates block until
268
+ `rate fallback jobs` hits zero.
269
+
270
+ ## Roadmap
271
+
272
+ **This repo — the CLI and the composite action — is free and open source, permanently.**
273
+ If a paid product ever appears, it will be the *hosted service* (continuous monitoring,
274
+ invoice reconciliation), not this code.
275
+
276
+ **1.0 is criteria-based, not date-based** (see [CHANGELOG](CHANGELOG.md) and
277
+ [docs/calibration.md](docs/calibration.md)): accuracy demonstrated on ≥5 real
278
+ organizations with reconciliation delta ≤5%; 100+ action installs; zero open
279
+ doctrine violations under a repeatable verification pass. Current state:
280
+ **engineering-complete to 1.0-rc — the remaining gates are market evidence.**
281
+ Stability guarantees: [docs/compatibility.md](docs/compatibility.md).
282
+
283
+ - **Phase B (hosted):** GitHub App + webhooks → org-wide dashboard, per-actor
284
+ budgets/alerts, weekly digests.
285
+ - **Phase C (reconciliation):** nightly join against
286
+ `/organizations/{org}/settings/billing/usage` — "our sum = your invoice line",
287
+ residual shown as an honest `unattributed` bucket; AI credits per user next
288
+ to minutes — the first unified agent bill.
289
+
290
+ ## Privacy & license
291
+
292
+ The CLI reads workflow-run/job metadata via the public REST API and stores
293
+ nothing anywhere. Apache-2.0 — see [LICENSE](LICENSE). Accuracy is best-effort; see
294
+ the methodology above before quoting numbers to your CFO.
295
+
296
+ [#192948]: https://github.com/orgs/community/discussions/192948
297
+ [gh-aw#20586]: https://github.com/github/gh-aw/issues/20586
298
+ [docs]: https://docs.github.com/en/billing/reference/actions-runner-pricing
299
+ [postponed]: https://github.com/resources/insights/2026-pricing-changes-for-github-actions
300
+ [#202838]: https://github.com/orgs/community/discussions/202838
package/action.yml ADDED
@@ -0,0 +1,85 @@
1
+ name: 'CostGrep'
2
+ description: >-
3
+ GitHub Actions cost breakdown by who triggered the run: human vs AI agent
4
+ (Copilot, Claude, Codex, Cursor...) vs bot. List-price model, public
5
+ methodology, per-job evidence trail, runs on your runner — no data leaves
6
+ your infrastructure.
7
+ author: 'Paraphern'
8
+
9
+ inputs:
10
+ days:
11
+ description: 'Look-back window in days'
12
+ default: '30'
13
+ token:
14
+ description: 'GitHub token with actions:read (repo-scoped GITHUB_TOKEN is enough)'
15
+ default: '${{ github.token }}'
16
+ max-runs:
17
+ description: 'Safety cap on analyzed workflow runs'
18
+ default: '500'
19
+ config:
20
+ description: 'Optional JSON file {agents, bots, rates, coab} to extend/override defaults'
21
+ default: ''
22
+ pr-comment:
23
+ description: 'Optional: post the report as a PR comment (PR number, or "auto" to take it from GITHUB_REF; token needs pull-requests:write)'
24
+ default: ''
25
+ slack-webhook:
26
+ description: 'Optional: post the report to a Slack incoming webhook (https://hooks.slack.com/services/... URL)'
27
+ default: ''
28
+ json-file:
29
+ description: 'Where to write the full JSON report (relative to workspace)'
30
+ default: 'costgrep.json'
31
+ csv-file:
32
+ description: 'Optional: write per-job evidence CSV here (audit trail)'
33
+ default: ''
34
+ md-file:
35
+ description: 'Optional: write shareable Markdown report with provenance here'
36
+ default: ''
37
+
38
+ outputs:
39
+ total-cost:
40
+ description: 'Total list-price CI spend, USD'
41
+ total-minutes:
42
+ description: 'Total billable minutes (rounded per job, like GitHub)'
43
+ agent-cost:
44
+ description: 'List-price spend attributed to AI agents, USD'
45
+ agent-assisted-cost:
46
+ description: 'Spend from human-triggered runs whose commits carry an AI Co-Authored-By trailer, USD'
47
+ agent-share-pct:
48
+ description: 'Agents share of total CI spend, % (e.g. "6.8")'
49
+ agent-runs:
50
+ description: 'Workflow runs attributed to AI agents'
51
+ bot-runs:
52
+ description: 'Workflow runs attributed to bots'
53
+ bot-runs-pct:
54
+ description: 'Bot share of all runs, %'
55
+
56
+ runs:
57
+ using: 'composite'
58
+ steps:
59
+ - name: 'costgrep: human vs agent vs bot CI spend'
60
+ shell: bash
61
+ env:
62
+ # All user-controllable inputs go through env, never via ${{ }} inline in
63
+ # bash — script-injection hardening per GitHub's security guide.
64
+ CG_TOKEN: ${{ inputs.token }}
65
+ CG_REPO: ${{ github.repository }}
66
+ CG_DAYS: ${{ inputs.days }}
67
+ CG_MAX_RUNS: ${{ inputs.max-runs }}
68
+ CG_JSON: ${{ inputs.json-file }}
69
+ CG_CSV: ${{ inputs.csv-file }}
70
+ CG_MD: ${{ inputs.md-file }}
71
+ CG_CONFIG: ${{ inputs.config }}
72
+ CG_PR: ${{ inputs.pr-comment }}
73
+ CG_SLACK: ${{ inputs.slack-webhook }}
74
+ run: |
75
+ ARGS=(--repo "$CG_REPO" --days "$CG_DAYS" --max-runs "$CG_MAX_RUNS" --token "$CG_TOKEN" --json-file "$CG_JSON" --gh-output "$GITHUB_OUTPUT" --step-summary)
76
+ [ -n "$CG_CSV" ] && ARGS+=(--csv-file "$CG_CSV")
77
+ [ -n "$CG_MD" ] && ARGS+=(--md-file "$CG_MD")
78
+ [ -n "$CG_CONFIG" ] && ARGS+=(--config "$CG_CONFIG")
79
+ [ -n "$CG_PR" ] && ARGS+=("--pr-comment=$CG_PR")
80
+ [ -n "$CG_SLACK" ] && ARGS+=("--slack-webhook=$CG_SLACK")
81
+ node "${{ github.action_path }}/scripts/costgrep.mjs" "${ARGS[@]}"
82
+
83
+ branding:
84
+ icon: 'bar-chart-2'
85
+ color: 'purple'
package/agents.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": "2026-10-03",
3
+ "note": "Data file: extend the classifier without touching code. PRs adding entries must cite evidence (a public run/PR/doc) — see CONTRIBUTING.md. Rates live in code (money = code review).",
4
+ "agents": [
5
+ "copilot", "copilot-swe-agent", "claude", "cursor", "codex", "openai-codex",
6
+ "gemini", "gemini-code-assist", "jules", "devin", "aider", "windsurf",
7
+ "codebuff", "opencode", "goose", "factory", "sweep", "coderabbit",
8
+ "qodo", "greptile", "ellipsis", "codeant", "kodu", "ampcode"
9
+ ],
10
+ "bots": [
11
+ "github-actions", "actions-user", "dependabot", "renovate", "greenkeeper",
12
+ "imgbot", "allcontributors", "lock", "stash", "bors", "mergify",
13
+ "semantic-release-bot", "codecov", "coveralls", "netlify", "vercel",
14
+ "github-advanced-security", "fossa", "changeset-bot"
15
+ ],
16
+ "coauthorNames": [
17
+ "copilot", "claude", "cursor", "gemini", "codex", "devin", "aider",
18
+ "windsurf", "jules", "goose", "codebuff", "opencode"
19
+ ]
20
+ }
package/package.json CHANGED
@@ -1,6 +1,17 @@
1
1
  {
2
2
  "name": "costgrep-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.7.3",
4
+ "description": "GitHub Actions cost breakdown: human vs AI agent vs bot. Zero-dependency CLI + composite action.",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "keywords": ["github-actions", "cost", "finops", "ci", "ai-agents", "copilot", "developer-tools", "cli"],
8
+ "homepage": "https://github.com/Paraphern/costgrep#readme",
9
+ "repository": { "type": "git", "url": "git+https://github.com/Paraphern/costgrep.git" },
10
+ "bugs": "https://github.com/Paraphern/costgrep/issues",
11
+ "bin": { "costgrep": "scripts/costgrep.mjs" },
12
+ "scripts": {
13
+ "test": "node --test"
14
+ },
15
+ "engines": { "node": ">=18" },
16
+ "files": ["scripts/", "agents.json", "action.yml", "README.md", "LICENSE"]
17
+ }