Unified read / write / stats / recalc / chart capability for Excel / CSV (.xlsx / .xlsm / .csv / .tsv). Trigger scenarios: reading cells, summarizing, grouping, highest/lowest values, total revenue, editing cell values or formulas, inserting rows/columns, adding total rows, inserting charts (bar/line/pie), freezing panes, auto-filter, conditional formatting, CSV ↔ XLSX conversion, building rosters / sales sheets / financial models, diagnosing #REF! / #DIV/0! formula errors, triggering recalculation. Typical phrasings: "who has the highest sales in this sheet", "total Q1 revenue", "change B2 to 500", "add a total row", "create a new roster", "convert csv to xlsx", "freeze the first row", "highlight negatives in column A red". Load this skill whenever the user mentions an .xlsx / .csv file or the final deliverable is a spreadsheet.
72
88%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Read with ExcelInspect; all write / modify / create operations use JsSandbox (preferred library: exceljs, which has broader support for styles / charts / images compared to xlsx/SheetJS); format conversion (including csv ↔ xlsx) can also use DocConvert.
| Tool | Responsibility | Read-only |
|---|---|---|
ExcelInspect | Read: summary / values / formulas / style / range / chart-list / defined-names | Yes |
JsSandbox | All writes: create / update / insert / formula / chart / image / structure / recalc… implemented with exceljs | No |
DocConvert | csv ↔ xlsx ↔ html / md | No |
Loading (two steps):
LoadSkill xlsx-skillToolSearch(query: "select:ExcelInspect,JsSandbox,DocConvert")
Readon a .xlsx returns only a lightweight summary; all analysis / stats / edit / creation must go throughExcelInspect+JsSandbox.
ExcelInspect(summary)ExcelInspect { action: "summary", filePath: "…" }Returns sheets[{name, rows, cols, hasFormula, hasChart}]. Then as needed:
ExcelInspect(action="values", sheetName, range)action="formulas"action="style", rangeaction="errors" — lists #REF! / #DIV/0! / #NAME? etc.For large sheets, pass range (e.g. "A1:D100") to avoid full-sheet preview truncation.
JsSandbox + exceljsexceljs supports styles / merges / formulas / charts / images / data validation. Handles tens of thousands of rows without issue.
import ExcelJS from 'exceljs'
const wb = new ExcelJS.Workbook()
const ws = wb.addWorksheet('Q1 Sales')
// Headers
ws.columns = [
{ header: 'Product', key: 'name', width: 20 },
{ header: 'Region', key: 'area', width: 10 },
{ header: 'Units', key: 'qty', width: 10 },
{ header: 'Price ($)', key: 'price', width: 14, style: { numFmt: '$#,##0.00' } },
{ header: 'Revenue ($)', key: 'total', width: 14, style: { numFmt: '$#,##0.00' } },
]
const rows = [
{ name: 'Smart Speaker', area: 'East', qty: 1240, price: 300 },
{ name: 'Robot Vacuum', area: 'South', qty: 410, price: 3000 },
{ name: 'Wireless Earbuds', area: 'North', qty: 860, price: 150 },
]
rows.forEach((r, i) => {
const excelRow = ws.addRow({ ...r, total: null })
const rowNum = excelRow.number
ws.getCell(`E${rowNum}`).value = { formula: `C${rowNum}*D${rowNum}` }
})
// Total row
const totalRow = ws.addRow({
name: 'Total', area: '',
qty: { formula: `SUM(C2:C${rows.length + 1})` },
price: null,
total: { formula: `SUM(E2:E${rows.length + 1})` },
})
totalRow.font = { bold: true }
// Freeze header + bold header + auto-filter
ws.views = [{ state: 'frozen', ySplit: 1 }]
ws.getRow(1).font = { bold: true }
ws.autoFilter = { from: 'A1', to: `E${rows.length + 1}` }
await wb.xlsx.writeFile('sales_q1.xlsx')
console.log('sales_q1.xlsx written')import ExcelJS from 'exceljs'
const wb = new ExcelJS.Workbook()
await wb.xlsx.readFile('sales_q1.xlsx')
const ws = wb.getWorksheet('Q1 Sales')
const last = ws.rowCount
ws.getCell(`F1`).value = 'YoY Growth'
for (let r = 2; r < last; r++) {
ws.getCell(`F${r}`).value = { formula: `(E${r}-E${r}*0.8)/(E${r}*0.8)` }
ws.getCell(`F${r}`).numFmt = '0.0%'
}
// Conditional format: F column < 0 → red font
ws.addConditionalFormatting({
ref: `F2:F${last - 1}`,
rules: [{
type: 'cellIs',
operator: 'lessThan',
formulae: ['0'],
style: { font: { color: { argb: 'FFFF0000' } } },
priority: 1,
}],
})
await wb.xlsx.writeFile('sales_q1.xlsx')
console.log('YoY + conditional format added')import ExcelJS from 'exceljs'
import fs from 'node:fs/promises'
const wb = new ExcelJS.Workbook()
await wb.xlsx.readFile('sales_q1.xlsx')
const ws = wb.getWorksheet('Q1 Sales')
// exceljs doesn't support addChart natively — use office-chart extension, or
// render a PNG via chartjs-node-canvas and embed it with addImage.
// Example using addImage:
// Assume bar.png was generated by chartjs-node-canvas
const imgId = wb.addImage({ buffer: await fs.readFile('bar.png'), extension: 'png' })
ws.addImage(imgId, { tl: { col: 6, row: 1 }, ext: { width: 480, height: 280 } })
await wb.xlsx.writeFile('sales_q1.xlsx')
console.log('chart image embedded')For native OOXML charts, use
officegenor hand-stitch chart XML — high complexity. For most use cases, "render chart as PNG → addImage" is sufficient.
import fs from 'node:fs/promises'
import ExcelJS from 'exceljs'
const csv = await fs.readFile(process.argv[2] ?? 'in.csv', 'utf-8')
const [headerLine, ...dataLines] = csv.trim().split(/\r?\n/)
const headers = headerLine.split(',')
const wb = new ExcelJS.Workbook()
const ws = wb.addWorksheet('Sheet1')
ws.addRow(headers).font = { bold: true }
for (const line of dataLines) {
ws.addRow(line.split(',').map(v => (isNaN(+v) || v === '' ? v : Number(v))))
}
ws.columns.forEach(c => { c.width = 18 })
ws.views = [{ state: 'frozen', ySplit: 1 }]
await wb.xlsx.writeFile('out.xlsx')
console.log('csv → xlsx done')Simple csv/xlsx conversion can also use DocConvert(from="csv", to="xlsx") without writing code.
import ExcelJS from 'exceljs'
const wb = new ExcelJS.Workbook()
await wb.xlsx.readFile('in.xlsx')
const errs = []
for (const ws of wb.worksheets) {
ws.eachRow((row, rn) => {
row.eachCell((cell, cn) => {
// exceljs reads error cells as { error: '#REF!' }
if (cell.value && typeof cell.value === 'object' && cell.value.error) {
errs.push({ sheet: ws.name, cell: cell.address, error: cell.value.error, formula: cell.formula })
}
})
})
}
console.log(JSON.stringify(errs, null, 2))DocConvert(from="csv", to="xlsx", sourcePath="…")
DocConvert(from="xlsx", to="csv", sourcePath="…")
DocConvert(from="xlsx", to="html", sourcePath="…")| Symptom | Cause | Fix |
|---|---|---|
| Formula saved as a string | Using cell.value = '=A1+B1' | Use { formula: 'A1+B1' } instead |
| CJK column width too narrow | Default width calculated in ASCII characters | Manually set column.width = 20 |
| Numbers display in scientific notation after open | More than 11 digits | cell.numFmt = '0' or '@' (text) |
| File reports "damaged" on open | Formula syntax error / circular reference | Run ExcelInspect(errors) first; or write static values before substituting formulas |
To patch a script: JsSandbox(action="edit-and-run", scriptPath=…, edits=[…]) — only pass the diff.
a1ab5be
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.