@nclamvn/vibecode-cli 1.8.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SESSION_NOTES.md +47 -12
- package/bin/vibecode.js +9 -3
- package/package.json +1 -1
- package/src/agent/index.js +68 -2
- package/src/agent/orchestrator.js +211 -7
- package/src/commands/agent.js +141 -47
package/SESSION_NOTES.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# VIBECODE CLI - SESSION NOTES
|
|
2
|
-
> Cập nhật: 2025-12-22
|
|
2
|
+
> Cập nhật: 2025-12-22 (Latest)
|
|
3
3
|
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
- CLI: https://github.com/nclamvn/vibecode-cli
|
|
21
21
|
- Docs: https://github.com/nclamvn/vibecode-docs
|
|
22
22
|
|
|
23
|
-
4. **NPM Package**
|
|
23
|
+
4. **NPM Package**
|
|
24
24
|
- Package: @nclamvn/vibecode-cli
|
|
25
|
+
- Version: **1.8.1** (latest)
|
|
25
26
|
- Status: Deprecated với message cảnh báo beta
|
|
26
27
|
|
|
27
28
|
5. **UI Fixes**
|
|
@@ -30,6 +31,12 @@
|
|
|
30
31
|
- Theme toggle icon thu nhỏ 60%
|
|
31
32
|
- Default: Light mode
|
|
32
33
|
|
|
34
|
+
6. **Bug Fix: Version Hardcoded** ✅ FIXED
|
|
35
|
+
- File: `src/config/constants.js`
|
|
36
|
+
- Before: `export const VERSION = '1.0.1'` (hardcoded)
|
|
37
|
+
- After: `export const VERSION = pkg.version` (dynamic)
|
|
38
|
+
- Published: v1.8.1
|
|
39
|
+
|
|
33
40
|
---
|
|
34
41
|
|
|
35
42
|
## 🚧 ĐANG LÀM
|
|
@@ -37,7 +44,10 @@
|
|
|
37
44
|
### Deploy Render
|
|
38
45
|
- Repo: nclamvn/vibecode-docs
|
|
39
46
|
- File: render.yaml đã có
|
|
40
|
-
-
|
|
47
|
+
- URL Dashboard: https://dashboard.render.com/static/new
|
|
48
|
+
- **BƯỚC TIẾP THEO**:
|
|
49
|
+
1. Điền `build` vào Publish Directory
|
|
50
|
+
2. Click "Deploy Static Site"
|
|
41
51
|
|
|
42
52
|
---
|
|
43
53
|
|
|
@@ -46,8 +56,9 @@
|
|
|
46
56
|
```
|
|
47
57
|
/Users/mac/vibecode-cli/
|
|
48
58
|
├── src/ # CLI source (26 commands)
|
|
59
|
+
│ └── config/constants.js # VERSION đọc từ package.json
|
|
49
60
|
├── bin/vibecode.js # Entry point
|
|
50
|
-
├── package.json # v1.8.
|
|
61
|
+
├── package.json # v1.8.1
|
|
51
62
|
├── docs-site/ # Docusaurus site (separate git repo)
|
|
52
63
|
│ ├── src/pages/index.tsx # Landing page
|
|
53
64
|
│ ├── docs/ # English docs
|
|
@@ -61,13 +72,16 @@
|
|
|
61
72
|
## 🔧 COMMANDS THƯỜNG DÙNG
|
|
62
73
|
|
|
63
74
|
```bash
|
|
64
|
-
# Dev server
|
|
75
|
+
# Dev server docs
|
|
65
76
|
cd /Users/mac/vibecode-cli/docs-site
|
|
66
77
|
npm start
|
|
67
78
|
|
|
68
|
-
# Build
|
|
79
|
+
# Build docs
|
|
69
80
|
npm run build
|
|
70
81
|
|
|
82
|
+
# Test CLI version
|
|
83
|
+
node bin/vibecode.js --version
|
|
84
|
+
|
|
71
85
|
# Push docs
|
|
72
86
|
cd /Users/mac/vibecode-cli/docs-site
|
|
73
87
|
git add . && git commit -m "message" && git push
|
|
@@ -76,6 +90,10 @@ git add . && git commit -m "message" && git push
|
|
|
76
90
|
cd /Users/mac/vibecode-cli
|
|
77
91
|
git add . && git commit -m "message" && git push
|
|
78
92
|
|
|
93
|
+
# Publish npm (khi cần)
|
|
94
|
+
npm version patch
|
|
95
|
+
npm publish
|
|
96
|
+
|
|
79
97
|
# Bỏ deprecate npm khi sẵn sàng
|
|
80
98
|
npm deprecate @nclamvn/vibecode-cli ""
|
|
81
99
|
```
|
|
@@ -86,6 +104,7 @@ npm deprecate @nclamvn/vibecode-cli ""
|
|
|
86
104
|
|
|
87
105
|
| Metric | Value |
|
|
88
106
|
|--------|-------|
|
|
107
|
+
| CLI Version | 1.8.1 |
|
|
89
108
|
| CLI Commands | 26 |
|
|
90
109
|
| Lines of Code | 18,612 |
|
|
91
110
|
| JS Files | 167 |
|
|
@@ -95,7 +114,7 @@ npm deprecate @nclamvn/vibecode-cli ""
|
|
|
95
114
|
|
|
96
115
|
## 🎯 VIỆC CẦN LÀM TIẾP
|
|
97
116
|
|
|
98
|
-
1. [ ] Hoàn thành deploy Render
|
|
117
|
+
1. [ ] Hoàn thành deploy Render (điền `build` → Deploy)
|
|
99
118
|
2. [ ] Test site trên production URL
|
|
100
119
|
3. [ ] Phase L: Unit Tests & TypeScript (optional)
|
|
101
120
|
4. [ ] Khi sẵn sàng: Bỏ deprecate npm package
|
|
@@ -104,11 +123,23 @@ npm deprecate @nclamvn/vibecode-cli ""
|
|
|
104
123
|
|
|
105
124
|
## 🔗 LINKS
|
|
106
125
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
126
|
+
| Resource | URL |
|
|
127
|
+
|----------|-----|
|
|
128
|
+
| Landing Local EN | http://localhost:3000/ |
|
|
129
|
+
| Landing Local VI | http://localhost:3000/vi/ |
|
|
130
|
+
| GitHub CLI | https://github.com/nclamvn/vibecode-cli |
|
|
131
|
+
| GitHub Docs | https://github.com/nclamvn/vibecode-docs |
|
|
132
|
+
| NPM Package | https://www.npmjs.com/package/@nclamvn/vibecode-cli |
|
|
133
|
+
| Render Dashboard | https://dashboard.render.com |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 📝 LỊCH SỬ SESSION
|
|
138
|
+
|
|
139
|
+
| Thời gian | Việc đã làm |
|
|
140
|
+
|-----------|-------------|
|
|
141
|
+
| Session 1 | Landing page, i18n, UI fixes |
|
|
142
|
+
| Session 2 | GitHub push, npm deprecate, version bug fix |
|
|
112
143
|
|
|
113
144
|
---
|
|
114
145
|
|
|
@@ -116,4 +147,8 @@ npm deprecate @nclamvn/vibecode-cli ""
|
|
|
116
147
|
|
|
117
148
|
Chỉ cần nói: **"tiếp tục"** hoặc **"continue"**
|
|
118
149
|
|
|
150
|
+
Hoặc cụ thể hơn:
|
|
151
|
+
- "tiếp tục deploy render"
|
|
152
|
+
- "đọc SESSION_NOTES.md và cho tôi biết trạng thái"
|
|
153
|
+
|
|
119
154
|
Claude sẽ đọc file này và biết cần làm gì tiếp.
|
package/bin/vibecode.js
CHANGED
|
@@ -146,12 +146,14 @@ program
|
|
|
146
146
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
147
147
|
|
|
148
148
|
program
|
|
149
|
-
.command('agent
|
|
149
|
+
.command('agent [description...]')
|
|
150
150
|
.description('Agent mode: autonomous multi-module builder with self-healing')
|
|
151
151
|
.option('-n, --new', 'Create new project directory')
|
|
152
|
+
.option('-r, --resume', 'Resume from last stopped module')
|
|
153
|
+
.option('--from <n>', 'Resume from specific module number', parseInt)
|
|
154
|
+
.option('-s, --status', 'Show current agent progress')
|
|
152
155
|
.option('-v, --verbose', 'Show detailed progress')
|
|
153
156
|
.option('--analyze', 'Analyze project structure without building')
|
|
154
|
-
.option('--status', 'Show agent status and memory stats')
|
|
155
157
|
.option('--report', 'Export memory report to markdown')
|
|
156
158
|
.option('--clear', 'Clear agent memory')
|
|
157
159
|
.option('--force', 'Force operation (for --clear)')
|
|
@@ -160,7 +162,11 @@ program
|
|
|
160
162
|
.option('--skip-tests', 'Skip tests after each module')
|
|
161
163
|
.option('--continue', 'Continue on module failure')
|
|
162
164
|
.option('--stdout', 'Output report to stdout (for --report)')
|
|
163
|
-
.action(
|
|
165
|
+
.action((description, options) => {
|
|
166
|
+
// Join variadic description
|
|
167
|
+
const desc = Array.isArray(description) ? description.join(' ') : description;
|
|
168
|
+
agentCommand(desc, options);
|
|
169
|
+
});
|
|
164
170
|
|
|
165
171
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
166
172
|
// Phase G Commands - Debug Mode
|
package/package.json
CHANGED
package/src/agent/index.js
CHANGED
|
@@ -11,7 +11,13 @@ import fs from 'fs-extra';
|
|
|
11
11
|
import { DecompositionEngine, createDecompositionEngine } from './decomposition.js';
|
|
12
12
|
import { MemoryEngine, createMemoryEngine } from './memory.js';
|
|
13
13
|
import { SelfHealingEngine, createSelfHealingEngine } from './self-healing.js';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
Orchestrator,
|
|
16
|
+
createOrchestrator,
|
|
17
|
+
ORCHESTRATOR_STATES,
|
|
18
|
+
loadProgress,
|
|
19
|
+
loadDecomposition
|
|
20
|
+
} from './orchestrator.js';
|
|
15
21
|
|
|
16
22
|
/**
|
|
17
23
|
* Vibecode Agent Class
|
|
@@ -213,6 +219,64 @@ export class VibecodeAgent {
|
|
|
213
219
|
console.log();
|
|
214
220
|
}
|
|
215
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Resume a previously interrupted build
|
|
224
|
+
*/
|
|
225
|
+
async resume(options = {}) {
|
|
226
|
+
if (!this.initialized) {
|
|
227
|
+
await this.initialize();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Load progress to show header
|
|
231
|
+
const progress = await loadProgress(this.projectPath);
|
|
232
|
+
if (!progress) {
|
|
233
|
+
console.log(chalk.yellow('\n📭 No previous session found.\n'));
|
|
234
|
+
console.log(chalk.gray(' Start new build: vibecode agent "description" --new\n'));
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this.showResumeHeader(progress, options);
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
this.buildResult = await this.orchestrator.resumeBuild(options);
|
|
242
|
+
this.showResults(this.buildResult);
|
|
243
|
+
return this.buildResult;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error(chalk.red(`\nAgent resume failed: ${error.message}`));
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Show resume header
|
|
252
|
+
*/
|
|
253
|
+
showResumeHeader(progress, options) {
|
|
254
|
+
const fromModule = options.fromModule !== undefined
|
|
255
|
+
? options.fromModule + 1
|
|
256
|
+
: progress.currentModule + 1;
|
|
257
|
+
const totalModules = progress.totalModules;
|
|
258
|
+
const completed = progress.completedModules?.length || progress.currentModule || 0;
|
|
259
|
+
|
|
260
|
+
console.log();
|
|
261
|
+
console.log(chalk.cyan('╭' + '─'.repeat(68) + '╮'));
|
|
262
|
+
console.log(chalk.cyan('│') + ' '.repeat(68) + chalk.cyan('│'));
|
|
263
|
+
console.log(chalk.cyan('│') + chalk.bold.white(' 🔄 RESUMING AGENT') + ' '.repeat(48) + chalk.cyan('│'));
|
|
264
|
+
console.log(chalk.cyan('│') + ' '.repeat(68) + chalk.cyan('│'));
|
|
265
|
+
|
|
266
|
+
const projectLine = ` Project: ${progress.projectName}`;
|
|
267
|
+
console.log(chalk.cyan('│') + chalk.white(projectLine) + ' '.repeat(Math.max(0, 66 - projectLine.length)) + chalk.cyan('│'));
|
|
268
|
+
|
|
269
|
+
const progressLine = ` Progress: ${completed}/${totalModules} modules completed`;
|
|
270
|
+
console.log(chalk.cyan('│') + chalk.white(progressLine) + ' '.repeat(Math.max(0, 66 - progressLine.length)) + chalk.cyan('│'));
|
|
271
|
+
|
|
272
|
+
const resumeLine = ` Resuming from module ${fromModule}`;
|
|
273
|
+
console.log(chalk.cyan('│') + chalk.yellow(resumeLine) + ' '.repeat(Math.max(0, 66 - resumeLine.length)) + chalk.cyan('│'));
|
|
274
|
+
|
|
275
|
+
console.log(chalk.cyan('│') + ' '.repeat(68) + chalk.cyan('│'));
|
|
276
|
+
console.log(chalk.cyan('╰' + '─'.repeat(68) + '╯'));
|
|
277
|
+
console.log();
|
|
278
|
+
}
|
|
279
|
+
|
|
216
280
|
/**
|
|
217
281
|
* Quick build helper
|
|
218
282
|
*/
|
|
@@ -321,5 +385,7 @@ export {
|
|
|
321
385
|
createSelfHealingEngine,
|
|
322
386
|
Orchestrator,
|
|
323
387
|
createOrchestrator,
|
|
324
|
-
ORCHESTRATOR_STATES
|
|
388
|
+
ORCHESTRATOR_STATES,
|
|
389
|
+
loadProgress,
|
|
390
|
+
loadDecomposition
|
|
325
391
|
};
|
|
@@ -15,6 +15,67 @@ import { ProgressDashboard } from '../ui/dashboard.js';
|
|
|
15
15
|
import { translateError, showError, inlineError } from '../ui/error-translator.js';
|
|
16
16
|
import { BackupManager } from '../core/backup.js';
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Progress file for resume functionality
|
|
20
|
+
*/
|
|
21
|
+
const PROGRESS_FILE = '.vibecode/agent-progress.json';
|
|
22
|
+
const DECOMPOSITION_FILE = '.vibecode/agent-decomposition.json';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Save agent progress to file
|
|
26
|
+
*/
|
|
27
|
+
export async function saveProgress(projectPath, progress) {
|
|
28
|
+
const progressPath = path.join(projectPath, PROGRESS_FILE);
|
|
29
|
+
await fs.ensureDir(path.dirname(progressPath));
|
|
30
|
+
await fs.writeFile(progressPath, JSON.stringify(progress, null, 2));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Load agent progress from file
|
|
35
|
+
*/
|
|
36
|
+
export async function loadProgress(projectPath) {
|
|
37
|
+
try {
|
|
38
|
+
const progressPath = path.join(projectPath, PROGRESS_FILE);
|
|
39
|
+
const content = await fs.readFile(progressPath, 'utf-8');
|
|
40
|
+
return JSON.parse(content);
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Clear agent progress file
|
|
48
|
+
*/
|
|
49
|
+
export async function clearProgress(projectPath) {
|
|
50
|
+
try {
|
|
51
|
+
await fs.unlink(path.join(projectPath, PROGRESS_FILE));
|
|
52
|
+
} catch {
|
|
53
|
+
// Ignore if file doesn't exist
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Save decomposition for resume
|
|
59
|
+
*/
|
|
60
|
+
export async function saveDecomposition(projectPath, decomposition) {
|
|
61
|
+
const decompositionPath = path.join(projectPath, DECOMPOSITION_FILE);
|
|
62
|
+
await fs.ensureDir(path.dirname(decompositionPath));
|
|
63
|
+
await fs.writeFile(decompositionPath, JSON.stringify(decomposition, null, 2));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Load decomposition from file
|
|
68
|
+
*/
|
|
69
|
+
export async function loadDecomposition(projectPath) {
|
|
70
|
+
try {
|
|
71
|
+
const decompositionPath = path.join(projectPath, DECOMPOSITION_FILE);
|
|
72
|
+
const content = await fs.readFile(decompositionPath, 'utf-8');
|
|
73
|
+
return JSON.parse(content);
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
18
79
|
/**
|
|
19
80
|
* Orchestrator states
|
|
20
81
|
*/
|
|
@@ -186,6 +247,13 @@ export class Orchestrator {
|
|
|
186
247
|
|
|
187
248
|
const decomposition = await this.decompositionEngine.decompose(description, options);
|
|
188
249
|
|
|
250
|
+
// Save decomposition for resume functionality
|
|
251
|
+
await saveDecomposition(this.projectPath, {
|
|
252
|
+
...decomposition,
|
|
253
|
+
description,
|
|
254
|
+
startedAt: new Date().toISOString()
|
|
255
|
+
});
|
|
256
|
+
|
|
189
257
|
// Store in memory
|
|
190
258
|
if (this.memoryEngine) {
|
|
191
259
|
await this.memoryEngine.setProjectContext({
|
|
@@ -201,11 +269,13 @@ export class Orchestrator {
|
|
|
201
269
|
// Step 2: Build modules in order
|
|
202
270
|
this.setState(ORCHESTRATOR_STATES.BUILDING);
|
|
203
271
|
|
|
204
|
-
const results = await this.buildModules(decomposition);
|
|
272
|
+
const results = await this.buildModules(decomposition, options);
|
|
205
273
|
|
|
206
274
|
// Step 3: Final summary
|
|
207
275
|
if (results.success) {
|
|
208
276
|
this.setState(ORCHESTRATOR_STATES.COMPLETED);
|
|
277
|
+
// Clear progress on success
|
|
278
|
+
await clearProgress(this.projectPath);
|
|
209
279
|
} else {
|
|
210
280
|
this.setState(ORCHESTRATOR_STATES.FAILED);
|
|
211
281
|
}
|
|
@@ -219,41 +289,149 @@ export class Orchestrator {
|
|
|
219
289
|
}
|
|
220
290
|
}
|
|
221
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Resume build from previous progress
|
|
294
|
+
*/
|
|
295
|
+
async resumeBuild(options = {}) {
|
|
296
|
+
this.buildState.startTime = Date.now();
|
|
297
|
+
|
|
298
|
+
// Load progress
|
|
299
|
+
const progress = await loadProgress(this.projectPath);
|
|
300
|
+
if (!progress) {
|
|
301
|
+
throw new Error('No previous progress found. Start a new build with: vibecode agent "description"');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Load decomposition
|
|
305
|
+
const savedDecomposition = await loadDecomposition(this.projectPath);
|
|
306
|
+
if (!savedDecomposition) {
|
|
307
|
+
throw new Error('No decomposition found. Start a new build with: vibecode agent "description"');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
// Restore decomposition engine state
|
|
312
|
+
await this.decompositionEngine.decompose(savedDecomposition.description, options);
|
|
313
|
+
|
|
314
|
+
// Update module statuses from progress
|
|
315
|
+
for (const mod of progress.modules) {
|
|
316
|
+
if (mod.status === 'done') {
|
|
317
|
+
this.decompositionEngine.updateModuleStatus(mod.id, 'completed', {
|
|
318
|
+
files: mod.files || []
|
|
319
|
+
});
|
|
320
|
+
this.buildState.completedModules.push(mod.id);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Store in memory
|
|
325
|
+
if (this.memoryEngine) {
|
|
326
|
+
await this.memoryEngine.setProjectContext({
|
|
327
|
+
description: progress.description,
|
|
328
|
+
type: savedDecomposition.projectType,
|
|
329
|
+
complexity: savedDecomposition.estimatedComplexity,
|
|
330
|
+
totalModules: savedDecomposition.totalModules
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
await this.log(`Resuming from module ${progress.currentModule + 1}`);
|
|
335
|
+
|
|
336
|
+
// Determine start index
|
|
337
|
+
const startIndex = options.fromModule !== undefined
|
|
338
|
+
? options.fromModule
|
|
339
|
+
: progress.currentModule;
|
|
340
|
+
|
|
341
|
+
// Build remaining modules
|
|
342
|
+
this.setState(ORCHESTRATOR_STATES.BUILDING);
|
|
343
|
+
const results = await this.buildModules(savedDecomposition, {
|
|
344
|
+
...options,
|
|
345
|
+
startIndex
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Final summary
|
|
349
|
+
if (results.success) {
|
|
350
|
+
this.setState(ORCHESTRATOR_STATES.COMPLETED);
|
|
351
|
+
await clearProgress(this.projectPath);
|
|
352
|
+
} else {
|
|
353
|
+
this.setState(ORCHESTRATOR_STATES.FAILED);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return this.generateBuildReport(savedDecomposition, results);
|
|
357
|
+
|
|
358
|
+
} catch (error) {
|
|
359
|
+
this.setState(ORCHESTRATOR_STATES.FAILED);
|
|
360
|
+
await this.log(`Resume failed: ${error.message}`, 'error');
|
|
361
|
+
throw error;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
222
365
|
/**
|
|
223
366
|
* Build all modules in dependency order
|
|
224
367
|
*/
|
|
225
|
-
async buildModules(decomposition) {
|
|
368
|
+
async buildModules(decomposition, options = {}) {
|
|
226
369
|
const results = {
|
|
227
370
|
success: true,
|
|
228
371
|
modules: {},
|
|
229
372
|
totalTime: 0
|
|
230
373
|
};
|
|
231
374
|
|
|
375
|
+
// Determine start index for resume
|
|
376
|
+
const startIndex = options.startIndex || 0;
|
|
377
|
+
const buildOrder = decomposition.buildOrder;
|
|
378
|
+
|
|
232
379
|
// Create and start dashboard if enabled
|
|
233
380
|
if (this.config.useDashboard) {
|
|
234
381
|
this.dashboard = new ProgressDashboard({
|
|
235
|
-
title: 'VIBECODE AGENT',
|
|
382
|
+
title: startIndex > 0 ? 'VIBECODE AGENT (RESUME)' : 'VIBECODE AGENT',
|
|
236
383
|
projectName: path.basename(this.projectPath),
|
|
237
384
|
mode: `Agent (${decomposition.totalModules} modules)`
|
|
238
385
|
});
|
|
239
386
|
|
|
240
|
-
// Set modules for dashboard
|
|
241
|
-
this.dashboard.setModules(
|
|
387
|
+
// Set modules for dashboard with correct status for resumed builds
|
|
388
|
+
this.dashboard.setModules(buildOrder.map((id, idx) => {
|
|
242
389
|
const mod = this.decompositionEngine.getModule(id);
|
|
243
|
-
return {
|
|
390
|
+
return {
|
|
391
|
+
name: mod?.name || id,
|
|
392
|
+
status: idx < startIndex ? 'completed' : 'pending'
|
|
393
|
+
};
|
|
244
394
|
}));
|
|
245
395
|
|
|
246
396
|
this.dashboard.start();
|
|
397
|
+
|
|
398
|
+
// Show resume message
|
|
399
|
+
if (startIndex > 0) {
|
|
400
|
+
this.dashboard.addLog(`Resuming from module ${startIndex + 1}: ${buildOrder[startIndex]}`);
|
|
401
|
+
}
|
|
247
402
|
}
|
|
248
403
|
|
|
249
404
|
try {
|
|
250
|
-
for (
|
|
405
|
+
for (let i = startIndex; i < buildOrder.length; i++) {
|
|
406
|
+
const moduleId = buildOrder[i];
|
|
407
|
+
|
|
251
408
|
// Check if we should stop
|
|
252
409
|
if (this.state === ORCHESTRATOR_STATES.PAUSED) {
|
|
253
410
|
await this.log('Build paused');
|
|
254
411
|
break;
|
|
255
412
|
}
|
|
256
413
|
|
|
414
|
+
// Save progress before building this module
|
|
415
|
+
await saveProgress(this.projectPath, {
|
|
416
|
+
projectName: path.basename(this.projectPath),
|
|
417
|
+
description: decomposition.description,
|
|
418
|
+
totalModules: decomposition.totalModules,
|
|
419
|
+
completedModules: this.buildState.completedModules,
|
|
420
|
+
currentModule: i,
|
|
421
|
+
modules: buildOrder.map((id, idx) => {
|
|
422
|
+
const mod = this.decompositionEngine.getModule(id);
|
|
423
|
+
return {
|
|
424
|
+
id,
|
|
425
|
+
name: mod?.name || id,
|
|
426
|
+
status: idx < i ? 'done' : idx === i ? 'building' : 'pending',
|
|
427
|
+
files: mod?.files || []
|
|
428
|
+
};
|
|
429
|
+
}),
|
|
430
|
+
startedAt: decomposition.startedAt || new Date().toISOString(),
|
|
431
|
+
lastUpdated: new Date().toISOString(),
|
|
432
|
+
error: null
|
|
433
|
+
});
|
|
434
|
+
|
|
257
435
|
// Check if module can be built (dependencies satisfied)
|
|
258
436
|
if (!this.decompositionEngine.canBuildModule(moduleId)) {
|
|
259
437
|
const depStatus = decomposition.dependencyGraph[moduleId];
|
|
@@ -295,11 +473,37 @@ export class Orchestrator {
|
|
|
295
473
|
}
|
|
296
474
|
}
|
|
297
475
|
|
|
476
|
+
// Save progress after module completion
|
|
477
|
+
await saveProgress(this.projectPath, {
|
|
478
|
+
projectName: path.basename(this.projectPath),
|
|
479
|
+
description: decomposition.description,
|
|
480
|
+
totalModules: decomposition.totalModules,
|
|
481
|
+
completedModules: this.buildState.completedModules,
|
|
482
|
+
currentModule: moduleResult.success ? i + 1 : i,
|
|
483
|
+
failedModule: moduleResult.success ? null : i,
|
|
484
|
+
modules: buildOrder.map((id, idx) => {
|
|
485
|
+
const mod = this.decompositionEngine.getModule(id);
|
|
486
|
+
const status = idx < i ? 'done'
|
|
487
|
+
: idx === i ? (moduleResult.success ? 'done' : 'failed')
|
|
488
|
+
: 'pending';
|
|
489
|
+
return {
|
|
490
|
+
id,
|
|
491
|
+
name: mod?.name || id,
|
|
492
|
+
status,
|
|
493
|
+
files: mod?.files || []
|
|
494
|
+
};
|
|
495
|
+
}),
|
|
496
|
+
startedAt: decomposition.startedAt || new Date().toISOString(),
|
|
497
|
+
lastUpdated: new Date().toISOString(),
|
|
498
|
+
error: moduleResult.success ? null : moduleResult.error
|
|
499
|
+
});
|
|
500
|
+
|
|
298
501
|
if (!moduleResult.success) {
|
|
299
502
|
results.success = false;
|
|
300
503
|
|
|
301
504
|
if (!this.config.continueOnFailure) {
|
|
302
505
|
await this.log(`Stopping build due to module failure: ${moduleId}`, 'error');
|
|
506
|
+
console.log(chalk.yellow(`\n💡 Resume later with: vibecode agent --resume\n`));
|
|
303
507
|
break;
|
|
304
508
|
}
|
|
305
509
|
}
|
package/src/commands/agent.js
CHANGED
|
@@ -7,7 +7,7 @@ import chalk from 'chalk';
|
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import fs from 'fs-extra';
|
|
9
9
|
|
|
10
|
-
import { createAgent } from '../agent/index.js';
|
|
10
|
+
import { createAgent, loadProgress } from '../agent/index.js';
|
|
11
11
|
import { printError, printSuccess } from '../ui/output.js';
|
|
12
12
|
import { isClaudeCodeAvailable } from '../providers/index.js';
|
|
13
13
|
|
|
@@ -16,6 +16,14 @@ import { isClaudeCodeAvailable } from '../providers/index.js';
|
|
|
16
16
|
* vibecode agent "description" [options]
|
|
17
17
|
*/
|
|
18
18
|
export async function agentCommand(description, options = {}) {
|
|
19
|
+
// Handle description as array (from commander variadic)
|
|
20
|
+
const desc = Array.isArray(description) ? description.join(' ') : description;
|
|
21
|
+
|
|
22
|
+
// Handle sub-commands that don't need Claude Code check first
|
|
23
|
+
if (options.status) {
|
|
24
|
+
return showAgentStatus(options);
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
// Check for Claude Code
|
|
20
28
|
const claudeAvailable = await isClaudeCodeAvailable();
|
|
21
29
|
if (!claudeAvailable) {
|
|
@@ -24,8 +32,24 @@ export async function agentCommand(description, options = {}) {
|
|
|
24
32
|
process.exit(1);
|
|
25
33
|
}
|
|
26
34
|
|
|
27
|
-
//
|
|
28
|
-
if (
|
|
35
|
+
// Handle resume
|
|
36
|
+
if (options.resume) {
|
|
37
|
+
return resumeCommand(options);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Validate description for non-resume commands
|
|
41
|
+
if (!desc || desc.trim().length < 5) {
|
|
42
|
+
// Check if there's a session to resume
|
|
43
|
+
const progress = await loadProgress(process.cwd());
|
|
44
|
+
if (progress) {
|
|
45
|
+
console.log(chalk.cyan('\n📦 Found existing session:'));
|
|
46
|
+
console.log(chalk.white(` Project: ${progress.projectName}`));
|
|
47
|
+
console.log(chalk.white(` Progress: ${progress.completedModules?.length || progress.currentModule}/${progress.totalModules} modules\n`));
|
|
48
|
+
console.log(chalk.gray(' Resume: vibecode agent --resume'));
|
|
49
|
+
console.log(chalk.gray(' Status: vibecode agent --status\n'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
29
53
|
printError('Description too short. Please provide more details.');
|
|
30
54
|
console.log(chalk.gray('Example: vibecode agent "SaaS dashboard with auth, billing, and analytics"'));
|
|
31
55
|
process.exit(1);
|
|
@@ -33,11 +57,7 @@ export async function agentCommand(description, options = {}) {
|
|
|
33
57
|
|
|
34
58
|
// Handle sub-commands
|
|
35
59
|
if (options.analyze) {
|
|
36
|
-
return analyzeCommand(
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (options.status) {
|
|
40
|
-
return statusCommand(options);
|
|
60
|
+
return analyzeCommand(desc, options);
|
|
41
61
|
}
|
|
42
62
|
|
|
43
63
|
if (options.report) {
|
|
@@ -49,7 +69,7 @@ export async function agentCommand(description, options = {}) {
|
|
|
49
69
|
}
|
|
50
70
|
|
|
51
71
|
// Main build flow
|
|
52
|
-
return buildCommand(
|
|
72
|
+
return buildCommand(desc, options);
|
|
53
73
|
}
|
|
54
74
|
|
|
55
75
|
/**
|
|
@@ -134,59 +154,133 @@ async function analyzeCommand(description, options) {
|
|
|
134
154
|
}
|
|
135
155
|
|
|
136
156
|
/**
|
|
137
|
-
* Show agent status
|
|
157
|
+
* Show agent status with resume info
|
|
138
158
|
*/
|
|
139
|
-
async function
|
|
159
|
+
async function showAgentStatus(options) {
|
|
160
|
+
const cwd = process.cwd();
|
|
161
|
+
const progress = await loadProgress(cwd);
|
|
162
|
+
|
|
163
|
+
if (!progress) {
|
|
164
|
+
console.log(chalk.yellow('\n📭 No active agent session.\n'));
|
|
165
|
+
console.log(chalk.gray(' Start new: vibecode agent "description" --new\n'));
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.log();
|
|
170
|
+
console.log(chalk.cyan('╭' + '─'.repeat(68) + '╮'));
|
|
171
|
+
console.log(chalk.cyan('│') + chalk.bold.white(' 🤖 AGENT STATUS') + ' '.repeat(50) + chalk.cyan('│'));
|
|
172
|
+
console.log(chalk.cyan('│') + ' '.repeat(68) + chalk.cyan('│'));
|
|
173
|
+
|
|
174
|
+
const projectLine = ` Project: ${progress.projectName}`;
|
|
175
|
+
console.log(chalk.cyan('│') + chalk.white(projectLine) + ' '.repeat(Math.max(0, 66 - projectLine.length)) + chalk.cyan('│'));
|
|
176
|
+
|
|
177
|
+
const completed = progress.completedModules?.length || progress.currentModule || 0;
|
|
178
|
+
const progressLine = ` Progress: ${completed}/${progress.totalModules} modules`;
|
|
179
|
+
console.log(chalk.cyan('│') + chalk.white(progressLine) + ' '.repeat(Math.max(0, 66 - progressLine.length)) + chalk.cyan('│'));
|
|
180
|
+
|
|
181
|
+
console.log(chalk.cyan('│') + ' '.repeat(68) + chalk.cyan('│'));
|
|
182
|
+
|
|
183
|
+
// Show modules with status
|
|
184
|
+
for (const mod of progress.modules || []) {
|
|
185
|
+
let icon, color;
|
|
186
|
+
switch (mod.status) {
|
|
187
|
+
case 'done':
|
|
188
|
+
icon = chalk.green('✓');
|
|
189
|
+
color = chalk.green;
|
|
190
|
+
break;
|
|
191
|
+
case 'building':
|
|
192
|
+
icon = chalk.yellow('◐');
|
|
193
|
+
color = chalk.yellow;
|
|
194
|
+
break;
|
|
195
|
+
case 'failed':
|
|
196
|
+
icon = chalk.red('✗');
|
|
197
|
+
color = chalk.red;
|
|
198
|
+
break;
|
|
199
|
+
default:
|
|
200
|
+
icon = chalk.gray('○');
|
|
201
|
+
color = chalk.gray;
|
|
202
|
+
}
|
|
203
|
+
const modLine = ` ${icon} ${color(mod.name)}`;
|
|
204
|
+
// Approximate length without ANSI codes
|
|
205
|
+
const approxLen = 5 + mod.name.length;
|
|
206
|
+
console.log(chalk.cyan('│') + modLine + ' '.repeat(Math.max(0, 66 - approxLen)) + chalk.cyan('│'));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (progress.error) {
|
|
210
|
+
console.log(chalk.cyan('│') + ' '.repeat(68) + chalk.cyan('│'));
|
|
211
|
+
const errorLine = ` Error: ${progress.error.substring(0, 55)}`;
|
|
212
|
+
console.log(chalk.cyan('│') + chalk.red(errorLine) + ' '.repeat(Math.max(0, 66 - errorLine.length)) + chalk.cyan('│'));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
console.log(chalk.cyan('│') + ' '.repeat(68) + chalk.cyan('│'));
|
|
216
|
+
|
|
217
|
+
const timeLine = ` Last updated: ${new Date(progress.lastUpdated).toLocaleString()}`;
|
|
218
|
+
console.log(chalk.cyan('│') + chalk.gray(timeLine) + ' '.repeat(Math.max(0, 66 - timeLine.length)) + chalk.cyan('│'));
|
|
219
|
+
|
|
220
|
+
console.log(chalk.cyan('│') + ' '.repeat(68) + chalk.cyan('│'));
|
|
221
|
+
console.log(chalk.cyan('│') + chalk.yellow(' 💡 Resume: vibecode agent --resume') + ' '.repeat(29) + chalk.cyan('│'));
|
|
222
|
+
console.log(chalk.cyan('│') + ' '.repeat(68) + chalk.cyan('│'));
|
|
223
|
+
console.log(chalk.cyan('╰' + '─'.repeat(68) + '╯'));
|
|
224
|
+
console.log();
|
|
225
|
+
|
|
226
|
+
// Output JSON if requested
|
|
227
|
+
if (options.json) {
|
|
228
|
+
console.log(JSON.stringify(progress, null, 2));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Resume agent from last stopped module
|
|
234
|
+
*/
|
|
235
|
+
async function resumeCommand(options) {
|
|
236
|
+
const cwd = process.cwd();
|
|
237
|
+
|
|
238
|
+
// Check for progress
|
|
239
|
+
const progress = await loadProgress(cwd);
|
|
240
|
+
if (!progress) {
|
|
241
|
+
console.log(chalk.yellow('\n📭 No session to resume.\n'));
|
|
242
|
+
console.log(chalk.gray(' Start new: vibecode agent "description" --new\n'));
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Create agent and resume
|
|
140
247
|
const agent = createAgent({
|
|
141
|
-
projectPath:
|
|
248
|
+
projectPath: cwd,
|
|
249
|
+
verbose: options.verbose || false
|
|
142
250
|
});
|
|
143
251
|
|
|
144
252
|
try {
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
console.log();
|
|
149
|
-
console.log(chalk.cyan('Agent Status'));
|
|
150
|
-
console.log(chalk.gray('─'.repeat(40)));
|
|
151
|
-
console.log();
|
|
152
|
-
|
|
153
|
-
console.log(` Initialized: ${status.initialized ? chalk.green('Yes') : chalk.red('No')}`);
|
|
154
|
-
console.log(` Project: ${status.projectPath}`);
|
|
155
|
-
|
|
156
|
-
if (status.memoryStats) {
|
|
157
|
-
console.log();
|
|
158
|
-
console.log(chalk.cyan('Memory Stats'));
|
|
159
|
-
console.log(chalk.gray('─'.repeat(40)));
|
|
160
|
-
console.log(` Modules: ${status.memoryStats.modulesCompleted}/${status.memoryStats.modulesTotal} completed`);
|
|
161
|
-
console.log(` Decisions: ${status.memoryStats.decisionsCount}`);
|
|
162
|
-
console.log(` Patterns: ${status.memoryStats.patternsCount}`);
|
|
163
|
-
console.log(` Errors: ${status.memoryStats.errorsFixed}/${status.memoryStats.errorsTotal} fixed`);
|
|
164
|
-
console.log(` Files: ${status.memoryStats.filesCreated}`);
|
|
165
|
-
}
|
|
253
|
+
// Determine from module
|
|
254
|
+
const fromModule = options.from ? parseInt(options.from) - 1 : undefined;
|
|
166
255
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
console.log(` Failed: ${status.healingStats.failed}`);
|
|
174
|
-
console.log(` Rate: ${status.healingStats.successRate}`);
|
|
175
|
-
}
|
|
256
|
+
const resumeOptions = {
|
|
257
|
+
maxModuleRetries: options.maxRetries || 3,
|
|
258
|
+
testAfterEachModule: !options.skipTests,
|
|
259
|
+
continueOnFailure: options.continue || false,
|
|
260
|
+
fromModule
|
|
261
|
+
};
|
|
176
262
|
|
|
177
|
-
|
|
263
|
+
const result = await agent.resume(resumeOptions);
|
|
178
264
|
|
|
179
|
-
//
|
|
180
|
-
if (
|
|
181
|
-
|
|
265
|
+
// Exit with appropriate code
|
|
266
|
+
if (result && !result.success) {
|
|
267
|
+
process.exit(1);
|
|
182
268
|
}
|
|
183
269
|
|
|
184
270
|
} catch (error) {
|
|
185
|
-
printError(`
|
|
271
|
+
printError(`Resume failed: ${error.message}`);
|
|
272
|
+
console.log(chalk.gray('Check .vibecode/agent/orchestrator.log for details.'));
|
|
186
273
|
process.exit(1);
|
|
187
274
|
}
|
|
188
275
|
}
|
|
189
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Legacy status command (for compatibility)
|
|
279
|
+
*/
|
|
280
|
+
async function statusCommand(options) {
|
|
281
|
+
return showAgentStatus(options);
|
|
282
|
+
}
|
|
283
|
+
|
|
190
284
|
/**
|
|
191
285
|
* Export memory report
|
|
192
286
|
*/
|