gameforge-cli 0.1.0 → 0.2.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 (75) hide show
  1. package/README.md +81 -41
  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 +17 -5
  7. package/dist/agents/core/Architect.d.ts.map +1 -1
  8. package/dist/agents/core/Architect.js +263 -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/Modifier.d.ts +13 -0
  19. package/dist/agents/core/Modifier.d.ts.map +1 -0
  20. package/dist/agents/core/Modifier.js +141 -0
  21. package/dist/agents/core/Modifier.js.map +1 -0
  22. package/dist/agents/core/Remediation.d.ts +3 -1
  23. package/dist/agents/core/Remediation.d.ts.map +1 -1
  24. package/dist/agents/core/Remediation.js +63 -3
  25. package/dist/agents/core/Remediation.js.map +1 -1
  26. package/dist/agents/specialists/CreativeSpecialist.d.ts.map +1 -1
  27. package/dist/agents/specialists/CreativeSpecialist.js +94 -25
  28. package/dist/agents/specialists/CreativeSpecialist.js.map +1 -1
  29. package/dist/agents/specialists/EntitySpecialist.d.ts.map +1 -1
  30. package/dist/agents/specialists/EntitySpecialist.js +63 -25
  31. package/dist/agents/specialists/EntitySpecialist.js.map +1 -1
  32. package/dist/agents/specialists/FeatureSpecialist.d.ts.map +1 -1
  33. package/dist/agents/specialists/FeatureSpecialist.js +53 -39
  34. package/dist/agents/specialists/FeatureSpecialist.js.map +1 -1
  35. package/dist/agents/specialists/TechSpecialist.d.ts.map +1 -1
  36. package/dist/agents/specialists/TechSpecialist.js +69 -32
  37. package/dist/agents/specialists/TechSpecialist.js.map +1 -1
  38. package/dist/config/schema.d.ts +1319 -709
  39. package/dist/config/schema.d.ts.map +1 -1
  40. package/dist/config/schema.js +142 -52
  41. package/dist/config/schema.js.map +1 -1
  42. package/dist/config/templates.d.ts.map +1 -1
  43. package/dist/config/templates.js +6 -66
  44. package/dist/config/templates.js.map +1 -1
  45. package/dist/core/Orchestrator.d.ts +17 -3
  46. package/dist/core/Orchestrator.d.ts.map +1 -1
  47. package/dist/core/Orchestrator.js +46 -16
  48. package/dist/core/Orchestrator.js.map +1 -1
  49. package/dist/index.js +335 -195
  50. package/dist/index.js.map +1 -1
  51. package/dist/types/issueReview.d.ts +19 -0
  52. package/dist/types/issueReview.d.ts.map +1 -0
  53. package/dist/types/issueReview.js +3 -0
  54. package/dist/types/issueReview.js.map +1 -0
  55. package/dist/utils/costTracker.d.ts +28 -0
  56. package/dist/utils/costTracker.d.ts.map +1 -1
  57. package/dist/utils/costTracker.js +71 -1
  58. package/dist/utils/costTracker.js.map +1 -1
  59. package/dist/utils/disambiguationHelper.d.ts +54 -0
  60. package/dist/utils/disambiguationHelper.d.ts.map +1 -0
  61. package/dist/utils/disambiguationHelper.js +252 -0
  62. package/dist/utils/disambiguationHelper.js.map +1 -0
  63. package/dist/utils/issueReviewer.d.ts +6 -0
  64. package/dist/utils/issueReviewer.d.ts.map +1 -0
  65. package/dist/utils/issueReviewer.js +159 -0
  66. package/dist/utils/issueReviewer.js.map +1 -0
  67. package/dist/utils/issueSelector.d.ts +26 -0
  68. package/dist/utils/issueSelector.d.ts.map +1 -0
  69. package/dist/utils/issueSelector.js +132 -0
  70. package/dist/utils/issueSelector.js.map +1 -0
  71. package/package.json +1 -1
  72. package/dist/core/CheckpointManager.d.ts +0 -16
  73. package/dist/core/CheckpointManager.d.ts.map +0 -1
  74. package/dist/core/CheckpointManager.js +0 -52
  75. package/dist/core/CheckpointManager.js.map +0 -1
