Skip to content

fix: respect CLAUDE_CONFIG_DIR in hooks for multi-profile setups#290

Closed
gjaya79 wants to merge 2 commits into
mksglu:nextfrom
gjaya79:fix/respect-claude-config-dir
Closed

fix: respect CLAUDE_CONFIG_DIR in hooks for multi-profile setups#290
gjaya79 wants to merge 2 commits into
mksglu:nextfrom
gjaya79:fix/respect-claude-config-dir

Conversation

@gjaya79

@gjaya79 gjaya79 commented Apr 15, 2026

Copy link
Copy Markdown

Summary

Fixes #289

Hooks hardcoded ~/.claude paths via join(homedir(), ".claude", ...), ignoring the CLAUDE_CONFIG_DIR environment variable. This broke multi-profile setups where users launch Claude Code with different config directories:

alias cc="CLAUDE_CONFIG_DIR=~/.claude-personal claude"
alias ccw="CLAUDE_CONFIG_DIR=~/.claude-work claude"

All path resolution now checks process.env.CLAUDE_CONFIG_DIR first, falling back to ~/.claude when unset. No behavior change for single-profile users.

Changes

  • session-helpers.mjs: Added getClaudeConfigDir() helper. Updated getSessionDBPath(), getSessionEventsPath(), getCleanupFlagPath() to use it for Claude opts.
  • pretooluse.mjs: Self-heal block now resolves configBase from env var for settings.json and installed_plugins.json paths.
  • sessionstart.mjs: CLAUDE.md discovery and debug log path respect the env var.
  • precompact.mjs: Debug log path respects the env var.

Test plan

  • Set CLAUDE_CONFIG_DIR=~/.claude-test and verify session DB is created under ~/.claude-test/context-mode/sessions/
  • Verify default behavior (no env var set) still uses ~/.claude/
  • Verify self-heal in pretooluse reads correct settings.json and installed_plugins.json
  • Verify CLAUDE.md is read from the correct config directory on session start

github-actions Bot and others added 2 commits April 15, 2026 18:22
Hooks hardcoded ~/.claude paths, ignoring the CLAUDE_CONFIG_DIR
environment variable. This broke setups where users run multiple
Claude Code profiles with separate config directories (e.g.,
CLAUDE_CONFIG_DIR=~/.claude-personal vs ~/.claude-work).

All path resolution now checks process.env.CLAUDE_CONFIG_DIR first,
falling back to ~/.claude when unset. Affected files:
- session-helpers.mjs: added getClaudeConfigDir(), used in
  getSessionDBPath, getSessionEventsPath, getCleanupFlagPath
- pretooluse.mjs: self-heal block (settings.json, installed_plugins)
- sessionstart.mjs: CLAUDE.md paths, debug log path
- precompact.mjs: debug log path

Fixes mksglu#289
@mksglu mksglu changed the base branch from main to next April 16, 2026 14:42
@mksglu

mksglu commented Apr 25, 2026

Copy link
Copy Markdown
Owner

Thank you @gjaya79 for identifying this issue and putting together the initial fix! 🙏

We've implemented a broader solution directly on next that addresses all platforms, not just Claude Code. Here's what landed:

What we shipped

Instead of patching CLAUDE_CONFIG_DIR in each hook file individually, we added a configDirEnv field to all 6 platform OPTS objects in session-helpers.mjs and a single resolveConfigDir(opts) helper that all session path functions now use.

Verified env vars from upstream source code:

Platform Env Var Source Evidence
Claude Code CLAUDE_CONFIG_DIR CHANGELOG.md:3074, .devcontainer/devcontainer.json:50
Gemini CLI GEMINI_CLI_HOME packages/core/src/utils/paths.ts:23
Codex CLI CODEX_HOME codex-rs/utils/home-dir/src/lib.rs:14

VS Code Copilot, Cursor, and Kiro were verified to have no config dir override env vars.

Key improvements over this PR

  • DRY: Single resolveConfigDir() function vs. 4 inline copies of the tilde-expansion pattern
  • All platforms: 3 platforms supported (Claude, Gemini, Codex) vs. only Claude
  • Tests: 7 new tests covering defaults, overrides, tilde expansion, and cross-platform isolation

The underlying issue you identified was absolutely correct — hardcoded ~/.claude paths broke multi-profile setups. Your report helped us catch that this was actually a broader gap affecting multiple platforms.

Closing in favor of the merged implementation. Thank you for the contribution!

@mksglu mksglu closed this Apr 25, 2026
mksglu added a commit that referenced this pull request Apr 28, 2026
1. Timeline sort: ContentStore results get timestamp fallback (new Date())
2. Read path no longer mutates state: knowledge-reuse write removed from
   ctx_search (read tool should not write)
