xlsx-generator

xlsx-generator is a skill for Claude Code from guyoung/boxagnts. It costs 72 tokens per session (11,358 once invoked), scanned A, original, MIT.

A JavaScript tool for creating Microsoft Excel (.xlsx) spreadsheets from table data, with formulas, formatting, multiple sheets, and frozen panes.

In plain words
What is it for?
Use it to create formatted reports, calculation sheets, and multi-sheet workbooks from structured data.
Why use it?
It removes the need to build Excel’s internal file structure and compression logic yourself when generating valid spreadsheet files.

Skill for Claude Code

Written for Claude Code: when-to-use in frontmatter.

Good fit Use it to create formatted reports, calculation sheets, and multi-sheet workbooks from structured data.

Compare 6 skills from other repositories ↓
Install with agentmods
npx agentmods add skills/guyoung/boxagnts/xlsx-generator
Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

Any agent
npx skills add guyoung/boxagnts --skill xlsx-generator
Clone the repo
git clone --depth 1 https://github.com/guyoung/boxagnts

Made for: Claude Code.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for xlsx-generator

README.md
[![agentmods](https://agentmods.dev/badge/skills/guyoung/boxagnts/xlsx-generator.svg)](https://agentmods.dev/skills/guyoung/boxagnts/xlsx-generator)
Your own site
<a href="https://agentmods.dev/skills/guyoung/boxagnts/xlsx-generator"><img src="https://agentmods.dev/badge/skills/guyoung/boxagnts/xlsx-generator.svg" alt="Measured on agentmods" height="20"></a>
Per session 72 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 11,358 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 0 findings. A grade says what 26 rules found in the file — not that it is safe.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5.1 $0.00072 $0.11358
Opus 5 $0.00036 $0.05679
Sonnet 5 $0.00014 $0.02272
Haiku 4.5 $0.00007 $0.01136

Measured 8d ago against content hash 8d572643d050, method: parsed. Prices are Anthropic first-party input rates as of 2026-09-07, from the pricing page.

Security

Grade A, and why

xlsx-generator scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 8d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Nothing flagged

None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.

app/extensions/skills/xlsx-generator/SKILL.md · 992 lines

How it starts

The opening of the file, as written. The whole thing — 992 lines — stays where its author put it; the contents beside it link to each section on GitHub.

CODE SKILL — VERBATIM REQUIRED This skill targets a formal specification (OOXML). Adaptation is prohibited per C2-A Rule 3. Copy the Runnable Entry Point EXACTLY, replace only template variables.

CRITICAL: Execution Constraints

This skill MUST be used as a single, complete jsexec invocation. The code below is the SOLE IMPLEMENTATION — it MUST NOT be replaced, simplified, or reimplemented.

Prohibited deviations:

  • Do NOT write your own crc32() — use ONLY the one in Runnable Entry Point
  • Do NOT write your own ZipStream class — use ONLY the one in Runnable Entry Point
  • Do NOT skip any OOXML file from the baseline (7-file minimum)
  • Do NOT change from DEFLATE compression (method 8) — it is used for smaller file sizes
  • Do NOT write your own style/XML generators — use ONLY buildStylesXML(), buildSheetXML(), buildSharedStrings()
  • Do NOT write your own buildContentTypes() or buildWorkbookRels()

Correct Invocation Pattern

  1. Copy the complete script from ## Runnable Entry Point below
  2. Replace ONLY template variables {{data}}, {{output_path}} with actual values
  3. Execute as a single jsexec call with NO modifications to the core logic

Argument Details

Arg Type Required Default Description
data string (JSON) yes JSON array of sheet definitions
output_path string no /generated_spreadsheet.xlsx Output .xlsx file path

Runnable Entry Point (COMPLETE IIFE, DO NOT MODIFY)

// ===== ⛔ Runnable Entry Point (IIFE) — DO NOT MODIFY ANY CODE BELOW ⛔ =====
(function() {
  const fs = require("fs");
  const zlib = require("zlib");

  // ═══════════════════════════════════════════════════════════════════
  // CRC32 (ZIP integrity)
  // ═══════════════════════════════════════════════════════════════════
  const crc32Table = (function() {
    const t = new Uint32Array(256);
    for (let i = 0; i < 256; i++) {
      let c = i;
      for (let k = 0; k < 8; k++)
        c = (c & 1) ? 0xEDB88320 ^ (c >>> 1) : c >>> 1;
      t[i] = c;
    }
    return t;
  })();

  function crc32(buf) {
    let crc = 0xFFFFFFFF;
    for (let i = 0; i < buf.length; i++)
      crc = (crc >>> 8) ^ crc32Table[(crc ^ buf[i]) & 0xFF];
    return (crc ^ 0xFFFFFFFF) >>> 0;
  }

  // ═══════════════════════════════════════════════════════════════════
  // Shared string pool
  // ═══════════════════════════════════════════════════════════════════
  const sharedMap = new Map();
  const shared = [];
  let _userStyleBase = 1;   // cellXfs offset for user styles (1=default, 2=after date)

  /**
   * Extract the display value from a cell for shared-string pooling.
   * Dates, booleans, formulas, and nulls are NOT added to the shared pool.
   */
  function cellDisplayValue(cell) {
    if (cell == null) return null;
    if (typeof cell === "number") return null;
    if (typeof cell === "boolean") return null;
    if (cell instanceof Date) return null;
    if (typeof cell === "string") {
      if (cell.startsWith("=")) return null;
      if (/^\d{4}-\d{2}-\d{2}/.test(cell)) return null;
      return cell;
    }
    if (typeof cell === "object" && cell.t === "s" && cell.v != null) {
      const s = String(cell.v);
      if (/^\d{4}-\d{2}-\d{2}/.test(s)) return null;
      return s;
    }
    return null;
  }

  function collectShared(rows) {
    for (const row of rows) {
      for (const cell of row) {
        const v = cellDisplayValue(cell);
        if (v !== null && !sharedMap.has(v)) {
          sharedMap.set(v, shared.length);
          shared.push(v);
        }
      }
    }
  }

  // ═══════════════════════════════════════════════════════════════════
  // Column helper
  // ═══════════════════════════════════════════════════════════════════
  function col(n) {
    let s = "";
    while (n >= 0) {
      s = String.fromCharCode((n % 26) + 65) + s;
      n = Math.floor(n / 26) - 1;
    }
    return s;
  }

  // ═══════════════════════════════════════════════════════════════════
  // XML escaping utilities
  // ═══════════════════════════════════════════════════════════════════
  function esc(v) {
    // ── XML 1.0 entity escaping + invalid character filtering ──
    // Per XML 1.0 spec (§2.2), valid characters are:
    //   #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
    // Control characters outside this set are stripped to prevent XML parse errors.
    // Unicode supplementary characters (emoji, CJK extension B+, etc.) are
    // represented as UTF-16 surrogate pairs and encoded correctly via
    // Buffer.from(xml) at final output stage — they pass through this filter.
    return String(v)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/\t/g, "&#9;")
      .replace(/\n/g, "&#10;")
      .replace(/\r/g, "&#13;")
      // Strip remaining invalid XML 1.0 control characters:
      // U+0000–U+0008, U+000B–U+000C, U+000E–U+001F, plus noncharacters U+FFFE/U+FFFF
      .replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]/g, "");
  }

  function safeString(v) {
    // ── Null‑safe string wrapper ──
    // Converts null/undefined to empty string; all other values
    // pass through esc() for XML‑safe output including Unicode
    // supplementary characters (emoji, etc.) handled via UTF‑8.
    if (v === null || v === undefined) return "";
    return esc(v);
  }

  // ═══════════════════════════════════════════════════════════════════
  // Date serial number helper
  // ═══════════════════════════════════════════════════════════════════
  function dateToSerial(d) {
    const epoch = Date.UTC(1899, 11, 30);
    const lotusBugFix = Date.UTC(1900, 2, 1);
    let serial = (d.getTime() - epoch) / 86400000;
    if (d.getTime() >= lotusBugFix) serial += 1;
    return serial;
  }

  function tryParseDateString(s) {
    // ── Guard: only process string values ──
    if (typeof s !== "string") return null;
    const trimmed = s.trim();

    // ── Supported date formats ──
    // • YYYY-MM-DD          (ISO 8601 date)
    // • YYYY/MM/DD          (alternative separator)
    // • YYYY-MM-DDTHH:MM    (ISO 8601 with time portion preserved)
    // • YYYY-MM-DDTHH:MM:SS
    // Unsupported: MM/DD/YYYY and DD/MM/YYYY are ambiguous across locales
    if (!/^\d{4}[-\/]\d{2}[-\/]\d{2}/.test(trimmed)) return null;

    const d = new Date(trimmed);
    if (isNaN(d.getTime())) return null;

    // ── Sanity check: reject dates before Excel epoch (1899-12-30) ──
    // Excel serial numbers go negative for pre-epoch dates; this
    // generator does not support dates before the Excel epoch
    const epoch = Date.UTC(1899, 11, 30);
    if (d.getTime() < epoch) return null;

    return d;
  }

  // ═══════════════════════════════════════════════════════════════════
  // Column width auto-fit
  // ═══════════════════════════════════════════════════════════════════
  function buildCols(rows, colConfig) {
    const maxCols = rows.reduce((m, r) => Math.max(m, r.length), 0);
    if (maxCols === 0) return "";

    const CHAR_WIDTH = 1.15;
    const MIN_WIDTH = 8;
    const MAX_WIDTH = 60;

    const contentWidths = new Array(maxCols).fill(MIN_WIDTH);
    for (let c = 0; c < maxCols; c++) {
      for (const row of rows) {
        const v = row[c];
        if (v == null) continue;
        let len = 0;
        if (v instanceof Date) {
          len = 10;
        } else if (typeof v === "object" && v.v != null) {
          len = String(v.v).length;
        } else if (typeof v === "string" && v.startsWith("=")) {
          len = v.length;
        } else {
          len = String(v).length;
        }
        contentWidths[c] = Math.max(contentWidths[c], Math.min(len * CHAR_WIDTH + 2, MAX_WIDTH));
      }
    }

    let xml = "<cols>";
    for (let c = 0; c < maxCols; c++) {
      const w = (colConfig && colConfig[c] && colConfig[c].width != null)
        ? colConfig[c].width
        : Math.round(contentWidths[c] * 100) / 100;
      xml += `<col min="${c + 1}" max="${c + 1}" width="${w}" customWidth="1"/>`;
    }
    xml += "</cols>";
    return xml;
  }

  // ═══════════════════════════════════════════════════════════════════
  // Styles builder
  // ═══════════════════════════════════════════════════════════════════
  function buildStyles(st) {
    const cfg = st || {};

    // ── numFmts ──
    const numFmts = cfg.numFmts || [];
    let numFmtsXml = "";
    if (numFmts.length > 0) {
      numFmtsXml = `<numFmts count="${numFmts.length}">`;
      for (const nf of numFmts)
        numFmtsXml += `<numFmt numFmtId="${nf.id}" formatCode="${esc(nf.formatCode)}"/>`;
      numFmtsXml += `</numFmts>`;
    }

    // ── fonts (always >=1) ──
    const fonts = cfg.fonts || [];
    const allFonts = [{ sz: 11, name: "Calibri" }, ...fonts];
    let fontsXml = `<fonts count="${allFonts.length}">`;
    for (const f of allFonts) {
      fontsXml += "<font>";
      if (f.b) fontsXml += "<b/>";
      if (f.i) fontsXml += "<i/>";
      if (f.u) fontsXml += "<u/>";
      if (f.sz) fontsXml += `<sz val="${f.sz}"/>`;
      if (f.color) fontsXml += `<color rgb="${f.color}"/>`;
      if (f.name) fontsXml += `<name val="${esc(f.name)}"/>`;
      fontsXml += "</font>";
    }
    fontsXml += "</fonts>";

    // ── fills (always >=2: none + gray125) ──
    const fills = cfg.fills || [];
    const allFills = [
      { patternType: "none" },
      { patternType: "gray125" },
      ...fills
    ];
    let fillsXml = `<fills count="${allFills.length}">`;
    for (const fl of allFills) {
      fillsXml += "<fill>";
      fillsXml += `<patternFill patternType="${fl.patternType}">`;
      if (fl.fgColor) fillsXml += `<fgColor rgb="${fl.fgColor}"/>`;
      if (fl.bgColor) fillsXml += `<bgColor rgb="${fl.bgColor}"/>`;
      fillsXml += "</patternFill>";
      fillsXml += "</fill>";
    }
    fillsXml += "</fills>";

    // ── borders (always >=1) ──
    const borders = cfg.borders || [];
    const allBorders = [{}, ...borders];
    let bordersXml = `<borders count="${allBorders.length}">`;
    for (const br of allBorders) {
      bordersXml += "<border>";
      for (const side of ["left", "right", "top", "bottom"]) {
        const s = br[side] || br.style || null;
        if (s) {
          bordersXml += `<${side} style="${s}"><color rgb="${br.color || "000000"}"/></${side}>`;
        } else {
          bordersXml += `<${side}/>`;
        }
      }
      bordersXml += "</border>";
    }
    bordersXml += "</borders>";

    // ── cellStyleXfs ──
    const cellStyleXfs = `<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>`;

    // ── cellXfs — combined format records ──
    //
    // cellXfs layout (the "style index" that <c s="N"> references):
    //
    //   Index 0  →  Default style (numFmtId=0, fontId=0, fillId=0, borderId=0)
    //   Index 1  →  Date format (only if numFmts.length > 0)
    //   Index N  →  User styles (N = _userStyleBase + userS), where userS is the
    //               0‑based index supplied in the cell object's `s` field
    //
    // _userStyleBase = 1 + (hasDate ? 1 : 0)
    //   • If no custom numFmts → _userStyleBase = 1, user style `s:0` → cellXfs[1]
    //   • If custom numFmts  → _userStyleBase = 2, user style `s:0` → cellXfs[2]
    //                          (cellXfs[1] is reserved for the date format entry)
    //
    // Internal cellXfs index = _userStyleBase + cellObject.s
    const hasDate = numFmts.length > 0;
    const userStyleCount = Math.max(fonts.length, fills.length, borders.length);
    _userStyleBase = 1 + (hasDate ? 1 : 0);

    let cellXfsCount = 1 + (hasDate ? 1 : 0) + userStyleCount;
    let cellXfsXml = `<cellXfs count="${cellXfsCount}">`;

    // Entry 0: default
    cellXfsXml += `<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>`;

    // Entry 1: date format
    if (hasDate) {
      cellXfsXml += `<xf numFmtId="${numFmts[0].id}" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/>`;
    }

    // User style entries
    for (let i = 0; i < userStyleCount; i++) {
      const fontId = Math.min(i + 1, allFonts.length - 1);
      const fillId = Math.min(i + 2, allFills.length - 1);
      const borderId = Math.min(i + 1, allBorders.length - 1);
      cellXfsXml += `<xf numFmtId="0" fontId="${fontId}" fillId="${fillId}" borderId="${borderId}" xfId="0" applyFont="1" applyFill="1" applyBorder="1"/>`;
    }

    cellXfsXml += `</cellXfs>`;

    const cellStylesXml = `<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>`;
    const dxfsXml = `<dxfs count="0"/>`;
    const tableStylesXml = `<tableStyles count="0" defaultTableStyle="TableStyleMedium2" defaultPivotStyle="PivotStyleLight16"/>`;

    return Buffer.from(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
${numFmtsXml}
${fontsXml}
${fillsXml}
${bordersXml}
${cellStyleXfs}
${cellXfsXml}
${cellStylesXml}
${dxfsXml}
${tableStylesXml}
</styleSheet>`);
  }

  // ═══════════════════════════════════════════════════════════════════
  // Theme builder
  // ═══════════════════════════════════════════════════════════════════
  function buildTheme() {
    return Buffer.from(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme">
  <a:themeElements>
    <a:clrScheme name="Office">
      <a:dk1><a:srgbClr val="000000"/></a:dk1>
      <a:lt1><a:srgbClr val="FFFFFF"/></a:lt1>
      <a:dk2><a:srgbClr val="44546A"/></a:dk2>
      <a:lt2><a:srgbClr val="E7E6E6"/></a:lt2>
      <a:accent1><a:srgbClr val="4472C4"/></a:accent1>
      <a:accent2><a:srgbClr val="ED7D31"/></a:accent2>
      <a:accent3><a:srgbClr val="A5A5A5"/></a:accent3>
      <a:accent4><a:srgbClr val="FFC000"/></a:accent4>
      <a:accent5><a:srgbClr val="5B9BD5"/></a:accent5>
      <a:accent6><a:srgbClr val="70AD47"/></a:accent6>
      <a:hlink><a:srgbClr val="0563C1"/></a:hlink>
      <a:folHlink><a:srgbClr val="954F72"/></a:folHlink>
    </a:clrScheme>
    <a:fontScheme name="Office">
      <a:majorFont><a:latin typeface="Calibri Light"/><a:ea typeface="Calibri Light"/><a:cs typeface="Calibri Light"/></a:majorFont>
      <a:minorFont><a:latin typeface="Calibri"/><a:ea typeface="Calibri"/><a:cs typeface="Calibri"/></a:minorFont>
    </a:fontScheme>
    <a:fmtScheme name="Office">
      <a:fillStyleLst>
        <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
        <a:gradFill rotWithShape="1">
          <a:gsLst>
            <a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="50000"/><a:satMod val="300000"/></a:schemeClr></a:gs>
            <a:gs pos="35000"><a:schemeClr val="phClr"><a:tint val="37000"/><a:satMod val="300000"/></a:schemeClr></a:gs>
            <a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="15000"/><a:satMod val="350000"/></a:schemeClr></a:gs>
          </a:gsLst>
          <a:lin ang="16200000" scaled="1"/>
        </a:gradFill>
        <a:gradFill rotWithShape="1">
          <a:gsLst>
            <a:gs pos="0"><a:schemeClr val="phClr"><a:shade val="51000"/><a:satMod val="130000"/></a:schemeClr></a:gs>
            <a:gs pos="80000"><a:schemeClr val="phClr"><a:shade val="93000"/><a:satMod val="130000"/></a:schemeClr></a:gs>
            <a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="94000"/><a:satMod val="135000"/></a:schemeClr></a:gs>
          </a:gsLst>
          <a:lin ang="16200000" scaled="0"/>
        </a:gradFill>
      </a:fillStyleLst>
      <a:lnStyleLst>
        <a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"><a:shade val="95000"/><a:satMod val="105000"/></a:schemeClr></a:solidFill><a:prstDash val="solid"/></a:ln>
        <a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln>
        <a:ln w="38100" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln>
      </a:lnStyleLst>
      <a:effectStyleLst>
        <a:effectStyle><a:effectLst/></a:effectStyle>
        <a:effectStyle><a:effectLst/></a:effectStyle>
        <a:effectStyle><a:effectLst/></a:effectStyle>
      </a:effectStyleLst>
      <a:bgFillStyleLst>
        <a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
        <a:gradFill rotWithShape="1">
          <a:gsLst>
            <a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="40000"/><a:satMod val="350000"/></a:schemeClr></a:gs>
            <a:gs pos="40000"><a:schemeClr val="phClr"><a:tint val="45000"/><a:satMod val="350000"/><a:shade val="99000"/></a:schemeClr></a:gs>
            <a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="20000"/><a:satMod val="255000"/></a:schemeClr></a:gs>
          </a:gsLst>
          <a:path path="circle"><a:fillToRect l="50000" t="-80000" r="50000" b="180000"/></a:path>
        </a:gradFill>
        <a:gradFill rotWithShape="1">
          <a:gsLst>
            <a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="80000"/><a:satMod val="300000"/></a:schemeClr></a:gs>
            <a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="30000"/><a:satMod val="200000"/></a:schemeClr></a:gs>
          </a:gsLst>
          <a:path path="circle"><a:fillToRect l="50000" t="50000" r="50000" b="50000"/></a:path>
        </a:gradFill>
      </a:bgFillStyleLst>
    </a:fmtScheme>
  </a:themeElements>
  <a:objectDefaults/>
  <a:extraClrSchemeLst/>
</a:theme>`);
  }

  // ═══════════════════════════════════════════════════════════════════
  // Worksheet builder
  // ═══════════════════════════════════════════════════════════════════
  function parseCell(v) {
    if (v == null) return { isEmpty: true };

    // ── Cell object: explicit type/style/formula ──
    // When `v.s` is set, the actual cellXfs index = _userStyleBase + v.s
    //   • _userStyleBase = 1 (no custom numFmts) → s:0 → cellXfs[1]
    //   • _userStyleBase = 2 (has custom numFmts) → s:0 → cellXfs[2]
    // This allows users to use 0‑based style indices (`s: 0, 1, 2...`)
    // without needing to know the internal cellXfs layout.
    // Date cells always receive `s: 1` — they use the dedicated date format
    // entry which is cellXfs[1] (only present when numFmts.length > 0).
    if (typeof v === "object" && v !== null && !(v instanceof Date)) {
      if (v.t === "s" && v.v != null) {
        const key = String(v.v);
        const idx = sharedMap.get(key);
        return { v: idx, t: "s", f: v.f || null, s: v.s != null ? _userStyleBase + v.s : 0, isEmpty: false };
      }
      return {
        v: v.v,
        t: v.t || (typeof v.v === "number" ? "n" : "s"),
        f: v.f || null,
        s: v.s != null ? _userStyleBase + v.s : 0,
        isEmpty: false
      };
    }

    // Date object
    if (v instanceof Date) {
      return { v: v, t: "d", f: null, s: 1, isEmpty: false };
    }

    // Date string (YYYY-MM-DD)
    if (typeof v === "string" && /^\d{4}-\d{2}-\d{2}/.test(v)) {
      const d = tryParseDateString(v);
      if (d) return { v: d, t: "d", f: null, s: 1, isEmpty: false };
    }

    // Formula
    if (typeof v === "string" && v.startsWith("=")) {
      return { v: v.substring(1), t: "str", f: null, s: 0, isEmpty: false, formulaText: v };
    }

    // Boolean
    if (typeof v === "boolean") {
      return { v: v ? 1 : 0, t: "b", f: null, s: 0, isEmpty: false };
    }

    // Number
    if (typeof v === "number") {
      return { v: v, t: "n", f: null, s: 0, isEmpty: false };
    }

    // String (default, via shared pool)
    const key = String(v);
    const idx = sharedMap.get(key);
    return { v: idx, t: "s", f: null, s: 0, isEmpty: false };
  }

  function buildSheet(sheet, index) {
    const rows = sheet.rows;
    const merges = sheet.merges || [];
    const frozen = sheet.frozen || { row: 0, col: 0 };
    const colConfig = sheet.columns || null;

    let xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`;

    // Sheet views (frozen panes + tab selection on first sheet)
    const isFirst = (index === 0);
    const hasFrozen = frozen.row > 0 || frozen.col > 0;
    if (hasFrozen || isFirst) {
      let paneXml = '';
      if (hasFrozen) {
        const topLeft = (frozen.col > 0 ? col(frozen.col) : "A") + (frozen.row + 1);
        const pane = [];
        if (frozen.col > 0) pane.push(`xSplit="${frozen.col}"`);
        if (frozen.row > 0) pane.push(`ySplit="${frozen.row}"`);
        let activePane = "bottomLeft";
        if (frozen.row > 0 && frozen.col === 0) activePane = "bottomLeft";
        else if (frozen.col > 0 && frozen.row === 0) activePane = "topRight";
        else if (frozen.row > 0 && frozen.col > 0) activePane = "bottomRight";
        paneXml = `<pane ${pane.join(" ")} topLeftCell="${topLeft}" activePane="${activePane}" state="frozen"/>`;
      }
      xml += `
<sheetViews>
  <sheetView workbookViewId="0"${isFirst ? ' tabSelected="1"' : ''}>
    ${paneXml}
  </sheetView>
</sheetViews>`;
    }

    // Column widths
    xml += `
${buildCols(rows, colConfig)}`;

    // Sheet data
    xml += `
<sheetData>`;

    for (let r = 0; r < rows.length; r++) {
      xml += `<row r="${r + 1}">`;

      for (let c = 0; c < rows[r].length; c++) {
        const cell = parseCell(rows[r][c]);
        if (cell.isEmpty) continue;

        const ref = col(c) + (r + 1);
        let cellXml = `<c r="${ref}"`;

        // Type attribute
        if (cell.t === "s") cellXml += ` t="s"`;
        else if (cell.t === "b") cellXml += ` t="b"`;
        else if (cell.t === "str") cellXml += ` t="str"`;

        // Style reference
        if (cell.s > 0) cellXml += ` s="${cell.s}"`;

        cellXml += `>`;

        // Formula
        if (cell.t === "str" && cell.formulaText) {
          cellXml += `<f>${esc(cell.formulaText.substring(1))}</f>`;
        } else if (cell.f) {
          cellXml += `<f>${esc(cell.f)}</f>`;
        }

        // Value
        if (cell.t === "d") {
          cellXml += `<v>${dateToSerial(cell.v)}</v>`;
        } else if (cell.t === "str" && cell.formulaText) {
          cellXml += `<v>0</v>`;
        } else if (cell.v != null) {
          cellXml += `<v>${cell.v}</v>`;
        }

        cellXml += `</c>`;
        xml += cellXml;
      }

      xml += `</row>`;
    }

    xml += `</sheetData>`;

    // Merge cells
    if (merges.length > 0) {
      xml += `
<mergeCells count="${merges.length}">`;
      for (const m of merges) {
        xml += `<mergeCell ref="${m}"/>`;
      }
      xml += `
</mergeCells>`;
    }

    xml += `</worksheet>`;
    return Buffer.from(xml);
  }

  // ═══════════════════════════════════════════════════════════════════
  // SharedStrings XML
  // ═══════════════════════════════════════════════════════════════════
  function buildSharedStrings(arr) {
    let xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
 count="${arr.length}" uniqueCount="${arr.length}">`;

    for (const v of arr) {
      xml += `<si><t>${safeString(v)}</t></si>`;
    }

    xml += `</sst>`;
    return Buffer.from(xml);
  }

  // ═══════════════════════════════════════════════════════════════════
  // Content Types
  // ═══════════════════════════════════════════════════════════════════
  function contentTypes(sheetCount) {
    let xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">

<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>

<Override PartName="/xl/workbook.xml"
 ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>

<Override PartName="/xl/sharedStrings.xml"
 ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>

<Override PartName="/xl/styles.xml"
 ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>

<Override PartName="/xl/theme/theme1.xml"
 ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>`;

    for (let i = 1; i <= sheetCount; i++) {
      xml += `
<Override PartName="/xl/worksheets/sheet${i}.xml"
 ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`;
    }

    xml += `</Types>`;
    return Buffer.from(xml);
  }

  // ═══════════════════════════════════════════════════════════════════
  // Root relationships
  // ═══════════════════════════════════════════════════════════════════
  function rootRels() {
    return Buffer.from(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1"
 Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
 Target="xl/workbook.xml"/>
</Relationships>`);
  }

  // ═══════════════════════════════════════════════════════════════════
  // Workbook definition
  // ═══════════════════════════════════════════════════════════════════
  function workbook(sheets) {
    let xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
 xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">

<sheets>`;

    for (let i = 0; i < sheets.length; i++) {
      xml += `<sheet name="${esc(sheets[i].name)}"
 sheetId="${i + 1}"
 r:id="rId${i + 1}"/>`;
    }

    xml += `</sheets>
</workbook>`;
    return Buffer.from(xml);
  }

  // ═══════════════════════════════════════════════════════════════════
  // Workbook relationships
  // ═══════════════════════════════════════════════════════════════════
  function workbookRels(sheetCount) {
    let xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">`;

    let nextRId = 1;

    for (let i = 0; i < sheetCount; i++) {
      xml += `
<Relationship Id="rId${nextRId++}"
 Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
 Target="worksheets/sheet${i + 1}.xml"/>`;
    }

    xml += `
<Relationship Id="rId${nextRId++}"
 Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
 Target="styles.xml"/>`;

    xml += `
<Relationship Id="rId${nextRId++}"
 Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"
 Target="theme/theme1.xml"/>`;

    xml += `
<Relationship Id="rId${nextRId++}"
 Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"
 Target="sharedStrings.xml"/>`;

    xml += `</Relationships>`;
    return Buffer.from(xml);
  }

  // ═══════════════════════════════════════════════════════════════════
  // ZIP builder
  // ═══════════════════════════════════════════════════════════════════
  class ZipStream {
    constructor(opts) {
      this.entries = [];
      this.chunks = [];
      this.offset = 0;
      this.compression = (opts && opts.compression === 8) ? 8 : 0;
    }

    writeFile(name, data) {
      const raw = Buffer.isBuffer(data) ? data : Buffer.from(data);
      const nameBuf = Buffer.from(name);
      const fileCrc = crc32(raw);

      let buf = raw;
      let method = 0;
      if (this.compression === 8) {
        buf = zlib.deflateRawSync(raw);
        method = 8;
      }

      const header = Buffer.alloc(30);
      header.writeUInt32LE(0x04034b50, 0);
      header.writeUInt16LE(20, 4);
      header.writeUInt16LE(0, 6);
      header.writeUInt16LE(method, 8);
      header.writeUInt32LE(fileCrc, 14);
      header.writeUInt32LE(buf.length, 18);
      header.writeUInt32LE(raw.length, 22);
      header.writeUInt16LE(nameBuf.length, 26);

      const local = Buffer.concat([header, nameBuf]);
      this.chunks.push(local, buf);

      this.entries.push({
        name,
        crc: fileCrc,
        size: raw.length,
        compressedSize: buf.length,
        method,
        offset: this.offset
      });
      this.offset += local.length + buf.length;
    }

    finalize() {
      const central = [];
      for (const e of this.entries) {
        const nameBuf = Buffer.from(e.name);
        const c = Buffer.alloc(46);
        c.writeUInt32LE(0x02014b50, 0);
        c.writeUInt16LE(20, 4);
        c.writeUInt16LE(20, 6);
        c.writeUInt16LE(0, 8);
        c.writeUInt16LE(e.method, 10);
        c.writeUInt32LE(e.crc, 16);
        c.writeUInt32LE(e.compressedSize, 20);
        c.writeUInt32LE(e.size, 24);
        c.writeUInt16LE(nameBuf.length, 28);
        c.writeUInt32LE(e.offset, 42);
        central.push(c, nameBuf);
      }

      const centralBuf = Buffer.concat(central);
      const eocd = Buffer.alloc(22);
      eocd.writeUInt32LE(0x06054b50, 0);
      eocd.writeUInt16LE(this.entries.length, 8);
      eocd.writeUInt16LE(this.entries.length, 10);
      eocd.writeUInt32LE(centralBuf.length, 12);
      eocd.writeUInt32LE(this.offset, 16);

      return Buffer.concat([...this.chunks, centralBuf, eocd]);
    }
  }

  // ═══════════════════════════════════════════════════════════════════
  // XLSX builder (main orchestrator)
  // ═══════════════════════════════════════════════════════════════════
  function buildXLSX(sheets, styles) {
    const files = [];

    // Reset shared strings pool (module-level state — safe in IIFE)
    sharedMap.clear();
    shared.length = 0;

    // Collect shared strings from all sheets
    for (const s of sheets) {
      collectShared(s.rows);
    }

    // Build all OOXML XML parts
    files.push({ name: "[Content_Types].xml", data: contentTypes(sheets.length) });
    files.push({ name: "_rels/.rels", data: rootRels() });
    files.push({ name: "xl/workbook.xml", data: workbook(sheets) });
    files.push({ name: "xl/_rels/workbook.xml.rels", data: workbookRels(sheets.length) });

    // Styles (always included — minimal if no config)
    files.push({ name: "xl/styles.xml", data: buildStyles(styles) });

    // Theme (always included — required by OOXML spec)
    files.push({ name: "xl/theme/theme1.xml", data: buildTheme() });

    // Worksheets
    for (let i = 0; i < sheets.length; i++) {
      files.push({
        name: `xl/worksheets/sheet${i + 1}.xml`,
        data: buildSheet(sheets[i], i)
      });
    }

    // Shared strings
    files.push({
      name: "xl/sharedStrings.xml",
      data: buildSharedStrings(shared)
    });

    // Package into ZIP (DEFLATE mode)
    const zs = new ZipStream({ compression: 8 });
    for (const f of files) {
      zs.writeFile(f.name, f.data);
    }
    return zs.finalize();
  }

  // ═══════════════════════════════════════════════════════════════════
  // Entry Point
  // ═══════════════════════════════════════════════════════════════════
  const data = {{data}};
  const outputPath = "{{output_path}}";

  // Normalize input: Format 1 (multi-sheet object) or Format 2 (simple 2D array)
  const isArr = Array.isArray(data);
  const sheets = isArr
    ? [{ name: "Sheet1", rows: data }]
    : data.sheets;
  const styles = isArr ? null : (data.styles || null);

  // Generate the XLSX buffer
  const buf = buildXLSX(sheets, styles);

  // Ensure output directory exists
  const outputDir = require("path").dirname(outputPath);
  try { fs.mkdirSync(outputDir, { recursive: true }); } catch (e) { /* Wasm compat */ }

  // Write output file
  fs.writeFileSync(outputPath, buf);
  console.log(`XLSX written: ${outputPath} (${buf.length} bytes)`);
})();

