tokengolf 0.3.0 → 0.4.0
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/.husky/pre-commit +4 -0
- package/.prettierignore +2 -0
- package/.prettierrc +6 -0
- package/.vscode/settings.json +15 -0
- package/CHANGELOG.md +254 -0
- package/CLAUDE.md +136 -10
- package/README.md +89 -47
- package/assets/demo-hud.png +0 -0
- package/assets/scorecard.png +0 -0
- package/dist/cli.js +790 -103
- package/docs/assets/demo-hud.png +0 -0
- package/docs/assets/scorecard.png +0 -0
- package/docs/assets/tokengolf-bg-min.jpg +0 -0
- package/docs/index.html +1080 -0
- package/eslint.config.js +39 -0
- package/hooks/post-tool-use-failure.js +27 -0
- package/hooks/post-tool-use.js +11 -7
- package/hooks/pre-compact.js +9 -3
- package/hooks/session-end.js +168 -42
- package/hooks/session-start.js +31 -11
- package/hooks/session-stop.js +6 -2
- package/hooks/statusline.sh +16 -7
- package/hooks/stop.js +27 -0
- package/hooks/subagent-start.js +27 -0
- package/hooks/user-prompt-submit.js +8 -6
- package/package.json +16 -3
- package/src/cli.js +23 -6
- package/src/components/ActiveRun.js +76 -24
- package/src/components/ScoreCard.js +132 -37
- package/src/components/StartRun.js +156 -53
- package/src/components/StatsView.js +89 -37
- package/src/lib/__tests__/score.test.js +596 -0
- package/src/lib/cost.js +84 -21
- package/src/lib/demo.js +186 -0
- package/src/lib/install.js +92 -62
- package/src/lib/score.js +433 -136
- package/src/lib/store.js +11 -11
- package/.claude/settings.local.json +0 -36
package/.prettierignore
ADDED
package/.prettierrc
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
3
|
+
"editor.formatOnSave": true,
|
|
4
|
+
"editor.codeActionsOnSave": {
|
|
5
|
+
"source.fixAll.eslint": "explicit"
|
|
6
|
+
},
|
|
7
|
+
"[javascript]": {
|
|
8
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
9
|
+
},
|
|
10
|
+
"[javascriptreact]": {
|
|
11
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
12
|
+
},
|
|
13
|
+
"prettier.requireConfig": true,
|
|
14
|
+
"eslint.useFlatConfig": true
|
|
15
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
TokenGolf patch notes — what changed, what it measures, and why the mechanic exists.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- StatusLine HUD effort label now reads exclusively from live `settings.json` — `/model` changes (High, Low, Max) reflect immediately without requiring a new session
|
|
11
|
+
- Medium effort no longer shown in HUD or scorecard — it's the default, so it's omitted (same as not annotating Sonnet as "normal difficulty")
|
|
12
|
+
- Scorecard `modelSuffix` no longer suppressed effort when stored as 'medium' — effort label now shows whenever explicitly stored in run state
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## [0.3.x] — 2026-03-09
|
|
17
|
+
|
|
18
|
+
### New Character Class
|
|
19
|
+
|
|
20
|
+
**⚜️ Paladin** — Opus running in plan mode. Thinks before it acts. The strategic archetype: slower, more deliberate, higher cost floor than Warlock but more structured output. Budget presets match Opus (same model, different playstyle).
|
|
21
|
+
|
|
22
|
+
### New Achievements
|
|
23
|
+
|
|
24
|
+
#### Prompting Skill
|
|
25
|
+
These measure *how* you communicate, not just how much. The same task done in 1 prompt vs 20 prompts represents fundamentally different skill levels — and different costs.
|
|
26
|
+
|
|
27
|
+
- **🥊 One Shot** — Won in a single prompt. The platonic ideal: perfectly specified the problem, Claude solved it, done. Rare on complex tasks. Chasing this forces you to front-load your context instead of iterating.
|
|
28
|
+
- **💬 Conversationalist** — 20+ prompts in one run. Not necessarily bad — complex exploratory work takes conversation. But if you're seeing this often, it's a signal to consolidate before prompting.
|
|
29
|
+
- **🤐 Terse** — ≤3 prompts, ≥10 tool calls. You said little and Claude did a lot. Strong delegation: clear brief, trusted execution, minimal correction loop.
|
|
30
|
+
- **🪑 Backseat Driver** — 15+ prompts, fewer than 1 tool call per prompt. High prompt-to-action ratio. You're steering more than executing — which can indicate under-specified initial prompts or excessive check-ins.
|
|
31
|
+
- **🏗️ High Leverage** — 5+ tool calls per prompt (≥2 prompts). Each prompt is producing significant automated work. The agentic sweet spot: you describe intent, Claude handles implementation.
|
|
32
|
+
|
|
33
|
+
#### Tool Mastery
|
|
34
|
+
What you reach for reveals how you think. These reward deliberate tool use and recognize distinct working styles.
|
|
35
|
+
|
|
36
|
+
- **👁️ Read Only** — Won without a single Edit or Write call. Pure exploration, analysis, or diagnosis. The run existed entirely in understanding — no mutations. Rarer than it sounds on most tasks.
|
|
37
|
+
- **🔪 Surgeon** — 1–3 Edit calls, completed under budget. Precise, targeted changes. Found the exact lines, changed them, done. The opposite of spray-and-pray editing.
|
|
38
|
+
- **🔍 Scout** — ≥60% of tool calls were Reads (≥5 total). Heavily investigation-weighted run. You understood the codebase before touching it. Good discipline on unfamiliar repos.
|
|
39
|
+
- **🐚 Bash Warrior** — 10+ Bash calls, Bash comprising ≥50% of tool usage. Shell-native approach. Either running tests, pipeline commands, or automating things other people would do by hand.
|
|
40
|
+
- **✏️ Editor** — 10+ Edit calls. High-edit run — significant code mutation. Normal on large refactors or multi-file features, a flag on simple tasks.
|
|
41
|
+
- **🧰 Toolbox** — 5+ distinct tool types used. Broad-spectrum run: read, write, bash, search, web, etc. Signals either a complex task or an exploratory style.
|
|
42
|
+
|
|
43
|
+
#### Cost per Prompt
|
|
44
|
+
Spend ÷ prompts. A ratio that catches two distinct failure modes: prompts that do nothing (low cost, high count) and prompts that burn (high cost, low count).
|
|
45
|
+
|
|
46
|
+
- **💲 Cheap Shots** — Under $0.01 per prompt (≥3 prompts). Extremely token-efficient per exchange. Either very focused prompts or a very cheap model.
|
|
47
|
+
- **🍷 Expensive Taste** — Over $0.50 per prompt (≥3 prompts). Each prompt is generating significant cost. Fine if the output justifies it — a red flag if you're asking simple questions. Fires on both won *and* died runs: it's a burn pattern, not a verdict.
|
|
48
|
+
|
|
49
|
+
#### Time
|
|
50
|
+
Session duration from `startedAt` to `endedAt`. Rewards focus and recognizes sustained work.
|
|
51
|
+
|
|
52
|
+
- **⏱️ Speedrun** — Won in under 5 minutes. You knew exactly what needed doing and did it. Near-impossible on anything requiring reading, common on small precise fixes.
|
|
53
|
+
- **🏃 Marathon** — Session over 60 minutes. Sustained work. Either a genuinely complex task or a sign the session scope crept.
|
|
54
|
+
- **🫠 Endurance** — Session over 3 hours. Deep work. Or a debugging session that got away from you.
|
|
55
|
+
|
|
56
|
+
#### Subagent Tracking *(requires SubagentStart hook)*
|
|
57
|
+
Claude Code spawns subagents for parallel work, web fetches, and background tasks. These track whether you're running solo or orchestrating a team.
|
|
58
|
+
|
|
59
|
+
- **🐺 Lone Wolf** — Completed with zero subagents spawned. Everything stayed in one context, one model, one thread. Surgical and contained.
|
|
60
|
+
- **📡 Summoner** — 5+ subagents spawned. Significant orchestration — Claude is delegating work to parallel agents. High leverage, higher cost.
|
|
61
|
+
- **🪖 Army of One** — 10+ subagents spawned *and* under 50% budget used. Orchestrated aggressively and stayed efficient. The hardest subagent achievement: scale without spend.
|
|
62
|
+
|
|
63
|
+
#### Tool Reliability *(requires PostToolUseFailure hook)*
|
|
64
|
+
Failed tool calls aren't free — each failure costs tokens to process, explain, and retry. These track reliability under fire.
|
|
65
|
+
|
|
66
|
+
- **✅ Clean Run** — Zero failed tool calls with ≥5 total tool uses. Every tool call landed. Precise paths, correct syntax, no retries. The floor here is ≥5 total so a 1-tool run doesn't trivially qualify.
|
|
67
|
+
- **🐂 Stubborn** — 10+ failed tool calls, still won. Things kept breaking and you kept going. Either a hostile environment (flaky tests, bad paths) or persistence in the face of compounding errors. You won anyway.
|
|
68
|
+
|
|
69
|
+
#### Turn Discipline *(requires Stop hook)*
|
|
70
|
+
`turnCount` tracks how many times Claude responded; `promptCount` tracks your messages. The ratio reveals the agentic depth of each exchange.
|
|
71
|
+
|
|
72
|
+
- **🤖 Agentic** — 3+ Claude turns per user prompt. Each prompt is triggering multi-step autonomous work — Claude is planning, executing, checking, and continuing without waiting for you. High trust, high autonomy.
|
|
73
|
+
- **🐕 Obedient** — Exactly 1 Claude turn per prompt (≥3 prompts). Claude responds once and waits. Very human-paced. Great for exploratory sessions; on automated tasks it may mean Claude isn't being trusted to run.
|
|
74
|
+
|
|
75
|
+
#### Death Marks
|
|
76
|
+
Cause-of-death taxonomy. These fire on bust only — they name what killed the run so the pattern is visible.
|
|
77
|
+
|
|
78
|
+
- **💥 Blowout** — Spent 2× your committed budget. Didn't just go over — significantly over. Either severe scope creep, a runaway agentic loop, or a fundamentally mis-scoped task.
|
|
79
|
+
- **😭 So Close** — Died within 10% of budget. Almost made it. The budget was right, the task was right — execution was slightly off. The most fixable death.
|
|
80
|
+
- **🔨 Tool Happy** — Died with 30+ tool calls. High activity, no completion. Signals either a sprawling unfocused approach or a task that needed to be broken into smaller runs.
|
|
81
|
+
- **🪦 Silent Death** — Died with ≤2 prompts. Barely started before busting. Usually means the very first prompt triggered something expensive — a large file read, a multi-file edit, an ultrathink call on a tight budget.
|
|
82
|
+
- **🤡 Fumble** — Died with 5+ failed tool calls. Errors compounded until the budget ran out. Bad paths, wrong syntax, environment issues — the run became error recovery rather than task completion.
|
|
83
|
+
|
|
84
|
+
### New Hooks
|
|
85
|
+
|
|
86
|
+
- **PostToolUseFailure** — Fires after any tool error. Increments `failedToolCalls` in run state. Powers Clean Run, Stubborn, Fumble. 15-line hook; synchronous JSON I/O.
|
|
87
|
+
- **SubagentStart** — Fires when Claude spawns a subagent. Increments `subagentSpawns`. Powers Lone Wolf, Summoner, Army of One.
|
|
88
|
+
- **Stop** — Fires after each Claude response turn. Increments `turnCount`. Combined with `promptCount` enables turn/prompt ratio achievements.
|
|
89
|
+
|
|
90
|
+
### Infrastructure
|
|
91
|
+
- **Vitest test suite** — 83 tests across all achievements and pure score functions. Catches silent regressions in `calculateAchievements()` — the function is complex enough that edge cases (null budget, missing optional fields, death mark isolation) need explicit coverage.
|
|
92
|
+
- **Husky pre-commit** — format:check → lint → build → test. Nothing broken ships.
|
|
93
|
+
- **ESLint + Prettier** — flat config, ecmaVersion latest, JSX via espree (no babel), formatOnSave wired to same `.prettierrc` as CI.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## [0.3.0] — 2026-02-xx
|
|
98
|
+
|
|
99
|
+
### Auto Scorecard on Exit
|
|
100
|
+
|
|
101
|
+
**SessionEnd hook** replaces the manually-triggered `tokengolf win` for clean exits. When you type `/exit` in Claude Code, the hook fires, scans your session transcripts for cost and ultrathink data, determines win/death/faint status, saves the run, and renders the ANSI scorecard directly in the terminal. Zero friction — play, exit, see your score.
|
|
102
|
+
|
|
103
|
+
The Stop hook (which Claude Code *does* fire) doesn't include `total_cost_usd` in its payload — cost must come from transcript parsing. SessionEnd is authoritative.
|
|
104
|
+
|
|
105
|
+
### Live HUD
|
|
106
|
+
|
|
107
|
+
**StatusLine** shows a persistent status line in every Claude Code session after `tokengolf install`:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
───────────────
|
|
111
|
+
⛳ implement pagination | 💎 $0.1203/$0.50 24% | LEGENDARY | 🪶 38% | ⚔️ Sonnet·High | Floor 1/5
|
|
112
|
+
───────────────
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Context window load is tracked live because it's a real resource that affects how Claude reasons:
|
|
116
|
+
- **🪶 green** (50–74%) — traveling light, context is healthy
|
|
117
|
+
- **🎒 yellow** (75–89%) — getting heavy, consider your next moves
|
|
118
|
+
- **📦 red** (90%+) — overencumbered, compaction imminent or needed
|
|
119
|
+
|
|
120
|
+
### Fainted / Rest Mechanic
|
|
121
|
+
|
|
122
|
+
Hitting Claude Code's usage limits mid-run doesn't kill the run — it *faints* it. The run state is preserved with `fainted: true`, the scorecard shows **💤 FAINTED — Run Continues**, and the next session picks up where you left off. `sessionCount` increments each time.
|
|
123
|
+
|
|
124
|
+
This maps to the real experience: usage limits aren't failure, they're an interruption. The run outcome is determined when you win or bust, not when the context window ends.
|
|
125
|
+
|
|
126
|
+
### Effort Level
|
|
127
|
+
|
|
128
|
+
Effort is now a wizard step — selected before the budget. It affects how Claude Code allocates its thinking:
|
|
129
|
+
- **Haiku** — skips the step entirely (effort is baked into the model)
|
|
130
|
+
- **Sonnet** — Low / Medium (default) / High
|
|
131
|
+
- **Opus** — Low / Medium (default) / High / Max
|
|
132
|
+
|
|
133
|
+
Max effort is Opus-only; the API returns an error on other models. Fast mode (`/fast` in Claude Code) is tracked separately — auto-detected from `~/.claude/settings.json`, not a wizard step.
|
|
134
|
+
|
|
135
|
+
### Context Window Achievements (Gear)
|
|
136
|
+
|
|
137
|
+
The PreCompact hook fires before any compaction event and records whether it was manual or automatic, and the context window percentage at the time. This powers four achievements that measure how you manage context load — a real skill that affects both cost and reasoning quality:
|
|
138
|
+
|
|
139
|
+
- **📦 Overencumbered** — Context auto-compacted during the run. Claude Code had to compact because you didn't. Not necessarily bad on long runs, but a signal on short ones.
|
|
140
|
+
- **🎒 Traveling Light** — You manually compacted at ≤50% context. Proactive housekeeping — you cleared context before it became a problem.
|
|
141
|
+
- **🪶 Ultralight** — Manual compact at ≤40% context. Aggressive context discipline. You're keeping the working set small deliberately.
|
|
142
|
+
- **🥷 Ghost Run** — Manual compact at ≤30% context. Surgical context management. The context barely registered before you cleared it.
|
|
143
|
+
|
|
144
|
+
### Effort & Fast Mode Achievements
|
|
145
|
+
|
|
146
|
+
- **🏎️ Speedrunner** — Low effort, completed under budget. You dialed back Claude's thinking and still shipped. Efficiency through restraint.
|
|
147
|
+
- **🏋️ Tryhard** — High or Max effort, LEGENDARY efficiency (<25% budget used). You ran expensive settings and still came in way under. The skill ceiling achievement for roguelike mode.
|
|
148
|
+
- **👑 Archmagus** — Opus at Max effort, completed. You ran the most expensive possible configuration and finished. No efficiency requirement — just completion.
|
|
149
|
+
- **⛈️ Lightning Run** — Opus fast mode, completed under budget. Fast mode roughly doubles cost per token. Finishing under budget requires either a very generous budget or very focused prompting.
|
|
150
|
+
- **🎰 Daredevil** — Opus fast mode, LEGENDARY efficiency. 2× cost model, <25% budget used. The rarest fast mode achievement.
|
|
151
|
+
|
|
152
|
+
### Rest & Recovery Achievements
|
|
153
|
+
|
|
154
|
+
- **🔥 No Rest for the Wicked** — Completed in a single session without fainting. Clean run start to finish.
|
|
155
|
+
- **🏕️ Made Camp** — Completed across multiple sessions. You hit limits, rested, came back. The run survived the interruption.
|
|
156
|
+
- **🧟 Came Back** — Fainted at least once and still won. Specifically marks the resilience: the run was interrupted by the usage limit and you finished it anyway.
|
|
157
|
+
|
|
158
|
+
### Ultrathink
|
|
159
|
+
|
|
160
|
+
`ultrathink` is not a slash command. Write the word in natural language — *"ultrathink: is this the right architecture?"* or *"can you ultrathink through the tradeoffs?"* — and Claude activates extended thinking mode with a large token budget.
|
|
161
|
+
|
|
162
|
+
Extended thinking tokens bill at full output rate. A single ultrathink on Sonnet can cost $0.50–2.00 depending on depth. The mechanic creates a genuine in-session decision: **do I burn tokens thinking, or do I trust my instincts and try?** Budget pressure makes that choice real.
|
|
163
|
+
|
|
164
|
+
Detection: thinking blocks appear in session JSONL transcripts as `{"type":"thinking"}` content blocks. `parseThinkingFromTranscripts()` scans all session files (same multi-file scan used for cost), counts invocations (assistant turns with at least one thinking block), and estimates tokens from character count ÷ 4. Shown on the scorecard with a `~` prefix to be honest about the approximation.
|
|
165
|
+
|
|
166
|
+
Achievements:
|
|
167
|
+
- **🔮 Spell Cast** — Used extended thinking and won. Informational — marks that ultrathink was in play.
|
|
168
|
+
- **🧮 Calculated Risk** — Ultrathink + LEGENDARY efficiency (<25% budget). You paid for deep thinking and came in well under. The ideal outcome: the thinking paid off.
|
|
169
|
+
- **🌀 Deep Thinker** — 3+ ultrathink invocations, completed under budget. Committed to the approach across multiple moments. Each invocation was a deliberate bet.
|
|
170
|
+
- **🤫 Silent Run** — Won with zero ultrathink at SOLID or better efficiency. Pure prompting discipline. You solved it without reaching for the spell. A counterweight to Spell Cast — the game rewards both using ultrathink well *and* not needing it.
|
|
171
|
+
- **🤦 Hubris** *(death mark)* — Used ultrathink and busted anyway. The spell didn't save you. Hubris is the only achievement that fires on a lost run — it names the cause of death. You reached for expensive thinking on a tight budget and paid for it.
|
|
172
|
+
|
|
173
|
+
### Model-Aware Budget Presets
|
|
174
|
+
|
|
175
|
+
The original budget tiers ($0.10 / $0.30 / $1.00 / $3.00) were model-agnostic. A $0.10 Diamond budget on Opus is instant death in one prompt. The same $0.10 on Haiku is a reasonable small task. The tiers had no relationship to actual model economics.
|
|
176
|
+
|
|
177
|
+
New presets calibrated to each class, anchored to ~$0.75/task Sonnet average from Anthropic's $6/day Claude Code benchmark:
|
|
178
|
+
|
|
179
|
+
| Tier | Haiku 🏹 | Sonnet ⚔️ | Opus 🧙 |
|
|
180
|
+
|------|---------|---------|--------|
|
|
181
|
+
| 💎 Diamond | $0.15 | $0.50 | $2.50 |
|
|
182
|
+
| 🥇 Gold | $0.40 | $1.50 | $7.50 |
|
|
183
|
+
| 🥈 Silver | $1.00 | $4.00 | $20.00 |
|
|
184
|
+
| 🥉 Bronze | $2.50 | $10.00 | $50.00 |
|
|
185
|
+
|
|
186
|
+
The efficiency ratings (LEGENDARY/EFFICIENT/SOLID/CLOSE CALL/BUSTED) still derive as percentages of whatever you commit to — same relative challenge at every tier, different absolute cost.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## [0.2.0] — 2026-01-xx
|
|
191
|
+
|
|
192
|
+
### Multi-Model Cost Tracking
|
|
193
|
+
|
|
194
|
+
Claude Code uses multiple models in a single session: Sonnet for main tasks, Haiku for subagents and background work. Haiku usage lives in *separate* `.jsonl` files — subagent sidechains — not the main session transcript. Cost detection now scans all `.jsonl` files modified since `run.startedAt`, capturing the full picture.
|
|
195
|
+
|
|
196
|
+
This unlocked the first multi-model achievements:
|
|
197
|
+
- **🏹 Frugal** — Haiku handled ≥50% of session cost. The session was more Haiku than Sonnet by spend. Signals heavy subagent use or deliberate model-mixing.
|
|
198
|
+
- **🎲 Rogue Run** — Haiku handled ≥75% of session cost. Haiku dominated. Extremely subagent-heavy, or you ran Haiku as primary and kept Sonnet minimal.
|
|
199
|
+
|
|
200
|
+
Both achievements reward a real behavior: letting cheaper models do the work. The multi-model breakdown appears on every scorecard.
|
|
201
|
+
|
|
202
|
+
### Flow Mode Auto-Tracking
|
|
203
|
+
|
|
204
|
+
Before this, you had to run `tokengolf start` to begin tracking. Now the SessionStart hook checks for an active run on every Claude Code session open — if none exists, it auto-creates a flow mode run (no quest, no budget). Any Claude Code session is automatically tracked. Just `/exit` and the scorecard appears.
|
|
205
|
+
|
|
206
|
+
### esbuild Pipeline
|
|
207
|
+
|
|
208
|
+
Node.js can't parse JSX natively. Added esbuild: `src/` is JSX source, `dist/cli.js` is the bundled output. `npm run build` wires it; `prepare` script ensures it rebuilds on `npm link` / `npm install`. The `bin` in package.json points to `dist/cli.js`.
|
|
209
|
+
|
|
210
|
+
### Fixed
|
|
211
|
+
|
|
212
|
+
- **Hook symlink resolution** — `npm link` installs hooks with wrong paths because `process.argv[1]` returns the symlink location. Fixed with `fs.realpathSync()` to follow symlinks to real paths. Added `_tg: true` marker on hook entries so re-running `tokengolf install` deduplicates cleanly.
|
|
213
|
+
- **SessionStart stdin hang** — Hook was trying to read stdin. SessionStart doesn't pipe any data. Rewrote to be synchronous, no stdin read.
|
|
214
|
+
- **Wrong-session cost reads** — `autoDetectCost` was using `run.cwd` (the directory where the hook fired) instead of `process.cwd()` (where the user runs `tokengolf win`). During development, this caused it to read the tokengolf dev session ($9.41) instead of the target project ($0.49).
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## [0.1.0] — 2026-01-xx
|
|
219
|
+
|
|
220
|
+
### Initial Release
|
|
221
|
+
|
|
222
|
+
The core game loop: declare a quest, commit to a budget, pick a character class, work in Claude Code normally, get a score.
|
|
223
|
+
|
|
224
|
+
**Character classes** map models to difficulty archetypes:
|
|
225
|
+
- 🏹 **Rogue** (Haiku) — Hard. Glass cannon. Low cost ceiling means precision is mandatory.
|
|
226
|
+
- ⚔️ **Fighter** (Sonnet) — Normal. Balanced cost and capability. The default run.
|
|
227
|
+
- 🧙 **Warlock** (Opus) — Easy. Powerful but expensive. The safety net class.
|
|
228
|
+
|
|
229
|
+
**Budget tiers** are the commitment layer — you declare a bust threshold before starting:
|
|
230
|
+
- 💎 Diamond / 🥇 Gold / 🥈 Silver / 🥉 Bronze / ✏️ Custom
|
|
231
|
+
|
|
232
|
+
**Efficiency ratings** derive as percentage of committed budget used:
|
|
233
|
+
- 🌟 LEGENDARY (<25%) / ⚡ EFFICIENT (<50%) / ✓ SOLID (<75%) / 😅 CLOSE CALL (<100%) / 💀 BUSTED (>100%)
|
|
234
|
+
|
|
235
|
+
**Spend tiers** reflect absolute cost regardless of budget:
|
|
236
|
+
- 💎 <$0.10 / 🥇 <$0.30 / 🥈 <$1.00 / 🥉 <$3.00 / 💸 >$3.00
|
|
237
|
+
|
|
238
|
+
**Base achievements:**
|
|
239
|
+
- 💎 **Diamond** — Total spend under $0.10. Absolute cost floor. Model-agnostic.
|
|
240
|
+
- 🥇 **Gold** — Completed with Haiku. You chose the hard class and finished.
|
|
241
|
+
- 🥈 **Silver** — Completed with Sonnet. The baseline completion mark.
|
|
242
|
+
- 🥉 **Bronze** — Completed with Opus. You had the safety net and still shipped.
|
|
243
|
+
- 🎯 **Sniper** — Under 25% of budget used. Came in well under commitment. Either great scope estimation or very efficient execution.
|
|
244
|
+
- ⚡ **Efficient** — Under 50% of budget used. Healthy margin. The run was right-sized.
|
|
245
|
+
- 🪙 **Penny Pincher** — Total spend under $0.10 on any class. Absolute frugality regardless of what you committed to.
|
|
246
|
+
|
|
247
|
+
**Hooks installed via `tokengolf install`:**
|
|
248
|
+
- `SessionStart` — Injects quest/budget/floor context into Claude's conversation at session open
|
|
249
|
+
- `PostToolUse` — Tracks tool usage by type; fires budget warning message to Claude at 80%
|
|
250
|
+
- `UserPromptSubmit` — Counts prompts; injects halfway nudge at 50% budget
|
|
251
|
+
|
|
252
|
+
**Two modes from day one:**
|
|
253
|
+
- **Flow** — No pre-commitment. Auto-tracked. Just work and see what you spent.
|
|
254
|
+
- **Roguelike** — Pre-commit quest + budget + class. Bust = permadeath. Deliberate practice mode.
|
package/CLAUDE.md
CHANGED
|
@@ -41,6 +41,7 @@ A Node.js CLI tool that wraps Claude Code sessions with game mechanics. Users de
|
|
|
41
41
|
| 🏹 Rogue | Haiku | Hard | Glass cannon. Must prompt precisely. |
|
|
42
42
|
| ⚔️ Fighter | Sonnet | Normal | Balanced. The default run. |
|
|
43
43
|
| 🧙 Warlock | Opus | Easy | Powerful but expensive. |
|
|
44
|
+
| ⚜️ Paladin | Opus (plan mode) | Calculated | Strategic planner. Thinks before acting. |
|
|
44
45
|
|
|
45
46
|
### Budget Tiers
|
|
46
47
|
| Tier | Spend | Emoji |
|
|
@@ -61,16 +62,109 @@ A Node.js CLI tool that wraps Claude Code sessions with game mechanics. Users de
|
|
|
61
62
|
| BUSTED | > 100% | red |
|
|
62
63
|
|
|
63
64
|
### Achievements
|
|
64
|
-
|
|
65
|
+
|
|
66
|
+
**Class Medals**
|
|
65
67
|
- 🥇 Gold — Completed with Haiku
|
|
68
|
+
- 💎 Diamond — Haiku under $0.10
|
|
66
69
|
- 🥈 Silver — Completed with Sonnet
|
|
70
|
+
- ⚜️ Paladin — Completed as Paladin (Opus plan mode)
|
|
71
|
+
- ♟️ Grand Strategist — LEGENDARY efficiency as Paladin
|
|
67
72
|
- 🥉 Bronze — Completed with Opus
|
|
73
|
+
|
|
74
|
+
**Budget Efficiency**
|
|
68
75
|
- 🎯 Sniper — Under 25% of budget used
|
|
69
76
|
- ⚡ Efficient — Under 50% of budget used
|
|
70
77
|
- 🪙 Penny Pincher — Total spend under $0.10
|
|
78
|
+
|
|
79
|
+
**Effort-Based**
|
|
80
|
+
- 🏎️ Speedrunner — Low effort, completed under budget
|
|
81
|
+
- 🏋️ Tryhard — High/Max effort, LEGENDARY efficiency
|
|
82
|
+
- 👑 Archmagus — Opus at max effort, completed
|
|
83
|
+
|
|
84
|
+
**Fast Mode (Opus-only)**
|
|
85
|
+
- ⛈️ Lightning Run — Opus fast mode, completed under budget
|
|
86
|
+
- 🎰 Daredevil — Opus fast mode, LEGENDARY efficiency
|
|
87
|
+
|
|
88
|
+
**Sessions**
|
|
89
|
+
- 🔥 No Rest for the Wicked — Completed in one session
|
|
90
|
+
- 🏕️ Made Camp — Completed across multiple sessions
|
|
91
|
+
- 🧟 Came Back — Fainted and finished anyway
|
|
92
|
+
|
|
93
|
+
**Gear (Compaction)**
|
|
94
|
+
- 📦 Overencumbered — Context auto-compacted during run
|
|
95
|
+
- 🥷 Ghost Run — Manual compact at ≤30% context
|
|
96
|
+
- 🪶 Ultralight — Manual compact at 31–40% context
|
|
97
|
+
- 🎒 Traveling Light — Manual compact at 41–50% context
|
|
98
|
+
|
|
99
|
+
**Ultrathink**
|
|
100
|
+
- 🔮 Spell Cast — Used extended thinking (won)
|
|
101
|
+
- 🧮 Calculated Risk — Ultrathink + LEGENDARY efficiency
|
|
102
|
+
- 🌀 Deep Thinker — ≥3 ultrathink invocations, completed
|
|
103
|
+
- 🤫 Silent Run — No extended thinking, SOLID or better, completed
|
|
104
|
+
|
|
105
|
+
**Paladin Planning Ratio**
|
|
106
|
+
- 🏛️ Architect — Opus handled >60% of cost (heavy planner)
|
|
107
|
+
- 💨 Blitz — Opus handled <25% of cost (light plan, fast execution)
|
|
108
|
+
- ⚖️ Equilibrium — Opus/Sonnet balanced at 40–60%
|
|
109
|
+
|
|
110
|
+
**Model Loyalty (non-Paladin)**
|
|
111
|
+
- 🔷 Purist — Single model family throughout
|
|
112
|
+
- 🦎 Chameleon — Multiple model families used, under budget
|
|
113
|
+
- 🔀 Tactical Switch — Exactly 1 model switch, under budget
|
|
114
|
+
- 🔒 Committed — No switches, one model family
|
|
115
|
+
- ⚠️ Class Defection — Declared one class but cost skewed to another
|
|
116
|
+
|
|
117
|
+
**Haiku Efficiency**
|
|
71
118
|
- 🏹 Frugal — Haiku handled ≥50% of session cost
|
|
72
119
|
- 🎲 Rogue Run — Haiku handled ≥75% of session cost
|
|
73
120
|
|
|
121
|
+
**Prompting Skill**
|
|
122
|
+
- 🥊 One Shot — Completed in a single prompt
|
|
123
|
+
- 💬 Conversationalist — ≥20 prompts
|
|
124
|
+
- 🤐 Terse — ≤3 prompts, ≥10 tool calls
|
|
125
|
+
- 🪑 Backseat Driver — ≥15 prompts, <1 tool call per prompt
|
|
126
|
+
- 🏗️ High Leverage — ≥5 tools per prompt (≥2 prompts)
|
|
127
|
+
|
|
128
|
+
**Tool Mastery**
|
|
129
|
+
- 👁️ Read Only — No Edit or Write calls (≥1 Read)
|
|
130
|
+
- ✏️ Editor — ≥10 Edit calls
|
|
131
|
+
- 🐚 Bash Warrior — ≥10 Bash calls, ≥50% of tool usage
|
|
132
|
+
- 🔍 Scout — ≥60% Read calls (≥5 total)
|
|
133
|
+
- 🔪 Surgeon — 1–3 Edit calls, completed under budget
|
|
134
|
+
- 🧰 Toolbox — ≥5 distinct tool types used
|
|
135
|
+
|
|
136
|
+
**Cost per Prompt**
|
|
137
|
+
- 💲 Cheap Shots — Under $0.01 per prompt (≥3 prompts)
|
|
138
|
+
- 🍷 Expensive Taste — Over $0.50 per prompt (≥3 prompts; also a death mark)
|
|
139
|
+
|
|
140
|
+
**Time**
|
|
141
|
+
- ⏱️ Speedrun — Completed in under 5 minutes
|
|
142
|
+
- 🏃 Marathon — Session 60–180 minutes
|
|
143
|
+
- 🫠 Endurance — Session over 3 hours
|
|
144
|
+
|
|
145
|
+
**Tool Reliability**
|
|
146
|
+
- ✅ Clean Run — Zero failed tool calls (≥5 total tool uses)
|
|
147
|
+
- 🐂 Stubborn — ≥10 failed tool calls, still won
|
|
148
|
+
|
|
149
|
+
**Subagents**
|
|
150
|
+
- 🐺 Lone Wolf — No subagents spawned
|
|
151
|
+
- 📡 Summoner — ≥5 subagents spawned
|
|
152
|
+
- 🪖 Army of One — ≥10 subagents, under 50% budget used
|
|
153
|
+
|
|
154
|
+
**Turn Discipline**
|
|
155
|
+
- 🤖 Agentic — ≥3 Claude turns per user prompt
|
|
156
|
+
- 🐕 Obedient — Exactly 1 turn per prompt (≥3 prompts)
|
|
157
|
+
|
|
158
|
+
**Death Marks** *(fire before won-only cutoff; some also fire on won runs)*
|
|
159
|
+
- 🎲 Indecisive — ≥3 model switches *(won or died)*
|
|
160
|
+
- 🤦 Hubris — Used ultrathink, busted anyway
|
|
161
|
+
- 💥 Blowout — Spent ≥2× budget
|
|
162
|
+
- 😭 So Close — Died within 10% of budget
|
|
163
|
+
- 🔨 Tool Happy — Died with ≥30 tool calls
|
|
164
|
+
- 🪦 Silent Death — Died with ≤2 prompts
|
|
165
|
+
- 🤡 Fumble — Died with ≥5 failed tool calls
|
|
166
|
+
- 🍷 Expensive Taste — Over $0.50/prompt *(won or died)*
|
|
167
|
+
|
|
74
168
|
---
|
|
75
169
|
|
|
76
170
|
## Tech Stack
|
|
@@ -81,6 +175,7 @@ A Node.js CLI tool that wraps Claude Code sessions with game mechanics. Users de
|
|
|
81
175
|
- **CLI parsing**: Commander.js
|
|
82
176
|
- **Persistence**: JSON files in `~/.tokengolf/` (no native deps, zero compilation)
|
|
83
177
|
- **Claude Code integration**: Hooks via `~/.claude/settings.json`
|
|
178
|
+
- **Testing**: Vitest (ESM-native, `npm test`)
|
|
84
179
|
- **Language**: JavaScript (no TypeScript — keep it simple)
|
|
85
180
|
|
|
86
181
|
### Build pipeline
|
|
@@ -109,12 +204,19 @@ tokengolf/
|
|
|
109
204
|
│ ├── store.js # Read/write ~/.tokengolf/runs.json
|
|
110
205
|
│ ├── score.js # Tiers, ratings, model classes, achievements
|
|
111
206
|
│ ├── cost.js # Auto-detect cost from ~/.claude/ transcripts
|
|
112
|
-
│
|
|
207
|
+
│ ├── install.js # Patches ~/.claude/settings.json with hooks
|
|
208
|
+
│ └── __tests__/
|
|
209
|
+
│ └── score.test.js # Vitest: 120 tests covering achievements + pure functions
|
|
113
210
|
├── hooks/
|
|
114
211
|
│ ├── session-start.js # Injects run context; auto-creates flow run
|
|
115
|
-
│ ├── session-
|
|
212
|
+
│ ├── session-end.js # Captures cost on /exit; saves run; renders scorecard
|
|
116
213
|
│ ├── post-tool-use.js # Tracks tool calls, fires budget warnings
|
|
117
|
-
│
|
|
214
|
+
│ ├── post-tool-use-failure.js # Tracks failedToolCalls
|
|
215
|
+
│ ├── user-prompt-submit.js # Counts prompts, fires 50% nudge
|
|
216
|
+
│ ├── pre-compact.js # Tracks compaction events for gear achievements
|
|
217
|
+
│ ├── subagent-start.js # Tracks subagentSpawns
|
|
218
|
+
│ ├── stop.js # Tracks turnCount
|
|
219
|
+
│ └── statusline.sh # Bash HUD shown in Claude Code statusline
|
|
118
220
|
├── dist/
|
|
119
221
|
│ └── cli.js # Built output (gitignored? check .gitignore)
|
|
120
222
|
├── CLAUDE.md # This file
|
|
@@ -150,6 +252,9 @@ Active run state. Written by `tokengolf start` or auto-created by SessionStart h
|
|
|
150
252
|
"compactionEvents": [],
|
|
151
253
|
"thinkingInvocations": 0,
|
|
152
254
|
"thinkingTokens": 0,
|
|
255
|
+
"failedToolCalls": 0,
|
|
256
|
+
"subagentSpawns": 2,
|
|
257
|
+
"turnCount": 12,
|
|
153
258
|
"startedAt": "2026-03-07T10:00:00Z"
|
|
154
259
|
}
|
|
155
260
|
```
|
|
@@ -195,7 +300,7 @@ Array of all completed runs. Append-only.
|
|
|
195
300
|
|
|
196
301
|
## Claude Code Hooks
|
|
197
302
|
|
|
198
|
-
|
|
303
|
+
Nine hooks in `hooks/` directory, installed via `tokengolf install`. Most complete in < 5s (synchronous JSON I/O). `session-end.js` uses async dynamic imports with a 30s timeout.
|
|
199
304
|
|
|
200
305
|
### `SessionStart` (`session-start.js`)
|
|
201
306
|
- Does NOT read stdin (SessionStart doesn't pipe data)
|
|
@@ -224,10 +329,26 @@ Six hooks in `hooks/` directory, installed via `tokengolf install`. Most complet
|
|
|
224
329
|
- Resting runs: updates state with fainted:true, does NOT clear — run continues next session
|
|
225
330
|
- Won/died runs: calls `saveRun()` (which runs `calculateAchievements()`), clears state, renders ANSI scorecard
|
|
226
331
|
|
|
332
|
+
### `PostToolUseFailure` (`post-tool-use-failure.js`)
|
|
333
|
+
- Reads stdin (event JSON with `tool_name` and error info)
|
|
334
|
+
- Increments `failedToolCalls` in `current-run.json`
|
|
335
|
+
- Powers Fumble death mark (≥5 failed tool calls)
|
|
336
|
+
|
|
337
|
+
### `SubagentStart` (`subagent-start.js`)
|
|
338
|
+
- Reads stdin (subagent event JSON)
|
|
339
|
+
- Increments `subagentSpawns` in `current-run.json`
|
|
340
|
+
- Powers Lone Wolf / Summoner / Army of One achievements
|
|
341
|
+
|
|
342
|
+
### `Stop` (`stop.js`)
|
|
343
|
+
- Reads stdin for turn data
|
|
344
|
+
- Increments `turnCount` in `current-run.json`
|
|
345
|
+
- Powers Agentic / Obedient turn discipline achievements
|
|
346
|
+
|
|
227
347
|
### `StatusLine` (`statusline.sh`)
|
|
228
348
|
- Bash script; uses `TG_SESSION_JSON=... python3 - "$STATE_FILE" <<'PYEOF'` pattern to avoid heredoc/stdin conflict
|
|
229
349
|
- Receives live session JSON (cost, context %, model) via stdin
|
|
230
350
|
- Shows: quest/mode | tier emoji + cost [/budget pct%] | [efficiency rating] | [🪶/🎒/📦 ctx%] | model label | [floor]
|
|
351
|
+
- Model label: `⚔️ Sonnet`, `⚔️ Sonnet·High`, `🏹 Haiku`, `🧙 Opus·Max`, etc. Effort appended only when explicitly set in settings.json (medium omitted — it's the default)
|
|
231
352
|
- Context load: 🪶 green (50–74%), 🎒 yellow (75–89%), 📦 red (90%+); hidden below 50%
|
|
232
353
|
- Separator lines (`───────────────`) above and below HUD row
|
|
233
354
|
- statusLine config must be an object: `{type:"command", command:"...statusline.sh", padding:1}`
|
|
@@ -253,7 +374,7 @@ Thinking tokens are estimated from character count ÷ 4 (approximate — display
|
|
|
253
374
|
|
|
254
375
|
## Key Design Decisions
|
|
255
376
|
|
|
256
|
-
1. **SessionEnd hook is authoritative** —
|
|
377
|
+
1. **SessionEnd hook is authoritative for cost/scorecard** — SessionEnd fires on `/exit`, scans transcripts, saves run, and renders ANSI scorecard. `tokengolf win` is a manual override that still works. The Stop hook is also active but only for `turnCount` tracking — it does NOT include `total_cost_usd` so it cannot determine final cost.
|
|
257
378
|
|
|
258
379
|
2. **Scan all transcripts for multi-model + ultrathink** — Claude Code creates separate `.jsonl` files for subagent sidechains (Haiku usage lives there). Same scan also picks up thinking blocks for ultrathink detection. One pass, all data.
|
|
259
380
|
|
|
@@ -263,9 +384,9 @@ Thinking tokens are estimated from character count ÷ 4 (approximate — display
|
|
|
263
384
|
|
|
264
385
|
5. **Budget presets are model-calibrated** — `MODEL_BUDGET_TIERS` in score.js defines Diamond/Gold/Silver/Bronze amounts per model class. Wizard calls `getModelBudgets(model)` so Haiku sees $0.15/$0.40/$1.00/$2.50 and Opus sees $2.50/$7.50/$20.00/$50.00. Efficiency ratings (LEGENDARY/EFFICIENT/etc.) still derive as % of whatever budget was committed — no change there.
|
|
265
386
|
|
|
266
|
-
6. **Ultrathink is natural language, not a slash command** — Writing `ultrathink` in a prompt triggers extended thinking mode. It's tracked via thinking blocks in transcripts, not via any hook. `thinkingInvocations === 0` on a won run = Silent Run achievement; on a died run with invocations > 0 = Hubris
|
|
387
|
+
6. **Ultrathink is natural language, not a slash command** — Writing `ultrathink` in a prompt triggers extended thinking mode. It's tracked via thinking blocks in transcripts, not via any hook. `thinkingInvocations === 0` on a won run = Silent Run achievement; on a died run with invocations > 0 = Hubris death mark.
|
|
267
388
|
|
|
268
|
-
7. **
|
|
389
|
+
7. **Death marks fire before the early return** — `calculateAchievements` has an `if (!won) return []` early exit, but death marks (blowout, so_close, tool_happy, silent_death, fumble, expensive_taste, hubris) fire before it. `indecisive` (model switches) and `expensive_taste` also fire on won runs — they're behavior patterns, not death verdicts.
|
|
269
390
|
|
|
270
391
|
---
|
|
271
392
|
|
|
@@ -277,7 +398,7 @@ Thinking tokens are estimated from character count ÷ 4 (approximate — display
|
|
|
277
398
|
- [x] Ink components: StartRun, ActiveRun, ScoreCard, StatsView
|
|
278
399
|
- [x] JSON persistence (state.js + store.js)
|
|
279
400
|
- [x] Scoring logic (tiers, ratings, achievements, multi-model)
|
|
280
|
-
- [x]
|
|
401
|
+
- [x] 9 Claude Code hooks: SessionStart, PostToolUse, PostToolUseFailure, UserPromptSubmit, PreCompact, SessionEnd, SubagentStart, Stop, StatusLine
|
|
281
402
|
- [x] `tokengolf install` hook installer with symlink resolution + statusLine config
|
|
282
403
|
- [x] Auto cost detection from transcripts (`cost.js`) — multi-file, multi-model
|
|
283
404
|
- [x] SessionEnd hook auto-displays ANSI scorecard on /exit; replaces dead Stop hook
|
|
@@ -292,7 +413,11 @@ Thinking tokens are estimated from character count ÷ 4 (approximate — display
|
|
|
292
413
|
- [x] Multi-session tracking (sessionCount increments on each SessionStart)
|
|
293
414
|
- [x] Model-aware budget presets in wizard (MODEL_BUDGET_TIERS, getModelBudgets)
|
|
294
415
|
- [x] Ultrathink detection from transcripts (thinkingInvocations, thinkingTokens)
|
|
295
|
-
- [x] 5 ultrathink achievements including Hubris
|
|
416
|
+
- [x] 5 ultrathink achievements including Hubris death mark
|
|
417
|
+
- [x] Paladin (⚜️ opusplan) character class with model-aware budgets and statusline support
|
|
418
|
+
- [x] 28 new achievements: prompting skill, tool mastery, cost/prompt, time, subagents, turn discipline, death marks
|
|
419
|
+
- [x] 3 new hooks: PostToolUseFailure, SubagentStart, Stop
|
|
420
|
+
- [x] Vitest test suite — 120 tests covering all achievements + pure score functions
|
|
296
421
|
|
|
297
422
|
### Next up (v0.4)
|
|
298
423
|
- [ ] `tokengolf floor` command to advance floor manually
|
|
@@ -310,6 +435,7 @@ When making changes:
|
|
|
310
435
|
- State mutations always go through `state.js` and `store.js` — never write to `~/.tokengolf/` directly from components
|
|
311
436
|
- Hooks must be fast (< 1s) — no async, no network, JSON file I/O only
|
|
312
437
|
- **Always run `npm run build` after source changes**
|
|
438
|
+
- **Run `npm test` after score.js changes** — 83 tests catch achievement regressions
|
|
313
439
|
- Test hooks standalone: `echo '{"tool_name":"Read"}' | node hooks/post-tool-use.js`
|
|
314
440
|
- Remember hooks run in a separate process with no access to shell env vars
|
|
315
441
|
- Always `process.exit(0)` at the end of hooks
|