Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Create, read, edit Excel .xlsx spreadsheets and CSVs.
.claude/skills/nousresearch-xlsx/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 84% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 79% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 62% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 40% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 37% | 0% |
Work with Excel .xlsx workbooks using Python and openpyxl: build styled multi-sheet workbooks with formulas and charts, inspect or dump existing files, edit cells and structure, and convert to/from CSV. All helper scripts are argparse CLIs that print JSON and use explicit UTF-8 I/O.
merged cells, freeze panes, autofilter, conditional formatting, charts, data-validation dropdowns, native Excel tables, defined names, hyperlinks, cell notes, sheet protection.
listing formulas vs cached values, notes, defined names, tables.
rows/columns (reference-aware via xlsx_restructure.py), copy/rename sheets, tables, names, notes, protection.
(xlsx_recalc.py).
first: soffice --headless --convert-to xlsx old.xls).
openpyxl (pip install openpyxl). No otherthird-party packages are needed; everything else is stdlib.
soffice) for headless recalculation orformat conversion.
Run the helper scripts with the terminal tool from this skill's scripts/ directory (every script supports --help):
bashpython scripts/xlsx_create.py spec.json report.xlsx # build from JSON spec python scripts/xlsx_read.py report.xlsx --sheets # inventory python scripts/xlsx_read.py report.xlsx --json --sheet Data python scripts/xlsx_read.py report.xlsx --formulas python scripts/xlsx_edit.py report.xlsx --sheet Data --set B2=42 --recalc python scripts/xlsx_restructure.py report.xlsx --sheet Data --insert-rows 3:2 python scripts/xlsx_recalc.py report.xlsx python scripts/csv_to_xlsx.py data.csv out.xlsx --encoding utf-8 python scripts/xlsx_to_csv.py report.xlsx out.csv --sheet Data
Author the JSON spec with write_file, inspect script JSON output with read_file or directly from stdout.
| Task | Command | |---|---| | Create workbook from spec | xlsx_create.py spec.json out.xlsx | | Sheet names + dimensions | xlsx_read.py f.xlsx --sheets | | Dump sheet as JSON | xlsx_read.py f.xlsx --json --sheet S | | Dump sheet as CSV | xlsx_read.py f.xlsx --csv --out d.csv | | List formulas + cached values | xlsx_read.py f.xlsx --formulas | | Set a cell / formula | xlsx_edit.py f.xlsx --set "A1==SUM(B:B)" | | Append a row | xlsx_edit.py f.xlsx --append '[1,"x",true]' | | Insert 2 rows, refs NOT shifted | xlsx_edit.py f.xlsx --insert-rows 3:2 | | Insert 2 rows, refs shifted | xlsx_restructure.py f.xlsx --insert-rows 3:2 | | Delete a column, refs shifted | xlsx_restructure.py f.xlsx --delete-cols B | | Create a native table | xlsx_edit.py f.xlsx --add-table Sales:A1:C9 | | Append inside a table | --table-append 'Sales=["West",5]' | | List tables | xlsx_edit.py f.xlsx --list-tables | | Defined names | --define-name "Rates='Data'!$B$2:$B$9" / --delete-name Rates / xlsx_read.py f.xlsx --names | | Hyperlink | --hyperlink "A1=https://example.com|Docs" | | Cell note | --note "B2=Check this|Reviewer"; read via xlsx_read.py f.xlsx --notes | | Protect sheet (see Pitfalls) | --protect your-password --unlock B2:B9 | | Recalculate via LibreOffice | xlsx_recalc.py f.xlsx | | Copy / rename sheet | --copy-sheet Src:New --rename-sheet Old:New | | Force recalc on open | xlsx_edit.py f.xlsx --recalc | | CSV -> styled xlsx | csv_to_xlsx.py in.csv out.xlsx | | xlsx -> CSV | xlsx_to_csv.py f.xlsx out.csv --encoding utf-8 |
xlsx_create.py --help and its docstring). Each sheet supports rows (scalars or styled cell objects), sparse cells overrides, column_widths, row_heights, merges, freeze_panes, autofilter, conditional_formats (cell_is rules and color scales), charts (bar/line/pie from cell ranges), validations (list dropdowns), tables (native Excel tables with a style name), and protection. Workbook-level defined_names maps names to refs. Cell objects also take hyperlink and note. Typed values: JSON numbers/bools pass through; dates use {"value": "2026-01-31", "type": "date"}. Number formats are Excel format strings: currency "$#,##0.00", percent "0.0%", date "yyyy-mm-dd".
"formula": "SUM(B2:B9)" in the spec or--set "C1==SUM(A:A)" in the editor. When writing formulas, add "full_calc_on_load": true (spec) or --recalc (editor); this sets the workbook's fullCalcOnLoad flag so Excel/LibreOffice recompute everything on open. openpyxl itself NEVER evaluates formulas.
--sheets for inventory (names, dimensions, mergedranges, chart count, tables, protection, defined names), --json/--csv for data, --formulas to pair each formula string with its cached result, --notes for cell comments, --names for defined names. Cached results exist only if the file was last saved by a real spreadsheet app; files fresh from openpyxl return null there. To materialize results headlessly run xlsx_recalc.py file.xlsx (uses LibreOffice; prints {"recalculated": false, ...} and exits 0 when soffice is absent), then reload with --data-only.
xlsx_edit.py applies renames/copies first, thenstructural row/column changes, then --set/--append. It edits in place unless --out is given — copy the file first if you need the original.
merges, tables, or filters, use xlsx_restructure.py instead of xlsx_edit.py. It rewrites formula references on ALL sheets (absolute $ refs, ranges, cross-sheet refs), shifts merges, autofilter, freeze panes, validation and conditional-format ranges, table refs, defined names, and row/column dimensions, then prints a JSON report including a not_shifted list. Rules and limits: references/restructuring.md.
csv_to_xlsx.py infers int/float/bool/ISO-dateper cell and styles the header row; xlsx_to_csv.py writes ISO dates and blank strings for empty cells. Both default to UTF-8 and accept --encoding (e.g. utf-8-sig for Excel-friendly BOM, cp1252 for legacy Windows exports).
LibreOffice converts headlessly (also works for CSV export of a single sheet):
bashsoffice --headless --convert-to pdf report.xlsx --outdir out/ soffice --headless --convert-to csv report.xlsx --outdir out/ # 1st sheet only
Only the first sheet lands in a CSV; for other sheets use xlsx_to_csv.py --sheet NAME. If soffice is missing, install LibreOffice or hand the file to the user unconverted.
via load_workbook(path, data_only=True) and only when the file was previously saved by Excel/LibreOffice. Otherwise you get None.
xlsx_edit.py insert/delete does not shift references (rawopenpyxl behavior). Use xlsx_restructure.py, which does — but even it cannot move chart anchors, images, or conditional-format RULE formulas; read its JSON report's not_shifted list and references/restructuring.md.
--protect sets the standardxlsx sheet-protection hash: it signals "don't edit this" to well-behaved apps and nothing more. Anyone can strip it by editing the zip's XML or unchecking it in LibreOffice. Never rely on it for confidentiality or integrity; it does not encrypt anything.
data_only=True then save silently discards all formulas(cached values replace them). Never save a workbook loaded that way unless that is the goal.
charts, so editing a charted workbook and saving drops the charts. Re-add charts after editing, or avoid re-saving charted files.
already do) and remember European CSVs often use ; delimiters and decimal commas — use --delimiter ';' and expect strings like "12,5" to stay strings.
openpyxl returns datetime/date objects. Dumps here emit ISO strings.
[ ] : * ? / \.xlsx_read.py out.xlsx --sheets and confirm sheetnames, dimensions, merged ranges, and chart counts match intent.
--json and compare against the source values.confirm --formulas lists them and that --recalc was applied.
xlsx_restructure.py: read its JSON report, then re-run--formulas and --sheets to confirm references and ranges landed where expected.
soffice --headless --convert-to pdf out.xlsx and inspect the PDF.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 9,552 | 5,738 | -40% | 1 | 1 | 0% | 1,524 | 3,016 | +98% | 0 | 0 | — |
case-02 | fail→fail | 23,793 | 4,388 | -82% | 1 | 1 | 0% | 6,230 | 3,006 | -52% | 0 | 0 | — |
case-03 | fail→fail | 9,036 | 5,764 | -36% | 1 | 1 | 0% | 523 | 3,172 | +507% | 0 | 0 | — |
case-04 | fail→pass | 13,091 | 3,362 | -74% | 1 | 1 | 0% | 1,768 | 3,254 | +84% | 0 | 0 | — |
case-05 | pass→pass | 9,070 | 6,121 | -33% | 1 | 1 | 0% | 1,630 | 3,647 | +124% | 0 | 0 | — |
case-06 | fail→pass | 10,528 | 4,933 | -53% | 1 | 1 | 0% | 1,965 | 3,514 | +79% | 0 | 0 | — |
case-07 | fail→fail | 9,697 | 6,224 | -36% | 1 | 1 | 0% | 1,757 | 3,074 | +75% | 0 | 0 | — |
case-08 | fail→fail | 6,649 | 5,902 | -11% | 1 | 1 | 0% | 1,372 | 3,072 | +124% | 0 | 0 | — |
case-09 | fail→pass | 11,344 | 3,223 | -72% | 1 | 1 | 0% | 1,946 | 3,151 | +62% | 0 | 0 | — |
case-10 | fail→fail | 6,297 | 5,185 | -18% | 1 | 1 | 0% | 1,137 | 3,064 | +169% | 0 | 0 | — |
case-11 | fail→fail | 45,780 | 6,231 | -86% | 1 | 1 | 0% | 1,301 | 2,998 | +130% | 0 | 0 | — |
case-12 | fail→pass | 17,938 | 8,020 | -55% | 1 | 1 | 0% | 2,909 | 4,079 | +40% | 0 | 0 | — |
case-13 | fail→pass | 13,001 | 3,040 | -77% | 1 | 1 | 0% | 2,369 | 3,234 | +37% | 0 | 0 | — |
case-14 | fail→pass | 7,635 | 2,856 | -63% | 1 | 1 | 0% | 1,498 | 3,200 | +114% | 0 | 0 | — |
case-15 | fail→fail | 5,606 | 4,389 | -22% | 1 | 1 | 0% | 1,099 | 2,975 | +171% | 0 | 0 | — |
case-16 | fail→pass | 11,567 | 2,759 | -76% | 1 | 1 | 0% | 2,045 | 3,221 | +58% | 0 | 0 | — |
case-17 | fail→fail | 12,204 | 5,093 | -58% | 1 | 1 | 0% | 2,142 | 3,073 | +43% | 0 | 0 | — |
case-18 | pass→pass | 13,804 | 16,089 | +17% | 1 | 1 | 0% | 2,333 | 4,603 | +97% | 0 | 0 | — |
case-19 | pass→pass | 11,300 | 8,014 | -29% | 1 | 1 | 0% | 1,956 | 4,024 | +106% | 0 | 0 | — |
case-20 | fail→pass | 14,503 | 3,586 | -75% | 1 | 1 | 0% | 2,450 | 3,156 | +29% | 0 | 0 | — |
case-21 | pass→pass | 6,723 | 5,892 | -12% | 1 | 1 | 0% | 1,239 | 3,757 | +203% | 0 | 0 | — |
case-22 | pass→pass | 6,617 | 2,055 | -69% | 1 | 1 | 0% | 1,168 | 3,054 | +161% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 13 counted toward the lift figure. The other 9 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +36 percentage points is the difference between those two pass rates over the 13 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 8/7/2026 | +5% |
Other measured skills in the registry, with their headline benchmark lift.