bmad-method 6.5.1-next.2 → 6.5.1-next.4

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/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.5.1-next.2",
4
+ "version": "6.5.1-next.4",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -55,7 +55,8 @@ Load {planning_artifacts}/epics.md and review:
55
55
  2. **Requirements Grouping**: Group related FRs that deliver cohesive user outcomes
56
56
  3. **Incremental Delivery**: Each epic should deliver value independently
57
57
  4. **Logical Flow**: Natural progression from user's perspective
58
- 5. **🔗 Dependency-Free Within Epic**: Stories within an epic must NOT depend on future stories
58
+ 5. **Dependency-Free Within Epic**: Stories within an epic must NOT depend on future stories
59
+ 6. **Implementation Efficiency**: Consider consolidating epics that all modify the same core files into fewer epics
59
60
 
60
61
  **⚠️ CRITICAL PRINCIPLE:**
61
62
  Organize by USER VALUE, not technical layers:
@@ -74,6 +75,18 @@ Organize by USER VALUE, not technical layers:
74
75
  - Epic 3: Frontend Components (creates reusable components) - **No user value**
75
76
  - Epic 4: Deployment Pipeline (CI/CD setup) - **No user value**
76
77
 
78
+ **❌ WRONG Epic Examples (File Churn on Same Component):**
79
+
80
+ - Epic 1: File Upload (modifies model, controller, web form, web API)
81
+ - Epic 2: File Status (modifies model, controller, web form, web API)
82
+ - Epic 3: File Access permissions (modifies model, controller, web form, web API)
83
+ - All three epics touch the same files — consolidate into one epic with ordered stories
84
+
85
+ **✅ CORRECT Alternative:**
86
+
87
+ - Epic 1: File Management Enhancement (upload, status, permissions as stories within one epic)
88
+ - Rationale: Single component, fully pre-designed, no feedback loop between epics
89
+
77
90
  **🔗 DEPENDENCY RULES:**
78
91
 
79
92
  - Each epic must deliver COMPLETE functionality for its domain
@@ -82,21 +95,38 @@ Organize by USER VALUE, not technical layers:
82
95
 
83
96
  ### 3. Design Epic Structure Collaboratively
84
97
 
85
- **Step A: Identify User Value Themes**
98
+ **Step A: Assess Context and Identify Themes**
99
+
100
+ First, assess how much of the solution design is already validated (Architecture, UX, Test Design).
101
+ When the outcome is certain and direction changes between epics are unlikely, prefer fewer but larger epics.
102
+ Split into multiple epics when there is a genuine risk boundary or when early feedback could change direction
103
+ of following epics.
104
+
105
+ Then, identify user value themes:
86
106
 
87
107
  - Look for natural groupings in the FRs
88
108
  - Identify user journeys or workflows
89
109
  - Consider user types and their goals
90
110
 
91
111
  **Step B: Propose Epic Structure**
92
- For each proposed epic:
112
+
113
+ For each proposed epic (considering whether epics share the same core files):
93
114
 
94
115
  1. **Epic Title**: User-centric, value-focused
95
116
  2. **User Outcome**: What users can accomplish after this epic
96
117
  3. **FR Coverage**: Which FR numbers this epic addresses
97
118
  4. **Implementation Notes**: Any technical or UX considerations
98
119
 
99
- **Step C: Create the epics_list**
120
+ **Step C: Review for File Overlap**
121
+
122
+ Assess whether multiple proposed epics repeatedly target the same core files. If overlap is significant:
123
+
124
+ - Distinguish meaningful overlap (same component end-to-end) from incidental sharing
125
+ - Ask whether to consolidate into one epic with ordered stories
126
+ - If confirmed, merge the epic FRs into a single epic, preserving dependency flow: each story must still fit within
127
+ a single dev agent's context
128
+
129
+ **Step D: Create the epics_list**
100
130
 
101
131
  Format the epics_list as:
102
132
 
@@ -90,6 +90,12 @@ Review the complete epic and story breakdown to ensure EVERY FR is covered:
90
90
  - Dependencies flow naturally
91
91
  - Foundation stories only setup what's needed
92
92
  - No big upfront technical work
93
+ - **File Churn Check:** Do multiple epics repeatedly modify the same core files?
94
+ - Assess whether the overlap pattern suggests unnecessary churn or is incidental
95
+ - If overlap is significant: Validate that splitting provides genuine value (risk mitigation, feedback loops, context size limits)
96
+ - If no justification for the split: Recommend consolidation into fewer epics
97
+ - ❌ WRONG: Multiple epics each modify the same core files with no feedback loop between them
98
+ - ✅ RIGHT: Epics target distinct files/components, OR consolidation was explicitly considered and rejected with rationale
93
99
 
94
100
  ### 5. Dependency Validation (CRITICAL)
95
101
 
@@ -1,5 +1,6 @@
1
1
  const path = require('node:path');
2
2
  const os = require('node:os');
3
+ const yaml = require('yaml');
3
4
  const fs = require('./fs-native');
4
5
 
