CLI reference

Every graft command, grouped by when you use it.

Reference29 min read

Look up a command here. In a terminal, graft --help lists the commands and graft <command> --help shows one command's usage and flags. To read graft's output from a script or an agent, see Script the CLI.

This page is generated from the table the CLI's own help reads, so the two always match. graft migrate and graft merge only report what they would change until you pass --apply.

Set up a project

graft create

Make a new site: a template, a database, Studio and (by default) a Next.js app, installed and ready. npm create graft runs this. It asks a few questions; flags answer them, and --yes takes the defaults for the rest.

graft create [dir] [--template <name>] [--edit online|code] [--database new|none|<url>] [--framework next|nuxt|sveltekit|none] [--yes] [--no-install] [--no-input]
FlagWhat it does
--template <name>blog, docs, shop, portfolio or blank (default: blog).
--edit online|codeonline keeps content in the database, edited in Studio (default); code keeps it as files in git.
--database new|none|<url>new makes a free Postgres to claim later; none compiles content to a file (with --edit code); or your own Postgres 13+ URL.
--framework next|nuxt|sveltekit|nonenext for a Next.js site that shows every collection (default); nuxt or sveltekit for the same site in Nuxt or SvelteKit; none for only the content.
-y, --yesUse the default for every question a flag did not answer.
--no-installWrite the files and stop before installing.
--no-inputNever stop to ask a question. Same as GRAFT_PROMPT_DISABLED.

Needs no database. May ask questions on a terminal; --no-input never does.

graft create my-site --template docs --edit code --database none --yes

graft init

Scaffold a Graft project in dir (default: here). The static engine is the default: no database and no environment variables.

graft init [dir] [--static] [--postgres] [--json]
FlagWhat it does
--staticStatic engine: no database, no env (the default).
--postgresPostgres engine: operational data, typed functions, database branching.
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

graft doctor

Check this project's setup: the database, its schema, content, the index and Studio sign-in. Prints a checklist and the one thing to do next.

graft doctor [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Uses DATABASE_URL when it is set, and works without it. Prints its answer as JSON with --json.

Exit codeWhat it means
0Everything is set up.
1Something is missing; the output says what to do next.

graft add

Copy an owned primitive (and the primitives it needs) from the registry into graft/, and regenerate the graft/ barrel. graft.config.ts needs no edit. With no item, list the primitives you can add.

graft add [item...] [--dry-run] [--overwrite] [--json]
FlagWhat it does
--dry-runShow what would change without changing it.
--overwriteReplace files that differ from the registry's.
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

graft add comments --dry-run

Write and check content

graft compile

Check every content file and build the index, once.

graft compile [--branch <id>] [--prune-unknown] [--json]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--prune-unknownRemove index entries in collections the schema no longer has (default: refuse, which protects a shared DATABASE_URL).
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft compile --json > changes.json

graft dev

Watch content/ and graft.config.ts, and compile on every change.

graft dev [--branch <id>]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).

Needs DATABASE_URL on the Postgres engine; none on the static engine. Runs until you stop it.

graft check

Check every content file the way a compile or a publish would: the schema, slug uniqueness, MDX safety, and references to documents that don't exist. Needs no database.

