CLI Reference
Reference every Grain CLI command, positional argument and documented flag, with expected output and common failure messages.
The Grain CLI connects local folders to workspaces, moves files, runs workspace commands, records revisions and manages shares. Install it with the Grain desktop app, then use this page with grain --help because the app bundle owns the CLI version.
Connect Claude Code or Codex
Open Settings → Integrations in the Grain desktop app whenever you want to connect an agent, including after skipping onboarding. Choose your agent and follow its installation instructions. Install the bundled CLI from the Grain CLI section on the same page first; its commands also run grain login, which signs you in through your browser. grain setup does the sign-in and plugin installation in one command. The copyable raw MCP entries are on Integrations.
The native Grain plugin includes both its workflow skill and MCP tools. Run the supplied commands in your terminal, then start a fresh agent session. Copying an installation command does not mean the plugin is installed. Use Refresh status to inspect installation and Check connection to check whether Grain tools are reachable; a successful tool check does not verify your server session or prove the external agent has loaded the plugin. Try asking your agent to create a short welcome document in Grain.
Use Grain by default for artifacts is a separate, optional preference in integration settings. It can be changed independently of plugin installation. Settings also provides update, removal, and legacy migration instructions. Removing a plugin does not revoke your CLI session or remove personal preferences; use the preference control or grain logout separately when desired.
This integration uses the local desktop app and CLI. Grain development checkouts use their own isolated manual MCP instructions instead of the production plugin.
Global invocation
grain [--json] [--workspace <id>] <command>
grain -v--json emits one JSON envelope and never prompts. --workspace <id> selects a workspace explicitly. The -v version option prints the CLI version. With no command, the CLI prints help and exits with status 1; an unknown command reports Unknown command: <command>.
Complete usage index
grain setup [--claude] [--codex] [--no-open] [--check]
grain login [--signup]
grain launch
grain logout
grain init [path] [--organization <id>] [--name <name>] [--template <template>] [--folder <path>] [--keep-failed] [--no-open] [--no-agent-config] [--dry-run]
grain new [name] [--template <template>] [--folder <path>] [--name <name>] [--organization <id>] [--keep-failed] [--checkout] [--no-open] [--no-start]
grain artifact --name <name> --file <html> [--organization <id>] [--folder <path>|--root-folder]
grain artifact --workspace <id> --file <html> [--expected-revision <revision-id>] [--title <title>] [--description <text>]
grain [--workspace <id>] list [--organization <id>] [--folder <path>] [--query <text>]
grain use [workspace-id|--clear] [--dry-run]
grain status
grain exec <command...>
grain app restart
grain app status
grain job start [--timeout-ms <n>] [--request-id <id>] -- <command...>
grain job status <id>
grain job cancel <id>
grain job acknowledge <id> --remote-stopped
grain operation status|cancel --kind exec|promotion --operation <id>
grain shell [--command <command>]
grain start [path] [--foreground] [--restart] [--no-pull] [--continue|--abort] [--no-open] [--debounce-ms <n>] [--push-interval-ms <n>]
grain stop [path]
grain pull [--force]
grain checkout [--dry-run]
grain checkout clone [workspace-id] [path]
grain checkout list
grain checkout remove <workspace-id> [--discard] [--dry-run]
grain checkout clean [--dry-run]
grain push [--force] [--no-reload] [--dry-run] [--title <title>] [--description <description>]
grain push --files <local-path...> [--to <workspace-dir>]
grain history [--limit <n>] [--before <revision-number>] [--revision <id>]
grain checkpoint [-m <message>] [--dry-run]
grain restore <revision-number> [--dry-run]
grain share (--public|--password <pw>|--password-env) [--path <p>] [--name <n>] [--no-reuse] [--dry-run]
grain share list
grain share update <share-id> [--mode off|public|password] [--password <pw>|--password-env] [--regenerate-slug] [--name <name>] [--path <path>] [--dry-run]
grain share revoke <share-id> [--dry-run]
grain open
grain release [--dry-run]
grain mcp
grain mcp install [--claude] [--codex]
grain mcp status [--claude] [--codex]
grain refresh [path]
grain clone [workspace-id] [path]
grain upload <local-path...> [--to <workspace-dir>]
grain doctor [path]
grain upgrade [--target react-vite|nextjs] [--idempotency-key <key>]
grain config inspect | validate --json-input | update --json-input | resolve-tab <id> | open-tab <id>
grain command list | schema <id> | execute --json-input
grain metadata
grain feedback [--kind issue|wish|praise] [--source user|agent] [--tool <name>] <text...>
grain feedback --json-input
grain screenshot [--out <path>] [--desktop <app-name>] [--max-width <pixels>]The grouped forms above are the canonical reference. The parser still accepts these compatibility routes: grain checkouts list, grain checkouts remove <workspace-id>, grain checkout clone, grain checkout clean, and grain open --refresh [path]. Prefer checkout list/remove, clone, release, and refresh [path] in new scripts so the command family matches the documented lifecycle.
Authentication and creation
After installing the Grain app and CLI, run grain setup. It opens browser sign-in when needed, installs the bundled Grain plugin (skill and MCP tools) for detected Claude Code or Codex clients, verifies authentication and MCP startup, and launches Grain signed in. Completed setup is detected by the desktop app automatically.
Use --claude and/or --codex to choose clients explicitly. Repeating setup preserves the previous selection and updates older Grain plugins for Homebrew support. Existing direct MCP entries must be reviewed and removed using the migration command shown by setup before plugin installation. --no-open completes setup without launching the app; grain launch opens it later without requiring a workspace. grain setup --check --json reports readiness, account identity, selected clients, and MCP runtime status without opening a browser, changing client configuration, or launching Grain. It can refresh an existing login to verify it. JSON mode never prompts; sign in interactively first if credentials are missing.
| Command and help | Expected output | Failure messages |
|---|---|---|
login [--signup] opens browser sign-in; --signup starts with account creation. Development profiles use their matching development app. Works without a terminal and in --json mode; waits up to 10 minutes. | Saves revocable CLI authentication in the system credential store. | Unknown login option: <option>; Timed out waiting for browser sign-in.; in JSON mode, a login_required error with reason open_failed when no browser can be opened. |
logout has no flags and removes local credentials after attempting remote revocation. | Confirms that local credentials were removed and whether the remote session was revoked; it also reports when the CLI was already logged out. | Unknown logout option: <option>; a warning says when the remote session could not be confirmed revoked. |
init [path]: --organization, --name, --template, --folder, --keep-failed, --no-open, --no-agent-config, --dry-run. | A static workspace prints Created static workspace <id>; uploading managed content; the app-runtime branch reports that it linked the local root to the workspace. Dry run states that no changes were made. | Not a directory: <path>; an unavailable template or a missing flag value returns a usage error. |
new [name]: --template, --folder, --name, --organization, --keep-failed, --checkout, --no-open, --no-start. | Progress ends by reporting the created path and linked workspace id. | Provide a project name: grain new <name>, or grain new --name <name>.; Checkout-mode workspaces use the canonical checkout path; do not pass a local path with --checkout. |
For a complete single HTML file, artifact --name <name> --file <html> creates a private revision in one call; it does not create a share. To revise that grain, pass its ID explicitly with artifact --workspace <id> --file <html>: it replaces index.html in the latest revision, keeps every other file, and records a new revision instead of creating a duplicate. Pass --expected-revision <revisionId> from the previous result so the command fails with remote_drift, writing nothing, if the grain changed since then; without it only a change landing during the call is detected. --title and --description label the revision. A clean local checkout of the grain is brought up to the new revision, and one with local changes is left alone with a stale_checkout warning. Only the explicit --workspace flag selects an update; GRAIN_WORKSPACE, a linked folder, or grain use never do. Updates never rename or move a grain, so --folder/--root-folder and a different --name are rejected; rename a grain with Rename in the Grain app (there is no CLI rename). Unchanged HTML returns already_correct without a new revision. Only html grains qualify: a runtime grain returns runtime_required and other templates such as wiki return unsupported (use checkout and push instead). For multi-file work, use new --checkout, edit the returned path, then push and checkpoint.
App configuration, commands and inspection
These commands are available when the selected workspace has an executable app runtime. They are separate from file synchronization:
| Command and help | Expected output | Failure messages |
|---|---|---|
config inspect has no flags. | Returns the current manifest, its hash, and the runtime configuration path. | A static workspace reports that runtime configuration is unavailable. |
config validate --json-input and config update --json-input read one JSON request from stdin. | Validation reports whether the proposed patch is valid; update returns the saved manifest and a new hash. | Invalid JSON/schema or a stale expectedHash returns an actionable configuration error. |
config resolve-tab <id> and config open-tab <id> accept one tab id. | Resolve prints the tab definition; open asks the desktop app to open the tab. | An unknown tab id or a workspace without runtime configuration is rejected. |
command list has no flags; command schema <id> accepts one command id. | List returns enabled command IDs and descriptions; schema returns the JSON Schema for one command's arguments. | Unknown commands and static workspaces return a structured error. |
command execute --json-input reads a command-call batch from stdin. | Returns per-command results; inspect each result even when the outer request succeeds. | Invalid arguments, disabled commands and command failures are reported per result. |
metadata has no flags. | Returns workspace metadata for an agent or script. | Usage: grain metadata for extra arguments. |
feedback <text...>: --kind issue|wish|praise (default issue), --source user|agent (default user), --tool <name>; --workspace <id> names the grain; or --json-input with source, kind, text, workspace, toolName. | Sends feedback about Grain to the Grain team and returns feedbackId, redacted (text that looked like a secret was removed) and the workspaceId actually recorded. | Missing text or an unknown kind is invalid_args; more than 10 an hour or 30 a day is rate_limited. |
screenshot: --out <path>, --desktop <app-name>, --max-width <pixels>. | Captures the selected workspace preview, or a named desktop app, and reports the saved path. | Invalid dimensions, an unavailable desktop capture target or a closed app returns an error. |
Selection and status
| Command and help | Expected output | Failure messages |
|---|---|---|
list: --organization, --folder, --query filter the organization tree and workspace names. Without --organization it lists the organization of --workspace <id>, else your only or active organization. | Each workspace line contains id, name, folder, source and sandbox status. JSON output adds organizationSelection, and availableOrganizations when the active organization was the default. | Unknown list option: <option>; Missing value for <flag>; Folder not found: <path>. |
| `use [workspace-id | --clear]: --clearremoves the default and--dry-run` previews a change. | Reports the selected default workspace by name and id, or prints a clear/no-change message. |
status has no command flags. | Lines report loggedIn, workspace, root and sandbox; offline and logged-out states appear as warnings. | Unknown status option: <option>. Workspace and network problems are returned as warnings where possible. |
Runtime and continuous sync
| Command and help | Expected output | Failure messages |
|---|---|---|
exec <command...> has no CLI flags in its usage. Put -- before a child command that begins with a dash. | Streams the child process output and exits with its status. | Usage: grain exec <command...>; static workspaces report This HTML workspace has no compute runtime. |
app restart and app status have no flags. | restart stops the preview server Grain started in the sandbox, starts it again, and waits until its port answers HTTP; status reports whether it answers now. Both print the port and HTTP status. | Usage: grain app restart | grain app status; a failed restart names the cause (for example A process Grain did not start is using port 3000) and points at /tmp/grain-start.log. HTML workspaces report no preview server. |
shell: --command <command> runs one remote shell command; without it, the shell is interactive. | Streams the remote shell session and returns its exit status. | Missing value for --command; JSON mode reports Interactive shell is unavailable with --json. |
start [path]: --foreground, --restart, --no-pull, --continue, --abort, --no-open, --debounce-ms, --push-interval-ms. | Reports the watched root, then prints synced <n> upsert(s), <n> delete(s) after changes. | --continue and --abort are mutually exclusive; invalid timings report Invalid --debounce-ms value or Invalid --push-interval-ms value. |
stop [path] has no flags. | Stops continuous sync for the linked folder. | Usage: grain stop [path]; checkout folders report A checkout folder does not run the continuous sync daemon. |
File movement and checkouts
| Command and help | Expected output | Failure messages |
|---|---|---|
pull: --force accepts intentional replacement when normal reconciliation is not enough. | pulled <n> remote change(s), deleted <n> local path(s), preserved <n> local change(s). | Unknown pull option: <option>. Usage: grain pull [--force]; an active sync client reports that another sync client holds the workspace lease. |
checkout: --dry-run resolves a checkout path without creating it. | Reports reuse, a planned path, or the workspace id and completed checkout path. | Unknown checkout option: <option>; Use --dry-run only once.; incomplete checkout paths must be removed before retrying. |
checkout list has no flags. checkout remove <workspace-id> accepts --discard and --dry-run. | List prints workspace id, path and dirty when applicable. Remove prints Removed checkout or Would remove checkout. | Checkout <id> has unpushed changes. and the action Push the checkout or rerun with --discard. |
push: --force, --no-reload, --dry-run. | pushed <n> path(s); dry run reports proposed upserts and deletes. | Remote drift reports The remote workspace changed since this folder was last synchronized.; an active operation reports a sync-client lease error. |
upload <local-path...> (also push --files): Without --to, uploads go to /project/workspace/uploads, outside the app folder, so they never sync into checkouts. --to <workspace-dir> is relative to the app folder (/project/workspace/app for app grains); run from inside the checkout, a new upload is copied into it at the same place and recorded, so the next push sees no drift. | Prints the compressed archive size while extracting, then Uploaded <destination> for each root. | Usage: grain upload <local-path...> [--to <workspace-dir>]; Missing value for --to; static uploads must run from a linked checkout. |
clone [workspace-id] [path] has no flags. | Reports the cloned workspace id and destination path. | Usage: grain clone [workspace-id] [path]; the target must be a directory and empty; an empty organization reports No workspaces found in this organization. |
History and publication
| Command and help | Expected output | Failure messages |
|---|---|---|
checkpoint: -m <message> labels the revision and --dry-run previews it. | checkpointed revision #<n> or checkout already matches the latest revision. | Missing checkpoint message. Usage: grain checkpoint [-m <message>] [--dry-run]; Use --dry-run only once. |
restore <revision-number>: --dry-run previews paths and the safety snapshot. | restored revision #<old> as #<new>. | Usage: grain restore <revision-number> [--dry-run]; Use --dry-run only once. |
share: create accepts --public, --password, --password-env, --path, --name, --no-reuse, --dry-run; update accepts --mode, --password, --password-env, --regenerate-slug, --name, --path, --dry-run; revoke accepts --dry-run; list has no flags. | Create and update print Share: <url> and Canonical: <url>. List prints each id, mode and URLs or No shares. Revoke prints Revoked: <url>. | --public and --password are mutually exclusive.; --mode must be off, public, or password.; list rejects mutation flags; update and revoke require a share id. |
open has no flags and opens the selected workspace in the desktop app. | Reports that the desktop app was asked to open the selected workspace id. | Usage: grain open; if the app is closed, the action asks you to start it and retry. |
refresh [path] has no flags and asks the desktop app to refresh previews. | Reports that the desktop app was asked to refresh previews for the selected workspace id, then whether the preview server answers (preview.reachable). When it does not, a preview_unreachable warning says to run grain app restart. | Usage: grain refresh [path]; if the app is closed, the action asks you to start it and retry. |
release: --dry-run previews removal of a clean checkout. | Removed checkout <id> at <path> or Would remove checkout <id> at <path>. | Dirty, sync-mode or invalid checkouts are not removed; unknown flags report Unknown release option: <option>. |
For a password share, prefer the non-argv flow so the secret is not exposed through process inspection or shell history:
read -r -s GRAIN_SHARE_PASSWORD
printf '\n'
export GRAIN_SHARE_PASSWORD
grain --workspace <workspace-id> share --password-env
unset GRAIN_SHARE_PASSWORD--password <value> remains available for disposable test values. --password-env requires GRAIN_SHARE_PASSWORD to be set and works for share creation and password-mode updates.
Jobs and operations
Use exec for a short foreground command and job start for a bounded detached worker. A job returns an operation ID; use job status, job cancel or job acknowledge with that ID. Jobs are stored in the local CLI profile and are not a cross-host scheduler. See App Runtime for time, output and unknown-worker recovery.
grain --json --workspace <workspace-id> job start --timeout-ms 900000 -- npm test
grain --json --workspace <workspace-id> job status <operation-id>Agent server, diagnostics and promotion
| Command and help | Expected output | Failure messages |
|---|---|---|
mcp has no flags and starts the Model Context Protocol server over standard input and output. | The process stays attached to stdio and responds to MCP requests rather than printing human progress. | Usage: grain mcp when extra arguments are supplied. |
doctor [path] has no flags. | Prints CLI version, authentication, API, session expiry, workspace link and remote root. | Unknown doctor option: <option> or Usage: grain doctor [path] for more than one path. |
upgrade: `--target react-vite | nextjs, --idempotency-key`. | Returns promotion details; it does not update the CLI binary. Poll operation status until runtimeReady: true. |
JSON mode and API boundary
Put global options before the command, for example grain --json --workspace <id> status. JSON mode emits one non-interactive envelope: {"ok":true,"data":{...}} or {"ok":false,"error":{"code":"...","message":"...","next_action":"..."}}. Remote command output is inside the envelope; inspect both the envelope and command-specific result.
The CLI’s control-plane session is not the same as the external HTTP API. Use the API page for mat_ and mfd_ credentials, SQL, registered commands and Front Door; use this CLI for artifact synchronization, history, promotion and shares.
Frequently asked questions
How do I get JSON from the Grain CLI?
Add the global --json option before the command. It emits exactly one envelope and avoids prompts.
How do I select a Grain workspace explicitly?
Add global --workspace <id> before the command, or set a machine default with the use command.
Does the upgrade command update the Grain CLI?
No. It promotes a pushed static workspace to an app runtime. The desktop app refreshes its bundled CLI when the app updates.
When should I use force on push or pull?
Use it only when you intentionally accept the overwrite described by the command. Normal remote drift should be pulled and reviewed before pushing again.