> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gavai.io/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI overview

> Install the gavai CLI, authenticate once, drive a workspace from a terminal or CI runner.

export const PageMeta = ({audience, readingTime, prereqs, level}) => {
  const items = [audience && ({
    key: "audience",
    label: "AUDIENCE",
    value: audience
  }), readingTime && ({
    key: "time",
    label: "READ",
    value: readingTime
  }), level && ({
    key: "level",
    label: "LEVEL",
    value: level
  }), prereqs && prereqs.length > 0 && ({
    key: "prereqs",
    label: "PREREQS",
    value: prereqs.join(" · ")
  })].filter(Boolean);
  if (items.length === 0) return null;
  return <div className="gv-page-meta" role="group" aria-label="Page metadata">
      {items.map(item => <div className="gv-page-meta__item" key={item.key}>
          <span className="gv-page-meta__label">{item.label}</span>
          <span className="gv-page-meta__value">{item.value}</span>
        </div>)}
    </div>;
};

export const ErrorTable = ({errors}) => <div className="gv-error-table" role="table" aria-label="Errors and fixes">
    <div className="gv-error-table__head" role="row">
      <span className="gv-error-table__col-head" role="columnheader">Error</span>
      <span className="gv-error-table__col-head" role="columnheader">Cause</span>
      <span className="gv-error-table__col-head" role="columnheader">Fix</span>
    </div>
    {errors.map((err, i) => <div className="gv-error-table__row" role="row" key={i}>
        <code className="gv-error-table__code" role="cell">{err.code}</code>
        <div className="gv-error-table__cause" role="cell">{err.cause}</div>
        <div className="gv-error-table__fix" role="cell">{err.fix}</div>
      </div>)}
  </div>;

export const ExpectedOutput = ({caption = "Expected output", children}) => <div className="gv-output">
    <div className="gv-output__label">
      <svg className="gv-output__icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <polyline points="15 10 20 15 15 20" />
        <path d="M4 4v7a4 4 0 0 0 4 4h12" />
      </svg>
      <span>{caption}</span>
    </div>
    <div className="gv-output__body">{children}</div>
  </div>;

export const Path = ({icon, title, href, children}) => <a className="gv-choose-path__card" href={href}>
    {icon && <span className="gv-choose-path__icon" aria-hidden="true">
        <Icon icon={icon} />
      </span>}
    <h4 className="gv-choose-path__title">{title}</h4>
    {children && <p className="gv-choose-path__body">{children}</p>}
    <svg className="gv-choose-path__arrow" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      <line x1="7" y1="17" x2="17" y2="7" />
      <polyline points="7 7 17 7 17 17" />
    </svg>
  </a>;

export const ChoosePath = ({eyebrow = "CHOOSE YOUR PATH", label, children}) => <section className="gv-choose-path">
    <div className="gv-choose-path__header">
      <Eyebrow label={eyebrow} />
      {label && <h3 className="gv-choose-path__label">{label}</h3>}
    </div>
    <div className="gv-choose-path__grid">{children}</div>
  </section>;

export const FeatureTable = ({columns, rows, featuredRow, caption, variant}) => <div className={`gv-feature-table ${variant ? `gv-feature-table--${variant}` : ""}`}>
    <table>
      {caption && <caption className="gv-feature-table__caption">{caption}</caption>}
      <thead>
        <tr>
          {columns.map(col => <th key={col.key} scope="col">
              <span className="gv-feature-table__header">{col.label}</span>
            </th>)}
        </tr>
      </thead>
      <tbody>
        {rows.map((row, i) => {
  const rowClass = [i === featuredRow ? "gv-feature-table__row--featured" : "", row.chosen ? "gv-feature-table__row--chosen" : ""].filter(Boolean).join(" ");
  return <tr key={i} className={rowClass}>
              {columns.map(col => {
    const v = row[col.key];
    if (variant === "tradeoff" && Array.isArray(v)) {
      const pill = col.key === "pros" ? "gv-feature-table__pill--pro" : col.key === "cons" ? "gv-feature-table__pill--con" : "";
      return <td key={col.key}>
                      {v.map((item, j) => <span key={j} className={`gv-feature-table__pill ${pill}`}>{item}</span>)}
                    </td>;
    }
    return <td key={col.key}>{v}</td>;
  })}
            </tr>;
})}
      </tbody>
    </table>
  </div>;

export const ArrowItem = ({children}) => <li className="gv-arrow-item">
    <span className="gv-arrow-item__arrow" aria-hidden="true">{"↳︎"}</span>
    <span className="gv-arrow-item__content">{children}</span>
  </li>;

export const ArrowList = ({children}) => <ul className="gv-arrow-list">{children}</ul>;

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="CLI" />

<PageMeta audience="Developers and CI operators" readingTime="6 min" prereqs={["gavAI account", "Bun or npm"]} level="Beginner" />

The `gavai` CLI is a thin wrapper around the public API. You can use it to fetch and patch page documents from a terminal, publish drafts from CI, invite teammates, and serve the Builder MCP server over stdio. By the end of this page you'll have the binary installed, an authenticated session on disk, and a map of the commands available to you.

<ChoosePath label="Pick the path that matches your setup">
  <Path icon="terminal" title="I want to iterate on a page locally" href="/cli/commands/get">
    Fetch a page document, patch it on disk, publish when it looks right.
  </Path>

  <Path icon="server" title="I'm wiring up CI" href="/cli/commands/publish">
    Promote drafts to live from a build pipeline once tests pass.
  </Path>
