From 80233f1b7237064a237866b6556041df2733ef51 Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Sat, 6 Jun 2026 22:26:14 -0700 Subject: [PATCH] fix: shrink default OpenCode install surface and gate hooks-runtime (#2140) Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> --- manifests/install-profiles.json | 8 ++++ scripts/lib/install-manifests.js | 70 ++++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/manifests/install-profiles.json b/manifests/install-profiles.json index 0ec84763..8352b2f9 100644 --- a/manifests/install-profiles.json +++ b/manifests/install-profiles.json @@ -11,6 +11,14 @@ "workflow-quality" ] }, + "opencode": { + "description": "Default OpenCode setup with commands, platform configs, and quality workflow support. It intentionally excludes hooks-runtime; opt in with --modules hooks-runtime.", + "modules": [ + "commands-core", + "platform-configs", + "workflow-quality" + ] + }, "core": { "description": "Minimal harness baseline with commands, hooks, platform configs, and quality workflow support.", "modules": [ diff --git a/scripts/lib/install-manifests.js b/scripts/lib/install-manifests.js index b340c1e4..4fb04b7a 100644 --- a/scripts/lib/install-manifests.js +++ b/scripts/lib/install-manifests.js @@ -111,6 +111,18 @@ const LEGACY_LANGUAGE_EXTRA_MODULE_IDS = Object.freeze({ swift: [], typescript: ['framework-language'], }); +const TARGET_DEFAULT_PROFILE_IDS = Object.freeze({ + opencode: 'opencode', +}); +const TARGET_DEFAULT_EXCLUSIONS = Object.freeze({ + opencode: [ + { + moduleId: 'hooks-runtime', + reason: 'OpenCode defaults intentionally exclude hooks-runtime until users opt in.', + optInCommand: './install.sh --target opencode --modules hooks-runtime', + }, + ], +}); function readJson(filePath, label) { try { @@ -416,6 +428,22 @@ function expandComponentIdsToModuleIds(componentIds, manifests) { return dedupeStrings(expandedModuleIds); } +function getTargetDefaultProfileId(target, manifests) { + const profileId = target ? TARGET_DEFAULT_PROFILE_IDS[target] : null; + return profileId && manifests.profiles[profileId] ? profileId : null; +} + +function getTargetDefaultExclusions(target, manifests) { + const exclusions = target ? TARGET_DEFAULT_EXCLUSIONS[target] : null; + if (!Array.isArray(exclusions)) { + return []; + } + + return exclusions + .filter(exclusion => manifests.modulesById.has(exclusion.moduleId)) + .map(exclusion => ({ ...exclusion })); +} + function resolveLegacyCompatibilitySelection(options = {}) { const manifests = loadInstallManifests(options); const target = options.target || null; @@ -471,11 +499,29 @@ function resolveLegacyCompatibilitySelection(options = {}) { function resolveInstallPlan(options = {}) { const manifests = loadInstallManifests(options); - const profileId = options.profileId || null; + const requestedProfileId = options.profileId || null; const explicitModuleIds = dedupeStrings(options.moduleIds); const includedComponentIds = dedupeStrings(options.includeComponentIds); const excludedComponentIds = dedupeStrings(options.excludeComponentIds); const requestedModuleIds = []; + 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 shouldUseTargetDefaultProfile = !requestedProfileId + && explicitModuleIds.length === 0 + && includedComponentIds.length === 0; + const targetDefaultProfileId = shouldUseTargetDefaultProfile + ? getTargetDefaultProfileId(target, manifests) + : null; + const profileId = requestedProfileId || targetDefaultProfileId; + const targetDefaultExclusions = targetDefaultProfileId + ? getTargetDefaultExclusions(target, manifests) + : []; if (profileId) { const profile = manifests.profiles[profileId]; @@ -501,13 +547,12 @@ function resolveInstallPlan(options = {}) { excludedModuleOwners.set(moduleId, owners); } } - - 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(', ')}` - ); + for (const exclusion of targetDefaultExclusions) { + const owners = excludedModuleOwners.get(exclusion.moduleId) || []; + owners.push(`${target} default`); + excludedModuleOwners.set(exclusion.moduleId, owners); } + const validatedProjectRoot = readOptionalStringOption(options, 'projectRoot'); const validatedHomeDir = readOptionalStringOption(options, 'homeDir'); const targetPlanningInput = target @@ -533,7 +578,10 @@ function resolveInstallPlan(options = {}) { const selectedIds = new Set(); const skippedTargetIds = new Set(); - const excludedIds = new Set(excludedModuleIds); + const excludedIds = new Set([ + ...excludedModuleIds, + ...targetDefaultExclusions.map(exclusion => exclusion.moduleId), + ]); const visitingIds = new Set(); const resolvedIds = new Set(); @@ -622,6 +670,12 @@ function resolveInstallPlan(options = {}) { explicitModuleIds, includedComponentIds, excludedComponentIds, + targetDefaultProfileId, + targetDefaultExclusions, + warnings: targetDefaultExclusions.map(exclusion => ( + `${exclusion.moduleId} is intentionally excluded from the OpenCode default. ` + + `Opt in with: ${exclusion.optInCommand}` + )), selectedModuleIds: selectedModules.map(module => module.id), skippedModuleIds: skippedModules.map(module => module.id), excludedModuleIds: excludedModules.map(module => module.id),