popeye-cli 1.3.0 → 1.4.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 (79) hide show
  1. package/README.md +115 -17
  2. package/dist/adapters/gemini.d.ts +2 -2
  3. package/dist/adapters/gemini.d.ts.map +1 -1
  4. package/dist/cli/index.d.ts +1 -0
  5. package/dist/cli/index.d.ts.map +1 -1
  6. package/dist/cli/index.js +5 -2
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/interactive.d.ts.map +1 -1
  9. package/dist/cli/interactive.js +307 -22
  10. package/dist/cli/interactive.js.map +1 -1
  11. package/dist/config/index.d.ts +2 -0
  12. package/dist/config/index.d.ts.map +1 -1
  13. package/dist/config/schema.d.ts +4 -0
  14. package/dist/config/schema.d.ts.map +1 -1
  15. package/dist/config/schema.js +2 -1
  16. package/dist/config/schema.js.map +1 -1
  17. package/dist/generators/all.d.ts +30 -0
  18. package/dist/generators/all.d.ts.map +1 -1
  19. package/dist/generators/all.js +5 -5
  20. package/dist/generators/all.js.map +1 -1
  21. package/dist/types/consensus.d.ts +10 -20
  22. package/dist/types/consensus.d.ts.map +1 -1
  23. package/dist/types/consensus.js +8 -3
  24. package/dist/types/consensus.js.map +1 -1
  25. package/dist/types/index.d.ts +2 -2
  26. package/dist/types/index.d.ts.map +1 -1
  27. package/dist/types/index.js +2 -2
  28. package/dist/types/index.js.map +1 -1
  29. package/dist/types/project.d.ts +15 -16
  30. package/dist/types/project.d.ts.map +1 -1
  31. package/dist/types/project.js +15 -8
  32. package/dist/types/project.js.map +1 -1
  33. package/dist/types/workflow.d.ts +2 -0
  34. package/dist/types/workflow.d.ts.map +1 -1
  35. package/dist/types/workflow.js +2 -1
  36. package/dist/types/workflow.js.map +1 -1
  37. package/dist/upgrade/context.d.ts +37 -0
  38. package/dist/upgrade/context.d.ts.map +1 -0
  39. package/dist/upgrade/context.js +284 -0
  40. package/dist/upgrade/context.js.map +1 -0
  41. package/dist/upgrade/handlers.d.ts +103 -0
  42. package/dist/upgrade/handlers.d.ts.map +1 -0
  43. package/dist/upgrade/handlers.js +384 -0
  44. package/dist/upgrade/handlers.js.map +1 -0
  45. package/dist/upgrade/index.d.ts +26 -0
  46. package/dist/upgrade/index.d.ts.map +1 -0
  47. package/dist/upgrade/index.js +194 -0
  48. package/dist/upgrade/index.js.map +1 -0
  49. package/dist/upgrade/transitions.d.ts +34 -0
  50. package/dist/upgrade/transitions.d.ts.map +1 -0
  51. package/dist/upgrade/transitions.js +56 -0
  52. package/dist/upgrade/transitions.js.map +1 -0
  53. package/dist/workflow/plan-mode.d.ts.map +1 -1
  54. package/dist/workflow/plan-mode.js +41 -5
  55. package/dist/workflow/plan-mode.js.map +1 -1
  56. package/dist/workflow/task-workflow.d.ts.map +1 -1
  57. package/dist/workflow/task-workflow.js +3 -2
  58. package/dist/workflow/task-workflow.js.map +1 -1
  59. package/package.json +1 -1
  60. package/src/adapters/gemini.ts +2 -2
  61. package/src/cli/index.ts +5 -2
  62. package/src/cli/interactive.ts +353 -23
  63. package/src/config/schema.ts +2 -1
  64. package/src/generators/all.ts +5 -5
  65. package/src/types/consensus.ts +11 -5
  66. package/src/types/index.ts +2 -0
  67. package/src/types/project.ts +18 -9
  68. package/src/types/workflow.ts +2 -1
  69. package/src/upgrade/context.ts +332 -0
  70. package/src/upgrade/handlers.ts +477 -0
  71. package/src/upgrade/index.ts +244 -0
  72. package/src/upgrade/transitions.ts +80 -0
  73. package/src/workflow/plan-mode.ts +41 -7
  74. package/src/workflow/task-workflow.ts +3 -2
  75. package/tests/cli/model-command.test.ts +93 -0
  76. package/tests/types/project.test.ts +90 -15
  77. package/tests/types/workflow-schema.test.ts +59 -0
  78. package/tests/upgrade/context.test.ts +211 -0
  79. package/tests/upgrade/transitions.test.ts +85 -0
