Skip to content

fix: respect CLAUDE_CONFIG_DIR across remaining code paths#2156

Merged
Yeachan-Heo merged 1 commit intoYeachan-Heo:devfrom
relvinhas:fix/claude-config-dir-runtime-paths
Apr 5, 2026
Merged

fix: respect CLAUDE_CONFIG_DIR across remaining code paths#2156
Yeachan-Heo merged 1 commit intoYeachan-Heo:devfrom
relvinhas:fix/claude-config-dir-runtime-paths

Conversation

@relvinhas
Copy link
Copy Markdown
Contributor

@relvinhas relvinhas commented Apr 4, 2026

Summary

PR #2086 partially addressed issue #2084 by fixing installer/setup profile handling. Several runtime code paths identified in that issue scope still hardcoded ~/.claude instead of using the active Claude Code config directory.

This continues the work started in PR #563 (initial getConfigDir() utility), PR #898 (HUD plugin cache), and PR #1125 (Keychain service name), and addresses all remaining runtime code paths identified in the issue #2155.

Shared helper

  • config-dir helper: implement full getClaudeConfigDir() in config-dir.ts with ~ expansion and trailing-slash normalisation, replacing the trivial getConfigDir() wrapper; remove the re-export from paths.ts (src/utils/config-dir.ts)

