gameforge-cli 0.1.0 → 0.2.1

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 (88) hide show
  1. package/README.md +117 -44
  2. package/dist/agents/base/BaseAgent.d.ts +31 -0
  3. package/dist/agents/base/BaseAgent.d.ts.map +1 -1
  4. package/dist/agents/base/BaseAgent.js +57 -0
  5. package/dist/agents/base/BaseAgent.js.map +1 -1
  6. package/dist/agents/core/Architect.d.ts +21 -5
  7. package/dist/agents/core/Architect.d.ts.map +1 -1
  8. package/dist/agents/core/Architect.js +413 -150
  9. package/dist/agents/core/Architect.js.map +1 -1
  10. package/dist/agents/core/Chaos.d.ts +4 -0
  11. package/dist/agents/core/Chaos.d.ts.map +1 -1
  12. package/dist/agents/core/Chaos.js +46 -11
  13. package/dist/agents/core/Chaos.js.map +1 -1
  14. package/dist/agents/core/Consistency.d.ts +1 -0
  15. package/dist/agents/core/Consistency.d.ts.map +1 -1
  16. package/dist/agents/core/Consistency.js +86 -11
  17. package/dist/agents/core/Consistency.js.map +1 -1
  18. package/dist/agents/core/DocumentUpdater.d.ts +13 -0
  19. package/dist/agents/core/DocumentUpdater.d.ts.map +1 -0
  20. package/dist/agents/core/DocumentUpdater.js +165 -0
  21. package/dist/agents/core/DocumentUpdater.js.map +1 -0
  22. package/dist/agents/core/Modifier.d.ts +13 -0
  23. package/dist/agents/core/Modifier.d.ts.map +1 -0
  24. package/dist/agents/core/Modifier.js +141 -0
  25. package/dist/agents/core/Modifier.js.map +1 -0
  26. package/dist/agents/core/Remediation.d.ts +3 -1
  27. package/dist/agents/core/Remediation.d.ts.map +1 -1
  28. package/dist/agents/core/Remediation.js +63 -3
  29. package/dist/agents/core/Remediation.js.map +1 -1
  30. package/dist/agents/specialists/CreativeSpecialist.d.ts.map +1 -1
  31. package/dist/agents/specialists/CreativeSpecialist.js +162 -25
  32. package/dist/agents/specialists/CreativeSpecialist.js.map +1 -1
  33. package/dist/agents/specialists/EntitySpecialist.d.ts.map +1 -1
  34. package/dist/agents/specialists/EntitySpecialist.js +79 -25
  35. package/dist/agents/specialists/EntitySpecialist.js.map +1 -1
  36. package/dist/agents/specialists/FeatureSpecialist.d.ts +4 -0
  37. package/dist/agents/specialists/FeatureSpecialist.d.ts.map +1 -1
  38. package/dist/agents/specialists/FeatureSpecialist.js +114 -39
  39. package/dist/agents/specialists/FeatureSpecialist.js.map +1 -1
  40. package/dist/agents/specialists/TechSpecialist.d.ts.map +1 -1
  41. package/dist/agents/specialists/TechSpecialist.js +169 -32
  42. package/dist/agents/specialists/TechSpecialist.js.map +1 -1
  43. package/dist/config/schema.d.ts +1319 -709
  44. package/dist/config/schema.d.ts.map +1 -1
  45. package/dist/config/schema.js +142 -52
  46. package/dist/config/schema.js.map +1 -1
  47. package/dist/config/templates.d.ts.map +1 -1
  48. package/dist/config/templates.js +6 -66
  49. package/dist/config/templates.js.map +1 -1
  50. package/dist/core/Orchestrator.d.ts +17 -3
  51. package/dist/core/Orchestrator.d.ts.map +1 -1
  52. package/dist/core/Orchestrator.js +46 -16
  53. package/dist/core/Orchestrator.js.map +1 -1
  54. package/dist/index.js +544 -226
  55. package/dist/index.js.map +1 -1
  56. package/dist/types/issueReview.d.ts +19 -0
  57. package/dist/types/issueReview.d.ts.map +1 -0
  58. package/dist/types/issueReview.js +3 -0
  59. package/dist/types/issueReview.js.map +1 -0
  60. package/dist/utils/costTracker.d.ts +28 -0
  61. package/dist/utils/costTracker.d.ts.map +1 -1
  62. package/dist/utils/costTracker.js +71 -1
  63. package/dist/utils/costTracker.js.map +1 -1
  64. package/dist/utils/disambiguationHelper.d.ts +54 -0
  65. package/dist/utils/disambiguationHelper.d.ts.map +1 -0
  66. package/dist/utils/disambiguationHelper.js +262 -0
  67. package/dist/utils/disambiguationHelper.js.map +1 -0
  68. package/dist/utils/fileManager.d.ts +7 -0
  69. package/dist/utils/fileManager.d.ts.map +1 -1
  70. package/dist/utils/fileManager.js +47 -0
  71. package/dist/utils/fileManager.js.map +1 -1
  72. package/dist/utils/issueReviewer.d.ts +10 -0
  73. package/dist/utils/issueReviewer.d.ts.map +1 -0
  74. package/dist/utils/issueReviewer.js +206 -0
  75. package/dist/utils/issueReviewer.js.map +1 -0
  76. package/dist/utils/issueSelector.d.ts +26 -0
  77. package/dist/utils/issueSelector.d.ts.map +1 -0
  78. package/dist/utils/issueSelector.js +132 -0
  79. package/dist/utils/issueSelector.js.map +1 -0
  80. package/dist/utils/pdfGenerator.d.ts +12 -0
  81. package/dist/utils/pdfGenerator.d.ts.map +1 -0
  82. package/dist/utils/pdfGenerator.js +341 -0
  83. package/dist/utils/pdfGenerator.js.map +1 -0
  84. package/package.json +20 -15
  85. package/dist/core/CheckpointManager.d.ts +0 -16
  86. package/dist/core/CheckpointManager.d.ts.map +0 -1
  87. package/dist/core/CheckpointManager.js +0 -52
  88. package/dist/core/CheckpointManager.js.map +0 -1
