Cloud multi-repo sessions never load repo .claude/settings.json (hooks silently don't fire), and CLAUDE_PROJECT_DIR is empty

Status Open
Maintainer reply None cached
Activity 1 comment · opened Jul 17, 2026

Environment

  • Claude Code on the web (claude.ai/code), cloud sandbox sessions
  • Repo has .claude/settings.json with SessionStart hooks (context injection)
  • Same configuration works correctly in local CLI sessions and in single-repo cloud sessions

Summary

Two related problems observed in multi-repo cloud sessions (a session started with two or more repositories):

  1. The repo's .claude/settings.json is never loaded. Project settings discovery appears to be cwd-based, and in multi-repo sessions the cwd is the parent directory (/home/user), not a repo root — so <repo>/.claude/settings.json is never discovered. The multi-repo registration mechanism does load the repo's CLAUDE.md, .claude/rules/, .claude/skills/, and plugins (which live at their own paths), which makes the failure easy to misdiagnose: most of .claude/ visibly works, so users assume hooks should too. But anything defined only in settings.json — hooks being the most visible — silently doesn't apply.
  1. CLAUDE_PROJECT_DIR is set to an empty string in cloud sessions. Any hook command using the documented ${CLAUDE_PROJECT_DIR} pattern (or a ${CLAUDE_PROJECT_DIR:-.} fallback) resolves relative to the wrong directory when cwd is outside the repo, producing a silent "No such file" failure.

Steps to reproduce

  1. Add to <repo>/.claude/settings.json:

``json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{"type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/inject.sh"}
]
}
]
}
}
`
where
inject.sh` echoes a recognizable marker to stdout.

  1. Start a single-repo cloud session with that repo → the marker appears in context. ✅
  2. Start a multi-repo cloud session including that repo → the marker never appears; the hook is not registered at all. ❌
  3. Inside the multi-repo session, echo "[$CLAUDE_PROJECT_DIR]" prints [] (empty, not unset), and pwd is /home/user while repos live at /home/user/<repo>.

Expected behavior

  • Repo .claude/settings.json is applied in multi-repo sessions the same way CLAUDE.md / rules / skills / plugins are (or the docs state explicitly that repo settings are single-repo-session only).
  • CLAUDE_PROJECT_DIR points at the repo the hook was loaded from (per the hooks documentation: "the absolute path to the project root directory"), or is left unset rather than empty so ${CLAUDE_PROJECT_DIR:-fallback} works.

Actual behavior

  • In multi-repo sessions, no hook from any repo's .claude/settings.json fires. Only platform-level hooks (~/.claude/launcher-settings.json) run.
  • CLAUDE_PROJECT_DIR is "" in cloud sessions, so the documented substitution pattern silently resolves relative to /home/user.

Impact

Sessions that rely on SessionStart context injection (persona/config/memory bootstrapping) start silently degraded — no error is surfaced, the model behaves as though the configuration doesn't exist. Presumably other settings.json-only configuration (e.g. permissions allow/deny rules) is dropped in multi-repo sessions as well, though hooks are where we verified it.

Workaround

An environment Setup script can bridge the gap by writing the hooks into the user-level seat (which is empty in cloud sandboxes) with absolute paths:

mkdir -p /root/.claude
cat > /root/.claude/settings.json << 'EOF'
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|clear|compact",
        "hooks": [
          {"type": "command", "command": "bash /home/user/<repo>/.claude/hooks/inject.sh", "timeout": 5000}
        ]
      }
    ]
  }
}
EOF

This works, but hardcodes the clone path and must be maintained per environment.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