CLI reference
One binary, liebstoeckel, with subcommands. A bare path is shorthand for live.
liebstoeckel <command> [args]liebstoeckel <deck> # shorthand for: liebstoeckel live <deck>liebstoeckel --help # the command surfaceliebstoeckel <command> --help # arguments and options for one commandliebstoeckel --versionEvery command has its own --help listing its arguments, options, and defaults.
The short alias lst is interchangeable with liebstoeckel (e.g. lst build).
Scaffold a new deck. It materializes in the current directory as ./<name>;
pass --dir <parent> to put it elsewhere (e.g. --dir presentations to drop it
into a monorepo’s workspace glob).
liebstoeckel new <name> [--brand <brand>] [--dir <parent>] [--no-org-brand]| Flag | Meaning |
|---|---|
--brand <brand> | brand theme to wire in (default: liebstoeckel, or your org’s default brand when logged in) |
--dir <parent> | parent directory for the deck (default: the current directory) |
--no-org-brand | don’t bake the logged-in org’s default brand; use the plain liebstoeckel brand |
Generates index.html, main.tsx, build.ts (using buildDeck from @liebstoeckel/thumbnails/build), server.ts, bunfig.toml, and a starter slides/01-intro.tsx. The name must be lower-case letters/digits/hyphens; it won’t overwrite an existing directory.
When you’re logged in (and didn’t pass --brand), new automatically bakes your
org’s default brand into the deck as owned source: brands/<name>.ts, wired into
<Present brandThemes={…}>, with data-brand set, so new decks are on-brand
instantly (see brand). Pass --no-org-brand to opt out, or --brand to
choose a specific one. It’s best-effort: offline or signed-out, you just get the
default liebstoeckel brand.
Scaffold registry items (charts, hooks, layouts, …) into a deck as owned
source: the code is copied into your project, not imported from a package, so
you can edit it freely. Leaf dependencies the items need (e.g. the relevant
@visx/* packages) are installed into the deck.
liebstoeckel add bar-chart # one item into the current deckliebstoeckel add chart bar-chart line-chart # several at once (optional category word)liebstoeckel add donut-chart --dir ./presentations/demoliebstoeckel add bar-chart --dry # show the plan, write nothing| Flag | Meaning |
|---|---|
--dir <deck> | target deck directory (default: current directory) |
--dry | print the plan (files + dependencies) without writing |
--force | overwrite files that already exist (default: skip them) |
--no-install | don’t run bun add for the items’ dependencies |
--json | structured plan/result output (default when stdout isn’t a TTY) |
add always prints its plan before writing: which files it will create,
overwrite, or skip, plus the dependencies it will install. Files land under the
deck’s charts/ (or the item’s own path); existing files are skipped unless
--force (so re-running is idempotent). Dependencies are installed with
bun add --ignore-scripts (the registry trust model), or skipped with
--no-install so you can run it yourself. With --json (or when piped), the plan
(--dry) or the result emits as JSON for an agent to consume.
Items resolve through a registry namespace. A bare name (bar-chart) comes
from the bundled default registry (@liebstoeckel); a scoped ref (@acme/heatmap)
comes from a registry you map in the deck’s liebstoeckel.json under
"registries", which today accepts a "default" keyword or a local path, with
npm/git/HTTP transports planned. Categories are chart, hook, element,
component, layout, motion; the leading category word is optional sugar.
registry
Section titled “registry”Browse the chart/component registry: the discovery surface for add, and the
machine-readable contract agents read before scaffolding.
liebstoeckel registry list # the catalog (name · type · data shape)liebstoeckel registry view bar-chart # one item: exports, props, dataShape, exampleliebstoeckel registry list --json # JSON (default when piped / not a TTY)view returns each component’s dataShape, the exact type of its data prop,
so you (or an agent) can wire data without reading the source. Output is JSON when
--json is passed or stdout isn’t a TTY, and a compact human view otherwise. This
reads the bundled @liebstoeckel registry.
Build a deck to a single self-contained .html (+ thumbnails), written to
./dist/<deck-slug>.html. The slug is the deck folder name, e.g. poll-demo/ →
dist/poll-demo.html. By default the build also
embeds a compressed copy of the deck’s own source (slides, package.json, etc., not
node_modules), so the compiled .html can later be ejected back to an editable
project. Source collection uses bun pm pack, so your deck’s package.json "files" list
controls exactly what’s embedded.
The deck defaults to the current directory; pass it as a positional [dir] or
with --dir <deck> (the convention shared by pack/live/export).
The build also embeds a third-party license notice built from the modules that
actually end up in the bundle (React, Motion, Yjs, the fonts, and the MPL-2.0 engine).
Minification strips license comments, so this puts the required attribution back. It is
recomputed on every build, so it stays correct when you swap a font or a library. Read
it with licenses.
The build requires a single copy of each @liebstoeckel/* package. A deck compiles
into one file, so if two framework packages disagree on a version — for example after
bumping @liebstoeckel/engine but leaving a plugin on an older release — the bundle would
embed two incompatible copies side by side. The build stops, prints the conflicting
versions, and asks you to update the framework packages together (e.g. bun update) so
they resolve to one version. --check reports the same conflict without writing anything.
liebstoeckel build # the deck in the current directoryliebstoeckel build --dir <deck> # or a positional [dir]liebstoeckel build [dir] --no-inline-package # build WITHOUT the recoverable sourceliebstoeckel build [dir] --check # validate only: no artifact, structured errorsliebstoeckel build [dir] --check --visual # + render each slide and lint for cut-off textliebstoeckel build [dir] --trust # pre-approve an unfamiliar deck's build-time codeLIEBSTOECKEL_NO_THUMBS=1 liebstoeckel build [dir] # skip thumbnailsThumbnails need a headless Chromium; the build skips them (never fails) when none is
found. See Chromium setup to install one or point at an existing
binary with LIEBSTOECKEL_CHROMIUM.
| Flag | Meaning |
|---|---|
--no-inline-package | don’t embed the deck’s source (smaller file, not ejectable) |
--no-inline-licenses | don’t embed the third-party license notices (not recommended) |
--allow-secret | override the secret gate that aborts if a packed file looks like a credential |
--check | validate the deck bundles and resolves one copy of each framework package (no output written); reports diagnostics + exits non-zero on error |
--visual | with --check: also render every slide headless and lint for cut-off or overflowing text (needs Chromium; skips with a reason when none is found) |
--trust | pre-approve this deck’s build-time code (a deck is code; remembered after the first build) |
--json | machine-readable JSON result (default when stdout isn’t a TTY); progress goes to stderr |
--check is the fast validation gate (no Chromium, no thumbnails, nothing written):
it returns { ok, diagnostics }, each diagnostic carrying message/file/line,
and exits non-zero when the deck doesn’t bundle. It checks that imports resolve and
MDX/TSX transforms; it does not type-check. Output is JSON when piped or with --json.
--check --visual adds a rendered lint pass on top: it bundles a throwaway copy of the
deck, steps through every slide in headless Chromium at the native 1280x720 stage, and
flags text the audience would not see: content clipped by its container
(clipped-text), chart labels sticking past their svg viewport (svg-clipped-text),
and text extending beyond the stage (offstage-text). Findings land under visual in
the JSON result, each with the slide index, a text snippet, a DOM path, and the overflow
in pixels; any finding makes ok false and the exit code non-zero, so an agent can loop
on it like a build error. Like thumbnails, it needs a headless Chromium; without one the
pass is skipped with a reason (visual.skipped) and does not fail the check.
With --json (or piped), a normal write build prints the human progress to stderr and
emits a single result object on stdout, so an agent can read the artifact path without
scraping prose:
{ "ok": true, "artifact": "/abs/path/dist/my-talk.html", "outfile": "my-talk.html", "thumbnails": 6 }thumbnails is null (with a thumbnailsSkipped reason) when no Chromium is available.
A refused untrusted build emits { "ok": false, "error": "untrusted deck", "hint": "…" }
and exits non-zero. Trusting a deck you didn’t write means agreeing to run its build-time
code on your machine — a human decision; an AI agent should surface it rather than approve
on its own. A human who trusts the deck approves it with --trust (or LIEBSTOECKEL_TRUST_BUILD=1).
Recover a built deck’s editable source from its .html.
liebstoeckel eject <deck.html> [outdir] [--force]outdir defaults to <deck>-source/. Eject refuses a non-empty directory unless --force, and
errors clearly if the HTML carries no embedded source (e.g. it was built --no-inline-package).
Rebuilding runs the deck’s own build-time code (macros/build plugins) — --ignore-scripts
only blocks npm lifecycle scripts, not that — so only rebuild a deck you trust.
liebstoeckel build confirms an unfamiliar deck once (see the warning under build):
cd <outdir> && bun install --ignore-scripts && liebstoeckel buildFor the full recover → edit (by hand or with an agent) → rebuild loop, see Editing a built deck.
Inspect, or emit, exactly the source a build would embed, without running a full build.
The deck defaults to the current directory; pass it as [dir] or --dir <deck>.
liebstoeckel pack [dir] # print the file list (defaults to cwd)liebstoeckel pack [dir] -o deck.tgz # write a gzip tarball (bun add-compatible)liebstoeckel pack [dir] --allow-secret # override the secret gatelicenses
Section titled “licenses”Report the third-party licenses bundled into a deck. Pass a built .html to print
the notices it already carries, or a deck [dir] / --dir <deck> (default: cwd) to
compute the report fresh from the deck’s real module graph.
liebstoeckel licenses dist/my-deck.html # print the notices embedded in a built deckliebstoeckel licenses # compute the report for the deck in cwdliebstoeckel licenses --dir <deck> # or a positional [dir]liebstoeckel licenses [dir] --json # structured: { ok, packages, firstParty, flagged }liebstoeckel licenses [dir] --check # exit non-zero if any non-standard license is bundledThe report lists only what is actually inlined into the .html: the client bundle and
the embedded fonts. Build-time tools like the MDX compiler, Tailwind, and the syntax
highlighter run during the build and aren’t redistributed, so they’re left out.
A built .html carries its notices as rendered text, so the command prints them
(with --json, as { source, notices }). A deck directory is recomputed from a
fresh bundle, so it gives the full structured report ({ ok, packages, firstParty, flagged }) and is the only input that works with --check. As with build --check,
the output is JSON when piped or with --json.
--check is a CI gate: it fails when a bundled package carries a license outside the
permissive/embeddable set (for example a copyleft chart library pulled in via
add), so you catch it before shipping.
Useful for verifying your "files" allowlist before shipping. The -o tarball is standard gzip
(bun add ./deck.tgz-installable); the in-HTML embed uses zstd internally.
Serve a deck for authoring: hot reload plus the dev shell (a sidebar beside
the deck; the plain deck alone is behind /deck), whose first tool is live annotations an AI agent can apply to
your slide source while you watch. With --dir, the server re-executes with
the deck as its working directory so the deck’s own bunfig.toml plugins load;
a port already in use fails with a message naming it.
liebstoeckel dev # the deck in the current directoryliebstoeckel dev --dir <deck> --port 3000liebstoeckel dev --host 0.0.0.0 # expose beyond localhost (trusted networks only)liebstoeckel dev poll # agent side: wait for an annotation batchliebstoeckel dev poll --reply <batchId> done --data '{"applied":["<id>"],"files":["slides/01-title.mdx"],"notes":[]}'| Flag | Meaning |
|---|---|
--dir <deck> | deck directory (default: current directory) |
--port N | port (default: 3000) |
--host <addr> | bind hostname (default 127.0.0.1) |
--json | machine-readable startup info |
dev poll
Section titled “dev poll”dev poll is the agent side. It blocks until the browser sends work (up to
--timeout ms, default 600000), prints one JSON event, and exits; every event
carries an _instructions field telling the agent exactly what to do next.
Replies are done with a JSON result or error "reason".
| Flag | Meaning |
|---|---|
--dir <deck> | deck directory (default: current directory); the running server is found through its .liebstoeckel/dev/server.json |
--timeout <ms> | how long to wait for an event before printing a timeout event and exiting (default 600000) |
--reply <id> | reply to the event with this id instead of waiting; pair with a positional done or error "reason" |
--data <json> | the JSON result for a done reply: {"applied": [annotationIds], "files": [paths], "notes": [strings]} |
Without a running dev server it prints {"error":"no_dev_server"} and exits 1.
The first run adds a small inert loader tag to the deck’s index.html. It
only activates under dev, where it loads the bridge script that connects the
framed deck to the sidebar, and every build strips it, so built decks are
unaffected.
Dev-mode state (annotations, screenshots, revert snapshots) lives under
.liebstoeckel/dev/ in the deck and is git-ignored.
If liebstoeckel dev reports that it could not load @liebstoeckel/dev-server,
the message names the underlying error; set LIEBSTOECKEL_DEBUG=1 to also
print its stack trace.
Present a deck to a room. Builds the deck if given a project dir, and captures slide thumbnails by default (so the overview grid is instant); the capture is skipped, not failed, when no Chromium is available.
The deck defaults to the current directory; pass it as a positional or with
--dir <deck> (the same convention as build/pack/export).
liebstoeckel live # the deck in the current directoryliebstoeckel live <deck.html | deck-dir> [--port N]liebstoeckel live --dir <deck> --relay <url> --relay-token <token>liebstoeckel live <deck> --no-thumbnails # skip the capture step| Flag | Meaning |
|---|---|
--port N | LAN port (default: auto) |
--relay <url> | present through a relay instead of LAN |
--relay-token <tok> | relay account token (or LIEBSTOECKEL_RELAY_TOKEN) |
--no-thumbnails | skip thumbnail capture (also via LIEBSTOECKEL_NO_THUMBS=1) |
--format / --width / --quality / --scale | tune thumbnails, as in thumbs |
Run a public relay server.
liebstoeckel relay [--port N] [--tokens tok1,tok2] [--public-url https://…]| Flag | Meaning |
|---|---|
--port N | listen port (or PORT) |
--tokens | comma-separated account tokens (or PRESENT_RELAY_TOKENS); one is generated if omitted |
--public-url | the public https:// origin (so links/WebSocket use wss://) |
thumbs
Section titled “thumbs”(Re)generate thumbnails for a built deck.
liebstoeckel thumbs <built-deck.html> [--format webp|jpeg|png] [--width 640] [--quality 80] [--scale 2]Needs Chromium and fails if none is found (Chromium setup).
export
Section titled “export”Export slides to PNG files or a single PDF (one slide, a range, or the
whole deck), rendered headless on the native 1280×720 canvas, the same pipeline as
thumbnails. The input is a built .html or a deck source directory (built on
demand); it defaults to the current directory, or pass it as a positional or
with --dir <deck>.
liebstoeckel export [deck.html|deck-dir|--dir <deck>] [opts]| flag | meaning |
|---|---|
--format png|pdf | output format (default: inferred from -o’s extension, else png) |
--slides <spec> | 1-based, inclusive: 3, 2-5, 1,3,5-7, 3-, -4 (default: all) |
-o, --out <path> | PDF: the .pdf file. PNG: an output directory |
--width <n> | viewport / page width (default 1280, the authoring canvas) |
--scale <n> | device-scale factor for PNG / raster PDF (default 2 → 2560×1440) |
--raster | PDF only: image-per-page instead of the default vector PDF |
--quality <n> | JPEG quality for the raster PDF (default 92) |
--page-size <size> | PDF only: the paper. slide (default, the page is the 16:9 canvas), a3, a4, a5, letter, legal, or an explicit <w>x<h> with a unit (210x297mm, 8.5x11in) |
--orientation landscape|portrait | PDF only, with a paper page: default landscape |
--margin <n><mm|cm|in|px> | PDF only, with a paper page: clearance on every side (default 10mm) |
# slide 3 of a built deck → PNGliebstoeckel export ./dist/my-deck.html --slides 3 -o ./out
# whole deck as a PDF, building the source dir on demandliebstoeckel export ./presentations/demo --format pdf -o demo.pdf
# printable handout: one slide per A4 page (landscape), 10 mm marginliebstoeckel export ./presentations/demo --page-size a4 -o handout.pdfPDF is vector by default: selectable, searchable text with embedded fonts and
vector charts/gradients, produced in a single page.pdf() over a print view that
stacks one slide per page (no merge, no extra dependency). Pass --raster for an
image-per-page PDF instead: no text layer, but pixel-exact fidelity for a slide
whose effects don’t reproduce under print.
Printing. By default each PDF page is exactly the 16:9 slide canvas, which
prints poorly on paper. Pass --page-size a4 (or letter, a3, an explicit
210x297mm, …) to get physical pages instead: the slide is scaled to fit the
printable area (the page minus --margin), centered, on white paper, aspect
preserved. Landscape is the default since slides are landscape; --orientation portrait fills the width and leaves the lower part of the page empty. Vector
text stays selectable, and --raster produces the same page geometry. A paper
page sizes the slide itself, so it cannot be combined with --width.
PNGs come straight off the page (lossless, native resolution), named
<deck>-slide-NN.png (1-based, zero-padded). Like thumbs, export needs Chromium
and fails loudly if none is found (Chromium setup).
Install the liebstoeckel-deck agent skill into a project so AI coding agents can
create and edit decks for you (scaffold → add charts → wire data → build). The skill
ships inside the CLI package, version-pinned so it always matches your installed
liebstoeckel, and is placed where each agent looks for it.
liebstoeckel skill install --dir ./my-deck # into one deck/project (writes AGENTS.md too)liebstoeckel skill install --global # into your user account (~) — every projectliebstoeckel skill install --target codex --dir ./my-deckliebstoeckel skill update # refresh the installed skill to the CLI's version| Flag | Meaning |
|---|---|
--scope <project|user> | where to install, a project/deck, or your user account (~); --global is shorthand for --scope user |
--target <list> | (install) claude, codex, cursor, gemini, or all (default); comma-separated |
--dir <deck> | the deck/project for a project-scope install (default: current directory) |
Scope. A project install writes the per-agent skill folders and the AGENTS.md fallback into the deck. A user install writes the folders under your home (~/.claude/skills/… and the rest) so every project sees the skill, and does not drop a bare ~/AGENTS.md. When you don’t pass a scope, an interactive terminal asks which you want; with --dir it’s a project install; and a non-interactive run refuses rather than scatter skill files into a directory that isn’t a deck — pass --scope user or --dir <deck> to be explicit.
It writes the same skill to each agent’s path (.claude/skills/, .agents/skills/ for
Codex/Gemini, .cursor/skills/ + a rule) and merges a managed block into AGENTS.md,
the universal fallback for agents without skill support. Re-running is idempotent. See
the Scaffolding guide for how an agent uses it.
The installed skill is version-pinned to the CLI that wrote it. skill update
rewrites the skill for the agent paths already present in the deck (plus the
AGENTS.md block) without adding new ones. You rarely need to run it yourself: the
installed skill is a pure function of the CLI version, so whenever an installed copy
is older than the CLI you run, any command refreshes it in place and prints a
one-line note on stderr. This covers project installs and a user-scope install
under ~ alike.
update
Section titled “update”Bring everything current in one command: the deck’s @liebstoeckel/* packages, a
global CLI install, and every installed copy of the agent skill, whichever of those
apply.
liebstoeckel update # the deck in the current directory (and/or the global CLI)liebstoeckel update --dir <deck> # a specific deck| Flag | Meaning |
|---|---|
--dir <deck> | the deck to update (default: current directory) |
What it does, in order:
- Checks the registry live for the latest
@liebstoeckel/cli(this command never uses the reminder cache; it also refreshes that cache with the answer). - Deck-local install: runs
bun update --latestfor exactly the@liebstoeckel/*packages the deck declares, never your other dependencies. Updating the whole scope together keeps the framework packages on one version, which the build requires. - The CLI’s own install root, when the running CLI lives in a different
project than the deck (for example a project root holding
@liebstoeckel/cliwith decks in subdirectories): that root’s declared@liebstoeckel/*packages update too, so the CLI itself never stays behind. A CLI install the command cannot manage (not declared where it is installed) is called out explicitly instead of being skipped in silence. - Global install (the CLI was installed with
bun add -g): runsbun add -g @liebstoeckel/cli@latest. Shapes combine when several apply. - Refreshes the agent skill wherever it is installed (the deck and your user account), using the freshly installed CLI, so the skill version matches the new CLI.
Outside a deck and without a global install there is nothing to act on, and the command says so instead of guessing.
After a deck update the command verifies that every @liebstoeckel/* package
resolved to a single version. Releases bump dependents together, so a split is
rare, but a package published against an older sibling can keep a stale nested
copy that targeted updates never re-resolve; when that happens the command
prints the affected packages and the clean reinstall
(rm -rf node_modules bun.lock && bun install) that collapses them.
doctor
Section titled “doctor”Check the build environment — the Bun version (must satisfy the CLI’s engines.bun) and whether a Chrome/Chromium was found for thumbnails and PNG/PDF export. Optionally install one.
liebstoeckel doctor # human summary (Bun + Chromium + config path)liebstoeckel doctor --json # machine-readable (default when piped)liebstoeckel doctor --install-chromium # download Playwright's Chromium, record its path| Flag | Meaning |
|---|---|
--install-chromium | Install Playwright’s Chromium if none resolves, then record its path in ~/.config/liebstoeckel/config.json so every later build/export reuses it |
--json | Machine-readable output (default when piped) |
--dir <deck> | Deck to check for scaffold migrations (default: the current directory) |
doctor resolves Chromium the same way builds do (see Chromium setup): an existing system Chrome is found automatically, so often nothing needs installing. Only export/thumbs require a browser; build skips thumbnails without one. When no browser is found, the human output lists every probed location, and --json adds them as probedCandidates.
The report also includes CLI staleness from the cached registry check (never a live
lookup, so doctor stays network-free): --json carries
cli: { version, latestKnown, updateAvailable }. Agents can gate on
updateAvailable and suggest liebstoeckel update; latestKnown is
null when no check has completed yet (for example offline).
Scaffold migrations
Section titled “Scaffold migrations”Scaffolded deck source is yours, and the conventions it starts from keep
improving. When a convention changes after a deck was created, doctor detects
the gap instead of leaving the deck to drift: run it inside a deck (or pass
--dir <deck>) and --json carries a migrations array with one entry per
known migration:
"migrations": [ { "id": "0001-hmr-entry-boundary", "needed": true, "since": "0.3.11", "reference": "references/migrations/0001-hmr-entry-boundary.md", "reason": "slide edits in dev mode reset the deck to slide 1 ...", "autoPatchable": true }]needed says the deck predates the convention; reference names the guide
(shipped inside the installed agent skill) with the why and the manual edit;
autoPatchable says liebstoeckel dev would apply it automatically.
doctor itself never modifies anything.
When dev starts, it applies every needed migration whose file still matches
the scaffolded shape, printing one notice per patched file
(↻ migrated main.tsx (0001-hmr-entry-boundary): <reason>); the patch always
includes a comment explaining itself. A file that diverged from the scaffold
is never rewritten, only hinted about.
A deck that deliberately diverges can opt out per migration in its
package.json, with a mandatory reason:
"liebstoeckel": { "migrationOptOut": { "0001-hmr-entry-boundary": "custom HMR via our own dispose handler" }}A suppressed migration is never detected, hinted, or patched; doctor --json
still lists it with suppressed: true and the reason, and warns (as
migrationWarnings) about opt-out entries that suppress nothing (an empty
reason, or an unknown id).
Staying up to date
Section titled “Staying up to date”The CLI checks its registry for a newer @liebstoeckel/cli at most once an hour,
in a detached background process, and prints a one-line note on a later run when an
update exists. The check shells out to bun pm view, so it follows the same registry
configuration as your installs (a scoped registry in .npmrc/bunfig.toml, or the
public npm registry); no command ever waits on the network for it. Acting on the note
is one command:
liebstoeckel update # CLI + the deck's @liebstoeckel/* packages + the agent skillReminders appear only on an interactive terminal. They are suppressed with --json,
when output is piped (agents), when CI is set, and entirely with
LIEBSTOECKEL_NO_UPDATE_CHECK=1. The check interval can be tuned with
LIEBSTOECKEL_UPDATE_CHECK_INTERVAL=<seconds>. The agent-skill refresh is separate
from these reminders: a stale installed skill is refreshed automatically on any
command, in every mode (see skill).
Cloud commands (coming soon)
Section titled “Cloud commands (coming soon)”The remaining commands talk to liebstoeckel cloud, the hosted control plane.
Sign in to liebstoeckel cloud (the hosted control plane) from the terminal using the
OAuth 2.0 device-authorization grant (RFC 8628), with no password. The CLI prints a URL;
open it in a browser, sign in with an email one-time code, and approve. The resulting
token is stored in ~/.config/liebstoeckel/credentials.json (mode 600).
liebstoeckel login --api https://<your-control-plane-host>| Flag | Meaning |
|---|---|
--api <url> | the control-plane base URL (or set LIEBSTOECKEL_API) |
Upload a built single-file deck to your cloud dashboard. Authenticates with the token
from login; the deck appears in your dashboard’s deck list, and for a team org it’s
visible to every member (the shared library).
liebstoeckel build # produces ./dist/<deck-slug>.htmlliebstoeckel push # no path → that built deck in ./distliebstoeckel push --org acme # push into a team you belong toliebstoeckel push ./dist/my-deck.html --title "My deck" # explicit path + title overrideRe-push = a new version. push identifies a deck by a stable key (the deck
folder name, or --name <key>); pushing the same key again updates that deck in place
at the same URL (a new version on Pro; the latest is replaced on free). --new forces a
separate deck. With no path, push uploads the single built .html in ./dist
(run build first). The title comes from --title → the deck’s own embedded
<title> → the deck key: the server reads the <title> straight from the
uploaded file, so any Unicode (em-dashes, smart quotes, emoji) just works. Only an
explicit --title override travels as a header (URL-encoded). A cover is read from
the deck’s built-in thumbnails.
| Flag | Meaning |
|---|---|
--title <title> | override the title (default: the deck’s embedded <title>, then its key) |
--name <key> | the deck key to upsert (default: the deck folder name) |
--new | create a separate deck instead of updating the matching one |
--org <slug> | push into this organization (default: the one set by orgs use, else personal) |
--api <url> | override the stored control-plane host |
List the organizations (personal workspace + teams) you belong to, and choose which one
push targets by default.
liebstoeckel orgs # list workspaces; → marks the push defaultliebstoeckel orgs use acme # make "acme" the default for pushTeams, members, roles (owner / admin / member), and invitations are managed in the
dashboard’s Team page; the CLI only needs to pick which org a deck lands in.
Share governed brand token sets across a team. Your org is an authenticated registry
(the same protocol as add/registry); a brand is pulled into a deck as owned source
(brands/<name>.ts) and baked at build, so the deck stays self-contained and WYSIWYG.
liebstoeckel brand list # brands in your org (→ marks the default)liebstoeckel brand push ./brand.ts --default # upload a theme token file (admins/owners)liebstoeckel brand pull acme # write brands/acme.ts into the current deckliebstoeckel new my-deck # auto-applies the org's default brandbrand push accepts a defineTheme(...) module or a flat tokens JSON. Editing is also
available in the dashboard’s Brand page. Brand sharing is a paid (Pro/Team) feature.
A brand’s fonts are picked from a curated Fontsource catalog in
the Brand page. brand pull writes the import "@fontsource-variable/…" into
brands/<name>.ts and runs bun add to install those font packages, so the webfont is
inlined into your single-file deck at build (pass --no-install to install them yourself).
A Custom… font carries no package and falls back to the system font unless you supply
its own @font-face.
| Flag | Meaning |
|---|---|
--default | (push) make this the org default that new applies |
--name <key> | (push) brand name (default: the theme’s name, then the file name) |
--dir <deck> | (pull) the deck to write brands/<name>.ts into (default: cwd) |
--no-install | (pull) write the files but don’t bun add the brand’s font packages |
--org <slug> | the organization to act on |
List your cloud decks in an organization, with view counts.
liebstoeckel decks # decks in your default workspaceliebstoeckel decks --org acme # decks in a team you belong to--org works the same on every cloud command (push, decks, …): explicit --org
wins, else the default from orgs use, else your personal workspace.
Code-first presentations your agent can author. One file out, no server.
Comments