Witan CLI

witan xlsx exec is the main spreadsheet interface for agents. It opens an Excel workbook, starts a sandboxed JavaScript runtime with the xlsx API preloaded, and exposes the live workbook as wb.

Use it when a task needs more than a single verifier command: read workbook structure, search for labels, trace dependencies, test formulas, write cells, recalculate, lint, and render from one script. The standalone render, calc, and lint commands remain useful for quick checks and CI, but exec is the workflow tool for agent-driven spreadsheet edits.

$ witan xlsx exec report.xlsx --code '
  const result = await xlsx.setCells(wb, [
    { address: "Summary!B5", formula: "=SUM(Revenue!B2:B13)" }
  ])
  const lint = await xlsx.lint(wb, { rangeAddresses: ["Summary!A1:C10"] })
  return { touched: result.touched, errors: result.errors, warnings: lint.total }
'
{"ok":true,"result":{"touched":{"Summary!B5":"$1,250,000","Summary!B6":"$187,500","Summary!B10":"$3,750,000"},"errors":[],"warnings":0},"writes_detected":true}

Witan Exec replaces the current Python scripting approach with a purpose-built JavaScript runtime that talks directly to a high-fidelity .NET spreadsheet engine. Today, your agent writes Python, runs it, reads the file back, writes more Python, and repeats — only ever approximating the spreadsheet with Python objects. With Witan Exec, your agent writes one script that reads, writes, recalculates, lints, and renders — all in a single execution, with results returned as structured JSON.

Installation

One binary, zero dependencies. Witan runs anywhere a shell does.

Quick install (macOS & Linux)

Detects your platform and installs the binary to /usr/local/bin:

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

PyPI

Ideal for sandboxed agent environments (Codex, Claude Code) where Python and uv are available but writing to /usr/local/bin is not:

# Zero-install one-shot
uvx witan xlsx render report.xlsx -r "Sheet1!A1:Z50"

# Persistent install
pip install witan

uvx downloads and runs the binary in a single command with no permanent install required — the fastest path when your agent can't modify system directories.

npm

Install the bundled CLI and Node.js SDK from npm. This requires Node.js 22 or later:

# Zero-install one-shot
npx witan xlsx render report.xlsx -r "Sheet1!A1:Z50"

# Persistent install
npm install witan

Use npx witan ... when you just need the CLI, or install witan when your project also uses the JavaScript SDK.

Manual download

If you prefer to download directly, pick your platform:

Platform Binary
macOS, Apple Silicon witan-darwin-arm64
macOS, Intel witan-darwin-amd64
Linux, x86_64 witan-linux-amd64
Linux, ARM64 witan-linux-arm64
Windows, x86_64 witan-windows-amd64.zip
Windows, ARM64 witan-windows-arm64.zip

Download and install in one line (example for macOS Apple Silicon):

curl -fsSL https://witanlabs.com/dl/witan-darwin-arm64 -o /usr/local/bin/witan && chmod +x /usr/local/bin/witan

Replace witan-darwin-arm64 with the binary name for your platform.

Windows (PowerShell)

Invoke-WebRequest https://witanlabs.com/dl/witan-windows-amd64.zip -OutFile witan.zip
Expand-Archive witan.zip -DestinationPath "$env:LOCALAPPDATA\witan"
$env:Path += ";$env:LOCALAPPDATA\witan"

Verify your installation

witan --version
witan xlsx exec report.xlsx --expr 'await xlsx.readCell(wb, "Summary!A1")'

If both commands succeed, you're ready to go.

Four engines in one tool

Witan Exec comes with all the tools you need in a single entry point.

The Edit Engine

Purpose-built methods for browsing and modifying workbooks, designed for agents rather than humans.

Reading & Discovery

Discover table structure automatically, then read by semantic label instead of hardcoded cell addresses.

readCell, readRange, readRow, readColumn
readRangeTsv, readRowTsv, readColumnTsv    // token-efficient TSV output
findCells, findRows                          // text/regex/number search
detectTables, tableLookup                    // automatic table detection
getUsedRange, listSheets, listDefinedNames

Writing & Structure

setCells                                     // values, formulas, formats
insertRowAfter, deleteRows
insertColumnAfter, deleteColumns
addSheet, deleteSheet, renameSheet

Dependency Tracing

Trace which inputs drive a bottom-line number, or find every formula that would break if you changed a cell.

getCellPrecedents, getCellDependents
traceToInputs, traceToOutputs

Formula Testing

Test what a formula would return without writing it to the workbook.

evaluateFormula, evaluateFormulas             // test without writing

Styling

