The command-line tool for Nibble. It starts a site, runs a site's tasks, and works on the content of any Nibble site you sign in to, as you and never as more than you.
For macOS and Linux, on Intel or ARM:
curl -LsSf https://github.com/mah3uz/nibble-cli/releases/latest/download/nibble-cli-installer.sh | shFor Windows, in PowerShell:
irm https://github.com/mah3uz/nibble-cli/releases/latest/download/nibble-cli-installer.ps1 | iexEither puts nibble in ~/.local/bin. Each release also has a sha256.sum for checking a download by hand. From a
clone, just install builds it and does the same, and tells you if ~/.local/bin isn't on your PATH yet.
nibble new my-site # checks this computer, fetches the latest release, verifies it, and installsInside a site's folder, any word that isn't one of the tool's own runs a task: nibble check is
bin/rails nibble:check, nibble schema show collections/posts is bin/rails nibble:schema:show collections/posts.
nibble auth login example.com # opens the site in your browser to sign in and choose what the CLI may do
nibble remote # what this connection can do on the site
nibble remote list-entries --collection posts
nibble remote update-entry --id 12 --lock-version 3 --data '{"title":"Better title"}' --dry-runEvery operation comes from the site itself, so nibble remote always matches the site you're signed in to.
Changes to entries are drafts until someone who may publish does. Add --dry-run to see a change without saving it.
What this connection can't do is listed after the rest, with the access it needs.
-
Upload a file:
nibble remote upload-asset --file photo.jpg --folder Blog --alt "A cat"sends the file from your computer (streamed, under its own name unless you add--filename) and makes the asset from it. -
Changes that need approval: some changes wait for the person you act for. The command prints their link and waits while they approve it in the Control Plane, then prints the result as usual. Ctrl-C stops waiting and leaves the request open (
nibble remote get-approval --approval <id>checks on it later);--no-waitprints the link and returns at once. With--jsonor a pipe it never waits and answers with theapproval_requirederror and its link. A request left waiting exits with status 2, apart from a failure's 1, so a script can tell the two apart. -
More than one site or account:
nibble auth list,nibble auth switch example.com:you@example.com, or--siteon any command. -
A folder that always means one site: put
site = "example.com"in.nibble.tomland runnibble auth allowthere once. A.nibble.tomlyou haven't allowed is refused, so a cloned repository can't point your commands at a site you didn't choose. -
Scripts and CI: create a token under Connected apps in the site's Control Plane, then set
NIBBLE_SITEandNIBBLE_TOKEN. -
A server without a browser:
nibble auth login example.com --device, if the site allows signing in with a code.
Sign-ins are kept in this system's keychain. Where there is none, --insecure-storage keeps them in a file only you
can read, and nibble doctor says so.
cd my-project
nibble connect example.com # signs in if needed, asks which AI apps to set up, then adds the MCP server and the site's guide
nibble connect --client codex # exactly these apps, without asking (claude-code, codex, cursor; claude and chatgpt print where to paste the address)
nibble disconnect example.com # removes them again; it doesn't sign you out, `nibble auth logout` doesA site's tools and guide load only where you work on that site, so connect installs into the current project and
never user-wide: Claude Code gets claude mcp add --scope project and ./.claude/skills, Codex
./.codex/config.toml (it reads that once you trust the project) and ./.codex/skills, and Cursor
./.cursor/mcp.json. A folder that is another Nibble site's project is refused, so one site's tools never land in
another's; run it from the site's own folder or a folder that isn't a Nibble site.
connect asks "Which AI apps should use 's tools in this folder?" and lists Claude Code, Codex, Cursor, Claude and
ChatGPT, marking those found on this computer; Enter sets up the ones found, numbers separated by commas choose, and
0 or none skips. Without a terminal it writes nothing and prints the command with --client to run. It can be run
again: the server entry is replaced and the skill refreshed. It prints the MCP address when you choose no app. disconnect removes only that
site's entries and skill folders from the current project, and from the user-wide places older versions of nibble
wrote to, and leaves everything else alone.
For finer control, nibble mcp install --client claude-code and nibble skill install --client claude do each part
alone (also into the current project), and nibble skill sync refreshes the project's installed skills.
After nibble auth login, nibble asks the same question on a terminal, and prints nibble connect <site> when it
can't ask. nibble doctor shows when the sign-in ends if it goes unused.
The site must have Agent access turned on by an administrator.
Add the line for your shell, then open a new terminal:
| Shell | Where | Line |
|---|---|---|
| bash | ~/.bashrc |
eval "$(nibble completion bash)" |
| zsh | ~/.zshrc, after compinit |
eval "$(nibble completion zsh)" |
| fish | run once | nibble completion fish > ~/.config/fish/completions/nibble.fish |
| PowerShell | $PROFILE |
nibble completion powershell | Out-String | Invoke-Expression |
Tab then completes, with a description beside each choice where the shell shows one:
- Commands and their options, and only the options not given yet.
- Inside a site, its tasks:
nibble sch→schema,nibble schema→show,snapshot,types. - Your sites and accounts for
--site,auth switchandauth logout; your sites forauth login; the apps for--client. - What this connection can do on the site:
nibble remoteoffers its operations, marking those that change content; after one, its arguments; after an argument, its values: the collections, taxonomies, global sets and menus you may use, the blueprints of the collection already given, the site's locales, and statuses and actions.--dataoffers-for standard input or@and a file.
Pressing Tab never contacts the site, reads the keychain, or runs anything in the folder you're in. It reads what
nibble remote last learned from the site and saved beside your settings; signing in saves the first. After changing
a site's schema, nibble remote describe-site brings completion up to date; otherwise it catches up within the hour.
A site's tasks are learned when nibble new makes it, and otherwise the first time you run one, or nibble doctor,
there.
On a terminal, lists print as tables and records as aligned fields, with colour for headings, operations and anything
that changes content. NO_COLOR=1 turns colour off, and it is never sent to a pipe or a file. Otherwise, or with
--json, every command prints {"ok": true, "data": …, "site": …} or {"ok": false, "error": {"code", "message", "hint"}}. --pick entries.0.title prints just that value, for a script to capture.
The CLI is built with Rust and just:
just test # formatting, clippy, unit tests, then the contract test against ../ (or: just test <path>)
just build # an optimised build at target/release/nibble
just install # build it and put it in ~/.local/binThe contract test starts a Nibble checkout on a test database, gives the CLI a token, and works on content through
the management API, so the CLI and Nibble can't drift apart unnoticed. Keep this repository in a Nibble checkout's
cli/ folder: just test finds Nibble at .., and Nibble's own release script runs script/ci from there before it
tags a release.
nibble remote reads each site's operations at run time, so a new operation needs no new CLI. What the CLI relies on
is the management API's contract number, Nibble::MANAGEMENT_API_VERSION; API_VERSION in src/api.rs must match
it, and every answer is checked against it.
just release 0.2.0It refuses a malformed or backwards version, a tag that exists, a dirty tree, a branch other than main, and an
empty Unreleased section in CHANGELOG.md. Then it sets the version in Cargo.toml, dates the changelog section,
runs script/ci, and only then commits and tags. If the checks fail, nothing is committed or tagged.
It then offers to push. The pushed tag starts dist's workflow in
.github/workflows/release.yml, which builds these and publishes them with the installers and checksums as a GitHub
release, its notes taken from the changelog:
| Target | Built on |
|---|---|
x86_64-unknown-linux-musl |
ubuntu-22.04 |
aarch64-unknown-linux-musl |
ubuntu-24.04-arm |
aarch64-apple-darwin |
macos-14 |
x86_64-apple-darwin |
macos-15-intel |
x86_64-pc-windows-msvc |
windows-2022 |
Linux builds are static, so one runs on any distribution. macOS builds aren't signed; installed with curl, macOS
doesn't quarantine them, so they run without a warning. The settings are in
dist-workspace.toml; after changing them, run dist generate to rewrite the workflow, and dist plan to see what a
release would build.
