Command-line tools for Pluma: plan a screen, scaffold page and block recipes into your app as your own code, run codemods across Pluma breaking changes, and file gap reports where Pluma falls short.
Command-line tools for Pluma, Customer.io's design
system. It ships codemods — automated migrations that move a consuming
app across Pluma breaking changes — grouped by the @customerio/pluma-components
version each change lands in, and recipes: page and block examples you copy
into your app as your own code, with plan to pick one for the screen you
describe.
It also files gap reports each time Pluma falls short of what a developer
or agent needed, to wherever the consuming project chooses.
No install needed — run it with pnx:
Or add it as a dev dependency and call the pluma-cli binary:
pluma-cli --help lists the commands, and pluma-cli <command> --help shows
one command's usage:
| Command | What it does |
|---|---|
plan | Turn a description of a screen into a starter recipe, the blocks and components that fit, and the rules to follow. |
scaffold | List recipes, show one, or copy one into your app as your own code. |
upgrade | List and run codemods that migrate your app across Pluma breaking changes. |
report-gap | Report that Pluma fell short of what you needed, to the handlers in pluma.config.mjs. |
pluma.config.mjsThe project-wide config file for Pluma tools, read from the working directory
of pluma-cli and of the MCP server. It is an ES module whose default export
is an object:
| Key | Description |
|---|---|
version | The shape the file is written against. Optional; only 1 exists. |
gapReport | One handler or a list of them. Gap reports go to each, in order. Without it, reports go to the fallback file. |
Keys a release doesn't know are ignored. A file that fails to load, or whose
version or handlers are invalid, stops report-gap with status 2 rather than
sending reports to the fallback.
@customerio/pluma-cli/gap-report exports the handlers gapReport takes. The
report-gap page
lists the four built-in handlers, the handler interface for writing your own,
and the report every handler receives.
.pluma/ directoryA run keeps its files in .pluma/ at the root of the repository it runs in —
the nearest directory up from the working directory that holds a .git, or the
working directory itself outside a repository:
| File | What it is |
|---|---|
.pluma/codemod-ledger.json | The run ledger, for the codemods that keep one. |
.pluma/codemod-report.json | The findings report of the last run that had any. |
.pluma/gap-reports.jsonl | Gap reports when no handler is configured, one per line. |
Commit the directory with the migrated files. A developer with work in
flight when the migration lands can then merge it in and run the codemods
again over their own changes, and the ledger keeps that run off everything the
first one migrated. It is the same directory however deep in the repository a
run starts, so every run in a monorepo shares one ledger. --ledger <path> and
--report <path> put either file somewhere else.
Before 5.0.0 the defaults were pluma-codemod-ledger.json and
pluma-codemod-report.json in the working directory. A run that finds a ledger
there or at the repository root moves it into .pluma/ and says so — commit
the move. If .pluma/ already holds a ledger (a monorepo whose apps each ran
the codemods from their own directory, say), the old rows are merged into it.
--dry reads an old ledger where it is and moves nothing. An old report can be
deleted; every run writes a fresh one.
Every command that takes --json prints exactly one JSON value on stdout and
nothing else there, so its output can be piped to jq. Messages and errors go
to stderr. The exit status is 0 when the command did what it was asked, 1 when
a codemod run or a gap report delivery failed, and 2 for invalid input.
Each command page describes its JSON shape:
plan,
scaffold,
upgrade and
report-gap.
The build bundles the codemod transforms and the recipes from
packages/components/src/recipes into dist/. Run from source, plan and
scaffold read the components package directly.