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
| Source | Bundled (installed by default) |
| Path | skills/document/xlsx |
| Version | 1.0.0 |
| Author | Claway Agent |
| License | MIT |
| Platforms | linux, macos |
| Tags | xlsx, excel, spreadsheet, accounting, 試算表, 報表, csv |
Reference: full SKILL.md
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-documentsfirst, 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.