---
name: xlsx
description: Use for spreadsheet work—.xlsx, .xlsm, .csv, .tsv—reading, editing, models, or conversion. Trigger when the deliverable is a spreadsheet file.
user-invocable: true
---

# SpreadsheetML (XLSX) skill

## Must-read guides (load with `read_file`)

| File | When |
|------|------|
| [`spreadsheet_practices.md`](spreadsheet_practices.md) | Formulas vs hardcoded values, cell formatting, conditional formatting, data validation, multi-sheet patterns, data cleaning, **mandatory recalc** path, financial conventions. |
| [`charting_and_analysis.md`](charting_and_analysis.md) | Creating charts (bar, line, pie, scatter, combo), chart styling, failure modes, data analysis patterns. |

## Tool decision matrix

| Task | Tool |
|------|------|
| Load / analyze / clean tabular data | **pandas** (`read_excel`, `read_csv`, `to_excel`) |
| Preserve formulas, styles, charts, formatting | **openpyxl** (use `load_workbook(keep_vba=True)` for `.xlsm`) |
| CSV / TSV ingest or export | **pandas** or `csv` stdlib |
| Profile data quality before editing | `scripts/data_profiler.py` |
| Recalculate formulas and detect errors | `scripts/workbook_recalculate.py` (LibreOffice) |
| Validate package structure | `scripts/ooxml_kit/audit_ooxml_package.py` |
| Raw OOXML surgery (themes, connections, unusual parts) | `expand_ooxml_zip.py` → edit XML → `build_ooxml_zip.py` |

Use **pandas** for bulk data work; switch to **openpyxl** when formatting, formulas, or charts matter. Combine both when needed: load with pandas, write with openpyxl for styled output.

## Scripts

| Script | Role |
|--------|------|
| `data_profiler.py` | Profile CSV/TSV/XLSX: column stats, data quality issues, merged cells. Outputs JSON. |
| `workbook_recalculate.py` | LibreOffice macro to recalculate formulas; prints JSON with `#REF!`-style error cells. |

## OOXML kit

`scripts/ooxml_kit/` matches the other Office skills: unpack/repack, validation, LibreOffice helper, XSD schemas under `schemas/`.

| Script | Role |
|--------|------|
| `expand_ooxml_zip.py` | Unpack workbook / shared strings / sheets for XML editing. |
| `build_ooxml_zip.py` | Pack tree back to `.xlsx` with validation. |
| `audit_ooxml_package.py` | SpreadsheetML package validation CLI. |

## Charting

Use the **openpyxl chart API** (`openpyxl.chart`). Never generate chart XML manually — it is fragile and error-prone. See `charting_and_analysis.md` for all chart type patterns and failure modes.

## Format conversion

```python
# CSV / TSV → XLSX
df = pd.read_csv("data.csv")            # or read_csv("data.tsv", sep="\t")
df.to_excel("out.xlsx", index=False)

# Multiple CSVs → multi-sheet workbook
with pd.ExcelWriter("out.xlsx", engine="openpyxl") as writer:
    for name, path in files.items():
        pd.read_csv(path).to_excel(writer, sheet_name=name, index=False)

# XLSX → CSV
pd.read_excel("in.xlsx").to_csv("out.csv", index=False)

# XLSM (macro-enabled) — preserve VBA
from openpyxl import load_workbook
wb = load_workbook("in.xlsm", keep_vba=True)
wb.save("out.xlsm")
```

## Data cleaning pipeline

1. **Profile**: `python scripts/data_profiler.py input.csv` — understand shape, types, issues.
2. **Clean**: pandas — drop junk rows, normalize headers, coerce types, handle merged cells.
3. **Structure**: Write clean data to openpyxl workbook with formatting.
4. **Validate**: If formulas exist, `python scripts/workbook_recalculate.py output.xlsx`.

## Typical flows

- **Profile**: `python scripts/data_profiler.py data.xlsx --sheet Sheet1`
- **Recalc**: `python scripts/workbook_recalculate.py model.xlsx`
- **Validate**: `python scripts/ooxml_kit/audit_ooxml_package.py report.xlsx`
- **Unpack**: `python scripts/ooxml_kit/expand_ooxml_zip.py book.xlsx unpacked/`
- **Pack**: `python scripts/ooxml_kit/build_ooxml_zip.py unpacked/ out.xlsx --original book.xlsx`

## Dependencies

`openpyxl`, `pandas`, `lxml`, `defusedxml`, LibreOffice for `workbook_recalculate.py`.

## Important: do not read .xlsx files with Read tool

After creating an `.xlsx` file, do **not** use the Read tool or Bash `view` to verify it — `.xlsx` files are binary ZIP archives and cannot be read as text. Instead:
- Confirm success from the script's stdout (e.g., `print("Created:", path)`)
- If verification is needed, use openpyxl or pandas to open the file and print sheet contents
- Always wrap the script body in `try/except` and print `ERROR:<type>:<message>` on failure
- **Only reference the file for delivery if the Bash output contains `SAVED:<path>`** — if the output shows an error or a non-zero exit code, the file was not created
