Documentation

Everything you need to use ai effectively. From your first prompt to advanced policy configuration.

Overview

ai is a single-binary Rust CLI agent. ai run "PROMPT" performs a one-shot request (or read the prompt from stdin); bare ai starts an interactive session. The response streams to stdout, while thinking and tool calls go to stderr, so results pipe cleanly into other commands.

The model starts with only what you give it — no filesystem, no network, no shell. Every capability beyond a plain answer is opt-in via CLI flags or a declarative policy file. Two exceptions are on by default: local semantic memory (disable with --no-memory) and session persistence (disable with --no-session).

Providers: native OpenAI and Anthropic APIs, every OpenAI/Anthropic-compatible endpoint via api_base + flavor, and the full models.dev catalog.

Quickstart

$ ai run "explain this regex: ^(?=.*[A-Z])(?=.*\d).{8,}$"
$ cat data.csv | ai run "summarize the trends in this CSV"
$ ai run -x cargo,git -r ./src/** -w ./src/** "fix all compiler warnings"
$ ai setup

Run ai setup once to pick a provider and model. After that, ai (no arguments) drops you into an interactive session that remembers the conversation and can resume it later.

Commands & Flags

Value flags accept either --flag value or --flag=value.

Commands

CommandDescription
aiStart an interactive session (the default with no subcommand).
ai run PROMPTOne-shot request. If PROMPT is absent, read it from stdin (cat f | ai run).
ai session listList saved sessions (the current one is marked *).
ai session delete NAMEDelete a session by name.
ai memory listList stored memory facts.
ai memory search QUERYSemantic search across memory and past transcripts, with match scores.
ai skills listList discovered skills with their origin (user/AI) and path.
ai skills delete NAMEDelete a skill's folder (SKILL.md plus bundled files).
ai dream [--jobs N]Run memory maintenance (see Memory Maintenance).
ai setup [FILE]Interactive provider/model wizard plus AI config editing (see Setup).
ai completions SHELLPrint a shell completion script (bash, zsh, fish, …) and exit.

Global flags

FlagDescription
-c, --config FILELoad configuration from FILE (default ~/.config/ai/config.yaml).
--provider PROVIDER, --model MODELOverride the provider or model for this run.
--system PROMPTOverride the system prompt for this invocation.
-s, --session-name NAMEName the session, or continue the most recent session with that name (an undated NAME matches across dates).
--no-sessionDo not read or write a session; run stateless.
--no-memoryDisable persistent memory for this run.
-r, --read PATHAllow read-only access to PATH. Repeatable; supports globs.
-w, --write PATHAllow read/write access to PATH. Repeatable; also grants read.
-x, --execute PATTERNAllow execution of PATTERN (comma-separated names). Repeatable.
--webAllow all web access (fetch and search).
--web-fetch PATTERN, --web-search PATTERNNarrow web access to matching URLs / queries. Repeatable.
--proxy URLRoute web requests through a proxy (http://… or socks5h://…).
-p, --policy FILELoad access policy from FILE. CLI rules take precedence.
-i, --askWhen a tool needs a resource not covered by any allow rule, ask you to approve or deny.
-t, --tool URLConnect to an MCP tool server. Repeatable.
-X, --container [IMAGE]Run external commands in a container (default debian:stable-slim).
--container-runtime RUNTIME, --no-containerChoose auto/docker/podman, or ignore a configured container.
-y, --yoloGrant all permissions. Dangerous.
--max-tokens N, --max-turns NCap output tokens, or agent turns (tool rounds; default 100).
--thinking [TOKENS]Enable extended reasoning (default 16,000 tokens).
-v/--verbose, -q/--quiet, --no-colorDebug logging, quieter output, and disabled ANSI colors.

Examples

# ask a question (no tools) $ ai run "what does the borrow checker enforce?" # give it filesystem access $ ai run -r ./src/** "refactor the auth module" $ ai run -w ./src/** "add input validation to handlers.rs" # allow specific commands $ ai run -x cargo,git "build and check for warnings" # web access $ ai run --web "what changed in the latest Rust release?" $ ai run --web-fetch https://docs.rs/** "summarize the serde docs" # interactive session with a name $ ai -s my-project -r ./** -w ./** -x cargo,git,npm # run external commands in a container $ ai run -X alpine:latest -r ./** "run the test suite" # pipe data in $ cat error.log | ai run "find the root cause of these errors"

Configuration

Config is YAML at ~/.config/ai/config.yaml (override with -c). Every field is optional; CLI flags and env vars override config values. Unknown keys warn; ai setup rejects them.

provider: openai api_key: env:OPENAI_API_KEY model: gpt-4o api_base: https://api.openai.com/v1 system_prompt: You are a helpful CLI assistant. context_window: 128000 max_tokens: 4096 session_dir: ~/.local/share/ai/sessions policy: ~/.config/ai/policy.txt memory: ~/.local/share/ai/memory.db embedding_model: multilingual-e5-small memory_max_distance: 0.175 skills: dir: ~/.local/share/ai/skills auto_create: false min_tuples: 10 dream: auto: false jobs: 4 proxy: socks5h://127.0.0.1:1080 search: providers: - name: brave - name: duckduckgo mcp: servers: - url: https://example.com/mcp name: example

Environment Variables

VariableProvider
OPENAI_API_KEYOpenAI
ANTHROPIC_API_KEYAnthropic
OLLAMA_API_KEYOllama
GROQ_API_KEYGroq
GEMINI_API_KEYGoogle Gemini
DEEPSEEK_API_KEYDeepSeek
MISTRAL_API_KEYMistral
OPENROUTER_API_KEYOpenRouter
XAI_API_KEYxAI
BRAVE_API_KEY, TAVILY_API_KEY, EXA_API_KEY, SERPER_API_KEYWeb search providers
HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, NO_PROXYWeb request proxy

An API key may be a literal or env:VAR_NAME; env: is preferred so secrets stay out of the file.

Field Reference

FieldDefaultDescription
provideropenaiProvider id (built-in or any models.dev id)
flavorderivedRequest shape: openai or anthropic
api_key—Key or env:VAR reference
api_baseprovider defaultBase URL (required for custom endpoints)
modelgpt-4oModel name
system_promptbuilt-inDefault system prompt
max_tokens, thinking—Output cap; extended-thinking budget
context_windowproviderContext size for the usage indicator
session_dir<data-dir>/ai/sessionsSession storage
policy—Policy file with allow/deny rules
memory<data-dir>/ai/memory.dbPersistent memory database (SQLite)
embedding_modelmultilingual-e5-smallLocal embedding model for semantic search
memory_max_distance0.175Cosine distance cutoff for injected hits
skills.dir<data-dir>/ai/skillsWhere SKILL.md files are discovered
skills.auto_create, skills.min_tuplesfalse, 10Let ai dream author skills; review threshold
dream.auto, dream.jobsfalse, 4Run maintenance at session end; parallel requests
proxyenvProxy URL for web requests
search.providersDuckDuckGo→Google→BingOrdered web-search backends
container.default_image, container.runtime, container.network—, auto, policyContainer isolation settings
mcp.servers—External MCP servers { url, name? }

Providers

ai speaks the OpenAI and Anthropic APIs natively. Any other provider works by setting api_base to its compatible endpoint and choosing the matching flavor (openai or anthropic). Built-ins cover OpenAI, Anthropic, Ollama, Groq, DeepSeek, Google Gemini, Mistral, OpenRouter, xAI, and llamafile; ai setup can also browse the full models.dev catalog.

# Ollama (local) $ ai run -c ollama.yaml "explain monads" # Any OpenAI-compatible endpoint $ ai run -c my-endpoint.yaml "write a fibonacci function"

Example configs

# ~/.config/ai/openai.yaml provider: openai api_key: env:OPENAI_API_KEY model: gpt-4o
# ~/.config/ai/ollama.yaml provider: openai flavor: openai api_base: http://localhost:11434/v1 api_key: ollama model: llama3.2
# ~/.config/ai/anthropic.yaml provider: anthropic api_key: env:ANTHROPIC_API_KEY model: claude-sonnet-4-20250514

Access Policy

The policy engine controls what files, commands, and web resources the model can access. Rules are evaluated first-match: the first matching Allow or Deny rule wins. CLI flags (-r, -w, -x, --web) are prepended to policy-file rules and take precedence.

Actions

ActionCovers
readread_file, list_dir, search_content, find_files, file_info, file_view
writewrite_file, replace_in_file, delete_file, create_directory, move_file, copy_file
executeexecute (external commands only)
web-fetchweb_fetch, download_file, browser tools
web-searchweb_search

Policy File Syntax

One rule per line. # starts a comment.

# grant read access to the home projects directory allow read ~/projects/** # deny access to secrets subdirectories deny read ~/projects/**/secrets/** # allow specific commands (comma-separated) allow execute cargo,git,npm,npx,rustc # allow web fetch from docs.rs allow web-fetch https://docs.rs/**

Path Matching

Path patterns support glob wildcards and are segment-aware: /tmp matches /tmp/foo.txt but not /tmpfile. Paths are resolved (symlinks included) before the policy is consulted, so a deny inside an allowed tree is honored. ~ is expanded.

Execute Patterns

The execute tool always runs. 150+ built-in commands (echo, cat, grep, sed, git, curl, …) run inside a virtual bash interpreter (bashkit) and are governed by filesystem read/write policy, not execute permissions. External commands (e.g. cargo, npm, python) are checked against the execute allowlist. See Filesystem & Containers for how containers change this.

Interactive Ask Mode

With -i/--ask, an unmatched action prompts you and remembers the choice for the session: allow once, remember the exact target, remember its directory, or deny.

$ ai -i "fix the bug"

Allow read for /home/user/project/src/auth.rs? [y=once, a=always, r=this dir, N=deny]

Filesystem & Containers

Every user-supplied path is resolved and authorized in one checked layer before the operation, so deny rules inside an allowed tree hold and symlink escapes are blocked.

External commands run through bashkit. In-process builtins run without a grant; external commands require -x unless a container is active. When a container is configured (-X/--container[=IMAGE], default debian:stable-slim, or container.default_image), a single session container starts at agent startup and every execute command runs inside it:

  • Policy read roots are bind-mounted read-only, write roots read-write; the host filesystem is otherwise invisible.
  • The container gets no network unless web access is granted (container.network: policy).
  • --no-container forces host execution; --container-runtime picks docker/podman.
  • --yolo does not disable the container — it relaxes tool policy only. Whole-filesystem (**) grants are not mountable and are warned about.

Sessions

Sessions persist the full message log (tool calls and results included) to JSON. Each session records its provider and model; resuming under a different pair forks a fresh session instead of replaying an incompatible history.

# start an interactive session with a name $ ai -s backend-refactor # resume it later (undated names match across dates) $ ai -s backend-refactor # list / delete sessions $ ai session list $ ai session delete backend-refactor

ai run and bare ai automatically continue the newest session when it is less than 60 minutes old and its provider/model match; otherwise a new session starts. One-off sessions are saved only after a successful run. Pass --no-session to run stateless.

In-Session Commands

CommandAction
/exit, /quitExit the session
/clearClear all messages in the session
/compactSummarize the conversation and replace history with the summary
/sessionPrint the current session name
/helpShow slash commands — and for anything else about ai, just ask
Context tracking: the interactive prompt shows session name, container (if any), and context usage as [NN%], colored by tier.

Memory

Memory is enabled by default and stored in a SQLite database (memory.db). Facts and past exchanges are embedded locally (ONNX via fastembed) and searched with sqlite-vec, so nothing leaves your machine. Relevant hits and transcript excerpts are injected per message, each shown with a cosine match score.

$ ai run "remember that our API uses snake_case JSON" $ ai memory search "how are request payloads formatted?" $ ai memory list

Four tools back this: memory_add (text, optional tags, origin), memory_search, memory_get (fetch one entry by key), and memory_delete. Tuning keys: embedding_model (default multilingual-e5-small) and memory_max_distance (lower is stricter). Disable with --no-memory.

Memory Maintenance

ai dream keeps memory healthy. It runs over a work pool (--jobs N, config dream.jobs, default 4):

  1. extract — distill durable facts from unprocessed transcript tuples.
  2. skills — when skills.auto_create is true, review sessions with at least skills.min_tuples unreviewed exchanges and author or update skills.
  3. prune — delete processed transcripts older than 7 days (skill-pending sessions are held back).
  4. judge — review entries unused and unjudged for 30 days and delete obsolete ones.

An interactive session offers to run maintenance at exit when the backlog is large, and dream.auto: true runs it automatically at the end of every saved session.

Skills

A skill is a SKILL.md file (YAML front matter with name and description) under the skills directory (skills.dir, default <data-dir>/ai/skills). Skills are listed in the system prompt, and the model loads one on demand with the load_skill tool, which returns the full body plus the absolute paths of any bundled files (scripts, references). The skills folder is granted read access automatically.

--- name: release description: Cut a release of this project --- 1. Bump the version in Cargo.toml 2. Update CHANGELOG.md 3. Commit, tag vX.Y.Z, push

With skills.auto_create: true, ai dream can author skills from past sessions. AI-created skills are tagged origin: ai and are the only ones the agent may modify or delete; they appear as (AI-created) in the prompt. Inspect and remove them with ai skills list and ai skills delete NAME.

Web & Search

Web access is off unless granted: --web for everything, or --web-fetch URL / --web-search QUERY for narrower grants. web_fetch retrieves a page and converts it to readable text; web_search uses an ordered ladder of providers configured in search.providers:

  • Keyed APIs — brave, tavily, exa, serper. An omitted api_key falls back to BRAVE_API_KEY / TAVILY_API_KEY / EXA_API_KEY / SERPER_API_KEY.
  • SearXNG — a normal entry that needs a url (queried via its JSON API).
  • Keyless scrapers — duckduckgo, google, bing.

The first provider that succeeds wins; a listed provider missing credentials is skipped with a warning. --probe-web QUERY tests the configured providers.

Brave Search API: Brave offers a free tier. Create a key at brave.com/search/api, set BRAVE_API_KEY (or api_key: env:BRAVE_API_KEY), and add - name: brave to search.providers.

MCP Tool Servers

Connect Model Context Protocol servers to extend ai with external tools — IDE integrations, database agents, API gateways. Use --tool URL for one run, or declare servers in config:

mcp: servers: - url: https://example.com/mcp name: example

Config servers and --tool URLs are merged and deduplicated. A config server that fails to connect is skipped with a warning; a --tool failure aborts startup.

Built-in Tools

30+ built-in tools are conditionally registered based on active policy rules.

Filesystem

read_file

Read file contents. Supports offset and limit for pagination.

pathoffset?limit?

write_file

Create or overwrite a file. Creates parent directories automatically.

pathcontent

list_dir

List directory contents with type prefixes (d directory, l symlink, f file).

pathoffset?limit?

replace_in_file

Surgical string replacement. Old string must match exactly once.

pathold_strnew_str

delete_file

Delete a file or directory. Directories require recursive: true.

pathrecursive?

create_directory

Create a directory and all parent directories (like mkdir -p).

path

file_info

File or directory metadata: size, permissions, modification time, and type.

path

move_file

Move or rename a file or directory. Checks write permission on source and destination.

sourcedestination

copy_file

Copy a file. Checks read on source, write on destination.

sourcedestination

file_view

Extract readable text from rich documents (PDF, DOCX, XLSX, PPTX, HTML, CSV, images, ZIP) via anydoc, returned as Markdown.

path

Execution

execute

Run a shell command. 150+ built-in commands run in-process via bashkit — no forking; external commands fork-exec and need an execute grant (or a container). Filesystem access is governed by read/write policy.

commandcwd?offset?limit?timeout?

Search

search_content

Regex search across files. Skips hidden directories and binaries. Max 500 matches, 1 MB per file.

pathpatternfile_types?offset?limit?

find_files

Glob-based file search. Results sorted alphabetically, truncated at 500.

pathpatternoffset?limit?

Web

web_fetch

Fetch a URL and return text, markdown, or HTML. Max 5 MB response.

urlformat?timeout?offset?limit?

web_search

Search via the configured provider ladder, with a stealth-browser fallback. Returns titles, URLs, and snippets. Max 20 results.

querynum_results?offset?limit?

download_file

Download a URL to disk. Max 5 MB. Requires write permission on the destination.

urlpath

Browser

browser_navigate

Navigate the headless browser to a URL. Returns the page title and content size. Requires web-fetch permission.

url

browser_click

Click an element by CSS selector, navigating first if a URL is given.

selectorurl?

browser_get_content

Get the full page content in markdown or raw HTML (after a navigate).

format?offset?limit?

browser_get_element

Extract a DOM element by CSS selector in markdown or raw HTML.

selectorformat?offset?limit?

browser_evaluate

Execute JavaScript on the current page and return the result as JSON.

expressionurl?

Memory & other

memory_add

Store a fact with optional tags and origin (user/agent). Returns a key. Upserts near-duplicates.

datatags?origin?

memory_search

Semantic search over memory and past transcripts. Returns best matches with keys and scores.

querylimit?

memory_get

Fetch one memory entry by key, with its full text, tags, origin, and source.

key

memory_delete

Delete a memory entry by key.

key

load_skill

Load a skill's instructions by name, plus the absolute paths of its bundled files.

name

manual

Look up this documentation by topic (commands, flags, configuration, behavior).

topic?

get_current_time

Return the current date and time.

exit_program

End an interactive session (or ai setup) when the user asks to quit.

Inline Help

The agent has a manual tool that serves this documentation, the example config, and the generated CLI help. Ask it questions about ai itself — flags, config keys, or how a feature works — and it answers from the manual rather than guessing.

$ ai run "how do I set up a container?" $ ai run "what does memory_max_distance do?"

In an interactive session, /help lists the slash commands and reminds you that you can just ask.

Output & Thinking

Assistant markdown is rendered to ANSI on a terminal and left as raw markdown when piped. A first-token spinner appears while waiting. Use --no-color (or NO_COLOR) to disable styling.

# quiet: no thinking output, no token stats $ ai run -q "what is 2+2" # verbose: debug-level logging $ ai run -v "explain this code" # extended thinking (Anthropic-flavored providers) $ ai run --thinking 32000 "audit this codebase for security vulnerabilities"

Thinking tokens are counted in the token stats, shown as (in, thinking, out).

Setup

ai setup has two phases. First a deterministic wizard: reuse an existing config or detected credentials, otherwise pick a provider and model — browsing the models.dev catalog or entering a custom URL — and confirm with a small connection test. Then an AI conversation edits the rest of the config, saving through a sandboxed, approval-gated tool. The API key is never sent to the model, and an existing config is backed up to config.yaml.bak.

$ ai setup $ ai setup /path/to/config.yaml

Prefer api_key: env:VAR_NAME so keys stay out of the file. Afterwards, ai is ready to use.