Files
ddev-claude-cli/README.md
T
2026-09-16 12:37:50 +02:00

186 lines
7.8 KiB
Markdown

# ddev-codex-claude-cli
`ddev-codex-claude-cli` is a DDEV add-on that runs Codex CLI and Claude Code CLI inside the DDEV `web` container while keeping runtime state under the project's `.ddev` directory on the host (bind-mounted into the container).
## What it does
- exposes `ddev codex`
- exposes `ddev claude`
- optionally exposes `ddev claude-swap` for juggling multiple Claude accounts (opt-in; see [Multiple Claude accounts](#multiple-claude-accounts))
- installs Codex CLI lazily the first time you run `ddev codex` (or whenever the persisted binary is missing) by downloading the latest release from GitHub: `curl -fsSL https://github.com/openai/codex/releases/latest/download/install.sh | sh`
- installs Claude Code CLI during `ddev start` when the persisted binary is missing or broken by downloading the stable native build: `curl -fsSL https://claude.ai/install.sh | bash -s stable`
- keeps normal `ddev start` fast by skipping Claude Code installation when the persisted binary is already runnable
- persists each CLI's binary, HOME, and XDG paths under **project-local** storage so each DDEV project has its own profiles
- seeds the project-local HOME from the container home on first bootstrap for `.gitignore_global` when present
- always runs Codex with `--sandbox danger-full-access --ask-for-approval untrusted`
## Runtime storage
Inside the web container, Codex CLI state lives under:
${DDEV_APPROOT}/.ddev/codex-cli
Claude Code CLI state lives under:
${DDEV_APPROOT}/.ddev/claude-code
On the host, those are your project's **`.ddev/codex-cli/`** and **`.ddev/claude-code/`** directories. They survive `ddev restart`, `ddev stop`, and `ddev poweroff` like any other project file. The same directories hold the add-on's **install scripts** (managed files) alongside runtime data created after the first start.
Codex layout:
| Path | Role |
|------|------|
| `.ddev/codex-cli/install.sh` | Codex install script |
| `.ddev/codex-cli/bin/codex` | Codex CLI binary |
| `.ddev/codex-cli/home` | `HOME` |
| `.ddev/codex-cli/config` | `XDG_CONFIG_HOME` |
| `.ddev/codex-cli/data` | `XDG_DATA_HOME` |
| `.ddev/codex-cli/state` | `XDG_STATE_HOME` |
Claude Code layout:
| Path | Role |
|------|------|
| `.ddev/claude-code/install.sh` | Claude Code install script |
| `.ddev/claude-code/home/.local/bin/claude` | Claude Code CLI binary |
| `.ddev/claude-code/home` | `HOME` |
| `.ddev/claude-code/config` | `XDG_CONFIG_HOME` |
| `.ddev/claude-code/data` | `XDG_DATA_HOME` |
| `.ddev/claude-code/state` | `XDG_STATE_HOME` |
Authentication and settings are **per project**: sign in once per project; they persist across container restarts.
## Install
From a DDEV project root:
ddev add-on get /path/to/ddev-codex-claude-cli
Then restart:
ddev restart
## Usage
ddev codex
ddev codex --version
ddev claude
ddev claude --version
ddev claude update
ddev claude-swap list # opt-in; see "Multiple Claude accounts"
## Updates
Codex CLI is installed lazily when `ddev codex` runs and the persisted binary is missing or broken.
Claude Code CLI is installed automatically on `ddev start` only when the persisted binary is missing or broken. Later starts run a fast health check and skip installation when Claude is already runnable.
To check for and install a newer Claude Code stable release, run:
ddev claude update
## Multiple Claude accounts
The add-on can integrate [`claude-swap`](https://github.com/realiti4/claude-swap)
(`cswap`) so one project can hold several Claude accounts and swap between them —
mainly to keep working past a 5-hour or weekly rate limit. It is **opt-in**: nothing
is installed during `ddev start`; the first `ddev claude-swap` call (or first
`ddev claude --account …`) installs `uv` and `claude-swap` into the project-local
Claude profile under `.ddev/claude-code/`.
Accounts are stored **per project**, next to the rest of the Claude state:
.ddev/claude-code/data/claude-swap/
They persist across restarts and are deleted when the add-on is removed. Back them up
first with `ddev claude-swap export backup.cswap` if you need to keep them.
### Swapping accounts without losing your session
A running `ddev claude` conversation is not tied to the auth token, and Claude Code
re-reads its credentials file whenever it changes. So:
1. Terminal 1: you are in `ddev claude`, working, and hit (or approach) the limit.
2. Terminal 2 (on the host): `ddev claude-swap switch <n|email|alias>`
(or `ddev claude-swap switch --strategy best`).
3. Back in terminal 1: send the next message — it goes out on the new account, in the
**same session** with all its context. If a request just failed with the limit
error, resend it.
To automate step 2, run `ddev claude-swap auto` in a spare terminal (or an
`ddev ssh` shell); it watches usage and switches around 90%.
### One-time setup
ddev claude # sign in as your first account
ddev claude-swap add --alias a # capture it
ddev claude # /login inside Claude Code as the next account, then exit
ddev claude-swap add --alias b # capture it
ddev claude-swap list # all accounts with usage and reset times
ddev claude-swap status # currently active account
`ddev claude-swap` forwards every argument straight to `cswap`, so any `cswap`
subcommand works (`switch`, `run`, `map`, `disable`, `export`, `import`, `tui`, …).
`ddev claude-swap upgrade` updates `claude-swap` to the latest release.
### Starting a session pinned to an account
Separately from the hot-swap flow, you can launch a session bound to one account
(with its own per-account chat history):
ddev claude --account b
ddev claude --account b --resume # remaining args pass straight to Claude Code
Set a project default so plain `ddev claude` always uses it:
echo b > .ddev/claude-code/claude-swap-account
# or export DDEV_CLAUDE_SWAP_ACCOUNT=b
Remove the file (or unset the variable) to go back to the normal single-account
launch.
### Requirements
The first install needs outbound network access to `astral.sh`, `pypi.org`,
`files.pythonhosted.org`, and `github.com` (for the `uv`-managed Python 3.12).
`claude-swap` captures whichever account is signed in at `cswap add` time, so the
initial multi-account setup is a few manual `/login` round-trips.
## Installed managed files
.ddev/config.codex-cli.yaml
.ddev/config.claude-code.yaml
.ddev/commands/web/codex
.ddev/commands/web/claude
.ddev/commands/web/claude-swap
.ddev/codex-cli/install.sh
.ddev/claude-code/install.sh
.ddev/claude-code/install-swap.sh
## Notes
- the first `ddev codex` in a project can take some time because it downloads the Codex CLI binary; later runs reuse `.ddev/codex-cli/bin/codex`
- the first `ddev start` after installing the add-on can take some time because it downloads Claude Code; later starts reuse `.ddev/claude-code/home/.local/bin/claude`
- if the container home has `.gitignore_global` (typically from host `~/.ddev/homeadditions/.gitignore_global`), it is copied into each project-local CLI `HOME` on first bootstrap; git's `core.excludesfile` is pointed at that project-local copy
- if you do not want add-on or CLI runtime files in your repository, ignore them in git, for example:
/.ddev/addon-metadata/ddev-codex-claude-cli
/.ddev/config.codex-cli.yaml
/.ddev/config.claude-code.yaml
/.ddev/commands/web/codex
/.ddev/commands/web/claude
/.ddev/commands/web/claude-swap
/.ddev/codex-cli/
/.ddev/claude-code/
Removing the add-on deletes `.ddev/codex-cli/` and `.ddev/claude-code/` as well (local auth, config, and CLI binaries). Back up those directories first if you need to keep them.
## macOS / Linux / WSL2
This add-on runs Codex CLI and Claude Code CLI in the DDEV container, so host OS differences are minimized as long as DDEV works normally.