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

# CLI Adapters

> How Crewship invokes vendor coding CLIs (Claude Code, Codex, Gemini, OpenCode, Cursor, Factory Droid) and the reasoning behind which ones are supported.

# CLI Adapters

## Overview

Crewship runs each agent as a subprocess of a vendor coding CLI inside the crew container. The agent record's `cli_adapter` column picks which binary and which command line; the orchestrator's `BuildCLICommand` (`internal/orchestrator/exec.go`) is the one place that knows the per-vendor flag conventions. Everything else in the orchestrator — prompt assembly, memory injection, journal writing, tool routing — is adapter-agnostic; the adapter layer is only responsible for translating Crewship's uniform internal representation into whatever flags / files / stdio shape a given vendor CLI expects.

The adapter pattern exists because no two coding-CLI vendors agree on anything. Claude Code reads its system prompt from a `--system-prompt` flag; every other adapter has no equivalent flag, so Crewship prepends the preamble inline with `[SYSTEM]/[USER]` delimiters on turn 1 and lets the CLI's own file-discovery path (`AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursor/rules/`, `.factory/AGENTS.md`) carry the same content into turn 2+. Factory Droid additionally expresses autonomy as a tier (`--auto low|medium|high`) instead of a tool list. Rather than picking a winner, Crewship supports all of them — six adapters as of 2026-04 — and lets the operator choose per agent. The selection lives on the agent row, so a `backend` crew can run Claude Code while a `data` crew uses Gemini, with no orchestrator-wide config change.

Adding another vendor is intentionally small: one new `case` in `BuildCLICommand` for the command-line translation, plus a normalisation rule in `agents_create.go` for the default tool profile and any vendor-specific defaults. The `adapter_hint` field on the [CLI pairing](/guides/cli-pairing) request is *telemetry only* — the backend never routes on it, and the frontend list of adapters lives in `lib/cli-adapters.ts` rather than in Go, so a new adapter is a TypeScript file plus a Go switch arm, not a schema migration.

## Supported adapters

| Adapter         | Binary                                                                                                           | Turn-1 system prompt                                                                   | Turn-2+ persistence                                         | Output                                            | Notes                                                                                                                                     |
| --------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `CLAUDE_CODE`   | `claude --print --output-format stream-json --include-partial-messages --dangerously-skip-permissions --verbose` | `--system-prompt` flag                                                                 | `AGENTS.md` + canonical memory file set written to CWD      | stream-JSON on stdout                             | Supports `--model`, `--mcp-config <path>`. `--tools` is **always** passed with the per-profile built-in allowlist (see below). Default.   |
| `CODEX_CLI`     | `codex exec --json --sandbox <mode>`                                                                             | `[SYSTEM]/[USER]` prepend in the prompt body (Codex has no system-prompt flag)         | `AGENTS.md` written into CWD before exec                    | NDJSON                                            | `--sandbox` is always passed: `read-only` for `tool_profile=MINIMAL`, `workspace-write` otherwise.                                        |
| `GEMINI_CLI`    | `gemini -p <message> --output-format stream-json`                                                                | `[SYSTEM]/[USER]` prepend (no documented `--system-instruction` flag in headless mode) | `GEMINI.md` + canonical memory file set                     | NDJSON                                            | Uses `-p` for the user message. `tool_profile=MINIMAL` adds `--approval-mode plan` (read-only); `--allowed-tools` is deprecated upstream. |
| `OPENCODE`      | `opencode run --format json`                                                                                     | `[SYSTEM]/[USER]` prepend                                                              | `AGENTS.md` written into CWD before exec                    | single JSON object (no streaming)                 | sst.dev's BYOK CLI; works with any provider.                                                                                              |
| `CURSOR_CLI`    | `cursor-agent -p --output-format stream-json`                                                                    | `[SYSTEM]/[USER]` prepend (no `--system-prompt` flag in headless mode)                 | `.cursor/rules/`, `AGENTS.md`, `CLAUDE.md` written into CWD | stream-JSON on stdout (same shape as Claude Code) | Added 2026-04. `-m <model>` for model override.                                                                                           |
| `FACTORY_DROID` | `droid exec --auto {low\|medium\|high} -o stream-json`                                                           | `[SYSTEM]/[USER]` prepend                                                              | `AGENTS.md` + `.factory/AGENTS.md` written into CWD         | stream-JSON (NDJSON)                              | Added 2026-04. Tiered autonomy — see below.                                                                                               |

The default fallback when `cli_adapter` is empty or unknown is `claude --print <message>` — minimal flags, no streaming, lowest-risk degradation.

