saya-cli 0.4.1

Database-aware AI agent for the terminal: full-screen TUI, schema discovery, and bounded read-only SQL over PostgreSQL, MySQL, SQLite, DuckDB, and Snowflake.
docs.rs failed to build saya-cli-0.4.1
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: saya-cli-0.3.0

saya is a database-aware AI agent for the terminal. Ask questions in plain language or run SQL directly. For agent queries, saya discovers the schema, shows you the SQL, then runs it read-only with bounded results against PostgreSQL, MySQL, SQLite, DuckDB, or Snowflake.

Install

Prebuilt binaries for macOS (Apple Silicon + Intel), Linux, and Windows are on every release, with SHA256SUMS to verify them.

brew install databook-studio/tap/saya   # macOS / Linux
cargo binstall saya-cli                 # prebuilt binary, no compile
cargo install saya-cli                  # from source (builds DuckDB; takes a few minutes)

Building from crates.io needs one flag for SQLite's maths functions, which the released binaries already carry:

LIBSQLITE3_FLAGS=-DSQLITE_ENABLE_MATH_FUNCTIONS cargo install saya-cli

→ installation

Quick start

saya config init                          # starter config in your user config dir (init prints the path)
$EDITOR ~/.config/saya/connections.toml   # point the example profile at your database
export SAYA_ANALYTICS_PASSWORD='...'      # the profile references it; never commit it
saya config doctor                        # secrets resolve? provider reachable?
saya                                      # start the TUI

config init writes to your user config directory, which saya trusts. Pass --project to write a .saya/ pair for a repository instead — that layer is untrusted, so security-critical settings in it are ignored unless you pass --trust-project-config. config doctor names what is missing and exits non-zero when the setup cannot run a query, so a script can tell.

The starter config points at a local Ollama; edit [ai] in config.toml for OpenAI, Anthropic, Gemini, or any OpenAI-compatible gateway.

One-shot, no TUI:

saya ask "how many orders shipped last week?"
saya query --sql "SELECT count(*) FROM orders"

Work with your data

  • Ask in plain language. saya inspects your schema, proposes SQL, and shows the statement in the transcript. Choose ask, read-only, or never with --approval-mode to control approval prompts; bypass is an explicit session-wide option.
  • Run SQL directly. Use saya query for a single read-only statement, or use saya to open the interactive terminal UI. The UI streams answers, offers / command search and @table schema completion, and supports exporting results. (TUI demo, export demo)
  • Connect more than one database. Work with PostgreSQL, MySQL, SQLite, DuckDB, and Snowflake; add profiles to a session so saya can inspect and query each connection. (cross-database demo)
  • Keep useful context. Optional memory carries typed facts about tables, columns, relationships, and metrics into later questions. Facts are bound to the schema, and inferred facts wait for your confirmation. Off by default. → Memory
  • Use local or hosted models. Supported providers include Ollama, OpenAI, OpenAI-compatible endpoints, Anthropic, and Gemini.
  • Automate from scripts. Piped input and non-interactive commands support text, JSON, or NDJSON output with documented exit codes.
  • 🏃 Long-running runs — saya run "<goal>" asks the model for a plan, shows it to you once with its scopes and budgets, and executes it step by step, pausing (never silently stopping) when a declared budget trips so a resume picks up at the first incomplete step. Scopes are declared up front with --allow, and today the one writable surface any run can reach is its own run directory. Approval is one ask, not one per tool call — and the trade is stated where you make it: if users rubber-stamp plans, the security story leans on the sandbox, the bounds, and the sentinel tests. → commands
  • 🖥️ Host commands (unsandboxed) — the run_command lane composes wherever a workspace root binds: PATH-resolved programs run as your user with your whole filesystem and network. run_command claims no containment — the contained lane's guarantees are run_program's, not this one's. Under bypass, a hostile workspace file is effectively arbitrary code execution as the user. --deny <program> (repeatable) refuses named programs at every session door before every grant, prompt, and bypass; the deny list bounds the direct ask only — a denied curl does not stop an allowed make from invoking curl. → commands

Safety

Database queries use two read-only layers. Every SQL statement is parsed and rejected if it writes, and the database session itself is opened read-only — Postgres default_transaction_read_only, MySQL transaction_read_only, SQLite PRAGMA query_only, a read-only DuckDB open. Results are bounded by a row cap and byte budgets, and marked when truncated.

Long-running runs require explicit --allow scopes before they start. Host commands require approval under ask; bypass is a deliberate session-wide approval and does not weaken SQL's read-only policy.

Secrets live in your environment or on disk as references, never inline in committed config. Resolved secrets, provider headers, and raw result rows are structurally excluded from saved sessions.

Neither layer can prove that an arbitrary database function is side-effect free, and Snowflake has no session read-only switch — so connect with a least-privilege, read-only database role. → SECURITY.md

Documentation

Start with saya --help for current flags and commands, or /help inside the TUI. These guides cover setup and common tasks:

Guide Covers
Installation binaries, Homebrew, cargo, building from source
Configuration config layers, environment variables, state paths
Connections every database type, TLS modes, secret references
Providers every supported provider and its settings
Commands the CLI surface and output formats
Querying databases worked examples, cross-database queries
Memory what saya remembers, and the trust model behind it
Security policy security boundaries and vulnerability reporting

Development

cargo fmt --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked

The demo GIFs are generated with vhs from the docs/*.tape scripts; the live ones need SAYA_API_KEY and a reachable database.

See CONTRIBUTING.md. Apache-2.0 licensed.