voyageai-cli 1.21.0 → 1.22.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voyageai-cli",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "CLI for Voyage AI embeddings, reranking, and MongoDB Atlas Vector Search",
5
5
  "bin": {
6
6
  "vai": "./src/cli.js"
@@ -37,9 +37,10 @@
37
37
  "test": "node --test test/**/*.test.js"
38
38
  },
39
39
  "engines": {
40
- "node": ">=18.0.0"
40
+ "node": ">=20.0.0"
41
41
  },
42
42
  "dependencies": {
43
+ "@clack/prompts": "^1.0.0",
43
44
  "commander": "^12.0.0",
44
45
  "dotenv": "^17.2.3",
45
46
  "mongodb": "^6.0.0",
package/src/cli.js CHANGED
@@ -34,6 +34,7 @@ const { registerApp } = require('./commands/app');
34
34
  const { registerAbout } = require('./commands/about');
35
35
  const { register: registerDoctor } = require('./commands/doctor');
36
36
  const { register: registerQuickstart } = require('./commands/quickstart');
37
+ const { registerBug } = require('./commands/bug');
37
38
  const { showBanner, showQuickStart, getVersion } = require('./lib/banner');
38
39
 
39
40
  const version = getVersion();
@@ -72,6 +73,7 @@ registerApp(program);
72
73
  registerAbout(program);
73
74
  registerDoctor(program);
74
75
  registerQuickstart(program);
76
+ registerBug(program);
75
77
 
76
78
  // Append disclaimer to all help output
77
79
  program.addHelpText('after', `
@@ -0,0 +1,249 @@
1
+ 'use strict';
2
+
3
+ const os = require('os');
4
+ const pc = require('picocolors');
5
+ const ui = require('../lib/ui');
6
+ const { send: sendTelemetry } = require('../lib/telemetry');
7
+
8
+ // Try to get package version safely
9
+ function getVersion() {
10
+ try {
11
+ return require('../../package.json').version;
12
+ } catch {
13
+ return 'unknown';
14
+ }
15
+ }
16
+
17
+ const GITHUB_ISSUES_URL = 'https://github.com/mrlynn/voyageai-cli/issues/new';
18
+ const BUG_API_URL = 'https://vai.mlynn.org/api/bugs';
19
+
20
+ /**
21
+ * Generate a GitHub issue URL with pre-filled template
22
+ */
23
+ function generateGitHubUrl(title, description, context = {}) {
24
+ const issueTitle = encodeURIComponent(`[Bug] ${title || 'Bug Report'}`);
25
+
26
+ const body = `## Description
27
+ ${description || 'Describe the bug here...'}
28
+
29
+ ## Steps to Reproduce
30
+ 1.
31
+ 2.
32
+ 3.
33
+
34
+ ## Expected Behavior
35
+
36
+
37
+ ## Actual Behavior
38
+
39
+
40
+ ## Environment
41
+ - **CLI Version:** ${context.cliVersion || getVersion()}
42
+ - **Node Version:** ${process.version}
43
+ - **Platform:** ${os.platform()} ${os.release()}
44
+ - **Arch:** ${os.arch()}
45
+ ${context.command ? `- **Command:** \`${context.command}\`` : ''}
46
+
47
+ ## Additional Context
48
+ ${context.errorMessage ? `### Error\n\`\`\`\n${context.errorMessage}\n\`\`\`` : 'Add any other context here.'}
49
+ `;
50
+
51
+ return `${GITHUB_ISSUES_URL}?title=${issueTitle}&body=${encodeURIComponent(body)}&labels=bug`;
52
+ }
53
+
54
+ /**
55
+ * Submit bug report to API
56
+ */
57
+ async function submitBugReport(data) {
58
+ try {
59
+ const response = await fetch(BUG_API_URL, {
60
+ method: 'POST',
61
+ headers: { 'Content-Type': 'application/json' },
62
+ body: JSON.stringify({
63
+ ...data,
64
+ source: 'cli',
65
+ cliVersion: getVersion(),
66
+ platform: os.platform(),
67
+ arch: os.arch(),
68
+ nodeVersion: process.version,
69
+ }),
70
+ });
71
+
72
+ if (!response.ok) {
73
+ const error = await response.json().catch(() => ({}));
74
+ throw new Error(error.error || `HTTP ${response.status}`);
75
+ }
76
+
77
+ return await response.json();
78
+ } catch (error) {
79
+ throw new Error(`Failed to submit bug report: ${error.message}`);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Open URL in default browser
85
+ */
86
+ function openUrl(url) {
87
+ const { exec } = require('child_process');
88
+ const command = os.platform() === 'darwin' ? 'open' :
89
+ os.platform() === 'win32' ? 'start' : 'xdg-open';
90
+ exec(`${command} "${url}"`);
91
+ }
92
+
93
+ /**
94
+ * Interactive bug report (when no arguments provided)
95
+ */
96
+ async function interactiveBugReport() {
97
+ const readline = require('readline');
98
+ const rl = readline.createInterface({
99
+ input: process.stdin,
100
+ output: process.stdout,
101
+ });
102
+
103
+ const question = (prompt) => new Promise((resolve) => {
104
+ rl.question(prompt, resolve);
105
+ });
106
+
107
+ console.log(ui.info('🐛 Bug Reporter'));
108
+ console.log(ui.dim('Report issues with the Vai CLI\n'));
109
+
110
+ try {
111
+ const title = await question(ui.label('Title', 'Brief description of the bug') + '\n> ');
112
+ if (!title.trim()) {
113
+ console.log(ui.warn('Bug report cancelled.'));
114
+ rl.close();
115
+ return;
116
+ }
117
+
118
+ const description = await question(ui.label('Description', 'What happened?') + '\n> ');
119
+ const steps = await question(ui.label('Steps to Reproduce', 'Optional, press Enter to skip') + '\n> ');
120
+ const email = await question(ui.label('Email', 'Optional, for follow-up') + '\n> ');
121
+
122
+ console.log('');
123
+ const method = await question('Submit to:\n [1] Bug tracker (anonymous)\n [2] GitHub Issues (public)\n [3] Both\n> ');
124
+
125
+ rl.close();
126
+
127
+ const bugData = {
128
+ title: title.trim(),
129
+ description: description.trim(),
130
+ stepsToReproduce: steps.trim() || null,
131
+ email: email.trim() || null,
132
+ };
133
+
134
+ if (method === '1' || method === '3') {
135
+ console.log(ui.dim('\nSubmitting to bug tracker...'));
136
+ try {
137
+ const result = await submitBugReport(bugData);
138
+ console.log(ui.success(`Bug submitted! ID: ${result.bugId}`));
139
+ } catch (error) {
140
+ console.log(ui.error(error.message));
141
+ }
142
+ }
143
+
144
+ if (method === '2' || method === '3') {
145
+ const url = generateGitHubUrl(bugData.title, bugData.description, {
146
+ cliVersion: getVersion(),
147
+ });
148
+ console.log(ui.dim('\nOpening GitHub...'));
149
+ openUrl(url);
150
+ console.log(ui.success('GitHub issue page opened in browser'));
151
+ }
152
+
153
+ if (!['1', '2', '3'].includes(method)) {
154
+ console.log(ui.warn('No submission method selected.'));
155
+ }
156
+
157
+ } catch (error) {
158
+ rl.close();
159
+ console.error(ui.error(`Error: ${error.message}`));
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Main bug command
165
+ */
166
+ async function bugCommand(args, flags) {
167
+ // --github flag: open GitHub issues directly
168
+ if (flags.github || flags.g) {
169
+ const title = args.join(' ');
170
+ const url = generateGitHubUrl(title, '', { cliVersion: getVersion() });
171
+ console.log(ui.info('Opening GitHub Issues...'));
172
+ openUrl(url);
173
+ return;
174
+ }
175
+
176
+ // --quick flag: quick submit with just title
177
+ if (flags.quick || flags.q) {
178
+ const title = args.join(' ');
179
+ if (!title) {
180
+ console.error(ui.error('Please provide a bug title: vai bug --quick "Something broke"'));
181
+ process.exit(1);
182
+ }
183
+
184
+ console.log(ui.dim('Submitting quick bug report...'));
185
+ try {
186
+ const result = await submitBugReport({
187
+ title,
188
+ description: title,
189
+ });
190
+ console.log(ui.success(`Bug submitted! ID: ${result.bugId}`));
191
+ console.log(ui.dim(`Create GitHub issue: ${result.githubIssueUrl}`));
192
+ } catch (error) {
193
+ console.error(ui.error(error.message));
194
+ process.exit(1);
195
+ }
196
+ return;
197
+ }
198
+
199
+ // If title provided as argument, use quick mode
200
+ if (args.length > 0) {
201
+ const title = args.join(' ');
202
+ console.log(ui.dim('Submitting bug report...'));
203
+ try {
204
+ const result = await submitBugReport({
205
+ title,
206
+ description: title,
207
+ });
208
+ console.log(ui.success(`Bug submitted! ID: ${result.bugId}`));
209
+ console.log(ui.dim('To create a GitHub issue with more details:'));
210
+ console.log(ui.dim(` ${result.githubIssueUrl.slice(0, 80)}...`));
211
+ } catch (error) {
212
+ console.error(ui.error(error.message));
213
+ process.exit(1);
214
+ }
215
+ return;
216
+ }
217
+
218
+ // No arguments: interactive mode
219
+ await interactiveBugReport();
220
+ }
221
+
222
+ /**
223
+ * Register the bug command with Commander
224
+ */
225
+ function registerBug(program) {
226
+ program
227
+ .command('bug [title...]')
228
+ .description('Report a bug or issue with the Vai CLI')
229
+ .option('-g, --github', 'Open GitHub Issues in browser')
230
+ .option('-q, --quick', 'Quick submit (title only, no interaction)')
231
+ .action(async (titleParts, options) => {
232
+ sendTelemetry('bug', {
233
+ method: options.github ? 'github' : options.quick ? 'quick' : 'interactive'
234
+ });
235
+
236
+ const args = titleParts || [];
237
+ const flags = {
238
+ github: options.github,
239
+ g: options.github,
240
+ quick: options.quick,
241
+ q: options.quick,
242
+ };
243
+
244
+ await bugCommand(args, flags);
245
+ });
246
+ }
247
+
248
+ module.exports = { registerBug };
249
+ module.exports.bugCommand = bugCommand;
@@ -3,6 +3,32 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
+ /**
7
+ * Safely get the CLI version, handling both development and packaged Electron app.
8
+ * @returns {string} The version string or 'unknown'
9
+ */
10
+ function getCliVersion() {
11
+ // Try multiple paths to find package.json
12
+ const possiblePaths = [
13
+ path.join(__dirname, '..', '..', 'package.json'), // Development: src/lib -> root
14
+ path.join(process.resourcesPath || '', 'cli-package.json'), // Packaged Electron app
15
+ path.join(__dirname, '..', 'package.json'), // Alternative structure
16
+ ];
17
+
18
+ for (const pkgPath of possiblePaths) {
19
+ try {
20
+ if (fs.existsSync(pkgPath)) {
21
+ const pkg = require(pkgPath);
22
+ if (pkg.version) return pkg.version;
23
+ }
24
+ } catch {
25
+ // Try next path
26
+ }
27
+ }
28
+
29
+ return 'unknown';
30
+ }
31
+
6
32
  /**
7
33
  * Lightweight template engine for code generation.
8
34
  *
@@ -295,7 +321,7 @@ function buildContext(project, options = {}) {
295
321
 
296
322
  // Metadata
297
323
  generatedAt: new Date().toISOString(),
298
- vaiVersion: require('../../package.json').version,
324
+ vaiVersion: getCliVersion(),
299
325
  };
300
326
 
301
327
  return context;
@@ -7123,5 +7123,194 @@ init();
7123
7123
  <canvas id="vsiCanvas" width="600" height="500" style="border:1px solid #3D4F58;border-radius:8px;image-rendering:pixelated;"></canvas>
7124
7124
  <div style="color:#889397;font-size:11px;font-family:monospace;text-align:center;">← → move &nbsp;|&nbsp; SPACE shoot &nbsp;|&nbsp; ESC exit</div>
7125
7125
  </div>
7126
+
7127
+ <!-- 🐛 Bug Reporter -->
7128
+ <style>
7129
+ .bug-floating-button{position:fixed;bottom:20px;right:20px;width:48px;height:48px;border-radius:50%;background:linear-gradient(135deg,#ff6b6b,#ee5a5a);border:none;cursor:pointer;font-size:24px;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 12px rgba(238,90,90,0.4);transition:all .2s;z-index:9998}
7130
+ .bug-floating-button:hover{transform:scale(1.1);box-shadow:0 6px 16px rgba(238,90,90,0.5)}
7131
+ .bug-reporter-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:9999}
7132
+ .bug-reporter-modal{background:var(--bg-surface);border-radius:12px;width:90%;max-width:500px;max-height:90vh;overflow-y:auto;box-shadow:0 20px 60px rgba(0,0,0,0.5)}
7133
+ .bug-reporter-header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--border)}
7134
+ .bug-reporter-header h2{flex:1;margin:0;font-size:18px;font-weight:600;color:var(--accent-text)}
7135
+ .bug-reporter-header .close-btn{background:none;border:none;color:var(--text-muted);font-size:24px;cursor:pointer;padding:4px 8px;border-radius:4px}
7136
+ .bug-reporter-header .close-btn:hover{background:rgba(255,255,255,0.1);color:var(--text)}
7137
+ .bug-reporter-form{padding:20px}
7138
+ .bug-reporter-form .form-group{margin-bottom:16px}
7139
+ .bug-reporter-form label{display:block;font-size:13px;font-weight:500;color:var(--text-muted);margin-bottom:6px}
7140
+ .bug-reporter-form input,.bug-reporter-form textarea{width:100%;padding:10px 12px;border:1px solid var(--border);border-radius:8px;background:var(--bg-input);color:var(--text);font-size:14px;font-family:inherit}
7141
+ .bug-reporter-form input:focus,.bug-reporter-form textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}
7142
+ .bug-reporter-form textarea{resize:vertical;min-height:80px}
7143
+ .bug-env-info{padding:12px;background:var(--accent-glow);border-radius:8px;font-size:12px;margin-bottom:16px;color:var(--accent)}
7144
+ .bug-env-info code{color:var(--text-muted);font-size:11px}
7145
+ .bug-error{padding:10px 12px;background:rgba(255,107,107,0.15);border:1px solid rgba(255,107,107,0.3);border-radius:8px;color:var(--error);font-size:13px;margin-bottom:16px}
7146
+ .bug-actions{display:flex;gap:12px}
7147
+ .bug-actions button{flex:1;padding:12px 16px;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s;border:none}
7148
+ .bug-actions .primary{background:linear-gradient(135deg,var(--accent),#00c853);color:#000}
7149
+ .bug-actions .primary:hover{box-shadow:0 4px 12px rgba(0,237,100,0.4)}
7150
+ .bug-actions .primary:disabled{opacity:0.6;cursor:not-allowed}
7151
+ .bug-actions .secondary{background:rgba(255,255,255,0.1);color:var(--text);border:1px solid var(--border)}
7152
+ .bug-actions .secondary:hover{background:rgba(255,255,255,0.15)}
7153
+ .bug-success{padding:40px 20px;text-align:center}
7154
+ .bug-success .icon{width:60px;height:60px;border-radius:50%;background:linear-gradient(135deg,var(--accent),#00c853);color:#000;font-size:32px;display:flex;align-items:center;justify-content:center;margin:0 auto 20px}
7155
+ .bug-success h3{margin:0 0 12px;color:var(--accent-text);font-size:20px}
7156
+ .bug-success p{margin:8px 0;color:var(--text-muted)}
7157
+ .bug-success code{background:rgba(255,255,255,0.1);padding:4px 8px;border-radius:4px;font-size:12px;color:var(--accent)}
7158
+ </style>
7159
+
7160
+ <button class="bug-floating-button" id="bugButton" title="Report a Bug">🐛</button>
7161
+
7162
+ <div class="bug-reporter-overlay" id="bugOverlay" style="display:none">
7163
+ <div class="bug-reporter-modal">
7164
+ <div class="bug-reporter-header">
7165
+ <span style="font-size:28px">🐛</span>
7166
+ <h2>Report a Bug</h2>
7167
+ <button class="close-btn" id="bugClose">×</button>
7168
+ </div>
7169
+ <div class="bug-reporter-form" id="bugForm">
7170
+ <div class="form-group">
7171
+ <label>Title *</label>
7172
+ <input type="text" id="bugTitle" placeholder="Brief description of the bug" maxlength="200">
7173
+ </div>
7174
+ <div class="form-group">
7175
+ <label>Description *</label>
7176
+ <textarea id="bugDescription" placeholder="What happened? What did you expect?" rows="4" maxlength="5000"></textarea>
7177
+ </div>
7178
+ <div class="form-group">
7179
+ <label>Steps to Reproduce</label>
7180
+ <textarea id="bugSteps" placeholder="1. Go to...&#10;2. Click on...&#10;3. See error" rows="3" maxlength="2000"></textarea>
7181
+ </div>
7182
+ <div class="form-group">
7183
+ <label>Email (optional, for follow-up)</label>
7184
+ <input type="email" id="bugEmail" placeholder="your@email.com">
7185
+ </div>
7186
+ <div class="bug-env-info">
7187
+ <span>📋 Environment will be included:</span><br>
7188
+ <code id="bugEnvInfo">Loading...</code>
7189
+ </div>
7190
+ <div class="bug-error" id="bugError" style="display:none"></div>
7191
+ <div class="bug-actions">
7192
+ <button class="primary" id="bugSubmit">Submit Bug Report</button>
7193
+ <button class="secondary" id="bugGithub">Open GitHub Issue</button>
7194
+ </div>
7195
+ </div>
7196
+ <div class="bug-success" id="bugSuccess" style="display:none">
7197
+ <div class="icon">✓</div>
7198
+ <h3>Bug Reported!</h3>
7199
+ <p>Bug ID: <code id="bugResultId"></code></p>
7200
+ <p>Thank you for helping improve Vai!</p>
7201
+ <div class="bug-actions" style="justify-content:center;margin-top:24px">
7202
+ <button class="primary" id="bugSuccessClose">Close</button>
7203
+ </div>
7204
+ </div>
7205
+ </div>
7206
+ </div>
7207
+
7208
+ <script>
7209
+ (function() {
7210
+ const BUG_API = 'https://vai.mlynn.org/api/bugs';
7211
+ const GITHUB_URL = 'https://github.com/mrlynn/voyageai-cli/issues/new';
7212
+
7213
+ function getEnv() {
7214
+ const env = { platform: navigator.platform, source: 'desktop-app' };
7215
+ if (window.electronAPI) {
7216
+ env.appVersion = window.electronAPI.appVersion || 'unknown';
7217
+ env.cliVersion = window.electronAPI.cliVersion || 'unknown';
7218
+ env.electronVersion = window.electronAPI.electronVersion || 'unknown';
7219
+ }
7220
+ env.currentScreen = document.querySelector('.tab-btn.active')?.textContent || 'unknown';
7221
+ return env;
7222
+ }
7223
+
7224
+ function showBugReporter() {
7225
+ const env = getEnv();
7226
+ document.getElementById('bugEnvInfo').textContent =
7227
+ `${env.platform} • App v${env.appVersion || '?'} • ${env.currentScreen}`;
7228
+ document.getElementById('bugOverlay').style.display = 'flex';
7229
+ document.getElementById('bugForm').style.display = 'block';
7230
+ document.getElementById('bugSuccess').style.display = 'none';
7231
+ document.getElementById('bugError').style.display = 'none';
7232
+ document.getElementById('bugTitle').value = '';
7233
+ document.getElementById('bugDescription').value = '';
7234
+ document.getElementById('bugSteps').value = '';
7235
+ document.getElementById('bugEmail').value = '';
7236
+ }
7237
+
7238
+ function hideBugReporter() {
7239
+ document.getElementById('bugOverlay').style.display = 'none';
7240
+ }
7241
+
7242
+ async function submitBug() {
7243
+ const title = document.getElementById('bugTitle').value.trim();
7244
+ const description = document.getElementById('bugDescription').value.trim();
7245
+ const steps = document.getElementById('bugSteps').value.trim();
7246
+ const email = document.getElementById('bugEmail').value.trim();
7247
+
7248
+ if (!title || title.length < 5) {
7249
+ document.getElementById('bugError').textContent = 'Title is required (min 5 characters)';
7250
+ document.getElementById('bugError').style.display = 'block';
7251
+ return;
7252
+ }
7253
+ if (!description || description.length < 10) {
7254
+ document.getElementById('bugError').textContent = 'Description is required (min 10 characters)';
7255
+ document.getElementById('bugError').style.display = 'block';
7256
+ return;
7257
+ }
7258
+
7259
+ document.getElementById('bugSubmit').disabled = true;
7260
+ document.getElementById('bugSubmit').textContent = 'Submitting...';
7261
+ document.getElementById('bugError').style.display = 'none';
7262
+
7263
+ try {
7264
+ const env = getEnv();
7265
+ const res = await fetch(BUG_API, {
7266
+ method: 'POST',
7267
+ headers: { 'Content-Type': 'application/json' },
7268
+ body: JSON.stringify({
7269
+ title, description, stepsToReproduce: steps || null, email: email || null,
7270
+ ...env
7271
+ })
7272
+ });
7273
+
7274
+ if (!res.ok) {
7275
+ const err = await res.json().catch(() => ({}));
7276
+ throw new Error(err.error || `HTTP ${res.status}`);
7277
+ }
7278
+
7279
+ const data = await res.json();
7280
+ document.getElementById('bugResultId').textContent = data.bugId;
7281
+ document.getElementById('bugForm').style.display = 'none';
7282
+ document.getElementById('bugSuccess').style.display = 'block';
7283
+ } catch (err) {
7284
+ document.getElementById('bugError').textContent = err.message;
7285
+ document.getElementById('bugError').style.display = 'block';
7286
+ } finally {
7287
+ document.getElementById('bugSubmit').disabled = false;
7288
+ document.getElementById('bugSubmit').textContent = 'Submit Bug Report';
7289
+ }
7290
+ }
7291
+
7292
+ function openGithub() {
7293
+ const title = document.getElementById('bugTitle').value.trim() || 'Bug Report';
7294
+ const description = document.getElementById('bugDescription').value.trim();
7295
+ const steps = document.getElementById('bugSteps').value.trim();
7296
+ const env = getEnv();
7297
+
7298
+ const body = `## Description\n${description}\n\n## Steps to Reproduce\n${steps || '1. \\n2. \\n3. '}\n\n## Environment\n- **App Version:** ${env.appVersion || 'N/A'}\n- **Platform:** ${env.platform}\n- **Screen:** ${env.currentScreen}`;
7299
+ const url = `${GITHUB_URL}?title=${encodeURIComponent('[Bug] ' + title)}&body=${encodeURIComponent(body)}&labels=bug`;
7300
+ window.open(url, '_blank');
7301
+ }
7302
+
7303
+ // Event listeners
7304
+ document.getElementById('bugButton').addEventListener('click', showBugReporter);
7305
+ document.getElementById('bugClose').addEventListener('click', hideBugReporter);
7306
+ document.getElementById('bugOverlay').addEventListener('click', (e) => {
7307
+ if (e.target.id === 'bugOverlay') hideBugReporter();
7308
+ });
7309
+ document.getElementById('bugSubmit').addEventListener('click', submitBug);
7310
+ document.getElementById('bugGithub').addEventListener('click', openGithub);
7311
+ document.getElementById('bugSuccessClose').addEventListener('click', hideBugReporter);
7312
+ })();
7313
+ </script>
7314
+
7126
7315
  </body>
7127
7316
  </html>