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/docs/STACK.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# โก AI CTO โ Technical Architecture & Stack Document
|
|
2
|
+
|
|
3
|
+
> **Goal:** Validate if choosing AI models is fun. Optimized for shipping in a single weekend with zero backend, pure deterministic logic, and rapid iteration via JSON configuration.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## ๐ฏ Guiding Principles
|
|
8
|
+
|
|
9
|
+
- ๐ฒ **100% Deterministic:** Seeded RNG ensures every player faces identical daily challenges.
|
|
10
|
+
- ๐ **Data-Driven Design:** Game rules, models, and requests are fully configurable via JSON schemas.
|
|
11
|
+
- โก **Zero Backend Architecture:** No authentication, no databases, no APIs, no network dependency.
|
|
12
|
+
- ๐งช **Test-Driven Engine:** Pure TypeScript core logic decoupled completely from the UI layer.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## ๐ ๏ธ Technology Stack Overview
|
|
17
|
+
|
|
18
|
+
| Domain | Technology | Rationale |
|
|
19
|
+
| :--- | :--- | :--- |
|
|
20
|
+
| **Framework** | React 19 + Vite + TypeScript | Blazing fast dev loop, minimal bundle size, ideal for static deployment |
|
|
21
|
+
| **Styling** | Tailwind CSS + shadcn/ui | Accelerated component building with zero custom CSS except animations |
|
|
22
|
+
| **State** | Zustand | Ultra-lightweight, single centralized state store |
|
|
23
|
+
| **Persistence** | LocalStorage | Simple client-side persistence for game state and settings |
|
|
24
|
+
| **Routing** | React Router v7 | Seamless single-page navigation |
|
|
25
|
+
| **Animations** | Framer Motion | Fluid card transitions, score incrementing, and cash feedback |
|
|
26
|
+
| **Icons** | Lucide React | Clean modern SVG icons |
|
|
27
|
+
| **Audio** | Howler.js / HTML5 Audio | Snappy sound effects (clicks, cash register, failures) |
|
|
28
|
+
| **Testing** | Vitest | Fast headless CLI unit testing for engine logic |
|
|
29
|
+
| **Deployment** | Vercel / Cloudflare Pages | Instant static deployment with edge distribution |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## ๐๏ธ Project Directory Structure
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
aicto/
|
|
37
|
+
โโโ src/
|
|
38
|
+
โ โโโ assets/ # Sound FX, custom graphics, and icons
|
|
39
|
+
โ โ โโโ icons/
|
|
40
|
+
โ โ โโโ sounds/
|
|
41
|
+
โ โโโ components/ # Reusable UI components (shadcn/ui based)
|
|
42
|
+
โ โ โโโ EndScreen.tsx
|
|
43
|
+
โ โ โโโ Header.tsx
|
|
44
|
+
โ โ โโโ ModelCard.tsx
|
|
45
|
+
โ โ โโโ RequestCard.tsx
|
|
46
|
+
โ โ โโโ ScoreBar.tsx
|
|
47
|
+
โ โโโ data/ # Static game JSON data
|
|
48
|
+
โ โ โโโ encyclopedia.json
|
|
49
|
+
โ โ โโโ models.json
|
|
50
|
+
โ โ โโโ requests.json
|
|
51
|
+
โ โโโ engine/ # Pure TypeScript game simulation engine
|
|
52
|
+
โ โ โโโ economy.ts
|
|
53
|
+
โ โ โโโ evaluate.ts
|
|
54
|
+
โ โ โโโ rng.ts
|
|
55
|
+
โ โ โโโ scoring.ts
|
|
56
|
+
โ โ โโโ seed.ts
|
|
57
|
+
โ โโโ hooks/ # Custom React hooks
|
|
58
|
+
โ โโโ pages/ # Page-level routes
|
|
59
|
+
โ โ โโโ Encyclopedia.tsx
|
|
60
|
+
โ โ โโโ Game.tsx
|
|
61
|
+
โ โ โโโ Home.tsx
|
|
62
|
+
โ โ โโโ Results.tsx
|
|
63
|
+
โ โ โโโ Settings.tsx
|
|
64
|
+
โ โโโ store/ # State management
|
|
65
|
+
โ โ โโโ game.ts
|
|
66
|
+
โ โโโ types/ # TypeScript interfaces & types
|
|
67
|
+
โ โโโ utils/ # Helper functions
|
|
68
|
+
โโโ tests/ # Engine unit tests (Vitest)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## ๐ Data Contracts & JSON Schemas
|
|
74
|
+
|
|
75
|
+
### 1. Request Schema (`requests.json`)
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"id": 12,
|
|
80
|
+
"title": "Generate SQL query for analytics dashboard",
|
|
81
|
+
"category": "coding",
|
|
82
|
+
"difficulty": "medium",
|
|
83
|
+
"latencyRequirement": 2.0,
|
|
84
|
+
"qualityRequirement": 3,
|
|
85
|
+
"reward": 18.00,
|
|
86
|
+
"penalty": -30.00
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 2. Model Schema (`models.json`)
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"id": "nano",
|
|
95
|
+
"name": "Nano",
|
|
96
|
+
"cost": 0.03,
|
|
97
|
+
"latency": 0.4,
|
|
98
|
+
"quality": 2,
|
|
99
|
+
"coding": 2,
|
|
100
|
+
"reasoning": 1,
|
|
101
|
+
"vision": 0,
|
|
102
|
+
"context": 1
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## ๐ง Game Engine Logic & Evaluation Pipeline
|
|
109
|
+
|
|
110
|
+
The game engine is pure TypeScript with zero React dependencies, allowing direct CLI execution and unit testing via Vitest.
|
|
111
|
+
|
|
112
|
+
```mermaid
|
|
113
|
+
flowchart TD
|
|
114
|
+
Data[๐ JSON Data] --> Engine[โก Game Engine]
|
|
115
|
+
Req[๐ฅ Request Requirements] --> Eval[๐งช evaluate request, model ]
|
|
116
|
+
Mod[๐ค Model Capabilities] --> Eval
|
|
117
|
+
Eval --> Outcome{Success Check}
|
|
118
|
+
Outcome -- Success --> Profit[๐ฐ Calculate Profit & Bonus]
|
|
119
|
+
Outcome -- Failure --> Refund[๐ Deduct Penalty & Trust]
|
|
120
|
+
Profit --> Store[๐ฆ Zustand Game Store]
|
|
121
|
+
Refund --> Store
|
|
122
|
+
Store --> UI[๐ป React UI & LocalStorage]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Scoring & Failure Probability Formula
|
|
126
|
+
|
|
127
|
+
Success is probabilistic when model capabilities fall below required thresholds:
|
|
128
|
+
|
|
129
|
+
| Quality Match Standard | Success Probability | Impact |
|
|
130
|
+
| :--- | :--- | :--- |
|
|
131
|
+
| **Perfect Match** (`Model >= Requirement`) | `100%` | Full Reward Granted |
|
|
132
|
+
| **Close Match** (`Difference = -1`) | `80%` | Minor failure risk |
|
|
133
|
+
| **Below Standard** (`Difference = -2`) | `35%` | High failure risk |
|
|
134
|
+
| **Far Below** (`Difference <= -3`) | `5%` | Almost guaranteed failure penalty |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## ๐ Vector-Based Capability Architecture
|
|
139
|
+
|
|
140
|
+
To support generic capability scoring without per-request hardcoding, requests and models are represented as multidimensional capability vectors:
|
|
141
|
+
|
|
142
|
+
### Vector Definition
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
export interface CapabilityVector {
|
|
146
|
+
quality: number;
|
|
147
|
+
reasoning: number;
|
|
148
|
+
coding: number;
|
|
149
|
+
vision: number;
|
|
150
|
+
context: number;
|
|
151
|
+
latency: number;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface Model {
|
|
155
|
+
id: string;
|
|
156
|
+
cost: number;
|
|
157
|
+
capabilities: CapabilityVector;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface RequestRequirements {
|
|
161
|
+
requirements: CapabilityVector;
|
|
162
|
+
maxLatency: number;
|
|
163
|
+
reward: number;
|
|
164
|
+
penalty: number;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
> [!TIP]
|
|
169
|
+
> **Extensibility:** Using vector scoring allows introducing new capabilities (e.g. `multilingual`, `audio`, `functionCalling`) by updating vectors in `models.json` and `requests.json` without modifying core engine evaluation code.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## ๐ฒ Randomness & Seeded RNG
|
|
174
|
+
|
|
175
|
+
To enable fair daily competitive scoring without a server:
|
|
176
|
+
- `seedrandom` provides pseudo-random outcomes based on the current date string (e.g., `2026-08-01`).
|
|
177
|
+
- All players face identical request orders, failure rolls, and scoring conditions on any given day.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## ๐ Minimal Client Analytics
|
|
182
|
+
|
|
183
|
+
Client-side event tracking captures essential gameplay metrics without requiring a custom backend:
|
|
184
|
+
|
|
185
|
+
- ๐ช **Drop-off Points:** Identify requests where players exit early.
|
|
186
|
+
- โ ๏ธ **Bottleneck Identification:** Track requests with highest failure rates.
|
|
187
|
+
- ๐ **Replay Frequency:** Monitor immediate replay button clicks.
|
|
188
|
+
- โฑ๏ธ **Completion Time:** Average duration to complete 20 requests.
|
|
189
|
+
- ๐ **Model Bias:** Distribution of Nano vs Mini vs Large selections.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## ๐ฆ Required Libraries Summary
|
|
194
|
+
|
|
195
|
+
| Category | Package | Purpose |
|
|
196
|
+
| :--- | :--- | :--- |
|
|
197
|
+
| **State** | `zustand` | Store global game state |
|
|
198
|
+
| **Animation** | `framer-motion` | Micro-animations and page transitions |
|
|
199
|
+
| **Sound** | `howler` | Fast web audio playback |
|
|
200
|
+
| **RNG** | `seedrandom` | Deterministic daily random seeds |
|
|
201
|
+
| **Class Helpers** | `clsx` + `tailwind-merge` | Conditional utility class merging |
|
|
202
|
+
| **Icons** | `lucide-react` | Dashboard and card icons |
|
|
203
|
+
| **Validation** | `zod` | JSON runtime schema validation |
|
|
204
|
+
| **Testing** | `vitest` | Headless unit testing for game engine |
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-cto",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Run an AI startup for 5 minutes (Terminal CLI Edition)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai-cto": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"play": "node ./bin/cli.js",
|
|
11
|
+
"test": "node ./scripts/playtest.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cli",
|
|
15
|
+
"game",
|
|
16
|
+
"ai",
|
|
17
|
+
"simulation"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT"
|
|
20
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { evaluateChoice, MODELS, REQUESTS } from '../bin/cli.js'; // or import game evaluator logic
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------
|
|
4
|
+
// Playtest Simulator Engine
|
|
5
|
+
// ---------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
function runSimulatedGame(strategyName, strategyFn) {
|
|
8
|
+
let capital = 100.00;
|
|
9
|
+
let trust = 85;
|
|
10
|
+
let totalRevenue = 0;
|
|
11
|
+
let totalCost = 0;
|
|
12
|
+
let successes = 0;
|
|
13
|
+
let failures = 0;
|
|
14
|
+
const turnLogs = [];
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < REQUESTS.length; i++) {
|
|
17
|
+
const req = REQUESTS[i];
|
|
18
|
+
const selectedModel = strategyFn(req, i);
|
|
19
|
+
|
|
20
|
+
const res = evaluateChoice(selectedModel, req);
|
|
21
|
+
capital += res.profit;
|
|
22
|
+
trust = Math.min(100, Math.max(0, trust + res.trustDelta));
|
|
23
|
+
totalCost += selectedModel.cost;
|
|
24
|
+
|
|
25
|
+
if (res.success) {
|
|
26
|
+
successes++;
|
|
27
|
+
totalRevenue += req.reward;
|
|
28
|
+
} else {
|
|
29
|
+
failures++;
|
|
30
|
+
totalRevenue += req.penalty;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
turnLogs.push({
|
|
34
|
+
turn: i + 1,
|
|
35
|
+
reqId: req.id,
|
|
36
|
+
title: req.title,
|
|
37
|
+
difficulty: req.diff,
|
|
38
|
+
modelChosen: selectedModel.name,
|
|
39
|
+
success: res.success,
|
|
40
|
+
profit: res.profit,
|
|
41
|
+
trustDelta: res.trustDelta,
|
|
42
|
+
headline: res.headline
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let rank = 'C Tier (Break-even)';
|
|
47
|
+
if (capital > 250 && trust >= 90) rank = 'S Tier (Unicorn Founder)';
|
|
48
|
+
else if (capital > 150) rank = 'A Tier (Profitable Startup)';
|
|
49
|
+
else if (capital > 50) rank = 'B Tier (Sustainable Ops)';
|
|
50
|
+
else if (capital < 0) rank = 'Bankrupt Founder';
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
strategyName,
|
|
54
|
+
capital,
|
|
55
|
+
trust,
|
|
56
|
+
totalRevenue,
|
|
57
|
+
totalCost,
|
|
58
|
+
successes,
|
|
59
|
+
failures,
|
|
60
|
+
rank,
|
|
61
|
+
turnLogs
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------
|
|
66
|
+
// Strategies
|
|
67
|
+
// ---------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
// 1. Optimal Strategy (Nano for Low, Mini for Med, Large for High)
|
|
70
|
+
const optimalStrategy = (req) => {
|
|
71
|
+
if (req.diff === 'Low') return MODELS[0]; // Nano
|
|
72
|
+
if (req.diff === 'Medium') return MODELS[1]; // Mini
|
|
73
|
+
return MODELS[2]; // Large
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// 2. Nano Spam Strategy (Nano for everything)
|
|
77
|
+
const nanoSpamStrategy = () => MODELS[0];
|
|
78
|
+
|
|
79
|
+
// 3. Large Safe Strategy (Large for everything)
|
|
80
|
+
const largeSafeStrategy = () => MODELS[2];
|
|
81
|
+
|
|
82
|
+
// 4. Random Strategy
|
|
83
|
+
const randomStrategy = () => MODELS[Math.floor(Math.random() * MODELS.length)];
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------
|
|
86
|
+
// Run 10 Runs per Strategy & Record Anomalies
|
|
87
|
+
// ---------------------------------------------------------
|
|
88
|
+
console.log('================================================================');
|
|
89
|
+
console.log('๐งช AI CTO โ AUTOMATED SIMULATION & ANOMALY DETECTION SUITE');
|
|
90
|
+
console.log('================================================================\n');
|
|
91
|
+
|
|
92
|
+
const RUNS = 10;
|
|
93
|
+
const strategies = [
|
|
94
|
+
{ name: 'Optimal Strategy', fn: optimalStrategy },
|
|
95
|
+
{ name: 'Nano Spam Strategy', fn: nanoSpamStrategy },
|
|
96
|
+
{ name: 'Large Safe Strategy', fn: largeSafeStrategy },
|
|
97
|
+
{ name: 'Random Choice Strategy', fn: randomStrategy }
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const allResults = [];
|
|
101
|
+
const requestFailureTracker = {};
|
|
102
|
+
|
|
103
|
+
REQUESTS.forEach(r => { requestFailureTracker[r.id] = { title: r.title, diff: r.diff, failures: 0, total: 0 }; });
|
|
104
|
+
|
|
105
|
+
strategies.forEach(strat => {
|
|
106
|
+
console.log(`\n--- Running 10 Playthroughs: ${strat.name} ---`);
|
|
107
|
+
const stratRuns = [];
|
|
108
|
+
|
|
109
|
+
for (let run = 1; run <= RUNS; run++) {
|
|
110
|
+
const result = runSimulatedGame(strat.name, strat.fn);
|
|
111
|
+
stratRuns.push(result);
|
|
112
|
+
|
|
113
|
+
result.turnLogs.forEach(log => {
|
|
114
|
+
requestFailureTracker[log.reqId].total++;
|
|
115
|
+
if (!log.success) requestFailureTracker[log.reqId].failures++;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
console.log(` Run #${run.toString().padStart(2)}: Capital = $${result.capital.toFixed(2).padStart(7)} | Trust = ${result.trust.toString().padStart(3)}% | Score = ${result.successes}/20 Successes | Rank = ${result.rank}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const avgCapital = stratRuns.reduce((acc, r) => acc + r.capital, 0) / RUNS;
|
|
122
|
+
const avgTrust = stratRuns.reduce((acc, r) => acc + r.trust, 0) / RUNS;
|
|
123
|
+
const avgSuccess = stratRuns.reduce((acc, r) => acc + r.successes, 0) / RUNS;
|
|
124
|
+
|
|
125
|
+
console.log(` ๐ SUMMARY (${strat.name}): Avg Profit = $${avgCapital.toFixed(2)} | Avg Trust = ${avgTrust.toFixed(1)}% | Avg Wins = ${avgSuccess.toFixed(1)}/20`);
|
|
126
|
+
allResults.push({ strat: strat.name, avgCapital, avgTrust, avgSuccess, stratRuns });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------
|
|
130
|
+
// Anomaly Analysis
|
|
131
|
+
// ---------------------------------------------------------
|
|
132
|
+
console.log('\n================================================================');
|
|
133
|
+
console.log('๐ ANOMALY DETECTION REPORT');
|
|
134
|
+
console.log('================================================================\n');
|
|
135
|
+
|
|
136
|
+
console.log('1. High Failure Rate Bottlenecks (> 50% Failure across all runs):');
|
|
137
|
+
Object.values(requestFailureTracker).forEach(item => {
|
|
138
|
+
const failRate = (item.failures / item.total) * 100;
|
|
139
|
+
if (failRate > 40) {
|
|
140
|
+
console.log(` โ ๏ธ [Req #${item.title}] Difficulty: ${item.diff} | Failure Rate: ${failRate.toFixed(1)}% (${item.failures}/${item.total} runs failed)`);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
console.log('\n2. Strategy ROI & Dominance Check:');
|
|
145
|
+
allResults.forEach(res => {
|
|
146
|
+
console.log(` - ${res.strat.padEnd(25)}: Avg Final Capital = $${res.avgCapital.toFixed(2)} (${res.avgCapital > 0 ? 'PROFITABLE' : 'UNPROFITABLE'})`);
|
|
147
|
+
});
|