create-threejs-game 1.0.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.
Files changed (39) hide show
  1. package/README.md +97 -0
  2. package/bin/cli.js +370 -0
  3. package/package.json +29 -0
  4. package/template/.claude/skills/threejs-animation/SKILL.md +552 -0
  5. package/template/.claude/skills/threejs-fundamentals/SKILL.md +488 -0
  6. package/template/.claude/skills/threejs-geometry/SKILL.md +548 -0
  7. package/template/.claude/skills/threejs-interaction/SKILL.md +660 -0
  8. package/template/.claude/skills/threejs-lighting/SKILL.md +481 -0
  9. package/template/.claude/skills/threejs-loaders/SKILL.md +623 -0
  10. package/template/.claude/skills/threejs-materials/SKILL.md +520 -0
  11. package/template/.claude/skills/threejs-postprocessing/SKILL.md +602 -0
  12. package/template/.claude/skills/threejs-shaders/SKILL.md +642 -0
  13. package/template/.claude/skills/threejs-textures/SKILL.md +628 -0
  14. package/template/.codex/skills/threejs-animation/SKILL.md +552 -0
  15. package/template/.codex/skills/threejs-fundamentals/SKILL.md +488 -0
  16. package/template/.codex/skills/threejs-geometry/SKILL.md +548 -0
  17. package/template/.codex/skills/threejs-interaction/SKILL.md +660 -0
  18. package/template/.codex/skills/threejs-lighting/SKILL.md +481 -0
  19. package/template/.codex/skills/threejs-loaders/SKILL.md +623 -0
  20. package/template/.codex/skills/threejs-materials/SKILL.md +520 -0
  21. package/template/.codex/skills/threejs-postprocessing/SKILL.md +602 -0
  22. package/template/.codex/skills/threejs-shaders/SKILL.md +642 -0
  23. package/template/.codex/skills/threejs-textures/SKILL.md +628 -0
  24. package/template/README.md +352 -0
  25. package/template/docs/.gitkeep +0 -0
  26. package/template/plans/.gitkeep +0 -0
  27. package/template/prompts/01-mockup-generation.md +44 -0
  28. package/template/prompts/02-prd-generation.md +119 -0
  29. package/template/prompts/03-tdd-generation.md +127 -0
  30. package/template/prompts/04-execution-plan.md +89 -0
  31. package/template/prompts/05-implementation.md +61 -0
  32. package/template/public/assets/.gitkeep +0 -0
  33. package/template/scripts/config.example.json +12 -0
  34. package/template/scripts/generate-assets-json.js +149 -0
  35. package/template/scripts/generate-mockup.js +197 -0
  36. package/template/scripts/generate-plan.js +295 -0
  37. package/template/scripts/generate-prd.js +297 -0
  38. package/template/scripts/generate-tdd.js +283 -0
  39. package/template/scripts/pipeline.js +229 -0
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # create-threejs-game
2
+
3
+ Scaffold a Three.js game project with AI-assisted design documents.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx create-threejs-game my-game
9
+ cd my-game
10
+ # Add assets to public/assets/my_game/
11
+ node scripts/pipeline.js
12
+ ```
13
+
14
+ ## What it does
15
+
16
+ This CLI creates a Three.js game project with:
17
+
18
+ - **Three.js skills** for Claude/Codex AI assistance
19
+ - **Automation scripts** to generate:
20
+ - Asset index (assets.json)
21
+ - Concept mockup (via Google AI Studio)
22
+ - Product Requirements Document (PRD)
23
+ - Technical Design Document (TDD)
24
+ - Implementation plan
25
+ - **Prompt templates** for each generation step
26
+
27
+ ## Interactive Setup
28
+
29
+ Run without arguments for interactive mode:
30
+
31
+ ```bash
32
+ npx create-threejs-game
33
+ ```
34
+
35
+ You'll be asked for:
36
+ 1. Project name
37
+ 2. Game description (1-3 sentences)
38
+ 3. API keys (only if not found in environment)
39
+
40
+ **Supported environment variables:**
41
+ - `GOOGLE_API_KEY` or `GOOGLE_AI_STUDIO_API_KEY`
42
+ - `ANTHROPIC_API_KEY`
43
+
44
+ ## Manual Steps
45
+
46
+ After scaffolding, you'll need to:
47
+
48
+ 1. **Add your 3D assets** to `public/assets/{game_name}/`
49
+ - Download from itch.io, Kenney.nl, Quaternius, etc.
50
+ - GLTF format preferred
51
+
52
+ 2. **Add Preview.jpg** to the assets folder
53
+ - Most asset packs include one
54
+ - Or screenshot your assets
55
+
56
+ 3. **Configure API keys** in `scripts/config.json`:
57
+ - Google AI Studio: for mockup generation
58
+ - Anthropic: for PRD/TDD/plan generation
59
+
60
+ ## Automated Pipeline
61
+
62
+ Once configured, run:
63
+
64
+ ```bash
65
+ node scripts/pipeline.js
66
+ ```
67
+
68
+ This generates all design documents automatically, leaving you with an implementation plan to follow in Claude Code or Cursor.
69
+
70
+ ## Project Structure
71
+
72
+ ```
73
+ my-game/
74
+ ├── .claude/skills/ # Three.js skills for Claude
75
+ ├── .codex/skills/ # Three.js skills for Codex
76
+ ├── docs/ # Generated PRD and TDD
77
+ ├── plans/ # Generated implementation plans
78
+ ├── prompts/ # Manual prompt templates (fallback)
79
+ ├── public/
80
+ │ └── assets/{game}/ # Your 3D assets go here
81
+ ├── scripts/
82
+ │ ├── config.json # Your configuration
83
+ │ ├── pipeline.js # Run full automation
84
+ │ └── generate-*.js # Individual generators
85
+ └── README.md # Detailed workflow guide
86
+ ```
87
+
88
+ ## Requirements
89
+
90
+ - Node.js 18+
91
+ - API keys (for automation):
92
+ - [Google AI Studio](https://aistudio.google.com/) - free tier available
93
+ - [Anthropic](https://console.anthropic.com/) - API access required
94
+
95
+ ## License
96
+
97
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,370 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * create-threejs-game CLI
5
+ *
6
+ * Interactive CLI to scaffold a Three.js game project with AI-assisted
7
+ * design documents and automation.
8
+ *
9
+ * Usage:
10
+ * npx create-threejs-game
11
+ * npx create-threejs-game my-game
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const readline = require('readline');
17
+ const { execSync, spawn } = require('child_process');
18
+
19
+ // Colors for terminal output
20
+ const colors = {
21
+ reset: '\x1b[0m',
22
+ bright: '\x1b[1m',
23
+ dim: '\x1b[2m',
24
+ red: '\x1b[31m',
25
+ green: '\x1b[32m',
26
+ yellow: '\x1b[33m',
27
+ blue: '\x1b[34m',
28
+ magenta: '\x1b[35m',
29
+ cyan: '\x1b[36m'
30
+ };
31
+
32
+ const c = (color, text) => `${colors[color]}${text}${colors.reset}`;
33
+
34
+ // Banner
35
+ function showBanner() {
36
+ console.log('');
37
+ console.log(c('cyan', '╔═══════════════════════════════════════════════════════════════╗'));
38
+ console.log(c('cyan', '║') + c('bright', ' CREATE-THREEJS-GAME ') + c('cyan', '║'));
39
+ console.log(c('cyan', '║') + ' AI-Assisted Three.js Game Scaffolding ' + c('cyan', '║'));
40
+ console.log(c('cyan', '╚═══════════════════════════════════════════════════════════════╝'));
41
+ console.log('');
42
+ }
43
+
44
+ // Readline interface
45
+ const rl = readline.createInterface({
46
+ input: process.stdin,
47
+ output: process.stdout
48
+ });
49
+
50
+ // Promisified question
51
+ function ask(question, defaultValue = '') {
52
+ const prompt = defaultValue
53
+ ? `${question} ${c('dim', `(${defaultValue})`)}: `
54
+ : `${question}: `;
55
+
56
+ return new Promise((resolve) => {
57
+ rl.question(prompt, (answer) => {
58
+ resolve(answer.trim() || defaultValue);
59
+ });
60
+ });
61
+ }
62
+
63
+ // Promisified yes/no
64
+ async function confirm(question, defaultYes = true) {
65
+ const hint = defaultYes ? 'Y/n' : 'y/N';
66
+ const answer = await ask(`${question} [${hint}]`);
67
+
68
+ if (!answer) return defaultYes;
69
+ return answer.toLowerCase().startsWith('y');
70
+ }
71
+
72
+ // Copy directory recursively
73
+ function copyDir(src, dest, exclude = []) {
74
+ if (!fs.existsSync(dest)) {
75
+ fs.mkdirSync(dest, { recursive: true });
76
+ }
77
+
78
+ const entries = fs.readdirSync(src, { withFileTypes: true });
79
+
80
+ for (const entry of entries) {
81
+ if (exclude.includes(entry.name)) continue;
82
+
83
+ const srcPath = path.join(src, entry.name);
84
+ const destPath = path.join(dest, entry.name);
85
+
86
+ if (entry.isDirectory()) {
87
+ copyDir(srcPath, destPath, exclude);
88
+ } else {
89
+ fs.copyFileSync(srcPath, destPath);
90
+ }
91
+ }
92
+ }
93
+
94
+ // Run a script
95
+ function runScript(scriptPath, args = [], cwd) {
96
+ return new Promise((resolve, reject) => {
97
+ const child = spawn('node', [scriptPath, ...args], {
98
+ cwd,
99
+ stdio: 'inherit'
100
+ });
101
+
102
+ child.on('close', (code) => {
103
+ if (code === 0) resolve();
104
+ else reject(new Error(`Script exited with code ${code}`));
105
+ });
106
+
107
+ child.on('error', reject);
108
+ });
109
+ }
110
+
111
+ // Main CLI
112
+ async function main() {
113
+ showBanner();
114
+
115
+ // Get project name from args or prompt
116
+ let projectName = process.argv[2];
117
+
118
+ if (!projectName) {
119
+ projectName = await ask(c('bright', 'Project name'), 'my-threejs-game');
120
+ }
121
+
122
+ // Sanitize project name
123
+ projectName = projectName.toLowerCase().replace(/[^a-z0-9-_]/g, '-');
124
+
125
+ const projectPath = path.join(process.cwd(), projectName);
126
+
127
+ // Check if directory exists
128
+ if (fs.existsSync(projectPath)) {
129
+ const overwrite = await confirm(
130
+ c('yellow', `Directory "${projectName}" already exists. Overwrite?`),
131
+ false
132
+ );
133
+ if (!overwrite) {
134
+ console.log(c('red', '\nAborted.'));
135
+ rl.close();
136
+ process.exit(1);
137
+ }
138
+ fs.rmSync(projectPath, { recursive: true });
139
+ }
140
+
141
+ console.log('');
142
+
143
+ // Game name is same as project name (sanitized for folder)
144
+ const gameName = projectName.replace(/-/g, '_');
145
+
146
+ // Get game details
147
+ console.log(c('bright', '📝 Game Details'));
148
+ console.log(c('dim', '─'.repeat(50)));
149
+ console.log(c('dim', 'Describe your game in 1-3 sentences. Be specific about:'));
150
+ console.log(c('dim', ' - Game type (RTS, tower defense, puzzle, etc.)'));
151
+ console.log(c('dim', ' - Setting/theme'));
152
+ console.log(c('dim', ' - Core mechanics'));
153
+ console.log('');
154
+
155
+ const gameDescription = await ask(c('bright', 'Game description'));
156
+
157
+ if (!gameDescription) {
158
+ console.log(c('yellow', '\nWarning: No description provided. You can edit config.json later.'));
159
+ }
160
+
161
+ console.log('');
162
+
163
+ // API Keys - check env vars first
164
+ let googleApiKey = process.env.GOOGLE_API_KEY || process.env.GOOGLE_AI_STUDIO_API_KEY || '';
165
+ let anthropicApiKey = process.env.ANTHROPIC_API_KEY || '';
166
+
167
+ const hasGoogleEnv = googleApiKey && googleApiKey.length > 10;
168
+ const hasAnthropicEnv = anthropicApiKey && anthropicApiKey.length > 10;
169
+
170
+ if (hasGoogleEnv && hasAnthropicEnv) {
171
+ console.log(c('bright', '🔑 API Keys'));
172
+ console.log(c('dim', '─'.repeat(50)));
173
+ console.log(c('green', ' ✓ ') + 'GOOGLE_API_KEY found in environment');
174
+ console.log(c('green', ' ✓ ') + 'ANTHROPIC_API_KEY found in environment');
175
+ console.log('');
176
+ } else {
177
+ console.log(c('bright', '🔑 API Keys (optional - can configure later)'));
178
+ console.log(c('dim', '─'.repeat(50)));
179
+
180
+ if (hasGoogleEnv) {
181
+ console.log(c('green', ' ✓ ') + 'GOOGLE_API_KEY found in environment');
182
+ } else {
183
+ console.log(c('dim', 'Google AI Studio enables automated mockup generation.'));
184
+ googleApiKey = await ask('Google AI Studio API key');
185
+ }
186
+
187
+ if (hasAnthropicEnv) {
188
+ console.log(c('green', ' ✓ ') + 'ANTHROPIC_API_KEY found in environment');
189
+ } else {
190
+ console.log(c('dim', 'Anthropic enables automated PRD/TDD/plan generation.'));
191
+ anthropicApiKey = await ask('Anthropic API key');
192
+ }
193
+
194
+ console.log('');
195
+ }
196
+
197
+ // Copy template
198
+ console.log(c('bright', '📦 Creating project...'));
199
+ console.log(c('dim', '─'.repeat(50)));
200
+
201
+ // Find template directory
202
+ const templateDir = path.join(__dirname, '..', 'template');
203
+ const fallbackTemplateDir = path.join(__dirname, '..');
204
+
205
+ const sourceDir = fs.existsSync(templateDir) ? templateDir : fallbackTemplateDir;
206
+
207
+ // Files/folders to copy
208
+ const itemsToCopy = ['.claude', '.codex', 'docs', 'plans', 'prompts', 'public', 'scripts', 'README.md'];
209
+
210
+ fs.mkdirSync(projectPath, { recursive: true });
211
+
212
+ for (const item of itemsToCopy) {
213
+ const srcPath = path.join(sourceDir, item);
214
+ const destPath = path.join(projectPath, item);
215
+
216
+ if (fs.existsSync(srcPath)) {
217
+ if (fs.statSync(srcPath).isDirectory()) {
218
+ copyDir(srcPath, destPath, ['node_modules', '.git']);
219
+ } else {
220
+ fs.copyFileSync(srcPath, destPath);
221
+ }
222
+ console.log(c('green', ' ✓ ') + item);
223
+ }
224
+ }
225
+
226
+ // Create config.json
227
+ const config = {
228
+ google_ai_studio: {
229
+ api_key: googleApiKey || 'YOUR_GOOGLE_AI_STUDIO_API_KEY'
230
+ },
231
+ anthropic: {
232
+ api_key: anthropicApiKey || 'YOUR_ANTHROPIC_API_KEY'
233
+ },
234
+ game: {
235
+ name: gameName,
236
+ description: gameDescription || 'YOUR_GAME_DESCRIPTION'
237
+ }
238
+ };
239
+
240
+ const configPath = path.join(projectPath, 'scripts', 'config.json');
241
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
242
+ console.log(c('green', ' ✓ ') + 'scripts/config.json');
243
+
244
+ // Create assets directory
245
+ const assetsDir = path.join(projectPath, 'public', 'assets', gameName);
246
+ fs.mkdirSync(assetsDir, { recursive: true });
247
+ fs.writeFileSync(path.join(assetsDir, '.gitkeep'), '');
248
+ console.log(c('green', ' ✓ ') + `public/assets/${gameName}/`);
249
+
250
+ console.log('');
251
+ console.log(c('green', `✅ Project created at: ${projectPath}`));
252
+ console.log('');
253
+
254
+ // Check if we can run automation
255
+ const hasGoogleKey = googleApiKey && !googleApiKey.includes('YOUR_');
256
+ const hasAnthropicKey = anthropicApiKey && !anthropicApiKey.includes('YOUR_');
257
+ const hasDescription = gameDescription && gameDescription.length > 10;
258
+
259
+ // Next steps
260
+ console.log(c('bright', '📋 Next Steps'));
261
+ console.log(c('dim', '─'.repeat(50)));
262
+ console.log('');
263
+
264
+ const steps = [];
265
+ let stepNum = 1;
266
+
267
+ // Step: Add assets
268
+ steps.push({
269
+ num: stepNum++,
270
+ manual: true,
271
+ text: `Add your 3D assets to ${c('cyan', `public/assets/${gameName}/`)}`,
272
+ detail: 'Download a GLTF asset pack from itch.io, Kenney.nl, or similar'
273
+ });
274
+
275
+ // Step: Add preview
276
+ steps.push({
277
+ num: stepNum++,
278
+ manual: true,
279
+ text: `Ensure ${c('cyan', 'Preview.jpg')} exists in the assets folder`,
280
+ detail: 'Most asset packs include one, or take a screenshot of your assets'
281
+ });
282
+
283
+ // Step: API keys
284
+ if (!hasGoogleKey || !hasAnthropicKey) {
285
+ const missing = [];
286
+ if (!hasGoogleKey) missing.push('Google AI Studio');
287
+ if (!hasAnthropicKey) missing.push('Anthropic');
288
+
289
+ steps.push({
290
+ num: stepNum++,
291
+ manual: true,
292
+ text: `Add API keys to ${c('cyan', 'scripts/config.json')}`,
293
+ detail: `Missing: ${missing.join(', ')}`
294
+ });
295
+ }
296
+
297
+ // Step: Description
298
+ if (!hasDescription) {
299
+ steps.push({
300
+ num: stepNum++,
301
+ manual: true,
302
+ text: `Add game description to ${c('cyan', 'scripts/config.json')}`,
303
+ detail: 'Be specific about game type, setting, and mechanics'
304
+ });
305
+ }
306
+
307
+ // Step: Run pipeline
308
+ steps.push({
309
+ num: stepNum++,
310
+ manual: false,
311
+ text: `Run ${c('cyan', 'node scripts/pipeline.js')}`,
312
+ detail: 'Generates assets.json, mockup, PRD, TDD, and execution plan'
313
+ });
314
+
315
+ // Step: Implement
316
+ steps.push({
317
+ num: stepNum++,
318
+ manual: false,
319
+ text: 'Open in Claude Code/Cursor and follow the generated plan',
320
+ detail: 'The plan will be in plans/ folder with implementation instructions'
321
+ });
322
+
323
+ // Print steps
324
+ for (const step of steps) {
325
+ const icon = step.manual ? c('yellow', '🖐️ ') : c('green', '🤖 ');
326
+ const label = step.manual ? c('yellow', '[MANUAL]') : c('green', '[AUTO]');
327
+
328
+ console.log(`${icon}${step.num}. ${step.text}`);
329
+ console.log(` ${label} ${c('dim', step.detail)}`);
330
+ console.log('');
331
+ }
332
+
333
+ // Summary
334
+ console.log(c('dim', '─'.repeat(50)));
335
+ console.log('');
336
+ console.log(c('bright', 'Quick commands:'));
337
+ console.log(` ${c('cyan', `cd ${projectName}`)}`);
338
+ console.log(` ${c('cyan', 'node scripts/pipeline.js')} ${c('dim', '# After adding assets')}`);
339
+ console.log('');
340
+
341
+ // Offer to open directory
342
+ const openDir = await confirm('Open project directory?', true);
343
+
344
+ rl.close();
345
+
346
+ if (openDir) {
347
+ try {
348
+ if (process.platform === 'darwin') {
349
+ execSync(`open "${projectPath}"`);
350
+ } else if (process.platform === 'win32') {
351
+ execSync(`start "" "${projectPath}"`);
352
+ } else {
353
+ execSync(`xdg-open "${projectPath}"`);
354
+ }
355
+ } catch (e) {
356
+ console.log(c('dim', `cd ${projectPath}`));
357
+ }
358
+ }
359
+
360
+ console.log('');
361
+ console.log(c('green', '🎮 Happy game building!'));
362
+ console.log('');
363
+ }
364
+
365
+ // Run
366
+ main().catch((err) => {
367
+ console.error(c('red', '\nError: ') + err.message);
368
+ rl.close();
369
+ process.exit(1);
370
+ });
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "create-threejs-game",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold a Three.js game project with AI-assisted design documents",
5
+ "bin": {
6
+ "create-threejs-game": "./bin/cli.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "template"
11
+ ],
12
+ "keywords": [
13
+ "threejs",
14
+ "game",
15
+ "scaffold",
16
+ "cli",
17
+ "ai",
18
+ "vibe-coding"
19
+ ],
20
+ "author": "ElementTech",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/ElementTech/create-threejs-game.git"
25
+ },
26
+ "engines": {
27
+ "node": ">=18.0.0"
28
+ }
29
+ }