fix: salvage stale PR plugin install fixes

This commit is contained in:
Affaan Mustafa
2026-05-11 18:39:05 -04:00
committed by Affaan Mustafa
parent 8aa8c32d2a
commit 9b385c9e30
4 changed files with 242 additions and 22 deletions

View File

@@ -1590,6 +1590,7 @@ Projects built on or inspired by Everything Claude Code:
| Project | Description |
|---------|-------------|
| [EVC](https://github.com/SaigonXIII/evc) | Marketing agent workspace — 42 commands for content operators, brand governance, and multi-channel publishing. [Visual overview](https://saigonxiii.github.io/evc). |
| [trading-skills](https://github.com/VictorVVedtion/trading-skills) | 68 trading-themed Claude Code skills with pre-trade review prompts and risk gates inspired by market operators. |
Built something with ECC? Open a PR to add it here.

View File

@@ -187,28 +187,157 @@ function detectTargetMode(rootDir) {
return 'consumer';
}
function findPluginInstall(rootDir) {
const homeDir = process.env.HOME || process.env.USERPROFILE || os.homedir() || '';
const pluginDirs = [
'ecc',
'ecc@ecc',
'everything-claude-code',
'everything-claude-code@everything-claude-code',
];
const candidateRoots = [
path.join(rootDir, '.claude', 'plugins'),
path.join(rootDir, '.claude', 'plugins', 'marketplaces'),
homeDir && path.join(homeDir, '.claude', 'plugins'),
homeDir && path.join(homeDir, '.claude', 'plugins', 'marketplaces'),
].filter(Boolean);
const candidates = candidateRoots.flatMap((pluginsDir) =>
pluginDirs.flatMap((pluginDir) => [
path.join(pluginsDir, pluginDir, '.claude-plugin', 'plugin.json'),
path.join(pluginsDir, pluginDir, 'plugin.json'),
])
);
const ECC_PLUGIN_KEY_PATTERNS = [
/^ecc@/i,
/^everything-claude-code@/i,
];
return candidates.find(candidate => fs.existsSync(candidate)) || null;
const ECC_LEGACY_PLUGIN_DIRS = [
'ecc',
'ecc@ecc',
'everything-claude-code',
'everything-claude-code@everything-claude-code',
];
const ECC_CACHE_MARKETPLACES = ['everything-claude-code', 'ecc'];
const ECC_CACHE_PLUGIN_NAMES = ['ecc', 'everything-claude-code'];
function uniquePaths(paths) {
return [...new Set(paths.filter(Boolean))];
}
function compareVersionDesc(a, b) {
const partsA = String(a).split('.').map(part => parseInt(part, 10) || 0);
const partsB = String(b).split('.').map(part => parseInt(part, 10) || 0);
const length = Math.max(partsA.length, partsB.length);
for (let index = 0; index < length; index += 1) {
const valueA = partsA[index] || 0;
const valueB = partsB[index] || 0;
if (valueA !== valueB) {
return valueB - valueA;
}
}
return 0;
}
function findPluginJsonUnder(installRoot) {
const pluginJson = path.join(installRoot, '.claude-plugin', 'plugin.json');
if (fs.existsSync(pluginJson)) {
return pluginJson;
}
const fallback = path.join(installRoot, 'plugin.json');
return fs.existsSync(fallback) ? fallback : null;
}
function findPluginInstallFromManifest(installedPluginsPaths) {
for (const installedPath of installedPluginsPaths) {
if (!fs.existsSync(installedPath)) {
continue;
}
const manifest = safeParseJson(safeRead(path.dirname(installedPath), path.basename(installedPath)));
if (!manifest || !manifest.plugins) {
continue;
}
for (const [key, value] of Object.entries(manifest.plugins)) {
if (!ECC_PLUGIN_KEY_PATTERNS.some(pattern => pattern.test(key))) {
continue;
}
const entries = Array.isArray(value) ? value : [];
for (const entry of entries) {
if (!entry || typeof entry.installPath !== 'string' || !entry.installPath.trim()) {
continue;
}
const installRoot = path.isAbsolute(entry.installPath)
? entry.installPath
: path.resolve(path.dirname(installedPath), entry.installPath);
const hit = findPluginJsonUnder(installRoot);
if (hit) {
return hit;
}
}
}
}
return null;
}
function findPluginInstallFlatLayout(candidateRoots) {
for (const pluginsDir of candidateRoots) {
for (const pluginDir of ECC_LEGACY_PLUGIN_DIRS) {
const hit = findPluginJsonUnder(path.join(pluginsDir, pluginDir));
if (hit) {
return hit;
}
}
}
return null;
}
function findPluginInstallMarketplaceCache(candidateRoots) {
for (const pluginsDir of candidateRoots) {
for (const marketplace of ECC_CACHE_MARKETPLACES) {
for (const pluginName of ECC_CACHE_PLUGIN_NAMES) {
const pluginRoot = path.join(pluginsDir, 'cache', marketplace, pluginName);
if (!fs.existsSync(pluginRoot)) {
continue;
}
let versions = [];
try {
versions = fs
.readdirSync(pluginRoot, { withFileTypes: true })
.filter(entry => entry.isDirectory())
.map(entry => entry.name)
.sort(compareVersionDesc);
} catch {
continue;
}
for (const version of versions) {
const hit = findPluginJsonUnder(path.join(pluginRoot, version));
if (hit) {
return hit;
}
}
}
}
}
return null;
}
function findPluginInstall(rootDir) {
const homeDirs = uniquePaths([
process.env.HOME,
process.env.USERPROFILE,
os.homedir(),
]);
const pluginRoots = uniquePaths([
path.join(rootDir, '.claude', 'plugins'),
...homeDirs.map(homeDir => path.join(homeDir, '.claude', 'plugins')),
]);
const installedPluginsPaths = uniquePaths([
path.join(rootDir, '.claude', 'plugins', 'installed_plugins.json'),
...homeDirs.map(homeDir => path.join(homeDir, '.claude', 'plugins', 'installed_plugins.json')),
]);
const flatRoots = uniquePaths([
...pluginRoots,
...pluginRoots.map(pluginsDir => path.join(pluginsDir, 'marketplaces')),
]);
return (
findPluginInstallFromManifest(installedPluginsPaths)
|| findPluginInstallFlatLayout(flatRoots)
|| findPluginInstallMarketplaceCache(pluginRoots)
);
}
function getRepoChecks(rootDir) {
@@ -735,4 +864,6 @@ if (require.main === module) {
module.exports = {
buildReport,
parseArgs,
findPluginInstall,
compareVersionDesc,
};

View File

@@ -28,6 +28,13 @@ from datetime import datetime, timedelta, timezone
from collections import defaultdict
from typing import Optional
if sys.platform == "win32":
try:
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
except Exception:
pass
try:
import fcntl
_HAS_FCNTL = True

View File

@@ -9,7 +9,7 @@ const path = require('path');
const { execFileSync, spawnSync } = require('child_process');
const SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'harness-audit.js');
const { parseArgs } = require(SCRIPT);
const { parseArgs, findPluginInstall, compareVersionDesc } = require(SCRIPT);
function createTempDir(prefix) {
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
@@ -389,6 +389,87 @@ function runTests() {
}
})) passed++; else failed++;
if (test('detects Claude plugin installs from installed_plugins.json', () => {
const homeDir = createTempDir('harness-audit-manifest-home-');
const projectRoot = createTempDir('harness-audit-manifest-project-');
const pluginsDir = path.join(homeDir, '.claude', 'plugins');
const installRoot = path.join(pluginsDir, 'cache', 'everything-claude-code', 'ecc', '2.0.0');
try {
fs.mkdirSync(path.join(installRoot, '.claude-plugin'), { recursive: true });
fs.writeFileSync(
path.join(installRoot, '.claude-plugin', 'plugin.json'),
JSON.stringify({ name: 'ecc', version: '2.0.0' }, null, 2)
);
fs.writeFileSync(
path.join(pluginsDir, 'installed_plugins.json'),
JSON.stringify({
plugins: {
'ecc@everything-claude-code': [
{ installPath: path.join('cache', 'everything-claude-code', 'ecc', '2.0.0') },
],
},
}, null, 2)
);
const originalHome = process.env.HOME;
process.env.HOME = homeDir;
try {
const found = findPluginInstall(projectRoot);
assert.ok(found);
assert.ok(found.includes(`${path.sep}cache${path.sep}everything-claude-code${path.sep}ecc${path.sep}2.0.0${path.sep}`));
} finally {
if (originalHome === undefined) {
delete process.env.HOME;
} else {
process.env.HOME = originalHome;
}
}
} finally {
cleanup(homeDir);
cleanup(projectRoot);
}
})) passed++; else failed++;
if (test('detects newest Claude plugin install from cache marketplace layout', () => {
const homeDir = createTempDir('harness-audit-cache-home-');
const projectRoot = createTempDir('harness-audit-cache-project-');
const pluginRoot = path.join(homeDir, '.claude', 'plugins', 'cache', 'everything-claude-code', 'ecc');
try {
for (const version of ['1.8.0', '1.10.0']) {
fs.mkdirSync(path.join(pluginRoot, version, '.claude-plugin'), { recursive: true });
fs.writeFileSync(
path.join(pluginRoot, version, '.claude-plugin', 'plugin.json'),
JSON.stringify({ name: 'ecc', version }, null, 2)
);
}
const originalHome = process.env.HOME;
process.env.HOME = homeDir;
try {
const found = findPluginInstall(projectRoot);
assert.ok(found);
assert.ok(found.includes(`${path.sep}1.10.0${path.sep}`), `expected newest version, got ${found}`);
} finally {
if (originalHome === undefined) {
delete process.env.HOME;
} else {
process.env.HOME = originalHome;
}
}
} finally {
cleanup(homeDir);
cleanup(projectRoot);
}
})) passed++; else failed++;
if (test('compareVersionDesc orders numeric version components', () => {
const versions = ['1.8.0', '1.10.0', '1.9.0', '2.0.0'].sort(compareVersionDesc);
assert.deepStrictEqual(versions, ['2.0.0', '1.10.0', '1.9.0', '1.8.0']);
assert.doesNotThrow(() => compareVersionDesc('1.0.0-rc.1', '1.0.0'));
})) passed++; else failed++;
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0);
}