@su-record/vibe 2.6.3 → 2.6.4

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.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Sync UI code with design files
2
+ description: Sync UI code with design files (Gemini or Claude)
3
3
  argument-hint: "design folder path"
4
4
  ---
5
5
 
@@ -7,6 +7,9 @@ argument-hint: "design folder path"
7
7
 
8
8
  Analyze design files and update existing UI code to match.
9
9
 
10
+ - **Gemini enabled**: Gemini analyzes designs and generates code
11
+ - **Gemini disabled**: Claude handles directly
12
+
10
13
  ## Usage
11
14
 
12
15
  ```
@@ -17,11 +20,88 @@ Analyze design files and update existing UI code to match.
17
20
 
18
21
  ## Process
19
22
 
20
- ### 1. Read ALL Design Files
23
+ ### 0. Check Gemini Status
21
24
 
22
- **MANDATORY: Read every file in the design folder.**
25
+ **FIRST: Check if Gemini is available.**
26
+
27
+ ```bash
28
+ vibe gemini status
29
+ ```
23
30
 
24
- Scan folder and read ALL supported files:
31
+ Or check: `~/.config/vibe/gemini.json` or `~/.config/vibe/gemini-apikey.json`
32
+
33
+ - **Gemini available** → Use Gemini for code generation (Step 1A)
34
+ - **Gemini NOT available** → Claude handles directly (Step 1B)
35
+
36
+ ---
37
+
38
+ ## Path A: Gemini Enabled
39
+
40
+ ### 1A. Collect ALL Design Files
41
+
42
+ Scan folder and collect all files to send to Gemini:
43
+
44
+ | Format | What to Collect |
45
+ | ------ | --------------- |
46
+ | `*.html` | Full HTML content |
47
+ | `*.png` / `*.jpg` / `*.webp` | Image as base64 |
48
+ | `*.json` | Design tokens, theme config |
49
+ | `*.css` / `*.scss` | Style variables |
50
+ | `*.md` | Design guidelines |
51
+ | `*.svg` | Vector content |
52
+
53
+ ### 2A. Send to Gemini
54
+
55
+ Use the Gemini UI Generator script:
56
+
57
+ ```bash
58
+ node hooks/scripts/gemini-ui-gen.js \
59
+ --design-folder ./design/ui/ \
60
+ --framework react \
61
+ --output ./src/components
62
+ ```
63
+
64
+ **Or call Gemini API directly with all files:**
65
+
66
+ ```javascript
67
+ // Prepare multipart content
68
+ const parts = [
69
+ // Images as inline data
70
+ { inlineData: { mimeType: "image/png", data: imageBase64 } },
71
+ // HTML/CSS/JSON as text
72
+ { text: `HTML Mockup:\n${htmlContent}` },
73
+ { text: `CSS Styles:\n${cssContent}` },
74
+ { text: `Design Tokens:\n${jsonContent}` },
75
+ // Prompt
76
+ { text: `
77
+ Analyze these design files and generate production-ready React components.
78
+
79
+ Requirements:
80
+ 1. Match the visual design exactly
81
+ 2. Use Tailwind CSS
82
+ 3. TypeScript with proper types
83
+ 4. Responsive and accessible
84
+
85
+ Output complete component code.
86
+ ` }
87
+ ];
88
+ ```
89
+
90
+ ### 3A. Apply Gemini's Output
91
+
92
+ 1. Parse Gemini's code output
93
+ 2. Extract component files
94
+ 3. Compare with existing code
95
+ 4. Show diff to user
96
+ 5. Apply with confirmation
97
+
98
+ ---
99
+
100
+ ## Path B: Gemini NOT Available (Claude Fallback)
101
+
102
+ ### 1B. Read ALL Design Files
103
+
104
+ **MANDATORY: Read every file in the design folder.**
25
105
 
26
106
  | Format | What to Extract |
27
107
  | ------ | --------------- |
@@ -42,7 +122,7 @@ Scan folder and read ALL supported files:
42
122
 
43
123
  **CRITICAL:** Do NOT skip any file. Read images with the Read tool.
44
124
 
45
- ### 2. Analyze Design Intent
125
+ ### 2B. Analyze Design Intent (Claude)
46
126
 
47
127
  From all design files, extract:
48
128
 
@@ -63,12 +143,15 @@ From all design files, extract:
63
143
  - Border radius, shadows
64
144
  - Dark/light theme support
65
145
 
66
- **Interactions:**
67
- - Button states (hover, active, disabled)
68
- - Form validation styles
69
- - Animations/transitions
146
+ ### 3B. Generate Code (Claude)
147
+
148
+ Claude generates the code directly based on analysis.
149
+
150
+ ---
151
+
152
+ ## Common Steps (Both Paths)
70
153
 
71
- ### 3. Find Existing UI Code
154
+ ### 4. Find Existing UI Code
72
155
 
73
156
  Search for existing UI implementation:
74
157
 
@@ -87,7 +170,7 @@ Detect framework:
87
170
  - Svelte: `*.svelte`
88
171
  - Angular: `*.component.ts`
89
172
 
90
- ### 4. Compare Design vs Code
173
+ ### 5. Compare Design vs Code
91
174
 
92
175
  Create a diff report:
93
176
 
@@ -98,7 +181,7 @@ Create a diff report:
98
181
  | Border radius | 12px | 8px | Update |
99
182
  | Component X | Exists | Missing | Create |
100
183
 
101
- ### 5. Generate Update Plan
184
+ ### 6. Generate Update Plan
102
185
 
103
186
  List all changes needed:
104
187
 
@@ -125,7 +208,7 @@ List all changes needed:
125
208
  **New Components:**
126
209
  - List components that exist in design but not in code
127
210
 
128
- ### 6. Execute Updates
211
+ ### 7. Execute Updates
129
212
 
130
213
  **Ask user before making changes:**
131
214
 
@@ -147,7 +230,7 @@ Proceed with updates? [Y/n]
147
230
  4. Create new components (if needed)
148
231
  5. Update imports
149
232
 
150
- ### 7. Verify Changes
233
+ ### 8. Verify Changes
151
234
 
152
235
  After updates:
153
236
 
@@ -159,6 +242,7 @@ After updates:
159
242
 
160
243
  ```
