mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-07-02 04:51:26 +08:00
6f01d26420
The inline node -e resolver blob was duplicated ~60x across hooks.json, command docs, and translations. Each copy inlined the full ~700-char plugin-root search using a spread over nested array literals (p.join(d,'plugins',...s) over [['ecc'],...]), which breaks Windows hook execution due to shell quoting (#2368). Collapse every copy to a 250-char locator that loads the committed resolve-ecc-root module and delegates to resolveEccRoot() — no spread, no nested array literals, no escaped double quotes. The real search logic now lives in one tested module. Also route session-start-bootstrap.js through resolveEccRoot() instead of its own duplicated reimplementation, and fix the auto-update.md 'marketplace' (singular) typo along the way. Guard tests updated: discovery behavior is asserted against resolveEccRoot(); the inline is asserted to delegate and to contain no Windows-fragile constructs. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
129 lines
4.7 KiB
JavaScript
129 lines
4.7 KiB
JavaScript
'use strict';
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const os = require('os');
|
||
|
||
const CURRENT_PLUGIN_SLUG = 'ecc';
|
||
const LEGACY_PLUGIN_SLUG = 'everything-claude-code';
|
||
const CURRENT_PLUGIN_HANDLE = `${CURRENT_PLUGIN_SLUG}@${CURRENT_PLUGIN_SLUG}`;
|
||
const LEGACY_PLUGIN_HANDLE = `${LEGACY_PLUGIN_SLUG}@${LEGACY_PLUGIN_SLUG}`;
|
||
const PLUGIN_CACHE_SLUGS = [CURRENT_PLUGIN_SLUG, LEGACY_PLUGIN_SLUG];
|
||
const PLUGIN_ROOT_SEGMENTS = [
|
||
[CURRENT_PLUGIN_SLUG],
|
||
[CURRENT_PLUGIN_HANDLE],
|
||
['marketplaces', CURRENT_PLUGIN_SLUG],
|
||
[LEGACY_PLUGIN_SLUG],
|
||
[LEGACY_PLUGIN_HANDLE],
|
||
['marketplaces', LEGACY_PLUGIN_SLUG],
|
||
];
|
||
|
||
/**
|
||
* Resolve the ECC source root directory.
|
||
*
|
||
* Tries, in order:
|
||
* 1. CLAUDE_PLUGIN_ROOT env var (set by Claude Code for hooks, or by user)
|
||
* 2. Standard install location (~/.claude/) — when scripts exist there
|
||
* 3. Known plugin roots under ~/.claude/plugins/ (current + legacy slugs)
|
||
* 4. Plugin cache auto-detection — scans ~/.claude/plugins/cache/{ecc,everything-claude-code}/
|
||
* 5. Fallback to ~/.claude/ (original behaviour)
|
||
*
|
||
* @param {object} [options]
|
||
* @param {string} [options.homeDir] Override home directory (for testing)
|
||
* @param {string} [options.envRoot] Override CLAUDE_PLUGIN_ROOT (for testing)
|
||
* @param {string} [options.probe] Relative path used to verify a candidate root
|
||
* contains ECC scripts. Default: 'scripts/lib/utils.js'
|
||
* @returns {string} Resolved ECC root path
|
||
*/
|
||
function resolveEccRoot(options = {}) {
|
||
const envRoot = options.envRoot !== undefined
|
||
? options.envRoot
|
||
: (process.env.CLAUDE_PLUGIN_ROOT || '');
|
||
|
||
if (envRoot && envRoot.trim()) {
|
||
return envRoot.trim();
|
||
}
|
||
|
||
const homeDir = options.homeDir || os.homedir();
|
||
const claudeDir = path.join(homeDir, '.claude');
|
||
const probe = options.probe || path.join('scripts', 'lib', 'utils.js');
|
||
|
||
// Standard install — files are copied directly into ~/.claude/
|
||
if (fs.existsSync(path.join(claudeDir, probe))) {
|
||
return claudeDir;
|
||
}
|
||
|
||
// Exact legacy plugin install locations. These preserve backwards
|
||
// compatibility without scanning arbitrary plugin trees.
|
||
const legacyPluginRoots = PLUGIN_ROOT_SEGMENTS.map((segments) =>
|
||
path.join(claudeDir, 'plugins', ...segments)
|
||
);
|
||
|
||
for (const candidate of legacyPluginRoots) {
|
||
if (fs.existsSync(path.join(candidate, probe))) {
|
||
return candidate;
|
||
}
|
||
}
|
||
|
||
// Plugin cache — Claude Code stores marketplace plugins under
|
||
// ~/.claude/plugins/cache/<plugin-name>/<org>/<version>/
|
||
try {
|
||
for (const slug of PLUGIN_CACHE_SLUGS) {
|
||
const cacheBase = path.join(claudeDir, 'plugins', 'cache', slug);
|
||
const orgDirs = fs.readdirSync(cacheBase, { withFileTypes: true });
|
||
|
||
for (const orgEntry of orgDirs) {
|
||
if (!orgEntry.isDirectory()) continue;
|
||
const orgPath = path.join(cacheBase, orgEntry.name);
|
||
|
||
let versionDirs;
|
||
try {
|
||
versionDirs = fs.readdirSync(orgPath, { withFileTypes: true });
|
||
} catch {
|
||
continue;
|
||
}
|
||
|
||
for (const verEntry of versionDirs) {
|
||
if (!verEntry.isDirectory()) continue;
|
||
const candidate = path.join(orgPath, verEntry.name);
|
||
if (fs.existsSync(path.join(candidate, probe))) {
|
||
return candidate;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch {
|
||
// Plugin cache doesn't exist or isn't readable — continue to fallback
|
||
}
|
||
|
||
return claudeDir;
|
||
}
|
||
|
||
/**
|
||
* Compact inline locator for embedding in hooks.json and command .md code blocks.
|
||
*
|
||
* Earlier revisions inlined the *entire* resolveEccRoot() search (~700 chars,
|
||
* duplicated ~80×). That blob used a spread (`...s`) over nested array literals,
|
||
* which broke Windows hook execution due to shell quoting (#2368).
|
||
*
|
||
* This minified form instead does the minimum needed to locate and load the
|
||
* committed resolve-ecc-root module, then delegates the real search to
|
||
* resolveEccRoot(). It contains no spread, no nested array literals, and no
|
||
* escaped double quotes, so it survives `node -e "..."` quoting on every shell.
|
||
*
|
||
* It loads the module from CLAUDE_PLUGIN_ROOT (set by Claude Code for plugin
|
||
* hooks and commands) or, when unset, from ~/.claude (the direct-install
|
||
* location). resolveEccRoot() then resolves the true root — including the
|
||
* marketplace/plugin-cache locations — from there.
|
||
*
|
||
* Usage in commands:
|
||
* const _r = <paste INLINE_RESOLVE>;
|
||
* const sm = require(_r + '/scripts/lib/session-manager');
|
||
*/
|
||
const INLINE_RESOLVE = `(function(){var p=require('path'),o=require('os');var e=process.env.CLAUDE_PLUGIN_ROOT;var b=(e&&e.trim())?e.trim():p.join(o.homedir(),'.claude');try{return require(p.join(b,'scripts','lib','resolve-ecc-root')).resolveEccRoot()}catch(x){return b}})()`;
|
||
|
||
module.exports = {
|
||
resolveEccRoot,
|
||
INLINE_RESOLVE,
|
||
};
|