fix(paths): expand ~ in CLAUDE_MEM_DATA_DIR (stray ~ dirs)#3350
fix(paths): expand ~ in CLAUDE_MEM_DATA_DIR (stray ~ dirs)#3350quinnmacro wants to merge 1 commit into
Conversation
… stray `~` dirs `resolveDataDir()` returned the `CLAUDE_MEM_DATA_DIR` value verbatim from both the env var and settings.json, without tilde expansion. Node's `path.join` / `fs` do not expand `~` (only the shell does), so a value written as the literal `~/.claude-mem` was treated as a *relative* path and resolved against the process cwd. claude-mem workers inherit the cwd of whatever spawned them (subagents pinned to a subdirectory, a plugin install dir, etc.), so a `~`-prefixed DATA_DIR produced stray `~/.claude-mem/` trees — a directory literally named `~` — wherever the worker happened to be running. Add `expandHome()` and route both return paths through it. Absolute and non-`~` values are untouched; `~/` and a bare `~` resolve to `homedir()`; the `~user/` form is intentionally left alone (out of scope, platform- dependent). Verified: 12 paths tests pass (6 new for expandHome, 3 for resolveDataDir tilde handling), 130 shared tests pass, `tsc --noEmit` clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Hi 👋 — gentle follow-up on this one. This came from a real production hit: a Greptile's T‑Rex already cloned the branch and ran the tests green (focused path tests, the shared suite, and Happy to squash, rebase onto a newer |
Problem
A literal
~-prefixedCLAUDE_MEM_DATA_DIR(e.g.~/.claude-memwritten intosettings.json, or set as an env var from a non-shell context) produced stray directories literally named~across the workspace.resolveDataDir()returned the value verbatim from both the env var andsettings.json, without tilde expansion. Node'spath.join/fsdo not expand~(only the shell does), so the value was treated as a relative path and resolved against the process cwd. claude-mem workers inherit the cwd of whatever spawned them — a subagent pinned to a subdirectory, a plugin-install dir, the repo root, an Obsidian plugin folder — so each worker created its owncwd/~/.claude-mem/tree (logs + asettings.jsonsnapshot), e.g.:The default value (
join(homedir(), '.claude-mem')) was already absolute, so this only bit users whosesettings.jsonliterally contained"~/.claude-mem"(hand-edited configs, or configs written by tooling that didn't expand~).Fix
Add
expandHome()insrc/shared/paths.tsand route both return paths ofresolveDataDir()through it:~/foo→join(homedir(), 'foo')~→homedir()~→ untouched~user/...→ intentionally untouched (resolving another user's home is out of scope and platform-dependent)This keeps every downstream path (
LOGS_DIR,DB_PATH,paths.*) absolute regardless of how the value was authored, without changing behavior for the (already-absolute) default.Verification
bun test tests/shared/paths.test.ts→ 12 pass (6 newexpandHomecases + 3resolveDataDirtilde cases + 3 existing)bun test tests/shared/→ 130 pass, 0 failtsc --noEmit→ 0 errorsThe
resolveDataDirtests exercise the env-var branch (consulted first) so they don't touch the real~/.claude-mem/settings.jsonon disk.Workaround already in place locally
Beyond this source fix, the equivalent defense without a code change is to set
CLAUDE_MEM_DATA_DIRto an absolute path in the Claude Codeenvblock (~/.claude/settings.json) sote()/resolveDataDir()hits the env-var branch first — useful for anyone who can't upgrade immediately.🤖 Generated with Claude Code