bmad-method 6.12.3-next.0 → 6.12.3-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,7 +14,7 @@
14
14
  "displayName": "BMad Method Analyze Plan Build",
15
15
  "source": "./",
16
16
  "description": "Full-lifecycle AI development framework — agents and workflows for product analysis, planning, architecture, and implementation.",
17
- "version": "6.12.3",
17
+ "version": "6.12.4",
18
18
  "author": {
19
19
  "name": "Brian (BMad) Madison"
20
20
  },
package/bmad-modules.yaml CHANGED
@@ -9,9 +9,9 @@
9
9
  # Omit to inherit the installer's hardcoded default (stable).
10
10
  #
11
11
  # pin (optional) — a git tag to install instead of the channel. Use it when
12
- # newer releases no longer work with this installer. The pin is the default for
13
- # every module that gets no explicit choice. --pin, --next=CODE and a stable or
14
- # next pick in the interactive picker override it; --channel and --all-* do not.
12
+ # newer releases no longer work with this installer. The installer skips the
13
+ # channel picker for a pinned module and prints a notice. --pin and --next=CODE
14
+ # override the pin; --channel and --all-* do not.
15
15
  #
16
16
  # deprecated (optional, default false) — when true, the module is hidden from
17
17
  # the installer picker UNLESS it is already installed (so existing users can
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method",
4
- "version": "6.12.3-next.0",
4
+ "version": "6.12.3-next.1",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
package/pr2.md ADDED
@@ -0,0 +1,11 @@
1
+ On 6.12.3 the TEA channel picker offers stable, which resolves to v3.0.2.
2
+ The install then fails with `Expected 'src/module.yaml' to exist in version v3.0.2`, because TEA v2+ uses the v7 layout.
3
+
4
+ A module with a registry `pin` now skips the picker, in both the install and update flows.
5
+ The installer prints `tea is pinned to v1.27.2. Newer versions need the v7 installer: npx skills add bmad-code-org/bmad-method-test-architecture-enterprise`.
6
+ Unpinned modules keep the stable and pin choices.
7
+ `--pin CODE=TAG` still overrides a registry pin.
8
+
9
+ Tests drive the install and update flows with a pinned and an unpinned module.
10
+ They fail without the change and pass with it.
11
+ `npm run quality` passes.
@@ -1931,12 +1931,6 @@ class UI {
1931
1931
  * Mutates channelOptions.pins and channelOptions.nextSet to reflect picker choices.
1932
1932
  */
1933
1933
  async _interactiveChannelGate({ options, channelOptions, selectedModules }) {
1934
- if (options.yes) return;
1935
- // If the user already declared their channel intent via flags, trust them
1936
- // and skip the gate.
1937
- const haveFlagIntent = channelOptions.global || channelOptions.nextSet.size > 0 || channelOptions.pins.size > 0;
1938
- if (haveFlagIntent) return;
1939
-
1940
1934
  // Figure out which selected modules actually get a channel (externals only).
1941
1935
  // Bundled core/bmm and custom modules skip the picker.
1942
1936
  const externalManager = new ExternalModuleManager();
@@ -1949,6 +1943,20 @@ class UI {
1949
1943
  });
1950
1944
  if (channelSelectable.length === 0) return;
1951
1945
 
1946
+ // A registry pin marks the newest version this installer can install.
1947
+ // Offering stable or next there leads to a failed install, so say what is
1948
+ // pinned (headless installs included) and skip the picker for those modules.
1949
+ await this._announcePinnedModules(channelSelectable, externalByCode, channelOptions);
1950
+
1951
+ if (options.yes) return;
1952
+ // If the user already declared their channel intent via flags, trust them
1953
+ // and skip the gate.
1954
+ const haveFlagIntent = channelOptions.global || channelOptions.nextSet.size > 0 || channelOptions.pins.size > 0;
1955
+ if (haveFlagIntent) return;
1956
+
1957
+ const choosable = channelSelectable.filter((code) => !externalByCode.get(code).pin);
1958
+ if (choosable.length === 0) return;
1959
+
1952
1960
  const fastPath = await prompts.confirm({
1953
1961
  message: `Ready to install (all stable)? Pick "n" to customize channels or pin versions.`,
1954
1962
  default: true,
@@ -1958,7 +1966,7 @@ class UI {
1958
1966
  // Customize path: per-module picker.
1959
1967
  const { fetchStableTags, parseGitHubRepo } = require('./modules/channel-resolver');
1960
1968
 
1961
- for (const code of channelSelectable) {
1969
+ for (const code of choosable) {
1962
1970
  const info = externalByCode.get(code);
1963
1971
  const repoUrl = info.url;
1964
1972
 
@@ -2001,6 +2009,22 @@ class UI {
2001
2009
  }
2002
2010
  }
2003
2011
 
2012
+ /**
2013
+ * Tell the user which selected modules sit on a registry pin and how to get a
2014
+ * newer version. Skips a module whose version the user set with --pin/--next.
2015
+ */
2016
+ async _announcePinnedModules(codes, externalByCode, channelOptions) {
2017
+ const { parseGitHubRepo } = require('./modules/channel-resolver');
2018
+ for (const code of codes) {
2019
+ const info = externalByCode.get(code);
2020
+ if (!info || !info.pin || info.builtIn) continue;
2021
+ if (channelOptions.pins.has(code) || channelOptions.nextSet.has(code)) continue;
2022
+ const repo = info.url ? parseGitHubRepo(info.url) : null;
2023
+ const where = repo ? ` Newer versions need the v7 installer: npx skills add ${repo.owner}/${repo.repo}` : '';
2024
+ await prompts.log.info(`${code} is pinned to ${info.pin}.${where}`);
2025
+ }
2026
+ }
2027
+
2004
2028
  /**
2005
2029
  * Resolve channel decisions for an update over an existing install.
2006
2030
  *
@@ -2043,7 +2067,7 @@ class UI {
2043
2067
  const prev = existingByName.get(code);
2044
2068
  if (!prev) return false;
2045
2069
  const info = externalByCode.get(code);
2046
- return info && !info.builtIn;
2070
+ return info && !info.builtIn && !info.pin;
2047
2071
  });
2048
2072
  if (existingWithChannel.length > 0) {
2049
2073
  reviewChannels = await prompts.confirm({
@@ -2063,6 +2087,19 @@ class UI {
2063
2087
  // their version is stapled to the CLI version, not a git tag. Skip
2064
2088
  // tag-API lookups for them; the "upgrade" mechanism is `npx bmad@X install`.
2065
2089
  if (info.builtIn) continue;
2090
+ // A registry-pinned module stays on its pin; the channel logic below would
2091
+ // move it to a version this installer cannot install. A version the user
2092
+ // pinned earlier is kept.
2093
+ if (info.pin) {
2094
+ const decided = channelOptions.global || channelOptions.nextSet.has(code) || channelOptions.pins.has(code);
2095
+ const keepsOwnPin = !decided && prev.channel === 'pinned' && prev.version && prev.version !== info.pin;
2096
+ if (keepsOwnPin) {
2097
+ channelOptions.pins.set(code, prev.version);
2098
+ } else {
2099
+ await this._announcePinnedModules([code], externalByCode, channelOptions);
2100
+ }
2101
+ continue;
2102
+ }
2066
2103
 
2067
2104
  const repoUrl = info.url;
2068
2105
  const parsed = repoUrl ? parseGitHubRepo(repoUrl) : null;