Skip to content

[BUG] Claude hook ignores CLAUDE_CONFIG_DIR: profile settings unread, and hook install writes to the wrong profile #1418

Description

@fzipi

Bug Description

The Claude hook code resolves the user-level settings file as a hardcoded ~/.claude/settings.json and never consults the CLAUDE_CONFIG_DIR environment variable. Claude Code treats CLAUDE_CONFIG_DIR as a full replacement for ~/.claude, so anyone running more than one Claude profile (separate accounts / config dirs, selected per project tree) hits two problems:

  1. Settings are invisible. A basicMemory block in the active profile's settings.json is never read. hook status reports settings: not found and primary project: (not set), and capture silently falls back to the default project.
  2. bm hook install writes to the wrong profile. It edits ~/.claude/settings.json regardless of which profile is active, so installing hooks while working under profile B modifies profile A's configuration — a different account's settings file.

There is no CLAUDE_CONFIG_DIR reference anywhere in the package:

$ grep -rn "CLAUDE_CONFIG_DIR" .../site-packages/basic_memory/
(no matches)

Two sites are affected, both in basic_memory/cli/commands/hook.py:

  • load_claude_settings()hook.py:254-255
    home = Path.home()
    sources: list[tuple[Path, tuple[str, ...]]] = [(home, ("settings.json",))]
  • _hook_config_path()hook.py:1252
    if harness is Harness.claude:
        return Path.home() / ".claude" / "settings.json"

The environment variable is available to the hook process — Claude Code is launched with it exported, and hooks inherit the environment — so this is only a matter of reading it.

Steps To Reproduce

  1. Install basic-memory 0.23.2 and wire the MCP server into Claude Code.
  2. Create a second Claude profile in a non-default config dir, e.g. ~/.claude-orga, and launch Claude Code with CLAUDE_CONFIG_DIR=~/.claude-orga.
  3. Put a valid basicMemory block in ~/.claude-orga/settings.json:
    { "basicMemory": { "primaryProject": "orgA", "captureFolder": "sessions" } }
  4. Ensure no .claude/settings.json or .claude/settings.local.json exists in the project tree.
  5. Run:
    basic-memory hook status --harness claude --project-dir ~/Workspace/orgA/notes
    

Expected Behavior

The block in ~/.claude-orga/settings.json is read as the user-level base, and hook status reports:

settings (claude, ~/Workspace/orgA/notes): found
primary project: orgA

Correspondingly, bm hook install should write to $CLAUDE_CONFIG_DIR/settings.json.

Actual Behavior

settings (claude, ~/Workspace/orgA/notes): not found
primary project: (not set)

The block is ignored. Moving the same block to a project-level .claude/settings.local.json makes it resolve correctly, confirming only the user-level base is affected.

The second site is worse in effect: with profile orgA active, bm hook install writes hook entries into ~/.claude/settings.json, which belongs to profile orgB. Nothing indicates the wrong file was touched.

Environment

  • OS: macOS 15 (Darwin 25.6.0, arm64)
  • Python version: 3.12 (uv-managed tool environment)
  • Basic Memory version: 0.23.2
  • Installation method: uv tool install basic-memory
  • Claude Code with the basic-memory plugin from basicmachines-co/basic-memory

Additional Context

There is a related correctness issue beyond the two above. Because ~/.claude/settings.json is the shared user-level base for every profile, a multi-profile user cannot safely put a primaryProject there at all: a default meant for profile B is also read while working under profile A, so notes can be routed into the wrong project's graph. Honoring CLAUDE_CONFIG_DIR removes that coupling, since each profile then has its own base.

Workaround, which may be worth documenting regardless: place the basicMemory block in a project-level .claude/settings.local.json at the root of each workspace. _claude_project_dir() walks ancestors, so a single file at the top of a workspace covers every checkout beneath it, and it does not leak to sibling trees. Keep outputStyle in the profile's own settings.json — Claude Code does honor CLAUDE_CONFIG_DIR for its own settings, so only the basicMemory block needs relocating.

Possible Solution

Add a helper and use it at both sites:

def _claude_user_dir() -> Path:
    """User-level Claude config dir. CLAUDE_CONFIG_DIR replaces ~/.claude entirely."""
    override = os.environ.get("CLAUDE_CONFIG_DIR", "").strip()
    return Path(override).expanduser() if override else Path.home() / ".claude"
  • load_claude_settings(): use it for the user-level source, and for the project != home comparison so a project sitting at the config dir is not added twice.
  • _hook_config_path(): return _claude_user_dir() / "settings.json".

Falling back to ~/.claude when the variable is unset keeps existing single-profile setups unchanged. Happy to send a PR if that direction looks right.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions