Skip to main content

Xlsx

Excel/試算表 files Jarvis can open, read, edit and build: .xlsx/.xlsm read + write via openpyxl, .csv via pandas. Use whenever a spreadsheet is the input or the output — '睇下呢個 Excel', '對下條數', '幫我加返一行', '整張報表', '轉做 Excel', invoices, expense claims, ledgers, monthly reports. NOT for Google Sheets (use google-workspace).

Skill metadata​

SourceBundled (installed by default)
Pathskills/document/xlsx
Version1.0.0
AuthorClaway Agent
LicenseMIT
Platformslinux, macos
Tagsxlsx, excel, spreadsheet, accounting, 試算表, 報表, csv

Reference: full SKILL.md​

info

The following is the complete skill definition that Claway loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.

Spreadsheets

A user sends .xlsx far more often than they send anything else. Open it and answer from what is actually in it — never guess at a number that is sitting in a cell you could have read.

Routing​

  • Google Sheets (a link to sheets.google.com, "我個 Google Sheet") → google-workspace. That is an API against a cloud document; this skill is for a file on disk.
  • A PDF that contains a table → extract with ocr-and-documents first, then come back here to build the spreadsheet.

Look before you write​

python3 - <<'PY'
import openpyxl
wb = openpyxl.load_workbook("book.xlsx", data_only=True) # data_only: cached VALUES
for ws in wb.worksheets:
print(f"[{ws.title}] {ws.max_row} rows x {ws.max_column} cols")
for row in ws.iter_rows(min_row=1, max_row=min(8, ws.max_row), values_only=True):
print(" ", row)
PY

data_only=True gives you the last value Excel calculated. data_only=False (the default) gives you the formula text instead. You almost always want values when reading and formulas when writing — and you cannot get both from one load.

⚠️ Never save a workbook you loaded with data_only=True. openpyxl does not recalculate, so every formula in the file is replaced by the stale number it happened to be showing. That silently destroys a working ledger. To edit a file that has formulas, load it again with data_only=False, change only what you must, and save that one.

Editing an existing file​

Preserve what you were given. A user who sends a company template expects their column widths, number formats and header styling to survive.

python3 - <<'PY'
import openpyxl
wb = openpyxl.load_workbook("ledger.xlsx") # formulas preserved
ws = wb["Sheet1"]
r = ws.max_row + 1
ws.cell(r, 1, "2026-08-27")
ws.cell(r, 2, "辦公室文具")
ws.cell(r, 3, 348.50).number_format = '#,##0.00'
ws.cell(r, 4, f"=SUM(C2:C{r})") # write formulas, not their answers
wb.save("ledger.xlsx")
PY

Write a formula wherever the value is derived. A hard-coded total is wrong the moment anyone edits a row above it, and the user will not know it stopped updating.

Building a report from scratch​

python3 - <<'PY'
import openpyxl
from openpyxl.styles import Font, Alignment, PatternFill
from openpyxl.utils import get_column_letter

wb = openpyxl.Workbook(); ws = wb.active; ws.title = "8月支出"
headers = ["日期", "項目", "類別", "金額 (HKD)"]
ws.append(headers)
for c in range(1, len(headers) + 1):
cell = ws.cell(1, c)
cell.font = Font(bold=True, color="FFFFFF")
cell.fill = PatternFill("solid", fgColor="2F5597")
cell.alignment = Alignment(horizontal="center")
ws.freeze_panes = "A2" # headers stay visible when scrolling

for row in [("2026-08-01", "打印紙", "文具", 120.0)]:
ws.append(row)
for r in range(2, ws.max_row + 1):
ws.cell(r, 4).number_format = '#,##0.00'

for c in range(1, ws.max_column + 1): # width to content, not default
longest = max(len(str(ws.cell(r, c).value or "")) for r in range(1, ws.max_row + 1))
ws.column_dimensions[get_column_letter(c)].width = min(40, max(10, longest + 4))
wb.save("報表.xlsx")
PY

Chinese headers need the width pass more than English ones do — a CJK character is about twice as wide as openpyxl's default assumes, so untreated columns show ####.

Money​

Use number_format, not rounding, to control what is displayed: rounding throws away precision the next calculation needs. '#,##0.00' for plain amounts, '"HK$"#,##0.00' when the currency has to be on the face of the cell, '#,##0.00;[Red]-#,##0.00' when negatives should read as such.

Read money as Decimal when you are checking totals — 0.1 + 0.2 != 0.3 in float, and an accountant WILL notice a reconciliation that is off by a cent.

from decimal import Decimal
total = sum(Decimal(str(c.value)) for c in ws["D"][1:] if c.value is not None)

Large files, and CSV​

Past a few tens of thousands of rows, read_only=True streams instead of building the whole tree in memory. For pure data with no formatting to keep, pandas is faster to reason about:

import pandas as pd
df = pd.read_excel("big.xlsx", sheet_name=0) # needs openpyxl installed
df.to_excel("out.xlsx", index=False)
pd.read_csv("data.csv") # CSV: pandas, not openpyxl

CSV from a Chinese Windows machine is often gbk, not UTF-8. If you see mojibake, retry with encoding="gbk" before concluding the file is corrupt.

Before you answer​

  • Say which sheet and which rows you used. "Row 14 of 支出明細" is checkable; "according to the spreadsheet" is not.
  • If a number does not reconcile, say so and show both figures. Quietly presenting one of them is how a wrong number reaches a filing.
  • Send the file back when you changed it — do not describe the change and leave the user to make it.