Skip to main content

Witan spreadsheet CLI and Python SDK for coding agents

Project description

witan-cli

The spreadsheet toolkit for coding agents — edit, render, calculate, and lint Excel workbooks.

Documentation | How we built it

Install

Quick Install Script

curl -fsSL https://witanlabs.com/install.sh | sh

Or try without installing: npx witan / uvx witan

From GitHub Releases

Download 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/witan

From PyPI

Install the bundled CLI and Python SDK from PyPI:

# one-shot run without permanent install
uvx witan --help

# persistent install
pip install witan

Python SDK example:

from witan import Workbook

with Workbook("report.xlsx") as wb:
    sheets = wb.list_sheets()
    tsv = wb.read_range_tsv("Summary!A1:F20")

Create and save a new workbook:

from witan import Workbook

with Workbook("model.xlsx", create=True) as wb:
    wb.add_sheet("Inputs")
    wb.set_cells([{"address": "Inputs!A1", "value": "Revenue"}])
    wb.save()

Async sessions are available for asyncio applications:

from witan import AsyncWorkbook

async with AsyncWorkbook("report.xlsx") as wb:
    cell = await wb.read_cell("Summary!A1")

Notebook and REPL sessions can use an explicit close instead of a context manager:

from witan import Workbook

wb = Workbook("report.xlsx")
tsv = wb.read_range_tsv("Summary!A1:F20")
wb.close()

In Jupyter/IPython, async sessions can use top-level await:

from witan import AsyncWorkbook

wb = AsyncWorkbook("report.xlsx")
cell = await wb.read_cell("Summary!A1")
await wb.close()

From npm

Install the bundled CLI and Node.js SDK from npm (requires Node.js 22+):

npm install witan

Node.js SDK example:

import { Workbook } from 'witan';

await using wb = await Workbook.open('report.xlsx');
const sheets = await wb.listSheets();
const tsv = await wb.readRangeTsv('Summary!A1:F20');

Create and save a new workbook:

import { Workbook } from 'witan';

await using wb = await Workbook.open('model.xlsx', { create: true });
await wb.addSheet('Inputs');
await wb.setCells([{ address: 'Inputs!A1', value: 'Revenue' }]);
await wb.save();

Alternative: explicit close

import { Workbook } from 'witan';

const wb = await Workbook.open('report.xlsx');
const tsv = await wb.readRangeTsv('Summary!A1:F20');
await wb.close();

From Source

Requires Go (version from go.mod):

go install github.com/witanlabs/witan-cli@latest

Quick Start

Run any command with npx witan or uvx witan without installing.

# Authenticate (recommended)
witan auth login

# Create a workbook from scratch
witan xlsx exec quickstart.xlsx --create --save --stdin <<'WITAN'
await xlsx.addSheet(wb, "Summary")
await xlsx.setCells(wb, [
  { address: "Summary!A1", value: "Metric" },
  { address: "Summary!B1", value: "Q1" },
  { address: "Summary!C1", value: "Q2" },
  { address: "Summary!A2", value: "Revenue" },
  { address: "Summary!B2", value: 1200, format: "$#,##0" },
  { address: "Summary!C2", value: 1500, format: "$#,##0" },
  { address: "Summary!A3", value: "Costs" },
  { address: "Summary!B3", value: 700, format: "$#,##0" },
  { address: "Summary!C3", value: 850, format: "$#,##0" },
  { address: "Summary!A4", value: "Profit" },
  { address: "Summary!B4", formula: "=B2-B3", format: "$#,##0" },
  { address: "Summary!C4", formula: "=C2-C3", format: "$#,##0" }
])
return await xlsx.readRange(wb, "Summary!A1:C4")
WITAN

# Render a range
witan xlsx render quickstart.xlsx -r "Summary!A1:C4"

# Recalculate formulas
witan xlsx calc quickstart.xlsx

# Lint formulas
witan xlsx lint quickstart.xlsx

# Run JS against workbook
witan xlsx exec quickstart.xlsx --expr 'await xlsx.readCell(wb, "Summary!C4")'

# 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")
WITAN

What This CLI Covers