@@ -5,12 +5,23 @@
5
5
  import * as readline from 'node:readline';
6
6
  import { promises as fs } from 'node:fs';
7
7
  import path from 'node:path';
8
+ import { createRequire } from 'node:module';
9
+ // Get package version
10
+ const require = createRequire(import.meta.url);
11
+ const packageJson = require('../../package.json');
12
+ const VERSION = packageJson.version;
8
13
  import { getAuthStatusForDisplay, authenticateClaude, authenticateOpenAI, authenticateGemini, authenticateGrok, isClaudeCLIInstalled, checkClaudeCLIAuth, checkGeminiAuth, checkGrokAuth, } from '../auth/index.js';
9
- import { runWorkflow, resumeWorkflow, getWorkflowStatus, getWorkflowSummary, } from '../workflow/index.js';
10
- import { analyzeProjectProgress, verifyProjectCompletion, } from '../state/index.js';
14
+ import { runWorkflow, resumeWorkflow, getWorkflowStatus, getWorkflowSummary, resetWorkflow, } from '../workflow/index.js';
15
+ import { analyzeProjectProgress, verifyProjectCompletion, storeSpecification, } from '../state/index.js';
11
16
  import { generateProject } from '../generators/index.js';
12
17
  import { discoverProjects, formatProjectForDisplay, } from '../state/registry.js';
13
18
  import { loadConfig, saveConfig } from '../config/index.js';
19
+ import { getValidUpgradeTargets, getTransitionDetails } from '../upgrade/transitions.js';
20
+ import { upgradeProject } from '../upgrade/index.js';
21
+ import { buildUpgradeContext } from '../upgrade/context.js';
22
+ import { OutputLanguageSchema, KNOWN_OPENAI_MODELS } from '../types/project.js';
23
+ import { GeminiModelSchema, KNOWN_GEMINI_MODELS } from '../types/consensus.js';
24
+ import { OpenAIModelSchema } from '../types/project.js';
14
25
  import { printSuccess, printError, printWarning, printInfo, printKeyValue, startSpinner, succeedSpinner, failSpinner, stopSpinner, theme, } from './output.js';
15
26
  /**
16
27
  * Read popeye.md from project directory
@@ -35,7 +46,7 @@ async function readPopeyeConfig(projectDir) {
35
46
  const cleanValue = value.trim();
36
47
  switch (key) {
37
48
  case 'language':
38
- if (['python', 'typescript', 'fullstack'].includes(cleanValue)) {
49
+ if (OutputLanguageSchema.safeParse(cleanValue).success) {
39
50
  config.language = cleanValue;
40
51
  }
41
52
  break;
@@ -64,6 +75,21 @@ async function readPopeyeConfig(projectDir) {
64
75
  case 'projectName':
65
76
  config.projectName = cleanValue;
66
77
  break;
78
+ case 'openaiModel':
79
+ if (OpenAIModelSchema.safeParse(cleanValue).success) {
80
+ config.openaiModel = cleanValue;
81
+ }
82
+ break;
83
+ case 'geminiModel':
84
+ if (GeminiModelSchema.safeParse(cleanValue).success) {
85
+ config.geminiModel = cleanValue;
86
+ }
87
+ break;
88
+ case 'grokModel':
89
+ if (cleanValue.length > 0) {
90
+ config.grokModel = cleanValue;
91
+ }
92
+ break;
67
93
  }
68
94
  }
69
95
  }
@@ -97,6 +123,11 @@ async function readPopeyeConfig(projectDir) {
97
123
  */
