refactor(cli): rename tools/cli to tools/installer and flatten lib directories

The entire CLI exists to serve the installer. Rename the directory to
reflect its purpose and collapse the two redundant lib/ directories
(cli/lib/ and cli/installers/lib/) into the top level.

- tools/cli/ -> tools/installer/
- tools/installer/installers/lib/{core,ide,modules}/ -> tools/installer/{core,ide,modules}/
- tools/installer/lib/*.js -> tools/installer/*.js
- Update all require() paths, package.json bin/main, CI workflow, tests, and docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Verkhovsky 2026-03-22 10:46:50 -06:00
parent cd6e17610d
commit 9c6534259d
71 changed files with 69 additions and 69 deletions

View File

@ -5,7 +5,7 @@ on:
branches: [main]
paths:
- "src/**"
- "tools/cli/**"
- "tools/installer/**"
- "package.json"
workflow_dispatch:
inputs:

View File

@ -61,7 +61,7 @@ IDs d'outils disponibles pour loption `--tools` :
**Recommandés :** `claude-code`, `cursor`
Exécutez `npx bmad-method install` de manière interactive une fois pour voir la liste complète actuelle des outils pris en charge, ou consultez la [configuration des codes de la plateforme](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/tools/cli/installers/lib/ide/platform-codes.yaml).
Exécutez `npx bmad-method install` de manière interactive une fois pour voir la liste complète actuelle des outils pris en charge, ou consultez la [configuration des codes de la plateforme](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/tools/installer/ide/platform-codes.yaml).
## Modes d'installation

View File

@ -61,7 +61,7 @@ Available tool IDs for the `--tools` flag:
**Preferred:** `claude-code`, `cursor`
Run `npx bmad-method install` interactively once to see the full current list of supported tools, or check the [platform codes configuration](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/tools/cli/installers/lib/ide/platform-codes.yaml).
Run `npx bmad-method install` interactively once to see the full current list of supported tools, or check the [platform codes configuration](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/tools/installer/ide/platform-codes.yaml).
## Installation Modes

View File

@ -61,7 +61,7 @@ sidebar:
**推荐:** `claude-code`、`cursor`
运行一次 `npx bmad-method install` 交互式安装以查看完整的当前支持工具列表,或查看 [平台代码配置](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/tools/cli/installers/lib/ide/platform-codes.yaml)。
运行一次 `npx bmad-method install` 交互式安装以查看完整的当前支持工具列表,或查看 [平台代码配置](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/tools/installer/ide/platform-codes.yaml)。
## 安装模式

View File

@ -18,14 +18,14 @@
},
"license": "MIT",
"author": "Brian (BMad) Madison",
"main": "tools/cli/bmad-cli.js",
"main": "tools/installer/bmad-cli.js",
"bin": {
"bmad": "tools/cli/bmad-cli.js",
"bmad-method": "tools/cli/bmad-cli.js"
"bmad": "tools/installer/bmad-cli.js",
"bmad-method": "tools/installer/bmad-cli.js"
},
"scripts": {
"bmad:install": "node tools/cli/bmad-cli.js install",
"bmad:uninstall": "node tools/cli/bmad-cli.js uninstall",
"bmad:install": "node tools/installer/bmad-cli.js install",
"bmad:uninstall": "node tools/installer/bmad-cli.js uninstall",
"docs:build": "node tools/build-docs.mjs",
"docs:dev": "astro dev --root website",
"docs:fix-links": "node tools/fix-doc-links.js",
@ -34,13 +34,13 @@
"format:check": "prettier --check \"**/*.{js,cjs,mjs,json,yaml}\"",
"format:fix": "prettier --write \"**/*.{js,cjs,mjs,json,yaml}\"",
"format:fix:staged": "prettier --write",
"install:bmad": "node tools/cli/bmad-cli.js install",
"install:bmad": "node tools/installer/bmad-cli.js install",
"lint": "eslint . --ext .js,.cjs,.mjs,.yaml --max-warnings=0",
"lint:fix": "eslint . --ext .js,.cjs,.mjs,.yaml --fix",
"lint:md": "markdownlint-cli2 \"**/*.md\"",
"prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0",
"quality": "npm run format:check && npm run lint && npm run lint:md && npm run docs:build && npm run test:install && npm run validate:refs && npm run validate:skills",
"rebundle": "node tools/cli/bundlers/bundle-web.js rebundle",
"rebundle": "node tools/installer/bundlers/bundle-web.js rebundle",
"test": "npm run test:refs && npm run test:install && npm run lint && npm run lint:md && npm run format:check",
"test:install": "node test/test-installation-components.js",
"test:refs": "node test/test-file-refs-csv.js",

