tokengolf 0.3.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/.claude/settings.local.json +36 -0
- package/CLAUDE.md +320 -0
- package/README.md +235 -0
- package/dist/cli.js +897 -0
- package/hooks/post-tool-use.js +43 -0
- package/hooks/pre-compact.js +35 -0
- package/hooks/session-end.js +172 -0
- package/hooks/session-start.js +100 -0
- package/hooks/session-stop.js +25 -0
- package/hooks/statusline.sh +72 -0
- package/hooks/user-prompt-submit.js +29 -0
- package/package.json +27 -0
- package/src/cli.js +115 -0
- package/src/components/ActiveRun.js +85 -0
- package/src/components/ScoreCard.js +157 -0
- package/src/components/StartRun.js +156 -0
- package/src/components/StatsView.js +112 -0
- package/src/lib/cost.js +149 -0
- package/src/lib/install.js +163 -0
- package/src/lib/score.js +330 -0
- package/src/lib/state.js +35 -0
- package/src/lib/store.js +76 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(grep:*)",
|
|
5
|
+
"Bash(npm install:*)",
|
|
6
|
+
"Bash(npm run:*)",
|
|
7
|
+
"Bash(npm link:*)",
|
|
8
|
+
"Bash(timeout 3 tokengolf status:*)",
|
|
9
|
+
"Bash(tokengolf stats:*)",
|
|
10
|
+
"Bash(ls:*)",
|
|
11
|
+
"Bash(tokengolf win:*)",
|
|
12
|
+
"Bash(node:*)",
|
|
13
|
+
"Bash(python3:*)",
|
|
14
|
+
"Bash(~/.tokengolf/current-run.json:*)",
|
|
15
|
+
"Bash(git add:*)",
|
|
16
|
+
"WebFetch(domain:github.com)",
|
|
17
|
+
"Bash(for f in a20dac9210b555390 ae511fa601e30efb2 aa25fad39a7781f65)",
|
|
18
|
+
"Bash(do echo \"=== $f ===\")",
|
|
19
|
+
"Read(//private/tmp/claude-501/-Users-josheche-projects-tokengolf/tasks/**)",
|
|
20
|
+
"Bash(done)",
|
|
21
|
+
"Bash(chmod 755:*)",
|
|
22
|
+
"Bash(bash:*)",
|
|
23
|
+
"Bash(tokengolf install:*)",
|
|
24
|
+
"Bash(head:*)",
|
|
25
|
+
"Bash(claude hooks:*)",
|
|
26
|
+
"Bash(git checkout:*)",
|
|
27
|
+
"Bash(git commit:*)",
|
|
28
|
+
"Bash(oh-my-posh claude:*)",
|
|
29
|
+
"Bash(git push:*)",
|
|
30
|
+
"Bash(gh pr:*)",
|
|
31
|
+
"Bash(gh api:*)",
|
|
32
|
+
"Bash(git:*)",
|
|
33
|
+
"Bash(npm whoami:*)"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# TokenGolf — CLAUDE.md
|
|
2
|
+
|
|
3
|
+
You are working on **TokenGolf**, a CLI game that gamifies Claude Code sessions by turning token/dollar efficiency into a score. This is the primary project context file. Read this fully before doing anything.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What TokenGolf Is
|
|
8
|
+
|
|
9
|
+
A Node.js CLI tool that wraps Claude Code sessions with game mechanics. Users declare a quest ("implement pagination for /users"), set a budget ($0.30), pick a model class, then work in Claude Code normally. At the end, they get a score based on how efficiently they used their budget.
|
|
10
|
+
|
|
11
|
+
**Core insight**: Claude Code already exposes session cost data. TokenGolf adds the game layer — the meaning, the stakes, the achievement system — on top of data that already exists.
|
|
12
|
+
|
|
13
|
+
**Tagline**: *"Flow mode tracks you. Roguelike mode trains you."*
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Two Modes
|
|
18
|
+
|
|
19
|
+
### Flow Mode
|
|
20
|
+
- Passive. No interruption. Just runs in the background.
|
|
21
|
+
- SessionStart hook auto-creates a flow run if none is active.
|
|
22
|
+
- Post-session: `tokengolf win` shows score + achievements with no pre-configuration.
|
|
23
|
+
- For people in flow state who don't want friction.
|
|
24
|
+
|
|
25
|
+
### Roguelike Mode
|
|
26
|
+
- Intentional. Pre-commitment before session starts.
|
|
27
|
+
- Declare quest + budget + model class = a "run" with real stakes.
|
|
28
|
+
- Budget bust = permadeath. Run logged as a death.
|
|
29
|
+
- Floor structure: Write code → Write tests → Fix tests → Code review → PR merged (BOSS)
|
|
30
|
+
- For deliberate practice. Trains prompting skills.
|
|
31
|
+
|
|
32
|
+
**Relationship**: Same engine, same data, same achievement system. Roguelike practice makes Flow sessions better over time. That's the meta loop.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Game Mechanics
|
|
37
|
+
|
|
38
|
+
### Model as Character Class
|
|
39
|
+
| Class | Model | Difficulty | Feel |
|
|
40
|
+
|-------|-------|------------|------|
|
|
41
|
+
| 🏹 Rogue | Haiku | Hard | Glass cannon. Must prompt precisely. |
|
|
42
|
+
| ⚔️ Fighter | Sonnet | Normal | Balanced. The default run. |
|
|
43
|
+
| 🧙 Warlock | Opus | Easy | Powerful but expensive. |
|
|
44
|
+
|
|
45
|
+
### Budget Tiers
|
|
46
|
+
| Tier | Spend | Emoji |
|
|
47
|
+
|------|-------|-------|
|
|
48
|
+
| Diamond | < $0.10 | 💎 |
|
|
49
|
+
| Gold | < $0.30 | 🥇 |
|
|
50
|
+
| Silver | < $1.00 | 🥈 |
|
|
51
|
+
| Bronze | < $3.00 | 🥉 |
|
|
52
|
+
| Reckless | > $3.00 | 💸 |
|
|
53
|
+
|
|
54
|
+
### Efficiency Ratings
|
|
55
|
+
| Rating | Budget Used | Color |
|
|
56
|
+
|--------|------------|-------|
|
|
57
|
+
| LEGENDARY | < 25% | magenta |
|
|
58
|
+
| EFFICIENT | < 50% | cyan |
|
|
59
|
+
| SOLID | < 75% | green |
|
|
60
|
+
| CLOSE CALL | < 100% | yellow |
|
|
61
|
+
| BUSTED | > 100% | red |
|
|
62
|
+
|
|
63
|
+
### Achievements
|
|
64
|
+
- 💎 Diamond — Haiku under $0.10
|
|
65
|
+
- 🥇 Gold — Completed with Haiku
|
|
66
|
+
- 🥈 Silver — Completed with Sonnet
|
|
67
|
+
- 🥉 Bronze — Completed with Opus
|
|
68
|
+
- 🎯 Sniper — Under 25% of budget used
|
|
69
|
+
- ⚡ Efficient — Under 50% of budget used
|
|
70
|
+
- 🪙 Penny Pincher — Total spend under $0.10
|
|
71
|
+
- 🏹 Frugal — Haiku handled ≥50% of session cost
|
|
72
|
+
- 🎲 Rogue Run — Haiku handled ≥75% of session cost
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Tech Stack
|
|
77
|
+
|
|
78
|
+
- **Runtime**: Node.js (ESM, `"type": "module"`)
|
|
79
|
+
- **Build**: esbuild (JSX transform, `npm run build` → `dist/cli.js`)
|
|
80
|
+
- **TUI**: [Ink v5](https://github.com/vadimdemedes/ink) + [@inkjs/ui v2](https://github.com/vadimdemedes/ink-ui)
|
|
81
|
+
- **CLI parsing**: Commander.js
|
|
82
|
+
- **Persistence**: JSON files in `~/.tokengolf/` (no native deps, zero compilation)
|
|
83
|
+
- **Claude Code integration**: Hooks via `~/.claude/settings.json`
|
|
84
|
+
- **Language**: JavaScript (no TypeScript — keep it simple)
|
|
85
|
+
|
|
86
|
+
### Build pipeline
|
|
87
|
+
Source is JSX (`src/`) → esbuild bundles to `dist/cli.js`. The `bin` in `package.json` points to `dist/cli.js`. Run `npm run build` after any source change. `prepare` runs build automatically on `npm link`/`npm install`.
|
|
88
|
+
|
|
89
|
+
**Do not test with `node src/cli.js`** — use `node dist/cli.js` or `tokengolf` after `npm link`.
|
|
90
|
+
|
|
91
|
+
### Why JSON not SQLite
|
|
92
|
+
`better-sqlite3` requires native compilation which causes install failures. JSON files in `~/.tokengolf/` are sufficient for the data volume (hundreds of runs max) and have zero friction.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Project Structure
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
tokengolf/
|
|
100
|
+
├── src/
|
|
101
|
+
│ ├── cli.js # Main entrypoint, all commands
|
|
102
|
+
│ ├── components/
|
|
103
|
+
│ │ ├── StartRun.js # Quest declaration wizard
|
|
104
|
+
│ │ ├── ActiveRun.js # Live run status display
|
|
105
|
+
│ │ ├── ScoreCard.js # End-of-run screen (win/death)
|
|
106
|
+
│ │ └── StatsView.js # Career stats dashboard
|
|
107
|
+
│ └── lib/
|
|
108
|
+
│ ├── state.js # Read/write ~/.tokengolf/current-run.json
|
|
109
|
+
│ ├── store.js # Read/write ~/.tokengolf/runs.json
|
|
110
|
+
│ ├── score.js # Tiers, ratings, model classes, achievements
|
|
111
|
+
│ ├── cost.js # Auto-detect cost from ~/.claude/ transcripts
|
|
112
|
+
│ └── install.js # Patches ~/.claude/settings.json with hooks
|
|
113
|
+
├── hooks/
|
|
114
|
+
│ ├── session-start.js # Injects run context; auto-creates flow run
|
|
115
|
+
│ ├── session-stop.js # Captures exact cost from Stop event
|
|
116
|
+
│ ├── post-tool-use.js # Tracks tool calls, fires budget warnings
|
|
117
|
+
│ └── user-prompt-submit.js # Counts prompts, fires 50% nudge
|
|
118
|
+
├── dist/
|
|
119
|
+
│ └── cli.js # Built output (gitignored? check .gitignore)
|
|
120
|
+
├── CLAUDE.md # This file
|
|
121
|
+
├── package.json
|
|
122
|
+
└── README.md
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## State Files (in `~/.tokengolf/`)
|
|
128
|
+
|
|
129
|
+
### `current-run.json`
|
|
130
|
+
Active run state. Written by `tokengolf start` or auto-created by SessionStart hook (flow mode). Cleared on `tokengolf win` or `tokengolf bust`.
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"id": "run_1741345200000",
|
|
135
|
+
"quest": "implement pagination for /users",
|
|
136
|
+
"model": "claude-sonnet-4-6",
|
|
137
|
+
"budget": 0.30,
|
|
138
|
+
"spent": 0.11,
|
|
139
|
+
"status": "active",
|
|
140
|
+
"mode": "roguelike",
|
|
141
|
+
"floor": 2,
|
|
142
|
+
"totalFloors": 5,
|
|
143
|
+
"promptCount": 8,
|
|
144
|
+
"totalToolCalls": 14,
|
|
145
|
+
"toolCalls": { "Read": 6, "Edit": 4, "Bash": 4 },
|
|
146
|
+
"sessionId": "abc123",
|
|
147
|
+
"cwd": "/Users/me/projects/my-app",
|
|
148
|
+
"sessionCount": 1,
|
|
149
|
+
"fainted": false,
|
|
150
|
+
"compactionEvents": [],
|
|
151
|
+
"thinkingInvocations": 0,
|
|
152
|
+
"thinkingTokens": 0,
|
|
153
|
+
"startedAt": "2026-03-07T10:00:00Z"
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Flow mode runs have `"quest": null, "budget": null, "mode": "flow"`.
|
|
158
|
+
|
|
159
|
+
### `runs.json`
|
|
160
|
+
Array of all completed runs. Append-only.
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
[
|
|
164
|
+
{
|
|
165
|
+
"id": "run_1741345200000",
|
|
166
|
+
"quest": "...",
|
|
167
|
+
"status": "won",
|
|
168
|
+
"spent": 0.18,
|
|
169
|
+
"budget": 0.30,
|
|
170
|
+
"model": "claude-sonnet-4-6",
|
|
171
|
+
"modelBreakdown": { "claude-sonnet-4-6": 0.15, "claude-haiku-4-5-20251001": 0.03 },
|
|
172
|
+
"achievements": [...],
|
|
173
|
+
"startedAt": "...",
|
|
174
|
+
"endedAt": "..."
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## CLI Commands
|
|
182
|
+
|
|
183
|
+
| Command | Description |
|
|
184
|
+
|---------|-------------|
|
|
185
|
+
| `tokengolf start` | Declare quest, model, budget — begin a roguelike run |
|
|
186
|
+
| `tokengolf status` | Show live status of current run |
|
|
187
|
+
| `tokengolf win` | Complete current run (auto-detects cost from transcripts) |
|
|
188
|
+
| `tokengolf win --spent 0.18` | Complete with manually specified cost |
|
|
189
|
+
| `tokengolf bust` | Mark run as budget busted (permadeath) |
|
|
190
|
+
| `tokengolf scorecard` | Show last run's score card |
|
|
191
|
+
| `tokengolf stats` | Career stats dashboard |
|
|
192
|
+
| `tokengolf install` | Patch `~/.claude/settings.json` with hooks |
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Claude Code Hooks
|
|
197
|
+
|
|
198
|
+
Six 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
|
+
|
|
200
|
+
### `SessionStart` (`session-start.js`)
|
|
201
|
+
- Does NOT read stdin (SessionStart doesn't pipe data)
|
|
202
|
+
- Reads `current-run.json`; if no active run, auto-creates a flow mode run
|
|
203
|
+
- Auto-detects `effort` from env var or `~/.claude/settings.json`; auto-detects `fastMode` from settings.json
|
|
204
|
+
- Increments `sessionCount` on existing runs
|
|
205
|
+
- Outputs `additionalContext` injected into Claude's conversation
|
|
206
|
+
|
|
207
|
+
### `PostToolUse` (`post-tool-use.js`)
|
|
208
|
+
- Reads stdin (event JSON with `tool_name`)
|
|
209
|
+
- Updates `toolCalls` count in `current-run.json`
|
|
210
|
+
- At 80%+ budget: outputs `systemMessage` warning to Claude
|
|
211
|
+
|
|
212
|
+
### `UserPromptSubmit` (`user-prompt-submit.js`)
|
|
213
|
+
- Increments `promptCount`
|
|
214
|
+
- At 50% budget: injects halfway nudge as `additionalContext`
|
|
215
|
+
|
|
216
|
+
### `PreCompact` (`pre-compact.js`)
|
|
217
|
+
- Reads stdin (compact event JSON with `trigger` and `context_window.used_percentage`)
|
|
218
|
+
- Appends to `compactionEvents` array in `current-run.json`
|
|
219
|
+
- Powers gear achievements (Ghost Run, Ultralight, Traveling Light, Overencumbered)
|
|
220
|
+
|
|
221
|
+
### `SessionEnd` (`session-end.js`)
|
|
222
|
+
- Reads stdin for `reason` field (detects Fainted if reason is `'other'`)
|
|
223
|
+
- Calls `autoDetectCost(run)` — returns spent, modelBreakdown, thinkingInvocations, thinkingTokens
|
|
224
|
+
- Resting runs: updates state with fainted:true, does NOT clear — run continues next session
|
|
225
|
+
- Won/died runs: calls `saveRun()` (which runs `calculateAchievements()`), clears state, renders ANSI scorecard
|
|
226
|
+
|
|
227
|
+
### `StatusLine` (`statusline.sh`)
|
|
228
|
+
- Bash script; uses `TG_SESSION_JSON=... python3 - "$STATE_FILE" <<'PYEOF'` pattern to avoid heredoc/stdin conflict
|
|
229
|
+
- Receives live session JSON (cost, context %, model) via stdin
|
|
230
|
+
- Shows: quest/mode | tier emoji + cost [/budget pct%] | [efficiency rating] | [🪶/🎒/📦 ctx%] | model label | [floor]
|
|
231
|
+
- Context load: 🪶 green (50–74%), 🎒 yellow (75–89%), 📦 red (90%+); hidden below 50%
|
|
232
|
+
- Separator lines (`───────────────`) above and below HUD row
|
|
233
|
+
- statusLine config must be an object: `{type:"command", command:"...statusline.sh", padding:1}`
|
|
234
|
+
|
|
235
|
+
### Hook installation
|
|
236
|
+
`tokengolf install` patches `~/.claude/settings.json`. Uses `fs.realpathSync(process.argv[1])` to resolve npm link symlinks to real hook paths. Hook entries tagged with `_tg: true` for reliable dedup. Non-destructive statusLine install: wraps existing statusline if one is configured.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Cost Detection (`src/lib/cost.js`)
|
|
241
|
+
|
|
242
|
+
`autoDetectCost(run)` is called by `session-end.js` and `tokengolf win/bust`. It:
|
|
243
|
+
1. Parses `~/.claude/projects/<cwd>/` transcript files — all `.jsonl` files modified since `run.startedAt`
|
|
244
|
+
2. Scans ALL files (not just the main session) — this captures subagent sidechain files where Haiku usage lives
|
|
245
|
+
3. Also calls `parseThinkingFromTranscripts(paths)` to count thinking blocks and estimate tokens
|
|
246
|
+
4. Returns `{ spent, modelBreakdown, thinkingInvocations, thinkingTokens }`
|
|
247
|
+
|
|
248
|
+
`process.cwd()` is used (not `run.cwd`) because the user always runs `tokengolf win` from their project directory.
|
|
249
|
+
|
|
250
|
+
Thinking tokens are estimated from character count ÷ 4 (approximate — displayed with `~` prefix). Invocations = assistant turns containing at least one `{"type":"thinking"}` content block.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Key Design Decisions
|
|
255
|
+
|
|
256
|
+
1. **SessionEnd hook is authoritative** — Replaces the dead Stop hook (Stop event didn't include `total_cost_usd`). SessionEnd fires on `/exit`, scans transcripts, saves run, and renders ANSI scorecard. `tokengolf win` is a manual override that still works.
|
|
257
|
+
|
|
258
|
+
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
|
+
|
|
260
|
+
3. **Floors are cosmetic** — Floor structure exists in the data model but isn't enforced. It's a UI element. Full roguelike floor mechanics with per-floor budgets are a future feature.
|
|
261
|
+
|
|
262
|
+
4. **Flow mode is automatic** — SessionStart hook creates a flow run if none exists. Any Claude Code session is tracked. Just `/exit` and the scorecard appears.
|
|
263
|
+
|
|
264
|
+
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
|
+
|
|
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 (the only death achievement).
|
|
267
|
+
|
|
268
|
+
7. **Hubris is a death achievement** — `calculateAchievements` normally returns `[]` early for non-won runs. Hubris is the one exception — it fires before the early return. Intentional design: Hubris is a mark of cause-of-death, not a consolation prize.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Current Status: v0.3
|
|
273
|
+
|
|
274
|
+
### Done
|
|
275
|
+
- [x] Full project scaffold with esbuild pipeline
|
|
276
|
+
- [x] All CLI commands wired up
|
|
277
|
+
- [x] Ink components: StartRun, ActiveRun, ScoreCard, StatsView
|
|
278
|
+
- [x] JSON persistence (state.js + store.js)
|
|
279
|
+
- [x] Scoring logic (tiers, ratings, achievements, multi-model)
|
|
280
|
+
- [x] 6 Claude Code hooks: SessionStart, PostToolUse, UserPromptSubmit, PreCompact, SessionEnd, StatusLine
|
|
281
|
+
- [x] `tokengolf install` hook installer with symlink resolution + statusLine config
|
|
282
|
+
- [x] Auto cost detection from transcripts (`cost.js`) — multi-file, multi-model
|
|
283
|
+
- [x] SessionEnd hook auto-displays ANSI scorecard on /exit; replaces dead Stop hook
|
|
284
|
+
- [x] Flow mode auto-tracking (SessionStart creates run if none exists)
|
|
285
|
+
- [x] Multi-model breakdown in ScoreCard
|
|
286
|
+
- [x] Haiku efficiency achievements (Frugal, Rogue Run)
|
|
287
|
+
- [x] Effort level wizard step (Low/Medium/High for Sonnet; +Max for Opus; Haiku skips)
|
|
288
|
+
- [x] Fast mode auto-detection from settings.json; tracked in run state
|
|
289
|
+
- [x] Fainted / rest mechanic (usage limit hit = fainted, run continues next session)
|
|
290
|
+
- [x] Context window % in StatusLine HUD: 🪶/🎒/📦 with green/yellow/red
|
|
291
|
+
- [x] PreCompact hook tracks manual vs auto compaction + context % for gear achievements
|
|
292
|
+
- [x] Multi-session tracking (sessionCount increments on each SessionStart)
|
|
293
|
+
- [x] Model-aware budget presets in wizard (MODEL_BUDGET_TIERS, getModelBudgets)
|
|
294
|
+
- [x] Ultrathink detection from transcripts (thinkingInvocations, thinkingTokens)
|
|
295
|
+
- [x] 5 ultrathink achievements including Hubris (only death achievement)
|
|
296
|
+
|
|
297
|
+
### Next up (v0.4)
|
|
298
|
+
- [ ] `tokengolf floor` command to advance floor manually
|
|
299
|
+
- [ ] Roguelike floor mechanics with per-floor sub-budgets
|
|
300
|
+
- [ ] Leaderboard / shareable run URLs
|
|
301
|
+
- [ ] Team mode (shared `runs.json` via git)
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Working in This Repo
|
|
306
|
+
|
|
307
|
+
When making changes:
|
|
308
|
+
- Keep it ESM (`import/export`, no `require`)
|
|
309
|
+
- Ink components are functional React — hooks only, no classes
|
|
310
|
+
- State mutations always go through `state.js` and `store.js` — never write to `~/.tokengolf/` directly from components
|
|
311
|
+
- Hooks must be fast (< 1s) — no async, no network, JSON file I/O only
|
|
312
|
+
- **Always run `npm run build` after source changes**
|
|
313
|
+
- Test hooks standalone: `echo '{"tool_name":"Read"}' | node hooks/post-tool-use.js`
|
|
314
|
+
- Remember hooks run in a separate process with no access to shell env vars
|
|
315
|
+
- Always `process.exit(0)` at the end of hooks
|
|
316
|
+
|
|
317
|
+
When adding a new CLI command:
|
|
318
|
+
1. Add it to `src/cli.js`
|
|
319
|
+
2. Add a component in `src/components/` if it needs a TUI
|
|
320
|
+
3. Document it in this file and README.md
|
package/README.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# ⛳ TokenGolf
|
|
2
|
+
|
|
3
|
+
> Gamify your Claude Code sessions. Flow mode tracks you. Roguelike mode trains you.
|
|
4
|
+
|
|
5
|
+
Turn Claude Code token efficiency into a game. Declare a quest, commit to a budget, pick a character class. Work normally. At the end, get a score based on how efficiently you used your budget.
|
|
6
|
+
|
|
7
|
+
**Better prompting → fewer tokens → higher score.**
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<!-- SCREENSHOT: tokengolf start wizard — quest/class/effort/budget selection -->
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
git clone <repo>
|
|
19
|
+
cd tokengolf
|
|
20
|
+
npm install && npm link
|
|
21
|
+
tokengolf install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Two Modes
|
|
27
|
+
|
|
28
|
+
### ⛳ Flow Mode
|
|
29
|
+
Just work. TokenGolf auto-creates a tracking session when you open Claude Code. `/exit` the session and the scorecard appears automatically. No pre-configuration required.
|
|
30
|
+
|
|
31
|
+
### ☠️ Roguelike Mode
|
|
32
|
+
Pre-commit before you start. Declare a quest, pick a class and effort level, set a budget. Go over budget = permadeath — the run is logged as a death. The deliberate pressure trains better prompting habits, which makes your Flow sessions cheaper over time.
|
|
33
|
+
|
|
34
|
+
**The meta loop:** Roguelike practice makes Flow sessions better. Better Flow = lower daily spend = better scores without even trying.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
tokengolf start # declare quest + class + effort + budget, begin a run
|
|
42
|
+
tokengolf status # live run status
|
|
43
|
+
tokengolf win # shipped it ✓ (auto-detects cost from transcripts)
|
|
44
|
+
tokengolf bust # manual permadeath override
|
|
45
|
+
tokengolf scorecard # last run scorecard
|
|
46
|
+
tokengolf stats # career dashboard
|
|
47
|
+
tokengolf install # patch ~/.claude/settings.json with hooks
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Character Classes & Effort
|
|
53
|
+
|
|
54
|
+
| Class | Model | Effort | Feel |
|
|
55
|
+
|-------|-------|--------|------|
|
|
56
|
+
| 🏹 Rogue | Haiku | — *(skips effort step)* | Glass cannon. Prompt precisely or die. |
|
|
57
|
+
| ⚔️ Fighter | Sonnet | Low / **Medium** / High | Balanced. The default run. |
|
|
58
|
+
| 🧙 Warlock | Opus | Low / **Medium** / High / Max | Powerful but costly. |
|
|
59
|
+
| ⚡ Warlock·Fast | Opus + fast mode | any | 2× cost. Maximum danger mode. |
|
|
60
|
+
|
|
61
|
+
`max` effort is Opus-only — the API returns an error if used on other models. Fast mode is toggled mid-session with `/fast` in Claude Code and is auto-detected by TokenGolf.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Budget Presets (Model-Calibrated)
|
|
66
|
+
|
|
67
|
+
The wizard shows different amounts depending on your class — same relative difficulty, different absolute cost. Anchored to the ~$0.75/task Sonnet average from Anthropic's $6/day Claude Code benchmark.
|
|
68
|
+
|
|
69
|
+
| Tier | Haiku 🏹 | Sonnet ⚔️ | Opus 🧙 | Feel |
|
|
70
|
+
|------|---------|---------|--------|------|
|
|
71
|
+
| 💎 Diamond | $0.15 | $0.50 | $2.50 | Surgical micro-task |
|
|
72
|
+
| 🥇 Gold | $0.40 | $1.50 | $7.50 | Focused small task |
|
|
73
|
+
| 🥈 Silver | $1.00 | $4.00 | $20.00 | Medium task |
|
|
74
|
+
| 🥉 Bronze | $2.50 | $10.00 | $50.00 | Heavy / complex |
|
|
75
|
+
| ✏️ Custom | any | any | any | Set your own bust threshold |
|
|
76
|
+
|
|
77
|
+
These are **bust thresholds** — your commitment. Efficiency tiers derive as percentages of whatever you commit to.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Scoring
|
|
82
|
+
|
|
83
|
+
**Efficiency rating** (roguelike mode — % of your budget used):
|
|
84
|
+
|
|
85
|
+
| 🌟 LEGENDARY | ⚡ EFFICIENT | ✓ SOLID | 😅 CLOSE CALL | 💀 BUSTED |
|
|
86
|
+
|---|---|---|---|---|
|
|
87
|
+
| < 25% | < 50% | < 75% | < 100% | > 100% |
|
|
88
|
+
|
|
89
|
+
**Spend tier** (absolute cost, shown on every scorecard):
|
|
90
|
+
|
|
91
|
+
| 💎 | 🥇 | 🥈 | 🥉 | 💸 |
|
|
92
|
+
|---|---|---|---|---|
|
|
93
|
+
| < $0.10 | < $0.30 | < $1.00 | < $3.00 | > $3.00 |
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Ultrathink
|
|
98
|
+
|
|
99
|
+
Write `ultrathink` anywhere in your prompt to trigger extended thinking mode. It's not a slash command — just say it in natural language:
|
|
100
|
+
|
|
101
|
+
> *"ultrathink: is this the right architecture before I write anything?"*
|
|
102
|
+
> *"can you ultrathink through the tradeoffs here?"*
|
|
103
|
+
|
|
104
|
+
Extended thinking tokens are billed at full output rate. A single ultrathink on Sonnet can cost $0.50–2.00 depending on problem depth. TokenGolf detects thinking blocks from your session transcripts and tracks invocations and estimated thinking tokens — both show on your scorecard.
|
|
105
|
+
|
|
106
|
+
**The skill is knowing when to ultrathink.** One expensive deep-think that prevents five wrong turns is efficient. Ultrathinking every prompt when you're at 80% budget is hubris.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Achievements
|
|
111
|
+
|
|
112
|
+
**Class**
|
|
113
|
+
- 💎 Diamond — Haiku under $0.10 total spend
|
|
114
|
+
- 🥇 Gold — Completed with Haiku
|
|
115
|
+
- 🥈 Silver — Completed with Sonnet
|
|
116
|
+
- 🥉 Bronze — Completed with Opus
|
|
117
|
+
|
|
118
|
+
**Efficiency**
|
|
119
|
+
- 🎯 Sniper — Under 25% of budget used
|
|
120
|
+
- ⚡ Efficient — Under 50% of budget used
|
|
121
|
+
- 🪙 Penny Pincher — Total spend under $0.10
|
|
122
|
+
|
|
123
|
+
**Effort**
|
|
124
|
+
- 🎯 Speedrunner — Low effort, completed under budget
|
|
125
|
+
- 💪 Tryhard — High/max effort, LEGENDARY efficiency
|
|
126
|
+
- 👑 Archmagus — Opus at max effort, completed
|
|
127
|
+
|
|
128
|
+
**Fast mode**
|
|
129
|
+
- ⚡ Lightning Run — Opus fast mode, completed under budget
|
|
130
|
+
- 🎰 Daredevil — Opus fast mode, LEGENDARY efficiency
|
|
131
|
+
|
|
132
|
+
**Ultrathink**
|
|
133
|
+
- 🔮 Spell Cast — Used extended thinking during the run
|
|
134
|
+
- 🧠 Calculated Risk — Ultrathink + LEGENDARY efficiency
|
|
135
|
+
- 🌀 Deep Thinker — 3+ ultrathink invocations, completed under budget
|
|
136
|
+
- 🤫 Silent Run — No extended thinking, SOLID or better *(think without thinking)*
|
|
137
|
+
- 🤦 Hubris — Used ultrathink, busted anyway *(death achievement)*
|
|
138
|
+
|
|
139
|
+
**Multi-model**
|
|
140
|
+
- 🏹 Frugal — Haiku handled ≥50% of session cost
|
|
141
|
+
- 🎲 Rogue Run — Haiku handled ≥75% of session cost
|
|
142
|
+
|
|
143
|
+
**Rest & recovery**
|
|
144
|
+
- ⚡ No Rest for the Wicked — Completed in one session
|
|
145
|
+
- 🏕️ Made Camp — Completed across multiple sessions
|
|
146
|
+
- 💪 Came Back — Fainted (hit usage limits) and finished anyway
|
|
147
|
+
|
|
148
|
+
**Context management (gear)**
|
|
149
|
+
- 📦 Overencumbered — Context auto-compacted during run
|
|
150
|
+
- 🎒 Traveling Light — Manual compact at ≤50% context
|
|
151
|
+
- 🪶 Ultralight — Manual compact at ≤40% context
|
|
152
|
+
- 🥷 Ghost Run — Manual compact at ≤30% context
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Live HUD
|
|
157
|
+
|
|
158
|
+
After `tokengolf install`, a status line appears in every Claude Code session:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
───────────────
|
|
162
|
+
⛳ implement pagination | 💎 $0.1203/$0.50 24% | LEGENDARY | 🪶 38% | ⚔️ Sonnet·High | Floor 1/5
|
|
163
|
+
───────────────
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
- **tier emoji** (💎🥇🥈🥉💸) updates live as cost accumulates
|
|
167
|
+
- **🪶 green** at 50–74% context — traveling light
|
|
168
|
+
- **🎒 yellow** at 75–89% context — getting heavy
|
|
169
|
+
- **📦 red** at 90%+ context — overencumbered, consider compacting
|
|
170
|
+
- **💤** instead of ⛳ if the previous session fainted (hit usage limits)
|
|
171
|
+
- Roguelike runs show floor progress; Flow runs omit budget/efficiency
|
|
172
|
+
|
|
173
|
+
<!-- SCREENSHOT: Claude Code split-screen showing the TokenGolf HUD in the status bar -->
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Auto Scorecard
|
|
178
|
+
|
|
179
|
+
When you `/exit` a Claude Code session, the scorecard appears automatically:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
╔════════════════════════════════════════════════════════════════════╗
|
|
183
|
+
║ 🏆 SESSION COMPLETE ║
|
|
184
|
+
║ implement pagination for /users ║
|
|
185
|
+
╠════════════════════════════════════════════════════════════════════╣
|
|
186
|
+
║ $0.18/$0.50 36% ⚡ EFFICIENT ⚔️ Sonnet·High 🥇 Gold ║
|
|
187
|
+
╠════════════════════════════════════════════════════════════════════╣
|
|
188
|
+
║ 🔮 1 ultrathink invocation ~8.4K thinking tokens ║
|
|
189
|
+
╠════════════════════════════════════════════════════════════════════╣
|
|
190
|
+
║ 🥈 silver_sonnet 🎯 sniper 🔮 spell_cast 🧠 calculated_risk ║
|
|
191
|
+
╠════════════════════════════════════════════════════════════════════╣
|
|
192
|
+
║ tokengolf scorecard · tokengolf start · tokengolf stats ║
|
|
193
|
+
╚════════════════════════════════════════════════════════════════════╝
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
<!-- SCREENSHOT: Auto-displayed scorecard after /exit in Claude Code terminal -->
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Hooks
|
|
201
|
+
|
|
202
|
+
Six hooks installed via `tokengolf install`:
|
|
203
|
+
|
|
204
|
+
| Hook | When | What it does |
|
|
205
|
+
|------|------|-------------|
|
|
206
|
+
| `SessionStart` | Session opens | Injects quest/budget/floor into Claude's context. Auto-creates Flow run if none active. Increments session count for multi-session runs. |
|
|
207
|
+
| `PostToolUse` | After every tool | Tracks tool usage by type. Fires budget warning at 80%. |
|
|
208
|
+
| `UserPromptSubmit` | Each prompt | Counts prompts. Injects halfway nudge at 50% budget. |
|
|
209
|
+
| `PreCompact` | Before compaction | Records manual vs auto compact + context % — powers gear achievements. |
|
|
210
|
+
| `SessionEnd` | Session closes | Scans transcripts for cost + ultrathink blocks, saves run, displays ANSI scorecard. Detects Fainted if session ended unexpectedly (usage limit hit). |
|
|
211
|
+
| `StatusLine` | Continuously | Live HUD with cost, tier, efficiency, context %, model class. |
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## The Meta Loop
|
|
216
|
+
|
|
217
|
+
The dungeon crawl framing maps directly to real mechanics:
|
|
218
|
+
|
|
219
|
+
- **Overencumbered** = context bloat slowing you down
|
|
220
|
+
- **Made Camp** = hit usage limits, came back next session
|
|
221
|
+
- **Ghost Run** = surgical context management before the boss
|
|
222
|
+
- **Hubris** = reached for ultrathink on a tight budget and paid for it
|
|
223
|
+
- **Silent Run** = solved it with pure prompting discipline, no extended thinking needed
|
|
224
|
+
|
|
225
|
+
Roguelike mode surfaces these patterns explicitly. Flow mode lets them compound over time.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## State
|
|
230
|
+
|
|
231
|
+
All data lives in `~/.tokengolf/`:
|
|
232
|
+
- `current-run.json` — active run
|
|
233
|
+
- `runs.json` — completed run history
|
|
234
|
+
|
|
235
|
+
No database, no native deps, no compilation.
|