| name | spreadjs |
| description | Creating & editing Excel workbooks via CLI — powered by SpreadJS |
hsx CLI
Setup
Before using hsx, check it's available. If not, install the package and its system dependencies (Cairo/Pango for canvas):
which hsx 2>/dev/null || npm install -g @hewliyang/headless-spreadjs
Commands
All operations are bash calls. Formulas recalculate instantly (no sync step needed).
Global options:
--timeout <seconds> (default: 30)
--no-daemon (run command directly bypassing daemon)
hsx create file.xlsx
hsx info file.xlsx
hsx get file.xlsx "Sheet1!A1:C10"
hsx csv file.xlsx "Sheet1!A1:C10" [--mode value|formula|both] [--formulas]
hsx set file.xlsx "Sheet1!A1:C3" '<json>'
hsx set file.xlsx "A1" '<json>' --copy-to "A1:D1"
hsx clear file.xlsx "A1:C10" [--type all|styles]
hsx search file.xlsx "term" [--sheet S] [--regex]
hsx copy file.xlsx "A1:C1" "A10:C10"
hsx diff left.xlsx right.xlsx [--inline-limit N] [--preview-limit N]
hsx deps file.xlsx "Sheet1!A1" [--depth N|--recursive]
hsx refs file.xlsx "Sheet1!A1" [--depth N|--recursive] [--max-formulas N]
hsx sheet file.xlsx list|create|delete|rename
hsx rc file.xlsx insert|delete|hide|freeze rows|columns [--ref R] [--count N]
hsx resize file.xlsx [--columns A:D] [--width N] [--rows 1:10] [--height N]
hsx objects file.xlsx [--sheet Name]
hsx screenshot file.xlsx [ref] [-o out.png]
hsx eval file.xlsx "code"
hsx daemon start|stop|status|flush
References use A1 notation: Sheet1!A1:C10, A1:C10 (active sheet), or A1 (single cell).
Dependency tracing defaults to one-hop (--depth 1). Use --depth N for bounded multi-hop traversal or --recursive (shorthand for --depth 50).
Write
hsx set file.xlsx "A1:B3" '[
[{"value":"Name","style":{"fontStyle":{"bold":true}}}, {"value":"Qty","style":{"fontStyle":{"bold":true}}}],
[{"value":"Alice"}, {"value":4}],
[{"value":"Bob"}, {"formula":"=B2+1"}]
]'
Each cell: {"value": ...}, {"formula": "=..."}, optional "style": {...}.
--copy-to <range>: after writing, expand the written cells across the target range using SpreadJS copyTo (like Excel drag-fill). Relative formula references adjust automatically. The source cells tile/repeat to fill the destination. Works cross-sheet.
hsx set file.xlsx "B7" '[[{"formula":"=SUM(B2:B6)","style":{"fontStyle":{"bold":true},"formatter":"#,##0"}}]]' --copy-to "B7:N7"
hsx set file.xlsx "B5:B6" '[
[{"formula":"=SUM(B2:B4)","style":{"fontStyle":{"bold":true}}}],
[{"formula":"=AVERAGE(B2:B4)","style":{"fontStyle":{"italic":true}}}]
]' --copy-to "B5:D6"
style (SpreadJS-style):
fontStyle: { bold?: boolean, italic?: boolean, underline?: boolean }
fontSize: Excel point size string (e.g. "11pt", "12pt")
fontFamily
foreColor
backColor
hAlign: SpreadJS HorizontalAlign enum value
vAlign: SpreadJS VerticalAlign enum value (0=top, 1=center, 2=bottom)
formatter: Excel number format string (e.g. "#,##0", "0.0%", "$#,##0.00", "yyyy-mm-dd")
wordWrap: boolean
textIndent: number (indent level)
borderLeft, borderTop, borderRight, borderBottom: { color?: string, style?: string }
style values: "thin", "medium", "dashed", "dotted", "thick", "double", "hair", "mediumDashed", "dashDot", "mediumDashDot", "dashDotDot", "mediumDashDotDot", "slantedDashDot"
Always set formatter inline in the style — don't use eval just to format cells.
Font size unit: always use Excel points ("11pt", "12pt", …). CSS pixel values like "11px" are rejected — they export as much smaller sizes ("11px" = ~8.25pt) and silently make sheets unreadable. For greenfield workbooks, use "11pt" body / "12pt" section headers / "14–16pt" titles; in existing workbooks, follow neighbouring conventions.
Read
hsx get file.xlsx "A1:B3"
hsx csv file.xlsx "A1:B3"
hsx csv file.xlsx "A1:B3" --mode formula
hsx csv file.xlsx "A1:B3" --mode both
Screenshot
hsx screenshot file.xlsx
hsx screenshot file.xlsx "Sheet1!A1:F20"
hsx screenshot file.xlsx "Summary!A1:H30" -o summary.png
Screenshots render the full workbook (default) or a specific range to PNG. Pivot table screenshots have limited visual fidelity.
Search
hsx search file.xlsx "Alice"
hsx search file.xlsx "^Q[1-4]$" --regex
hsx search file.xlsx "Revenue" --sheet Summary
Copy / Diff
hsx copy file.xlsx "A1:C1" "A1:C10"
hsx copy file.xlsx "Sheet1!A1:B5" "Sheet2!A1:B5"
hsx diff old.xlsx new.xlsx
hsx diff old.xlsx new.xlsx --inline-limit 1000 --preview-limit 50
hsx diff output is JSON with a top-level summary string plus counts (changedCells, comparedCells).
outputMode: "inline" → full diff rows in diffs
outputMode: "tmpfile" → preview rows in diffs, full NDJSON at diffFile (grep-friendly)
jq -r '.diffFile' diff-output.json
rg '"sheet":"Sheet1"' "$(jq -r '.diffFile' diff-output.json)"
Dependency tracing (deps / refs)
hsx deps model.xlsx "Sheet3!A1"
hsx deps model.xlsx "Sheet3!A1" --depth 2
hsx refs model.xlsx "Sheet1!A1"
hsx refs model.xlsx "Sheet1!A1" --recursive --max-formulas 300000
Output is JSON and includes hop counts plus stats. This is best-effort for dynamic formulas (e.g. INDIRECT, OFFSET).
Sheets
hsx sheet file.xlsx list
hsx sheet file.xlsx create "Revenue"
hsx sheet file.xlsx rename "Sheet1" "Data"
hsx sheet file.xlsx delete "OldSheet"
Rows & Columns
hsx rc file.xlsx insert rows --ref 5 --count 3
hsx rc file.xlsx delete columns --ref B --count 2
hsx rc file.xlsx hide rows --ref 10
hsx rc file.xlsx freeze rows --ref 1
hsx rc file.xlsx unfreeze rows
Resize
hsx resize file.xlsx --columns A:D --width 120
hsx resize file.xlsx --rows 1:1 --height 30
hsx resize file.xlsx --columns A --width 200 --sheet Revenue
Eval (escape hatch)
For charts, sparklines, pivot tables, conditional formatting — anything the structured commands don't cover.
hsx eval file.xlsx "
// Globals: workbook, sheet (active), GC, file, range(ref)
sheet.charts.add('Revenue', GC.Spread.Sheets.Charts.ChartType.line,
0, 120, 500, 300, 'A1:E6');
"
hsx eval file.xlsx "
range('B2').value(96); // active sheet
range("'Data Sheet'!C5").formula('B2*2'); // quoted sheet names supported
"
hsx eval file.xlsx "
const data = workbook.getSheetFromName('Data');
data.tables.add('SalesTable', 0, 0, 10, 4,
GC.Spread.Sheets.Tables.TableThemes.medium2);
"
hsx eval file.xlsx "
console.log('Sheets:', workbook.getSheetCount());
return sheet.getValue(0, 0);
"
Stdin also works: echo "return sheet.getValue(0,0)" | hsx eval file.xlsx
SpreadJS API Reference
When using hsx eval, you have access to the full SpreadJS API via the GC namespace. If you need to look up a specific API (enum values, method signatures, class properties), grep the type definitions:
grep -n "enum ChartType" ./spreadjs.d.ts
grep -n "class Worksheet" ./spreadjs.d.ts
grep -n "addRows\|deleteRows\|addColumns" ./spreadjs.d.ts
grep -n "fontWeight:\|foreColor:\|backColor:\|formatter:" ./spreadjs.d.ts
grep -n "conditionalFormats\|ConditionalFormatting" ./spreadjs.d.ts
The file is at ./spreadjs.d.ts. Use grep -n to find what you need, then read with offset to see full signatures and JSDoc examples.
Best Practices
- Use formulas, not hardcoded computed values:
{"formula":"=SUM(A1:A9)"} not {"value":45}
- Keep each
set call focused; build incrementally across multiple calls
- Use
hsx get or hsx csv to verify after writes
- Use
--copy-to to expand formulas + styles across rows/columns instead of spelling out every cell
- Set
formatter in the cell style inline — avoid eval just for number formatting
- Use
hsx deps / hsx refs for repeatable lineage checks; use eval for custom one-offs
- Prefer uniform column widths; use empty columns for indentation
- Always specify units in headers:
Revenue ($mm), Growth (%)
- Don't shrink fonts to fit layout. Native Excel defaults are ~11pt (Aptos), 20px row height, 62px column width. If content is too wide, adjust column widths or wrap text — don't drop fonts below 11pt. Use ~11pt body, 12pt section headers, and 14–16pt titles.
- Keep colors plain & professional unless requested otherwise.
- Excel is a presentational format. Use vision to ensure the workbook is visually sound.