From 9b385c9e30ea3a160249b76de1931750bb352a37 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Mon, 11 May 2026 18:39:05 -0400 Subject: [PATCH] fix: salvage stale PR plugin install fixes --- README.md | 1 + scripts/harness-audit.js | 173 +++++++++++++++--- .../scripts/instinct-cli.py | 7 + tests/scripts/harness-audit.test.js | 83 ++++++++- 4 files changed, 242 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index aba2daa9..105cc3ec 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/scripts/harness-audit.js b/scripts/harness-audit.js index 4d2f71f2..79bc57c8 100644 --- a/scripts/harness-audit.js +++ b/scripts/harness-audit.js @@ -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, }; diff --git a/skills/continuous-learning-v2/scripts/instinct-cli.py b/skills/continuous-learning-v2/scripts/instinct-cli.py index e5611424..a4ce1cdb 100755 --- a/skills/continuous-learning-v2/scripts/instinct-cli.py +++ b/skills/continuous-learning-v2/scripts/instinct-cli.py @@ -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 diff --git a/tests/scripts/harness-audit.test.js b/tests/scripts/harness-audit.test.js index 0983d1b2..685200e3 100644 --- a/tests/scripts/harness-audit.test.js +++ b/tests/scripts/harness-audit.test.js @@ -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); }