gameforge-cli 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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +366 -0
  3. package/dist/agents/base/BaseAgent.d.ts +45 -0
  4. package/dist/agents/base/BaseAgent.d.ts.map +1 -0
  5. package/dist/agents/base/BaseAgent.js +179 -0
  6. package/dist/agents/base/BaseAgent.js.map +1 -0
  7. package/dist/agents/core/Architect.d.ts +16 -0
  8. package/dist/agents/core/Architect.d.ts.map +1 -0
  9. package/dist/agents/core/Architect.js +487 -0
  10. package/dist/agents/core/Architect.js.map +1 -0
  11. package/dist/agents/core/Chaos.d.ts +14 -0
  12. package/dist/agents/core/Chaos.d.ts.map +1 -0
  13. package/dist/agents/core/Chaos.js +67 -0
  14. package/dist/agents/core/Chaos.js.map +1 -0
  15. package/dist/agents/core/Consistency.d.ts +16 -0
  16. package/dist/agents/core/Consistency.d.ts.map +1 -0
  17. package/dist/agents/core/Consistency.js +132 -0
  18. package/dist/agents/core/Consistency.js.map +1 -0
  19. package/dist/agents/core/Inquisitor.d.ts +20 -0
  20. package/dist/agents/core/Inquisitor.d.ts.map +1 -0
  21. package/dist/agents/core/Inquisitor.js +159 -0
  22. package/dist/agents/core/Inquisitor.js.map +1 -0
  23. package/dist/agents/core/Remediation.d.ts +16 -0
  24. package/dist/agents/core/Remediation.d.ts.map +1 -0
  25. package/dist/agents/core/Remediation.js +151 -0
  26. package/dist/agents/core/Remediation.js.map +1 -0
  27. package/dist/agents/specialists/CreativeSpecialist.d.ts +7 -0
  28. package/dist/agents/specialists/CreativeSpecialist.d.ts.map +1 -0
  29. package/dist/agents/specialists/CreativeSpecialist.js +70 -0
  30. package/dist/agents/specialists/CreativeSpecialist.js.map +1 -0
  31. package/dist/agents/specialists/EntitySpecialist.d.ts +8 -0
  32. package/dist/agents/specialists/EntitySpecialist.d.ts.map +1 -0
  33. package/dist/agents/specialists/EntitySpecialist.js +74 -0
  34. package/dist/agents/specialists/EntitySpecialist.js.map +1 -0
  35. package/dist/agents/specialists/FeatureSpecialist.d.ts +8 -0
  36. package/dist/agents/specialists/FeatureSpecialist.d.ts.map +1 -0
  37. package/dist/agents/specialists/FeatureSpecialist.js +83 -0
  38. package/dist/agents/specialists/FeatureSpecialist.js.map +1 -0
  39. package/dist/agents/specialists/TechSpecialist.d.ts +7 -0
  40. package/dist/agents/specialists/TechSpecialist.d.ts.map +1 -0
  41. package/dist/agents/specialists/TechSpecialist.js +62 -0
  42. package/dist/agents/specialists/TechSpecialist.js.map +1 -0
  43. package/dist/config/budget.d.ts +36 -0
  44. package/dist/config/budget.d.ts.map +1 -0
  45. package/dist/config/budget.js +37 -0
  46. package/dist/config/budget.js.map +1 -0
  47. package/dist/config/schema.d.ts +1336 -0
  48. package/dist/config/schema.d.ts.map +1 -0
  49. package/dist/config/schema.js +134 -0
  50. package/dist/config/schema.js.map +1 -0
  51. package/dist/config/templates.d.ts +10 -0
  52. package/dist/config/templates.d.ts.map +1 -0
  53. package/dist/config/templates.js +202 -0
  54. package/dist/config/templates.js.map +1 -0
  55. package/dist/core/CheckpointManager.d.ts +16 -0
  56. package/dist/core/CheckpointManager.d.ts.map +1 -0
  57. package/dist/core/CheckpointManager.js +52 -0
  58. package/dist/core/CheckpointManager.js.map +1 -0
  59. package/dist/core/Orchestrator.d.ts +11 -0
  60. package/dist/core/Orchestrator.d.ts.map +1 -0
  61. package/dist/core/Orchestrator.js +46 -0
  62. package/dist/core/Orchestrator.js.map +1 -0
  63. package/dist/core/SessionManager.d.ts +68 -0
  64. package/dist/core/SessionManager.d.ts.map +1 -0
  65. package/dist/core/SessionManager.js +162 -0
  66. package/dist/core/SessionManager.js.map +1 -0
  67. package/dist/core/StateMachine.d.ts +46 -0
  68. package/dist/core/StateMachine.d.ts.map +1 -0
  69. package/dist/core/StateMachine.js +82 -0
  70. package/dist/core/StateMachine.js.map +1 -0
  71. package/dist/index.d.ts +3 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +830 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/utils/costTracker.d.ts +30 -0
  76. package/dist/utils/costTracker.d.ts.map +1 -0
  77. package/dist/utils/costTracker.js +64 -0
  78. package/dist/utils/costTracker.js.map +1 -0
  79. package/dist/utils/debugLogger.d.ts +48 -0
  80. package/dist/utils/debugLogger.d.ts.map +1 -0
  81. package/dist/utils/debugLogger.js +179 -0
  82. package/dist/utils/debugLogger.js.map +1 -0
  83. package/dist/utils/fileManager.d.ts +14 -0
  84. package/dist/utils/fileManager.d.ts.map +1 -0
  85. package/dist/utils/fileManager.js +135 -0
  86. package/dist/utils/fileManager.js.map +1 -0
  87. package/dist/utils/modelSelector.d.ts +11 -0
  88. package/dist/utils/modelSelector.d.ts.map +1 -0
  89. package/dist/utils/modelSelector.js +38 -0
  90. package/dist/utils/modelSelector.js.map +1 -0
  91. package/package.json +49 -0
