Skip to main content

Witan spreadsheet CLI and Python SDK for coding agents

Project description

witan-cli

The Office toolkit for coding agents — edit, render, calculate, and lint Excel workbooks, plus render and script PPTX files.

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

# Render a PPTX slide
witan pptx render deck.pptx --slide 1 -o slide-1.png

# Run Office.js-compatible JavaScript against a PPTX file
witan pptx exec deck.pptx --expr 'PowerPoint.run(async context => { const count = context.presentation.slides.getCount(); await context.sync(); return count.value })'

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

Uploaded Python 3Windows ARM64

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

Uploaded Python 3Windows x86-64

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

Uploaded Python 3musllinux: musl 1.2+ x86-64

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

Uploaded Python 3musllinux: musl 1.2+ ARM64

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

Uploaded Python 3manylinux: glibc 2.17+ x86-64

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

Uploaded Python 3manylinux: glibc 2.17+ ARM64

witan-0.11.0-py3-none-macosx_11_0_arm64.whl (3.5 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

witan-0.11.0-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.11.0-py3-none-win_arm64.whl.

File metadata

  • Download URL: witan-0.11.0-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.11.0-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 93d97b0cf021d368d71417708bad47fd6fe286ada0830098af15f82ac1973a4a
MD5 d1bb7ecda80d8c2875b89992514572e0
BLAKE2b-256 c177e0a5d96d66f1345350ed36c70d038f06621b5f84ffaea9647bc23a3c136d

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: witan-0.11.0-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.11.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 b33d1e464d95530c0daaa2acb8b3fb1186aae6ae94a0e286488efd0436432722
MD5 e5fb82191a1d1e141c1b2f308e6ed9c2
BLAKE2b-256 f19129001dfd2dc9655d8cb92d3a9838d627eec6ef4f0ad2af4ce6afdcc52611

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.11.0-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 2fd9a3b3e95dfcfcd93290290ab07cf3407ee44be9b26b8ac341b11b10e303aa
MD5 1827adc2598f6c103624e803e201d050
BLAKE2b-256 cba02bc4e4772fd53df2968eaf085a9081cd245d65aa3d57d5291e5898c1f6be

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.11.0-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 fd4ef8c603e7bdb68641cc371c4bd72a9f40e5ac0071ac63099b79904f85a8c1
MD5 21054a48d2d8ddf169ffc292986a36b5
BLAKE2b-256 1fef6277788cbfed2d79bee63ac5abe08752c9aed8a2fcfafb324503c8eeac17

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.11.0-py3-none-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 beb4b22ee59658477ab36a39638ac3f3f734f3d8f75652f64866bfc86cfa3008
MD5 fd9eee2871b6d1323caf65b5d6be5de2
BLAKE2b-256 869956630efb08142a84d81f2f5abb52b487bf8c9a579b7a16f4e022d04f7b63

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.11.0-py3-none-manylinux_2_17_aarch64.whl
Algorithm Hash digest
SHA256 e991ca19167ee8a8eb12d89886a3975e9e6c2ae49916932ebf07bd74e460ddc3
MD5 5c6577a4c0bec67695b650a386875e4f
BLAKE2b-256 e87a8f7a49387f86eb4b74728956be49ac5974f997c4140c15c7c486af133b34

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.11.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 619f9cba363b4129c65aa3eaf711db5e8cc7cfc22ea7c969badc2a29b9f7c28f
MD5 507fcb27b1a4aaacb9f7f17225676055
BLAKE2b-256 422ff2f348f61d1f81b01ee241e350501686dfcf9031759761d93d1f9cac5939

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.11.0-py3-none-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 ca8b39aa5d0fba7290341574b3b14d377fdbf9caa6255c4cef992c0af3dcb589
MD5 f3a0e0dfa98dc161cc103a8d855efe39
BLAKE2b-256 5581a387f74d89e91d9376151eca58e74cf88e6f321c1143b6c68364b7074fe2

See more details on using hashes here.

Provenance

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