Package chooser
Install the package that matches the job.
Start with the narrow npm package for your problem. The full headless runtime is still available when you need lower-level APIs or agent metadata.
Start with the SheetJS-named package before moving to a full workbook runtime. The bridge example covers SheetJS, xlsx-populate, and ExcelJS with one workbook.
npx --package @bilig/sheetjs-formula-recalc sheetjs-recalc --demo --json
npx --package @bilig/xlsx-formula-recalc xlsx-recalc --demo --json
npx --package @bilig/sheetjs-formula-recalc sheetjs-recalc pricing.xlsx \
--set Inputs!B2=48 \
--read Summary!B7 \
--out pricing.recalculated.xlsx \
--json
npm install @bilig/workpaper
Formula workbook state for Node services, queue workers, route handlers, tests, and agent tools.
agent
npm create @bilig/workpaper@latest pricing-agent -- --agent
Build an agent spreadsheet tool that writes inputs, recalculates formulas, verifies readback, and persists state.
xlsx
npm install @bilig/xlsx-formula-recalc
Edit XLSX inputs, recalculate formulas in Node, read proof values, and export the updated workbook.
sheetjs
npm install @bilig/sheetjs-formula-recalc
Keep SheetJS / xlsx for file I/O and add the live formula-readback bridge after input edits.
exceljs
npm install exceljs @bilig/exceljs-formula-recalc
Keep ExcelJS for workbook files and add recalculated formula readback when cached values are stale.
runtime
npm install @bilig/headless
Use the full runtime package for lower-level subpaths, provenance checks, skill metadata, and MCP tooling.
When it fits
Stop translating workbook logic into one-off code.
Pricing rules, import checks, payouts, and budget models often start life in cells. If that shape is still the clearest source of truth, keep it and run it from Node.
Build the workbook model.
Start from arrays, records, CSV-shaped data, or saved WorkPaper JSON.
Write the input cell.
Update a quantity, rate, assumption, imported value, or threshold.
Read the calculated value.
Use the value the workbook produced after the input changed.
Save the document.
Persist sheets, formulas, and config as JSON between runs.
Quickstart
Create a workbook in a blank Node project.
This installs the package, changes one input, reads the calculated total, saves JSON, and restores the workbook.
Blank Node project
mkdir bilig-headless-eval
cd bilig-headless-eval
npm init -y
npm pkg set type=module
npm install @bilig/workpaper
npm install -D tsx typescript @types/node
curl -fsSLo quickstart.ts \
https://proompteng.github.io/bilig/npm-eval.ts
npx tsx quickstart.ts
More TypeScript examples live in examples/headless-workpaper , examples/serverless-workpaper-api, and examples/xlsx-recalculation-node.
import {
WorkPaper,
createWorkPaperFromDocument,
exportWorkPaperDocument,
parseWorkPaperDocument,
serializeWorkPaperDocument,
} from "@bilig/workpaper";
const workbook = WorkPaper.buildFromSheets({
Inputs: [
["Metric", "Value"],
["Customers", 20],
["Average revenue", 1200],
],
Summary: [
["Metric", "Value"],
["Revenue", "=Inputs!B2*Inputs!B3"],
],
});
const inputs = workbook.getSheetId("Inputs");
const summary = workbook.getSheetId("Summary");
if (inputs === undefined || summary === undefined) {
throw new Error("Missing sheet");
}
const before = readNumber(workbook.getCellValue({ sheet: summary, row: 1, col: 1 }));
workbook.setCellContents({ sheet: inputs, row: 1, col: 1 }, 32);
const after = readNumber(workbook.getCellValue({ sheet: summary, row: 1, col: 1 }));
const saved = serializeWorkPaperDocument(exportWorkPaperDocument(workbook, { includeConfig: true }));
const restored = createWorkPaperFromDocument(parseWorkPaperDocument(saved));
const restoredSummary = restored.getSheetId("Summary");
if (restoredSummary === undefined) {
throw new Error("Missing restored Summary sheet");
}
const afterRestore = readNumber(restored.getCellValue({ sheet: restoredSummary, row: 1, col: 1 }));
console.log({ before, after, afterRestore, verified: after === afterRestore });
function readNumber(cell: unknown): number {
if (typeof cell === "object" && cell !== null && typeof (cell as { value: unknown }).value === "number") {
return (cell as { value: number }).value;
}
throw new Error(`Expected numeric cell value, got ${JSON.stringify(cell)}`);
}
Where it runs
Put the workbook behind the code that owns the workflow.
Route handler, queue worker, CLI, or MCP server: load the workbook, edit cells, read results, and save state.
$ npm exec --package @bilig/workpaper@0.40.43 -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable
ok tools/list
ok tools/call read_range
ok tools/call set_cell_contents
ok WorkPaper JSON persisted to file
$ curl https://bilig.proompteng.ai/mcp -H 'accept: application/json, text/event-stream' -H 'mcp-protocol-version: 2025-11-25' ...
ok stateless Streamable HTTP tools/list
Benchmark
The benchmark is public. So is the caveat.
The checked JSON has bilig ahead on the aggregate mean and p95 scorecard, with holdouts visible. The worst p95 row is named beside the number.
94 of 100 comparable mean-latency rows are faster in the checked file. structural-move-rows is the current worst p95 row:
4.047x. Browser UI rendering is not part of this benchmark.
- Command
pnpm workpaper:bench:competitive:check- Artifact
-
packages/benchmarks/baselines/workpaper-vs-hyperformula.json - Worst p95
- structural-move-rows is the current worst p95 row:
4.047x. - Out of scope
- UI rendering, Excel file compatibility, and workbook shapes this suite does not cover.
Start here
Pick the path that matches the job.
Try the package, put it behind a service route, expose it as an agent tool, or compare it with the spreadsheet stack you already use.
bilig-formula-clinic against a reduced workbook and paste the Markdown output into GitHub.
contribute
Take a starter issue that improves the examples.
Start with code/test picks, example tasks, adapters, or focused docs coverage.
Reference
Short paths for real integration work.
Open the install path, service recipe, agent adapter, or comparison page that matches the thing you are building.
Run
- @bilig/workpaper npm package
- @bilig/xlsx-formula-recalc npm package
- @bilig/exceljs-formula-recalc npm package
- @bilig/headless npm package
- Node quickstart
- starter package status
- TypeScript examples
- workbook automation examples
- JSON records input
- invoice totals
- budget variance alerts
- fulfillment capacity plan
- quote approval threshold
- subscription MRR forecast
Build
- Node service recipe
- server-side spreadsheet automation
- Google Sheets API boundary
- serverless WorkPaper API route
- Express, Fastify, Hono, Oak, Hapi, and AdonisJS adapters
- formula-backed persistence
- build a revenue model
- serverless API route example
- quote-approval-api smoke
- Postgres, Redis, and object storage adapters
- XLSX formula recalculation in Node
- curlable XLSX recalculation proof
- downloadable proof script
Agents
- why agents need workbook APIs
- agent instructions
- agent skill manifest
- full LLM context pack
- Context7 indexed docs
- headless WorkPaper agent handbook
- agent workbook challenge
- agent tool-calling recipe
- OpenAI Agents SDK tool calls
- OpenAI Responses tool calls
- agent spreadsheet tool-call loop
- agent XLSX recalculation without LibreOffice
- Agent framework tools
- Mastra spreadsheet tool
- LlamaIndex.TS spreadsheet tool
- LangGraph.js ToolNode
- CopilotKit action
- Cloudflare Agents tool
- CrewAI spreadsheet tool
- MCP spreadsheet tool server
- spreadsheet MCP server comparison
- MCP directory status
- MCP client setup
- Claude Desktop MCPB bundle
- MCP Registry listing
- agent skill discovery index
- MCP workflow discussion
Decide
- why use Bilig?
- screenshots vs workbook APIs
- Show HN formula workbook proof
- formula workbooks for Node services and agent tools
- AI spreadsheet agent tool for Node.js
- Node spreadsheet formula engine
- curlable XLSX recalculation proof
- downloadable proof script
- XLSX formula recalculation in Node.js
- agent XLSX formula recalculation without LibreOffice
- Excel file as a Node calculation engine
- stale XLSX formula cache in Node.js
- SheetJS formula result not updating in Node.js
- Microsoft Graph Excel recalculation in Node.js
- spreadsheet MCP server comparison
- xlsx-calc alternative for Node recalculation
- ExcelJS formula recalculation in Node.js
- ExcelJS shared formulas in Node.js
- xlsx-template formula recalculation in Node.js
- xlsx-populate formula results in Node.js
- headless spreadsheet engine for Node services and agents
- quote approval WorkPaper API proof
- starter package status
- production adoption checklist
- npm provenance and package trust
- evaluate Excel formulas in Node.js
- Google Sheets API boundary
- stale XLSX cache and Excel oracle checks
- JavaScript spreadsheet library for Node services
- SheetJS and ExcelJS alternatives
- HyperFormula alternative notes
- unsupported formula troubleshooting
- local benchmark walkthrough
- what the benchmark proves
- benchmark critique
- engine comparison
- compatibility limits
- formula bug clinic
- formula clinic report script
- submit a workbook fixture
- fixture discussion
- starter issues
- first-timers-only queue
- adoption blocker form
- GitHub discussions
- quickstart feedback
- workflow feedback
- serverless route feedback
- persistence adapter feedback
- security policy
- support policy
Run it on one calculation you care about.
If the quickstart matches your workflow, star the repo. If it does not, name the blocker.