mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
feat: wire manifest resolution into install execution (#509)
This commit is contained in:
@@ -2,14 +2,14 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
const { applyInstallPlan } = require('./install/apply');
|
||||
const { LEGACY_INSTALL_TARGETS, parseInstallArgs } = require('./install/request');
|
||||
const {
|
||||
SUPPORTED_INSTALL_TARGETS,
|
||||
listLegacyCompatibilityLanguages,
|
||||
resolveLegacyCompatibilitySelection,
|
||||
resolveInstallPlan,
|
||||
} = require('./install-manifests');
|
||||
const { getInstallTargetAdapter } = require('./install-targets/registry');
|
||||
const { createInstallState } = require('./install-state');
|
||||
|
||||
const LANGUAGE_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
||||
const EXCLUDED_GENERATED_SOURCE_SUFFIXES = [
|
||||
@@ -68,8 +68,11 @@ function readDirectoryNames(dirPath) {
|
||||
}
|
||||
|
||||
function listAvailableLanguages(sourceRoot = getSourceRoot()) {
|
||||
return readDirectoryNames(path.join(sourceRoot, 'rules'))
|
||||
.filter(name => name !== 'common');
|
||||
return [...new Set([
|
||||
...listLegacyCompatibilityLanguages(),
|
||||
...readDirectoryNames(path.join(sourceRoot, 'rules'))
|
||||
.filter(name => name !== 'common'),
|
||||
])].sort();
|
||||
}
|
||||
|
||||
function validateLegacyTarget(target) {
|
||||
@@ -108,6 +111,16 @@ function isGeneratedRuntimeSourcePath(sourceRelativePath) {
|
||||
return EXCLUDED_GENERATED_SOURCE_SUFFIXES.some(suffix => normalizedPath.endsWith(suffix));
|
||||
}
|
||||
|
||||
function createStatePreview(options) {
|
||||
const { createInstallState } = require('./install-state');
|
||||
return createInstallState(options);
|
||||
}
|
||||
|
||||
function applyInstallPlan(plan) {
|
||||
const { applyInstallPlan: applyPlan } = require('./install/apply');
|
||||
return applyPlan(plan);
|
||||
}
|
||||
|
||||
function buildCopyFileOperation({ moduleId, sourcePath, sourceRelativePath, destinationPath, strategy }) {
|
||||
return {
|
||||
kind: 'copy-file',
|
||||
@@ -449,7 +462,7 @@ function createLegacyInstallPlan(options = {}) {
|
||||
manifestVersion: getManifestVersion(sourceRoot),
|
||||
};
|
||||
|
||||
const statePreview = createInstallState({
|
||||
const statePreview = createStatePreview({
|
||||
adapter: plan.adapter,
|
||||
targetRoot: plan.targetRoot,
|
||||
installStatePath: plan.installStatePath,
|
||||
@@ -485,6 +498,38 @@ function createLegacyInstallPlan(options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function createLegacyCompatInstallPlan(options = {}) {
|
||||
const sourceRoot = options.sourceRoot || getSourceRoot();
|
||||
const projectRoot = options.projectRoot || process.cwd();
|
||||
const target = options.target || 'claude';
|
||||
|
||||
validateLegacyTarget(target);
|
||||
|
||||
const selection = resolveLegacyCompatibilitySelection({
|
||||
repoRoot: sourceRoot,
|
||||
target,
|
||||
legacyLanguages: options.legacyLanguages || [],
|
||||
});
|
||||
|
||||
return createManifestInstallPlan({
|
||||
sourceRoot,
|
||||
projectRoot,
|
||||
homeDir: options.homeDir,
|
||||
target,
|
||||
profileId: null,
|
||||
moduleIds: selection.moduleIds,
|
||||
includeComponentIds: [],
|
||||
excludeComponentIds: [],
|
||||
legacyLanguages: selection.legacyLanguages,
|
||||
legacyMode: true,
|
||||
requestProfileId: null,
|
||||
requestModuleIds: [],
|
||||
requestIncludeComponentIds: [],
|
||||
requestExcludeComponentIds: [],
|
||||
mode: 'legacy-compat',
|
||||
});
|
||||
}
|
||||
|
||||
function materializeScaffoldOperation(sourceRoot, operation) {
|
||||
const sourcePath = path.join(sourceRoot, operation.sourceRelativePath);
|
||||
if (!fs.existsSync(sourcePath)) {
|
||||
@@ -526,6 +571,21 @@ function createManifestInstallPlan(options = {}) {
|
||||
const sourceRoot = options.sourceRoot || getSourceRoot();
|
||||
const projectRoot = options.projectRoot || process.cwd();
|
||||
const target = options.target || 'claude';
|
||||
const legacyLanguages = Array.isArray(options.legacyLanguages)
|
||||
? [...options.legacyLanguages]
|
||||
: [];
|
||||
const requestProfileId = Object.hasOwn(options, 'requestProfileId')
|
||||
? options.requestProfileId
|
||||
: (options.profileId || null);
|
||||
const requestModuleIds = Object.hasOwn(options, 'requestModuleIds')
|
||||
? [...options.requestModuleIds]
|
||||
: (Array.isArray(options.moduleIds) ? [...options.moduleIds] : []);
|
||||
const requestIncludeComponentIds = Object.hasOwn(options, 'requestIncludeComponentIds')
|
||||
? [...options.requestIncludeComponentIds]
|
||||
: (Array.isArray(options.includeComponentIds) ? [...options.includeComponentIds] : []);
|
||||
const requestExcludeComponentIds = Object.hasOwn(options, 'requestExcludeComponentIds')
|
||||
? [...options.requestExcludeComponentIds]
|
||||
: (Array.isArray(options.excludeComponentIds) ? [...options.excludeComponentIds] : []);
|
||||
const plan = resolveInstallPlan({
|
||||
repoRoot: sourceRoot,
|
||||
projectRoot,
|
||||
@@ -543,21 +603,17 @@ function createManifestInstallPlan(options = {}) {
|
||||
repoCommit: getRepoCommit(sourceRoot),
|
||||
manifestVersion: getManifestVersion(sourceRoot),
|
||||
};
|
||||
const statePreview = createInstallState({
|
||||
const statePreview = createStatePreview({
|
||||
adapter,
|
||||
targetRoot: plan.targetRoot,
|
||||
installStatePath: plan.installStatePath,
|
||||
request: {
|
||||
profile: plan.profileId,
|
||||
modules: Array.isArray(options.moduleIds) ? [...options.moduleIds] : [],
|
||||
includeComponents: Array.isArray(options.includeComponentIds)
|
||||
? [...options.includeComponentIds]
|
||||
: [],
|
||||
excludeComponents: Array.isArray(options.excludeComponentIds)
|
||||
? [...options.excludeComponentIds]
|
||||
: [],
|
||||
legacyLanguages: [],
|
||||
legacyMode: false,
|
||||
profile: requestProfileId,
|
||||
modules: requestModuleIds,
|
||||
includeComponents: requestIncludeComponentIds,
|
||||
excludeComponents: requestExcludeComponentIds,
|
||||
legacyLanguages,
|
||||
legacyMode: Boolean(options.legacyMode),
|
||||
},
|
||||
resolution: {
|
||||
selectedModules: plan.selectedModuleIds,
|
||||
@@ -568,7 +624,7 @@ function createManifestInstallPlan(options = {}) {
|
||||
});
|
||||
|
||||
return {
|
||||
mode: 'manifest',
|
||||
mode: options.mode || 'manifest',
|
||||
target,
|
||||
adapter: {
|
||||
id: adapter.id,
|
||||
@@ -578,8 +634,9 @@ function createManifestInstallPlan(options = {}) {
|
||||
targetRoot: plan.targetRoot,
|
||||
installRoot: plan.targetRoot,
|
||||
installStatePath: plan.installStatePath,
|
||||
warnings: [],
|
||||
languages: [],
|
||||
warnings: Array.isArray(options.warnings) ? [...options.warnings] : [],
|
||||
languages: legacyLanguages,
|
||||
legacyLanguages,
|
||||
profileId: plan.profileId,
|
||||
requestedModuleIds: plan.requestedModuleIds,
|
||||
explicitModuleIds: plan.explicitModuleIds,
|
||||
@@ -597,6 +654,7 @@ module.exports = {
|
||||
SUPPORTED_INSTALL_TARGETS,
|
||||
LEGACY_INSTALL_TARGETS,
|
||||
applyInstallPlan,
|
||||
createLegacyCompatInstallPlan,
|
||||
createManifestInstallPlan,
|
||||
createLegacyInstallPlan,
|
||||
getSourceRoot,
|
||||
|
||||
@@ -11,6 +11,50 @@ const COMPONENT_FAMILY_PREFIXES = {
|
||||
framework: 'framework:',
|
||||
capability: 'capability:',
|
||||
};
|
||||
const LEGACY_COMPAT_BASE_MODULE_IDS_BY_TARGET = Object.freeze({
|
||||
claude: [
|
||||
'rules-core',
|
||||
'agents-core',
|
||||
'commands-core',
|
||||
'hooks-runtime',
|
||||
'platform-configs',
|
||||
'workflow-quality',
|
||||
],
|
||||
cursor: [
|
||||
'rules-core',
|
||||
'agents-core',
|
||||
'commands-core',
|
||||
'hooks-runtime',
|
||||
'platform-configs',
|
||||
'workflow-quality',
|
||||
],
|
||||
antigravity: [
|
||||
'rules-core',
|
||||
'agents-core',
|
||||
'commands-core',
|
||||
],
|
||||
});
|
||||
const LEGACY_LANGUAGE_ALIAS_TO_CANONICAL = Object.freeze({
|
||||
go: 'go',
|
||||
golang: 'go',
|
||||
java: 'java',
|
||||
javascript: 'typescript',
|
||||
kotlin: 'java',
|
||||
perl: 'perl',
|
||||
php: 'php',
|
||||
python: 'python',
|
||||
swift: 'swift',
|
||||
typescript: 'typescript',
|
||||
});
|
||||
const LEGACY_LANGUAGE_EXTRA_MODULE_IDS = Object.freeze({
|
||||
go: ['framework-language'],
|
||||
java: ['framework-language'],
|
||||
perl: [],
|
||||
php: [],
|
||||
python: ['framework-language'],
|
||||
swift: [],
|
||||
typescript: ['framework-language'],
|
||||
});
|
||||
|
||||
function readJson(filePath, label) {
|
||||
try {
|
||||
@@ -24,6 +68,19 @@ function dedupeStrings(values) {
|
||||
return [...new Set((Array.isArray(values) ? values : []).map(value => String(value).trim()).filter(Boolean))];
|
||||
}
|
||||
|
||||
function assertKnownModuleIds(moduleIds, manifests) {
|
||||
const unknownModuleIds = dedupeStrings(moduleIds)
|
||||
.filter(moduleId => !manifests.modulesById.has(moduleId));
|
||||
|
||||
if (unknownModuleIds.length === 1) {
|
||||
throw new Error(`Unknown install module: ${unknownModuleIds[0]}`);
|
||||
}
|
||||
|
||||
if (unknownModuleIds.length > 1) {
|
||||
throw new Error(`Unknown install modules: ${unknownModuleIds.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
function intersectTargets(modules) {
|
||||
if (!Array.isArray(modules) || modules.length === 0) {
|
||||
return [];
|
||||
@@ -102,6 +159,17 @@ function listInstallModules(options = {}) {
|
||||
}));
|
||||
}
|
||||
|
||||
function listLegacyCompatibilityLanguages() {
|
||||
return Object.keys(LEGACY_LANGUAGE_ALIAS_TO_CANONICAL).sort();
|
||||
}
|
||||
|
||||
function validateInstallModuleIds(moduleIds, options = {}) {
|
||||
const manifests = loadInstallManifests(options);
|
||||
const normalizedModuleIds = dedupeStrings(moduleIds);
|
||||
assertKnownModuleIds(normalizedModuleIds, manifests);
|
||||
return normalizedModuleIds;
|
||||
}
|
||||
|
||||
function listInstallComponents(options = {}) {
|
||||
const manifests = loadInstallManifests(options);
|
||||
const family = options.family || null;
|
||||
@@ -154,6 +222,59 @@ function expandComponentIdsToModuleIds(componentIds, manifests) {
|
||||
return dedupeStrings(expandedModuleIds);
|
||||
}
|
||||
|
||||
function resolveLegacyCompatibilitySelection(options = {}) {
|
||||
const manifests = loadInstallManifests(options);
|
||||
const target = options.target || null;
|
||||
|
||||
if (target && !SUPPORTED_INSTALL_TARGETS.includes(target)) {
|
||||
throw new Error(
|
||||
`Unknown install target: ${target}. Expected one of ${SUPPORTED_INSTALL_TARGETS.join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
const legacyLanguages = dedupeStrings(options.legacyLanguages)
|
||||
.map(language => language.toLowerCase());
|
||||
const normalizedLegacyLanguages = dedupeStrings(legacyLanguages);
|
||||
|
||||
if (normalizedLegacyLanguages.length === 0) {
|
||||
throw new Error('No legacy languages were provided');
|
||||
}
|
||||
|
||||
const unknownLegacyLanguages = normalizedLegacyLanguages
|
||||
.filter(language => !Object.hasOwn(LEGACY_LANGUAGE_ALIAS_TO_CANONICAL, language));
|
||||
|
||||
if (unknownLegacyLanguages.length === 1) {
|
||||
throw new Error(
|
||||
`Unknown legacy language: ${unknownLegacyLanguages[0]}. Expected one of ${listLegacyCompatibilityLanguages().join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
if (unknownLegacyLanguages.length > 1) {
|
||||
throw new Error(
|
||||
`Unknown legacy languages: ${unknownLegacyLanguages.join(', ')}. Expected one of ${listLegacyCompatibilityLanguages().join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
const canonicalLegacyLanguages = normalizedLegacyLanguages
|
||||
.map(language => LEGACY_LANGUAGE_ALIAS_TO_CANONICAL[language]);
|
||||
const baseModuleIds = LEGACY_COMPAT_BASE_MODULE_IDS_BY_TARGET[target || 'claude']
|
||||
|| LEGACY_COMPAT_BASE_MODULE_IDS_BY_TARGET.claude;
|
||||
const moduleIds = dedupeStrings([
|
||||
...baseModuleIds,
|
||||
...(target === 'antigravity'
|
||||
? []
|
||||
: canonicalLegacyLanguages.flatMap(language => LEGACY_LANGUAGE_EXTRA_MODULE_IDS[language] || [])),
|
||||
]);
|
||||
|
||||
assertKnownModuleIds(moduleIds, manifests);
|
||||
|
||||
return {
|
||||
legacyLanguages: normalizedLegacyLanguages,
|
||||
canonicalLegacyLanguages,
|
||||
moduleIds,
|
||||
};
|
||||
}
|
||||
|
||||
function resolveInstallPlan(options = {}) {
|
||||
const manifests = loadInstallManifests(options);
|
||||
const profileId = options.profileId || null;
|
||||
@@ -212,7 +333,7 @@ function resolveInstallPlan(options = {}) {
|
||||
const visitingIds = new Set();
|
||||
const resolvedIds = new Set();
|
||||
|
||||
function resolveModule(moduleId, dependencyOf) {
|
||||
function resolveModule(moduleId, dependencyOf, rootRequesterId) {
|
||||
const module = manifests.modulesById.get(moduleId);
|
||||
if (!module) {
|
||||
throw new Error(`Unknown install module: ${moduleId}`);
|
||||
@@ -230,16 +351,15 @@ function resolveInstallPlan(options = {}) {
|
||||
|
||||
if (target && !module.targets.includes(target)) {
|
||||
if (dependencyOf) {
|
||||
throw new Error(
|
||||
`Module ${dependencyOf} depends on ${moduleId}, which does not support target ${target}`
|
||||
);
|
||||
skippedTargetIds.add(rootRequesterId || dependencyOf);
|
||||
return false;
|
||||
}
|
||||
skippedTargetIds.add(moduleId);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resolvedIds.has(moduleId)) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (visitingIds.has(moduleId)) {
|
||||
@@ -248,15 +368,27 @@ function resolveInstallPlan(options = {}) {
|
||||
|
||||
visitingIds.add(moduleId);
|
||||
for (const dependencyId of module.dependencies) {
|
||||
resolveModule(dependencyId, moduleId);
|
||||
const dependencyResolved = resolveModule(
|
||||
dependencyId,
|
||||
moduleId,
|
||||
rootRequesterId || moduleId
|
||||
);
|
||||
if (!dependencyResolved) {
|
||||
visitingIds.delete(moduleId);
|
||||
if (!dependencyOf) {
|
||||
skippedTargetIds.add(moduleId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
visitingIds.delete(moduleId);
|
||||
resolvedIds.add(moduleId);
|
||||
selectedIds.add(moduleId);
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const moduleId of effectiveRequestedIds) {
|
||||
resolveModule(moduleId, null);
|
||||
resolveModule(moduleId, null, moduleId);
|
||||
}
|
||||
|
||||
const selectedModules = manifests.modules.filter(module => selectedIds.has(module.id));
|
||||
@@ -299,7 +431,10 @@ module.exports = {
|
||||
getManifestPaths,
|
||||
loadInstallManifests,
|
||||
listInstallComponents,
|
||||
listLegacyCompatibilityLanguages,
|
||||
listInstallModules,
|
||||
listInstallProfiles,
|
||||
resolveInstallPlan,
|
||||
resolveLegacyCompatibilitySelection,
|
||||
validateInstallModuleIds,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { validateInstallModuleIds } = require('../install-manifests');
|
||||
|
||||
const LEGACY_INSTALL_TARGETS = ['claude', 'cursor', 'antigravity'];
|
||||
|
||||
function dedupeStrings(values) {
|
||||
@@ -35,7 +37,7 @@ function parseInstallArgs(argv) {
|
||||
index += 1;
|
||||
} else if (arg === '--modules') {
|
||||
const raw = args[index + 1] || '';
|
||||
parsed.moduleIds = raw.split(',').map(value => value.trim()).filter(Boolean);
|
||||
parsed.moduleIds = dedupeStrings(raw.split(','));
|
||||
index += 1;
|
||||
} else if (arg === '--with') {
|
||||
const componentId = args[index + 1] || '';
|
||||
@@ -70,7 +72,9 @@ function normalizeInstallRequest(options = {}) {
|
||||
? options.config
|
||||
: null;
|
||||
const profileId = options.profileId || config?.profileId || null;
|
||||
const moduleIds = dedupeStrings([...(config?.moduleIds || []), ...(options.moduleIds || [])]);
|
||||
const moduleIds = validateInstallModuleIds(
|
||||
dedupeStrings([...(config?.moduleIds || []), ...(options.moduleIds || [])])
|
||||
);
|
||||
const includeComponentIds = dedupeStrings([
|
||||
...(config?.includeComponentIds || []),
|
||||
...(options.includeComponentIds || []),
|
||||
@@ -79,29 +83,32 @@ function normalizeInstallRequest(options = {}) {
|
||||
...(config?.excludeComponentIds || []),
|
||||
...(options.excludeComponentIds || []),
|
||||
]);
|
||||
const languages = Array.isArray(options.languages) ? [...options.languages] : [];
|
||||
const legacyLanguages = dedupeStrings(dedupeStrings([
|
||||
...(Array.isArray(options.legacyLanguages) ? options.legacyLanguages : []),
|
||||
...(Array.isArray(options.languages) ? options.languages : []),
|
||||
]).map(language => language.toLowerCase()));
|
||||
const target = options.target || config?.target || 'claude';
|
||||
const hasManifestBaseSelection = Boolean(profileId) || moduleIds.length > 0 || includeComponentIds.length > 0;
|
||||
const usingManifestMode = hasManifestBaseSelection || excludeComponentIds.length > 0;
|
||||
|
||||
if (usingManifestMode && languages.length > 0) {
|
||||
if (usingManifestMode && legacyLanguages.length > 0) {
|
||||
throw new Error(
|
||||
'Legacy language arguments cannot be combined with --profile, --modules, --with, --without, or manifest config selections'
|
||||
);
|
||||
}
|
||||
|
||||
if (!options.help && !hasManifestBaseSelection && languages.length === 0) {
|
||||
if (!options.help && !hasManifestBaseSelection && legacyLanguages.length === 0) {
|
||||
throw new Error('No install profile, module IDs, included components, or legacy languages were provided');
|
||||
}
|
||||
|
||||
return {
|
||||
mode: usingManifestMode ? 'manifest' : 'legacy',
|
||||
mode: usingManifestMode ? 'manifest' : 'legacy-compat',
|
||||
target,
|
||||
profileId,
|
||||
moduleIds,
|
||||
includeComponentIds,
|
||||
excludeComponentIds,
|
||||
languages,
|
||||
legacyLanguages,
|
||||
configPath: config?.path || options.configPath || null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
createLegacyCompatInstallPlan,
|
||||
createLegacyInstallPlan,
|
||||
createManifestInstallPlan,
|
||||
} = require('../install-executor');
|
||||
@@ -23,6 +24,17 @@ function createInstallPlanFromRequest(request, options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
if (request.mode === 'legacy-compat') {
|
||||
return createLegacyCompatInstallPlan({
|
||||
target: request.target,
|
||||
legacyLanguages: request.legacyLanguages,
|
||||
projectRoot: options.projectRoot,
|
||||
homeDir: options.homeDir,
|
||||
claudeRulesDir: options.claudeRulesDir,
|
||||
sourceRoot: options.sourceRoot,
|
||||
});
|
||||
}
|
||||
|
||||
if (request.mode === 'legacy') {
|
||||
return createLegacyInstallPlan({
|
||||
target: request.target,
|
||||
|
||||
Reference in New Issue
Block a user