Docs / run_script

run_script.

When to use it

  • Per-row conditional transforms: "for every row where margin is below zero, …".
  • Repeating a formula pattern across many rows or columns.
  • Address arithmetic, where the agent would otherwise compute cell references by hand.
  • Edits that depend on reading existing cells first.

One script cannot drift out of alignment mid-payload the way a long set_range can, because it computes addresses programmatically.

Example

{
  "path": "~/models/forecast.xlsx",
  "script": "const s = sheet(\"Model\");\nconst rows = s.values(\"A5:F120\");\nrows.forEach((r, i) => {\n  const row = 5 + i;\n  if (r[0] === null) return;\n  s.set(`G${row}`, `=F${row}/E${row}-1`);\n});\ns.format(\"G5:G120\", { number_format: \"0.0%\" });\nlog(`wrote ${rows.length} growth formulas`);"
}

Written out, the script is:

const s = sheet("Model");
const rows = s.values("A5:F120");
rows.forEach((r, i) => {
  const row = 5 + i;
  if (r[0] === null) return;          // skip blank label rows
  s.set(`G${row}`, `=F${row}/E${row}-1`);
});
s.format("G5:G120", { number_format: "0.0%" });
log(`wrote ${rows.length} growth formulas`);

API

Plain synchronous JavaScript in strict mode. No async, no imports, no network, no DOM. The source runs as a function body.

CallDoes
sheets()Array of sheet names. Loads every sheet.
sheet(name)Handle for one sheet. The name must match exactly and be a string literal, because only named sheets are loaded. It may be omitted only when the workbook has a single sheet. A name that does not exist is created when the script's writes land.
.get("B7"){ value, formula }: the evaluated value as of before the script, overlaid with the script's own literal writes.
.values("A2:C50")2D array of values, rows by columns.
.set("B7", x)Write a value. Strings starting with = are formulas. null clears the cell's content.
.setValues("A9", [[…],[…]])Bulk write with set_range semantics: null and "" leave the existing cell alone.
.format("A1:F1", {…})Same keys as set_format: bold, number_format, background_color, font_color and the rest.
.clear("B2:D10")Empty values and formulas, keep formatting.
.usedRange()"A1:Q48" or null.
log(…)Up to 100 entries of up to 4,000 characters, returned as script_logs. For debugging and small extracts, not bulk reads.

Rules and limits

  • Formulas are not evaluated during the script. .get on a cell the script just wrote a formula into returns { value: null, formula }. The readback after the script carries evaluated results; verify there.
  • 5 seconds of CPU, then the script is killed with a timeout error.
  • 20,000 written cells per script, counting set, setValues, format and clear together. Reads are not budgeted: .values() over 7,000 rows by 100 columns is the right way to scan, and far cheaper than many read_range turns.
  • Structural operations are not in the script API. Inserting or deleting rows and columns, deleting or renaming sheets, merges and freezes use the dedicated tools.
  • Workbook size is not a limit. Sheets are fed to the script per sheet from the calculation engine.

Response

The same readback as every write (batch_id, write_check, cells, row_map, review_url), plus writes, the number of cells the script touched, and script_logs. A script that throws returns ok: false with the error and whatever was logged before it, and nothing is written.

The script's writes flow into the same pending batch system as every other tool. The user sees them on the review page and can reject the whole batch or individual cells.

Found a mistake? Open an issue or email support@gridpath.dev.