# Agents & automation

> Run the Shelve CLI from CI, scripts, and AI coding agents with global flags, JSON output, and non-interactive guards.

The Shelve CLI is designed to work in **CI**, **shell scripts**, and **AI agent shells** (Cursor, Claude Code, Codex, …) without hanging on prompts or polluting logs with spinners.

<callout type="warning">

Prefer **shelve run -- <cmd>** over **shelve pull**. `run` keeps secrets in the child process memory only. `pull` writes plaintext to disk where agents can read them.

</callout>

## Recommended workflow

1. Run **shelve doctor --json** (or `shelve doctor`) to validate setup.
2. Set context once with environment variables (no `shelve login` prompt required):```bash [terminal]
export SHELVE_TOKEN="shlv_…"
export SHELVE_TEAM_SLUG="my-team"
export SHELVE_PROJECT="my-app"
export SHELVE_DEFAULT_ENV="development"   # optional
export SHELVE_URL="https://app.shelve.cloud" # optional
```
3. Run **shelve init** once per repo (agent ignore files + `.gitignore` block).
4. Inject secrets with **shelve run -- pnpm dev** (or your command).
5. Use **--json** when a script needs to parse output.
6. Use **--non-interactive** (or rely on agent/CI auto-detection) so missing flags fail fast.

## Global flags

These flags can appear **before or after** the subcommand (for example `shelve run --json -- pnpm dev` or `shelve --json config`):

<field-group>
<field name="--json" type="boolean">

Success → JSON on **stdout**: `{ "ok": true, "command"?: string, "data"?: object }`.<br />


Errors → JSON on **stderr**: `{ "ok": false, "error": { "code", "message", "status"?, "hint"? } }`.<br />


Secret **values are never** included in JSON output.

</field>

<field name="--quiet" type="boolean" alias="-q">

Suppress clack intro/outro/spinners. Minimal text output only.

</field>

<field name="--yes" type="boolean" alias="-y">

Skip confirmation prompts (`pull`, `push` when `confirmChanges` is enabled, etc.).

</field>

<field name="--non-interactive" type="boolean">

Fail with a structured error instead of prompting when required input is missing (`MISSING_*`, `AUTH_REQUIRED`, …).

</field>

<field name="--debug" type="boolean">

Verbose debug logs. Also enabled by `SHELVE_DEBUG=1` or `DEBUG=true`. HTTP requests are logged without Authorization headers or secret values.

</field>
</field-group>

### Auto non-interactive mode

The CLI enters non-interactive mode when any of these is true:

- `--non-interactive` is passed
- `CI=true` or `CI=1`
- The shell is detected as an AI agent via [`std-env`](https://github.com/unjs/std-env) (`cursor`, `claude`, `codex`, …)
- `AI_AGENT` is set (force-detect)

In agent shells, **shelve pull without --yes fails immediately** with code `AGENT_BLOCKED` instead of prompting.

## JSON output by command

<table>
<thead>
  <tr>
    <th>
      Command
    </th>
    
    <th>
      <code>
        data
      </code>
      
       shape
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        doctor
      </code>
    </td>
    
    <td>
      <code>
        { healthy, checks[], exitCodes, errorCodes }
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        config
      </code>
    </td>
    
    <td>
      Merged config; <code>
        token
      </code>
      
       redacted as <code>
        "***"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        me
      </code>
    </td>
    
    <td>
      <code>
        { loggedIn, username?, email? }
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        push
      </code>
    </td>
    
    <td>
      <code>
        { env, variableCount, pushed }
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pull
      </code>
    </td>
    
    <td>
      <code>
        { env, variableCount, file, keys[] }
      </code>
      
       — no values
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        init
      </code>
    </td>
    
    <td>
      <code>
        { writtenFiles, skippedFiles, gitignoreUpdated }
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        login
      </code>
    </td>
    
    <td>
      <code>
        { username, email }
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        create
      </code>
    </td>
    
    <td>
      <code>
        { name, slug, configPath }
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        logout
      </code>
    </td>
    
    <td>
      <code>
        { loggedOut: true }
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        generate
      </code>
    </td>
    
    <td>
      <code>
        { type, path }
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        upgrade
      </code>
    </td>
    
    <td>
      <code>
        { previous, current, updated }
      </code>
    </td>
  </tr>
</tbody>
</table>

`shelve run` inherits the child stdio. Startup errors are structured on stderr; with `--json`, a spawn event is also emitted on stderr: `{ "ok": true, "event": "child_spawned", "env", "variableCount", "keys", "command", "pid" }`.

## Exit codes

<table>
<thead>
  <tr>
    <th>
      Code
    </th>
    
    <th>
      Meaning
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Success
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      CLI, API, or validation error
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        128 + n
      </code>
    </td>
    
    <td>
      Child killed by signal (<code>
        run
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        129
      </code>
    </td>
    
    <td>
      Parent process gone / stdin EIO
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        130
      </code>
      
       / <code>
        143
      </code>
    </td>
    
    <td>
      SIGINT / SIGTERM (forwarded from <code>
        run
      </code>
      
      )
    </td>
  </tr>
</tbody>
</table>

## Examples

```bash [terminal]
# Machine-readable config (token redacted)
shelve --json config

# CI push without prompts
shelve --non-interactive --yes push --env staging

# Agent-safe dev server
shelve run -- pnpm dev

# Debug API round-trip
shelve --debug run --env preview -- pnpm build
```

## Install the agent skill

Shelve publishes a single [Agent Skill](https://docus.dev/en/ai/skills) at `/.well-known/skills/` on [shelve.cloud](https://shelve.cloud) (`shelve` — CLI, platform, and sync policies):

```bash [terminal]
npx skills add https://shelve.cloud
```

The skill teaches agents Shelve end-to-end: platform model (teams, tokens, UI), CLI workflows, sync policies, and security rules (prefer `run`, avoid disk writes, use `SHELVE_*` env vars). Reference files ship alongside `SKILL.md` (`cli-commands.md`, `platform.md`, `sync-policies.md`, `agent-workflows.md`).

## Local testing (contributors)

From the Shelve monorepo, use the zero-network playground:

```bash [terminal]
pnpm play
pnpm play -- --json config
```

See the [playground README](https://github.com/HugoRCD/shelve/tree/main/playground/run) on GitHub.
