audit-usage · git:20260915.23f9728 · 2026-09-15 · sha256 3a36111c1a97a9e4

audit-usage git:20260915.23f9728A

Immutable. This exact content is served forever at /api/v1/blob/3a36111c1a97a9e4.

---
name: audit-usage
description: >
  Reads the execution trail the `audit` hook writes into `.claude/audit-usage/` and
  consolidates it across runs — spend per skill, duration, failure rate, permissions
  granted, and which report to open next. Answers "what has this project cost me so
  far", which no single report answers. Explicit invocation only.
argument-hint: "[empty for the consolidated view | last | <skill> | <report name fragment>]"
disable-model-invocation: true
allowed-tools: Bash, Read
---

## Trail on disk

!`ls -1t .claude/audit-usage 2>/dev/null`

## Ledger — one line per finished run

!`tail -n 60 .claude/audit-usage/history.jsonl 2>/dev/null`

## Pricing

!`cat .claude/audit-usage/pricing.json 2>/dev/null`

---

Everything below reads. This skill never writes into `.claude/audit-usage/` and never
edits project code — the trail is written by `ArchHook.java audit`, at `Stop`, and by
nothing else.

Answer in the language of the reports themselves (Portuguese), so the consolidated view
and the per-run report read as one document.

## When the three blocks came back empty

The trail is off: `.claude/audit-usage/` does not exist. That is the switch, not a
failure. Say so, and give the two commands that turn it on — creating the directory is
enough, the hook is already wired in `.claude/settings.json`:

```bash
mkdir -p .claude/audit-usage
printf '.claude/audit-usage/.state/\n' >> .gitignore
```

Then stop. Do not create the directory yourself: switching a versioned audit trail on is
the user's decision, not a side effect of asking what it holds.

If the directory exists but `history.jsonl` is missing, no run has **finished** yet. A
`<timestamp>--<skill>.md` may still be there from a run in progress — report it as such
and say the ledger line is written only when the run closes.

## Procedure

### 1 · Route on the argument

| Argument | Do this |
|---|---|
| empty | § 2 — consolidated view over the whole ledger |
| `last` / `último` | § 3 for the newest `.md` in the listing above |
| a skill name (`new-feature`, `init-project`, …) | § 2 filtered to that skill, then § 3 for its newest report |
| any other text | treat it as a fragment of a report filename; § 3 for the single match. Two or more matches: list them and ask which |

### 2 · Consolidated view

Parse the ledger block above — one JSON object per line, fields `iso`, `skill`,
`duration_ms`, `status`, `tokens_billable`, `cost`, `files`, `failures`, `report`.
`cost` is absent on runs recorded while `pricing.json` still had `null` prices; treat
absent as unknown, never as zero.

Render, in this order:

1. **Header line** — how many runs, over what period, how many distinct skills.
2. **Per-run table**, newest first, at most 15 rows: `| # | 🕐 Quando | 🎯 Skill | Status
   | ⏱️ Duração | 🧮 Faturável | 💰 Custo | 📁 Arq. | 🔁 Falhas |`. Reuse the status icon
   already in the ledger (`✅` / `⚠️` / `❌`). Older rows collapse into one
   `… mais N execuções` line.
3. **Totals** — sum of billable tokens, sum of cost, total wall-clock, run count.
   Sum only the runs that carry a `cost`, and say how many were left out.
4. **Spend per skill**, as a bar chart, descending, so the money pit is visible without
   arithmetic:

   ```text
   /new-feature      ███████████████████░░░░░  62%   1.240.500 tok   US$ 18,44   7×
   /init-project     ███████░░░░░░░░░░░░░░░░░  23%     460.100 tok   US$  6,84   2×
   ```

   Bars are 25 characters, `█` and `░`, proportional to billable tokens — not to
   duration, which is wall-clock and includes time the user spent thinking.
5. **Health** — failure rate (`failures > 0` over total), and the skill with the worst
   ratio. A skill that fails repeatedly is a bad spec, not bad luck.
6. **What to read next** — the path of the single most relevant report and the exact
   `/audit-usage <fragment>` that opens it.

### 3 · Single run

`Read` the `.md` file. It is already a finished, icon-rich report — **do not re-render
it and do not paste it back in full.** Summarize in at most six lines: what was run, the
outcome, what it cost, the longest step, and anything that deserves attention
(`⏳ em andamento`, compaction incidents, permissions added, repeated tool failures).
Then give the path so the user can open the whole thing.

Point out `⏳ em andamento` when you see it: it means the run never closed — the session
was killed, or it is still open right now.

## What this skill will not do

- **Never edits or deletes anything under `.claude/audit-usage/`.** Pruning old reports
  is the user's call; if they ask, show the command and let them run it.
- **Never recomputes a cost from memory.** The numbers come from the ledger and from
  `pricing.json`. If prices are `null`, the honest answer is "custo não configurado" plus
  the path to fill in — never an invented rate. A price written from memory is wrong the
  day after it changes, and it looks exactly as authoritative as a correct one.
- **Never reads `.state/`.** Those are the hook's open append-only logs; the rendered
  `.md` is the readable form.
- **Never quotes a redacted value.** The reports blank credential-shaped text on the way
  in. If something still looks like a live secret in a report, say so and point at
  `.claude/schemas/extensions.json`'s `audit.redact` block — a pattern is missing there.

## Why this is a skill, and why the hook ignores it

The hook already answers "what happened in **this** run". Nothing answers "what have the
last twenty runs cost, and which skill is eating the budget" — that needs the ledger
parsed and aggregated, which is a procedure, not a norm and not enforcement.

`audit-usage` is the one `disable-model-invocation: true` skill the trail does **not**
record, declared in `.claude/schemas/extensions.json` under `audit.exclude_skills`.
Without that, reading the trail would append a report about reading the trail, and every
later read would be mostly reads. Invoking it still **closes** whatever run is open, so
what gets read is a finished report instead of one stuck at `⏳ em andamento`.

## Contract

- **Reads** — `.claude/audit-usage/*.md`, `.claude/audit-usage/history.jsonl`,
  `.claude/audit-usage/pricing.json`
- **Writes** — nothing
- **Handoff** — none. Terminal skill: it reports and stops