bmad-method 6.12.2 → 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.
- package/.claude-plugin/marketplace.json +1 -1
- package/bmad-modules.yaml +3 -1
- package/body.md +17 -0
- package/package.json +1 -1
- package/pr2.md +11 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/__pycache__/sprint_plan.cpython-311.pyc +0 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/__pycache__/sprint_status.cpython-311.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/scripts/__pycache__/config_utils.cpython-311.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_config_utils.cpython-311.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_resolve_config.cpython-311.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-311.pyc +0 -0
- package/tools/installer/core/installer.js +1 -1
- package/tools/installer/modules/channel-plan.js +14 -6
- package/tools/installer/ui.js +53 -33
- package/tools/tests/__pycache__/test_validate_skills.cpython-311.pyc +0 -0
|
@@ -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.
|
|
17
|
+
"version": "6.12.4",
|
|
18
18
|
"author": {
|
|
19
19
|
"name": "Brian (BMad) Madison"
|
|
20
20
|
},
|
package/bmad-modules.yaml
CHANGED
|
@@ -9,7 +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.
|
|
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.
|
|
13
15
|
#
|
|
14
16
|
# deprecated (optional, default false) — when true, the module is hidden from
|
|
15
17
|
# the installer picker UNLESS it is already installed (so existing users can
|
package/body.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Picking a channel in the picker is ignored when the module has a registry `pin`.
|
|
2
|
+
The `tea` pin (v1.27.2) wins, and the manifest records `channel: pinned` with no message.
|
|
3
|
+
|
|
4
|
+
`decideChannelForModule` checked `registryPin` before `--next=CODE`, and the picker never recorded a `stable` pick.
|
|
5
|
+
|
|
6
|
+
New order: `--pin` > `--next=CODE` or picker stable > registry pin > `--channel`/`--all-*` > registry default > `stable`.
|
|
7
|
+
|
|
8
|
+
- `channel-plan.js`: explicit per-module picks resolve before the registry pin. New `stableSet` holds a `stable` pick.
|
|
9
|
+
- `ui.js`: `next` is gone from the install picker and from the update flow's channel review. A stable pick fills `stableSet`. A legacy install that tracks `main` moves to stable without a prompt.
|
|
10
|
+
- Tests: "registry pin beats --next" splits into three cases. Picker-level tests drive the install and update flows, including the all-stable fast path that keeps the pin.
|
|
11
|
+
|
|
12
|
+
`--yes`, the all-stable fast path and `--channel` still get the pin.
|
|
13
|
+
|
|
14
|
+
`--next=CODE` stays for headless use.
|
|
15
|
+
On 6.12 it now installs `main` for a pinned module, which fails for TEA v2+ because that layout needs the v7 installer.
|
|
16
|
+
|
|
17
|
+
`npm run quality` passes.
|
package/package.json
CHANGED
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.
|
package/src/bmm-skills/plan/bmad-sprint-planning/scripts/__pycache__/sprint_plan.cpython-311.pyc
CHANGED
|
Binary file
|
|
Binary file
|
package/src/bmm-skills/ship/bmad-retrospective/scripts/__pycache__/sprint_status.cpython-311.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1474,7 +1474,7 @@ class Installer {
|
|
|
1474
1474
|
// 'pinned' would trigger a stable-channel tag lookup at config-collection
|
|
1475
1475
|
// time, burning GitHub API quota and potentially failing.
|
|
1476
1476
|
const manifestData = await this.manifest.read(bmadDir);
|
|
1477
|
-
const channelOptions = { global: null, nextSet: new Set(), pins: new Map(), warnings: [] };
|
|
1477
|
+
const channelOptions = { global: null, nextSet: new Set(), stableSet: new Set(), pins: new Map(), warnings: [] };
|
|
1478
1478
|
if (manifestData?.modulesDetailed) {
|
|
1479
1479
|
const { fetchStableTags, classifyUpgrade, parseGitHubRepo } = require('../modules/channel-resolver');
|
|
1480
1480
|
for (const entry of manifestData.modulesDetailed) {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* 3. Registry defaults (default_channel from bmad-modules.yaml)
|
|
11
11
|
* 4. Hardcoded fallback 'stable'
|
|
12
12
|
*
|
|
13
|
-
* Precedence: --pin > --next=CODE
|
|
13
|
+
* Precedence: --pin > explicit per-module choice (--next=CODE, picker next/stable) > registry pin
|
|
14
|
+
* > --channel (global) > registry default > 'stable'.
|
|
14
15
|
*
|
|
15
16
|
* This module is pure. No prompts, no git, no filesystem.
|
|
16
17
|
*/
|
|
@@ -24,6 +25,7 @@ const VALID_CHANNELS = new Set(['stable', 'next']);
|
|
|
24
25
|
* @returns {{
|
|
25
26
|
* global: 'stable'|'next'|null,
|
|
26
27
|
* nextSet: Set<string>,
|
|
28
|
+
* stableSet: Set<string>,
|
|
27
29
|
* pins: Map<string, string>,
|
|
28
30
|
* warnings: string[]
|
|
29
31
|
* }}
|
|
@@ -58,6 +60,9 @@ function parseChannelOptions(options = {}) {
|
|
|
58
60
|
nextSet.add(trimmed);
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
// Filled only by the interactive per-module picker (no CLI flag).
|
|
64
|
+
const stableSet = new Set();
|
|
65
|
+
|
|
61
66
|
// --pin CODE=TAG (repeatable)
|
|
62
67
|
const pins = new Map();
|
|
63
68
|
for (const spec of options.pin || []) {
|
|
@@ -76,7 +81,7 @@ function parseChannelOptions(options = {}) {
|
|
|
76
81
|
// headless installs with --next=/--pin for a community module don't hang.
|
|
77
82
|
const acceptBypass = options.yes === true || options.acceptBypass === true;
|
|
78
83
|
|
|
79
|
-
return { global, nextSet, pins, warnings, acceptBypass };
|
|
84
|
+
return { global, nextSet, stableSet, pins, warnings, acceptBypass };
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
function normalizeChannel(raw, warnings, flagName) {
|
|
@@ -109,17 +114,20 @@ function parsePinSpec(spec) {
|
|
|
109
114
|
* source describes where the decision came from, for logging / debugging.
|
|
110
115
|
*/
|
|
111
116
|
function decideChannelForModule({ code, channelOptions, registryDefault, registryPin }) {
|
|
112
|
-
const { global, nextSet, pins } = channelOptions || { nextSet: new Set(), pins: new Map() };
|
|
117
|
+
const { global, nextSet, stableSet, pins } = channelOptions || { nextSet: new Set(), stableSet: new Set(), pins: new Map() };
|
|
113
118
|
|
|
114
119
|
if (pins && pins.has(code)) {
|
|
115
120
|
return { channel: 'pinned', pin: pins.get(code), source: 'flag:--pin' };
|
|
116
121
|
}
|
|
117
|
-
if (registryPin) {
|
|
118
|
-
return { channel: 'pinned', pin: registryPin, source: 'registry:pin' };
|
|
119
|
-
}
|
|
120
122
|
if (nextSet && nextSet.has(code)) {
|
|
121
123
|
return { channel: 'next', source: 'flag:--next' };
|
|
122
124
|
}
|
|
125
|
+
if (stableSet && stableSet.has(code)) {
|
|
126
|
+
return { channel: 'stable', source: 'picker:stable' };
|
|
127
|
+
}
|
|
128
|
+
if (registryPin) {
|
|
129
|
+
return { channel: 'pinned', pin: registryPin, source: 'registry:pin' };
|
|
130
|
+
}
|
|
123
131
|
if (global) {
|
|
124
132
|
return { channel: global, source: 'flag:--channel' };
|
|
125
133
|
}
|
package/tools/installer/ui.js
CHANGED
|
@@ -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
|
|
1969
|
+
for (const code of choosable) {
|
|
1962
1970
|
const info = externalByCode.get(code);
|
|
1963
1971
|
const repoUrl = info.url;
|
|
1964
1972
|
|
|
@@ -1980,15 +1988,12 @@ class UI {
|
|
|
1980
1988
|
message: `${code}: choose a channel`,
|
|
1981
1989
|
choices: [
|
|
1982
1990
|
{ name: stableLabel, value: 'stable' },
|
|
1983
|
-
{ name: 'next (main HEAD \u2014 current development)', value: 'next' },
|
|
1984
1991
|
{ name: 'pin (specific version)', value: 'pin' },
|
|
1985
1992
|
],
|
|
1986
1993
|
default: 'stable',
|
|
1987
1994
|
});
|
|
1988
1995
|
|
|
1989
|
-
if (choice === '
|
|
1990
|
-
channelOptions.nextSet.add(code);
|
|
1991
|
-
} else if (choice === 'pin') {
|
|
1996
|
+
if (choice === 'pin') {
|
|
1992
1997
|
const pinValue = await prompts.text({
|
|
1993
1998
|
message: `Enter a version tag for '${code}' (e.g. v1.6.0):`,
|
|
1994
1999
|
validate: (value) => {
|
|
@@ -1998,8 +2003,25 @@ class UI {
|
|
|
1998
2003
|
},
|
|
1999
2004
|
});
|
|
2000
2005
|
channelOptions.pins.set(code, String(pinValue).trim());
|
|
2006
|
+
} else {
|
|
2007
|
+
channelOptions.stableSet.add(code);
|
|
2001
2008
|
}
|
|
2002
|
-
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
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}`);
|
|
2003
2025
|
}
|
|
2004
2026
|
}
|
|
2005
2027
|
|
|
@@ -2014,8 +2036,7 @@ class UI {
|
|
|
2014
2036
|
* frozen unless the user also passes `--pin CODE=NEW_TAG`.
|
|
2015
2037
|
* - On `next`: no prompt (pull HEAD).
|
|
2016
2038
|
* - On `pinned`: no prompt (stays pinned).
|
|
2017
|
-
* - No channel recorded and `version: null`:
|
|
2018
|
-
* ("Switch to stable / Keep on next").
|
|
2039
|
+
* - No channel recorded and `version: null`: moves to stable, no prompt.
|
|
2019
2040
|
*
|
|
2020
2041
|
* Decisions that freeze the current version are applied by adding a pin to
|
|
2021
2042
|
* `channelOptions.pins` so downstream clone logic honors them.
|
|
@@ -2046,11 +2067,11 @@ class UI {
|
|
|
2046
2067
|
const prev = existingByName.get(code);
|
|
2047
2068
|
if (!prev) return false;
|
|
2048
2069
|
const info = externalByCode.get(code);
|
|
2049
|
-
return info && !info.builtIn;
|
|
2070
|
+
return info && !info.builtIn && !info.pin;
|
|
2050
2071
|
});
|
|
2051
2072
|
if (existingWithChannel.length > 0) {
|
|
2052
2073
|
reviewChannels = await prompts.confirm({
|
|
2053
|
-
message: 'Review channel assignments (stable /
|
|
2074
|
+
message: 'Review channel assignments (stable / pin) for your existing modules?',
|
|
2054
2075
|
default: false,
|
|
2055
2076
|
});
|
|
2056
2077
|
}
|
|
@@ -2066,12 +2087,25 @@ class UI {
|
|
|
2066
2087
|
// their version is stapled to the CLI version, not a git tag. Skip
|
|
2067
2088
|
// tag-API lookups for them; the "upgrade" mechanism is `npx bmad@X install`.
|
|
2068
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
|
+
}
|
|
2069
2103
|
|
|
2070
2104
|
const repoUrl = info.url;
|
|
2071
2105
|
const parsed = repoUrl ? parseGitHubRepo(repoUrl) : null;
|
|
2072
2106
|
|
|
2073
2107
|
// Legacy migration: manifest carries no channel and a null/empty
|
|
2074
|
-
// version.
|
|
2108
|
+
// version. The next channel is gone from the picker, so move it to stable.
|
|
2075
2109
|
const recordedChannel = prev.channel || null;
|
|
2076
2110
|
const needsMigration = !recordedChannel && (prev.version == null || prev.version === '');
|
|
2077
2111
|
if (needsMigration) {
|
|
@@ -2079,15 +2113,7 @@ class UI {
|
|
|
2079
2113
|
// Conservative headless default: stable.
|
|
2080
2114
|
continue;
|
|
2081
2115
|
}
|
|
2082
|
-
|
|
2083
|
-
message: `${code}: your existing install tracks the main branch. Switch to stable releases (recommended for production), or keep on main?`,
|
|
2084
|
-
choices: [
|
|
2085
|
-
{ name: 'Switch to stable', value: 'stable' },
|
|
2086
|
-
{ name: 'Keep on main (next)', value: 'next' },
|
|
2087
|
-
],
|
|
2088
|
-
default: 'stable',
|
|
2089
|
-
});
|
|
2090
|
-
if (chosen === 'next') channelOptions.nextSet.add(code);
|
|
2116
|
+
channelOptions.stableSet.add(code);
|
|
2091
2117
|
continue;
|
|
2092
2118
|
}
|
|
2093
2119
|
|
|
@@ -2105,9 +2131,6 @@ class UI {
|
|
|
2105
2131
|
if (recordedChannel !== 'stable') {
|
|
2106
2132
|
switchChoices.push({ name: 'Switch to stable (released version)', value: 'stable' });
|
|
2107
2133
|
}
|
|
2108
|
-
if (recordedChannel !== 'next') {
|
|
2109
|
-
switchChoices.push({ name: 'Switch to next (main HEAD)', value: 'next' });
|
|
2110
|
-
}
|
|
2111
2134
|
switchChoices.push({ name: 'Pin to a specific version tag', value: 'pin' });
|
|
2112
2135
|
|
|
2113
2136
|
const choice = await prompts.select({
|
|
@@ -2116,10 +2139,6 @@ class UI {
|
|
|
2116
2139
|
default: 'keep',
|
|
2117
2140
|
});
|
|
2118
2141
|
|
|
2119
|
-
if (choice === 'next') {
|
|
2120
|
-
channelOptions.nextSet.add(code);
|
|
2121
|
-
continue;
|
|
2122
|
-
}
|
|
2123
2142
|
if (choice === 'pin') {
|
|
2124
2143
|
const pinValue = await prompts.text({
|
|
2125
2144
|
message: `Enter a version tag for '${code}' (e.g. v1.6.0):`,
|
|
@@ -2135,6 +2154,7 @@ class UI {
|
|
|
2135
2154
|
if (choice === 'stable') {
|
|
2136
2155
|
// Switch to stable: install at the top stable tag without an
|
|
2137
2156
|
// upgrade-classification prompt (the user explicitly opted in).
|
|
2157
|
+
channelOptions.stableSet.add(code);
|
|
2138
2158
|
// Also warm the tag cache here so the actual clone step doesn't
|
|
2139
2159
|
// need a second GitHub API call (can hit rate limits).
|
|
2140
2160
|
if (parsed) {
|
|
Binary file
|