Quickstart
Install Witan, create a workbook, and run the main verification commands. No signup or API key is required for the personal tier.
1. Install
Install the standalone binary with the quick install script:
curl -fsSL https://witanlabs.com/install.sh | sh
This detects your platform and installs the witan binary to /usr/local/bin by default. For Windows, sandboxed environments, or manual release downloads, see the CLI Scripting page.
Alternatively, use npm or PyPI when those runtimes fit your environment better:
# npm one-shot
npx witan --help
# npm project install
npm install witan
# PyPI one-shot
uvx witan --help
# PyPI persistent install
pip install witan
The npm package includes the CLI and JavaScript SDK. The PyPI package includes the CLI and Python SDK.
2. Install the agent skills
If you use Witan through Claude Code, Codex, Deep Agents, or another skills-aware agent, install the Witan skills in your project:
npx skills add witanlabs/witan-cli
This adds the Witan skills — for spreadsheet work, witan-xlsx and witan-read-source. Agent workflows should install them before working with Witan so the agent has commands, flags, and workbook workflows in context. See Agent Skills for platform-specific notes.
3. Verify
witan --version
If the command prints a version string, the CLI is installed.
4. Create a workbook
Create a small workbook in the current directory:
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
The workbook is exposed inside the JavaScript runtime as the global wb. Methods on xlsx do the work. The result is returned as JSON, and --save writes quickstart.xlsx to disk.
--create refuses to overwrite an existing file. If you rerun this step, delete quickstart.xlsx first or choose a different filename.
5. Read a cell
witan xlsx exec quickstart.xlsx --expr 'await xlsx.readCell(wb, "Summary!C4")'
This reads the Q2 profit formula result from the workbook you just created.
6. Render a range
witan xlsx render quickstart.xlsx -r "Summary!A1:C4"
The command prints the output image path and render metadata:
/var/folders/xx/.../witan-render-123456.png
Summary!A1:C4 | ~384×120px | dpr=2
7. Recalculate
witan xlsx calc quickstart.xlsx
By default this runs a full-workbook calculation, writes updated cached formula values back to disk, and reports formula errors. Use --verify for a non-mutating check, or --range to seed recalculation from specific ranges while still following downstream dependents.
You do not need a separate calc step after normal exec writes. Write APIs such as xlsx.setCells recalculate affected formulas before returning.
8. Lint
witan xlsx lint quickstart.xlsx
Fifteen rules check for issues recalculation can miss, including overlapping SUMs, unsorted lookups, mixed currencies, clipped display values, and overlapping objects.
Combine operations in one script
exec can chain workbook operations into one request. This example discovers structure, looks up a value by label, writes it, and verifies the result:
witan xlsx exec quickstart.xlsx --save --stdin <<'WITAN'
const summary = await xlsx.describeSheet(wb, "Summary")
const tables = Object.values(summary.tables)
const table = tables[0]
if (!table) throw new Error("No table detected on Summary")
const [q2Revenue] = await xlsx.tableLookup(wb, {
table: table.address,
rowLabel: "Revenue",
columnLabel: "Q2"
})
if (!q2Revenue) throw new Error("Q2 revenue not found")
const result = await xlsx.setCells(wb, [{
address: q2Revenue.address,
value: 1650,
format: "$#,##0"
}])
const profit = await xlsx.readCell(wb, "Summary!C4")
const lint = await xlsx.lint(wb, { rangeAddresses: [table.address] })
return { updated: q2Revenue.address, profit: profit.text,
errors: result.errors, warnings: lint.total }
WITAN
The --save flag persists writes to the file on disk. Without it, exec is non-persistent by default: your script can modify the live workbook session, recalculate, lint, and inspect the result, but the local workbook file is not overwritten.