@nclamvn/vibecode-cli 1.5.0 → 1.7.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/.vibecode/learning/fixes.json +1 -0
- package/.vibecode/learning/preferences.json +1 -0
- package/bin/vibecode.js +86 -3
- package/docs-site/README.md +41 -0
- package/docs-site/blog/2019-05-28-first-blog-post.md +12 -0
- package/docs-site/blog/2019-05-29-long-blog-post.md +44 -0
- package/docs-site/blog/2021-08-01-mdx-blog-post.mdx +24 -0
- package/docs-site/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- package/docs-site/blog/2021-08-26-welcome/index.md +29 -0
- package/docs-site/blog/authors.yml +25 -0
- package/docs-site/blog/tags.yml +19 -0
- package/docs-site/docs/commands/agent.md +162 -0
- package/docs-site/docs/commands/assist.md +71 -0
- package/docs-site/docs/commands/build.md +53 -0
- package/docs-site/docs/commands/config.md +30 -0
- package/docs-site/docs/commands/debug.md +173 -0
- package/docs-site/docs/commands/doctor.md +34 -0
- package/docs-site/docs/commands/go.md +128 -0
- package/docs-site/docs/commands/index.md +79 -0
- package/docs-site/docs/commands/init.md +42 -0
- package/docs-site/docs/commands/learn.md +82 -0
- package/docs-site/docs/commands/lock.md +33 -0
- package/docs-site/docs/commands/plan.md +29 -0
- package/docs-site/docs/commands/review.md +31 -0
- package/docs-site/docs/commands/snapshot.md +34 -0
- package/docs-site/docs/commands/start.md +32 -0
- package/docs-site/docs/commands/status.md +37 -0
- package/docs-site/docs/commands/undo.md +83 -0
- package/docs-site/docs/configuration.md +72 -0
- package/docs-site/docs/faq.md +83 -0
- package/docs-site/docs/getting-started.md +119 -0
- package/docs-site/docs/guides/agent-mode.md +94 -0
- package/docs-site/docs/guides/debug-mode.md +83 -0
- package/docs-site/docs/guides/magic-mode.md +107 -0
- package/docs-site/docs/installation.md +98 -0
- package/docs-site/docs/intro.md +67 -0
- package/docs-site/docusaurus.config.ts +141 -0
- package/docs-site/package-lock.json +18039 -0
- package/docs-site/package.json +48 -0
- package/docs-site/sidebars.ts +70 -0
- package/docs-site/src/components/HomepageFeatures/index.tsx +72 -0
- package/docs-site/src/components/HomepageFeatures/styles.module.css +16 -0
- package/docs-site/src/css/custom.css +30 -0
- package/docs-site/src/pages/index.module.css +23 -0
- package/docs-site/src/pages/index.tsx +44 -0
- package/docs-site/src/pages/markdown-page.md +7 -0
- package/docs-site/src/theme/Footer/index.tsx +127 -0
- package/docs-site/src/theme/Footer/styles.module.css +285 -0
- package/docs-site/static/.nojekyll +0 -0
- package/docs-site/static/img/docusaurus-social-card.jpg +0 -0
- package/docs-site/static/img/docusaurus.png +0 -0
- package/docs-site/static/img/favicon.ico +0 -0
- package/docs-site/static/img/logo.svg +1 -0
- package/docs-site/static/img/undraw_docusaurus_mountain.svg +171 -0
- package/docs-site/static/img/undraw_docusaurus_react.svg +170 -0
- package/docs-site/static/img/undraw_docusaurus_tree.svg +40 -0
- package/docs-site/tsconfig.json +8 -0
- package/package.json +5 -2
- package/src/agent/orchestrator.js +104 -35
- package/src/commands/build.js +13 -3
- package/src/commands/debug.js +109 -1
- package/src/commands/git.js +923 -0
- package/src/commands/go.js +9 -2
- package/src/commands/learn.js +294 -0
- package/src/commands/shell.js +486 -0
- package/src/commands/undo.js +281 -0
- package/src/commands/watch.js +556 -0
- package/src/commands/wizard.js +322 -0
- package/src/core/backup.js +325 -0
- package/src/core/learning.js +295 -0
- package/src/debug/image-analyzer.js +304 -0
- package/src/debug/index.js +30 -1
- package/src/index.js +50 -0
- package/src/ui/__tests__/error-translator.test.js +390 -0
- package/src/ui/dashboard.js +364 -0
- package/src/ui/error-translator.js +775 -0
- package/src/utils/image.js +222 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image utilities for Vibecode CLI
|
|
3
|
+
* Handle clipboard images, file reading, and base64 conversion
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { exec } from 'child_process';
|
|
9
|
+
import { promisify } from 'util';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
|
|
12
|
+
const execAsync = promisify(exec);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Save clipboard image to temp file
|
|
16
|
+
* Supports macOS, Linux, and Windows
|
|
17
|
+
*/
|
|
18
|
+
export async function saveClipboardImage() {
|
|
19
|
+
const platform = process.platform;
|
|
20
|
+
const tempDir = os.tmpdir();
|
|
21
|
+
const tempFile = path.join(tempDir, `vibecode-screenshot-${Date.now()}.png`);
|
|
22
|
+
|
|
23
|
+
if (platform === 'darwin') {
|
|
24
|
+
// macOS: Use pngpaste or osascript
|
|
25
|
+
try {
|
|
26
|
+
// Try pngpaste first (if installed via brew install pngpaste)
|
|
27
|
+
await execAsync(`pngpaste "${tempFile}"`);
|
|
28
|
+
|
|
29
|
+
// Verify file was created
|
|
30
|
+
const stats = await fs.stat(tempFile);
|
|
31
|
+
if (stats.size > 0) {
|
|
32
|
+
return tempFile;
|
|
33
|
+
}
|
|
34
|
+
throw new Error('Empty file');
|
|
35
|
+
} catch {
|
|
36
|
+
// Fallback to osascript
|
|
37
|
+
try {
|
|
38
|
+
const script = `
|
|
39
|
+
set theFile to POSIX file "${tempFile}"
|
|
40
|
+
try
|
|
41
|
+
set imageData to the clipboard as «class PNGf»
|
|
42
|
+
set fileRef to open for access theFile with write permission
|
|
43
|
+
write imageData to fileRef
|
|
44
|
+
close access fileRef
|
|
45
|
+
return "success"
|
|
46
|
+
on error errMsg
|
|
47
|
+
return "error: " & errMsg
|
|
48
|
+
end try
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
const { stdout } = await execAsync(`osascript -e '${script.replace(/'/g, "'\\''")}'`);
|
|
52
|
+
|
|
53
|
+
if (stdout.trim().startsWith('success')) {
|
|
54
|
+
// Verify file was created
|
|
55
|
+
const stats = await fs.stat(tempFile);
|
|
56
|
+
if (stats.size > 0) {
|
|
57
|
+
return tempFile;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
throw new Error('No image in clipboard');
|
|
62
|
+
} catch (e) {
|
|
63
|
+
throw new Error('No image in clipboard. Copy a screenshot first (Cmd+Shift+4).');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} else if (platform === 'linux') {
|
|
67
|
+
// Linux: Use xclip
|
|
68
|
+
try {
|
|
69
|
+
await execAsync(`xclip -selection clipboard -t image/png -o > "${tempFile}" 2>/dev/null`);
|
|
70
|
+
|
|
71
|
+
const stats = await fs.stat(tempFile);
|
|
72
|
+
if (stats.size > 0) {
|
|
73
|
+
return tempFile;
|
|
74
|
+
}
|
|
75
|
+
throw new Error('No image in clipboard');
|
|
76
|
+
} catch {
|
|
77
|
+
throw new Error('No image in clipboard. Make sure xclip is installed: sudo apt install xclip');
|
|
78
|
+
}
|
|
79
|
+
} else if (platform === 'win32') {
|
|
80
|
+
// Windows: Use PowerShell
|
|
81
|
+
try {
|
|
82
|
+
const psScript = `
|
|
83
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
84
|
+
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
|
85
|
+
if ($img -ne $null) {
|
|
86
|
+
$img.Save('${tempFile.replace(/\\/g, '\\\\')}', [System.Drawing.Imaging.ImageFormat]::Png)
|
|
87
|
+
Write-Output 'success'
|
|
88
|
+
} else {
|
|
89
|
+
Write-Output 'no image'
|
|
90
|
+
}
|
|
91
|
+
`.replace(/\n/g, ' ');
|
|
92
|
+
|
|
93
|
+
const { stdout } = await execAsync(`powershell -Command "${psScript}"`);
|
|
94
|
+
|
|
95
|
+
if (stdout.trim() === 'success') {
|
|
96
|
+
return tempFile;
|
|
97
|
+
}
|
|
98
|
+
throw new Error('No image in clipboard');
|
|
99
|
+
} catch {
|
|
100
|
+
throw new Error('No image in clipboard');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Read image file and convert to base64
|
|
109
|
+
*/
|
|
110
|
+
export async function imageToBase64(imagePath) {
|
|
111
|
+
const absolutePath = path.resolve(imagePath);
|
|
112
|
+
|
|
113
|
+
// Check file exists
|
|
114
|
+
try {
|
|
115
|
+
await fs.access(absolutePath);
|
|
116
|
+
} catch {
|
|
117
|
+
throw new Error(`Image file not found: ${imagePath}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Read file
|
|
121
|
+
const imageBuffer = await fs.readFile(absolutePath);
|
|
122
|
+
const base64 = imageBuffer.toString('base64');
|
|
123
|
+
|
|
124
|
+
// Detect mime type from extension
|
|
125
|
+
const ext = path.extname(absolutePath).toLowerCase();
|
|
126
|
+
const mimeTypes = {
|
|
127
|
+
'.png': 'image/png',
|
|
128
|
+
'.jpg': 'image/jpeg',
|
|
129
|
+
'.jpeg': 'image/jpeg',
|
|
130
|
+
'.gif': 'image/gif',
|
|
131
|
+
'.webp': 'image/webp',
|
|
132
|
+
'.bmp': 'image/bmp'
|
|
133
|
+
};
|
|
134
|
+
const mimeType = mimeTypes[ext] || 'image/png';
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
base64,
|
|
138
|
+
mimeType,
|
|
139
|
+
dataUrl: `data:${mimeType};base64,${base64}`,
|
|
140
|
+
path: absolutePath,
|
|
141
|
+
size: imageBuffer.length
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get image information
|
|
147
|
+
*/
|
|
148
|
+
export async function getImageInfo(imagePath) {
|
|
149
|
+
const info = await imageToBase64(imagePath);
|
|
150
|
+
const stats = await fs.stat(imagePath);
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
...info,
|
|
154
|
+
fileSize: stats.size,
|
|
155
|
+
fileName: path.basename(imagePath),
|
|
156
|
+
extension: path.extname(imagePath).toLowerCase()
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Check if file is a valid image
|
|
162
|
+
*/
|
|
163
|
+
export async function isValidImage(imagePath) {
|
|
164
|
+
const validExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'];
|
|
165
|
+
const ext = path.extname(imagePath).toLowerCase();
|
|
166
|
+
|
|
167
|
+
if (!validExtensions.includes(ext)) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
await fs.access(imagePath);
|
|
173
|
+
const stats = await fs.stat(imagePath);
|
|
174
|
+
return stats.size > 0;
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Cleanup temporary vibecode images
|
|
182
|
+
*/
|
|
183
|
+
export async function cleanupTempImages() {
|
|
184
|
+
const tempDir = os.tmpdir();
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const files = await fs.readdir(tempDir);
|
|
188
|
+
const vibecodeImages = files.filter(f => f.startsWith('vibecode-screenshot-'));
|
|
189
|
+
|
|
190
|
+
let cleaned = 0;
|
|
191
|
+
for (const file of vibecodeImages) {
|
|
192
|
+
try {
|
|
193
|
+
await fs.unlink(path.join(tempDir, file));
|
|
194
|
+
cleaned++;
|
|
195
|
+
} catch {
|
|
196
|
+
// Ignore errors
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return cleaned;
|
|
201
|
+
} catch {
|
|
202
|
+
return 0;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Format file size for display
|
|
208
|
+
*/
|
|
209
|
+
export function formatFileSize(bytes) {
|
|
210
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
211
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
212
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export default {
|
|
216
|
+
saveClipboardImage,
|
|
217
|
+
imageToBase64,
|
|
218
|
+
getImageInfo,
|
|
219
|
+
isValidImage,
|
|
220
|
+
cleanupTempImages,
|
|
221
|
+
formatFileSize
|
|
222
|
+
};
|