From 30d94a878dba4b8e666f01774fe396f139257291 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Sun, 26 Apr 2026 10:41:47 -0500 Subject: [PATCH] fix(installer): mirror launch channel as default for external modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the user runs `npx bmad-method@next install`, the installer itself runs from a prerelease, but the interactive channel gate previously hardcoded "(all stable)" — defaulting tea/community modules to stable while bmad-method itself was on next. The bleeding-edge launch did not flow through. Detect the installer's own version via semver.prerelease() and default the gate (and per-module picker) to match — "all next" for prerelease launches, "all stable" for stable. Users keep full control: hit "n" to customize per module, or pass explicit --channel / --pin / --next flags to override. --- tools/installer/ui.js | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/tools/installer/ui.js b/tools/installer/ui.js index f2f6e31c1..cab7fa939 100644 --- a/tools/installer/ui.js +++ b/tools/installer/ui.js @@ -2,6 +2,7 @@ const path = require('node:path'); const os = require('node:os'); const semver = require('semver'); const fs = require('./fs-native'); +const installerPackageJson = require('../../package.json'); const { CLIUtils } = require('./cli-utils'); const { ExternalModuleManager } = require('./modules/external-manager'); const { resolveModuleVersion } = require('./modules/version-resolver'); @@ -330,10 +331,12 @@ class UI { selectedModules.unshift('core'); } - // Interactive channel gate: "Ready to install (all stable)? [Y/n]" - // Only shown for fresh installs with no channel flags and an external module - // selected. Non-interactive installs skip this and fall through to the - // registry default (stable) or whatever flags were supplied. + // Interactive channel gate: "Ready to install (all stable)? [Y/n]" — or + // "(all next)" when the installer was launched from a prerelease + // (npx bmad-method@next). Only shown for fresh installs with no channel + // flags and an external module selected. Non-interactive installs skip this + // and fall through to the registry default (stable) or whatever flags were + // supplied. await this._interactiveChannelGate({ options, channelOptions, selectedModules }); let toolSelection = await this.promptToolSelection(confirmedDirectory, options); @@ -1779,14 +1782,17 @@ class UI { } /** - * Fast-path channel gate: confirm "all stable" or open the per-module picker. + * Fast-path channel gate: confirm "all stable" / "all next" or open the + * per-module picker. The default channel mirrors the installer's launch tag — + * a prerelease bmad-method (npx bmad-method@next) defaults the gate to "all + * next"; a stable launch keeps the original "all stable" default. * * Skipped when: * - running non-interactively (--yes) * - the user already passed channel flags (--channel / --pin / --next) * - no externals/community modules are selected * - * Mutates channelOptions.pins and channelOptions.nextSet to reflect picker choices. + * Mutates channelOptions.global / .pins / .nextSet to reflect gate + picker choices. */ async _interactiveChannelGate({ options, channelOptions, selectedModules }) { if (options.yes) return; @@ -1812,11 +1818,24 @@ class UI { }); if (channelSelectable.length === 0) return; + // When the user launched the installer from a prerelease (npx bmad-method@next), + // mirror that intent for external modules: default the gate to "all next" so the + // bleeding-edge launch flows end-to-end. Stable launches keep the original + // "all stable" default. + const launchedFromPrerelease = semver.prerelease(installerPackageJson.version) !== null; + const fastPathChannel = launchedFromPrerelease ? 'next' : 'stable'; + const fastPath = await prompts.confirm({ - message: `Ready to install (all stable)? Pick "n" to customize channels or pin versions.`, + message: `Ready to install (all ${fastPathChannel})? Pick "n" to customize channels or pin versions.`, default: true, }); - if (fastPath) return; // stable for all, registry default applies + if (fastPath) { + if (fastPathChannel === 'next') { + channelOptions.global = 'next'; + } + // stable: leave channelOptions untouched; registry default applies. + return; + } // Customize path: per-module picker. const { fetchStableTags, parseGitHubRepo } = require('./modules/channel-resolver'); @@ -1846,7 +1865,7 @@ class UI { { name: 'next (main HEAD \u2014 current development)', value: 'next' }, { name: 'pin (specific version)', value: 'pin' }, ], - default: 'stable', + default: fastPathChannel, }); if (choice === 'next') {