Skip to content

refactor(installer): remove custom content installation feature#2227

Merged
bmadcode merged 2 commits intomainfrom
install-custom-content
Apr 8, 2026
Merged

refactor(installer): remove custom content installation feature#2227
bmadcode merged 2 commits intomainfrom
install-custom-content

Conversation

@bmadcode
Copy link
Copy Markdown
Collaborator

@bmadcode bmadcode commented Apr 8, 2026

Summary

  • Removes the entire local filesystem custom content feature from the installer to clear the path for marketplace-based plugin installation
  • Deletes 3 files: custom-handler.js, custom-module-cache.js, custom-modules.js (combined ~675 lines)
  • Strips custom content handling from 8 installer source files (~1,400 lines removed)
  • Removes --custom-content CLI flag, interactive prompts, module caching, manifest tracking, and missing-source resolution
  • Deletes 2 test suites and their fixture helper
  • Updates docs across all 5 translations (EN, ZH-CN, VI-VN, FR, CS)

Net: -2,094 lines across 21 files

What is NOT touched

  • detectCustomFiles() in installer.js (preserves user-modified files during updates; different concept, coincidental naming)
  • External module manager / marketplace infrastructure
  • CHANGELOG entries (historical record)

Test plan

  • npm test passes (196 tests, lint, format)
  • Smoke test: fresh install — no custom content prompts, no _config/custom/ dir
  • Smoke test: quick update — completes without errors
  • Smoke test: modify flow — no "Modify custom modules?" prompt
  • npx bmad-method install --help confirms --custom-content is gone

Remove the entire local filesystem custom content feature from the
installer to make way for marketplace-based plugin installation.

Deleted: custom-handler.js, custom-module-cache.js, custom-modules.js
Removed: --custom-content CLI flag, interactive custom content prompts,
custom module caching, manifest tracking, missing-source resolution,
and related test suites. Updated docs across all translations.
@augmentcode
Copy link
Copy Markdown

augmentcode bot commented Apr 8, 2026

🤖 Augment PR Summary

Summary: This PR removes the installer’s local filesystem “custom content” feature to make room for marketplace-based plugin/module installation.

Changes:

  • Deletes the custom-content implementation (custom handler, custom modules manager, and custom module cache) and removes the --custom-content CLI flag + related prompts.
  • Simplifies installer flow to only handle built-in modules and external official modules; custom-source discovery, caching, and missing-source resolution are removed.
  • Updates installation state tracking by dropping customModules from ExistingInstall, manifest reading/writing, and manifest generation.
  • Adjusts quick-update behavior to update only modules with available sources and preserve/skip modules without sources.
  • Removes custom-content-specific tests/fixtures and updates docs across multiple translations to reflect the new behavior.

Technical Notes: The installer still preserves user-modified files via detectCustomFiles(), but no longer attempts to install or update custom modules from local paths.

🤖 Was this summary useful? React with 👍 or 👎

Copy link
Copy Markdown

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

- Has a `code` field in the `module.yaml`

:::note[Still stuck?]
::: note[Still stuck?]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

::: note[...] looks like a syntax change from the prior :::note[...] admonition form; if this site uses Docusaurus-style admonitions, the space can prevent the note block from rendering.

Severity: medium

Other Locations
  • docs/fr/how-to/non-interactive-installation.md:163
  • docs/cs/how-to/non-interactive-installation.md:151
  • docs/zh-cn/how-to/non-interactive-installation.md:150

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

}

return { modules, customModules };
return { modules };
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listAvailable() now returns only { modules }, but the JSDoc above still says it returns customModules as well, which is now misleading for callers.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Fix admonition syntax (remove accidental space in :::note) across 4
translated docs files, and update stale JSDoc on listAvailable().
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

📝 Walkthrough

Walkthrough

The PR removes the --custom-content CLI flag and all supporting infrastructure for custom modules, including caching, manifest integration, content discovery, UI prompts, and documentation across all language versions.

Changes

Cohort / File(s) Summary
Documentation Updates (Multi-Language)
docs/cs/how-to/non-interactive-installation.md, docs/how-to/non-interactive-installation.md, docs/fr/how-to/non-interactive-installation.md, docs/vi-vn/how-to/non-interactive-installation.md, docs/zh-cn/how-to/non-interactive-installation.md
Removed --custom-content option documentation, example sections, and validation/error-handling guidance for custom module paths from non-interactive installation guides.
Documentation Step Updates
docs/cs/how-to/install-bmad.md, docs/how-to/install-bmad.md, docs/fr/how-to/install-bmad.md, docs/vi-vn/how-to/install-bmad.md, docs/zh-cn/how-to/install-bmad.md
Updated step 5 wording to replace "custom content" with "settings" and "tool integrations" across all language versions.
CLI Option Removal
tools/installer/commands/install.js
Removed --custom-content <paths> option declaration from install command interface.
Custom Module Infrastructure (Deleted)
tools/installer/core/custom-module-cache.js, tools/installer/custom-handler.js, tools/installer/modules/custom-modules.js
Removed entire modules: custom module caching/manifest persistence, filesystem discovery of custom content, and custom module installation workflow.
Installer Core Updates
tools/installer/core/existing-install.js, tools/installer/core/install-paths.js, tools/installer/core/manifest.js, tools/installer/core/manifest-generator.js
Removed customModules state tracking, custom cache directory setup, custom module manifest methods, and custom module preservation logic from existing install detection and manifest generation.
Installer Main Logic
tools/installer/core/installer.js
Removed custom module imports, discovery, caching, scanning, installation workflows, and interactive missing-source handlers; updated module availability and installation control flow to exclude custom modules.
Module Management
tools/installer/modules/official-modules.js
Removed custom module tracking from available modules list and removed custom module schema/config resolution paths; simplified module discovery to standard module.yaml only.
UI Prompting
tools/installer/ui.js
Removed custom content validation, discovery prompting, and module selection logic; eliminated customContent field from returned install/update configurations; removed CustomHandler import and helper utilities.
Test Cleanup
test/test-installation-components.js
Removed custom module manifest fixture helper and two test suites (33 and 34) that validated custom module manifest preservation and source handling.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Removes the --custom-content feature and all supporting infrastructure (CustomModules, CustomModuleCache, CustomHandler) that were added in previous PR changes.
  • Modifies manifest.js module version resolution (getModuleVersionInfo) and manifest handling, likely coordinating with other manifest-related refactoring efforts.
  • Affects non-interactive installation documentation and CLI flag handling that may overlap with other installation workflow modifications.

Suggested reviewers

  • alexeyv
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary change: removal of the custom content installation feature from the installer.
Description check ✅ Passed The description comprehensively explains the scope, changes, and test plan for removing the custom content feature, and is directly related to the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch install-custom-content

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tools/installer/modules/official-modules.js (1)

1022-1033: ⚠️ Potential issue | 🟠 Major

Preserve existing module config when module.yaml is missing.

Lines 1022-1030 already keep this._existingConfig[moduleName] in the quick path, but Line 1309 returns without doing the same in the full path. That leaves this.collectedConfig[moduleName] unset even though the previous values were already loaded, so full config collection can discard persisted module settings just because the source schema is unavailable.

💡 Proposed fix
     let configPath = null;
     if (await fs.pathExists(moduleConfigPath)) {
       configPath = moduleConfigPath;
     } else {
+      if (this._existingConfig?.[moduleName]) {
+        this.collectedConfig[moduleName] = { ...this._existingConfig[moduleName] };
+      }
       // No config for this module
       return;
     }
As per coding guidelines, `tools/**`: Build script/tooling. Check error handling and proper exit codes.

Also applies to: 1294-1311

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/installer/modules/official-modules.js` around lines 1022 - 1033, When
moduleConfigPath is missing the code currently preserves existing settings in
the early/quick path but not in the full config-collection path, causing
this.collectedConfig[moduleName] to be left unset; update the full-path branch
(the code that returns false around where moduleConfigPath is checked) to check
this._existingConfig[moduleName] and, if present, set
this.collectedConfig[moduleName] = { ...this._existingConfig[moduleName] }
(creating the object first if needed) before returning false; apply the same
change to the analogous block around the other occurrence (the 1294-1311 area)
so both quick and full collection paths preserve persisted module settings.
tools/installer/core/installer.js (1)

1148-1215: ⚠️ Potential issue | 🔴 Critical

Don't let quick update delete skipped modules.

modulesToUpdate now excludes installed modules with no current source, but install() still treats anything missing from config.modules as deselected and removes it before _prepareUpdateState() backs files up. A quick update on an install that still contains a legacy/custom module—or an external module that is temporarily unavailable—will silently delete that module directory even though this branch labels it as "skipped".

🐛 Suggested fix
 async install(originalConfig) {
   let updateState = null;

   try {
     const config = Config.build(originalConfig);
     const paths = await InstallPaths.create(config);
     const officialModules = await OfficialModules.build(config, paths);
     const existingInstall = await ExistingInstall.detect(paths.bmadDir);

     if (existingInstall.installed) {
-      await this._removeDeselectedModules(existingInstall, config, paths);
+      await this._removeDeselectedModules(
+        existingInstall,
+        config,
+        paths,
+        new Set(originalConfig._preserveModules || []),
+      );
       updateState = await this._prepareUpdateState(paths, config, existingInstall, officialModules);
       await this._removeDeselectedIdes(existingInstall, config, paths);
     }
-  async _removeDeselectedModules(existingInstall, config, paths) {
+  async _removeDeselectedModules(existingInstall, config, paths, preservedModules = new Set()) {
     const previouslyInstalled = new Set(existingInstall.moduleIds);
     const newlySelected = new Set(config.modules || []);
+    for (const moduleId of preservedModules) {
+      newlySelected.add(moduleId);
+    }
     const toRemove = [...previouslyInstalled].filter((m) => !newlySelected.has(m) && m !== 'core');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/installer/core/installer.js` around lines 1148 - 1215, The quick-update
flow builds installConfig with modules: modulesToUpdate but that causes
install() / _prepareUpdateState() to treat skipped/legacy modules as deselected
and delete them; fix by ensuring skipped modules are preserved: either
(preferred) update install()/_prepareUpdateState() to honor
installConfig._preserveModules (skip deletion/backups for any id in
_preserveModules) or (alternative) merge skippedModules into
installConfig.modules (modulesToUpdate ∪ skippedModules) while still setting
_preserveModules to skippedModules so they are not re-installed; update
references in install(), _prepareUpdateState(), and where installConfig is
consumed to use installConfig._preserveModules to prevent removal of those
module directories.
🧹 Nitpick comments (1)
tools/installer/modules/official-modules.js (1)

98-102: Update the listAvailable() return contract comment.

listAvailable() now returns only { modules }, but the JSDoc still advertises a customModules array. Leaving that stale makes this API change easy to miss in the next edit.

Also applies to: 124-124

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/installer/modules/official-modules.js` around lines 98 - 102, Update
the JSDoc for the listAvailable() function to reflect its current return
contract: replace the outdated description that says it returns "{ modules array
and customModules array }" with the correct return type "{ modules }" (i.e., an
object containing only the modules array). Modify the comment block above
listAvailable() (and the duplicate JSDoc occurrence referenced) to remove any
mention of customModules and adjust the `@returns` line to accurately state it
returns {Object} with property modules (or { modules: Array }). Ensure the
function name listAvailable is unchanged and only the documentation text is
updated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@tools/installer/core/installer.js`:
- Around line 1148-1215: The quick-update flow builds installConfig with
modules: modulesToUpdate but that causes install() / _prepareUpdateState() to
treat skipped/legacy modules as deselected and delete them; fix by ensuring
skipped modules are preserved: either (preferred) update
install()/_prepareUpdateState() to honor installConfig._preserveModules (skip
deletion/backups for any id in _preserveModules) or (alternative) merge
skippedModules into installConfig.modules (modulesToUpdate ∪ skippedModules)
while still setting _preserveModules to skippedModules so they are not
re-installed; update references in install(), _prepareUpdateState(), and where
installConfig is consumed to use installConfig._preserveModules to prevent
removal of those module directories.

In `@tools/installer/modules/official-modules.js`:
- Around line 1022-1033: When moduleConfigPath is missing the code currently
preserves existing settings in the early/quick path but not in the full
config-collection path, causing this.collectedConfig[moduleName] to be left
unset; update the full-path branch (the code that returns false around where
moduleConfigPath is checked) to check this._existingConfig[moduleName] and, if
present, set this.collectedConfig[moduleName] = {
...this._existingConfig[moduleName] } (creating the object first if needed)
before returning false; apply the same change to the analogous block around the
other occurrence (the 1294-1311 area) so both quick and full collection paths
preserve persisted module settings.

---

Nitpick comments:
In `@tools/installer/modules/official-modules.js`:
- Around line 98-102: Update the JSDoc for the listAvailable() function to
reflect its current return contract: replace the outdated description that says
it returns "{ modules array and customModules array }" with the correct return
type "{ modules }" (i.e., an object containing only the modules array). Modify
the comment block above listAvailable() (and the duplicate JSDoc occurrence
referenced) to remove any mention of customModules and adjust the `@returns` line
to accurately state it returns {Object} with property modules (or { modules:
Array }). Ensure the function name listAvailable is unchanged and only the
documentation text is updated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 50cccfab-ae71-419c-b86f-982cdddbdb8c

📥 Commits

Reviewing files that changed from the base of the PR and between 9ca0316 and 537ff0c.

📒 Files selected for processing (21)
  • docs/cs/how-to/non-interactive-installation.md
  • docs/fr/how-to/install-bmad.md
  • docs/fr/how-to/non-interactive-installation.md
  • docs/how-to/install-bmad.md
  • docs/how-to/non-interactive-installation.md
  • docs/vi-vn/how-to/install-bmad.md
  • docs/vi-vn/how-to/non-interactive-installation.md
  • docs/zh-cn/how-to/install-bmad.md
  • docs/zh-cn/how-to/non-interactive-installation.md
  • test/test-installation-components.js
  • tools/installer/commands/install.js
  • tools/installer/core/custom-module-cache.js
  • tools/installer/core/existing-install.js
  • tools/installer/core/install-paths.js
  • tools/installer/core/installer.js
  • tools/installer/core/manifest-generator.js
  • tools/installer/core/manifest.js
  • tools/installer/custom-handler.js
  • tools/installer/modules/custom-modules.js
  • tools/installer/modules/official-modules.js
  • tools/installer/ui.js
💤 Files with no reviewable changes (7)
  • tools/installer/commands/install.js
  • tools/installer/core/install-paths.js
  • tools/installer/core/manifest-generator.js
  • test/test-installation-components.js
  • tools/installer/core/custom-module-cache.js
  • tools/installer/modules/custom-modules.js
  • tools/installer/custom-handler.js

@bmadcode bmadcode merged commit 5dbfb58 into main Apr 8, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant