vibepup 1.0.2 → 1.0.3
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/.ralph/runs/iter-0001/agent_response.txt +189 -0
- package/PUBLISHING.md +39 -1
- package/README.md +69 -4
- package/bin/ralph.js +166 -50
- package/lib/ralph-win.js +401 -0
- package/lib/ralph.sh +10 -23
- package/package.json +5 -2
- package/prd.md +4 -0
- package/prd.state.json +1 -0
- package/repo-map.md +0 -0
- package/tui/animations/loaders.go +55 -0
- package/tui/animations/memes.go +35 -0
- package/tui/animations/registry.go +54 -0
- package/tui/config/flags.go +34 -0
- package/tui/go.mod +21 -16
- package/tui/go.sum +42 -34
- package/tui/main.go +269 -198
- package/tui/motion/engine.go +86 -0
- package/tui/persona/quip.go +89 -0
- package/tui/process/runner.go +94 -0
- package/tui/theme/registry.go +74 -0
- package/tui/ui/layout.go +17 -0
- package/tui/ui/statusbar.go +28 -0
- package/tui/ui/viewport.go +63 -0
- package/tui/vibepup-tui +0 -0
- package/vibepup-1.0.3.tgz +0 -0
package/lib/ralph-win.js
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const { spawn, spawnSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const ENGINE_DIR = path.resolve(__dirname);
|
|
9
|
+
const PROJECT_DIR = process.cwd();
|
|
10
|
+
const RUNS_DIR = path.join(PROJECT_DIR, '.ralph', 'runs');
|
|
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
|
+
});
|
package/lib/ralph.sh
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
set -e
|
|
3
|
+
set -o pipefail
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
# --- Hardened Environment (Anti-Interactive) ---
|
|
@@ -86,7 +87,7 @@ while [[ "$#" -gt 0 ]]; do
|
|
|
86
87
|
esac
|
|
87
88
|
done
|
|
88
89
|
|
|
89
|
-
echo "🐾 Vibepup v1.0 (CLI Mode)"
|
|
90
|
+
echo "🐾 Vibepup v1.0.3 (CLI Mode)"
|
|
90
91
|
echo " Engine: $ENGINE_DIR"
|
|
91
92
|
echo " Context: $PROJECT_DIR"
|
|
92
93
|
|
|
@@ -144,28 +145,14 @@ fi
|
|
|
144
145
|
# --- Smart Model Discovery ---
|
|
145
146
|
get_available_models() {
|
|
146
147
|
local PREF_MODELS=("$@")
|
|
147
|
-
local AVAILABLE_MODELS=()
|
|
148
|
+
local AVAILABLE_MODELS=("${PREF_MODELS[@]}")
|
|
148
149
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
local ALL_MODELS
|
|
152
|
-
ALL_MODELS=$(opencode models --refresh 2>/dev/null | grep -E "^[a-z0-9-]+/[a-z0-9.-]+" || true)
|
|
153
|
-
|
|
154
|
-
for PREF in "${PREF_MODELS[@]}"; do
|
|
155
|
-
if echo "$ALL_MODELS" | grep -q "^$PREF$"; then
|
|
156
|
-
AVAILABLE_MODELS+=("$PREF")
|
|
157
|
-
fi
|
|
158
|
-
done
|
|
159
|
-
|
|
160
|
-
if [ ${#AVAILABLE_MODELS[@]} -eq 0 ]; then
|
|
161
|
-
echo "⚠️ No preferred models found. Falling back to generic discovery." >&2
|
|
162
|
-
AVAILABLE_MODELS+=($(echo "$ALL_MODELS" | grep "gpt-4o" | head -n 1))
|
|
163
|
-
AVAILABLE_MODELS+=($(echo "$ALL_MODELS" | grep "claude-sonnet" | head -n 1))
|
|
164
|
-
fi
|
|
150
|
+
# Refresh models cache silently (ignoring errors) to ensure CLI is ready
|
|
151
|
+
opencode models --refresh >/dev/null 2>&1 || true
|
|
165
152
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
153
|
+
# Always add a reliable fallback at the end if not present
|
|
154
|
+
if [[ ! " ${AVAILABLE_MODELS[*]} " =~ " opencode/grok-code " ]]; then
|
|
155
|
+
AVAILABLE_MODELS+=("opencode/grok-code")
|
|
169
156
|
fi
|
|
170
157
|
|
|
171
158
|
echo "${AVAILABLE_MODELS[@]}"
|
|
@@ -192,7 +179,6 @@ if [ "$MODE" == "new" ]; then
|
|
|
192
179
|
# NOTE: We assume agents/architect.md is in lib/agents/
|
|
193
180
|
opencode run "PROJECT IDEA: $PROJECT_IDEA" \
|
|
194
181
|
--file "$ENGINE_DIR/agents/architect.md" \
|
|
195
|
-
--agent general \
|
|
196
182
|
--model "$ARCHITECT_MODEL"
|
|
197
183
|
|
|
198
184
|
echo "✅ Architect initialization complete."
|
|
@@ -368,6 +354,7 @@ while true; do
|
|
|
368
354
|
MODELS=("${BUILD_MODELS[@]}")
|
|
369
355
|
fi
|
|
370
356
|
|
|
357
|
+
echo " Queue: ${MODELS[*]}"
|
|
371
358
|
SUCCESS=false
|
|
372
359
|
for MODEL in "${MODELS[@]}"; do
|
|
373
360
|
echo " Using: $MODEL"
|
|
@@ -378,7 +365,7 @@ while true; do
|
|
|
378
365
|
|
|
379
366
|
RESPONSE=$(cat "$ITER_DIR/agent_response.txt")
|
|
380
367
|
|
|
381
|
-
if echo "$RESPONSE" | grep -
|
|
368
|
+
if echo "$RESPONSE" | grep -Eiq "not supported|ModelNotFoundError|Make sure the model is enabled"; then
|
|
382
369
|
echo " ⚠️ Model $MODEL not supported. Falling back..."
|
|
383
370
|
continue
|
|
384
371
|
fi
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibepup",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A loyal, DX-first split-brain agent harness with cyberpunk vibes.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"vibepup": "bin/ralph.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
10
|
+
"build": "cd tui && go build -o vibepup-tui .",
|
|
11
|
+
"run:local": "node bin/ralph.js",
|
|
12
|
+
"pack:local": "npm run build && npm pack"
|
|
10
13
|
},
|
|
11
14
|
"repository": {
|
|
12
15
|
"type": "git",
|
package/prd.md
ADDED
package/prd.state.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
package/repo-map.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package animations
|
|
2
|
+
|
|
3
|
+
import "time"
|
|
4
|
+
|
|
5
|
+
var (
|
|
6
|
+
vhsFrames = []string{
|
|
7
|
+
"▞▚▞▚▞▚▞▚▞▚▞▚",
|
|
8
|
+
"▚▞▚▞▚▞▚▞▚▞▚▞",
|
|
9
|
+
"▞▚▞▚▞▚▞▚▞▚▞▚",
|
|
10
|
+
}
|
|
11
|
+
crtFrames = []string{
|
|
12
|
+
"│││││││││││││",
|
|
13
|
+
"┃┃┃┃┃┃┃┃┃┃┃┃┃",
|
|
14
|
+
"║║║║║║║║║║║║║",
|
|
15
|
+
}
|
|
16
|
+
matrixFrames = []string{
|
|
17
|
+
"aabb0101zz",
|
|
18
|
+
"0101zzaabb",
|
|
19
|
+
"zzaabb0101",
|
|
20
|
+
}
|
|
21
|
+
slimeFrames = []string{
|
|
22
|
+
"(o˶╹︿╹˶o)",
|
|
23
|
+
"(o˶╹﹏╹˶o)",
|
|
24
|
+
"(o˶╹︿╹˶o)~",
|
|
25
|
+
"~(o˶╹︿╹˶o)",
|
|
26
|
+
}
|
|
27
|
+
floppyFrames = []string{
|
|
28
|
+
"💾",
|
|
29
|
+
"💽",
|
|
30
|
+
"💿",
|
|
31
|
+
}
|
|
32
|
+
waveFrames = []string{
|
|
33
|
+
"~ ~ ~",
|
|
34
|
+
" ~ ~ ",
|
|
35
|
+
" ~ ~ ",
|
|
36
|
+
" ~ ~ ~",
|
|
37
|
+
}
|
|
38
|
+
fireworkFrames = []string{
|
|
39
|
+
" . ",
|
|
40
|
+
" .*. ",
|
|
41
|
+
".*★*",
|
|
42
|
+
" .*. ",
|
|
43
|
+
" ' ",
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
func init() {
|
|
48
|
+
Register(Preset{Name: "vhs-scan", Kind: Loader, Frames: vhsFrames, Interval: 70 * time.Millisecond, Density: 3})
|
|
49
|
+
Register(Preset{Name: "crt-wipe", Kind: Loader, Frames: crtFrames, Interval: 60 * time.Millisecond, Density: 2})
|
|
50
|
+
Register(Preset{Name: "matrix-rain", Kind: Loader, Frames: matrixFrames, Interval: 90 * time.Millisecond, Density: 2})
|
|
51
|
+
Register(Preset{Name: "slime-bounce", Kind: Loader, Frames: slimeFrames, Interval: 80 * time.Millisecond, Density: 1})
|
|
52
|
+
Register(Preset{Name: "floppy-spin", Kind: Loader, Frames: floppyFrames, Interval: 80 * time.Millisecond, Density: 1})
|
|
53
|
+
Register(Preset{Name: "vibe-wave", Kind: Idle, Frames: waveFrames, Interval: 120 * time.Millisecond, Density: 1})
|
|
54
|
+
Register(Preset{Name: "fireworks", Kind: Event, Frames: fireworkFrames, Interval: 100 * time.Millisecond, Density: 2})
|
|
55
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
package animations
|
|
2
|
+
|
|
3
|
+
import "time"
|
|
4
|
+
|
|
5
|
+
var (
|
|
6
|
+
dogeFrames = []string{
|
|
7
|
+
"wow much wait",
|
|
8
|
+
"such load very vibe",
|
|
9
|
+
"plz hold pupper",
|
|
10
|
+
}
|
|
11
|
+
shrekFrames = []string{
|
|
12
|
+
" /|、",
|
|
13
|
+
" (°、 。 7",
|
|
14
|
+
" | 、`\\",
|
|
15
|
+
" じしf_, )ノ",
|
|
16
|
+
}
|
|
17
|
+
catFrames = []string{
|
|
18
|
+
"/ᐠ. 。.ᐟ\\", // cat face
|
|
19
|
+
"/ᐠ。‸。ᐟ\\",
|
|
20
|
+
"/ᐠ – ᆽ – ᐟ\\",
|
|
21
|
+
}
|
|
22
|
+
wojakFrames = []string{
|
|
23
|
+
"(・_・;)",
|
|
24
|
+
"(・_・`)",
|
|
25
|
+
"(・_・;)",
|
|
26
|
+
"(・_・`)",
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
func init() {
|
|
31
|
+
Register(Preset{Name: "doge-wow", Kind: Event, Frames: dogeFrames, Interval: 110 * time.Millisecond, Density: 1})
|
|
32
|
+
Register(Preset{Name: "shrek-blink", Kind: Event, Frames: shrekFrames, Interval: 140 * time.Millisecond, Density: 1})
|
|
33
|
+
Register(Preset{Name: "cat-bounce", Kind: Event, Frames: catFrames, Interval: 90 * time.Millisecond, Density: 1})
|
|
34
|
+
Register(Preset{Name: "wojak-stare", Kind: Event, Frames: wojakFrames, Interval: 120 * time.Millisecond, Density: 1})
|
|
35
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
package animations
|
|
2
|
+
|
|
3
|
+
import "time"
|
|
4
|
+
|
|
5
|
+
type Kind string
|
|
6
|
+
|
|
7
|
+
const (
|
|
8
|
+
Loader Kind = "loader"
|
|
9
|
+
Idle Kind = "idle"
|
|
10
|
+
Event Kind = "event"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
type Preset struct {
|
|
14
|
+
Name string
|
|
15
|
+
Kind Kind
|
|
16
|
+
Frames []string
|
|
17
|
+
Interval time.Duration
|
|
18
|
+
Density int
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
var presets = map[string]Preset{}
|
|
22
|
+
|
|
23
|
+
func Register(p Preset) {
|
|
24
|
+
if p.Interval == 0 {
|
|
25
|
+
p.Interval = time.Millisecond * 80
|
|
26
|
+
}
|
|
27
|
+
if p.Density == 0 {
|
|
28
|
+
p.Density = 1
|
|
29
|
+
}
|
|
30
|
+
presets[p.Name] = p
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func Get(name string) Preset {
|
|
34
|
+
if p, ok := presets[name]; ok {
|
|
35
|
+
return p
|
|
36
|
+
}
|
|
37
|
+
return presets["vhs-scan"]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
func All() []Preset {
|
|
41
|
+
out := make([]Preset, 0, len(presets))
|
|
42
|
+
for _, p := range presets {
|
|
43
|
+
out = append(out, p)
|
|
44
|
+
}
|
|
45
|
+
return out
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func Frame(p Preset, index int) (string, int) {
|
|
49
|
+
if len(p.Frames) == 0 {
|
|
50
|
+
return "", index
|
|
51
|
+
}
|
|
52
|
+
next := (index + 1) % len(p.Frames)
|
|
53
|
+
return p.Frames[index%len(p.Frames)], next
|
|
54
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
package config
|
|
2
|
+
|
|
3
|
+
import "flag"
|
|
4
|
+
|
|
5
|
+
type Flags struct {
|
|
6
|
+
Quiet bool
|
|
7
|
+
NoEmoji bool
|
|
8
|
+
Dense bool
|
|
9
|
+
PerfLow bool
|
|
10
|
+
Snark string
|
|
11
|
+
Theme string
|
|
12
|
+
Anim string
|
|
13
|
+
FX string
|
|
14
|
+
NoAlt bool
|
|
15
|
+
ForceRun bool
|
|
16
|
+
Runner string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
func Parse() Flags {
|
|
20
|
+
f := Flags{}
|
|
21
|
+
flag.BoolVar(&f.Quiet, "quiet", false, "reduce motion and chatter")
|
|
22
|
+
flag.BoolVar(&f.NoEmoji, "no-emoji", false, "disable emoji rendering")
|
|
23
|
+
flag.BoolVar(&f.Dense, "dense", false, "increase animation density")
|
|
24
|
+
flag.BoolVar(&f.PerfLow, "perf-low", false, "lower FPS and effects for slower terminals")
|
|
25
|
+
flag.StringVar(&f.Snark, "snark", "mild", "snark level: mild|spicy|unhinged")
|
|
26
|
+
flag.StringVar(&f.Theme, "theme", "dracula-vibe", "theme name")
|
|
27
|
+
flag.StringVar(&f.Anim, "anim", "vhs-scan", "animation preset")
|
|
28
|
+
flag.StringVar(&f.FX, "fx", "fire", "sysc effect: fire|matrix|none")
|
|
29
|
+
flag.BoolVar(&f.NoAlt, "no-alt", true, "disable alt screen (stay in current terminal)")
|
|
30
|
+
flag.BoolVar(&f.ForceRun, "force-run", false, "run child process even if stdout is not a TTY")
|
|
31
|
+
flag.StringVar(&f.Runner, "runner", "", "path to runner script (internal use)")
|
|
32
|
+
flag.Parse()
|
|
33
|
+
return f
|
|
34
|
+
}
|