voyageai-cli 1.20.6 → 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/CHANGELOG.md +142 -26
- package/README.md +130 -2
- package/package.json +3 -2
- package/src/cli.js +10 -0
- package/src/commands/bug.js +249 -0
- package/src/commands/eval.js +420 -10
- package/src/commands/generate.js +220 -0
- package/src/commands/playground.js +93 -0
- package/src/commands/purge.js +271 -0
- package/src/commands/refresh.js +322 -0
- package/src/commands/scaffold.js +217 -0
- package/src/lib/codegen.js +339 -0
- package/src/lib/explanations.js +155 -0
- package/src/lib/scaffold-structure.js +114 -0
- package/src/lib/templates/nextjs/README.md.tpl +106 -0
- package/src/lib/templates/nextjs/env.example.tpl +8 -0
- package/src/lib/templates/nextjs/layout.jsx.tpl +29 -0
- package/src/lib/templates/nextjs/lib-mongo.js.tpl +111 -0
- package/src/lib/templates/nextjs/lib-voyage.js.tpl +103 -0
- package/src/lib/templates/nextjs/package.json.tpl +33 -0
- package/src/lib/templates/nextjs/page-search.jsx.tpl +147 -0
- package/src/lib/templates/nextjs/route-ingest.js.tpl +114 -0
- package/src/lib/templates/nextjs/route-search.js.tpl +97 -0
- package/src/lib/templates/nextjs/theme.js.tpl +84 -0
- package/src/lib/templates/python/README.md.tpl +145 -0
- package/src/lib/templates/python/app.py.tpl +221 -0
- package/src/lib/templates/python/chunker.py.tpl +127 -0
- package/src/lib/templates/python/env.example.tpl +12 -0
- package/src/lib/templates/python/mongo_client.py.tpl +125 -0
- package/src/lib/templates/python/requirements.txt.tpl +10 -0
- package/src/lib/templates/python/voyage_client.py.tpl +124 -0
- package/src/lib/templates/vanilla/README.md.tpl +156 -0
- package/src/lib/templates/vanilla/client.js.tpl +103 -0
- package/src/lib/templates/vanilla/connection.js.tpl +126 -0
- package/src/lib/templates/vanilla/env.example.tpl +11 -0
- package/src/lib/templates/vanilla/ingest.js.tpl +231 -0
- package/src/lib/templates/vanilla/package.json.tpl +31 -0
- package/src/lib/templates/vanilla/retrieval.js.tpl +100 -0
- package/src/lib/templates/vanilla/search-api.js.tpl +175 -0
- package/src/lib/templates/vanilla/server.js.tpl +81 -0
- package/src/lib/zip.js +130 -0
- package/src/playground/index.html +708 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,49 +4,165 @@ All notable changes to voyageai-cli are documented here.
|
|
|
4
4
|
|
|
5
5
|
Format based on [Keep a Changelog](https://keepachangelog.com/).
|
|
6
6
|
|
|
7
|
-
## [
|
|
7
|
+
## [1.21.0] - 2026-02-10
|
|
8
8
|
|
|
9
9
|
### Added
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
10
|
+
- **`vai generate`** — Emit production code snippets for RAG applications
|
|
11
|
+
- Components: client, connection, retrieval, ingest, search-api
|
|
12
|
+
- Targets: Node.js/Express, Next.js/MUI, Python/Flask
|
|
13
|
+
- Auto-detects target from project files
|
|
14
|
+
- Reads configuration from `.vai.json`
|
|
15
|
+
- **`vai scaffold`** — Create complete starter projects
|
|
16
|
+
- Vanilla (9 files), Next.js (13 files), Python (8 files)
|
|
17
|
+
- Includes server, API routes, client, connection, retrieval, ingest
|
|
18
|
+
- `.env.example` and README with setup instructions
|
|
19
|
+
- **`vai purge`** — Remove embeddings from MongoDB by criteria
|
|
20
|
+
- Filter by `--source`, `--before`, `--model`, `--stale`
|
|
21
|
+
- `--dry-run` for safe preview before deletion
|
|
22
|
+
- **`vai refresh`** — Re-embed documents with new model/settings
|
|
23
|
+
- `--model` and `--dimensions` for model upgrades
|
|
24
|
+
- `--rechunk` to re-chunk before re-embedding
|
|
25
|
+
- Batch processing with progress reporting
|
|
26
|
+
- **`vai eval compare`** — Compare multiple configurations side-by-side
|
|
27
|
+
- **`vai eval --save/--baseline`** — Track quality metrics over time
|
|
28
|
+
- **`vai init`** — Modernized with @clack/prompts and back navigation
|
|
29
|
+
- **Desktop App: Check for Updates** — Menu item in app menu (macOS) / Help menu (Windows/Linux)
|
|
30
|
+
- **Desktop App: Generate tab** — Code generation and scaffold UI
|
|
31
|
+
- Electron: Create projects on disk with native file dialog
|
|
32
|
+
- Web: Download as ZIP file
|
|
33
|
+
- **Playground: ZIP scaffold** — `/api/scaffold` endpoint for web mode
|
|
34
|
+
- **Onboarding walkthrough** — Updated to 9 steps covering all tabs
|
|
35
|
+
- **Sidebar reorganized** — Tools (Embed, Compare, Search, Multimodal, Generate) / Learn (Benchmark, Explore, About)
|
|
36
|
+
- 3 new explanation topics: `code-generation`, `scaffolding`, `eval-comparison`
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- Template engine (`src/lib/codegen.js`) supports `{{var}}`, `{{#if}}`, `{{#each}}` — no external deps
|
|
40
|
+
- 390 tests (up from 360)
|
|
41
|
+
|
|
42
|
+
## [1.20.0] - 2026-02-04
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
- **Desktop App** — Signed and notarized macOS DMG via electron-builder
|
|
46
|
+
- Auto-update via electron-updater
|
|
47
|
+
- Settings cog in header for API key management
|
|
48
|
+
- OS keychain encryption for stored credentials
|
|
49
|
+
- **Multimodal tab** — Image ↔ text similarity, cross-modal gallery search
|
|
50
|
+
- **Vector Space Invaders** — Easter egg (Konami code or 7x logo click)
|
|
51
|
+
- Anonymous telemetry to vai.mlynn.org (opt-out available)
|
|
52
|
+
- 4 new multimodal explanation topics
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
- CLI npm package optimized with `files` whitelist (327KB, was 550MB)
|
|
56
|
+
|
|
57
|
+
## [1.19.0] - 2026-02-03
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
- **`vai benchmark`** — 8 subcommands for model comparison
|
|
61
|
+
- `embed`, `rerank`, `similarity`, `cost`, `batch`, `asymmetric`, `quantization`, `space`
|
|
62
|
+
- Latency (p50/p95), throughput, cost per million tokens
|
|
63
|
+
- `--save` results to JSON
|
|
64
|
+
- **`vai estimate`** — Cost calculator for symmetric vs asymmetric retrieval
|
|
65
|
+
- Marketing site vai.mlynn.org deployed
|
|
66
|
+
|
|
67
|
+
## [1.18.0] - 2026-02-02
|
|
68
|
+
|
|
69
|
+
### Added
|
|
70
|
+
- **`vai eval`** — Evaluate retrieval quality with MRR, nDCG, Recall@K, Precision@K
|
|
71
|
+
- Supports retrieval and rerank modes
|
|
72
|
+
- Custom K values and test sets
|
|
73
|
+
- **`vai query`** — Two-stage retrieval (vector search + rerank)
|
|
74
|
+
- **`vai pipeline`** — End-to-end chunk → embed → store
|
|
75
|
+
- `--create-index` flag for automatic index creation
|
|
76
|
+
- Progress reporting on stderr
|
|
77
|
+
- **`vai chunk`** — 5 chunking strategies (fixed, sentence, paragraph, recursive, markdown)
|
|
78
|
+
- **`vai init`** — Initialize project with `.vai.json` configuration
|
|
79
|
+
|
|
80
|
+
## [1.17.0] - 2026-02-01
|
|
81
|
+
|
|
82
|
+
### Added
|
|
83
|
+
- **Playground** — Web UI at `vai playground`
|
|
84
|
+
- Embed, Compare, Search, Benchmark tabs
|
|
85
|
+
- Model selector, dark/light theme toggle
|
|
86
|
+
- Real-time similarity heatmap
|
|
87
|
+
- **`vai explain`** — 22 interactive concept explainers
|
|
88
|
+
- Topics: embeddings, reranking, vector-search, RAG, cosine-similarity, etc.
|
|
89
|
+
- Alias resolution for common terms
|
|
90
|
+
- Links and "try it" commands for each topic
|
|
91
|
+
- **`vai app`** — Launch Electron desktop app
|
|
92
|
+
- **`vai completions`** — Shell completions for bash and zsh
|
|
93
|
+
- **`vai about`** — Version and system info
|
|
94
|
+
|
|
95
|
+
## [1.15.0] - 2026-01-30
|
|
96
|
+
|
|
97
|
+
### Added
|
|
98
|
+
- **voyage-4 model family** support
|
|
99
|
+
- voyage-4-large, voyage-4, voyage-4-lite
|
|
100
|
+
- Shared embedding space for asymmetric retrieval
|
|
101
|
+
- MoE architecture detection
|
|
102
|
+
- **Quantization support** — `--output-dtype` for int8/ubinary embeddings
|
|
103
|
+
- **Dimensions parameter** — `--dimensions` for reduced output dimensions
|
|
104
|
+
|
|
105
|
+
### Changed
|
|
106
|
+
- Default model updated to voyage-4-lite
|
|
107
|
+
- MODEL_CATALOG expanded with architecture info and benchmark scores
|
|
108
|
+
|
|
109
|
+
## [1.10.0] - 2026-01-25
|
|
110
|
+
|
|
111
|
+
### Added
|
|
112
|
+
- **`vai ingest`** — Bulk import from JSONL/JSON/CSV/text
|
|
113
|
+
- Batching with configurable batch size
|
|
114
|
+
- Progress bar and dry-run mode
|
|
115
|
+
- Token estimation and cost preview
|
|
116
|
+
- **`vai similarity`** — Compute cosine similarity without MongoDB
|
|
117
|
+
- **`vai demo`** — Interactive guided walkthrough
|
|
13
118
|
- ASCII banner when running `vai` with no arguments
|
|
14
|
-
- CONTRIBUTING.md for open-source contributors
|
|
15
|
-
- This changelog
|
|
16
119
|
|
|
17
|
-
|
|
120
|
+
### Changed
|
|
121
|
+
- Improved error messages with actionable hints
|
|
122
|
+
- Rate limit retry with exponential backoff (up to 3 attempts)
|
|
123
|
+
|
|
124
|
+
## [1.5.0] - 2026-01-20
|
|
125
|
+
|
|
126
|
+
### Added
|
|
127
|
+
- **PDF support** — Optional pdf-parse dependency for `vai pipeline`
|
|
128
|
+
- **HTML stripping** — Automatic for .html files
|
|
129
|
+
- **Readers module** — Unified file reading with type detection
|
|
130
|
+
- **Directory scanning** — Recursive with extension filtering
|
|
131
|
+
|
|
132
|
+
### Fixed
|
|
133
|
+
- JSONL parsing edge cases
|
|
134
|
+
- Empty chunk handling
|
|
135
|
+
|
|
136
|
+
## [1.1.0] - 2026-01-15
|
|
18
137
|
|
|
19
138
|
### Added
|
|
20
|
-
-
|
|
139
|
+
- **`vai config`** — Persistent config management (`~/.vai/config.json`)
|
|
21
140
|
- `set`, `get`, `delete`, `path`, `reset` subcommands
|
|
22
141
|
- Secrets masked in output, config file chmod 600
|
|
23
|
-
- `--stdin` flag for secure key input
|
|
24
|
-
-
|
|
142
|
+
- `--stdin` flag for secure key input
|
|
143
|
+
- **`vai ping`** — Test API and MongoDB connectivity
|
|
25
144
|
- `.env` file support via dotenv
|
|
26
|
-
- Colored output with picocolors
|
|
27
|
-
- Animated spinners on
|
|
28
|
-
- npm update notifier (
|
|
145
|
+
- Colored output with picocolors
|
|
146
|
+
- Animated spinners on network operations
|
|
147
|
+
- npm update notifier (daily, non-blocking)
|
|
29
148
|
- GitHub Actions CI (Node 18, 20, 22)
|
|
30
|
-
- README badges (CI, npm, license, node version)
|
|
31
|
-
- Credential priority chain: env var → .env → config file
|
|
32
|
-
- Security documentation in README
|
|
33
149
|
|
|
34
150
|
### Fixed
|
|
35
|
-
- `
|
|
36
|
-
- `
|
|
37
|
-
- `index create` parseInt handling for dimensions (was producing NaN)
|
|
151
|
+
- `rerank` endpoint corrected to `/v1/rerank`
|
|
152
|
+
- `index create` parseInt handling for dimensions
|
|
38
153
|
|
|
39
|
-
## [1.0.0] - 2026-
|
|
154
|
+
## [1.0.0] - 2026-01-10
|
|
40
155
|
|
|
41
156
|
### Added
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
157
|
+
- **`vai embed`** — Generate embeddings (text, file, stdin, bulk)
|
|
158
|
+
- **`vai rerank`** — Rerank documents with relevance scoring
|
|
159
|
+
- **`vai store`** — Embed and insert into MongoDB Atlas
|
|
160
|
+
- **`vai search`** — $vectorSearch with pre-filter support
|
|
161
|
+
- **`vai index`** — Create, list, delete Atlas Vector Search indexes
|
|
162
|
+
- **`vai models`** — List available Voyage AI models with pricing
|
|
48
163
|
- REST API integration with `https://ai.mongodb.com/v1/`
|
|
49
164
|
- MongoDB Atlas Vector Search integration
|
|
50
165
|
- API retry on 429 with exponential backoff
|
|
51
166
|
- `--json` and `--quiet` flags on all commands
|
|
52
167
|
- 50+ unit tests
|
|
168
|
+
- MIT license
|
package/README.md
CHANGED
|
@@ -196,6 +196,91 @@ Creates `.vai.json` with your defaults — model, database, collection, chunking
|
|
|
196
196
|
}
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
+
### Code Generation & Scaffolding
|
|
200
|
+
|
|
201
|
+
#### `vai generate` — Production code snippets
|
|
202
|
+
|
|
203
|
+
Generate ready-to-use code from your `.vai.json` config:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# List available components
|
|
207
|
+
vai generate --list
|
|
208
|
+
|
|
209
|
+
# Generate and pipe to files
|
|
210
|
+
vai generate client > lib/voyage.js
|
|
211
|
+
vai generate retrieval > lib/retrieval.js
|
|
212
|
+
vai generate search-api > routes/search.js
|
|
213
|
+
|
|
214
|
+
# Different targets
|
|
215
|
+
vai generate client --target python # Flask
|
|
216
|
+
vai generate retrieval --target nextjs # Next.js + MUI
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Components: `client`, `connection`, `retrieval`, `ingest`, `search-api`
|
|
220
|
+
|
|
221
|
+
Targets: `vanilla` (Node.js/Express), `nextjs` (Next.js + MUI), `python` (Flask)
|
|
222
|
+
|
|
223
|
+
#### `vai scaffold` — Complete starter projects
|
|
224
|
+
|
|
225
|
+
Create a full project directory with all files pre-configured:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# Node.js + Express API (9 files)
|
|
229
|
+
vai scaffold my-rag-api
|
|
230
|
+
|
|
231
|
+
# Next.js + Material UI (13 files)
|
|
232
|
+
vai scaffold my-app --target nextjs
|
|
233
|
+
|
|
234
|
+
# Python + Flask (8 files)
|
|
235
|
+
vai scaffold flask-api --target python
|
|
236
|
+
|
|
237
|
+
# Preview without creating files
|
|
238
|
+
vai scaffold my-app --dry-run
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Each project includes: server, API routes, Voyage AI client, MongoDB connection, retrieval module, ingestion pipeline, `.env.example`, and README.
|
|
242
|
+
|
|
243
|
+
### Data Lifecycle
|
|
244
|
+
|
|
245
|
+
#### `vai purge` — Remove stale embeddings
|
|
246
|
+
|
|
247
|
+
Remove embeddings from MongoDB based on criteria:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# Remove docs embedded with an old model
|
|
251
|
+
vai purge --model voyage-3.5
|
|
252
|
+
|
|
253
|
+
# Remove docs whose source files no longer exist
|
|
254
|
+
vai purge --stale
|
|
255
|
+
|
|
256
|
+
# Remove docs older than a date
|
|
257
|
+
vai purge --before 2026-01-01
|
|
258
|
+
|
|
259
|
+
# Filter by source pattern
|
|
260
|
+
vai purge --source "docs/old/*.md"
|
|
261
|
+
|
|
262
|
+
# Preview before deleting
|
|
263
|
+
vai purge --model voyage-3.5 --dry-run
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
#### `vai refresh` — Re-embed with new settings
|
|
267
|
+
|
|
268
|
+
Re-embed documents in-place with a new model, dimensions, or chunk settings:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# Upgrade to a new model
|
|
272
|
+
vai refresh --model voyage-4-large
|
|
273
|
+
|
|
274
|
+
# Change dimensions for cost savings
|
|
275
|
+
vai refresh --model voyage-4-large --dimensions 256
|
|
276
|
+
|
|
277
|
+
# Re-chunk with a better strategy, then re-embed
|
|
278
|
+
vai refresh --rechunk --strategy markdown --chunk-size 1024
|
|
279
|
+
|
|
280
|
+
# Preview what would change
|
|
281
|
+
vai refresh --model voyage-4-large --dry-run
|
|
282
|
+
```
|
|
283
|
+
|
|
199
284
|
### Core Workflow
|
|
200
285
|
|
|
201
286
|
#### `vai pipeline` — Chunk → embed → store
|
|
@@ -345,6 +430,37 @@ vai benchmark quantization --model voyage-4-large --dtypes float,int8,ubinary
|
|
|
345
430
|
vai benchmark cost --tokens 500 --volumes 100,1000,10000,100000
|
|
346
431
|
```
|
|
347
432
|
|
|
433
|
+
### Evaluation
|
|
434
|
+
|
|
435
|
+
Measure and compare your retrieval quality:
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
# Evaluate retrieval pipeline
|
|
439
|
+
vai eval --test-set test.jsonl --db myapp --collection docs
|
|
440
|
+
|
|
441
|
+
# Save results for later comparison
|
|
442
|
+
vai eval --test-set test.jsonl --save baseline.json
|
|
443
|
+
|
|
444
|
+
# Compare against a baseline (shows deltas)
|
|
445
|
+
vai eval --test-set test.jsonl --baseline baseline.json
|
|
446
|
+
|
|
447
|
+
# Compare multiple configurations
|
|
448
|
+
vai eval compare --test-set test.jsonl --configs baseline.json,experiment.json
|
|
449
|
+
|
|
450
|
+
# Evaluate reranking in isolation
|
|
451
|
+
vai eval --mode rerank --test-set rerank-test.jsonl
|
|
452
|
+
|
|
453
|
+
# Compare rerank models
|
|
454
|
+
vai eval --mode rerank --models "rerank-2.5,rerank-2.5-lite" --test-set test.jsonl
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
**Metrics:** MRR, nDCG@K, Recall@K, MAP, Precision@K
|
|
458
|
+
|
|
459
|
+
**Test set format (JSONL):**
|
|
460
|
+
```json
|
|
461
|
+
{"query": "What is vector search?", "relevant": ["doc_id_1", "doc_id_2"]}
|
|
462
|
+
```
|
|
463
|
+
|
|
348
464
|
### Learn
|
|
349
465
|
|
|
350
466
|
Interactive explanations of key concepts:
|
|
@@ -394,21 +510,33 @@ Covers all 22 commands, subcommands, flags, model names, and explain topics.
|
|
|
394
510
|
|
|
395
511
|
| Command | Description |
|
|
396
512
|
|---------|-------------|
|
|
513
|
+
| **Project Setup** | |
|
|
397
514
|
| `vai init` | Initialize project with `.vai.json` |
|
|
515
|
+
| `vai generate` | Generate code snippets (retrieval, ingest, client) |
|
|
516
|
+
| `vai scaffold` | Create complete starter projects |
|
|
517
|
+
| **RAG Pipeline** | |
|
|
398
518
|
| `vai pipeline` | Chunk → embed → store (end-to-end) |
|
|
399
519
|
| `vai query` | Search + rerank (two-stage retrieval) |
|
|
400
520
|
| `vai chunk` | Chunk documents (5 strategies) |
|
|
401
521
|
| `vai estimate` | Cost estimator (symmetric vs asymmetric) |
|
|
522
|
+
| **Embeddings** | |
|
|
402
523
|
| `vai embed` | Generate embeddings |
|
|
403
524
|
| `vai rerank` | Rerank documents by relevance |
|
|
404
525
|
| `vai similarity` | Compare text similarity |
|
|
526
|
+
| **Data Management** | |
|
|
405
527
|
| `vai store` | Embed and store single documents |
|
|
406
528
|
| `vai ingest` | Bulk import with progress |
|
|
407
529
|
| `vai search` | Vector similarity search |
|
|
408
530
|
| `vai index` | Manage Atlas Vector Search indexes |
|
|
409
|
-
| `vai
|
|
531
|
+
| `vai purge` | Remove embeddings by criteria |
|
|
532
|
+
| `vai refresh` | Re-embed with new model/settings |
|
|
533
|
+
| **Evaluation** | |
|
|
534
|
+
| `vai eval` | Evaluate retrieval quality (MRR, nDCG, Recall) |
|
|
535
|
+
| `vai eval compare` | Compare configurations side-by-side |
|
|
410
536
|
| `vai benchmark` | 8 subcommands for model comparison |
|
|
411
|
-
|
|
|
537
|
+
| **Tools & Learning** | |
|
|
538
|
+
| `vai models` | List models, benchmarks, architecture |
|
|
539
|
+
| `vai explain` | 25 interactive concept explainers |
|
|
412
540
|
| `vai config` | Manage persistent configuration |
|
|
413
541
|
| `vai ping` | Test API and MongoDB connectivity |
|
|
414
542
|
| `vai playground` | Interactive web playground |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "voyageai-cli",
|
|
3
|
-
"version": "1.
|
|
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": ">=
|
|
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
|
@@ -26,10 +26,15 @@ const { registerChunk } = require('./commands/chunk');
|
|
|
26
26
|
const { registerQuery } = require('./commands/query');
|
|
27
27
|
const { registerPipeline } = require('./commands/pipeline');
|
|
28
28
|
const { registerEval } = require('./commands/eval');
|
|
29
|
+
const { registerGenerate } = require('./commands/generate');
|
|
30
|
+
const { registerScaffold } = require('./commands/scaffold');
|
|
31
|
+
const { register: registerPurge } = require('./commands/purge');
|
|
32
|
+
const { register: registerRefresh } = require('./commands/refresh');
|
|
29
33
|
const { registerApp } = require('./commands/app');
|
|
30
34
|
const { registerAbout } = require('./commands/about');
|
|
31
35
|
const { register: registerDoctor } = require('./commands/doctor');
|
|
32
36
|
const { register: registerQuickstart } = require('./commands/quickstart');
|
|
37
|
+
const { registerBug } = require('./commands/bug');
|
|
33
38
|
const { showBanner, showQuickStart, getVersion } = require('./lib/banner');
|
|
34
39
|
|
|
35
40
|
const version = getVersion();
|
|
@@ -60,10 +65,15 @@ registerChunk(program);
|
|
|
60
65
|
registerQuery(program);
|
|
61
66
|
registerPipeline(program);
|
|
62
67
|
registerEval(program);
|
|
68
|
+
registerGenerate(program);
|
|
69
|
+
registerScaffold(program);
|
|
70
|
+
registerPurge(program);
|
|
71
|
+
registerRefresh(program);
|
|
63
72
|
registerApp(program);
|
|
64
73
|
registerAbout(program);
|
|
65
74
|
registerDoctor(program);
|
|
66
75
|
registerQuickstart(program);
|
|
76
|
+
registerBug(program);
|
|
67
77
|
|
|
68
78
|
// Append disclaimer to all help output
|
|
69
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;
|