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.

Skills under skills/ are published independently of CLI releases — merging to main makes them live for npx skills add witanlabs/witan-cli. Each skill carries its own version; see skills/README.md.

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, plus skill checks (SKILL.md size limit, per-skill version bump on change).

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

Uploaded Python 3Windows ARM64

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

Uploaded Python 3Windows x86-64

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

Uploaded Python 3musllinux: musl 1.2+ x86-64

witan-0.13.0-py3-none-musllinux_1_2_aarch64.whl (3.4 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

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

Uploaded Python 3manylinux: glibc 2.17+ x86-64

witan-0.13.0-py3-none-manylinux_2_17_aarch64.whl (3.4 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

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

Uploaded Python 3macOS 11.0+ ARM64

witan-0.13.0-py3-none-macosx_10_15_x86_64.whl (3.8 MB view details)

Uploaded Python 3macOS 10.15+ x86-64

File details

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

File metadata

  • Download URL: witan-0.13.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.13.0-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 b34a2217c83ebb6efbd20cc5d58ea3be872ac7a7fa966255b6e3a4c86fbfa254
MD5 3a58ba7d78e6a2d275ee37655f799ee5
BLAKE2b-256 89356cf9292af8415f73c67ef0ad716a7f760350a644efe2eae70a92b9180c7c

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: witan-0.13.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.13.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 349d6cf64e49321c19c554395d295ba3d8861f2f732e835564a221fc6b044f55
MD5 abac865a9842d5e6f5347d42e0cb1f13
BLAKE2b-256 d1615f0ecf4081dc3fecaa374ef4d82f8318542ca81068128654fe3250971c9f

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.13.0-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 eba1441dc72b4b71b5b9276259ea7feeadf55dc20859ae49dc267313c5afc2de
MD5 e3187a425808281c56d8530f0c1f8b76
BLAKE2b-256 9551b51a461fbd013361f22c304af25be07728b68be3dcdc29a3362b3357936d

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.13.0-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 9e41e39148cf5cb51214161834e7ddd9ee58bb0e2feb1a448374b9419a067068
MD5 6d5cec1555b98838b70ba67a4bb5a2f5
BLAKE2b-256 8a6d264d71b8cb4c08a985728481ccbd28fe1e837382f4d3db2cc3f8a425e2dd

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.13.0-py3-none-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 e91b0e803870e0a0804e10daa57c285a10487ab9a12be604b2d4adc1a3b3f87a
MD5 d040756cc68961ea2e7167f4e5fbc32f
BLAKE2b-256 5e15784488eb01f490b5907897c942a87bd9eec32a681d85e366d00005fe498e

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.13.0-py3-none-manylinux_2_17_aarch64.whl
Algorithm Hash digest
SHA256 db79a048a6ebf634373d56031ca3cb98c4a9f8160fb829bba0a64bc66fcb88c6
MD5 7fc0c623e7cd3d7c2d04715d2fe6c3f1
BLAKE2b-256 47a52787adbf94ed38da102f2459692e38d7a4252609b7c6efe7819fd82a2573

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.13.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 020fc124e300acce8ba1bc4dd9603604dc81cb62711438152c2389a9fb52bbb2
MD5 68fde373f7e5788fd4b6c7116e0e7296
BLAKE2b-256 bafeb4e7367792e2cbea04bbd147376a35930eb040c190e16edbb9dda59e61dc

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for witan-0.13.0-py3-none-macosx_10_15_x86_64.whl
Algorithm Hash digest
SHA256 b1ce8f5823449fff2b00fed69844f61ca3898e5386ea7edced3a2a6a90b766eb
MD5 44339d333b45bd83e8c3ac6f1f92eb07
BLAKE2b-256 bb13733e6b01fd6abb2ed5e530cfe502c4b413b9596f4efb762c89c797f3b123

See more details on using hashes here.

Provenance

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