@@ -32,13 +32,23 @@ Do NOT wrap the JSON in \`\`\`json or \`\`\` tags.`,
32
32
  return `Q: ${t.question}\nA: ${t.answer}${marker}`;
33
33
  })
34
34
  .join('\n\n');
35
+ // Check if there are ambiguities that need clarification
36
+ const clarifications = await this.checkForAmbiguities(transcript, onProgress);
37
+ // If there are clarifications, add them to the transcript
38
+ const enhancedTranscript = [...transcript, ...clarifications];
39
+ const enhancedTranscriptStr = enhancedTranscript
40
+ .map(t => {
41
+ const marker = t.autoGenerated ? ' [Auto-Generated]' : '';
42
+ return `Q: ${t.question}\nA: ${t.answer}${marker}`;
43
+ })
44
+ .join('\n\n');
35
45
  // Multi-pass generation with constrained prompts
36
46
  onProgress?.('Pass 1/3: Generating meta and core features...');
37
- const metaAndFeatures = await this.generateMetaAndFeatures(transcriptStr, onProgress);
47
+ const metaAndFeatures = await this.generateMetaAndFeatures(enhancedTranscriptStr, onProgress);
38
48
  onProgress?.('Pass 2/3: Generating key game objects...');
39
- const gameObjects = await this.generateGameObjects(transcriptStr, metaAndFeatures, onProgress);
49
+ const gameObjects = await this.generateGameObjects(enhancedTranscriptStr, metaAndFeatures, onProgress);
40
50
  onProgress?.('Pass 3/3: Generating creative and technical specs...');
41
- const creativeAndTech = await this.generateCreativeAndTech(transcriptStr, metaAndFeatures, onProgress);
51
+ const creativeAndTech = await this.generateCreativeAndTech(enhancedTranscriptStr, metaAndFeatures, onProgress);
42
52
  // Merge all parts into final GameBible
43
53
  onProgress?.('Merging and validating...');
44
54
  const gameBible = {
@@ -48,6 +58,10 @@ Do NOT wrap the JSON in \`\`\`json or \`\`\` tags.`,
48
58
  creative: creativeAndTech.creative,
49
59
  technical: creativeAndTech.technical
50
60
  };
61
+ // Check post-generation ambiguities
62
+ await this.checkPostGenerationAmbiguities(gameBible, enhancedTranscript, onProgress);
63
+ // Normalize/coerce common LLM output issues before validation
64
+ this.normalizeGameBible(gameBible);
51
65
  try {
52
66
  const validated = schema_1.GameBibleSchema.parse(gameBible);
53
67
  // Debug logging: Successful validation
@@ -86,16 +100,320 @@ Do NOT wrap the JSON in \`\`\`json or \`\`\` tags.`,
86
100
  throw error;
87
101
  }
88
102
  }
