Files
ECC/scripts/lib/install/inventory.js
T
Affaan MustafaandGitHub c05b2d6614 chore: retire the Everything Claude Code name, ECC only (#3330)
* chore: retire the Everything Claude Code name, ECC only

Replace the expanded product name and the everything-claude-code slug
with ECC across READMEs, translated docs, harness folders, skills,
commands, scripts and examples. Repo URLs now point at affaan-m/ECC.

Rename repo-internal artifacts to ecc forms (ecc-conventions skill,
ecc-guardrails, ecc-research-playbook, ecc-team-config, ecc-instincts
with ecc-* ids) and update .claude/ecc-tools.json.

Legacy install ids, plugin dirs, package names and MCP prefixes stay
where existing installs resolve against them. The codex global-state
check and harness-audit repo detection accept the new name first and
the legacy one as a fallback. The Discord bot prefers ~/GitHub/ECC and
falls back to the nested legacy checkout path.

Refresh the skill-triggers registryDigest for the changed skill text.

* fix(ecc-tools): drop managed paths to the removed Claude repo skill

.claude/skills/<repo-skill>/SKILL.md was deleted in #2770 but stayed listed in
ecc-tools.json and the team config. Remove those entries and point the Claude
Code adapter at the existing .agents/skills/ecc-conventions/SKILL.md.
2026-10-01 17:50:41 -05:00

151 lines
4.7 KiB
JavaScript

'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const { isWithinRoot, realpathNearestExisting } = require('../path-safety');
const CURRENT_PLUGIN_ID = 'ecc@ecc';
// Legacy plugin ids, kept so existing installs are still recognized.
const LEGACY_PLUGIN_IDS = new Set([
'everything-claude-code@everything-claude-code',
'everything-claude-code@ecc',
]);
function resolveClaudePaths(options = {}) {
const homeDir = options.homeDir
|| process.env.HOME
|| process.env.USERPROFILE
|| os.homedir();
const configDir = options.configDir
|| process.env.CLAUDE_CONFIG_DIR
|| path.join(homeDir, '.claude');
const projectRoot = options.projectRoot || process.cwd();
return {
homeDir: path.resolve(homeDir),
configDir: path.resolve(configDir),
projectRoot: path.resolve(projectRoot),
};
}
function readJsonObject(filePath, label) {
let value;
try {
value = JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (error) {
throw new Error(`${label} is invalid at ${filePath}: ${error.message}`);
}
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`${label} is invalid at ${filePath}: expected a JSON object`);
}
return value;
}
function findManualClaudePlugin(options = {}) {
const { configDir } = resolveClaudePaths(options);
const pluginsDir = path.join(configDir, 'plugins');
const candidates = [
['ecc', '.claude-plugin', 'plugin.json'],
['ecc', 'plugin.json'],
['ecc@ecc', '.claude-plugin', 'plugin.json'],
['ecc@ecc', 'plugin.json'],
// Legacy install dir, kept for existing installs.
['everything-claude-code', '.claude-plugin', 'plugin.json'],
['everything-claude-code', 'plugin.json'],
];
for (const segments of candidates) {
const manifestPath = path.join(pluginsDir, ...segments);
if (fs.existsSync(manifestPath)) {
return {
manifestPath,
installPath: path.dirname(path.dirname(manifestPath)),
};
}
}
return null;
}
function validateManagedState(state, statePath, expectedRoot) {
const selectedModules = state?.resolution?.selectedModules;
const operations = state?.operations;
if (
state?.schemaVersion !== 'ecc.install.v1'
|| !state.target
|| typeof state.target !== 'object'
|| Array.isArray(state.target)
|| !Array.isArray(selectedModules)
|| !selectedModules.every(moduleId => typeof moduleId === 'string' && moduleId.length > 0)
|| !Array.isArray(operations)
) {
throw new Error(`Managed Claude install-state is invalid at ${statePath}`);
}
for (const operation of operations) {
if (
!operation
|| typeof operation !== 'object'
|| typeof operation.destinationPath !== 'string'
|| !path.isAbsolute(operation.destinationPath)
|| !isWithinRoot(operation.destinationPath, expectedRoot)
) {
throw new Error(`Managed Claude install-state is invalid at ${statePath}`);
}
}
return { selectedModules, operations };
}
function operationOverlapsPlugin(operation, expectedRoot) {
const canonicalRoot = realpathNearestExisting(expectedRoot);
const canonicalDestination = realpathNearestExisting(operation.destinationPath);
const relativePath = path.relative(canonicalRoot, canonicalDestination);
const firstSegment = relativePath.split(path.sep)[0];
return ['agents', 'commands', 'hooks', 'skills'].includes(firstSegment);
}
function findManagedClaudeInstalls(options = {}) {
const { configDir, projectRoot } = resolveClaudePaths(options);
const candidates = [
{
statePath: path.join(configDir, 'ecc', 'install-state.json'),
expectedRoot: configDir,
},
{
statePath: path.join(projectRoot, '.claude', 'ecc', 'install-state.json'),
expectedRoot: path.join(projectRoot, '.claude'),
},
];
const findings = [];
for (const candidate of candidates) {
if (!fs.existsSync(candidate.statePath)) continue;
const state = readJsonObject(candidate.statePath, 'Managed Claude install-state');
const { selectedModules, operations } = validateManagedState(
state,
candidate.statePath,
candidate.expectedRoot
);
const modulesOverlap = selectedModules.some(moduleId => moduleId !== 'rules-core');
const operationsOverlap = operations.some(operation => (
operationOverlapsPlugin(operation, candidate.expectedRoot)
));
findings.push({
statePath: candidate.statePath,
selectedModules: [...selectedModules],
overlapsPlugin: modulesOverlap || operationsOverlap,
});
}
return findings;
}
module.exports = {
CURRENT_PLUGIN_ID,
LEGACY_PLUGIN_IDS,
findManagedClaudeInstalls,
findManualClaudePlugin,
resolveClaudePaths,
};