ai-cto 0.1.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/README.md +72 -0
- package/bin/cli.js +491 -0
- package/docs/BALANCE.md +99 -0
- package/docs/COPY.md +121 -0
- package/docs/PLAN.md +0 -0
- package/docs/PRD.md +195 -0
- package/docs/SCHEMA.md +293 -0
- package/docs/STACK.md +204 -0
- package/package.json +20 -0
- package/scripts/playtest.js +147 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# 🤖 AI CTO
|
|
2
|
+
|
|
3
|
+
> **Run an AI startup for 5 minutes. Every request is a business decision.**
|
|
4
|
+
|
|
5
|
+
Choose the cheapest model that satisfies the customer. Higher profit wins. No tutorials — learn by making mistakes.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🎮 Play Options
|
|
10
|
+
|
|
11
|
+
### Option A: Minimal Terminal CLI (Fastest Playtest)
|
|
12
|
+
|
|
13
|
+
Play directly in your terminal with ultra-clean, minimal text output:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Play terminal edition
|
|
17
|
+
npm run play
|
|
18
|
+
|
|
19
|
+
# Or execute via npx
|
|
20
|
+
npx .
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Option B: Visual Web App Dashboard
|
|
24
|
+
|
|
25
|
+
Play in your browser with real-time HUD stats and animated turn cards:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Start local development server
|
|
29
|
+
npm run dev
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🚀 Quick Highlights
|
|
35
|
+
|
|
36
|
+
- ⏱️ **Fast Sessions:** 20 requests per day (~3-5 minutes total).
|
|
37
|
+
- 🤖 **Model Trade-Offs:** Balance cost (`$`), latency (`⚡`), and quality (`🎯`) across Nano, Mini, and Large model tiers.
|
|
38
|
+
- 🎲 **Daily Seeded Challenge:** Everyone gets the same 20 daily requests for fair competitive scoring.
|
|
39
|
+
- ⚡ **Zero Backend:** Pure client-side / CLI execution.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 📚 Documentation
|
|
44
|
+
|
|
45
|
+
- 📑 **[PRD.md](docs/PRD.md)** — Product Requirements Document
|
|
46
|
+
- 📐 **[SCHEMA.md](docs/SCHEMA.md)** — Data Schemas & Pros/Cons Review
|
|
47
|
+
- 💬 **[COPY.md](docs/COPY.md)** — Master Copywriting Reference
|
|
48
|
+
- ⚡ **[STACK.md](docs/STACK.md)** — Tech Stack & Architecture
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 💻 Commands
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Install dependencies
|
|
56
|
+
npm install
|
|
57
|
+
|
|
58
|
+
# Play terminal CLI game
|
|
59
|
+
npm run play
|
|
60
|
+
|
|
61
|
+
# Start web app dev server
|
|
62
|
+
npm run dev
|
|
63
|
+
|
|
64
|
+
# Build web production bundle
|
|
65
|
+
npm run build
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 📄 License
|
|
71
|
+
|
|
72
|
+
MIT License
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import readline from 'readline';
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------
|
|
6
|
+
// Game Data Definition
|
|
7
|
+
// ---------------------------------------------------------
|
|
8
|
+
export const MODELS = [
|
|
9
|
+
{
|
|
10
|
+
id: 'nano',
|
|
11
|
+
name: 'Nano',
|
|
12
|
+
cost: 0.03,
|
|
13
|
+
latency: 0.4,
|
|
14
|
+
desc: 'Fast & Cheap',
|
|
15
|
+
cap: { quality: 2, coding: 1, reasoning: 1, vision: 0, context: 1 }
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'mini',
|
|
19
|
+
name: 'Mini',
|
|
20
|
+
cost: 0.14,
|
|
21
|
+
latency: 1.2,
|
|
22
|
+
desc: 'Standard Balanced',
|
|
23
|
+
cap: { quality: 4, coding: 3.5, reasoning: 3, vision: 2, context: 3 }
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'large',
|
|
27
|
+
name: 'Large',
|
|
28
|
+
cost: 1.50,
|
|
29
|
+
latency: 4.0,
|
|
30
|
+
desc: 'Heavyweight Reasoning',
|
|
31
|
+
cap: { quality: 5, coding: 5, reasoning: 5, vision: 5, context: 5 }
|
|
32
|
+
}
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
export const REQUESTS = [
|
|
36
|
+
{
|
|
37
|
+
id: 1,
|
|
38
|
+
title: 'Fix customer support email typo',
|
|
39
|
+
desc: 'Customer requested fixing minor spelling errors in automated outbound email.',
|
|
40
|
+
reward: 4.00,
|
|
41
|
+
penalty: -8.00,
|
|
42
|
+
sla: 2.0,
|
|
43
|
+
diff: 'Low',
|
|
44
|
+
req: { quality: 2, coding: 0, reasoning: 1, vision: 0, context: 1 }
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 2,
|
|
48
|
+
title: 'Summarize user feedback survey',
|
|
49
|
+
desc: 'Synthesize 10 customer feedback bullet points into 3 action items.',
|
|
50
|
+
reward: 6.00,
|
|
51
|
+
penalty: -12.00,
|
|
52
|
+
sla: 3.0,
|
|
53
|
+
diff: 'Low',
|
|
54
|
+
req: { quality: 2, coding: 0, reasoning: 1, vision: 0, context: 1 }
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 3,
|
|
58
|
+
title: 'Format JSON payload for API',
|
|
59
|
+
desc: 'Convert flat key-value pairs into valid nested JSON format.',
|
|
60
|
+
reward: 8.00,
|
|
61
|
+
penalty: -15.00,
|
|
62
|
+
sla: 1.5,
|
|
63
|
+
diff: 'Low',
|
|
64
|
+
req: { quality: 2, coding: 2, reasoning: 1, vision: 0, context: 1 }
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 4,
|
|
68
|
+
title: 'Generate SQL query for analytics',
|
|
69
|
+
desc: 'Write SQL query to join user transactions and count monthly active users.',
|
|
70
|
+
reward: 18.00,
|
|
71
|
+
penalty: -35.00,
|
|
72
|
+
sla: 2.0,
|
|
73
|
+
diff: 'Medium',
|
|
74
|
+
req: { quality: 3.5, coding: 3.5, reasoning: 2, vision: 0, context: 1 }
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 5,
|
|
78
|
+
title: 'Extract receipt total from photo',
|
|
79
|
+
desc: 'OCR receipt image and return final total USD amount.',
|
|
80
|
+
reward: 22.00,
|
|
81
|
+
penalty: -45.00,
|
|
82
|
+
sla: 3.0,
|
|
83
|
+
diff: 'Medium',
|
|
84
|
+
req: { quality: 3, coding: 1, reasoning: 2, vision: 3, context: 1 }
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 6,
|
|
88
|
+
title: 'Draft tweet sequence for launch',
|
|
89
|
+
desc: 'Write a 3-part engaging Twitter thread for product launch announcement.',
|
|
90
|
+
reward: 7.00,
|
|
91
|
+
penalty: -10.00,
|
|
92
|
+
sla: 2.5,
|
|
93
|
+
diff: 'Low',
|
|
94
|
+
req: { quality: 2, coding: 0, reasoning: 1, vision: 0, context: 1 }
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 7,
|
|
98
|
+
title: 'Debug React component hook crash',
|
|
99
|
+
desc: 'Identify missing dependency array causing infinite re-render loop.',
|
|
100
|
+
reward: 25.00,
|
|
101
|
+
penalty: -50.00,
|
|
102
|
+
sla: 2.5,
|
|
103
|
+
diff: 'Medium',
|
|
104
|
+
req: { quality: 4, coding: 4, reasoning: 3, vision: 0, context: 2 }
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 8,
|
|
108
|
+
title: 'Translate release notes to Spanish',
|
|
109
|
+
desc: 'Accurately translate technical change log for localized documentation.',
|
|
110
|
+
reward: 9.00,
|
|
111
|
+
penalty: -18.00,
|
|
112
|
+
sla: 2.0,
|
|
113
|
+
diff: 'Low',
|
|
114
|
+
req: { quality: 3, coding: 0, reasoning: 1, vision: 0, context: 1 }
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 9,
|
|
118
|
+
title: 'Parse 50-page PDF legal contract',
|
|
119
|
+
desc: 'Identify non-compete clause page numbers across 50-page document.',
|
|
120
|
+
reward: 45.00,
|
|
121
|
+
penalty: -90.00,
|
|
122
|
+
sla: 4.5,
|
|
123
|
+
diff: 'High',
|
|
124
|
+
req: { quality: 4, coding: 0, reasoning: 3, vision: 0, context: 4.5 }
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 10,
|
|
128
|
+
title: 'Check UI screenshot alignment',
|
|
129
|
+
desc: 'Compare mobile screenshot with design mockup and list visual bugs.',
|
|
130
|
+
reward: 35.00,
|
|
131
|
+
penalty: -70.00,
|
|
132
|
+
sla: 4.0,
|
|
133
|
+
diff: 'High',
|
|
134
|
+
req: { quality: 4, coding: 1, reasoning: 3, vision: 4, context: 2 }
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 11,
|
|
138
|
+
title: 'Rewrite marketing email tagline',
|
|
139
|
+
desc: 'Generate 5 catchy alternate headline variations for promotional email.',
|
|
140
|
+
reward: 5.00,
|
|
141
|
+
penalty: -8.00,
|
|
142
|
+
sla: 1.5,
|
|
143
|
+
diff: 'Low',
|
|
144
|
+
req: { quality: 2, coding: 0, reasoning: 1, vision: 0, context: 1 }
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 12,
|
|
148
|
+
title: 'Refactor legacy Python migration script',
|
|
149
|
+
desc: 'Upgrade Python 2 script to Python 3 with async database connections.',
|
|
150
|
+
reward: 55.00,
|
|
151
|
+
penalty: -110.00,
|
|
152
|
+
sla: 4.5,
|
|
153
|
+
diff: 'High',
|
|
154
|
+
req: { quality: 4.5, coding: 4.5, reasoning: 4, vision: 0, context: 3 }
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: 13,
|
|
158
|
+
title: 'Generate annual tax filing report',
|
|
159
|
+
desc: 'High-risk financial compliance output. Any calculation mistake triggers audit refund.',
|
|
160
|
+
reward: 80.00,
|
|
161
|
+
penalty: -250.00,
|
|
162
|
+
sla: 5.0,
|
|
163
|
+
diff: 'High',
|
|
164
|
+
req: { quality: 5, coding: 3, reasoning: 5, vision: 0, context: 3 }
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 14,
|
|
168
|
+
title: 'Classify customer support ticket priority',
|
|
169
|
+
desc: 'Assign ticket tags (Low, Normal, Urgent) based on customer description.',
|
|
170
|
+
reward: 4.50,
|
|
171
|
+
penalty: -9.00,
|
|
172
|
+
sla: 1.0,
|
|
173
|
+
diff: 'Low',
|
|
174
|
+
req: { quality: 2, coding: 0, reasoning: 1, vision: 0, context: 1 }
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: 15,
|
|
178
|
+
title: 'Extract wireframe elements from napkin sketch',
|
|
179
|
+
desc: 'Parse blurry photo of whiteboard architecture sketch into structured SVG list.',
|
|
180
|
+
reward: 40.00,
|
|
181
|
+
penalty: -85.00,
|
|
182
|
+
sla: 4.0,
|
|
183
|
+
diff: 'High',
|
|
184
|
+
req: { quality: 4.5, coding: 2, reasoning: 4, vision: 4.5, context: 2 }
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 16,
|
|
188
|
+
title: 'Validate JWT authentication middleware',
|
|
189
|
+
desc: 'Security audit request to inspect token verification function for bypass risks.',
|
|
190
|
+
reward: 60.00,
|
|
191
|
+
penalty: -140.00,
|
|
192
|
+
sla: 4.5,
|
|
193
|
+
diff: 'High',
|
|
194
|
+
req: { quality: 5, coding: 4.5, reasoning: 4.5, vision: 0, context: 2 }
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: 17,
|
|
198
|
+
title: 'Clean up CSV phone number formatting',
|
|
199
|
+
desc: 'Standardize 50,000 phone number strings to E.164 international format.',
|
|
200
|
+
reward: 8.00,
|
|
201
|
+
penalty: -16.00,
|
|
202
|
+
sla: 1.5,
|
|
203
|
+
diff: 'Low',
|
|
204
|
+
req: { quality: 2, coding: 1, reasoning: 1, vision: 0, context: 2 }
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: 18,
|
|
208
|
+
title: 'Summarize 100-page earnings transcript',
|
|
209
|
+
desc: 'Extract quarterly guidance figures and CEO statements across multi-hour meeting.',
|
|
210
|
+
reward: 50.00,
|
|
211
|
+
penalty: -100.00,
|
|
212
|
+
sla: 4.5,
|
|
213
|
+
diff: 'High',
|
|
214
|
+
req: { quality: 4, coding: 0, reasoning: 3.5, vision: 0, context: 5 }
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
id: 19,
|
|
218
|
+
title: 'Generate Regex pattern for credit card masking',
|
|
219
|
+
desc: 'Write regex string to redact Visa and Mastercard numbers from log files.',
|
|
220
|
+
reward: 20.00,
|
|
221
|
+
penalty: -40.00,
|
|
222
|
+
sla: 2.0,
|
|
223
|
+
diff: 'Medium',
|
|
224
|
+
req: { quality: 3.5, coding: 3.5, reasoning: 2, vision: 0, context: 1 }
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: 20,
|
|
228
|
+
title: 'Enterprise API multi-service migration plan',
|
|
229
|
+
desc: 'Final customer request of the day. High-stakes architectural audit for enterprise tier.',
|
|
230
|
+
reward: 100.00,
|
|
231
|
+
penalty: -300.00,
|
|
232
|
+
sla: 4.5,
|
|
233
|
+
diff: 'High',
|
|
234
|
+
req: { quality: 5, coding: 4, reasoning: 5, vision: 0, context: 4 }
|
|
235
|
+
}
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
// ANSI Styles
|
|
239
|
+
const C = {
|
|
240
|
+
reset: '\x1b[0m',
|
|
241
|
+
bold: '\x1b[1m',
|
|
242
|
+
dim: '\x1b[2m',
|
|
243
|
+
green: '\x1b[32m',
|
|
244
|
+
red: '\x1b[31m',
|
|
245
|
+
yellow: '\x1b[33m',
|
|
246
|
+
blue: '\x1b[34m',
|
|
247
|
+
cyan: '\x1b[36m',
|
|
248
|
+
selected: '\x1b[46m\x1b[30m\x1b[1m', // Cyan background, dark text
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// ---------------------------------------------------------
|
|
252
|
+
// Game Engine Evaluator
|
|
253
|
+
// ---------------------------------------------------------
|
|
254
|
+
export function evaluateChoice(model, req) {
|
|
255
|
+
const latencyRatio = model.latency / req.sla;
|
|
256
|
+
const latencyMet = model.latency <= req.sla;
|
|
257
|
+
const gaps = [
|
|
258
|
+
req.req.quality - model.cap.quality,
|
|
259
|
+
req.req.coding - model.cap.coding,
|
|
260
|
+
req.req.reasoning - model.cap.reasoning,
|
|
261
|
+
req.req.vision - model.cap.vision,
|
|
262
|
+
req.req.context - model.cap.context,
|
|
263
|
+
];
|
|
264
|
+
const maxGap = Math.max(0, ...gaps);
|
|
265
|
+
|
|
266
|
+
let successProb = 1.0;
|
|
267
|
+
|
|
268
|
+
// Stricter SLA Latency Enforcement (Fix Anomaly 2)
|
|
269
|
+
if (latencyRatio > 2.0) {
|
|
270
|
+
successProb = 0.0; // Automatic SLA Rejection
|
|
271
|
+
} else if (!latencyMet) {
|
|
272
|
+
successProb *= 0.50; // 50% drop
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Capability Gap Penalties
|
|
276
|
+
if (maxGap > 0) {
|
|
277
|
+
if (maxGap <= 1) successProb *= 0.75;
|
|
278
|
+
else if (maxGap <= 2) successProb *= 0.35;
|
|
279
|
+
else successProb *= 0.05;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const success = Math.random() <= successProb;
|
|
283
|
+
const isOverEngineered = model.id === 'large' && req.diff === 'Low';
|
|
284
|
+
const overEngineeringPenalty = isOverEngineered ? 3.00 : 0.00;
|
|
285
|
+
|
|
286
|
+
if (success) {
|
|
287
|
+
const profit = req.reward - model.cost - overEngineeringPenalty;
|
|
288
|
+
let headline = 'Perfect fit.';
|
|
289
|
+
let trustDelta = 3;
|
|
290
|
+
|
|
291
|
+
if (isOverEngineered) {
|
|
292
|
+
headline = 'You burned money. (Over-engineered)';
|
|
293
|
+
trustDelta = -1;
|
|
294
|
+
} else if (model.id === 'nano' && req.diff !== 'Low') {
|
|
295
|
+
headline = 'Excellent margin!';
|
|
296
|
+
trustDelta = 4;
|
|
297
|
+
} else if (maxGap > 0) {
|
|
298
|
+
headline = 'You got lucky!';
|
|
299
|
+
trustDelta = 2;
|
|
300
|
+
} else if (!latencyMet) {
|
|
301
|
+
headline = 'Barely met SLA.';
|
|
302
|
+
trustDelta = 1;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return { success: true, profit, trustDelta, headline };
|
|
306
|
+
} else {
|
|
307
|
+
const profit = req.penalty - model.cost;
|
|
308
|
+
let headline = 'Customer rejected output!';
|
|
309
|
+
let trustDelta = -6;
|
|
310
|
+
|
|
311
|
+
if (latencyRatio > 2.0) {
|
|
312
|
+
headline = 'SLA Violation! Automatic Rejection (Latency > 2x SLA).';
|
|
313
|
+
trustDelta = -8;
|
|
314
|
+
} else if (!latencyMet) {
|
|
315
|
+
headline = 'Fast but sloppy. (SLA Exceeded)';
|
|
316
|
+
} else if (maxGap >= 2) {
|
|
317
|
+
headline = 'Inference costs eating profit. Underpowered model failed.';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return { success: false, profit, trustDelta, headline };
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ---------------------------------------------------------
|
|
325
|
+
// Interactive Arrow-Key Menu Prompt
|
|
326
|
+
// ---------------------------------------------------------
|
|
327
|
+
function selectOptionWithArrowKeys(options, renderHeader) {
|
|
328
|
+
return new Promise((resolve) => {
|
|
329
|
+
let selectedIndex = 0;
|
|
330
|
+
|
|
331
|
+
readline.emitKeypressEvents(process.stdin);
|
|
332
|
+
if (process.stdin.isTTY) {
|
|
333
|
+
process.stdin.setRawMode(true);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function render() {
|
|
337
|
+
console.clear();
|
|
338
|
+
renderHeader();
|
|
339
|
+
console.log(`\n ${C.bold}Choose Model (Use ↑ / ↓ arrow keys, press ENTER):${C.reset}\n`);
|
|
340
|
+
|
|
341
|
+
options.forEach((opt, idx) => {
|
|
342
|
+
if (idx === selectedIndex) {
|
|
343
|
+
console.log(` ${C.selected} ➔ ${opt.name.padEnd(6)} | $${opt.cost.toFixed(2).padStart(4)} | ${opt.latency.toFixed(1)}s SLA | ${opt.desc} ${C.reset}`);
|
|
344
|
+
} else {
|
|
345
|
+
console.log(` ${C.dim} ${opt.name.padEnd(6)} | $${opt.cost.toFixed(2).padStart(4)} | ${opt.latency.toFixed(1)}s SLA | ${opt.desc}${C.reset}`);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
console.log('');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
render();
|
|
352
|
+
|
|
353
|
+
function onKeypress(str, key) {
|
|
354
|
+
if (key.name === 'up') {
|
|
355
|
+
selectedIndex = (selectedIndex - 1 + options.length) % options.length;
|
|
356
|
+
render();
|
|
357
|
+
} else if (key.name === 'down') {
|
|
358
|
+
selectedIndex = (selectedIndex + 1) % options.length;
|
|
359
|
+
render();
|
|
360
|
+
} else if (key.name === 'return') {
|
|
361
|
+
cleanup();
|
|
362
|
+
resolve(options[selectedIndex]);
|
|
363
|
+
} else if (key.ctrl && key.name === 'c') {
|
|
364
|
+
cleanup();
|
|
365
|
+
process.exit(0);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function cleanup() {
|
|
370
|
+
process.stdin.removeListener('keypress', onKeypress);
|
|
371
|
+
if (process.stdin.isTTY) {
|
|
372
|
+
process.stdin.setRawMode(false);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
process.stdin.on('keypress', onKeypress);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function waitForEnter(promptText) {
|
|
381
|
+
return new Promise((resolve) => {
|
|
382
|
+
readline.emitKeypressEvents(process.stdin);
|
|
383
|
+
if (process.stdin.isTTY) {
|
|
384
|
+
process.stdin.setRawMode(true);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
console.log(` ${C.dim}${promptText}${C.reset}`);
|
|
388
|
+
|
|
389
|
+
function onKeypress(str, key) {
|
|
390
|
+
if (key.name === 'return' || key.name === 'space') {
|
|
391
|
+
cleanup();
|
|
392
|
+
resolve();
|
|
393
|
+
} else if (key.ctrl && key.name === 'c') {
|
|
394
|
+
cleanup();
|
|
395
|
+
process.exit(0);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function cleanup() {
|
|
400
|
+
process.stdin.removeListener('keypress', onKeypress);
|
|
401
|
+
if (process.stdin.isTTY) {
|
|
402
|
+
process.stdin.setRawMode(false);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
process.stdin.on('keypress', onKeypress);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// ---------------------------------------------------------
|
|
411
|
+
// Main Game Controller
|
|
412
|
+
// ---------------------------------------------------------
|
|
413
|
+
async function main() {
|
|
414
|
+
let capital = 100.00;
|
|
415
|
+
let trust = 85;
|
|
416
|
+
let totalRevenue = 0;
|
|
417
|
+
let totalCost = 0;
|
|
418
|
+
const history = [];
|
|
419
|
+
|
|
420
|
+
for (let i = 0; i < REQUESTS.length; i++) {
|
|
421
|
+
const req = REQUESTS[i];
|
|
422
|
+
const turnNum = i + 1;
|
|
423
|
+
|
|
424
|
+
const renderTurnHeader = () => {
|
|
425
|
+
console.log(`${C.bold}${C.cyan}`);
|
|
426
|
+
console.log(`================================================================`);
|
|
427
|
+
console.log(` 🤖 AI CTO — Run an AI Startup (Terminal Arrow-Key Edition)`);
|
|
428
|
+
console.log(`================================================================${C.reset}`);
|
|
429
|
+
console.log(` Turn: ${C.bold}${turnNum}/20${C.reset} | Capital: ${C.green}${C.bold}$${capital.toFixed(2)}${C.reset} | Trust: ${C.blue}${C.bold}${trust}%${C.reset}`);
|
|
430
|
+
console.log(`${C.dim}----------------------------------------------------------------${C.reset}`);
|
|
431
|
+
console.log(` 📥 ${C.bold}${req.title}${C.reset}`);
|
|
432
|
+
console.log(` ${C.dim}${req.desc}${C.reset}`);
|
|
433
|
+
console.log(` Reward: ${C.green}+$${req.reward.toFixed(2)}${C.reset} | Penalty: ${C.red}$${req.penalty.toFixed(2)}${C.reset} | Max SLA: ${C.yellow}${req.sla}s${C.reset} | Diff: ${req.diff}`);
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const selectedModel = await selectOptionWithArrowKeys(MODELS, renderTurnHeader);
|
|
437
|
+
|
|
438
|
+
const res = evaluateChoice(selectedModel, req);
|
|
439
|
+
capital += res.profit;
|
|
440
|
+
trust = Math.min(100, Math.max(0, trust + res.trustDelta));
|
|
441
|
+
totalCost += selectedModel.cost;
|
|
442
|
+
|
|
443
|
+
console.clear();
|
|
444
|
+
renderTurnHeader();
|
|
445
|
+
console.log(`${C.dim}----------------------------------------------------------------${C.reset}`);
|
|
446
|
+
|
|
447
|
+
if (res.success) {
|
|
448
|
+
totalRevenue += req.reward;
|
|
449
|
+
console.log(`\n ${C.green}${C.bold}✅ SUCCESS! ${res.headline}${C.reset}`);
|
|
450
|
+
console.log(` Profit: ${C.green}+${res.profit >= 0 ? '$' + res.profit.toFixed(2) : '-$' + Math.abs(res.profit).toFixed(2)}${C.reset} | Capital: ${C.bold}$${capital.toFixed(2)}${C.reset} | Selected: ${selectedModel.name}`);
|
|
451
|
+
} else {
|
|
452
|
+
totalRevenue += req.penalty;
|
|
453
|
+
console.log(`\n ${C.red}${C.bold}❌ FAILURE! ${res.headline}${C.reset}`);
|
|
454
|
+
console.log(` Penalty Loss: ${C.red}-$${Math.abs(res.profit).toFixed(2)}${C.reset} | Capital: ${C.bold}$${capital.toFixed(2)}${C.reset} | Selected: ${selectedModel.name}`);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
history.push({ req, model: selectedModel, res });
|
|
458
|
+
|
|
459
|
+
if (i < REQUESTS.length - 1) {
|
|
460
|
+
console.log('');
|
|
461
|
+
await waitForEnter('[Press ENTER or SPACE for next request...]');
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// End of Session Summary
|
|
466
|
+
console.clear();
|
|
467
|
+
console.log(`\n================================================================`);
|
|
468
|
+
console.log(` 🏆 DAY COMPLETE — SESSION SUMMARY`);
|
|
469
|
+
console.log(`================================================================`);
|
|
470
|
+
console.log(` Gross Revenue: ${totalRevenue >= 0 ? '$' + totalRevenue.toFixed(2) : '-$' + Math.abs(totalRevenue).toFixed(2)}`);
|
|
471
|
+
console.log(` Inference Cost: -$${totalCost.toFixed(2)}`);
|
|
472
|
+
console.log(` --------------------------------------------------------------`);
|
|
473
|
+
console.log(` ${C.bold}FINAL CAPITAL: $${capital.toFixed(2)}${C.reset}`);
|
|
474
|
+
console.log(` Customer Trust: ${trust}%`);
|
|
475
|
+
|
|
476
|
+
let rank = 'C Tier — Break-even';
|
|
477
|
+
if (capital > 250 && trust >= 90) rank = `${C.yellow}${C.bold}S Tier — Unicorn Founder (Top 5%)${C.reset}`;
|
|
478
|
+
else if (capital > 150) rank = `${C.green}${C.bold}A Tier — Profitable AI Startup (Top 15%)${C.reset}`;
|
|
479
|
+
else if (capital > 50) rank = `${C.cyan}B Tier — Sustainable Ops (Top 35%)${C.reset}`;
|
|
480
|
+
else if (capital < 0) rank = `${C.red}${C.bold}Bankrupt Founder — Inference Costs Ate Capital${C.reset}`;
|
|
481
|
+
|
|
482
|
+
console.log(` Performance: ${rank}`);
|
|
483
|
+
console.log(`================================================================\n`);
|
|
484
|
+
|
|
485
|
+
process.exit(0);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (process.argv[1] && process.argv[1].endsWith('cli.js')) {
|
|
489
|
+
main().catch(console.error);
|
|
490
|
+
}
|
|
491
|
+
|
package/docs/BALANCE.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# ⚖️ AI CTO — Game Balance & Economy Framework (BALANCE.md)
|
|
2
|
+
|
|
3
|
+
> **Purpose:** This document specifies the economic rules, probability formulas, model pricing ratios, and design guardrails to ensure **AI CTO** remains balanced, competitive, and free of dominant strategies as new models or requests are added.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🎯 1. Core Balance Philosophy
|
|
8
|
+
|
|
9
|
+
To keep gameplay engaging and strategic, the game enforces three golden rules:
|
|
10
|
+
|
|
11
|
+
1. 🚫 **No Single Dominant Strategy:** No model tier should ever be the correct choice for all requests.
|
|
12
|
+
- **Spamming Nano** $\rightarrow$ Instant bankruptcy (`-$1,136` loss) due to failure penalties on complex tasks.
|
|
13
|
+
- **Spamming Large** $\rightarrow$ Over-engineering penalties ($3 customer complaint refund) and SLA latency rejections degrade customer trust to `~53%`, preventing S-Tier ranks.
|
|
14
|
+
2. ⚖️ **Asymmetric Risk vs Reward:** High-payout requests carry severe failure penalties. Taking cheap risks must be a calculated gamble, not a brainless choice.
|
|
15
|
+
3. ⏱️ **Strict SLA Time Pressure:** Latency SLA constraints (> 2x SLA limit = Automatic Rejection) prevent players from using slow, heavy models on time-critical requests.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🤖 2. Model Pricing & Capability Scaling Matrix
|
|
20
|
+
|
|
21
|
+
Model tiers are balanced according to fixed cost-to-capability ratios:
|
|
22
|
+
|
|
23
|
+
| Model Tier | Cost / Req | Latency SLA | Max Capability | Primary Domain / Purpose |
|
|
24
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
25
|
+
| **Nano** | `$0.03` | `0.4s` | `2.0 / 5` | High-margin text cleanup, simple parsing, low-risk requests. |
|
|
26
|
+
| **Mini** | `$0.14` | `1.2s` | `4.0 / 5` | Standard business tasks, SQL generation, standard code refactoring. |
|
|
27
|
+
| **Large** | `$1.50` | `4.0s` | `5.0 / 5` | High-stakes code audit, 50+ page PDF analysis, tax compliance. |
|
|
28
|
+
|
|
29
|
+
### Model Multipliers & Ratios
|
|
30
|
+
- **Cost Scaling Ratio:** `Nano (1x) : Mini (~4.6x) : Large (50.0x)`
|
|
31
|
+
- **Latency Ratio:** `Nano (0.4s) : Mini (1.2s) : Large (4.0s)`
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 📊 3. Request Economic Tiers & Penalty Ratios
|
|
36
|
+
|
|
37
|
+
Incoming requests are categorized into three difficulty tiers with specific financial bounds:
|
|
38
|
+
|
|
39
|
+
| Tier | Payout Range ($) | Penalty Multiplier | SLA Target | Optimal Choice |
|
|
40
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
41
|
+
| **Low-Risk** | `$4.00 – $10.00` | `1.5x – 2.0x Reward` | `1.0s – 2.5s` | **Nano** |
|
|
42
|
+
| **Medium-Risk** | `$15.00 – $35.00` | `2.0x – 2.5x Reward` | `2.0s – 3.5s` | **Mini** |
|
|
43
|
+
| **High-Risk / Audit** | `$40.00 – $100.00` | `3.0x – 4.0x Reward` | `3.5s – 5.0s` | **Large** |
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🎲 4. Probability & Capability Gap Formula
|
|
48
|
+
|
|
49
|
+
$$\text{Capability Gap} = \max\Big(0, \max_{\text{dimension}} \big(\text{Requirement}_{\text{dim}} - \text{Capability}_{\text{dim}}\big)\Big)$$
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
let successProbability = 1.0;
|
|
53
|
+
const latencyRatio = model.latency / request.slaLatency;
|
|
54
|
+
|
|
55
|
+
// 1. Stricter SLA Enforcement
|
|
56
|
+
if (latencyRatio > 2.0) {
|
|
57
|
+
// Severe breach (> 2x limit) -> Automatic Rejection Failure
|
|
58
|
+
successProbability = 0.0;
|
|
59
|
+
} else if (model.latency > request.slaLatency) {
|
|
60
|
+
// Moderate breach -> 50% drop in success rate
|
|
61
|
+
successProbability *= 0.50;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 2. Capability Gap Penalties
|
|
65
|
+
if (maxGap === 1) successProbability *= 0.75;
|
|
66
|
+
else if (maxGap === 2) successProbability *= 0.35;
|
|
67
|
+
else if (maxGap > 2) successProbability *= 0.05;
|
|
68
|
+
|
|
69
|
+
// 3. Over-Engineering Refund Penalty
|
|
70
|
+
if (model.id === 'large' && request.difficulty === 'low') {
|
|
71
|
+
profit -= 3.00; // Customer complaint refund applied
|
|
72
|
+
trustDelta -= 1;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 🏆 5. Empirical 40-Game Playtest Verification Benchmarks
|
|
79
|
+
|
|
80
|
+
For a standard 20-request daily challenge session:
|
|
81
|
+
|
|
82
|
+
| Playstyle Strategy | Avg Final Capital | Customer Trust (%) | Dominant Rank Achieved |
|
|
83
|
+
| :--- | :--- | :--- | :--- |
|
|
84
|
+
| 👑 **Optimal Strategy** | **`+$638.80`** | **`100.0%`** | **S Tier (Unicorn Founder)** |
|
|
85
|
+
| 🛡️ **Large Safe Strategy** | `+$417.50` | `53.9%` | **A Tier (S-Tier Blocked)** |
|
|
86
|
+
| 🎲 **Random Strategy** | `-$423.65` | `46.4%` | **Bankrupt** |
|
|
87
|
+
| 💀 **Nano-Spammer** | `-$1,136.70` | `32.2%` | **Bankrupt** |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 🔍 6. Balancing Checklist for Adding New Content
|
|
92
|
+
|
|
93
|
+
When adding a **new Request** or **new Model Tier**:
|
|
94
|
+
|
|
95
|
+
- [ ] **Dominant Strategy Check:** Does Nano beat this request with >50% profit margin? If yes, lower payout or raise penalty.
|
|
96
|
+
- [ ] **Over-Engineering Check:** Does choosing Large on this request result in negative net profit or customer complaint penalty?
|
|
97
|
+
- [ ] **SLA Check:** Is the SLA latency tight enough that Large triggers an SLA penalty on fast requests?
|
|
98
|
+
- [ ] **High-Risk Penalty Check:** Is the failure penalty severe enough that picking Nano has an expected value ($EV < 0$)?
|
|
99
|
+
$$EV = (\text{Prob}_{\text{success}} \times \text{Reward}) + (\text{Prob}_{\text{fail}} \times \text{Penalty}) - \text{Model Cost}$$
|