bmad-method 6.2.3-next.9 → 6.3.0

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.
Files changed (72) hide show
  1. package/.claude-plugin/marketplace.json +0 -3
  2. package/README.md +8 -9
  3. package/README_CN.md +1 -1
  4. package/README_VN.md +110 -0
  5. package/package.json +1 -1
  6. package/removals.txt +17 -0
  7. package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-13-responsive-accessibility.md +1 -1
  8. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/data/prd-purpose.md +197 -0
  9. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01-discovery.md +1 -1
  10. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01b-legacy-conversion.md +1 -1
  11. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-02-review.md +1 -1
  12. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-03-edit.md +1 -1
  13. package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md +1 -3
  14. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-01-document-discovery.md +1 -1
  15. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-02-prd-analysis.md +1 -1
  16. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-03-epic-coverage-validation.md +1 -1
  17. package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/workflow.md +1 -1
  18. package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/workflow.md +1 -1
  19. package/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +5 -0
  20. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/SKILL.md +29 -0
  21. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/generate-trail.md +38 -0
  22. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-01-orientation.md +105 -0
  23. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-02-walkthrough.md +89 -0
  24. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-03-detail-pass.md +106 -0
  25. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-04-testing.md +74 -0
  26. package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-05-wrapup.md +24 -0
  27. package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-01-gather-context.md +38 -15
  28. package/src/bmm-skills/4-implementation/bmad-correct-course/checklist.md +2 -2
  29. package/src/bmm-skills/4-implementation/bmad-correct-course/workflow.md +8 -8
  30. package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/checklist.md +1 -1
  31. package/src/bmm-skills/4-implementation/bmad-quick-dev/compile-epic-context.md +62 -0
  32. package/src/bmm-skills/4-implementation/bmad-quick-dev/spec-template.md +1 -1
  33. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-01-clarify-and-route.md +33 -6
  34. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-02-plan.md +20 -8
  35. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-03-implement.md +2 -0
  36. package/src/bmm-skills/4-implementation/bmad-quick-dev/step-oneshot.md +16 -4
  37. package/src/bmm-skills/4-implementation/bmad-quick-dev/workflow.md +1 -5
  38. package/src/bmm-skills/4-implementation/bmad-retrospective/workflow.md +134 -134
  39. package/src/bmm-skills/4-implementation/bmad-sprint-planning/sprint-status-template.yaml +1 -1
  40. package/src/bmm-skills/4-implementation/bmad-sprint-planning/workflow.md +3 -3
  41. package/src/bmm-skills/4-implementation/bmad-sprint-status/workflow.md +2 -2
  42. package/src/bmm-skills/module-help.csv +2 -0
  43. package/src/core-skills/bmad-help/SKILL.md +4 -2
  44. package/src/core-skills/bmad-party-mode/SKILL.md +8 -6
  45. package/src/core-skills/module-help.csv +1 -0
  46. package/tools/installer/cli-utils.js +18 -9
  47. package/tools/installer/commands/install.js +1 -1
  48. package/tools/installer/core/existing-install.js +2 -8
  49. package/tools/installer/core/install-paths.js +0 -3
  50. package/tools/installer/core/installer.js +180 -463
  51. package/tools/installer/core/manifest-generator.js +8 -14
  52. package/tools/installer/core/manifest.js +94 -102
  53. package/tools/installer/ide/_config-driven.js +149 -38
  54. package/tools/installer/ide/shared/skill-manifest.js +1 -16
  55. package/tools/installer/install-messages.yaml +19 -26
  56. package/tools/installer/modules/community-manager.js +377 -0
  57. package/tools/installer/modules/custom-module-manager.js +644 -0
  58. package/tools/installer/modules/external-manager.js +65 -49
  59. package/tools/installer/modules/official-modules.js +117 -65
  60. package/tools/installer/modules/plugin-resolver.js +398 -0
  61. package/tools/installer/modules/registry-client.js +66 -0
  62. package/tools/installer/{external-official-modules.yaml → modules/registry-fallback.yaml} +3 -12
  63. package/tools/installer/ui.js +549 -666
  64. package/src/bmm-skills/4-implementation/bmad-agent-qa/SKILL.md +0 -61
  65. package/src/bmm-skills/4-implementation/bmad-agent-qa/bmad-skill-manifest.yaml +0 -11
  66. package/src/bmm-skills/4-implementation/bmad-agent-quick-flow-solo-dev/SKILL.md +0 -53
  67. package/src/bmm-skills/4-implementation/bmad-agent-quick-flow-solo-dev/bmad-skill-manifest.yaml +0 -11
  68. package/src/bmm-skills/4-implementation/bmad-agent-sm/SKILL.md +0 -55
  69. package/src/bmm-skills/4-implementation/bmad-agent-sm/bmad-skill-manifest.yaml +0 -11
  70. package/tools/installer/core/custom-module-cache.js +0 -260
  71. package/tools/installer/custom-handler.js +0 -112
  72. package/tools/installer/modules/custom-modules.js +0 -197