package/dist/index.js CHANGED
@@ -29,6 +29,8 @@ const TechSpecialist_1 = require("./agents/specialists/TechSpecialist");
29
29
  const CreativeSpecialist_1 = require("./agents/specialists/CreativeSpecialist");
30
30
  const Orchestrator_1 = require("./core/Orchestrator");
31
31
  const debugLogger_1 = require("./utils/debugLogger");
32
+ const issueReviewer_1 = require("./utils/issueReviewer");
33
+ const Modifier_1 = require("./agents/core/Modifier");
32
34
  dotenv_1.default.config();
33
35
  const program = new commander_1.Command();
34
36
  program
@@ -77,6 +79,13 @@ program
77
79
  });
78
80
  console.log(picocolors_1.default.dim('Usage: gameforge create --template <id>\n'));
79
81
  });
82
+ program
83
+ .command('modify <sessionId>')
84
+ .description('Modify an existing game design document')
85
+ .option('-d, --debug', 'Enable debug logging to file')
86
+ .action(async (sessionId, options) => {
87
+ await runModifyFlow(sessionId, options);
88
+ });
80
89
  program.parse();
81
90
  async function runCreationFlow(options) {
82
91
  console.clear();
@@ -142,12 +151,19 @@ async function runCreationFlow(options) {
142
151
  // Phase 1: Discovery
143
152
  console.log(picocolors_1.default.cyan('\nšŸ“‹ Phase 1: Discovery\n'));
144
153
  stateMachine.transition(StateMachine_1.GameForgePhase.DISCOVERY);
154
+ costTracker.startPhase('discovery');
145
155
  const inquisitor = new Inquisitor_1.Inquisitor(apiKey, costTracker, modelSelector);
146
156
  const questions = getInquisitorQuestions(options.template);
147
157
  const transcript = await inquisitor.execute(questions);
148
158
  transcript.forEach(entry => {
149
159
  stateMachine.addTranscriptEntry(entry.question, entry.answer, entry.autoGenerated);
150
160
  });
161
+ // End discovery phase and show cost
162
+ const discoveryCost = costTracker.endPhase();
163
+ if (discoveryCost && discoveryCost.operations > 0) {
164
+ console.log(picocolors_1.default.green('\nāœ“ Discovery complete'));
165
+ costTracker.printPhaseSummary(discoveryCost);
166
+ }
151
167
  // Gate 1: Review Concept
152
168
  console.log(picocolors_1.default.cyan('\nšŸ“Š Concept Review\n'));
153
169
  stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_CONCEPT);
@@ -161,6 +177,7 @@ async function runCreationFlow(options) {
161
177
  // Phase 2: Architecture
162
178
  console.log(picocolors_1.default.cyan('\nšŸ—ļø Phase 2: Architecture\n'));
163
179
  stateMachine.transition(StateMachine_1.GameForgePhase.ARCHITECTURE);
180
+ costTracker.startPhase('architecture');
164
181
  const spinner = (0, ora_1.default)('Generating Game Bible...').start();
165
182
  const architect = new Architect_1.Architect(apiKey, costTracker, modelSelector);
166
183
  let bible = await architect.execute(transcript, (message) => {
@@ -173,6 +190,11 @@ async function runCreationFlow(options) {
173
190
  }
174
191
  stateMachine.updateBible(bible);
175
192
  spinner.succeed('Game Bible generated');
193
+ // End architecture phase and show cost
194
+ const architectureCost = costTracker.endPhase();
195
+ if (architectureCost) {
196
+ costTracker.printPhaseSummary(architectureCost);
197
+ }
176
198
  // Save checkpoint
177
199
  await sessionManager.saveCheckpoint(sessionId, stateMachine.getState(), 'after_architecture');
178
200
  // Gate 2: Review Data
@@ -188,7 +210,8 @@ async function runCreationFlow(options) {
188
210
  // Phase 3: Production (Specialists)
189
211
  console.log(picocolors_1.default.cyan('\nšŸŽØ Phase 3: Production\n'));
190
212
  stateMachine.transition(StateMachine_1.GameForgePhase.PRODUCTION);
191
- const orchestrator = new Orchestrator_1.SpecialistOrchestrator(2); // Concurrency of 2
213
+ costTracker.startPhase('production');
214
+ const orchestrator = new Orchestrator_1.SpecialistOrchestrator();
192
215
  const specialists = [
193
216
  { name: 'Creative Direction', agent: new CreativeSpecialist_1.CreativeSpecialist(apiKey, costTracker, modelSelector) },
194
217
  { name: 'Feature Specifications', agent: new FeatureSpecialist_1.FeatureSpecialist(apiKey, costTracker, modelSelector) },
@@ -196,9 +219,16 @@ async function runCreationFlow(options) {
196
219
  { name: 'Technical Specifications', agent: new TechSpecialist_1.TechSpecialist(apiKey, costTracker, modelSelector) }
197
220
  ];
198
221
  let markdownSections = await orchestrator.runAllSpecialists(bible, specialists);
222
+ // End production phase and show cost
223
+ const productionCost = costTracker.endPhase();
224
+ if (productionCost) {
225
+ console.log(picocolors_1.default.green('\nāœ“ Production complete'));
226
+ costTracker.printPhaseSummary(productionCost);
227
+ }
199
228
  // Phase 4: Validation
200
229
  console.log(picocolors_1.default.cyan('\nāœ… Phase 4: Validation\n'));
201
230
  stateMachine.transition(StateMachine_1.GameForgePhase.VALIDATION);
231
+ costTracker.startPhase('validation');
202
232
  // Run consistency check
203
233
  const consistencyAgent = new Consistency_1.ConsistencyAgent();
204
234
  let currentConsistencyIssues = consistencyAgent.validate(bible);
@@ -210,106 +240,86 @@ async function runCreationFlow(options) {
210
240
  chaosSpinner.text = `Chaos Agent: ${message}`;
211
241
  });
212
242
  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;
243
+ // End validation phase and show cost
244
+ const validationCost = costTracker.endPhase();
245
+ if (validationCost) {
246
+ costTracker.printPhaseSummary(validationCost);
247
+ }
248
+ // Gate 3: Review Issues One by One
249
+ stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_ISSUES);
216
250
  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)`));
251
+ const totalIssues = currentConsistencyIssues.length + currentChaosIssues.length;
252
+ if (totalIssues > 0) {
253
+ console.log(picocolors_1.default.yellow(`\nāš ļø ${totalIssues} issues found. Let's review them one by one.\n`));
254
+ // Review each issue one by one
255
+ const reviewed = await (0, issueReviewer_1.reviewIssuesOneByOne)(currentConsistencyIssues, currentChaosIssues);
256
+ // If user selected any issues to fix
257
+ if (reviewed.toFix.length > 0) {
258
+ const proceed = await confirmGate(`Fix ${reviewed.toFix.length} selected issue(s)?`);
259
+ if (proceed) {
260
+ fixesApplied = true;
261
+ console.log(picocolors_1.default.cyan(`\nšŸ”§ Fixing ${reviewed.toFix.length} issues...\n`));
262
+ costTracker.startPhase('remediation');
263
+ const remediationSpinner = (0, ora_1.default)('Running Remediation Agent...').start();
264
+ const remediationAgent = new Remediation_1.Remediation(apiKey, costTracker, modelSelector);
265
+ try {
266
+ // Convert to IssueWithNotes format for Remediation
267
+ const issuesToFix = (0, issueReviewer_1.convertToIssuesWithNotes)(reviewed);
268
+ const result = await remediationAgent.execute(bible, issuesToFix, (message) => {
269
+ remediationSpinner.text = `Remediation: ${message}`;
270
+ });
271
+ remediationSpinner.succeed(`Fixed ${result.fixedIssues.length} issues`);
272
+ // End remediation phase and show cost
273
+ const remediationCost = costTracker.endPhase();
274
+ if (remediationCost) {
275
+ costTracker.printPhaseSummary(remediationCost);
276
+ }
277
+ if (result.fixedIssues.length > 0) {
278
+ console.log(picocolors_1.default.green('\nFixes applied:'));
279
+ result.fixedIssues.forEach((fix) => {
280
+ console.log(picocolors_1.default.dim(` āœ“ ${fix}`));
281
+ });
282
+ }
283
+ if (result.unfixableIssues.length > 0) {
284
+ console.log(picocolors_1.default.yellow('\nCould not fix:'));
285
+ result.unfixableIssues.forEach((issue) => {
286
+ console.log(picocolors_1.default.dim(` āœ— ${issue}`));
287
+ });
288
+ }
289
+ // Update bible
290
+ bible = result.updatedBible;
291
+ stateMachine.updateBible(bible);
292
+ }
293
+ catch (error) {
294
+ remediationSpinner.fail('Remediation failed');
295
+ console.error(picocolors_1.default.red('Error during remediation:'), error);
296
+ const continueAnyway = await confirmGate('Continue to save despite remediation failure?');
297
+ if (!continueAnyway) {
298
+ console.log(picocolors_1.default.yellow('Exiting...'));
299
+ return;
300
+ }
301
+ }
233
302
  }
234
303
  }
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') {
304
+ // Final confirmation to save
305
+ const saveConfirm = await confirmGate('Save and export GDD?');
306
+ if (!saveConfirm) {
253
307
  console.log(picocolors_1.default.yellow('Exiting...'));
254
308
  return;
255
309
  }
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;
310
+ }
311
+ else {
312
+ console.log(picocolors_1.default.green('\nāœ“ No issues found!'));
313
+ const proceed = await confirmGate('Save and export GDD?');
314
+ if (!proceed) {
315
+ console.log(picocolors_1.default.yellow('Exiting...'));
316
+ return;
307
317
  }
308
318
  }
309
319
  // If fixes were applied, re-run specialists to regenerate markdown
310
320
  if (fixesApplied) {
311
321
  console.log(picocolors_1.default.cyan('\nšŸ”„ Regenerating documentation with fixed Bible...\n'));
312
- markdownSections = await orchestrator.runAllSpecialists(bible, specialists);
322
+ markdownSections = await orchestrator.runAllSpecialists(bible, specialists, { skipDisambiguation: true });
313
323
  }
314
324
  // Save all outputs
315
325
  console.log(picocolors_1.default.cyan('\nšŸ’¾ Saving outputs...\n'));
@@ -333,7 +343,9 @@ async function runCreationFlow(options) {
333
343
  console.log(picocolors_1.default.green('\nāœ… Complete!\n'));
334
344
  console.log(picocolors_1.default.dim(`Session ID: ${sessionId}`));
335
345
  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)`));
346
+ // Show per-phase cost breakdown
347
+ costTracker.printFinalBreakdown();
348
+ console.log(picocolors_1.default.dim(`Total cost: $${report.total.toFixed(4)} (${report.operations} operations)`));
337
349
  console.log(picocolors_1.default.dim(`Budget remaining: $${report.remaining.toFixed(2)}\n`));
338
350
  if (debugLogger_1.debugLogger.isEnabled()) {
339
351
  console.log(picocolors_1.default.yellow(`šŸ› Debug log saved to: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
@@ -441,19 +453,6 @@ async function confirmGate(message) {
441
453
  });
442
454
  return response.confirm;
443
455
  }
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
456
  async function listSessions() {
458
457
  console.log(picocolors_1.default.cyan('\nšŸ“‹ Saved Sessions\n'));
459
458
  const sessionManager = new SessionManager_1.SessionManager();
@@ -678,9 +677,10 @@ async function resumeFlow(idOrCheckpoint, options = {}) {
678
677
  async function continueToProduction(stateMachine, bible, apiKey, costTracker, modelSelector, fileManager, sessionId, sessionManager) {
679
678
  // Phase 3: Production (Specialists)
680
679
  console.log(picocolors_1.default.cyan('\nšŸŽØ Phase 3: Production\n'));
680
+ costTracker.startPhase('production');
681
681
  // Note: Caller should have already transitioned to PRODUCTION phase
682
682
  const projectId = sessionId;
683
- const orchestrator = new Orchestrator_1.SpecialistOrchestrator(2);
683
+ const orchestrator = new Orchestrator_1.SpecialistOrchestrator();
684
684
  const specialists = [
685
685
  { name: 'Creative Direction', agent: new CreativeSpecialist_1.CreativeSpecialist(apiKey, costTracker, modelSelector) },
686
686
  { name: 'Feature Specifications', agent: new FeatureSpecialist_1.FeatureSpecialist(apiKey, costTracker, modelSelector) },
@@ -688,9 +688,16 @@ async function continueToProduction(stateMachine, bible, apiKey, costTracker, mo
688
688
  { name: 'Technical Specifications', agent: new TechSpecialist_1.TechSpecialist(apiKey, costTracker, modelSelector) }
689
689
  ];
690
690
  let markdownSections = await orchestrator.runAllSpecialists(bible, specialists);
691
+ // End production phase and show cost
692
+ const productionCost = costTracker.endPhase();
693
+ if (productionCost) {
694
+ console.log(picocolors_1.default.green('\nāœ“ Production complete'));
695
+ costTracker.printPhaseSummary(productionCost);
696
+ }
691
697
  // Phase 4: Validation
692
698
  console.log(picocolors_1.default.cyan('\nāœ… Phase 4: Validation\n'));
693
699
  stateMachine.transition(StateMachine_1.GameForgePhase.VALIDATION);
700
+ costTracker.startPhase('validation');
694
701
  const consistencyAgent = new Consistency_1.ConsistencyAgent();
695
702
  let currentConsistencyIssues = consistencyAgent.validate(bible);
696
703
  console.log(picocolors_1.default.yellow(`Found ${currentConsistencyIssues.length} consistency issues`));
@@ -698,106 +705,86 @@ async function continueToProduction(stateMachine, bible, apiKey, costTracker, mo
698
705
  const chaosAgent = new Chaos_1.Chaos(apiKey, costTracker, modelSelector);
699
706
  let currentChaosIssues = await chaosAgent.execute(bible);
700
707
  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;
708
+ // End validation phase and show cost
709
+ const validationCost = costTracker.endPhase();
710
+ if (validationCost) {
711
+ costTracker.printPhaseSummary(validationCost);
712
+ }
713
+ // Gate 3: Review Issues One by One
714
+ stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_ISSUES);
704
715
  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)`));
716
+ const totalIssues = currentConsistencyIssues.length + currentChaosIssues.length;
717
+ if (totalIssues > 0) {
718
+ console.log(picocolors_1.default.yellow(`\nāš ļø ${totalIssues} issues found. Let's review them one by one.\n`));
719
+ // Review each issue one by one
720
+ const reviewed = await (0, issueReviewer_1.reviewIssuesOneByOne)(currentConsistencyIssues, currentChaosIssues);
721
+ // If user selected any issues to fix
722
+ if (reviewed.toFix.length > 0) {
723
+ const proceed = await confirmGate(`Fix ${reviewed.toFix.length} selected issue(s)?`);
724
+ if (proceed) {
725
+ fixesApplied = true;
726
+ console.log(picocolors_1.default.cyan(`\nšŸ”§ Fixing ${reviewed.toFix.length} issues...\n`));
727
+ costTracker.startPhase('remediation');
728
+ const remediationSpinner = (0, ora_1.default)('Running Remediation Agent...').start();
729
+ const remediationAgent = new Remediation_1.Remediation(apiKey, costTracker, modelSelector);
730
+ try {
731
+ // Convert to IssueWithNotes format for Remediation
732
+ const issuesToFix = (0, issueReviewer_1.convertToIssuesWithNotes)(reviewed);
733
+ const result = await remediationAgent.execute(bible, issuesToFix, (message) => {
734
+ remediationSpinner.text = `Remediation: ${message}`;
735
+ });
736
+ remediationSpinner.succeed(`Fixed ${result.fixedIssues.length} issues`);
737
+ // End remediation phase and show cost
738
+ const remediationCost = costTracker.endPhase();
739
+ if (remediationCost) {
740
+ costTracker.printPhaseSummary(remediationCost);
741
+ }
742
+ if (result.fixedIssues.length > 0) {
743
+ console.log(picocolors_1.default.green('\nFixes applied:'));
744
+ result.fixedIssues.forEach((fix) => {
745
+ console.log(picocolors_1.default.dim(` āœ“ ${fix}`));
746
+ });
747
+ }
748
+ if (result.unfixableIssues.length > 0) {
749
+ console.log(picocolors_1.default.yellow('\nCould not fix:'));
750
+ result.unfixableIssues.forEach((issue) => {
751
+ console.log(picocolors_1.default.dim(` āœ— ${issue}`));
752
+ });
753
+ }
754
+ // Update bible
755
+ bible = result.updatedBible;
756
+ stateMachine.updateBible(bible);
757
+ }
758
+ catch (error) {
759
+ remediationSpinner.fail('Remediation failed');
760
+ console.error(picocolors_1.default.red('Error during remediation:'), error);
761
+ const continueAnyway = await confirmGate('Continue to save despite remediation failure?');
762
+ if (!continueAnyway) {
763
+ console.log(picocolors_1.default.yellow('Exiting...'));
764
+ return;
765
+ }
766
+ }
721
767
  }
722
768
  }
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') {
769
+ // Final confirmation to save
770
+ const saveConfirm = await confirmGate('Save and export GDD?');
771
+ if (!saveConfirm) {
741
772
  console.log(picocolors_1.default.yellow('Exiting...'));
742
773
  return;
743
774
  }
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;
775
+ }
776
+ else {
777
+ console.log(picocolors_1.default.green('\nāœ“ No issues found!'));
778
+ const proceed = await confirmGate('Save and export GDD?');
779
+ if (!proceed) {
780
+ console.log(picocolors_1.default.yellow('Exiting...'));
781
+ return;
795
782
  }
796
783
  }
797
784
  // If fixes were applied, re-run specialists to regenerate markdown
798
785
  if (fixesApplied) {
799
786
  console.log(picocolors_1.default.cyan('\nšŸ”„ Regenerating documentation with fixed Bible...\n'));
800
- markdownSections = await orchestrator.runAllSpecialists(bible, specialists);
787
+ markdownSections = await orchestrator.runAllSpecialists(bible, specialists, { skipDisambiguation: true });
801
788
  }
802
789
  // Save all outputs
803
790
  console.log(picocolors_1.default.cyan('\nšŸ’¾ Saving outputs...\n'));
@@ -818,7 +805,9 @@ async function continueToProduction(stateMachine, bible, apiKey, costTracker, mo
818
805
  console.log(picocolors_1.default.green('\nāœ… Complete!\n'));
819
806
  console.log(picocolors_1.default.dim(`Session ID: ${sessionId}`));
820
807
  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)`));
808
+ // Show per-phase cost breakdown
809
+ costTracker.printFinalBreakdown();
810
+ console.log(picocolors_1.default.dim(`Total cost: $${report.total.toFixed(4)} (${report.operations} operations)`));
822
811
  console.log(picocolors_1.default.dim(`Budget remaining: $${report.remaining.toFixed(2)}\n`));
823
812
  if (debugLogger_1.debugLogger.isEnabled()) {
824
813
  console.log(picocolors_1.default.yellow(`šŸ› Debug log saved to: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
@@ -827,4 +816,155 @@ async function continueToProduction(stateMachine, bible, apiKey, costTracker, mo
827
816
  console.log(picocolors_1.default.dim('View this session anytime with:'));
828
817
  console.log(picocolors_1.default.dim(` gameforge session ${sessionId}\n`));
829
818
  }
819
+ async function runModifyFlow(sessionId, options) {
820
+ console.clear();
821
+ console.log(picocolors_1.default.cyan('\n╔══════════════════════════════════════╗'));
822
+ console.log(picocolors_1.default.cyan('ā•‘ GameForge CLI - Modify Document ā•‘'));
823
+ console.log(picocolors_1.default.cyan('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n'));
824
+ // Enable debug logging if requested
825
+ if (options.debug) {
826
+ debugLogger_1.debugLogger.enable();
827
+ debugLogger_1.debugLogger.log('Starting modify flow for session: ' + sessionId);
828
+ }
829
+ const apiKey = process.env.ANTHROPIC_API_KEY;
830
+ if (!apiKey) {
831
+ console.error(picocolors_1.default.red('Error: ANTHROPIC_API_KEY environment variable is not set'));
832
+ console.log(picocolors_1.default.dim('\nSet your API key:'));
833
+ console.log(picocolors_1.default.dim(' export ANTHROPIC_API_KEY=your-key-here\n'));
834
+ return;
835
+ }
836
+ const sessionManager = new SessionManager_1.SessionManager();
837
+ const fileManager = new fileManager_1.FileManager();
838
+ // Load latest checkpoint for this session
839
+ console.log(picocolors_1.default.cyan('Loading session...\n'));
840
+ try {
841
+ const latest = await sessionManager.getLatestCheckpoint(sessionId);
842
+ if (!latest || !latest.state.bible) {
843
+ console.error(picocolors_1.default.red(`No existing GDD found for session: ${sessionId}`));
844
+ console.log(picocolors_1.default.dim('\nList all sessions with: gameforge sessions\n'));
845
+ return;
846
+ }
847
+ let bible = latest.state.bible;
848
+ console.log(picocolors_1.default.green(`āœ“ Loaded GDD: ${bible.meta?.title || 'Untitled'}`));
849
+ console.log(picocolors_1.default.dim(` Genre: ${bible.meta?.genre || 'Unknown'}`));
850
+ console.log(picocolors_1.default.dim(` Features: ${bible.features?.length || 0}`));
851
+ console.log(picocolors_1.default.dim(` Session: ${sessionId}\n`));
852
+ // Set up cost tracking with remaining budget from checkpoint
853
+ const budgetLimit = latest.state.budgetLimit || 5.0;
854
+ const costTracker = new costTracker_1.CostTracker(budgetLimit);
855
+ const modelSelector = new modelSelector_1.ModelSelector();
856
+ // Prompt for modification description
857
+ const response = await enquirer_1.default.prompt({
858
+ type: 'input',
859
+ name: 'modification',
860
+ message: 'Describe the changes you want to make:'
861
+ });
862
+ const modification = response.modification.trim();
863
+ if (!modification) {
864
+ console.log(picocolors_1.default.yellow('\nNo modification provided. Exiting.'));
865
+ return;
866
+ }
867
+ // Apply modification via Modifier agent
868
+ console.log(picocolors_1.default.cyan('\nšŸ”§ Applying modifications...\n'));
869
+ costTracker.startPhase('remediation'); // Use 'remediation' as closest phase type
870
+ const modifier = new Modifier_1.Modifier(apiKey, costTracker, modelSelector);
871
+ const modifySpinner = (0, ora_1.default)('Processing modification request...').start();
872
+ const result = await modifier.execute(bible, modification, (message) => {
873
+ modifySpinner.text = `Modifier: ${message}`;
874
+ });
875
+ modifySpinner.succeed('Modifications applied');
876
+ // End modification phase and show cost
877
+ const modificationCost = costTracker.endPhase();
878
+ if (modificationCost) {
879
+ costTracker.printPhaseSummary(modificationCost);
880
+ }
881
+ // Show summary of changes
882
+ if (result.changesSummary.length > 0) {
883
+ console.log(picocolors_1.default.green('\nChanges made:'));
884
+ result.changesSummary.forEach((change) => {
885
+ console.log(picocolors_1.default.dim(` āœ“ ${change}`));
886
+ });
887
+ }
888
+ if (result.errors.length > 0) {
889
+ console.log(picocolors_1.default.yellow('\nWarnings/Errors:'));
890
+ result.errors.forEach((err) => {
891
+ console.log(picocolors_1.default.dim(` ⚠ ${err}`));
892
+ });
893
+ }
894
+ bible = result.updatedBible;
895
+ // Confirm and regenerate documentation
896
+ const proceed = await confirmGate('\nRegenerate documentation with these changes?');
897
+ if (!proceed) {
898
+ console.log(picocolors_1.default.yellow('Changes discarded.'));
899
+ return;
900
+ }
901
+ // Run specialists to regenerate markdown
902
+ console.log(picocolors_1.default.cyan('\nšŸŽØ Regenerating documentation...\n'));
903
+ costTracker.startPhase('production');
904
+ const orchestrator = new Orchestrator_1.SpecialistOrchestrator();
905
+ const specialists = [
906
+ { name: 'Creative Direction', agent: new CreativeSpecialist_1.CreativeSpecialist(apiKey, costTracker, modelSelector) },
907
+ { name: 'Feature Specifications', agent: new FeatureSpecialist_1.FeatureSpecialist(apiKey, costTracker, modelSelector) },
908
+ { name: 'Entity Specifications', agent: new EntitySpecialist_1.EntitySpecialist(apiKey, costTracker, modelSelector) },
909
+ { name: 'Technical Specifications', agent: new TechSpecialist_1.TechSpecialist(apiKey, costTracker, modelSelector) }
910
+ ];
911
+ const markdownSections = await orchestrator.runAllSpecialists(bible, specialists, {
912
+ skipDisambiguation: true
913
+ });
914
+ // End specialists phase
915
+ const specialistsCost = costTracker.endPhase();
916
+ if (specialistsCost) {
917
+ costTracker.printPhaseSummary(specialistsCost);
918
+ }
919
+ // Run validation for gap analysis
920
+ console.log(picocolors_1.default.cyan('\nšŸ” Running validation...\n'));
921
+ costTracker.startPhase('validation');
922
+ const consistencyAgent = new Consistency_1.ConsistencyAgent();
923
+ const consistencyIssues = consistencyAgent.validate(bible);
924
+ const chaosSpinner = (0, ora_1.default)('Running Chaos Agent...').start();
925
+ const chaosAgent = new Chaos_1.Chaos(apiKey, costTracker, modelSelector);
926
+ const chaosIssues = await chaosAgent.execute(bible, (message) => {
927
+ chaosSpinner.text = `Chaos Agent: ${message}`;
928
+ });
929
+ chaosSpinner.succeed(`Found ${chaosIssues.length} design issues`);
930
+ const validationCost = costTracker.endPhase();
931
+ if (validationCost) {
932
+ costTracker.printPhaseSummary(validationCost);
933
+ }
934
+ // Save outputs
935
+ console.log(picocolors_1.default.cyan('\nšŸ’¾ Saving outputs...\n'));
936
+ const projectDir = await fileManager.initialize(sessionId);
937
+ await fileManager.saveGameBible(projectDir, bible);
938
+ await fileManager.saveCoverPage(projectDir, bible);
939
+ for (const [sectionName, content] of markdownSections) {
940
+ await fileManager.saveMarkdown(projectDir, sectionName, content);
941
+ }
942
+ await fileManager.saveGapAnalysis(projectDir, chaosIssues, consistencyIssues);
943
+ await fileManager.saveCombinedGDD(projectDir, bible, markdownSections, chaosIssues, consistencyIssues);
944
+ // Save checkpoint
945
+ const stateMachine = new StateMachine_1.StateMachine(budgetLimit);
946
+ stateMachine.restoreState(latest.state);
947
+ stateMachine.updateBible(bible);
948
+ await sessionManager.saveCheckpoint(sessionId, stateMachine.getState(), 'after_modification');
949
+ // Final output
950
+ const report = costTracker.getReport();
951
+ console.log(picocolors_1.default.green('\nāœ… Modification complete!\n'));
952
+ console.log(picocolors_1.default.dim(`Session ID: ${sessionId}`));
953
+ console.log(picocolors_1.default.dim(`Output directory: ${projectDir}`));
954
+ costTracker.printFinalBreakdown();
955
+ console.log(picocolors_1.default.dim(`Total cost: $${report.total.toFixed(4)} (${report.operations} operations)`));
956
+ console.log(picocolors_1.default.dim(`Budget remaining: $${report.remaining.toFixed(2)}\n`));
957
+ if (debugLogger_1.debugLogger.isEnabled()) {
958
+ console.log(picocolors_1.default.yellow(`šŸ› Debug log saved to: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
959
+ debugLogger_1.debugLogger.disable();
960
+ }
961
+ }
962
+ catch (error) {
963
+ console.error(picocolors_1.default.red('Error during modification:'), error);
964
+ if (debugLogger_1.debugLogger.isEnabled()) {
965
+ debugLogger_1.debugLogger.log('Modification flow failed: ' + String(error));
966
+ debugLogger_1.debugLogger.disable();
967
+ }
968
+ }
969
+ }
830
970
  //# sourceMappingURL=index.js.map