5
6
  /**
@@ -86,8 +87,11 @@ function getExternalModuleCachePath(moduleName, ...segments) {
86
87
  * Built-in modules (core, bmm) live under <src>. External official modules are
87
88
  * cloned into ~/.bmad/cache/external-modules/<name>/ with varying internal
88
89
  * layouts (some at src/module.yaml, some at skills/module.yaml, some nested).
89
- * Local custom-source modules are not cached; their path is read from the
90
- * CustomModuleManager resolution cache set during the same install run.
90
+ * Url-source custom modules are cloned into ~/.bmad/cache/custom-modules/<host>/<owner>/<repo>/
91
+ * and are resolved by walking the cache and matching `code` or `name` from the
92
+ * discovered module.yaml. Local custom-source modules are not cached; their
93
+ * path is read from the CustomModuleManager resolution cache set during the
94
+ * same install run.
91
95
  * This mirrors the candidate-path search in
92
96
  * ExternalModuleManager.findExternalModuleSource but performs no git/network
93
97
  * work, which keeps it safe to call during manifest writing.
@@ -99,11 +103,14 @@ async function resolveInstalledModuleYaml(moduleName) {
99
103
  const builtIn = path.join(getModulePath(moduleName), 'module.yaml');
100
104
  if (await fs.pathExists(builtIn)) return builtIn;
101
105
 
102
- // Search a resolved root directory using the same candidate-path pattern.
103
- async function searchRoot(root) {
106
+ // Collect every module.yaml under a root using the standard candidate paths.
107
+ // Url-source repos can host multiple plugins (discovery mode), so we need all
108
+ // matches, not just the first. Returned in priority order.
109
+ async function searchRootAll(root) {
110
+ const results = [];
104
111
  for (const dir of ['skills', 'src']) {
105
112
  const direct = path.join(root, dir, 'module.yaml');
106
- if (await fs.pathExists(direct)) return direct;
113
+ if (await fs.pathExists(direct)) results.push(direct);
107
114
 
108
115
  const dirPath = path.join(root, dir);
109
116
  if (await fs.pathExists(dirPath)) {
@@ -111,7 +118,7 @@ async function resolveInstalledModuleYaml(moduleName) {
111
118
  for (const entry of entries) {
112
119
  if (!entry.isDirectory()) continue;
113
120
  const nested = path.join(dirPath, entry.name, 'module.yaml');
114
- if (await fs.pathExists(nested)) return nested;
121
+ if (await fs.pathExists(nested)) results.push(nested);
115
122
  }
116
123
  }
117
124
  }
@@ -121,12 +128,19 @@ async function resolveInstalledModuleYaml(moduleName) {
121
128
  for (const entry of rootEntries) {
122
129
  if (!entry.isDirectory() || !entry.name.endsWith('-setup')) continue;
123
130
  const setupAssets = path.join(root, entry.name, 'assets', 'module.yaml');
124
- if (await fs.pathExists(setupAssets)) return setupAssets;
131
+ if (await fs.pathExists(setupAssets)) results.push(setupAssets);
125
132
  }
126
133
 
127
134
  const atRoot = path.join(root, 'module.yaml');
128
- if (await fs.pathExists(atRoot)) return atRoot;
129
- return null;
135
+ if (await fs.pathExists(atRoot)) results.push(atRoot);
136
+ return results;
137
+ }
138
+
139
+ // Backwards-compatible single-result variant for the existing external-cache
140
+ // and resolution-cache fallbacks (one module per root by construction).
141
+ async function searchRoot(root) {
142
+ const all = await searchRootAll(root);
143
+ return all.length > 0 ? all[0] : null;
130
144
  }
131
145
 
132
146
  const cacheRoot = getExternalModuleCachePath(moduleName);
@@ -150,6 +164,37 @@ async function resolveInstalledModuleYaml(moduleName) {
150
164
  // Resolution cache unavailable — continue
151
165
  }
152
166
 
167
+ // Fallback: url-source custom modules cloned to ~/.bmad/cache/custom-modules/.
168
+ // Walk every cached repo, enumerate ALL module.yaml files via searchRootAll
169
+ // (a single repo can host multiple plugins in discovery mode), and match by
170
+ // the yaml's `code` or `name` field. This works on re-install runs where
171
+ // _resolutionCache is empty and covers both discovery-mode (with marketplace.json)
172
+ // and direct-mode modules, since we identify repo roots by .bmad-source.json
173
+ // (written by cloneRepo) or .claude-plugin/ rather than by marketplace.json.
174
+ try {
175
+ const customCacheDir = path.join(os.homedir(), '.bmad', 'cache', 'custom-modules');
176
+ if (await fs.pathExists(customCacheDir)) {
177
+ const { CustomModuleManager } = require('./modules/custom-module-manager');
178
+ const customMgr = new CustomModuleManager();
179
+ const repoRoots = await customMgr._findCacheRepoRoots(customCacheDir);
180
+ for (const { repoPath } of repoRoots) {
181
+ const candidates = await searchRootAll(repoPath);
182
+ for (const candidate of candidates) {
183
+ try {
184
+ const parsed = yaml.parse(await fs.readFile(candidate, 'utf8'));
185
+ if (parsed && (parsed.code === moduleName || parsed.name === moduleName)) {
186
+ return candidate;
187
+ }
188
+ } catch {
189
+ // Malformed yaml — skip
190
+ }
191
+ }
192
+ }
193
+ }
194
+ } catch {
195
+ // Custom-modules cache walk failed — continue
196
+ }
197
+
153
198
  return null;
154
199
  }
155
200