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.4-py3-none-win_arm64.whl (3.4 MB view details)

Uploaded Python 3Windows ARM64

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

Uploaded Python 3Windows x86-64

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

Uploaded Python 3musllinux: musl 1.2+ x86-64

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

Uploaded Python 3musllinux: musl 1.2+ ARM64

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

Uploaded Python 3manylinux: glibc 2.17+ x86-64

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

Uploaded Python 3manylinux: glibc 2.17+ ARM64

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

Uploaded Python 3macOS 11.0+ ARM64

witan-0.10.4-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.4-py3-none-win_arm64.whl.

File metadata

  • Download URL: witan-0.10.4-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.4-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 aac2828c0142eec5667010d9436cdfd6c73249f836accbefef39b6754171ce09
MD5 911c7918121083898f1baf64d4f684bf
BLAKE2b-256 5324ece00490511d69062ca108320a217d9dc822952ae65a7baadda697d23454

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.4-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.4-py3-none-win_amd64.whl.

File metadata

  • Download URL: witan-0.10.4-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.4-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 8931f0de25c8bf34b244c717a85873e6f7e205c8052027da5fabdcaf1eaa5f36
MD5 0934b5486492b36074f0611ddd66aad0
BLAKE2b-256 a68c6d44dd7742ce347e68877daf2c40914de925015416765f95cd98eb6548d4

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.4-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.4-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for witan-0.10.4-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 7530f903d954ef001a23bdfdbe4479a0586bbe9056670234261e6a45c1322da8
MD5 ca5e3a3e709f938f54ee106871875029
BLAKE2b-256 68d1238cfbb02b4f4a84ba8f392fde346666b61093ca4fdb5624ccad09545e22

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.4-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.4-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for witan-0.10.4-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 422e13f3e42cb291b8eded58ecac658a14ba768949a89be219d8786ddaf10c5d
MD5 acd00b173c16c727469e5e23d29b84bf
BLAKE2b-256 d73f31e1c977aceda6104025763ae2548ee9ed502040fe594f8bb6ea36c34cd9

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.4-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.4-py3-none-manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for witan-0.10.4-py3-none-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 3cc5c5666b37172f722a9e38366ccf18540b2ed3b10c49dfaf5715fde40d0b55
MD5 d957c3916f751b5514eca481387cb004
BLAKE2b-256 56b177cc36140819e6fbf4b154c57a14b3725d2298864befe25609e6ab51b83a

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.4-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.4-py3-none-manylinux_2_17_aarch64.whl.

File metadata

File hashes

Hashes for witan-0.10.4-py3-none-manylinux_2_17_aarch64.whl
Algorithm Hash digest
SHA256 5f83b3f8cccfe5e499204424f7d9ae45ebfa1f9220d9ba22bd2c53ccf0b6c8ca
MD5 40250fb43246476b64cd538af3ec6abb
BLAKE2b-256 d787a47cca63bf18de4890ae7cbbf72b75ae0456ea98b27587e80d0740d13456

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.4-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.4-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for witan-0.10.4-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 be9cb7ea02513f6b4742cccafef27c95a79511d845748a148c15b4dce295ad94
MD5 53afe082949b6632dee9cb1f6bafa1d2
BLAKE2b-256 fadd50e8b1cbd1979b9b48f5f44080209a5f166a856bf64e302b5f2c4455ba0b

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.4-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.4-py3-none-macosx_10_15_x86_64.whl.

File metadata

File hashes

Hashes for witan-0.10.4-py3-none-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 9ffba5a51253b72fb451541239b686572a11ea6915706ede784f7bbd9cc7b3dd
MD5 1c011e5b3c2b56f590ec48c5f8c68fab
BLAKE2b-256 1dab109d3832184d2805332363eefb65c59184a9ba37cf235c88178cad63ea62

See more details on using hashes here.

Provenance

The following attestation bundles were made for witan-0.10.4-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