fix: respect CLAUDE_CONFIG_DIR across remaining code paths#2156
Merged
Yeachan-Heo merged 1 commit intoYeachan-Heo:devfrom Apr 5, 2026
Merged
Conversation
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
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
207a744 to
b8948f6
Compare
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) 🦞]* |
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR #2086 partially addressed issue #2084 by fixing installer/setup profile handling. Several runtime code paths identified in that issue scope still hardcoded
~/.claudeinstead 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
getClaudeConfigDir()inconfig-dir.tswith~expansion and trailing-slash normalisation, replacing the trivialgetConfigDir()wrapper; remove the re-export frompaths.ts(src/utils/config-dir.ts)TypeScript runtime
worktree-paths.ts#L425)projects/lookups from the active config directory (worktree-paths.ts#L592,#L630)getClaudeConfigDir()instead of hardcoded path; remove the deadhomeDirparameter fromfindRuleFilesand the staleUSER_RULE_DIRexport (finder.ts#L226)getClaudeConfigDir()for runtime paths and help text (default:~/.claude/)index.ts#L247— stop-callback example#L465— default stop-callback file path#L844-L856— install description and help text#L894— post-install outputindex.ts#L129)skills-tools.ts#L122,#L160).omc-config.jsonfrom the active config directory (shared-memory.ts#L63)index.ts#L36)~/.claudein error message output (executor.ts#L356)${CLAUDE_CONFIG_DIR}token toforbidden_path_prefixesdefaults and token expander, replacing the hardcoded${HOME}/.claudeprefix (config.ts#L20,#L53)CLAUDE_CONFIG_DIRstring, 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)find-node.shhelper (index.ts)Runtime scripts
lib/config-dir.mjshelper instead of repeating~/.claudefallbacks (scripts/)scripts/lib/config-dir.cjssopersistent-mode.cjsreuses the same logictemplates/hooks/lib/config-dir.mjs(mirrorsscripts/lib/config-dir.mjs) so template scripts resolve the config directory consistentlylib/config-dirhelper instead of embedding duplicate resolution logic (plugin-setup.mjs)persistent-mode.cjs#L450persistent-mode.mjs#L434nodeBinto module scope so both the settings.json and hooks.json patching blocks can access it — pre-existingconstscoping bug where hooks.json patching always failed silently with aReferenceError(plugin-setup.mjs#L229)Shell scripts
config-dir.shhelper with~expansion and trailing-slash normalisationscripts/find-node.sh#L22scripts/setup-claude-md.sh#L20scripts/setup-progress.sh#L12CLAUDE_CONFIG_DIRenv var (uninstall.sh#L16)lib/config-dir.shfor post-install path verification (test-pr25.sh#L191)Skill files
replace
$HOME/.claudewith${CLAUDE_CONFIG_DIR:-$HOME/.claude}in executable shell code blocks, and hardcoded~/.claudein LLM-actionable directives (Read/Write tool targets, step instructions) with${CLAUDE_CONFIG_DIR:-~/.claude}so both shell execution and LLM tool-call construction honour the active config directory:skills/omc-doctor/SKILL.mdskills/skill/SKILL.mdskills/configure-notifications/SKILL.mdskills/cancel/SKILL.mdskills/hud/SKILL.mdskills/omc-setup/SKILL.mdskills/omc-setup/phases/02-configure.mdskills/omc-setup/phases/03-integrations.mdskills/omc-setup/phases/04-welcome.mdskills/team/SKILL.mdskills/learner/SKILL.mdNote on LLM-actionable prose: directives like
Read ${CLAUDE_CONFIG_DIR:-~/.claude}/settings.jsonuse shell-style fallback syntax which an LLM will not literally expand. This is a known limitation — strictly better than the previous hardcoded~/.claude(which always missed custom directories), but not equivalent to true runtime resolution. A proper fix would require a skill-template preprocessing step that substitutes the active config directory before the LLM sees the prompt; that is out of scope for this change and tracked as a future improvement.Tests
paths.jstoconfig-dir.jsto match the new import sourcevi.mockhoisting indoctor-conflicts.test.ts/varsymlink mismatch inbridge-integration.test.ts,edge-cases.test.ts, andteam-status.test.tshud-windows.test.tsandmingw-escape.test.tsgetClaudeConfigDir()or the exportedCLAUDE_CONFIG_DIRconstant in auto-update, installer, delegation enforcement, factcheck, and team integration tests so the suite passes under custom config directoriesThe 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 forpersistent-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.cjsrequires CJSrequire(), 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.mjsis a byte-identical copy ofscripts/lib/config-dir.mjs. It exists so that template-based standalone installs (which copy thetemplates/hooks/tree without running the full installer) have a working helper in place. The installer'sensureStandaloneHookScriptsalso copies the canonicalscripts/lib/config-dir.mjsinto the hookslib/directory at install time, so both paths converge on the same file. This follows the existing pattern ofstdin.mjsandatomic-write.mjsintemplates/hooks/lib/.Compatibility note: the
findRuleFiles(projectRoot, homeDir, currentFile)signature and theUSER_RULE_DIRexport 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
homeDirand forwards to the new two-argument form.Testing
Focused regression coverage in:
src/__tests__/auto-update.test.ts— plugin cache sync paths under custom config directorysrc/__tests__/cli-config-stop-callback.test.ts— CLI default file-callback pathsrc/__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 isolationsrc/__tests__/doctor-conflicts.test.ts— hook ownership classification under custom config directory; vi.hoisted() fix for mock factorysrc/__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 copysrc/__tests__/hud-windows.test.ts— string pattern assertions updated for getClaudeConfigDir() and CLAUDE_CONFIG_DIR shell expansionssrc/__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 directorysrc/__tests__/purge-stale-cache.test.ts— mock function name alignment (getConfigDir → getClaudeConfigDir)src/__tests__/session-history-search.test.ts— transcript root resolutionsrc/__tests__/setup-claude-md-script.test.ts— shell flow with~-prefixedCLAUDE_CONFIG_DIRsrc/__tests__/shared-memory.test.ts— shared-memory reads from active config directorysrc/hooks/factcheck/__tests__/factcheck.test.ts— forbidden path prefix usesgetClaudeConfigDir(),${CLAUDE_CONFIG_DIR}token expansionsrc/installer/__tests__/standalone-hook-reconcile.test.ts— standalone hooks libconfig-dir.mjsinstalled alongside hookssrc/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 expansionsrc/team/__tests__/bridge-integration.test.ts— bridge lifecycle under custom config directory; macOS /var symlink fixsrc/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 directorysrc/team/__tests__/message-router.test.ts— inbox routing to MCP workers under custom config directorysrc/team/__tests__/outbox-reader.test.ts— outbox cursor and message reads under custom config directorysrc/team/__tests__/team-registration.test.ts— worker registration under custom config directorysrc/team/__tests__/team-status.test.ts— mock path alignment (paths.js → config-dir.js); macOS /var symlink fix (realpathSync)Closes #2155.