CLI overview
folio is the primary command-line interface. It is a thin wrapper over
the Python SDK, so every verb has a one-to-one SDK equivalent. JSON to
stdout, errors to stderr with a non-zero exit code.
Synopsis
folio <verb> [SHEET] [OPTIONS]SHEET is a path to a sheet directory. Most verbs take it as the first
positional argument.
Verbs
| Verb | What it does |
|---|---|
folio init | Scaffold a new sheet directory with starter contract.yaml / records.jsonl / README.md / skills/getting-started.md. |
folio validate | Validate contract.yaml, records.jsonl, and (optionally) README.md frontmatter. |
folio query | Run a DuckDB SELECT against the sheet’s records view. |
folio list | List records as a paginated JSON envelope (or TOON). |
folio count | Count records, optionally with a WHERE-clause filter. |
folio upsert | Insert or update records by primary key. |
folio delete | Delete records by primary key id. |
folio materialize | Run derivations. Returns the §10.6 envelope. |
folio status | Per-target counts for derived fields. |
folio provenance | Latest entry or full append-only history for one cell. |
folio serve | Start the Viewer (alias for folio-viewer serve). |
folio script | Discover and run reusable scripts under sheet/scripts/. |
folio skill | List, render, and validate packaged procedures under sheet/skills/. |
folio export | Export the sheet — datapackage (Frictionless descriptor) or claude-skills (Claude Code-compatible SKILL.md set). |
Global behaviour
Output format
Most verbs print machine-readable JSON to stdout. Pipe to jq:
folio query ./customers "SELECT id, country FROM records LIMIT 5" | jqExit codes
| Code | Meaning |
|---|---|
0 | Success. |
1 | A FolioError (validation, lock timeout, permission denied, …). The error message is on stderr. |
2 | Usage error (Typer rejected the arguments). |
Actor
Write verbs (upsert, delete, materialize, script run for write
contexts) require --actor. Folio doesn’t default to agent:cli because
audit trails should name a real principal.
--help everywhere
Every verb prints a help text with options:
folio --helpfolio materialize --helpExamples
# Bootstrap a new sheetfolio init --name my-sheetfolio validate ./my-sheet
# Validate, list, queryfolio validate ./customersfolio list ./customers --limit 10folio query ./customers "SELECT country, COUNT(*) AS n FROM records GROUP BY 1"
# Mutateecho '{"id":"cust_999","company_name":"X","country":"FR"}' \ | folio upsert ./customers --file - --actor agent:human
folio delete ./customers --ids cust_999 --actor agent:human
# Derive and inspectfolio materialize ./customers --actor agent:demofolio status ./customersfolio provenance ./customers cust_001 country_code --history
# Run a reusable script (args are positional, passed to the script after the sheet path)folio script list ./customersfolio script run ./customers country_to_code '{"country":"Japan"}'
# Discover and render a packaged operating procedurefolio skill list ./customersfolio skill show ./customers refresh-country-codes
# Export, servefolio export datapackage ./customers --stdout > customers.datapackage.jsonfolio serve ./customers --port 3000 --actor agent:humanWhere to next
- Per-verb pages from the sidebar (
validate,query, …). - Python SDK overview — every verb’s underlying API.
- MCP overview — the same SDK exposed as MCP tools.