98
124
  async function writePopeyeConfig(projectDir, config) {
99
125
  const configPath = path.join(projectDir, 'popeye.md');
126
+ const modelLines = [
127
+ config.openaiModel ? `openaiModel: ${config.openaiModel}` : '',
128
+ config.geminiModel ? `geminiModel: ${config.geminiModel}` : '',
129
+ config.grokModel ? `grokModel: ${config.grokModel}` : '',
130
+ ].filter(Boolean).join('\n');
100
131
  const content = `---
101
132
  # Popeye Project Configuration
102
133
  language: ${config.language}
@@ -105,6 +136,7 @@ arbitrator: ${config.enableArbitration ? config.arbitrator : 'off'}
105
136
  created: ${config.created}
106
137
  lastRun: ${new Date().toISOString()}
107
138
  ${config.projectName ? `projectName: ${config.projectName}` : ''}
139
+ ${modelLines}
108
140
  ---
109
141
 
110
142
  # ${config.projectName || 'Popeye Project'}
@@ -147,6 +179,12 @@ function applyPopeyeConfig(state, config) {
147
179
  state.reviewer = config.reviewer;
148
180
  state.arbitrator = config.arbitrator;
149
181
  state.enableArbitration = config.enableArbitration;
182
+ if (config.openaiModel)
183
+ state.openaiModel = config.openaiModel;
184
+ if (config.geminiModel)
185
+ state.geminiModel = config.geminiModel;
186
+ if (config.grokModel)
187
+ state.grokModel = config.grokModel;
150
188
  }
151
189
  // Note: startSpinner, succeedSpinner, failSpinner, stopSpinner are used in handleIdea
152
190
  /**
@@ -173,7 +211,7 @@ function getTerminalWidth() {
173
211
  */
174
212
  function drawHeader() {
175
213
  const width = getTerminalWidth();
176
- const title = ' Popeye CLI ';
214
+ const title = ` Popeye CLI v${VERSION} `;
177
215
  const subtitle = ' Autonomous Code Generation with AI Consensus ';
178
216
  // Top border
179
217
  console.log(theme.dim(box.topLeft + box.horizontal.repeat(width - 2) + box.topRight));
@@ -639,7 +677,9 @@ function showHelp() {
639
677
  ['/config', 'Show/change configuration'],
640
678
  ['/config reviewer', 'Set reviewer (openai/gemini/grok)'],
641
679
  ['/config arbitrator', 'Set arbitrator (openai/gemini/grok/off)'],
642
- ['/lang <lang>', 'Set language (python/typescript/fullstack)'],
680
+ ['/lang <lang>', 'Set language (be/fe/fs/web/all)'],
681
+ ['/model [provider] [model]', 'Show/set AI model (openai/gemini/grok)'],
682
+ ['/upgrade [target]', 'Upgrade project type (e.g., fullstack -> all)'],
643
683
  ['/new <idea>', 'Force start a new project (skips existing check)'],
644
684
  ['/resume', 'Resume interrupted project'],
645
685
  ['/clear', 'Clear screen'],
@@ -767,6 +807,9 @@ async function handleInput(input, state) {
767
807
  case '/resume':
768
808
  await handleResume(state, args);
769
809
  break;
810
+ case '/upgrade':
811
+ await handleUpgrade(state, args);
812
+ break;
770
813
  case '/new':
771
814
  // Force start a new project even if existing projects found
772
815
  if (args.length === 0) {
@@ -917,9 +960,12 @@ async function handleConfig(state, args = []) {
917
960
  printKeyValue('Language', state.language);
918
961
  }
919
962
  return;
963
+ case 'model':
964
+ handleModel(args.slice(1), state);
965
+ return;
920
966
  default:
921
967
  printError(`Unknown config option: ${subcommand}`);
922
- printInfo('Options: reviewer, arbitrator, language');
968
+ printInfo('Options: reviewer, arbitrator, language, model');
923
969
  return;
924
970
  }
925
971
  }
@@ -936,11 +982,16 @@ async function handleConfig(state, args = []) {
936
982
  console.log(` ${theme.dim('Grok:')} ${state.grokAuth ? theme.success('● Ready') : theme.dim('○ Not configured')}`);
937
983
  console.log();
938
984
  console.log(theme.primary.bold(' AI Configuration:'));
939
- const configReviewerName = state.reviewer === 'openai' ? 'OpenAI (GPT-4o)' : state.reviewer === 'grok' ? 'Grok' : 'Gemini';
985
+ const configReviewerName = state.reviewer === 'openai' ? `OpenAI (${state.openaiModel})` : state.reviewer === 'grok' ? `Grok (${state.grokModel})` : `Gemini (${state.geminiModel})`;
940
986
  const configArbitratorName = state.arbitrator === 'openai' ? 'OpenAI' : state.arbitrator === 'grok' ? 'Grok' : 'Gemini';
941
987
  console.log(` ${theme.dim('Reviewer:')} ${theme.primary(configReviewerName)}`);
942
988
  console.log(` ${theme.dim('Arbitrator:')} ${state.enableArbitration ? theme.primary(configArbitratorName) : theme.dim('Disabled')}`);
943
989
  console.log();
990
+ console.log(theme.primary.bold(' Models:'));
991
+ console.log(` ${theme.dim('OpenAI:')} ${theme.primary(state.openaiModel)}`);
992
+ console.log(` ${theme.dim('Gemini:')} ${theme.primary(state.geminiModel)}`);
993
+ console.log(` ${theme.dim('Grok:')} ${theme.primary(state.grokModel)}`);
994
+ console.log();
944
995
  console.log(theme.primary.bold(' Consensus:'));
945
996
  console.log(` ${theme.dim('Threshold:')} ${config.consensus.threshold}%`);
946
997
  console.log(` ${theme.dim('Max Iters:')} ${config.consensus.max_disagreements}`);
@@ -949,6 +1000,7 @@ async function handleConfig(state, args = []) {
949
1000
  console.log(theme.dim(' /config reviewer <openai|gemini|grok>'));
950
1001
  console.log(theme.dim(' /config arbitrator <openai|gemini|grok|off>'));
951
1002
  console.log(theme.dim(' /config language <be|fe|fs|web|all>'));
1003
+ console.log(theme.dim(' /config model <provider> <model>'));
952
1004
  console.log();
953
1005
  }
954
1006
  /**
@@ -998,24 +1050,240 @@ function handleLanguage(args, state) {
998
1050
  printSuccess(`Language set to ${lang}`);
999
1051
  }
1000
1052
  /**
1001
- * Handle /model command
1053
+ * Available models per provider for display
1054
+ */
1055
+ const KNOWN_MODELS = {
1056
+ openai: KNOWN_OPENAI_MODELS,
1057
+ gemini: KNOWN_GEMINI_MODELS,
1058
+ grok: ['grok-3', 'grok-3-mini', 'grok-2'],
1059
+ };
1060
+ /**
1061
+ * Handle /model command - multi-provider model switching
1002
1062
  */
1003
1063
  function handleModel(args, state) {
1004
- const validModels = ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini'];
1064
+ // /model (no args) -> show all provider models
1005
1065
  if (args.length === 0) {
1006
1066
  console.log();
1007
- printKeyValue('Current model', state.model);
1008
- printInfo(`Available: ${validModels.join(', ')}`);
1067
+ console.log(theme.primary.bold(' Models:'));
1068
+ console.log(` ${theme.dim('OpenAI:')} ${theme.primary(state.openaiModel)}`);
1069
+ console.log(` ${theme.dim('Gemini:')} ${theme.primary(state.geminiModel)}`);
1070
+ console.log(` ${theme.dim('Grok:')} ${theme.primary(state.grokModel)}`);
1071
+ console.log();
1072
+ console.log(theme.secondary(' Usage:'));
1073
+ console.log(theme.dim(' /model <provider> <model> Set model for provider'));
1074
+ console.log(theme.dim(' /model <provider> list Show available models'));
1075
+ console.log(theme.dim(' /model <openai-model> Set OpenAI model (shortcut)'));
1009
1076
  return;
1010
1077
  }
1011
- const model = args[0];
1012
- if (!validModels.includes(model)) {
1013
- printError(`Invalid model. Use one of: ${validModels.join(', ')}`);
1078
+ const firstArg = args[0].toLowerCase();
1079
+ // Check if first arg is a provider
1080
+ if (firstArg === 'openai' || firstArg === 'gemini' || firstArg === 'grok') {
1081
+ const provider = firstArg;
1082
+ // /model <provider> or /model <provider> list -> show known models
1083
+ if (args.length === 1 || args[1]?.toLowerCase() === 'list') {
1084
+ console.log();
1085
+ const currentModel = provider === 'openai' ? state.openaiModel
1086
+ : provider === 'gemini' ? state.geminiModel : state.grokModel;
1087
+ console.log(theme.primary.bold(` ${provider} models:`));
1088
+ console.log(` ${theme.dim('Current:')} ${theme.primary(currentModel)}`);
1089
+ console.log(` ${theme.dim('Known models:')}`);
1090
+ for (const m of KNOWN_MODELS[provider]) {
1091
+ const marker = m === currentModel ? theme.success(' (active)') : '';
1092
+ console.log(` ${theme.secondary(m)}${marker}`);
1093
+ }
1094
+ console.log();
1095
+ console.log(theme.dim(' Custom models are also accepted (e.g., gpt-5, gemini-2.5-pro)'));
1096
+ return;
1097
+ }
1098
+ // /model <provider> <model> -> set model (warn if unknown but accept)
1099
+ const newModel = args[1];
1100
+ if (!newModel || newModel.length === 0) {
1101
+ printError('Model name must not be empty.');
1102
+ return;
1103
+ }
1104
+ const isKnown = KNOWN_MODELS[provider].includes(newModel);
1105
+ if (provider === 'openai') {
1106
+ state.openaiModel = newModel;
1107
+ }
1108
+ else if (provider === 'gemini') {
1109
+ state.geminiModel = newModel;
1110
+ }
1111
+ else if (provider === 'grok') {
1112
+ state.grokModel = newModel;
1113
+ }
1114
+ console.log();
1115
+ if (isKnown) {
1116
+ printSuccess(`${provider} model set to ${newModel}`);
1117
+ }
1118
+ else {
1119
+ printSuccess(`${provider} model set to ${newModel}`);
1120
+ printInfo(`Note: '${newModel}' is not in the known models list. Make sure it's a valid ${provider} model.`);
1121
+ }
1122
+ return;
1123
+ }
1124
+ // Backward compat: /model <known-openai-model> (auto-detect known OpenAI model name)
1125
+ if (KNOWN_OPENAI_MODELS.includes(firstArg)) {
1126
+ state.openaiModel = firstArg;
1127
+ console.log();
1128
+ printSuccess(`OpenAI model set to ${firstArg}`);
1129
+ return;
1130
+ }
1131
+ printError(`Unknown provider: ${firstArg}`);
1132
+ printInfo('Use: /model <openai|gemini|grok> <model>');
1133
+ }
1134
+ /**
1135
+ * Handle /upgrade command - upgrade project type
1136
+ */
1137
+ async function handleUpgrade(state, args) {
1138
+ if (!state.projectDir) {
1139
+ printError('No active project. Start or resume a project first.');
1140
+ return;
1141
+ }
1142
+ // Load current state to get language
1143
+ const status = await getWorkflowStatus(state.projectDir);
1144
+ if (!status.exists || !status.state) {
1145
+ printError('No project state found in current directory.');
1146
+ return;
1147
+ }
1148
+ const currentLanguage = status.state.language;
1149
+ const validTargets = getValidUpgradeTargets(currentLanguage);
1150
+ if (validTargets.length === 0) {
1151
+ printInfo(`Project type '${currentLanguage}' is already at maximum scope. No upgrades available.`);
1152
+ return;
1153
+ }
1154
+ // Determine target
1155
+ let targetLanguage = null;
1156
+ if (args.length > 0) {
1157
+ const langAliases = {
1158
+ 'py': 'python', 'python': 'python', 'be': 'python', 'backend': 'python',
1159
+ 'ts': 'typescript', 'typescript': 'typescript', 'fe': 'typescript', 'frontend': 'typescript',
1160
+ 'fs': 'fullstack', 'fullstack': 'fullstack',
1161
+ 'web': 'website', 'website': 'website',
1162
+ 'all': 'all',
1163
+ };
1164
+ const resolved = langAliases[args[0].toLowerCase()];
1165
+ if (resolved && validTargets.includes(resolved)) {
1166
+ targetLanguage = resolved;
1167
+ }
1168
+ else if (resolved) {
1169
+ printError(`Cannot upgrade from '${currentLanguage}' to '${resolved}'.`);
1170
+ printInfo(`Valid targets: ${validTargets.join(', ')}`);
1171
+ return;
1172
+ }
1173
+ else {
1174
+ printError(`Unknown target: ${args[0]}`);
1175
+ printInfo(`Valid targets: ${validTargets.join(', ')}`);
1176
+ return;
1177
+ }
1178
+ }
1179
+ else {
1180
+ // Prompt selection
1181
+ const target = await promptSelection(`Upgrade '${currentLanguage}' project to:`, validTargets.map((t) => {
1182
+ const details = getTransitionDetails(currentLanguage, t);
1183
+ return {
1184
+ value: t,
1185
+ label: `${t} - ${details?.description || ''}`,
1186
+ };
1187
+ }), validTargets[0]);
1188
+ targetLanguage = target;
1189
+ }
1190
+ if (!targetLanguage)
1191
+ return;
1192
+ const transition = getTransitionDetails(currentLanguage, targetLanguage);
1193
+ if (!transition)
1194
+ return;
1195
+ // Show dry-run summary
1196
+ console.log();
1197
+ console.log(theme.primary.bold(' Upgrade Summary:'));
1198
+ console.log(` ${theme.dim('From:')} ${theme.primary(currentLanguage)}`);
1199
+ console.log(` ${theme.dim('To:')} ${theme.primary(targetLanguage)}`);
1200
+ console.log(` ${theme.dim('New apps:')} ${transition.newApps.join(', ') || 'none'}`);
1201
+ console.log(` ${theme.dim('Restructure:')} ${transition.requiresRestructure ? 'Yes - code will be moved to apps/' : 'No'}`);
1202
+ console.log(` ${theme.dim('Description:')} ${transition.description}`);
1203
+ console.log();
1204
+ // Confirm
1205
+ const confirmed = await promptYesNo(theme.primary('Proceed with upgrade?'), true);
1206
+ if (!confirmed) {
1207
+ printInfo('Upgrade cancelled.');
1014
1208
  return;
1015
1209
  }
1016
- state.model = model;
1210
+ // Execute upgrade
1017
1211
  console.log();
1018
- printSuccess(`Model set to ${model}`);
1212
+ startSpinner(`Upgrading ${currentLanguage} -> ${targetLanguage}...`);
1213
+ const result = await upgradeProject(state.projectDir, targetLanguage);
1214
+ if (result.success) {
1215
+ succeedSpinner(`Upgraded to ${targetLanguage}`);
1216
+ state.language = targetLanguage;
1217
+ console.log();
1218
+ if (result.filesCreated.length > 0) {
1219
+ printInfo(`Created ${result.filesCreated.length} new files`);
1220
+ }
1221
+ if (result.filesMoved.length > 0) {
1222
+ printInfo(`Moved ${result.filesMoved.length} items`);
1223
+ }
1224
+ printSuccess(`Project upgraded from '${currentLanguage}' to '${targetLanguage}'`);
1225
+ // Build upgrade context for planning
1226
+ console.log();
1227
+ startSpinner('Building expansion context...');
1228
+ const upgradeContext = await buildUpgradeContext(state.projectDir, transition, status.state.idea || 'Project expansion', currentLanguage);
1229
+ succeedSpinner('Expansion context ready');
1230
+ // Show what will be planned
1231
+ console.log();
1232
+ console.log(theme.primary.bold(' Expansion Planning:'));
1233
+ console.log(` ${theme.dim('Existing apps:')} ${upgradeContext.existingApps.join(', ')} (already built)`);
1234
+ console.log(` ${theme.dim('New apps:')} ${upgradeContext.newApps.join(', ')} (will be planned)`);
1235
+ console.log();
1236
+ // Ask user if they want to start planning now
1237
+ const startPlanning = await promptYesNo(theme.primary('Start planning the new apps now?'), true);
1238
+ if (!startPlanning) {
1239
+ printInfo('You can start planning later with /resume');
1240
+ return;
1241
+ }
1242
+ // Reset state to plan phase so workflow re-plans for the expanded project
1243
+ // This clears old plan/milestones but keeps the idea and project metadata
1244
+ await resetWorkflow(state.projectDir, 'plan');
1245
+ // Clear old specification so the idea gets re-expanded for the new project scope
1246
+ // The upgrade context will guide the planner to focus on new apps
1247
+ await storeSpecification(state.projectDir, '');
1248
+ console.log();
1249
+ printInfo('Starting expansion planning...');
1250
+ console.log();
1251
+ const workflowResult = await resumeWorkflow(state.projectDir, {
1252
+ consensusConfig: {
1253
+ reviewer: state.reviewer,
1254
+ arbitrator: state.arbitrator,
1255
+ enableArbitration: state.enableArbitration,
1256
+ openaiModel: state.openaiModel,
1257
+ geminiModel: state.geminiModel,
1258
+ grokModel: state.grokModel,
1259
+ },
1260
+ additionalContext: upgradeContext.summary,
1261
+ onProgress: (phase, message) => {
1262
+ console.log(` ${theme.dim(`[${phase}]`)} ${message}`);
1263
+ },
1264
+ });
1265
+ console.log();
1266
+ if (workflowResult.success) {
1267
+ printSuccess('Expansion planning and implementation complete!');
1268
+ console.log(` ${theme.dim('Location:')} ${state.projectDir}`);
1269
+ }
1270
+ else if (workflowResult.rateLimitPaused) {
1271
+ console.log(` ${theme.warning('Rate Limit Reached')}`);
1272
+ console.log(` ${theme.dim(workflowResult.error || 'API rate limit exceeded')}`);
1273
+ console.log();
1274
+ console.log(` ${theme.info('Your progress has been saved.')}`);
1275
+ console.log(` ${theme.dim('Run')} ${theme.highlight('/resume')} ${theme.dim('after the rate limit resets to continue.')}`);
1276
+ }
1277
+ else {
1278
+ printError(workflowResult.error || 'Expansion workflow failed');
1279
+ printInfo('Use /resume to retry.');
1280
+ }
1281
+ }
1282
+ else {
1283
+ failSpinner('Upgrade failed');
1284
+ printError(result.error || 'Unknown error during upgrade');
1285
+ printInfo('All changes have been rolled back.');
1286
+ }
1019
1287
  }
