@nclamvn/vibecode-cli 2.2.0 → 3.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.
@@ -0,0 +1,412 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // VIBECODE CLI - Favorite Command
3
+ // Manage favorite prompts for quick reuse
4
+ // ═══════════════════════════════════════════════════════════════════════════════
5
+
6
+ import chalk from 'chalk';
7
+ import inquirer from 'inquirer';
8
+ import { spawn } from 'child_process';
9
+ import {
10
+ loadFavorites,
11
+ addFavorite,
12
+ removeFavorite,
13
+ getFavorite,
14
+ searchFavorites,
15
+ updateFavoriteUsage,
16
+ exportFavorites,
17
+ importFavorites,
18
+ clearFavorites,
19
+ getFavoritesStats
20
+ } from '../utils/history.js';
21
+
22
+ /**
23
+ * Favorite command entry point
24
+ */
25
+ export async function favoriteCommand(action, args = [], options = {}) {
26
+ // Default action: list
27
+ if (!action || action === 'list') {
28
+ return listFavorites();
29
+ }
30
+
31
+ // Add favorite
32
+ if (action === 'add') {
33
+ const prompt = Array.isArray(args) ? args.join(' ') : args;
34
+ if (!prompt) {
35
+ console.log(chalk.red('\n ❌ Please provide a prompt to save.\n'));
36
+ console.log(chalk.gray(' Usage: vibecode fav add "Your prompt here"\n'));
37
+ return;
38
+ }
39
+ return addFavoriteCommand(prompt, options);
40
+ }
41
+
42
+ // Remove favorite
43
+ if (action === 'remove' || action === 'rm' || action === 'delete') {
44
+ const identifier = Array.isArray(args) ? args[0] : args;
45
+ if (!identifier) {
46
+ console.log(chalk.red('\n ❌ Please specify which favorite to remove.\n'));
47
+ console.log(chalk.gray(' Usage: vibecode fav remove <number or name>\n'));
48
+ return;
49
+ }
50
+ return removeFavoriteCommand(identifier);
51
+ }
52
+
53
+ // Run favorite
54
+ if (action === 'run' || action === 'use') {
55
+ const identifier = Array.isArray(args) ? args[0] : args;
56
+ if (!identifier) {
57
+ return selectAndRunFavorite();
58
+ }
59
+ return runFavoriteCommand(identifier, options);
60
+ }
61
+
62
+ // Search favorites
63
+ if (action === 'search' || action === 'find') {
64
+ const query = Array.isArray(args) ? args.join(' ') : args;
65
+ if (!query) {
66
+ console.log(chalk.red('\n ❌ Please provide a search query.\n'));
67
+ return;
68
+ }
69
+ return searchFavoritesCommand(query);
70
+ }
71
+
72
+ // Export
73
+ if (action === 'export') {
74
+ const data = await exportFavorites();
75
+ console.log(JSON.stringify(data, null, 2));
76
+ return;
77
+ }
78
+
79
+ // Import
80
+ if (action === 'import') {
81
+ return importFavoritesCommand(options);
82
+ }
83
+
84
+ // Clear
85
+ if (action === 'clear') {
86
+ return clearFavoritesCommand();
87
+ }
88
+
89
+ // Stats
90
+ if (action === 'stats') {
91
+ return showFavoritesStats();
92
+ }
93
+
94
+ // Unknown action - show help
95
+ showFavoriteHelp();
96
+ }
97
+
98
+ /**
99
+ * List all favorites
100
+ */
101
+ async function listFavorites() {
102
+ const favorites = await loadFavorites();
103
+
104
+ console.log(chalk.cyan(`
105
+ ╭────────────────────────────────────────────────────────────────────╮
106
+ │ ⭐ FAVORITES │
107
+ ╰────────────────────────────────────────────────────────────────────╯
108
+ `));
109
+
110
+ if (favorites.length === 0) {
111
+ console.log(chalk.gray(' No favorites yet.\n'));
112
+ console.log(chalk.gray(' Add one:\n'));
113
+ console.log(chalk.gray(' vibecode fav add "Your prompt here"\n'));
114
+ return;
115
+ }
116
+
117
+ for (let i = 0; i < favorites.length; i++) {
118
+ const fav = favorites[i];
119
+ const usage = fav.usageCount
120
+ ? chalk.gray(` (used ${fav.usageCount}x)`)
121
+ : '';
122
+
123
+ const displayName = (fav.name || fav.description || 'Untitled').substring(0, 40);
124
+
125
+ console.log(
126
+ chalk.yellow(` ${(i + 1).toString().padStart(2)}. `) +
127
+ chalk.white(displayName) +
128
+ usage
129
+ );
130
+
131
+ // Show command preview
132
+ const cmdPreview = fav.command.length > 55
133
+ ? fav.command.substring(0, 52) + '...'
134
+ : fav.command;
135
+ console.log(chalk.gray(` ${cmdPreview}`));
136
+
137
+ // Show tags if any
138
+ if (fav.tags && fav.tags.length > 0) {
139
+ console.log(chalk.gray(` Tags: ${fav.tags.join(', ')}`));
140
+ }
141
+
142
+ console.log('');
143
+ }
144
+
145
+ console.log(chalk.gray(` Commands:`));
146
+ console.log(chalk.gray(` ${chalk.cyan('vibecode fav run <n>')} Run favorite`));
147
+ console.log(chalk.gray(` ${chalk.cyan('vibecode fav add "..."')} Add favorite`));
148
+ console.log(chalk.gray(` ${chalk.cyan('vibecode fav remove <n>')} Remove favorite\n`));
149
+ }
150
+
151
+ /**
152
+ * Add a new favorite
153
+ */
154
+ async function addFavoriteCommand(prompt, options) {
155
+ // Build command
156
+ let command;
157
+ if (options.template) {
158
+ command = `vibecode go --template ${options.template}`;
159
+ } else {
160
+ command = `vibecode go "${prompt}"`;
161
+ }
162
+
163
+ // Add extra options if specified
164
+ if (options.preview) command += ' --preview';
165
+ if (options.deploy) command += ' --deploy';
166
+ if (options.notify) command += ' --notify';
167
+
168
+ const name = options.name || prompt.substring(0, 40);
169
+ const tags = options.tags ? options.tags.split(',').map(t => t.trim()) : [];
170
+
171
+ const result = await addFavorite(name, command, prompt, tags);
172
+
173
+ if (result.success) {
174
+ console.log(chalk.green(`\n ⭐ Added to favorites!\n`));
175
+ console.log(chalk.white(` Name: ${name}`));
176
+ console.log(chalk.gray(` Command: ${command}\n`));
177
+ } else {
178
+ console.log(chalk.yellow(`\n ⚠️ ${result.message}\n`));
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Remove a favorite
184
+ */
185
+ async function removeFavoriteCommand(identifier) {
186
+ const result = await removeFavorite(identifier);
187
+
188
+ if (result.success) {
189
+ console.log(chalk.green(`\n ✅ Removed: "${result.removed.name}"\n`));
190
+ } else {
191
+ console.log(chalk.red(`\n ❌ ${result.message}\n`));
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Run a favorite by identifier
197
+ */
198
+ async function runFavoriteCommand(identifier, options = {}) {
199
+ const favorite = await getFavorite(identifier);
200
+
201
+ if (!favorite) {
202
+ console.log(chalk.red(`\n ❌ Favorite "${identifier}" not found.\n`));
203
+ console.log(chalk.gray(' Run `vibecode fav` to see your favorites.\n'));
204
+ return;
205
+ }
206
+
207
+ console.log(chalk.cyan(`\n ⭐ Running: ${favorite.name}\n`));
208
+ console.log(chalk.gray(` ${favorite.command}\n`));
209
+
210
+ if (!options.yes) {
211
+ const { confirm } = await inquirer.prompt([{
212
+ type: 'confirm',
213
+ name: 'confirm',
214
+ message: 'Execute?',
215
+ default: true
216
+ }]);
217
+
218
+ if (!confirm) {
219
+ console.log(chalk.gray('\n Cancelled.\n'));
220
+ return;
221
+ }
222
+ }
223
+
224
+ // Update usage count
225
+ await updateFavoriteUsage(favorite.id);
226
+
227
+ console.log(chalk.cyan('\n Executing...\n'));
228
+
229
+ // Execute
230
+ const child = spawn('sh', ['-c', favorite.command], {
231
+ stdio: 'inherit',
232
+ cwd: process.cwd(),
233
+ shell: true
234
+ });
235
+
236
+ child.on('error', (error) => {
237
+ console.log(chalk.red(`\n ❌ Error: ${error.message}\n`));
238
+ });
239
+ }
240
+
241
+ /**
242
+ * Interactive favorite selection
243
+ */
244
+ async function selectAndRunFavorite() {
245
+ const favorites = await loadFavorites();
246
+
247
+ if (favorites.length === 0) {
248
+ console.log(chalk.yellow('\n No favorites yet.\n'));
249
+ console.log(chalk.gray(' Add one: vibecode fav add "Your prompt"\n'));
250
+ return;
251
+ }
252
+
253
+ const { selected } = await inquirer.prompt([{
254
+ type: 'list',
255
+ name: 'selected',
256
+ message: 'Select favorite to run:',
257
+ choices: favorites.map((f, i) => {
258
+ const cmdPreview = f.command.length > 40
259
+ ? f.command.substring(0, 37) + '...'
260
+ : f.command;
261
+ return {
262
+ name: `${f.name} - ${chalk.gray(cmdPreview)}`,
263
+ value: i + 1
264
+ };
265
+ })
266
+ }]);
267
+
268
+ await runFavoriteCommand(selected, { yes: true });
269
+ }
270
+
271
+ /**
272
+ * Search favorites
273
+ */
274
+ async function searchFavoritesCommand(query) {
275
+ const results = await searchFavorites(query);
276
+
277
+ console.log(chalk.cyan(`\n 🔍 Favorites matching "${query}":\n`));
278
+
279
+ if (results.length === 0) {
280
+ console.log(chalk.gray(' No matches found.\n'));
281
+ return;
282
+ }
283
+
284
+ for (let i = 0; i < results.length; i++) {
285
+ const fav = results[i];
286
+ console.log(chalk.yellow(` ${i + 1}. `) + chalk.white(fav.name));
287
+
288
+ const cmdPreview = fav.command.length > 55
289
+ ? fav.command.substring(0, 52) + '...'
290
+ : fav.command;
291
+ console.log(chalk.gray(` ${cmdPreview}\n`));
292
+ }
293
+
294
+ console.log(chalk.gray(` Run: vibecode fav run "<name>"\n`));
295
+ }
296
+
297
+ /**
298
+ * Import favorites from stdin
299
+ */
300
+ async function importFavoritesCommand(options) {
301
+ // Check if data is being piped
302
+ if (process.stdin.isTTY) {
303
+ console.log(chalk.yellow('\n Paste JSON data and press Ctrl+D when done:\n'));
304
+ }
305
+
306
+ let data = '';
307
+ process.stdin.setEncoding('utf8');
308
+
309
+ return new Promise((resolve) => {
310
+ process.stdin.on('readable', () => {
311
+ let chunk;
312
+ while ((chunk = process.stdin.read()) !== null) {
313
+ data += chunk;
314
+ }
315
+ });
316
+
317
+ process.stdin.on('end', async () => {
318
+ try {
319
+ const parsed = JSON.parse(data);
320
+
321
+ if (!Array.isArray(parsed)) {
322
+ console.log(chalk.red('\n ❌ Invalid format: expected an array\n'));
323
+ resolve();
324
+ return;
325
+ }
326
+
327
+ const result = await importFavorites(parsed, !options.replace);
328
+ console.log(chalk.green(`\n ✅ Imported ${result.imported} favorites`));
329
+ console.log(chalk.gray(` Total favorites: ${result.total}\n`));
330
+ } catch (error) {
331
+ console.log(chalk.red(`\n ❌ Invalid JSON: ${error.message}\n`));
332
+ }
333
+ resolve();
334
+ });
335
+ });
336
+ }
337
+
338
+ /**
339
+ * Clear all favorites
340
+ */
341
+ async function clearFavoritesCommand() {
342
+ const favorites = await loadFavorites();
343
+
344
+ if (favorites.length === 0) {
345
+ console.log(chalk.gray('\n No favorites to clear.\n'));
346
+ return;
347
+ }
348
+
349
+ const { confirm } = await inquirer.prompt([{
350
+ type: 'confirm',
351
+ name: 'confirm',
352
+ message: `Clear all ${favorites.length} favorites?`,
353
+ default: false
354
+ }]);
355
+
356
+ if (confirm) {
357
+ await clearFavorites();
358
+ console.log(chalk.green('\n ✅ All favorites cleared\n'));
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Show favorites statistics
364
+ */
365
+ async function showFavoritesStats() {
366
+ const stats = await getFavoritesStats();
367
+
368
+ console.log(chalk.cyan(`
369
+ ╭────────────────────────────────────────────────────────────────────╮
370
+ │ 📊 FAVORITES STATISTICS │
371
+ ╰────────────────────────────────────────────────────────────────────╯
372
+ `));
373
+
374
+ console.log(chalk.white(` Total favorites: ${stats.total}`));
375
+ console.log(chalk.white(` Total usage: ${stats.totalUsage} runs`));
376
+
377
+ if (stats.mostUsed) {
378
+ console.log(chalk.white(` Most used: ${stats.mostUsed}`));
379
+ }
380
+
381
+ console.log('');
382
+ }
383
+
384
+ /**
385
+ * Show help for favorite command
386
+ */
387
+ function showFavoriteHelp() {
388
+ console.log(chalk.cyan(`
389
+ ⭐ Favorites Commands:
390
+
391
+ ${chalk.yellow('vibecode fav')} List all favorites
392
+ ${chalk.yellow('vibecode fav list')} List all favorites
393
+
394
+ ${chalk.yellow('vibecode fav add "prompt"')} Add a favorite
395
+ ${chalk.yellow('vibecode fav add "prompt" -n X')} Add with custom name
396
+ ${chalk.yellow('vibecode fav add "prompt" -t T')} Add with template
397
+
398
+ ${chalk.yellow('vibecode fav run <n>')} Run favorite by number
399
+ ${chalk.yellow('vibecode fav run "name"')} Run favorite by name
400
+ ${chalk.yellow('vibecode fav run')} Interactive selection
401
+
402
+ ${chalk.yellow('vibecode fav remove <n>')} Remove favorite
403
+ ${chalk.yellow('vibecode fav search <query>')} Search favorites
404
+
405
+ ${chalk.yellow('vibecode fav export')} Export to JSON
406
+ ${chalk.yellow('vibecode fav import')} Import from JSON
407
+ ${chalk.yellow('vibecode fav clear')} Clear all favorites
408
+ ${chalk.yellow('vibecode fav stats')} Show statistics
409
+ `));
410
+ }
411
+
412
+ export default favoriteCommand;