gameforge-cli 0.2.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.
- package/README.md +38 -5
- package/dist/agents/core/Architect.d.ts +4 -0
- package/dist/agents/core/Architect.d.ts.map +1 -1
- package/dist/agents/core/Architect.js +150 -0
- package/dist/agents/core/Architect.js.map +1 -1
- package/dist/agents/core/DocumentUpdater.d.ts +13 -0
- package/dist/agents/core/DocumentUpdater.d.ts.map +1 -0
- package/dist/agents/core/DocumentUpdater.js +165 -0
- package/dist/agents/core/DocumentUpdater.js.map +1 -0
- package/dist/agents/specialists/CreativeSpecialist.d.ts.map +1 -1
- package/dist/agents/specialists/CreativeSpecialist.js +68 -0
- package/dist/agents/specialists/CreativeSpecialist.js.map +1 -1
- package/dist/agents/specialists/EntitySpecialist.d.ts.map +1 -1
- package/dist/agents/specialists/EntitySpecialist.js +16 -0
- package/dist/agents/specialists/EntitySpecialist.js.map +1 -1
- package/dist/agents/specialists/FeatureSpecialist.d.ts +4 -0
- package/dist/agents/specialists/FeatureSpecialist.d.ts.map +1 -1
- package/dist/agents/specialists/FeatureSpecialist.js +61 -0
- package/dist/agents/specialists/FeatureSpecialist.js.map +1 -1
- package/dist/agents/specialists/TechSpecialist.d.ts.map +1 -1
- package/dist/agents/specialists/TechSpecialist.js +100 -0
- package/dist/agents/specialists/TechSpecialist.js.map +1 -1
- package/dist/index.js +316 -138
- package/dist/index.js.map +1 -1
- package/dist/utils/disambiguationHelper.d.ts.map +1 -1
- package/dist/utils/disambiguationHelper.js +18 -8
- package/dist/utils/disambiguationHelper.js.map +1 -1
- package/dist/utils/fileManager.d.ts +7 -0
- package/dist/utils/fileManager.d.ts.map +1 -1
- package/dist/utils/fileManager.js +47 -0
- package/dist/utils/fileManager.js.map +1 -1
- package/dist/utils/issueReviewer.d.ts +4 -0
- package/dist/utils/issueReviewer.d.ts.map +1 -1
- package/dist/utils/issueReviewer.js +47 -0
- package/dist/utils/issueReviewer.js.map +1 -1
- package/dist/utils/pdfGenerator.d.ts +12 -0
- package/dist/utils/pdfGenerator.d.ts.map +1 -0
- package/dist/utils/pdfGenerator.js +341 -0
- package/dist/utils/pdfGenerator.js.map +1 -0
- package/package.json +20 -15
package/dist/index.js
CHANGED
|
@@ -31,7 +31,18 @@ const Orchestrator_1 = require("./core/Orchestrator");
|
|
|
31
31
|
const debugLogger_1 = require("./utils/debugLogger");
|
|
32
32
|
const issueReviewer_1 = require("./utils/issueReviewer");
|
|
33
33
|
const Modifier_1 = require("./agents/core/Modifier");
|
|
34
|
+
const DocumentUpdater_1 = require("./agents/core/DocumentUpdater");
|
|
34
35
|
dotenv_1.default.config();
|
|
36
|
+
/**
|
|
37
|
+
* Creates a clickable file link for terminals that support OSC 8 hyperlinks.
|
|
38
|
+
* Falls back to plain text in unsupported terminals.
|
|
39
|
+
*/
|
|
40
|
+
function fileLink(filePath, displayText) {
|
|
41
|
+
const absolutePath = path_1.default.resolve(filePath);
|
|
42
|
+
const fileUrl = `file://${absolutePath}`;
|
|
43
|
+
const text = displayText || filePath;
|
|
44
|
+
return `\x1b]8;;${fileUrl}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
45
|
+
}
|
|
35
46
|
const program = new commander_1.Command();
|
|
36
47
|
program
|
|
37
48
|
.name('gameforge')
|
|
@@ -168,11 +179,6 @@ async function runCreationFlow(options) {
|
|
|
168
179
|
console.log(picocolors_1.default.cyan('\nš Concept Review\n'));
|
|
169
180
|
stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_CONCEPT);
|
|
170
181
|
showConceptBrief(transcript);
|
|
171
|
-
const proceed1 = await confirmGate('Proceed to architecture phase?');
|
|
172
|
-
if (!proceed1) {
|
|
173
|
-
console.log(picocolors_1.default.yellow('Exiting...'));
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
182
|
await sessionManager.saveCheckpoint(sessionId, stateMachine.getState(), 'after_discovery');
|
|
177
183
|
// Phase 2: Architecture
|
|
178
184
|
console.log(picocolors_1.default.cyan('\nšļø Phase 2: Architecture\n'));
|
|
@@ -180,8 +186,26 @@ async function runCreationFlow(options) {
|
|
|
180
186
|
costTracker.startPhase('architecture');
|
|
181
187
|
const spinner = (0, ora_1.default)('Generating Game Bible...').start();
|
|
182
188
|
const architect = new Architect_1.Architect(apiKey, costTracker, modelSelector);
|
|
189
|
+
// Provide spinner control so the agent can pause during user prompts
|
|
190
|
+
let spinnerPaused = false;
|
|
191
|
+
architect.setSpinnerControl({
|
|
192
|
+
pause: () => {
|
|
193
|
+
if (!spinnerPaused) {
|
|
194
|
+
spinner.stop();
|
|
195
|
+
spinnerPaused = true;
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
resume: (text) => {
|
|
199
|
+
if (spinnerPaused) {
|
|
200
|
+
spinner.start(text || spinner.text);
|
|
201
|
+
spinnerPaused = false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
});
|
|
183
205
|
let bible = await architect.execute(transcript, (message) => {
|
|
184
|
-
|
|
206
|
+
if (!spinnerPaused) {
|
|
207
|
+
spinner.text = `Generating Game Bible: ${message}`;
|
|
208
|
+
}
|
|
185
209
|
});
|
|
186
210
|
// Merge template features if a template was selected
|
|
187
211
|
if (selectedTemplate && selectedTemplate.partialBible.features) {
|
|
@@ -202,11 +226,6 @@ async function runCreationFlow(options) {
|
|
|
202
226
|
stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_DATA);
|
|
203
227
|
console.log(picocolors_1.default.green(`ā Generated ${bible.features.length} features`));
|
|
204
228
|
console.log(picocolors_1.default.green(`ā Generated ${bible.gameObjects.length} game objects`));
|
|
205
|
-
const proceed2 = await confirmGate('Proceed to production phase?');
|
|
206
|
-
if (!proceed2) {
|
|
207
|
-
console.log(picocolors_1.default.yellow('Exiting...'));
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
229
|
// Phase 3: Production (Specialists)
|
|
211
230
|
console.log(picocolors_1.default.cyan('\nšØ Phase 3: Production\n'));
|
|
212
231
|
stateMachine.transition(StateMachine_1.GameForgePhase.PRODUCTION);
|
|
@@ -236,8 +255,26 @@ async function runCreationFlow(options) {
|
|
|
236
255
|
// Run chaos analysis
|
|
237
256
|
let chaosSpinner = (0, ora_1.default)('Running Chaos Agent...').start();
|
|
238
257
|
const chaosAgent = new Chaos_1.Chaos(apiKey, costTracker, modelSelector);
|
|
258
|
+
// Provide spinner control so the agent can pause during user prompts
|
|
259
|
+
let chaosSpinnerPaused = false;
|
|
260
|
+
chaosAgent.setSpinnerControl({
|
|
261
|
+
pause: () => {
|
|
262
|
+
if (!chaosSpinnerPaused) {
|
|
263
|
+
chaosSpinner.stop();
|
|
264
|
+
chaosSpinnerPaused = true;
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
resume: (text) => {
|
|
268
|
+
if (chaosSpinnerPaused) {
|
|
269
|
+
chaosSpinner.start(text || chaosSpinner.text);
|
|
270
|
+
chaosSpinnerPaused = false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
});
|
|
239
274
|
let currentChaosIssues = await chaosAgent.execute(bible, (message) => {
|
|
240
|
-
|
|
275
|
+
if (!chaosSpinnerPaused) {
|
|
276
|
+
chaosSpinner.text = `Chaos Agent: ${message}`;
|
|
277
|
+
}
|
|
241
278
|
});
|
|
242
279
|
chaosSpinner.succeed(`Found ${currentChaosIssues.length} design issues`);
|
|
243
280
|
// End validation phase and show cost
|
|
@@ -248,6 +285,8 @@ async function runCreationFlow(options) {
|
|
|
248
285
|
// Gate 3: Review Issues One by One
|
|
249
286
|
stateMachine.transition(StateMachine_1.GameForgePhase.REVIEW_ISSUES);
|
|
250
287
|
let fixesApplied = false;
|
|
288
|
+
let gapAnalysisConsistencyIssues = currentConsistencyIssues;
|
|
289
|
+
let gapAnalysisChaosIssues = currentChaosIssues;
|
|
251
290
|
const totalIssues = currentConsistencyIssues.length + currentChaosIssues.length;
|
|
252
291
|
if (totalIssues > 0) {
|
|
253
292
|
console.log(picocolors_1.default.yellow(`\nā ļø ${totalIssues} issues found. Let's review them one by one.\n`));
|
|
@@ -289,6 +328,10 @@ async function runCreationFlow(options) {
|
|
|
289
328
|
// Update bible
|
|
290
329
|
bible = result.updatedBible;
|
|
291
330
|
stateMachine.updateBible(bible);
|
|
331
|
+
// Filter issues for gap analysis: only show unfixable issues
|
|
332
|
+
const filtered = (0, issueReviewer_1.filterIssuesForGapAnalysis)(currentConsistencyIssues, currentChaosIssues, reviewed, result.unfixableIssues);
|
|
333
|
+
gapAnalysisConsistencyIssues = filtered.consistencyIssues;
|
|
334
|
+
gapAnalysisChaosIssues = filtered.chaosIssues;
|
|
292
335
|
}
|
|
293
336
|
catch (error) {
|
|
294
337
|
remediationSpinner.fail('Remediation failed');
|
|
@@ -298,23 +341,36 @@ async function runCreationFlow(options) {
|
|
|
298
341
|
console.log(picocolors_1.default.yellow('Exiting...'));
|
|
299
342
|
return;
|
|
300
343
|
}
|
|
344
|
+
// Remediation failed - show all issues that were marked for fixing as unfixable
|
|
345
|
+
const filtered = (0, issueReviewer_1.filterIssuesForGapAnalysis)(currentConsistencyIssues, currentChaosIssues, reviewed, [] // Treat all toFix issues as unfixable since remediation failed
|
|
346
|
+
);
|
|
347
|
+
// Show the issues that were marked to fix since they remain unfixed
|
|
348
|
+
gapAnalysisConsistencyIssues = reviewed.toFix
|
|
349
|
+
.filter(r => r.issueType === 'consistency')
|
|
350
|
+
.map(r => r.issue);
|
|
351
|
+
gapAnalysisChaosIssues = reviewed.toFix
|
|
352
|
+
.filter(r => r.issueType === 'chaos')
|
|
353
|
+
.map(r => r.issue);
|
|
301
354
|
}
|
|
302
355
|
}
|
|
356
|
+
else {
|
|
357
|
+
// User declined to run remediation - show issues marked to fix as they remain unfixed
|
|
358
|
+
gapAnalysisConsistencyIssues = reviewed.toFix
|
|
359
|
+
.filter(r => r.issueType === 'consistency')
|
|
360
|
+
.map(r => r.issue);
|
|
361
|
+
gapAnalysisChaosIssues = reviewed.toFix
|
|
362
|
+
.filter(r => r.issueType === 'chaos')
|
|
363
|
+
.map(r => r.issue);
|
|
364
|
+
}
|
|
303
365
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
return;
|
|
366
|
+
else {
|
|
367
|
+
// All issues were ignored, don't show any in gap analysis
|
|
368
|
+
gapAnalysisConsistencyIssues = [];
|
|
369
|
+
gapAnalysisChaosIssues = [];
|
|
309
370
|
}
|
|
310
371
|
}
|
|
311
372
|
else {
|
|
312
373
|
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;
|
|
317
|
-
}
|
|
318
374
|
}
|
|
319
375
|
// If fixes were applied, re-run specialists to regenerate markdown
|
|
320
376
|
if (fixesApplied) {
|
|
@@ -324,16 +380,12 @@ async function runCreationFlow(options) {
|
|
|
324
380
|
// Save all outputs
|
|
325
381
|
console.log(picocolors_1.default.cyan('\nš¾ Saving outputs...\n'));
|
|
326
382
|
const projectDir = await fileManager.initialize(projectId);
|
|
383
|
+
// Save game bible JSON
|
|
327
384
|
await fileManager.saveGameBible(projectDir, bible);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
}
|
|
333
|
-
await fileManager.saveGapAnalysis(projectDir, currentChaosIssues, currentConsistencyIssues);
|
|
334
|
-
// Save combined GDD
|
|
335
|
-
await fileManager.saveCombinedGDD(projectDir, bible, markdownSections, currentChaosIssues, currentConsistencyIssues);
|
|
336
|
-
console.log(picocolors_1.default.green('ā Saved combined Game Design Document'));
|
|
385
|
+
console.log(picocolors_1.default.green('ā Saved game_bible.json'));
|
|
386
|
+
// Save combined GDD (markdown)
|
|
387
|
+
await fileManager.saveCombinedGDD(projectDir, bible, markdownSections, gapAnalysisChaosIssues, gapAnalysisConsistencyIssues);
|
|
388
|
+
console.log(picocolors_1.default.green('ā Saved Game_Design_Document.md'));
|
|
337
389
|
// Complete
|
|
338
390
|
stateMachine.transition(StateMachine_1.GameForgePhase.COMPLETE);
|
|
339
391
|
// Save final checkpoint
|
|
@@ -343,15 +395,31 @@ async function runCreationFlow(options) {
|
|
|
343
395
|
console.log(picocolors_1.default.green('\nā
Complete!\n'));
|
|
344
396
|
console.log(picocolors_1.default.dim(`Session ID: ${sessionId}`));
|
|
345
397
|
console.log(picocolors_1.default.dim(`Output directory: ${projectDir}`));
|
|
398
|
+
console.log(picocolors_1.default.cyan('\nš Generated Files:'));
|
|
399
|
+
console.log(picocolors_1.default.green(` ā ${fileLink(path_1.default.join(projectDir, 'game_bible.json'))}`));
|
|
400
|
+
console.log(picocolors_1.default.green(` ā ${fileLink(path_1.default.join(projectDir, 'Game_Design_Document.md'))}`));
|
|
346
401
|
// Show per-phase cost breakdown
|
|
347
402
|
costTracker.printFinalBreakdown();
|
|
348
403
|
console.log(picocolors_1.default.dim(`Total cost: $${report.total.toFixed(4)} (${report.operations} operations)`));
|
|
349
404
|
console.log(picocolors_1.default.dim(`Budget remaining: $${report.remaining.toFixed(2)}\n`));
|
|
405
|
+
// Ask if user wants to make changes
|
|
406
|
+
const wantChanges = await confirmGate('Would you like to make any changes to the design?');
|
|
407
|
+
if (wantChanges) {
|
|
408
|
+
try {
|
|
409
|
+
await runModifyFlowWithBible(sessionId, bible, costTracker, modelSelector, fileManager, sessionManager, stateMachine, options);
|
|
410
|
+
}
|
|
411
|
+
catch (modifyError) {
|
|
412
|
+
console.error(picocolors_1.default.red('Error during modification:'), modifyError);
|
|
413
|
+
if (debugLogger_1.debugLogger.isEnabled()) {
|
|
414
|
+
debugLogger_1.debugLogger.logError('ModifyFlowFromCreate', modifyError);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
350
418
|
if (debugLogger_1.debugLogger.isEnabled()) {
|
|
351
419
|
console.log(picocolors_1.default.yellow(`š Debug log saved to: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
|
|
352
420
|
debugLogger_1.debugLogger.disable();
|
|
353
421
|
}
|
|
354
|
-
console.log(picocolors_1.default.dim('
|
|
422
|
+
console.log(picocolors_1.default.dim('\nView this session anytime with:'));
|
|
355
423
|
console.log(picocolors_1.default.dim(` gameforge session ${sessionId}\n`));
|
|
356
424
|
}
|
|
357
425
|
catch (error) {
|
|
@@ -625,7 +693,27 @@ async function resumeFlow(idOrCheckpoint, options = {}) {
|
|
|
625
693
|
}
|
|
626
694
|
const architect = new Architect_1.Architect(apiKey, costTracker, modelSelector);
|
|
627
695
|
const spinner = (0, ora_1.default)('Generating Game Bible...').start();
|
|
628
|
-
|
|
696
|
+
// Provide spinner control so the agent can pause during user prompts
|
|
697
|
+
let spinnerPaused = false;
|
|
698
|
+
architect.setSpinnerControl({
|
|
699
|
+
pause: () => {
|
|
700
|
+
if (!spinnerPaused) {
|
|
701
|
+
spinner.stop();
|
|
702
|
+
spinnerPaused = true;
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
resume: (text) => {
|
|
706
|
+
if (spinnerPaused) {
|
|
707
|
+
spinner.start(text || spinner.text);
|
|
708
|
+
spinnerPaused = false;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
bible = await architect.execute(savedState.transcript, (message) => {
|
|
713
|
+
if (!spinnerPaused) {
|
|
714
|
+
spinner.text = `Generating Game Bible: ${message}`;
|
|
715
|
+
}
|
|
716
|
+
});
|
|
629
717
|
spinner.succeed('Game Bible generated');
|
|
630
718
|
}
|
|
631
719
|
stateMachine.updateBible(bible);
|
|
@@ -703,7 +791,27 @@ async function continueToProduction(stateMachine, bible, apiKey, costTracker, mo
|
|
|
703
791
|
console.log(picocolors_1.default.yellow(`Found ${currentConsistencyIssues.length} consistency issues`));
|
|
704
792
|
let chaosSpinner = (0, ora_1.default)('Running Chaos Agent...').start();
|
|
705
793
|
const chaosAgent = new Chaos_1.Chaos(apiKey, costTracker, modelSelector);
|
|
706
|
-
|
|
794
|
+
// Provide spinner control so the agent can pause during user prompts
|
|
795
|
+
let chaosSpinnerPaused = false;
|
|
796
|
+
chaosAgent.setSpinnerControl({
|
|
797
|
+
pause: () => {
|
|
798
|
+
if (!chaosSpinnerPaused) {
|
|
799
|
+
chaosSpinner.stop();
|
|
800
|
+
chaosSpinnerPaused = true;
|
|
801
|
+
}
|
|
802
|
+
},
|
|
803
|
+
resume: (text) => {
|
|
804
|
+
if (chaosSpinnerPaused) {
|
|
805
|
+
chaosSpinner.start(text || chaosSpinner.text);
|
|
806
|
+
chaosSpinnerPaused = false;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
let currentChaosIssues = await chaosAgent.execute(bible, (message) => {
|
|
811
|
+
if (!chaosSpinnerPaused) {
|
|
812
|
+
chaosSpinner.text = `Chaos Agent: ${message}`;
|
|
813
|
+
}
|
|
814
|
+
});
|
|
707
815
|
chaosSpinner.succeed(`Found ${currentChaosIssues.length} design issues`);
|
|
708
816
|
// End validation phase and show cost
|
|
709
817
|
const validationCost = costTracker.endPhase();
|
|
@@ -789,15 +897,12 @@ async function continueToProduction(stateMachine, bible, apiKey, costTracker, mo
|
|
|
789
897
|
// Save all outputs
|
|
790
898
|
console.log(picocolors_1.default.cyan('\nš¾ Saving outputs...\n'));
|
|
791
899
|
const projectDir = await fileManager.initialize(projectId);
|
|
900
|
+
// Save game bible JSON
|
|
792
901
|
await fileManager.saveGameBible(projectDir, bible);
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
const filename = name.replace(/\s+/g, '_') + '.md';
|
|
796
|
-
await fileManager.saveMarkdown(projectDir, filename, content);
|
|
797
|
-
}
|
|
798
|
-
await fileManager.saveGapAnalysis(projectDir, currentChaosIssues, currentConsistencyIssues);
|
|
799
|
-
// Save combined GDD
|
|
902
|
+
console.log(picocolors_1.default.green('ā Saved game_bible.json'));
|
|
903
|
+
// Save combined GDD (markdown)
|
|
800
904
|
await fileManager.saveCombinedGDD(projectDir, bible, markdownSections, currentChaosIssues, currentConsistencyIssues);
|
|
905
|
+
console.log(picocolors_1.default.green('ā Saved Game_Design_Document.md'));
|
|
801
906
|
stateMachine.transition(StateMachine_1.GameForgePhase.COMPLETE);
|
|
802
907
|
// Save final checkpoint
|
|
803
908
|
await sessionManager.saveCheckpoint(sessionId, stateMachine.getState(), 'complete');
|
|
@@ -844,7 +949,7 @@ async function runModifyFlow(sessionId, options) {
|
|
|
844
949
|
console.log(picocolors_1.default.dim('\nList all sessions with: gameforge sessions\n'));
|
|
845
950
|
return;
|
|
846
951
|
}
|
|
847
|
-
|
|
952
|
+
const bible = latest.state.bible;
|
|
848
953
|
console.log(picocolors_1.default.green(`ā Loaded GDD: ${bible.meta?.title || 'Untitled'}`));
|
|
849
954
|
console.log(picocolors_1.default.dim(` Genre: ${bible.meta?.genre || 'Unknown'}`));
|
|
850
955
|
console.log(picocolors_1.default.dim(` Features: ${bible.features?.length || 0}`));
|
|
@@ -853,54 +958,103 @@ async function runModifyFlow(sessionId, options) {
|
|
|
853
958
|
const budgetLimit = latest.state.budgetLimit || 5.0;
|
|
854
959
|
const costTracker = new costTracker_1.CostTracker(budgetLimit);
|
|
855
960
|
const modelSelector = new modelSelector_1.ModelSelector();
|
|
856
|
-
//
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
961
|
+
// Create state machine for checkpoints
|
|
962
|
+
const stateMachine = new StateMachine_1.StateMachine(budgetLimit);
|
|
963
|
+
stateMachine.restoreState(latest.state);
|
|
964
|
+
// Run the modification loop
|
|
965
|
+
await runModifyFlowWithBible(sessionId, bible, costTracker, modelSelector, fileManager, sessionManager, stateMachine, options);
|
|
966
|
+
}
|
|
967
|
+
catch (error) {
|
|
968
|
+
console.error(picocolors_1.default.red('Error during modification:'), error);
|
|
969
|
+
if (debugLogger_1.debugLogger.isEnabled()) {
|
|
970
|
+
debugLogger_1.debugLogger.log('Modification flow failed: ' + String(error));
|
|
971
|
+
debugLogger_1.debugLogger.disable();
|
|
866
972
|
}
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
async function runModifyFlowWithBible(sessionId, bible, costTracker, modelSelector, fileManager, sessionManager, stateMachine, options) {
|
|
976
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
977
|
+
// Prompt for modification description
|
|
978
|
+
const response = await enquirer_1.default.prompt({
|
|
979
|
+
type: 'input',
|
|
980
|
+
name: 'modification',
|
|
981
|
+
message: 'Describe the changes you want to make:'
|
|
982
|
+
});
|
|
983
|
+
const modification = response.modification.trim();
|
|
984
|
+
if (!modification) {
|
|
985
|
+
console.log(picocolors_1.default.yellow('\nNo modification provided.'));
|
|
986
|
+
showFinalSummary(costTracker, sessionId, options);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
// Apply modification via Modifier agent
|
|
990
|
+
console.log(picocolors_1.default.cyan('\nš§ Applying modifications...\n'));
|
|
991
|
+
costTracker.startPhase('remediation');
|
|
992
|
+
const modifier = new Modifier_1.Modifier(apiKey, costTracker, modelSelector);
|
|
993
|
+
const modifySpinner = (0, ora_1.default)('Processing modification request...').start();
|
|
994
|
+
const result = await modifier.execute(bible, modification, (message) => {
|
|
995
|
+
modifySpinner.text = `Modifier: ${message}`;
|
|
996
|
+
});
|
|
997
|
+
modifySpinner.succeed('Modifications applied');
|
|
998
|
+
// End modification phase and show cost
|
|
999
|
+
const modificationCost = costTracker.endPhase();
|
|
1000
|
+
if (modificationCost) {
|
|
1001
|
+
costTracker.printPhaseSummary(modificationCost);
|
|
1002
|
+
}
|
|
1003
|
+
// Show summary of changes
|
|
1004
|
+
if (result.changesSummary.length > 0) {
|
|
1005
|
+
console.log(picocolors_1.default.green('\nChanges made:'));
|
|
1006
|
+
result.changesSummary.forEach((change) => {
|
|
1007
|
+
console.log(picocolors_1.default.dim(` ā ${change}`));
|
|
874
1008
|
});
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1009
|
+
}
|
|
1010
|
+
if (result.errors.length > 0) {
|
|
1011
|
+
console.log(picocolors_1.default.yellow('\nWarnings/Errors:'));
|
|
1012
|
+
result.errors.forEach((err) => {
|
|
1013
|
+
console.log(picocolors_1.default.dim(` ā ${err}`));
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
const updatedBible = result.updatedBible;
|
|
1017
|
+
// Try to use targeted document updates instead of full regeneration
|
|
1018
|
+
console.log(picocolors_1.default.cyan('\nšØ Updating documentation...\n'));
|
|
1019
|
+
costTracker.startPhase('production');
|
|
1020
|
+
const projectDir = await fileManager.initialize(sessionId);
|
|
1021
|
+
const existingGDD = await fileManager.readGDD(projectDir);
|
|
1022
|
+
let markdownSections = null;
|
|
1023
|
+
let updatedGDD = null;
|
|
1024
|
+
if (existingGDD && result.changesSummary.length > 0) {
|
|
1025
|
+
// Use targeted document updates - preserve existing content
|
|
1026
|
+
console.log(picocolors_1.default.dim(' Using targeted updates to preserve existing content...\n'));
|
|
1027
|
+
const documentUpdater = new DocumentUpdater_1.DocumentUpdater(apiKey, costTracker, modelSelector);
|
|
1028
|
+
const updateSpinner = (0, ora_1.default)('Applying targeted document updates...').start();
|
|
1029
|
+
const updateResult = await documentUpdater.execute(existingGDD, result.changesSummary, updatedBible, (message) => {
|
|
1030
|
+
updateSpinner.text = `Document Updater: ${message}`;
|
|
1031
|
+
});
|
|
1032
|
+
// Require both: no errors AND document was actually modified
|
|
1033
|
+
const updateSuccessful = updateResult.errors.length === 0 && updateResult.updatedDocument !== existingGDD;
|
|
1034
|
+
if (updateSuccessful) {
|
|
1035
|
+
updateSpinner.succeed(`Updated ${updateResult.sectionsModified.length} sections`);
|
|
1036
|
+
updatedGDD = updateResult.updatedDocument;
|
|
1037
|
+
if (updateResult.sectionsModified.length > 0) {
|
|
1038
|
+
console.log(picocolors_1.default.dim(' Modified sections:'));
|
|
1039
|
+
updateResult.sectionsModified.forEach(section => {
|
|
1040
|
+
console.log(picocolors_1.default.dim(` ⢠${section}`));
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
893
1043
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1044
|
+
else {
|
|
1045
|
+
// Log specific reason for fallback
|
|
1046
|
+
if (updateResult.errors.length > 0) {
|
|
1047
|
+
updateSpinner.warn(`Targeted update failed: ${updateResult.errors[0]}`);
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
updateSpinner.warn('Targeted update produced no changes');
|
|
1051
|
+
}
|
|
1052
|
+
console.log(picocolors_1.default.yellow(' Falling back to full document regeneration...\n'));
|
|
900
1053
|
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1054
|
+
}
|
|
1055
|
+
// Fall back to full regeneration if targeted update failed or no existing GDD
|
|
1056
|
+
if (!updatedGDD) {
|
|
1057
|
+
console.log(picocolors_1.default.dim(' Regenerating full documentation...\n'));
|
|
904
1058
|
const orchestrator = new Orchestrator_1.SpecialistOrchestrator();
|
|
905
1059
|
const specialists = [
|
|
906
1060
|
{ name: 'Creative Direction', agent: new CreativeSpecialist_1.CreativeSpecialist(apiKey, costTracker, modelSelector) },
|
|
@@ -908,63 +1062,87 @@ async function runModifyFlow(sessionId, options) {
|
|
|
908
1062
|
{ name: 'Entity Specifications', agent: new EntitySpecialist_1.EntitySpecialist(apiKey, costTracker, modelSelector) },
|
|
909
1063
|
{ name: 'Technical Specifications', agent: new TechSpecialist_1.TechSpecialist(apiKey, costTracker, modelSelector) }
|
|
910
1064
|
];
|
|
911
|
-
|
|
1065
|
+
markdownSections = await orchestrator.runAllSpecialists(updatedBible, specialists, {
|
|
912
1066
|
skipDisambiguation: true
|
|
913
1067
|
});
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1068
|
+
}
|
|
1069
|
+
// End production phase
|
|
1070
|
+
const productionCost = costTracker.endPhase();
|
|
1071
|
+
if (productionCost) {
|
|
1072
|
+
costTracker.printPhaseSummary(productionCost);
|
|
1073
|
+
}
|
|
1074
|
+
// Run validation for gap analysis
|
|
1075
|
+
console.log(picocolors_1.default.cyan('\nš Running validation...\n'));
|
|
1076
|
+
costTracker.startPhase('validation');
|
|
1077
|
+
const consistencyAgent = new Consistency_1.ConsistencyAgent();
|
|
1078
|
+
const consistencyIssues = consistencyAgent.validate(updatedBible);
|
|
1079
|
+
const chaosSpinner = (0, ora_1.default)('Running Chaos Agent...').start();
|
|
1080
|
+
const chaosAgent = new Chaos_1.Chaos(apiKey, costTracker, modelSelector);
|
|
1081
|
+
const chaosIssues = await chaosAgent.execute(updatedBible, (message) => {
|
|
1082
|
+
chaosSpinner.text = `Chaos Agent: ${message}`;
|
|
1083
|
+
});
|
|
1084
|
+
chaosSpinner.succeed(`Found ${chaosIssues.length} design issues`);
|
|
1085
|
+
const validationCost = costTracker.endPhase();
|
|
1086
|
+
if (validationCost) {
|
|
1087
|
+
costTracker.printPhaseSummary(validationCost);
|
|
1088
|
+
}
|
|
1089
|
+
// Save outputs
|
|
1090
|
+
console.log(picocolors_1.default.cyan('\nš¾ Saving outputs...\n'));
|
|
1091
|
+
// Save game bible JSON
|
|
1092
|
+
await fileManager.saveGameBible(projectDir, updatedBible);
|
|
1093
|
+
console.log(picocolors_1.default.green('ā Saved game_bible.json'));
|
|
1094
|
+
// Save GDD - either the updated version or regenerated
|
|
1095
|
+
if (updatedGDD) {
|
|
1096
|
+
// Update gap analysis section in the targeted-update GDD before saving
|
|
1097
|
+
const gddWithGapAnalysis = fileManager.updateGapAnalysisInDocument(updatedGDD, chaosIssues, consistencyIssues, sessionId);
|
|
1098
|
+
await fileManager.saveUpdatedGDD(projectDir, gddWithGapAnalysis);
|
|
1099
|
+
console.log(picocolors_1.default.green('ā Saved Game_Design_Document.md (targeted update)'));
|
|
1100
|
+
}
|
|
1101
|
+
else if (markdownSections) {
|
|
1102
|
+
// Save the fully regenerated GDD
|
|
1103
|
+
await fileManager.saveCombinedGDD(projectDir, updatedBible, markdownSections, chaosIssues, consistencyIssues);
|
|
1104
|
+
console.log(picocolors_1.default.green('ā Saved Game_Design_Document.md (full regeneration)'));
|
|
1105
|
+
}
|
|
1106
|
+
// Save checkpoint
|
|
1107
|
+
stateMachine.updateBible(updatedBible);
|
|
1108
|
+
await sessionManager.saveCheckpoint(sessionId, stateMachine.getState(), 'after_modification');
|
|
1109
|
+
// Show iteration summary (without final breakdown)
|
|
1110
|
+
const report = costTracker.getReport();
|
|
1111
|
+
console.log(picocolors_1.default.green('\nā
Modification complete!\n'));
|
|
1112
|
+
console.log(picocolors_1.default.dim(`Session ID: ${sessionId}`));
|
|
1113
|
+
console.log(picocolors_1.default.dim(`Output directory: ${projectDir}`));
|
|
1114
|
+
console.log(picocolors_1.default.cyan('\nš Generated Files:'));
|
|
1115
|
+
console.log(picocolors_1.default.green(` ā ${fileLink(path_1.default.join(projectDir, 'game_bible.json'))}`));
|
|
1116
|
+
console.log(picocolors_1.default.green(` ā ${fileLink(path_1.default.join(projectDir, 'Game_Design_Document.md'))}`));
|
|
1117
|
+
console.log(picocolors_1.default.dim(`\nIteration cost: $${report.total.toFixed(4)} (${report.operations} operations)`));
|
|
1118
|
+
console.log(picocolors_1.default.dim(`Budget remaining: $${report.remaining.toFixed(2)}\n`));
|
|
1119
|
+
// Ask if user wants to make more changes
|
|
1120
|
+
const moreChanges = await confirmGate('Would you like to make more changes?');
|
|
1121
|
+
if (moreChanges) {
|
|
1122
|
+
// Loop back for more modifications with try-catch
|
|
1123
|
+
try {
|
|
1124
|
+
await runModifyFlowWithBible(sessionId, updatedBible, costTracker, modelSelector, fileManager, sessionManager, stateMachine, options);
|
|
941
1125
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
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();
|
|
1126
|
+
catch (error) {
|
|
1127
|
+
console.error(picocolors_1.default.red('Error during modification:'), error);
|
|
1128
|
+
if (debugLogger_1.debugLogger.isEnabled()) {
|
|
1129
|
+
debugLogger_1.debugLogger.logError('ModifyFlowRecursive', error);
|
|
1130
|
+
}
|
|
1131
|
+
showFinalSummary(costTracker, sessionId, options);
|
|
960
1132
|
}
|
|
961
1133
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1134
|
+
else {
|
|
1135
|
+
showFinalSummary(costTracker, sessionId, options);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
function showFinalSummary(costTracker, sessionId, options) {
|
|
1139
|
+
// Show final cost breakdown only once when exiting the loop
|
|
1140
|
+
costTracker.printFinalBreakdown();
|
|
1141
|
+
if (debugLogger_1.debugLogger.isEnabled()) {
|
|
1142
|
+
console.log(picocolors_1.default.yellow(`š Debug log saved to: ${debugLogger_1.debugLogger.getLogFilePath()}\n`));
|
|
1143
|
+
debugLogger_1.debugLogger.disable();
|
|
968
1144
|
}
|
|
1145
|
+
console.log(picocolors_1.default.dim('\nView this session anytime with:'));
|
|
1146
|
+
console.log(picocolors_1.default.dim(` gameforge session ${sessionId}\n`));
|
|
969
1147
|
}
|
|
970
1148
|
//# sourceMappingURL=index.js.map
|