voyageai-cli 1.21.0 → 1.22.1

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.1",
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;
@@ -1151,6 +1151,175 @@ const concepts = {
1151
1151
  ],
1152
1152
  },
1153
1153
 
1154
+ 'auto-embedding': {
1155
+ title: 'MongoDB Auto-Embedding',
1156
+ summary: 'Automatic vector embedding generation in Atlas Vector Search',
1157
+ content: [
1158
+ `${pc.bold('What is Auto-Embedding?')}`,
1159
+ `${pc.cyan('Auto-Embedding')} is a MongoDB Atlas Vector Search feature (currently in Preview)`,
1160
+ `that automatically generates vector embeddings for text fields using Voyage AI`,
1161
+ `models — no embedding code required.`,
1162
+ ``,
1163
+ `${pc.bold('How it works:')}`,
1164
+ ` ${pc.dim('1.')} Configure your vector search index with ${pc.cyan('autoEmbed')} type`,
1165
+ ` ${pc.dim('2.')} Specify which text field to embed and which Voyage AI model to use`,
1166
+ ` ${pc.dim('3.')} MongoDB automatically generates embeddings when documents are inserted/updated`,
1167
+ ` ${pc.dim('4.')} At query time, pass natural language text — MongoDB embeds it automatically`,
1168
+ ``,
1169
+ `${pc.bold('Supported models:')}`,
1170
+ ` ${pc.cyan('voyage-4-lite')} — High-volume, cost-sensitive applications`,
1171
+ ` ${pc.cyan('voyage-4')} — Balanced performance (recommended)`,
1172
+ ` ${pc.cyan('voyage-4-large')} — Maximum accuracy for complex relationships`,
1173
+ ` ${pc.cyan('voyage-code-3')} — Code search and technical documentation`,
1174
+ ``,
1175
+ `${pc.bold('Index definition example:')}`,
1176
+ ` ${pc.dim('{')}`,
1177
+ ` ${pc.dim('"mappings": {')}`,
1178
+ ` ${pc.dim('"fields": {')}`,
1179
+ ` ${pc.cyan('"summary"')}: ${pc.dim('{')}`,
1180
+ ` ${pc.dim('"type": "')}${pc.cyan('autoEmbed')}${pc.dim('",')}`,
1181
+ ` ${pc.dim('"model": "voyage-4"')}`,
1182
+ ` ${pc.dim('}')}`,
1183
+ ` ${pc.dim('}')}`,
1184
+ ` ${pc.dim('}')}`,
1185
+ ` ${pc.dim('}')}`,
1186
+ ``,
1187
+ `${pc.bold('Query syntax:')} Use ${pc.cyan('query.text')} in $vectorSearch instead of ${pc.cyan('queryVector')}:`,
1188
+ ` ${pc.dim('$vectorSearch: {')}`,
1189
+ ` ${pc.dim('index: "myIndex",')}`,
1190
+ ` ${pc.dim('path: "summary",')}`,
1191
+ ` ${pc.cyan('query: { text: "properties near amusement parks" }')},`,
1192
+ ` ${pc.dim('numCandidates: 100,')}`,
1193
+ ` ${pc.dim('limit: 10')}`,
1194
+ ` ${pc.dim('}')}`,
1195
+ ``,
1196
+ `${pc.bold('API keys:')}`,
1197
+ `Auto-Embedding uses Voyage AI API keys configured during mongot deployment.`,
1198
+ `Best practice: use separate keys for indexing vs. querying to avoid rate limit`,
1199
+ `conflicts. Keys can be created from Atlas (AI Models section) or Voyage AI directly.`,
1200
+ ``,
1201
+ `${pc.bold('Current limitations (Preview):')}`,
1202
+ ` ${pc.dim('•')} ${pc.yellow('Not yet available')} on Atlas clusters (only self-managed Community Edition)`,
1203
+ ` ${pc.dim('•')} Not available on local Atlas deployments via Atlas CLI`,
1204
+ ` ${pc.dim('•')} Not available on MongoDB Enterprise Edition`,
1205
+ ` ${pc.dim('•')} Available via Docker, tarball, package manager, or Kubernetes with 8.2+ CE`,
1206
+ ``,
1207
+ `${pc.bold('When to use Auto-Embedding:')}`,
1208
+ ` ${pc.dim('•')} Simple use cases where you want zero embedding code`,
1209
+ ` ${pc.dim('•')} Single-field text embedding scenarios`,
1210
+ ` ${pc.dim('•')} When your data changes frequently and you want automatic sync`,
1211
+ ` ${pc.dim('•')} Self-managed MongoDB deployments`,
1212
+ ``,
1213
+ `${pc.bold('When to use vai (manual embedding) instead:')}`,
1214
+ ` ${pc.dim('•')} Atlas clusters (auto-embedding not yet available)`,
1215
+ ` ${pc.dim('•')} Custom chunking strategies needed`,
1216
+ ` ${pc.dim('•')} Multi-field or multi-collection embeddings`,
1217
+ ` ${pc.dim('•')} Reranking pipelines (auto-embedding doesn't include reranking)`,
1218
+ ` ${pc.dim('•')} Quantization (int8/binary) for storage optimization`,
1219
+ ` ${pc.dim('•')} Multimodal embeddings (images + text)`,
1220
+ ].join('\n'),
1221
+ links: [
1222
+ 'https://www.mongodb.com/docs/atlas/atlas-vector-search/crud-embeddings/create-embeddings-automatic/',
1223
+ 'https://www.mongodb.com/docs/voyageai/management/api-keys/',
1224
+ ],
1225
+ tryIt: [
1226
+ 'vai explain vai-vs-auto-embedding',
1227
+ 'vai explain vector-search',
1228
+ 'vai models --type embedding',
1229
+ ],
1230
+ },
1231
+
1232
+ 'vai-vs-auto-embedding': {
1233
+ title: 'VAI vs Auto-Embedding — When to Use Each',
1234
+ summary: 'Choosing between manual embedding pipelines and MongoDB auto-embedding',
1235
+ content: [
1236
+ `Both ${pc.cyan('vai')} (manual embedding) and ${pc.cyan('MongoDB Auto-Embedding')} use the same`,
1237
+ `Voyage AI models, but they serve different use cases and deployment scenarios.`,
1238
+ ``,
1239
+ `${pc.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}`,
1240
+ ``,
1241
+ `${pc.bold(pc.cyan('VAI (Manual Embedding Pipeline)'))}`,
1242
+ ``,
1243
+ `You embed text explicitly using ${pc.cyan('vai embed')}, ${pc.cyan('vai pipeline')}, or ${pc.cyan('vai store')},`,
1244
+ `then store the vectors in any database. Full control over every step.`,
1245
+ ``,
1246
+ `${pc.bold('Use vai when:')}`,
1247
+ ` ${pc.green('✓')} Using ${pc.cyan('MongoDB Atlas clusters')} (auto-embedding not available yet)`,
1248
+ ` ${pc.green('✓')} Using ${pc.cyan('any vector database')} (Pinecone, Weaviate, Qdrant, etc.)`,
1249
+ ` ${pc.green('✓')} You need ${pc.cyan('custom chunking')} (sentence, paragraph, semantic, sliding window)`,
1250
+ ` ${pc.green('✓')} You need ${pc.cyan('reranking')} (vai supports two-stage retrieval pipelines)`,
1251
+ ` ${pc.green('✓')} You want ${pc.cyan('quantization')} (int8, binary) for storage optimization`,
1252
+ ` ${pc.green('✓')} You need ${pc.cyan('multimodal embeddings')} (images + text)`,
1253
+ ` ${pc.green('✓')} You need ${pc.cyan('flexible dimensions')} (256, 512, 1024, 2048)`,
1254
+ ` ${pc.green('✓')} You want to ${pc.cyan('mix models')} (embed docs with -large, query with -lite)`,
1255
+ ` ${pc.green('✓')} You need ${pc.cyan('batch processing')} with custom concurrency/rate limiting`,
1256
+ ` ${pc.green('✓')} You're building ${pc.cyan('RAG pipelines')} with custom retrieval logic`,
1257
+ ``,
1258
+ `${pc.dim('Workflow:')}`,
1259
+ ` ${pc.cyan('vai chunk')} → ${pc.cyan('vai embed')} → ${pc.cyan('vai store')} → ${pc.cyan('vai search')} → ${pc.cyan('vai rerank')}`,
1260
+ ``,
1261
+ `${pc.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}`,
1262
+ ``,
1263
+ `${pc.bold(pc.cyan('MongoDB Auto-Embedding'))}`,
1264
+ ``,
1265
+ `MongoDB automatically generates embeddings when you insert/update documents.`,
1266
+ `No embedding code needed — just configure your index and insert data.`,
1267
+ ``,
1268
+ `${pc.bold('Use Auto-Embedding when:')}`,
1269
+ ` ${pc.green('✓')} Using ${pc.cyan('self-managed MongoDB Community Edition')} (8.2+)`,
1270
+ ` ${pc.green('✓')} You want ${pc.cyan('zero embedding code')} — simplest possible setup`,
1271
+ ` ${pc.green('✓')} You're embedding a ${pc.cyan('single text field')} per collection`,
1272
+ ` ${pc.green('✓')} Your data ${pc.cyan('changes frequently')} and you want automatic sync`,
1273
+ ` ${pc.green('✓')} You don't need reranking, quantization, or multimodal`,
1274
+ ` ${pc.green('✓')} Standard chunking is sufficient (or you pre-chunk your data)`,
1275
+ ``,
1276
+ `${pc.dim('Workflow:')}`,
1277
+ ` ${pc.cyan('db.collection.insertOne({text: "..."})')} → embeddings auto-generated`,
1278
+ ` ${pc.cyan('$vectorSearch: {query: {text: "..."}}')} → query auto-embedded`,
1279
+ ``,
1280
+ `${pc.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}`,
1281
+ ``,
1282
+ `${pc.bold('FEATURE COMPARISON')}`,
1283
+ ``,
1284
+ `${pc.dim('Feature vai Auto-Embedding')}`,
1285
+ `${pc.dim('─────────────────────────────────────────────────────────────────')}`,
1286
+ `Atlas clusters ${pc.green('Yes')} ${pc.yellow('Not yet')}`,
1287
+ `Self-managed CE 8.2+ ${pc.green('Yes')} ${pc.green('Yes')}`,
1288
+ `Other vector DBs ${pc.green('Yes')} ${pc.dim('No')}`,
1289
+ `Custom chunking ${pc.green('Yes')} ${pc.dim('No')}`,
1290
+ `Reranking ${pc.green('Yes')} ${pc.dim('No')}`,
1291
+ `Quantization ${pc.green('Yes')} ${pc.dim('No')}`,
1292
+ `Multimodal ${pc.green('Yes')} ${pc.dim('No')}`,
1293
+ `Flexible dimensions ${pc.green('Yes')} ${pc.dim('No')}`,
1294
+ `Mix query/doc models ${pc.green('Yes')} ${pc.dim('No')}`,
1295
+ `Auto-sync on update ${pc.dim('Manual')} ${pc.green('Yes')}`,
1296
+ `Zero code setup ${pc.dim('No')} ${pc.green('Yes')}`,
1297
+ ``,
1298
+ `${pc.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')}`,
1299
+ ``,
1300
+ `${pc.bold('RECOMMENDATION')}`,
1301
+ ``,
1302
+ `${pc.dim('•')} For ${pc.cyan('Atlas users')}: Use vai — auto-embedding isn't available yet`,
1303
+ `${pc.dim('•')} For ${pc.cyan('production RAG')}: Use vai — you'll want reranking and custom chunking`,
1304
+ `${pc.dim('•')} For ${pc.cyan('quick prototypes')} on self-managed CE: Auto-embedding is faster to set up`,
1305
+ `${pc.dim('•')} For ${pc.cyan('complex pipelines')}: vai gives you full control over every step`,
1306
+ ``,
1307
+ `${pc.bold('Migration path:')} Start with auto-embedding for simplicity, then migrate to`,
1308
+ `vai when you need advanced features. The models are the same — your embeddings`,
1309
+ `will be compatible.`,
1310
+ ].join('\n'),
1311
+ links: [
1312
+ 'https://www.mongodb.com/docs/atlas/atlas-vector-search/crud-embeddings/create-embeddings-automatic/',
1313
+ 'https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/',
1314
+ ],
1315
+ tryIt: [
1316
+ 'vai explain auto-embedding',
1317
+ 'vai pipeline --help',
1318
+ 'vai chunk --help',
1319
+ 'vai rerank --help',
1320
+ ],
1321
+ },
1322
+
1154
1323
  'eval-comparison': {
1155
1324
  title: 'Evaluation Comparison — vai eval compare',
1156
1325
  summary: 'Compare configurations and track quality over time',
@@ -1320,6 +1489,23 @@ const aliases = {
1320
1489
  'save-results': 'eval-comparison',
1321
1490
  'a-b-test': 'eval-comparison',
1322
1491
  regression: 'eval-comparison',
1492
+ // Auto-embedding aliases
1493
+ 'auto-embedding': 'auto-embedding',
1494
+ 'auto-embed': 'auto-embedding',
1495
+ autoembed: 'auto-embedding',
1496
+ 'autoEmbed': 'auto-embedding',
1497
+ 'automatic-embedding': 'auto-embedding',
1498
+ 'automatic-embeddings': 'auto-embedding',
1499
+ 'atlas-auto-embed': 'auto-embedding',
1500
+ 'mongodb-auto-embedding': 'auto-embedding',
1501
+ 'zero-code': 'auto-embedding',
1502
+ // VAI vs Auto-embedding aliases
1503
+ 'vai-vs-auto-embedding': 'vai-vs-auto-embedding',
1504
+ 'vai-vs-autoembedding': 'vai-vs-auto-embedding',
1505
+ 'manual-vs-auto': 'vai-vs-auto-embedding',
1506
+ 'auto-vs-manual': 'vai-vs-auto-embedding',
1507
+ 'which-approach': 'vai-vs-auto-embedding',
1508
+ 'embedding-approach': 'vai-vs-auto-embedding',
1323
1509
  // Provider comparison aliases
1324
1510
  'provider-comparison': 'provider-comparison',
1325
1511
  providers: 'provider-comparison',
@@ -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>