@@ -4,64 +4,98 @@ const path = require('node:path');
4
4
  const { execSync } = require('node:child_process');
5
5
  const yaml = require('yaml');
6
6
  const prompts = require('../prompts');
7
+ const { RegistryClient } = require('./registry-client');
8
+
9
+ const REGISTRY_RAW_URL = 'https://raw.githubusercontent.com/bmad-code-org/bmad-plugins-marketplace/main/registry/official.yaml';
10
+ const FALLBACK_CONFIG_PATH = path.join(__dirname, 'registry-fallback.yaml');
7
11
 
8
12
  /**
9
- * Manages external official modules defined in external-official-modules.yaml
10
- * These are modules hosted in external repositories that can be installed
13
+ * Manages official modules from the remote BMad marketplace registry.
14
+ * Fetches registry/official.yaml from GitHub; falls back to the bundled
15
+ * external-official-modules.yaml when the network is unavailable.
11
16
  *
12
17
  * @class ExternalModuleManager
13
18
  */
14
19
  class ExternalModuleManager {
15
20
  constructor() {
16
- this.externalModulesConfigPath = path.join(__dirname, '../external-official-modules.yaml');
17
- this.cachedModules = null;
21
+ this._client = new RegistryClient();
18
22
  }
19
23
 
20
24
  /**
21
- * Load and parse the external-official-modules.yaml file
22
- * @returns {Object} Parsed YAML content with modules object
25
+ * Load the official modules registry from GitHub, falling back to the
26
+ * bundled YAML file if the fetch fails.
27
+ * @returns {Object} Parsed YAML content with modules array
23
28
  */
24
29
  async loadExternalModulesConfig() {
25
30
  if (this.cachedModules) {
26
31
  return this.cachedModules;
27
32
  }
28
33
 
34
+ // Try remote registry first
35
+ try {
36
+ const content = await this._client.fetch(REGISTRY_RAW_URL);
37
+ const config = yaml.parse(content);
38
+ if (config?.modules?.length) {
39
+ this.cachedModules = config;
40
+ return config;
41
+ }
42
+ } catch {
43
+ // Fall through to local fallback
44
+ }
45
+
46
+ // Fallback to bundled file
29
47
  try {
30
- const content = await fs.readFile(this.externalModulesConfigPath, 'utf8');
48
+ const content = await fs.readFile(FALLBACK_CONFIG_PATH, 'utf8');
31
49
  const config = yaml.parse(content);
32
50
  this.cachedModules = config;
51
+ await prompts.log.warn('Could not reach BMad registry; using bundled module list.');
33
52
  return config;
34
53
  } catch (error) {
35
- await prompts.log.warn(`Failed to load external modules config: ${error.message}`);
36
- return { modules: {} };
54
+ await prompts.log.warn(`Failed to load modules config: ${error.message}`);
55
+ return { modules: [] };
37
56
  }
38
57
  }
39
58
 
40
59
  /**
41
- * Get list of available external modules
60
+ * Normalize a module entry from either the remote registry format
61
+ * (snake_case, array) or the legacy bundled format (kebab-case, object map).
62
+ * @param {Object} mod - Raw module config from YAML
63
+ * @param {string} [key] - Key name (only for legacy map format)
64
+ * @returns {Object} Normalized module info
65
+ */
66
+ _normalizeModule(mod, key) {
67
+ return {
68
+ key: key || mod.name,
69
+ url: mod.repository || mod.url,
70
+ moduleDefinition: mod.module_definition || mod['module-definition'],
71
+ code: mod.code,
72
+ name: mod.display_name || mod.name,
73
+ description: mod.description || '',
74
+ defaultSelected: mod.default_selected === true || mod.defaultSelected === true,
75
+ type: mod.type || 'bmad-org',
76
+ npmPackage: mod.npm_package || mod.npmPackage || null,
77
+ builtIn: mod.built_in === true,
78
+ isExternal: mod.built_in !== true,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Get list of available modules from the registry
42
84
  * @returns {Array<Object>} Array of module info objects
43
85
  */
44
86
  async listAvailable() {
45
87
  const config = await this.loadExternalModulesConfig();
46
- const modules = [];
47
88
 
48
- for (const [key, moduleConfig] of Object.entries(config.modules || {})) {
49
- modules.push({
50
- key,
51
- url: moduleConfig.url,
52
- moduleDefinition: moduleConfig['module-definition'],
53
- code: moduleConfig.code,
54
- name: moduleConfig.name,
55
- header: moduleConfig.header,
56
- subheader: moduleConfig.subheader,
57
- description: moduleConfig.description || '',
58
- defaultSelected: moduleConfig.defaultSelected === true,
59
- type: moduleConfig.type || 'community', // bmad-org or community
60
- npmPackage: moduleConfig.npmPackage || null, // Include npm package name
61
- isExternal: true,
62
- });
89
+ // Remote format: modules is an array
90
+ if (Array.isArray(config.modules)) {
91
+ return config.modules.map((mod) => this._normalizeModule(mod));
63
92
  }
64
93
 
94
+ // Legacy bundled format: modules is an object map
95
+ const modules = [];
96
+ for (const [key, mod] of Object.entries(config.modules || {})) {
97
+ modules.push(this._normalizeModule(mod, key));
98
+ }
65
99
  return modules;
66
100
  }
67
101
 
@@ -81,27 +115,8 @@ class ExternalModuleManager {
81
115
  * @returns {Object|null} Module info or null if not found
82
116
  */
83
117
  async getModuleByKey(key) {
84
- const config = await this.loadExternalModulesConfig();
85
- const moduleConfig = config.modules?.[key];
86
-
87
- if (!moduleConfig) {
88
- return null;
89
- }
90
-
91
- return {
92
- key,
93
- url: moduleConfig.url,
94
- moduleDefinition: moduleConfig['module-definition'],
95
- code: moduleConfig.code,
96
- name: moduleConfig.name,
97
- header: moduleConfig.header,
98
- subheader: moduleConfig.subheader,
99
- description: moduleConfig.description || '',
100
- defaultSelected: moduleConfig.defaultSelected === true,
101
- type: moduleConfig.type || 'community', // bmad-org or community
102
- npmPackage: moduleConfig.npmPackage || null, // Include npm package name
103
- isExternal: true,
104
- };
118
+ const modules = await this.listAvailable();
119
+ return modules.find((m) => m.key === key) || null;
105
120
  }
106
121
 
107
122
  /**
@@ -154,7 +169,7 @@ class ExternalModuleManager {
154
169
  const moduleInfo = await this.getModuleByCode(moduleCode);
155
170
 
156
171
  if (!moduleInfo) {
157
- throw new Error(`External module '${moduleCode}' not found in external-official-modules.yaml`);
172
+ throw new Error(`External module '${moduleCode}' not found in the BMad registry`);
158
173
  }
159
174
 
160
175
  const cacheDir = this.getExternalCacheDir();
@@ -304,7 +319,7 @@ class ExternalModuleManager {
304
319
  async findExternalModuleSource(moduleCode, options = {}) {
305
320
  const moduleInfo = await this.getModuleByCode(moduleCode);
306
321
 
307
- if (!moduleInfo) {
322
+ if (!moduleInfo || moduleInfo.builtIn) {
308
323
  return null;
309
324
  }
310
325
 
@@ -349,6 +364,7 @@ class ExternalModuleManager {
349
364
  // Nothing found: return configured path (preserves old behavior for error messaging)
350
365
  return path.dirname(configuredPath);
351
366
  }
367
+ cachedModules = null;
352
368
  }
353
369
 
354
370
  module.exports = { ExternalModuleManager };
@@ -98,11 +98,10 @@ class OfficialModules {
98
98
  /**
99
99
  * List all available built-in modules (core and bmm).
100
100
  * All other modules come from external-official-modules.yaml
101
- * @returns {Object} Object with modules array and customModules array
101
+ * @returns {Object} Object with modules array
102
102
  */
103
103
  async listAvailable() {
104
104
  const modules = [];
105
- const customModules = [];
106
105
 
107
106
  // Add built-in core module (directly under src/core-skills)
108
107
  const corePath = getSourcePath('core-skills');
@@ -122,7 +121,7 @@ class OfficialModules {
122
121
  }
123
122
  }
124
123
 
125
- return { modules, customModules };
124
+ return { modules };
126
125
  }
127
126
 
128
127
  /**
@@ -133,25 +132,28 @@ class OfficialModules {
133
132
  * @returns {Object|null} Module info or null if not a valid module
134
133
  */
135
134
  async getModuleInfo(modulePath, defaultName, sourceDescription) {
136
- // Check for module structure (module.yaml OR custom.yaml)
137
135
  const moduleConfigPath = path.join(modulePath, 'module.yaml');
138
- const rootCustomConfigPath = path.join(modulePath, 'custom.yaml');
139
- let configPath = null;
140
-
141
- if (await fs.pathExists(moduleConfigPath)) {
142
- configPath = moduleConfigPath;
143
- } else if (await fs.pathExists(rootCustomConfigPath)) {
144
- configPath = rootCustomConfigPath;
145
- }
146
136
 
147
- // Skip if this doesn't look like a module
148
- if (!configPath) {
137
+ if (!(await fs.pathExists(moduleConfigPath))) {
138
+ // Check resolution cache for strategy 5 modules (no module.yaml on disk)
139
+ const { CustomModuleManager } = require('./custom-module-manager');
140
+ const customMgr = new CustomModuleManager();
141
+ const resolved = customMgr.getResolution(defaultName);
142
+ if (resolved && resolved.synthesizedModuleYaml) {
143
+ return {
144
+ id: resolved.code,
145
+ path: modulePath,
146
+ name: resolved.name,
147
+ description: resolved.description,
148
+ version: resolved.version || '1.0.0',
149
+ source: sourceDescription,
150
+ dependencies: [],
151
+ defaultSelected: false,
152
+ };
153
+ }
149
154
  return null;
150
155
  }
151
156
 
152
- // Mark as custom if it's using custom.yaml OR if it's outside src/bmm or src/core
153
- const isCustomSource =
154
- sourceDescription !== 'src/bmm-skills' && sourceDescription !== 'src/core-skills' && sourceDescription !== 'src/modules';
155
157
  const moduleInfo = {
156
158
  id: defaultName,
157
159
  path: modulePath,
@@ -162,12 +164,11 @@ class OfficialModules {
162
164
  description: 'BMAD Module',
163
165
  version: '5.0.0',
164
166
  source: sourceDescription,
165
- isCustom: configPath === rootCustomConfigPath || isCustomSource,
166
167
  };
167
168
 
168
169
  // Read module config for metadata
169
170
  try {
170
- const configContent = await fs.readFile(configPath, 'utf8');
171
+ const configContent = await fs.readFile(moduleConfigPath, 'utf8');
171
172
  const config = yaml.parse(configContent);
172
173
 
173
174
  // Use the code property as the id if available
@@ -217,6 +218,22 @@ class OfficialModules {
217
218
  return externalSource;
218
219
  }
219
220
 
221
+ // Check community modules
222
+ const { CommunityModuleManager } = require('./community-manager');
223
+ const communityMgr = new CommunityModuleManager();
224
+ const communitySource = await communityMgr.findModuleSource(moduleCode, options);
225
+ if (communitySource) {
226
+ return communitySource;
227
+ }
228
+
229
+ // Check custom modules (from user-provided URLs, already cloned to cache)
230
+ const { CustomModuleManager } = require('./custom-module-manager');
231
+ const customMgr = new CustomModuleManager();
232
+ const customSource = await customMgr.findModuleSourceByCode(moduleCode, options);
233
+ if (customSource) {
234
+ return customSource;
235
+ }
236
+
220
237
  return null;
221
238
  }
222
239
 
@@ -231,6 +248,14 @@ class OfficialModules {
231
248
  * @param {Object} options.logger - Logger instance for output
232
249
  */
233
250
  async install(moduleName, bmadDir, fileTrackingCallback = null, options = {}) {
251
+ // Check if this module has a plugin resolution (custom marketplace install)
252
+ const { CustomModuleManager } = require('./custom-module-manager');
253
+ const customMgr = new CustomModuleManager();
254
+ const resolved = customMgr.getResolution(moduleName);
255
+ if (resolved) {
256
+ return this.installFromResolution(resolved, bmadDir, fileTrackingCallback, options);
257
+ }
258
+
234
259
  const sourcePath = await this.findModuleSource(moduleName, { silent: options.silent });
235
260
  const targetPath = path.join(bmadDir, moduleName);
236
261
 
@@ -264,6 +289,62 @@ class OfficialModules {
264
289
  return { success: true, module: moduleName, path: targetPath, versionInfo };
265
290
  }
266
291
 
292
+ /**
293
+ * Install a module from a PluginResolver resolution result.
294
+ * Copies specific skill directories and places module-help.csv at the target root.
295
+ * @param {Object} resolved - ResolvedModule from PluginResolver
296
+ * @param {string} bmadDir - Target bmad directory
297
+ * @param {Function} fileTrackingCallback - Optional callback to track installed files
298
+ * @param {Object} options - Installation options
299
+ */
300
+ async installFromResolution(resolved, bmadDir, fileTrackingCallback = null, options = {}) {
301
+ const targetPath = path.join(bmadDir, resolved.code);
302
+
303
+ if (await fs.pathExists(targetPath)) {
304
+ await fs.remove(targetPath);
305
+ }
306
+
307
+ await fs.ensureDir(targetPath);
308
+
309
+ // Copy each skill directory, flattened by leaf name
310
+ for (const skillPath of resolved.skillPaths) {
311
+ const skillDirName = path.basename(skillPath);
312
+ const skillTarget = path.join(targetPath, skillDirName);
313
+ await this.copyModuleWithFiltering(skillPath, skillTarget, fileTrackingCallback, options.moduleConfig);
314
+ }
315
+
316
+ // Place module-help.csv at the module root
317
+ if (resolved.moduleHelpCsvPath) {
318
+ // Strategies 1-4: copy the existing file
319
+ const helpTarget = path.join(targetPath, 'module-help.csv');
320
+ await fs.copy(resolved.moduleHelpCsvPath, helpTarget, { overwrite: true });
321
+ if (fileTrackingCallback) fileTrackingCallback(helpTarget);
322
+ } else if (resolved.synthesizedHelpCsv) {
323
+ // Strategy 5: write synthesized content
324
+ const helpTarget = path.join(targetPath, 'module-help.csv');
325
+ await fs.writeFile(helpTarget, resolved.synthesizedHelpCsv, 'utf8');
326
+ if (fileTrackingCallback) fileTrackingCallback(helpTarget);
327
+ }
328
+
329
+ // Create directories declared in module.yaml (strategies 1-4 may have these)
330
+ if (!options.skipModuleInstaller) {
331
+ await this.createModuleDirectories(resolved.code, bmadDir, options);
332
+ }
333
+
334
+ // Update manifest
335
+ const { Manifest } = require('../core/manifest');
336
+ const manifestObj = new Manifest();
337
+
338
+ await manifestObj.addModule(bmadDir, resolved.code, {
339
+ version: resolved.version || null,
340
+ source: 'custom',
341
+ npmPackage: null,
342
+ repoUrl: resolved.repoUrl || null,
343
+ });
344
+
345
+ return { success: true, module: resolved.code, path: targetPath, versionInfo: { version: resolved.version || '' } };
346
+ }
347
+
267
348
  /**
268
349
  * Update an existing module
269
350
  * @param {string} moduleName - Name of the module to update
@@ -824,20 +905,15 @@ class OfficialModules {
824
905
  const results = [];
825
906
 
826
907
  for (const moduleName of modules) {
827
- // Resolve module.yaml path - custom paths first, then standard location, then OfficialModules search
908
+ // Resolve module.yaml path - standard location first, then OfficialModules search
828
909
  let moduleConfigPath = null;
829
- const customPath = this.customModulePaths?.get(moduleName);
830
- if (customPath) {
831
- moduleConfigPath = path.join(customPath, 'module.yaml');
910
+ const standardPath = path.join(getModulePath(moduleName), 'module.yaml');
911
+ if (await fs.pathExists(standardPath)) {
912
+ moduleConfigPath = standardPath;
832
913
  } else {
833
- const standardPath = path.join(getModulePath(moduleName), 'module.yaml');
834
- if (await fs.pathExists(standardPath)) {
835
- moduleConfigPath = standardPath;
836
- } else {
837
- const moduleSourcePath = await this.findModuleSource(moduleName, { silent: true });
838
- if (moduleSourcePath) {
839
- moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
840
- }
914
+ const moduleSourcePath = await this.findModuleSource(moduleName, { silent: true });
915
+ if (moduleSourcePath) {
916
+ moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
841
917
  }
842
918
  }
843
919
 
@@ -882,12 +958,9 @@ class OfficialModules {
882
958
  * @param {Array} modules - List of modules to configure (including 'core')
883
959
  * @param {string} projectDir - Target project directory
884
960
  * @param {Object} options - Additional options
885
- * @param {Map} options.customModulePaths - Map of module ID to source path for custom modules
886
961
  * @param {boolean} options.skipPrompts - Skip prompts and use defaults (for --yes flag)
887
962
  */
888
963
  async collectAllConfigurations(modules, projectDir, options = {}) {
889
- // Store custom module paths for use in collectModuleConfig
890
- this.customModulePaths = options.customModulePaths || new Map();
891
964
  this.skipPrompts = options.skipPrompts || false;
892
965
  this.modulesToCustomize = undefined;
893
966
  await this.loadExistingConfig(projectDir);
@@ -1042,25 +1115,7 @@ class OfficialModules {
1042
1115
  }
1043
1116
  }
1044
1117
 
1045
- let configPath = null;
1046
- let isCustomModule = false;
1047
-
1048
- if (await fs.pathExists(moduleConfigPath)) {
1049
- configPath = moduleConfigPath;
1050
- } else {
1051
- // Check if this is a custom module with custom.yaml
1052
- const moduleSourcePath = await this.findModuleSource(moduleName, { silent: true });
1053
-
1054
- if (moduleSourcePath) {
1055
- const rootCustomConfigPath = path.join(moduleSourcePath, 'custom.yaml');
1056
-
1057
- if (await fs.pathExists(rootCustomConfigPath)) {
1058
- isCustomModule = true;
1059
- // For custom modules, we don't have an install-config schema, so just use existing values
1060
- // The custom.yaml values will be loaded and merged during installation
1061
- }
1062
- }
1063
-
1118
+ if (!(await fs.pathExists(moduleConfigPath))) {
1064
1119
  // No config schema for this module - use existing values
1065
1120
  if (this._existingConfig && this._existingConfig[moduleName]) {
1066
1121
  if (!this.collectedConfig[moduleName]) {
@@ -1071,7 +1126,7 @@ class OfficialModules {
1071
1126
  return false;
1072
1127
  }
1073
1128
 
1074
- const configContent = await fs.readFile(configPath, 'utf8');
1129
+ const configContent = await fs.readFile(moduleConfigPath, 'utf8');
1075
1130
  const moduleConfig = yaml.parse(configContent);
1076
1131
 
1077
1132
  if (!moduleConfig) {
@@ -1172,7 +1227,13 @@ class OfficialModules {
1172
1227
  // Collect all answers (static + prompted)
1173
1228
  let allAnswers = { ...staticAnswers };
1174
1229
 
1175
- if (questions.length > 0) {
1230
+ if (questions.length > 0 && silentMode) {
1231
+ // In silent mode (quick update), use defaults for new fields instead of prompting
1232
+ for (const q of questions) {
1233
+ allAnswers[q.name] = typeof q.default === 'function' ? q.default({}) : q.default;
1234
+ }
1235
+ await prompts.log.message(` \u2713 ${moduleName.toUpperCase()} module configured with defaults`);
1236
+ } else if (questions.length > 0) {
1176
1237
  // Only show header if we actually have questions
1177
1238
  await CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
1178
1239
  await prompts.log.message('');
@@ -1332,16 +1393,7 @@ class OfficialModules {
1332
1393
  this.allAnswers = {};
1333
1394
  }
1334
1395
  // Load module's config
1335
- // First, check if we have a custom module path for this module
1336
- let moduleConfigPath = null;
1337
-
1338
- if (this.customModulePaths && this.customModulePaths.has(moduleName)) {
1339
- const customPath = this.customModulePaths.get(moduleName);
1340
- moduleConfigPath = path.join(customPath, 'module.yaml');
1341
- } else {
1342
- // Try the standard src/modules location
1343
- moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
1344
- }
1396
+ let moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
1345
1397
 
1346
1398
  // If not found in src/modules or custom paths, search the project
1347
1399
  if (!(await fs.pathExists(moduleConfigPath))) {