Skip to content

Commit ce50168

Browse files
fix(engine): write skills/sub-agents to ${HOME}/.claude regardless of CLAUDE_CONFIG_DIR
setup-claude.sh writes per-pod settings.json, MCP config, custom skills, and sub-agents into either ${CLAUDE_CONFIG_DIR}/ (when set for session persistence) or ${HOME}/.claude/ (when not). Claude Code respects CLAUDE_CONFIG_DIR for the first two, but its slash-command skill and sub-agent discovery is hardcoded to read from the HOME-relative paths ${HOME}/.claude/skills/ and ${HOME}/.claude/agents/, irrespective of CLAUDE_CONFIG_DIR. The result on session-persistence-enabled deployments: skills were correctly delivered into the pod's PVC mount but invisible to the agent — invocations failed with "Unknown skill: <name>". The fix moves both directories under ${HOME}/.claude/ unconditionally. Skills and sub-agents are deterministically regenerated from the CLAUDE_SKILL_* and CLAUDE_SUBAGENT_PATH_* env vars at every pod start — the env vars are set by the controller per Job, so a restart rebuilds them identically. They don't benefit from PVC persistence the way the session JSONLs do, so the previous co-location of all config under ${CLAUDE_DIR} was incidental rather than intentional. settings.json, the MCP config, and CLAUDE_CONFIG_DIR's export are unchanged — those genuinely live under whichever directory the operator pointed at, and Claude Code reads them from there. Behaviour change: skill/sub-agent files no longer take PVC space on deployments that enable session persistence. Operators who relied on in-place edits surviving pod restarts (an unintended emergent property of the previous layout) will see those edits regenerated from env vars. Anyone authoring skills should be doing so via the controller's config (inline or ConfigMap-backed) rather than editing the file inside the running pod, so this is closer to the documented contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b741e67 commit ce50168

2 files changed

Lines changed: 37 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2828

2929
### Fixed
3030

31+
- **Skill and sub-agent discovery under session persistence**:
32+
`setup-claude.sh` previously wrote `CLAUDE_SKILL_*` and
33+
`CLAUDE_SUBAGENT_PATH_*` files to `${CLAUDE_CONFIG_DIR:-${HOME}/.claude}/skills/`
34+
and `${CLAUDE_CONFIG_DIR:-${HOME}/.claude}/agents/`. When session
35+
persistence was enabled (`CLAUDE_CONFIG_DIR` set to a PVC-backed
36+
path), the files landed there but were invisible to Claude Code,
37+
whose slash-command discovery reads from the HOME-relative paths
38+
`${HOME}/.claude/skills/` and `${HOME}/.claude/agents/` regardless
39+
of `CLAUDE_CONFIG_DIR`. The agent then surfaced the file as
40+
"Unknown skill: <name>" on `/skill-name` invocations. The script
41+
now writes both directories under `${HOME}/.claude/` always —
42+
these files are deterministically regenerated from the env vars at
43+
every pod start, so they don't need PVC persistence the way
44+
session JSONL files do.
3145
- **Review poller acting on bot summary comments**: the classifier now only
3246
ignores bot comments that lack a file position (summaries, coverage reports).
3347
Inline diff comments from bots (e.g. CodeRabbit review suggestions) are still

docker/engine-claude-code/setup-claude.sh

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,50 @@ cp /etc/claude-code/mcp.json /workspace/.mcp.json
5050
# path is in use. A no-op when CLAUDE_CONFIG_DIR was already set in the env.
5151
export CLAUDE_CONFIG_DIR="${CLAUDE_DIR}"
5252

53-
# Write custom skill files if any skill env vars are present.
53+
# Write custom skill files if any skill env vars are present. Skills go
54+
# to ${HOME}/.claude/skills/ — not ${CLAUDE_DIR}/skills/ — because
55+
# Claude Code's slash-command discovery reads from the HOME-relative
56+
# path regardless of CLAUDE_CONFIG_DIR. Writing to ${CLAUDE_DIR}/skills/
57+
# under session persistence (CLAUDE_CONFIG_DIR set) leaves the file on
58+
# the PVC but invisible to the agent: invocations fail with "Unknown
59+
# skill: <name>". Skills are deterministically regenerated from the
60+
# CLAUDE_SKILL_* env vars at every pod start, so they don't need PVC
61+
# persistence the way session JSONL files do.
62+
#
5463
# Inline skills: CLAUDE_SKILL_INLINE_<NAME>=<base64-encoded Markdown>
5564
# Path skills: CLAUDE_SKILL_PATH_<NAME>=<path on image>
5665
if env | grep -q '^CLAUDE_SKILL_'; then
57-
mkdir -p "${CLAUDE_DIR}/skills"
66+
SKILLS_DIR="${HOME}/.claude/skills"
67+
mkdir -p "${SKILLS_DIR}"
5868

5969
# Write inline skills (base64-decoded content).
6070
for var in $(env | grep '^CLAUDE_SKILL_INLINE_' | sed 's/=.*//'); do
6171
name=$(printf '%s' "$var" | sed 's/^CLAUDE_SKILL_INLINE_//' | tr '[:upper:]' '[:lower:]' | tr '_' '-')
62-
printenv "$var" | base64 -d > "${CLAUDE_DIR}/skills/${name}.md"
72+
printenv "$var" | base64 -d > "${SKILLS_DIR}/${name}.md"
6373
done
6474

6575
# Copy path-based skills from the image.
6676
for var in $(env | grep '^CLAUDE_SKILL_PATH_' | sed 's/=.*//'); do
6777
name=$(printf '%s' "$var" | sed 's/^CLAUDE_SKILL_PATH_//' | tr '[:upper:]' '[:lower:]' | tr '_' '-')
6878
path=$(printenv "$var")
69-
[ -f "$path" ] && cp "$path" "${CLAUDE_DIR}/skills/${name}.md"
79+
[ -f "$path" ] && cp "$path" "${SKILLS_DIR}/${name}.md"
7080
done
7181
fi
7282

73-
# Write ConfigMap-backed sub-agent files to ~/.claude/agents/.
83+
# Write ConfigMap-backed sub-agent files to ${HOME}/.claude/agents/.
84+
# Same HOME-relative reasoning as skills above: Claude Code's sub-agent
85+
# discovery uses the HOME-relative path regardless of CLAUDE_CONFIG_DIR,
86+
# so writing under ${CLAUDE_DIR} when session persistence is on would
87+
# make the sub-agent invisible to the agent.
88+
#
7489
# Sub-agent env vars: CLAUDE_SUBAGENT_PATH_<NAME>=<path on volume>
7590
if env | grep -q '^CLAUDE_SUBAGENT_PATH_'; then
76-
mkdir -p "${CLAUDE_DIR}/agents"
91+
AGENTS_DIR="${HOME}/.claude/agents"
92+
mkdir -p "${AGENTS_DIR}"
7793
for var in $(env | grep '^CLAUDE_SUBAGENT_PATH_' | sed 's/=.*//'); do
7894
name=$(printf '%s' "$var" | sed 's/^CLAUDE_SUBAGENT_PATH_//' | tr '[:upper:]' '[:lower:]' | tr '_' '-')
7995
path=$(printenv "$var")
80-
[ -f "$path" ] && cp "$path" "${CLAUDE_DIR}/agents/${name}.md"
96+
[ -f "$path" ] && cp "$path" "${AGENTS_DIR}/${name}.md"
8197
done
8298
fi
8399

0 commit comments

Comments
 (0)