#!/usr/bin/env node /** * Refactored ECC installer runtime. * * Keeps the legacy language-based install entrypoint intact while moving * target-specific mutation logic into testable Node code. */ const os = require('os'); const { SUPPORTED_INSTALL_TARGETS, listLegacyCompatibilityLanguages, } = require('./lib/install-manifests'); const { LEGACY_INSTALL_TARGETS, normalizeInstallRequest, parseInstallArgs, } = require('./lib/install/request'); function showHelp(exitCode = 0) { const languages = listLegacyCompatibilityLanguages(); console.log(` Usage: install.sh [--target <${LEGACY_INSTALL_TARGETS.join('|')}>] [--dry-run] [--json] [ ...] install.sh [--target <${SUPPORTED_INSTALL_TARGETS.join('|')}>] [--dry-run] [--json] --profile [--with ]... [--without ]... install.sh [--target <${SUPPORTED_INSTALL_TARGETS.join('|')}>] [--dry-run] [--json] --modules [--with ]... [--without ]... install.sh [--dry-run] [--json] --config Targets: claude (default) - Install rules to ~/.claude/rules/ cursor - Install rules, hooks, and bundled Cursor configs to ./.cursor/ antigravity - Install rules, workflows, skills, and agents to ./.agent/ Options: --profile Resolve and install a manifest profile --modules Resolve and install explicit module IDs --with Include a user-facing install component --without Exclude a user-facing install component --config Load install intent from ecc-install.json --dry-run Show the install plan without copying files --json Emit machine-readable plan/result JSON --help Show this help text Available languages: ${languages.map(language => ` - ${language}`).join('\n')} `); process.exit(exitCode); } function printHumanPlan(plan, dryRun) { console.log(`${dryRun ? 'Dry-run install plan' : 'Applying install plan'}:\n`); console.log(`Mode: ${plan.mode}`); console.log(`Target: ${plan.target}`); console.log(`Adapter: ${plan.adapter.id}`); console.log(`Install root: ${plan.installRoot}`); console.log(`Install-state: ${plan.installStatePath}`); if (plan.mode === 'legacy') { console.log(`Languages: ${plan.languages.join(', ')}`); } else { if (plan.mode === 'legacy-compat') { console.log(`Legacy languages: ${plan.legacyLanguages.join(', ')}`); } console.log(`Profile: ${plan.profileId || '(custom modules)'}`); console.log(`Included components: ${plan.includedComponentIds.join(', ') || '(none)'}`); console.log(`Excluded components: ${plan.excludedComponentIds.join(', ') || '(none)'}`); console.log(`Requested modules: ${plan.requestedModuleIds.join(', ') || '(none)'}`); console.log(`Selected modules: ${plan.selectedModuleIds.join(', ') || '(none)'}`); if (plan.skippedModuleIds.length > 0) { console.log(`Skipped modules: ${plan.skippedModuleIds.join(', ')}`); } if (plan.excludedModuleIds.length > 0) { console.log(`Excluded modules: ${plan.excludedModuleIds.join(', ')}`); } } console.log(`Operations: ${plan.operations.length}`); if (plan.warnings.length > 0) { console.log('\nWarnings:'); for (const warning of plan.warnings) { console.log(`- ${warning}`); } } console.log('\nPlanned file operations:'); for (const operation of plan.operations) { console.log(`- ${operation.sourceRelativePath} -> ${operation.destinationPath}`); } if (!dryRun) { console.log(`\nDone. Install-state written to ${plan.installStatePath}`); } } function main() { try { const options = parseInstallArgs(process.argv); if (options.help) { showHelp(0); } const { findDefaultInstallConfigPath, loadInstallConfig, } = require('./lib/install/config'); const { applyInstallPlan } = require('./lib/install-executor'); const { createInstallPlanFromRequest } = require('./lib/install/runtime'); const defaultConfigPath = options.configPath || options.languages.length > 0 ? null : findDefaultInstallConfigPath({ cwd: process.cwd() }); const config = options.configPath ? loadInstallConfig(options.configPath, { cwd: process.cwd() }) : (defaultConfigPath ? loadInstallConfig(defaultConfigPath, { cwd: process.cwd() }) : null); const request = normalizeInstallRequest({ ...options, config, }); const plan = createInstallPlanFromRequest(request, { projectRoot: process.cwd(), homeDir: process.env.HOME || os.homedir(), claudeRulesDir: process.env.CLAUDE_RULES_DIR || null, }); if (options.dryRun) { if (options.json) { console.log(JSON.stringify({ dryRun: true, plan }, null, 2)); } else { printHumanPlan(plan, true); } return; } const result = applyInstallPlan(plan); if (options.json) { console.log(JSON.stringify({ dryRun: false, result }, null, 2)); } else { printHumanPlan(result, false); } } catch (error) { console.error(`Error: ${error.message}\n`); showHelp(1); } } main();