-
-
Notifications
You must be signed in to change notification settings - Fork 230
feat(ai-cli): add @tanstack/ai-cli (ts-ai), a CLI over TanStack AI #717
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
AlemTuzlak
wants to merge
13
commits into
main
Choose a base branch
from
feat/ai-cli
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
fdbc9fb
feat(ai-cli): add @tanstack/ai-cli (ts-ai), a CLI over TanStack AI
AlemTuzlak 329f56a
ci: apply automated fixes
autofix-ci[bot] efb4c54
fix(ai-cli): make provider factory-resolution test deterministic
AlemTuzlak 5e4af8b
feat(ai-cli): brand the terminal UI, mcp connection info, --output-dir
AlemTuzlak cffa541
feat(ai-cli): animate the welcome wordmark and clear the screen on la…
AlemTuzlak 5317fed
docs(ai-cli): add a TanStack Intent agent skill for the ts-ai CLI
AlemTuzlak 9ccc029
ci: apply automated fixes
autofix-ci[bot] 1e3d4e1
chore: pin all packages/ libraries to workspace:* via pnpm overrides
AlemTuzlak cd55506
fix(ai-cli): address review feedback, pixelated preview, interactive hub
AlemTuzlak a87c3ef
ci: apply automated fixes
autofix-ci[bot] e4f4d83
feat(ai-cli): always preview, pause results until Esc, loading spinner
AlemTuzlak aacf97e
ci: apply automated fixes
autofix-ci[bot] 87a99a9
fix(ai-cli): Ctrl+C always exits the CLI from any interactive screen
AlemTuzlak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| --- | ||
| '@tanstack/ai-cli': minor | ||
| --- | ||
|
|
||
| Add `@tanstack/ai-cli` — a type-safe CLI over TanStack AI exposing the core | ||
| activities as the `ts-ai` binary (`chat`, `image`, `video`, `audio`, `speech`, | ||
| `transcribe`, `summarize`), plus `introspect` (machine-readable manifest), | ||
| `mcp` (expose commands as MCP tools), and `update`. | ||
|
|
||
| Designed machine-first for agent harnesses: every command is a stateless | ||
| single-shot subprocess with `--json` buffered output, `--stream` AG-UI event | ||
| output, strict stdout-is-payload discipline, typed exit codes, and structured | ||
| error objects. Providers resolve from a `provider/model` slug (openai, | ||
| anthropic, gemini, openrouter, and fal bundled for zero-install) with keys from | ||
| `--apiKey`, a conventional `.env`, or environment variables, and all options are | ||
| expressible via `--config` (file or inline JSON). | ||
|
|
||
| For humans there's a lazily-loaded, TanStack-branded Ink layer: running `ts-ai` | ||
| with no command on a TTY opens a full-width welcome screen (island logo on | ||
| graphics-capable terminals + a two-color `TANSTACK` / pink `AI` wordmark) and | ||
| menu, `ts-ai chat` with no prompt drops into an interactive REPL, and image | ||
| results preview inline. `chat` supports tools via `--mcp` servers, sandboxed | ||
| `--code-mode` execution, and `--schema` structured output, plus | ||
| `ts-ai introspect` (machine-readable manifest), `ts-ai mcp` (expose commands as | ||
| MCP tools — prints a ready-to-paste client config to stderr), and `ts-ai update`. | ||
|
|
||
| Generations (`image`, `video`, `audio`, `speech`) write to the current directory | ||
| by default; `--output-dir <dir>` sets the target directory (created if missing, | ||
| cross-platform) and `-o/--output <path>` sets an exact path. | ||
|
|
||
| Ships a TanStack Intent agent skill (`ai-cli`) so coding agents learn how to | ||
| drive `ts-ai` correctly (machine-mode contract, exit codes, `--config`, | ||
| `--output-dir`, `introspect`, `mcp`). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,209 @@ | ||
| --- | ||
| title: ts-ai CLI | ||
| id: cli-overview | ||
| order: 1 | ||
| description: "Run TanStack AI from the terminal or any agent harness with the ts-ai CLI — chat, image, video, audio, speech, transcribe, and summarize, with JSON output, AG-UI streaming, and a self-describing manifest." | ||
| keywords: | ||
| - tanstack ai | ||
| - cli | ||
| - ts-ai | ||
| - agent harness | ||
| - json output | ||
| --- | ||
|
|
||
| `@tanstack/ai-cli` installs the `ts-ai` binary — a thin, type-safe CLI over the | ||
| core TanStack AI activities. It is built **machine-first**: every command is a | ||
| stateless, single-shot subprocess with structured I/O, so it drops cleanly into | ||
| an agent harness — while still giving humans a pretty, interactive experience on | ||
| a TTY. | ||
|
|
||
| ## Install | ||
|
|
||
| ```bash | ||
| # Zero-install one-off | ||
| npx @tanstack/ai-cli image "a watercolor fox" -o fox.png | ||
|
|
||
| # Or install globally | ||
| pnpm add -g @tanstack/ai-cli | ||
| ts-ai --version | ||
| ``` | ||
|
|
||
| OpenAI, Anthropic, Gemini, OpenRouter, and Fal are bundled, so those providers | ||
| work with no extra install. Other providers (Ollama, Grok, Groq, ElevenLabs) | ||
| are loaded on demand — if one isn't installed, `ts-ai` exits with code `4` and | ||
| tells you which package to add. | ||
|
|
||
| ## Choosing a model and key | ||
|
|
||
| Pick a model with a `provider/model` slug. The API key comes from `--apiKey` | ||
| or, by default, the conventional environment variable for that provider | ||
| (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`, | ||
| `FAL_KEY`). | ||
|
|
||
| ```bash | ||
| ts-ai chat "Explain MCP in one sentence" --model openai/gpt-5.5 | ||
| ts-ai chat "Summarize this PR" --model anthropic/claude-sonnet-4-6 --api-key sk-... | ||
| ``` | ||
|
|
||
| `ts-ai` also loads a `.env` from the current directory automatically, so dropping | ||
| `OPENAI_API_KEY=...` in a project `.env` is enough. Real environment variables | ||
| and `--apiKey` always take precedence over `.env`. | ||
|
|
||
| ## Interactive mode | ||
|
|
||
| Run `ts-ai` with no command on a terminal and you get a full-width TanStack AI | ||
| welcome screen — the island logo (on graphics-capable terminals) above the | ||
| wordmark — and a menu (Chat, Image, Video, …). Pick **Chat** to drop into a live | ||
| REPL (`/clear` to reset, `/exit` to quit); pick a generation command to type a | ||
| prompt and run it inline. | ||
|
|
||
| ```bash | ||
| ts-ai # animated menu → pick an action | ||
| ts-ai chat # no prompt on a TTY → interactive chat REPL | ||
| ``` | ||
|
|
||
| ## Commands | ||
|
|
||
| | Command | What it does | | ||
| | --- | --- | | ||
| | `ts-ai chat <prompt>` | Chat / agentic text generation | | ||
| | `ts-ai image <prompt>` | Generate an image | | ||
| | `ts-ai video <prompt>` | Generate a video (async job; blocks until done) | | ||
| | `ts-ai audio <prompt>` | Generate audio (music / sfx) | | ||
| | `ts-ai speech <text>` | Text-to-speech (alias: `tts`) | | ||
| | `ts-ai transcribe <file>` | Speech-to-text (alias: `stt`) | | ||
| | `ts-ai summarize <text>` | Summarize text | | ||
| | `ts-ai introspect` | Print the machine-readable CLI manifest | | ||
| | `ts-ai mcp` | Expose every command as an MCP tool over stdio | | ||
| | `ts-ai update` | Update `ts-ai` to the latest version | | ||
|
|
||
| The prompt is everything after the command that isn't a flag, so multi-word and | ||
| multi-line prompts work without quoting gymnastics. When no prompt is given, | ||
| input is read from stdin — handy for pipes: | ||
|
|
||
| ```bash | ||
| cat article.txt | ts-ai summarize --model openai/gpt-5.5 | ||
| ``` | ||
|
|
||
| Attach files with the repeatable `--attachment` flag: | ||
|
|
||
| ```bash | ||
| ts-ai chat "What's in this diagram?" --model openai/gpt-5.5 --attachment diagram.png | ||
| ``` | ||
|
|
||
| ## Output: humans vs harnesses | ||
|
|
||
| `ts-ai` decides how to render based on whether stdout is an interactive TTY: | ||
|
|
||
| - **On a TTY** it renders a pretty, [Ink](https://github.com/vadimdemedes/ink)-based | ||
| view: streamed chat text, inline image previews, and saved-file callouts. | ||
| - **In `--json` mode, or when stdout is piped/redirected**, it never renders | ||
| anything human-facing. stdout carries only the payload; all progress, warnings, | ||
| and logs go to stderr. | ||
|
|
||
| ### Buffered JSON | ||
|
|
||
| `--json` returns a single JSON object you can parse directly: | ||
|
|
||
| ```bash | ||
| ts-ai image "a red bicycle" --model openai/gpt-image-1 --json | ||
| # {"id":"...","model":"gpt-image-1","images":[{"path":"./ts-ai-image-<ts>.png","mimeType":"image/png"}],"usage":{...}} | ||
| ``` | ||
|
|
||
| Media commands (`image`, `video`, `audio`, `speech`) always write the artifact | ||
| to a file and report the path in the JSON. By default the file lands in the | ||
| **current directory** with an auto-generated name. Control where it goes: | ||
|
|
||
| - `--output-dir <dir>` — write the auto-named file into `<dir>` (created if | ||
| missing). Works the same on Windows and macOS. | ||
| - `-o/--output <path>` — set the exact file path (wins over `--output-dir`). | ||
| - `-o -` — stream the raw bytes to stdout (for piping). | ||
|
|
||
| ```bash | ||
| ts-ai image "a red bicycle" --model openai/gpt-image-1 # ./ts-ai-image-<ts>.png | ||
| ts-ai image "a red bicycle" --model openai/gpt-image-1 --output-dir ./out | ||
| ts-ai image "a red bicycle" --model openai/gpt-image-1 -o ./pics/bike.png | ||
| ``` | ||
|
|
||
| ### Streaming the AG-UI event stream | ||
|
|
||
| `--stream` emits the TanStack AI / AG-UI event stream as newline-delimited JSON, | ||
| one event per line, so a harness can reconstruct state incrementally: | ||
|
|
||
| ```bash | ||
| ts-ai chat "Write a haiku" --model openai/gpt-5.5 --stream | ||
| ``` | ||
|
|
||
| ## Stateless multi-turn | ||
|
|
||
| `chat` keeps no state of its own. Pass the full conversation in with `--messages` | ||
| (a JSON array) and thread the returned messages back yourself: | ||
|
|
||
| ```bash | ||
| ts-ai chat --model openai/gpt-5.5 --json \ | ||
| --messages '[{"role":"user","content":"hi"},{"role":"assistant","content":"hello!"},{"role":"user","content":"what did I just say?"}]' | ||
| ``` | ||
|
|
||
| `--threadId` is accepted purely as a correlation id (for telemetry / AG-UI) and | ||
| never causes anything to be persisted. | ||
|
|
||
| ## Structured output | ||
|
|
||
| Constrain `chat` to a JSON Schema and get a validated object back under `.data`: | ||
|
|
||
| ```bash | ||
| ts-ai chat "Classify: 'the app crashes on launch'" \ | ||
| --model openai/gpt-5.5 \ | ||
| --schema ./ticket.schema.json \ | ||
| --json | ||
| # {"data":{"severity":"high","area":"startup"},"model":"gpt-5.5"} | ||
| ``` | ||
|
|
||
| ## Configuration | ||
|
|
||
| Every option is settable as a flag. For nested, provider-specific options use | ||
| `--config`, which accepts a JSON file path **or** an inline JSON string whose | ||
| shape mirrors the command's options. Precedence is **flags > `--config` > env > | ||
| defaults**: | ||
|
|
||
| ```bash | ||
| ts-ai image "a logo" --model openai/gpt-image-1 \ | ||
| --config '{"size":"1024x1024","modelOptions":{"background":"transparent"}}' | ||
| ``` | ||
|
|
||
| Provider-specific options live only under `modelOptions`. | ||
|
|
||
| ## Using ts-ai inside an agent harness | ||
|
|
||
| Two patterns make `ts-ai` easy to drive programmatically: | ||
|
|
||
| 1. **`ts-ai introspect`** prints a versioned JSON manifest of every command, | ||
| flag, type, and exit code — read it once and auto-generate tool definitions. | ||
| 2. **`ts-ai mcp`** starts an MCP server (stdio) that exposes each command as a | ||
| tool, so any MCP-capable agent can register `ts-ai` directly. On startup it | ||
| prints the connection details to **stderr** (stdout is the JSON-RPC channel) | ||
| — a ready-to-paste client config, the transport, and the tool list: | ||
|
|
||
| ```jsonc | ||
| // add to Claude Desktop / Cursor | ||
| "mcpServers": { | ||
| "tanstack-ai": { "command": "ts-ai", "args": ["mcp"] } | ||
| } | ||
| ``` | ||
|
|
||
| ### Exit codes | ||
|
|
||
| | Code | Meaning | | ||
| | --- | --- | | ||
| | `0` | Success | | ||
| | `1` | Generic runtime error | | ||
| | `2` | Usage / validation error | | ||
| | `3` | Provider/API error or output-schema validation failure | | ||
| | `4` | Required provider package not installed | | ||
|
|
||
| In `--json` mode a non-zero exit also prints a structured error object on stdout | ||
| so the failure stays parseable: | ||
|
|
||
| ```json | ||
| { "error": { "code": "USAGE", "message": "Missing --model (e.g. openai/gpt-5.5).", "provider": "openai" } } | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.