View File

@ -172,7 +172,7 @@ parts: 1
- Deferred: CI/CD integration, telemetry for module authors, air-gapped enterprise install, zip bundle integrity verification (checksums/signing), deeper non-technical platform integrations
## Current Installer (migration context)
- Entry: `tools/cli/bmad-cli.js` (Commander.js) → `tools/cli/installers/lib/core/installer.js`
- Entry: `tools/installer/bmad-cli.js` (Commander.js) → `tools/installer/core/installer.js`
- Platforms: `platform-codes.yaml` (~20 platforms with target dirs, legacy dirs, template types, special flags)
- Manifests: CSV files (skill/workflow/agent-manifest.csv) are current source of truth, not JSON
- External modules: `external-official-modules.yaml` (CIS, GDS, TEA, WDS) from npm with semver

View File

@ -15,7 +15,7 @@
const path = require('node:path');
const os = require('node:os');
const fs = require('fs-extra');
const { loadSkillManifest, getInstallToBmad } = require('../tools/cli/installers/lib/ide/shared/skill-manifest');
const { loadSkillManifest, getInstallToBmad } = require('../tools/installer/ide/shared/skill-manifest');
// ANSI colors
const colors = {

View File

@ -14,9 +14,9 @@
const path = require('node:path');
const os = require('node:os');
const fs = require('fs-extra');
const { ManifestGenerator } = require('../tools/cli/installers/lib/core/manifest-generator');
const { IdeManager } = require('../tools/cli/installers/lib/ide/manager');
const { clearCache, loadPlatformCodes } = require('../tools/cli/installers/lib/ide/platform-codes');
const { ManifestGenerator } = require('../tools/installer/core/manifest-generator');
const { IdeManager } = require('../tools/installer/ide/manager');
const { clearCache, loadPlatformCodes } = require('../tools/installer/ide/platform-codes');
// ANSI colors
const colors = {

View File

@ -34,7 +34,7 @@ function assert(condition, testName, errorMessage = '') {
// ---------------------------------------------------------------------------
// These regexes are extracted from ModuleManager.vendorWorkflowDependencies()
// in tools/cli/installers/lib/modules/manager.js
// in tools/installer/modules/manager.js
// ---------------------------------------------------------------------------
// Source regex (line ~1081) — uses non-capturing group for _bmad

View File

@ -6,7 +6,7 @@ Create a reference documentation page at `docs/reference/modules.md` that lists
## Source of Truth
Read `tools/cli/external-official-modules.yaml` — this is the authoritative registry of official external modules. Use the module names, codes, npm package names, and repository URLs from this file.
Read `tools/installer/external-official-modules.yaml` — this is the authoritative registry of official external modules. Use the module names, codes, npm package names, and repository URLs from this file.
## Research Step

View File

@ -5,7 +5,7 @@ const path = require('node:path');
const fs = require('node:fs');
const { execSync } = require('node:child_process');
const semver = require('semver');
const prompts = require('./lib/prompts');
const prompts = require('./prompts');
// The installer flow uses many sequential @clack/prompts, each adding keypress
// listeners to stdin. Raise the limit to avoid spurious EventEmitter warnings.

View File

@ -8,7 +8,7 @@ const CLIUtils = {
*/
getVersion() {
try {
const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
const packageJson = require(path.join(__dirname, '..', '..', 'package.json'));
return packageJson.version || 'Unknown';
} catch {
return 'Unknown';

View File

@ -1,7 +1,7 @@
const path = require('node:path');
const prompts = require('../lib/prompts');
const { Installer } = require('../installers/lib/core/installer');
const { UI } = require('../lib/ui');
const prompts = require('../prompts');
const { Installer } = require('../core/installer');
const { UI } = require('../ui');
const installer = new Installer();
const ui = new UI();

View File

@ -1,8 +1,8 @@
const path = require('node:path');
const prompts = require('../lib/prompts');
const { Installer } = require('../installers/lib/core/installer');
const { Manifest } = require('../installers/lib/core/manifest');
const { UI } = require('../lib/ui');
const prompts = require('../prompts');
const { Installer } = require('../core/installer');
const { Manifest } = require('../core/manifest');
const { UI } = require('../ui');
const installer = new Installer();
const manifest = new Manifest();

View File

@ -1,7 +1,7 @@
const path = require('node:path');
const fs = require('fs-extra');
const prompts = require('../lib/prompts');
const { Installer } = require('../installers/lib/core/installer');
const prompts = require('../prompts');
const { Installer } = require('../core/installer');
const installer = new Installer();

View File

@ -7,7 +7,7 @@
const fs = require('fs-extra');
const path = require('node:path');
const crypto = require('node:crypto');
const prompts = require('../../../lib/prompts');
const prompts = require('../prompts');
class CustomModuleCache {
constructor(bmadDir) {

View File

@ -1,6 +1,6 @@
const path = require('node:path');
const fs = require('fs-extra');
const { getProjectRoot } = require('../../../lib/project-root');
const { getProjectRoot } = require('../project-root');
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
class InstallPaths {

View File

@ -4,11 +4,11 @@ const { Manifest } = require('./manifest');
const { OfficialModules } = require('../modules/official-modules');
const { CustomModules } = require('../modules/custom-modules');
const { IdeManager } = require('../ide/manager');
const { FileOps } = require('../../../lib/file-ops');
const { FileOps } = require('../file-ops');
const { Config } = require('./config');
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
const { getProjectRoot, getSourcePath, getModulePath } = require('../project-root');
const { ManifestGenerator } = require('./manifest-generator');
const prompts = require('../../../lib/prompts');
const prompts = require('../prompts');
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
const { InstallPaths } = require('./install-paths');
const { ExternalModuleManager } = require('../modules/external-manager');

View File

@ -3,8 +3,8 @@ const fs = require('fs-extra');
const yaml = require('yaml');
const crypto = require('node:crypto');
const csv = require('csv-parse/sync');
const { getSourcePath, getModulePath } = require('../../../lib/project-root');
const prompts = require('../../../lib/prompts');
const { getSourcePath, getModulePath } = require('../project-root');
const prompts = require('../prompts');
const {
loadSkillManifest: loadSkillManifestShared,
getCanonicalId: getCanonicalIdShared,
@ -13,7 +13,7 @@ const {
} = require('../ide/shared/skill-manifest');
// Load package.json for version info
const packageJson = require('../../../../../package.json');
const packageJson = require('../../../package.json');
/**
* Generates manifest files for installed skills and agents

View File

@ -1,8 +1,8 @@
const path = require('node:path');
const fs = require('fs-extra');
const crypto = require('node:crypto');
const { getProjectRoot } = require('../../../lib/project-root');
const prompts = require('../../../lib/prompts');
const { getProjectRoot } = require('../project-root');
const prompts = require('../prompts');
class Manifest {
/**

View File

@ -1,7 +1,7 @@
const path = require('node:path');
const fs = require('fs-extra');
const yaml = require('yaml');
const prompts = require('../../lib/prompts');
const prompts = require('./prompts');
/**
* Handler for custom content (custom.yaml)
* Discovers custom agents and workflows in the project

View File

@ -2,7 +2,7 @@ const os = require('node:os');
const path = require('node:path');
const fs = require('fs-extra');
const yaml = require('yaml');
const prompts = require('../../../lib/prompts');
const prompts = require('../prompts');
const csv = require('csv-parse/sync');
const { BMAD_FOLDER_NAME } = require('./shared/path-utils');

View File

@ -1,5 +1,5 @@
const { BMAD_FOLDER_NAME } = require('./shared/path-utils');
const prompts = require('../../../lib/prompts');
const prompts = require('../prompts');
/**
* IDE Manager - handles IDE-specific setup

View File

@ -2,7 +2,7 @@ const path = require('node:path');
const fs = require('fs-extra');
const yaml = require('yaml');
const { glob } = require('glob');
const { getSourcePath } = require('../../../../lib/project-root');
const { getSourcePath } = require('../../project-root');
async function loadModuleInjectionConfig(handler, moduleName) {
const sourceModulesPath = getSourcePath('modules');

View File

@ -1,7 +1,7 @@
const fs = require('fs-extra');
const path = require('node:path');
const yaml = require('yaml');
const prompts = require('../../lib/prompts');
const prompts = require('./prompts');
/**
* Load and display installer messages from messages.yaml
@ -18,7 +18,7 @@ class MessageLoader {
return this.messages;
}
const messagesPath = path.join(__dirname, '..', 'install-messages.yaml');
const messagesPath = path.join(__dirname, 'install-messages.yaml');
try {
const content = fs.readFileSync(messagesPath, 'utf8');

View File

@ -3,7 +3,7 @@ const fs = require('fs-extra');
const yaml = require('yaml');
const { CustomHandler } = require('../custom-handler');
const { Manifest } = require('../core/manifest');
const prompts = require('../../../lib/prompts');
const prompts = require('../prompts');
class CustomModules {
constructor() {

View File

@ -3,7 +3,7 @@ const os = require('node:os');
const path = require('node:path');
const { execSync } = require('node:child_process');
const yaml = require('yaml');
const prompts = require('../../../lib/prompts');
const prompts = require('../prompts');
/**
* Manages external official modules defined in external-official-modules.yaml
@ -13,7 +13,7 @@ const prompts = require('../../../lib/prompts');
*/
class ExternalModuleManager {
constructor() {
this.externalModulesConfigPath = path.join(__dirname, '../../../external-official-modules.yaml');
this.externalModulesConfigPath = path.join(__dirname, '../external-official-modules.yaml');
this.cachedModules = null;
}

View File

@ -1,9 +1,9 @@
const path = require('node:path');
const fs = require('fs-extra');
const yaml = require('yaml');
const prompts = require('../../../lib/prompts');
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
const { CLIUtils } = require('../../../lib/cli-utils');
const prompts = require('../prompts');
const { getProjectRoot, getSourcePath, getModulePath } = require('../project-root');
const { CLIUtils } = require('../cli-utils');
const { ExternalModuleManager } = require('./external-manager');
class OfficialModules {

View File

@ -2,8 +2,8 @@ const path = require('node:path');
const os = require('node:os');
const fs = require('fs-extra');
const { CLIUtils } = require('./cli-utils');
const { CustomHandler } = require('../installers/lib/custom-handler');
const { ExternalModuleManager } = require('../installers/lib/modules/external-manager');
const { CustomHandler } = require('./custom-handler');
const { ExternalModuleManager } = require('./modules/external-manager');
const prompts = require('./prompts');
// Separator class for visual grouping in select/multiselect prompts
@ -32,7 +32,7 @@ class UI {
await CLIUtils.displayLogo();
// Display version-specific start message from install-messages.yaml
const { MessageLoader } = require('../installers/lib/message-loader');
const { MessageLoader } = require('./message-loader');
const messageLoader = new MessageLoader();
await messageLoader.displayStartMessage();
@ -51,7 +51,7 @@ class UI {
confirmedDirectory = await this.getConfirmedDirectory();
}
const { Installer } = require('../installers/lib/core/installer');
const { Installer } = require('./core/installer');
const installer = new Installer();
const { bmadDir } = await installer.findBmadDir(confirmedDirectory);
@ -279,7 +279,7 @@ class UI {
customModuleResult = await this.handleCustomModulesInModifyFlow(confirmedDirectory, selectedModules);
} else {
// Preserve existing custom modules if user doesn't want to modify them
const { Installer } = require('../installers/lib/core/installer');
const { Installer } = require('./core/installer');
const installer = new Installer();
const { bmadDir } = await installer.findBmadDir(confirmedDirectory);
@ -452,15 +452,15 @@ class UI {
* @returns {Object} Tool configuration
*/
async promptToolSelection(projectDir, options = {}) {
const { ExistingInstall } = require('../installers/lib/core/existing-install');
const { Installer } = require('../installers/lib/core/installer');
const { ExistingInstall } = require('./core/existing-install');
const { Installer } = require('./core/installer');
const installer = new Installer();
const { bmadDir } = await installer.findBmadDir(projectDir || process.cwd());
const existingInstall = await ExistingInstall.detect(bmadDir);
const configuredIdes = existingInstall.ides;
// Get IDE manager to fetch available IDEs dynamically
const { IdeManager } = require('../installers/lib/ide/manager');
const { IdeManager } = require('./ide/manager');
const ideManager = new IdeManager();
await ideManager.ensureInitialized(); // IMPORTANT: Must initialize before getting IDEs
@ -690,8 +690,8 @@ class UI {
* @returns {Object} Object with existingInstall, installedModuleIds, and bmadDir
*/
async getExistingInstallation(directory) {
const { ExistingInstall } = require('../installers/lib/core/existing-install');
const { Installer } = require('../installers/lib/core/installer');
const { ExistingInstall } = require('./core/existing-install');
const { Installer } = require('./core/installer');
const installer = new Installer();
const { bmadDir } = await installer.findBmadDir(directory);
const existingInstall = await ExistingInstall.detect(bmadDir);
@ -709,7 +709,7 @@ class UI {
* @returns {Object} Collected module configurations keyed by module name
*/
async collectModuleConfigs(directory, modules, options = {}) {
const { OfficialModules } = require('../installers/lib/modules/official-modules');
const { OfficialModules } = require('./modules/official-modules');
const configCollector = new OfficialModules();
// Seed core config from CLI options if provided
@ -809,7 +809,7 @@ class UI {
}
// Add official modules
const { OfficialModules } = require('../installers/lib/modules/official-modules');
const { OfficialModules } = require('./modules/official-modules');
const officialModules = new OfficialModules();
const { modules: availableModules, customModules: customModulesFromCache } = await officialModules.listAvailable();
@ -866,7 +866,7 @@ class UI {
* @returns {Array} Selected module codes (excluding core)
*/
async selectAllModules(installedModuleIds = new Set()) {
const { OfficialModules } = require('../installers/lib/modules/official-modules');
const { OfficialModules } = require('./modules/official-modules');
const officialModulesSource = new OfficialModules();
const { modules: localModules } = await officialModulesSource.listAvailable();
@ -963,7 +963,7 @@ class UI {
* @returns {Array} Default module codes
*/
async getDefaultModules(installedModuleIds = new Set()) {
const { OfficialModules } = require('../installers/lib/modules/official-modules');
const { OfficialModules } = require('./modules/official-modules');
const officialModules = new OfficialModules();
const { modules: localModules } = await officialModules.listAvailable();
@ -1023,7 +1023,7 @@ class UI {
const files = await fs.readdir(directory);
if (files.length > 0) {
// Check for any bmad installation (any folder with _config/manifest.yaml)
const { Installer } = require('../installers/lib/core/installer');
const { Installer } = require('./core/installer');
const installer = new Installer();
const bmadResult = await installer.findBmadDir(directory);
const hasBmadInstall =
@ -1265,8 +1265,8 @@ class UI {
* @returns {Array} List of configured IDEs
*/
async getConfiguredIdes(directory) {
const { ExistingInstall } = require('../installers/lib/core/existing-install');
const { Installer } = require('../installers/lib/core/installer');
const { ExistingInstall } = require('./core/existing-install');
const { Installer } = require('./core/installer');
const installer = new Installer();
const { bmadDir } = await installer.findBmadDir(directory);
const existingInstall = await ExistingInstall.detect(bmadDir);
@ -1415,7 +1415,7 @@ class UI {
const { existingInstall } = await this.getExistingInstallation(directory);
// Check if there are any custom modules in cache
const { Installer } = require('../installers/lib/core/installer');
const { Installer } = require('./core/installer');
const installer = new Installer();
const { bmadDir } = await installer.findBmadDir(directory);