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 CHANGED
@@ -1,6 +1,6 @@
1
- # ai-cli
1
+ # ai
2
2
 
3
- Minimal terminal AI assistant.
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
- ## Setup
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 # interactive mode
23
- ai "hello" # single message
24
- ai -m gpt-5 "hello" # use specific model
25
- ai --image ./img.png "what?" # analyze image (single message)
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
- ## Headless Mode
34
-
35
- Run the full agent non-interactively. Useful for CI pipelines, scripts, and automation.
22
+ ### Piping
36
23
 
37
24
  ```bash
38
- ai -p "explain this codebase" # output to stdout
39
- ai -p --json "write tests for src/auth.ts" > result.json # structured JSON
40
- ai -p --force "fix all type errors" # skip confirmations
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
- Exit codes: `0` success, `1` error, `2` agent stuck.
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
- On error, includes an `error` field with the message.
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
- ### Installing Skills
145
-
146
- Shorthand (like skills.sh):
147
-
148
- ```bash
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
- Full GitHub URL:
43
+ ### image
155
44
 
156
- ```bash
157
- /skills add https://github.com/anthropics/skills/tree/main/skills/pdf
158
45
  ```
159
-
160
- Local path:
161
-
162
- ```bash
163
- /skills add /path/to/skill
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
- ### Creating Skills
53
+ ### video
167
54
 
168
- ```bash
169
- /skills create my-skill
170
55
  ```
171
-
172
- Creates `~/.ai-cli/skills/my-skill/SKILL.md`
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
- ## Review Loop
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
- Configure max iterations in `~/.ai-cli/config.json`:
205
-
206
- ```json
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
- ## Tools
216
-
217
- The AI can:
218
-
219
- **files** - read, write, edit, delete, copy, rename, search
70
+ ### models
220
71
 
221
- **commands** - run shell commands, background processes
222
-
223
- **memory** - save facts across sessions ("remember X")
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
- **web** - search, fetch urls, check weather
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
- ## MCP
80
+ ### Multi-Model Comparison
228
81
 
229
- Connect to external tools via [Model Context Protocol](https://modelcontextprotocol.io):
82
+ Generate with multiple models by comma-separating `-m`:
230
83
 
231
84
  ```bash
232
- /mcp # list servers
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
- ### Transports
240
-
241
- - **http** - HTTP endpoint
242
- - **sse** - server-sent events
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
- Environment variables expand with `${VAR}` or `${VAR:-default}`.
94
+ ### Inline Preview
266
95
 
267
- MCP tools are prefixed with server name (e.g., `weather_get_forecast`).
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
- ## Models
98
+ ### Output Behavior
270
99
 
271
- Supports fuzzy matching:
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
- ```bash
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
- ## Storage
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
- All data in `~/.ai-cli/`:
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
- ```bash
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.0.13",
4
- "main": "dist/ai.mjs",
5
- "bin": {
6
- "ai": "dist/ai.mjs"
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
- "engines": {
9
- "node": ">=18.0.0"
11
+ "bin": {
12
+ "ai": "./src/index.ts"
10
13
  },
11
14
  "files": [
12
- "dist/",
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
- "build": "node build.mjs",
17
- "test": "bun test tests/*.test.ts",
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:check": "biome format .",
26
- "check": "biome check .",
27
- "prepublishOnly": "bun run build"
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
- "@ai-sdk/gateway": "3.0.40",
32
- "@ai-sdk/mcp": "^1.0.18",
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
- "@ai-cli/typescript-config": "workspace:*",
38
- "@types/node": "^24.1.0",
39
- "@xterm/headless": "^6.0.0",
40
- "ai": "6.0.79",
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
  }
@@ -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
+ });