fix: shrink default OpenCode install surface and gate hooks-runtime (#2140)

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
This commit is contained in:
Matt Van Horn 2026-06-06 22:26:14 -07:00 committed by GitHub
parent 53d4b7d664
commit 80233f1b72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 70 additions and 8 deletions

View File

@ -11,6 +11,14 @@
"workflow-quality" "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": { "core": {
"description": "Minimal harness baseline with commands, hooks, platform configs, and quality workflow support.", "description": "Minimal harness baseline with commands, hooks, platform configs, and quality workflow support.",
"modules": [ "modules": [

View File

@ -111,6 +111,18 @@ const LEGACY_LANGUAGE_EXTRA_MODULE_IDS = Object.freeze({
swift: [], swift: [],
typescript: ['framework-language'], 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) { function readJson(filePath, label) {
try { try {
@ -416,6 +428,22 @@ function expandComponentIdsToModuleIds(componentIds, manifests) {
return dedupeStrings(expandedModuleIds); 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 = {}) { function resolveLegacyCompatibilitySelection(options = {}) {
const manifests = loadInstallManifests(options); const manifests = loadInstallManifests(options);
const target = options.target || null; const target = options.target || null;
@ -471,11 +499,29 @@ function resolveLegacyCompatibilitySelection(options = {}) {
function resolveInstallPlan(options = {}) { function resolveInstallPlan(options = {}) {
const manifests = loadInstallManifests(options); const manifests = loadInstallManifests(options);
const profileId = options.profileId || null; const requestedProfileId = options.profileId || null;
const explicitModuleIds = dedupeStrings(options.moduleIds); const explicitModuleIds = dedupeStrings(options.moduleIds);
const includedComponentIds = dedupeStrings(options.includeComponentIds); const includedComponentIds = dedupeStrings(options.includeComponentIds);
const excludedComponentIds = dedupeStrings(options.excludeComponentIds); const excludedComponentIds = dedupeStrings(options.excludeComponentIds);
const requestedModuleIds = []; 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) { if (profileId) {
const profile = manifests.profiles[profileId]; const profile = manifests.profiles[profileId];
@ -501,13 +547,12 @@ function resolveInstallPlan(options = {}) {
excludedModuleOwners.set(moduleId, owners); excludedModuleOwners.set(moduleId, owners);
} }
} }
for (const exclusion of targetDefaultExclusions) {
const target = options.target || null; const owners = excludedModuleOwners.get(exclusion.moduleId) || [];
if (target && !SUPPORTED_INSTALL_TARGETS.includes(target)) { owners.push(`${target} default`);
throw new Error( excludedModuleOwners.set(exclusion.moduleId, owners);
`Unknown install target: ${target}. Expected one of ${SUPPORTED_INSTALL_TARGETS.join(', ')}`
);
} }
const validatedProjectRoot = readOptionalStringOption(options, 'projectRoot'); const validatedProjectRoot = readOptionalStringOption(options, 'projectRoot');
const validatedHomeDir = readOptionalStringOption(options, 'homeDir'); const validatedHomeDir = readOptionalStringOption(options, 'homeDir');
const targetPlanningInput = target const targetPlanningInput = target
@ -533,7 +578,10 @@ function resolveInstallPlan(options = {}) {
const selectedIds = new Set(); const selectedIds = new Set();
const skippedTargetIds = 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 visitingIds = new Set();
const resolvedIds = new Set(); const resolvedIds = new Set();
@ -622,6 +670,12 @@ function resolveInstallPlan(options = {}) {
explicitModuleIds, explicitModuleIds,
includedComponentIds, includedComponentIds,
excludedComponentIds, 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), selectedModuleIds: selectedModules.map(module => module.id),
skippedModuleIds: skippedModules.map(module => module.id), skippedModuleIds: skippedModules.map(module => module.id),
excludedModuleIds: excludedModules.map(module => module.id), excludedModuleIds: excludedModules.map(module => module.id),