package/dist/index.js ADDED
@@ -0,0 +1,830 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const picocolors_1 = __importDefault(require("picocolors"));
9
+ const dotenv_1 = __importDefault(require("dotenv"));
10
+ const ora_1 = __importDefault(require("ora"));
11
+ const enquirer_1 = __importDefault(require("enquirer"));
12
+ const fs_extra_1 = __importDefault(require("fs-extra"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const StateMachine_1 = require("./core/StateMachine");
15
+ const SessionManager_1 = require("./core/SessionManager");
16
+ const costTracker_1 = require("./utils/costTracker");
17
+ const modelSelector_1 = require("./utils/modelSelector");
18
+ const fileManager_1 = require("./utils/fileManager");
19
+ const budget_1 = require("./config/budget");
20
+ const templates_1 = require("./config/templates");
21
+ const Inquisitor_1 = require("./agents/core/Inquisitor");
22
+ const Architect_1 = require("./agents/core/Architect");
23
+ const Consistency_1 = require("./agents/core/Consistency");
24
+ const Chaos_1 = require("./agents/core/Chaos");
25
+ const Remediation_1 = require("./agents/core/Remediation");
26
+ const FeatureSpecialist_1 = require("./agents/specialists/FeatureSpecialist");
27
+ const EntitySpecialist_1 = require("./agents/specialists/EntitySpecialist");
28
+ const TechSpecialist_1 = require("./agents/specialists/TechSpecialist");
29
+ const CreativeSpecialist_1 = require("./agents/specialists/CreativeSpecialist");
30
+ const Orchestrator_1 = require("./core/Orchestrator");
31
+ const debugLogger_1 = require("./utils/debugLogger");
32
+ dotenv_1.default.config();
33
+ const program = new commander_1.Command();
34
+ program
35
+ .name('gameforge')
36
+ .description('AI-powered Game Design Document generator')
37
+ .version('0.1.0');
38
+ program
39
+ .command('create')
40
+ .description('Create a new game design document')
41
+ .option('-b, --budget <tier>', 'Budget tier: quick | standard | deep', 'standard')
42
+ .option('-t, --template <id>', 'Start from template')
43
+ .option('-d, --debug', 'Enable debug logging to file')
44
+ .action(async (options) => {
45
+ await runCreationFlow(options);
46
+ });
47
+ program
48
+ .command('resume <checkpointId>')
49
+ .description('Resume from a saved checkpoint')
50
+ .option('-d, --debug', 'Enable debug logging to file')
51
+ .action(async (checkpointId, options) => {
52
+ await resumeFlow(checkpointId, options);
53
+ });
54
+ program
55
+ .command('sessions')
56
+ .description('List all saved sessions')
57
+ .action(async () => {
58
+ await listSessions();
59
+ });
60
+ program
61
+ .command('session <sessionId>')
62
+ .description('Show details and checkpoints for a session')
63
+ .action(async (sessionId) => {
64
+ await showSession(sessionId);
65
+ });
66
+ program
67
+ .command('templates')
68
+ .description('List available genre templates')
69
+ .action(() => {
70
+ console.log(picocolors_1.default.cyan('\nšŸ“š Available Templates\n'));
71
+ templates_1.GENRE_TEMPLATES.forEach(template => {
72
+ console.log(picocolors_1.default.green(`${template.id}`));
73
+ console.log(picocolors_1.default.bold(` ${template.name}`));
74
+ console.log(picocolors_1.default.dim(` ${template.description}`));
75
+ console.log(picocolors_1.default.dim(` Tags: ${template.tags.join(', ')}`));
76
+ console.log(picocolors_1.default.dim(` Features: ${template.partialBible.features?.length || 0}\n`));
77
+ });
78
+ console.log(picocolors_1.default.dim('Usage: gameforge create --template <id>\n'));
79
+ });
80
+ program.parse();
81
+ async function runCreationFlow(options) {
82
+ console.clear();
83
+ console.log(picocolors_1.default.cyan('\n╔══════════════════════════════╗'));
84
+ console.log(picocolors_1.default.cyan('ā•‘ GameForge CLI ā•‘'));
85
+ console.log(picocolors_1.default.cyan('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n'));
86
+ console.log(picocolors_1.default.dim('AI-Powered Game Design Documents\n'));
87
+ // Enable debug logging if requested
88
+ if (options.debug) {
89
+ const projectId = `gameforge-${Date.now()}`;
90
+ debugLogger_1.debugLogger.enable(projectId);
91
+ console.log(picocolors_1.default.yellow(`šŸ› Debug mode enabled`));
92
+ console.log(picocolors_1.default.dim(` Log file: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
93
+ }
94
+ // Setup
95
+ const budgetTier = budget_1.BUDGET_TIERS[options.budget];
96
+ if (!budgetTier) {
97
+ console.error(picocolors_1.default.red(`Unknown budget tier: ${options.budget}`));
98
+ console.log(picocolors_1.default.dim('Available tiers: quick, standard, deep'));
99
+ process.exit(1);
100
+ }
101
+ const apiKey = process.env.ANTHROPIC_API_KEY;
102
+ if (!apiKey) {
103
+ console.error(picocolors_1.default.red('Error: ANTHROPIC_API_KEY not set in .env'));
104
+ console.log(picocolors_1.default.yellow('\nPlease create a .env file with your Anthropic API key:'));
105
+ console.log(picocolors_1.default.dim('ANTHROPIC_API_KEY=your_api_key_here\n'));
106
+ process.exit(1);
107
+ }
108
+ // Prompt for session title
109
+ const { title } = await enquirer_1.default.prompt({
110
+ type: 'input',
111
+ name: 'title',
112
+ message: 'Session name (e.g., "My Card Game", "RPG Project"):',
113
+ initial: 'New Game Design'
114
+ });
115
+ const costTracker = new costTracker_1.CostTracker(budgetTier.maxBudget);
116
+ const modelSelector = new modelSelector_1.ModelSelector();
117
+ const stateMachine = new StateMachine_1.StateMachine(budgetTier.maxBudget);
118
+ const sessionManager = new SessionManager_1.SessionManager();
119
+ const fileManager = new fileManager_1.FileManager();
120
+ // Create a new session
121
+ const sessionId = await sessionManager.createSession(title);
122
+ const projectId = sessionId; // Use session ID as project ID for consistency
123
+ console.log(picocolors_1.default.green(`\nāœ“ Session created: ${sessionId}`));
124
+ console.log(picocolors_1.default.green(`Budget: ${budgetTier.name} ($${budgetTier.maxBudget})`));
125
+ console.log(picocolors_1.default.dim(`Model: ${budgetTier.preferredModel}\n`));
126
+ try {
127
+ // Check if template was requested
128
+ let selectedTemplate = null;
129
+ if (options.template) {
130
+ selectedTemplate = templates_1.GENRE_TEMPLATES.find(t => t.id === options.template);
131
+ if (!selectedTemplate) {
132
+ console.error(picocolors_1.default.red(`Template not found: ${options.template}`));
133
+ console.log(picocolors_1.default.yellow('\nAvailable templates:'));
134
+ templates_1.GENRE_TEMPLATES.forEach(t => {
135
+ console.log(picocolors_1.default.dim(` - ${t.id}: ${t.name}`));
136
+ });
137
+ process.exit(1);
138
+ }
139
+ console.log(picocolors_1.default.green(`Using template: ${selectedTemplate.name}`));
140
+ console.log(picocolors_1.default.dim(`${selectedTemplate.description}\n`));
141
+ }
142
+ // Phase 1: Discovery
143
+ console.log(picocolors_1.default.cyan('\nšŸ“‹ Phase 1: Discovery\n'));
144
+ stateMachine.transition(StateMachine_1.GameForgePhase.DISCOVERY);
145
+ const inquisitor = new Inquisitor_1.Inquisitor(apiKey, costTracker, modelSelector);
146
+ const questions = getInquisitorQuestions(options.template);
147
+ const transcript = await inquisitor.execute(questions);
148
+ transcript.forEach(entry => {
149
+ stateMachine.addTranscriptEntry(entry.question, entry.answer, entry.autoGenerated);
150
+ });
151
+ // Gate 1: Review Concept
152
+ console.log(picocolors_1.default.cyan('\nšŸ“Š Concept Review\n'));
153
+ stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_CONCEPT);
154
+ showConceptBrief(transcript);
155
+ const proceed1 = await confirmGate('Proceed to architecture phase?');
156
+ if (!proceed1) {
157
+ console.log(picocolors_1.default.yellow('Exiting...'));
158
+ return;
159
+ }
160
+ await sessionManager.saveCheckpoint(sessionId, stateMachine.getState(), 'after_discovery');
161
+ // Phase 2: Architecture
162
+ console.log(picocolors_1.default.cyan('\nšŸ—ļø Phase 2: Architecture\n'));
163
+ stateMachine.transition(StateMachine_1.GameForgePhase.ARCHITECTURE);
164
+ const spinner = (0, ora_1.default)('Generating Game Bible...').start();
165
+ const architect = new Architect_1.Architect(apiKey, costTracker, modelSelector);
166
+ let bible = await architect.execute(transcript, (message) => {
167
+ spinner.text = `Generating Game Bible: ${message}`;
168
+ });
169
+ // Merge template features if a template was selected
170
+ if (selectedTemplate && selectedTemplate.partialBible.features) {
171
+ console.log(picocolors_1.default.dim(` Merging ${selectedTemplate.partialBible.features.length} template features...`));
172
+ bible.features = [...selectedTemplate.partialBible.features, ...bible.features];
173
+ }
174
+ stateMachine.updateBible(bible);
175
+ spinner.succeed('Game Bible generated');
176
+ // Save checkpoint
177
+ await sessionManager.saveCheckpoint(sessionId, stateMachine.getState(), 'after_architecture');
178
+ // Gate 2: Review Data
179
+ console.log(picocolors_1.default.cyan('\nšŸ“Š Data Review\n'));
180
+ stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_DATA);
181
+ console.log(picocolors_1.default.green(`āœ“ Generated ${bible.features.length} features`));
182
+ console.log(picocolors_1.default.green(`āœ“ Generated ${bible.gameObjects.length} game objects`));
183
+ const proceed2 = await confirmGate('Proceed to production phase?');
184
+ if (!proceed2) {
185
+ console.log(picocolors_1.default.yellow('Exiting...'));
186
+ return;
187
+ }
188
+ // Phase 3: Production (Specialists)
189
+ console.log(picocolors_1.default.cyan('\nšŸŽØ Phase 3: Production\n'));
190
+ stateMachine.transition(StateMachine_1.GameForgePhase.PRODUCTION);
191
+ const orchestrator = new Orchestrator_1.SpecialistOrchestrator(2); // Concurrency of 2
192
+ const specialists = [
193
+ { name: 'Creative Direction', agent: new CreativeSpecialist_1.CreativeSpecialist(apiKey, costTracker, modelSelector) },
194
+ { name: 'Feature Specifications', agent: new FeatureSpecialist_1.FeatureSpecialist(apiKey, costTracker, modelSelector) },
195
+ { name: 'Entity Specifications', agent: new EntitySpecialist_1.EntitySpecialist(apiKey, costTracker, modelSelector) },
196
+ { name: 'Technical Specifications', agent: new TechSpecialist_1.TechSpecialist(apiKey, costTracker, modelSelector) }
197
+ ];
198
+ let markdownSections = await orchestrator.runAllSpecialists(bible, specialists);
199
+ // Phase 4: Validation
200
+ console.log(picocolors_1.default.cyan('\nāœ… Phase 4: Validation\n'));
201
+ stateMachine.transition(StateMachine_1.GameForgePhase.VALIDATION);
202
+ // Run consistency check
203
+ const consistencyAgent = new Consistency_1.ConsistencyAgent();
204
+ let currentConsistencyIssues = consistencyAgent.validate(bible);
205
+ console.log(picocolors_1.default.yellow(`Found ${currentConsistencyIssues.length} consistency issues`));
206
+ // Run chaos analysis
207
+ let chaosSpinner = (0, ora_1.default)('Running Chaos Agent...').start();
208
+ const chaosAgent = new Chaos_1.Chaos(apiKey, costTracker, modelSelector);
209
+ let currentChaosIssues = await chaosAgent.execute(bible, (message) => {
210
+ chaosSpinner.text = `Chaos Agent: ${message}`;
211
+ });
212
+ chaosSpinner.succeed(`Found ${currentChaosIssues.length} design issues`);
213
+ // Gate 3: Review Issues with Fix Option
214
+ const MAX_FIX_ATTEMPTS = 3;
215
+ let fixAttempts = 0;
216
+ let fixesApplied = false;
217
+ while (true) {
218
+ stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_ISSUES);
219
+ const totalIssues = currentConsistencyIssues.length + currentChaosIssues.length;
220
+ if (totalIssues > 0) {
221
+ console.log(picocolors_1.default.yellow(`\nāš ļø ${totalIssues} issues found:`));
222
+ currentConsistencyIssues.slice(0, 3).forEach((issue) => {
223
+ console.log(picocolors_1.default.dim(` - [${issue.severity}] ${issue.message}`));
224
+ });
225
+ currentChaosIssues.slice(0, 3).forEach((issue) => {
226
+ console.log(picocolors_1.default.dim(` - [${issue.severity}] ${issue.description}`));
227
+ });
228
+ if (totalIssues > 6) {
229
+ console.log(picocolors_1.default.dim(` ... and ${totalIssues - 6} more issues`));
230
+ }
231
+ if (fixAttempts > 0) {
232
+ console.log(picocolors_1.default.cyan(`\n(Fix attempt ${fixAttempts}/${MAX_FIX_ATTEMPTS} complete)`));
233
+ }
234
+ }
235
+ else {
236
+ console.log(picocolors_1.default.green('\nāœ“ All issues resolved!'));
237
+ }
238
+ // Determine action based on state
239
+ let action;
240
+ if (totalIssues === 0) {
241
+ const proceed = await confirmGate('Save and export GDD?');
242
+ action = proceed ? 'save' : 'exit';
243
+ }
244
+ else if (fixAttempts >= MAX_FIX_ATTEMPTS) {
245
+ console.log(picocolors_1.default.yellow(`\nMaximum fix attempts (${MAX_FIX_ATTEMPTS}) reached.`));
246
+ const proceed = await confirmGate('Save and export GDD with remaining issues?');
247
+ action = proceed ? 'save' : 'exit';
248
+ }
249
+ else {
250
+ action = await selectReviewAction();
251
+ }
252
+ if (action === 'exit') {
253
+ console.log(picocolors_1.default.yellow('Exiting...'));
254
+ return;
255
+ }
256
+ if (action === 'save') {
257
+ break;
258
+ }
259
+ // action === 'fix': Run remediation
260
+ fixAttempts++;
261
+ fixesApplied = true;
262
+ console.log(picocolors_1.default.cyan(`\nšŸ”§ Fixing issues (attempt ${fixAttempts}/${MAX_FIX_ATTEMPTS})...\n`));
263
+ const remediationSpinner = (0, ora_1.default)('Running Remediation Agent...').start();
264
+ const remediationAgent = new Remediation_1.Remediation(apiKey, costTracker, modelSelector);
265
+ try {
266
+ const result = await remediationAgent.execute(bible, currentConsistencyIssues, currentChaosIssues, (message) => { remediationSpinner.text = `Remediation: ${message}`; });
267
+ remediationSpinner.succeed(`Fixed ${result.fixedIssues.length} issues`);
268
+ if (result.fixedIssues.length > 0) {
269
+ console.log(picocolors_1.default.green('\nFixes applied:'));
270
+ result.fixedIssues.forEach(fix => {
271
+ console.log(picocolors_1.default.dim(` āœ“ ${fix}`));
272
+ });
273
+ }
274
+ if (result.unfixableIssues.length > 0) {
275
+ console.log(picocolors_1.default.yellow('\nCould not fix:'));
276
+ result.unfixableIssues.forEach(issue => {
277
+ console.log(picocolors_1.default.dim(` āœ— ${issue}`));
278
+ });
279
+ }
280
+ // Update bible
281
+ bible = result.updatedBible;
282
+ stateMachine.updateBible(bible);
283
+ // Transition back to production for re-validation
284
+ stateMachine.transition(StateMachine_1.GameForgePhase.PRODUCTION);
285
+ // Re-run validation
286
+ console.log(picocolors_1.default.cyan('\nšŸ”„ Re-validating...\n'));
287
+ stateMachine.transition(StateMachine_1.GameForgePhase.VALIDATION);
288
+ const newConsistencyAgent = new Consistency_1.ConsistencyAgent();
289
+ currentConsistencyIssues = newConsistencyAgent.validate(bible);
290
+ console.log(picocolors_1.default.yellow(`Found ${currentConsistencyIssues.length} consistency issues`));
291
+ chaosSpinner = (0, ora_1.default)('Re-running Chaos Agent...').start();
292
+ const newChaosAgent = new Chaos_1.Chaos(apiKey, costTracker, modelSelector);
293
+ currentChaosIssues = await newChaosAgent.execute(bible, (message) => {
294
+ chaosSpinner.text = `Chaos Agent: ${message}`;
295
+ });
296
+ chaosSpinner.succeed(`Found ${currentChaosIssues.length} design issues`);
297
+ }
298
+ catch (error) {
299
+ remediationSpinner.fail('Remediation failed');
300
+ console.error(picocolors_1.default.red('Error during remediation:'), error);
301
+ const continueAnyway = await confirmGate('Continue to save despite remediation failure?');
302
+ if (!continueAnyway) {
303
+ console.log(picocolors_1.default.yellow('Exiting...'));
304
+ return;
305
+ }
306
+ break;
307
+ }
308
+ }
309
+ // If fixes were applied, re-run specialists to regenerate markdown
310
+ if (fixesApplied) {
311
+ console.log(picocolors_1.default.cyan('\nšŸ”„ Regenerating documentation with fixed Bible...\n'));
312
+ markdownSections = await orchestrator.runAllSpecialists(bible, specialists);
313
+ }
314
+ // Save all outputs
315
+ console.log(picocolors_1.default.cyan('\nšŸ’¾ Saving outputs...\n'));
316
+ const projectDir = await fileManager.initialize(projectId);
317
+ await fileManager.saveGameBible(projectDir, bible);
318
+ await fileManager.saveCoverPage(projectDir, bible);
319
+ for (const [name, content] of markdownSections) {
320
+ const filename = name.replace(/\s+/g, '_') + '.md';
321
+ await fileManager.saveMarkdown(projectDir, filename, content);
322
+ }
323
+ await fileManager.saveGapAnalysis(projectDir, currentChaosIssues, currentConsistencyIssues);
324
+ // Save combined GDD
325
+ await fileManager.saveCombinedGDD(projectDir, bible, markdownSections, currentChaosIssues, currentConsistencyIssues);
326
+ console.log(picocolors_1.default.green('āœ“ Saved combined Game Design Document'));
327
+ // Complete
328
+ stateMachine.transition(StateMachine_1.GameForgePhase.COMPLETE);
329
+ // Save final checkpoint
330
+ await sessionManager.saveCheckpoint(sessionId, stateMachine.getState(), 'complete');
331
+ // Show final report
332
+ const report = costTracker.getReport();
333
+ console.log(picocolors_1.default.green('\nāœ… Complete!\n'));
334
+ console.log(picocolors_1.default.dim(`Session ID: ${sessionId}`));
335
+ console.log(picocolors_1.default.dim(`Output directory: ${projectDir}`));
336
+ console.log(picocolors_1.default.dim(`Total cost: $${report.total.toFixed(2)} (${report.operations} operations)`));
337
+ console.log(picocolors_1.default.dim(`Budget remaining: $${report.remaining.toFixed(2)}\n`));
338
+ if (debugLogger_1.debugLogger.isEnabled()) {
339
+ console.log(picocolors_1.default.yellow(`šŸ› Debug log saved to: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
340
+ debugLogger_1.debugLogger.disable();
341
+ }
342
+ console.log(picocolors_1.default.dim('View this session anytime with:'));
343
+ console.log(picocolors_1.default.dim(` gameforge session ${sessionId}\n`));
344
+ }
345
+ catch (error) {
346
+ if (debugLogger_1.debugLogger.isEnabled()) {
347
+ debugLogger_1.debugLogger.logError('RunCreationFlow', error);
348
+ console.log(picocolors_1.default.yellow(`\nšŸ› Debug log with error details: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
349
+ debugLogger_1.debugLogger.disable();
350
+ }
351
+ console.error(picocolors_1.default.red('\nāŒ Error:'), error);
352
+ process.exit(1);
353
+ }
354
+ }
355
+ function getInquisitorQuestions(templateId) {
356
+ return [
357
+ {
358
+ question: 'What is your game\'s core concept? (Elevator pitch)',
359
+ category: 'meta',
360
+ allowCustom: true,
361
+ allowAIDecide: false
362
+ },
363
+ {
364
+ question: 'Select primary genre:',
365
+ category: 'meta',
366
+ suggestedAnswers: ['Roguelike', 'FPS', 'RPG', 'Platformer', 'Puzzle', 'Strategy', 'Simulation'],
367
+ allowCustom: true,
368
+ allowAIDecide: false
369
+ },
370
+ {
371
+ question: 'What platforms are you targeting?',
372
+ category: 'technical',
373
+ suggestedAnswers: ['PC Only', 'PC + Consoles', 'Mobile', 'Multi-platform'],
374
+ allowCustom: true,
375
+ allowAIDecide: true
376
+ },
377
+ {
378
+ question: 'What is the estimated scope?',
379
+ category: 'meta',
380
+ suggestedAnswers: ['Prototype', 'Vertical Slice', 'MVP', 'Full Game'],
381
+ allowCustom: false,
382
+ allowAIDecide: true
383
+ },
384
+ {
385
+ question: 'Which game engine?',
386
+ category: 'technical',
387
+ suggestedAnswers: ['Unreal Engine 5', 'Unity', 'Godot', 'Custom'],
388
+ allowCustom: true,
389
+ allowAIDecide: true
390
+ },
391
+ {
392
+ question: 'Describe your art style:',
393
+ category: 'creative',
394
+ suggestedAnswers: ['2D Pixel Art', '3D Low-poly', '3D Realistic', 'Hand-drawn 2D', 'Voxel'],
395
+ allowCustom: true,
396
+ allowAIDecide: true
397
+ },
398
+ {
399
+ question: 'What is the core gameplay loop?',
400
+ category: 'gameplay',
401
+ allowCustom: true,
402
+ allowAIDecide: false
403
+ },
404
+ {
405
+ question: 'Who is the target audience?',
406
+ category: 'meta',
407
+ suggestedAnswers: ['Casual players', 'Hardcore gamers', 'Families', 'Competitive players'],
408
+ allowCustom: true,
409
+ allowAIDecide: true
410
+ },
411
+ {
412
+ question: 'Monetization strategy?',
413
+ category: 'meta',
414
+ suggestedAnswers: ['Premium (one-time purchase)', 'Free-to-play + IAP', 'Free with Ads', 'Subscription'],
415
+ allowCustom: true,
416
+ allowAIDecide: true
417
+ },
418
+ {
419
+ question: 'Any key features you want to include?',
420
+ category: 'gameplay',
421
+ allowCustom: true,
422
+ allowAIDecide: false
423
+ }
424
+ ];
425
+ }
426
+ function showConceptBrief(transcript) {
427
+ console.log(picocolors_1.default.bold('Concept Brief:'));
428
+ transcript.forEach(entry => {
429
+ const marker = entry.autoGenerated ? picocolors_1.default.yellow(' [AI]') : '';
430
+ console.log(picocolors_1.default.dim(` ${entry.question}`));
431
+ console.log(picocolors_1.default.green(` → ${entry.answer}${marker}`));
432
+ });
433
+ console.log('');
434
+ }
435
+ async function confirmGate(message) {
436
+ const response = await enquirer_1.default.prompt({
437
+ type: 'confirm',
438
+ name: 'confirm',
439
+ message,
440
+ initial: true
441
+ });
442
+ return response.confirm;
443
+ }
444
+ async function selectReviewAction() {
445
+ const response = await enquirer_1.default.prompt({
446
+ type: 'select',
447
+ name: 'action',
448
+ message: 'What would you like to do?',
449
+ choices: [
450
+ { name: 'save', message: 'Save and export GDD' },
451
+ { name: 'fix', message: 'Fix issues and re-validate' },
452
+ { name: 'exit', message: 'Exit without saving' }
453
+ ]
454
+ });
455
+ return response.action;
456
+ }
457
+ async function listSessions() {
458
+ console.log(picocolors_1.default.cyan('\nšŸ“‹ Saved Sessions\n'));
459
+ const sessionManager = new SessionManager_1.SessionManager();
460
+ try {
461
+ const sessions = await sessionManager.listSessions();
462
+ if (sessions.length === 0) {
463
+ console.log(picocolors_1.default.dim('No sessions found.'));
464
+ console.log(picocolors_1.default.dim('\nCreate a new session with: gameforge create\n'));
465
+ return;
466
+ }
467
+ sessions.forEach((session) => {
468
+ console.log(picocolors_1.default.green(session.sessionId));
469
+ console.log(picocolors_1.default.bold(` ${session.title}`));
470
+ console.log(picocolors_1.default.dim(` Phase: ${session.currentPhase}`));
471
+ console.log(picocolors_1.default.dim(` Checkpoints: ${session.checkpointCount}`));
472
+ console.log(picocolors_1.default.dim(` Created: ${new Date(session.createdAt).toLocaleString()}`));
473
+ console.log(picocolors_1.default.dim(` Modified: ${new Date(session.lastModified).toLocaleString()}\n`));
474
+ });
475
+ console.log(picocolors_1.default.dim('Usage:'));
476
+ console.log(picocolors_1.default.dim(' gameforge resume <session-id> Resume from latest checkpoint'));
477
+ console.log(picocolors_1.default.dim(' gameforge session <session-id> View session details\n'));
478
+ }
479
+ catch (error) {
480
+ console.error(picocolors_1.default.red('Error listing sessions:'), error);
481
+ }
482
+ }
483
+ async function showSession(sessionId) {
484
+ console.log(picocolors_1.default.cyan(`\nšŸ“‹ Session Details: ${sessionId}\n`));
485
+ const sessionManager = new SessionManager_1.SessionManager();
486
+ try {
487
+ const session = await sessionManager.getSession(sessionId);
488
+ if (!session) {
489
+ console.error(picocolors_1.default.red(`Session not found: ${sessionId}`));
490
+ console.log(picocolors_1.default.dim('\nList all sessions with: gameforge sessions\n'));
491
+ return;
492
+ }
493
+ console.log(picocolors_1.default.bold(session.title));
494
+ console.log(picocolors_1.default.dim(`Session ID: ${session.sessionId}`));
495
+ console.log(picocolors_1.default.dim(`Current Phase: ${session.currentPhase}`));
496
+ console.log(picocolors_1.default.dim(`Created: ${new Date(session.createdAt).toLocaleString()}`));
497
+ console.log(picocolors_1.default.dim(`Last Modified: ${new Date(session.lastModified).toLocaleString()}`));
498
+ console.log(picocolors_1.default.dim(`Total Checkpoints: ${session.checkpointCount}\n`));
499
+ // List checkpoints for this session
500
+ const checkpoints = await sessionManager.listCheckpoints(sessionId);
501
+ if (checkpoints.length === 0) {
502
+ console.log(picocolors_1.default.dim('No checkpoints found for this session.'));
503
+ }
504
+ else {
505
+ console.log(picocolors_1.default.cyan('Checkpoints:\n'));
506
+ checkpoints.forEach((cp, idx) => {
507
+ const isLatest = cp.checkpointId === session.latestCheckpointId;
508
+ const prefix = isLatest ? picocolors_1.default.green('→ ') : ' ';
509
+ console.log(`${prefix}${picocolors_1.default.bold(cp.label)} ${isLatest ? picocolors_1.default.green('(latest)') : ''}`);
510
+ console.log(` ${picocolors_1.default.dim('ID:')} ${cp.checkpointId}`);
511
+ console.log(` ${picocolors_1.default.dim('Phase:')} ${cp.phase}`);
512
+ console.log(` ${picocolors_1.default.dim('Budget:')} $${cp.budgetUsed.toFixed(2)} / $${cp.budgetLimit.toFixed(2)}`);
513
+ console.log(` ${picocolors_1.default.dim('Saved:')} ${new Date(cp.timestamp).toLocaleString()}\n`);
514
+ });
515
+ }
516
+ console.log(picocolors_1.default.dim('Usage:'));
517
+ console.log(picocolors_1.default.dim(` gameforge resume ${sessionId} Resume from latest checkpoint`));
518
+ console.log(picocolors_1.default.dim(` gameforge resume <checkpoint-id> Resume from specific checkpoint\n`));
519
+ }
520
+ catch (error) {
521
+ console.error(picocolors_1.default.red('Error showing session:'), error);
522
+ }
523
+ }
524
+ async function resumeFlow(idOrCheckpoint, options = {}) {
525
+ console.clear();
526
+ console.log(picocolors_1.default.cyan('\n╔══════════════════════════════╗'));
527
+ console.log(picocolors_1.default.cyan('ā•‘ GameForge CLI - Resume ā•‘'));
528
+ console.log(picocolors_1.default.cyan('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n'));
529
+ const apiKey = process.env.ANTHROPIC_API_KEY;
530
+ if (!apiKey) {
531
+ console.error(picocolors_1.default.red('Error: ANTHROPIC_API_KEY not set in .env'));
532
+ process.exit(1);
533
+ }
534
+ const sessionManager = new SessionManager_1.SessionManager();
535
+ try {
536
+ // Determine if this is a session ID or checkpoint ID
537
+ let sessionId;
538
+ let checkpointId;
539
+ let savedState;
540
+ if (idOrCheckpoint.startsWith('session-')) {
541
+ // It's a session ID - load latest checkpoint
542
+ sessionId = idOrCheckpoint;
543
+ console.log(picocolors_1.default.yellow(`Loading latest checkpoint for session: ${sessionId}...`));
544
+ const latest = await sessionManager.getLatestCheckpoint(sessionId);
545
+ if (!latest) {
546
+ console.error(picocolors_1.default.red(`No checkpoints found for session: ${sessionId}`));
547
+ console.log(picocolors_1.default.dim('\nList all sessions with: gameforge sessions\n'));
548
+ process.exit(1);
549
+ }
550
+ checkpointId = latest.id;
551
+ savedState = latest.state;
552
+ console.log(picocolors_1.default.green(`āœ“ Using checkpoint: ${checkpointId}`));
553
+ }
554
+ else {
555
+ // It's a checkpoint ID - load it directly
556
+ checkpointId = idOrCheckpoint;
557
+ console.log(picocolors_1.default.yellow(`Loading checkpoint: ${checkpointId}...`));
558
+ savedState = await sessionManager.loadCheckpoint(checkpointId);
559
+ // Extract session ID from checkpoint ID
560
+ sessionId = checkpointId.split('_')[0];
561
+ // Check if this is an old-format checkpoint (starts with "gameforge-" instead of "session-")
562
+ if (sessionId.startsWith('gameforge-')) {
563
+ // Old checkpoint format - create a session for it if it doesn't exist
564
+ const existingSession = await sessionManager.getSession(sessionId);
565
+ if (!existingSession) {
566
+ console.log(picocolors_1.default.dim('Migrating old checkpoint to session-based format...'));
567
+ // Extract the checkpoint label to create a meaningful session title
568
+ const checkpointData = await fs_extra_1.default.readJson(path_1.default.join('.gameforge', 'checkpoints', `${checkpointId}.json`));
569
+ const sessionTitle = `Migrated Session (${checkpointData.label})`;
570
+ // Create a new session with the old project ID as the session ID for backward compatibility
571
+ const sessionPath = path_1.default.join('.gameforge', 'sessions', `${sessionId}.json`);
572
+ const sessionMetadata = {
573
+ sessionId: sessionId,
574
+ title: sessionTitle,
575
+ createdAt: checkpointData.timestamp,
576
+ lastModified: checkpointData.timestamp,
577
+ currentPhase: savedState.phase,
578
+ checkpointCount: 1,
579
+ latestCheckpointId: checkpointId
580
+ };
581
+ await fs_extra_1.default.writeJson(sessionPath, sessionMetadata, { spaces: 2 });
582
+ console.log(picocolors_1.default.green(`āœ“ Created session: ${sessionId}`));
583
+ }
584
+ }
585
+ }
586
+ // Enable debug logging if requested
587
+ if (options.debug) {
588
+ debugLogger_1.debugLogger.enable(sessionId);
589
+ console.log(picocolors_1.default.yellow(`šŸ› Debug mode enabled`));
590
+ console.log(picocolors_1.default.dim(` Log file: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
591
+ }
592
+ console.log(picocolors_1.default.green(`āœ“ Loaded checkpoint from phase: ${savedState.phase}`));
593
+ console.log(picocolors_1.default.dim(` Budget used: $${savedState.costAccumulated.toFixed(2)}/$${savedState.budgetLimit.toFixed(2)}\n`));
594
+ // Recreate the state machine and restore state
595
+ const stateMachine = new StateMachine_1.StateMachine(savedState.budgetLimit);
596
+ stateMachine.restoreState(savedState);
597
+ const costTracker = new costTracker_1.CostTracker(savedState.budgetLimit);
598
+ const modelSelector = new modelSelector_1.ModelSelector();
599
+ const fileManager = new fileManager_1.FileManager();
600
+ const projectId = sessionId;
601
+ // Determine what phase we're resuming from
602
+ const currentPhase = savedState.phase;
603
+ // If we have a transcript from discovery/review phase, we can use or regenerate the Game Bible
604
+ if (currentPhase === StateMachine_1.GameForgePhase.REVIEW_CONCEPT || currentPhase === StateMachine_1.GameForgePhase.DISCOVERY ||
605
+ currentPhase === StateMachine_1.GameForgePhase.ARCHITECTURE || currentPhase === StateMachine_1.GameForgePhase.REVIEW_DATA) {
606
+ if (!savedState.transcript || savedState.transcript.length === 0) {
607
+ console.log(picocolors_1.default.yellow('Checkpoint has no transcript data. Please restart with a new session.'));
608
+ return;
609
+ }
610
+ let bible;
611
+ // Check if we already have a generated bible in the checkpoint
612
+ if (savedState.bible && savedState.bible.features && savedState.bible.features.length > 0) {
613
+ // Use existing bible from checkpoint
614
+ console.log(picocolors_1.default.cyan('\nšŸ—ļø Loading Game Bible from checkpoint\n'));
615
+ console.log(picocolors_1.default.green(`āœ“ Found existing Game Bible with ${savedState.bible.features.length} features`));
616
+ console.log(picocolors_1.default.green(`āœ“ Found ${savedState.bible.gameObjects.length} game objects\n`));
617
+ bible = savedState.bible;
618
+ }
619
+ else {
620
+ // No bible in checkpoint, need to regenerate (backward compatibility)
621
+ console.log(picocolors_1.default.cyan('\nšŸ—ļø Generating Game Bible\n'));
622
+ console.log(picocolors_1.default.dim(`Found ${savedState.transcript.length} interview questions/answers\n`));
623
+ // Transition to architecture phase if we're coming from review_concept or discovery
624
+ if (currentPhase === StateMachine_1.GameForgePhase.REVIEW_CONCEPT || currentPhase === StateMachine_1.GameForgePhase.DISCOVERY) {
625
+ stateMachine.transition(StateMachine_1.GameForgePhase.ARCHITECTURE);
626
+ }
627
+ const architect = new Architect_1.Architect(apiKey, costTracker, modelSelector);
628
+ const spinner = (0, ora_1.default)('Generating Game Bible...').start();
629
+ bible = await architect.execute(savedState.transcript);
630
+ spinner.succeed('Game Bible generated');
631
+ }
632
+ stateMachine.updateBible(bible);
633
+ // Transition to review_data phase
634
+ stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_DATA);
635
+ // Save checkpoint after successful generation (only if we regenerated)
636
+ if (!savedState.bible || !savedState.bible.features) {
637
+ await sessionManager.saveCheckpoint(sessionId, stateMachine.getState(), 'after_architecture');
638
+ }
639
+ // Transition to production and continue
640
+ stateMachine.transition(StateMachine_1.GameForgePhase.PRODUCTION);
641
+ await continueToProduction(stateMachine, bible, apiKey, costTracker, modelSelector, fileManager, sessionId, sessionManager);
642
+ return;
643
+ }
644
+ if (currentPhase === StateMachine_1.GameForgePhase.PRODUCTION || currentPhase === StateMachine_1.GameForgePhase.VALIDATION || currentPhase === StateMachine_1.GameForgePhase.REVIEW_ISSUES) {
645
+ console.log(picocolors_1.default.cyan('\nšŸŽØ Resuming from Production/Validation\n'));
646
+ const bible = savedState.bible; // GameBible
647
+ if (!bible || !bible.features) {
648
+ console.error(picocolors_1.default.red('Error: No GameBible found in checkpoint'));
649
+ return;
650
+ }
651
+ // Handle phase transitions based on current phase
652
+ if (currentPhase === StateMachine_1.GameForgePhase.REVIEW_ISSUES) {
653
+ // REVIEW_ISSUES can transition back to PRODUCTION for retry
654
+ console.log(picocolors_1.default.yellow('Regenerating all outputs from production phase...'));
655
+ stateMachine.transition(StateMachine_1.GameForgePhase.PRODUCTION);
656
+ }
657
+ else if (currentPhase === StateMachine_1.GameForgePhase.VALIDATION) {
658
+ // VALIDATION cannot go back, but we're already past production
659
+ console.log(picocolors_1.default.yellow('Checkpoint is at validation phase. Continuing from there...'));
660
+ // We'll skip the production phase and go straight to validation in continueToProduction
661
+ }
662
+ // If currentPhase is already PRODUCTION, no transition needed
663
+ await continueToProduction(stateMachine, bible, apiKey, costTracker, modelSelector, fileManager, sessionId, sessionManager);
664
+ return;
665
+ }
666
+ console.log(picocolors_1.default.green('Checkpoint already complete!'));
667
+ }
668
+ catch (error) {
669
+ if (debugLogger_1.debugLogger.isEnabled()) {
670
+ debugLogger_1.debugLogger.logError('ResumeFlow', error);
671
+ console.log(picocolors_1.default.yellow(`\nšŸ› Debug log with error details: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
672
+ debugLogger_1.debugLogger.disable();
673
+ }
674
+ console.error(picocolors_1.default.red('\nāŒ Error resuming:'), error);
675
+ process.exit(1);
676
+ }
677
+ }
678
+ async function continueToProduction(stateMachine, bible, apiKey, costTracker, modelSelector, fileManager, sessionId, sessionManager) {
679
+ // Phase 3: Production (Specialists)
680
+ console.log(picocolors_1.default.cyan('\nšŸŽØ Phase 3: Production\n'));
681
+ // Note: Caller should have already transitioned to PRODUCTION phase
682
+ const projectId = sessionId;
683
+ const orchestrator = new Orchestrator_1.SpecialistOrchestrator(2);
684
+ const specialists = [
685
+ { name: 'Creative Direction', agent: new CreativeSpecialist_1.CreativeSpecialist(apiKey, costTracker, modelSelector) },
686
+ { name: 'Feature Specifications', agent: new FeatureSpecialist_1.FeatureSpecialist(apiKey, costTracker, modelSelector) },
687
+ { name: 'Entity Specifications', agent: new EntitySpecialist_1.EntitySpecialist(apiKey, costTracker, modelSelector) },
688
+ { name: 'Technical Specifications', agent: new TechSpecialist_1.TechSpecialist(apiKey, costTracker, modelSelector) }
689
+ ];
690
+ let markdownSections = await orchestrator.runAllSpecialists(bible, specialists);
691
+ // Phase 4: Validation
692
+ console.log(picocolors_1.default.cyan('\nāœ… Phase 4: Validation\n'));
693
+ stateMachine.transition(StateMachine_1.GameForgePhase.VALIDATION);
694
+ const consistencyAgent = new Consistency_1.ConsistencyAgent();
695
+ let currentConsistencyIssues = consistencyAgent.validate(bible);
696
+ console.log(picocolors_1.default.yellow(`Found ${currentConsistencyIssues.length} consistency issues`));
697
+ let chaosSpinner = (0, ora_1.default)('Running Chaos Agent...').start();
698
+ const chaosAgent = new Chaos_1.Chaos(apiKey, costTracker, modelSelector);
699
+ let currentChaosIssues = await chaosAgent.execute(bible);
700
+ chaosSpinner.succeed(`Found ${currentChaosIssues.length} design issues`);
701
+ // Gate 3: Review Issues with Fix Option
702
+ const MAX_FIX_ATTEMPTS = 3;
703
+ let fixAttempts = 0;
704
+ let fixesApplied = false;
705
+ while (true) {
706
+ stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_ISSUES);
707
+ const totalIssues = currentConsistencyIssues.length + currentChaosIssues.length;
708
+ if (totalIssues > 0) {
709
+ console.log(picocolors_1.default.yellow(`\nāš ļø ${totalIssues} issues found:`));
710
+ currentConsistencyIssues.slice(0, 3).forEach((issue) => {
711
+ console.log(picocolors_1.default.dim(` - [${issue.severity}] ${issue.message}`));
712
+ });
713
+ currentChaosIssues.slice(0, 3).forEach((issue) => {
714
+ console.log(picocolors_1.default.dim(` - [${issue.severity}] ${issue.description}`));
715
+ });
716
+ if (totalIssues > 6) {
717
+ console.log(picocolors_1.default.dim(` ... and ${totalIssues - 6} more issues`));
718
+ }
719
+ if (fixAttempts > 0) {
720
+ console.log(picocolors_1.default.cyan(`\n(Fix attempt ${fixAttempts}/${MAX_FIX_ATTEMPTS} complete)`));
721
+ }
722
+ }
723
+ else {
724
+ console.log(picocolors_1.default.green('\nāœ“ All issues resolved!'));
725
+ }
726
+ // Determine action based on state
727
+ let action;
728
+ if (totalIssues === 0) {
729
+ const proceed = await confirmGate('Save and export GDD?');
730
+ action = proceed ? 'save' : 'exit';
731
+ }
732
+ else if (fixAttempts >= MAX_FIX_ATTEMPTS) {
733
+ console.log(picocolors_1.default.yellow(`\nMaximum fix attempts (${MAX_FIX_ATTEMPTS}) reached.`));
734
+ const proceed = await confirmGate('Save and export GDD with remaining issues?');
735
+ action = proceed ? 'save' : 'exit';
736
+ }
737
+ else {
738
+ action = await selectReviewAction();
739
+ }
740
+ if (action === 'exit') {
741
+ console.log(picocolors_1.default.yellow('Exiting...'));
742
+ return;
743
+ }
744
+ if (action === 'save') {
745
+ break;
746
+ }
747
+ // action === 'fix': Run remediation
748
+ fixAttempts++;
749
+ fixesApplied = true;
750
+ console.log(picocolors_1.default.cyan(`\nšŸ”§ Fixing issues (attempt ${fixAttempts}/${MAX_FIX_ATTEMPTS})...\n`));
751
+ const remediationSpinner = (0, ora_1.default)('Running Remediation Agent...').start();
752
+ const remediationAgent = new Remediation_1.Remediation(apiKey, costTracker, modelSelector);
753
+ try {
754
+ const result = await remediationAgent.execute(bible, currentConsistencyIssues, currentChaosIssues, (message) => { remediationSpinner.text = `Remediation: ${message}`; });
755
+ remediationSpinner.succeed(`Fixed ${result.fixedIssues.length} issues`);
756
+ if (result.fixedIssues.length > 0) {
757
+ console.log(picocolors_1.default.green('\nFixes applied:'));
758
+ result.fixedIssues.forEach(fix => {
759
+ console.log(picocolors_1.default.dim(` āœ“ ${fix}`));
760
+ });
761
+ }
762
+ if (result.unfixableIssues.length > 0) {
763
+ console.log(picocolors_1.default.yellow('\nCould not fix:'));
764
+ result.unfixableIssues.forEach(issue => {
765
+ console.log(picocolors_1.default.dim(` āœ— ${issue}`));
766
+ });
767
+ }
768
+ // Update bible
769
+ bible = result.updatedBible;
770
+ stateMachine.updateBible(bible);
771
+ // Transition back to production for re-validation
772
+ stateMachine.transition(StateMachine_1.GameForgePhase.PRODUCTION);
773
+ // Re-run validation
774
+ console.log(picocolors_1.default.cyan('\nšŸ”„ Re-validating...\n'));
775
+ stateMachine.transition(StateMachine_1.GameForgePhase.VALIDATION);
776
+ const newConsistencyAgent = new Consistency_1.ConsistencyAgent();
777
+ currentConsistencyIssues = newConsistencyAgent.validate(bible);
778
+ console.log(picocolors_1.default.yellow(`Found ${currentConsistencyIssues.length} consistency issues`));
779
+ chaosSpinner = (0, ora_1.default)('Re-running Chaos Agent...').start();
780
+ const newChaosAgent = new Chaos_1.Chaos(apiKey, costTracker, modelSelector);
781
+ currentChaosIssues = await newChaosAgent.execute(bible, (message) => {
782
+ chaosSpinner.text = `Chaos Agent: ${message}`;
783
+ });
784
+ chaosSpinner.succeed(`Found ${currentChaosIssues.length} design issues`);
785
+ }
786
+ catch (error) {
787
+ remediationSpinner.fail('Remediation failed');
788
+ console.error(picocolors_1.default.red('Error during remediation:'), error);
789
+ const continueAnyway = await confirmGate('Continue to save despite remediation failure?');
790
+ if (!continueAnyway) {
791
+ console.log(picocolors_1.default.yellow('Exiting...'));
792
+ return;
793
+ }
794
+ break;
795
+ }
796
+ }
797
+ // If fixes were applied, re-run specialists to regenerate markdown
798
+ if (fixesApplied) {
799
+ console.log(picocolors_1.default.cyan('\nšŸ”„ Regenerating documentation with fixed Bible...\n'));
800
+ markdownSections = await orchestrator.runAllSpecialists(bible, specialists);
801
+ }
802
+ // Save all outputs
803
+ console.log(picocolors_1.default.cyan('\nšŸ’¾ Saving outputs...\n'));
804
+ const projectDir = await fileManager.initialize(projectId);
805
+ await fileManager.saveGameBible(projectDir, bible);
806
+ await fileManager.saveCoverPage(projectDir, bible);
807
+ for (const [name, content] of markdownSections) {
808
+ const filename = name.replace(/\s+/g, '_') + '.md';
809
+ await fileManager.saveMarkdown(projectDir, filename, content);
810
+ }
811
+ await fileManager.saveGapAnalysis(projectDir, currentChaosIssues, currentConsistencyIssues);
812
+ // Save combined GDD
813
+ await fileManager.saveCombinedGDD(projectDir, bible, markdownSections, currentChaosIssues, currentConsistencyIssues);
814
+ stateMachine.transition(StateMachine_1.GameForgePhase.COMPLETE);
815
+ // Save final checkpoint
816
+ await sessionManager.saveCheckpoint(sessionId, stateMachine.getState(), 'complete');
817
+ const report = costTracker.getReport();
818
+ console.log(picocolors_1.default.green('\nāœ… Complete!\n'));
819
+ console.log(picocolors_1.default.dim(`Session ID: ${sessionId}`));
820
+ console.log(picocolors_1.default.dim(`Output directory: ${projectDir}`));
821
+ console.log(picocolors_1.default.dim(`Total cost: $${report.total.toFixed(2)} (${report.operations} operations)`));
822
+ console.log(picocolors_1.default.dim(`Budget remaining: $${report.remaining.toFixed(2)}\n`));
823
+ if (debugLogger_1.debugLogger.isEnabled()) {
824
+ console.log(picocolors_1.default.yellow(`šŸ› Debug log saved to: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
825
+ debugLogger_1.debugLogger.disable();
826
+ }
827
+ console.log(picocolors_1.default.dim('View this session anytime with:'));
828
+ console.log(picocolors_1.default.dim(` gameforge session ${sessionId}\n`));
829
+ }
830
+ //# sourceMappingURL=index.js.map