The spreadsheet toolkit for coding agents — edit, render, calculate, and lint Excel workbooks.
Documentation | How we built it
curl -fsSL https://witanlabs.com/agents/install.sh | shDownload the latest artifacts from:
Example (macOS Apple Silicon):
curl -fsSL https://github.com/witanlabs/witan-cli/releases/latest/download/witan-darwin-arm64.tar.gz | tar -xz
install -m 0755 witan /usr/local/bin/witanInstall from PyPI (recommended for sandboxed agent environments):
# one-shot run without permanent install
uvx witan --help
# persistent install
pip install witanRequires Go (version from go.mod):
go install github.com/witanlabs/witan-cli@latest# Authenticate (recommended)
witan auth login
# Render a range
witan xlsx render report.xlsx -r "Sheet1!A1:F20"
# Recalculate formulas
witan xlsx calc report.xlsx
# Lint formulas
witan xlsx lint report.xlsx
# Run JS against workbook
witan xlsx exec report.xlsx --expr 'await xlsx.readCell(wb, "Summary!A1")'
# Create a new workbook from scratch
witan xlsx exec model.xlsx --create --save --code 'await xlsx.addSheet(wb, "Inputs"); return true'
# Author a ListObject table in one call
witan xlsx exec model.xlsx --save --stdin <<'WITAN'
await xlsx.addListObject(wb, "Sheet1", {
name: "SalesTable",
ref: "A1:C4",
showTotalsRow: true,
columns: [
{ name: "Region", totalsRowLabel: "Total" },
{ name: "Sales", totalsRowFunction: "sum" },
{ name: "DoubleSales", calculatedColumnFormula: "=B2*2" }
],
rows: [
[{ value: "North" }, { value: 10 }, {}],
[{ value: "South" }, { value: 20 }, {}]
]
})
return await xlsx.readRange(wb, "SalesTable")
WITAN
# Author a What-If Data Table block
witan xlsx exec model.xlsx --save --stdin <<'WITAN'
await xlsx.addDataTable(wb, "Sheet1", {
type: "oneVariableColumn",
ref: "E1:F4",
columnInputCell: "H1",
inputValues: [5, 10, 15],
formulas: ["=H1*2"]
})
return await xlsx.getDataTable(wb, "Sheet1!E1:F4")
WITAN
# Author a chart from workbook data
witan xlsx exec dashboard.xlsx --save --stdin <<'WITAN'
await xlsx.addChart(wb, "Summary", {
name: "Revenue",
position: { from: { cell: "F2" }, to: { cell: "N18" } },
groups: [
{
type: "column",
series: [
{
name: { ref: "Data!B1" },
categories: "Data!A2:A9",
values: "Data!B2:B9"
}
]
}
],
title: { text: "Revenue" },
legend: { position: "right" }
})
await xlsx.previewStyles(wb, "Summary!F2:N18")
WITANwitan-cli currently exposes four spreadsheet commands:
witan xlsx calcwitan xlsx execwitan xlsx lintwitan xlsx render
The lower-level Witan spreadsheet runtime supports broader workbook operations; this CLI focuses on the four agent-facing workflows above.
Authentication can be done via witan auth login, --api-key, or WITAN_API_KEY.
Use witan auth status to inspect the active credential, validation state, and selected organization.
Environment variables:
WITAN_API_KEY: API key (optional when usingwitan auth login)WITAN_API_URL: API base URL override (default:https://api.witanlabs.com)WITAN_STATELESS: set1ortrueto force stateless modeWITAN_CONFIG_DIR: override config directory (default:~/.config/witan)WITAN_MANAGEMENT_API_URL: management API override for auth login/token exchange
Modes:
- Stateful (default when authenticated): uploads workbook revisions and reuses them across commands
- Stateless (
--statelessorWITAN_STATELESS=1): sends workbook bytes on every request, no server-side file reuse
witan xlsx exec --create always uses the stateless exec endpoint and only supports new .xlsx targets.
Limits:
- Workbook inputs must be
<= 25MB.
# build local binary
make build
# run test suite
make test
# static checks
make vet
make format-check
# build release artifacts into dist/
make dist VERSION=v0.1.0
# build PyPI wheels (stable tags only)
make pypi-wheels VERSION=v0.1.0The local binary is written to ./witan.
Releases are handled by GitHub Actions:
- Publish workflow:
.github/workflows/witan-cli-release.yml(triggered by pushingv*tags) - Artifacts:
witan-darwin-arm64.tar.gzwitan-darwin-amd64.tar.gzwitan-linux-amd64.tar.gzwitan-linux-arm64.tar.gzwitan-windows-amd64.zipwitan-windows-arm64.zipwitan-install.shwitan-*.whl(PyPI wheels for supported platforms; stable tags only)witan-checksums.txt
PyPI publishing:
- Stable tags (
vX.Y.Z) publish wheels to PyPI using GitHub OIDC trusted publishing. - Pre-release tags (for example
v1.2.3-rc.1) skip PyPI publish.
GitHub release publishing:
- The workflow uploads artifacts directly to the matching GitHub Release tag.
- If the release already exists (for example, created in the GitHub UI), assets are attached with
--clobber.
Cutting a release (UI-driven):
- Add release notes under
## UnreleasedinCHANGELOG.md. - Create a GitHub Release in the UI with a new tag
vX.Y.Z(or prerelease tagvX.Y.Z-suffix). - Tag push triggers
Witan CLI Release. - The workflow builds artifacts, attaches them to the GitHub Release, and publishes to PyPI for stable tags.
- On successful release, CI runs
scripts/roll-changelog.sh, pushes the changelog update to achore/changelog-release-X.Y.Zbranch, and opens a PR into the default branch. - For stable tags, verify
witan==X.Y.Zon PyPI andwitan --version.
Manual git tag ... && git push ... is equivalent to UI tag creation and triggers the same workflow.
Go CI runs in .github/workflows/golang.yml on pushes to main and pull requests.