bmad-method 6.3.1-next.18 → 6.3.1-next.19

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.3.1-next.18",
4
+ "version": "6.3.1-next.19",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -2,7 +2,7 @@ const path = require('node:path');
2
2
  const fs = require('../fs-native');
3
3
  const yaml = require('yaml');
4
4
  const crypto = require('node:crypto');
5
- const { getModulePath } = require('../project-root');
5
+ const { resolveInstalledModuleYaml } = require('../project-root');
6
6
  const prompts = require('../prompts');
7
7
 
8
8
  // Load package.json for version info
@@ -244,8 +244,17 @@ class ManifestGenerator {
244
244
  const debug = process.env.BMAD_DEBUG_MANIFEST === 'true';
245
245
 
246
246
  for (const moduleName of this.updatedModules) {
247
- const moduleYamlPath = path.join(getModulePath(moduleName), 'module.yaml');
248
- if (!(await fs.pathExists(moduleYamlPath))) continue;
247
+ const moduleYamlPath = await resolveInstalledModuleYaml(moduleName);
248
+ if (!moduleYamlPath) {
249
+ // External modules live in ~/.bmad/cache/external-modules, not src/modules.
250
+ // Warn rather than silently skip so missing agent rosters don't vanish
251
+ // from config.toml without notice.
252
+ console.warn(
253
+ `[warn] collectAgentsFromModuleYaml: could not locate module.yaml for '${moduleName}'. ` +
254
+ `Agents declared by this module will not be written to config.toml.`,
255
+ );
256
+ continue;
257
+ }
249
258
 
250
259
  let moduleDef;
251
260
  try {
@@ -271,7 +280,9 @@ class ManifestGenerator {
271
280
  }
272
281
 
273
282
  if (debug) {
274
- console.log(`[DEBUG] collectAgentsFromModuleYaml: ${moduleName} contributed ${moduleDef.agents.length} agents`);
283
+ console.log(
284
+ `[DEBUG] collectAgentsFromModuleYaml: ${moduleName} contributed ${moduleDef.agents.length} agents from ${moduleYamlPath}`,
285
+ );
275
286
  }
276
287
  }
277
288
 
@@ -410,8 +421,14 @@ class ManifestGenerator {
410
421
  // team config, so the operator should notice.
411
422
  const scopeByModuleKey = {};
412
423
  for (const moduleName of this.updatedModules) {
413
- const moduleYamlPath = path.join(getModulePath(moduleName), 'module.yaml');
414
- if (!(await fs.pathExists(moduleYamlPath))) continue;
424
+ const moduleYamlPath = await resolveInstalledModuleYaml(moduleName);
425
+ if (!moduleYamlPath) {
426
+ console.warn(
427
+ `[warn] writeCentralConfig: could not locate module.yaml for '${moduleName}'. ` +
428
+ `Answers from this module will default to team scope — user-scoped keys may mis-file into config.toml.`,
429
+ );
430
+ continue;
431
+ }
415
432
  try {
416
433
  const parsed = yaml.parse(await fs.readFile(moduleYamlPath, 'utf8'));
417
434
  if (!parsed || typeof parsed !== 'object') continue;
@@ -1,4 +1,5 @@
1
1
  const path = require('node:path');
2
+ const os = require('node:os');
2
3
  const fs = require('./fs-native');
3
4
 
4
5
  /**
@@ -69,9 +70,62 @@ function getModulePath(moduleName, ...segments) {
69
70
  return getSourcePath('modules', moduleName, ...segments);
70
71
  }
71
72
 
73
+ /**
74
+ * Path to the local external-module clone cache.
75
+ * External official modules (bmb, cis, gds, tea, wds, etc.) are cloned here
76
+ * by ExternalModuleManager during install and are not copied into <src>/modules/.
77
+ */
78
+ function getExternalModuleCachePath(moduleName, ...segments) {
79
+ const base = process.env.BMAD_EXTERNAL_MODULES_CACHE || path.join(os.homedir(), '.bmad', 'cache', 'external-modules');
80
+ return path.join(base, moduleName, ...segments);
81
+ }
82
+
83
+ /**
84
+ * Locate an installed module's `module.yaml` by filesystem lookup only.
85
+ *
86
+ * Built-in modules (core, bmm) live under <src>. External official modules are
87
+ * cloned into ~/.bmad/cache/external-modules/<name>/ with varying internal
88
+ * layouts (some at src/module.yaml, some at skills/module.yaml, some nested).
89
+ * This mirrors the candidate-path search in
90
+ * ExternalModuleManager.findExternalModuleSource but performs no git/network
91
+ * work, which keeps it safe to call during manifest writing.
92
+ *
93
+ * @param {string} moduleName
94
+ * @returns {Promise<string|null>} Absolute path to module.yaml, or null if not found.
95
+ */
96
+ async function resolveInstalledModuleYaml(moduleName) {
97
+ const builtIn = path.join(getModulePath(moduleName), 'module.yaml');
98
+ if (await fs.pathExists(builtIn)) return builtIn;
99
+
100
+ const cacheRoot = getExternalModuleCachePath(moduleName);
101
+ if (!(await fs.pathExists(cacheRoot))) return null;
102
+
103
+ for (const dir of ['skills', 'src']) {
104
+ const direct = path.join(cacheRoot, dir, 'module.yaml');
105
+ if (await fs.pathExists(direct)) return direct;
106
+
107
+ const dirPath = path.join(cacheRoot, dir);
108
+ if (await fs.pathExists(dirPath)) {
109
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
110
+ for (const entry of entries) {
111
+ if (!entry.isDirectory()) continue;
112
+ const nested = path.join(dirPath, entry.name, 'module.yaml');
113
+ if (await fs.pathExists(nested)) return nested;
114
+ }
115
+ }
116
+ }
117
+
118
+ const atRoot = path.join(cacheRoot, 'module.yaml');
119
+ if (await fs.pathExists(atRoot)) return atRoot;
120
+
121
+ return null;
122
+ }
123
+
72
124
  module.exports = {
73
125
  getProjectRoot,
74
126
  getSourcePath,
75
127
  getModulePath,
128
+ getExternalModuleCachePath,
129
+ resolveInstalledModuleYaml,
76
130
  findProjectRoot,
77
131
  };