getStyle, setStyle
getSheetProperties, setSheetProperties

This is a custom JavaScript library backed by a .NET RPC server — combining the best language for agent scripting with the best runtime for xlsx/xml fidelity.

The Rendering Engine

await xlsx.previewStyles(wb, "Sheet1!A1:F20")

Generate PNG screenshots of any cell range, directly in code.

The Formula Engine

const result = await xlsx.setCells(wb, [
  { address: "Sheet1!B5", formula: "=SUM(B1:B4)" }
])
print(result.errors)  // New formula errors, if any

Every write triggers automatic recalculation. Errors reported immediately.

The Linting Engine

const diagnostics = await xlsx.lint(wb)
print(diagnostics.filter(d => d.severity === "Warning"))

Run all 11 semantic rules against the workbook from within your code.

How it works

$ witan xlsx exec report.xlsx --expr 'await xlsx.readCell(wb, "Summary!A1")'

Witan spins up a sandboxed JavaScript runtime with @witan/xlsx pre-loaded. The workbook is available as the global wb. Your code calls methods on the xlsx object with access to 30+ methods for every spreadsheet operation. The result is returned as structured JSON.

Example scenario

Discover, look up, trace, test, write, and verify

// 1. Discover the workbook's structure
const tables = await xlsx.detectTables(wb)
const summary = tables.find(t => t.tableName === "Summary")

// 2. Look up a value by label, not by hardcoded cell address
const revenue = await xlsx.tableLookup(wb, {
  table: summary.address,
  rowLabel: "Total Revenue",
  columnLabel: "Q4"
})

// 3. Check what depends on this cell before changing it
const downstream = await xlsx.getCellDependents(wb, revenue.address, 3)
print(`Updating ${revenue.address} will affect ${downstream.cells.length} cells`)

// 4. Test a formula before writing it
const preview = await xlsx.evaluateFormula(wb, "Summary",
  `=${revenue.address}*1.1`)

// 5. Write the update
const result = await xlsx.setCells(wb, [{
  address: revenue.address,
  formula: `=${revenue.address.replace("Q4","Q3")}*1.1`
}])

// 6. Verify — lint and screenshot
const lint = await xlsx.lint(wb, { rangeAddresses: [summary.address] })
await xlsx.previewStyles(wb, summary.address)

return { updated: revenue.address, downstream: downstream.cells.length,
         warnings: lint.diagnostics }

One tool call. Discover structure, look up by label, check impact, test a formula, write, lint, and screenshot — all in one execution.

Access tracking

Every exec call returns an accesses array showing exactly which cells were read and written:

{
  "ok": true,
  "stdout": "...",
  "result": { },
  "writes_detected": true,
  "accesses": [
    { "operation": "read", "address": "Summary!B2:B10" },
    { "operation": "write", "address": "Summary!B5" },
    { "operation": "write", "address": "Summary!B6" }
  ]
}

CLI reference

witan xlsx exec <file> [flags]
Flag Description Default
--code Inline JavaScript source
--script Path to a JavaScript file
--stdin Read JavaScript from stdin
--expr Expression shorthand (wrapped as return (<expr>);)
--input-json JSON value passed as input to the script {}
--timeout-ms Execution timeout in milliseconds (max 90000) 30000
--max-output-chars Maximum stdout characters to capture (max 50000) 20000
--save Persist writes and overwrite local workbook false
--json Output full response envelope false

Exactly one of --code, --script, --stdin, or --expr is required.

Output format

Success

{"ok":true,"stdout":"...","result":<json>,"writes_detected":<bool>,"accesses":[...]}

Failure

{"ok":false,"stdout":"...","error":{"type":"...","code":"...","message":"..."}}

Usage examples

# Quick cell lookup
witan xlsx exec report.xlsx --expr 'await xlsx.readCell(wb, "Summary!A1")'

# Run a script file with parameters
witan xlsx exec report.xlsx --script ./analyze.js --input-json '{"threshold":10}'

# Edit and save
witan xlsx exec report.xlsx --save --code '
  await xlsx.setCells(wb, [{ address: "Sheet1!A1", value: "Updated" }])
  return { ok: true }
'

# Pipe from stdin
cat analysis.js | witan xlsx exec report.xlsx --stdin

Supported file formats

  • .xlsx (Office Open XML)
  • .xls (Excel 97–2003)
  • .xlsm (macro-enabled — macros are preserved in the file but not executed)

Next steps

  • Quickstart — run your first commands.
  • Concepts — understand how the runtime, the workbook handle, and access tracking fit together.
  • Skills — install the skills that teach your agent how to use Witan.