popeye-cli 1.0.0 → 1.0.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/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +4 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.d.ts +2 -2
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +335 -179
- package/dist/cli/interactive.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/index.ts +4 -7
- package/src/cli/interactive.ts +381 -198
package/dist/cli/interactive.js
CHANGED
|
@@ -1,188 +1,301 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Interactive mode
|
|
3
|
-
*
|
|
3
|
+
* Claude Code-style interface for Popeye CLI
|
|
4
4
|
*/
|
|
5
5
|
import * as readline from 'node:readline';
|
|
6
|
-
import { getAuthStatusForDisplay } from '../auth/index.js';
|
|
6
|
+
import { getAuthStatusForDisplay, authenticateClaude, authenticateOpenAI } from '../auth/index.js';
|
|
7
7
|
import { runWorkflow, resumeWorkflow, getWorkflowStatus, getWorkflowSummary, } from '../workflow/index.js';
|
|
8
8
|
import { generateProject } from '../generators/index.js';
|
|
9
9
|
import { loadConfig } from '../config/index.js';
|
|
10
|
-
import {
|
|
10
|
+
import { printSuccess, printError, printWarning, printInfo, printKeyValue, startSpinner, succeedSpinner, failSpinner, stopSpinner, theme, } from './output.js';
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Box drawing characters for Claude Code-style UI
|
|
13
13
|
*/
|
|
14
|
-
const
|
|
15
|
-
'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
'/auth': {
|
|
24
|
-
description: 'Check authentication status',
|
|
25
|
-
handler: handleAuth,
|
|
26
|
-
},
|
|
27
|
-
'/config': {
|
|
28
|
-
description: 'Show current configuration',
|
|
29
|
-
handler: handleConfig,
|
|
30
|
-
},
|
|
31
|
-
'/language': {
|
|
32
|
-
description: 'Set output language (python/typescript)',
|
|
33
|
-
handler: handleLanguage,
|
|
34
|
-
},
|
|
35
|
-
'/model': {
|
|
36
|
-
description: 'Set OpenAI model',
|
|
37
|
-
handler: handleModel,
|
|
38
|
-
},
|
|
39
|
-
'/project': {
|
|
40
|
-
description: 'Set project directory',
|
|
41
|
-
handler: handleProject,
|
|
42
|
-
},
|
|
43
|
-
'/resume': {
|
|
44
|
-
description: 'Resume current project workflow',
|
|
45
|
-
handler: handleResume,
|
|
46
|
-
},
|
|
47
|
-
'/clear': {
|
|
48
|
-
description: 'Clear the screen',
|
|
49
|
-
handler: handleClear,
|
|
50
|
-
},
|
|
51
|
-
'/exit': {
|
|
52
|
-
description: 'Exit interactive mode',
|
|
53
|
-
handler: handleExit,
|
|
54
|
-
},
|
|
55
|
-
'/quit': {
|
|
56
|
-
description: 'Exit interactive mode',
|
|
57
|
-
handler: handleExit,
|
|
58
|
-
},
|
|
14
|
+
const box = {
|
|
15
|
+
topLeft: '╭',
|
|
16
|
+
topRight: '╮',
|
|
17
|
+
bottomLeft: '╰',
|
|
18
|
+
bottomRight: '╯',
|
|
19
|
+
horizontal: '─',
|
|
20
|
+
vertical: '│',
|
|
21
|
+
leftT: '├',
|
|
22
|
+
rightT: '┤',
|
|
59
23
|
};
|
|
60
24
|
/**
|
|
61
|
-
*
|
|
25
|
+
* Get terminal width
|
|
62
26
|
*/
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
27
|
+
function getTerminalWidth() {
|
|
28
|
+
return process.stdout.columns || 80;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Draw the header box
|
|
32
|
+
*/
|
|
33
|
+
function drawHeader() {
|
|
34
|
+
const width = getTerminalWidth();
|
|
35
|
+
const title = ' Popeye CLI ';
|
|
36
|
+
const subtitle = ' AI-Powered Code Generation ';
|
|
37
|
+
// Top border
|
|
38
|
+
console.log(theme.dim(box.topLeft + box.horizontal.repeat(width - 2) + box.topRight));
|
|
39
|
+
// Title line
|
|
40
|
+
const titlePadding = Math.floor((width - title.length - 2) / 2);
|
|
41
|
+
console.log(theme.dim(box.vertical) +
|
|
42
|
+
' '.repeat(titlePadding) +
|
|
43
|
+
theme.primary.bold(title) +
|
|
44
|
+
' '.repeat(width - titlePadding - title.length - 2) +
|
|
45
|
+
theme.dim(box.vertical));
|
|
46
|
+
// Subtitle line
|
|
47
|
+
const subPadding = Math.floor((width - subtitle.length - 2) / 2);
|
|
48
|
+
console.log(theme.dim(box.vertical) +
|
|
49
|
+
' '.repeat(subPadding) +
|
|
50
|
+
theme.secondary(subtitle) +
|
|
51
|
+
' '.repeat(width - subPadding - subtitle.length - 2) +
|
|
52
|
+
theme.dim(box.vertical));
|
|
53
|
+
// Bottom border
|
|
54
|
+
console.log(theme.dim(box.bottomLeft + box.horizontal.repeat(width - 2) + box.bottomRight));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Draw the input box frame
|
|
58
|
+
*/
|
|
59
|
+
function drawInputFrame(state) {
|
|
60
|
+
const width = getTerminalWidth();
|
|
61
|
+
// Status items
|
|
62
|
+
const langStatus = `${state.language}`;
|
|
63
|
+
const modelStatus = `${state.model}`;
|
|
64
|
+
const authStatus = state.claudeAuth && state.openaiAuth ? '●' : '○';
|
|
65
|
+
const authColor = state.claudeAuth && state.openaiAuth ? theme.success : theme.warning;
|
|
66
|
+
// Build status line
|
|
67
|
+
const statusItems = [
|
|
68
|
+
theme.dim('lang:') + theme.primary(langStatus),
|
|
69
|
+
theme.dim('model:') + theme.secondary(modelStatus),
|
|
70
|
+
authColor(authStatus) + theme.dim(' auth'),
|
|
71
|
+
];
|
|
72
|
+
const statusText = statusItems.join(theme.dim(' │ '));
|
|
73
|
+
// Calculate visible length (without ANSI codes)
|
|
74
|
+
// eslint-disable-next-line no-control-regex
|
|
75
|
+
const stripAnsi = (str) => str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
76
|
+
const statusLen = stripAnsi(statusText).length;
|
|
77
|
+
// Top line with status
|
|
78
|
+
const topLine = box.topLeft +
|
|
79
|
+
box.horizontal.repeat(2) +
|
|
80
|
+
' ' + stripAnsi(statusText) + ' ' +
|
|
81
|
+
box.horizontal.repeat(Math.max(0, width - statusLen - 6)) +
|
|
82
|
+
box.topRight;
|
|
83
|
+
console.log(theme.dim(topLine.slice(0, 1)) + statusText + theme.dim(topLine.slice(statusLen + 4)));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Clear screen and redraw UI
|
|
87
|
+
*/
|
|
88
|
+
function redrawUI(_state) {
|
|
89
|
+
console.clear();
|
|
90
|
+
drawHeader();
|
|
67
91
|
console.log();
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Prompt for input with styled prompt
|
|
95
|
+
*/
|
|
96
|
+
function getPrompt() {
|
|
97
|
+
return theme.dim(box.vertical) + ' ' + theme.primary('❯') + ' ';
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check and perform authentication
|
|
101
|
+
*/
|
|
102
|
+
async function ensureAuthentication(state) {
|
|
103
|
+
const status = await getAuthStatusForDisplay();
|
|
104
|
+
state.claudeAuth = status.claude.authenticated;
|
|
105
|
+
state.openaiAuth = status.openai.authenticated;
|
|
106
|
+
if (state.claudeAuth && state.openaiAuth) {
|
|
107
|
+
return true;
|
|
82
108
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
console.log();
|
|
110
|
+
printWarning('Authentication required to continue');
|
|
111
|
+
console.log();
|
|
112
|
+
// Authenticate Claude if needed
|
|
113
|
+
if (!state.claudeAuth) {
|
|
114
|
+
console.log(theme.dim(box.vertical) + ' ' + theme.primary('Claude CLI') + theme.dim(' - Browser authentication'));
|
|
115
|
+
console.log(theme.dim(box.vertical));
|
|
116
|
+
const rl = readline.createInterface({
|
|
117
|
+
input: process.stdin,
|
|
118
|
+
output: process.stdout,
|
|
119
|
+
});
|
|
120
|
+
const proceed = await new Promise((resolve) => {
|
|
121
|
+
rl.question(theme.dim(box.vertical) + ' Press Enter to open browser (or "skip" to skip): ', (answer) => {
|
|
122
|
+
rl.close();
|
|
123
|
+
resolve(answer.toLowerCase() !== 'skip');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
if (proceed) {
|
|
127
|
+
startSpinner('Opening browser for Claude authentication...');
|
|
128
|
+
try {
|
|
129
|
+
const success = await authenticateClaude();
|
|
130
|
+
if (success) {
|
|
131
|
+
succeedSpinner('Claude authenticated');
|
|
132
|
+
state.claudeAuth = true;
|
|
103
133
|
}
|
|
104
134
|
else {
|
|
105
|
-
|
|
135
|
+
failSpinner('Claude authentication failed');
|
|
106
136
|
}
|
|
107
137
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
138
|
+
catch (err) {
|
|
139
|
+
failSpinner('Claude authentication failed');
|
|
140
|
+
printError(err instanceof Error ? err.message : 'Authentication failed');
|
|
111
141
|
}
|
|
112
142
|
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
printError(error instanceof Error ? error.message : 'Unknown error');
|
|
115
|
-
}
|
|
116
143
|
console.log();
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
144
|
+
}
|
|
145
|
+
// Authenticate OpenAI if needed
|
|
146
|
+
if (!state.openaiAuth) {
|
|
147
|
+
console.log(theme.dim(box.vertical) + ' ' + theme.primary('OpenAI API') + theme.dim(' - API key required'));
|
|
148
|
+
console.log(theme.dim(box.vertical));
|
|
149
|
+
const rl2 = readline.createInterface({
|
|
150
|
+
input: process.stdin,
|
|
151
|
+
output: process.stdout,
|
|
152
|
+
});
|
|
153
|
+
const proceed2 = await new Promise((resolve) => {
|
|
154
|
+
rl2.question(theme.dim(box.vertical) + ' Press Enter to open key entry page (or "skip" to skip): ', (answer) => {
|
|
155
|
+
rl2.close();
|
|
156
|
+
resolve(answer.toLowerCase() !== 'skip');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
if (proceed2) {
|
|
160
|
+
startSpinner('Opening browser for OpenAI API key entry...');
|
|
161
|
+
try {
|
|
162
|
+
const success = await authenticateOpenAI();
|
|
163
|
+
if (success) {
|
|
164
|
+
succeedSpinner('OpenAI authenticated');
|
|
165
|
+
state.openaiAuth = true;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
failSpinner('OpenAI authentication failed');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
failSpinner('OpenAI authentication failed');
|
|
173
|
+
printError(err instanceof Error ? err.message : 'Authentication failed');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
120
176
|
console.log();
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
});
|
|
177
|
+
}
|
|
178
|
+
return state.claudeAuth && state.openaiAuth;
|
|
124
179
|
}
|
|
125
180
|
/**
|
|
126
|
-
*
|
|
181
|
+
* Display help
|
|
127
182
|
*/
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
183
|
+
function showHelp() {
|
|
184
|
+
console.log();
|
|
185
|
+
console.log(theme.primary.bold(' Commands:'));
|
|
186
|
+
console.log();
|
|
187
|
+
const commands = [
|
|
188
|
+
['/help', 'Show this help message'],
|
|
189
|
+
['/status', 'Show current project status'],
|
|
190
|
+
['/auth', 'Re-authenticate services'],
|
|
191
|
+
['/config', 'Show configuration'],
|
|
192
|
+
['/language <lang>', 'Set language (python/typescript)'],
|
|
193
|
+
['/model <model>', 'Set OpenAI model'],
|
|
194
|
+
['/resume', 'Resume interrupted project'],
|
|
195
|
+
['/clear', 'Clear screen'],
|
|
196
|
+
['/exit', 'Exit Popeye'],
|
|
197
|
+
];
|
|
198
|
+
for (const [cmd, desc] of commands) {
|
|
199
|
+
console.log(` ${theme.primary(cmd.padEnd(20))} ${theme.dim(desc)}`);
|
|
132
200
|
}
|
|
133
201
|
console.log();
|
|
134
|
-
|
|
202
|
+
console.log(theme.secondary(' Or just type your project idea to get started!'));
|
|
203
|
+
console.log();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Handle a command or idea
|
|
207
|
+
*/
|
|
208
|
+
async function handleInput(input, state) {
|
|
209
|
+
const trimmed = input.trim();
|
|
210
|
+
if (!trimmed)
|
|
211
|
+
return true;
|
|
212
|
+
// Handle commands
|
|
213
|
+
if (trimmed.startsWith('/')) {
|
|
214
|
+
const [cmd, ...args] = trimmed.split(/\s+/);
|
|
215
|
+
const command = cmd.toLowerCase();
|
|
216
|
+
switch (command) {
|
|
217
|
+
case '/help':
|
|
218
|
+
showHelp();
|
|
219
|
+
break;
|
|
220
|
+
case '/exit':
|
|
221
|
+
case '/quit':
|
|
222
|
+
case '/q':
|
|
223
|
+
console.log();
|
|
224
|
+
printInfo('Goodbye!');
|
|
225
|
+
return false;
|
|
226
|
+
case '/clear':
|
|
227
|
+
redrawUI(state);
|
|
228
|
+
break;
|
|
229
|
+
case '/status':
|
|
230
|
+
await handleStatus(state);
|
|
231
|
+
break;
|
|
232
|
+
case '/auth':
|
|
233
|
+
await ensureAuthentication(state);
|
|
234
|
+
break;
|
|
235
|
+
case '/config':
|
|
236
|
+
await handleConfig(state);
|
|
237
|
+
break;
|
|
238
|
+
case '/language':
|
|
239
|
+
handleLanguage(args, state);
|
|
240
|
+
break;
|
|
241
|
+
case '/model':
|
|
242
|
+
handleModel(args, state);
|
|
243
|
+
break;
|
|
244
|
+
case '/resume':
|
|
245
|
+
await handleResume(state);
|
|
246
|
+
break;
|
|
247
|
+
default:
|
|
248
|
+
printError(`Unknown command: ${cmd}`);
|
|
249
|
+
printInfo('Type /help for available commands');
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
// Handle as project idea
|
|
254
|
+
await handleIdea(trimmed, state);
|
|
255
|
+
return true;
|
|
135
256
|
}
|
|
136
257
|
/**
|
|
137
258
|
* Handle /status command
|
|
138
259
|
*/
|
|
139
|
-
async function handleStatus(
|
|
260
|
+
async function handleStatus(state) {
|
|
261
|
+
console.log();
|
|
140
262
|
if (!state.projectDir) {
|
|
141
|
-
printInfo('No
|
|
263
|
+
printInfo('No active project');
|
|
142
264
|
return;
|
|
143
265
|
}
|
|
144
266
|
const status = await getWorkflowStatus(state.projectDir);
|
|
145
267
|
if (!status.exists) {
|
|
146
268
|
printInfo('No project found in current directory');
|
|
147
269
|
printKeyValue('Directory', state.projectDir);
|
|
148
|
-
printKeyValue('Language', state.language);
|
|
149
|
-
printKeyValue('Model', state.model);
|
|
150
270
|
return;
|
|
151
271
|
}
|
|
152
272
|
const summary = await getWorkflowSummary(state.projectDir);
|
|
153
273
|
console.log(summary);
|
|
154
274
|
}
|
|
155
|
-
/**
|
|
156
|
-
* Handle /auth command
|
|
157
|
-
*/
|
|
158
|
-
async function handleAuth(_args, state) {
|
|
159
|
-
const status = await getAuthStatusForDisplay();
|
|
160
|
-
printAuthStatus(status);
|
|
161
|
-
state.authenticated = status.claude.authenticated && status.openai.authenticated;
|
|
162
|
-
if (!state.authenticated) {
|
|
163
|
-
console.log();
|
|
164
|
-
printInfo('Run "popeye-cli auth login" to authenticate');
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
275
|
/**
|
|
168
276
|
* Handle /config command
|
|
169
277
|
*/
|
|
170
|
-
async function handleConfig(
|
|
278
|
+
async function handleConfig(state) {
|
|
171
279
|
const config = await loadConfig();
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
280
|
+
console.log();
|
|
281
|
+
console.log(theme.primary.bold(' Session:'));
|
|
282
|
+
console.log(` ${theme.dim('Directory:')} ${state.projectDir || 'Not set'}`);
|
|
283
|
+
console.log(` ${theme.dim('Language:')} ${theme.primary(state.language)}`);
|
|
284
|
+
console.log(` ${theme.dim('Model:')} ${theme.secondary(state.model)}`);
|
|
285
|
+
console.log(` ${theme.dim('Claude:')} ${state.claudeAuth ? theme.success('●') : theme.error('○')}`);
|
|
286
|
+
console.log(` ${theme.dim('OpenAI:')} ${state.openaiAuth ? theme.success('●') : theme.error('○')}`);
|
|
287
|
+
console.log();
|
|
288
|
+
console.log(theme.primary.bold(' Consensus:'));
|
|
289
|
+
console.log(` ${theme.dim('Threshold:')} ${config.consensus.threshold}%`);
|
|
290
|
+
console.log(` ${theme.dim('Max Iterations:')} ${config.consensus.max_disagreements}`);
|
|
291
|
+
console.log();
|
|
180
292
|
}
|
|
181
293
|
/**
|
|
182
294
|
* Handle /language command
|
|
183
295
|
*/
|
|
184
|
-
|
|
296
|
+
function handleLanguage(args, state) {
|
|
185
297
|
if (args.length === 0) {
|
|
298
|
+
console.log();
|
|
186
299
|
printKeyValue('Current language', state.language);
|
|
187
300
|
printInfo('Use /language <python|typescript> to change');
|
|
188
301
|
return;
|
|
@@ -193,16 +306,18 @@ async function handleLanguage(args, state) {
|
|
|
193
306
|
return;
|
|
194
307
|
}
|
|
195
308
|
state.language = lang;
|
|
196
|
-
|
|
309
|
+
console.log();
|
|
310
|
+
printSuccess(`Language set to ${lang}`);
|
|
197
311
|
}
|
|
198
312
|
/**
|
|
199
313
|
* Handle /model command
|
|
200
314
|
*/
|
|
201
|
-
|
|
315
|
+
function handleModel(args, state) {
|
|
202
316
|
const validModels = ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini'];
|
|
203
317
|
if (args.length === 0) {
|
|
318
|
+
console.log();
|
|
204
319
|
printKeyValue('Current model', state.model);
|
|
205
|
-
printInfo(`
|
|
320
|
+
printInfo(`Available: ${validModels.join(', ')}`);
|
|
206
321
|
return;
|
|
207
322
|
}
|
|
208
323
|
const model = args[0];
|
|
@@ -211,32 +326,19 @@ async function handleModel(args, state) {
|
|
|
211
326
|
return;
|
|
212
327
|
}
|
|
213
328
|
state.model = model;
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Handle /project command
|
|
218
|
-
*/
|
|
219
|
-
async function handleProject(args, state) {
|
|
220
|
-
const path = await import('node:path');
|
|
221
|
-
if (args.length === 0) {
|
|
222
|
-
printKeyValue('Current directory', state.projectDir || 'Not set');
|
|
223
|
-
printInfo('Use /project <directory> to change');
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
const dir = path.resolve(args[0]);
|
|
227
|
-
state.projectDir = dir;
|
|
228
|
-
printSuccess(`Project directory set to: ${dir}`);
|
|
329
|
+
console.log();
|
|
330
|
+
printSuccess(`Model set to ${model}`);
|
|
229
331
|
}
|
|
230
332
|
/**
|
|
231
333
|
* Handle /resume command
|
|
232
334
|
*/
|
|
233
|
-
async function handleResume(
|
|
335
|
+
async function handleResume(state) {
|
|
234
336
|
if (!state.projectDir) {
|
|
235
|
-
printError('No project directory set
|
|
337
|
+
printError('No project directory set');
|
|
236
338
|
return;
|
|
237
339
|
}
|
|
238
|
-
if (!state.
|
|
239
|
-
printError('
|
|
340
|
+
if (!state.claudeAuth || !state.openaiAuth) {
|
|
341
|
+
printError('Authentication required. Run /auth first.');
|
|
240
342
|
return;
|
|
241
343
|
}
|
|
242
344
|
const status = await getWorkflowStatus(state.projectDir);
|
|
@@ -244,13 +346,15 @@ async function handleResume(_args, state) {
|
|
|
244
346
|
printError('No project found to resume');
|
|
245
347
|
return;
|
|
246
348
|
}
|
|
349
|
+
console.log();
|
|
247
350
|
printInfo('Resuming workflow...');
|
|
248
351
|
console.log();
|
|
249
352
|
const result = await resumeWorkflow(state.projectDir, {
|
|
250
353
|
onProgress: (phase, message) => {
|
|
251
|
-
console.log(` [${phase}] ${message}`);
|
|
354
|
+
console.log(` ${theme.dim(`[${phase}]`)} ${message}`);
|
|
252
355
|
},
|
|
253
356
|
});
|
|
357
|
+
console.log();
|
|
254
358
|
if (result.success) {
|
|
255
359
|
printSuccess('Workflow completed!');
|
|
256
360
|
}
|
|
@@ -258,33 +362,28 @@ async function handleResume(_args, state) {
|
|
|
258
362
|
printError(result.error || 'Workflow failed');
|
|
259
363
|
}
|
|
260
364
|
}
|
|
261
|
-
/**
|
|
262
|
-
* Handle /clear command
|
|
263
|
-
*/
|
|
264
|
-
async function handleClear(_args, _state) {
|
|
265
|
-
clearConsole();
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Handle /exit command
|
|
269
|
-
*/
|
|
270
|
-
async function handleExit(_args, _state) {
|
|
271
|
-
printInfo('Goodbye!');
|
|
272
|
-
process.exit(0);
|
|
273
|
-
}
|
|
274
365
|
/**
|
|
275
366
|
* Handle project idea input
|
|
276
367
|
*/
|
|
277
368
|
async function handleIdea(idea, state) {
|
|
278
|
-
if (!state.
|
|
279
|
-
|
|
280
|
-
|
|
369
|
+
if (!state.claudeAuth || !state.openaiAuth) {
|
|
370
|
+
console.log();
|
|
371
|
+
printError('Authentication required');
|
|
372
|
+
printInfo('Running authentication flow...');
|
|
373
|
+
console.log();
|
|
374
|
+
const authenticated = await ensureAuthentication(state);
|
|
375
|
+
if (!authenticated) {
|
|
376
|
+
printWarning('Skipping project creation - authentication incomplete');
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
281
379
|
}
|
|
282
|
-
printSection('Creating Project');
|
|
283
|
-
printKeyValue('Idea', idea);
|
|
284
|
-
printKeyValue('Language', state.language);
|
|
285
|
-
printKeyValue('Model', state.model);
|
|
286
380
|
console.log();
|
|
287
|
-
|
|
381
|
+
console.log(theme.primary.bold(' Creating Project'));
|
|
382
|
+
console.log(` ${theme.dim('Idea:')} ${idea}`);
|
|
383
|
+
console.log(` ${theme.dim('Language:')} ${theme.primary(state.language)}`);
|
|
384
|
+
console.log(` ${theme.dim('Model:')} ${theme.secondary(state.model)}`);
|
|
385
|
+
console.log();
|
|
386
|
+
// Generate project name from idea
|
|
288
387
|
const projectName = idea
|
|
289
388
|
.toLowerCase()
|
|
290
389
|
.replace(/[^a-z0-9\s]/g, '')
|
|
@@ -311,20 +410,77 @@ async function handleIdea(idea, state) {
|
|
|
311
410
|
}
|
|
312
411
|
succeedSpinner(`Created ${scaffoldResult.filesCreated.length} files`);
|
|
313
412
|
// Run workflow
|
|
413
|
+
console.log();
|
|
414
|
+
printInfo('Starting AI workflow...');
|
|
415
|
+
console.log();
|
|
314
416
|
const workflowResult = await runWorkflow(spec, {
|
|
315
417
|
projectDir,
|
|
316
418
|
onProgress: (phase, message) => {
|
|
317
|
-
console.log(` [${phase}] ${message}`);
|
|
419
|
+
console.log(` ${theme.dim(`[${phase}]`)} ${message}`);
|
|
318
420
|
},
|
|
319
421
|
});
|
|
320
422
|
stopSpinner();
|
|
423
|
+
console.log();
|
|
321
424
|
if (workflowResult.success) {
|
|
322
425
|
printSuccess('Project created successfully!');
|
|
323
|
-
|
|
426
|
+
console.log(` ${theme.dim('Location:')} ${projectDir}`);
|
|
324
427
|
state.projectDir = projectDir;
|
|
325
428
|
}
|
|
326
429
|
else {
|
|
327
430
|
printError(workflowResult.error || 'Workflow failed');
|
|
328
431
|
}
|
|
329
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* Start interactive mode with auto-authentication
|
|
435
|
+
*/
|
|
436
|
+
export async function startInteractiveMode() {
|
|
437
|
+
console.clear();
|
|
438
|
+
// Initialize state
|
|
439
|
+
const config = await loadConfig();
|
|
440
|
+
const state = {
|
|
441
|
+
projectDir: process.cwd(),
|
|
442
|
+
language: config.project.default_language,
|
|
443
|
+
model: config.apis.openai.model,
|
|
444
|
+
claudeAuth: false,
|
|
445
|
+
openaiAuth: false,
|
|
446
|
+
};
|
|
447
|
+
// Draw header
|
|
448
|
+
drawHeader();
|
|
449
|
+
console.log();
|
|
450
|
+
// Check and perform authentication
|
|
451
|
+
const isAuthenticated = await ensureAuthentication(state);
|
|
452
|
+
if (!isAuthenticated) {
|
|
453
|
+
console.log();
|
|
454
|
+
printWarning('Some services are not authenticated. Some features may not work.');
|
|
455
|
+
printInfo('You can authenticate later with /auth');
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
console.log();
|
|
459
|
+
printSuccess('Ready! Type your project idea or /help for commands');
|
|
460
|
+
}
|
|
461
|
+
console.log();
|
|
462
|
+
// Create readline interface
|
|
463
|
+
const rl = readline.createInterface({
|
|
464
|
+
input: process.stdin,
|
|
465
|
+
output: process.stdout,
|
|
466
|
+
});
|
|
467
|
+
// Input loop
|
|
468
|
+
const promptUser = () => {
|
|
469
|
+
drawInputFrame(state);
|
|
470
|
+
rl.question(getPrompt(), async (input) => {
|
|
471
|
+
// Clear the input frame line
|
|
472
|
+
process.stdout.write('\x1b[1A\x1b[2K'); // Move up and clear
|
|
473
|
+
const shouldContinue = await handleInput(input, state);
|
|
474
|
+
if (shouldContinue) {
|
|
475
|
+
console.log();
|
|
476
|
+
promptUser();
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
rl.close();
|
|
480
|
+
process.exit(0);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
};
|
|
484
|
+
promptUser();
|
|
485
|
+
}
|
|
330
486
|
//# sourceMappingURL=interactive.js.map
|