tokengolf 0.4.1 → 0.4.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/dist/cli.js +1360 -643
- package/docs/index.html +844 -375
- package/package.json +1 -1
- package/scripts/demo-to-html.js +104 -0
- package/src/cli.js +45 -5
- package/src/lib/demo-active.js +56 -0
- package/src/lib/demo-fixtures.js +496 -0
- package/src/lib/demo-hud.js +3 -0
- package/src/lib/demo-render.js +16 -0
- package/src/lib/demo-scorecard.js +56 -0
- package/src/lib/demo-stats.js +56 -0
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
// Shared fixture data for all demo variants
|
|
2
|
+
// Each fixture is a realistic run/stats object ready to render
|
|
3
|
+
|
|
4
|
+
const NOW = '2026-03-10T14:00:00Z';
|
|
5
|
+
const AGO = (mins) => new Date(Date.now() - mins * 60000).toISOString();
|
|
6
|
+
|
|
7
|
+
// ─── ScoreCard fixtures ─────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export const SCORECARD_FIXTURES = [
|
|
10
|
+
{
|
|
11
|
+
title: 'Won · Sonnet · LEGENDARY · multi-model · achievements',
|
|
12
|
+
run: {
|
|
13
|
+
id: 'run_demo_1',
|
|
14
|
+
quest: 'add pagination to /users endpoint',
|
|
15
|
+
model: 'claude-sonnet-4-6',
|
|
16
|
+
budget: 1.5,
|
|
17
|
+
spent: 0.23,
|
|
18
|
+
status: 'won',
|
|
19
|
+
mode: 'roguelike',
|
|
20
|
+
effort: 'high',
|
|
21
|
+
fastMode: false,
|
|
22
|
+
floor: 5,
|
|
23
|
+
totalFloors: 5,
|
|
24
|
+
promptCount: 6,
|
|
25
|
+
totalToolCalls: 18,
|
|
26
|
+
toolCalls: { Read: 8, Edit: 4, Bash: 3, Grep: 2, Glob: 1 },
|
|
27
|
+
failedToolCalls: 0,
|
|
28
|
+
subagentSpawns: 0,
|
|
29
|
+
turnCount: 14,
|
|
30
|
+
thinkingInvocations: 0,
|
|
31
|
+
thinkingTokens: 0,
|
|
32
|
+
sessionCount: 1,
|
|
33
|
+
fainted: false,
|
|
34
|
+
compactionEvents: [],
|
|
35
|
+
modelBreakdown: { 'claude-sonnet-4-6': 0.19, 'claude-haiku-4-5-20251001': 0.04 },
|
|
36
|
+
achievements: [
|
|
37
|
+
{ key: 'sniper', label: 'Sniper — Under 25% budget', emoji: '🎯' },
|
|
38
|
+
{ key: 'silver_sonnet', label: 'Silver — Completed with Sonnet', emoji: '🥈' },
|
|
39
|
+
{ key: 'no_rest', label: 'No Rest for the Wicked — One session', emoji: '🔥' },
|
|
40
|
+
{ key: 'clean_run', label: 'Clean Run — Zero failed tool calls', emoji: '✅' },
|
|
41
|
+
{ key: 'toolbox', label: 'Toolbox — 5+ distinct tool types', emoji: '🧰' },
|
|
42
|
+
{ key: 'silent_run', label: 'Silent Run — No extended thinking', emoji: '🤫' },
|
|
43
|
+
],
|
|
44
|
+
startedAt: AGO(12),
|
|
45
|
+
endedAt: NOW,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
title: 'Won · Flow mode · minimal',
|
|
50
|
+
run: {
|
|
51
|
+
id: 'run_demo_2',
|
|
52
|
+
quest: null,
|
|
53
|
+
model: 'claude-sonnet-4-6',
|
|
54
|
+
budget: null,
|
|
55
|
+
spent: 0.0034,
|
|
56
|
+
status: 'won',
|
|
57
|
+
mode: 'flow',
|
|
58
|
+
effort: null,
|
|
59
|
+
fastMode: false,
|
|
60
|
+
floor: 1,
|
|
61
|
+
totalFloors: 5,
|
|
62
|
+
promptCount: 2,
|
|
63
|
+
totalToolCalls: 5,
|
|
64
|
+
toolCalls: { Read: 3, Edit: 2 },
|
|
65
|
+
failedToolCalls: 0,
|
|
66
|
+
subagentSpawns: 0,
|
|
67
|
+
turnCount: 3,
|
|
68
|
+
thinkingInvocations: 0,
|
|
69
|
+
thinkingTokens: 0,
|
|
70
|
+
sessionCount: 1,
|
|
71
|
+
fainted: false,
|
|
72
|
+
compactionEvents: [],
|
|
73
|
+
modelBreakdown: { 'claude-sonnet-4-6': 0.0034 },
|
|
74
|
+
achievements: [
|
|
75
|
+
{ key: 'penny', label: 'Penny Pincher — Under $0.10', emoji: '🪙' },
|
|
76
|
+
{ key: 'cheap_shots', label: 'Cheap Shots — Under $0.01/prompt', emoji: '💲' },
|
|
77
|
+
],
|
|
78
|
+
startedAt: AGO(3),
|
|
79
|
+
endedAt: NOW,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
title: 'Won · Opus · ultrathink · EFFICIENT',
|
|
84
|
+
run: {
|
|
85
|
+
id: 'run_demo_3',
|
|
86
|
+
quest: 'refactor auth middleware to support SAML SSO',
|
|
87
|
+
model: 'claude-opus-4-6',
|
|
88
|
+
budget: 7.5,
|
|
89
|
+
spent: 3.41,
|
|
90
|
+
status: 'won',
|
|
91
|
+
mode: 'roguelike',
|
|
92
|
+
effort: 'max',
|
|
93
|
+
fastMode: false,
|
|
94
|
+
floor: 5,
|
|
95
|
+
totalFloors: 5,
|
|
96
|
+
promptCount: 8,
|
|
97
|
+
totalToolCalls: 32,
|
|
98
|
+
toolCalls: { Read: 12, Edit: 8, Bash: 6, Grep: 4, Write: 2 },
|
|
99
|
+
failedToolCalls: 1,
|
|
100
|
+
subagentSpawns: 3,
|
|
101
|
+
turnCount: 24,
|
|
102
|
+
thinkingInvocations: 4,
|
|
103
|
+
thinkingTokens: 12400,
|
|
104
|
+
sessionCount: 2,
|
|
105
|
+
fainted: true,
|
|
106
|
+
compactionEvents: [{ trigger: 'auto', context_window: { used_percentage: 92 } }],
|
|
107
|
+
modelBreakdown: { 'claude-opus-4-6': 3.22, 'claude-haiku-4-5-20251001': 0.19 },
|
|
108
|
+
achievements: [
|
|
109
|
+
{ key: 'efficient', label: 'Efficient — Under 50% budget', emoji: '⚡' },
|
|
110
|
+
{ key: 'bronze_opus', label: 'Bronze — Completed with Opus', emoji: '🥉' },
|
|
111
|
+
{ key: 'archmagus', label: 'Archmagus — Opus max effort, completed', emoji: '👑' },
|
|
112
|
+
{ key: 'deep_thinker', label: 'Deep Thinker — 3+ ultrathink invocations', emoji: '🌀' },
|
|
113
|
+
{ key: 'spell_cast', label: 'Spell Cast — Used extended thinking', emoji: '🔮' },
|
|
114
|
+
{ key: 'came_back', label: 'Came Back — Fainted and finished', emoji: '🧟' },
|
|
115
|
+
{ key: 'overencumbered', label: 'Overencumbered — Auto-compacted', emoji: '📦' },
|
|
116
|
+
{ key: 'summoner', label: 'Summoner — 3+ subagents spawned', emoji: '📡' },
|
|
117
|
+
],
|
|
118
|
+
startedAt: AGO(95),
|
|
119
|
+
endedAt: NOW,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
title: 'Won · Haiku · Diamond · one-shot',
|
|
124
|
+
run: {
|
|
125
|
+
id: 'run_demo_4',
|
|
126
|
+
quest: 'fix typo in README badge URL',
|
|
127
|
+
model: 'claude-haiku-4-5-20251001',
|
|
128
|
+
budget: 0.15,
|
|
129
|
+
spent: 0.0089,
|
|
130
|
+
status: 'won',
|
|
131
|
+
mode: 'roguelike',
|
|
132
|
+
effort: null,
|
|
133
|
+
fastMode: false,
|
|
134
|
+
floor: 5,
|
|
135
|
+
totalFloors: 5,
|
|
136
|
+
promptCount: 1,
|
|
137
|
+
totalToolCalls: 3,
|
|
138
|
+
toolCalls: { Read: 1, Edit: 1, Bash: 1 },
|
|
139
|
+
failedToolCalls: 0,
|
|
140
|
+
subagentSpawns: 0,
|
|
141
|
+
turnCount: 2,
|
|
142
|
+
thinkingInvocations: 0,
|
|
143
|
+
thinkingTokens: 0,
|
|
144
|
+
sessionCount: 1,
|
|
145
|
+
fainted: false,
|
|
146
|
+
compactionEvents: [],
|
|
147
|
+
modelBreakdown: { 'claude-haiku-4-5-20251001': 0.0089 },
|
|
148
|
+
achievements: [
|
|
149
|
+
{ key: 'diamond', label: 'Diamond — Haiku under $0.10', emoji: '💎' },
|
|
150
|
+
{ key: 'gold_haiku', label: 'Gold — Completed with Haiku', emoji: '🥇' },
|
|
151
|
+
{ key: 'one_shot', label: 'One Shot — Single prompt', emoji: '🥊' },
|
|
152
|
+
{ key: 'sniper', label: 'Sniper — Under 25% budget', emoji: '🎯' },
|
|
153
|
+
{ key: 'penny', label: 'Penny Pincher — Under $0.10', emoji: '🪙' },
|
|
154
|
+
{ key: 'speedrun', label: 'Speedrun — Under 5 minutes', emoji: '⏱️' },
|
|
155
|
+
{ key: 'lone_wolf', label: 'Lone Wolf — No subagents', emoji: '🐺' },
|
|
156
|
+
{ key: 'surgeon', label: 'Surgeon — 1-3 edits, under budget', emoji: '🔪' },
|
|
157
|
+
],
|
|
158
|
+
startedAt: AGO(2),
|
|
159
|
+
endedAt: NOW,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
title: 'Died · Sonnet · BUSTED · death marks',
|
|
164
|
+
run: {
|
|
165
|
+
id: 'run_demo_5',
|
|
166
|
+
quest: 'migrate postgres schema to v3',
|
|
167
|
+
model: 'claude-sonnet-4-6',
|
|
168
|
+
budget: 1.5,
|
|
169
|
+
spent: 3.87,
|
|
170
|
+
status: 'died',
|
|
171
|
+
mode: 'roguelike',
|
|
172
|
+
effort: 'high',
|
|
173
|
+
fastMode: false,
|
|
174
|
+
floor: 3,
|
|
175
|
+
totalFloors: 5,
|
|
176
|
+
promptCount: 22,
|
|
177
|
+
totalToolCalls: 48,
|
|
178
|
+
toolCalls: { Read: 18, Edit: 12, Bash: 10, Grep: 5, Write: 3 },
|
|
179
|
+
failedToolCalls: 7,
|
|
180
|
+
subagentSpawns: 2,
|
|
181
|
+
turnCount: 34,
|
|
182
|
+
thinkingInvocations: 2,
|
|
183
|
+
thinkingTokens: 5200,
|
|
184
|
+
sessionCount: 1,
|
|
185
|
+
fainted: false,
|
|
186
|
+
compactionEvents: [
|
|
187
|
+
{ trigger: 'auto', context_window: { used_percentage: 94 } },
|
|
188
|
+
{ trigger: 'auto', context_window: { used_percentage: 91 } },
|
|
189
|
+
],
|
|
190
|
+
modelBreakdown: { 'claude-sonnet-4-6': 3.52, 'claude-haiku-4-5-20251001': 0.35 },
|
|
191
|
+
achievements: [
|
|
192
|
+
{ key: 'hubris', label: 'Hubris — Ultrathink, busted anyway', emoji: '🤦' },
|
|
193
|
+
{ key: 'blowout', label: 'Blowout — Spent 2× budget', emoji: '💥' },
|
|
194
|
+
{ key: 'fumble', label: 'Fumble — 5+ failed tool calls', emoji: '🤡' },
|
|
195
|
+
{ key: 'tool_happy', label: 'Tool Happy — 30+ tool calls, died', emoji: '🔨' },
|
|
196
|
+
{ key: 'expensive_taste', label: 'Expensive Taste — $0.50+/prompt', emoji: '🍷' },
|
|
197
|
+
],
|
|
198
|
+
startedAt: AGO(45),
|
|
199
|
+
endedAt: NOW,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
title: 'Died · Haiku · SO CLOSE',
|
|
204
|
+
run: {
|
|
205
|
+
id: 'run_demo_6',
|
|
206
|
+
quest: 'fix N+1 query in dashboard loader',
|
|
207
|
+
model: 'claude-haiku-4-5-20251001',
|
|
208
|
+
budget: 0.4,
|
|
209
|
+
spent: 0.43,
|
|
210
|
+
status: 'died',
|
|
211
|
+
mode: 'roguelike',
|
|
212
|
+
effort: null,
|
|
213
|
+
fastMode: false,
|
|
214
|
+
floor: 4,
|
|
215
|
+
totalFloors: 5,
|
|
216
|
+
promptCount: 9,
|
|
217
|
+
totalToolCalls: 15,
|
|
218
|
+
toolCalls: { Read: 7, Edit: 4, Bash: 3, Grep: 1 },
|
|
219
|
+
failedToolCalls: 1,
|
|
220
|
+
subagentSpawns: 0,
|
|
221
|
+
turnCount: 12,
|
|
222
|
+
thinkingInvocations: 0,
|
|
223
|
+
thinkingTokens: 0,
|
|
224
|
+
sessionCount: 1,
|
|
225
|
+
fainted: false,
|
|
226
|
+
compactionEvents: [],
|
|
227
|
+
modelBreakdown: { 'claude-haiku-4-5-20251001': 0.43 },
|
|
228
|
+
achievements: [
|
|
229
|
+
{ key: 'so_close', label: 'So Close — Died within 10% of budget', emoji: '😭' },
|
|
230
|
+
{ key: 'silent_death', label: 'Silent Death — Died with 2 prompts', emoji: '🪦' },
|
|
231
|
+
],
|
|
232
|
+
startedAt: AGO(18),
|
|
233
|
+
endedAt: NOW,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
title: 'Won · Paladin · Architect · multi-model breakdown',
|
|
238
|
+
run: {
|
|
239
|
+
id: 'run_demo_7',
|
|
240
|
+
quest: 'design and implement webhook retry system',
|
|
241
|
+
model: 'opusplan',
|
|
242
|
+
budget: 4.5,
|
|
243
|
+
spent: 2.18,
|
|
244
|
+
status: 'won',
|
|
245
|
+
mode: 'roguelike',
|
|
246
|
+
effort: 'high',
|
|
247
|
+
fastMode: false,
|
|
248
|
+
floor: 5,
|
|
249
|
+
totalFloors: 5,
|
|
250
|
+
promptCount: 10,
|
|
251
|
+
totalToolCalls: 28,
|
|
252
|
+
toolCalls: { Read: 10, Edit: 7, Bash: 5, Grep: 3, Write: 2, Agent: 1 },
|
|
253
|
+
failedToolCalls: 0,
|
|
254
|
+
subagentSpawns: 1,
|
|
255
|
+
turnCount: 20,
|
|
256
|
+
thinkingInvocations: 2,
|
|
257
|
+
thinkingTokens: 8100,
|
|
258
|
+
sessionCount: 1,
|
|
259
|
+
fainted: false,
|
|
260
|
+
compactionEvents: [],
|
|
261
|
+
modelBreakdown: {
|
|
262
|
+
'claude-opus-4-6': 1.42,
|
|
263
|
+
'claude-sonnet-4-6': 0.68,
|
|
264
|
+
'claude-haiku-4-5-20251001': 0.08,
|
|
265
|
+
},
|
|
266
|
+
achievements: [
|
|
267
|
+
{ key: 'paladin', label: 'Paladin — Opus planning mode', emoji: '⚜️' },
|
|
268
|
+
{ key: 'efficient', label: 'Efficient — Under 50% budget', emoji: '⚡' },
|
|
269
|
+
{ key: 'architect', label: 'Architect — Opus handled 65% of cost', emoji: '🏛️' },
|
|
270
|
+
{ key: 'spell_cast', label: 'Spell Cast — Used extended thinking', emoji: '🔮' },
|
|
271
|
+
{ key: 'clean_run', label: 'Clean Run — Zero failed tool calls', emoji: '✅' },
|
|
272
|
+
{ key: 'toolbox', label: 'Toolbox — 6 distinct tool types', emoji: '🧰' },
|
|
273
|
+
],
|
|
274
|
+
startedAt: AGO(35),
|
|
275
|
+
endedAt: NOW,
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
// ─── ActiveRun fixtures ─────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
export const ACTIVERUN_FIXTURES = [
|
|
283
|
+
{
|
|
284
|
+
title: 'Roguelike · Sonnet · early run · Floor 1',
|
|
285
|
+
run: {
|
|
286
|
+
id: 'run_demo_a1',
|
|
287
|
+
quest: 'implement rate limiting middleware',
|
|
288
|
+
model: 'claude-sonnet-4-6',
|
|
289
|
+
budget: 1.5,
|
|
290
|
+
spent: 0.12,
|
|
291
|
+
status: 'active',
|
|
292
|
+
mode: 'roguelike',
|
|
293
|
+
effort: 'high',
|
|
294
|
+
floor: 1,
|
|
295
|
+
totalFloors: 5,
|
|
296
|
+
promptCount: 3,
|
|
297
|
+
totalToolCalls: 8,
|
|
298
|
+
toolCalls: { Read: 4, Grep: 2, Glob: 2 },
|
|
299
|
+
startedAt: AGO(8),
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
title: 'Roguelike · Opus · mid-run · Floor 3 · budget warning',
|
|
304
|
+
run: {
|
|
305
|
+
id: 'run_demo_a2',
|
|
306
|
+
quest: 'refactor database connection pooling',
|
|
307
|
+
model: 'claude-opus-4-6',
|
|
308
|
+
budget: 7.5,
|
|
309
|
+
spent: 6.34,
|
|
310
|
+
status: 'active',
|
|
311
|
+
mode: 'roguelike',
|
|
312
|
+
effort: 'max',
|
|
313
|
+
floor: 3,
|
|
314
|
+
totalFloors: 5,
|
|
315
|
+
promptCount: 12,
|
|
316
|
+
totalToolCalls: 35,
|
|
317
|
+
toolCalls: { Read: 14, Edit: 9, Bash: 8, Grep: 3, Write: 1 },
|
|
318
|
+
startedAt: AGO(42),
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
title: 'Flow mode · Haiku · light session',
|
|
323
|
+
run: {
|
|
324
|
+
id: 'run_demo_a3',
|
|
325
|
+
quest: 'Flow',
|
|
326
|
+
model: 'claude-haiku-4-5-20251001',
|
|
327
|
+
budget: null,
|
|
328
|
+
spent: 0.018,
|
|
329
|
+
status: 'active',
|
|
330
|
+
mode: 'flow',
|
|
331
|
+
effort: null,
|
|
332
|
+
floor: 1,
|
|
333
|
+
totalFloors: 5,
|
|
334
|
+
promptCount: 4,
|
|
335
|
+
totalToolCalls: 11,
|
|
336
|
+
toolCalls: { Read: 6, Grep: 3, Bash: 2 },
|
|
337
|
+
startedAt: AGO(5),
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
title: 'Roguelike · Haiku · Floor 4 · CLOSE CALL',
|
|
342
|
+
run: {
|
|
343
|
+
id: 'run_demo_a4',
|
|
344
|
+
quest: 'fix flaky test in CI pipeline',
|
|
345
|
+
model: 'claude-haiku-4-5-20251001',
|
|
346
|
+
budget: 0.4,
|
|
347
|
+
spent: 0.31,
|
|
348
|
+
status: 'active',
|
|
349
|
+
mode: 'roguelike',
|
|
350
|
+
effort: null,
|
|
351
|
+
floor: 4,
|
|
352
|
+
totalFloors: 5,
|
|
353
|
+
promptCount: 7,
|
|
354
|
+
totalToolCalls: 16,
|
|
355
|
+
toolCalls: { Read: 6, Bash: 5, Edit: 3, Grep: 2 },
|
|
356
|
+
startedAt: AGO(15),
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
// ─── StatsView fixtures ─────────────────────────────────────────────
|
|
362
|
+
|
|
363
|
+
export const STATS_FIXTURES = [
|
|
364
|
+
{
|
|
365
|
+
title: 'Veteran player — 24 runs, mixed results',
|
|
366
|
+
stats: {
|
|
367
|
+
total: 24,
|
|
368
|
+
wins: 18,
|
|
369
|
+
deaths: 6,
|
|
370
|
+
winRate: 75,
|
|
371
|
+
avgSpend: 1.42,
|
|
372
|
+
bestRun: {
|
|
373
|
+
quest: 'fix typo in README badge URL',
|
|
374
|
+
model: 'claude-haiku-4-5-20251001',
|
|
375
|
+
spent: 0.0089,
|
|
376
|
+
budget: 0.15,
|
|
377
|
+
},
|
|
378
|
+
recentRuns: [
|
|
379
|
+
{
|
|
380
|
+
status: 'won',
|
|
381
|
+
quest: 'add pagination to /users',
|
|
382
|
+
spent: 0.23,
|
|
383
|
+
budget: 1.5,
|
|
384
|
+
model: 'claude-sonnet-4-6',
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
status: 'won',
|
|
388
|
+
quest: 'refactor auth middleware',
|
|
389
|
+
spent: 3.41,
|
|
390
|
+
budget: 7.5,
|
|
391
|
+
model: 'claude-opus-4-6',
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
status: 'died',
|
|
395
|
+
quest: 'migrate postgres schema',
|
|
396
|
+
spent: 3.87,
|
|
397
|
+
budget: 1.5,
|
|
398
|
+
model: 'claude-sonnet-4-6',
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
status: 'won',
|
|
402
|
+
quest: 'fix N+1 query',
|
|
403
|
+
spent: 0.08,
|
|
404
|
+
budget: 0.4,
|
|
405
|
+
model: 'claude-haiku-4-5-20251001',
|
|
406
|
+
},
|
|
407
|
+
{ status: 'won', quest: null, spent: 0.0034, model: 'claude-sonnet-4-6' },
|
|
408
|
+
{
|
|
409
|
+
status: 'won',
|
|
410
|
+
quest: 'webhook retry system',
|
|
411
|
+
spent: 2.18,
|
|
412
|
+
budget: 4.5,
|
|
413
|
+
model: 'opusplan',
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
status: 'died',
|
|
417
|
+
quest: 'rewrite billing module',
|
|
418
|
+
spent: 12.4,
|
|
419
|
+
budget: 7.5,
|
|
420
|
+
model: 'claude-opus-4-6',
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
status: 'won',
|
|
424
|
+
quest: 'add dark mode toggle',
|
|
425
|
+
spent: 0.41,
|
|
426
|
+
budget: 1.5,
|
|
427
|
+
model: 'claude-sonnet-4-6',
|
|
428
|
+
},
|
|
429
|
+
],
|
|
430
|
+
achievements: [
|
|
431
|
+
{ emoji: '🎯', label: 'Sniper' },
|
|
432
|
+
{ emoji: '🥈', label: 'Silver' },
|
|
433
|
+
{ emoji: '🔥', label: 'No Rest' },
|
|
434
|
+
{ emoji: '✅', label: 'Clean Run' },
|
|
435
|
+
{ emoji: '🧰', label: 'Toolbox' },
|
|
436
|
+
{ emoji: '💎', label: 'Diamond' },
|
|
437
|
+
{ emoji: '🥊', label: 'One Shot' },
|
|
438
|
+
{ emoji: '🪙', label: 'Penny Pincher' },
|
|
439
|
+
{ emoji: '⏱️', label: 'Speedrun' },
|
|
440
|
+
{ emoji: '🐺', label: 'Lone Wolf' },
|
|
441
|
+
{ emoji: '🔪', label: 'Surgeon' },
|
|
442
|
+
{ emoji: '⚡', label: 'Efficient' },
|
|
443
|
+
],
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
title: 'New player — 3 runs',
|
|
448
|
+
stats: {
|
|
449
|
+
total: 3,
|
|
450
|
+
wins: 2,
|
|
451
|
+
deaths: 1,
|
|
452
|
+
winRate: 67,
|
|
453
|
+
avgSpend: 0.89,
|
|
454
|
+
bestRun: {
|
|
455
|
+
quest: 'add loading spinner',
|
|
456
|
+
model: 'claude-sonnet-4-6',
|
|
457
|
+
spent: 0.34,
|
|
458
|
+
budget: 1.5,
|
|
459
|
+
},
|
|
460
|
+
recentRuns: [
|
|
461
|
+
{
|
|
462
|
+
status: 'won',
|
|
463
|
+
quest: 'add loading spinner',
|
|
464
|
+
spent: 0.34,
|
|
465
|
+
budget: 1.5,
|
|
466
|
+
model: 'claude-sonnet-4-6',
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
status: 'died',
|
|
470
|
+
quest: 'setup auth flow',
|
|
471
|
+
spent: 1.92,
|
|
472
|
+
budget: 1.5,
|
|
473
|
+
model: 'claude-sonnet-4-6',
|
|
474
|
+
},
|
|
475
|
+
{ status: 'won', quest: null, spent: 0.41, model: 'claude-sonnet-4-6' },
|
|
476
|
+
],
|
|
477
|
+
achievements: [
|
|
478
|
+
{ emoji: '🥈', label: 'Silver' },
|
|
479
|
+
{ emoji: '🔥', label: 'No Rest' },
|
|
480
|
+
],
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
title: 'Empty — no runs yet',
|
|
485
|
+
stats: {
|
|
486
|
+
total: 0,
|
|
487
|
+
wins: 0,
|
|
488
|
+
deaths: 0,
|
|
489
|
+
winRate: 0,
|
|
490
|
+
avgSpend: 0,
|
|
491
|
+
bestRun: null,
|
|
492
|
+
recentRuns: [],
|
|
493
|
+
achievements: [],
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Shared render helper for demo mode — provides a fake stdin so useInput doesn't crash
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
|
|
5
|
+
function makeFakeStdin() {
|
|
6
|
+
const s = new Readable({ read() {} });
|
|
7
|
+
s.setRawMode = () => s;
|
|
8
|
+
s.ref = () => s;
|
|
9
|
+
s.unref = () => s;
|
|
10
|
+
s.isTTY = true;
|
|
11
|
+
return s;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function demoRender(element) {
|
|
15
|
+
return render(element, { stdin: makeFakeStdin() });
|
|
16
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Demo: ScoreCard variants (Ink)
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import { demoRender } from './demo-render.js';
|
|
5
|
+
import { ScoreCard } from '../components/ScoreCard.js';
|
|
6
|
+
import { SCORECARD_FIXTURES } from './demo-fixtures.js';
|
|
7
|
+
|
|
8
|
+
function DemoScoreCard({ fixture }) {
|
|
9
|
+
return (
|
|
10
|
+
<Box flexDirection="column">
|
|
11
|
+
<Box paddingX={1}>
|
|
12
|
+
<Text color="gray" dimColor>
|
|
13
|
+
{'─'.repeat(50)}
|
|
14
|
+
</Text>
|
|
15
|
+
</Box>
|
|
16
|
+
<Box paddingX={1}>
|
|
17
|
+
<Text color="gray" dimColor italic>
|
|
18
|
+
{fixture.title}
|
|
19
|
+
</Text>
|
|
20
|
+
</Box>
|
|
21
|
+
<ScoreCard run={fixture.run} />
|
|
22
|
+
</Box>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function runScoreCardDemo(index) {
|
|
27
|
+
const fixtures = index != null ? [SCORECARD_FIXTURES[index]] : SCORECARD_FIXTURES;
|
|
28
|
+
if (!fixtures[0]) {
|
|
29
|
+
console.log(
|
|
30
|
+
`Invalid index. ${SCORECARD_FIXTURES.length} scorecards available (0-${SCORECARD_FIXTURES.length - 1}).`
|
|
31
|
+
);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log('');
|
|
36
|
+
console.log('\x1b[1m\x1b[36m⛳ TokenGolf — ScoreCard Demo\x1b[0m');
|
|
37
|
+
console.log(`\x1b[2m${fixtures.length} variant${fixtures.length > 1 ? 's' : ''}\x1b[0m`);
|
|
38
|
+
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
let i = 0;
|
|
41
|
+
function renderNext() {
|
|
42
|
+
if (i >= fixtures.length) {
|
|
43
|
+
resolve();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const fixture = fixtures[i];
|
|
47
|
+
const inst = demoRender(React.createElement(DemoScoreCard, { fixture }));
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
inst.unmount();
|
|
50
|
+
i++;
|
|
51
|
+
renderNext();
|
|
52
|
+
}, 100);
|
|
53
|
+
}
|
|
54
|
+
renderNext();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Demo: StatsView variants (Ink)
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import { demoRender } from './demo-render.js';
|
|
5
|
+
import { StatsView } from '../components/StatsView.js';
|
|
6
|
+
import { STATS_FIXTURES } from './demo-fixtures.js';
|
|
7
|
+
|
|
8
|
+
function DemoStatsView({ fixture }) {
|
|
9
|
+
return (
|
|
10
|
+
<Box flexDirection="column">
|
|
11
|
+
<Box paddingX={1}>
|
|
12
|
+
<Text color="gray" dimColor>
|
|
13
|
+
{'─'.repeat(50)}
|
|
14
|
+
</Text>
|
|
15
|
+
</Box>
|
|
16
|
+
<Box paddingX={1}>
|
|
17
|
+
<Text color="gray" dimColor italic>
|
|
18
|
+
{fixture.title}
|
|
19
|
+
</Text>
|
|
20
|
+
</Box>
|
|
21
|
+
<StatsView stats={fixture.stats} />
|
|
22
|
+
</Box>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function runStatsDemo(index) {
|
|
27
|
+
const fixtures = index != null ? [STATS_FIXTURES[index]] : STATS_FIXTURES;
|
|
28
|
+
if (!fixtures[0]) {
|
|
29
|
+
console.log(
|
|
30
|
+
`Invalid index. ${STATS_FIXTURES.length} variants available (0-${STATS_FIXTURES.length - 1}).`
|
|
31
|
+
);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log('');
|
|
36
|
+
console.log('\x1b[1m\x1b[36m⛳ TokenGolf — StatsView Demo\x1b[0m');
|
|
37
|
+
console.log(`\x1b[2m${fixtures.length} variant${fixtures.length > 1 ? 's' : ''}\x1b[0m`);
|
|
38
|
+
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
let i = 0;
|
|
41
|
+
function renderNext() {
|
|
42
|
+
if (i >= fixtures.length) {
|
|
43
|
+
resolve();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const fixture = fixtures[i];
|
|
47
|
+
const inst = demoRender(React.createElement(DemoStatsView, { fixture }));
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
inst.unmount();
|
|
50
|
+
i++;
|
|
51
|
+
renderNext();
|
|
52
|
+
}, 100);
|
|
53
|
+
}
|
|
54
|
+
renderNext();
|
|
55
|
+
});
|
|
56
|
+
}
|