| name | xlsx-code-mode |
| description | Use this skill any time an Excel file (.xls, .xlsx, .xlsm) needs to be read, explored, understood, or modified. You cannot read excel files with cat, head, or normal file-reading tools — this is the only way to inspect them. Trigger when you or the user need to open, look at, or explore a workbook; find out what sheets it has or where specific data lives; read cells, rows, columns, or ranges; search for values, labels, or patterns; trace formula dependencies or understand how a cell is calculated; run what-if scenarios by changing inputs and reading recalculated outputs; or edit cells, rows, columns, and sheets. Trigger when the user references a spreadsheet file by name or path — even casually (e.g. 'check the xlsx', 'what's in report.xlsx') — and also when you need to inspect a workbook yourself as part of a larger task. The tool runs sandboxed JavaScript against the workbook server-side via `witan xlsx exec`." |
Setup
Files are cached server-side by content hash so repeated operations skip re-upload. If WITAN_STATELESS=1 is set (or --stateless is passed), files are processed but not stored.
The CLI automatically applies per-attempt request timeouts and retries transient API failures (408, 429, 500, 502, 503, 504, plus timeout/network errors). Non-retryable 4xx responses fail immediately.
The CLI automatically converts older .xls files to .xlsx, so it fully supports all Excel file formats.
Quick Reference
witan xlsx exec model.xlsx --stdin <<'WITAN'
const a = await xlsx.readCell(wb, "'Workers' Compensation'!B50")
const b = await xlsx.readRangeTsv(wb, { sheet: "Reserve Summary (Net)", from: {row:1,col:1}, to: {row:10,col:5} })
return { a: a.value, b }
WITAN
witan xlsx exec model.xlsx --stdin <<'WITAN'
const result = await xlsx.scenarios(wb, {
inputs: [
{ address: "Inputs!B5", values: [0.02, 0.04, 0.06] },
{ address: "Inputs!B6", values: [0.08, 0.10, 0.12] },
],
outputs: ["Output!C30", "Output!C45"],
mode: "cartesian",
includeStats: true,
})
console.log(result.tsv)
WITAN
witan xlsx exec model.xlsx --stdin <<'WITAN'
await xlsx.setConditionalFormatting(wb, "Sheet1", [
{
type: "cellValue",
address: "A1:A100",
operator: "greaterThan",
formula: "100",
style: { fill: { color: "#FF0000" } }
},
{
type: "twoColorScale",
address: "B1:B100",
lowValue: { type: "min", color: "#FFFFFF" },
highValue: { type: "max", color: "#FF0000" }
}
])
WITAN
witan xlsx exec model.xlsx --expr 'xlsx.listSheets(wb)'
Exit Codes
| Command | Code | Meaning |
|---|
exec | 0 | Script completed successfully (ok: true) |
exec | 1 | Transport/API error, invalid request, or script error (ok: false) |
exec — Workbook Scripting
Runs JavaScript against a workbook via the Witan API. The workbook is opened server-side; scripts interact through the xlsx and wb globals.
Invocation patterns
Recommended: --stdin with heredoc — safe for all sheet names, supports multi-line scripts, and batches multiple operations into a single CLI invocation:
witan xlsx exec report.xlsx --stdin <<'WITAN'
const sheets = await xlsx.listSheets(wb)
const cell = await xlsx.readCell(wb, "'My Sheet'!A1")
return { sheets, cell }
WITAN
The single-quoted heredoc delimiter (<<'WITAN') prevents all shell expansion. Apostrophes, parentheses, double quotes, and glob characters in sheet names pass through verbatim to JavaScript — no escaping needed.
Other invocation patterns (use only when --stdin is impractical):
witan xlsx exec report.xlsx --expr 'xlsx.listSheets(wb)'
witan xlsx exec report.xlsx --script scenario.js --input-json '{"rate": 1.05}'
Provide exactly one code source: --expr, --code, --script, or --stdin. They are mutually exclusive.
Flags
| Flag | Short | Default | Description |
|---|
--expr | | — | Expression shorthand; wraps as return (<expr>); |
--code | | — | Inline JavaScript source |
--script | | — | Path to a JavaScript file |
--stdin | | — | Read JavaScript source from stdin |
--input-json | | {} | JSON value passed as input to the script |
--timeout-ms | | — | Execution timeout in milliseconds (> 0); omit to use server default |
--max-output-chars | | — | Maximum stdout characters to capture (> 0); omit to use server default |
--save | | false | Persist changes to the workbook file |
--json | | false | Print the full response envelope as JSON |
Runtime globals
| Name | Type | Description |
|---|
xlsx | object | Curated API surface — all functions listed below |
wb | WorkbookContext | The opened workbook handle; pass as first arg to all xlsx.* calls |
input | any | Parsed value from --input-json (defaults to {}) |
print | function | Output to stdout (like console.log but captured in response) |
Top-level await is supported. No imports allowed (static or dynamic).
API reference
Functions are grouped by purpose. All are async and take wb as the first argument.
Reading
| Function | Signature | Description |
|---|
listSheets | (wb) | List all sheets with used ranges, visibility, and cross-sheet dependencies |
getWorkbookProperties | (wb) | Workbook-level metadata (active sheet, default font, metadata, theme colors) |
getSheetProperties | (wb, sheet, filter?) | Get sheet properties (view, format, columns, rows, merges); filter.columns/rows to limit |
listDefinedNames | (wb) | All defined names |
readCell | (wb, cell, opts?) | Read a single cell; opts.context adds surrounding cells |
readRange | (wb, range) | Read all cells in a range |
readRow | (wb, sheet, row, opts?) | Read a row; opts.startCol/endCol to limit |
readColumn | (wb, sheet, col, opts?) | Read a column; opts.startRow/endRow to limit |
readRangeTsv | (wb, range, opts?) | Read range as TSV text; opts.includeEmpty, includeFormulas |
readColumnTsv | (wb, sheet, col, opts?) | Read column as TSV text; opts.startRow/endRow, includeEmpty, includeFormulas |
readRowTsv | (wb, sheet, row, opts?) | Read row as TSV text; opts.startCol/endCol, includeEmpty, includeFormulas |
getStyle | (wb, cell) | Get style properties (fill, font, alignment, border, numberFormat, richText) of a cell |
Searching
| Function | Signature | Description |
|---|
findCells | (wb, matcher, opts?) | Find cells by value or pattern; opts.in, context, limit, offset, formulas |
findRows | (wb, matcher, opts?) | Find rows by value or pattern; opts.in, context, limit, offset |
describeSheets | (wb) | Per-sheet structure map + detected tables |
tableLookup | (wb, { table, rowLabel, columnLabel }) | Look up a value by row and column labels |
matcher accepts: string, string array (OR match), number, boolean, RegExp, or RegExp array. Searches are fuzzy and case-insensitive by default.
Tracing
| Function | Signature | Description |
|---|
getCellPrecedents | (wb, address, depth?) | Cells that feed into this cell; depth defaults to 1 |
getCellDependents | (wb, address, depth?) | Cells that depend on this cell |
traceToInputs | (wb, cell) | Trace all the way to leaf input cells (no formula) |
traceToOutputs | (wb, cell) | Trace all the way to terminal output cells |
Computing
| Function | Signature | Description |
|---|
scenarios | (wb, { inputs, outputs, mode?, includeStats? }) | Batch what-if sweeps with TSV + structured outputs |
evaluateFormula | (wb, sheet, formula) | Evaluate a formula string in a sheet context |
evaluateFormulas | (wb, sheet, formulas) | Evaluate multiple formulas at once |
Validating
| Function | Signature | Description |
|---|
lint | (wb, options?) | Find potential issues |
Rendering
| Function | Signature | Description |
|---|
previewStyles | (wb, range) | Generate a PNG screenshot of a cell range; image is auto-registered |
Conditional Formatting
| Function | Signature | Description |
|---|
getConditionalFormatting | (wb, sheet) | Get all CF rules on a sheet (iconSet is read-only) |
setConditionalFormatting | (wb, sheet, rules, opts?) | Add writable CF rules; opts.clear to replace all |
removeConditionalFormatting | (wb, sheet, indices) | Remove rules by index |
Writing (ephemeral)
| Function | Signature | Description |
|---|
setCells | (wb, cells) | Write values/formulas to cells; returns { touched, changed, errors } |
findAndReplace | (wb, find, replace, opts?) | Bulk text replacement in values or formulas; supports regex/capture groups |
scaleRange | (wb, range, factor, opts?) | Multiply numeric cells by a factor; opts.skipFormulas (default true) |
insertRowAfter | (wb, sheet, row, count?) | Insert rows after a given row |
deleteRows | (wb, sheet, row, count?) | Delete rows starting at a given row |
insertColumnAfter | (wb, sheet, col, count?) | Insert columns after a given column |
deleteColumns | (wb, sheet, col, count?) | Delete columns starting at a given column |
autoFitColumns | (wb, sheet, columns?, opts?) | Auto-fit column widths to content; opts.minWidth/maxWidth/padding |
sortRange | (wb, range, keys, opts?) | Sort rows in a range by one or more keys; opts.hasHeader defaults to true |
copyRange | (wb, source, destination, opts?) | Copy a range to a destination anchor; opts.pasteType supports all, values, formulas, formats |
addSheet | (wb, name) | Add a new sheet |
deleteSheet | (wb, name) | Delete a sheet |
renameSheet | (wb, oldName, newName) | Rename a sheet |
addDefinedName | (wb, name, range, scope?) | Add a defined name |
setWorkbookProperties | (wb, properties) | Set workbook-level properties |
setSheetProperties | (wb, sheet, properties) | Set sheet-level properties (columns, rows, merges, view) |
setStyle | (wb, target, style) | Apply styles to a cell or range |
The ephemeral write contract
By default, exec does not write workbook bytes back to disk. All write operations (setCells, scaleRange, inserts, deletes) take effect in the server-side session only. The result.touched map contains the recalculated formatted text values — read answers from there.
This means:
- No risk of corrupting the original file
- No
reset() needed — each invocation starts clean
- For independent what-if tests, each
exec invocation starts from the original file
To persist changes back to the workbook file, pass the --save flag.
setCells result shape
{
touched: Record<string, string>
changed: string[]
errors: Diagnostic[]
}
Read the output value from result.touched["Sheet!Address"]. Never compute the answer in JavaScript.
What-if / sensitivity workflow
For questions like "what happens to Y if X changes?", follow this sequence. Steps 1 and 2 should be separate exec calls — review the output of step 1 before proceeding.
- Find the output cell (separate exec call) — search for what the user is asking about (e.g., "net income", "reserve"). Use
findCells with context: 2 and review the candidates. Labels and formula cells often share the same text — pick the formula cell, not the label. This step may take more than one attempt: try synonym arrays, read surrounding rows with readRangeTsv, or check multiple sheets.
- Trace + run the what-if (second exec call) — once you have the output address, call
traceToInputs(wb, outputAddr) to confirm the user's input actually drives it. Trace results can contain hundreds of cells — filter by nearbyLabel matching the user's term instead of printing them all. Then setCells to make the change and read the answer from result.touched[outputAddr]. If the output address is missing from touched, the cell didn't recalculate — you likely have the wrong address.
- Report before and after — always include the baseline value (read before the edit) and the new value from
touched.
For sweeping multiple values (sensitivity tables), use scenarios instead — it runs all combinations in one call and returns structured before/after data.
Common pitfalls:
- Don't search for the output after
setCells — find it first so you know the exact address to check in touched.
- If
findCells returns several hits for the same label, use context or readRangeTsv to disambiguate (e.g., "Net Income" may appear as both a label and a formula cell on different rows).
- After
setCells, verify result.errors is empty. New errors mean the edit broke a formula.
- If the question names a specific metric (e.g., "loss ratio"), don't just search for that term — also check the sheet/row the question references, since models often have multiple versions of the same metric across sheets.
Circular reference convergence
When a workbook has iterative calculation enabled (circular references between
cells), setCells returns partially-converged intermediate values in
result.touched — this is expected and not an error. Do not try to debug or
"fix" these intermediate values.
To fully converge circular references after setting formulas, run:
witan xlsx calc model.xlsx --show-touched
This recalculates all formulas with iterative solving and saves the converged
values back to the file. After running calc, inspect the output to verify that
all cells have the expected values.
Response format
When --json is used, the full response envelope is returned:
Success:
{
"ok": true,
"stdout": "...",
"result": "<json>",
"writes_detected": false,
"accesses": [...]
}
Failure:
{
"ok": false,
"stdout": "...",
"error": { "type": "...", "code": "...", "message": "..." }
}
The accesses array documents all cell reads and writes with operation type and address.
render — Visual Screenshot
Renders a sheet range as a PNG image, useful for inspecting layout, merged cells, formatting, and labels.
witan xlsx render report.xlsx -r "Sheet1!A1:Z50"
witan xlsx render report.xlsx -r "'My Sheet'!B5:H20" -o snapshot.png
witan xlsx render report.xlsx -r "Sheet1!A1:F10" --dpr 2
witan xlsx render report.xlsx -r "Sheet1!A1:F10" --diff before.png
| Flag | Short | Default | Description |
|---|
--range | -r | — | Sheet-qualified range to render (required) |
--output | -o | — | Output path (default: temporary file) |
--dpr | | auto | Device pixel ratio 1-3 |
--format | | png | Output format: png or webp |
--diff | | — | Compare against a baseline PNG and write diff image |
The previewStyles exec function (see Rendering in the API reference) provides the same capability from within a script.
Error Guide
| Error | Fix |
|---|
exactly one of --code, --script, --stdin, or --expr is required | Provide exactly one code source flag |
--code, --script, --stdin, and --expr are mutually exclusive | Only use one code source flag per invocation |
exec code must not be empty | Provide non-empty code |
Import statements are not allowed | No import in exec scripts; use the xlsx global |
EXEC_SYNTAX_ERROR | Fix JavaScript syntax in your script |
EXEC_RUNTIME_ERROR | Fix runtime error (check the message for details) |
EXEC_RESULT_TOO_LARGE | Return less data; use console.log() for large output instead of return values |
--timeout-ms must be > 0 | Omit the flag (no timeout) or provide a positive value |
invalid --input-json | Provide valid JSON |
Sheet 'X' not found | Check the sheet name; use listSheets to enumerate |
| Shell quoting errors with sheet names | Use --stdin <<'WITAN' heredoc — it avoids all shell quoting issues |
findCells returns empty | Try synonym arrays, broader search, or check spelling |
setCells result missing expected output | The output cell may not be a dependent; trace the formula chain |
Full Type Definitions
type CellAddressOrCoordinates =
| string
| {
sheet: string;
row: number;
col: number | string;
};
type RangeAddressOrCoordinates =
| string
| {
sheet: string;
}
| {
sheet: string;
from: {
row?: number;
col?: number | string;
};
to: {
row?: number;
col?: number | string;
};
};
type VisibilityType = "visible" | "outsidePrintArea" | "collapsed" | "hidden";
interface SheetInfo {
address: string;
from: {
row: number;
col: number;
};
to: {
row: number;
col: number;
};
rows: number;
cols: number;
sheet: string;
hidden?: boolean;
printArea?: string;
precedents?: string[];
dependents?: string[];
}
interface WorkbookProperties {
activeSheetIndex: number;
defaultFont: {
name: string;
size: number;
};
metadata?: {
author?: string;
title?: string;
subject?: string;
company?: string;
created?: string;
modified?: string;
};
themeColors?: {
dark1: string;
light1: string;
dark2: string;
light2: string;
accent1: string;
accent2: string;
accent3: string;
accent4: string;
accent5: string;
accent6: string;
hyperlink: string;
followedHyperlink: string;
majorFont?: string;
minorFont?: string;
};
}
function getWorkbookProperties(wb): Promise<WorkbookProperties>;
function setWorkbookProperties(
wb,
properties: {
activeSheetIndex?: number;
defaultFont?: {
name?: string;
size?: number;
};
metadata?: {
author?: string;
title?: string;
subject?: string;
company?: string;
};
themeColors?: {
dark1?: string;
light1?: string;
dark2?: string;
light2?: string;
accent1?: string;
accent2?: string;
accent3?: string;
accent4?: string;
accent5?: string;
accent6?: string;
hyperlink?: string;
followedHyperlink?: string;
majorFont?: string;
minorFont?: string;
};
},
): Promise<void>;
function listSheets(wb): Promise<SheetInfo[]>;
interface DefinedName {
name: string;
range: string;
scope: string | null;
}
function listDefinedNames(wb): Promise<DefinedName[]>;
function addDefinedName(
wb,
name: string,
range: string,
scope?: string,
): Promise<DefinedName>;
function addSheet(wb, name: string): Promise<string>;
function deleteSheet(wb, name: string): Promise<void>;
function renameSheet(wb, oldName: string, newName: string): Promise<void>;
interface CellNoteResponse {
author: string;
text: string;
}
interface CellHyperlinkResponse {
type: "internal" | "external";
target: string;
tooltip?: string;
}
interface CellThreadCommentResponse {
authorId: string;
text: string;
createdAt: string;
}
interface CellThreadResponse {
resolved: boolean;
comments: CellThreadCommentResponse[];
}
interface Value {
address: string;
sheet: string;
row: number;
col: number;
colLetter: string;
value: string | number | boolean | null;
formula?: string;
type: "string" | "number" | "bool" | "date" | "error" | "blank";
text: string;
format?: string;
numberType?:
| "currency"
| "percent"
| "fraction"
| "exponential"
| "date"
| "text"
| "number";
visibility: VisibilityType;
context?: string;
note?: CellNoteResponse;
link?: CellHyperlinkResponse;
thread?: CellThreadResponse;
}
function readCell(
wb,
cell: CellAddressOrCoordinates,
opts?: {
context?: number;
},
): Promise<Value>;
function readRange(wb, range: RangeAddressOrCoordinates): Promise<Value[][]>;
function readColumn(
wb,
sheetName: string,
col: number | string,
opts?: {
startRow?: number;
endRow?: number;
},
): Promise<Value[]>;
function readRow(
wb,
sheetName: string,
row: number,
opts?: {
startCol?: number;
endCol?: number;
},
): Promise<Value[]>;
function readRangeTsv(
wb,
range: RangeAddressOrCoordinates,
opts?: {
includeEmpty?: boolean;
includeFormulas?: boolean;
},
): Promise<string>;
function readColumnTsv(
wb,
sheetName: string,
col: number | string,
opts?: {
startRow?: number;
endRow?: number;
includeEmpty?: boolean;
includeFormulas?: boolean;
},
): Promise<string>;
function readRowTsv(
wb,
sheetName: string,
row: number,
opts?: {
startCol?: number;
endCol?: number;
includeEmpty?: boolean;
includeFormulas?: boolean;
},
): Promise<string>;
declare class SearchResults<T> extends Array<T> {
truncated?: boolean;
}
type MatcherInput = string | string[] | number | boolean | RegExp | RegExp[];
type ReplaceMatcherInput = string | RegExp;
function findCells(
wb,
matcher: MatcherInput,
opts?: {
in?: RangeAddressOrCoordinates | string;
context?: number;
limit?: number;
offset?: number;
formulas?: boolean;
},
): Promise<
SearchResults<{
type: "cell";
address: string;
value: any;
text: string;
formula?: string;
row: number;
col: number;
colLetter: string;
sheet: string;
visibility: VisibilityType;
context?: string;
role: string;
}>
>;
function findRows(
wb,
matcher: MatcherInput,
opts?: {
in?: RangeAddressOrCoordinates | string;
context?: number;
limit?: number;
offset?: number;
},
): Promise<
SearchResults<{
type: "row";
row: number;
sheet: string;
matchedAt: string;
range: string;
tsv: string;
visibility: VisibilityType;
context?: string;
}>
>;
interface FindAndReplaceResult {
replaced: number;
cells: string[];
errors: Diagnostic[];
}
function findAndReplace(
wb,
find: ReplaceMatcherInput,
replace: string,
opts?: {
in?: RangeAddressOrCoordinates | string;
matchCase?: boolean;
wholeCell?: boolean;
inFormulas?: boolean;
limit?: number;
},
): Promise<FindAndReplaceResult>;
function describeSheets(wb): Promise<
Record<
string,
{
tables: Record<
string,
{
address: string;
headerRows: string;
headerCols: string | null;
tableName?: string;
}
>;
structure: string;
}
>
>;
function tableLookup(
wb,
args: {
/** Range address for the table (eg. "Sheet1!A1:D10") */
table: string;
/** Row label to search for in the first column */
rowLabel: string | number | boolean;
/** Column label to search for in the first row */
columnLabel: string | number | boolean;
},
): Promise<
{
address: string;
value: any;
text: string;
row: number;
col: number;
colLetter: string;
sheet: string;
visibility: VisibilityType;
rowLabelFoundAt: string;
rowLabelFound: string;
columnLabelFoundAt: string;
columnLabelFound: string;
}[]
>;
interface Diagnostic {
code: string;
detail?: string;
address: string;
formula?: string;
}
interface ScenarioInput {
address: CellAddressOrCoordinates;
values: (number | string | boolean | null)[];
}
interface ScenarioEntry {
inputs: Record<string, string>;
outputs: Record<string, string>;
errors: Diagnostic[];
}
interface OutputStats {
min: number;
max: number;
mean: number;
count: number;
}
interface ScenariosResult {
tsv: string;
scenarios: ScenarioEntry[];
stats?: Record<string, OutputStats>;
scenarioCount: number;
inputCount: number;
outputCount: number;
}
interface InvalidatedTile {
sheet: string;
tileRow: number;
tileCol: number;
}
interface UpdatedSheetInfo {
name: string;
usedRange: {
startRow: number;
startCol: number;
endRow: number;
endCol: number;
} | null;
tileRowCount: number;
tileColCount: number;
}
interface SetCellsResult {
touched: Record<string, string>;
changed: string[];
errors: Diagnostic[];
invalidatedTiles: InvalidatedTile[];
updatedSheets: UpdatedSheetInfo[];
}
function setCells(
wb,
cells: Array<{
address: CellAddressOrCoordinates;
value?: unknown;
formula?: string;
format?: string;
/** Legacy note payload: `note: { text: "Review this", author: "Agent" }`; clear with `note: null`. */
note?: { text?: string; author?: string } | null;
/** Hyperlink payload: external `{ url: "https://example.com" }` or internal `{ ref: "Sheet2!B5" }`; clear with `link: null`. */
link?: {
url?: string;
ref?: string;
tooltip?: string;
} | null;
/** Thread payload: append comments via `add`, toggle state via `resolved`, or delete with `delete: true` / `thread: null`. */
thread?: {
add?: Array<{ author?: string; text: string }>;
resolved?: boolean;
delete?: boolean;
} | null;
}>,
): Promise<SetCellsResult>;
function scenarios(
wb,
args: {
inputs: ScenarioInput[];
outputs: (string | CellAddressOrCoordinates)[];
mode?: "cartesian" | "parallel";
includeStats?: boolean;
},
): Promise<ScenariosResult>;
function scaleRange(
wb,
range: RangeAddressOrCoordinates,
factor: number,
opts?: {
skipFormulas?: boolean;
},
): Promise<SetCellsResult | null>;
function insertRowAfter(
wb,
sheetName: string,
row: number,
count?: number,
): Promise<void>;
function deleteRows(
wb,
sheetName: string,
row: number,
count?: number,
): Promise<void>;
function insertColumnAfter(
wb,
sheetName: string,
column: number | string,
count?: number,
): Promise<void>;
function deleteColumns(
wb,
sheetName: string,
column: number | string,
count?: number,
): Promise<void>;
function autoFitColumns(
wb,
sheetName: string,
columns?: Array<number | string>,
opts?: {
minWidth?: number;
maxWidth?: number;
padding?: number;
},
): Promise<
Record<
string,
{
width: number;
previousWidth: number;
}
>
>;
function sortRange(
wb,
range: RangeAddressOrCoordinates,
keys: Array<{
col: number | string;
order?: "asc" | "desc";
}>,
opts?: {
hasHeader?: boolean;
},
): Promise<void>;
function copyRange(
wb,
source: RangeAddressOrCoordinates,
destination: RangeAddressOrCoordinates,
opts?: {
pasteType?: "all" | "values" | "formulas" | "formats";
},
): Promise<{
destination: string;
cellsCopied: number;
}>;
type RichTextRun = {
text: string;
style?: {
name?: string;
size?: number;
color?: string;
bold?: boolean;
italic?: boolean;
strike?: boolean;
underline?: string;
verticalAlign?: string;
};
};
type StyleObj = {
fill?: {
color?: string;
pattern?: string;
patternColor?: string;
gradient?: {
type: string;
degree?: number;
color1: string;
color2: string;
top?: number;
bottom?: number;
left?: number;
right?: number;
};
};
font?: {
name?: string;
size?: number;
color?: string;
bold?: boolean;
italic?: boolean;
strike?: boolean;
underline?: string;
verticalAlign?: string;
};
alignment?: {
horizontal?: string;
vertical?: string;
rotation?: number;
wrapText?: boolean;
shrinkToFit?: boolean;
indent?: number;
};
border?: {
top?: {
style: string;
color: string;
};
bottom?: {
style: string;
color: string;
};
left?: {
style: string;
color: string;
};
right?: {
style: string;
color: string;
};
diagonal?: {
style: string;
color?: string;
up?: boolean;
down?: boolean;
};
};
numberFormat?: string;
centerContinuousSpan?: number;
richText?: RichTextRun[];
};
function getStyle(wb, cell: CellAddressOrCoordinates): Promise<StyleObj>;
function setStyle(
wb,
target: CellAddressOrCoordinates | RangeAddressOrCoordinates,
style: StyleObj,
): Promise<void>;
interface CfColorScalePoint {
type:
| "formula"
| "max"
| "min"
| "num"
| "percent"
| "percentile"
| "autoMin"
| "autoMax";
value?: number;
formula?: string;
color?: string;
}
interface CfDataBarThreshold {
type:
| "formula"
| "max"
| "min"
| "num"
| "percent"
| "percentile"
| "autoMin"
| "autoMax";
value?: number;
formula?: string;
}
interface CfDataBarConfig {
showValue?: boolean;
gradient?: boolean;
border?: boolean;
negativeBarColorSameAsPositive?: boolean;
negativeBarBorderColorSameAsPositive?: boolean;
axisPosition?: "automatic" | "middle" | "none";
direction?: "context" | "leftToRight" | "rightToLeft";
fillColor?: string;
borderColor?: string;
negativeFillColor?: string;
negativeBorderColor?: string;
axisColor?: string;
lowValue?: CfDataBarThreshold;
highValue?: CfDataBarThreshold;
}
type CfWritableRuleType =
| "cellValue"
| "containsText"
| "notContainsText"
| "beginsWith"
| "endsWith"
| "containsBlanks"
| "notContainsBlanks"
| "containsErrors"
| "notContainsErrors"
| "expression"
| "timePeriod"
| "top"
| "bottom"
| "aboveAverage"
| "belowAverage"
| "duplicateValues"
| "uniqueValues"
| "twoColorScale"
| "threeColorScale"
| "dataBar";
type CfReadableRuleType = CfWritableRuleType | "iconSet";
interface CfRuleShared {
address: string;
priority?: number;
stopIfTrue?: boolean;
style?: StyleObj;
operator?:
| "equal"
| "notEqual"
| "greaterThan"
| "greaterThanOrEqual"
| "lessThan"
| "lessThanOrEqual"
| "between"
| "notBetween"
| "above"
| "aboveOrEqual"
| "below"
| "belowOrEqual";
formula?: string;
formula2?: string;
text?: string;
rank?: number;
percent?: boolean;
bottom?: boolean;
stdDev?: number;
lowValue?: CfColorScalePoint;
midValue?: CfColorScalePoint;
highValue?: CfColorScalePoint;
dataBar?: CfDataBarConfig;
timePeriod?:
| "today"
| "yesterday"
| "tomorrow"
| "last7Days"
| "thisWeek"
| "lastWeek"
| "nextWeek"
| "thisMonth"
| "lastMonth"
| "nextMonth";
}
interface CfReadableRule extends CfRuleShared {
index?: number;
type: CfReadableRuleType;
}
interface CfWritableRule extends CfRuleShared {
type: CfWritableRuleType;
}
type CfRule = CfReadableRule;
function getConditionalFormatting(
wb,
sheetName: string,
): Promise<CfReadableRule[]>;
function setConditionalFormatting(
wb,
sheetName: string,
rules: CfWritableRule[],
opts?: {
clear?: boolean;
},
): Promise<void>;
function removeConditionalFormatting(
wb,
sheetName: string,
indices: number[],
): Promise<void>;
interface SheetProperties {
view: {
showGridLines: boolean;
zoomScale: number;
};
format: {
defaultRowHeight: number;
defaultColWidth: number;
font?: {
name?: string;
size?: number;
} | null;
};
columns: Record<
string,
{
col: string;
width: number;
}
>;
rows: Record<
number,
{
row: number;
height: number;
}
>;
merges?: string[] | null;
}
function setSheetProperties(
wb,
sheetName: string,
properties: {
view?: {
showGridLines?: boolean;
zoomScale?: number;
};
format?: {
defaultRowHeight?: number;
defaultColWidth?: number;
font?: {
name?: string;
size?: number;
};
};
columns?: Record<
number | string,
{
width: number;
}
>;
rows?: Record<
number,
{
height: number;
}
>;
merges?: string[];
},
): Promise<void>;
function getSheetProperties(
wb,
sheetName: string,
filter?: {
columns?: (number | string)[];
rows?: number[];
},
): Promise<SheetProperties>;
interface DependencyResult {
cells: {
address: string;
depth: number;
formula?: string;
referenceType?: "direct" | "range" | "named" | "table";
}[];
warnings?: Diagnostic[];
}
function getCellPrecedents(
wb,
address: CellAddressOrCoordinates,
depth?: number,
): Promise<DependencyResult>;
function getCellDependents(
wb,
address: CellAddressOrCoordinates,
depth?: number,
): Promise<DependencyResult>;
function traceToInputs(
wb,
cell: CellAddressOrCoordinates,
): Promise<
{
address: string;
referenceCount: number;
text?: string;
nearbyLabel?: string;
context?: string;
}[]
>;
function traceToOutputs(
wb,
cell: CellAddressOrCoordinates,
): Promise<
{
address: string;
formula?: string;
text?: string;
visibility: VisibilityType;
nearbyLabel?: string;
context?: string;
}[]
>;
interface FormulaResult {
formula: string;
value: number | string | boolean | null | unknown[][];
error?: {
code: string;
detail?: string;
};
}
function evaluateFormulas(
wb,
sheet: string,
formulas: string[],
): Promise<FormulaResult[]>;
function evaluateFormula(
wb,
sheet: string,
formula: string,
): Promise<FormulaResult>;
function lint(
wb,
options?: {
/** Array of cell ranges to analyze (e.g., ["Sheet1!A1:B10", "Sheet2!C1:C20"]). If omitted, analyzes entire workbook. */
rangeAddresses?: string[];
/** Array of rule IDs to skip (e.g., ["D031"] to skip spelling checks) */
skipRuleIds?: string[];
/** Array of rule IDs to exclusively run (e.g., ["D003"] to only check empty cell coercion) */
onlyRuleIds?: string[];
},
): Promise<{
diagnostics: {
severity: "Info" | "Warning" | "Error";
ruleId: string;
message: string;
location: string | null;
visibility: VisibilityType | null;
}[];
total: number;
}>;
function previewStyles(wb, range: RangeAddressOrCoordinates): Promise<void>;
Scope
This skill is for reading and manipulating Excel spreadsheets (.xls, .xlsx, .xlsm) only. It does not handle non-spreadsheet documents (PDF, DOCX, PPTX, HTML, text).