## Tool profiles & the built-in tool allowlist

An agent's `tool_profile` (`FULL` | `CODING` | `MINIMAL`, default `CODING`) curates which of the CLI's **built-in** tools the agent may use. This is an **allowlist**, not a denylist: tools not on the list are removed from the model's context entirely.

Why an allowlist matters: a headless Claude Code agent otherwise inherits the CLI's *entire* default tool catalog — including harness-internal tools (`TaskCreate`/`TaskUpdate`/`TaskList`/`TaskGet`/`TaskStop`, `TodoWrite`, `ToolSearch`, `Agent`, `Workflow`, `Cron*`, `ScheduleWakeup`, `RemoteTrigger`, `EnterPlanMode`, `AskUserQuestion`, …) that have **no Crewship backing**. An agent that calls one (e.g. `TaskCreate` to "create a task") writes to ephemeral in-process CLI state that persists nowhere the user can see, then can't explain where the data went. A denylist would let any newly-added builtin leak back in on the next CLI upgrade; the allowlist is closed by default.

The per-profile built-in sets (single source of truth: `builtinToolAllowlist` in `internal/orchestrator/tool_profiles.go`):

| Profile            | Built-in tools                             | Posture                                   |
| ------------------ | ------------------------------------------ | ----------------------------------------- |
| `MINIMAL`          | `Read, Glob, Grep, ToolSearch`             | Read-only — inspection, review, grading.  |
| `CODING` (default) | `+ Write, Edit, Bash, WebFetch, WebSearch` | Write + execute + network. The workhorse. |
| `FULL`             | `+ NotebookEdit`                           | Everything useful.                        |

`ToolSearch` is in **every** profile on purpose. Claude Code defers MCP tools by default (tool search enabled) and the model discovers them via the built-in `ToolSearch` tool — drop it and the agent can no longer see or call **any** MCP tool (`crewship-memory`, Composio/YouTube, everything). Verified: with `ToolSearch` allowed, MCP tools load on demand while a builtin that's *not* in the allowlist (e.g. `TaskCreate`) stays unreachable even via `ToolSearch`. Keeping deferral also scales to large MCP catalogs (e.g. GitHub's \~846 tools) without bloating context.

This governs **only built-in tools**. MCP tools (`crewship-memory`, Composio apps, …) come from the agent's `.mcp.json` via `--mcp-config` and are unaffected by `--tools`, so they always resolve (discovered through `ToolSearch`). The real Crewship capabilities (missions, issues, pipelines, keeper, peer messaging) are sidecar HTTP endpoints advertised in the system prompt — not CLI tools — and are gated by the agent's role capabilities.

Per-adapter enforcement (each CLI exposes a different lever):

| Adapter                   | Mechanism                                                                                                                       |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `CLAUDE_CODE`             | `--tools <allowlist>` (named built-ins).                                                                                        |
| `CODEX_CLI`               | `--sandbox read-only` (MINIMAL) vs `workspace-write` — capability gate, no named-tool list.                                     |
| `GEMINI_CLI`              | `--approval-mode plan` (MINIMAL, read-only); `--allowed-tools` is deprecated upstream.                                          |
| `FACTORY_DROID`           | `--auto low\|medium\|high` autonomy tier.                                                                                       |
| `OPENCODE` / `CURSOR_CLI` | Profile-based built-in curation not yet wired (these CLIs lack the harness-internal tool surface that motivated the allowlist). |

## Cursor (`CURSOR_CLI`)

`cursor-agent -p` is Cursor's headless mode. `-p` (print) prevents the interactive TUI from spawning; `--output-format stream-json` aligns the JSONL stream with the format the chat-bridge already parses for Claude Code, so the same reader handles both.

Cursor reads its system instructions from files in the working directory: `.cursor/rules/`, `AGENTS.md`, and `CLAUDE.md`. `SetupSystemPrompt` writes those before exec, so there is no `--system-prompt` flag on the command line. The very first turn, however, has no file-discovery yet, so the orchestrator also prepends the preamble inline with `[SYSTEM]/[USER]` delimiters — turn-2+ then falls back to file-discovery so persona edits land without burning a re-prepend.

`--mode=plan` and `--mode=ask` are valid Cursor flags (added 2026-01-16) but are not exposed today; the default `agent` mode is what `BuildCLICommand` emits. If a tool profile needs read-only browsing, extend the `CURSOR_CLI` case to map `tool_profile=CONSULTATIVE` to `--mode=ask`.

## Factory Droid (`FACTORY_DROID`)

