@nclamvn/vibecode-cli 1.3.0 → 1.5.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/README.md +310 -49
- package/bin/vibecode.js +65 -0
- package/package.json +1 -1
- package/src/agent/decomposition.js +476 -0
- package/src/agent/index.js +325 -0
- package/src/agent/memory.js +542 -0
- package/src/agent/orchestrator.js +644 -0
- package/src/agent/self-healing.js +516 -0
- package/src/commands/agent.js +255 -0
- package/src/commands/assist.js +413 -0
- package/src/commands/debug.js +457 -0
- package/src/commands/go.js +380 -0
- package/src/core/test-runner.js +38 -5
- package/src/debug/analyzer.js +329 -0
- package/src/debug/evidence.js +228 -0
- package/src/debug/fixer.js +348 -0
- package/src/debug/index.js +349 -0
- package/src/debug/verifier.js +346 -0
- package/src/index.js +31 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Agent Command
|
|
3
|
+
// Autonomous multi-module builder
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs-extra';
|
|
9
|
+
|
|
10
|
+
import { createAgent } from '../agent/index.js';
|
|
11
|
+
import { printError, printSuccess } from '../ui/output.js';
|
|
12
|
+
import { isClaudeCodeAvailable } from '../providers/index.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Agent command entry point
|
|
16
|
+
* vibecode agent "description" [options]
|
|
17
|
+
*/
|
|
18
|
+
export async function agentCommand(description, options = {}) {
|
|
19
|
+
// Check for Claude Code
|
|
20
|
+
const claudeAvailable = await isClaudeCodeAvailable();
|
|
21
|
+
if (!claudeAvailable) {
|
|
22
|
+
printError('Claude Code CLI not found.');
|
|
23
|
+
console.log(chalk.gray('Install with: npm install -g @anthropic-ai/claude-code'));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Validate description
|
|
28
|
+
if (!description || description.trim().length < 5) {
|
|
29
|
+
printError('Description too short. Please provide more details.');
|
|
30
|
+
console.log(chalk.gray('Example: vibecode agent "SaaS dashboard with auth, billing, and analytics"'));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Handle sub-commands
|
|
35
|
+
if (options.analyze) {
|
|
36
|
+
return analyzeCommand(description, options);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (options.status) {
|
|
40
|
+
return statusCommand(options);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (options.report) {
|
|
44
|
+
return reportCommand(options);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (options.clear) {
|
|
48
|
+
return clearCommand(options);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Main build flow
|
|
52
|
+
return buildCommand(description, options);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Main agent build command
|
|
57
|
+
*/
|
|
58
|
+
async function buildCommand(description, options) {
|
|
59
|
+
// Determine project path
|
|
60
|
+
let projectPath = process.cwd();
|
|
61
|
+
|
|
62
|
+
// If --new flag, create new directory
|
|
63
|
+
if (options.new) {
|
|
64
|
+
const projectName = generateProjectName(description);
|
|
65
|
+
projectPath = path.join(process.cwd(), projectName);
|
|
66
|
+
|
|
67
|
+
if (await fs.pathExists(projectPath)) {
|
|
68
|
+
printError(`Directory already exists: ${projectName}`);
|
|
69
|
+
console.log(chalk.gray('Choose a different name or delete the existing directory.'));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
await fs.ensureDir(projectPath);
|
|
74
|
+
await fs.ensureDir(path.join(projectPath, '.vibecode'));
|
|
75
|
+
process.chdir(projectPath);
|
|
76
|
+
|
|
77
|
+
console.log(chalk.gray(`Created project: ${projectName}`));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Create and initialize agent
|
|
81
|
+
const agent = createAgent({
|
|
82
|
+
projectPath,
|
|
83
|
+
verbose: options.verbose || false
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
await agent.initialize();
|
|
88
|
+
|
|
89
|
+
// Build with options
|
|
90
|
+
const buildOptions = {
|
|
91
|
+
maxModuleRetries: options.maxRetries || 3,
|
|
92
|
+
testAfterEachModule: !options.skipTests,
|
|
93
|
+
continueOnFailure: options.continue || false
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const result = await agent.build(description, buildOptions);
|
|
97
|
+
|
|
98
|
+
// Exit with appropriate code
|
|
99
|
+
if (!result.success) {
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
} catch (error) {
|
|
104
|
+
printError(`Agent failed: ${error.message}`);
|
|
105
|
+
console.log(chalk.gray('Check .vibecode/agent/orchestrator.log for details.'));
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Analyze project without building
|
|
112
|
+
*/
|
|
113
|
+
async function analyzeCommand(description, options) {
|
|
114
|
+
const agent = createAgent({
|
|
115
|
+
projectPath: process.cwd(),
|
|
116
|
+
verbose: options.verbose || false
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
await agent.initialize();
|
|
121
|
+
const analysis = await agent.analyze(description);
|
|
122
|
+
|
|
123
|
+
// Output JSON if requested
|
|
124
|
+
if (options.json) {
|
|
125
|
+
console.log(JSON.stringify(analysis, null, 2));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return analysis;
|
|
129
|
+
|
|
130
|
+
} catch (error) {
|
|
131
|
+
printError(`Analysis failed: ${error.message}`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Show agent status
|
|
138
|
+
*/
|
|
139
|
+
async function statusCommand(options) {
|
|
140
|
+
const agent = createAgent({
|
|
141
|
+
projectPath: process.cwd()
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
await agent.initialize();
|
|
146
|
+
const status = agent.getStatus();
|
|
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
|
+
}
|
|
166
|
+
|
|
167
|
+
if (status.healingStats) {
|
|
168
|
+
console.log();
|
|
169
|
+
console.log(chalk.cyan('Self-Healing Stats'));
|
|
170
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
171
|
+
console.log(` Attempts: ${status.healingStats.total}`);
|
|
172
|
+
console.log(` Success: ${status.healingStats.successful}`);
|
|
173
|
+
console.log(` Failed: ${status.healingStats.failed}`);
|
|
174
|
+
console.log(` Rate: ${status.healingStats.successRate}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log();
|
|
178
|
+
|
|
179
|
+
// Output JSON if requested
|
|
180
|
+
if (options.json) {
|
|
181
|
+
console.log(JSON.stringify(status, null, 2));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
} catch (error) {
|
|
185
|
+
printError(`Status failed: ${error.message}`);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Export memory report
|
|
192
|
+
*/
|
|
193
|
+
async function reportCommand(options) {
|
|
194
|
+
const agent = createAgent({
|
|
195
|
+
projectPath: process.cwd()
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
await agent.initialize();
|
|
200
|
+
const report = await agent.exportReport();
|
|
201
|
+
|
|
202
|
+
if (options.stdout) {
|
|
203
|
+
console.log(report);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
} catch (error) {
|
|
207
|
+
printError(`Report failed: ${error.message}`);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Clear agent memory
|
|
214
|
+
*/
|
|
215
|
+
async function clearCommand(options) {
|
|
216
|
+
if (!options.force) {
|
|
217
|
+
console.log(chalk.yellow('This will clear all agent memory.'));
|
|
218
|
+
console.log(chalk.gray('Use --force to confirm.'));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const agent = createAgent({
|
|
223
|
+
projectPath: process.cwd()
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
await agent.initialize();
|
|
228
|
+
await agent.clearMemory();
|
|
229
|
+
printSuccess('Agent memory cleared');
|
|
230
|
+
|
|
231
|
+
} catch (error) {
|
|
232
|
+
printError(`Clear failed: ${error.message}`);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Generate project name from description
|
|
239
|
+
*/
|
|
240
|
+
function generateProjectName(description) {
|
|
241
|
+
const stopWords = ['a', 'an', 'the', 'for', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'with', 'my', 'our'];
|
|
242
|
+
|
|
243
|
+
const words = description
|
|
244
|
+
.toLowerCase()
|
|
245
|
+
.replace(/[^a-z0-9\s]/g, '')
|
|
246
|
+
.split(/\s+/)
|
|
247
|
+
.filter(w => w.length > 2 && !stopWords.includes(w))
|
|
248
|
+
.slice(0, 3);
|
|
249
|
+
|
|
250
|
+
if (words.length === 0) {
|
|
251
|
+
return `vibecode-agent-${Date.now().toString(36)}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return words.join('-') + '-agent';
|
|
255
|
+
}
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// VIBECODE CLI - Assist Command
|
|
3
|
+
// AI Expert Mode - Direct Claude Code Access with Full Project Context
|
|
4
|
+
// "User NEVER bế tắc" - The Ultimate Escape Hatch
|
|
5
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
import readline from 'readline';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import fs from 'fs-extra';
|
|
12
|
+
import os from 'os';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Assist Command - AI Expert Mode
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* vibecode assist - Interactive mode
|
|
19
|
+
* vibecode assist "fix the error" - One-shot with prompt
|
|
20
|
+
* vibecode expert "help me debug" - Alias
|
|
21
|
+
*/
|
|
22
|
+
export async function assistCommand(initialPrompt = [], options = {}) {
|
|
23
|
+
const projectPath = process.cwd();
|
|
24
|
+
|
|
25
|
+
// Render welcome header
|
|
26
|
+
console.log(renderHeader());
|
|
27
|
+
|
|
28
|
+
// Gather all context
|
|
29
|
+
const context = await gatherContext(projectPath);
|
|
30
|
+
console.log(renderContextSummary(context));
|
|
31
|
+
|
|
32
|
+
// If initial prompt provided, run once
|
|
33
|
+
if (initialPrompt && initialPrompt.length > 0) {
|
|
34
|
+
const prompt = initialPrompt.join(' ');
|
|
35
|
+
await runClaudeCode(prompt, context, projectPath, options);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Interactive mode
|
|
40
|
+
await interactiveAssist(context, projectPath, options);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Gather all available project context
|
|
45
|
+
*/
|
|
46
|
+
async function gatherContext(projectPath) {
|
|
47
|
+
const context = {
|
|
48
|
+
project: path.basename(projectPath),
|
|
49
|
+
cwd: projectPath,
|
|
50
|
+
state: null,
|
|
51
|
+
memory: null,
|
|
52
|
+
debugHistory: null,
|
|
53
|
+
claudeMd: null,
|
|
54
|
+
packageJson: null,
|
|
55
|
+
files: [],
|
|
56
|
+
gitBranch: null
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Load vibecode state
|
|
60
|
+
try {
|
|
61
|
+
const statePath = path.join(projectPath, '.vibecode', 'state.json');
|
|
62
|
+
if (await fs.pathExists(statePath)) {
|
|
63
|
+
context.state = await fs.readJson(statePath);
|
|
64
|
+
}
|
|
65
|
+
} catch {}
|
|
66
|
+
|
|
67
|
+
// Load agent memory
|
|
68
|
+
try {
|
|
69
|
+
const memoryPath = path.join(projectPath, '.vibecode', 'agent', 'memory.json');
|
|
70
|
+
if (await fs.pathExists(memoryPath)) {
|
|
71
|
+
context.memory = await fs.readJson(memoryPath);
|
|
72
|
+
}
|
|
73
|
+
} catch {}
|
|
74
|
+
|
|
75
|
+
// Load debug history
|
|
76
|
+
try {
|
|
77
|
+
const fixesPath = path.join(projectPath, '.vibecode', 'debug', 'fixes.md');
|
|
78
|
+
if (await fs.pathExists(fixesPath)) {
|
|
79
|
+
context.debugHistory = await fs.readFile(fixesPath, 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
} catch {}
|
|
82
|
+
|
|
83
|
+
// Load CLAUDE.md
|
|
84
|
+
try {
|
|
85
|
+
const claudeMdPath = path.join(projectPath, 'CLAUDE.md');
|
|
86
|
+
if (await fs.pathExists(claudeMdPath)) {
|
|
87
|
+
context.claudeMd = await fs.readFile(claudeMdPath, 'utf-8');
|
|
88
|
+
}
|
|
89
|
+
} catch {}
|
|
90
|
+
|
|
91
|
+
// Load package.json
|
|
92
|
+
try {
|
|
93
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
94
|
+
if (await fs.pathExists(pkgPath)) {
|
|
95
|
+
context.packageJson = await fs.readJson(pkgPath);
|
|
96
|
+
}
|
|
97
|
+
} catch {}
|
|
98
|
+
|
|
99
|
+
// List project files
|
|
100
|
+
try {
|
|
101
|
+
const files = await fs.readdir(projectPath);
|
|
102
|
+
context.files = files.filter(f =>
|
|
103
|
+
!f.startsWith('.') &&
|
|
104
|
+
f !== 'node_modules' &&
|
|
105
|
+
f !== 'dist' &&
|
|
106
|
+
f !== 'build'
|
|
107
|
+
).slice(0, 25);
|
|
108
|
+
} catch {}
|
|
109
|
+
|
|
110
|
+
// Get git branch
|
|
111
|
+
try {
|
|
112
|
+
const { execSync } = await import('child_process');
|
|
113
|
+
context.gitBranch = execSync('git branch --show-current', {
|
|
114
|
+
cwd: projectPath,
|
|
115
|
+
encoding: 'utf-8'
|
|
116
|
+
}).trim();
|
|
117
|
+
} catch {}
|
|
118
|
+
|
|
119
|
+
return context;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Build context prompt for Claude Code
|
|
124
|
+
*/
|
|
125
|
+
function buildContextPrompt(context) {
|
|
126
|
+
const parts = [];
|
|
127
|
+
|
|
128
|
+
parts.push(`# VIBECODE PROJECT CONTEXT`);
|
|
129
|
+
parts.push('');
|
|
130
|
+
parts.push(`## Project: ${context.project}`);
|
|
131
|
+
parts.push(`## Path: ${context.cwd}`);
|
|
132
|
+
if (context.gitBranch) {
|
|
133
|
+
parts.push(`## Branch: ${context.gitBranch}`);
|
|
134
|
+
}
|
|
135
|
+
parts.push('');
|
|
136
|
+
|
|
137
|
+
// CLAUDE.md rules
|
|
138
|
+
if (context.claudeMd) {
|
|
139
|
+
parts.push('## Project Rules (CLAUDE.md)');
|
|
140
|
+
parts.push(context.claudeMd.substring(0, 2000));
|
|
141
|
+
parts.push('');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Vibecode state
|
|
145
|
+
if (context.state) {
|
|
146
|
+
parts.push('## Vibecode State');
|
|
147
|
+
parts.push(`- Current State: ${context.state.current_state || 'unknown'}`);
|
|
148
|
+
parts.push(`- Session: ${context.state.session_id || 'none'}`);
|
|
149
|
+
if (context.state.build_started) {
|
|
150
|
+
parts.push(`- Build Started: ${context.state.build_started}`);
|
|
151
|
+
}
|
|
152
|
+
parts.push('');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Agent memory
|
|
156
|
+
if (context.memory) {
|
|
157
|
+
parts.push('## Agent Memory');
|
|
158
|
+
if (context.memory.decisions?.length) {
|
|
159
|
+
parts.push(`### Decisions (${context.memory.decisions.length})`);
|
|
160
|
+
context.memory.decisions.slice(-5).forEach(d => {
|
|
161
|
+
parts.push(`- ${d.decision}: ${d.rationale?.substring(0, 100) || ''}`);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (context.memory.learnings?.length) {
|
|
165
|
+
parts.push(`### Learnings (${context.memory.learnings.length})`);
|
|
166
|
+
context.memory.learnings.slice(-5).forEach(l => {
|
|
167
|
+
parts.push(`- ${l.description?.substring(0, 100) || l}`);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (context.memory.moduleStates) {
|
|
171
|
+
const completed = Object.entries(context.memory.moduleStates)
|
|
172
|
+
.filter(([_, v]) => v.status === 'completed')
|
|
173
|
+
.map(([k, _]) => k);
|
|
174
|
+
const failed = Object.entries(context.memory.moduleStates)
|
|
175
|
+
.filter(([_, v]) => v.status === 'failed')
|
|
176
|
+
.map(([k, _]) => k);
|
|
177
|
+
if (completed.length) parts.push(`### Completed Modules: ${completed.join(', ')}`);
|
|
178
|
+
if (failed.length) parts.push(`### Failed Modules: ${failed.join(', ')}`);
|
|
179
|
+
}
|
|
180
|
+
parts.push('');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Debug history (condensed)
|
|
184
|
+
if (context.debugHistory) {
|
|
185
|
+
parts.push('## Recent Debug Attempts');
|
|
186
|
+
// Take last 1000 chars of debug history
|
|
187
|
+
const condensed = context.debugHistory.slice(-1000);
|
|
188
|
+
parts.push(condensed);
|
|
189
|
+
parts.push('');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Package info
|
|
193
|
+
if (context.packageJson) {
|
|
194
|
+
parts.push('## Package');
|
|
195
|
+
parts.push(`- Name: ${context.packageJson.name || 'unknown'}`);
|
|
196
|
+
if (context.packageJson.scripts) {
|
|
197
|
+
parts.push(`- Scripts: ${Object.keys(context.packageJson.scripts).join(', ')}`);
|
|
198
|
+
}
|
|
199
|
+
parts.push('');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Project files
|
|
203
|
+
if (context.files.length > 0) {
|
|
204
|
+
parts.push('## Project Files');
|
|
205
|
+
parts.push(context.files.join(', '));
|
|
206
|
+
parts.push('');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
parts.push('---');
|
|
210
|
+
parts.push('');
|
|
211
|
+
parts.push('You have full access to this project. Help the user with whatever they need.');
|
|
212
|
+
parts.push('Use --dangerously-skip-permissions mode - you can modify files directly.');
|
|
213
|
+
parts.push('');
|
|
214
|
+
|
|
215
|
+
return parts.join('\n');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Run Claude Code with prompt
|
|
220
|
+
*/
|
|
221
|
+
async function runClaudeCode(prompt, context, projectPath, options) {
|
|
222
|
+
// Build full prompt with context
|
|
223
|
+
let fullPrompt;
|
|
224
|
+
if (options.noContext) {
|
|
225
|
+
fullPrompt = prompt;
|
|
226
|
+
} else {
|
|
227
|
+
const contextPrompt = buildContextPrompt(context);
|
|
228
|
+
fullPrompt = `${contextPrompt}\n## USER REQUEST:\n${prompt}`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Write to temp file
|
|
232
|
+
const tempFile = path.join(os.tmpdir(), `vibecode-summon-${Date.now()}.md`);
|
|
233
|
+
await fs.writeFile(tempFile, fullPrompt);
|
|
234
|
+
|
|
235
|
+
// Also save to project for reference
|
|
236
|
+
const summonDir = path.join(projectPath, '.vibecode', 'summon');
|
|
237
|
+
await fs.ensureDir(summonDir);
|
|
238
|
+
await fs.writeFile(path.join(summonDir, 'last-prompt.md'), fullPrompt);
|
|
239
|
+
|
|
240
|
+
return new Promise((resolve, reject) => {
|
|
241
|
+
console.log(chalk.blue('\n🤝 AI Assist responding...\n'));
|
|
242
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
243
|
+
console.log();
|
|
244
|
+
|
|
245
|
+
const claude = spawn('claude', [
|
|
246
|
+
'--dangerously-skip-permissions',
|
|
247
|
+
'--print',
|
|
248
|
+
'-p', tempFile
|
|
249
|
+
], {
|
|
250
|
+
cwd: projectPath,
|
|
251
|
+
stdio: ['inherit', 'inherit', 'inherit']
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
claude.on('close', async (code) => {
|
|
255
|
+
console.log();
|
|
256
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
257
|
+
console.log(chalk.green('\n✓ Claude Code finished\n'));
|
|
258
|
+
|
|
259
|
+
// Cleanup temp file
|
|
260
|
+
await fs.remove(tempFile).catch(() => {});
|
|
261
|
+
|
|
262
|
+
resolve(code);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
claude.on('error', async (err) => {
|
|
266
|
+
console.log(chalk.red(`\nError: ${err.message}`));
|
|
267
|
+
await fs.remove(tempFile).catch(() => {});
|
|
268
|
+
reject(err);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Interactive assist REPL
|
|
275
|
+
*/
|
|
276
|
+
async function interactiveAssist(context, projectPath, options) {
|
|
277
|
+
const rl = readline.createInterface({
|
|
278
|
+
input: process.stdin,
|
|
279
|
+
output: process.stdout,
|
|
280
|
+
prompt: chalk.blue('assist> ')
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
console.log(chalk.cyan(`
|
|
284
|
+
Commands:
|
|
285
|
+
• Type any request → Claude Code responds
|
|
286
|
+
• /context - Show injected context
|
|
287
|
+
• /refresh - Reload project context
|
|
288
|
+
• /back - Return to vibecode
|
|
289
|
+
• /quit - Exit
|
|
290
|
+
`));
|
|
291
|
+
|
|
292
|
+
rl.prompt();
|
|
293
|
+
|
|
294
|
+
rl.on('line', async (line) => {
|
|
295
|
+
const input = line.trim();
|
|
296
|
+
|
|
297
|
+
if (!input) {
|
|
298
|
+
rl.prompt();
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Handle commands
|
|
303
|
+
if (input === '/back' || input === '/quit' || input === '/exit' || input === '/q') {
|
|
304
|
+
console.log(chalk.blue('\n👋 AI Assist session ended.\n'));
|
|
305
|
+
rl.close();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (input === '/context') {
|
|
310
|
+
console.log(chalk.gray('\n' + buildContextPrompt(context) + '\n'));
|
|
311
|
+
rl.prompt();
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (input === '/refresh') {
|
|
316
|
+
context = await gatherContext(projectPath);
|
|
317
|
+
console.log(chalk.green('\n✓ Context refreshed\n'));
|
|
318
|
+
console.log(renderContextSummary(context));
|
|
319
|
+
rl.prompt();
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (input === '/help') {
|
|
324
|
+
console.log(chalk.cyan(`
|
|
325
|
+
🤝 VIBECODE ASSIST - Help
|
|
326
|
+
|
|
327
|
+
AI Expert Mode with full project context.
|
|
328
|
+
Everything you type will be sent to Claude Code.
|
|
329
|
+
|
|
330
|
+
Commands:
|
|
331
|
+
/context Show the context being injected
|
|
332
|
+
/refresh Reload project context (state, memory, etc.)
|
|
333
|
+
/back Return to normal vibecode
|
|
334
|
+
/quit Exit assist mode
|
|
335
|
+
/help Show this help
|
|
336
|
+
|
|
337
|
+
Tips:
|
|
338
|
+
• Be specific: "Fix the type error in prisma/seed.ts line 54"
|
|
339
|
+
• Reference files: "Look at src/app/api/auth and fix the session issue"
|
|
340
|
+
• Ask questions: "Why is this component not rendering?"
|
|
341
|
+
`));
|
|
342
|
+
rl.prompt();
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Run Claude Code with the input
|
|
347
|
+
try {
|
|
348
|
+
await runClaudeCode(input, context, projectPath, options);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.log(chalk.red(`Error: ${error.message}`));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
rl.prompt();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
rl.on('close', () => {
|
|
357
|
+
process.exit(0);
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Render welcome header
|
|
363
|
+
*/
|
|
364
|
+
function renderHeader() {
|
|
365
|
+
return chalk.blue(`
|
|
366
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
367
|
+
│ │
|
|
368
|
+
│ 🤝 VIBECODE ASSIST │
|
|
369
|
+
│ AI Expert Mode - Direct Claude Code Access │
|
|
370
|
+
│ │
|
|
371
|
+
│ Full project context injected automatically. │
|
|
372
|
+
│ Claude Code has permission to modify files. │
|
|
373
|
+
│ │
|
|
374
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
375
|
+
`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Render context summary
|
|
380
|
+
*/
|
|
381
|
+
function renderContextSummary(context) {
|
|
382
|
+
const items = [];
|
|
383
|
+
items.push(` Project: ${context.project}`);
|
|
384
|
+
|
|
385
|
+
if (context.state) {
|
|
386
|
+
items.push(` State: ${context.state.current_state || 'unknown'}`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (context.memory) {
|
|
390
|
+
const decisions = context.memory.decisions?.length || 0;
|
|
391
|
+
const learnings = context.memory.learnings?.length || 0;
|
|
392
|
+
items.push(` Memory: ${decisions} decisions, ${learnings} learnings`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (context.debugHistory) {
|
|
396
|
+
items.push(` Debug history: available`);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (context.claudeMd) {
|
|
400
|
+
items.push(` CLAUDE.md: loaded`);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
items.push(` Files: ${context.files.length} visible`);
|
|
404
|
+
|
|
405
|
+
if (context.gitBranch) {
|
|
406
|
+
items.push(` Branch: ${context.gitBranch}`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return chalk.gray(`
|
|
410
|
+
Context loaded:
|
|
411
|
+
${items.join('\n')}
|
|
412
|
+
`);
|
|
413
|
+
}
|