From 28f35f3ec745d4df60bd27222010a1a536bf0aae Mon Sep 17 00:00:00 2001 From: Shintaro Okamura <48118431+okash1n@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:30:36 +0900 Subject: [PATCH] fix: resolve marketplace root path dynamically for XDG-compliant environments (#1031) The marketplace root was hardcoded to `~/.claude/plugins/marketplaces/thedotmack`, which does not exist on XDG-compliant setups (e.g. Nix-managed Claude Code) where plugins are stored under `~/.config/claude/plugins/`. This caused an ENOENT error on every SessionStart: ENOENT: no such file or directory, open '~/.claude/plugins/marketplaces/thedotmack/package.json' Now the script: 1. Derives the base path from CLAUDE_PLUGIN_ROOT if available 2. Probes the XDG path (~/.config/claude/plugins/...) 3. Falls back to the legacy path (~/.claude/plugins/...) Fixes thedotmack/claude-mem#1030 --- scripts/smart-install.js | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/scripts/smart-install.js b/scripts/smart-install.js index 050c2fd1..74d6e7c1 100644 --- a/scripts/smart-install.js +++ b/scripts/smart-install.js @@ -10,10 +10,40 @@ import { execSync, spawnSync } from 'child_process'; import { join } from 'path'; import { homedir } from 'os'; -const ROOT = join(homedir(), '.claude', 'plugins', 'marketplaces', 'thedotmack'); -const MARKER = join(ROOT, '.install-version'); const IS_WINDOWS = process.platform === 'win32'; +/** + * Resolve the marketplace root directory. + * + * Claude Code may store plugins under either `~/.claude/plugins/` (legacy) or + * `~/.config/claude/plugins/` (XDG-compliant, e.g. Nix-managed installs). + * When `CLAUDE_PLUGIN_ROOT` is set we derive the base from it; otherwise we + * probe both candidate paths and fall back to the legacy location. + */ +function resolveRoot() { + const marketplaceRel = join('plugins', 'marketplaces', 'thedotmack'); + + // Derive from CLAUDE_PLUGIN_ROOT (e.g. .../plugins/cache/thedotmack/claude-mem/) + if (process.env.CLAUDE_PLUGIN_ROOT) { + let dir = process.env.CLAUDE_PLUGIN_ROOT; + const cacheIndex = dir.indexOf(join('plugins', 'cache')); + if (cacheIndex !== -1) { + const base = dir.substring(0, cacheIndex); + const candidate = join(base, marketplaceRel); + if (existsSync(join(candidate, 'package.json'))) return candidate; + } + } + + // Probe XDG path first, then legacy + const xdg = join(homedir(), '.config', 'claude', marketplaceRel); + if (existsSync(join(xdg, 'package.json'))) return xdg; + + return join(homedir(), '.claude', marketplaceRel); +} + +const ROOT = resolveRoot(); +const MARKER = join(ROOT, '.install-version'); + // Common installation paths (handles fresh installs before PATH reload) const BUN_COMMON_PATHS = IS_WINDOWS ? [join(homedir(), '.bun', 'bin', 'bun.exe')]