Vibes DIY
Vibes DIY / Docs
creator docs · cli

The CLI handbook

Everything you can do in the Vibes DIY web UI — and a good deal you can't — is scriptable from the shell. There's nothing to install: every command runs as npx vibes-diy <command>. This page is the tour by task; the command reference is the complete flag-level contract, generated from the CLI's own help output. For the story of building whole sites with one command, see the generate case study.

Sign in once (or never, in CI)

shnpx vibes-diy login

opens a browser flow and enrolls this machine with a device certificate — that cert is the credential for everything below; there are no API keys. For headless environments (CI, containers), export the enrollment's keybag file via the VIBES_DEVICE_ID env var instead — the exact recipe, including why it's the keybag JSON and not a bare id, is in the CI lane pattern.

Claim your handle

Signing in gets you an account; it does not get you a handle. Your handle is the public name your apps are published under — vibes.diy/<handle>/<app> — and nothing picks one for you, so the first push, publish, generate or edit on a brand-new account stops and asks for one:

shnpx vibes-diy claim-handle              # see suggestions — claims nothing
npx vibes-diy claim-handle potluck-pat  # claim that name, and make it your default

Run with no name and you get a list of available handles (names derived from your profile first, random three-word slugs after) plus the command to claim one. Nothing is reserved until you type a name back — that's the point: the name is public and permanent-feeling, so it's worth choosing on purpose. If the name is already taken you're told so, and can just try another.

Claiming also sets the handle as your default, so every later command acts as it without any extra flag.

Act as a specific handle (--handle)

One account can hold several handles (your public you/… names). One is your default — the identity every command uses when you don't say otherwise. To act as a different one of your handles for a single command, pass --handle <slug>:

shnpx vibes-diy db put --vibe you/potluck --db signups --handle you-bot '{"rsvp":"yes"}'
npx vibes-diy push --handle you-bot          # publish under you-bot this once

--handle is stateless: it overrides the acting handle for that one invocation only and never changes your saved default. (To change the default itself — a persistent switch — use npx vibes-diy user-settings --set-default-handle <slug>.) The handle you pass must already be bound to your account; you can't act as someone else's handle.

Every command that acts as an identity accepts it — push, edit, pull, generate, put-asset, versions, publish, unpublish, and all db, secrets, and developer subcommands. It's the clean primitive for CI or a shared device that should write as a dedicated bot handle without disturbing whoever's default is set on that machine.

Ship apps from the shell

shnpx vibes-diy system > RULES.md     # the coding rules the generator follows
# write App.jsx (and optionally access.js, backend.js) …
npx vibes-diy push                   # deploy — prints a live HTTPS URL

Or skip the writing part entirely:

shnpx vibes-diy generate "a potluck signup with dish categories"
npx vibes-diy edit you/potluck "add a vegetarian filter"

generate opens a chat on the same lane as a first prompt on the web: the server names the app and builds it. Then, by default, generate publishes it the way a website publish does — reachable by anyone, or gated when the app is one the build judged privacy-sensitive, with the app's own access rules governing its documents either way. A build that has no access.js yet stays private with a note, since nothing would govern what visitors may do with its data; add an access.js and push, or pass --no-access-fn to open it anyway. --private publishes it private instead — reachable only by you and the people you approve — whether or not the app already existed. It then stays in that conversation, printing the build's progress and writing each version of the files to disk; in a terminal you can type a follow-up (/exit or Ctrl-D to leave), and under --json it returns as soon as the build is done. --record <dir> keeps a raw transcript of the run for debugging. A new app's first build runs on our fastest builder (the Lightning build); --careful has the agent plan the whole app first and build it on our most capable model. edit sends a follow-up prompt to an existing vibe, writes the changed files to disk, and pushes live. Round-trip an app you already have with pull / push:

shnpx vibes-diy pull you/potluck --dir ./potluck   # draft by default; --published for the live release
npx vibes-diy push                                # from that directory

Versions, publish, unpublish

shnpx vibes-diy versions you/potluck   # every release: fsId, mode, seq — ● marks published
npx vibes-diy pull you/potluck --fsId <id>   # retrieve any historical version
npx vibes-diy publish you/potluck    # promote the latest draft to production
npx vibes-diy unpublish you/potluck  # reversible takedown — code, data, grants all kept

Publishing only ever mints a new production release; it never destroys history, and unpublish is a tombstone you can lift with publish. One caveat that matters when testing: access.js and backend.js always run the live release — old fsIds serve pinned pages, not pinned backends — so stage risky backend changes under a secondary slug.