3. configDir: use CLAUDE_CONFIG_DIR env var + absolute path (fixes #290 regression)
4. SessionDB: open once before query loop, close in finally (was per-query)
5. Auto-memory: 1MB file size guard on readFileSync
6. Debug logging: all catch blocks log with DEBUG=context-mode env var

Removes 3 knowledge-reuse tests that validated deleted write-on-read behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mksglu added a commit that referenced this pull request Apr 28, 2026
* feat: unified persistent memory & timeline search (#363)

Two pillars for session continuity after compaction:

Pillar 1 — Unified Timeline Search:
- ctx_search gains sort="timeline" mode searching 3 sources chronologically
- ContentStore (current session) + SessionDB (prior sessions) + auto-memory
- Default sort="relevance" preserves current behavior exactly
- New searchAllSources() in src/search/unified.ts
- SessionDB.searchEvents() with project_dir filter + LIKE escaping
- searchAutoMemory() with adapter-aware dash-separated paths
- FTS5 schema migration: 4 new UNINDEXED columns (source_category, session_id, event_id, timestamp)

Pillar 2 — Auto-Injection on Compaction:
- buildAutoInjection() fires ONLY on SessionStart(source="compact")
- <behavioral_directive> for roles, <rules> for decisions, <active_skills> for skills
- 500 token hard cap with priority-based overflow strategy
- Never fires mid-session — context-mode does not pollute the context it protects

Static Reinforcement (~99 tokens, session start only):
- 12 adapter configs: Session Continuity + MEMORY sections (4 tiers by hook support)
- routing-block.mjs: <session_continuity> tag + tool hierarchy 0. MEMORY
- ctx_search tool description: SESSION STATE reminder

Bug Fixes:
- snapshot.ts: add missing case "role" + buildRolesSection()
- extract.ts: skill priority P3 → P2
- pi-extension.ts: minPriority 2 → 3

12 New Categories:
Phase 1: user-prompt, compaction, rejected-approach, session-resume, constraint, knowledge-reuse
Phase 2: agent-finding, error-resolution, external-ref, blocked-on, iteration-loop, latency

8 review rounds, 16+ engineers per round, TDD-first implementation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: move rejected-approach from pretooluse to posttooluse via marker file

PreToolUse cannot safely load SessionDB (native module loading breaks
hook stdout on CI). Use tmpdir marker file pattern (same as latency)
instead: pretooluse writes marker, posttooluse reads and writes event.

Also implements knowledge-reuse event (search_hit_prior) with sessionId
wiring and 3 new tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: wire searchAllSources into ctx_search handler + skip empty guard for timeline

Two critical wiring bugs found by final review:
1. searchAllSources was imported but never called — sort="timeline" silently
   fell back to ContentStore-only search
2. Empty-index guard blocked timeline mode even when SessionDB had data

Now sort="timeline" creates a read-only SessionDB connection and calls
searchAllSources() for unified 3-source search. Default sort="relevance"
unchanged — calls store.searchWithFallback() directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: correct tool name prefixes in qwen-code, openclaw, kilo adapter configs

- qwen-code: bare ctx_* → mcp__context-mode__ctx_* (22 refs)
- openclaw: bare ctx_search → context-mode__ctx_search (line 49)
- kilo: bare ctx_search → context-mode_ctx_search (line 49)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review — 6 blockers resolved

1. Timeline sort: ContentStore results get timestamp fallback (new Date())
2. Read path no longer mutates state: knowledge-reuse write removed from
   ctx_search (read tool should not write)
3. configDir: use CLAUDE_CONFIG_DIR env var + absolute path (fixes #290 regression)
4. SessionDB: open once before query loop, close in finally (was per-query)
5. Auto-memory: 1MB file size guard on readFileSync
6. Debug logging: all catch blocks log with DEBUG=context-mode env var

Removes 3 knowledge-reuse tests that validated deleted write-on-read behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: timeline timestamp correctness + output format + CI fix

1. auto-memory: set timestamp from file mtime (was missing entirely)
2. ContentStore: use sessionStartTime instead of new Date() per-result
3. Normalize SQLite datetime format to ISO before sort
4. Output headers show origin + timestamp: [prior-session | 2026-04-27 14:30 | source]
5. pretooluse.mjs: move marker writes BEFORE stdout (fixes macOS CI timeout)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: macOS CI + auto-memory match quality + snippet boundaries

1. macOS CI: test sentinel path uses /tmp (matching sentinelDir()) instead
   of tmpdir() which returns /var/folders/... on macOS — fixes isMCPReady()
   returning false in CI, causing all redirect assertions to fail
2. Auto-memory: raise minimum term length from 2 to 3 chars, use word
   boundary regex instead of String.includes() (prevents "if" matching
   "specific", "definition", etc.)
3. Snippet extraction: walk to nearest paragraph boundary (\n\n) instead
   of cutting mid-word at arbitrary byte offset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: surface real timestamps from FTS5 chunks for correct timeline sort

ContentStore search results now carry actual indexing timestamps instead
of falling back to Date.now(). Changes:

1. store.ts: add chunks.timestamp to all 12 search SQL SELECT statements
2. store.ts: populate timestamp on INSERT with new Date().toISOString()
3. types.ts: add timestamp to SearchResult interface
4. unified.ts: use r.timestamp from ContentStore, fallback sessionStartTime

Timeline sort now produces correct chronological ordering across all 3
sources (ContentStore indexed_at, SessionDB created_at, auto-memory mtime).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove session bundle files from PR

Session bundles (session-db, session-extract, session-snapshot) are
CI-generated artifacts. Only server.bundle.mjs and cli.bundle.mjs
belong in commits. CI will regenerate session bundles on merge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove PRD from PR — belongs in issues, not in repo

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hooks ignore CLAUDE_CONFIG_DIR — hardcode ~/.claude paths

3 participants