`droid exec` is Factory's headless single-shot mode. The `--auto` flag picks the autonomy tier:

| `--auto` value | Behaviour                                             | When                                                                                                              |
| -------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `low`          | Read-only — agent inspects but does not mutate files. | `tool_profile` is `MINIMAL`.                                                                                      |
| `medium`       | Can edit files within scope.                          | Default — `tool_profile` is `CODING` or unset.                                                                    |
| `high`         | Fully autonomous.                                     | `tool_profile` is `FULL`. Factory's docs warn it can perform destructive operations without further confirmation. |

The mapping lives in `internal/orchestrator/adapter_droid.go` (`BuildCommand`): `MINIMAL → low`, `FULL → high`, and everything else (`CODING` or an unset profile) → `medium`.

The default is `medium` because the API normalises an empty `ToolProfile` to `CODING` (see `internal/api/agents_create.go`), and `CODING` agents are expected to write code. The previous "default = low" choice was theoretical; in production almost every agent would be told to write code anyway. The current inversion (default-medium, explicit-low) is honest about production behaviour.

<Warning>
  `--auto high` is reachable by setting an agent's `tool_profile` to `FULL`. Factory's docs warn this tier can perform destructive operations without further confirmation, so gate `FULL` agents behind a Harbormaster approval rather than making it the default.
</Warning>

## Adapters that are deliberately NOT supported

`BuildCLICommand` only ships adapters for CLI agents that meet four criteria. CLI agents that don't meet all four are skipped today. Adding a new adapter is a one-line change once the upstream surface stabilises; open a GitHub issue to discuss before opening a PR.

<Accordion title="The four inclusion criteria">
  * **Stable headless / `--exec` surface** — single-shot invocation
    with deterministic stdout, no interactive REPL requirement
  * **Settled flag set** — no recent breaking changes to the
    command-line API
  * **Self-contained binary** — no IDE extension or browser harness
    required at runtime
  * **Documented function-calling or MCP support** — necessary for
    the memory + skill primitives to wire in
</Accordion>

## How the adapter signals the sidecar

The orchestrator stamps two environment variables on the **container env** (visible to every process running inside that crew container — both the agent CLI and the sidecar process). The sidecar reads them once at startup and tags every `POST /api/v1/internal/cost/record` it sends to the server:

* `CREWSHIP_BILLING_MODE` — `metered` for API-key credentials, `flat_rate` for subscription credentials (Claude Max, ChatGPT Plus, Cursor, Copilot).
* `CREWSHIP_SUBSCRIPTION_PLAN` — display label, currently `"Anthropic Max"` for the OAuth Claude Code path. Only set when `CREWSHIP_BILLING_MODE=flat_rate`.

The agent CLI process technically inherits the same env (containers don't isolate env vars between sibling exec sessions), but the agent has no reason to read them — the variables are interpreted by the sidecar, which is the one code path that knows what to do with billing mode.

See [Environment Variables](/configuration/environment#paymaster-billing-modes) for the operator-facing reference and [Paymaster billing modes](/guides/paymaster#billing-modes) for the full rationale.

## Picking an adapter for an agent

Set `cli_adapter` on the agent (UI: agent canvas → "Runtime" panel; CLI: `crewship agent update <slug> --cli-adapter CURSOR_CLI`; API: `PUT /api/v1/agents/{id}` with `cli_adapter` in the body). The string must match one of the constants above exactly.

Pick by what the credential supports:

* **Anthropic API key or Claude Max OAuth** → `CLAUDE_CODE`.
* **OpenAI API key or ChatGPT Plus** → `CODEX_CLI`.
* **Google AI key or Pro/Ultra** → `GEMINI_CLI`.
* **Cursor subscription** → `CURSOR_CLI`.
* **Factory account** → `FACTORY_DROID`.
* **Anything else (BYOK)** → `OPENCODE`.

Switching adapters is hot — the next exec uses the new value. The container does not need to be rebuilt unless the new adapter's binary is missing from the image (verify with `crewship runtimes info <runtime>`).

## Related

* [Paymaster](/guides/paymaster) — billing modes, quota enforcement, sidecar usage parsing.
* [Environment Variables](/configuration/environment#paymaster-billing-modes) — `CREWSHIP_BILLING_MODE`, `CREWSHIP_SUBSCRIPTION_PLAN`.
* [Architecture — CLI Adapters](/architecture#cli-adapters) — output processing and stream-JSON shape.
* [Orchestration](/guides/orchestration) — agent lifecycle around the CLI exec.