witan-cli exposes four spreadsheet commands:

  • witan xlsx calc
  • witan xlsx exec
  • witan xlsx lint
  • witan xlsx render

The PyPI package also exposes witan.Workbook and witan.AsyncWorkbook, backed by witan xlsx rpc subprocess sessions. Public SDK methods use snake_case names matching the xlsx exec operation surface, such as read_range_tsv, find_cells, sweep_inputs, set_cells, add_chart, and set_conditional_formatting.

The lower-level Witan spreadsheet runtime supports broader workbook operations; this CLI focuses on the four agent-facing workflows above.

Auth, Config, and Modes

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 using witan auth login)
  • WITAN_API_URL: API base URL override (default: https://api.witanlabs.com)
  • WITAN_STATELESS: set 1 or true to force stateless mode
  • WITAN_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 (--stateless or WITAN_STATELESS=1): sends workbook bytes on every request, no server-side file reuse

In stateful mode, load-balancer affinity cookies are persisted at ~/.config/witan/cookies.json or $WITAN_CONFIG_DIR/cookies.json when WITAN_CONFIG_DIR is set.

witan xlsx exec --create always uses the stateless exec endpoint and only supports new .xlsx targets.

Limits:

  • Workbook inputs must be <= 25MB.

Development

# 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.0

The local binary is written to ./witan.

Release Process

Releases are handled by GitHub Actions:

  • Publish workflow: .github/workflows/witan-cli-release.yml (triggered by pushing v* tags)
  • Artifacts:
    • witan-darwin-arm64.tar.gz
    • witan-darwin-amd64.tar.gz
    • witan-linux-amd64.tar.gz
    • witan-linux-arm64.tar.gz
    • witan-windows-amd64.zip
    • witan-windows-arm64.zip
    • witan-install.sh
    • witan-*.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):

  1. Add release notes under ## Unreleased in CHANGELOG.md.
  2. Create a GitHub Release in the UI with a new tag vX.Y.Z (or prerelease tag vX.Y.Z-suffix).
  3. Tag push triggers Witan CLI Release.
  4. The workflow builds artifacts, attaches them to the GitHub Release, and publishes to PyPI for stable tags.
  5. On successful release, CI runs scripts/roll-changelog.sh, pushes the changelog update to a chore/changelog-release-X.Y.Z branch, and opens a PR into the default branch.
  6. For stable tags, verify witan==X.Y.Z on PyPI, witan --version, python -m witan --version, and from witan import Workbook, AsyncWorkbook.

Manual git tag ... && git push ... is equivalent to UI tag creation and triggers the same workflow.

CI

Go and Python CI runs in .github/workflows/golang.yml on pushes to main and pull requests. The workflow runs go test, go vet, pytest, mypy, and python -m compileall python/witan.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

witan-0.10.3-py3-none-win_arm64.whl (3.4 MB view details)

Uploaded Python 3Windows ARM64

witan-0.10.3-py3-none-win_amd64.whl (3.8 MB view details)

Uploaded Python 3Windows x86-64