Read the full file on GitHub · 992 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 8d ago First seen · 992 lines · 72 tokens per session scan A 8d572643d050

Subscribe to this mod's changes

xlsx-generator is a skill published in the GitHub repository guyoung/boxagnts (11 stars, last pushed 1mo ago), licensed MIT. It adds 72 tokens to every session and 11,358 once invoked, about $0.0004 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.

Related

Other skills, from other repositories

data-analysis

Use this skill when the user uploads Excel (.xlsx/.xls) or CSV files and wants to perform data analysis, generate statistics, create summaries, pivot tables, SQL queries, or any form of structured data exploration. Supports multi-sheet Excel workbooks, aggregation, filtering, joins, and exporting results to…

bytedance/deer-flow · 69 tokens

officecli-financial-model

Use this skill when the user wants to build a financial model — 3-statement model, DCF valuation, LBO, SaaS unit economics, sensitivity / scenario analysis, debt schedule, or fundraising projections — in Excel. Trigger on: 'financial model', '3-statement model', 'P&L + BS + CF', 'DCF', 'WACC', 'NPV', 'terminal value'…

iOfficeAI/OfficeCLI · 222 tokens

xlsx

Use this skill any time a spreadsheet file is the primary input or output. This means any task where the user wants to: open, read, edit, or fix an existing .xlsx, .xlsm, .csv, or .tsv file (e.g., adding columns, computing formulas, formatting, charting, cleaning messy data); create a new spreadsheet from scratch or…

agentscope-ai/QwenPaw · 201 tokens

xlsx

A guide for working with spreadsheet files such as Excel workbooks, CSV files, and TSV files.

agentscope-ai/QwenPaw · 232 tokens

officecli-data-dashboard

Use this skill to build a multi-element Excel dashboard — Dashboard sheet on open, multiple formula-driven KPI cards, multiple charts, sparklines, and conditional formatting — from CSV or tabular input. Trigger on: 'dashboard', 'KPI dashboard', 'analytics dashboard', 'executive dashboard', 'metrics dashboard', 'CSV to…

iOfficeAI/OfficeCLI · 157 tokens

officecli-xlsx

Use this skill any time a .xlsx file is involved -- as input, output, or both. This includes: creating spreadsheets, financial models, dashboards, or trackers; reading, parsing, or extracting data from any .xlsx file; editing, modifying, or updating existing workbooks; working with formulas, charts, pivot tables, or…

iOfficeAI/OfficeCLI · 119 tokens