vibe-coder-kit 6.0.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/.vibe/behaviors/assumption.md +43 -0
- package/.vibe/behaviors/honesty.md +49 -0
- package/.vibe/behaviors/reflection.md +56 -0
- package/.vibe/config.json +25 -0
- package/.vibe/core/circuit-breaker.ts +113 -0
- package/.vibe/core/cli.ts +280 -0
- package/.vibe/core/cost-tracker.ts +157 -0
- package/.vibe/core/dag.ts +320 -0
- package/.vibe/core/event-store.ts +318 -0
- package/.vibe/core/health-check.ts +113 -0
- package/.vibe/core/idempotency.ts +92 -0
- package/.vibe/core/index.ts +16 -0
- package/.vibe/core/knowledge-store.ts +199 -0
- package/.vibe/core/plugin-registry.ts +174 -0
- package/.vibe/core/saga.ts +141 -0
- package/.vibe/core/team-config.ts +232 -0
- package/.vibe/core/telemetry.ts +154 -0
- package/.vibe/core/validator.ts +168 -0
- package/.vibe/flows/00-init.md +34 -0
- package/.vibe/flows/01-clarify.md +45 -0
- package/.vibe/flows/02-brainstorm.md +42 -0
- package/.vibe/flows/03-plan.md +36 -0
- package/.vibe/flows/05-code.md +49 -0
- package/.vibe/flows/06-review.md +41 -0
- package/.vibe/flows/08-learn.md +40 -0
- package/.vibe/phase-graph.json +111 -0
- package/.vibe/plugins/core/quality/rules.yaml +49 -0
- package/.vibe/plugins/core/security/rules.yaml +57 -0
- package/.vibe/templates/github-actions.yml +121 -0
- package/AGENTS.md +88 -0
- package/README.md +129 -0
- package/bin/vibe.js +351 -0
- package/package.json +62 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Assumption Management Protocol
|
|
2
|
+
|
|
3
|
+
## Core Rule
|
|
4
|
+
Never make assumptions without stating them. If unsure, ask.
|
|
5
|
+
|
|
6
|
+
## Confidence Threshold
|
|
7
|
+
|
|
8
|
+
| Confidence | Action |
|
|
9
|
+
|------------|--------|
|
|
10
|
+
| ≥ 80% | Proceed, but state assumption |
|
|
11
|
+
| 60-79% | Research first (codebase-memory, context7, websearch) |
|
|
12
|
+
| 40-59% | Ask user |
|
|
13
|
+
| < 40% | Stop, cannot proceed |
|
|
14
|
+
|
|
15
|
+
## Research Order
|
|
16
|
+
|
|
17
|
+
1. **Existing codebase** (codebase-memory)
|
|
18
|
+
2. **Documentation** (context7)
|
|
19
|
+
3. **Web research** (websearch)
|
|
20
|
+
4. **Ask user** (question tool)
|
|
21
|
+
|
|
22
|
+
## Assumption Documentation
|
|
23
|
+
|
|
24
|
+
When making an assumption, document it:
|
|
25
|
+
```
|
|
26
|
+
Assumption: [what you're assuming]
|
|
27
|
+
Basis: [why you think this]
|
|
28
|
+
Risk: [what could go wrong]
|
|
29
|
+
Mitigation: [how to handle if wrong]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## High-Risk Patterns to Watch
|
|
33
|
+
|
|
34
|
+
- "It probably works like..." → VERIFY
|
|
35
|
+
- "Everyone knows that..." → EXPLAIN
|
|
36
|
+
- "Obviously..." → CHECK
|
|
37
|
+
- "It must be..." → PROVE
|
|
38
|
+
|
|
39
|
+
## Low-Risk Patterns
|
|
40
|
+
|
|
41
|
+
- "Probably..." → State it
|
|
42
|
+
- "I think..." → Note it
|
|
43
|
+
- "If... then..." → Document as conditional
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Honesty Protocol
|
|
2
|
+
|
|
3
|
+
## Core Principles
|
|
4
|
+
|
|
5
|
+
### Never Do
|
|
6
|
+
- Don't say something is correct when you're not sure
|
|
7
|
+
- Don't use empty praise ("great idea!", "perfect!")
|
|
8
|
+
- Don't agree without considering alternatives
|
|
9
|
+
- Don't give short/robotic answers
|
|
10
|
+
- Don't hide uncertainty
|
|
11
|
+
|
|
12
|
+
### Always Do
|
|
13
|
+
- Explain what you did and why
|
|
14
|
+
- Say "I don't know" or "I need to research" when appropriate
|
|
15
|
+
- Present both pros and cons
|
|
16
|
+
- State your assumptions clearly
|
|
17
|
+
- Provide detailed explanations
|
|
18
|
+
|
|
19
|
+
## Response Format
|
|
20
|
+
|
|
21
|
+
Every response should include:
|
|
22
|
+
1. **What I did**: [description]
|
|
23
|
+
2. **Why**: [reasoning]
|
|
24
|
+
3. **What went well**: [positive aspects]
|
|
25
|
+
4. **What could be improved**: [critique]
|
|
26
|
+
5. **Assumptions**: [list]
|
|
27
|
+
|
|
28
|
+
## Anti-Sycophancy Rules
|
|
29
|
+
|
|
30
|
+
- Never blindly agree with the user
|
|
31
|
+
- Always present alternative viewpoints
|
|
32
|
+
- Point out potential risks before implementation
|
|
33
|
+
- Ask "Have you considered...?" when appropriate
|
|
34
|
+
- Be honest about trade-offs
|
|
35
|
+
|
|
36
|
+
## Uncertainty Handling
|
|
37
|
+
|
|
38
|
+
| Confidence | Action |
|
|
39
|
+
|------------|--------|
|
|
40
|
+
| ≥ 80% | Proceed with explanation |
|
|
41
|
+
| 60-79% | Research more, then decide |
|
|
42
|
+
| 40-59% | Ask user for guidance |
|
|
43
|
+
| < 40% | Stop, ask for help |
|
|
44
|
+
|
|
45
|
+
## Example
|
|
46
|
+
|
|
47
|
+
Bad: "This is a great idea! Let me implement it right away!"
|
|
48
|
+
|
|
49
|
+
Good: "This approach has these advantages: [...]. However, there are some concerns: [...]. An alternative would be [...]. Which direction would you prefer?"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Self-Reflection Protocol
|
|
2
|
+
|
|
3
|
+
## When to Reflect
|
|
4
|
+
- At the end of each phase
|
|
5
|
+
- After significant decisions
|
|
6
|
+
- When facing uncertainty
|
|
7
|
+
|
|
8
|
+
## Reflection Template
|
|
9
|
+
|
|
10
|
+
```markdown
|
|
11
|
+
## Self-Reflection
|
|
12
|
+
|
|
13
|
+
**Phase:** [phase name]
|
|
14
|
+
**Date:** [date]
|
|
15
|
+
**Confidence:** [1-10]
|
|
16
|
+
|
|
17
|
+
### What Went Well
|
|
18
|
+
- [positive point 1]
|
|
19
|
+
- [positive point 2]
|
|
20
|
+
|
|
21
|
+
### What Could Be Improved
|
|
22
|
+
- [improvement 1]
|
|
23
|
+
- [improvement 2]
|
|
24
|
+
|
|
25
|
+
### Assumptions Made
|
|
26
|
+
- [assumption 1]
|
|
27
|
+
- [assumption 2]
|
|
28
|
+
|
|
29
|
+
### Scale Notes
|
|
30
|
+
- How would this perform at 1M users?
|
|
31
|
+
- What bottlenecks exist?
|
|
32
|
+
|
|
33
|
+
### Production Risks
|
|
34
|
+
- What could go wrong in production?
|
|
35
|
+
- What monitoring is needed?
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Confidence Scale
|
|
39
|
+
|
|
40
|
+
| Level | Meaning | Action |
|
|
41
|
+
|-------|---------|--------|
|
|
42
|
+
| 10 | Certain | Proceed confidently |
|
|
43
|
+
| 8-9 | Very confident | Proceed with minor notes |
|
|
44
|
+
| 6-7 | Confident | Note concerns |
|
|
45
|
+
| 4-5 | Uncertain | Research more |
|
|
46
|
+
| 2-3 | Very uncertain | Ask for help |
|
|
47
|
+
| 1 | Clueless | Stop, cannot proceed |
|
|
48
|
+
|
|
49
|
+
## Quality Checklist
|
|
50
|
+
|
|
51
|
+
Before completing reflection:
|
|
52
|
+
- [ ] Confidence level assigned
|
|
53
|
+
- [ ] Pros and cons listed
|
|
54
|
+
- [ ] Assumptions documented
|
|
55
|
+
- [ ] Scale considerations noted
|
|
56
|
+
- [ ] Production risks identified
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"projectType": "web",
|
|
4
|
+
"teamSize": "solo",
|
|
5
|
+
"painPoint": "consistency",
|
|
6
|
+
"experienceLevel": "beginner",
|
|
7
|
+
"activePhases": ["init", "clarify", "code", "review", "learn"],
|
|
8
|
+
"activeRules": 12,
|
|
9
|
+
"subagentsRequired": false,
|
|
10
|
+
"selfReflectionRequired": false,
|
|
11
|
+
"telemetry": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"exportInterval": 300000
|
|
14
|
+
},
|
|
15
|
+
"healthChecks": {
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"interval": 30000,
|
|
18
|
+
"timeout": 5000
|
|
19
|
+
},
|
|
20
|
+
"costTracking": {
|
|
21
|
+
"enabled": true,
|
|
22
|
+
"budgetPerSession": 100000,
|
|
23
|
+
"alertThreshold": 80
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Circuit Breaker Pattern
|
|
2
|
+
// Protects against cascading failures in agent operations
|
|
3
|
+
|
|
4
|
+
export type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
|
|
5
|
+
|
|
6
|
+
export interface CircuitBreakerConfig {
|
|
7
|
+
failureThreshold: number;
|
|
8
|
+
resetTimeoutMs: number;
|
|
9
|
+
halfOpenMaxAttempts: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class CircuitBreaker {
|
|
13
|
+
private state: CircuitState = 'CLOSED';
|
|
14
|
+
private failures = 0;
|
|
15
|
+
private lastFailureTime = 0;
|
|
16
|
+
private halfOpenAttempts = 0;
|
|
17
|
+
private config: CircuitBreakerConfig;
|
|
18
|
+
|
|
19
|
+
constructor(config?: Partial<CircuitBreakerConfig>) {
|
|
20
|
+
this.config = {
|
|
21
|
+
failureThreshold: config?.failureThreshold ?? 3,
|
|
22
|
+
resetTimeoutMs: config?.resetTimeoutMs ?? 60000,
|
|
23
|
+
halfOpenMaxAttempts: config?.halfOpenMaxAttempts ?? 1,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getState(): CircuitState {
|
|
28
|
+
return this.state;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private checkTransition(): void {
|
|
32
|
+
if (this.state === 'OPEN') {
|
|
33
|
+
if (Date.now() - this.lastFailureTime >= this.config.resetTimeoutMs) {
|
|
34
|
+
this.state = 'HALF_OPEN';
|
|
35
|
+
this.halfOpenAttempts = 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async execute<T>(operation: () => Promise<T>): Promise<T> {
|
|
41
|
+
this.checkTransition();
|
|
42
|
+
const currentState = this.getState();
|
|
43
|
+
|
|
44
|
+
if (currentState === 'OPEN') {
|
|
45
|
+
throw new CircuitOpenError(
|
|
46
|
+
`Circuit breaker is OPEN. Retry after ${this.config.resetTimeoutMs}ms`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (currentState === 'HALF_OPEN' && this.halfOpenAttempts >= this.config.halfOpenMaxAttempts) {
|
|
51
|
+
throw new CircuitOpenError(
|
|
52
|
+
`Circuit breaker is HALF_OPEN. Max attempts reached.`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const result = await operation();
|
|
58
|
+
this.onSuccess();
|
|
59
|
+
return result;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
this.onFailure();
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private onSuccess(): void {
|
|
67
|
+
if (this.state === 'HALF_OPEN') {
|
|
68
|
+
this.state = 'CLOSED';
|
|
69
|
+
this.failures = 0;
|
|
70
|
+
this.halfOpenAttempts = 0;
|
|
71
|
+
console.log('[CircuitBreaker] State: HALF_OPEN -> CLOSED');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private onFailure(): void {
|
|
76
|
+
this.failures++;
|
|
77
|
+
this.lastFailureTime = Date.now();
|
|
78
|
+
|
|
79
|
+
if (this.state === 'HALF_OPEN') {
|
|
80
|
+
this.halfOpenAttempts++;
|
|
81
|
+
if (this.halfOpenAttempts >= this.config.halfOpenMaxAttempts) {
|
|
82
|
+
this.state = 'OPEN';
|
|
83
|
+
console.log('[CircuitBreaker] State: HALF_OPEN -> OPEN');
|
|
84
|
+
}
|
|
85
|
+
} else if (this.failures >= this.config.failureThreshold) {
|
|
86
|
+
this.state = 'OPEN';
|
|
87
|
+
console.log(`[CircuitBreaker] State: CLOSED -> OPEN (${this.failures} failures)`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
reset(): void {
|
|
92
|
+
this.state = 'CLOSED';
|
|
93
|
+
this.failures = 0;
|
|
94
|
+
this.halfOpenAttempts = 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
getStats(): { state: CircuitState; failures: number; lastFailure: string | null } {
|
|
98
|
+
return {
|
|
99
|
+
state: this.getState(),
|
|
100
|
+
failures: this.failures,
|
|
101
|
+
lastFailure: this.lastFailureTime
|
|
102
|
+
? new Date(this.lastFailureTime).toISOString()
|
|
103
|
+
: null,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class CircuitOpenError extends Error {
|
|
109
|
+
constructor(message: string) {
|
|
110
|
+
super(message);
|
|
111
|
+
this.name = 'CircuitOpenError';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// CLI Interface
|
|
2
|
+
// vibe init, vibe status, vibe run, vibe doctor, vibe rollback
|
|
3
|
+
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
|
|
7
|
+
export interface CLIConfig {
|
|
8
|
+
projectType: 'web' | 'api' | 'mobile' | 'data' | 'unknown';
|
|
9
|
+
teamSize: 'solo' | 'small' | 'medium' | 'large';
|
|
10
|
+
painPoint: 'consistency' | 'review' | 'onboarding' | 'debt';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface StatusReport {
|
|
14
|
+
currentPhase: string;
|
|
15
|
+
progress: number;
|
|
16
|
+
activeAgent: string | null;
|
|
17
|
+
contextUsage: { used: number; total: number };
|
|
18
|
+
duration: string;
|
|
19
|
+
recentEvents: Array<{ phase: string; type: string; time: string }>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class VibeCLI {
|
|
23
|
+
private rootDir: string;
|
|
24
|
+
private configPath: string;
|
|
25
|
+
|
|
26
|
+
constructor(rootDir: string) {
|
|
27
|
+
this.rootDir = rootDir;
|
|
28
|
+
this.configPath = join(rootDir, '.vibe', 'config.json');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async init(options?: { template?: string; interactive?: boolean }): Promise<void> {
|
|
32
|
+
console.log('🚀 Vibe Coder Kit — Initial Setup\n');
|
|
33
|
+
|
|
34
|
+
// Auto-detect project type
|
|
35
|
+
const detected = this.detectProject();
|
|
36
|
+
console.log(`✓ Proje tespit edildi: ${detected.type} (${detected.stack})`);
|
|
37
|
+
|
|
38
|
+
// Load or create config
|
|
39
|
+
const config: CLIConfig = {
|
|
40
|
+
projectType: detected.type as CLIConfig['projectType'],
|
|
41
|
+
teamSize: 'solo',
|
|
42
|
+
painPoint: 'consistency',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Write config
|
|
46
|
+
writeFileSync(this.configPath, JSON.stringify(config, null, 2));
|
|
47
|
+
console.log('✓ Yapılandırma kaydedildi');
|
|
48
|
+
|
|
49
|
+
// Initialize directories
|
|
50
|
+
this.ensureDirectories();
|
|
51
|
+
console.log('✓ Dizinler hazırlandı');
|
|
52
|
+
|
|
53
|
+
// Initialize state
|
|
54
|
+
this.initializeState();
|
|
55
|
+
console.log('✓ State başlatıldı');
|
|
56
|
+
|
|
57
|
+
// Initialize knowledge base
|
|
58
|
+
this.initializeKnowledge();
|
|
59
|
+
console.log('✓ Knowledge base başlatıldı');
|
|
60
|
+
|
|
61
|
+
console.log('\n🚀 Hazır! "vibe status" ile durumu kontrol edin.\n');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private detectProject(): { type: string; stack: string } {
|
|
65
|
+
const packageJsonPath = join(this.rootDir, 'package.json');
|
|
66
|
+
const cargoTomlPath = join(this.rootDir, 'Cargo.toml');
|
|
67
|
+
const requirementsPath = join(this.rootDir, 'requirements.txt');
|
|
68
|
+
const pubspecPath = join(this.rootDir, 'pubspec.yaml');
|
|
69
|
+
|
|
70
|
+
if (existsSync(packageJsonPath)) {
|
|
71
|
+
try {
|
|
72
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
73
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
74
|
+
|
|
75
|
+
if (deps['next'] || deps['react'] || deps['vue']) {
|
|
76
|
+
return { type: 'web', stack: 'Node.js' };
|
|
77
|
+
}
|
|
78
|
+
if (deps['express'] || deps['fastify'] || deps['nestjs']) {
|
|
79
|
+
return { type: 'api', stack: 'Node.js' };
|
|
80
|
+
}
|
|
81
|
+
return { type: 'unknown', stack: 'Node.js' };
|
|
82
|
+
} catch {
|
|
83
|
+
return { type: 'unknown', stack: 'Node.js' };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (existsSync(cargoTomlPath)) return { type: 'api', stack: 'Rust' };
|
|
88
|
+
if (existsSync(requirementsPath)) return { type: 'data', stack: 'Python' };
|
|
89
|
+
if (existsSync(pubspecPath)) return { type: 'mobile', stack: 'Flutter' };
|
|
90
|
+
|
|
91
|
+
return { type: 'unknown', stack: 'Unknown' };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private ensureDirectories(): void {
|
|
95
|
+
const dirs = [
|
|
96
|
+
'.vibe/state', '.vibe/state/snapshots',
|
|
97
|
+
'.vibe/memory/knowledge', '.vibe/memory/decisions',
|
|
98
|
+
'.vibe/memory/gotchas', '.vibe/memory/conventions',
|
|
99
|
+
'.vibe/workspace/plans', '.vibe/workspace/reports',
|
|
100
|
+
'.vibe/workspace/archive',
|
|
101
|
+
];
|
|
102
|
+
for (const dir of dirs) {
|
|
103
|
+
const fullPath = join(this.rootDir, dir);
|
|
104
|
+
if (!existsSync(fullPath)) {
|
|
105
|
+
mkdirSync(fullPath, { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private initializeState(): void {
|
|
111
|
+
const eventsPath = join(this.rootDir, '.vibe/state/events.jsonl');
|
|
112
|
+
if (!existsSync(eventsPath)) {
|
|
113
|
+
writeFileSync(eventsPath, '');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private initializeKnowledge(): void {
|
|
118
|
+
const indexPath = join(this.rootDir, '.vibe/memory/INDEX.md');
|
|
119
|
+
if (!existsSync(indexPath)) {
|
|
120
|
+
writeFileSync(indexPath, '# Knowledge Index\n\n| # | Date | Title | Tags | File |\n|---|------|-------|------|------|\n');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
status(): StatusReport {
|
|
125
|
+
const eventsPath = join(this.rootDir, '.vibe/state/events.jsonl');
|
|
126
|
+
let events: Array<{ phase: string; type: string; timestamp: string }> = [];
|
|
127
|
+
|
|
128
|
+
if (existsSync(eventsPath)) {
|
|
129
|
+
try {
|
|
130
|
+
const content = readFileSync(eventsPath, 'utf-8');
|
|
131
|
+
events = content.split('\n').filter(l => l.trim()).map(l => {
|
|
132
|
+
try {
|
|
133
|
+
return JSON.parse(l);
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}).filter((e): e is { phase: string; type: string; timestamp: string } => e !== null);
|
|
138
|
+
} catch {
|
|
139
|
+
events = [];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const lastEvent = events[events.length - 1];
|
|
144
|
+
const currentPhase = lastEvent?.phase || 'init';
|
|
145
|
+
|
|
146
|
+
const phaseProgress: Record<string, number> = {
|
|
147
|
+
init: 10, clarify: 20, brainstorm: 30, plan: 40,
|
|
148
|
+
approve: 50, code: 70, review: 85, fix: 85,
|
|
149
|
+
learn: 95, deploy: 98, done: 100,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
currentPhase,
|
|
154
|
+
progress: phaseProgress[currentPhase] || 0,
|
|
155
|
+
activeAgent: lastEvent?.type === 'STARTED' ? 'running' : null,
|
|
156
|
+
contextUsage: { used: 0, total: 8000 },
|
|
157
|
+
duration: this.calculateDuration(events),
|
|
158
|
+
recentEvents: events.slice(-5).map(e => ({
|
|
159
|
+
phase: e.phase,
|
|
160
|
+
type: e.type,
|
|
161
|
+
time: e.timestamp,
|
|
162
|
+
})),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private calculateDuration(events: Array<{ timestamp: string }>): string {
|
|
167
|
+
if (events.length < 2) return '0m';
|
|
168
|
+
const first = new Date(events[0].timestamp);
|
|
169
|
+
const last = new Date(events[events.length - 1].timestamp);
|
|
170
|
+
const diffMs = last.getTime() - first.getTime();
|
|
171
|
+
const minutes = Math.floor(diffMs / 60000);
|
|
172
|
+
if (minutes < 60) return `${minutes}m`;
|
|
173
|
+
const hours = Math.floor(minutes / 60);
|
|
174
|
+
return `${hours}h ${minutes % 60}m`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
doctor(): Array<{ check: string; status: 'ok' | 'warning' | 'error'; message: string }> {
|
|
178
|
+
const results: Array<{ check: string; status: 'ok' | 'warning' | 'error'; message: string }> = [];
|
|
179
|
+
|
|
180
|
+
// Check CLI version
|
|
181
|
+
results.push({ check: 'CLI Version', status: 'ok', message: '1.0.0 (latest)' });
|
|
182
|
+
|
|
183
|
+
// Check Node.js
|
|
184
|
+
const nodeVersion = process.version;
|
|
185
|
+
results.push({
|
|
186
|
+
check: 'Node.js',
|
|
187
|
+
status: parseInt(nodeVersion.slice(1)) >= 18 ? 'ok' : 'error',
|
|
188
|
+
message: `${nodeVersion} (required: >=18)`,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Check config
|
|
192
|
+
results.push({
|
|
193
|
+
check: 'Config',
|
|
194
|
+
status: existsSync(this.configPath) ? 'ok' : 'error',
|
|
195
|
+
message: existsSync(this.configPath) ? 'Loaded' : 'Not found',
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Check state
|
|
199
|
+
const eventsPath = join(this.rootDir, '.vibe/state/events.jsonl');
|
|
200
|
+
results.push({
|
|
201
|
+
check: 'State',
|
|
202
|
+
status: existsSync(eventsPath) ? 'ok' : 'warning',
|
|
203
|
+
message: existsSync(eventsPath) ? 'Clean' : 'No events yet',
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Check knowledge base
|
|
207
|
+
const indexPath = join(this.rootDir, '.vibe/memory/INDEX.md');
|
|
208
|
+
results.push({
|
|
209
|
+
check: 'Knowledge Base',
|
|
210
|
+
status: existsSync(indexPath) ? 'ok' : 'warning',
|
|
211
|
+
message: existsSync(indexPath) ? 'Initialized' : 'Not initialized',
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return results;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
displayStatus(): void {
|
|
218
|
+
const status = this.status();
|
|
219
|
+
const bar = '█'.repeat(Math.floor(status.progress / 5)) + '░'.repeat(20 - Math.floor(status.progress / 5));
|
|
220
|
+
|
|
221
|
+
console.log('┌────────────────────────────────────────────┐');
|
|
222
|
+
console.log(`│ Phase: ${status.currentPhase.padEnd(20)} [${bar}] ${status.progress}% │`);
|
|
223
|
+
console.log(`│ Agent: ${(status.activeAgent || 'idle').padEnd(29)} │`);
|
|
224
|
+
console.log(`│ Context: ${status.contextUsage.used} / ${status.contextUsage.total} tokens${' '.repeat(Math.max(0, 16 - String(status.contextUsage.used).length - String(status.contextUsage.total).length))}│`);
|
|
225
|
+
console.log(`│ Duration: ${status.duration.padEnd(28)} │`);
|
|
226
|
+
console.log('└────────────────────────────────────────────┘');
|
|
227
|
+
|
|
228
|
+
if (status.recentEvents.length > 0) {
|
|
229
|
+
console.log('\nRecent events:');
|
|
230
|
+
for (const event of status.recentEvents) {
|
|
231
|
+
const icon = event.type === 'COMPLETED' ? '✓' : event.type === 'FAILED' ? '✗' : '→';
|
|
232
|
+
console.log(` ${icon} ${event.phase} — ${event.type} (${new Date(event.time).toLocaleTimeString()})`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
displayDoctor(): void {
|
|
238
|
+
const results = this.doctor();
|
|
239
|
+
console.log('\nVibe Coder Kit — Health Check\n');
|
|
240
|
+
|
|
241
|
+
for (const r of results) {
|
|
242
|
+
const icon = r.status === 'ok' ? '✓' : r.status === 'warning' ? '⚠' : '✗';
|
|
243
|
+
console.log(`${icon} ${r.check}: ${r.message}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const errors = results.filter(r => r.status === 'error');
|
|
247
|
+
if (errors.length > 0) {
|
|
248
|
+
console.log(`\n${errors.length} error(s) found. Run 'vibe doctor --fix' to resolve.`);
|
|
249
|
+
} else {
|
|
250
|
+
console.log('\n✓ All checks passed.');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// CLI Entry Point
|
|
256
|
+
if (require.main === module) {
|
|
257
|
+
const args = process.argv.slice(2);
|
|
258
|
+
const command = args[0];
|
|
259
|
+
const rootDir = process.cwd();
|
|
260
|
+
|
|
261
|
+
const cli = new VibeCLI(rootDir);
|
|
262
|
+
|
|
263
|
+
switch (command) {
|
|
264
|
+
case 'init':
|
|
265
|
+
cli.init().catch(console.error);
|
|
266
|
+
break;
|
|
267
|
+
case 'status':
|
|
268
|
+
cli.displayStatus();
|
|
269
|
+
break;
|
|
270
|
+
case 'doctor':
|
|
271
|
+
cli.displayDoctor();
|
|
272
|
+
break;
|
|
273
|
+
default:
|
|
274
|
+
console.log('Vibe Coder Kit v6.0.0\n');
|
|
275
|
+
console.log('Commands:');
|
|
276
|
+
console.log(' vibe init — Initialize project');
|
|
277
|
+
console.log(' vibe status — Show current status');
|
|
278
|
+
console.log(' vibe doctor — Health check');
|
|
279
|
+
}
|
|
280
|
+
}
|