witan-0.10.3-py3-none-musllinux_1_2_x86_64.whl (3.7 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

witan-0.10.3-py3-none-musllinux_1_2_aarch64.whl (3.3 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

witan-0.10.3-py3-none-manylinux_2_17_x86_64.whl (3.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

witan-0.10.3-py3-none-manylinux_2_17_aarch64.whl (3.3 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

witan-0.10.3-py3-none-macosx_11_0_arm64.whl (3.4 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

witan-0.10.3-py3-none-macosx_10_15_x86_64.whl (3.7 MB view details)

Uploaded Python 3macOS 10.15+ x86-64

File details

Details for the file witan-0.10.3-py3-none-win_arm64.whl.

File metadata

  • Download URL: witan-0.10.3-py3-none-win_arm64.whl
  • Upload date:
  • Size: 3.4 MB
  • Tags: Python 3, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for witan-0.10.3-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 8659fd5014e837c2e99254fefbe075eaac29d72afa5bb33dcfe487befd42f6a9
MD5 3bb843797bcd6ce3fc8ae83c03d982c1
BLAKE2b-256 a3069dd5540baef7b9b200665daab6f2e69f65af9612f9ab0f27018eedb4dbed

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.3-py3-none-win_arm64.whl:

Publisher: witan-cli-release.yml on witanlabs/witan-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file witan-0.10.3-py3-none-win_amd64.whl.

File metadata

  • Download URL: witan-0.10.3-py3-none-win_amd64.whl
  • Upload date:
  • Size: 3.8 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for witan-0.10.3-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 01ac1c467892282941677e70aa28901f6d664e4226fbbec17a4f43d8aff72574
MD5 55db8686c8e454af9fbe9dbb876dc77e
BLAKE2b-256 a6df704dd4d02796d1cc14e92a7e12521c7419639c9148c289952161893830ba

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.3-py3-none-win_amd64.whl:

Publisher: witan-cli-release.yml on witanlabs/witan-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file witan-0.10.3-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for witan-0.10.3-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 ec99036e93eb9c59b3a62db258be0bc871b4fb19c712428a6297e2b33df9c255
MD5 a30d096a0b16ef7e97ca84a35498175b
BLAKE2b-256 639d485e3324c6719dbc073264402a6ce0db7dc1f89076f051dd4e47446513c6

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.3-py3-none-musllinux_1_2_x86_64.whl:

Publisher: witan-cli-release.yml on witanlabs/witan-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file witan-0.10.3-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for witan-0.10.3-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 b61feb0ca7df15a9ee28d25df4b59a7f746671ce0083dfe89e3def99eb20f70a
MD5 9afaa4bf2e70bb9020775561dd6e8190
BLAKE2b-256 78e980848c2d5a3ed834678cf0b552d561672a83f71aaf382cbf1a93990e77a0

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.3-py3-none-musllinux_1_2_aarch64.whl:

Publisher: witan-cli-release.yml on witanlabs/witan-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file witan-0.10.3-py3-none-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for witan-0.10.3-py3-none-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 bf8dfd2663acf924738639d32b96c464039e2ce36af744d514c70b02d0d6542e
MD5 2f00686ae7866130b57462de7e6e960e
BLAKE2b-256 582fb634ed32ae467b8b9a657fa91371cb8b2ec068b8f84c0e167b91a126773d

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.3-py3-none-manylinux_2_17_x86_64.whl:

Publisher: witan-cli-release.yml on witanlabs/witan-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file witan-0.10.3-py3-none-manylinux_2_17_aarch64.whl.

File metadata

File hashes

Hashes for witan-0.10.3-py3-none-manylinux_2_17_aarch64.whl
Algorithm Hash digest
SHA256 85bc938a4218cf3c51cb8e54d6f8e3f348e8421e39043f489617118d6d335a82
MD5 a6813a1621ca8c6d95ebefbe2dfc1db2
BLAKE2b-256 a43ccfd9ca23708c47d12e5658001ef23ca04ae69fb053f584b17cd5ce08dfbf

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.3-py3-none-manylinux_2_17_aarch64.whl:

Publisher: witan-cli-release.yml on witanlabs/witan-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file witan-0.10.3-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for witan-0.10.3-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a73911307df1c3cab2eb6e7d8bd4bd6b7ef13f30c6ae632d493c717d05e2bfa2
MD5 8c1cbc545591527eb20a04ed7ee02486
BLAKE2b-256 198ebf984734c070f8a9ce4477afb4338548224204a7b3fd17402305c2016d4c

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.3-py3-none-macosx_11_0_arm64.whl:

Publisher: witan-cli-release.yml on witanlabs/witan-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file witan-0.10.3-py3-none-macosx_10_15_x86_64.whl.

File metadata

File hashes

Hashes for witan-0.10.3-py3-none-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 505d73de079bf0dc704c76ddf434d12a4822bd8a944ac8e9bf88f59487514703
MD5 335b11204cb8f563642acd9fc26533bb
BLAKE2b-256 5b97d71299f72f2dd0cda9d370ff47894b697db535d787e593d6938a29314482

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.3-py3-none-macosx_10_15_x86_64.whl:

Publisher: witan-cli-release.yml on witanlabs/witan-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page