ai-cli 0.0.13 → 0.1.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/README.md +67 -246
- package/package.json +33 -34
- package/src/cli.test.ts +95 -0
- package/src/commands/completions.ts +296 -0
- package/src/commands/image.ts +136 -0
- package/src/commands/models.ts +117 -0
- package/src/commands/text.ts +117 -0
- package/src/commands/video.ts +113 -0
- package/src/index.ts +30 -0
- package/src/lib/color.ts +5 -0
- package/src/lib/h264-wasm.ts +164 -0
- package/src/lib/h264.test.ts +48 -0
- package/src/lib/jobs.ts +192 -0
- package/src/lib/kitty.ts +55 -0
- package/src/lib/models.test.ts +197 -0
- package/src/lib/models.ts +163 -0
- package/src/lib/mp4.test.ts +231 -0
- package/src/lib/mp4.ts +560 -0
- package/src/lib/openh264.d.mts +28 -0
- package/src/lib/openh264.mjs +423 -0
- package/src/lib/openh264.wasm +0 -0
- package/src/lib/openh264.wasm.d.ts +2 -0
- package/src/lib/output.ts +97 -0
- package/src/lib/p-map.test.ts +63 -0
- package/src/lib/p-map.ts +30 -0
- package/src/lib/parse.test.ts +114 -0
- package/src/lib/parse.ts +44 -0
- package/src/lib/png.test.ts +104 -0
- package/src/lib/png.ts +90 -0
- package/src/lib/progress.ts +214 -0
- package/src/lib/shimmer.test.ts +39 -0
- package/src/lib/shimmer.ts +42 -0
- package/src/lib/stdin.ts +31 -0
- package/dist/ai.mjs +0 -630
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# ai
|
|
1
|
+
# ai
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A tiny, agent-native CLI for generating images, video and text with dead-simple commands, stdin support and predictable artifact outputs. Uses [Vercel AI SDK](https://sdk.vercel.ai) and [AI Gateway](https://vercel.com/docs/ai-gateway) for unified access to hundreds of models.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,292 +8,113 @@ Minimal terminal AI assistant.
|
|
|
8
8
|
npm install -g ai-cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
ai init
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Get your API key from [Vercel AI Gateway](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%2Fapi-keys&title=Go+to+AI+Gateway)
|
|
11
|
+
Requires an [AI Gateway](https://vercel.com/docs/ai-gateway) API key or a provider-specific key (e.g. `OPENAI_API_KEY`).
|
|
18
12
|
|
|
19
13
|
## Usage
|
|
20
14
|
|
|
21
15
|
```bash
|
|
22
|
-
ai
|
|
23
|
-
ai "
|
|
24
|
-
ai
|
|
25
|
-
ai
|
|
26
|
-
ai -l # list models
|
|
27
|
-
echo "explain this" | ai # pipe input
|
|
28
|
-
ai --system "respond in Spanish" "hola" # custom system prompt
|
|
29
|
-
|
|
30
|
-
# in interactive mode, ctrl+v to paste image from clipboard
|
|
16
|
+
ai image "a cute dog"
|
|
17
|
+
ai video "a spinning triangle"
|
|
18
|
+
ai text "explain quantum computing"
|
|
19
|
+
ai models # list available models
|
|
31
20
|
```
|
|
32
21
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Run the full agent non-interactively. Useful for CI pipelines, scripts, and automation.
|
|
22
|
+
### Piping
|
|
36
23
|
|
|
37
24
|
```bash
|
|
38
|
-
ai
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
ai -p --no-save "what dependencies are outdated?" # ephemeral (no history)
|
|
42
|
-
git diff | ai -p "review this for bugs" # pipe + headless
|
|
43
|
-
ai -p -m gpt-5 --force "refactor the database layer" # combine flags
|
|
44
|
-
ai -p --plan "how should I refactor auth?" # plan mode (read-only)
|
|
45
|
-
ai -p -r <chatId> "continue" # resume a session
|
|
46
|
-
ai -p --timeout 60 "fix type errors" # abort after 60s
|
|
47
|
-
ai -p -q "explain this codebase" # suppress stderr status
|
|
25
|
+
ai image "a dragon" | ai video "animate this"
|
|
26
|
+
cat notes.txt | ai text "summarize this"
|
|
27
|
+
git diff | ai text "explain these changes"
|
|
48
28
|
```
|
|
49
29
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
**Note:** When `--timeout` fires during a tool execution (e.g., mid-file-write), the agent is interrupted immediately. The workspace may contain partial changes. Combine with version control or review the working tree after a timeout.
|
|
53
|
-
|
|
54
|
-
JSON output format:
|
|
55
|
-
|
|
56
|
-
```json
|
|
57
|
-
{
|
|
58
|
-
"output": "...",
|
|
59
|
-
"model": "anthropic/claude-sonnet-4.5",
|
|
60
|
-
"tokens": 1234,
|
|
61
|
-
"cost": 0.05,
|
|
62
|
-
"exitCode": 0,
|
|
63
|
-
"chatId": "abc123",
|
|
64
|
-
"usage": {
|
|
65
|
-
"inputTokens": 800,
|
|
66
|
-
"outputTokens": 434,
|
|
67
|
-
"cacheReadTokens": 0,
|
|
68
|
-
"cacheWriteTokens": 0,
|
|
69
|
-
"reasoningTokens": 0
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
```
|
|
30
|
+
### Common Options
|
|
73
31
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
## Options
|
|
77
|
-
|
|
78
|
-
- `-m, --model` - model (default: anthropic/claude-sonnet-4.5)
|
|
79
|
-
- `--image` - attach image file
|
|
80
|
-
- `-r, --resume` - resume a previous chat by ID
|
|
81
|
-
- `--plan` - start in plan mode (think before acting)
|
|
82
|
-
- `-p, --print` - headless mode: full agent, output to stdout, then exit
|
|
83
|
-
- `--json` - structured JSON output (implies --print)
|
|
84
|
-
- `--system` - append custom text to the system prompt
|
|
85
|
-
- `--fast` - enable Anthropic fast mode (speed=fast)
|
|
86
|
-
- `--force` - auto-approve all tool actions (--print only)
|
|
87
|
-
- `--no-save` - don't persist the chat to history (--print only)
|
|
88
|
-
- `--timeout` - abort after N seconds (--print only)
|
|
89
|
-
- `-q, --quiet` - suppress stderr status output (--print only)
|
|
90
|
-
- `-l, --list` - list models
|
|
91
|
-
- `--no-color` - disable color output
|
|
92
|
-
- `-v, --version` - show version
|
|
93
|
-
- `-h, --help` - help
|
|
94
|
-
|
|
95
|
-
## Commands
|
|
96
|
-
|
|
97
|
-
### Chat
|
|
98
|
-
- `/new` - new chat
|
|
99
|
-
- `/chats` - list chats
|
|
100
|
-
- `/chat <n>` - load chat
|
|
101
|
-
- `/delete` - delete chat
|
|
102
|
-
- `/clear` - clear screen
|
|
103
|
-
|
|
104
|
-
### Files
|
|
105
|
-
- `/copy` - copy response
|
|
106
|
-
- `/rollback` - undo changes
|
|
107
|
-
|
|
108
|
-
### Context
|
|
109
|
-
- `/usage` - token usage and cost
|
|
110
|
-
- `/compress` - compress history
|
|
111
|
-
- `/plan` - toggle plan mode (think before acting)
|
|
112
|
-
- `/review` - review loop (auto-reviews changes for bugs)
|
|
113
|
-
|
|
114
|
-
### Model
|
|
115
|
-
- `/model` - select model interactively
|
|
116
|
-
- `/model <query>` - switch to matching model
|
|
117
|
-
|
|
118
|
-
### System
|
|
119
|
-
- `/info` - version, model, balance, storage
|
|
120
|
-
- `/processes` - background processes
|
|
121
|
-
- `/memory` - saved memories
|
|
122
|
-
- `/mcp` - mcp servers
|
|
123
|
-
- `/settings` - preferences
|
|
124
|
-
- `/permissions` - tool permission rules
|
|
125
|
-
- `/alias` - shortcuts
|
|
126
|
-
- `/purge` - delete all chats
|
|
127
|
-
- `/help` - commands
|
|
128
|
-
|
|
129
|
-
## Skills
|
|
130
|
-
|
|
131
|
-
Skills extend the AI with specialized capabilities. They follow the [Agent Skills](https://agentskills.io) open standard.
|
|
132
|
-
|
|
133
|
-
### Managing Skills
|
|
32
|
+
All commands support:
|
|
134
33
|
|
|
135
|
-
```bash
|
|
136
|
-
/skills # list installed
|
|
137
|
-
/skills add <url> # install from git
|
|
138
|
-
/skills remove <name> # uninstall
|
|
139
|
-
/skills show <name> # view content
|
|
140
|
-
/skills create <name> # create new
|
|
141
|
-
/skills path # show directory
|
|
142
34
|
```
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
/skills add vercel-labs/agent-skills/skills/react-best-practices
|
|
150
|
-
/skills add anthropics/skills/skills/pdf
|
|
151
|
-
/skills add owner/repo
|
|
35
|
+
-m, --model <id> Model ID (creator/model-name), comma-separated for multi-model
|
|
36
|
+
-o, --output <path> Output file path or directory
|
|
37
|
+
-n, --count <n> Number of generations per model (default: 1)
|
|
38
|
+
-p, --concurrency <n> Max parallel generations (default: 4, video: 2)
|
|
39
|
+
-q, --quiet Suppress progress output
|
|
40
|
+
--json Output metadata as JSON
|
|
152
41
|
```
|
|
153
42
|
|
|
154
|
-
|
|
43
|
+
### image
|
|
155
44
|
|
|
156
|
-
```bash
|
|
157
|
-
/skills add https://github.com/anthropics/skills/tree/main/skills/pdf
|
|
158
45
|
```
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
46
|
+
--size <WxH> Image size (e.g. 1024x1024)
|
|
47
|
+
--aspect-ratio <W:H> Aspect ratio (e.g. 16:9)
|
|
48
|
+
--quality <level> Quality (standard, hd)
|
|
49
|
+
--style <style> Style (vivid, natural)
|
|
50
|
+
--no-preview Disable inline image preview
|
|
164
51
|
```
|
|
165
52
|
|
|
166
|
-
###
|
|
53
|
+
### video
|
|
167
54
|
|
|
168
|
-
```bash
|
|
169
|
-
/skills create my-skill
|
|
170
55
|
```
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
## Rules
|
|
175
|
-
|
|
176
|
-
Custom instructions loaded into every conversation:
|
|
177
|
-
|
|
178
|
-
- `~/.ai-cli/AGENTS.md` - global rules
|
|
179
|
-
- `./AGENTS.md` - project rules
|
|
180
|
-
|
|
181
|
-
Manage with `/rules`:
|
|
182
|
-
|
|
183
|
-
```bash
|
|
184
|
-
/rules show # view rules
|
|
185
|
-
/rules edit # open in editor
|
|
186
|
-
/rules clear # remove rules
|
|
187
|
-
/rules path # show path
|
|
56
|
+
--aspect-ratio <W:H> Aspect ratio (e.g. 16:9)
|
|
57
|
+
--duration <seconds> Duration in seconds
|
|
58
|
+
--no-preview Disable inline video frame preview
|
|
188
59
|
```
|
|
189
60
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
After the coding agent finishes making file changes, a separate review agent automatically inspects all modifications for severe and high-priority bugs. If it finds issues, it fixes them and re-reviews, up to a configurable number of passes.
|
|
193
|
-
|
|
194
|
-
The review agent runs in its own isolated context with a strict system prompt -- it has no attachment to the code it's reviewing and is intentionally more critical than the coding agent.
|
|
61
|
+
### text
|
|
195
62
|
|
|
196
|
-
Enabled by default. Toggle with:
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
/review on # enable
|
|
200
|
-
/review off # disable
|
|
201
|
-
/review # show status
|
|
202
63
|
```
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
{
|
|
208
|
-
"review": {
|
|
209
|
-
"enabled": true,
|
|
210
|
-
"maxIterations": 3
|
|
211
|
-
}
|
|
212
|
-
}
|
|
64
|
+
-f, --format <fmt> Output format: md, txt (default: md)
|
|
65
|
+
-s, --system <prompt> System prompt
|
|
66
|
+
--max-tokens <n> Maximum tokens to generate
|
|
67
|
+
-t, --temperature <n> Temperature (0-2)
|
|
213
68
|
```
|
|
214
69
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
The AI can:
|
|
218
|
-
|
|
219
|
-
**files** - read, write, edit, delete, copy, rename, search
|
|
70
|
+
### models
|
|
220
71
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
72
|
+
```
|
|
73
|
+
--type <type> Filter by type: text, image, video
|
|
74
|
+
--provider <name> Filter by provider (e.g. openai, google)
|
|
75
|
+
--json Output as JSON (includes descriptions)
|
|
76
|
+
```
|
|
224
77
|
|
|
225
|
-
|
|
78
|
+
All model types (text, image, video) are fetched live from the AI Gateway. If the gateway is unreachable, all model types fall back to a built-in list.
|
|
226
79
|
|
|
227
|
-
|
|
80
|
+
### Multi-Model Comparison
|
|
228
81
|
|
|
229
|
-
|
|
82
|
+
Generate with multiple models by comma-separating `-m`:
|
|
230
83
|
|
|
231
84
|
```bash
|
|
232
|
-
|
|
233
|
-
/mcp add weather http https://mcp.example.com
|
|
234
|
-
/mcp add db stdio npx @example/mcp-db
|
|
235
|
-
/mcp remove weather # remove server
|
|
236
|
-
/mcp reload # reconnect all
|
|
85
|
+
ai image "a sunset" -m "openai/gpt-image-1,xai/grok-imagine-image,bfl/flux-2-pro"
|
|
237
86
|
```
|
|
238
87
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
-
|
|
243
|
-
- **stdio** - spawn local process
|
|
244
|
-
|
|
245
|
-
### Config
|
|
246
|
-
|
|
247
|
-
Servers stored in `~/.ai-cli/mcp.json`:
|
|
248
|
-
|
|
249
|
-
```json
|
|
250
|
-
{
|
|
251
|
-
"servers": {
|
|
252
|
-
"weather": {
|
|
253
|
-
"type": "http",
|
|
254
|
-
"url": "https://mcp.example.com"
|
|
255
|
-
},
|
|
256
|
-
"db": {
|
|
257
|
-
"type": "stdio",
|
|
258
|
-
"command": "npx",
|
|
259
|
-
"args": ["@example/mcp-db"]
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
88
|
+
Combine with `-n` to generate multiple per model:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
ai image "a sunset" -n 2 -m "openai/gpt-image-1,bfl/flux-2-pro" # 4 images total
|
|
263
92
|
```
|
|
264
93
|
|
|
265
|
-
|
|
94
|
+
### Inline Preview
|
|
266
95
|
|
|
267
|
-
|
|
96
|
+
When running in a terminal that supports the [Kitty graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) (Kitty, Ghostty, WezTerm, Warp, iTerm2), generated images and videos are displayed inline automatically. Video previews decode an H.264 keyframe from the midpoint of the video using [openh264](https://github.com/cisco/openh264) compiled to WebAssembly — no native dependencies required. Use `--no-preview` to disable this, or set `AI_CLI_PREVIEW=1` to force it on in undetected terminals.
|
|
268
97
|
|
|
269
|
-
|
|
98
|
+
### Output Behavior
|
|
270
99
|
|
|
271
|
-
|
|
100
|
+
- **text**: saves to `output.md` (interactive), stdout when piped
|
|
101
|
+
- **image/video**: saves to file (interactive), raw binary stdout when piped
|
|
102
|
+
- **`-o <dir>`**: saves inside the directory with auto-generated names
|
|
272
103
|
|
|
273
|
-
|
|
274
|
-
ai -m claude-4 # → anthropic/claude-sonnet-4
|
|
275
|
-
ai -m gpt-5 # → openai/gpt-5
|
|
276
|
-
ai -m sonnet # → finds sonnet model
|
|
277
|
-
```
|
|
104
|
+
### Environment Variables
|
|
278
105
|
|
|
279
|
-
|
|
106
|
+
| Variable | Description |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `AI_GATEWAY_API_KEY` | AI Gateway authentication key |
|
|
109
|
+
| `OPENAI_API_KEY` | Provider-specific key (or other provider keys) |
|
|
110
|
+
| `AI_CLI_TEXT_MODEL` | Default text model (overrides `openai/gpt-5.5`) |
|
|
111
|
+
| `AI_CLI_IMAGE_MODEL` | Default image model (overrides `openai/gpt-image-2`) |
|
|
112
|
+
| `AI_CLI_VIDEO_MODEL` | Default video model (overrides `bytedance/seedance-2.0`) |
|
|
113
|
+
| `AI_CLI_OUTPUT_DIR` | Default output directory for generated files |
|
|
114
|
+
| `AI_CLI_PREVIEW` | Set to `1` to force inline image preview, `0` to disable |
|
|
280
115
|
|
|
281
|
-
|
|
116
|
+
The `-m` flag always takes priority over `AI_CLI_*_MODEL` env vars. The `-o` flag always takes priority over `AI_CLI_OUTPUT_DIR`.
|
|
282
117
|
|
|
283
|
-
|
|
284
|
-
~/.ai-cli/
|
|
285
|
-
├── config.json # settings and api key
|
|
286
|
-
├── mcp.json # mcp servers
|
|
287
|
-
├── chats/ # chat history
|
|
288
|
-
├── memories.json # saved memories
|
|
289
|
-
├── skills/ # installed skills
|
|
290
|
-
└── AGENTS.md # global rules
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
## Environment
|
|
294
|
-
|
|
295
|
-
Alternatively set your API key:
|
|
118
|
+
## License
|
|
296
119
|
|
|
297
|
-
|
|
298
|
-
export AI_GATEWAY_API_KEY=your-key
|
|
299
|
-
```
|
|
120
|
+
[Apache-2.0](LICENSE)
|
package/package.json
CHANGED
|
@@ -1,49 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "A tiny, agent-native CLI for generating images, video and text with dead-simple commands, stdin support and predictable artifact outputs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/vercel-labs/ai-cli.git"
|
|
7
10
|
},
|
|
8
|
-
"
|
|
9
|
-
"
|
|
11
|
+
"bin": {
|
|
12
|
+
"ai": "./src/index.ts"
|
|
10
13
|
},
|
|
11
14
|
"files": [
|
|
12
|
-
"
|
|
15
|
+
"src",
|
|
13
16
|
"README.md"
|
|
14
17
|
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ai",
|
|
20
|
+
"cli",
|
|
21
|
+
"generative",
|
|
22
|
+
"image",
|
|
23
|
+
"video",
|
|
24
|
+
"text",
|
|
25
|
+
"ai-sdk",
|
|
26
|
+
"vercel"
|
|
27
|
+
],
|
|
15
28
|
"scripts": {
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"test:coverage": "bun test --coverage tests/*.test.ts",
|
|
19
|
-
"test:e2e": "bun test tests/e2e/",
|
|
20
|
-
"test:evals": "bun test tests/evals/",
|
|
21
|
-
"test:evals:matrix": "bun run tests/evals/run-matrix.ts",
|
|
22
|
-
"lint": "biome lint .",
|
|
23
|
-
"format": "biome format . --write",
|
|
29
|
+
"dev": "bun run src/index.ts",
|
|
30
|
+
"build": "bun build src/index.ts --compile --outfile=dist/ai",
|
|
24
31
|
"typecheck": "tsc --noEmit",
|
|
25
|
-
"format
|
|
26
|
-
"check": "
|
|
27
|
-
"
|
|
32
|
+
"format": "oxfmt --write src/",
|
|
33
|
+
"format:check": "oxfmt --check src/",
|
|
34
|
+
"lint": "oxlint src/",
|
|
35
|
+
"test": "bun test",
|
|
36
|
+
"prepublishOnly": "bun run typecheck"
|
|
28
37
|
},
|
|
29
|
-
"type": "module",
|
|
30
38
|
"dependencies": {
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"@mozilla/readability": "^0.6.0",
|
|
34
|
-
"linkedom": "^0.18.12"
|
|
39
|
+
"ai": "^6.0.173",
|
|
40
|
+
"commander": "^14.0.3"
|
|
35
41
|
},
|
|
36
42
|
"devDependencies": {
|
|
37
|
-
"@
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"ansi-escapes": "^7.3.0",
|
|
42
|
-
"arg": "^5.0.2",
|
|
43
|
-
"esbuild": "^0.25.8",
|
|
44
|
-
"rc9": "^2.1.2",
|
|
45
|
-
"typescript": "^5.8.3",
|
|
46
|
-
"yoctocolors": "^2.1.1",
|
|
47
|
-
"zod": "^4.1.8"
|
|
43
|
+
"@types/bun": "^1.3.13",
|
|
44
|
+
"oxfmt": "^0.47.0",
|
|
45
|
+
"oxlint": "^1.62.0",
|
|
46
|
+
"typescript": "^6.0.3"
|
|
48
47
|
}
|
|
49
48
|
}
|
package/src/cli.test.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
const CLI = ["bun", "run", "src/index.ts"];
|
|
4
|
+
const ROOT = import.meta.dir + "/..";
|
|
5
|
+
|
|
6
|
+
async function run(...args: string[]) {
|
|
7
|
+
const proc = Bun.spawn([...CLI, ...args], {
|
|
8
|
+
cwd: ROOT,
|
|
9
|
+
stdout: "pipe",
|
|
10
|
+
stderr: "pipe",
|
|
11
|
+
stdin: "ignore",
|
|
12
|
+
});
|
|
13
|
+
const [stdout, stderr] = await Promise.all([
|
|
14
|
+
new Response(proc.stdout).text(),
|
|
15
|
+
new Response(proc.stderr).text(),
|
|
16
|
+
]);
|
|
17
|
+
const exitCode = await proc.exited;
|
|
18
|
+
return { exitCode, stdout, stderr };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("cli integration", () => {
|
|
22
|
+
test("--help exits 0 and lists subcommands", async () => {
|
|
23
|
+
const { exitCode, stdout } = await run("--help");
|
|
24
|
+
expect(exitCode).toBe(0);
|
|
25
|
+
for (const sub of ["text", "image", "video", "models", "completions"]) {
|
|
26
|
+
expect(stdout).toContain(sub);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("--version exits 0 and prints semver", async () => {
|
|
31
|
+
const { exitCode, stdout } = await run("--version");
|
|
32
|
+
expect(exitCode).toBe(0);
|
|
33
|
+
expect(stdout.trim()).toMatch(/^\d+\.\d+\.\d+/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("completions zsh exits 0 with valid output", async () => {
|
|
37
|
+
const { exitCode, stdout } = await run("completions", "zsh");
|
|
38
|
+
expect(exitCode).toBe(0);
|
|
39
|
+
expect(stdout).toContain("#compdef ai");
|
|
40
|
+
expect(stdout).toContain("--no-preview");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("completions bash exits 0 with valid output", async () => {
|
|
44
|
+
const { exitCode, stdout } = await run("completions", "bash");
|
|
45
|
+
expect(exitCode).toBe(0);
|
|
46
|
+
expect(stdout).toContain("complete -F");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("completions fish exits 0 with valid output", async () => {
|
|
50
|
+
const { exitCode, stdout } = await run("completions", "fish");
|
|
51
|
+
expect(exitCode).toBe(0);
|
|
52
|
+
expect(stdout).toContain("complete -c ai");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("completions with invalid shell exits 1", async () => {
|
|
56
|
+
const { exitCode, stderr } = await run("completions", "powershell");
|
|
57
|
+
expect(exitCode).toBe(1);
|
|
58
|
+
expect(stderr).toContain("Unknown shell");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("text with no prompt and no stdin exits 1", async () => {
|
|
62
|
+
const { exitCode, stderr } = await run("text");
|
|
63
|
+
expect(exitCode).toBe(1);
|
|
64
|
+
expect(stderr).toContain("prompt is required");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("text --help exits 0 and lists flags", async () => {
|
|
68
|
+
const { exitCode, stdout } = await run("text", "--help");
|
|
69
|
+
expect(exitCode).toBe(0);
|
|
70
|
+
expect(stdout).toContain("--model");
|
|
71
|
+
expect(stdout).toContain("--format");
|
|
72
|
+
expect(stdout).toContain("--temperature");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("image --help exits 0 and lists flags", async () => {
|
|
76
|
+
const { exitCode, stdout } = await run("image", "--help");
|
|
77
|
+
expect(exitCode).toBe(0);
|
|
78
|
+
expect(stdout).toContain("--no-preview");
|
|
79
|
+
expect(stdout).toContain("--size");
|
|
80
|
+
expect(stdout).toContain("--aspect-ratio");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("video --help exits 0 and lists flags", async () => {
|
|
84
|
+
const { exitCode, stdout } = await run("video", "--help");
|
|
85
|
+
expect(exitCode).toBe(0);
|
|
86
|
+
expect(stdout).toContain("--duration");
|
|
87
|
+
expect(stdout).toContain("--aspect-ratio");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("models --type invalid exits 1", async () => {
|
|
91
|
+
const { exitCode, stderr } = await run("models", "--type", "audio");
|
|
92
|
+
expect(exitCode).toBe(1);
|
|
93
|
+
expect(stderr).toContain("must be one of");
|
|
94
|
+
});
|
|
95
|
+
});
|