feat: rename to ddev-claude-cli, drop Codex CLI, add opt-in headless browser
This commit is contained in:
1 parent
b6d88ce4dc
commit
d8076fa5a0
8 files changed
+322
-64
No files matched your search
@@ -1,45 +1,30 @@
|
||||
# ddev-codex-claude-cli
|
||||
# ddev-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).
|
||||
`ddev-claude-cli` is a DDEV add-on that runs 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).
|
||||
|
||||
> Formerly `ddev-codex-claude-cli`. See [Upgrading from ddev-codex-claude-cli](#upgrading-from-ddev-codex-claude-cli).
|
||||
|
||||
## 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))
|
||||
- optionally runs a headless Chromium as a sidecar container and wires it into Claude Code as the `playwright` MCP server (opt-in; see [Headless browser](#headless-browser))
|
||||
- optionally keeps read-only mirrors of sibling repos under `.ddev/claude-code/home/refs/` for Claude to grep (opt-in; see [Sibling repo mirrors](#sibling-repo-mirrors))
|
||||
- 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
|
||||
- persists the CLI's binary, HOME, and XDG paths under **project-local** storage so each DDEV project has its own profile
|
||||
- seeds the project-local HOME from the container home on first bootstrap for `.gitignore_global` when present
|
||||
- keeps its own artifacts out of the project's git via a managed block in `.git/info/exclude`, refreshed on every `ddev start` and `ddev claude` (see [Keeping add-on files out of git](#keeping-add-on-files-out-of-git))
|
||||
- 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:
|
||||
Inside the web container, 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.
|
||||
On the host, that is your project's **`.ddev/claude-code/`** directory. It survives `ddev restart`, `ddev stop`, and `ddev poweroff` like any other project file. The same directory holds 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:
|
||||
Layout:
|
||||
|
||||
| Path | Role |
|
||||
|------|------|
|
||||
@@ -56,7 +41,7 @@ Authentication and settings are **per project**: sign in once per project; they
|
||||
|
||||
From a DDEV project root:
|
||||
|
||||
ddev add-on get /path/to/ddev-codex-claude-cli
|
||||
ddev add-on get /path/to/ddev-claude-cli
|
||||
|
||||
Then restart:
|
||||
|
||||
@@ -64,10 +49,6 @@ Then restart:
|
||||
|
||||
## Usage
|
||||
|
||||
ddev codex
|
||||
|
||||
ddev codex --version
|
||||
|
||||
ddev claude
|
||||
|
||||
ddev claude --version
|
||||
@@ -78,9 +59,9 @@ Then restart:
|
||||
|
||||
ddev claude-refs # opt-in; see "Sibling repo mirrors"
|
||||
|
||||
## Updates
|
||||
ddev claude-browser enable # opt-in; see "Headless browser"
|
||||
|
||||
Codex CLI is installed lazily when `ddev codex` runs and the persisted binary is missing or broken.
|
||||
## Updates
|
||||
|
||||
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.
|
||||
|
||||
@@ -155,6 +136,57 @@ The first install needs outbound network access to `astral.sh`, `pypi.org`,
|
||||
`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.
|
||||
|
||||
## Headless browser
|
||||
|
||||
The add-on can run a headless Chromium next to the web container so Claude can open the
|
||||
site, click through it, fill in forms, read the console and network log, and take
|
||||
screenshots. It uses Microsoft's [Playwright MCP](https://github.com/microsoft/playwright-mcp)
|
||||
server image (`mcr.microsoft.com/playwright/mcp`) and needs no other add-on. It is **opt-in
|
||||
per project**, because the extra container costs a few hundred MB of memory while the
|
||||
project runs.
|
||||
|
||||
ddev claude-browser enable # copies the compose file into .ddev/ and restarts
|
||||
ddev claude-browser status
|
||||
ddev claude-browser disable # removes it again and restarts
|
||||
|
||||
What enabling does:
|
||||
|
||||
- copies `.ddev/claude-code/browser/docker-compose.claude-browser.yaml` (the template) to
|
||||
`.ddev/docker-compose.claude-browser.yaml`, which adds a `claude-browser` service. The
|
||||
first start builds a small layer on top of the upstream image so the browser runs as
|
||||
your user; later starts reuse it.
|
||||
- on that restart, and on every `ddev claude`, `install.sh` registers the service with
|
||||
Claude Code at user scope as the MCP server `playwright`
|
||||
(`http://claude-browser:8931/mcp`). After `disable`, the next start removes that
|
||||
registration again, but only if it still points at the add-on's URL. A `playwright`
|
||||
server you added yourself is never touched (and blocks the add-on's registration;
|
||||
the start-up output says so).
|
||||
|
||||
Then, inside `ddev claude`, ask for things like "open the site and check the login form
|
||||
works". `/mcp` should list `playwright` as connected. The first browser call in a session
|
||||
asks for permission as usual; allow `mcp__playwright` in `/permissions` if you want it
|
||||
unprompted.
|
||||
|
||||
How it is set up:
|
||||
|
||||
- The browser reaches the project at its normal URL (`https://<project>.ddev.site`) through
|
||||
the DDEV router, and at `http://web` directly. It ignores certificate errors, since
|
||||
DDEV's mkcert CA is not installed in the container.
|
||||
- Each MCP session gets a fresh in-memory profile (`--isolated`), so several `ddev claude`
|
||||
sessions can use the browser at once. Logins do not survive the session.
|
||||
- The project is mounted at `/var/www/html`, the same path as in the web container. File
|
||||
uploads and saved screenshots use paths Claude can open directly. Auto-named output goes
|
||||
to `.ddev/claude-code/browser-output/`, which is gitignored with the rest of
|
||||
`.ddev/claude-code/`.
|
||||
- Port 8931 is only exposed on the project's Docker network, never published to the host.
|
||||
- The image is pinned to a tested release. To use another one, set `CLAUDE_BROWSER_IMAGE` in
|
||||
`.ddev/.env`, e.g. `CLAUDE_BROWSER_IMAGE=mcr.microsoft.com/playwright/mcp:v0.0.84`, then
|
||||
`ddev restart`.
|
||||
- While the enabled copy still has its `#ddev-generated` line, `install.sh` refreshes it from
|
||||
the template after add-on upgrades and asks you to `ddev restart`. To customise the
|
||||
service, delete that line from `.ddev/docker-compose.claude-browser.yaml`. Updates then
|
||||
stop, and `ddev claude-browser disable` leaves the file for you to delete yourself.
|
||||
|
||||
## Sibling repo mirrors
|
||||
|
||||
If a project works alongside sibling repos on the same self-hosted Git host, Claude can be given
|
||||
@@ -305,41 +337,63 @@ changing the URL or rotating the token, run `claude mcp remove <name> -s user` a
|
||||
|
||||
## Installed managed files
|
||||
|
||||
.ddev/config.codex-claude.yaml
|
||||
.ddev/commands/web/codex
|
||||
.ddev/config.claude-cli.yaml
|
||||
.ddev/commands/web/claude
|
||||
.ddev/commands/web/claude-swap
|
||||
.ddev/commands/web/claude-refs
|
||||
.ddev/codex-cli/install.sh
|
||||
.ddev/commands/host/claude-browser
|
||||
.ddev/claude-code/install.sh
|
||||
.ddev/claude-code/install-swap.sh
|
||||
.ddev/claude-code/refs-sync.sh
|
||||
.ddev/claude-code/git-exclude.sh
|
||||
.ddev/claude-code/browser-setup.sh
|
||||
.ddev/claude-code/browser/docker-compose.claude-browser.yaml
|
||||
|
||||
Plus `.ddev/docker-compose.claude-browser.yaml` while the [headless browser](#headless-browser) is enabled.
|
||||
|
||||
## Keeping add-on files out of git
|
||||
|
||||
Every artifact the add-on drops into a project — `.ddev/claude-code/`, `.ddev/codex-cli/`, the
|
||||
`commands/web/*` wrappers, `.ddev/config.codex-claude.yaml`, `.ddev/addon-metadata/ddev-codex-claude-cli`,
|
||||
`.ddev/plans`, and `.ddev/claude-refs.txt` — should stay out of the project's git history.
|
||||
Every artifact the add-on drops into a project — `.ddev/claude-code/`, the
|
||||
command wrappers, `.ddev/config.claude-cli.yaml`, `.ddev/docker-compose.claude-browser.yaml`,
|
||||
`.ddev/addon-metadata/ddev-claude-cli`, `.ddev/plans`, `.ddev/claude-refs.txt`, and the legacy
|
||||
`.ddev/codex-cli/` — should stay out of the project's git history.
|
||||
|
||||
`.ddev/claude-code/git-exclude.sh` maintains a marker-delimited block for this in the project's
|
||||
`.git/info/exclude` (not `.gitignore`): a per-clone file that every git process honours, including
|
||||
git run from the host, regardless of `$HOME` or container config. `install.sh` runs it on every
|
||||
`ddev start` and every `ddev claude` (and the codex installer runs the same script too), so the
|
||||
`ddev start` and every `ddev claude`, so the
|
||||
block always matches the add-on's current file list — nothing to copy or keep in sync by hand.
|
||||
|
||||
Content outside the `>>> ddev-codex-claude-cli >>>` / `<<< ddev-codex-claude-cli <<<` markers is
|
||||
left untouched; only the block between them is rewritten. Personal patterns (`.DS_Store`, `.idea`,
|
||||
Content outside the `>>> ddev-claude-cli >>>` / `<<< ddev-claude-cli <<<` markers is
|
||||
left untouched; only the block between them is rewritten. A block left by the add-on's former
|
||||
name (`ddev-codex-claude-cli`) is removed. Personal patterns (`.DS_Store`, `.idea`,
|
||||
`CLAUDE.md`, …) are not part of this block — they belong in your own `~/.gitignore_global`, wired
|
||||
up via `core.excludesfile` as described below, since whether a given project tracks its own
|
||||
`CLAUDE.md` is not the add-on's call.
|
||||
|
||||
- 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 — this only affects git run *inside* the container, which is why the add-on's own artifacts use the `.git/info/exclude` block instead
|
||||
- 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`
|
||||
- if the container home has `.gitignore_global` (typically from host `~/.ddev/homeadditions/.gitignore_global`), it is copied into the project-local `HOME` on first bootstrap; git's `core.excludesfile` is pointed at that project-local copy — this only affects git run *inside* the container, which is why the add-on's own artifacts use the `.git/info/exclude` block instead
|
||||
- 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`
|
||||
|
||||
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. The `.git/info/exclude` block is left in place; an orphaned entry for a path that no longer exists is harmless.
|
||||
Removing the add-on deletes `.ddev/claude-code/` and the browser compose file as well (local auth, config, and CLI binary). Back up that directory first if you need to keep them. The `.git/info/exclude` block is left in place; an orphaned entry for a path that no longer exists is harmless.
|
||||
|
||||
## Upgrading from ddev-codex-claude-cli
|
||||
|
||||
The add-on used to be called `ddev-codex-claude-cli` and also shipped Codex CLI
|
||||
(`ddev codex`). Codex has been removed. To upgrade a project, install the new add-on over the
|
||||
old one:
|
||||
|
||||
ddev add-on get /path/to/ddev-claude-cli
|
||||
ddev restart
|
||||
|
||||
**Do not run `ddev add-on remove ddev-codex-claude-cli` first.** Its removal actions delete
|
||||
`.ddev/claude-code/`, including your Claude login, settings, and claude-swap accounts.
|
||||
|
||||
Installing `ddev-claude-cli` takes over `.ddev/claude-code/` as it is. It also removes the old
|
||||
add-on's managed files (`config.codex-claude.yaml`, `commands/web/codex`,
|
||||
`codex-cli/install.sh`, each only while still marked `#ddev-generated`) and its
|
||||
`addon-metadata` entry, so DDEV no longer lists it. `.ddev/codex-cli/`, which holds Codex
|
||||
auth and config, is left in place, still gitignored, for you to back up or delete.
|
||||
|
||||
## 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.
|
||||
This add-on runs Claude Code CLI in the DDEV container, so host OS differences are minimized as long as DDEV works normally.
|
||||
Reference in new issue
Block a user