vibepup 1.0.3 → 1.0.4
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 +51 -0
- package/bin/ralph.js +24 -24
- package/lib/ralph-win.js +4 -397
- package/lib/ralph.sh +2 -264
- package/lib/runner/index.js +578 -0
- package/package.json +8 -3
- package/test/cli.test.js +139 -0
- package/tui/go.mod +14 -9
- package/tui/go.sum +6 -0
- package/tui/main_test.go +58 -0
- package/tui/vibepup-tui +0 -0
- package/vibepup-1.0.3.tgz +0 -0
package/README.md
CHANGED
|
@@ -93,6 +93,11 @@ vibepup --tui
|
|
|
93
93
|
vibepup free
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
+
### 1f. Doctor (diagnose setup issues)
|
|
97
|
+
```bash
|
|
98
|
+
vibepup doctor
|
|
99
|
+
```
|
|
100
|
+
|
|
96
101
|
### 2. Fetch!
|
|
97
102
|
Go to any empty folder and tell Vibepup what to build.
|
|
98
103
|
|
|
@@ -136,6 +141,52 @@ Vibepup works out of the box. For the easiest free-tier bootstrap, run:
|
|
|
136
141
|
vibepup free
|
|
137
142
|
```
|
|
138
143
|
|
|
144
|
+
### ✅ Full Onboarding (step-by-step)
|
|
145
|
+
|
|
146
|
+
#### Step 0: Diagnose (recommended)
|
|
147
|
+
```bash
|
|
148
|
+
vibepup doctor
|
|
149
|
+
```
|
|
150
|
+
This checks Node/npm/opencode and your model registry.
|
|
151
|
+
|
|
152
|
+
#### Step 1: Install Node 20+
|
|
153
|
+
Free auth requires Node 20+.
|
|
154
|
+
- **WSL/Linux:** `nvm install 20 && nvm use 20`
|
|
155
|
+
- **Windows:** https://nodejs.org/en/download
|
|
156
|
+
|
|
157
|
+
#### Step 2: Fix npm permissions (WSL/Linux)
|
|
158
|
+
If npm fails with `EACCES`:
|
|
159
|
+
```bash
|
|
160
|
+
mkdir -p ~/.npm-global
|
|
161
|
+
npm config set prefix ~/.npm-global
|
|
162
|
+
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
|
|
163
|
+
source ~/.bashrc
|
|
164
|
+
```
|
|
165
|
+
Then retry the install.
|
|
166
|
+
|
|
167
|
+
#### Step 3: Install opencode
|
|
168
|
+
```bash
|
|
169
|
+
npm install -g opencode-ai
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Step 4: Free-tier auth
|
|
173
|
+
```bash
|
|
174
|
+
npm install -g opencode-antigravity-auth
|
|
175
|
+
opencode auth login antigravity
|
|
176
|
+
opencode models --refresh
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
If you cannot open a browser:
|
|
180
|
+
```bash
|
|
181
|
+
opencode auth print-token antigravity
|
|
182
|
+
export OPENCODE_ANTIGRAVITY_TOKEN="<token>"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Step 5: Run Vibepup
|
|
186
|
+
```bash
|
|
187
|
+
vibepup --watch
|
|
188
|
+
```
|
|
189
|
+
|
|
139
190
|
If `opencode` is missing, Vibepup will try to install it on Linux/macOS and then guide you. You can also set up a free tier manually:
|
|
140
191
|
|
|
141
192
|
```bash
|
package/bin/ralph.js
CHANGED
|
@@ -35,8 +35,8 @@ async function promptPlatform() {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
async function main() {
|
|
38
|
-
const scriptPath = path.join(__dirname, '../lib/
|
|
39
|
-
const windowsRunnerPath = path.join(__dirname, '../lib/
|
|
38
|
+
const scriptPath = path.join(__dirname, '../lib/runner/index.js');
|
|
39
|
+
const windowsRunnerPath = path.join(__dirname, '../lib/runner/index.js');
|
|
40
40
|
const allArgs = process.argv.slice(2);
|
|
41
41
|
const useTui = allArgs.includes('--tui');
|
|
42
42
|
|
|
@@ -90,9 +90,9 @@ async function main() {
|
|
|
90
90
|
const binName = os.platform() === 'win32' ? 'vibepup-tui.exe' : 'vibepup-tui';
|
|
91
91
|
const binPath = path.join(tuiDir, binName);
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
if (fs.existsSync(binPath)) {
|
|
94
|
+
const tuiArgs = ['--runner', scriptPath, ...args];
|
|
95
|
+
const tui = spawn(binPath, tuiArgs, shellOptions);
|
|
96
96
|
tui.on('error', (err) => {
|
|
97
97
|
console.error('❌ Failed to start Vibepup TUI.');
|
|
98
98
|
console.error(String(err));
|
|
@@ -102,9 +102,9 @@ async function main() {
|
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
if (os.platform() !== 'win32' && fs.existsSync(tuiDir)) {
|
|
106
|
+
const goArgs = ['run', '.', '--runner', scriptPath, ...args];
|
|
107
|
+
const goCmd = spawn('go', goArgs, { ...shellOptions, cwd: tuiDir });
|
|
108
108
|
goCmd.on('error', (err) => {
|
|
109
109
|
console.error('❌ Failed to start Vibepup TUI.');
|
|
110
110
|
console.error(String(err));
|
|
@@ -120,7 +120,7 @@ async function main() {
|
|
|
120
120
|
process.exit(1);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
let command =
|
|
123
|
+
let command = process.execPath;
|
|
124
124
|
let cmdArgs = [scriptPath, ...args];
|
|
125
125
|
|
|
126
126
|
if (isWindows) {
|
|
@@ -146,21 +146,21 @@ async function main() {
|
|
|
146
146
|
|
|
147
147
|
const normalizedPlatform = (selectedPlatform || '').toLowerCase();
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
149
|
+
if (normalizedPlatform === 'wsl' || (!normalizedPlatform && wslAvailable)) {
|
|
150
|
+
if (!wslAvailable) {
|
|
151
|
+
console.error('❌ WSL not found. Install WSL2 or use --platform=windows.');
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
const wslScriptPath = toWslPath(scriptPath);
|
|
155
|
+
const wslCwd = toWslPath(process.cwd());
|
|
156
|
+
command = 'wsl.exe';
|
|
157
|
+
cmdArgs = ['--cd', wslCwd, '--', 'node', wslScriptPath, ...args];
|
|
158
|
+
} else {
|
|
159
|
+
console.warn('⚠️ Using Windows-native mode.');
|
|
160
|
+
console.warn(' 💡 Tip: install WSL2 for full Linux parity.');
|
|
161
|
+
command = process.execPath;
|
|
162
|
+
cmdArgs = [windowsRunnerPath, ...args];
|
|
163
|
+
}
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
const vibepup = spawn(command, cmdArgs, shellOptions);
|
package/lib/ralph-win.js
CHANGED
|
@@ -1,401 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const fs = require('fs');
|
|
4
3
|
const path = require('path');
|
|
5
|
-
const
|
|
6
|
-
const { spawn, spawnSync } = require('child_process');
|
|
4
|
+
const { spawnSync } = require('child_process');
|
|
7
5
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const DEFAULT_ITERATIONS = 5;
|
|
13
|
-
const RALPH_MAX_TURN_SECONDS = Number.parseInt(process.env.RALPH_MAX_TURN_SECONDS || '900', 10);
|
|
14
|
-
const RALPH_NO_OUTPUT_SECONDS = Number.parseInt(process.env.RALPH_NO_OUTPUT_SECONDS || '180', 10);
|
|
15
|
-
|
|
16
|
-
const BUILD_MODELS_PREF = [
|
|
17
|
-
'github-copilot/gpt-5.2-codex',
|
|
18
|
-
'github-copilot/claude-sonnet-4.5',
|
|
19
|
-
'github-copilot/gemini-3-pro-preview',
|
|
20
|
-
'github-copilot-enterprise/gpt-5.2-codex',
|
|
21
|
-
'github-copilot-enterprise/claude-sonnet-4.5',
|
|
22
|
-
'github-copilot-enterprise/gemini-3-pro-preview',
|
|
23
|
-
'openai/gpt-5.2-codex',
|
|
24
|
-
'openai/gpt-5.1-codex-max',
|
|
25
|
-
'google/gemini-3-pro-preview',
|
|
26
|
-
'opencode/grok-code',
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
const PLAN_MODELS_PREF = [
|
|
30
|
-
'github-copilot/claude-opus-4.5',
|
|
31
|
-
'github-copilot/gemini-3-pro-preview',
|
|
32
|
-
'github-copilot-enterprise/claude-opus-4.5',
|
|
33
|
-
'github-copilot-enterprise/gemini-3-pro-preview',
|
|
34
|
-
'openai/gpt-5.2',
|
|
35
|
-
'google/antigravity-claude-opus-4-5-thinking',
|
|
36
|
-
'google/gemini-3-pro-preview',
|
|
37
|
-
'opencode/glm-4.7-free',
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
const SYSTEM_PROMPT = path.join(ENGINE_DIR, 'prompt.md');
|
|
41
|
-
const ARCHITECT_FILE = path.join(ENGINE_DIR, 'agents', 'architect.md');
|
|
42
|
-
|
|
43
|
-
const isWindows = process.platform === 'win32';
|
|
44
|
-
|
|
45
|
-
const parseArgs = () => {
|
|
46
|
-
const args = process.argv.slice(2);
|
|
47
|
-
let iterations = DEFAULT_ITERATIONS;
|
|
48
|
-
let watchMode = false;
|
|
49
|
-
let mode = 'default';
|
|
50
|
-
let projectIdea = '';
|
|
51
|
-
let freeMode = false;
|
|
52
|
-
|
|
53
|
-
let index = 0;
|
|
54
|
-
while (index < args.length) {
|
|
55
|
-
const arg = args[index];
|
|
56
|
-
if (arg === 'free') {
|
|
57
|
-
freeMode = true;
|
|
58
|
-
index += 1;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (arg === 'new') {
|
|
62
|
-
mode = 'new';
|
|
63
|
-
projectIdea = args[index + 1] || '';
|
|
64
|
-
index += 2;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (arg === '--watch') {
|
|
68
|
-
watchMode = true;
|
|
69
|
-
index += 1;
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
if (/^\d+$/.test(arg)) {
|
|
73
|
-
iterations = Number.parseInt(arg, 10);
|
|
74
|
-
index += 1;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
index += 1;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
iterations,
|
|
82
|
-
watchMode,
|
|
83
|
-
mode,
|
|
84
|
-
projectIdea,
|
|
85
|
-
freeMode,
|
|
86
|
-
};
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const ensureDir = (dir) => fs.mkdirSync(dir, { recursive: true });
|
|
90
|
-
|
|
91
|
-
const md5File = (filePath) => {
|
|
92
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
93
|
-
return crypto.createHash('md5').update(content).digest('hex');
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const fileExists = (filePath) => fs.existsSync(filePath);
|
|
97
|
-
|
|
98
|
-
const readTail = (filePath, maxLines) => {
|
|
99
|
-
if (!fileExists(filePath)) return '';
|
|
100
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
101
|
-
const lines = content.split(/\r?\n/);
|
|
102
|
-
return lines.slice(Math.max(0, lines.length - maxLines)).join('\n');
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const ensureProjectFiles = () => {
|
|
106
|
-
if (!fileExists(path.join(PROJECT_DIR, 'prd.md'))) {
|
|
107
|
-
if (fileExists(path.join(PROJECT_DIR, 'prd.json'))) {
|
|
108
|
-
console.log('🔄 Migrating legacy prd.json to prd.md...');
|
|
109
|
-
const data = JSON.parse(fs.readFileSync(path.join(PROJECT_DIR, 'prd.json'), 'utf8'));
|
|
110
|
-
const lines = data.map((item) => `- [ ] ${item.description}`);
|
|
111
|
-
fs.writeFileSync(path.join(PROJECT_DIR, 'prd.md'), lines.join('\n') + '\n', 'utf8');
|
|
112
|
-
fs.renameSync(path.join(PROJECT_DIR, 'prd.json'), path.join(PROJECT_DIR, 'prd.json.bak'));
|
|
113
|
-
} else {
|
|
114
|
-
console.log('⚠️ No prd.md found. Initializing...');
|
|
115
|
-
const init = [
|
|
116
|
-
'# Product Requirements Document (PRD)',
|
|
117
|
-
'',
|
|
118
|
-
'- [ ] Initialize repo-map.md with project architecture',
|
|
119
|
-
'- [ ] Setup initial project structure',
|
|
120
|
-
'',
|
|
121
|
-
].join('\n');
|
|
122
|
-
fs.writeFileSync(path.join(PROJECT_DIR, 'prd.md'), init, 'utf8');
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (!fileExists(path.join(PROJECT_DIR, 'repo-map.md'))) {
|
|
127
|
-
fs.writeFileSync(path.join(PROJECT_DIR, 'repo-map.md'), '', 'utf8');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (!fileExists(path.join(PROJECT_DIR, 'prd.state.json'))) {
|
|
131
|
-
fs.writeFileSync(path.join(PROJECT_DIR, 'prd.state.json'), '{}', 'utf8');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (!fileExists(path.join(PROJECT_DIR, 'progress.log'))) {
|
|
135
|
-
fs.writeFileSync(path.join(PROJECT_DIR, 'progress.log'), '', 'utf8');
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const detectPhase = () => {
|
|
140
|
-
const repoMapPath = path.join(PROJECT_DIR, 'repo-map.md');
|
|
141
|
-
if (!fileExists(repoMapPath)) return 'PLAN';
|
|
142
|
-
const content = fs.readFileSync(repoMapPath, 'utf8');
|
|
143
|
-
return content.trim().length === 0 ? 'PLAN' : 'BUILD';
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const resolveAvailableModels = (prefModels) => {
|
|
147
|
-
console.error('🔍 Verifying available models...');
|
|
148
|
-
const result = spawnSync('opencode', ['models', '--refresh'], { encoding: 'utf8' });
|
|
149
|
-
const output = result.stdout || '';
|
|
150
|
-
const lines = output.split(/\r?\n/).filter((line) => /^[a-z0-9-]+\/[a-z0-9.-]+$/.test(line));
|
|
151
|
-
const available = [];
|
|
152
|
-
for (const pref of prefModels) {
|
|
153
|
-
if (lines.includes(pref)) {
|
|
154
|
-
available.push(pref);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
if (available.length === 0) {
|
|
158
|
-
console.error('⚠️ No preferred models found. Falling back to generic discovery.');
|
|
159
|
-
const gptFallback = lines.find((line) => line.includes('gpt-4o'));
|
|
160
|
-
const claudeFallback = lines.find((line) => line.includes('claude-sonnet'));
|
|
161
|
-
if (gptFallback) available.push(gptFallback);
|
|
162
|
-
if (claudeFallback) available.push(claudeFallback);
|
|
163
|
-
}
|
|
164
|
-
if (available.length === 0) {
|
|
165
|
-
available.push('opencode/grok-code');
|
|
166
|
-
console.error('⚠️ Using fallback model: opencode/grok-code');
|
|
167
|
-
}
|
|
168
|
-
return available;
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
const runWithWatchdog = (logPath, command, args) => new Promise((resolve) => {
|
|
172
|
-
fs.writeFileSync(logPath, '', 'utf8');
|
|
173
|
-
const logStream = fs.createWriteStream(logPath, { flags: 'a' });
|
|
174
|
-
const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
175
|
-
let lastOutput = Date.now();
|
|
176
|
-
let killed = false;
|
|
177
|
-
|
|
178
|
-
const handleData = (data) => {
|
|
179
|
-
lastOutput = Date.now();
|
|
180
|
-
logStream.write(data);
|
|
181
|
-
process.stdout.write(data);
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
child.stdout.on('data', handleData);
|
|
185
|
-
child.stderr.on('data', handleData);
|
|
186
|
-
|
|
187
|
-
const interval = setInterval(() => {
|
|
188
|
-
const now = Date.now();
|
|
189
|
-
if (now - lastOutput > RALPH_NO_OUTPUT_SECONDS * 1000) {
|
|
190
|
-
logStream.write('[RALPH] NO OUTPUT: likely waiting for input / hung tool\n');
|
|
191
|
-
if (!killed) {
|
|
192
|
-
killed = true;
|
|
193
|
-
child.kill('SIGINT');
|
|
194
|
-
setTimeout(() => child.kill('SIGTERM'), 3000);
|
|
195
|
-
setTimeout(() => child.kill('SIGKILL'), 4000);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
if (now - startTime > RALPH_MAX_TURN_SECONDS * 1000) {
|
|
199
|
-
logStream.write('[RALPH] TIMEOUT: killing opencode turn\n');
|
|
200
|
-
if (!killed) {
|
|
201
|
-
killed = true;
|
|
202
|
-
child.kill('SIGINT');
|
|
203
|
-
setTimeout(() => child.kill('SIGTERM'), 3000);
|
|
204
|
-
setTimeout(() => child.kill('SIGKILL'), 4000);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}, 5000);
|
|
208
|
-
|
|
209
|
-
const startTime = Date.now();
|
|
210
|
-
|
|
211
|
-
child.on('close', (code) => {
|
|
212
|
-
clearInterval(interval);
|
|
213
|
-
logStream.end();
|
|
214
|
-
resolve(code || 0);
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
const runAgent = async (model, phase, iterDir) => {
|
|
219
|
-
const logPath = path.join(iterDir, 'agent_response.txt');
|
|
220
|
-
const promptSuffix = phase === 'PLAN'
|
|
221
|
-
? 'MODE: PLAN. Focus on exploring and mapping. Do NOT write implementation code yet.'
|
|
222
|
-
: 'MODE: BUILD. Focus on completing tasks in prd.md.';
|
|
223
|
-
|
|
224
|
-
const args = [
|
|
225
|
-
'run',
|
|
226
|
-
`Proceed with task. ${promptSuffix}`,
|
|
227
|
-
'--file', SYSTEM_PROMPT,
|
|
228
|
-
'--file', path.join(PROJECT_DIR, 'prd.md'),
|
|
229
|
-
'--file', path.join(PROJECT_DIR, 'prd.state.json'),
|
|
230
|
-
'--file', path.join(PROJECT_DIR, 'repo-map.md'),
|
|
231
|
-
'--file', path.join(iterDir, 'progress.tail.log'),
|
|
232
|
-
'--model', model,
|
|
233
|
-
];
|
|
234
|
-
|
|
235
|
-
return runWithWatchdog(logPath, 'opencode', args);
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
const runArchitect = () => {
|
|
239
|
-
console.log('🏗️ Phase 0: The Architect');
|
|
240
|
-
const args = [
|
|
241
|
-
'run',
|
|
242
|
-
`PROJECT IDEA: ${projectIdea}`,
|
|
243
|
-
'--file', ARCHITECT_FILE,
|
|
244
|
-
'--agent', 'general',
|
|
245
|
-
'--model', planModels[0],
|
|
246
|
-
];
|
|
247
|
-
const result = spawnSync('opencode', args, { stdio: 'inherit' });
|
|
248
|
-
return result.status || 0;
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
const ensureOpencode = (freeMode) => {
|
|
252
|
-
const exists = spawnSync('opencode', ['--version'], { stdio: 'ignore' }).status === 0;
|
|
253
|
-
if (exists) return true;
|
|
254
|
-
|
|
255
|
-
if (freeMode) {
|
|
256
|
-
console.log('🔧 Free setup: installing opencode...');
|
|
257
|
-
const npmAvailable = spawnSync('npm', ['--version'], { stdio: 'ignore' }).status === 0;
|
|
258
|
-
if (!npmAvailable) {
|
|
259
|
-
console.error('❌ npm not found. Install Node.js or use WSL2 for full setup.');
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
spawnSync('npm', ['install', '-g', 'opencode-ai', 'opencode-antigravity-auth'], { stdio: 'inherit' });
|
|
263
|
-
} else {
|
|
264
|
-
console.error('❌ opencode not found. Vibepup requires opencode to run.');
|
|
265
|
-
console.error(' Install with: npm install -g opencode-ai');
|
|
266
|
-
console.error(' Free-tier option: vibepup free');
|
|
267
|
-
return false;
|
|
268
|
-
}
|
|
269
|
-
return true;
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const runFreeSetup = () => {
|
|
273
|
-
console.log('✨ Vibepup Free Setup');
|
|
274
|
-
console.log(' 1) Installing auth plugin');
|
|
275
|
-
spawnSync('npm', ['install', '-g', 'opencode-antigravity-auth'], { stdio: 'inherit' });
|
|
276
|
-
console.log(' 2) Starting Google auth');
|
|
277
|
-
spawnSync('opencode', ['auth', 'login', 'antigravity'], { stdio: 'inherit' });
|
|
278
|
-
console.log(' 3) Refreshing models');
|
|
279
|
-
spawnSync('opencode', ['models', '--refresh'], { stdio: 'inherit' });
|
|
280
|
-
console.log("✅ Free setup complete. Run 'vibepup --watch' next.");
|
|
281
|
-
process.exit(0);
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const { iterations, watchMode, mode, projectIdea, freeMode } = parseArgs();
|
|
285
|
-
|
|
286
|
-
console.log('🐾 Vibepup v1.0 (Windows Native CLI Mode)');
|
|
287
|
-
console.log(` Engine: ${ENGINE_DIR}`);
|
|
288
|
-
console.log(` Context: ${PROJECT_DIR}`);
|
|
289
|
-
console.log(' Tips:');
|
|
290
|
-
console.log(" - Run 'vibepup free' for free-tier setup");
|
|
291
|
-
console.log(" - Run 'vibepup new \"My idea\"' to bootstrap a project");
|
|
292
|
-
console.log(" - Run 'vibepup --tui' for a guided interface");
|
|
293
|
-
|
|
294
|
-
ensureDir(RUNS_DIR);
|
|
295
|
-
ensureProjectFiles();
|
|
296
|
-
|
|
297
|
-
if (!ensureOpencode(freeMode)) {
|
|
298
|
-
process.exit(127);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (freeMode) {
|
|
302
|
-
runFreeSetup();
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const buildModels = resolveAvailableModels(BUILD_MODELS_PREF);
|
|
306
|
-
const planModels = resolveAvailableModels(PLAN_MODELS_PREF);
|
|
307
|
-
|
|
308
|
-
if (mode === 'new') {
|
|
309
|
-
const code = runArchitect();
|
|
310
|
-
if (code !== 0) process.exit(code);
|
|
311
|
-
console.log('✅ Architect initialization complete.');
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
let lastHash = md5File(path.join(PROJECT_DIR, 'prd.md'));
|
|
315
|
-
let i = 1;
|
|
316
|
-
|
|
317
|
-
const runLoop = async () => {
|
|
318
|
-
while (true) {
|
|
319
|
-
const currentHash = md5File(path.join(PROJECT_DIR, 'prd.md'));
|
|
320
|
-
if (currentHash !== lastHash) {
|
|
321
|
-
console.log('👀 PRD Changed! Restarting loop...');
|
|
322
|
-
fs.appendFileSync(path.join(PROJECT_DIR, 'progress.log'), '--- PRD CHANGED: RESTARTING LOOP ---\n', 'utf8');
|
|
323
|
-
lastHash = currentHash;
|
|
324
|
-
if (watchMode) {
|
|
325
|
-
i = 1;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
if (!watchMode && i > iterations) {
|
|
330
|
-
console.log('⏸️ Max iterations reached.');
|
|
331
|
-
break;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const phase = detectPhase();
|
|
335
|
-
const iterId = `iter-${String(i).padStart(4, '0')}`;
|
|
336
|
-
const iterDir = path.join(RUNS_DIR, iterId);
|
|
337
|
-
ensureDir(iterDir);
|
|
338
|
-
const tail = readTail(path.join(PROJECT_DIR, 'progress.log'), 200);
|
|
339
|
-
fs.writeFileSync(path.join(iterDir, 'progress.tail.log'), tail, 'utf8');
|
|
340
|
-
const latestLink = path.join(RUNS_DIR, 'latest');
|
|
341
|
-
try {
|
|
342
|
-
if (fileExists(latestLink)) fs.rmSync(latestLink, { recursive: true, force: true });
|
|
343
|
-
} catch (_) {}
|
|
344
|
-
try {
|
|
345
|
-
fs.symlinkSync(iterDir, latestLink, 'junction');
|
|
346
|
-
} catch (_) {}
|
|
347
|
-
|
|
348
|
-
console.log('');
|
|
349
|
-
console.log(`🔁 Loop ${i} (${phase} Phase)`);
|
|
350
|
-
console.log(` Logs: ${iterDir}`);
|
|
351
|
-
|
|
352
|
-
const models = phase === 'PLAN' ? planModels : buildModels;
|
|
353
|
-
let success = false;
|
|
354
|
-
|
|
355
|
-
for (const model of models) {
|
|
356
|
-
console.log(` Using: ${model}`);
|
|
357
|
-
const exitCode = await runAgent(model, phase, iterDir);
|
|
358
|
-
const response = fs.readFileSync(path.join(iterDir, 'agent_response.txt'), 'utf8');
|
|
359
|
-
|
|
360
|
-
if (/not supported|ModelNotFoundError|Make sure the model is enabled/i.test(response)) {
|
|
361
|
-
console.log(` ⚠️ Model ${model} not supported. Falling back...`);
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (exitCode === 0 && response.trim().length > 0) {
|
|
366
|
-
success = true;
|
|
367
|
-
if (response.includes('<promise>COMPLETE</promise>')) {
|
|
368
|
-
console.log('✅ Agent signaled completion.');
|
|
369
|
-
if (!watchMode) {
|
|
370
|
-
process.exit(0);
|
|
371
|
-
}
|
|
372
|
-
console.log('⏸️ Project Complete. Waiting for changes in prd.md...');
|
|
373
|
-
while (md5File(path.join(PROJECT_DIR, 'prd.md')) === lastHash) {
|
|
374
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
375
|
-
}
|
|
376
|
-
console.log('👀 Change detected! Resuming...');
|
|
377
|
-
i = 1;
|
|
378
|
-
break;
|
|
379
|
-
}
|
|
380
|
-
break;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
console.log(` ⚠️ Model ${model} failed (Exit: ${exitCode}). Falling back...`);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
if (!success) {
|
|
387
|
-
console.log('❌ All models failed this iteration.');
|
|
388
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
lastHash = md5File(path.join(PROJECT_DIR, 'prd.md'));
|
|
392
|
-
i += 1;
|
|
393
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
394
|
-
}
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
runLoop().catch((err) => {
|
|
398
|
-
console.error('❌ Vibepup Windows runner failed.');
|
|
399
|
-
console.error(String(err));
|
|
400
|
-
process.exit(1);
|
|
401
|
-
});
|
|
6
|
+
const runnerPath = path.join(__dirname, 'runner', 'index.js');
|
|
7
|
+
const result = spawnSync(process.execPath, [runnerPath, ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
8
|
+
process.exit(result.status || 0);
|