</ChoosePath>

## Install

<CodeGroup>
  ```bash bun theme={null}
  bun add -g @gavai/gavai-cli
  ```

  ```bash npm theme={null}
  npm i -g @gavai/gavai-cli
  ```
</CodeGroup>

The `gavai` binary lands on `$PATH`. Verify with `gavai --version`.

## Authenticate

```bash theme={null}
gavai login
```

The CLI opens `console.gavai.app` in your default browser, runs OAuth 2.1 with PKCE, and writes tokens to `~/.config/gavai/credentials.json`. Subsequent commands refresh the access token silently — you re-run `gavai login` only when the refresh token itself is revoked.

Confirm the session:

```bash theme={null}
gavai whoami
```

<ExpectedOutput>
  ```text theme={null}
  gavin@acme.io (workspace: acme)
  ```
</ExpectedOutput>

## A typical workflow

Fetch a page, patch a headline, publish the draft:

```bash theme={null}
gavai get page_01HXR8K2M4 > page.json
gavai patch page_01HXR8K2M4 --ops patches.json
gavai publish page_01HXR8K2M4
```

Three calls, three resource transitions: draft is read, draft is mutated, live is promoted. Every command in the CLI follows the same shape — workspace-scoped, JSON in, JSON out.

## What the CLI is for

<ArrowList>
  <ArrowItem>**Local iteration** — `gavai get` / `gavai patch` loops while shaping a page document by hand</ArrowItem>
  <ArrowItem>**CI publishes** — promote drafts to live from a build pipeline once tests pass</ArrowItem>
  <ArrowItem>**MCP host bridge** — `gavai mcp` exposes the Builder MCP server over stdio for Claude Desktop and other MCP clients; see [Builder MCP](/mcp/builder-mcp)</ArrowItem>
  <ArrowItem>**One-off API calls** — `gavai public-api` for ad-hoc requests without writing an SDK script</ArrowItem>
</ArrowList>

It is not a replacement for the Builder web app. Visual page composition still happens at [console.gavai.app](https://console.gavai.app). Non-technical users should not be pointed at the CLI.

## Credentials file

Tokens live at `~/.config/gavai/credentials.json`. Treat it like an SSH key — back it up if you must, but don't edit it by hand. Delete the file and re-run `gavai login` if it becomes stale or corrupted.

```json credentials.json theme={null}
{
  "access_token": "gst_live_8KdYx2...",
  "refresh_token": "grt_live_qPm91J...",
  "expires_at": "2026-05-11T18:00:00Z",
  "active_workspace": "acme",
  "endpoint": "https://api.gavai.app"
}
```

`access_token` is the short-lived bearer (`gst_*`) the CLI sends on every API call. `refresh_token` renews it transparently. `active_workspace` is the slug that scopes commands like `gavai get` and `gavai patch`. `endpoint` is the API base URL — leave it alone unless gavAI support tells you otherwise.

## Commands

<FeatureTable
  columns={["Command", "Purpose"]}
  rows={[
["[`gavai login`](/cli/commands/login)", "Authenticate via OAuth 2.1 + PKCE and store credentials locally"],
["[`gavai logout`](/cli/commands/logout)", "Delete the local credentials file"],
["[`gavai whoami`](/cli/commands/whoami)", "Print the signed-in user and active workspace"],
["[`gavai tenant`](/cli/commands/tenant)", "List workspaces the signed-in user can access"],
["[`gavai get <id>`](/cli/commands/get)", "Fetch a page document as JSON from the active workspace"],
["[`gavai patch <id>`](/cli/commands/patch)", "Apply RFC 6902 JSON Patch operations to a draft"],
["[`gavai publish <id>`](/cli/commands/publish)", "Promote the current draft to live"],
["[`gavai invite`](/cli/commands/invite)", "Send a workspace invitation by email"],
["[`gavai public-api`](/cli/commands/public-api)", "Make an authenticated request to the public API"],
["[`gavai mcp`](/cli/commands/mcp)", "Serve the Builder MCP server over stdio"],
["[`gavai dmca`](/cli/commands/dmca)", "Print gavAI's registered DMCA agent number"],
]}
/>

## Exit codes

Every command returns one of these. CI scripts can branch on them.

<ErrorTable
  errors={[
{ code: "0", cause: "Success.", fix: "—" },
{ code: "1", cause: "Generic error.", fix: "Re-run with `--verbose` to see the underlying message." },
{ code: "2", cause: "Invalid arguments.", fix: "Check `gavai <command> --help` for the expected shape." },
{ code: "3", cause: "Authentication required.", fix: "Run `gavai login`." },
{ code: "4", cause: "Authorization denied — insufficient scope.", fix: "Mint a token with the required scope in Settings → API keys." },
{ code: "5", cause: "Resource not found.", fix: "Verify the ID and the active workspace with `gavai whoami`." },
{ code: "6", cause: "Conflict — concurrent edit on the same page.", fix: "Re-fetch with `gavai get`, re-apply the patch, retry." },
{ code: "7", cause: "Network or API server error.", fix: "Retry with backoff. Check status at status.gavai.app." },
]}
/>

<Card title="gavai login" href="/cli/commands/login">
  Start here — authenticate the CLI with your gavAI account.
</Card>
