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
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Execution Plan Generation Script
5
+ *
6
+ * Uses Claude API (Opus 4.5) to generate an implementation plan
7
+ * based on the PRD and TDD.
8
+ *
9
+ * Usage:
10
+ * node generate-plan.js [plan-name]
11
+ *
12
+ * Example:
13
+ * node generate-plan.js initial-implementation
14
+ *
15
+ * Requires:
16
+ * - config.json with anthropic.api_key and game settings
17
+ * - docs/prd.md
18
+ * - docs/tdd.md
19
+ * - public/assets/{game_name}/assets.json
20
+ */
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const https = require('https');
25
+
26
+ // Generate random plan name if not provided
27
+ function generatePlanName() {
28
+ const adjectives = ['swift', 'bold', 'bright', 'calm', 'dark', 'eager', 'fair', 'grand', 'keen', 'light', 'mossy', 'noble', 'proud', 'quick', 'rare', 'sage', 'true', 'vast', 'warm', 'wise'];
29
+ const nouns = ['dawn', 'dusk', 'flame', 'frost', 'glade', 'grove', 'haven', 'isle', 'lake', 'marsh', 'oak', 'peak', 'pine', 'ridge', 'shore', 'stone', 'tide', 'vale', 'wind', 'wood'];
30
+ const names = ['ada', 'blake', 'chen', 'darwin', 'euler', 'feynman', 'gauss', 'hopper', 'ito', 'jung', 'knuth', 'lovelace', 'maxwell', 'newton', 'oracle', 'pascal', 'quine', 'riemann', 'shannon', 'turing'];
31
+
32
+ const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
33
+ const noun = nouns[Math.floor(Math.random() * nouns.length)];
34
+ const name = names[Math.floor(Math.random() * names.length)];
35
+
36
+ return `${adj}-${noun}-${name}`;
37
+ }
38
+
39
+ // Load config
40
+ const scriptDir = __dirname;
41
+ const projectRoot = path.join(scriptDir, '..');
42
+ const configPath = path.join(scriptDir, 'config.json');
43
+
44
+ if (!fs.existsSync(configPath)) {
45
+ console.error('Error: config.json not found');
46
+ process.exit(1);
47
+ }
48
+
49
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
50
+ const { anthropic, game } = config;
51
+
52
+ // Check config first, then env vars
53
+ const apiKey = (anthropic?.api_key && !anthropic.api_key.includes('YOUR_'))
54
+ ? anthropic.api_key
55
+ : process.env.ANTHROPIC_API_KEY;
56
+
57
+ if (!apiKey) {
58
+ console.error('Error: Anthropic API key not found');
59
+ console.error('Set ANTHROPIC_API_KEY env var or configure scripts/config.json');
60
+ process.exit(1);
61
+ }
62
+
63
+ // Use resolved key
64
+ anthropic.api_key = apiKey;
65
+
66
+ // Plan name from args or generate
67
+ const planName = process.argv[2] || generatePlanName();
68
+
69
+ // Paths
70
+ const prdPath = path.join(projectRoot, 'docs', 'prd.md');
71
+ const tddPath = path.join(projectRoot, 'docs', 'tdd.md');
72
+ const assetsJsonPath = path.join(projectRoot, 'public', 'assets', game.name, 'assets.json');
73
+ const plansDir = path.join(projectRoot, 'plans');
74
+ const outputPath = path.join(plansDir, `${planName}.md`);
75
+
76
+ // Check required files
77
+ const missingFiles = [];
78
+ if (!fs.existsSync(prdPath)) missingFiles.push(prdPath + ' (run generate-prd.js first)');
79
+ if (!fs.existsSync(tddPath)) missingFiles.push(tddPath + ' (run generate-tdd.js first)');
80
+ if (!fs.existsSync(assetsJsonPath)) missingFiles.push(assetsJsonPath);
81
+
82
+ if (missingFiles.length > 0) {
83
+ console.error('Error: Missing required files:');
84
+ missingFiles.forEach(f => console.error(' - ' + f));
85
+ process.exit(1);
86
+ }
87
+
88
+ // Read files
89
+ const prdContent = fs.readFileSync(prdPath, 'utf-8');
90
+ const tddContent = fs.readFileSync(tddPath, 'utf-8');
91
+ const assetsJson = fs.readFileSync(assetsJsonPath, 'utf-8');
92
+
93
+ // Build prompt
94
+ const prompt = `Implement the game defined in the PRD below, adhering to the technical design in the TDD.
95
+
96
+ ## PRD (Product Requirements Document):
97
+ ${prdContent}
98
+
99
+ ## TDD (Technical Design Document):
100
+ ${tddContent.substring(0, 80000)}
101
+
102
+ ## Available Assets (assets.json):
103
+ \`\`\`json
104
+ ${assetsJson}
105
+ \`\`\`
106
+
107
+ ---
108
+
109
+ Create a concise execution plan for implementing this game. The plan should be actionable and reference specific sections from the TDD.
110
+
111
+ # ${game.name.charAt(0).toUpperCase() + game.name.slice(1)} - Implementation Plan
112
+
113
+ ## Overview
114
+ - Target file: \`public/index.html\` (single HTML file with inline CSS/JS)
115
+ - Key references to PRD and TDD sections
116
+ - Asset path format: \`assets/${game.name}/glTF/<FILENAME>.gltf\`
117
+
118
+ ## Implementation Phases
119
+
120
+ ### Phase 1: Core Engine (Critical)
121
+ What to implement from TDD:
122
+ - HTML structure with import map for Three.js r160
123
+ - Renderer with letterboxing
124
+ - Camera system
125
+ - Lighting setup
126
+ - Ground plane
127
+
128
+ ### Phase 2: Asset Loading (Critical)
129
+ - GLTFLoader setup
130
+ - Core asset manifest
131
+ - Fallback primitives
132
+
133
+ ### Phase 3: ECS Architecture (Critical)
134
+ - Entity class
135
+ - All component classes
136
+ - Entity Manager
137
+ - Entity Factory
138
+
139
+ ### Phase 4: Selection System (Critical)
140
+ - Click selection
141
+ - Box selection
142
+ - Visual feedback (rings, animations)
143
+
144
+ ### Phase 5: Command System (Critical)
145
+ - Move commands
146
+ - Attack commands
147
+ - Context-sensitive commands
148
+
149
+ ### Phase 6: Movement System (Critical)
150
+ - Unit movement
151
+ - Collision/separation
152
+ - Turn rate smoothing
153
+
154
+ ### Phase 7: Combat System (Critical)
155
+ - Melee attacks
156
+ - Ranged attacks (if applicable)
157
+ - Damage and death
158
+
159
+ ### Phase 8: Economy System (Important)
160
+ - Resource gathering (if applicable)
161
+ - Building placement (if applicable)
162
+ - Unit training (if applicable)
163
+
164
+ ### Phase 9: AI System (Important)
165
+ - State machine
166
+ - Economy behavior
167
+ - Combat behavior
168
+
169
+ ### Phase 10: UI & Game States (Important)
170
+ - Menu screen
171
+ - HUD elements
172
+ - Pause screen
173
+ - Game over screen
174
+
175
+ ### Phase 11: Effects (Polish)
176
+ - Screen shake
177
+ - Time dilation
178
+ - Particles
179
+ - Death sequences
180
+
181
+ ### Phase 12: Mobile/Polish (Polish)
182
+ - Touch controls
183
+ - Onboarding tooltips
184
+ - Final polish
185
+
186
+ ## Map Setup
187
+ - Initial entity positions
188
+ - Resource placement
189
+ - Obstacle placement
190
+
191
+ ## Verification Checklist
192
+ From PRD Success Criteria:
193
+ - [ ] Game loads without errors
194
+ - [ ] (Include all success criteria)
195
+
196
+ ## Estimated Code Size
197
+ ~3000-4000 lines of JavaScript
198
+
199
+ ## Next Step
200
+ To implement, use this prompt with Claude Code:
201
+ \`\`\`
202
+ Please proceed with implementing the game based on the plan in plans/${planName}.md
203
+ Use the Three.js skills for reference.
204
+ \`\`\`
205
+ `;
206
+
207
+ console.log('Generating execution plan with Claude API...');
208
+ console.log('Game:', game.name);
209
+ console.log('Plan name:', planName);
210
+
211
+ // Claude API request
212
+ const requestBody = JSON.stringify({
213
+ model: 'claude-sonnet-4-20250514',
214
+ max_tokens: 8000,
215
+ messages: [{
216
+ role: 'user',
217
+ content: prompt
218
+ }]
219
+ });
220
+
221
+ const options = {
222
+ hostname: 'api.anthropic.com',
223
+ port: 443,
224
+ path: '/v1/messages',
225
+ method: 'POST',
226
+ headers: {
227
+ 'Content-Type': 'application/json',
228
+ 'x-api-key': anthropic.api_key,
229
+ 'anthropic-version': '2023-06-01'
230
+ }
231
+ };
232
+
233
+ const req = https.request(options, (res) => {
234
+ let data = '';
235
+
236
+ res.on('data', (chunk) => {
237
+ data += chunk;
238
+ process.stdout.write('.');
239
+ });
240
+
241
+ res.on('end', () => {
242
+ console.log('\n');
243
+
244
+ try {
245
+ const response = JSON.parse(data);
246
+
247
+ if (response.error) {
248
+ console.error('API Error:', response.error.message);
249
+ process.exit(1);
250
+ }
251
+
252
+ // Extract text content
253
+ const content = response.content || [];
254
+ let planContent = '';
255
+
256
+ for (const block of content) {
257
+ if (block.type === 'text') {
258
+ planContent += block.text;
259
+ }
260
+ }
261
+
262
+ if (!planContent) {
263
+ console.error('No content in response');
264
+ process.exit(1);
265
+ }
266
+
267
+ // Ensure plans directory exists
268
+ if (!fs.existsSync(plansDir)) {
269
+ fs.mkdirSync(plansDir, { recursive: true });
270
+ }
271
+
272
+ // Save plan
273
+ fs.writeFileSync(outputPath, planContent);
274
+
275
+ console.log('Execution plan generated successfully!');
276
+ console.log('Output:', outputPath);
277
+ console.log('');
278
+ console.log('Next step - run in Claude Code:');
279
+ console.log(` Please proceed with implementing the game based on the plan in plans/${planName}.md`);
280
+
281
+ } catch (err) {
282
+ console.error('Error parsing response:', err.message);
283
+ console.error('Raw response:', data.substring(0, 1000));
284
+ process.exit(1);
285
+ }
286
+ });
287
+ });
288
+
289
+ req.on('error', (err) => {
290
+ console.error('Request error:', err.message);
291
+ process.exit(1);
292
+ });
293
+
294
+ req.write(requestBody);
295
+ req.end();
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * PRD Generation Script
5
+ *
6
+ * Uses Claude API (Opus 4.5) to generate a Product Requirements Document
7
+ * based on the concept mockup, assets, and game description.
8
+ *
9
+ * Usage:
10
+ * node generate-prd.js
11
+ *
12
+ * Requires:
13
+ * - config.json with anthropic.api_key and game settings
14
+ * - public/{game_name}/concept.jpg (run generate-mockup.js first)
15
+ * - public/assets/{game_name}/Preview.jpg
16
+ * - public/assets/{game_name}/assets.json
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+ const https = require('https');
22
+
23
+ // Load config
24
+ const scriptDir = __dirname;
25
+ const projectRoot = path.join(scriptDir, '..');
26
+ const configPath = path.join(scriptDir, 'config.json');
27
+
28
+ if (!fs.existsSync(configPath)) {
29
+ console.error('Error: config.json not found');
30
+ process.exit(1);
31
+ }
32
+
33
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
34
+ const { anthropic, game } = config;
35
+
36
+ // Check config first, then env vars
37
+ const apiKey = (anthropic?.api_key && !anthropic.api_key.includes('YOUR_'))
38
+ ? anthropic.api_key
39
+ : process.env.ANTHROPIC_API_KEY;
40
+
41
+ if (!apiKey) {
42
+ console.error('Error: Anthropic API key not found');
43
+ console.error('Set ANTHROPIC_API_KEY env var or configure scripts/config.json');
44
+ process.exit(1);
45
+ }
46
+
47
+ // Use resolved key
48
+ anthropic.api_key = apiKey;
49
+
50
+ // Paths
51
+ const assetsDir = path.join(projectRoot, 'public', 'assets', game.name);
52
+ const conceptPath = path.join(projectRoot, 'public', game.name, 'concept.jpg');
53
+ const previewPath = path.join(assetsDir, 'Preview.jpg');
54
+ const assetsJsonPath = path.join(assetsDir, 'assets.json');
55
+ const outputPath = path.join(projectRoot, 'docs', 'prd.md');
56
+
57
+ // Check required files
58
+ const missingFiles = [];
59
+ if (!fs.existsSync(conceptPath)) missingFiles.push(conceptPath);
60
+ if (!fs.existsSync(previewPath)) missingFiles.push(previewPath);
61
+ if (!fs.existsSync(assetsJsonPath)) missingFiles.push(assetsJsonPath);
62
+
63
+ if (missingFiles.length > 0) {
64
+ console.error('Error: Missing required files:');
65
+ missingFiles.forEach(f => console.error(' - ' + f));
66
+ if (missingFiles.includes(conceptPath)) {
67
+ console.error('\nRun: node generate-mockup.js first');
68
+ }
69
+ process.exit(1);
70
+ }
71
+
72
+ // Read files
73
+ const conceptImage = fs.readFileSync(conceptPath).toString('base64');
74
+ const previewImage = fs.readFileSync(previewPath).toString('base64');
75
+ const assetsJson = fs.readFileSync(assetsJsonPath, 'utf-8');
76
+
77
+ // Load Three.js skills for context
78
+ const skillsDir = path.join(projectRoot, '.claude', 'skills');
79
+ let skillsContext = '';
80
+ if (fs.existsSync(skillsDir)) {
81
+ const skillFolders = fs.readdirSync(skillsDir);
82
+ for (const folder of skillFolders.slice(0, 3)) { // Just first 3 for context length
83
+ const skillPath = path.join(skillsDir, folder, 'SKILL.md');
84
+ if (fs.existsSync(skillPath)) {
85
+ const content = fs.readFileSync(skillPath, 'utf-8');
86
+ skillsContext += `\n\n### ${folder}\n${content.substring(0, 2000)}...`;
87
+ }
88
+ }
89
+ }
90
+
91
+ // Build prompt
92
+ const prompt = `${game.description}
93
+
94
+ I have concept mockups that reflect how the game looks (attached as concept.jpg).
95
+ I have also added a preview of the assets that are available (attached as Preview.jpg).
96
+
97
+ Here is the assets.json index of all available assets:
98
+ \`\`\`json
99
+ ${assetsJson}
100
+ \`\`\`
101
+
102
+ Create a comprehensive Game Design Document (PRD) with the following sections:
103
+
104
+ ## 1. Summary
105
+ - Brief description of the game
106
+ - Target platform (browser-based)
107
+ - Key assumptions and constraints for V1
108
+ - Match length / session time
109
+
110
+ ## 2. Technical Requirements
111
+ - Three.js version (r160 recommended)
112
+ - Delivery format (single HTML file preferred)
113
+ - Unit system (world units = meters)
114
+ - Required loaders (GLTFLoader)
115
+ - Valid materials and lights
116
+
117
+ ## 3. Canvas & Viewport
118
+ - Internal resolution (e.g., 960×540)
119
+ - Aspect ratio handling (letterboxing if fixed)
120
+ - Background style
121
+
122
+ ## 4. Visual Style & Art Direction
123
+ - Overall look description
124
+ - Color palette with hex codes and purposes
125
+ - Mood/atmosphere
126
+ - Camera style and defaults (pitch, yaw, zoom range)
127
+ - Lighting mood
128
+
129
+ ## 5. Player Specifications
130
+ - Faction/player identity if applicable
131
+ - Unit types with appearance, size, role, and stats
132
+ - Starting setup (resources, units, position)
133
+ - Movement constraints
134
+
135
+ ## 6. Physics & Movement
136
+ - Movement model (kinematic, physics-based)
137
+ - Gravity, speeds, collision approach
138
+ - Unit movement values table
139
+
140
+ ## 7. Obstacles/Enemies
141
+ - Enemy types and behaviors
142
+ - Neutral obstacles using available assets
143
+ - Spawn timing and difficulty scaling
144
+
145
+ ## 8. World & Environment
146
+ - Map layout and dimensions
147
+ - Resource/pickup nodes and their values
148
+ - Buildings/structures using available GLTF assets (reference specific asset names)
149
+ - Fallback primitives if assets fail to load
150
+
151
+ ## 9. Collision & Scoring
152
+ - Collision shapes and approach
153
+ - Win/lose conditions
154
+ - Score system and point values
155
+ - High score storage (localStorage key)
156
+
157
+ ## 10. Controls
158
+ - Complete input mapping table
159
+ - Desktop and touch/mobile controls
160
+ - Keyboard shortcuts
161
+
162
+ ## 11. Game States
163
+ - Menu state (buttons, background)
164
+ - Playing state (active systems, UI shown)
165
+ - Paused state (trigger, display, frozen elements)
166
+ - Game Over state (display, stats, retry flow)
167
+
168
+ ## 12. Game Feel & Juice (REQUIRED)
169
+ - Input response feedback (selection, commands)
170
+ - Animation timing table
171
+ - Screen effects (shake, flash, zoom, time dilation)
172
+ - Death sequences
173
+ - Milestone celebrations
174
+ - Idle life animations
175
+
176
+ ## 13. UX Requirements
177
+ - Controls visibility
178
+ - Onboarding flow
179
+ - Readability considerations
180
+ - Forgiving mechanics
181
+
182
+ ## 14. Out of Scope (V1)
183
+ - Features explicitly NOT included
184
+
185
+ ## 15. Success Criteria
186
+ - Checklist of requirements the game must meet
187
+
188
+ Reference the assets.json for available models. Use specific asset names (e.g., "TownCenter_FirstAge_Level1.gltf") when specifying which assets to use for game elements.`;
189
+
190
+ console.log('Generating PRD with Claude API (Opus 4.5)...');
191
+ console.log('Game:', game.name);
192
+
193
+ // Claude API request
194
+ const requestBody = JSON.stringify({
195
+ model: 'claude-sonnet-4-20250514',
196
+ max_tokens: 16000,
197
+ messages: [{
198
+ role: 'user',
199
+ content: [
200
+ {
201
+ type: 'image',
202
+ source: {
203
+ type: 'base64',
204
+ media_type: 'image/jpeg',
205
+ data: conceptImage
206
+ }
207
+ },
208
+ {
209
+ type: 'image',
210
+ source: {
211
+ type: 'base64',
212
+ media_type: 'image/jpeg',
213
+ data: previewImage
214
+ }
215
+ },
216
+ {
217
+ type: 'text',
218
+ text: prompt
219
+ }
220
+ ]
221
+ }]
222
+ });
223
+
224
+ const options = {
225
+ hostname: 'api.anthropic.com',
226
+ port: 443,
227
+ path: '/v1/messages',
228
+ method: 'POST',
229
+ headers: {
230
+ 'Content-Type': 'application/json',
231
+ 'x-api-key': anthropic.api_key,
232
+ 'anthropic-version': '2023-06-01'
233
+ }
234
+ };
235
+
236
+ const req = https.request(options, (res) => {
237
+ let data = '';
238
+
239
+ res.on('data', (chunk) => {
240
+ data += chunk;
241
+ process.stdout.write('.');
242
+ });
243
+
244
+ res.on('end', () => {
245
+ console.log('\n');
246
+
247
+ try {
248
+ const response = JSON.parse(data);
249
+
250
+ if (response.error) {
251
+ console.error('API Error:', response.error.message);
252
+ process.exit(1);
253
+ }
254
+
255
+ // Extract text content
256
+ const content = response.content || [];
257
+ let prdContent = '';
258
+
259
+ for (const block of content) {
260
+ if (block.type === 'text') {
261
+ prdContent += block.text;
262
+ }
263
+ }
264
+
265
+ if (!prdContent) {
266
+ console.error('No content in response');
267
+ process.exit(1);
268
+ }
269
+
270
+ // Ensure docs directory exists
271
+ const docsDir = path.join(projectRoot, 'docs');
272
+ if (!fs.existsSync(docsDir)) {
273
+ fs.mkdirSync(docsDir, { recursive: true });
274
+ }
275
+
276
+ // Save PRD
277
+ fs.writeFileSync(outputPath, prdContent);
278
+
279
+ console.log('PRD generated successfully!');
280
+ console.log('Output:', outputPath);
281
+ console.log('Length:', prdContent.length, 'characters');
282
+
283
+ } catch (err) {
284
+ console.error('Error parsing response:', err.message);
285
+ console.error('Raw response:', data.substring(0, 1000));
286
+ process.exit(1);
287
+ }
288
+ });
289
+ });
290
+
291
+ req.on('error', (err) => {
292
+ console.error('Request error:', err.message);
293
+ process.exit(1);
294
+ });
295
+
296
+ req.write(requestBody);
297
+ req.end();