Milda CLI - talk to your design system
@mildastudio/cli (the milda command) is the authenticated, platform-neutral terminal client to your design system. You sign in, then ask it about your system - what components exist, a component's exact contract, the tokens, what changed since the last release - and generate real source. It is the human and CI counterpart of the MCP server: two clients, one hosted source of truth.
#What it's for
Your design system lives on the platform - the document is the single source of truth, authored in the Studio or as text. The CLI is a client to it, not a second editor. Most of what it does is a platform-neutral conversation about the contract: listing components, showing one's props/events/slots, listing tokens, diffing versions. None of that involves a target framework. Generating code is one target-parameterized command on top.
Whatever it generates is yours - real, self-contained source, identical to what a release publishes.
#Install
The CLI ships as a standalone binary - no Node required. On macOS and Linux, one line downloads the right build for your OS and CPU, verifies its checksum, and installs it:
curl -fsSL https://cdn.milda.dev/cli/install.sh | sh
It installs into a directory already on your PATH when one is available (so milda works right away - no restart), otherwise into ~/.milda/bin and adds that to your shell profile (zsh/bash/fish) for new terminals. Force a location with MILDA_INSTALL_DIR; skip the profile edit with MILDA_NO_MODIFY_PATH=1. Pin a version with environment variables:
curl -fsSL https://cdn.milda.dev/cli/install.sh | MILDA_VERSION=0.1.0 sh # MILDA_INSTALL_DIR=/usr/local/bin - install somewhere else
#Manual download
Prefer to fetch it yourself (or on Windows)? Binaries and their checksums live under https://cdn.milda.dev/cli/latest/ (or /cli/v<version>/ to pin):
cli/latest/milda-darwin-arm64 # macOS (Apple silicon) cli/latest/milda-linux-x64 # Linux (x86_64) cli/latest/milda-linux-arm64 # Linux (arm64) cli/latest/milda-windows-x64.exe # Windows cli/latest/SHASUMS256.txt # checksums for all of the above
Then make it executable, verify the checksum, and put it on your PATH:
curl -fsSLO https://cdn.milda.dev/cli/latest/milda-darwin-arm64 curl -fsSL https://cdn.milda.dev/cli/latest/SHASUMS256.txt | shasum -a 256 -c --ignore-missing chmod +x milda-darwin-arm64 && mv milda-darwin-arm64 /usr/local/bin/milda
#Staying up to date
The CLI checks for a newer version once a day (only on an interactive terminal - never in CI or --json) and prints a quiet one-line nudge. Update any time with:
milda upgrade # or: milda upgrade --check
upgrade just re-runs the installer (download → checksum → replace). Silence the daily check with MILDA_NO_UPDATE_CHECK=1.
#Authenticate
Reading a design system is authenticated - it's a private platform asset, not a public file. Sign in through the browser once:
milda login
This opens your browser to authorize the CLI (the same Google sign-in as the Studio), then stores a token at ~/.config/milda/credentials.json. In CI, skip the browser and set a token in the environment instead:
export MILDA_TOKEN=<token> # non-interactive; overrides the stored login
milda whoami shows who you are; milda logout clears and revokes the token. Public, published design systems can be read with no token at all.
#Commands
Every read command has a --json twin for scripting. Pick the design system with --project @org/ds or a milda.config.json.
milda ls- list the components (name, archetype, description).milda show <component>- one component's exact contract: props (type, required, default), events, composition slots. What you consult before writing code against it.milda tokens [--type color]- the design tokens.milda diff [--against <file>] [--fail-on major]- contract changes versus the last release and the semver bump they imply. The CI gate against breaking changes (non-zero exit at the threshold).milda check [paths…] --package @org/ds- does the code that uses the system use it the way the system says? Its usage rules and prop constraints, and the drift no rule has to spell out (below).milda pull [@org/ds]- fetch the full document tomilda.json, so the commands above andgeneraterun offline afterward with--in milda.json.milda generate --target react- real, self-contained component source into an output directory. React is the only target today; the command is shaped for more.milda init- write amilda.config.json;milda init --consumer @org/dswrites the.npmrcscope routing so consumers cannpm install @org/ds.milda login/logout/whoami- authentication (above).
#A typical loop
milda login # once, in the browser milda ls --project @acme/ds # what's in the system? milda show Button --project @acme/ds # the exact contract milda pull @acme/ds # snapshot to milda.json milda generate --in milda.json --out src/ds --target react
#Checking the code that uses it
milda check reads a consumer's TSX and JSX against the design system, and answers one question: does this code use the system the way the system says? It is not a linter and not a type checker - the generated package's types already reject a wrong prop. What it reports is what type-checks and is still wrong:
- every usage rule the system carries, under its own id (
rule:one-primary-action), with the rule's severity and its reason word for word - the same sentence the package'sAGENTS.mdtold the agent; - the interface constraints a type could not hold - a JavaScript consumer, an
as any, a condition the target could not type - asinterface-constraint; - a colour or a length written by hand where a token holds that value (
raw-value, with the exact replacement), a native element the library already has a component for, a home-grown copy of one of its components, and an import of something the system has deprecated.
milda check src --package @acme/ds # the system from the API milda check src --package @acme/ds --in acme.milda # or from a file, offline milda check src --package @acme/ds --format github # inline PR annotations milda check src --package @acme/ds --json # counts per check, for scripts
The system is found in order: --in (a .milda file or a pulled snapshot), then the design-system.milda a release ships inside the installed package, then the hosted API. --package names the import that is the library, and can be given more than once for an alias such as @/ds. The exit code is non-zero on an error; --fail-on warning or never moves the line.
A checker never sees the whole application, so it reports only what the code proves. A component of the app is followed to the files that render it, through relative imports and barrels, so a Tab inside your own TabRow inside Tabs is a tab within a tab list. A value it cannot read (variant={kind}, a spread) decides nothing, an instance behind a condition is not counted against a maximum, and a component the run cannot account for - handed around as a value, imported through a path alias - proves nothing about where its contents end up. Check the whole app in one run rather than file by file.
#Reading vs. generating
The read commands (ls, show, tokens, diff) exchange the framework-agnostic contract - no target involved, so they are identical no matter what you generate to. generate is the one target-specific command, reached only through an explicit --target. Adding a future target (Vue, SwiftUI…) doesn't change any of the other commands.
npm on your own credentials.#Endpoints & local development
The CLI talks to the hosted read API (api.milda.dev) and, for login, to the Studio (milda.app). Point it elsewhere - a local stack, a self-hosted deployment - with --api / MILDA_API_URL and --studio / MILDA_STUDIO_URL.
#From source
Contributing, or want to run an unreleased build? From the monorepo the milda command maps to:
npx tsx packages/cli/src/index.ts <command> [options] # build a standalone binary for your platform: npm run build:sea -w @mildastudio/cli # → packages/cli/artifacts/milda-<os>-<arch>
For agents rather than humans, see the MCP server; for how a system gets published, see the tour and Release & registry.