Expand a leading ~ in IMPECCABLE_CACHE_ROOT against the home dir

Env files and settings JSON hand '~/caches' to Node unexpanded; without
this it would resolve to a literal '~' directory under the process cwd.
Mirrors the exact treatment IMPECCABLE_HOOK_LOG already gets in
writeAuditLog (HOME || USERPROFILE fallback), plus the Windows '~\'
spelling.

Prepared with AI assistance (Claude Code) under direction of
0xDarkMatter, per the maintainer-approved issue #422.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
0xDarkMatter
2026-08-28 15:14:49 +05:00
committed by Abdul Wahab
co-authored by Claude Fable 5
parent 5c82d58b7e
commit 30b3628f5b
2 changed files with 29 additions and 4 deletions
+9 -4
View File
@@ -220,12 +220,17 @@ export function getLocalConfigPath(cwd) {
// disposable state relocates.
// Read from process.env (not runHook's injected env): the cache root is a
// machine-scoped setting like CURSOR_PROJECT_DIR, not a per-invocation
// switch. Trim guards against stray whitespace in env files; resolving both
// sides makes the slug deterministic when callers hand in a trailing
// separator or unnormalized cwd.
// switch. Trim guards against stray whitespace in env files; `~/` expands to
// the home dir (settings/env files hand it to Node unexpanded — same
// treatment IMPECCABLE_HOOK_LOG gets in writeAuditLog); resolving both sides
// makes the slug deterministic when callers hand in a trailing separator or
// unnormalized cwd.
function hookStateDir(cwd) {
const raw = process.env.IMPECCABLE_CACHE_ROOT;
const root = typeof raw === 'string' ? raw.trim() : '';
let root = typeof raw === 'string' ? raw.trim() : '';
if (root.startsWith('~/') || root.startsWith('~\\') || root === '~') {
root = path.join(process.env.HOME || process.env.USERPROFILE || '.', root.slice(2));
}
if (root) {
const slug = path.resolve(String(cwd)).replace(/[:\\/.]/g, '-');
return path.join(path.resolve(root), slug);