npx claudepluginhub tjc-lp/xlLLM-friendly Excel CLI for reading, writing, styling, and analyzing spreadsheets. Run `xl <command> --help` for comprehensive usage.
The best Excel library for Scala. Type-safe, purely functional, blazing fast.
//> using scala 3.7.4
//> using dep com.tjclp::xl:0.9.6
import com.tjclp.xl.{*, given}
@main def demo(): Unit =
// Create a financial report
val report = Sheet("Q1 Report")
.put(
"A1" -> "Revenue", "B1" -> money"$$1,250,000",
"A2" -> "Expenses", "B2" -> money"$$875,000",
"A3" -> "Net Income", "B3" -> fx"=B1-B2",
"A4" -> "Margin", "B4" -> fx"=B3/$$B$$1"
)
.style(
"A1:A4" -> CellStyle.default.bold,
"B4" -> CellStyle.default.percent
)
Excel.write(Workbook(report), "/tmp/q1-report.xlsx")
println(s"Created /tmp/q1-report.xlsx with ${report.cellCount} cells")
ref"A1") catch errors before runtime// build.mill — single dependency for everything
def ivyDeps = Agg(ivy"com.tjclp::xl:0.9.6")
// Or individual modules for minimal footprint:
// ivy"com.tjclp::xl-core:0.9.6" — Pure domain model only
// ivy"com.tjclp::xl-ooxml:0.9.6" — Add OOXML read/write
// ivy"com.tjclp::xl-cats-effect:0.9.6" — Add IO streaming
// ivy"com.tjclp::xl-evaluator:0.9.6" — Add formula evaluation
import com.tjclp.xl.{*, given}
// Read
val workbook = Excel.read("data.xlsx")
// Modify — string literals are validated at compile time
val updated = workbook.update("Sheet1", sheet =>
sheet
.put("A1" -> "Updated!", "B1" -> 42)
.style("A1:B1" -> CellStyle.default.bold)
)
// Write
Excel.write(updated, "output.xlsx")
val cell: ARef = ref"A1" // Single cell
val range: CellRange = ref"A1:B10" // Range
val qualified = ref"Sheet1!A1:C100" // With sheet name
// Runtime interpolation (returns Either)
val col = "A"; val row = "1"
val dynamic = ref"$col$row" // Either[XLError, RefType]
val price = money"$$1,234.56" // Currency format
val growth = percent"12.5%" // Percent format
val date = date"2025-11-24" // ISO date format
Note: Use
$$to escape$in string interpolators (Scala syntax requirement).
val text = "Error: ".bold.red + "Fix this!".underline
sheet.put("A1" -> text) // Multiple formats in one cell
Type-safe evaluation and dependency analysis.
// Add formulas to cells
val sheet = Sheet("Model")
.put(
"A1" -> 100,
"A2" -> 200,
"A3" -> fx"=SUM(A1:A2)", // Formula literal
"A4" -> fx"=A3 * 1.1"
)
// Evaluate all formulas (with cycle detection)
sheet.evaluateWithDependencyCheck() match
case Right(results) => // Map[ARef, CellValue] of computed values
case Left(error) => // Circular reference detected
Functions: SUM, AVERAGE, MIN, MAX, COUNT, IF, AND, OR, VLOOKUP, XLOOKUP, SUMIF, COUNTIF, NPV, IRR, TODAY, DATE, and more.
import com.tjclp.xl.io.{ExcelIO, RowData}
import cats.effect.IO
import fs2.Stream
val excel = ExcelIO.instance[IO]
// Read 1M+ rows with O(1) memory
excel.readStream(path)
.filter(_.rowIndex > 1) // Skip header
.evalMap(row => process(row))
// Write 1M+ rows with O(1) memory
Stream.range(1, 1_000_001)
.map(i => RowData(i, Map(0 -> CellValue.Number(i))))
.through(excel.writeStream(path, "Data"))
Install the xl command-line tool for LLM-friendly Excel operations:
make install # Installs to ~/.local/bin/xl
Stateless by design — each command is self-contained: