quick-update fails for locally-sourced custom modules: bmadDir not forwarded to OfficialModules.install()'s source lookup

Author: masem1899Created Sep 13, 2026Updated Sep 13, 2026

Title: quick-update fails for locally-sourced custom modules: bmadDir not forwarded to OfficialModules.install()'s source lookup

Describe the bug

npx bmad-method install --action quick-update --yes fails with:

Installation failed: Source for module '<code>' is not available. It will be
retained but cannot be updated without its source files.

for a custom module that was originally installed via --custom-source <local-path> (not a Git URL). The module's localPath is correctly recorded in _bmad/_config/manifest.yaml, and that path does exist on disk — the lookup that's supposed to use it never receives the information it needs to find it.

Root cause

tools/installer/modules/official-modules.js, OfficialModules.install(moduleName, bmadDir, fileTrackingCallback, options):

const sourcePath = await this.findModuleSource(moduleName, {
  silent: options.silent,
  channelOptions: options.channelOptions,
});

bmadDir is a parameter of install() itself, but it is never included in the options object passed to findModuleSource(). That call eventually reaches custom-module-manager.js's _findLocalSourceFromManifest(moduleCode, options):

const bmadDir = options.bmadDir;
if (!bmadDir) return null;   // always true here, so the manifest lookup never runs

so the manifest-fallback path (the only way to resolve a local-path custom module without a --custom-source re-supplied on the command line) is dead code from this call site.

Notably, Installer.quickUpdate() (tools/installer/core/installer.js) gets this right a few lines earlier, when just checking whether the module is available to update:

const customSource = await customMgr.findModuleSourceByCode(moduleId, { bmadDir });

— so the module correctly passes the "is it updatable" check and lands in modulesToUpdate, then fails later during the actual install step in _installOfficialModules()OfficialModules.install(), which is the call site missing bmadDir.

Steps to reproduce

  1. npx bmad-method install --custom-source /path/to/a/local/custom-module --yes (first install, local path — not a Git URL).
  2. Confirm _bmad/_config/manifest.yaml records localPath: /path/to/a/local/custom-module for that module.
  3. Change nothing else; just run npx bmad-method install --action quick-update --yes.

Expected: the module refreshes from the recorded localPath.

Actual: Installation failed: Source for module '<code>' is not available..., aborting the whole quick-update (core/bmm etc. may have already reported "up to date" before the abort).

Suggested fix

In official-modules.js, install():

 const sourcePath = await this.findModuleSource(moduleName, {
   silent: options.silent,
   channelOptions: options.channelOptions,
+  bmadDir,
 });

(bmadDir is already in scope as the method's own parameter.) Worth checking whether _installOfficialModules()'s other calls to officialModules.findModuleSource(...) (e.g. the one that resolves display name/version right after install()) have the same gap.

Environment

  • bmad-method version: 6.12.0
  • Custom module source type: local path (--custom-source <local-dir>)
  • OS: Windows (via Git Bash), Node 22.20.0

Source: bmad-code-org/BMAD-METHOD