From 5d949624c0b783844af557c9fa22a6a558c379e8 Mon Sep 17 00:00:00 2001 From: Peter Wagstaff Date: Mon, 23 Feb 2026 19:06:18 -0800 Subject: [PATCH 1/2] fix: use non-interactive sudo to prevent password prompts Use sudo -n so the scripts fail silently instead of blocking on a password prompt in containers without passwordless sudo. Co-Authored-By: Claude Opus 4.6 --- src/agent-persistence/oncreate.sh | 2 +- src/agent-persistence/poststart.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/agent-persistence/oncreate.sh b/src/agent-persistence/oncreate.sh index 49335f5..0e32d0b 100644 --- a/src/agent-persistence/oncreate.sh +++ b/src/agent-persistence/oncreate.sh @@ -8,7 +8,7 @@ if [ -e "${VOLUME_ROOT}" ]; then 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 diff --git a/src/agent-persistence/poststart.sh b/src/agent-persistence/poststart.sh index 582b970..07e295b 100644 --- a/src/agent-persistence/poststart.sh +++ b/src/agent-persistence/poststart.sh @@ -8,7 +8,7 @@ if [ -e "${VOLUME_ROOT}" ]; then 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 From 48167fb540a694991cca27e7bbb190eaef43a835 Mon Sep 17 00:00:00 2001 From: Peter Wagstaff Date: Mon, 23 Feb 2026 19:07:03 -0800 Subject: [PATCH 2/2] feat: add per-project scope option for volume isolation Add a "scope" option (default: "shared") that controls whether all containers share the same persistence data or each gets an isolated subdirectory within the volume keyed by DEVCONTAINER_ID. When scope is "per-project", the scripts use /mnt/agent-persistence/$DEVCONTAINER_ID/ instead of the volume root. This requires DEVCONTAINER_ID to be set in the project's containerEnv since ${devcontainerId} substitution is not supported in feature containerEnv metadata. Co-Authored-By: Claude Opus 4.6 --- src/agent-persistence/README.md | 21 +++++++++++++++++++ .../devcontainer-feature.json | 8 ++++++- src/agent-persistence/install.sh | 1 + src/agent-persistence/oncreate.sh | 6 +++++- src/agent-persistence/poststart.sh | 3 +++ 5 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/agent-persistence/README.md b/src/agent-persistence/README.md index 3376ea9..57661fa 100644 --- a/src/agent-persistence/README.md +++ b/src/agent-persistence/README.md @@ -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. --- diff --git a/src/agent-persistence/devcontainer-feature.json b/src/agent-persistence/devcontainer-feature.json index 9955bc1..582e2dd 100644 --- a/src/agent-persistence/devcontainer-feature.json +++ b/src/agent-persistence/devcontainer-feature.json @@ -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", @@ -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, diff --git a/src/agent-persistence/install.sh b/src/agent-persistence/install.sh index ee9dd82..65fb608 100644 --- a/src/agent-persistence/install.sh +++ b/src/agent-persistence/install.sh @@ -18,6 +18,7 @@ chmod +x "${INSTALL_DIR}/oncreate.sh" chmod +x "${INSTALL_DIR}/poststart.sh" cat > "${INSTALL_DIR}/env" <