CLAUDE_CONFIG_DIR semantics inconsistent: settings.json under .claude/ but skills at root
When CLAUDE_CONFIG_DIR is set, Claude Code resolves config paths inconsistently across files:
| File | Path scanned | Implies CLAUDE_CONFIG_DIR is... |
|---|---|---|
| settings.json | ${CLAUDE_CONFIG_DIR}/.claude/settings.json | parent of .claude/ (like ~) |
| User skills | ${CLAUDE_CONFIG_DIR}/skills/ | the .claude/ dir itself (like ~/.claude) |
Repro (Claude Code v2.1.122):
export CLAUDE_CONFIG_DIR=/tmp/cc-test
rm -rf "$CLAUDE_CONFIG_DIR"
mkdir -p "$CLAUDE_CONFIG_DIR/.claude/skills/foo" "$CLAUDE_CONFIG_DIR/skills/bar"
cat > "$CLAUDE_CONFIG_DIR/.claude/skills/foo/SKILL.md" <<'SKILL'
---
name: foo
description: Test skill placed under CLAUDE_CONFIG_DIR/.claude/skills/ (parent-of-.claude semantics).
---
# foo
When invoked, respond `foo invoked` and stop.
SKILL
cat > "$CLAUDE_CONFIG_DIR/skills/bar/SKILL.md" <<'SKILL'
---
name: bar
description: Test skill placed under CLAUDE_CONFIG_DIR/skills/ (config-dir-as-.claude semantics).
---
# bar
When invoked, respond `bar invoked` and stop.
SKILL
claude --setting-sources user --debug-file /tmp/cc.log -p "/foo"
# → Unknown command: /foo (skill not registered)
claude --setting-sources user -p "/bar"
# → bar invoked (skill registered and invocable)
grep "Loading skills" /tmp/cc.log
# → Loading skills from: managed=..., user=/tmp/cc-test/skills, project=[...]
The debug log confirms user-level skills are scanned at ${CLAUDE_CONFIG_DIR}/skills/ while settings.json resolves under ${CLAUDE_CONFIG_DIR}/.claude/.
Expected: consistent semantics — either both under .claude/ (treating CLAUDE_CONFIG_DIR as a parent) or both at the root (treating it as the .claude dir itself).
Impact: containerized/sandboxed Claude Code installs that anchor CLAUDE_CONFIG_DIR to a custom path lose user-level skill discovery without the inconsistency being documented anywhere. The official docs don't describe CLAUDE_CONFIG_DIR's layout in detail. Related: #3833.
Suggested fix: unify the path semantics, or document the layout explicitly so consumers can pick a CLAUDE_CONFIG_DIR value that works for both.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