LifeosConfig.ts defines and populates paths.memoryDir (defaulting to ~/.claude/LIFEOS/MEMORY), but nothing consumes it. The path resolver (hooks/lib/paths.ts — getLifeosDir() / paiPath()) and every memory reader hardcode getLifeosDir()/MEMORY; the config value is dead. (Verified: no references to .memoryDir outside its own definition, local tree + code search.)
Why it's worth wiring
- Non-destructive upgrades. When the resolved root moves across a release — as in PAI→LifeOS, where
getLifeosDir() went from ~/.claude to ~/.claude/LIFEOS, moving the memory root ~/.claude/MEMORY → ~/.claude/LIFEOS/MEMORY — accumulated memory under the old root is silently stranded: readers point at the new (empty) location while the corpus sits in the old one. A configurable, resolver-independent memory root makes upgrades non-destructive for users with accumulated state.
- Data/framework separation. Lets users keep heritable memory outside the framework-managed tree (backup, portability, surviving a reinstall that clears
LIFEOS/).
Scope (so you can judge before any code)
~171 call sites: 33 via paiPath('MEMORY', …) (central) and 138 inline join(…, 'MEMORY', …). It must be all-or-nothing — wiring only the central sites leaves the inline readers pointed elsewhere, i.e. a split memory tree.
Proposed approach
A getMemoryDir() in paths.ts returning config.paths.memoryDir ?? join(getLifeosDir(), 'MEMORY') (default installs stay byte-identical), then a codemod routing the inline sites through it; optionally a LIFEOS_MEMORY_DIR env var for parity with LIFEOS_DIR.
Happy to do the codemod + tests if you want the feature and agree with the shape — flagging first rather than dropping a 171-site change to core path resolution into your queue unsolicited.
LifeosConfig.tsdefines and populatespaths.memoryDir(defaulting to~/.claude/LIFEOS/MEMORY), but nothing consumes it. The path resolver (hooks/lib/paths.ts—getLifeosDir()/paiPath()) and every memory reader hardcodegetLifeosDir()/MEMORY; the config value is dead. (Verified: no references to.memoryDiroutside its own definition, local tree + code search.)Why it's worth wiring
getLifeosDir()went from~/.claudeto~/.claude/LIFEOS, moving the memory root~/.claude/MEMORY→~/.claude/LIFEOS/MEMORY— accumulated memory under the old root is silently stranded: readers point at the new (empty) location while the corpus sits in the old one. A configurable, resolver-independent memory root makes upgrades non-destructive for users with accumulated state.LIFEOS/).Scope (so you can judge before any code)
~171 call sites: 33 via
paiPath('MEMORY', …)(central) and 138 inlinejoin(…, 'MEMORY', …). It must be all-or-nothing — wiring only the central sites leaves the inline readers pointed elsewhere, i.e. a split memory tree.Proposed approach
A
getMemoryDir()inpaths.tsreturningconfig.paths.memoryDir ?? join(getLifeosDir(), 'MEMORY')(default installs stay byte-identical), then a codemod routing the inline sites through it; optionally aLIFEOS_MEMORY_DIRenv var for parity withLIFEOS_DIR.Happy to do the codemod + tests if you want the feature and agree with the shape — flagging first rather than dropping a 171-site change to core path resolution into your queue unsolicited.