graft check [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

Exit codeWhat it means
1At least one file has a problem; each is listed.

graft content

List every document; the same as graft content list.

graft content [--branch <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft content list

List documents with their slug, title and file. Reads the content files, or the database in a project whose content is kept there.

graft content list [collection] [--branch <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft content get

Print one document: its frontmatter, then its body, the way the file reads. With --json, the collection, slug, file path, data and body.

graft content get <collection/slug> [--branch <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft content get posts/launch > launch.mdx

Search with words, "quoted phrases", or and -exclusions. Searches the compiled index, so results are as fresh as the last compile.

graft content search <query...> [--branch <id>] [--collection <name>] [--limit <n>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--collection <name>Search one collection (default: all).
--limit <n>Most matches to print, best first (default: 20).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft content search pricing "free tier" --collection pages

graft content write

Write a document from an MDX file (--file), or from frontmatter as JSON (--data) and an optional body (--body-file). It is checked against the collection's schema and compiled, exactly as an agent's write_content is. A project whose content is kept in the database saves it as a draft.

graft content write <collection/slug> [--branch <id>] [--file <doc.mdx|->] [--data <json>] [--body-file <file|->] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--file <doc.mdx|->The whole document as an MDX file; - reads it from stdin.
--data <json>The frontmatter as a JSON object; - reads it from stdin.
--body-file <file|->The body, with --data; - reads it from stdin.
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft content get posts/launch > launch.mdx
graft content write posts/launch --file launch.mdx

graft content set

Set one frontmatter field on each document named, each written as graft content write would. One document that fails does not stop the others. --value null removes the field.

graft content set <collection> <slug...> [--branch <id>] [--field <name>] [--value <json>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--field <name>The frontmatter field to set.
--value <json>The new value as JSON; text that is not JSON is taken as a string.
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

Exit codeWhat it means
1At least one document was not saved; each is listed.
graft content set posts launch roadmap --field tags --value '["2026"]'

graft content delete

Delete a document. The first run files an approval and exits 3 with its id. After a person runs graft approve <id>, run it again with --approval <id>.

graft content delete <collection/slug> [--branch <id>] [--approval <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--approval <id>The approval a person granted for this delete.
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft compilations

List recent compiles and what each one changed.

graft compilations [--branch <id>] [--json]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft asset put

Upload a binary to the asset store (S3_* env) and print the frontmatter reference. The key defaults to assets/<file name>. A key that already holds a file is refused with ASSET_EXISTS, because the store keeps no version history.

graft asset put <file> [key] [--overwrite] [--json]
FlagWhat it does
--overwriteReplace the file already stored under the key.
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

graft asset put ./hero.png assets/hero.png

graft drafts

List unpublished drafts, for content kept in the database.

graft drafts [--branch <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft drafts publish

Publish drafts by path (from graft drafts). On a site that reviews changes, this asks for a review instead. A path someone else published since is refused with CONTENT_CONFLICT; --resolve <path>=mine or =theirs settles it.

graft drafts publish <path...> [--branch <id>] [--message <text>] [--resolve <path>=mine|theirs]... [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--message <text>The commit or review title.
--resolve <path>=mine|theirsSettle a conflict on one path. Repeat it for more than one.
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft drafts discard

Throw away drafts, back to the published version.

graft drafts discard <path...> [--branch <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL. Prints its answer as JSON with --json.

Look into the project

graft schema

Print each collection's fields, and the typed functions.

graft schema [collection] [--branch <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft fn

List the typed functions.

graft fn [--branch <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft fn describe

Print one function's input schema and access rules.

graft fn describe <name> [--branch <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft fn run

Run a function through the same checks as POST /api/fn/<name>: input validation, access rules, rate limits, the audit log, and the approval a destructive function needs (exit 3, then --approval <id>).

graft fn run <name> [--branch <id>] [--input <json|->] [--approval <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--input <json|->The input as a JSON object; - reads it from stdin (default: {}).
--approval <id>The approval a person granted for this call.
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft explain

Explain an error code: what it means and how to recover.

graft explain <code> [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

graft explain SCHEMA_VALIDATION_FAILED

graft mcp tools

List the MCP tools an agent sees for this project.

graft mcp tools [--branch <id>] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft mcp call

Call any MCP tool by name, as an agent would.

graft mcp call <tool> [--branch <id>] [--input <json|->] [--json] [--remote] [--local]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--input <json|->The tool's input as a JSON object; - reads it from stdin (default: {}).
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL on the Postgres engine; none on the static engine. Prints its answer as JSON with --json.

graft mcp call list_content --input '{"collection":"posts"}'

Run Graft

graft mcp

Serve the project MCP over stdio: content and function tools, for .mcp.json and other local agents. On the Postgres engine it needs DATABASE_URL.

graft mcp [--branch <id>] [--elicit-approvals]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--elicit-approvalsAsk this terminal's operator to decide a destructive call, instead of failing with an id for graft approve.

Needs DATABASE_URL on the Postgres engine; none on the static engine.

graft serve

Run the headless Graft runtime over HTTP: POST /api/fn/<name>, POST /api/mcp, GET /api/content/v1/*, GET /healthz. This is what a self-hosted container runs.

graft serve [--branch <id>] [--port <n>] [--host <host>] [--studio] [--max-body-bytes <n>]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--port <n>Port to listen on (default: 3903). Same as PORT.
--host <host>Host to listen on (default: 127.0.0.1, this computer only). Same as HOST.
--studioServe Studio at /studio too. Same as GRAFT_STUDIO.
--max-body-bytes <n>Largest request body any route reads (default: 1 MiB, and 32 MiB for MCP). Same as GRAFT_MAX_BODY_BYTES.

Needs DATABASE_URL. Runs until you stop it.

graft studio

Run Studio: edit content, approve or deny actions, and read the OpenAPI document. Without a database, saves go to your files.

graft studio [--branch <id>] [--port <n>] [--host <host>]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--port <n>Port to listen on (default: 4983). Same as GRAFT_STUDIO_PORT.
--host <host>Host to listen on (default: 127.0.0.1, this computer only). Same as HOST.

Uses DATABASE_URL when it is set, and works without it. Runs until you stop it.

graft studio invite

Print a sign-in link for someone without a GitHub account. It is signed with GRAFT_STUDIO_SECRET and expires after --days. To give someone an account instead, invite them from Studio's Settings.

graft studio invite <email> [--role <role>] [--name <name>] [--days <n>] [--url <url>] [--json]
FlagWhat it does
--role <role>viewer, contributor, editor (default) or admin.
--name <name>The name commits are attributed to.
--days <n>How long the link works, 1 to 30 (default: 7).
--url <url>Where Studio is served. Same as GRAFT_STUDIO_URL.
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

graft studio setup-code

Print the code a hosted Studio asks for before it creates the owner account. It comes from GRAFT_STUDIO_SECRET, so run it with the site's settings.

graft studio setup-code

Needs no database.

Keep content in the database

graft content import

For a project with content = { source: "database" }: load the files under the content directory into the database. Every file is checked first. Only documents with nothing published come in: a published document is never overwritten, and a missing file deletes nothing. To change published content, use Studio or graft push.

graft content import [--branch <id>] [--dry-run] [--json]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--dry-runShow what would change without changing it.
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft pull

For a project with content = { source: "database" }: write what is published into the files under the content directory. A file you changed since your last pull is kept for graft push. One changed both here and in the database is left as it is and listed, unless --force takes the published version.

graft pull [doc...] [--branch <id>] [--only <doc>]... [--force] [--json]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--only <doc>Only this document, like posts/launch (same as naming it as an argument). Repeat it for more than one.
--forceSettle a conflict: pull takes the published version, push publishes yours.
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft pull posts/launch

graft push

Publish the files you changed since your last graft pull, checked as graft compile checks them. Refused, with nothing written, if someone published one of those documents since your pull: pull first. --force publishes over it.

graft push [doc...] [--branch <id>] [--only <doc>]... [--dry-run] [--force] [--json]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--only <doc>Only this document, like posts/launch (same as naming it as an argument). Repeat it for more than one.
--dry-runShow what would change without changing it.
--forceSettle a conflict: pull takes the published version, push publishes yours.
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft mirror

Commit everything published since the last run to the GitHub repository in GRAFT_GITHUB_REPO, as one commit. Run it on a schedule, or let graft serve do it when graft.config.ts sets content.mirror.

graft mirror [--branch <id>] [--json]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

Manage the database

graft db migrate

Apply the Postgres schema migrations that ship with @usegraft/db. This is the first command a Postgres-engine project runs.

graft db migrate [--dry-run] [--json]
FlagWhat it does
--dry-runList pending schema migrations without applying them.
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft db claim

Keep the free database graft create --database new made. Prints the link to claim it (a free Neon account), waits until you have, then asks for the new connection string and saves it to .env. Claiming changes the password, so the old DATABASE_URL stops working.

graft db claim [--url <postgres-url>] [--no-input]
FlagWhat it does
--url <postgres-url>The claimed database's connection string, instead of the question.
--no-inputNever stop to ask a question. Same as GRAFT_PROMPT_DISABLED.

Needs no database. May ask questions on a terminal; --no-input never does.

graft migrate

Show pending content and data migrations. --apply runs them and records each in the ledger. A concurrent --apply on the same branch waits for this one and then skips what it applied.

graft migrate [--branch <id>] [--apply] [--json]
FlagWhat it does
-b, --branch <id>Content branch to work on (default: main).
--applyMake the change (without it, only report what would change).
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft harden

Grant an existing Postgres role the runtime privilege set: it can request and consume approvals but never decide them.

graft harden <role> [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

Approve actions

graft approvals

List actions waiting for a person to approve them.

graft approvals [--json] [--remote] [--local]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.
--remoteWork on the hosted site in GRAFT_URL, or the one graft login saved.
--localWork on the project in this folder, even when GRAFT_URL is set.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft approve

Approve an action; the caller then repeats its call with the id.

graft approve <approval-id> [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft deny

Deny an action.

graft deny <approval-id> [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

Work with preview branches

graft branch

List branches.

graft branch [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft branch create

Create a preview branch.

graft branch create <name> [--from <parent>] [--backend overlay|neon] [--json]
FlagWhat it does
--from <parent>Branch to fork from (default: main).
--backend overlay|neonoverlay (default) shares the parent's rows; neon forks a physical Neon branch.
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft branch drop

Delete a branch: purge an overlay's rows, or delete a Neon fork.

graft branch drop <name> [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

graft merge

Merge a branch: replay its migrations, move its data, recompile.

graft merge <branch> [--into <target>] [--apply] [--json]
FlagWhat it does
--into <target>Branch to merge into (default: main).
--applyMake the change (without it, only report what would change).
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs DATABASE_URL. Prints its answer as JSON with --json.

Keep CI cheap

graft ci changes

Print content-only=true when every changed file is content, and write it to $GITHUB_OUTPUT for later steps to read.

graft ci changes [--base <ref>] [--json]
FlagWhat it does
--base <ref>Git ref to compare against (default: the pull request's base, the host's previous deploy, or the commit a push started from).
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

graft ci skip-build

Exit 0 when the deploy can be skipped: only content changed, and the site already serves it from the database. Never skips on the previous commit alone.

graft ci skip-build [--base <ref>] [--json]
FlagWhat it does
--base <ref>Git ref to compare against (default: the pull request's base, the host's previous deploy, or the commit a push started from).
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

Exit codeWhat it means
0Skip the build.
1Build.

graft ci init

Write a workflow that runs graft check on content changes.

graft ci init [--overwrite]
FlagWhat it does
--overwriteReplace the workflow file if it exists.

Needs no database.

Work with a hosted site

graft login

Check a token against a hosted Graft site and save it, so commands that take --remote and graft api can use it. Make the token in Studio under Developer, Agents. It is read from stdin, or asked for without echoing it, never taken as an argument. No token can approve or deny an action.

graft login <url> [--with-token] [--json] [--no-input]
FlagWhat it does
--with-tokenRead the token from stdin (the default when stdin is not a terminal).
--jsonPrint one JSON object on stdout; messages go to stderr.
--no-inputNever stop to ask a question. Same as GRAFT_PROMPT_DISABLED.

Needs no database. Prints its answer as JSON with --json. May ask questions on a terminal; --no-input never does.

printf %s "$TOKEN" | graft login https://cms.example.com --with-token

graft logout

Remove a site's saved token (default: the default site). The token itself keeps working until someone revokes it in Studio under Developer, Agents.

graft logout [url] [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

graft auth status

List every site the CLI can reach, from graft login and GRAFT_URL, and check each token against the site. Tokens are shown by their first characters only.

graft auth status [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

Exit codeWhat it means
0At least one token works, or --json was given.
4No token works, or there is none.

graft auth token

Print a site's saved token, for piping into another tool.

graft auth token [url]

Needs no database.

graft api

Call a route on the site in GRAFT_URL, or the one graft login saved. A path is under /api/studio/v1 unless it starts with /api/fn or /api/content/v1. Fields make the request a POST with a JSON body; with -X GET or --paginate they become the query string. Prints the answer; with no path, lists the routes.

graft api [path] [--method <method>] [--raw-field <key=value>]... [--field <key=value>]... [--header <name: value>]... [--input <file|->] [--include] [--paginate] [--slurp] [--yes] [--no-input]
FlagWhat it does
-X, --method <method>The HTTP method (default: GET, or POST when there are fields or --input, unless --paginate).
-f, --raw-field <key=value>A field whose value is sent as a string. Repeat it for more than one.
-F, --field <key=value>A field whose value is typed: true, false, null and numbers as JSON, @file and @- as the file's text. Repeat it for more than one.
-H, --header name: value>An extra request header. Repeat it for more than one.
--input <file|->Send this file as the body; - reads stdin.
-i, --includePrint the status line and headers before the body.
--paginateGET every page, following nextCursor, and print each one.
--slurpWith --paginate, print every page as one JSON array once the last arrives.
-y, --yesSend -X DELETE without asking first.
--no-inputNever stop to ask a question. Same as GRAFT_PROMPT_DISABLED.

Needs no database. May ask questions on a terminal; --no-input never does.

graft api workspace
graft api entries -X GET -f collection=posts -F limit=20
graft api entries -f collection=posts --paginate
graft api entries --paginate --slurp
graft api -X PUT entry --input entry.json

Learn the CLI

graft help

Show help for a command or a topic.

graft help [command-or-topic...]

Needs no database.

graft help branch create
graft help exit-codes

graft commands

List every command; --json describes each one and its flags.

graft commands [--json]
FlagWhat it does
--jsonPrint one JSON object on stdout; messages go to stderr.

Needs no database. Prints its answer as JSON with --json.

Exit codes

Every command exits with one of these, unless its entry above lists its own.

Exit codeWhat it means
0It worked.
1It failed; the error says why and what to do.
2The command was called wrong: an unknown command or flag, or a missing argument. Nothing ran.
3A person has to approve the action first. Run graft approve <id>, then repeat it.
4The credential is missing, wrong, or lacks the access this needs.

Environment variables

These change how the CLI behaves. Your project's own settings are on the Configuration page.

VariableWhat it does
DATABASE_URLThe Postgres database. Read from the environment, or from .env in this folder or any folder above it.
GRAFT_DEBUGSet to 1 to print the stack trace of an unexpected error.
GRAFT_URLA hosted site for commands that take --remote; when set, they use it without the flag. Read from the environment only, never from .env.
GRAFT_TOKENThe token for GRAFT_URL, instead of the one graft login saved.
XDG_CONFIG_HOMEWhere graft login keeps tokens, under graft/hosts.json (default: ~/.config).
GRAFT_PROMPT_DISABLEDSet to 1 to never ask a question, as --no-input does.
GRAFT_NO_UPDATE_CHECKSet to 1 to never check npm for a newer Graft. Also read from .env.
CIWhen set, the update check is off.
NO_COLORWhen set, never print color.
FORCE_COLORWhen set, print color even when the output is not a terminal.
CLICOLORSet to 0 to turn color off on a terminal.
CLICOLOR_FORCESame as FORCE_COLOR.
TERMdumb turns color off.
XDG_CACHE_HOMEWhere the update check caches its answer, under graft/ (default: ~/.cache).
npm_config_registryThe npm registry the update check asks.

Which commands need a database

Graft reads .env from the current folder or any folder above it.

  • Always need DATABASE_URL: graft compilations, graft drafts, graft drafts publish, graft drafts discard, graft serve, graft content import, graft pull, graft push, graft mirror, graft db migrate, graft migrate, graft harden, graft approvals, graft approve, graft deny, graft branch, graft branch create, graft branch drop, graft merge.
  • Need it on the Postgres engine only: graft compile, graft dev, graft content, graft content list, graft content get, graft content search, graft content write, graft content set, graft content delete, graft schema, graft fn, graft fn describe, graft fn run, graft mcp tools, graft mcp call, graft mcp.
  • Use it when it is set: graft doctor, graft studio.
  • Never need it: graft create, graft init, graft add, graft check, graft asset put, graft explain, graft studio invite, graft studio setup-code, graft db claim, graft ci changes, graft ci skip-build, graft ci init, graft login, graft logout, graft auth status, graft auth token, graft api, graft help, graft commands.