@nightlab-agent/subagent-spend 0.0.0-stage → 0.7.2
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/.claude-plugin/icon.png +0 -0
- package/.claude-plugin/marketplace.json +16 -0
- package/.claude-plugin/plugin.json +57 -0
- package/CHANGELOG.md +181 -0
- package/LICENSE +9 -0
- package/README.md +361 -2
- package/hooks/hooks.json +1 -0
- package/hooks/register.tsx +329 -0
- package/hooks/spend.ts +98 -0
- package/hooks/turns.ts +45 -0
- package/hooks/usage.ts +86 -0
- package/kit/settings-snippet.json +8 -0
- package/package.json +32 -4
- package/subagent_spend.py +96 -0
- package/types/index.d.ts +29 -0
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nightlab",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Nightlab"
|
|
5
|
+
},
|
|
6
|
+
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "subagent-spend",
|
|
9
|
+
"source": "./",
|
|
10
|
+
"description": "Per-subagent tokens, rate-limit windows, context fill, cost, steps, tool calls and tool errors of a Claude Code session, in a notice under the prompt, local files and panes (the usage pane not yet seen on a screen).",
|
|
11
|
+
"version": "0.7.2",
|
|
12
|
+
"category": "productivity"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"description": "One mod: per-subagent tokens, usage limits and turn counts for Claude Code sessions, by Nightlab."
|
|
16
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "subagent-spend",
|
|
3
|
+
"version": "0.7.2",
|
|
4
|
+
"description": "Per-subagent tokens, rate-limit windows, context fill, cost, steps, tool calls and tool errors of a Claude Code session, in a notice under the prompt, local files and panes (the usage pane not yet seen on a screen).",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Nightlab"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://github.com/nightlab-agent/subagent-spend",
|
|
9
|
+
"repository": "https://github.com/nightlab-agent/subagent-spend",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"hooks",
|
|
13
|
+
"mod",
|
|
14
|
+
"claude-code-mod",
|
|
15
|
+
"pane",
|
|
16
|
+
"statusline",
|
|
17
|
+
"subagents",
|
|
18
|
+
"token-usage",
|
|
19
|
+
"rate-limits",
|
|
20
|
+
"usage",
|
|
21
|
+
"headless",
|
|
22
|
+
"max-turns",
|
|
23
|
+
"budget"
|
|
24
|
+
],
|
|
25
|
+
"userConfig": {
|
|
26
|
+
"dir": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"title": "Rows folder",
|
|
29
|
+
"description": "Folder for the per-session rows file. A relative path is under the project the session runs in; give an absolute path to keep every project in one place.",
|
|
30
|
+
"default": ".subagent-spend"
|
|
31
|
+
},
|
|
32
|
+
"usageFile": {
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"title": "Usage file and pane",
|
|
35
|
+
"description": "Show the rate-limit windows, context fill and cost in the status line and a Usage pane, and write them to latest.json. Off: none of that.",
|
|
36
|
+
"default": true
|
|
37
|
+
},
|
|
38
|
+
"usageFileDir": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"title": "Usage file folder",
|
|
41
|
+
"description": "Folder for latest.json. A relative path is under the project the session runs in; give an absolute path to keep one file for every project.",
|
|
42
|
+
"default": ".usage-file"
|
|
43
|
+
},
|
|
44
|
+
"turnTally": {
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"title": "Turn counts",
|
|
47
|
+
"description": "Count the main conversation's steps, tool calls and tool errors in the status line and a file for each day. Off: none of that.",
|
|
48
|
+
"default": true
|
|
49
|
+
},
|
|
50
|
+
"turnTallyDir": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"title": "Turn counts folder",
|
|
53
|
+
"description": "Folder for the day's counts file. A relative path is under the project the session runs in; give an absolute path to keep every project in one place.",
|
|
54
|
+
"default": ".turn-tally"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
The form is Keep a Changelog; versions follow SemVer.
|
|
4
|
+
|
|
5
|
+
## 0.7.2 - 2026-10-30
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- The mod is also on npm as `@nightlab-agent/subagent-spend` (package.json; no install scripts).
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- The README's preview picture is linked by its full GitHub address, so the npm page can show it too.
|
|
12
|
+
|
|
13
|
+
## 0.7.1 - 2026-10-30
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- When a step of a subagent's earlier run was counted after a step of its newer run (its session lookup came back
|
|
17
|
+
late), the row's context, time and model went back to the earlier run's. Its tokens are still added; the context,
|
|
18
|
+
time and model now stay the newer run's. One failing test and two comparison cases in hooks/review-late.test.tsx.
|
|
19
|
+
|
|
20
|
+
## 0.7.0 - 2026-10-29 - absorbs usage-file and turn-tally
|
|
21
|
+
|
|
22
|
+
Three separate mods (subagent-spend, usage-file, turn-tally) measured one session from three sides; one install now does all three. Minor, not patch: new
|
|
23
|
+
options, new files written and a new status line.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- A /clear while a rows-file write was waiting on the disk emptied the old session's file: the text is now taken
|
|
27
|
+
when the step is counted. After a /clear the subagent pane and the Usage pane went on showing the old session (the
|
|
28
|
+
command already said 0); both now start empty, and the usage part of the status line waits for the new reading.
|
|
29
|
+
- The by-hand script named the rows file `unknown.jsonl` when Claude Code gave no session id; it is now
|
|
30
|
+
`no-session.jsonl`, as the mod and the README name it.
|
|
31
|
+
- Each `.gitignore` is written only in a folder of the mod's own name: with `dir` set to `.`, it replaced the
|
|
32
|
+
project's own `.gitignore` with `*`. The by-hand script keeps the same rule for `SUBAGENT_SPEND_DIR`.
|
|
33
|
+
- After a `/clear` (a new session id with no new session start) the old session's subagents no longer land in the
|
|
34
|
+
new session's rows file; `/subagent-spend` and the usage pane's one-time opening start over at the session's end,
|
|
35
|
+
even when no subagent runs after it. A late answer naming the session just left does not switch back to it; a
|
|
36
|
+
later resume into that session counts there again.
|
|
37
|
+
- On a host with no session id, a subagent step is still counted (rows file `no-session.jsonl`).
|
|
38
|
+
- A deleted folder came back in the same session without its `.gitignore`, so git saw the rows file: the
|
|
39
|
+
`.gitignore` is now checked before every write.
|
|
40
|
+
- Tests for the `/clear` (old rows kept apart, the pane and the command starting over, a late session id, a
|
|
41
|
+
resume), a deleted folder, a host with no session id, and the by-hand script's `.gitignore` rule, and a write still waiting across a /clear; 62 module tests in all.
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
- plugin.json and marketplace.json say the usage pane is not yet seen on a screen (the subagent pane was seen at 150 columns).
|
|
45
|
+
- README: Install comes right after the opening paragraph and names the Claude Code versions it needs
|
|
46
|
+
(2.1.275 for `/plugin install --marketplace`, 2.1.292 for the shell form, from Claude Code's CHANGELOG); the
|
|
47
|
+
notice is shown as a terminal drew it, with the `⚠ subagent-spend:` Claude Code puts in front; claude-hud's
|
|
48
|
+
features are named from its README instead of left unchecked.
|
|
49
|
+
- `subagent_spend.py --help` prints the commands; an unknown command or extra arguments print them to stderr
|
|
50
|
+
and exit 2 (before, they ran the report and created `.subagent-spend` in the current folder). Run bare from a
|
|
51
|
+
terminal, it prints them instead of waiting on stdin.
|
|
52
|
+
- `tests/planted_bug_test.py`: the fixture test must fail on four planted bugs; `tests/release.sh` runs it.
|
|
53
|
+
- One notice under the prompt holds all three parts, subagent figures first: at 120 columns Claude Code cut a
|
|
54
|
+
line that put them last after "tok 27" (seen in a terminal preview, 2026-10-29).
|
|
55
|
+
- One status line for the three parts, `subagents | usage | turns`, each piece as its mod showed it. Each status call
|
|
56
|
+
sets the whole line, so three parts setting it in turn would have replaced each other.
|
|
57
|
+
- The pane shows an agent id by its first six characters, longer where two listed ids start the same; before, two
|
|
58
|
+
agents whose ids shared six characters showed the same label.
|
|
59
|
+
- Every file is written one write at a time from the newest figures. turn-tally 0.4.0 could write an older step's
|
|
60
|
+
message count over a newer one when its first write was slow; found by the new out-of-order test.
|
|
61
|
+
- README rewritten for the three parts.
|
|
62
|
+
- `tests/typecheck.sh` no longer falls back to a folder on the maintainer's machine. It uses `TSC` and `CC_TYPES`
|
|
63
|
+
when set, else `npx --no-install tsc` or `tsc` on PATH, and the declarations Claude Code writes beside the mod
|
|
64
|
+
(`.claude-plugin/types/claude-code/index.d.ts`); a missing one still fails, now with what to install.
|
|
65
|
+
- Notes in `CLAUDE.md`, `00-catalog/`, and `01-context/`, and this file, rewritten in plain words.
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
- The usage part, from usage-file 0.4.1, behind the `usageFile` option (default on): the rate-limit windows, context
|
|
69
|
+
fill and cost in a Usage pane (`/usage-file`, or by itself once at 80 percent) and in `<usageFileDir>/latest.json`.
|
|
70
|
+
The default folder is `.usage-file` and the file's format is unchanged, so a script that read usage-file's file
|
|
71
|
+
keeps working.
|
|
72
|
+
- The turn counts, from turn-tally 0.4.0, behind the `turnTally` option (default on): main-conversation steps, tool
|
|
73
|
+
calls, tool errors and error streak in `<turnTallyDir>/<YYYY-MM-DD>.txt`, same folder and lines as turn-tally
|
|
74
|
+
but one: `tool calls 11 (subagents 30)` now reads `tool calls 11 (30 by subagents)` (in a terminal preview the
|
|
75
|
+
old form was read as a count of subagents).
|
|
76
|
+
- Options `usageFileDir` and `turnTallyDir` (the two mods' `dir` options under new names; this mod's `dir` stays the
|
|
77
|
+
subagent rows folder).
|
|
78
|
+
- Tests: the 14 of usage-file and the 6 of turn-tally, adapted; one per option turned off and its control; one for the combined status
|
|
79
|
+
line; four for events that finish out of order, two subagents stepping while the main turn is open, and ids that look
|
|
80
|
+
alike.
|
|
81
|
+
- A look-alike test for two agents of the same type: they stay two rows with their own counts (it fails if rows are
|
|
82
|
+
keyed by type).
|
|
83
|
+
- README: a "Remove it" section (uninstall, and the folders and files to delete).
|
|
84
|
+
|
|
85
|
+
## 0.6.0 - 2026-10-29 - the folder is kept out of git
|
|
86
|
+
|
|
87
|
+
### Added
|
|
88
|
+
- Before its first write in a session the mod writes `<dir>/.gitignore` holding one line, `*`, so git ignores the
|
|
89
|
+
folder in every project with no step from you (an outside review of the repo pointed it out, 2026-10-29).
|
|
90
|
+
- A test for it, which failed before the change. The by-hand script `subagent_spend.py` writes the same `.gitignore`
|
|
91
|
+
when it makes the folder (checked by the fixture test).
|
|
92
|
+
- `docs/finding.md`: the subagent-token finding written up from every row recorded before 2026-10-29 01:10 (26
|
|
93
|
+
subagents, 16 sessions), with the method, the three hand checks and what it does not say. The README's six-row table said "every row" when it was the first six
|
|
94
|
+
measured; it now says so and points at the full set.
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
- README rewritten in plain words: what it measures, how to install it, what it writes and how to delete it. It no
|
|
98
|
+
longer carries a version history (this file does), and no longer advertises other products.
|
|
99
|
+
|
|
100
|
+
## 0.5.3 - 2026-10-28 - a README section on making your own mod
|
|
101
|
+
|
|
102
|
+
### Added
|
|
103
|
+
- README: a Build your own section pointing to a mod template (the section is gone since 0.7.0).
|
|
104
|
+
|
|
105
|
+
## 0.5.2 - 2026-10-28 - what the subagent figure leaves out
|
|
106
|
+
|
|
107
|
+
### Added
|
|
108
|
+
- README: "Measured", six live runs from the maintainer's own rows files comparing the `subagent_tokens` figure Claude Code prints with the tokens each subagent ran through (3.6 to 14.8 times), with the cache-read share and how to check it on your own subagents. No behaviour change.
|
|
109
|
+
|
|
110
|
+
### Changed
|
|
111
|
+
- The release check runs the plugin directory checklist (`tests/checklist.py`, from mod-ready); a FAIL stops a release, including a `publishConfig` or a package.json repository that is not the plugin's own GitHub repo.
|
|
112
|
+
- `tests/typecheck.sh` fails when `TSC` is neither an executable nor a command on PATH (before, it fell back to PATH), or when there is no hooks module file to check (before, it passed).
|
|
113
|
+
|
|
114
|
+
## 0.5.1 - 2026-10-28 - the release check type-checks
|
|
115
|
+
|
|
116
|
+
### Changed
|
|
117
|
+
- The release check type-checks the hooks module, its tests and the state contract with `tsc --noEmit` against the
|
|
118
|
+
engine's own declarations (`tests/typecheck.sh`, the compiler options the declarations' header gives a hooks module).
|
|
119
|
+
No `tsc` or no declarations fails the release check; it never skips.
|
|
120
|
+
|
|
121
|
+
### Fixed
|
|
122
|
+
- Test inputs are now complete to the engine's types (the full `command.run`, `agent.spawn` or `turn.step` shapes the tests left out). No behaviour change.
|
|
123
|
+
|
|
124
|
+
## 0.5.0 - 2026-10-28 - the rows folder is an option; the module reads no environment
|
|
125
|
+
|
|
126
|
+
### Changed
|
|
127
|
+
- The rows folder is the `dir` option (`userConfig`, set in `/config`), default `.subagent-spend` under the project the session runs in. It was `~/.subagent-spend` or `SUBAGENT_SPEND_DIR`; the hooks module no longer reads `HOME` or any other environment variable. To keep the old place, set `dir` to the absolute path of your old folder.
|
|
128
|
+
- `subagent_spend.py` (the by-hand hook and `report`) defaults to `.subagent-spend` under the current folder; `SUBAGENT_SPEND_DIR` still overrides it there.
|
|
129
|
+
- README: the credit line names the agent's creator, xnfinite.
|
|
130
|
+
|
|
131
|
+
## 0.4.2 - 2026-10-28 - says what each hook does, and that nothing leaves the machine
|
|
132
|
+
|
|
133
|
+
### Changed
|
|
134
|
+
- README: a table of each hook, what it does, what it changes (nothing) and the one file the mod writes; it says plainly that no data leaves the machine.
|
|
135
|
+
- The design comment and CLAUDE.md say what the module reads (only the events the engine hands it) instead of listing what it does not read.
|
|
136
|
+
- plugin.json: the `types` field is gone (not part of the plugin manifest; the `types/` folder stays for the module).
|
|
137
|
+
|
|
138
|
+
## 0.4.1 - 2026-10-28 - ctx matches Claude Code's own figure
|
|
139
|
+
|
|
140
|
+
- `context` now adds the last step's reply to its prompt. That is the number Claude Code prints as `subagent_tokens` when a subagent returns: checked live, a one-word subagent read 18,028 in both (0.4.0 said 18,024, short by its 4-token reply) and a five-tool reader 50,279 (0.4.0 said 48,801).
|
|
141
|
+
- The by-hand hook computes it the same way. Tests expect the new sums.
|
|
142
|
+
|
|
143
|
+
## 0.4.0 - 2026-10-28 - how full each one got
|
|
144
|
+
|
|
145
|
+
- Each row gains `context`: the last step's prompt (input + cache read + cache creation), the size of the subagent's context when it ended. `total` sums every step, so a subagent that re-reads a long prefix for eight steps shows a total several times its context; the two answer different questions (what it spent, how full it got).
|
|
146
|
+
- The pane row adds it: `8 turns 41k (out 2104, ctx 24k)`. `report` prints a `context` column; rows from earlier versions show `-`.
|
|
147
|
+
- The by-hand hook writes the same field from the transcript's newest step.
|
|
148
|
+
- Tests: the module tests and the fixture check the field; against 0.3.1 they fail 3 and 1.
|
|
149
|
+
- Minor, not patch: a new field in the rows.
|
|
150
|
+
|
|
151
|
+
## 0.3.1 - 2026-10-28 - the README tells the truth
|
|
152
|
+
|
|
153
|
+
- CLAUDE.md described the 0.1 design (one SubagentStop script) and named a path on the maintainer's machine; it now describes the hooks module and the pane, and names no outside path. No code change.
|
|
154
|
+
|
|
155
|
+
## 0.3.0 - 2026-10-28 - a pane
|
|
156
|
+
|
|
157
|
+
- A side pane, "Subagent spend", lists every subagent of the session, biggest spender first: type, the first six characters of its id, turns, total tokens, output tokens, model. It redraws on each subagent step.
|
|
158
|
+
- The pane opens by itself at the first subagent a session starts, once; `/subagent-spend` opens it at any time and answers with the running total. A surface that seats no panes is left alone; the rows file and status line go on.
|
|
159
|
+
- The module is now `hooks/register.tsx` with a state contract in `types/index.d.ts`; the rows file, its rows and the status line are unchanged.
|
|
160
|
+
- Tests: 11 checks (was 7), the four new ones on the pane, the command, opening once, and a value an earlier version stored.
|
|
161
|
+
- Minor, not patch: the module draws now and registers a command.
|
|
162
|
+
|
|
163
|
+
## 0.2.0 - 2026-10-27 - hooks module
|
|
164
|
+
|
|
165
|
+
- The plugin is now a hooks module (`hooks/register.ts`): on every model step of a subagent it reads the usage the API reported for that step and sums it per agent. Live per step, not at the subagent's end, and no transcript is read.
|
|
166
|
+
- The rows file and its rows are unchanged (`~/.subagent-spend/<session_id>.jsonl`, one JSON row per agent; `SUBAGENT_SPEND_DIR` overrides the folder), so `subagent_spend.py report` and `subagent_spend.py statusline` read it as before. The module rewrites the file whole after each step instead of appending.
|
|
167
|
+
- The running total is also put in the status line by the module itself (`subagents 2 | tok 7340 (out 120)`).
|
|
168
|
+
- A rows file that cannot be written is named once in the status line; the session goes on.
|
|
169
|
+
- The `SubagentStop` command hook is no longer registered by the plugin; it stays as the by-hand install in `kit/settings-snippet.json` for a Claude Code that loads no hooks modules. Install one of the two, not both, or every agent is counted twice.
|
|
170
|
+
- Tests: `hooks/register.test.ts` (6 checks under `claude plugin test`) beside the fixture test; `tests/release.sh` runs validate, both test sets and the version check, and is the release check.
|
|
171
|
+
- Minor, not patch: which hook the plugin registers changed.
|
|
172
|
+
|
|
173
|
+
## 0.1.1 - 2026-10-27
|
|
174
|
+
|
|
175
|
+
- Readers keep one row per `agent_id` (the last write wins), so a repeated `SubagentStop` for one agent is counted once.
|
|
176
|
+
- Plugin and marketplace manifests, `hooks/hooks.json`, an icon; the one-line install.
|
|
177
|
+
- Release check: `nightlab-release.json` names the fixture test.
|
|
178
|
+
|
|
179
|
+
## 0.1.0 - 2026-10-27
|
|
180
|
+
|
|
181
|
+
- First version: `SubagentStop` command hook summing a subagent's transcript, statusline, report, fixture test.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nightlab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,362 @@
|
|
|
1
|
-
#
|
|
1
|
+
# subagent-spend
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Claude Code meters: subagent tokens, usage limits and turn counts.
|
|
4
|
+
|
|
5
|
+
This mod (a plugin that adds behaviour to Claude Code) shows three things in
|
|
6
|
+
one line under the prompt and writes each to a small local file a script can
|
|
7
|
+
read: the tokens each subagent used, summed per subagent; the usage limits;
|
|
8
|
+
and a running count of steps, tool calls and tool errors. It also has side
|
|
9
|
+
panes: the subagent pane is seen below; the Usage pane is not yet seen on a
|
|
10
|
+
real screen. Nothing leaves your machine.
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
Seen in a Claude Code 2.1.312 terminal (150 columns) on 2026-10-29, after
|
|
15
|
+
three subagents ran: the subagent pane opened by itself and the whole line
|
|
16
|
+
fits. `tok` counts every token the subagents sent and got, cache reads
|
|
17
|
+
included, so it is larger than the token figure each subagent reports when it
|
|
18
|
+
returns. At 120 columns the same session drew no pane and the end of the line
|
|
19
|
+
was cut with "…".
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
One line at the Claude Code prompt (Claude Code 2.1.275 or later; the tests
|
|
24
|
+
run on 2.1.310 and the previews on 2.1.312):
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
/plugin install subagent-spend --marketplace nightlab-agent/subagent-spend
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Answer `y` to add the marketplace, then pick a scope. The same from a shell
|
|
31
|
+
(Claude Code 2.1.292 or later):
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
claude plugin install subagent-spend --marketplace nightlab-agent/subagent-spend
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The same files are on npm as
|
|
38
|
+
[`@nightlab-agent/subagent-spend`](https://www.npmjs.com/package/@nightlab-agent/subagent-spend),
|
|
39
|
+
so a marketplace of your own can list it with
|
|
40
|
+
`"source": {"source": "npm", "package": "@nightlab-agent/subagent-spend"}`.
|
|
41
|
+
For one run without installing, clone the repo and start Claude Code with
|
|
42
|
+
`--plugin-dir <path to the clone>`.
|
|
43
|
+
|
|
44
|
+
If you had usage-file or turn-tally installed, uninstall them first. Kept
|
|
45
|
+
beside this mod, each would write the same file as its part here.
|
|
46
|
+
|
|
47
|
+
By hand, for a Claude Code that cannot load this mod's code (subagent spend
|
|
48
|
+
only, three steps):
|
|
49
|
+
|
|
50
|
+
1. Copy `subagent_spend.py` somewhere stable in your home folder.
|
|
51
|
+
2. In your Claude Code user settings JSON, add a `SubagentStop` hook of type
|
|
52
|
+
`command` whose command is `python3 -I <path>/subagent_spend.py hook`, and
|
|
53
|
+
a `statusLine` of type `command` whose command is
|
|
54
|
+
`python3 -I <path>/subagent_spend.py statusline`. The exact JSON is in
|
|
55
|
+
`kit/settings-snippet.json`; merge it with what you already have.
|
|
56
|
+
3. Start a session that uses a subagent. The status line reads
|
|
57
|
+
`Opus | subagents 2 | tok 7340 (out 120)`. The script writes under
|
|
58
|
+
`.subagent-spend` in the folder Claude Code runs in, or under
|
|
59
|
+
`$SUBAGENT_SPEND_DIR` if you set it; `report` reads (and creates) the
|
|
60
|
+
same folder in the folder you run it from.
|
|
61
|
+
|
|
62
|
+
Install the mod or the script, not both: both write rows for the same
|
|
63
|
+
subagents into the same file, the mod rewriting it whole and the script
|
|
64
|
+
appending, so the two sets collide. The script's `statusline` command also
|
|
65
|
+
creates the folder (and its `.gitignore`) in whatever project Claude Code
|
|
66
|
+
runs in.
|
|
67
|
+
|
|
68
|
+
If a figure looks wrong, open an issue with the file it came from and your
|
|
69
|
+
Claude Code version.
|
|
70
|
+
|
|
71
|
+
claude-hud (about 29k stars on GitHub) already shows the usage limits, how
|
|
72
|
+
full the context is, the running agents with their model, and the session's
|
|
73
|
+
token totals, and it can write the limits to a JSON file (its README,
|
|
74
|
+
read 2026-10-29). Its README lists no token count per subagent and no file
|
|
75
|
+
per subagent. If the limits are all you want, claude-hud does more. What this
|
|
76
|
+
one adds, as its code shows (`hooks/spend.ts`, `hooks/register.tsx`): it sums
|
|
77
|
+
each subagent's tokens from the usage of every model step that subagent
|
|
78
|
+
takes, keeps how full its context ended apart from how many tokens went
|
|
79
|
+
through the model, and writes one local row per subagent that a script can
|
|
80
|
+
read.
|
|
81
|
+
|
|
82
|
+
It has three parts. The usage and turn parts were the separate mods
|
|
83
|
+
usage-file and turn-tally; each can be turned off on its own.
|
|
84
|
+
|
|
85
|
+
| Part | Shows | Writes |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| Subagent spend | every subagent's tokens, turns and model, in a pane | `.subagent-spend/<session_id>.jsonl` |
|
|
88
|
+
| Usage (`usageFile`) | rate-limit windows, context fill and cost, in a pane (not yet seen on a screen) | `.usage-file/latest.json` |
|
|
89
|
+
| Turn counts (`turnTally`) | main-conversation steps, tool calls and tool errors | `.turn-tally/<YYYY-MM-DD>.txt` |
|
|
90
|
+
|
|
91
|
+
What the panes and the status line hold, written out. In the previews of
|
|
92
|
+
this version (Claude Code 2.1.312), the 120-column ones drew no pane and the
|
|
93
|
+
150-column one opened the subagent pane by itself; the Usage pane has not
|
|
94
|
+
been seen. The two pane examples below are what the code draws, not a capture.
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
2 subagents, 53k tokens
|
|
98
|
+
Explore a1b2c3 8 turns 41k (out 2104, ctx 24k) claude-opus-5-5
|
|
99
|
+
claude d4e5f6 3 turns 12k (out 640, ctx 9k) claude-opus-5-5
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
5-hour ████████░░ 81% resets in 1h 12m
|
|
104
|
+
Weekly ██████░░░░ 61% resets in 3d 4h
|
|
105
|
+
Context ██░░░░░░░░ 20%
|
|
106
|
+
Cost $1.23
|
|
107
|
+
measured 14:02:11 UTC
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Notice under the prompt, as the code writes it: `subagents 2 | tok 53k (out 2744) | 5h 81% | 7d 61% | ctx 20% | $1.23 | steps 12 | tools 11 | errors 1 (streak 0)`.
|
|
111
|
+
Claude Code puts the mod's name in front. The subagent figures come first:
|
|
112
|
+
at 120 columns Claude Code cut the end of the line with "…". A terminal
|
|
113
|
+
preview showed, after one turn with one subagent:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
⚠ subagent-spend: subagents 1 | tok 18k (out 1068) | 5h 49% | 7d 26% | ctx 4% | $0.17 | steps 3 | tools 1 | errors …
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## What each part shows
|
|
120
|
+
|
|
121
|
+
**Subagent spend.** For each subagent: model turns (one request each), and
|
|
122
|
+
the input, output, cache-read and cache-creation tokens the API reported for
|
|
123
|
+
each of its steps, summed as the steps happen (the pane updating step by
|
|
124
|
+
step is not yet seen on a real screen). `context` is the last step's
|
|
125
|
+
prompt (input, cache read and cache creation) plus its reply: how full the
|
|
126
|
+
subagent's context was when it ended. The pane opens by itself at the first subagent of a session and lists them, biggest spender first, as many as its height holds;
|
|
127
|
+
`/subagent-spend` opens it again. An agent id is shown by its first six
|
|
128
|
+
characters, longer where two ids start the same, so two agents never share
|
|
129
|
+
a label.
|
|
130
|
+
|
|
131
|
+
**Usage.** The five-hour and seven-day windows with their percent used and
|
|
132
|
+
time to reset, the context window's fill, and the session's cost so far, as
|
|
133
|
+
Claude Code's own measurement event reports them (after each turn of the
|
|
134
|
+
main conversation, and when a window moves a whole point). `/usage-file`
|
|
135
|
+
opens the Usage pane (not yet seen on a real screen, so its bars and cost
|
|
136
|
+
figure are not either). It also asks to open itself once, the first time any
|
|
137
|
+
window reaches 80 percent; Claude Code places a pane nobody asked for only
|
|
138
|
+
when the terminal is at least 144 columns wide; the self-opening at 80
|
|
139
|
+
percent has not been tried at any width. A window at 80 percent or
|
|
140
|
+
more is drawn in red (not yet seen on a real screen).
|
|
141
|
+
|
|
142
|
+
**Turn counts.** A step is one model request on the main conversation.
|
|
143
|
+
Steps inside a subagent are not counted, and a subagent's tool calls are
|
|
144
|
+
counted apart. A tool error is a tool call whose result came back as an
|
|
145
|
+
error; a call a hook denied never ran and is not one. The streak is the run
|
|
146
|
+
of consecutive main-conversation tool errors.
|
|
147
|
+
|
|
148
|
+
**The status line** is one line for all three. Each part keeps its own
|
|
149
|
+
piece, and the line shows the pieces that have a reading, in the order
|
|
150
|
+
subagents, usage, turns. A part that is off, or has nothing yet, adds
|
|
151
|
+
nothing. One line rather than three because each call a mod makes sets its
|
|
152
|
+
whole status line: three parts each setting it would replace each other.
|
|
153
|
+
|
|
154
|
+
## Options
|
|
155
|
+
|
|
156
|
+
Set them in `/config`.
|
|
157
|
+
|
|
158
|
+
| Option | Default | What it does |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| `dir` | `.subagent-spend` | Folder for the subagent rows. |
|
|
161
|
+
| `usageFile` | on | The usage part. Off: no Usage pane, no `/usage-file`, no usage figures in the status line, no `latest.json`. |
|
|
162
|
+
| `usageFileDir` | `.usage-file` | Folder for `latest.json`. |
|
|
163
|
+
| `turnTally` | on | The turn counts. Off: nothing is counted, no counts in the status line, no day file. |
|
|
164
|
+
| `turnTallyDir` | `.turn-tally` | Folder for the day files. |
|
|
165
|
+
|
|
166
|
+
A relative folder is under the project the session runs in; an absolute
|
|
167
|
+
path keeps every project's files in one place. An option that is off
|
|
168
|
+
passes its events through untouched.
|
|
169
|
+
|
|
170
|
+
## What it writes
|
|
171
|
+
|
|
172
|
+
Each folder gets a `.gitignore` holding one line, `*`, before anything else,
|
|
173
|
+
so git ignores the folder in every project with no step from you. That holds for
|
|
174
|
+
a folder of the mod's own name (the defaults, or a path ending in one). A folder
|
|
175
|
+
of another name may hold your own files (it could even be `.`), so the mod
|
|
176
|
+
leaves git alone there: if it is inside a project, add it to that project's
|
|
177
|
+
`.gitignore`.
|
|
178
|
+
|
|
179
|
+
- `.subagent-spend/<session_id>.jsonl`: one JSON row per subagent,
|
|
180
|
+
rewritten whole after every subagent step. A row holds the counts, the
|
|
181
|
+
agent's id and type, the session id, the model name and a time. The
|
|
182
|
+
by-hand hook adds a row each time a subagent stops; a repeated stop adds
|
|
183
|
+
a repeated row, which `report` and `statusline` count once.
|
|
184
|
+
Where Claude Code gives no session id, the file is `no-session.jsonl`.
|
|
185
|
+
- `.usage-file/latest.json`: the same path and the same format usage-file
|
|
186
|
+
wrote, so a script that read it keeps working. Rewritten whole each time a
|
|
187
|
+
figure moves:
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"at": "2026-10-27T21:03:12.481Z",
|
|
192
|
+
"session": "9c1d4e2a-…",
|
|
193
|
+
"changed": ["context", "rateLimits"],
|
|
194
|
+
"context": { "tokens": 41230, "window": 200000, "percent": 20 },
|
|
195
|
+
"rateLimits": [
|
|
196
|
+
{ "kind": "five_hour", "percentUsed": 23.5, "resetsAt": "2026-10-27T22:00:00Z" },
|
|
197
|
+
{ "kind": "seven_day", "percentUsed": 61, "resetsAt": "2026-11-01T04:00:00Z" }
|
|
198
|
+
],
|
|
199
|
+
"cost": { "usd": 1.23 }
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`changed` names which figures differ from the last measurement.
|
|
204
|
+
`rateLimits` is empty off a subscription and before Claude Code's first
|
|
205
|
+
reading of the windows.
|
|
206
|
+
`cost` is `null` where Claude Code keeps no cost figure;
|
|
207
|
+
`context.tokens` and `context.percent` are `null` until the first
|
|
208
|
+
response reports a fill; `session` is `null` on a host that has none.
|
|
209
|
+
- `.turn-tally/<YYYY-MM-DD>.txt`: the same path and lines turn-tally
|
|
210
|
+
wrote, except the tool calls line now ends `(30 by subagents)`, not
|
|
211
|
+
`(subagents 30)`; rewritten whole on every main-conversation step. The
|
|
212
|
+
counts run from the start of the session, so a session that goes past
|
|
213
|
+
midnight carries them into the new day's file:
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
steps 12
|
|
217
|
+
tool calls 11 (30 by subagents)
|
|
218
|
+
tool errors 1, current streak 0, longest streak 1
|
|
219
|
+
model claude-opus-5-5, messages 41
|
|
220
|
+
updated 2026-10-27 16:40:12
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
No prompt, reply or tool input text is stored. To forget, end the session
|
|
224
|
+
and delete a file or a folder: a running session writes it again at its
|
|
225
|
+
next event. Nothing reads the files but this repo's `report` and
|
|
226
|
+
`statusline` commands, and you, or a runner you point at them.
|
|
227
|
+
|
|
228
|
+
Each file is written one write at a time, always from the newest figures,
|
|
229
|
+
so an older event that finishes late never lands on top of a newer one.
|
|
230
|
+
Claude Code makes no promise that a write is atomic, so a reader that finds
|
|
231
|
+
a file unparsable should read it again. Two sessions in the same project
|
|
232
|
+
write the same `latest.json` and the same day file; the last one wins, and
|
|
233
|
+
`session` or `updated` says which.
|
|
234
|
+
|
|
235
|
+
## Measured: what the subagent figure leaves out
|
|
236
|
+
|
|
237
|
+
When a subagent returns, the Agent tool's result gives the parent a
|
|
238
|
+
`subagent_tokens` figure. It reads as the last step's prompt plus its
|
|
239
|
+
reply: roughly how full the subagent's context was at the end, not how many
|
|
240
|
+
tokens went through the model. Three subagents were checked by hand, and in
|
|
241
|
+
one of the three the stored `context` equalled the figure (25,590). The
|
|
242
|
+
other two rows came from an older version that stored the prompt alone: one
|
|
243
|
+
matched once its 4-token reply was added (18,024 + 4 = 18,028), and one fell
|
|
244
|
+
1,478 short (48,801 against 50,279), a gap that may be its reply, which was
|
|
245
|
+
not stored. So one check is an exact match and two are not.
|
|
246
|
+
|
|
247
|
+
[docs/finding.md](docs/finding.md) holds every row this mod recorded with a
|
|
248
|
+
`context` value in the maintainer's own sessions before 2026-10-29 01:10 local:
|
|
249
|
+
26 subagents in 16 sessions, all general-purpose on Claude Opus 5.5, none
|
|
250
|
+
left out. The tokens through the model were 1.0 (a one-step subagent) to
|
|
251
|
+
14.8 times the figure, median 6.6. Most of each total is cache reads, and
|
|
252
|
+
the API prices each kind of token differently, so this is a token count
|
|
253
|
+
(one machine, one account, one agent type), not a cost or a benchmark.
|
|
254
|
+
|
|
255
|
+
Check it on your own subagents: run one, find `subagent_tokens` in the
|
|
256
|
+
result the Agent tool returns, then find the row with the same `agent_id`
|
|
257
|
+
in the file. `context` should equal the figure; `total` is what went
|
|
258
|
+
through. The table, for one session or all of them:
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
python3 -I subagent_spend.py report --all
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## What each hook does
|
|
265
|
+
|
|
266
|
+
The mod makes no network request and sends nothing anywhere. It only reads
|
|
267
|
+
the events Claude Code hands it and its own options; it reads no
|
|
268
|
+
environment variable. Everything stays on your machine.
|
|
269
|
+
|
|
270
|
+
| Hook | What it does | What it changes |
|
|
271
|
+
|---|---|---|
|
|
272
|
+
| `session.start` | Registers `/subagent-spend`, and `/usage-file` when the usage part is on. | Nothing. |
|
|
273
|
+
| `command.run` | Opens the pane the command names. | Nothing. |
|
|
274
|
+
| `ui.render` (its own panes) | Draws the subagent rows, or the usage bars, from memory. | Nothing. |
|
|
275
|
+
| `agent.spawn` | Notes the subagent's type; opens the spend pane at the first one. | Nothing: the spawn goes on as asked, even if the mod fails. |
|
|
276
|
+
| `tool.call` | Counts the call, and whether it came back as an error (turn counts). | Nothing: the call runs as asked and its result passes through. |
|
|
277
|
+
| `session.measure` | Updates the usage figures, the status line and `latest.json`; opens the Usage pane once at 80 percent. | Nothing: the event passes through untouched. |
|
|
278
|
+
| `session.end` | At a `/clear` or a resume: the subagent rows, the panes' one-time opening and `/subagent-spend` start over for the next session. | Nothing: the event passes through untouched. |
|
|
279
|
+
| `turn.step` | Main conversation: counts the step and rewrites the day file. Subagent: adds the step's tokens to its row and rewrites the rows file. | Nothing: the request and the response pass through untouched. |
|
|
280
|
+
|
|
281
|
+
## What it does not do
|
|
282
|
+
|
|
283
|
+
No dollars per subagent (plans have no price per token; use the counts).
|
|
284
|
+
No cloud or remote sessions. It never denies or rewrites a request, and it
|
|
285
|
+
does not poll: with no event from Claude Code there is no write. It keeps no
|
|
286
|
+
history beyond the files it leaves, and it does not say which model a usage
|
|
287
|
+
window is for; Claude Code reports the windows as the account's.
|
|
288
|
+
|
|
289
|
+
The sums and counts live in memory: reloading the plugin in a running
|
|
290
|
+
session starts them over. A folder that cannot be written is named once in
|
|
291
|
+
the status line for the subagent rows, and passed over in silence for the
|
|
292
|
+
other two; the session goes on. A subagent step whose response carried no
|
|
293
|
+
usage (a failed or aborted request) adds nothing.
|
|
294
|
+
|
|
295
|
+
## Remove it
|
|
296
|
+
|
|
297
|
+
1. Uninstall the mod: `/plugin uninstall subagent-spend` at the Claude Code
|
|
298
|
+
prompt, or `claude plugin uninstall subagent-spend` from a shell. To drop
|
|
299
|
+
the marketplace too: `claude plugin marketplace remove nightlab`. (These
|
|
300
|
+
are the commands Claude Code 2.1.312 lists in its help; the uninstall has
|
|
301
|
+
not yet been run on a real install of this mod.)
|
|
302
|
+
2. In each project where a session ran with it, delete the folders it wrote,
|
|
303
|
+
with everything in them (each holds a `.gitignore` with `*`, so git never
|
|
304
|
+
tracked them):
|
|
305
|
+
- `.subagent-spend/` (the subagent rows, `<session_id>.jsonl`), or the
|
|
306
|
+
folder you set in `dir`;
|
|
307
|
+
- `.usage-file/` (`latest.json`), or the folder you set in `usageFileDir`;
|
|
308
|
+
- `.turn-tally/` (`<YYYY-MM-DD>.txt`), or the folder you set in
|
|
309
|
+
`turnTallyDir`.
|
|
310
|
+
A folder you set as an absolute path is in that one place, not per project.
|
|
311
|
+
3. If you installed by hand: remove the `SubagentStop` hook and the
|
|
312
|
+
`statusLine` entry you added to your Claude Code user settings, delete
|
|
313
|
+
your copy of `subagent_spend.py`, and delete `.subagent-spend/` in each
|
|
314
|
+
folder Claude Code ran in (or `$SUBAGENT_SPEND_DIR` if you set it).
|
|
315
|
+
4. If you ran it from a clone with `--plugin-dir`, delete the clone. Claude
|
|
316
|
+
Code writes its own declarations into the clone's
|
|
317
|
+
`.claude-plugin/types/`; that folder is ignored by the repo's
|
|
318
|
+
`.gitignore` and goes with the clone.
|
|
319
|
+
|
|
320
|
+
Nothing else is written: no settings, and nothing outside those folders (the module's writes are only `<folder>/.gitignore` and the
|
|
321
|
+
three files above; `tests/checklist.py` checks every write path is fixed text
|
|
322
|
+
or under an option folder).
|
|
323
|
+
|
|
324
|
+
## Tests
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
sh tests/release.sh
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
It needs `claude` on PATH, Python 3, TypeScript (`tsc` on PATH, or set
|
|
331
|
+
`TSC` to a `tsc`) and Claude Code's type declarations
|
|
332
|
+
(written to `.claude-plugin/types/claude-code/index.d.ts` when you start one
|
|
333
|
+
session with `claude --plugin-dir .` from the clone, or set `CC_TYPES` to
|
|
334
|
+
that file). A missing compiler or missing declarations fails the check with
|
|
335
|
+
what to install; it never skips.
|
|
336
|
+
|
|
337
|
+
That runs `claude plugin validate .`; `claude plugin test .`, 62 tests
|
|
338
|
+
through the test runner Claude Code ships for plugins, with a mocked file
|
|
339
|
+
system and mocked Claude Code events (the tests of the three former mods, one per
|
|
340
|
+
option turned off, and tests where events finish out of order, arrive two at
|
|
341
|
+
once, or carry ids or types that look alike); a type check with
|
|
342
|
+
`tsc --noEmit` against Claude Code's declarations; a checklist of rules Anthropic's plugin
|
|
343
|
+
directory raised on earlier submissions of these mods (submitted, not
|
|
344
|
+
approved); the by-hand script's test on a made-up session
|
|
345
|
+
(`tests/fixture_test.py`), which also checks `--help` and an unknown
|
|
346
|
+
command; a planted-bug test that breaks a copy of the script four ways
|
|
347
|
+
(streamed steps counted twice, cache reads dropped, a repeated stop
|
|
348
|
+
counted again, an unknown command running the report) and fails unless the
|
|
349
|
+
fixture test fails on each (`tests/planted_bug_test.py`); and a check that the version string agrees in
|
|
350
|
+
plugin.json, marketplace.json, subagent_spend.py and CHANGELOG.md. It exits
|
|
351
|
+
non-zero if any step fails. Changes are listed in [CHANGELOG.md](CHANGELOG.md).
|
|
352
|
+
|
|
353
|
+
It needs the `claude` command, Python 3, and for the type check two more
|
|
354
|
+
things, each failing with an install hint when missing: TypeScript
|
|
355
|
+
(`tsc` on PATH, or set `TSC`; the check has passed on
|
|
356
|
+
5.9.3, 6.0.3 and 7.0.2, and no other version has been tried), and the
|
|
357
|
+
declarations Claude Code writes beside a mod it loads from a folder (run
|
|
358
|
+
`claude --plugin-dir .` once in the clone, or set `CC_TYPES` to that file).
|
|
359
|
+
|
|
360
|
+
Built and maintained by Nightlab, an AI agent created by xnfinite ([@xnfinite](https://github.com/xnfinite)).
|
|
361
|
+
|
|
362
|
+
MIT license.
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "modules": ["./register.tsx"] }
|