@nclamvn/vibecode-cli 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vibecode.js +50 -1
- package/package.json +1 -1
- package/src/commands/build.js +470 -0
- package/src/commands/config.js +149 -0
- package/src/commands/plan.js +105 -0
- package/src/commands/review.js +290 -0
- package/src/commands/snapshot.js +252 -0
- package/src/commands/status.js +13 -2
- package/src/config/constants.js +26 -16
- package/src/config/templates.js +272 -1
- package/src/core/session.js +8 -1
- package/src/index.js +14 -0
- package/src/providers/claude-code.js +159 -0
- package/src/providers/index.js +45 -0
- package/src/ui/output.js +3 -3
package/src/config/constants.js
CHANGED
|
@@ -3,51 +3,61 @@
|
|
|
3
3
|
// Spec Hash: 0fe43335f5a325e3279a079ce616c052
|
|
4
4
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
5
|
|
|
6
|
-
export const VERSION = '
|
|
6
|
+
export const VERSION = '2.0.0';
|
|
7
7
|
export const SPEC_HASH = '0fe43335f5a325e3279a079ce616c052';
|
|
8
8
|
|
|
9
9
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
-
// State Machine
|
|
10
|
+
// State Machine - Phase A + B
|
|
11
11
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
12
|
|
|
13
13
|
export const STATES = {
|
|
14
|
+
// Phase A: Planning
|
|
14
15
|
INIT: 'INIT',
|
|
15
16
|
INTAKE_CAPTURED: 'INTAKE_CAPTURED',
|
|
16
17
|
BLUEPRINT_DRAFTED: 'BLUEPRINT_DRAFTED',
|
|
17
18
|
CONTRACT_DRAFTED: 'CONTRACT_DRAFTED',
|
|
18
19
|
CONTRACT_LOCKED: 'CONTRACT_LOCKED',
|
|
20
|
+
// Phase B: Execution
|
|
21
|
+
PLAN_CREATED: 'PLAN_CREATED',
|
|
19
22
|
BUILD_IN_PROGRESS: 'BUILD_IN_PROGRESS',
|
|
20
23
|
BUILD_DONE: 'BUILD_DONE',
|
|
21
24
|
REVIEW_PASSED: 'REVIEW_PASSED',
|
|
22
|
-
REVIEW_FAILED: 'REVIEW_FAILED'
|
|
25
|
+
REVIEW_FAILED: 'REVIEW_FAILED',
|
|
26
|
+
SHIPPED: 'SHIPPED'
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
export const TRANSITIONS = {
|
|
30
|
+
// Phase A transitions
|
|
26
31
|
[STATES.INIT]: [STATES.INTAKE_CAPTURED],
|
|
27
32
|
[STATES.INTAKE_CAPTURED]: [STATES.BLUEPRINT_DRAFTED],
|
|
28
33
|
[STATES.BLUEPRINT_DRAFTED]: [STATES.CONTRACT_DRAFTED, STATES.INTAKE_CAPTURED],
|
|
29
34
|
[STATES.CONTRACT_DRAFTED]: [STATES.CONTRACT_LOCKED, STATES.BLUEPRINT_DRAFTED],
|
|
30
|
-
|
|
35
|
+
// Phase B transitions
|
|
36
|
+
[STATES.CONTRACT_LOCKED]: [STATES.PLAN_CREATED],
|
|
37
|
+
[STATES.PLAN_CREATED]: [STATES.BUILD_IN_PROGRESS],
|
|
31
38
|
[STATES.BUILD_IN_PROGRESS]: [STATES.BUILD_DONE],
|
|
32
39
|
[STATES.BUILD_DONE]: [STATES.REVIEW_PASSED, STATES.REVIEW_FAILED],
|
|
33
|
-
[STATES.REVIEW_PASSED]: [],
|
|
34
|
-
[STATES.REVIEW_FAILED]: [STATES.BUILD_IN_PROGRESS, STATES.
|
|
40
|
+
[STATES.REVIEW_PASSED]: [STATES.SHIPPED],
|
|
41
|
+
[STATES.REVIEW_FAILED]: [STATES.BUILD_IN_PROGRESS, STATES.PLAN_CREATED],
|
|
42
|
+
[STATES.SHIPPED]: []
|
|
35
43
|
};
|
|
36
44
|
|
|
37
45
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
38
|
-
// Progress Display Mapping
|
|
46
|
+
// Progress Display Mapping (7 stages)
|
|
39
47
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
40
48
|
|
|
41
49
|
export const PROGRESS_MAP = {
|
|
42
|
-
[STATES.INIT]: { intake: '🔄', blueprint: '⬜', contract: '⬜', build: '⬜', ship: '⬜' },
|
|
43
|
-
[STATES.INTAKE_CAPTURED]: { intake: '✅', blueprint: '🔄', contract: '⬜', build: '⬜', ship: '⬜' },
|
|
44
|
-
[STATES.BLUEPRINT_DRAFTED]: { intake: '✅', blueprint: '✅', contract: '🔄', build: '⬜', ship: '⬜' },
|
|
45
|
-
[STATES.CONTRACT_DRAFTED]: { intake: '✅', blueprint: '✅', contract: '🔄', build: '⬜', ship: '⬜' },
|
|
46
|
-
[STATES.CONTRACT_LOCKED]: { intake: '✅', blueprint: '✅', contract: '✅',
|
|
47
|
-
[STATES.
|
|
48
|
-
[STATES.
|
|
49
|
-
[STATES.
|
|
50
|
-
[STATES.
|
|
50
|
+
[STATES.INIT]: { intake: '🔄', blueprint: '⬜', contract: '⬜', plan: '⬜', build: '⬜', review: '⬜', ship: '⬜' },
|
|
51
|
+
[STATES.INTAKE_CAPTURED]: { intake: '✅', blueprint: '🔄', contract: '⬜', plan: '⬜', build: '⬜', review: '⬜', ship: '⬜' },
|
|
52
|
+
[STATES.BLUEPRINT_DRAFTED]: { intake: '✅', blueprint: '✅', contract: '🔄', plan: '⬜', build: '⬜', review: '⬜', ship: '⬜' },
|
|
53
|
+
[STATES.CONTRACT_DRAFTED]: { intake: '✅', blueprint: '✅', contract: '🔄', plan: '⬜', build: '⬜', review: '⬜', ship: '⬜' },
|
|
54
|
+
[STATES.CONTRACT_LOCKED]: { intake: '✅', blueprint: '✅', contract: '✅', plan: '🔄', build: '⬜', review: '⬜', ship: '⬜' },
|
|
55
|
+
[STATES.PLAN_CREATED]: { intake: '✅', blueprint: '✅', contract: '✅', plan: '✅', build: '🔄', review: '⬜', ship: '⬜' },
|
|
56
|
+
[STATES.BUILD_IN_PROGRESS]: { intake: '✅', blueprint: '✅', contract: '✅', plan: '✅', build: '🔄', review: '⬜', ship: '⬜' },
|
|
57
|
+
[STATES.BUILD_DONE]: { intake: '✅', blueprint: '✅', contract: '✅', plan: '✅', build: '✅', review: '🔄', ship: '⬜' },
|
|
58
|
+
[STATES.REVIEW_PASSED]: { intake: '✅', blueprint: '✅', contract: '✅', plan: '✅', build: '✅', review: '✅', ship: '🔄' },
|
|
59
|
+
[STATES.REVIEW_FAILED]: { intake: '✅', blueprint: '✅', contract: '✅', plan: '✅', build: '⚠️', review: '❌', ship: '⬜' },
|
|
60
|
+
[STATES.SHIPPED]: { intake: '✅', blueprint: '✅', contract: '✅', plan: '✅', build: '✅', review: '✅', ship: '✅' }
|
|
51
61
|
};
|
|
52
62
|
|
|
53
63
|
// ─────────────────────────────────────────────────────────────────────────────
|
package/src/config/templates.js
CHANGED
|
@@ -188,7 +188,278 @@ export function getContractTemplate(projectName, sessionId) {
|
|
|
188
188
|
|
|
189
189
|
---
|
|
190
190
|
|
|
191
|
-
*Generated by Vibecode CLI v1.
|
|
191
|
+
*Generated by Vibecode CLI v1.1*
|
|
192
192
|
*Lock this contract with \`vibecode lock\` when ready*
|
|
193
193
|
`;
|
|
194
194
|
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get plan template
|
|
198
|
+
*/
|
|
199
|
+
export function getPlanTemplate(projectName, sessionId, specHash, contractContent) {
|
|
200
|
+
const timestamp = new Date().toISOString();
|
|
201
|
+
|
|
202
|
+
// Extract deliverables from contract
|
|
203
|
+
const deliverablesMatch = contractContent.match(/## 📦 Deliverables[\s\S]*?\|[\s\S]*?(?=\n---|\n##|$)/);
|
|
204
|
+
const deliverables = deliverablesMatch ? deliverablesMatch[0] : '[Extract from contract]';
|
|
205
|
+
|
|
206
|
+
return `# 📋 EXECUTION PLAN: ${projectName}
|
|
207
|
+
|
|
208
|
+
## Session: ${sessionId}
|
|
209
|
+
## Created: ${timestamp}
|
|
210
|
+
## Spec Hash: ${specHash}
|
|
211
|
+
## Status: ACTIVE
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 🎯 Objective
|
|
216
|
+
|
|
217
|
+
Execute the locked contract deliverables in order.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 📦 Deliverables from Contract
|
|
222
|
+
|
|
223
|
+
${deliverables}
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 🔧 Execution Steps
|
|
228
|
+
|
|
229
|
+
| Step | Task | Command/Action | Status |
|
|
230
|
+
|------|------|----------------|--------|
|
|
231
|
+
| 1 | Setup | Initialize project structure | ⬜ |
|
|
232
|
+
| 2 | Core Build | Implement main functionality | ⬜ |
|
|
233
|
+
| 3 | Styling | Apply design system | ⬜ |
|
|
234
|
+
| 4 | Testing | Run tests and validate | ⬜ |
|
|
235
|
+
| 5 | Review | Check against acceptance criteria | ⬜ |
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 📝 Notes
|
|
240
|
+
|
|
241
|
+
- Follow contract scope strictly
|
|
242
|
+
- Capture evidence for each step
|
|
243
|
+
- No scope creep allowed
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## ⏱️ Time Tracking
|
|
248
|
+
|
|
249
|
+
| Phase | Started | Completed | Duration |
|
|
250
|
+
|-------|---------|-----------|----------|
|
|
251
|
+
| Build | ${timestamp} | - | - |
|
|
252
|
+
| Review | - | - | - |
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
*Generated by Vibecode CLI v1.1*
|
|
257
|
+
`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Get coder pack template - instructions for AI coder
|
|
262
|
+
*/
|
|
263
|
+
export function getCoderPackTemplate(projectName, sessionId, specHash, contractContent, blueprintContent) {
|
|
264
|
+
const timestamp = new Date().toISOString();
|
|
265
|
+
|
|
266
|
+
return `# 🏗️ CODER PACK: ${projectName}
|
|
267
|
+
|
|
268
|
+
## Session: ${sessionId}
|
|
269
|
+
## Generated: ${timestamp}
|
|
270
|
+
## Spec Hash: ${specHash}
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## ⚠️ IMPORTANT - READ FIRST
|
|
275
|
+
|
|
276
|
+
You are the **Thợ (Builder)**. Your job is to execute the locked contract exactly as specified.
|
|
277
|
+
|
|
278
|
+
**Rules:**
|
|
279
|
+
1. Do NOT add features not in the contract
|
|
280
|
+
2. Do NOT modify the contract
|
|
281
|
+
3. Capture evidence of your work
|
|
282
|
+
4. Report any blockers immediately
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## 📜 CONTRACT (LOCKED)
|
|
287
|
+
|
|
288
|
+
${contractContent}
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 📘 BLUEPRINT REFERENCE
|
|
293
|
+
|
|
294
|
+
${blueprintContent}
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## 🔧 BUILD INSTRUCTIONS
|
|
299
|
+
|
|
300
|
+
1. Read the contract deliverables table
|
|
301
|
+
2. Implement each item in order
|
|
302
|
+
3. For each deliverable:
|
|
303
|
+
- Create/modify the required files
|
|
304
|
+
- Test the implementation
|
|
305
|
+
- Mark as complete
|
|
306
|
+
4. Capture evidence:
|
|
307
|
+
- Screenshots if UI
|
|
308
|
+
- Terminal output if CLI
|
|
309
|
+
- Test results if automated
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## 📁 Evidence Collection
|
|
314
|
+
|
|
315
|
+
Save evidence to: \`.vibecode/sessions/${sessionId}/evidence/\`
|
|
316
|
+
|
|
317
|
+
Required evidence:
|
|
318
|
+
- \`changes.diff\` - Git diff of all changes
|
|
319
|
+
- \`build.log\` - Terminal output during build
|
|
320
|
+
- \`screenshots/\` - Visual evidence if applicable
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## ✅ Completion Checklist
|
|
325
|
+
|
|
326
|
+
- [ ] All deliverables implemented
|
|
327
|
+
- [ ] All acceptance criteria met
|
|
328
|
+
- [ ] Evidence captured
|
|
329
|
+
- [ ] No scope creep
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
*Run \`vibecode build --complete\` when finished*
|
|
334
|
+
|
|
335
|
+
*Generated by Vibecode CLI v1.1*
|
|
336
|
+
`;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Get build report template
|
|
341
|
+
*/
|
|
342
|
+
export function getBuildReportTemplate(projectName, sessionId, specHash, startTime, endTime, evidence) {
|
|
343
|
+
const duration = endTime ? Math.round((new Date(endTime) - new Date(startTime)) / 1000 / 60) : 'In progress';
|
|
344
|
+
|
|
345
|
+
return `# 🏗️ BUILD REPORT: ${projectName}
|
|
346
|
+
|
|
347
|
+
## Session: ${sessionId}
|
|
348
|
+
## Spec Hash: ${specHash}
|
|
349
|
+
## Status: ${endTime ? 'COMPLETED' : 'IN PROGRESS'}
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## ⏱️ Timeline
|
|
354
|
+
|
|
355
|
+
| Metric | Value |
|
|
356
|
+
|--------|-------|
|
|
357
|
+
| Started | ${startTime} |
|
|
358
|
+
| Completed | ${endTime || '-'} |
|
|
359
|
+
| Duration | ${duration} ${endTime ? 'minutes' : ''} |
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## 📁 Evidence Collected
|
|
364
|
+
|
|
365
|
+
${evidence.hasDiff ? '- ✅ changes.diff' : '- ⬜ changes.diff'}
|
|
366
|
+
${evidence.hasLog ? '- ✅ build.log' : '- ⬜ build.log'}
|
|
367
|
+
${evidence.screenshots > 0 ? `- ✅ ${evidence.screenshots} screenshots` : '- ⬜ No screenshots'}
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## 📊 Summary
|
|
372
|
+
|
|
373
|
+
- Files changed: ${evidence.filesChanged || 'N/A'}
|
|
374
|
+
- Lines added: ${evidence.linesAdded || 'N/A'}
|
|
375
|
+
- Lines removed: ${evidence.linesRemoved || 'N/A'}
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## 🔍 Next Steps
|
|
380
|
+
|
|
381
|
+
${endTime ? '1. Run `vibecode review` to validate build' : '1. Continue building\n2. Run `vibecode build --complete` when done'}
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
*Generated by Vibecode CLI v1.1*
|
|
386
|
+
`;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Get review report template
|
|
391
|
+
*/
|
|
392
|
+
export function getReviewReportTemplate(projectName, sessionId, specHash, checks, passed) {
|
|
393
|
+
const timestamp = new Date().toISOString();
|
|
394
|
+
|
|
395
|
+
const checksList = checks.map(c =>
|
|
396
|
+
`| ${c.name} | ${c.passed ? '✅ PASS' : '❌ FAIL'} | ${c.message || '-'} |`
|
|
397
|
+
).join('\n');
|
|
398
|
+
|
|
399
|
+
return `# 🔍 REVIEW REPORT: ${projectName}
|
|
400
|
+
|
|
401
|
+
## Session: ${sessionId}
|
|
402
|
+
## Spec Hash: ${specHash}
|
|
403
|
+
## Reviewed: ${timestamp}
|
|
404
|
+
## Result: ${passed ? '✅ PASSED' : '❌ FAILED'}
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## 📋 Automated Checks
|
|
409
|
+
|
|
410
|
+
| Check | Result | Details |
|
|
411
|
+
|-------|--------|---------|
|
|
412
|
+
${checksList}
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## ✔️ Acceptance Criteria Review
|
|
417
|
+
|
|
418
|
+
[Manually verified against contract]
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## 📊 Summary
|
|
423
|
+
|
|
424
|
+
- Total checks: ${checks.length}
|
|
425
|
+
- Passed: ${checks.filter(c => c.passed).length}
|
|
426
|
+
- Failed: ${checks.filter(c => !c.passed).length}
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## 🎯 Verdict
|
|
431
|
+
|
|
432
|
+
${passed ? `
|
|
433
|
+
**REVIEW PASSED**
|
|
434
|
+
|
|
435
|
+
All checks passed. Ready for shipping.
|
|
436
|
+
Run \`vibecode snapshot\` to create release.
|
|
437
|
+
` : `
|
|
438
|
+
**REVIEW FAILED**
|
|
439
|
+
|
|
440
|
+
Some checks failed. Please fix the issues and run \`vibecode build\` again.
|
|
441
|
+
`}
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
*Generated by Vibecode CLI v1.1*
|
|
446
|
+
`;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Get manifest template for release
|
|
451
|
+
*/
|
|
452
|
+
export function getManifestTemplate(projectName, version, specHash, sessionId, files) {
|
|
453
|
+
const timestamp = new Date().toISOString();
|
|
454
|
+
|
|
455
|
+
return {
|
|
456
|
+
name: projectName,
|
|
457
|
+
version: version,
|
|
458
|
+
specHash: specHash,
|
|
459
|
+
sessionId: sessionId,
|
|
460
|
+
timestamp: timestamp,
|
|
461
|
+
generator: 'vibecode-cli@1.1.0',
|
|
462
|
+
files: files,
|
|
463
|
+
status: 'SHIPPED'
|
|
464
|
+
};
|
|
465
|
+
}
|
package/src/core/session.js
CHANGED
|
@@ -84,9 +84,16 @@ export async function getSessionFilesStatus() {
|
|
|
84
84
|
if (!sessionPath) return null;
|
|
85
85
|
|
|
86
86
|
return {
|
|
87
|
+
// Phase A files
|
|
87
88
|
intake: await pathExists(path.join(sessionPath, 'intake.md')),
|
|
88
89
|
blueprint: await pathExists(path.join(sessionPath, 'blueprint.md')),
|
|
89
|
-
contract: await pathExists(path.join(sessionPath, 'contract.md'))
|
|
90
|
+
contract: await pathExists(path.join(sessionPath, 'contract.md')),
|
|
91
|
+
// Phase B files
|
|
92
|
+
plan: await pathExists(path.join(sessionPath, 'plan.md')),
|
|
93
|
+
coderPack: await pathExists(path.join(sessionPath, 'coder_pack.md')),
|
|
94
|
+
buildReport: await pathExists(path.join(sessionPath, 'build_report.md')),
|
|
95
|
+
reviewReport: await pathExists(path.join(sessionPath, 'review_report.md')),
|
|
96
|
+
manifest: await pathExists(path.join(sessionPath, 'manifest.json'))
|
|
90
97
|
};
|
|
91
98
|
}
|
|
92
99
|
|
package/src/index.js
CHANGED
|
@@ -3,10 +3,24 @@
|
|
|
3
3
|
// Spec Hash: 0fe43335f5a325e3279a079ce616c052
|
|
4
4
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
5
|
|
|
6
|
+
// Phase A Commands
|
|
6
7
|
export { initCommand } from './commands/init.js';
|
|
7
8
|
export { startCommand } from './commands/start.js';
|
|
8
9
|
export { statusCommand } from './commands/status.js';
|
|
9
10
|
export { lockCommand } from './commands/lock.js';
|
|
10
11
|
export { doctorCommand } from './commands/doctor.js';
|
|
11
12
|
|
|
13
|
+
// Phase B Commands
|
|
14
|
+
export { planCommand } from './commands/plan.js';
|
|
15
|
+
export { buildCommand } from './commands/build.js';
|
|
16
|
+
export { reviewCommand } from './commands/review.js';
|
|
17
|
+
export { snapshotCommand } from './commands/snapshot.js';
|
|
18
|
+
|
|
19
|
+
// Phase C Commands
|
|
20
|
+
export { configCommand } from './commands/config.js';
|
|
21
|
+
|
|
22
|
+
// Constants
|
|
12
23
|
export { VERSION, SPEC_HASH, STATES } from './config/constants.js';
|
|
24
|
+
|
|
25
|
+
// Providers
|
|
26
|
+
export { PROVIDERS, getProvider, getDefaultProvider } from './providers/index.js';
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Claude Code Provider
|
|
3
|
+
// "Claude/LLM là PIPELINE, là KIẾN TRÚC SƯ"
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { pathExists, appendToFile } from '../utils/files.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Claude Code optimal configuration
|
|
12
|
+
* Contract LOCKED = License to build (không cần hỏi thêm)
|
|
13
|
+
*/
|
|
14
|
+
export const CLAUDE_CODE_CONFIG = {
|
|
15
|
+
command: 'claude',
|
|
16
|
+
flags: [
|
|
17
|
+
'--dangerously-skip-permissions', // Trust the AI - Contract đã locked
|
|
18
|
+
],
|
|
19
|
+
timeout: 30 * 60 * 1000, // 30 minutes max
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if Claude Code CLI is available
|
|
24
|
+
*/
|
|
25
|
+
export async function isClaudeCodeAvailable() {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
const proc = spawn('which', ['claude'], { shell: true });
|
|
28
|
+
proc.on('close', (code) => {
|
|
29
|
+
resolve(code === 0);
|
|
30
|
+
});
|
|
31
|
+
proc.on('error', () => {
|
|
32
|
+
resolve(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Spawn Claude Code with optimal settings
|
|
39
|
+
*
|
|
40
|
+
* @param {string} prompt - The coder pack / prompt to send
|
|
41
|
+
* @param {object} options - Configuration options
|
|
42
|
+
* @param {string} options.cwd - Working directory
|
|
43
|
+
* @param {string} options.logPath - Path to write build logs
|
|
44
|
+
* @param {function} options.onOutput - Callback for output
|
|
45
|
+
* @returns {Promise<{success: boolean, code: number}>}
|
|
46
|
+
*/
|
|
47
|
+
export async function spawnClaudeCode(prompt, options = {}) {
|
|
48
|
+
const { cwd, logPath, onOutput } = options;
|
|
49
|
+
const fs = await import('fs-extra');
|
|
50
|
+
const os = await import('os');
|
|
51
|
+
|
|
52
|
+
// Check if Claude Code is available
|
|
53
|
+
const available = await isClaudeCodeAvailable();
|
|
54
|
+
if (!available) {
|
|
55
|
+
throw new Error('Claude Code CLI not found. Install with: npm install -g @anthropic/claude-code');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Write prompt to temp file to avoid shell escaping issues
|
|
59
|
+
const tempDir = os.default.tmpdir();
|
|
60
|
+
const promptFile = path.join(tempDir, `vibecode-prompt-${Date.now()}.md`);
|
|
61
|
+
await fs.default.writeFile(promptFile, prompt, 'utf-8');
|
|
62
|
+
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
// Use cat to pipe the prompt file content to claude
|
|
65
|
+
const command = `cat "${promptFile}" | claude ${CLAUDE_CODE_CONFIG.flags.join(' ')}`;
|
|
66
|
+
|
|
67
|
+
// Log the command being run
|
|
68
|
+
if (logPath) {
|
|
69
|
+
appendToFile(logPath, `\n[${new Date().toISOString()}] Running: claude with prompt from ${promptFile}\n`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const proc = spawn(command, [], {
|
|
73
|
+
cwd: cwd || process.cwd(),
|
|
74
|
+
stdio: 'inherit', // Stream directly to terminal
|
|
75
|
+
shell: true,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
let timeoutId = setTimeout(() => {
|
|
79
|
+
proc.kill();
|
|
80
|
+
// Cleanup temp file
|
|
81
|
+
fs.default.remove(promptFile).catch(() => {});
|
|
82
|
+
reject(new Error('Claude Code process timed out'));
|
|
83
|
+
}, CLAUDE_CODE_CONFIG.timeout);
|
|
84
|
+
|
|
85
|
+
proc.on('close', async (code) => {
|
|
86
|
+
clearTimeout(timeoutId);
|
|
87
|
+
|
|
88
|
+
// Cleanup temp file
|
|
89
|
+
await fs.default.remove(promptFile).catch(() => {});
|
|
90
|
+
|
|
91
|
+
const result = {
|
|
92
|
+
success: code === 0,
|
|
93
|
+
code: code || 0,
|
|
94
|
+
timestamp: new Date().toISOString()
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
if (logPath) {
|
|
98
|
+
const status = result.success ? 'SUCCESS' : 'FAILED';
|
|
99
|
+
appendToFile(logPath, `\n[${result.timestamp}] Claude Code ${status} (exit code: ${code})\n`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
resolve(result);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
proc.on('error', async (error) => {
|
|
106
|
+
clearTimeout(timeoutId);
|
|
107
|
+
// Cleanup temp file
|
|
108
|
+
await fs.default.remove(promptFile).catch(() => {});
|
|
109
|
+
|
|
110
|
+
if (logPath) {
|
|
111
|
+
appendToFile(logPath, `\n[${new Date().toISOString()}] ERROR: ${error.message}\n`);
|
|
112
|
+
}
|
|
113
|
+
reject(error);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Build prompt with optional CLAUDE.md injection
|
|
120
|
+
*
|
|
121
|
+
* @param {string} coderPackContent - Content of coder_pack.md
|
|
122
|
+
* @param {string} projectRoot - Project root directory
|
|
123
|
+
* @returns {Promise<string>} - Final prompt
|
|
124
|
+
*/
|
|
125
|
+
export async function buildPromptWithContext(coderPackContent, projectRoot) {
|
|
126
|
+
let fullPrompt = coderPackContent;
|
|
127
|
+
|
|
128
|
+
// Check for CLAUDE.md in project root
|
|
129
|
+
const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
|
|
130
|
+
if (await pathExists(claudeMdPath)) {
|
|
131
|
+
const fs = await import('fs-extra');
|
|
132
|
+
const claudeMd = await fs.default.readFile(claudeMdPath, 'utf-8');
|
|
133
|
+
|
|
134
|
+
// Inject CLAUDE.md rules before coder pack
|
|
135
|
+
fullPrompt = `# PROJECT RULES (from CLAUDE.md)
|
|
136
|
+
|
|
137
|
+
${claudeMd}
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
# BUILD INSTRUCTIONS
|
|
142
|
+
|
|
143
|
+
${coderPackContent}`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return fullPrompt;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get provider info for status display
|
|
151
|
+
*/
|
|
152
|
+
export function getProviderInfo() {
|
|
153
|
+
return {
|
|
154
|
+
name: 'Claude Code',
|
|
155
|
+
command: CLAUDE_CODE_CONFIG.command,
|
|
156
|
+
mode: '--dangerously-skip-permissions',
|
|
157
|
+
description: 'AI coding with guardrails disabled (contract-approved)'
|
|
158
|
+
};
|
|
159
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - AI Provider Manager
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
spawnClaudeCode,
|
|
7
|
+
isClaudeCodeAvailable,
|
|
8
|
+
buildPromptWithContext,
|
|
9
|
+
getProviderInfo,
|
|
10
|
+
CLAUDE_CODE_CONFIG
|
|
11
|
+
} from './claude-code.js';
|
|
12
|
+
|
|
13
|
+
// Future providers:
|
|
14
|
+
// export { callAnthropicAPI } from './anthropic-api.js';
|
|
15
|
+
// export { spawnCursor } from './cursor.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Available providers
|
|
19
|
+
*/
|
|
20
|
+
export const PROVIDERS = {
|
|
21
|
+
'claude-code': {
|
|
22
|
+
name: 'Claude Code',
|
|
23
|
+
description: 'Official Claude CLI for coding',
|
|
24
|
+
available: true
|
|
25
|
+
},
|
|
26
|
+
'anthropic-api': {
|
|
27
|
+
name: 'Anthropic API',
|
|
28
|
+
description: 'Direct API calls (coming soon)',
|
|
29
|
+
available: false
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get provider by name
|
|
35
|
+
*/
|
|
36
|
+
export function getProvider(name) {
|
|
37
|
+
return PROVIDERS[name] || null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get default provider
|
|
42
|
+
*/
|
|
43
|
+
export function getDefaultProvider() {
|
|
44
|
+
return 'claude-code';
|
|
45
|
+
}
|
package/src/ui/output.js
CHANGED
|
@@ -52,11 +52,11 @@ export function printInfo(message) {
|
|
|
52
52
|
export function printProgress(state) {
|
|
53
53
|
const progress = PROGRESS_MAP[state] || PROGRESS_MAP.INIT;
|
|
54
54
|
|
|
55
|
-
const bar = ` ${progress.intake} INTAKE ${progress.blueprint} BLUEPRINT ${progress.contract} CONTRACT ${progress.build} BUILD ${progress.ship} SHIP`;
|
|
55
|
+
const bar = ` ${progress.intake} INTAKE ${progress.blueprint} BLUEPRINT ${progress.contract} CONTRACT ${progress.plan} PLAN ${progress.build} BUILD ${progress.review} REVIEW ${progress.ship} SHIP`;
|
|
56
56
|
|
|
57
|
-
console.log(chalk.gray('┌─ Progress
|
|
57
|
+
console.log(chalk.gray('┌─ Progress ───────────────────────────────────────────────────────────────────────────┐'));
|
|
58
58
|
console.log(chalk.white(bar));
|
|
59
|
-
console.log(chalk.gray('
|
|
59
|
+
console.log(chalk.gray('└──────────────────────────────────────────────────────────────────────────────────────┘'));
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|