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.
- package/README.md +115 -17
- package/dist/adapters/gemini.d.ts +2 -2
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +307 -22
- package/dist/cli/interactive.js.map +1 -1
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/schema.d.ts +4 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +2 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/generators/all.d.ts +30 -0
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +5 -5
- package/dist/generators/all.js.map +1 -1
- package/dist/types/consensus.d.ts +10 -20
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +8 -3
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/project.d.ts +15 -16
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +15 -8
- package/dist/types/project.js.map +1 -1
- package/dist/types/workflow.d.ts +2 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +2 -1
- package/dist/types/workflow.js.map +1 -1
- package/dist/upgrade/context.d.ts +37 -0
- package/dist/upgrade/context.d.ts.map +1 -0
- package/dist/upgrade/context.js +284 -0
- package/dist/upgrade/context.js.map +1 -0
- package/dist/upgrade/handlers.d.ts +103 -0
- package/dist/upgrade/handlers.d.ts.map +1 -0
- package/dist/upgrade/handlers.js +384 -0
- package/dist/upgrade/handlers.js.map +1 -0
- package/dist/upgrade/index.d.ts +26 -0
- package/dist/upgrade/index.d.ts.map +1 -0
- package/dist/upgrade/index.js +194 -0
- package/dist/upgrade/index.js.map +1 -0
- package/dist/upgrade/transitions.d.ts +34 -0
- package/dist/upgrade/transitions.d.ts.map +1 -0
- package/dist/upgrade/transitions.js +56 -0
- package/dist/upgrade/transitions.js.map +1 -0
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +41 -5
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/task-workflow.d.ts.map +1 -1
- package/dist/workflow/task-workflow.js +3 -2
- package/dist/workflow/task-workflow.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/gemini.ts +2 -2
- package/src/cli/index.ts +5 -2
- package/src/cli/interactive.ts +353 -23
- package/src/config/schema.ts +2 -1
- package/src/generators/all.ts +5 -5
- package/src/types/consensus.ts +11 -5
- package/src/types/index.ts +2 -0
- package/src/types/project.ts +18 -9
- package/src/types/workflow.ts +2 -1
- package/src/upgrade/context.ts +332 -0
- package/src/upgrade/handlers.ts +477 -0
- package/src/upgrade/index.ts +244 -0
- package/src/upgrade/transitions.ts +80 -0
- package/src/workflow/plan-mode.ts +41 -7
- package/src/workflow/task-workflow.ts +3 -2
- package/tests/cli/model-command.test.ts +93 -0
- package/tests/types/project.test.ts +90 -15
- package/tests/types/workflow-schema.test.ts +59 -0
- package/tests/upgrade/context.test.ts +211 -0
- package/tests/upgrade/transitions.test.ts +85 -0
package/dist/cli/interactive.js
CHANGED
|
@@ -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 (
|
|
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 =
|
|
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 (
|
|
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' ?
|
|
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
|
-
*
|
|
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
|
-
|
|
1064
|
+
// /model (no args) -> show all provider models
|
|
1005
1065
|
if (args.length === 0) {
|
|
1006
1066
|
console.log();
|
|
1007
|
-
|
|
1008
|
-
|
|
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
|
|
1012
|
-
if
|
|
1013
|
-
|
|
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
|
-
|
|
1210
|
+
// Execute upgrade
|
|
1017
1211
|
console.log();
|
|
1018
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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,
|