161
244
  📂 Design Analysis: ./design/ui/
245
+ 🤖 Mode: Gemini (or Claude fallback)
162
246
 
163
247
  Files Read:
164
248
  ✓ homepage.html (structure)
@@ -190,34 +274,33 @@ Apply changes? [Y/n]
190
274
  Next: Run `npm run build` to verify
191
275
  ```
192
276
 
193
- ## Example
277
+ ## Example (with Gemini)
194
278
 
195
279
  ```
196
280
  User: /vibe.utils --ui-sync ./design/dashboard/
197
281
 
198
- Claude: Reading design folder...
282
+ Claude: Checking Gemini status...
283
+ ✅ Gemini: OAuth authenticated (user@example.com)
199
284
 
200
- 📂 Files found:
285
+ 📂 Collecting design files...
201
286
  - dashboard.html
202
287
  - dashboard-dark.png
203
288
  - dashboard-light.png
204
289
  - tokens.json
205
290
  - components.md
206
291
 
207
- Reading dashboard.html...
208
- [Extracts: header, sidebar, 3-column grid, card components]
292
+ 🤖 Sending to Gemini for analysis...
209
293
 
210
- Reading dashboard-dark.png...
211
- [Analyzes: dark theme colors, spacing, typography]
294
+ Gemini Response:
295
+ Layout: Header + Sidebar + 3-column main
296
+ Theme: Dark (#0F172A bg, #F8FAFC text)
297
+ Components: Header, Sidebar, StatCard, DataTable
212
298
 
213
- Reading tokens.json...
214
- [Extracts: color palette, spacing scale, typography scale]
215
-
216
- 📊 Design Analysis Complete:
217
-
218
- Layout: Header + Sidebar + 3-column main
219
- Theme: Dark (#0F172A bg, #F8FAFC text)
220
- Components: Header, Sidebar, StatCard, DataTable
299
+ Generated code:
300
+ - Header.tsx (64 lines)
301
+ - Sidebar.tsx (45 lines)
302
+ - StatCard.tsx (32 lines)
303
+ - DataTable.tsx (78 lines)
221
304
 
222
305
  🔍 Comparing with existing code...
223
306
 
@@ -228,31 +311,48 @@ Missing: src/components/DataTable.tsx
228
311
  Differences:
229
312
  1. StatCard border-radius: design=16px, code=8px
230
313
  2. Sidebar width: design=240px, code=200px
231
- 3. Header height: design=64px, code=56px
232
- 4. Primary color: design=#6366F1, code=#3B82F6
233
- 5. DataTable component: missing
314
+ 3. Primary color: design=#6366F1, code=#3B82F6
315
+ 4. DataTable component: missing
316
+
317
+ Apply 3 updates + create 1 component? [Y/n]
318
+ ```
234
319
 
235
- Apply 4 updates + create 1 component? [Y/n]
320
+ ## Example (Claude Fallback)
236
321
 
237
- User: Y
322
+ ```
323
+ User: /vibe.utils --ui-sync ./design/dashboard/
238
324
 
239
- Changes applied:
240
- - src/components/StatCard.tsx (border-radius)
241
- - src/components/Sidebar.tsx (width)
242
- - src/components/Header.tsx (height)
243
- - tailwind.config.js (primary color)
244
- - src/components/DataTable.tsx (created)
325
+ Claude: Checking Gemini status...
326
+ ⚠️ Gemini not configured. Using Claude fallback.
245
327
 
246
- Run `npm run dev` to preview changes.
328
+ 📂 Reading design files...
329
+
330
+ Reading dashboard.html...
331
+ [Extracts: header, sidebar, 3-column grid]
332
+
333
+ Reading dashboard-dark.png...
334
+ [Analyzes: dark theme colors, spacing]
335
+
336
+ Reading tokens.json...
337
+ [Extracts: color palette, spacing scale]
338
+
339
+ 📊 Design Analysis Complete (Claude):
340
+
341
+ Layout: Header + Sidebar + 3-column main
342
+ Theme: Dark (#0F172A bg, #F8FAFC text)
343
+
344
+ 🔍 Comparing with existing code...
345
+ ...
247
346
  ```
248
347
 
249
348
  ## Notes
250
349
 
350
+ - Always check Gemini status first
351
+ - Gemini handles image analysis better than Claude for visual matching
352
+ - Claude fallback works but may be less accurate for complex visuals
251
353
  - Always read ALL files including images
252
354
  - Ask for confirmation before making changes
253
355
  - Preserve existing functionality while updating styles
254
- - Create backup recommendation for large changes
255
- - Support both CSS-in-JS and traditional CSS
256
356
 
257
357
  ---
258
358
 
@@ -0,0 +1,409 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Gemini UI Code Generator
5
+ *
6
+ * 디자인 파일(이미지, HTML 등)을 분석해서 UI 코드를 생성합니다.
7
+ *
8
+ * Usage:
9
+ * node gemini-ui-gen.js --image ./design.png --framework react --output ./src/components
10
+ * node gemini-ui-gen.js --html ./mockup.html --framework vue --output ./src/components
11
+ * node gemini-ui-gen.js --design-folder ./design/ --framework react --output ./src
12
+ */
13
+
14
+ import fs from 'fs';
15
+ import path from 'path';
16
+ import os from 'os';
17
+
18
+ // ============================================
19
+ // Config
20
+ // ============================================
21
+
22
+ function getGlobalConfigDir() {
23
+ return process.platform === 'win32'
24
+ ? path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'vibe')
25
+ : path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'vibe');
26
+ }
27
+
28
+ function getGeminiCredentials() {
29
+ const configDir = getGlobalConfigDir();
30
+
31
+ // OAuth 토큰 확인
32
+ const tokenPath = path.join(configDir, 'gemini-token.json');
33
+ if (fs.existsSync(tokenPath)) {
34
+ const tokenData = JSON.parse(fs.readFileSync(tokenPath, 'utf-8'));
35
+ if (tokenData.access_token) {
36
+ return { type: 'oauth', accessToken: tokenData.access_token };
37
+ }
38
+ }
39
+
40
+ // API Key 확인
41
+ const keyPath = path.join(configDir, 'gemini-apikey.json');
42
+ if (fs.existsSync(keyPath)) {
43
+ const keyData = JSON.parse(fs.readFileSync(keyPath, 'utf-8'));
44
+ if (keyData.apiKey) {
45
+ return { type: 'apikey', apiKey: keyData.apiKey };
46
+ }
47
+ }
48
+
49
+ return null;
50
+ }
51
+
52
+ // ============================================
53
+ // Gemini API with Vision
54
+ // ============================================
55
+
56
+ async function callGeminiWithImage(imageBase64, mimeType, prompt, creds) {
57
+ const model = 'gemini-2.0-flash';
58
+
59
+ const requestBody = {
60
+ contents: [
61
+ {
62
+ role: 'user',
63
+ parts: [
64
+ {
65
+ inlineData: {
66
+ mimeType,
67
+ data: imageBase64
68
+ }
69
+ },
70
+ {
71
+ text: prompt
72
+ }
73
+ ]
74
+ }
75
+ ],
76
+ generationConfig: {
77
+ maxOutputTokens: 8192,
78
+ temperature: 0.3,
79
+ }
80
+ };
81
+
82
+ let url;
83
+ let headers;
84
+
85
+ if (creds.type === 'apikey') {
86
+ url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${creds.apiKey}`;
87
+ headers = { 'Content-Type': 'application/json' };
88
+ } else {
89
+ // OAuth - Antigravity
90
+ url = 'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent';
91
+ headers = {
92
+ 'Authorization': `Bearer ${creds.accessToken}`,
93
+ 'Content-Type': 'application/json',
94
+ 'x-goog-api-client': 'vibe-ui-gen',
95
+ };
96
+
97
+ // Wrap for Antigravity
98
+ const wrappedBody = {
99
+ project: 'anthropic-api-proxy',
100
+ model: 'gemini-2.0-flash-001',
101
+ request: requestBody,
102
+ requestType: 'agent',
103
+ userAgent: 'antigravity',
104
+ requestId: `ui-gen-${Date.now()}`,
105
+ };
106
+ requestBody = wrappedBody;
107
+ }
108
+
109
+ const response = await fetch(url, {
110
+ method: 'POST',
111
+ headers,
112
+ body: JSON.stringify(creds.type === 'apikey' ? requestBody : requestBody),
113
+ });
114
+
115
+ if (!response.ok) {
116
+ const errorText = await response.text();
117
+ throw new Error(`Gemini API error (${response.status}): ${errorText}`);
118
+ }
119
+
120
+ const result = await response.json();
121
+ const responseData = result.response || result;
122
+
123
+ if (!responseData.candidates || responseData.candidates.length === 0) {
124
+ throw new Error('Gemini returned empty response');
125
+ }
126
+
127
+ return responseData.candidates[0].content?.parts?.[0]?.text || '';
128
+ }
129
+
130
+ async function callGeminiText(prompt, creds) {
131
+ const model = 'gemini-2.0-flash';
132
+
133
+ const requestBody = {
134
+ contents: [
135
+ {
136
+ role: 'user',
137
+ parts: [{ text: prompt }]
138
+ }
139
+ ],
140
+ generationConfig: {
141
+ maxOutputTokens: 8192,
142
+ temperature: 0.3,
143
+ }
144
+ };
145
+
146
+ let url;
147
+ let headers;
148
+
149
+ if (creds.type === 'apikey') {
150
+ url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${creds.apiKey}`;
151
+ headers = { 'Content-Type': 'application/json' };
152
+ } else {
153
+ url = 'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent';
154
+ headers = {
155
+ 'Authorization': `Bearer ${creds.accessToken}`,
156
+ 'Content-Type': 'application/json',
157
+ };
158
+ }
159
+
160
+ const response = await fetch(url, {
161
+ method: 'POST',
162
+ headers,
163
+ body: JSON.stringify(requestBody),
164
+ });
165
+
166
+ if (!response.ok) {
167
+ const errorText = await response.text();
168
+ throw new Error(`Gemini API error (${response.status}): ${errorText}`);
169
+ }
170
+
171
+ const result = await response.json();
172
+ const responseData = result.response || result;
173
+
174
+ return responseData.candidates?.[0]?.content?.parts?.[0]?.text || '';
175
+ }
176
+
177
+ // ============================================
178
+ // UI Code Generation
179
+ // ============================================
180
+
181
+ function getFrameworkPrompt(framework) {
182
+ const prompts = {
183
+ react: `Generate React TypeScript components using:
184
+ - Functional components with hooks
185
+ - Tailwind CSS for styling
186
+ - Proper TypeScript types/interfaces
187
+ - Export as default`,
188
+
189
+ vue: `Generate Vue 3 components using:
190
+ - Composition API with <script setup>
191
+ - Tailwind CSS for styling
192
+ - TypeScript support
193
+ - Single File Component format`,
194
+
195
+ svelte: `Generate Svelte components using:
196
+ - Svelte 5 runes syntax
197
+ - Tailwind CSS for styling
198
+ - TypeScript support`,
199
+
200
+ html: `Generate semantic HTML5 with:
201
+ - Tailwind CSS classes
202
+ - Accessible markup
203
+ - Responsive design`,
204
+ };
205
+
206
+ return prompts[framework] || prompts.react;
207
+ }
208
+
209
+ async function generateUIFromImage(imagePath, framework, creds) {
210
+ const imageBuffer = fs.readFileSync(imagePath);
211
+ const imageBase64 = imageBuffer.toString('base64');
212
+
213
+ const ext = path.extname(imagePath).toLowerCase();
214
+ const mimeTypes = {
215
+ '.png': 'image/png',
216
+ '.jpg': 'image/jpeg',
217
+ '.jpeg': 'image/jpeg',
218
+ '.webp': 'image/webp',
219
+ '.gif': 'image/gif',
220
+ };
221
+ const mimeType = mimeTypes[ext] || 'image/png';
222
+
223
+ const prompt = `Analyze this UI design image and generate production-ready code.
224
+
225
+ ${getFrameworkPrompt(framework)}
226
+
227
+ Requirements:
228
+ 1. Match the visual design exactly (colors, spacing, typography, layout)
229
+ 2. Extract exact colors as hex values
230
+ 3. Use proper semantic HTML structure
231
+ 4. Make it responsive (mobile-first)
232
+ 5. Include hover/focus states where appropriate
233
+ 6. Add appropriate accessibility attributes
234
+
235
+ Output format:
236
+ \`\`\`${framework === 'html' ? 'html' : 'tsx'}
237
+ // Component code here
238
+ \`\`\`
239
+
240
+ Also provide a summary of:
241
+ - Colors extracted
242
+ - Components identified
243
+ - Layout structure`;
244
+
245
+ return callGeminiWithImage(imageBase64, mimeType, prompt, creds);
246
+ }
247
+
248
+ async function generateUIFromHTML(htmlPath, framework, creds) {
249
+ const htmlContent = fs.readFileSync(htmlPath, 'utf-8');
250
+
251
+ const prompt = `Convert this HTML mockup to production-ready ${framework} code.
252
+
253
+ HTML Mockup:
254
+ \`\`\`html
255
+ ${htmlContent}
256
+ \`\`\`
257
+
258
+ ${getFrameworkPrompt(framework)}
259
+
260
+ Requirements:
261
+ 1. Preserve the exact visual appearance
262
+ 2. Extract inline styles to Tailwind classes
263
+ 3. Create reusable components where appropriate
264
+ 4. Add proper TypeScript types
265
+ 5. Make it responsive
266
+
267
+ Output the converted code in proper format.`;
268
+
269
+ return callGeminiText(prompt, creds);
270
+ }
271
+
272
+ async function analyzeDesignFolder(folderPath, framework, creds) {
273
+ const files = fs.readdirSync(folderPath);
274
+ const results = [];
275
+
276
+ // Read all files
277
+ for (const file of files) {
278
+ const filePath = path.join(folderPath, file);
279
+ const ext = path.extname(file).toLowerCase();
280
+
281
+ if (['.png', '.jpg', '.jpeg', '.webp'].includes(ext)) {
282
+ console.log(`📷 Analyzing image: ${file}`);
283
+ const result = await generateUIFromImage(filePath, framework, creds);
284
+ results.push({ file, type: 'image', result });
285
+ } else if (ext === '.html') {
286
+ console.log(`📄 Analyzing HTML: ${file}`);
287
+ const result = await generateUIFromHTML(filePath, framework, creds);
288
+ results.push({ file, type: 'html', result });
289
+ } else if (ext === '.json') {
290
+ console.log(`📋 Reading tokens: ${file}`);
291
+ const content = fs.readFileSync(filePath, 'utf-8');
292
+ results.push({ file, type: 'tokens', content });
293
+ } else if (['.css', '.scss'].includes(ext)) {
294
+ console.log(`🎨 Reading styles: ${file}`);
295
+ const content = fs.readFileSync(filePath, 'utf-8');
296
+ results.push({ file, type: 'styles', content });
297
+ }
298
+ }
299
+
300
+ return results;
301
+ }
302
+
303
+ // ============================================
304
+ // CLI
305
+ // ============================================
306
+
307
+ async function main() {
308
+ const args = process.argv.slice(2);
309
+
310
+ // Parse arguments
311
+ const options = {
312
+ image: null,
313
+ html: null,
314
+ designFolder: null,
315
+ framework: 'react',
316
+ output: './generated',
317
+ };
318
+
319
+ for (let i = 0; i < args.length; i++) {
320
+ switch (args[i]) {
321
+ case '--image':
322
+ options.image = args[++i];
323
+ break;
324
+ case '--html':
325
+ options.html = args[++i];
326
+ break;
327
+ case '--design-folder':
328
+ case '--folder':
329
+ options.designFolder = args[++i];
330
+ break;
331
+ case '--framework':
332
+ case '-f':
333
+ options.framework = args[++i];
334
+ break;
335
+ case '--output':
336
+ case '-o':
337
+ options.output = args[++i];
338
+ break;
339
+ case '--help':
340
+ case '-h':
341
+ console.log(`
342
+ Gemini UI Code Generator
343
+
344
+ Usage:
345
+ node gemini-ui-gen.js --image ./design.png --framework react
346
+ node gemini-ui-gen.js --html ./mockup.html --framework vue
347
+ node gemini-ui-gen.js --design-folder ./design/ --framework react
348
+
349
+ Options:
350
+ --image <path> Image file to analyze
351
+ --html <path> HTML mockup to convert
352
+ --design-folder <path> Folder with design files
353
+ --framework <name> Target framework (react, vue, svelte, html)
354
+ --output <path> Output directory
355
+ --help Show this help
356
+ `);
357
+ process.exit(0);
358
+ }
359
+ }
360
+
361
+ // Check credentials
362
+ const creds = getGeminiCredentials();
363
+ if (!creds) {
364
+ console.error('❌ Gemini credentials not found. Run: vibe gemini auth');
365
+ process.exit(1);
366
+ }
367
+
368
+ console.log(`🤖 Gemini UI Generator (${creds.type})`);
369
+ console.log(`📦 Framework: ${options.framework}`);
370
+
371
+ try {
372
+ let result;
373
+
374
+ if (options.image) {
375
+ console.log(`\n📷 Analyzing: ${options.image}\n`);
376
+ result = await generateUIFromImage(options.image, options.framework, creds);
377
+ } else if (options.html) {
378
+ console.log(`\n📄 Converting: ${options.html}\n`);
379
+ result = await generateUIFromHTML(options.html, options.framework, creds);
380
+ } else if (options.designFolder) {
381
+ console.log(`\n📂 Analyzing folder: ${options.designFolder}\n`);
382
+ const results = await analyzeDesignFolder(options.designFolder, options.framework, creds);
383
+ result = JSON.stringify(results, null, 2);
384
+ } else {
385
+ console.error('❌ No input specified. Use --image, --html, or --design-folder');
386
+ process.exit(1);
387
+ }
388
+
389
+ console.log('\n' + '='.repeat(60) + '\n');
390
+ console.log(result);
391
+ console.log('\n' + '='.repeat(60));
392
+
393
+ // Output to file if specified
394
+ if (options.output && result) {
395
+ if (!fs.existsSync(options.output)) {
396
+ fs.mkdirSync(options.output, { recursive: true });
397
+ }
398
+ const outputFile = path.join(options.output, `generated-${Date.now()}.txt`);
399
+ fs.writeFileSync(outputFile, result);
400
+ console.log(`\n✅ Output saved to: ${outputFile}`);
401
+ }
402
+
403
+ } catch (error) {
404
+ console.error(`\n❌ Error: ${error.message}`);
405
+ process.exit(1);
406
+ }
407
+ }
408
+
409
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@su-record/vibe",
3
- "version": "2.6.3",
3
+ "version": "2.6.4",
4
4
  "description": "Vibe - Claude Code exclusive SPEC-driven AI coding framework with 35+ integrated tools",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",