popeye-cli 1.1.0 → 1.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/.env.example +24 -1
- package/CONTRIBUTING.md +275 -0
- package/OPEN_SOURCE_MANIFESTO.md +172 -0
- package/README.md +340 -27
- package/dist/adapters/claude.d.ts +28 -2
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +273 -20
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/grok.d.ts +73 -0
- package/dist/adapters/grok.d.ts.map +1 -0
- package/dist/adapters/grok.js +430 -0
- package/dist/adapters/grok.js.map +1 -0
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +6 -1
- package/dist/adapters/openai.js.map +1 -1
- package/dist/auth/grok.d.ts +73 -0
- package/dist/auth/grok.d.ts.map +1 -0
- package/dist/auth/grok.js +211 -0
- package/dist/auth/grok.js.map +1 -0
- package/dist/auth/index.d.ts +9 -6
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +23 -6
- package/dist/auth/index.js.map +1 -1
- package/dist/cli/commands/auth.d.ts +1 -1
- package/dist/cli/commands/auth.d.ts.map +1 -1
- package/dist/cli/commands/auth.js +79 -8
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/create.d.ts.map +1 -1
- package/dist/cli/commands/create.js +15 -4
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +406 -35
- package/dist/cli/interactive.js.map +1 -1
- package/dist/config/defaults.d.ts +3 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +9 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +16 -3
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +27 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +24 -3
- package/dist/config/schema.js.map +1 -1
- package/dist/generators/fullstack.d.ts +32 -0
- package/dist/generators/fullstack.d.ts.map +1 -0
- package/dist/generators/fullstack.js +497 -0
- package/dist/generators/fullstack.js.map +1 -0
- package/dist/generators/index.d.ts +4 -3
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +15 -1
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/python.d.ts +17 -1
- package/dist/generators/python.d.ts.map +1 -1
- package/dist/generators/python.js +34 -21
- package/dist/generators/python.js.map +1 -1
- package/dist/generators/templates/fullstack.d.ts +113 -0
- package/dist/generators/templates/fullstack.d.ts.map +1 -0
- package/dist/generators/templates/fullstack.js +1004 -0
- package/dist/generators/templates/fullstack.js.map +1 -0
- package/dist/generators/typescript.d.ts +19 -1
- package/dist/generators/typescript.d.ts.map +1 -1
- package/dist/generators/typescript.js +37 -21
- package/dist/generators/typescript.js.map +1 -1
- package/dist/types/cli.d.ts +4 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/cli.js.map +1 -1
- package/dist/types/consensus.d.ts +119 -2
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +12 -1
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/project.d.ts +76 -0
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +1 -1
- package/dist/types/project.js.map +1 -1
- package/dist/types/workflow.d.ts +170 -16
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +26 -3
- package/dist/types/workflow.js.map +1 -1
- package/dist/workflow/consensus.d.ts +29 -3
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +334 -27
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/execution-mode.d.ts +2 -0
- package/dist/workflow/execution-mode.d.ts.map +1 -1
- package/dist/workflow/execution-mode.js +20 -0
- package/dist/workflow/execution-mode.js.map +1 -1
- package/dist/workflow/index.d.ts +2 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +11 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/milestone-workflow.d.ts +2 -0
- package/dist/workflow/milestone-workflow.d.ts.map +1 -1
- package/dist/workflow/milestone-workflow.js +19 -2
- package/dist/workflow/milestone-workflow.js.map +1 -1
- package/dist/workflow/plan-mode.d.ts +66 -2
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +187 -11
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/plan-storage.d.ts +252 -8
- package/dist/workflow/plan-storage.d.ts.map +1 -1
- package/dist/workflow/plan-storage.js +580 -33
- package/dist/workflow/plan-storage.js.map +1 -1
- package/dist/workflow/project-verification.js +1 -1
- package/dist/workflow/project-verification.js.map +1 -1
- package/dist/workflow/task-workflow.d.ts +2 -0
- package/dist/workflow/task-workflow.d.ts.map +1 -1
- package/dist/workflow/task-workflow.js +23 -1
- package/dist/workflow/task-workflow.js.map +1 -1
- package/dist/workflow/test-runner.d.ts +8 -0
- package/dist/workflow/test-runner.d.ts.map +1 -1
- package/dist/workflow/test-runner.js +92 -0
- package/dist/workflow/test-runner.js.map +1 -1
- package/dist/workflow/workspace-manager.d.ts +342 -0
- package/dist/workflow/workspace-manager.d.ts.map +1 -0
- package/dist/workflow/workspace-manager.js +733 -0
- package/dist/workflow/workspace-manager.js.map +1 -0
- package/package.json +1 -1
- package/src/adapters/claude.ts +322 -25
- package/src/adapters/grok.ts +492 -0
- package/src/adapters/openai.ts +8 -2
- package/src/auth/grok.ts +255 -0
- package/src/auth/index.ts +27 -9
- package/src/cli/commands/auth.ts +89 -10
- package/src/cli/commands/create.ts +13 -4
- package/src/cli/interactive.ts +453 -34
- package/src/config/defaults.ts +9 -0
- package/src/config/index.ts +17 -3
- package/src/config/schema.ts +25 -3
- package/src/generators/fullstack.ts +551 -0
- package/src/generators/index.ts +25 -1
- package/src/generators/python.ts +65 -21
- package/src/generators/templates/fullstack.ts +1047 -0
- package/src/generators/typescript.ts +69 -21
- package/src/types/cli.ts +4 -0
- package/src/types/consensus.ts +135 -3
- package/src/types/project.ts +82 -1
- package/src/types/workflow.ts +58 -4
- package/src/workflow/consensus.ts +461 -31
- package/src/workflow/execution-mode.ts +32 -0
- package/src/workflow/index.ts +12 -0
- package/src/workflow/milestone-workflow.ts +24 -2
- package/src/workflow/plan-mode.ts +238 -10
- package/src/workflow/plan-storage.ts +835 -35
- package/src/workflow/project-verification.ts +1 -1
- package/src/workflow/task-workflow.ts +29 -1
- package/src/workflow/test-runner.ts +110 -0
- package/src/workflow/workspace-manager.ts +912 -0
package/dist/cli/interactive.js
CHANGED
|
@@ -5,13 +5,149 @@
|
|
|
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 { getAuthStatusForDisplay, authenticateClaude, authenticateOpenAI, authenticateGemini, isClaudeCLIInstalled, checkClaudeCLIAuth, checkGeminiAuth, } from '../auth/index.js';
|
|
8
|
+
import { getAuthStatusForDisplay, authenticateClaude, authenticateOpenAI, authenticateGemini, authenticateGrok, isClaudeCLIInstalled, checkClaudeCLIAuth, checkGeminiAuth, checkGrokAuth, } from '../auth/index.js';
|
|
9
9
|
import { runWorkflow, resumeWorkflow, getWorkflowStatus, getWorkflowSummary, } from '../workflow/index.js';
|
|
10
10
|
import { analyzeProjectProgress, verifyProjectCompletion, } from '../state/index.js';
|
|
11
11
|
import { generateProject } from '../generators/index.js';
|
|
12
12
|
import { discoverProjects, formatProjectForDisplay, } from '../state/registry.js';
|
|
13
13
|
import { loadConfig, saveConfig } from '../config/index.js';
|
|
14
14
|
import { printSuccess, printError, printWarning, printInfo, printKeyValue, startSpinner, succeedSpinner, failSpinner, stopSpinner, theme, } from './output.js';
|
|
15
|
+
/**
|
|
16
|
+
* Read popeye.md from project directory
|
|
17
|
+
* Returns null if file doesn't exist
|
|
18
|
+
*/
|
|
19
|
+
async function readPopeyeConfig(projectDir) {
|
|
20
|
+
const configPath = path.join(projectDir, 'popeye.md');
|
|
21
|
+
try {
|
|
22
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
23
|
+
// Parse YAML frontmatter
|
|
24
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
25
|
+
if (!frontmatterMatch) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const frontmatter = frontmatterMatch[1];
|
|
29
|
+
const config = {};
|
|
30
|
+
// Parse each line of YAML
|
|
31
|
+
for (const line of frontmatter.split('\n')) {
|
|
32
|
+
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
33
|
+
if (match) {
|
|
34
|
+
const [, key, value] = match;
|
|
35
|
+
const cleanValue = value.trim();
|
|
36
|
+
switch (key) {
|
|
37
|
+
case 'language':
|
|
38
|
+
if (['python', 'typescript', 'fullstack'].includes(cleanValue)) {
|
|
39
|
+
config.language = cleanValue;
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
case 'reviewer':
|
|
43
|
+
if (['openai', 'gemini', 'grok'].includes(cleanValue)) {
|
|
44
|
+
config.reviewer = cleanValue;
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
case 'arbitrator':
|
|
48
|
+
if (['openai', 'gemini', 'grok', 'off'].includes(cleanValue)) {
|
|
49
|
+
if (cleanValue === 'off') {
|
|
50
|
+
config.enableArbitration = false;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
config.arbitrator = cleanValue;
|
|
54
|
+
config.enableArbitration = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
case 'created':
|
|
59
|
+
config.created = cleanValue;
|
|
60
|
+
break;
|
|
61
|
+
case 'lastRun':
|
|
62
|
+
config.lastRun = cleanValue;
|
|
63
|
+
break;
|
|
64
|
+
case 'projectName':
|
|
65
|
+
config.projectName = cleanValue;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Extract notes section if present
|
|
71
|
+
const notesMatch = content.match(/## Notes\n([\s\S]*?)(?=\n## |$)/);
|
|
72
|
+
if (notesMatch) {
|
|
73
|
+
config.notes = notesMatch[1].trim();
|
|
74
|
+
}
|
|
75
|
+
// Return config only if we have the essential fields
|
|
76
|
+
if (config.language && config.reviewer) {
|
|
77
|
+
return {
|
|
78
|
+
language: config.language,
|
|
79
|
+
reviewer: config.reviewer,
|
|
80
|
+
arbitrator: config.arbitrator || 'gemini',
|
|
81
|
+
enableArbitration: config.enableArbitration ?? true,
|
|
82
|
+
created: config.created || new Date().toISOString(),
|
|
83
|
+
lastRun: config.lastRun || new Date().toISOString(),
|
|
84
|
+
projectName: config.projectName,
|
|
85
|
+
description: config.description,
|
|
86
|
+
notes: config.notes,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Write popeye.md to project directory
|
|
97
|
+
*/
|
|
98
|
+
async function writePopeyeConfig(projectDir, config) {
|
|
99
|
+
const configPath = path.join(projectDir, 'popeye.md');
|
|
100
|
+
const content = `---
|
|
101
|
+
# Popeye Project Configuration
|
|
102
|
+
language: ${config.language}
|
|
103
|
+
reviewer: ${config.reviewer}
|
|
104
|
+
arbitrator: ${config.enableArbitration ? config.arbitrator : 'off'}
|
|
105
|
+
created: ${config.created}
|
|
106
|
+
lastRun: ${new Date().toISOString()}
|
|
107
|
+
${config.projectName ? `projectName: ${config.projectName}` : ''}
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
# ${config.projectName || 'Popeye Project'}
|
|
111
|
+
|
|
112
|
+
${config.description ? `## Description\n${config.description}\n` : ''}
|
|
113
|
+
## Notes
|
|
114
|
+
${config.notes || 'Add any guidance or notes for Claude here...'}
|
|
115
|
+
|
|
116
|
+
## Configuration
|
|
117
|
+
- **Language**: ${config.language}
|
|
118
|
+
- **Reviewer**: ${config.reviewer}
|
|
119
|
+
- **Arbitrator**: ${config.enableArbitration ? config.arbitrator : 'disabled'}
|
|
120
|
+
|
|
121
|
+
## Session History
|
|
122
|
+
- ${config.created.split('T')[0]}: Project created
|
|
123
|
+
- ${new Date().toISOString().split('T')[0]}: Last session
|
|
124
|
+
`;
|
|
125
|
+
await fs.writeFile(configPath, content, 'utf-8');
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Update lastRun in popeye.md without changing other content
|
|
129
|
+
*/
|
|
130
|
+
async function updatePopeyeLastRun(projectDir) {
|
|
131
|
+
const configPath = path.join(projectDir, 'popeye.md');
|
|
132
|
+
try {
|
|
133
|
+
let content = await fs.readFile(configPath, 'utf-8');
|
|
134
|
+
// Update lastRun in frontmatter
|
|
135
|
+
content = content.replace(/lastRun:\s*.+/, `lastRun: ${new Date().toISOString()}`);
|
|
136
|
+
await fs.writeFile(configPath, content, 'utf-8');
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// File doesn't exist, ignore
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Apply popeye.md config to session state
|
|
144
|
+
*/
|
|
145
|
+
function applyPopeyeConfig(state, config) {
|
|
146
|
+
state.language = config.language;
|
|
147
|
+
state.reviewer = config.reviewer;
|
|
148
|
+
state.arbitrator = config.arbitrator;
|
|
149
|
+
state.enableArbitration = config.enableArbitration;
|
|
150
|
+
}
|
|
15
151
|
// Note: startSpinner, succeedSpinner, failSpinner, stopSpinner are used in handleIdea
|
|
16
152
|
/**
|
|
17
153
|
* Box drawing characters for Claude Code-style UI
|
|
@@ -65,17 +201,25 @@ function drawInputBoxTop(state) {
|
|
|
65
201
|
const width = Math.min(getTerminalWidth(), 100);
|
|
66
202
|
// Hints line (above the box)
|
|
67
203
|
const hints = [
|
|
68
|
-
theme.dim('/lang ') + theme.primary('py') + theme.dim('|') + theme.primary('ts'),
|
|
204
|
+
theme.dim('/lang ') + theme.primary('py') + theme.dim('|') + theme.primary('ts') + theme.dim('|') + theme.primary('fs'),
|
|
69
205
|
theme.dim('/config'),
|
|
70
206
|
theme.dim('/help'),
|
|
71
207
|
theme.dim('/exit'),
|
|
72
208
|
];
|
|
73
209
|
console.log(' ' + hints.join(' '));
|
|
74
210
|
// Status items for the top line
|
|
75
|
-
const langStatus = state.language;
|
|
76
|
-
const reviewerStatus = state.reviewer === 'openai' ? 'O' : 'G';
|
|
77
|
-
const arbitratorStatus = state.enableArbitration
|
|
78
|
-
|
|
211
|
+
const langStatus = state.language === 'fullstack' ? 'fs' : state.language;
|
|
212
|
+
const reviewerStatus = state.reviewer === 'openai' ? 'O' : state.reviewer === 'grok' ? 'X' : 'G';
|
|
213
|
+
const arbitratorStatus = state.enableArbitration
|
|
214
|
+
? (state.arbitrator === 'openai' ? 'O' : state.arbitrator === 'grok' ? 'X' : 'G')
|
|
215
|
+
: '-';
|
|
216
|
+
// Check auth based on which providers are configured
|
|
217
|
+
const reviewerAuthed = state.reviewer === 'openai' ? state.openaiAuth
|
|
218
|
+
: state.reviewer === 'grok' ? state.grokAuth : state.geminiAuth;
|
|
219
|
+
const arbitratorAuthed = !state.enableArbitration ? true
|
|
220
|
+
: state.arbitrator === 'openai' ? state.openaiAuth
|
|
221
|
+
: state.arbitrator === 'grok' ? state.grokAuth : state.geminiAuth;
|
|
222
|
+
const allAuth = state.claudeAuth && reviewerAuthed && arbitratorAuthed;
|
|
79
223
|
const authIcon = allAuth ? '●' : '○';
|
|
80
224
|
const authColor = allAuth ? theme.success : theme.warning;
|
|
81
225
|
// Build status text
|
|
@@ -217,6 +361,8 @@ async function ensureAuthentication(state) {
|
|
|
217
361
|
state.claudeAuth = status.claude.authenticated;
|
|
218
362
|
state.openaiAuth = status.openai.authenticated;
|
|
219
363
|
state.geminiAuth = status.gemini?.authenticated || false;
|
|
364
|
+
const grokStatus = await checkGrokAuth();
|
|
365
|
+
state.grokAuth = grokStatus.authenticated;
|
|
220
366
|
console.log();
|
|
221
367
|
printInfo('Checking authentication...');
|
|
222
368
|
console.log();
|
|
@@ -275,16 +421,19 @@ async function ensureAuthentication(state) {
|
|
|
275
421
|
state.reviewer = await promptSelection('Who should review Claude\'s plans?', [
|
|
276
422
|
{ label: theme.secondary('OpenAI') + theme.dim(' - GPT-4o reviews plans'), value: 'openai' },
|
|
277
423
|
{ label: theme.secondary('Gemini') + theme.dim(' - Gemini 2.0 reviews plans'), value: 'gemini' },
|
|
424
|
+
{ label: theme.secondary('Grok') + theme.dim(' - xAI Grok reviews plans'), value: 'grok' },
|
|
278
425
|
], 'openai');
|
|
279
426
|
// Ask about arbitration
|
|
280
427
|
console.log();
|
|
281
428
|
state.enableArbitration = await promptYesNo(theme.primary('Enable arbitration when consensus is stuck?'), true);
|
|
282
429
|
if (state.enableArbitration) {
|
|
283
|
-
// Auto-select
|
|
284
|
-
const defaultArbitrator = state.reviewer === 'openai' ? 'gemini'
|
|
430
|
+
// Auto-select a different provider as arbitrator
|
|
431
|
+
const defaultArbitrator = state.reviewer === 'openai' ? 'gemini'
|
|
432
|
+
: state.reviewer === 'gemini' ? 'openai' : 'gemini';
|
|
285
433
|
state.arbitrator = await promptSelection('Who should arbitrate when stuck?', [
|
|
286
434
|
{ label: theme.secondary('Gemini') + theme.dim(' - Google Gemini breaks deadlocks'), value: 'gemini' },
|
|
287
435
|
{ label: theme.secondary('OpenAI') + theme.dim(' - OpenAI breaks deadlocks'), value: 'openai' },
|
|
436
|
+
{ label: theme.secondary('Grok') + theme.dim(' - xAI Grok breaks deadlocks'), value: 'grok' },
|
|
288
437
|
], defaultArbitrator);
|
|
289
438
|
// Authenticate Gemini if needed for reviewer or arbitrator
|
|
290
439
|
const needsGemini = state.reviewer === 'gemini' || state.arbitrator === 'gemini';
|
|
@@ -308,6 +457,39 @@ async function ensureAuthentication(state) {
|
|
|
308
457
|
state.enableArbitration = false;
|
|
309
458
|
}
|
|
310
459
|
}
|
|
460
|
+
// Authenticate Grok if needed for reviewer or arbitrator
|
|
461
|
+
const needsGrok = state.reviewer === 'grok' || state.arbitrator === 'grok';
|
|
462
|
+
if (needsGrok && !state.grokAuth) {
|
|
463
|
+
console.log();
|
|
464
|
+
console.log(theme.dim(box.vertical) + ' ' + theme.primary('Grok API') + theme.dim(' - Required for ' + (state.reviewer === 'grok' ? 'review' : 'arbitration')));
|
|
465
|
+
console.log(theme.dim(box.vertical));
|
|
466
|
+
try {
|
|
467
|
+
const success = await authenticateGrok();
|
|
468
|
+
if (success) {
|
|
469
|
+
printSuccess('Grok API ready');
|
|
470
|
+
state.grokAuth = true;
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
printWarning('Grok API not authenticated');
|
|
474
|
+
if (state.reviewer === 'grok') {
|
|
475
|
+
printWarning('Falling back to OpenAI as reviewer');
|
|
476
|
+
state.reviewer = 'openai';
|
|
477
|
+
}
|
|
478
|
+
if (state.arbitrator === 'grok') {
|
|
479
|
+
state.enableArbitration = false;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
printError(err instanceof Error ? err.message : 'Authentication failed');
|
|
485
|
+
if (state.reviewer === 'grok') {
|
|
486
|
+
state.reviewer = 'openai';
|
|
487
|
+
}
|
|
488
|
+
if (state.arbitrator === 'grok') {
|
|
489
|
+
state.enableArbitration = false;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
311
493
|
}
|
|
312
494
|
// Also check if reviewer is gemini and we need to auth
|
|
313
495
|
if (state.reviewer === 'gemini' && !state.geminiAuth) {
|
|
@@ -330,21 +512,46 @@ async function ensureAuthentication(state) {
|
|
|
330
512
|
state.reviewer = 'openai';
|
|
331
513
|
}
|
|
332
514
|
}
|
|
515
|
+
// Also check if reviewer is grok and we need to auth
|
|
516
|
+
if (state.reviewer === 'grok' && !state.grokAuth) {
|
|
517
|
+
console.log();
|
|
518
|
+
console.log(theme.dim(box.vertical) + ' ' + theme.primary('Grok API') + theme.dim(' - Required for review'));
|
|
519
|
+
console.log(theme.dim(box.vertical));
|
|
520
|
+
try {
|
|
521
|
+
const success = await authenticateGrok();
|
|
522
|
+
if (success) {
|
|
523
|
+
printSuccess('Grok API ready');
|
|
524
|
+
state.grokAuth = true;
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
printWarning('Grok API not authenticated - falling back to OpenAI');
|
|
528
|
+
state.reviewer = 'openai';
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
catch (err) {
|
|
532
|
+
printError(err instanceof Error ? err.message : 'Authentication failed');
|
|
533
|
+
state.reviewer = 'openai';
|
|
534
|
+
}
|
|
535
|
+
}
|
|
333
536
|
// Save the configuration to persist between sessions
|
|
334
537
|
await saveConsensusConfig(state);
|
|
335
538
|
// Show summary
|
|
336
539
|
console.log();
|
|
337
540
|
console.log(theme.secondary(' Configuration saved. Use /config to change later.'));
|
|
338
|
-
|
|
339
|
-
|
|
541
|
+
const reviewerName = state.reviewer === 'openai' ? 'OpenAI (GPT-4o)' : state.reviewer === 'grok' ? 'Grok' : 'Gemini';
|
|
542
|
+
const arbitratorName = state.arbitrator === 'openai' ? 'OpenAI' : state.arbitrator === 'grok' ? 'Grok' : 'Gemini';
|
|
543
|
+
console.log(` ${theme.dim('Reviewer:')} ${theme.primary(reviewerName)}`);
|
|
544
|
+
console.log(` ${theme.dim('Arbitrator:')} ${state.enableArbitration ? theme.primary(arbitratorName) : theme.dim('Disabled')}`);
|
|
340
545
|
console.log();
|
|
341
546
|
}
|
|
342
547
|
else if (state.claudeAuth && state.openaiAuth && alreadyConfigured) {
|
|
343
548
|
// Show loaded configuration
|
|
344
549
|
console.log();
|
|
345
550
|
console.log(theme.secondary(' Using saved configuration (use /config to change):'));
|
|
346
|
-
|
|
347
|
-
|
|
551
|
+
const savedReviewerName = state.reviewer === 'openai' ? 'OpenAI (GPT-4o)' : state.reviewer === 'grok' ? 'Grok' : 'Gemini';
|
|
552
|
+
const savedArbitratorName = state.arbitrator === 'openai' ? 'OpenAI' : state.arbitrator === 'grok' ? 'Grok' : 'Gemini';
|
|
553
|
+
console.log(` ${theme.dim('Reviewer:')} ${theme.primary(savedReviewerName)}`);
|
|
554
|
+
console.log(` ${theme.dim('Arbitrator:')} ${state.enableArbitration ? theme.primary(savedArbitratorName) : theme.dim('Disabled')}`);
|
|
348
555
|
console.log();
|
|
349
556
|
// Authenticate Gemini if needed based on saved config
|
|
350
557
|
const needsGemini = state.reviewer === 'gemini' || (state.enableArbitration && state.arbitrator === 'gemini');
|
|
@@ -374,6 +581,34 @@ async function ensureAuthentication(state) {
|
|
|
374
581
|
}
|
|
375
582
|
console.log();
|
|
376
583
|
}
|
|
584
|
+
// Authenticate Grok if needed based on saved config
|
|
585
|
+
const needsGrok = state.reviewer === 'grok' || (state.enableArbitration && state.arbitrator === 'grok');
|
|
586
|
+
if (needsGrok && !state.grokAuth) {
|
|
587
|
+
console.log(theme.dim(box.vertical) + ' ' + theme.primary('Grok API') + theme.dim(' - Required for ' + (state.reviewer === 'grok' ? 'review' : 'arbitration')));
|
|
588
|
+
console.log(theme.dim(box.vertical));
|
|
589
|
+
try {
|
|
590
|
+
const success = await authenticateGrok();
|
|
591
|
+
if (success) {
|
|
592
|
+
printSuccess('Grok API ready');
|
|
593
|
+
state.grokAuth = true;
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
printWarning('Grok API not authenticated');
|
|
597
|
+
if (state.reviewer === 'grok') {
|
|
598
|
+
printWarning('Falling back to OpenAI as reviewer');
|
|
599
|
+
state.reviewer = 'openai';
|
|
600
|
+
}
|
|
601
|
+
if (state.enableArbitration && state.arbitrator === 'grok') {
|
|
602
|
+
printWarning('Disabling arbitration');
|
|
603
|
+
state.enableArbitration = false;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
catch (err) {
|
|
608
|
+
printError(err instanceof Error ? err.message : 'Grok authentication failed');
|
|
609
|
+
}
|
|
610
|
+
console.log();
|
|
611
|
+
}
|
|
377
612
|
}
|
|
378
613
|
return state.claudeAuth && state.openaiAuth;
|
|
379
614
|
}
|
|
@@ -390,9 +625,9 @@ function showHelp() {
|
|
|
390
625
|
['/status', 'Show current project status'],
|
|
391
626
|
['/auth', 'Re-authenticate services'],
|
|
392
627
|
['/config', 'Show/change configuration'],
|
|
393
|
-
['/config reviewer', 'Set reviewer (openai/gemini)'],
|
|
394
|
-
['/config arbitrator', 'Set arbitrator (openai/gemini/off)'],
|
|
395
|
-
['/lang <lang>', 'Set language (python/typescript)'],
|
|
628
|
+
['/config reviewer', 'Set reviewer (openai/gemini/grok)'],
|
|
629
|
+
['/config arbitrator', 'Set arbitrator (openai/gemini/grok/off)'],
|
|
630
|
+
['/lang <lang>', 'Set language (python/typescript/fullstack)'],
|
|
396
631
|
['/new <idea>', 'Force start a new project (skips existing check)'],
|
|
397
632
|
['/resume', 'Resume interrupted project'],
|
|
398
633
|
['/clear', 'Clear screen'],
|
|
@@ -446,6 +681,13 @@ async function handleInfo() {
|
|
|
446
681
|
console.log(` ${theme.dim('API Key:')} ${theme.dim(geminiStatus.keyLastFour)}`);
|
|
447
682
|
}
|
|
448
683
|
console.log();
|
|
684
|
+
console.log(theme.secondary(' Grok:'));
|
|
685
|
+
const grokStatus = await checkGrokAuth();
|
|
686
|
+
console.log(` ${theme.dim('Authenticated:')} ${grokStatus.authenticated ? theme.success('Yes') : theme.dim('No')}`);
|
|
687
|
+
if (grokStatus.authenticated && grokStatus.keyLastFour) {
|
|
688
|
+
console.log(` ${theme.dim('API Key:')} ${theme.dim(grokStatus.keyLastFour)}`);
|
|
689
|
+
}
|
|
690
|
+
console.log();
|
|
449
691
|
console.log(theme.secondary(' Environment:'));
|
|
450
692
|
console.log(` ${theme.dim('Node.js:')} ${process.version}`);
|
|
451
693
|
console.log(` ${theme.dim('Platform:')} ${process.platform}`);
|
|
@@ -569,33 +811,41 @@ async function handleConfig(state, args = []) {
|
|
|
569
811
|
case 'reviewer':
|
|
570
812
|
if (args.length > 1) {
|
|
571
813
|
const newReviewer = args[1].toLowerCase();
|
|
572
|
-
if (newReviewer === 'openai' || newReviewer === 'gemini') {
|
|
814
|
+
if (newReviewer === 'openai' || newReviewer === 'gemini' || newReviewer === 'grok') {
|
|
573
815
|
if (newReviewer === 'gemini' && !state.geminiAuth) {
|
|
574
816
|
printWarning('Gemini API not authenticated. Run /auth first.');
|
|
575
817
|
return;
|
|
576
818
|
}
|
|
819
|
+
if (newReviewer === 'grok' && !state.grokAuth) {
|
|
820
|
+
printWarning('Grok API not authenticated. Run /auth first.');
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
577
823
|
state.reviewer = newReviewer;
|
|
578
824
|
// Save to config
|
|
579
825
|
await saveConsensusConfig(state);
|
|
580
826
|
printSuccess(`Reviewer set to ${newReviewer}`);
|
|
581
827
|
}
|
|
582
828
|
else {
|
|
583
|
-
printError('Invalid reviewer. Use: openai or
|
|
829
|
+
printError('Invalid reviewer. Use: openai, gemini, or grok');
|
|
584
830
|
}
|
|
585
831
|
}
|
|
586
832
|
else {
|
|
587
833
|
printKeyValue('Reviewer', state.reviewer);
|
|
588
|
-
printInfo('Use: /config reviewer <openai|gemini>');
|
|
834
|
+
printInfo('Use: /config reviewer <openai|gemini|grok>');
|
|
589
835
|
}
|
|
590
836
|
return;
|
|
591
837
|
case 'arbitrator':
|
|
592
838
|
if (args.length > 1) {
|
|
593
839
|
const newArbitrator = args[1].toLowerCase();
|
|
594
|
-
if (newArbitrator === 'openai' || newArbitrator === 'gemini') {
|
|
840
|
+
if (newArbitrator === 'openai' || newArbitrator === 'gemini' || newArbitrator === 'grok') {
|
|
595
841
|
if (newArbitrator === 'gemini' && !state.geminiAuth) {
|
|
596
842
|
printWarning('Gemini API not authenticated. Run /auth first.');
|
|
597
843
|
return;
|
|
598
844
|
}
|
|
845
|
+
if (newArbitrator === 'grok' && !state.grokAuth) {
|
|
846
|
+
printWarning('Grok API not authenticated. Run /auth first.');
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
599
849
|
state.arbitrator = newArbitrator;
|
|
600
850
|
state.enableArbitration = true;
|
|
601
851
|
// Save to config
|
|
@@ -609,24 +859,34 @@ async function handleConfig(state, args = []) {
|
|
|
609
859
|
printSuccess('Arbitration disabled');
|
|
610
860
|
}
|
|
611
861
|
else {
|
|
612
|
-
printError('Invalid arbitrator. Use: openai, gemini, or off');
|
|
862
|
+
printError('Invalid arbitrator. Use: openai, gemini, grok, or off');
|
|
613
863
|
}
|
|
614
864
|
}
|
|
615
865
|
else {
|
|
616
866
|
printKeyValue('Arbitrator', state.enableArbitration ? state.arbitrator : 'disabled');
|
|
617
|
-
printInfo('Use: /config arbitrator <openai|gemini|off>');
|
|
867
|
+
printInfo('Use: /config arbitrator <openai|gemini|grok|off>');
|
|
618
868
|
}
|
|
619
869
|
return;
|
|
620
870
|
case 'language':
|
|
621
871
|
case 'lang':
|
|
622
872
|
if (args.length > 1) {
|
|
623
|
-
|
|
624
|
-
|
|
873
|
+
// Map shortcuts to full language names
|
|
874
|
+
const langAliases = {
|
|
875
|
+
'py': 'python',
|
|
876
|
+
'python': 'python',
|
|
877
|
+
'ts': 'typescript',
|
|
878
|
+
'typescript': 'typescript',
|
|
879
|
+
'fs': 'fullstack',
|
|
880
|
+
'fullstack': 'fullstack',
|
|
881
|
+
};
|
|
882
|
+
const input = args[1].toLowerCase();
|
|
883
|
+
const lang = langAliases[input];
|
|
884
|
+
if (lang) {
|
|
625
885
|
state.language = lang;
|
|
626
886
|
printSuccess(`Language set to ${lang}`);
|
|
627
887
|
}
|
|
628
888
|
else {
|
|
629
|
-
printError('Invalid language. Use:
|
|
889
|
+
printError('Invalid language. Use: py, ts, fs (or python, typescript, fullstack)');
|
|
630
890
|
}
|
|
631
891
|
}
|
|
632
892
|
else {
|
|
@@ -649,19 +909,22 @@ async function handleConfig(state, args = []) {
|
|
|
649
909
|
console.log(` ${theme.dim('Claude:')} ${state.claudeAuth ? theme.success('● Ready') : theme.error('○ Not authenticated')}`);
|
|
650
910
|
console.log(` ${theme.dim('OpenAI:')} ${state.openaiAuth ? theme.success('● Ready') : theme.error('○ Not authenticated')}`);
|
|
651
911
|
console.log(` ${theme.dim('Gemini:')} ${state.geminiAuth ? theme.success('● Ready') : theme.dim('○ Not configured')}`);
|
|
912
|
+
console.log(` ${theme.dim('Grok:')} ${state.grokAuth ? theme.success('● Ready') : theme.dim('○ Not configured')}`);
|
|
652
913
|
console.log();
|
|
653
914
|
console.log(theme.primary.bold(' AI Configuration:'));
|
|
654
|
-
|
|
655
|
-
|
|
915
|
+
const configReviewerName = state.reviewer === 'openai' ? 'OpenAI (GPT-4o)' : state.reviewer === 'grok' ? 'Grok' : 'Gemini';
|
|
916
|
+
const configArbitratorName = state.arbitrator === 'openai' ? 'OpenAI' : state.arbitrator === 'grok' ? 'Grok' : 'Gemini';
|
|
917
|
+
console.log(` ${theme.dim('Reviewer:')} ${theme.primary(configReviewerName)}`);
|
|
918
|
+
console.log(` ${theme.dim('Arbitrator:')} ${state.enableArbitration ? theme.primary(configArbitratorName) : theme.dim('Disabled')}`);
|
|
656
919
|
console.log();
|
|
657
920
|
console.log(theme.primary.bold(' Consensus:'));
|
|
658
921
|
console.log(` ${theme.dim('Threshold:')} ${config.consensus.threshold}%`);
|
|
659
922
|
console.log(` ${theme.dim('Max Iters:')} ${config.consensus.max_disagreements}`);
|
|
660
923
|
console.log();
|
|
661
924
|
console.log(theme.secondary(' Change settings:'));
|
|
662
|
-
console.log(theme.dim(' /config reviewer <openai|gemini>'));
|
|
663
|
-
console.log(theme.dim(' /config arbitrator <openai|gemini|off>'));
|
|
664
|
-
console.log(theme.dim(' /config language <python|typescript>'));
|
|
925
|
+
console.log(theme.dim(' /config reviewer <openai|gemini|grok>'));
|
|
926
|
+
console.log(theme.dim(' /config arbitrator <openai|gemini|grok|off>'));
|
|
927
|
+
console.log(theme.dim(' /config language <python|typescript|fullstack>'));
|
|
665
928
|
console.log();
|
|
666
929
|
}
|
|
667
930
|
/**
|
|
@@ -671,12 +934,22 @@ function handleLanguage(args, state) {
|
|
|
671
934
|
if (args.length === 0) {
|
|
672
935
|
console.log();
|
|
673
936
|
printKeyValue('Current language', state.language);
|
|
674
|
-
printInfo('Use /language <python|typescript> to change');
|
|
937
|
+
printInfo('Use /language <py|ts|fs> or <python|typescript|fullstack> to change');
|
|
675
938
|
return;
|
|
676
939
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
940
|
+
// Map shortcuts to full language names
|
|
941
|
+
const langAliases = {
|
|
942
|
+
'py': 'python',
|
|
943
|
+
'python': 'python',
|
|
944
|
+
'ts': 'typescript',
|
|
945
|
+
'typescript': 'typescript',
|
|
946
|
+
'fs': 'fullstack',
|
|
947
|
+
'fullstack': 'fullstack',
|
|
948
|
+
};
|
|
949
|
+
const input = args[0].toLowerCase();
|
|
950
|
+
const lang = langAliases[input];
|
|
951
|
+
if (!lang) {
|
|
952
|
+
printError('Invalid language. Use: py, ts, fs (or python, typescript, fullstack)');
|
|
680
953
|
return;
|
|
681
954
|
}
|
|
682
955
|
state.language = lang;
|
|
@@ -902,6 +1175,13 @@ async function handleResume(state, args) {
|
|
|
902
1175
|
printError('No project directory set');
|
|
903
1176
|
return;
|
|
904
1177
|
}
|
|
1178
|
+
// Check for popeye.md and load project-specific configuration
|
|
1179
|
+
const popeyeConfig = await readPopeyeConfig(state.projectDir);
|
|
1180
|
+
if (popeyeConfig) {
|
|
1181
|
+
applyPopeyeConfig(state, popeyeConfig);
|
|
1182
|
+
await updatePopeyeLastRun(state.projectDir);
|
|
1183
|
+
printInfo(`Loaded config from popeye.md (${popeyeConfig.language}, reviewer: ${popeyeConfig.reviewer})`);
|
|
1184
|
+
}
|
|
905
1185
|
const status = await getWorkflowStatus(state.projectDir);
|
|
906
1186
|
if (status.exists && status.state) {
|
|
907
1187
|
// Formal project state exists - analyze actual progress before resuming
|
|
@@ -1053,6 +1333,16 @@ async function handleResume(state, args) {
|
|
|
1053
1333
|
printSuccess('Workflow completed!');
|
|
1054
1334
|
console.log(` ${theme.dim('Location:')} ${state.projectDir}`);
|
|
1055
1335
|
}
|
|
1336
|
+
else if (result.rateLimitPaused) {
|
|
1337
|
+
// Rate limit pause - show friendly message, not an error
|
|
1338
|
+
console.log();
|
|
1339
|
+
console.log(` ${theme.warning('Rate Limit Reached')}`);
|
|
1340
|
+
console.log(` ${theme.dim(result.error || 'API rate limit exceeded')}`);
|
|
1341
|
+
console.log();
|
|
1342
|
+
console.log(` ${theme.info('Your progress has been saved.')}`);
|
|
1343
|
+
console.log(` ${theme.dim('Run')} ${theme.highlight('/resume')} ${theme.dim('after the rate limit resets to continue.')}`);
|
|
1344
|
+
console.log();
|
|
1345
|
+
}
|
|
1056
1346
|
else {
|
|
1057
1347
|
printError(result.error || 'Workflow failed');
|
|
1058
1348
|
printInfo('You can run /resume again with additional guidance');
|
|
@@ -1189,7 +1479,38 @@ async function handleResume(state, args) {
|
|
|
1189
1479
|
* Extracts key nouns and creates a kebab-case name
|
|
1190
1480
|
*/
|
|
1191
1481
|
function generateProjectName(idea) {
|
|
1192
|
-
//
|
|
1482
|
+
// 1. First, try to find explicit project name patterns
|
|
1483
|
+
const explicitPatterns = [
|
|
1484
|
+
/(?:called|named|for|planning|project)\s+["']?([A-Z][a-zA-Z0-9]+)["']?/i,
|
|
1485
|
+
/["']([A-Z][a-zA-Z0-9]+)["']/, // Quoted names
|
|
1486
|
+
/\b([A-Z][a-z]+(?:[A-Z][a-z]+)+)\b/, // CamelCase names like "TodoMaster"
|
|
1487
|
+
];
|
|
1488
|
+
for (const pattern of explicitPatterns) {
|
|
1489
|
+
const match = idea.match(pattern);
|
|
1490
|
+
if (match && match[1] && match[1].length >= 3 && match[1].length <= 30) {
|
|
1491
|
+
// Convert to kebab-case
|
|
1492
|
+
return match[1]
|
|
1493
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
1494
|
+
.toLowerCase()
|
|
1495
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
1496
|
+
.replace(/-+/g, '-')
|
|
1497
|
+
.replace(/^-|-$/g, '');
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
// 2. Look for standalone capitalized words (potential project names)
|
|
1501
|
+
// Exclude common capitalized words at sentence start
|
|
1502
|
+
const capitalizedWords = idea.match(/\b([A-Z][a-z]{2,})\b/g) || [];
|
|
1503
|
+
const excludeCapitalized = new Set([
|
|
1504
|
+
'Build', 'Create', 'Make', 'Develop', 'Write', 'Implement', 'Design',
|
|
1505
|
+
'Read', 'Start', 'Help', 'Please', 'Want', 'Need', 'Use', 'Add',
|
|
1506
|
+
'The', 'This', 'That', 'What', 'When', 'Where', 'How', 'Why',
|
|
1507
|
+
]);
|
|
1508
|
+
const projectNameCandidates = capitalizedWords.filter(w => !excludeCapitalized.has(w) && w.length >= 3);
|
|
1509
|
+
if (projectNameCandidates.length > 0) {
|
|
1510
|
+
// Use the first non-excluded capitalized word
|
|
1511
|
+
return projectNameCandidates[0].toLowerCase();
|
|
1512
|
+
}
|
|
1513
|
+
// 3. Fall back to extracting meaningful words
|
|
1193
1514
|
const stopWords = new Set([
|
|
1194
1515
|
'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
|
|
1195
1516
|
'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are', 'were', 'been',
|
|
@@ -1199,6 +1520,9 @@ function generateProjectName(idea) {
|
|
|
1199
1520
|
'want', 'like', 'please', 'help', 'me', 'i', 'my', 'we', 'our', 'you',
|
|
1200
1521
|
'your', 'that', 'which', 'who', 'what', 'where', 'when', 'why', 'how',
|
|
1201
1522
|
'this', 'these', 'those', 'it', 'its', 'simple', 'basic', 'new',
|
|
1523
|
+
// Action verbs that shouldn't be project names
|
|
1524
|
+
'read', 'start', 'planning', 'reading', 'starting', 'begin', 'beginning',
|
|
1525
|
+
'all', 'files', 'file', 'directory', 'folder', 'also',
|
|
1202
1526
|
]);
|
|
1203
1527
|
// Extract meaningful words
|
|
1204
1528
|
const words = idea
|
|
@@ -1348,6 +1672,18 @@ async function handleIdea(idea, state) {
|
|
|
1348
1672
|
return;
|
|
1349
1673
|
}
|
|
1350
1674
|
succeedSpinner(`Created ${scaffoldResult.filesCreated.length} files`);
|
|
1675
|
+
// Create popeye.md with project configuration
|
|
1676
|
+
await writePopeyeConfig(projectDir, {
|
|
1677
|
+
language: state.language,
|
|
1678
|
+
reviewer: state.reviewer,
|
|
1679
|
+
arbitrator: state.arbitrator,
|
|
1680
|
+
enableArbitration: state.enableArbitration,
|
|
1681
|
+
created: new Date().toISOString(),
|
|
1682
|
+
lastRun: new Date().toISOString(),
|
|
1683
|
+
projectName,
|
|
1684
|
+
description: idea,
|
|
1685
|
+
});
|
|
1686
|
+
printInfo('Created popeye.md with project configuration');
|
|
1351
1687
|
// Run workflow with reviewer/arbitrator settings
|
|
1352
1688
|
console.log();
|
|
1353
1689
|
printInfo('Starting AI workflow...');
|
|
@@ -1377,6 +1713,17 @@ async function handleIdea(idea, state) {
|
|
|
1377
1713
|
console.log(` ${theme.dim('Location:')} ${projectDir}`);
|
|
1378
1714
|
state.projectDir = projectDir;
|
|
1379
1715
|
}
|
|
1716
|
+
else if (workflowResult.rateLimitPaused) {
|
|
1717
|
+
// Rate limit pause - show friendly message, not an error
|
|
1718
|
+
console.log();
|
|
1719
|
+
console.log(` ${theme.warning('Rate Limit Reached')}`);
|
|
1720
|
+
console.log(` ${theme.dim(workflowResult.error || 'API rate limit exceeded')}`);
|
|
1721
|
+
console.log();
|
|
1722
|
+
console.log(` ${theme.info('Your progress has been saved.')}`);
|
|
1723
|
+
console.log(` ${theme.dim('Run')} ${theme.highlight('/resume')} ${theme.dim('after the rate limit resets to continue.')}`);
|
|
1724
|
+
console.log();
|
|
1725
|
+
state.projectDir = projectDir;
|
|
1726
|
+
}
|
|
1380
1727
|
else {
|
|
1381
1728
|
printError(workflowResult.error || 'Workflow failed');
|
|
1382
1729
|
}
|
|
@@ -1423,6 +1770,18 @@ async function handleNewProject(idea, state) {
|
|
|
1423
1770
|
return;
|
|
1424
1771
|
}
|
|
1425
1772
|
succeedSpinner(`Created ${scaffoldResult.filesCreated.length} files`);
|
|
1773
|
+
// Create popeye.md with project configuration
|
|
1774
|
+
await writePopeyeConfig(projectDir, {
|
|
1775
|
+
language: state.language,
|
|
1776
|
+
reviewer: state.reviewer,
|
|
1777
|
+
arbitrator: state.arbitrator,
|
|
1778
|
+
enableArbitration: state.enableArbitration,
|
|
1779
|
+
created: new Date().toISOString(),
|
|
1780
|
+
lastRun: new Date().toISOString(),
|
|
1781
|
+
projectName,
|
|
1782
|
+
description: idea,
|
|
1783
|
+
});
|
|
1784
|
+
printInfo('Created popeye.md with project configuration');
|
|
1426
1785
|
// Run workflow with reviewer/arbitrator settings
|
|
1427
1786
|
console.log();
|
|
1428
1787
|
printInfo('Starting AI workflow...');
|
|
@@ -1452,6 +1811,17 @@ async function handleNewProject(idea, state) {
|
|
|
1452
1811
|
console.log(` ${theme.dim('Location:')} ${projectDir}`);
|
|
1453
1812
|
state.projectDir = projectDir;
|
|
1454
1813
|
}
|
|
1814
|
+
else if (workflowResult.rateLimitPaused) {
|
|
1815
|
+
// Rate limit pause - show friendly message, not an error
|
|
1816
|
+
console.log();
|
|
1817
|
+
console.log(` ${theme.warning('Rate Limit Reached')}`);
|
|
1818
|
+
console.log(` ${theme.dim(workflowResult.error || 'API rate limit exceeded')}`);
|
|
1819
|
+
console.log();
|
|
1820
|
+
console.log(` ${theme.info('Your progress has been saved.')}`);
|
|
1821
|
+
console.log(` ${theme.dim('Run')} ${theme.highlight('/resume')} ${theme.dim('after the rate limit resets to continue.')}`);
|
|
1822
|
+
console.log();
|
|
1823
|
+
state.projectDir = projectDir;
|
|
1824
|
+
}
|
|
1455
1825
|
else {
|
|
1456
1826
|
printError(workflowResult.error || 'Workflow failed');
|
|
1457
1827
|
}
|
|
@@ -1471,6 +1841,7 @@ export async function startInteractiveMode() {
|
|
|
1471
1841
|
claudeAuth: false,
|
|
1472
1842
|
openaiAuth: false,
|
|
1473
1843
|
geminiAuth: false,
|
|
1844
|
+
grokAuth: false,
|
|
1474
1845
|
// Load saved reviewer/arbitrator settings from config
|
|
1475
1846
|
reviewer: config.consensus.reviewer,
|
|
1476
1847
|
arbitrator: config.consensus.arbitrator === 'off' ? 'openai' : config.consensus.arbitrator,
|
|
@@ -1485,7 +1856,7 @@ export async function startInteractiveMode() {
|
|
|
1485
1856
|
console.log(theme.dim(' ├─ ') + theme.secondary('Reviewer (configurable)') + theme.dim(' - Reviews plans until consensus'));
|
|
1486
1857
|
console.log(theme.dim(' └─ ') + theme.secondary('Arbitrator (optional)') + theme.dim(' - Breaks deadlocks when stuck'));
|
|
1487
1858
|
console.log();
|
|
1488
|
-
console.log(theme.dim(' You can choose OpenAI or
|
|
1859
|
+
console.log(theme.dim(' You can choose OpenAI, Gemini, or Grok as reviewer/arbitrator during setup.'));
|
|
1489
1860
|
console.log(theme.dim(' Plans are saved to docs/ folder in markdown format.'));
|
|
1490
1861
|
console.log();
|
|
1491
1862
|
// Check and perform authentication
|