Skip to content
Open
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
21 changes: 21 additions & 0 deletions src/agent-persistence/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,33 @@ Persist configurations for coding agents (Claude Code, Codex, Gemini Code Assist

| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| scope | Volume scope: `shared` uses one volume across all containers, `per-project` isolates data per dev container within the volume. | string | shared |
| claude | Mount Claude Code config (`~/.claude`) via volume. | boolean | true |
| codex | Mount Codex config (`~/.codex`) via volume. | boolean | true |
| gemini | Mount Gemini Code Assist config (`~/.gemini`) and caches (`~/.cache/google-vscode-extension`, `~/.cache/cloud-code`) via volumes. | boolean | true |
| github-cli | Mount GitHub CLI config (`~/.config/gh`) via volume. | boolean | true |

## Per-project scope

By default, all dev containers share a single persistence volume. With `"scope": "per-project"`, each dev container gets an isolated subdirectory within the volume, so credentials and configuration don't leak between projects.

```json
"features": {
"ghcr.io/fjktkm/devcontainer-features/agent-persistence:1": {
"scope": "per-project"
}
}
```

**Required:** When using `per-project` scope, you must also add `DEVCONTAINER_ID` to your `containerEnv` in `devcontainer.json`. This is needed because `${devcontainerId}` substitution is not supported inside feature metadata's `containerEnv`:

```json
"containerEnv": {
"DEVCONTAINER_ID": "${devcontainerId}"
}
```

Without this, the feature falls back to shared scope.

---

Expand Down
8 changes: 7 additions & 1 deletion src/agent-persistence/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "agent-persistence",
"version": "1.3.1",
"version": "1.4.0",
"name": "Agent Persistence",
"description": "Persist configurations for coding agents (Claude Code, Codex, Gemini Code Assist) and tools they commonly use (GitHub CLI) across dev container rebuilds using Docker volumes",
"documentationURL": "https://github.com/fjktkm/devcontainer-features/tree/main/src/agent-persistence",
Expand All @@ -14,6 +14,12 @@
"coding-agent"
],
"options": {
"scope": {
"type": "string",
"enum": ["shared", "per-project"],
"default": "shared",
"description": "Volume scope: 'shared' uses one volume across all containers (default), 'per-project' isolates data per dev container within the volume."
},
"claude": {
"type": "boolean",
"default": true,
Expand Down
1 change: 1 addition & 0 deletions src/agent-persistence/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ chmod +x "${INSTALL_DIR}/oncreate.sh"
chmod +x "${INSTALL_DIR}/poststart.sh"

cat > "${INSTALL_DIR}/env" <<EOF
export SCOPE="${SCOPE:-shared}"
export CLAUDE="${CLAUDE:-true}"
export CODEX="${CODEX:-true}"
export GEMINI="${GEMINI:-true}"
Expand Down
8 changes: 6 additions & 2 deletions src/agent-persistence/oncreate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
set -e

VOLUME_ROOT="/mnt/agent-persistence"
if [ -e "${VOLUME_ROOT}" ]; then
if [ "$SCOPE" = "per-project" ] && [ -n "$DEVCONTAINER_ID" ]; then
VOLUME_ROOT="/mnt/agent-persistence/$DEVCONTAINER_ID"
fi
if [ -e "${VOLUME_ROOT}" ] || [ "$SCOPE" = "per-project" ]; then
mkdir -p "${VOLUME_ROOT}"
TARGET_USER="$(id -un)"
TARGET_GROUP="$(id -gn)"
RUN_AS_ROOT=""
if [ "$(id -u)" -ne 0 ]; then
command -v sudo >/dev/null 2>&1 && RUN_AS_ROOT="sudo"
command -v sudo >/dev/null 2>&1 && RUN_AS_ROOT="sudo -n"
fi
if [ -n "${RUN_AS_ROOT}" ] || [ "$(id -u)" -eq 0 ]; then
${RUN_AS_ROOT} chown -R "${TARGET_USER}:${TARGET_GROUP}" "${VOLUME_ROOT}" 2>/dev/null || true
Expand Down
5 changes: 4 additions & 1 deletion src/agent-persistence/poststart.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
set -e

VOLUME_ROOT="/mnt/agent-persistence"
if [ "$SCOPE" = "per-project" ] && [ -n "$DEVCONTAINER_ID" ]; then
VOLUME_ROOT="/mnt/agent-persistence/$DEVCONTAINER_ID"
fi
if [ -e "${VOLUME_ROOT}" ]; then
TARGET_USER="$(id -un)"
TARGET_GROUP="$(id -gn)"
RUN_AS_ROOT=""
if [ "$(id -u)" -ne 0 ]; then
command -v sudo >/dev/null 2>&1 && RUN_AS_ROOT="sudo"
command -v sudo >/dev/null 2>&1 && RUN_AS_ROOT="sudo -n"
fi
if [ -n "${RUN_AS_ROOT}" ] || [ "$(id -u)" -eq 0 ]; then
${RUN_AS_ROOT} chown -R "${TARGET_USER}:${TARGET_GROUP}" "${VOLUME_ROOT}" 2>/dev/null || true
Expand Down