Pulling a specific version

versions prints the release list, and the version menu on your app's page labels the same releases v1, v2, v3… pull --version speaks that label, so the CLI and the web UI agree about which one is "v3" by construction:

shnpx vibes-diy pull you/potluck --version v3   # the version the menu calls v3
npx vibes-diy pull you/potluck --version 3    # the bare number works too
npx vibes-diy pull you/potluck --fsId <id>    # an exact version, by id
npx vibes-diy pull you/potluck --published    # the live production release

With no selector at all you get your latest draft — the newest thing you pushed, released or not.

Name a version that doesn't exist and the error tells you what does, instead of leaving you to guess:

Error: No version v9 of you/potluck. Available: v1 (v1 is the published head).

And two selectors together is an error, never a silent winner — the flag whose version you didn't want would otherwise be the file you start editing:

Error: Pick one version selector, not 2: --version and --published

(There is also a --draft selector. For your own vibes it does nothing you don't already get, since the latest draft is the default; it exists so a platform administrator doing support work on someone else's app has to opt in explicitly to their unreleased work, because a bare pull of a foreign app serves the published release.)

Suggestion chips on your app's card

The suggestion chips a visitor sees on your app's edit card are part of that published release — they're whatever your app last offered when you published, not whatever your chat said a minute ago. Set them yourself when you want specific invitations in front of strangers:

shnpx vibes-diy app chips you/potluck "Add a signup sheet" "Track who brings what"
npx vibes-diy app chips you/potluck --clear   # show visitors no chips at all

Up to three chips (extras are trimmed). This changes what visitors see, not your own edit card, which keeps following your latest chat turn.

One thing to know: publishing the vibe again re-derives the chips from your chat and overwrites whatever you set by hand. Set your chips after you publish, and set them again after your next publish.

Changing an app's look after it is built

Two different things make up how an app looks, and they change at two different speeds.

Colours change now. A theme (and the style tweaks on top of it) is served with the app, so saving one is the whole job — nothing is rebuilt. Visitors pick it up on the next cache beat, within about ten minutes; add ?cb=1 to the app's URL to see it at once.

Layout changes on the next build. Every app is also built with a layout archetype — the shape of the page — and three flourishes, the small touches on its controls and headings. Those live in the app's own code, so choosing new ones doesn't rearrange anything by itself: it tells the builder what to use next time. The very next edit you ask for restructures the app onto them, keeping your data and how the app works.

In the web editor, the Style tab starts with whole-style Presets and a Font dropdown, followed by colors and a spacing pad. Preview your changes, then use Publish style at the top to make them public; the same button reads Published once the draft matches the published style. Revert to saved restores that published style.

Below those controls, pick an archetype and exactly three flourishes, press Save layout, and then press Rebuild layout when you want the app rearranged right away rather than on your next edit. Some flourishes are greyed out — an app wearing a house look only offers the ones that look sits well with, and the one that rules it out is named. The theme picker is in Settings.

From the shell:

shnpx vibes-diy app look get you/potluck     # theme, style, archetype + flourishes

npx vibes-diy app look set you/potluck --theme slab
npx vibes-diy app look set you/potluck --variety "card-grid:hover-lift,accent-rule,tint-wash"
npx vibes-diy app look set you/potluck --variety none      # back to no chosen layout
npx vibes-diy app look set you/potluck --variety "list-feed:hover-lift,accent-rule,tint-wash" --relayout

app look get also tells you where the current layout came from: drawn for you when the app was built, chosen at build time, or changed since. --relayout is the one flag that builds something — it asks for the rearrangement immediately, which spends a build the way any other edit does. Without it, saving only arms the next one. npx vibes-diy variety prints the archetype and flourish names with what each one does, and a name that isn't one of them is refused before anything is written, with the valid names listed.

Your app's data is scriptable

shnpx vibes-diy list                                    # your vibes
npx vibes-diy db list  --vibe you/potluck             # its databases
npx vibes-diy db query --vibe you/potluck --db dishes # latest docs
npx vibes-diy db get   --vibe you/potluck --db dishes <docId>
npx vibes-diy db put   --vibe you/potluck --db dishes '{"kind":"dish","name":"chili"}'
npx vibes-diy db del   --vibe you/potluck --db dishes <docId>
npx vibes-diy db subscribe --vibe you/potluck --db dishes   # live change tail

Attaching files to a document

db put can carry files along with the document. Each --file names a file or a glob, and every match lands on the document as _files.<filename> — the same attachments an app saves from the browser, with a URL your app can show:

shnpx vibes-diy db put --vibe you/potluck --db dishes \
  '{"_id":"dish:chili","kind":"dish","name":"chili"}' --file chili.png
npx vibes-diy db put --vibe you/potluck --db dishes \
  '{"_id":"dish:menu","kind":"menu"}' --file 'photos/*.jpg'

--files is the same flag spelled the other way. Anyone who can write the app's data can attach files this way, not only its owner. Run it again and files that haven't changed stay as they are — only new or edited files upload — and attachments already on the document that you didn't name are kept. Each file prints uploaded <name> or unchanged <name> as it goes. Images, text and JSON attach on any account; other file types ask for a card on file first.

subscribe streams document changes as they commit — a real-time tail you can pipe into anything. These commands are also how external workers join an app's workflow — see doc-status state machines.

Sample data

An app can ship sample records in a seed.json beside its code. They land in the app's real databases when you ask for them — the same thing the Add sample data button in the Data tab does, and what the builder can do for you in chat:

shnpx vibes-diy db seed add    you/potluck   # add the live version's sample records
npx vibes-diy db seed remove you/potluck   # take them out again

add keeps every record already there, including sample records you edited, and adds the ones that are missing. remove takes the sample records out and keeps them out until you add sample data again; records people added stay. Both print what they did per database. Only the app's owner can run them.

Reading your own vibes, and reading someone else's

The read commands (get/query/list) behave differently depending on whose vibe you name, and the split is worth knowing before it surprises you.

Your own vibes — including any vibe owned by any handle on your account — read with the owner's admin-mode override applied for you. That's why the CLI can show you a document your browser session can't: it takes the override lane your ownership already entitles you to. You never type a flag for this.

Someone else's vibe is read as you, with their app's access rules applied to you like any other visitor. If those rules don't let you see it, the read is refused:

$ npx vibes-diy db list --vibe mike/clone-arrange-luck-soft
Error: Access denied
(reading another owner's vibe: retry with --admin to request platform-admin elevation)

That hint is honest about what --admin is: a request, not a grant. Adding it asks the server for platform-admin elevation, and the server honors that only for the short, verified allowlist of platform administrators who do support work. For everyone else the flag changes nothing — the same read is refused with or without it. It is not a way to see more of somebody else's app than they've shared with you.

Writes are honest by default, and db put --admin and db del --admin are not the same override. Both bypass the older per-database ACL, but only del will proceed past an access function that refuses the operation; a put the access function refuses stays refused, --admin or not. That asymmetry is deliberate rather than an oversight: a delete removes something, while a forced write would put words in another person's mouth — a document attributed to them that they never wrote. Every forced delete is audited.

The full story is in the access model.

Secrets, assets, transcripts

shnpx vibes-diy secrets set STRIPE_KEY --vibe you/potluck   # backend-only, write-only
npx vibes-diy secrets ls  --vibe you/potluck              # names + last4, never values
npx vibes-diy secrets rm  STRIPE_KEY --vibe you/potluck

npx vibes-diy put-asset ./hero.jpg    # content-addressed upload — prints CID + public URL

npx vibes-diy codegen-log you/potluck # the builder↔LLM transcript that generated the app
npx vibes-diy app-chats  you/potluck  # the app's own runtime AI conversations

Secrets surface as ctx.secrets in backend.js. put-asset returns an immutable, publicly served URL — handy for images your vibes or blog posts reference. The two transcript commands answer different questions: codegen-log is how this app got built, app-chats is what the app's users asked its AI.

The CLI teaches you the platform

shnpx vibes-diy skills            # list the codegen skill docs
npx vibes-diy skills backend    # dump one — the same doc rendered at /docs/reference/
npx vibes-diy themes            # the design themes
npx vibes-diy system            # the base system prompt itself

These dump the exact documents the code generator reads — the same corpus published as the generator's reference, available offline in your terminal.

Mount your vibes into any AI agent

shnpx vibes-diy mcp

starts an MCP server (stdio transport) exposing list/get/put/query/delete over your vibes' data, plus adding and removing an app's sample data — so Claude Code, Cursor, or any MCP-capable agent can read and write your apps' databases with your device credential and your access rules. npx vibes-diy mcp --help prints the client setup snippet.

Updated CLIs keep working as settings grow

Current CLI builds read the user-settings fields they understand and skip fields a newer server adds, so a server upgrade doesn't break settings commands. If a settings command fails right after a server upgrade, update the CLI first — builds older than this tolerance fix still hard-fail on fields they have never heard of.

Where to go next