CLI reference
The commands you will actually use.
Add a component
bunx --bun shadcn@latest add @woohl/buttonResolves the whole chain: npm packages, internal registry dependencies, and the
token layer. Files land at the paths your components.json aliases define.
Add several at once
bunx --bun shadcn@latest add @woohl/dialog @woohl/dropdown @woohl/selectShared dependencies are resolved once, not per item.
Where things land
Exactly where anything from ui.shadcn.com would: the CLI resolves code against
the aliases in your own components.json, so these paths follow whatever you
have configured. The defaults:
| Item type | Installs to | Example |
|---|---|---|
registry:ui | <ui alias>/<file> | src/components/ui/button.tsx |
registry:block | <components alias>/<file> | src/components/price.tsx |
registry:lib | <lib alias>/<file> | src/lib/utils.ts |
The class merger arrives as lib/utils.ts exporting cn, which is the file
your utils alias already points at — it does not stand up a second helper
beside the one you have.
Two things are not code and so carry an explicit target:
| Item | Installs to |
|---|---|
@woohl/tokens | styles/** |
@woohl/brand-assets | assets/brand/** |
Why the stylesheets are files
The token layer is around 1,400 lines of @theme and @theme inline blocks
across sixteen stylesheets. That is not expressible as a registry item's
cssVars map, so it ships as files and you wire it with one @import — see
Installation.
Updating
There is no update command. Re-running add overwrites the file, which is the
honest model for source you own: your local edits are yours, and taking a new
version means deciding what to do with them.
bunx --bun shadcn@latest add @woohl/button --overwriteDiff before you accept it if you have edited the file — the CLI will not merge for you.
Building the registry
From inside woohl-ui:
bun run registry:build # compile registry/ into public/r/*.json
bun run build # registry + MDX + next build
bun run start -p 4321 # serve it for @woohl-local installsregistry:build derives every dependency from the source imports, so adding an
import to a component is enough — the manifest updates itself.