xllify lets you build custom Excel functions in plain language: no C++ or VBA required. You describe what you want, and it generates the function code and packages it as a native Excel add-in (.xll for Windows desktop, or an Office.js add-in for Excel Online).
This repo is for developers who prefer to work locally in VS Code, perhaps with Claude Code. You get the same AI-powered function generation and build pipeline, but driven from your editor with your functions stored in version control. You can of course write your function code by hand.
-
Install the xllify CLI:
The CLI tools (
xllifyandxllify-lua) are available for:- macOS — Apple Silicon (arm64)
- Windows — x86-64
- Linux — amd64
# macOS / Linux as non-root user # you can inspect install.sh with curl https://xllify.com/install.sh i curl -fsSL https://xllify.com/install.sh | bash # Windows (PowerShell) irm https://xllify.com/install.ps1 | iex
-
Clone this repo (or use Use this template on GitHub)
-
Copy
.env.exampleto.envand setXLLIFY_DEV_KEYto your key from xllify.com -
Run
$ make new-projecton Mac/Linux ornew-project.ps1on Windows -
Open the repo in Claude Code or your editor of choice (Visual Studio Code recommended)
When forking or reusing this template, generate a fresh app_id UUID in xllify.json:
# macOS / Linux
make new-project
# Windows (PowerShell)
.\new-project.ps1This overwrites the app_id field in xllify.json with a new UUID. Commit the result.
$ claude
> "Create a function that calculates compound interest"Simply describe what you want in Claude Code:
"Create a function that calculates compound interest"
"Add a function that strips HTML tags from a cell"
"Build me an XLL"
"Build me a production Office Add-in"
"Set up local dev so I can test this as an Office Add-in in Excel Online"
"Run the add-in locally"
"Add tests for the compound interest function"
"What functions do I have registered?"
Claude Code will use the xllify CLI to generate code and produce deployable add-ins.
The direct commands to run build-related activities are as follows.
make xll # builds/xllify.xll (Windows desktop Excel)
make officejs # builds/xllify.zip (Office Add-in)
make dev # add local dev support for Office Add-ins (requires node)
make clean # remove all build outputsThe first time you build a dev add-in (or ask Claude Code to set one up), the build output is unpacked and scaffold files are copied into the repo root. Once that's done:
npm install
npm run certs # first run only — installs localhost HTTPS certificates
npm startIf xllify-lua is not on your PATH, run npm run install-xllify before npm start.
This local dev workflow applies to Office Add-ins only. XLL add-ins are standalone
.xllfiles and do not require a local server.
Config values are read from xllify.json. Override on the command line if needed:
make officejs BASE_URL=https://myserver.comTargets are file-based — Make skips rebuilds when nothing in functions/ has changed.
functions/ # Your .luau function files
tests/ # Test files (_test.luau)
work/ # Staging area for new functions before saving
builds/ # Build output (.xll, .zip)
.claude/ # Claude Code config and instructions
xllify ships two CLI tools: xllify (the build/suggest CLI) and xllify-lua (the local function runner).
Requires XLLIFY_DEV_KEY set in your .env.
AI-generates a Luau function from a plain-language description. Prints Luau code to stdout and a summary/examples to stderr.
xllify suggest "compound interest calculator" > work/compound_interest.luauIf the query can be answered by a native Excel formula, the CLI notes that on stderr and exits.
Builds a Windows XLL add-in. Builds run remotely on the xllify API — your .luau source files are sent to the server and the compiled artifact is returned. Fully on-premises builds are available by arrangement; contact xllify.com for details.
xllify build xll --title "My Addin" --namespace myaddin -o builds/my-addin.xll functions/*.luau| Flag | Description |
|---|---|
--title <title> |
Add-in display title |
--namespace <ns> |
Function namespace prefix (e.g. xfy) |
-o <path> |
Output file path (defaults to <title>.xll) |
Builds an Office.js add-in zip (Excel Online + Desktop). Like build xll, this runs remotely on the xllify API.
xllify build officejs --title "My Addin" --namespace myaddin -o builds/my-addin.zip functions/*.luau
# Dev build pointed at a local server
xllify build officejs --title "My Addin" --namespace myaddin --dev-url http://localhost:3000 -o builds/my-addin.zip functions/*.luau| Flag | Description |
|---|---|
--title <title> |
Add-in display title (required) |
--namespace <ns> |
Function namespace prefix (required) |
--dev-url <url> |
Base URL for dev builds |
-o <path> |
Output file path |
| Variable | Description |
|---|---|
XLLIFY_DEV_KEY |
Your xllify dev key (required) |
XLLIFY_API_URL |
Override API base URL (default: https://xllify.com) |
Tests and runs Luau functions locally without building an XLL.
xllify-lua --load functions/myfunc.luau--load can be specified multiple times to load several files.
| Command | Description |
|---|---|
list |
List all registered functions |
desc <FUNCNAME> |
Show function signature, parameters, and examples |
call <FUNCNAME> [args...] |
Call a function with arguments |
test <testfile.luau> |
Run a test file |
# Scalar args
xllify-lua --load functions/bmi.luau call BMI 70 1.75
# String args (quote them)
xllify-lua --load functions/text.luau call StripHTML "hello world"
# Array args (JSON syntax)
xllify-lua --load functions/math.luau call Sum [1,2,3,4,5]
# 2D array / matrix
xllify-lua --load functions/matrix.luau call Transpose [[1,2,3],[4,5,6]]Argument types parsed automatically: numbers, strings (quoted), booleans (true/false), 1D arrays [...], 2D arrays [[...],[...]].
xllify-lua --json --load functions/bmi.luau call BMI 70 1.75
# Returns raw JSON value, useful for scripting| Return type | Example output |
|---|---|
| Number | 22.857143 |
| String | "Hello" |
| Boolean | true |
| Matrix | Formatted table with column labels and row numbers |
| Error | {"error":"message"} (JSON mode) |
Test files use the xllify.TestSuite / xllify.Test / xllify.Assert API.
Example test file (tests/bmi_test.luau):
xllify.TestSuite("BMI Tests", function()
xllify.Test("normal BMI", function()
local result = xllify.Call("BMI", 70, 1.75)
xllify.Assert.Equal(22.857143, result)
end)
xllify.Test("returns a number", function()
local result = xllify.Call("BMI", 80, 1.80)
xllify.Assert.Equal(false, result == nil)
end)
end)Run tests:
# Human-readable
xllify-lua --load functions/bmi.luau test tests/bmi_test.luau
# JSON output
xllify-lua --load functions/bmi.luau --json test tests/bmi_test.luau
# JUnit XML (for CI)
xllify-lua --load functions/bmi.luau --junit test tests/bmi_test.luau > test-results.xmlAssertion helpers:
| Assertion | Description |
|---|---|
xllify.Assert.Equal(expected, actual) |
Deep equality; numbers use epsilon (1e-9) |
xllify.Assert.IsMatrix(value) |
Assert value is a 2D array |
xllify.Assert.Throws(fn) |
Assert function raises an error |
xllify.Assert.IsNumber(value) |
Assert value is a number |
xllify.Assert.True(condition, message?) |
Assert condition is truthy |
Utility:
local dims = xllify.GetDimensions(matrix) -- returns {rows, cols}Every .luau function file runs inside the xllify runtime with a global xllify table pre-populated with these functions, forming a small standard library. Here is the full API.
Register a Luau function as an Excel-callable function.
xllify.ExcelFunction({
name = "BMI",
description = "Calculate Body Mass Index",
category = "Health",
parameters = {
{ name = "weight_kg", type = "number", description = "Weight in kilograms" },
{ name = "height_m", type = "number", description = "Height in metres" },
},
result = { type = "number", dimensionality = "scalar" },
}, function(weight_kg, height_m)
return weight_kg / (height_m * height_m)
end)config fields:
| Field | Type | Description |
|---|---|---|
name |
string |
Function name as it appears in Excel (required) |
description |
string? |
Shown in the Excel function wizard |
category |
string? |
Category grouping in the wizard |
execution_type |
"sync"|"async" |
Defaults to "sync" |
parameters |
param[]? |
Parameter descriptors (see below) |
result |
result? |
Return type descriptor |
Parameter descriptor:
| Field | Type | Description |
|---|---|---|
name |
string |
Parameter name |
type |
"number"|"string"|"boolean"|"table" |
Value type |
description |
string? |
Shown in Excel wizard |
dimensionality |
"scalar"|"matrix" |
Scalar (default) or 2D range |
Result descriptor:
| Field | Type | Description |
|---|---|---|
type |
"number"|"string"|"boolean"|"any" |
Return type |
dimensionality |
"scalar"|"matrix" |
Scalar (default) or 2D array |
These helpers are always available inside your Luau files:
local tbl, err = xllify.json_parse('{"key": 42}') -- table or nil + error string
local str = xllify.json_stringify(tbl) -- JSON stringxllify.strip_html("<b>hello</b>") -- "hello"
xllify.spell_number(1234.5) -- "one thousand two hundred thirty-four point five"
xllify.regex_match(text, pattern, n?) -- nth match (1-indexed, default 1), ECMAScript regex
xllify.regex_replace(text, pattern, repl) -- replace matchesxllify.levenshtein_distance("kitten", "sitting") -- 3xllify.version -- runtime version string, e.g. "0.9.10"
xllify._namespace -- namespace prefix applied to registered names
xllify.GetRegisteredFunctions() -- metadata table for all registered functions