@nclamvn/vibecode-cli 1.1.0 → 1.3.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.
@@ -0,0 +1,164 @@
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
+ '--print', // Non-interactive mode (no TTY required)
19
+ ],
20
+ timeout: 30 * 60 * 1000, // 30 minutes max
21
+ };
22
+
23
+ /**
24
+ * Check if Claude Code CLI is available
25
+ */
26
+ export async function isClaudeCodeAvailable() {
27
+ return new Promise((resolve) => {
28
+ const proc = spawn('which', ['claude'], { shell: true });
29
+ proc.on('close', (code) => {
30
+ resolve(code === 0);
31
+ });
32
+ proc.on('error', () => {
33
+ resolve(false);
34
+ });
35
+ });
36
+ }
37
+
38
+ /**
39
+ * Spawn Claude Code with optimal settings
40
+ *
41
+ * @param {string} prompt - The coder pack / prompt to send
42
+ * @param {object} options - Configuration options
43
+ * @param {string} options.cwd - Working directory
44
+ * @param {string} options.logPath - Path to write build logs
45
+ * @param {function} options.onOutput - Callback for output
46
+ * @returns {Promise<{success: boolean, code: number}>}
47
+ */
48
+ export async function spawnClaudeCode(prompt, options = {}) {
49
+ const { cwd, logPath, onOutput } = options;
50
+ const fs = await import('fs-extra');
51
+ const os = await import('os');
52
+
53
+ // Check if Claude Code is available
54
+ const available = await isClaudeCodeAvailable();
55
+ if (!available) {
56
+ throw new Error('Claude Code CLI not found. Install with: npm install -g @anthropic/claude-code');
57
+ }
58
+
59
+ // Write prompt to temp file to avoid shell escaping issues
60
+ const tempDir = os.default.tmpdir();
61
+ const promptFile = path.join(tempDir, `vibecode-prompt-${Date.now()}.md`);
62
+ await fs.default.writeFile(promptFile, prompt, 'utf-8');
63
+
64
+ return new Promise((resolve, reject) => {
65
+ // Build command with --print mode and -p for prompt file
66
+ const args = [
67
+ ...CLAUDE_CODE_CONFIG.flags,
68
+ '-p', promptFile
69
+ ];
70
+ const command = `claude ${args.map(a => `"${a}"`).join(' ')}`;
71
+
72
+ // Log the command being run
73
+ if (logPath) {
74
+ appendToFile(logPath, `\n[${new Date().toISOString()}] Running: claude --print -p ${promptFile}\n`);
75
+ }
76
+
77
+ const proc = spawn('claude', args, {
78
+ cwd: cwd || process.cwd(),
79
+ stdio: 'inherit', // Stream directly to terminal
80
+ shell: false, // No shell needed, safer
81
+ });
82
+
83
+ let timeoutId = setTimeout(() => {
84
+ proc.kill();
85
+ // Cleanup temp file
86
+ fs.default.remove(promptFile).catch(() => {});
87
+ reject(new Error('Claude Code process timed out'));
88
+ }, CLAUDE_CODE_CONFIG.timeout);
89
+
90
+ proc.on('close', async (code) => {
91
+ clearTimeout(timeoutId);
92
+
93
+ // Cleanup temp file
94
+ await fs.default.remove(promptFile).catch(() => {});
95
+
96
+ const result = {
97
+ success: code === 0,
98
+ code: code || 0,
99
+ timestamp: new Date().toISOString()
100
+ };
101
+
102
+ if (logPath) {
103
+ const status = result.success ? 'SUCCESS' : 'FAILED';
104
+ appendToFile(logPath, `\n[${result.timestamp}] Claude Code ${status} (exit code: ${code})\n`);
105
+ }
106
+
107
+ resolve(result);
108
+ });
109
+
110
+ proc.on('error', async (error) => {
111
+ clearTimeout(timeoutId);
112
+ // Cleanup temp file
113
+ await fs.default.remove(promptFile).catch(() => {});
114
+
115
+ if (logPath) {
116
+ appendToFile(logPath, `\n[${new Date().toISOString()}] ERROR: ${error.message}\n`);
117
+ }
118
+ reject(error);
119
+ });
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Build prompt with optional CLAUDE.md injection
125
+ *
126
+ * @param {string} coderPackContent - Content of coder_pack.md
127
+ * @param {string} projectRoot - Project root directory
128
+ * @returns {Promise<string>} - Final prompt
129
+ */
130
+ export async function buildPromptWithContext(coderPackContent, projectRoot) {
131
+ let fullPrompt = coderPackContent;
132
+
133
+ // Check for CLAUDE.md in project root
134
+ const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
135
+ if (await pathExists(claudeMdPath)) {
136
+ const fs = await import('fs-extra');
137
+ const claudeMd = await fs.default.readFile(claudeMdPath, 'utf-8');
138
+
139
+ // Inject CLAUDE.md rules before coder pack
140
+ fullPrompt = `# PROJECT RULES (from CLAUDE.md)
141
+
142
+ ${claudeMd}
143
+
144
+ ---
145
+
146
+ # BUILD INSTRUCTIONS
147
+
148
+ ${coderPackContent}`;
149
+ }
150
+
151
+ return fullPrompt;
152
+ }
153
+
154
+ /**
155
+ * Get provider info for status display
156
+ */
157
+ export function getProviderInfo() {
158
+ return {
159
+ name: 'Claude Code',
160
+ command: CLAUDE_CODE_CONFIG.command,
161
+ mode: '--dangerously-skip-permissions --print',
162
+ description: 'AI coding in non-interactive mode (contract-approved)'
163
+ };
164
+ }
@@ -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
+ }