1020
1288
  /**
1021
1289
  * Prompt for additional context
@@ -1361,6 +1629,9 @@ async function handleResume(state, args) {
1361
1629
  reviewer: state.reviewer,
1362
1630
  arbitrator: state.arbitrator,
1363
1631
  enableArbitration: state.enableArbitration,
1632
+ openaiModel: state.openaiModel,
1633
+ geminiModel: state.geminiModel,
1634
+ grokModel: state.grokModel,
1364
1635
  },
1365
1636
  additionalContext,
1366
1637
  onProgress: (phase, message) => {
@@ -1480,7 +1751,7 @@ async function handleResume(state, args) {
1480
1751
  idea: discovered.idea || discovered.plan?.slice(0, 500) || `Continue developing ${projectName}`,
1481
1752
  name: projectName,
1482
1753
  language: discovered.language || state.language,
1483
- openaiModel: state.model,
1754
+ openaiModel: state.openaiModel,
1484
1755
  outputDir: state.projectDir,
1485
1756
  };
1486
1757
  console.log();
@@ -1496,6 +1767,9 @@ async function handleResume(state, args) {
1496
1767
  reviewer: state.reviewer,
1497
1768
  arbitrator: state.arbitrator,
1498
1769
  enableArbitration: state.enableArbitration,
1770
+ openaiModel: state.openaiModel,
1771
+ geminiModel: state.geminiModel,
1772
+ grokModel: state.grokModel,
1499
1773
  },
1500
1774
  onProgress: (phase, message) => {
1501
1775
  console.log(` ${theme.dim(`[${phase}]`)} ${message}`);
@@ -1695,13 +1969,13 @@ async function handleIdea(idea, state) {
1695
1969
  console.log(` ${theme.dim('Idea:')} ${idea}`);
1696
1970
  console.log(` ${theme.dim('Name:')} ${theme.primary(projectName)}`);
1697
1971
  console.log(` ${theme.dim('Language:')} ${theme.primary(state.language)}`);
1698
- console.log(` ${theme.dim('Model:')} ${theme.secondary(state.model)}`);
1972
+ console.log(` ${theme.dim('Model:')} ${theme.secondary(state.openaiModel)}`);
1699
1973
  console.log();
1700
1974
  const spec = {
1701
1975
  idea,
1702
1976
  name: projectName,
1703
1977
  language: state.language,
1704
- openaiModel: state.model,
1978
+ openaiModel: state.openaiModel,
1705
1979
  outputDir: cwd,
1706
1980
  };
1707
1981
  // Generate scaffold
@@ -1723,6 +1997,9 @@ async function handleIdea(idea, state) {
1723
1997
  lastRun: new Date().toISOString(),
1724
1998
  projectName,
1725
1999
  description: idea,
2000
+ openaiModel: state.openaiModel,
2001
+ geminiModel: state.geminiModel,
2002
+ grokModel: state.grokModel,
1726
2003
  });
1727
2004
  printInfo('Created popeye.md with project configuration');
1728
2005
  // Run workflow with reviewer/arbitrator settings
@@ -1739,7 +2016,9 @@ async function handleIdea(idea, state) {
1739
2016
  reviewer: state.reviewer,
1740
2017
  arbitrator: state.arbitrator,
1741
2018
  enableArbitration: state.enableArbitration,
2019
+ openaiModel: state.openaiModel,
1742
2020
  geminiModel: state.geminiModel,
2021
+ grokModel: state.grokModel,
1743
2022
  },
1744
2023
  onProgress: (phase, message) => {
1745
2024
  console.log(` ${theme.dim(`[${phase}]`)} ${message}`);
@@ -1793,13 +2072,13 @@ async function handleNewProject(idea, state) {
1793
2072
  console.log(` ${theme.dim('Idea:')} ${idea}`);
1794
2073
  console.log(` ${theme.dim('Name:')} ${theme.primary(projectName)}`);
1795
2074
  console.log(` ${theme.dim('Language:')} ${theme.primary(state.language)}`);
1796
- console.log(` ${theme.dim('Model:')} ${theme.secondary(state.model)}`);
2075
+ console.log(` ${theme.dim('Model:')} ${theme.secondary(state.openaiModel)}`);
1797
2076
  console.log();
1798
2077
  const spec = {
1799
2078
  idea,
1800
2079
  name: projectName,
1801
2080
  language: state.language,
1802
- openaiModel: state.model,
2081
+ openaiModel: state.openaiModel,
1803
2082
  outputDir: cwd,
1804
2083
  };
1805
2084
  // Generate scaffold
@@ -1821,6 +2100,9 @@ async function handleNewProject(idea, state) {
1821
2100
  lastRun: new Date().toISOString(),
1822
2101
  projectName,
1823
2102
  description: idea,
2103
+ openaiModel: state.openaiModel,
2104
+ geminiModel: state.geminiModel,
2105
+ grokModel: state.grokModel,
1824
2106
  });
1825
2107
  printInfo('Created popeye.md with project configuration');
1826
2108
  // Run workflow with reviewer/arbitrator settings
@@ -1837,7 +2119,9 @@ async function handleNewProject(idea, state) {
1837
2119
  reviewer: state.reviewer,
1838
2120
  arbitrator: state.arbitrator,
1839
2121
  enableArbitration: state.enableArbitration,
2122
+ openaiModel: state.openaiModel,
1840
2123
  geminiModel: state.geminiModel,
2124
+ grokModel: state.grokModel,
1841
2125
  },
1842
2126
  onProgress: (phase, message) => {
1843
2127
  console.log(` ${theme.dim(`[${phase}]`)} ${message}`);
@@ -1877,8 +2161,9 @@ export async function startInteractiveMode() {
1877
2161
  const state = {
1878
2162
  projectDir: process.cwd(),
1879
2163
  language: config.project.default_language,
1880
- model: config.apis.openai.model,
2164
+ openaiModel: config.apis.openai.model,
1881
2165
  geminiModel: 'gemini-2.0-flash',
2166
+ grokModel: config.apis.grok.model,
1882
2167
  claudeAuth: false,
1883
2168
  openaiAuth: false,
1884
2169
  geminiAuth: false,