TypeScript runtime

  • transcript validation: allow paths under the active config directory (worktree-paths.ts#L425)
  • transcript fallback resolution: build projects/ lookups from the active config directory (worktree-paths.ts#L592, #L630)
  • rules discovery: use getClaudeConfigDir() instead of hardcoded path; remove the dead homeDir parameter from findRuleFiles and the stale USER_RULE_DIR export (finder.ts#L226)
  • CLI: use getClaudeConfigDir() for runtime paths and help text (default: ~/.claude/)
  • orchestrator: allow absolute paths under the active config directory while preserving project-root and relative-path checks (index.ts#L129)
  • skills tools: reference the active config directory in runtime output and help text (skills-tools.ts#L122, #L160)
  • shared memory: read .omc-config.json from the active config directory (shared-memory.ts#L63)
  • session history search: resolve transcript roots from the active config directory (index.ts#L36)
  • auto-slash-command executor: replace hardcoded ~/.claude in error message output (executor.ts#L356)
  • factcheck guard: add ${CLAUDE_CONFIG_DIR} token to forbidden_path_prefixes defaults and token expander, replacing the hardcoded ${HOME}/.claude prefix (config.ts#L20,#L53)
  • HUD usage API: document and test that Keychain service names continue to hash the exact CLAUDE_CONFIG_DIR string, preserving distinct service-name mapping for ~-prefixed vs expanded inputs (PR fix: support CLAUDE_CONFIG_DIR in HUD Keychain credential lookup #1125 behaviour)(usage-api.ts)
  • installer: copy config-dir helpers alongside standalone hooks and the HUD find-node.sh helper (index.ts)

Runtime scripts

  • Node ESM scripts: use a shared lib/config-dir.mjs helper instead of repeating ~/.claude fallbacks (scripts/)
  • Node CJS helper: add scripts/lib/config-dir.cjs so persistent-mode.cjs reuses the same logic
  • standalone hook templates: add templates/hooks/lib/config-dir.mjs (mirrors scripts/lib/config-dir.mjs) so template scripts resolve the config directory consistently
  • HUD wrapper: import the shared lib/config-dir helper instead of embedding duplicate resolution logic (plugin-setup.mjs)
  • persistent-mode: resolve native tasks/todos from the active config directory
  • plugin setup (incidental): move nodeBin to module scope so both the settings.json and hooks.json patching blocks can access it — pre-existing const scoping bug where hooks.json patching always failed silently with a ReferenceError(plugin-setup.mjs#L229)

Shell scripts

Skill files

Tests

  • align mock paths from paths.js to config-dir.js to match the new import source
  • fix vi.mock hoisting in doctor-conflicts.test.ts
  • resolve macOS /var symlink mismatch in bridge-integration.test.ts, edge-cases.test.ts, and team-status.test.ts
  • update string-pattern assertions in hud-windows.test.ts and mingw-escape.test.ts
  • use getClaudeConfigDir() or the exported CLAUDE_CONFIG_DIR constant in auto-update, installer, delegation enforcement, factcheck, and team integration tests so the suite passes under custom config directories

The helper is implemented in src/utils/config-dir.ts (canonical TypeScript source) and mirrored across three runtime surfaces:

  • scripts/lib/config-dir.mjs (ESM hooks/HUD)
  • scripts/lib/config-dir.cjs (CJS for persistent-mode.cjs)
  • scripts/lib/config-dir.sh (POSIX shell)

Each mirror exists because the runtime surfaces cannot share a single module: standalone hook scripts run as plain ESM outside the compiled TypeScript bundle, persistent-mode.cjs requires CJS require(), and shell scripts have no Node runtime at all. The logic is intentionally small (~20 lines per surface) so keeping them in sync manually is straightforward; code generation was considered but would add build-pipeline complexity disproportionate to four short files. Each mirror carries a "keep in sync" header comment pointing to its siblings.

templates/hooks/lib/config-dir.mjs is a byte-identical copy of scripts/lib/config-dir.mjs. It exists so that template-based standalone installs (which copy the templates/hooks/ tree without running the full installer) have a working helper in place. The installer's ensureStandaloneHookScripts also copies the canonical scripts/lib/config-dir.mjs into the hooks lib/ directory at install time, so both paths converge on the same file. This follows the existing pattern of stdin.mjs and atomic-write.mjs in templates/hooks/lib/.

Compatibility note: the findRuleFiles(projectRoot, homeDir, currentFile) signature and the USER_RULE_DIR export have been removed from the rules-injector surface. There are no known downstream consumers, but maintainers should treat that as a small exported-surface break when choosing release semantics.
If backwards compatibility is a concern, the old signature could be kept as a deprecated overload that ignores homeDir and forwards to the new two-argument form.

Testing

npm run lint -- \
  src/__tests__/auto-update.test.ts \
  src/__tests__/cli-config-stop-callback.test.ts \
  src/__tests__/config-dir.test.ts \
  src/__tests__/delegation-enforcement-levels.test.ts \
  src/__tests__/doctor-conflicts.test.ts \
  src/__tests__/hud-api-key-source.test.ts \
  src/__tests__/hud-marketplace-resolution.test.ts \
  src/__tests__/hud-windows.test.ts \
  src/__tests__/hud/cli-diagnostic.test.ts \
  src/__tests__/hud/usage-api-lock.test.ts \
  src/__tests__/hud/usage-api-stale.test.ts \
  src/__tests__/hud/usage-api.test.ts \
  src/__tests__/installer.test.ts \
  src/__tests__/purge-stale-cache.test.ts \
  src/__tests__/session-history-search.test.ts \
  src/__tests__/setup-claude-md-script.test.ts \
  src/__tests__/shared-memory.test.ts \
  src/cli/index.ts \
  src/features/session-history-search/index.ts \
  src/hooks/auto-slash-command/executor.ts \
  src/hooks/factcheck/config.ts \
  src/hooks/factcheck/__tests__/factcheck.test.ts \
  src/hooks/index.ts \
  src/hooks/omc-orchestrator/constants.ts \
  src/hooks/omc-orchestrator/index.ts \
  src/hooks/rules-injector/constants.ts \
  src/hooks/rules-injector/finder.ts \
  src/hooks/rules-injector/index.ts \
  src/hud/usage-api.ts \
  src/installer/__tests__/standalone-hook-reconcile.test.ts \
  src/installer/index.ts \
  src/lib/shared-memory.ts \
  src/lib/worktree-paths.ts \
  src/notifications/__tests__/config-merge.test.ts \
  src/notifications/__tests__/profiles.test.ts \
  src/openclaw/__tests__/config.test.ts \
  src/skills/__tests__/mingw-escape.test.ts \
  src/team/__tests__/bridge-integration.test.ts \
  src/team/__tests__/edge-cases.test.ts \
  src/team/__tests__/inbox-outbox.test.ts \
  src/team/__tests__/message-router.test.ts \
  src/team/__tests__/outbox-reader.test.ts \
  src/team/__tests__/team-registration.test.ts \
  src/team/__tests__/team-status.test.ts \
  src/tools/shared-memory-tools.ts \
  src/tools/skills-tools.ts \
  src/utils/paths.ts

npm test -- --run \
  src/__tests__/auto-update.test.ts \
  src/__tests__/cli-config-stop-callback.test.ts \
  src/__tests__/config-dir.test.ts \
  src/__tests__/delegation-enforcement-levels.test.ts \
  src/__tests__/doctor-conflicts.test.ts \
  src/__tests__/hud-api-key-source.test.ts \
  src/__tests__/hud-marketplace-resolution.test.ts \
  src/__tests__/hud-windows.test.ts \
  src/__tests__/hud/cli-diagnostic.test.ts \
  src/__tests__/hud/usage-api-lock.test.ts \
  src/__tests__/hud/usage-api-stale.test.ts \
  src/__tests__/hud/usage-api.test.ts \
  src/__tests__/installer-hooks-merge.test.ts \
  src/__tests__/installer.test.ts \
  src/__tests__/keyword-detector-script.test.ts \
  src/__tests__/pipeline-orchestrator.test.ts \
  src/__tests__/plugin-setup-deps.test.ts \
  src/__tests__/plugin-setup-devpaths.test.ts \
  src/__tests__/post-tool-verifier.test.mjs \
  src/__tests__/pre-tool-enforcer.test.ts \
  src/__tests__/purge-stale-cache.test.ts \
  src/__tests__/session-history-search.test.ts \
  src/__tests__/session-start-cache-cleanup.test.ts \
  src/__tests__/session-start-script-context.test.ts \
  src/__tests__/session-start-timeout-cleanup.test.ts \
  src/__tests__/setup-claude-md-script.test.ts \
  src/__tests__/setup-progress-script.test.ts \
  src/__tests__/shared-memory-concurrency.test.ts \
  src/__tests__/shared-memory.test.ts \
  src/__tests__/skills.test.ts \
  src/__tests__/tools/skills-tools.test.ts \
  src/hooks/factcheck/__tests__/factcheck.test.ts \
  src/installer/__tests__/hook-templates.test.ts \
  src/installer/__tests__/safe-installer.test.ts \
  src/installer/__tests__/session-start-template.test.ts \
  src/installer/__tests__/standalone-hook-reconcile.test.ts \
  src/notifications/__tests__/config-merge.test.ts \
  src/notifications/__tests__/profiles.test.ts \
  src/openclaw/__tests__/config.test.ts \
  src/skills/__tests__/mingw-escape.test.ts \
  src/team/__tests__/bridge-integration.test.ts \
  src/team/__tests__/edge-cases.test.ts \
  src/team/__tests__/inbox-outbox.test.ts \
  src/team/__tests__/message-router.test.ts \
  src/team/__tests__/outbox-reader.test.ts \
  src/team/__tests__/team-registration.test.ts \
  src/team/__tests__/team-status.test.ts

bash -n scripts/find-node.sh scripts/lib/config-dir.sh \
  scripts/setup-claude-md.sh scripts/setup-progress.sh \
  scripts/test-pr25.sh scripts/uninstall.sh

npm exec tsc -- --noEmit

Focused regression coverage in:

  • src/__tests__/auto-update.test.ts — plugin cache sync paths under custom config directory
  • src/__tests__/cli-config-stop-callback.test.ts — CLI default file-callback path
  • src/__tests__/config-dir.test.ts — shared helper expansion (TS, MJS, shell), trailing-slash stripping, downstream integration (transcript validation, rules discovery)
  • src/__tests__/delegation-enforcement-levels.test.ts — orchestrator absolute-path allowance, CLAUDE_CONFIG_DIR env isolation
  • src/__tests__/doctor-conflicts.test.ts — hook ownership classification under custom config directory; vi.hoisted() fix for mock factory
  • src/__tests__/hud-api-key-source.test.ts — mock path alignment (paths.js → config-dir.js)
  • src/__tests__/hud-marketplace-resolution.test.ts — HUD wrapper helper copy
  • src/__tests__/hud-windows.test.ts — string pattern assertions updated for getClaudeConfigDir() and CLAUDE_CONFIG_DIR shell expansions
  • src/__tests__/hud/cli-diagnostic.test.ts — mock path alignment (paths.js → config-dir.js)
  • src/__tests__/hud/usage-api-lock.test.ts — mock path alignment (paths.js → config-dir.js)
  • src/__tests__/hud/usage-api-stale.test.ts — mock path alignment (paths.js → config-dir.js)
  • src/__tests__/hud/usage-api.test.ts — Keychain service-name hashing unchanged (PR fix: support CLAUDE_CONFIG_DIR in HUD Keychain credential lookup #1125 behaviour preserved)
  • src/__tests__/installer.test.ts — file path assertions, isProjectScopedPlugin under custom config directory
  • src/__tests__/purge-stale-cache.test.ts — mock function name alignment (getConfigDir → getClaudeConfigDir)
  • src/__tests__/session-history-search.test.ts — transcript root resolution
  • src/__tests__/setup-claude-md-script.test.ts — shell flow with ~-prefixed CLAUDE_CONFIG_DIR
  • src/__tests__/shared-memory.test.ts — shared-memory reads from active config directory
  • src/hooks/factcheck/__tests__/factcheck.test.ts — forbidden path prefix uses getClaudeConfigDir(), ${CLAUDE_CONFIG_DIR} token expansion
  • src/installer/__tests__/standalone-hook-reconcile.test.ts — standalone hooks lib config-dir.mjs installed alongside hooks
  • src/notifications/__tests__/config-merge.test.ts — mock path alignment (paths.js → config-dir.js)
  • src/notifications/__tests__/profiles.test.ts — mock path alignment (paths.js → config-dir.js)
  • src/openclaw/__tests__/config.test.ts — mock path alignment (paths.js → config-dir.js)
  • src/skills/__tests__/mingw-escape.test.ts — SKILL.md shell snippet assertion updated for CLAUDE_CONFIG_DIR expansion
  • src/team/__tests__/bridge-integration.test.ts — bridge lifecycle under custom config directory; macOS /var symlink fix
  • src/team/__tests__/edge-cases.test.ts — inbox-outbox and registration edge cases under custom config directory; macOS /var symlink fix (realpathSync)
  • src/team/__tests__/inbox-outbox.test.ts — team inbox/outbox filesystem operations under custom config directory
  • src/team/__tests__/message-router.test.ts — inbox routing to MCP workers under custom config directory
  • src/team/__tests__/outbox-reader.test.ts — outbox cursor and message reads under custom config directory
  • src/team/__tests__/team-registration.test.ts — worker registration under custom config directory
  • src/team/__tests__/team-status.test.ts — mock path alignment (paths.js → config-dir.js); macOS /var symlink fix (realpathSync)

Closes #2155.

@relvinhas relvinhas marked this pull request as ready for review April 4, 2026 07:18
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

Several code paths hardcoded ~/.claude instead of resolving the active config directory. Introduce
getClaudeConfigDir() as a shared helper (with ESM/CJS/shell mirrors in scripts/lib/) and update
all call sites:

- transcript validation: allow paths under the active config directory
- transcript fallback resolution: build projects/ lookups from the active config directory instead
  of inline env-var fallback
- rules discovery: use getClaudeConfigDir() instead of hardcoded path
- rules injector: remove dead homeDir parameter from findRuleFiles and USER_RULE_DIR export;
  breaking change to the public surface (no npm dependents)
- uninstall: preserve existing CLAUDE_CONFIG_DIR env var
- CLI: use getClaudeConfigDir() for runtime paths and config directory descriptions
  (default: ~/.claude/)
- orchestrator: allow absolute paths under the active config directory while preserving
  project-root and relative-path checks
- skills tools: reference the active config directory in runtime output and help text
- shared memory: read .omc-config.json from the active config directory
- session history search: resolve transcript roots from the active config directory
- auto-slash-command executor: replace hardcoded ~/.claude in error message output
- factcheck guard: add ${CLAUDE_CONFIG_DIR} token to forbidden_path_prefixes defaults and token
  expander, replacing the hardcoded ${HOME}/.claude prefix
- config-dir helper: implement full getClaudeConfigDir() in config-dir.ts with tilde expansion
  and trailing-slash normalization, replacing the trivial getConfigDir() wrapper and the re-export
  from paths.ts
- runtime Node scripts: use shared config-dir helpers (ESM and CJS) instead of repeating
  ~/.claude fallbacks
- standalone hook templates: add templates/hooks/lib/config-dir.mjs shared helper (mirrors
  scripts/lib/config-dir.mjs) so template scripts resolve the config directory consistently
- HUD wrapper: import the shared lib/config-dir helper instead of embedding duplicate
  config-directory resolution logic
- persistent-mode scripts: resolve native tasks/todos from [$CLAUDE_CONFIG_DIR|~/.claude]
- shell scripts: use a shared config-dir.sh helper with ~ expansion and trailing-slash
  normalization
- installer: copy config-dir helpers alongside standalone hooks and the HUD find-node.sh helper
- plugin setup: move nodeBin to module scope so both settings.json and hooks.json patching blocks
  can access it (pre-existing const scoping bug where hooks.json patch always failed silently)
- skill snippets: replace $HOME/.claude with ${CLAUDE_CONFIG_DIR:-$HOME/.claude} in executable
  shell snippets across 8 skill files
- skill prose instructions: replace hardcoded ~/.claude in LLM-actionable directives (Read/Write
  tool targets, bash code blocks, path references in step instructions) across 9 skill files so
  an LLM executing the skill resolves the active config directory
- HUD usage API: document and test that Keychain service names hash the exact CLAUDE_CONFIG_DIR
  string, preserving distinct service-name mapping for ~-prefixed vs expanded inputs
- test files: align mock paths from paths.js to config-dir.js; fix vi.mock hoisting in
  doctor-conflicts.test.ts; resolve macOS /var symlink mismatch in bridge-integration,
  team-status, and edge-cases tests; update string-pattern assertions in hud-windows.test.ts
  and mingw-escape.test.ts
- test script: source lib/config-dir.sh in scripts/test-pr25.sh for post-install path
  verification

Adds focused regression coverage in:

- src/__tests__/auto-update.test.ts
- src/__tests__/cli-config-stop-callback.test.ts
- src/__tests__/config-dir.test.ts
- src/__tests__/delegation-enforcement-levels.test.ts
- src/__tests__/doctor-conflicts.test.ts
- src/__tests__/hud-api-key-source.test.ts
- src/__tests__/hud-marketplace-resolution.test.ts
- src/__tests__/hud-windows.test.ts
- src/__tests__/hud/cli-diagnostic.test.ts
- src/__tests__/hud/usage-api-lock.test.ts
- src/__tests__/hud/usage-api-stale.test.ts
- src/__tests__/hud/usage-api.test.ts
- src/__tests__/installer.test.ts
- src/__tests__/purge-stale-cache.test.ts
- src/__tests__/session-history-search.test.ts
- src/__tests__/setup-claude-md-script.test.ts
- src/__tests__/shared-memory.test.ts
- src/hooks/factcheck/__tests__/factcheck.test.ts
- src/installer/__tests__/standalone-hook-reconcile.test.ts
- src/notifications/__tests__/config-merge.test.ts
- src/notifications/__tests__/profiles.test.ts
- src/openclaw/__tests__/config.test.ts
- src/skills/__tests__/mingw-escape.test.ts
- src/team/__tests__/bridge-integration.test.ts
- src/team/__tests__/edge-cases.test.ts
- src/team/__tests__/inbox-outbox.test.ts
- src/team/__tests__/message-router.test.ts
- src/team/__tests__/outbox-reader.test.ts
- src/team/__tests__/team-registration.test.ts
- src/team/__tests__/team-status.test.ts
@relvinhas relvinhas force-pushed the fix/claude-config-dir-runtime-paths branch from 207a744 to b8948f6 Compare April 4, 2026 19:05
@Yeachan-Heo
Copy link
Copy Markdown
Owner

Reviewed via OMC session. Approve and merge. Well-scoped fix with 7366 tests passing, addresses real configuration portability issue.\n\n—\n*[repo owner'''s gaebal-gajae (clawdbot) 🦞]*

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.

2 participants