chore: no idea?
This commit is contained in:
1 parent
63f3199076
commit
afc232ac27
8 files changed
+411
-18
No files matched your search
@@ -7,11 +7,13 @@
|
||||
- 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 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
|
||||
- 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
|
||||
@@ -74,6 +76,8 @@ Then restart:
|
||||
|
||||
ddev claude-swap list # opt-in; see "Multiple Claude accounts"
|
||||
|
||||
ddev claude-refs # opt-in; see "Sibling repo mirrors"
|
||||
|
||||
## Updates
|
||||
|
||||
Codex CLI is installed lazily when `ddev codex` runs and the persisted binary is missing or broken.
|
||||
@@ -151,34 +155,127 @@ 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.
|
||||
|
||||
## Sibling repo mirrors
|
||||
|
||||
If a project works alongside sibling repos on the same self-hosted Git host, Claude can be given
|
||||
read-only, auto-refreshing local clones of them to grep for prior art — plus, optionally, the
|
||||
`gitea` MCP server for their issues and PRs. This is **opt-in per project** and driven entirely by
|
||||
one manifest, `.ddev/claude-refs.txt`; projects without that file see no change at all.
|
||||
|
||||
`.ddev/claude-refs.txt` is **local by default**: the add-on's [managed git-exclude
|
||||
block](#keeping-add-on-files-out-of-git) ignores it, since it typically names a LAN-only Git host,
|
||||
a bot token, and mirrors specific to one person's sandbox. A project that wants the manifest shared
|
||||
with the rest of the team can still track it with `git add -f .ddev/claude-refs.txt`.
|
||||
|
||||
### Manifest: `.ddev/claude-refs.txt`
|
||||
|
||||
```
|
||||
# Settings: one per line, starting with @
|
||||
@host git.example.com # required
|
||||
@user claude-ro # bot account; with @token, drives git credentials
|
||||
@token ~/.gitea-ro-token # default; 0600 file in the sandbox home, never committed
|
||||
@depth 50 # default
|
||||
@mcp gitea https://mcp.example.com/mcp # optional: name + URL, registered with the token as Bearer
|
||||
|
||||
# Mirrors: <dir-name> <owner/repo> <description, rest of line, becomes the README row>
|
||||
sibling-api org/sibling-api Go service this project talks to over HTTP.
|
||||
shared-lib org/shared-lib Shared component library, imported as a dependency.
|
||||
```
|
||||
|
||||
Inline `#` comments are only allowed on setting lines, after all the values the setting reads.
|
||||
Keep them off mirror lines, or they end up in the description.
|
||||
|
||||
Environment overrides, mainly for testing: `CLAUDE_REFS_MANIFEST`, `CLAUDE_REFS_HOME`,
|
||||
`CLAUDE_REFS_MAX_AGE` (seconds), `CLAUDE_REFS_TIMEOUT` (seconds per clone or fetch), and
|
||||
`CLAUDE_REFS_DETACHED` (set by `install.sh`; sends output to `refs/.sync.log`).
|
||||
|
||||
### What happens once a project has a manifest
|
||||
|
||||
- On `ddev start` and on every `ddev claude`, `install.sh` runs the sync in the background
|
||||
(`setsid nohup`) so neither command waits on the network. A stamp file throttles real syncs to
|
||||
at most one every 6h (`CLAUDE_REFS_MAX_AGE`), written only after a fully successful run, so a
|
||||
failure retries next launch. If the Git host is unreachable, the sync logs a line and exits
|
||||
cleanly rather than failing `ddev start`.
|
||||
- Mirrors are shallow (`--depth 50 --single-branch` by default), live under
|
||||
`.ddev/claude-code/home/refs/` (gitignored, survives `ddev restart`), and are **throwaway**:
|
||||
every sync hard-resets them to the remote's current state, so local changes are lost and nothing
|
||||
is ever merged. A directory that drops out of the manifest is left in place with a warning
|
||||
rather than deleted.
|
||||
- An entry whose repo resolves to the project's own `origin` is skipped, so the project never ends
|
||||
up with a second, stale copy of itself.
|
||||
- `refs/README.md` is regenerated from the manifest on every sync — descriptions come from the
|
||||
manifest, so the table can't drift from what's on disk.
|
||||
- The bot account's credential file and git config are rewritten from the manifest on every run,
|
||||
so a renamed bot account heals itself on the next `ddev claude` instead of failing silently.
|
||||
- Run `ddev claude-refs` to sync immediately instead of waiting for the throttle; inside a Claude
|
||||
session, running `.ddev/claude-code/refs-sync.sh --force` does the same and is already on the
|
||||
permission allowlist.
|
||||
- If `@mcp` is set, the MCP server is registered once at user scope (`claude mcp add --scope
|
||||
user`), and required permission-allowlist entries are added once — both idempotently, so re-runs
|
||||
never duplicate them.
|
||||
|
||||
Access control (which repos the mirrors can see) stays entirely in the Git host: the bot account's
|
||||
collaborator access decides what's visible. The manifest only names repos; it grants nothing by
|
||||
itself.
|
||||
|
||||
### Adopting in a project
|
||||
|
||||
1. On the Git host, add the bot account as a **read-only** collaborator to each sibling repo.
|
||||
2. Create `.ddev/claude-refs.txt` with `@host`, `@user`, and the mirrors you want. It stays local
|
||||
unless you `git add -f` it (see above).
|
||||
3. Put the bot's token at `.ddev/claude-code/home/.gitea-ro-token`, mode `0600` (matches the
|
||||
manifest's default `@token ~/.gitea-ro-token`; never commit it).
|
||||
4. `ddev restart`, then check `.ddev/claude-code/home/refs/.sync.log` after a minute or so.
|
||||
5. Paste this into the project's `CLAUDE.md`, adding a sentence about the most relevant sibling:
|
||||
|
||||
```markdown
|
||||
## Sibling repos
|
||||
|
||||
Read-only shallow clones of sibling projects live in `.ddev/claude-code/home/refs/`
|
||||
(gitignored, declared in `.ddev/claude-refs.txt`). Grep them for prior art before
|
||||
re-deriving a solution; `refs/README.md` says what each one is. They are reference
|
||||
material: never commit or push from them. They sync in the background at most every
|
||||
6h; run `ddev claude-refs` before trusting anything time-sensitive.
|
||||
```
|
||||
|
||||
The container still needs to resolve and reach the Git host (and the MCP host, if used).
|
||||
|
||||
## Installed managed files
|
||||
|
||||
.ddev/config.codex-cli.yaml
|
||||
.ddev/config.claude-code.yaml
|
||||
.ddev/config.codex-claude.yaml
|
||||
.ddev/commands/web/codex
|
||||
.ddev/commands/web/claude
|
||||
.ddev/commands/web/claude-swap
|
||||
.ddev/commands/web/claude-refs
|
||||
.ddev/codex-cli/install.sh
|
||||
.ddev/claude-code/install.sh
|
||||
.ddev/claude-code/install-swap.sh
|
||||
.ddev/claude-code/refs-sync.sh
|
||||
.ddev/claude-code/git-exclude.sh
|
||||
|
||||
## Notes
|
||||
## 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.
|
||||
|
||||
`.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
|
||||
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`,
|
||||
`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`
|
||||
- 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.
|
||||
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.
|
||||
|
||||
## macOS / Linux / WSL2
|
||||
|
||||
|
||||
Reference in new issue
Block a user