xl
Agent-first CLI for Excel workbooks (.xlsx / .xlsm)
A transactional spreadsheet execution layer that lets AI agents and humans inspect, plan, validate, and apply workbook changes deterministically — like terraform for Excel.
xl wb inspect -f budget.xlsx # discover structure
xl plan add-column -f budget.xlsx \ # generate a plan (non-mutating)
-t Sales -n Margin \
--formula "=[@Revenue]-[@Cost]" \
--out plan.json
xl validate plan -f budget.xlsx \ # check before applying
--plan plan.json
xl apply -f budget.xlsx \ # preview changes
--plan plan.json --dry-run
xl apply -f budget.xlsx \ # apply with safety backup
--plan plan.json --backup
xl verify assert -f budget.xlsx \ # confirm expected state
--assertions '[{"type":"table.column_exists","table":"Sales","column":"Margin"}]'
Every command returns a structured JSON envelope:
{
"ok": true,
"command": "table.ls",
"result": [...],
"errors": [],
"warnings": [],
"metrics": { "duration_ms": 42 }
}
No Excel installation required — works entirely with openpyxl.
Features
- Inspect — sheets, tables, named ranges, fingerprints, lock status
- Read — cell values, range statistics (min/max/mean/sum/count/stddev), formula search by regex
- Query — SQL over table data via DuckDB (
SELECT Region, SUM(Sales) FROM Sales GROUP BY Region) - Mutate — add/delete columns, append rows, set cells/formulas, number formats, column widths, freeze panes
- Patch plans — generate JSON plans, compose multi-step plans, validate before applying
- Apply — execute plans with
--dry-runpreview and--backupsafety copy - Verify — post-apply assertions (
table.column_exists,cell.value_equals,row_count.gte, ...) - Diff — cell-level comparison between two workbook files
- Lint — detect volatile functions, broken references, formula anti-patterns
- Workflows — multi-step YAML pipelines with
xl run - Server mode — stdio JSON server for agent tool integration (MCP/ACP)
Safety rails
All mutating commands include built-in protections:
| Feature | Description |
|---|---|
--dry-run |
Preview every change without writing to disk |
--backup |
Timestamped .bak copy before any write |
| Fingerprint conflict detection | Plans record the file's xxhash; apply rejects if the workbook changed since the plan was created |
| Exclusive file locking | Sidecar .xl.lock prevents concurrent mutations; --wait-lock N retries for N seconds |
| Formula protection | Refuses to overwrite existing formulas unless --force-overwrite-formulas is set |
| Policy files | Optional xl-policy.yaml for protected sheets, ranges, mutation thresholds, and command restrictions |
Installation
Requires Python 3.12+ and uv.
From PyPI (recommended)
uv tool install xl-agent-cli
# Verify
xl version
xl --help
One-shot with uvx
uvx --from xl-agent-cli xl --help
Local development install
git clone https://github.com/ThomasRohde/xl-cli.git
cd xl-cli
uv sync
# Verify
uv run xl version
uv run xl --help
Global install from source
git clone https://github.com/ThomasRohde/xl-cli.git
cd xl-cli
uv tool install --from . xl-agent-cli
If xl is not found, open a new terminal so your PATH refreshes.
Quick start
1. Inspect a workbook
xl wb inspect -f data.xlsx
Returns sheets, tables (with columns and row counts), named ranges, and a fingerprint hash.
2. List tables
xl table ls -f data.xlsx
3. Query with SQL
xl query -f data.xlsx \
--sql "SELECT Region, SUM(Revenue) as Total FROM Sales GROUP BY Region ORDER BY Total DESC"
4. Add a calculated column (safe workflow)
# Generate a plan (does NOT modify the workbook)
xl plan add-column -f data.xlsx -t Sales -n GrossMarginPct \
--formula "=[@GrossMargin]/[@Revenue]" \
--out plan.json
# Validate the plan
xl validate plan -f data.xlsx --plan plan.json
# Preview what would change
xl apply -f data.xlsx --plan plan.json --dry-run
# Apply for real with backup
xl apply -f data.xlsx --plan plan.json --backup
# Verify the result
xl verify assert -f data.xlsx \
--assertions '[{"type":"table.column_exists","table":"Sales","column":"GrossMarginPct"}]'
5. Format a report
xl format number -f report.xlsx --ref "Sales[Revenue]" --style currency --decimals 2
xl format width -f report.xlsx --sheet Sheet1 --columns A,B,C,D --width 15
xl format freeze -f report.xlsx --sheet Sheet1 --ref B2
6. Compare before and after
xl diff compare --file-a original.xlsx --file-b modified.xlsx
Command reference
| Group | Commands | Description |
|---|---|---|
xl wb |
inspect, create, lock-status |
Workbook metadata, creation, fingerprint, lock check |
xl sheet |
ls, create, delete, rename |
Sheet lifecycle and management |
xl table |
create, ls, add-column, append-rows, delete, delete-column |
Table operations |
xl cell |
get, set |
Read/write individual cells |
xl range |
stat, clear |
Range statistics and clearing |
xl formula |
set, lint, find |
Set formulas, lint for issues, search by regex |
xl format |
number, width, freeze |
Number formats, column widths, freeze panes |
xl query |
(top-level) | SQL queries over table data via DuckDB |
xl plan |
show, add-column, create-table, set-cells, format, compose, delete-sheet, rename-sheet, delete-table, delete-column |
Generate and compose patch plans |
xl validate |
workbook, plan, refs, workflow |
Validate health, plans, references, workflows |
xl apply |
(top-level) | Apply patch plans with --dry-run and --backup |
xl verify |
assert |
Post-apply assertions |
xl diff |
compare |
Cell-level workbook comparison |
xl run |
(top-level) | Execute multi-step YAML workflows |
xl serve |
(top-level) | stdio server for agent tool integration |
xl guide |
(top-level) | Machine-readable JSON orientation guide |
Use xl <group> --help or xl <group> <command> --help for detailed usage.
Reference syntax
Commands that target cells, ranges, or table columns use ref syntax:
| Pattern | Example | Usage |
|---|---|---|
SheetName!Cell |
Sheet1!B2 |
Single cell |
SheetName!Start:End |
Sheet1!A1:D10 |
Cell range |
TableName[ColumnName] |
Sales[Revenue] |
Table column (formula/format) |
=[@ColumnName] |
=[@Revenue]-[@Cost] |
Structured ref inside table formulas |
Exit codes
| Code | Meaning |
|---|---|
0 |
Success |
10 |
Validation error (bad input, schema mismatch, invalid plan) |
20 |
Protection error (protected range or sheet) |
30 |
Formula error (overwrite blocked, parse failure) |
40 |
Conflict (fingerprint mismatch — workbook changed) |
50 |
IO error (file not found, locked, permission denied) |
60 |
Recalculation error |
70 |
Unsupported operation |
90 |
Internal error |
Agent integration
xl is designed to be driven by AI coding agents. Every command returns machine-parseable JSON, uses deterministic exit codes, and provides progressive discovery:
xl guide # full structured JSON orientation (start here)
xl --help # command group overview
xl table --help # group detail with examples
xl table add-column --help # command detail with usage examples
Token-optimized help (TOON)
When an LLM drives the CLI, verbose Rich-formatted --help output wastes tokens. Set LLM=true to switch all help to TOON (Token-Oriented Object Notation) — a compact key:value format that cuts token usage by ~75%.
# Enable TOON for the session
export LLM=true # bash/zsh
$env:LLM = "true" # PowerShell
xl --help
name: xl
description: Agent-first CLI for reading transforming and validating Excel workbooks
options[2]:
flag,type,required,default,help
--version/-V,flag,false,false,Print version and exit.
--human,flag,false,false,Force human-readable help (overrides LLM=true).
groups[11]:
name,description
cell,Read and write individual cell values.
table,Table operations — list add columns append rows.
...
The --human flag overrides LLM=true for a single invocation:
xl --human --help # Rich output even with LLM=true set
| Condition | Help format |
|---|---|
| Default (no env var) | Rich/Markdown (human-readable) |
LLM=true |
TOON (compact, token-optimized) |
LLM=true + --human |
Rich/Markdown (override) |
stdio server mode
For tool-use integrations (MCP, ACP, or custom):
xl serve --stdio
Reads JSON commands from stdin, writes JSON responses to stdout.
Development
# Install with dev dependencies
uv sync
# Run all tests
uv run pytest tests/ -v
# Run a specific test file
uv run pytest tests/test_cli.py -v
# Run the CLI directly
uv run xl --help
Tech stack
| Component | Library |
|---|---|
| Excel engine | openpyxl |
| CLI framework | Typer |
| Data models | Pydantic v2 |
| JSON serialization | orjson |
| SQL queries | DuckDB |
| Terminal output | Rich |
| YAML parsing | PyYAML |
| Structured logging | structlog |
| File locking | portalocker |
| Fingerprinting | xxhash |
| Build | hatchling |
Project structure
src/xl/
├── cli.py # Typer CLI app, all command definitions
├── contracts/ # Pydantic models (ResponseEnvelope, PatchPlan, WorkflowSpec)
├── engine/ # WorkbookContext, response dispatcher, verify, workflow runner
├── adapters/ # openpyxl engine, DuckDB queries
├── validation/ # Plan/workbook validators, policy engine
├── io/ # Fingerprint, backup, atomic write, file locking
├── observe/ # Timer and event utilities
├── diff/ # Workbook comparison logic
├── help/ # TOON help output for LLM consumers
└── server/ # stdio server mode
tests/ # Unit, integration, golden, property, performance tests
License
MIT
Metadata
Release files for xl-agent-cli 1.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| xl_agent_cli-1.6.1.tar.gz | 181.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xl_agent_cli-1.6.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 254.9 kB
Release files / xl_agent_cli-1.6.1.tar.gz
| Download URL | xl_agent_cli-1.6.1.tar.gz |
|---|---|
| Size | 181.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
693781814bac9093df9699085da99c4846246a7fee2737f0a0ab3e5cd3761424
|
|
BLAKE2b-256 checksum How to use checksums |
c8105de6ea589adb9810e3e0ce143949021aaecfbbfcb91ffc0d9a14c0a4c894
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 26, 2026.
Transparency logRelease files / xl_agent_cli-1.6.1-py3-none-any.whl
| Download URL | xl_agent_cli-1.6.1-py3-none-any.whl |
|---|---|
| Size | 73.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
980e5bae46c4d86ec2e123823ac64d148a2bd2ddfe3408d5b53d4118e9ab9a62
|
|
BLAKE2b-256 checksum How to use checksums |
1a02580f4f1486ec576f688def9be6c83997892875da0ee476cfc9ad8b23ceb8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 26, 2026.
Transparency log