Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cli-agent-friendly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shelve/cli": minor
---

Add agent-friendly global flags (`--json`, `--quiet`, `--yes`, `--non-interactive`), structured JSON errors on stderr, non-interactive guards across all commands, and machine-readable output for query commands. Adds `shelve doctor`, `run` spawn JSON events, GitHub Action, CI playground smoke tests, LP skills manifest check, troubleshooting docs, `shelve-app` agent skill, and llms.txt skill links. Includes `login --token`, `generate --type`, HTTP debug logging, fixes silent fetch failures in `shelve run`, full CLI documentation on shelve.cloud, and a Docus-published agent skill at `/.well-known/skills/`.
49 changes: 49 additions & 0 deletions .github/actions/shelve-run/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Shelve Run GitHub Action

Composite action that runs a command with secrets injected from [Shelve](https://shelve.cloud) via `@shelve/cli run`.

## Usage

```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: ./.github/actions/shelve-run
with:
token: ${{ secrets.SHELVE_TOKEN }}
team-slug: my-team
project: my-app
env: ci
command: pnpm test
```

## Inputs

| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `command` | yes | — | Command after `--` |
| `token` | yes | — | API token |
| `team-slug` | yes | — | Team slug |
| `project` | yes | — | Project name |
| `env` | no | `''` | Environment (or set repo variable `SHELVE_DEFAULT_ENV`) |
| `url` | no | `https://app.shelve.cloud` | Shelve instance URL |
| `cli-version` | no | `5` | `@shelve/cli` npm version (major pin) |

## Notes

- Uses `SHELVE_*` environment variables internally — no token on the command line.
- Passes `--non-interactive` so CI never hangs on prompts.
- Prefer scoped, expiring tokens from [app.shelve.cloud/user/tokens](https://app.shelve.cloud/user/tokens).

## Validate setup first

```yaml
- run: npx @shelve/cli doctor --json
env:
SHELVE_TOKEN: ${{ secrets.SHELVE_TOKEN }}
SHELVE_TEAM_SLUG: my-team
SHELVE_PROJECT: my-app
```
45 changes: 45 additions & 0 deletions .github/actions/shelve-run/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Shelve Run
description: Inject secrets from Shelve and run a command (wraps `shelve run -- …`)
inputs:
command:
description: Command to run after `--` (e.g. `pnpm test` or `npm run build`)
required: true
token:
description: Shelve API token (use `${{ secrets.SHELVE_TOKEN }}`)
required: true
team-slug:
description: Shelve team slug
required: true
project:
description: Shelve project name
required: true
env:
description: Shelve environment name (optional; uses SHELVE_DEFAULT_ENV if omitted)
required: false
default: ''
url:
description: Shelve instance URL
required: false
default: 'https://app.shelve.cloud'
cli-version:
description: npm version of @shelve/cli to run (default major 5)
required: false
default: '5'
runs:
using: composite
steps:
- name: Run with Shelve
shell: bash
env:
SHELVE_TOKEN: ${{ inputs.token }}
SHELVE_TEAM_SLUG: ${{ inputs.team-slug }}
SHELVE_PROJECT: ${{ inputs.project }}
SHELVE_URL: ${{ inputs.url }}
SHELVE_DEFAULT_ENV: ${{ inputs.env }}
run: |
set -euo pipefail
ENV_FLAG=""
if [ -n "${{ inputs.env }}" ]; then
ENV_FLAG="--env ${{ inputs.env }}"
fi
npx --yes "@shelve/cli@${{ inputs.cli-version }}" run ${ENV_FLAG} --non-interactive -- ${{ inputs.command }}
70 changes: 67 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ jobs:
TURBO_TEAM: ${{ vars.TURBO_TEAM }}

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false

- name: Install pnpm
uses: pnpm/action-setup@v6
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6
with:
run_install: false

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24.15.0
cache: pnpm
Expand All @@ -34,3 +36,65 @@ jobs:

- name: Run tests
run: pnpm run test

cli-playground:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false

- name: Install pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6
with:
run_install: false

- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24.15.0
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Playground — JSON config
run: pnpm play:json

- name: Playground — run start
run: pnpm play:start

- name: Playground — failing exit code
run: |
set +e
pnpm play:fail
code=$?
set -e
test "$code" -eq 7

lp-agent-skills:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false

- name: Install pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6
with:
run_install: false

- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24.15.0
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build landing (prerender skills)
run: pnpm build:lp

- name: Verify /.well-known/skills manifest
run: test -f apps/lp/.output/public/.well-known/skills/index.json
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ More details (progressive disclosure):
- [Build env and outputs](docs/agents/build-env.md)
- [Reference docs](docs/agents/docs-links.md)
- [AI collaboration rules](docs/agents/ai-workflow.md)
- [Shelve CLI for agents & automation](docs/agents/cli.md) — install skill: `npx skills add https://shelve.cloud`
4 changes: 4 additions & 0 deletions apps/lp/content/docs/3.cli/1.init.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ The `init` command prepares a repository for use with Shelve and, crucially, kee

```bash [terminal]
shelve init
shelve init --cwd ./packages/my-app
shelve --json init
```

## What it does
Expand All @@ -29,6 +31,8 @@ Each ignore file is framed with `# shelve-managed-block` / `# end shelve-managed
::
::

With global `--json`, stdout returns `{ writtenFiles, skippedFiles, gitignoreUpdated }`.

## Why this matters

AI coding agents (Cursor, Claude Code, Codex, Aider, Continue…) can read every file in your workspace unless you tell them otherwise. If you ever run `shelve pull` to write a `.env` to disk, an agent can trivially exfiltrate its contents into a prompt or a suggested commit. `shelve init` closes that hole in one command.
Expand Down
132 changes: 132 additions & 0 deletions apps/lp/content/docs/3.cli/10.agents-automation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
title: Agents & automation
description: 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.
::

## 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 }`.
Errors → JSON on **stderr**: `{ "ok": false, "error": { "code", "message", "status"?, "hint"? } }`.
Secret **values are never** included in JSON output.
::

::field{name="--quiet" alias="-q" type="boolean"}
Suppress clack intro/outro/spinners. Minimal text output only.
::

::field{name="--yes" alias="-y" type="boolean"}
Skip confirmation prompts (`pull`, `push` when `confirmChanges` is enabled, etc.).
::

::field{name="--non-interactive" type="boolean"}
Fail with a structured error instead of prompting when required input is missing (`MISSING_*`, `AUTH_REQUIRED`, …).
::

::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.
::
::

### 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

| Command | `data` shape |
|---------|----------------|
| `doctor` | `{ healthy, checks[], exitCodes, errorCodes }` |
| `config` | Merged config; `token` redacted as `"***"` |
| `me` | `{ loggedIn, username?, email? }` |
| `push` | `{ env, variableCount, pushed }` |
| `pull` | `{ env, variableCount, file, keys[] }` — no values |
| `init` | `{ writtenFiles, skippedFiles, gitignoreUpdated }` |
| `login` | `{ username, email }` |
| `create` | `{ name, slug, configPath }` |
| `logout` | `{ loggedOut: true }` |
| `generate` | `{ type, path }` |
| `upgrade` | `{ previous, current, updated }` |

`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

| Code | Meaning |
|------|---------|
| `0` | Success |
| `1` | CLI, API, or validation error |
| `128 + n` | Child killed by signal (`run`) |
| `129` | Parent process gone / stdin EIO |
| `130` / `143` | SIGINT / SIGTERM (forwarded from `run`) |

## 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 [Agent Skills](https://docus.dev/en/ai/skills) at `/.well-known/skills/` on [shelve.cloud](https://shelve.cloud) (`shelve` + `shelve-app`):

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

The skill teaches agents the correct Shelve workflows, flags, and security rules (prefer `run`, avoid disk writes, use `SHELVE_*` env vars).

## 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.
Loading
Loading