103
+ /**
104
+ * Normalize/coerce common LLM output issues before schema validation.
105
+ * Fixes issues like strings-instead-of-arrays, missing defaults, etc.
106
+ */
107
+ normalizeGameBible(bible) {
108
+ // Helper to convert string to array if needed
109
+ const ensureArray = (value) => {
110
+ if (Array.isArray(value))
111
+ return value;
112
+ if (typeof value === 'string' && value.trim())
113
+ return [value];
114
+ return [];
115
+ };
116
+ // Normalize features
117
+ if (bible.features) {
118
+ for (const feature of bible.features) {
119
+ // Ensure array fields are arrays
120
+ if (feature.dependencies !== undefined) {
121
+ feature.dependencies = ensureArray(feature.dependencies);
122
+ }
123
+ if (feature.gameplayLoop !== undefined) {
124
+ feature.gameplayLoop = ensureArray(feature.gameplayLoop);
125
+ }
126
+ if (feature.uiRequirements !== undefined) {
127
+ feature.uiRequirements = ensureArray(feature.uiRequirements);
128
+ }
129
+ // Nested optional objects
130
+ if (feature.narrative?.storyBeats !== undefined) {
131
+ feature.narrative.storyBeats = ensureArray(feature.narrative.storyBeats);
132
+ }
133
+ if (feature.narrative?.characters !== undefined) {
134
+ feature.narrative.characters = ensureArray(feature.narrative.characters);
135
+ }
136
+ if (feature.economy?.currencies !== undefined) {
137
+ feature.economy.currencies = ensureArray(feature.economy.currencies);
138
+ }
139
+ if (feature.economy?.balanceFactors !== undefined) {
140
+ feature.economy.balanceFactors = ensureArray(feature.economy.balanceFactors);
141
+ }
142
+ if (feature.progression?.unlockConditions !== undefined) {
143
+ feature.progression.unlockConditions = ensureArray(feature.progression.unlockConditions);
144
+ }
145
+ if (feature.procedural?.constraints !== undefined) {
146
+ feature.procedural.constraints = ensureArray(feature.procedural.constraints);
147
+ }
148
+ if (feature.physics?.interactions !== undefined) {
149
+ feature.physics.interactions = ensureArray(feature.physics.interactions);
150
+ }
151
+ }
152
+ }
153
+ // Normalize game objects/entities
154
+ if (bible.gameObjects) {
155
+ for (const entity of bible.gameObjects) {
156
+ // Ensure array fields are arrays
157
+ if (entity.narrative?.dialogues !== undefined) {
158
+ entity.narrative.dialogues = ensureArray(entity.narrative.dialogues);
159
+ }
160
+ if (entity.stats?.behaviors !== undefined) {
161
+ entity.stats.behaviors = ensureArray(entity.stats.behaviors);
162
+ }
163
+ if (entity.references?.features !== undefined) {
164
+ entity.references.features = ensureArray(entity.references.features);
165
+ }
166
+ if (entity.references?.relatedEntities !== undefined) {
167
+ entity.references.relatedEntities = ensureArray(entity.references.relatedEntities);
168
+ }
169
+ if (entity.combat?.abilities !== undefined) {
170
+ entity.combat.abilities = ensureArray(entity.combat.abilities);
171
+ }
172
+ if (entity.cardGame?.effects !== undefined) {
173
+ entity.cardGame.effects = ensureArray(entity.cardGame.effects);
174
+ }
175
+ }
176
+ }
177
+ // Normalize creative section
178
+ if (bible.creative?.referenceLinks !== undefined) {
179
+ bible.creative.referenceLinks = ensureArray(bible.creative.referenceLinks);
180
+ }
181
+ if (bible.creative?.rhythm?.difficulty_levels !== undefined) {
182
+ bible.creative.rhythm.difficulty_levels = ensureArray(bible.creative.rhythm.difficulty_levels);
183
+ }
184
+ if (bible.creative?.rhythm?.note_types !== undefined) {
185
+ bible.creative.rhythm.note_types = ensureArray(bible.creative.rhythm.note_types);
186
+ }
187
+ if (bible.creative?.horror?.scareTechniques !== undefined) {
188
+ bible.creative.horror.scareTechniques = ensureArray(bible.creative.horror.scareTechniques);
189
+ }
190
+ // Normalize technical section
191
+ if (bible.technical?.buildTargets !== undefined) {
192
+ bible.technical.buildTargets = ensureArray(bible.technical.buildTargets);
193
+ }
194
+ if (bible.technical?.localization?.languages !== undefined) {
195
+ bible.technical.localization.languages = ensureArray(bible.technical.localization.languages);
196
+ }
197
+ // Normalize meta section arrays
198
+ if (bible.meta?.genre !== undefined) {
199
+ bible.meta.genre = ensureArray(bible.meta.genre);
200
+ }
201
+ if (bible.meta?.targetPlatform !== undefined) {
202
+ bible.meta.targetPlatform = ensureArray(bible.meta.targetPlatform);
203
+ }
204
+ }
205
+ /**
206
+ * Check the transcript for potential ambiguities and ask clarifying questions
207
+ * Returns additional transcript entries with clarifications
208
+ */
209
+ async checkForAmbiguities(transcript, onProgress) {
210
+ onProgress?.('Checking for ambiguities...');
211
+ const clarifications = [];
212
+ // Check for vague scope
213
+ const scopeAnswer = transcript.find(t => t.question.toLowerCase().includes('scope'))?.answer || '';
214
+ if (scopeAnswer && scopeAnswer.length > 0 &&
215
+ !scopeAnswer.toLowerCase().includes('prototype') &&
216
+ !scopeAnswer.toLowerCase().includes('mvp') &&
217
+ !scopeAnswer.toLowerCase().includes('vertical slice') &&
218
+ !scopeAnswer.toLowerCase().includes('full game')) {
219
+ const result = await this.askClarification('The project scope seems unclear. What is the target development scope?', transcript, 'Scope Clarification');
220
+ clarifications.push({
221
+ question: 'Clarification: Project Scope',
222
+ answer: result.answer,
223
+ autoGenerated: result.wasAIGenerated
224
+ });
225
+ }
226
+ // Check for unclear monetization
227
+ const monetizationAnswer = transcript.find(t => t.question.toLowerCase().includes('monetization') ||
228
+ t.question.toLowerCase().includes('revenue'))?.answer || '';
229
+ const normalizedMonetizationAnswer = monetizationAnswer
230
+ .trim()
231
+ .toLowerCase()
232
+ .replace(/[^\w\s]/g, '');
233
+ if (monetizationAnswer && normalizedMonetizationAnswer === 'custom answer') {
234
+ const result = await this.askClarification('Could you clarify the monetization strategy? How will this game generate revenue?', transcript, 'Monetization Clarification');
235
+ clarifications.push({
236
+ question: 'Clarification: Monetization Details',
237
+ answer: result.answer,
238
+ autoGenerated: result.wasAIGenerated
239
+ });
240
+ }
241
+ // Check for vague art style
242
+ const artAnswer = transcript.find(t => t.question.toLowerCase().includes('art') ||
243
+ t.question.toLowerCase().includes('visual'))?.answer || '';
244
+ if (!artAnswer || artAnswer.length < 10) {
245
+ const result = await this.askClarification('The visual art style needs more detail. What specific art direction should we follow?', transcript, 'Art Style Clarification');
246
+ clarifications.push({
247
+ question: 'Clarification: Art Style Details',
248
+ answer: result.answer,
249
+ autoGenerated: result.wasAIGenerated
250
+ });
251
+ }
252
+ // Check for vague target audience
253
+ const audienceAnswer = transcript.find(t => t.question.toLowerCase().includes('audience') ||
254
+ t.question.toLowerCase().includes('target'))?.answer || '';
255
+ const normalizedAudience = audienceAnswer.toLowerCase();
256
+ if (audienceAnswer.length < 15 ||
257
+ normalizedAudience.includes('everyone') ||
258
+ normalizedAudience.includes('all ages') ||
259
+ normalizedAudience.includes('casual and hardcore') ||
260
+ normalizedAudience.includes('broad')) {
261
+ const result = await this.askClarification('The target audience is very broad. Who is the PRIMARY audience? (e.g., "competitive FPS players 18-35" vs "everyone")', transcript, 'Target Audience Clarification');
262
+ clarifications.push({
263
+ question: 'Clarification: Target Audience',
264
+ answer: result.answer,
265
+ autoGenerated: result.wasAIGenerated
266
+ });
267
+ }
268
+ // Check for genre mixing overload
269
+ const genreAnswer = transcript.find(t => t.question.toLowerCase().includes('genre'))?.answer || '';
270
+ const genreCount = genreAnswer.split(/[,/&+]/).filter(g => g.trim().length > 0).length;
271
+ if (genreCount > 3) {
272
+ const result = await this.askClarification(`You're mixing ${genreCount} genres. This can dilute the experience. What's the PRIMARY genre?`, transcript, 'Genre Focus');
273
+ clarifications.push({
274
+ question: 'Clarification: Primary Genre',
275
+ answer: result.answer,
276
+ autoGenerated: result.wasAIGenerated
277
+ });
278
+ }
279
+ if (clarifications.length > 0) {
280
+ onProgress?.(`Added ${clarifications.length} clarifications`);
281
+ }
282
+ return clarifications;
283
+ }
284
+ /**
285
+ * Check the generated GameBible for structural issues and ask clarifying questions
286
+ */
287
+ async checkPostGenerationAmbiguities(bible, transcript, onProgress) {
288
+ // Check for too many features for scope
289
+ const scope = bible.meta.estimatedScope;
290
+ const featureCount = bible.features.length;
291
+ const featureLimits = {
292
+ 'Prototype': 5,
293
+ 'Vertical Slice': 8,
294
+ 'MVP': 12,
295
+ 'Full Game': 20
296
+ };
297
+ const limit = featureLimits[scope] || 12;
298
+ if (featureCount > limit) {
299
+ const result = await this.askClarification(`You have ${featureCount} features for a "${scope}" project. This may be too ambitious. Should we prioritize the most critical features?`, bible, 'Feature Count');
300
+ // Note: We inform but don't auto-reduce features - user decides
301
+ onProgress?.(`Feature count acknowledged: ${featureCount} features for ${scope} scope`);
302
+ }
303
+ // Check for core gameplay loop
304
+ const hasGameplayLoop = bible.features.some((f) => f.gameplayLoop && f.gameplayLoop.length > 0);
305
+ if (!hasGameplayLoop) {
306
+ const result = await this.askClarification('No gameplay loops are defined. What does the player DO moment-to-moment in this game?', bible, 'Core Gameplay Loop');
307
+ onProgress?.('Core gameplay loop clarified');
308
+ }
309
+ // Check for genre-specific features
310
+ const genres = bible.meta.genre.map((g) => g.toLowerCase());
311
+ // RPG without progression
312
+ if (genres.some((g) => g.includes('rpg')) &&
313
+ !bible.features.some((f) => f.progression)) {
314
+ const result = await this.askClarification('RPG games typically need a progression system. Should we add character/player progression?', bible, 'RPG Progression');
315
+ const decision = (result.answer || '').toLowerCase();
316
+ if (decision.includes('yes') || decision.includes('add') || decision.includes('should')) {
317
+ onProgress?.('Note: User confirmed RPG progression system should be added - consider this for future feature expansion');
318
+ }
319
+ else {
320
+ onProgress?.('RPG progression acknowledged as intentional design decision');
321
+ }
322
+ }
323
+ // Multiplayer genre without networking
324
+ if ((genres.some((g) => g.includes('multiplayer') || g.includes('mmo') || g.includes('competitive')) ||
325
+ bible.features.some((f) => f.multiplayer)) &&
326
+ !bible.technical?.networking) {
327
+ const result = await this.askClarification('Multiplayer features detected but no networking architecture specified. What network model should we use?', bible, 'Multiplayer Networking');
328
+ const decision = (result.answer || '').toLowerCase();
329
+ // Extract network architecture from user response
330
+ if (!bible.technical) {
331
+ bible.technical = { engine: { primary: 'Unity', version: '2023.2', reasoning: 'Default engine' }, buildTargets: [] };
332
+ }
333
+ if (decision.includes('client-server') || decision.includes('client server')) {
334
+ bible.technical.networking = { architecture: 'Client-Server', maxPlayers: 8 };
335
+ onProgress?.('Added Client-Server networking architecture');
336
+ }
337
+ else if (decision.includes('peer-to-peer') || decision.includes('p2p')) {
338
+ bible.technical.networking = { architecture: 'Peer-to-Peer', maxPlayers: 4 };
339
+ onProgress?.('Added Peer-to-Peer networking architecture');
340
+ }
341
+ else if (decision.includes('relay')) {
342
+ bible.technical.networking = { architecture: 'Relay Server', maxPlayers: 8 };
343
+ onProgress?.('Added Relay Server networking architecture');
344
+ }
345
+ else {
346
+ onProgress?.('Multiplayer networking clarified - manual specification may be needed');
347
+ }
348
+ }
349
+ // Card game without cards
350
+ if (genres.some((g) => g.includes('card') || g.includes('ccg') || g.includes('tcg')) &&
351
+ !bible.gameObjects.some((e) => e.category === 'Card')) {
352
+ const result = await this.askClarification('Card game genre but no card entities defined. Should we add card definitions?', bible, 'Card Game Entities');
353
+ const decision = (result.answer || '').toLowerCase();
354
+ if (decision.includes('yes') || decision.includes('add') || decision.includes('should')) {
355
+ onProgress?.('Note: User confirmed cards should be added - consider expanding entity list');
356
+ }
357
+ else {
358
+ onProgress?.('Card-less design acknowledged as intentional');
359
+ }
360
+ }
361
+ // Horror without atmosphere
362
+ if (genres.some((g) => g.includes('horror')) &&
363
+ !bible.creative?.horror?.atmosphere) {
364
+ const result = await this.askClarification('Horror game but no horror atmosphere defined. What kind of horror experience are you creating?', bible, 'Horror Atmosphere');
365
+ const decision = (result.answer || '').toLowerCase();
366
+ // Attempt to add horror atmosphere based on user response
367
+ if (bible.creative && decision.length > 10) {
368
+ if (!bible.creative.horror) {
369
+ bible.creative.horror = {};
370
+ }
371
+ const horror = bible.creative.horror;
372
+ // Extract atmosphere keywords from response
373
+ if (decision.includes('psychological')) {
374
+ horror.atmosphere = 'Psychological tension and unease';
375
+ }
376
+ else if (decision.includes('survival')) {
377
+ horror.atmosphere = 'Survival horror with resource scarcity';
378
+ }
379
+ else if (decision.includes('jump scare') || decision.includes('jumpscare')) {
380
+ horror.atmosphere = 'Jump scares and sudden frights';
381
+ }
382
+ else if (decision.includes('cosmic') || decision.includes('lovecraft')) {
383
+ horror.atmosphere = 'Cosmic horror and existential dread';
384
+ }
385
+ else {
386
+ horror.atmosphere = result.answer.substring(0, 100);
387
+ }
388
+ onProgress?.('Added horror atmosphere based on user input');
389
+ }
390
+ }
391
+ // Story/narrative game without NPCs
392
+ if ((genres.some((g) => g.includes('rpg') || g.includes('adventure') || g.includes('story')) ||
393
+ bible.features.some((f) => f.narrative)) &&
394
+ !bible.gameObjects.some((e) => e.category === 'NPC')) {
395
+ const result = await this.askClarification('Story-driven game with no NPCs. Should we add key characters to populate the world?', bible, 'Story Characters');
396
+ const decision = (result.answer || '').toLowerCase();
397
+ if (decision.includes('yes') || decision.includes('add') || decision.includes('should')) {
398
+ onProgress?.('Note: User confirmed NPCs should be added - consider expanding entity list');
399
+ }
400
+ else {
401
+ onProgress?.('NPC-less design acknowledged as intentional (e.g., environmental storytelling)');
402
+ }
403
+ }
404
+ }
89
405
  async generateMetaAndFeatures(transcriptStr, onProgress) {
90
406
  const prompt = `Generate the meta section and core features for a Game Design Document.
91
407
 
92
408
  INTERVIEW:
93
409
  ${transcriptStr}
94
410
 
95
- CONSTRAINTS - Keep it focused:
411
+ CONSTRAINTS - Keep it focused on DESIGN, not implementation:
96
412
  - Generate 3-8 core features maximum (prioritize the most important systems)
97
- - Keep technical specs concise
413
+ - Focus on what the feature DOES for the player, not how it's coded
414
+ - Describe gameplay loops in plain language
98
415
  - Limit to essential dependencies only
416
+ - ONLY include fields that are relevant to the genre - omit optional fields that don't apply
99
417
 
100
418
  OUTPUT STRUCTURE (use this EXACT format):
101
419
  {
@@ -114,44 +432,36 @@ OUTPUT STRUCTURE (use this EXACT format):
114
432
  {
115
433
  "id": "FEAT-001",
116
434
  "name": "Feature Name",
117
- "intent": "Why this feature exists",
118
- "dependencies": ["FEAT-002"],
119
- "gameplayLoop": ["step1", "step2", "step3"],
120
- "uiRequirements": ["UI element description"],
435
+ "intent": "Why this feature exists and what it adds to player experience",
436
+ "dependencies": [],
437
+ "gameplayLoop": ["Player does X", "System responds with Y", "Player decides Z"],
438
+ "uiRequirements": ["Health bar", "Inventory panel"],
121
439
  "technical": {
122
- "dataStructure": "struct FeatureName { field: type; }",
123
- "mathFormulas": [
124
- {
125
- "expression": "damage = baseDamage * (1 + critChance)",
126
- "variables": {
127
- "baseDamage": { "type": "float", "range": "1.0-100.0" },
128
- "critChance": { "type": "float", "range": "0.0-1.0" }
129
- },
130
- "validated": false,
131
- "balanceNotes": "Adjust critChance if damage spikes too high"
132
- }
133
- ],
134
- "fileLocation": "src/features/FeatureName/",
135
440
  "estimatedComplexity": "Medium"
136
441
  },
137
- "agile": {
138
- "epic": "EPIC-001: Epic Name",
139
- "userStories": [
140
- "As a player, I want to... so that..."
141
- ],
142
- "acceptanceCriteria": [
143
- "Given... when... then..."
144
- ]
442
+ "multiplayer": {
443
+ "playerCount": "2-8",
444
+ "networkModel": "Client-Server"
145
445
  }
146
446
  }
147
447
  ]
148
448
  }
149
449
 
150
- CRITICAL - For mathFormulas field:
151
- - If the feature has NO formulas, use: "mathFormulas": []
152
- - If it HAS formulas, each must be an OBJECT with structure shown above
153
- - NEVER use strings for mathFormulas elements
154
- - Variable types MUST be exactly "int", "float", or "bool" - NOT "array<int>", "number", "integer", or any other variation
450
+ OPTIONAL FIELDS - Include ONLY if relevant to the game genre:
451
+ - features[].gameplayLoop - OPTIONAL for non-gameplay features (e.g., settings, menus)
452
+ - features[].monetization - OPTIONAL for games with monetization (strategy only)
453
+ - features[].multiplayer - OPTIONAL for multiplayer features (playerCount as string, networkModel)
454
+ - features[].narrative - OPTIONAL for story-driven features (storyBeats, characters)
455
+ - features[].economy - OPTIONAL for games with currency/trading (currencies, tradeable)
456
+ - features[].progression - OPTIONAL for games with leveling/unlocks (unlockConditions, skillTrees)
457
+ - features[].procedural - OPTIONAL for procedural generation features (algorithm name only)
458
+ - features[].physics - OPTIONAL for physics-heavy features (realism level only)
459
+
460
+ IMPORTANT: This is a DESIGN document, not an engineering spec. Do NOT include:
461
+ - Code or pseudo-code
462
+ - File paths or directory structures
463
+ - Detailed math formulas with variable types
464
+ - Implementation details
155
465
 
156
466
  Output ONLY the JSON, no markdown blocks.`;
157
467
  const response = await this.callLLMWithAutoRetry(prompt, modelSelector_1.TaskComplexity.COMPLEX, {
@@ -170,50 +480,28 @@ ${transcriptStr}
170
480
  FEATURES:
171
481
  ${JSON.stringify(metaAndFeatures.features.map(f => ({ id: f.id, name: f.name })), null, 2)}
172
482
 
173
- CONSTRAINTS - Focus on essentials:
483
+ CONSTRAINTS - Keep it design-focused:
174
484
  - Generate 5-15 game objects maximum (most important entities only)
175
485
  - For card games: include a few representative cards, not every single card
176
- - Keep backstories and dialogues brief (1-2 sentences max)
177
- - Minimal attributes per object
486
+ - Keep backstories brief (1-2 sentences max)
487
+ - Focus on what the entity IS and what it DOES, not technical implementation
488
+ - ONLY include fields that are relevant to the entity type
178
489
 
179
490
  OUTPUT STRUCTURE (use this EXACT format):
180
491
  [
181
492
  {
182
493
  "id": "NPC-001",
183
494
  "name": "Character Name",
184
- "category": "NPC", // MUST be one of: "NPC", "Monster", "Item", "Interactable"
495
+ "category": "NPC",
185
496
  "narrative": {
186
- "backstory": "Brief 1-2 sentence backstory",
187
- "dialogues": ["Dialogue line 1", "Dialogue line 2"],
188
- "externalData": {
189
- "type": "Spreadsheet",
190
- "schema": "columns: name (string), dialogueKey (string), emotion (string)",
191
- "sampleRows": 5,
192
- "fileReference": "npcs/character_name_dialogues.csv"
193
- }
497
+ "backstory": "Brief 1-2 sentence backstory"
194
498
  },
195
499
  "stats": {
196
500
  "attributes": {
197
- "HP": 100,
198
- "Speed": "Fast",
199
- "AttackPower": 25
501
+ "Role": "Merchant",
502
+ "Personality": "Friendly"
200
503
  },
201
- "behaviors": ["patrol", "attack on sight"],
202
- "balanceFormulas": [
203
- {
204
- "expression": "effectiveHP = HP * (1 + armor * 0.01)",
205
- "variables": {
206
- "HP": { "type": "int", "range": "1-1000" },
207
- "armor": { "type": "int", "range": "0-100" }
208
- },
209
- "validated": false,
210
- "balanceNotes": "Ensure armor doesn't make character invincible"
211
- }
212
- ]
213
- },
214
- "references": {
215
- "features": ["FEAT-001"],
216
- "relatedEntities": ["ITEM-005"]
504
+ "behaviors": ["sells items", "gives quests"]
217
505
  }
218
506
  },
219
507
  {
@@ -222,35 +510,30 @@ OUTPUT STRUCTURE (use this EXACT format):
222
510
  "category": "Item",
223
511
  "stats": {
224
512
  "attributes": {
225
- "value": 100,
226
- "weight": 5
513
+ "Type": "Consumable",
514
+ "Effect": "Restores health"
227
515
  },
228
- "behaviors": ["consumable", "tradeable"]
229
- },
230
- "references": {
231
- "features": ["FEAT-003"],
232
- "relatedEntities": []
516
+ "behaviors": ["single use"]
233
517
  }
234
518
  }
235
519
  ]
236
520
 
237
- CRITICAL - For optional complex fields:
238
- - category MUST be exactly one of: "NPC", "Monster", "Item", "Interactable"
239
- - NOT "Environment", "System", "Card", or any other value
240
- - For environments, use category "Interactable"
241
- - For game systems, use category "Item" or omit the object entirely
242
- - narrative is OPTIONAL - omit entirely if not needed, don't use empty strings
243
- - narrative.externalData is OPTIONAL - omit if not needed
244
- - If present, MUST be an object: { type: "Spreadsheet"|"Database"|"JSON", schema: string, sampleRows: number }
245
- - NEVER use a string for externalData
246
- - stats.balanceFormulas is OPTIONAL - use [] if no formulas
247
- - If formulas exist, each MUST be an object (see example above)
248
- - NEVER use strings for formula array elements
249
- - Variable types MUST be exactly "int", "float", or "bool" - NOT "array<int>", "number", "integer", or any other variation
250
- - stats.attributes values MUST be numbers or strings - NOT booleans
251
- - Use strings for descriptive values: "Fast", "Slow", "Marked", "Unmarked"
252
- - Use numbers for numeric values: 100, 5.5, 0
253
- - NEVER use true/false for attributes
521
+ CRITICAL - Category must be one of these EXACT values:
522
+ - "NPC", "Monster", "Item", "Interactable", "Vehicle", "Card", "Building", "Ability"
523
+
524
+ OPTIONAL FIELDS - Include ONLY if relevant:
525
+ - narrative - OPTIONAL - brief backstory only
526
+ - stats - OPTIONAL - simple attributes and behaviors
527
+ - combat - OPTIONAL for combat entities (health, damage, armor)
528
+ - social - OPTIONAL for NPCs (relationship type, questGiver boolean)
529
+ - cardGame - OPTIONAL for cards (cost, effects, cardType)
530
+ - ai - OPTIONAL for AI entities (aiType like "Passive" or "Aggressive")
531
+
532
+ IMPORTANT - This is a DESIGN document:
533
+ - Use descriptive attributes, not numeric RPG stats unless the game is an RPG
534
+ - Do NOT include balance formulas or detailed math
535
+ - Do NOT include detection radiuses, behavior trees, or AI implementation details
536
+ - Focus on the entity's role in the game, not how it's programmed
254
537
 
255
538
  Output ONLY the JSON array.`;
256
539
  const response = await this.callLLMWithAutoRetry(prompt, modelSelector_1.TaskComplexity.COMPLEX, {
@@ -261,22 +544,22 @@ Output ONLY the JSON array.`;
261
544
  return this.parseJSON(response.content);
262
545
  }
263
546
  async generateCreativeAndTech(transcriptStr, metaAndFeatures, onProgress) {
264
- const prompt = `Generate creative and technical sections.
547
+ const prompt = `Generate creative vision and technical overview sections.
265
548
 
266
549
  GAME: ${metaAndFeatures.meta.title}
267
550
  GENRE: ${metaAndFeatures.meta.genre.join(', ')}
268
551
 
269
- CONSTRAINTS - Be concise:
270
- - Art style: 2-3 sentences max
271
- - Audio mood: 2-3 sentences max
272
- - Keep asset counts realistic
552
+ CONSTRAINTS - Focus on creative vision and high-level technical decisions:
553
+ - Art style: 2-3 sentences describing the visual mood and aesthetic
554
+ - Audio mood: 2-3 sentences describing the sound and music direction
555
+ - Keep asset counts realistic estimates
556
+ - Technical section should be a brief overview, not an engineering spec
273
557
 
274
558
  OUTPUT STRUCTURE (use this EXACT format):
275
559
  {
276
560
  "creative": {
277
- "artStyle": "2-3 sentence description of visual style",
278
- "audioMood": "2-3 sentence description of audio direction",
279
- "referenceLinks": ["https://example.com/reference1"],
561
+ "artStyle": "2-3 sentence description of visual style, mood, and aesthetic influences",
562
+ "audioMood": "2-3 sentence description of music style and sound design direction",
280
563
  "assetRequirements": {
281
564
  "characters": 10,
282
565
  "environments": 5,
@@ -284,48 +567,44 @@ OUTPUT STRUCTURE (use this EXACT format):
284
567
  "ui": 15,
285
568
  "sfx": 30,
286
569
  "music": 8
287
- },
288
- "pipeline": {
289
- "toolsRequired": ["Blender", "Photoshop", "Audacity"],
290
- "workflowNotes": "Brief workflow description"
291
570
  }
292
571
  },
293
572
  "technical": {
294
573
  "engine": {
295
- "primary": "Unreal Engine 5",
296
- "version": "5.4",
297
- "plugins": ["Niagara", "Chaos Physics"],
298
- "reasoning": "Why this engine was chosen"
574
+ "primary": "Unity",
575
+ "version": "2023.2",
576
+ "reasoning": "Brief explanation of why this engine fits the project"
299
577
  },
300
- "toolsRequired": [
301
- {
302
- "name": "Visual Studio",
303
- "purpose": "C++ development",
304
- "required": true
305
- },
306
- {
307
- "name": "Git",
308
- "purpose": "Version control",
309
- "required": true
310
- }
311
- ],
312
- "localization": {
313
- "strategy": "English Only",
314
- "languages": ["en"],
315
- "stringCount": 500
316
- },
317
- "directoryStructure": "Assets/\n Characters/\n Environments/\nSource/\n Core/\n Features/",
318
578
  "buildTargets": ["Windows", "Mac"]
319
579
  }
320
580
  }
321
581
 
322
- CRITICAL - Field requirements:
582
+ OPTIONAL FIELDS - Include ONLY if relevant:
583
+ Creative:
584
+ - assetRequirements.vehicles - OPTIONAL for games with vehicles
585
+ - assetRequirements.cards - OPTIONAL for card games
586
+ - assetRequirements.animations - OPTIONAL for animation-heavy games
587
+ - visualNovel - OPTIONAL for visual novel games
588
+ - rhythm - OPTIONAL for rhythm games
589
+ - horror - OPTIONAL for horror games (atmosphere, lighting mood)
590
+
591
+ Technical:
592
+ - localization - OPTIONAL (strategy and languages only)
593
+ - networking - OPTIONAL for multiplayer (architecture and maxPlayers only)
594
+ - accessibility - OPTIONAL (colorblindMode, subtitles, remappableControls, difficultyOptions)
595
+
596
+ CRITICAL:
323
597
  - engine.primary MUST be one of: "Unreal Engine 5", "Unity", "Godot", "Custom"
324
- - engine.plugins is OPTIONAL - use [] if none, or omit field entirely
325
- - localization.strategy MUST be: "None", "English Only", "EFIGS", or "Global"
326
- - localization.languages is OPTIONAL but recommended (use ["en"] for English Only)
598
+ - localization.strategy (if included) MUST be: "None", "English Only", "EFIGS", or "Global"
327
599
  - buildTargets MUST use exact values: "Windows", "Mac", "Linux", "iOS", "Android", "Console"
328
600
 
601
+ DO NOT INCLUDE (these are implementation details, not design):
602
+ - Production pipeline or workflow
603
+ - Tool lists or software requirements
604
+ - Directory structures
605
+ - Tick rates, bandwidth, or networking implementation details
606
+ - Performance targets or scalability settings
607
+
329
608
  Output ONLY the JSON.`;
330
609
  const response = await this.callLLMWithAutoRetry(prompt, modelSelector_1.TaskComplexity.COMPLEX, {
331
610
  initialMaxTokens: 16000,
@@ -439,45 +718,29 @@ Output ONLY the JSON.`;
439
718
  "name": "string",
440
719
  "intent": "string",
441
720
  "dependencies": ["FEAT-XXX"],
442
- "monetization": { "strategy": "None|IAP|Premium|Ads|Hybrid", "implementation": "string" },
443
721
  "gameplayLoop": ["step1", "step2"],
444
722
  "uiRequirements": ["string"],
445
723
  "technical": {
446
- "dataStructure": "pseudo-code string",
447
- "mathFormulas": [{ "expression": "string", "variables": {"name": {"type": "int|float|bool", "range": "string"}}, "validated": false, "balanceNotes": "string" }],
448
- "fileLocation": "path/to/files",
449
724
  "estimatedComplexity": "Low|Medium|High|Very High"
450
- },
451
- "agile": {
452
- "epic": "EPIC-XXX: Name",
453
- "userStories": ["As a player, I want..."],
454
- "acceptanceCriteria": ["Criteria"]
455
725
  }
456
726
  }],
457
727
  "gameObjects": [{
458
728
  "id": "NPC-XXX|ITEM-XXX|MON-XXX",
459
729
  "name": "string",
460
730
  "category": "NPC|Monster|Item|Interactable",
461
- "narrative": { "backstory": "string", "dialogues": ["string"], "externalData": {...} },
731
+ "narrative": { "backstory": "string" },
462
732
  "stats": {
463
- "attributes": {"HP": 100, "Speed": "Fast"},
464
- "behaviors": ["string"],
465
- "balanceFormulas": [...]
466
- },
467
- "references": { "features": ["FEAT-XXX"], "relatedEntities": ["ENTITY-XXX"] }
733
+ "attributes": {"Role": "Merchant", "Type": "Friendly"},
734
+ "behaviors": ["string"]
735
+ }
468
736
  }],
469
737
  "creative": {
470
738
  "artStyle": "string",
471
739
  "audioMood": "string",
472
- "referenceLinks": ["url"],
473
- "assetRequirements": { "characters": 0, "environments": 0, "props": 0, "ui": 0, "sfx": 0, "music": 0 },
474
- "pipeline": { "toolsRequired": ["Tool"], "workflowNotes": "string" }
740
+ "assetRequirements": { "characters": 0, "environments": 0, "props": 0, "ui": 0, "sfx": 0, "music": 0 }
475
741
  },
476
742
  "technical": {
477
- "engine": { "primary": "Unreal Engine 5|Unity|Godot|Custom", "version": "string", "plugins": ["string"], "reasoning": "string" },
478
- "toolsRequired": [{ "name": "Tool", "purpose": "string", "required": true }],
479
- "localization": { "strategy": "None|English Only|EFIGS|Global", "languages": ["en"], "stringCount": 0 },
480
- "directoryStructure": "path structure",
743
+ "engine": { "primary": "Unreal Engine 5|Unity|Godot|Custom", "version": "string", "reasoning": "string" },
481
744
  "buildTargets": ["Windows", "Mac", "Linux", "iOS", "Android", "Console"]
482
745
  }
483
746
  }`;