bmad-method 6.2.2 → 6.2.3-next.1

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 (77) hide show
  1. package/.claude-plugin/marketplace.json +78 -0
  2. package/package.json +8 -8
  3. package/src/core-skills/bmad-distillator/resources/distillate-format-reference.md +1 -1
  4. package/src/core-skills/bmad-init/scripts/bmad_init.py +35 -4
  5. package/src/core-skills/bmad-init/scripts/tests/test_bmad_init.py +64 -0
  6. package/tools/{cli → installer}/bmad-cli.js +3 -1
  7. package/tools/{cli/lib → installer}/cli-utils.js +3 -4
  8. package/tools/{cli → installer}/commands/install.js +3 -3
  9. package/tools/{cli → installer}/commands/status.js +4 -4
  10. package/tools/{cli → installer}/commands/uninstall.js +5 -5
  11. package/tools/installer/core/config.js +52 -0
  12. package/tools/{cli/installers/lib → installer}/core/custom-module-cache.js +1 -1
  13. package/tools/installer/core/existing-install.js +127 -0
  14. package/tools/installer/core/install-paths.js +129 -0
  15. package/tools/installer/core/installer.js +1790 -0
  16. package/tools/{cli/installers/lib → installer}/core/manifest-generator.js +3 -3
  17. package/tools/{cli/installers/lib → installer}/core/manifest.js +2 -2
  18. package/tools/{cli/installers/lib/custom/handler.js → installer/custom-handler.js} +1 -1
  19. package/tools/{cli/installers/lib → installer}/ide/_config-driven.js +30 -397
  20. package/tools/{cli/installers/lib → installer}/ide/manager.js +1 -53
  21. package/tools/installer/ide/platform-codes.js +37 -0
  22. package/tools/installer/ide/platform-codes.yaml +190 -0
  23. package/tools/{cli/installers/lib → installer}/ide/shared/module-injections.js +1 -1
  24. package/tools/{cli/installers/lib → installer}/message-loader.js +2 -2
  25. package/tools/installer/modules/custom-modules.js +197 -0
  26. package/tools/installer/modules/external-manager.js +323 -0
  27. package/tools/{cli/installers/lib/core/config-collector.js → installer/modules/official-modules.js} +714 -43
  28. package/tools/{cli/lib → installer}/ui.js +65 -299
  29. package/tools/javascript-conventions.md +5 -0
  30. package/tools/bmad-npx-wrapper.js +0 -38
  31. package/tools/cli/installers/lib/core/dependency-resolver.js +0 -743
  32. package/tools/cli/installers/lib/core/detector.js +0 -223
  33. package/tools/cli/installers/lib/core/ide-config-manager.js +0 -157
  34. package/tools/cli/installers/lib/core/installer.js +0 -3002
  35. package/tools/cli/installers/lib/ide/_base-ide.js +0 -657
  36. package/tools/cli/installers/lib/ide/platform-codes.js +0 -100
  37. package/tools/cli/installers/lib/ide/platform-codes.yaml +0 -341
  38. package/tools/cli/installers/lib/modules/external-manager.js +0 -136
  39. package/tools/cli/installers/lib/modules/manager.js +0 -928
  40. package/tools/cli/lib/config.js +0 -213
  41. package/tools/cli/lib/platform-codes.js +0 -116
  42. package/tools/lib/xml-utils.js +0 -13
  43. /package/tools/{cli → installer}/README.md +0 -0
  44. /package/tools/{cli → installer}/external-official-modules.yaml +0 -0
  45. /package/tools/{cli/lib → installer}/file-ops.js +0 -0
  46. /package/tools/{cli/installers/lib → installer}/ide/shared/agent-command-generator.js +0 -0
  47. /package/tools/{cli/installers/lib → installer}/ide/shared/bmad-artifacts.js +0 -0
  48. /package/tools/{cli/installers/lib → installer}/ide/shared/path-utils.js +0 -0
  49. /package/tools/{cli/installers/lib → installer}/ide/shared/skill-manifest.js +0 -0
  50. /package/tools/{cli/installers/lib → installer}/ide/templates/agent-command-template.md +0 -0
  51. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/antigravity.md +0 -0
  52. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/default-agent.md +0 -0
  53. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/default-task.md +0 -0
  54. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/default-tool.md +0 -0
  55. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/default-workflow.md +0 -0
  56. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/gemini-agent.toml +0 -0
  57. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/gemini-task.toml +0 -0
  58. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/gemini-tool.toml +0 -0
  59. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/gemini-workflow-yaml.toml +0 -0
  60. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/gemini-workflow.toml +0 -0
  61. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/kiro-agent.md +0 -0
  62. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/kiro-task.md +0 -0
  63. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/kiro-tool.md +0 -0
  64. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/kiro-workflow.md +0 -0
  65. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/opencode-agent.md +0 -0
  66. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/opencode-task.md +0 -0
  67. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/opencode-tool.md +0 -0
  68. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/opencode-workflow-yaml.md +0 -0
  69. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/opencode-workflow.md +0 -0
  70. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/rovodev.md +0 -0
  71. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/trae.md +0 -0
  72. /package/tools/{cli/installers/lib → installer}/ide/templates/combined/windsurf-workflow.md +0 -0
  73. /package/tools/{cli/installers/lib → installer}/ide/templates/split/.gitkeep +0 -0
  74. /package/tools/{cli/installers → installer}/install-messages.yaml +0 -0
  75. /package/tools/{cli/lib → installer}/project-root.js +0 -0
  76. /package/tools/{cli/lib → installer}/prompts.js +0 -0
  77. /package/tools/{cli/lib → installer}/yaml-format.js +0 -0
@@ -0,0 +1,190 @@
1
+ # BMAD Platform Codes Configuration
2
+ #
3
+ # Each platform entry has:
4
+ # name: Display name shown to users
5
+ # preferred: Whether shown as a recommended option on install
6
+ # suspended: (optional) Message explaining why install is blocked
7
+ # installer:
8
+ # target_dir: Directory where skill directories are installed
9
+ # legacy_targets: (optional) Old target dirs to clean up on reinstall
10
+ # ancestor_conflict_check: (optional) Refuse install when ancestor dir has BMAD files
11
+
12
+ platforms:
13
+ antigravity:
14
+ name: "Google Antigravity"
15
+ preferred: false
16
+ installer:
17
+ legacy_targets:
18
+ - .agent/workflows
19
+ target_dir: .agent/skills
20
+
21
+ auggie:
22
+ name: "Auggie"
23
+ preferred: false
24
+ installer:
25
+ legacy_targets:
26
+ - .augment/commands
27
+ target_dir: .augment/skills
28
+
29
+ claude-code:
30
+ name: "Claude Code"
31
+ preferred: true
32
+ installer:
33
+ legacy_targets:
34
+ - .claude/commands
35
+ target_dir: .claude/skills
36
+ ancestor_conflict_check: true
37
+
38
+ cline:
39
+ name: "Cline"
40
+ preferred: false
41
+ installer:
42
+ legacy_targets:
43
+ - .clinerules/workflows
44
+ target_dir: .cline/skills
45
+
46
+ codex:
47
+ name: "Codex"
48
+ preferred: false
49
+ installer:
50
+ legacy_targets:
51
+ - .codex/prompts
52
+ - ~/.codex/prompts
53
+ target_dir: .agents/skills
54
+ ancestor_conflict_check: true
55
+
56
+ codebuddy:
57
+ name: "CodeBuddy"
58
+ preferred: false
59
+ installer:
60
+ legacy_targets:
61
+ - .codebuddy/commands
62
+ target_dir: .codebuddy/skills
63
+
64
+ crush:
65
+ name: "Crush"
66
+ preferred: false
67
+ installer:
68
+ legacy_targets:
69
+ - .crush/commands
70
+ target_dir: .crush/skills
71
+
72
+ cursor:
73
+ name: "Cursor"
74
+ preferred: true
75
+ installer:
76
+ legacy_targets:
77
+ - .cursor/commands
78
+ target_dir: .cursor/skills
79
+
80
+ gemini:
81
+ name: "Gemini CLI"
82
+ preferred: false
83
+ installer:
84
+ legacy_targets:
85
+ - .gemini/commands
86
+ target_dir: .gemini/skills
87
+
88
+ github-copilot:
89
+ name: "GitHub Copilot"
90
+ preferred: false
91
+ installer:
92
+ legacy_targets:
93
+ - .github/agents
94
+ - .github/prompts
95
+ target_dir: .github/skills
96
+
97
+ iflow:
98
+ name: "iFlow"
99
+ preferred: false
100
+ installer:
101
+ legacy_targets:
102
+ - .iflow/commands
103
+ target_dir: .iflow/skills
104
+
105
+ kilo:
106
+ name: "KiloCoder"
107
+ preferred: false
108
+ suspended: "Kilo Code does not yet support the Agent Skills standard. Support is paused until they implement it. See https://github.com/kilocode/kilo-code/issues for updates."
109
+ installer:
110
+ legacy_targets:
111
+ - .kilocode/workflows
112
+ target_dir: .kilocode/skills
113
+
114
+ kiro:
115
+ name: "Kiro"
116
+ preferred: false
117
+ installer:
118
+ legacy_targets:
119
+ - .kiro/steering
120
+ target_dir: .kiro/skills
121
+
122
+ ona:
123
+ name: "Ona"
124
+ preferred: false
125
+ installer:
126
+ target_dir: .ona/skills
127
+
128
+ opencode:
129
+ name: "OpenCode"
130
+ preferred: false
131
+ installer:
132
+ legacy_targets:
133
+ - .opencode/agents
134
+ - .opencode/commands
135
+ - .opencode/agent
136
+ - .opencode/command
137
+ target_dir: .opencode/skills
138
+ ancestor_conflict_check: true
139
+
140
+ pi:
141
+ name: "Pi"
142
+ preferred: false
143
+ installer:
144
+ target_dir: .pi/skills
145
+
146
+ qoder:
147
+ name: "Qoder"
148
+ preferred: false
149
+ installer:
150
+ target_dir: .qoder/skills
151
+
152
+ qwen:
153
+ name: "QwenCoder"
154
+ preferred: false
155
+ installer:
156
+ legacy_targets:
157
+ - .qwen/commands
158
+ target_dir: .qwen/skills
159
+
160
+ roo:
161
+ name: "Roo Code"
162
+ preferred: false
163
+ installer:
164
+ legacy_targets:
165
+ - .roo/commands
166
+ target_dir: .roo/skills
167
+
168
+ rovo-dev:
169
+ name: "Rovo Dev"
170
+ preferred: false
171
+ installer:
172
+ legacy_targets:
173
+ - .rovodev/workflows
174
+ target_dir: .rovodev/skills
175
+
176
+ trae:
177
+ name: "Trae"
178
+ preferred: false
179
+ installer:
180
+ legacy_targets:
181
+ - .trae/rules
182
+ target_dir: .trae/skills
183
+
184
+ windsurf:
185
+ name: "Windsurf"
186
+ preferred: false
187
+ installer:
188
+ legacy_targets:
189
+ - .windsurf/workflows
190
+ target_dir: .windsurf/skills
@@ -2,7 +2,7 @@ const path = require('node:path');
2
2
  const fs = require('fs-extra');
3
3
  const yaml = require('yaml');
4
4
  const { glob } = require('glob');
5
- const { getSourcePath } = require('../../../../lib/project-root');
5
+ const { getSourcePath } = require('../../project-root');
6
6
 
7
7
  async function loadModuleInjectionConfig(handler, moduleName) {
8
8
  const sourceModulesPath = getSourcePath('modules');
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('node:path');
3
3
  const yaml = require('yaml');
4
- const prompts = require('../../lib/prompts');
4
+ const prompts = require('./prompts');
5
5
 
6
6
  /**
7
7
  * Load and display installer messages from messages.yaml
@@ -18,7 +18,7 @@ class MessageLoader {
18
18
  return this.messages;
19
19
  }
20
20
 
21
- const messagesPath = path.join(__dirname, '..', 'install-messages.yaml');
21
+ const messagesPath = path.join(__dirname, 'install-messages.yaml');
22
22
 
23
23
  try {
24
24
  const content = fs.readFileSync(messagesPath, 'utf8');
@@ -0,0 +1,197 @@
1
+ const path = require('node:path');
2
+ const fs = require('fs-extra');
3
+ const yaml = require('yaml');
4
+ const { CustomHandler } = require('../custom-handler');
5
+ const { Manifest } = require('../core/manifest');
6
+ const prompts = require('../prompts');
7
+
8
+ class CustomModules {
9
+ constructor() {
10
+ this.paths = new Map();
11
+ }
12
+
13
+ has(moduleCode) {
14
+ return this.paths.has(moduleCode);
15
+ }
16
+
17
+ get(moduleCode) {
18
+ return this.paths.get(moduleCode);
19
+ }
20
+
21
+ set(moduleId, sourcePath) {
22
+ this.paths.set(moduleId, sourcePath);
23
+ }
24
+
25
+ /**
26
+ * Install a custom module from its source path.
27
+ * @param {string} moduleName - Module identifier
28
+ * @param {string} bmadDir - Target bmad directory
29
+ * @param {Function} fileTrackingCallback - Optional callback to track installed files
30
+ * @param {Object} options - Install options
31
+ * @param {Object} options.moduleConfig - Pre-collected module configuration
32
+ * @returns {Object} Install result
33
+ */
34
+ async install(moduleName, bmadDir, fileTrackingCallback = null, options = {}) {
35
+ const sourcePath = this.paths.get(moduleName);
36
+ if (!sourcePath) {
37
+ throw new Error(`No source path for custom module '${moduleName}'`);
38
+ }
39
+
40
+ if (!(await fs.pathExists(sourcePath))) {
41
+ throw new Error(`Source for custom module '${moduleName}' not found at: ${sourcePath}`);
42
+ }
43
+
44
+ const targetPath = path.join(bmadDir, moduleName);
45
+
46
+ // Read custom.yaml and merge into module config
47
+ let moduleConfig = options.moduleConfig ? { ...options.moduleConfig } : {};
48
+ const customConfigPath = path.join(sourcePath, 'custom.yaml');
49
+ if (await fs.pathExists(customConfigPath)) {
50
+ try {
51
+ const content = await fs.readFile(customConfigPath, 'utf8');
52
+ const customConfig = yaml.parse(content);
53
+ if (customConfig) {
54
+ moduleConfig = { ...moduleConfig, ...customConfig };
55
+ }
56
+ } catch (error) {
57
+ await prompts.log.warn(`Failed to read custom.yaml for ${moduleName}: ${error.message}`);
58
+ }
59
+ }
60
+
61
+ // Remove existing installation
62
+ if (await fs.pathExists(targetPath)) {
63
+ await fs.remove(targetPath);
64
+ }
65
+
66
+ // Copy files with filtering
67
+ await this._copyWithFiltering(sourcePath, targetPath, fileTrackingCallback);
68
+
69
+ // Add to manifest
70
+ const manifest = new Manifest();
71
+ const versionInfo = await manifest.getModuleVersionInfo(moduleName, bmadDir, sourcePath);
72
+ await manifest.addModule(bmadDir, moduleName, {
73
+ version: versionInfo.version,
74
+ source: versionInfo.source,
75
+ npmPackage: versionInfo.npmPackage,
76
+ repoUrl: versionInfo.repoUrl,
77
+ });
78
+
79
+ return { success: true, module: moduleName, path: targetPath, moduleConfig };
80
+ }
81
+
82
+ /**
83
+ * Copy module files, filtering out install-time-only artifacts.
84
+ * @param {string} sourcePath - Source module directory
85
+ * @param {string} targetPath - Target module directory
86
+ * @param {Function} fileTrackingCallback - Optional callback to track installed files
87
+ */
88
+ async _copyWithFiltering(sourcePath, targetPath, fileTrackingCallback = null) {
89
+ const files = await this._getFileList(sourcePath);
90
+
91
+ for (const file of files) {
92
+ if (file.startsWith('sub-modules/')) continue;
93
+
94
+ const isInSidecar = path
95
+ .dirname(file)
96
+ .split('/')
97
+ .some((dir) => dir.toLowerCase().endsWith('-sidecar'));
98
+ if (isInSidecar) continue;
99
+
100
+ if (file === 'module.yaml') continue;
101
+ if (file === 'config.yaml') continue;
102
+
103
+ const sourceFile = path.join(sourcePath, file);
104
+ const targetFile = path.join(targetPath, file);
105
+
106
+ // Skip web-only agents
107
+ if (file.startsWith('agents/') && file.endsWith('.md')) {
108
+ const content = await fs.readFile(sourceFile, 'utf8');
109
+ if (/<agent[^>]*\slocalskip="true"[^>]*>/.test(content)) {
110
+ continue;
111
+ }
112
+ }
113
+
114
+ await fs.ensureDir(path.dirname(targetFile));
115
+ await fs.copy(sourceFile, targetFile, { overwrite: true });
116
+
117
+ if (fileTrackingCallback) {
118
+ fileTrackingCallback(targetFile);
119
+ }
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Recursively list all files in a directory.
125
+ * @param {string} dir - Directory to scan
126
+ * @param {string} baseDir - Base directory for relative paths
127
+ * @returns {string[]} Relative file paths
128
+ */
129
+ async _getFileList(dir, baseDir = dir) {
130
+ const files = [];
131
+ const entries = await fs.readdir(dir, { withFileTypes: true });
132
+
133
+ for (const entry of entries) {
134
+ const fullPath = path.join(dir, entry.name);
135
+ if (entry.isDirectory()) {
136
+ files.push(...(await this._getFileList(fullPath, baseDir)));
137
+ } else {
138
+ files.push(path.relative(baseDir, fullPath));
139
+ }
140
+ }
141
+
142
+ return files;
143
+ }
144
+
145
+ /**
146
+ * Discover custom module source paths from all available sources.
147
+ * @param {Object} config - Installation configuration
148
+ * @param {Object} paths - InstallPaths instance
149
+ * @returns {Map<string, string>} Map of module ID to source path
150
+ */
151
+ async discoverPaths(config, paths) {
152
+ this.paths = new Map();
153
+
154
+ if (config._quickUpdate) {
155
+ if (config._customModuleSources) {
156
+ for (const [moduleId, customInfo] of config._customModuleSources) {
157
+ this.paths.set(moduleId, customInfo.sourcePath);
158
+ }
159
+ }
160
+ return this.paths;
161
+ }
162
+
163
+ // From UI: selectedFiles
164
+ if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
165
+ const customHandler = new CustomHandler();
166
+ for (const customFile of config.customContent.selectedFiles) {
167
+ const customInfo = await customHandler.getCustomInfo(customFile, paths.projectRoot);
168
+ if (customInfo && customInfo.id) {
169
+ this.paths.set(customInfo.id, customInfo.path);
170
+ }
171
+ }
172
+ }
173
+
174
+ // From UI: sources
175
+ if (config.customContent && config.customContent.sources) {
176
+ for (const source of config.customContent.sources) {
177
+ this.paths.set(source.id, source.path);
178
+ }
179
+ }
180
+
181
+ // From UI: cachedModules
182
+ if (config.customContent && config.customContent.cachedModules) {
183
+ const selectedCachedIds = config.customContent.selectedCachedModules || [];
184
+ const shouldIncludeAll = selectedCachedIds.length === 0 && config.customContent.selected;
185
+
186
+ for (const cachedModule of config.customContent.cachedModules) {
187
+ if (cachedModule.id && cachedModule.cachePath && (shouldIncludeAll || selectedCachedIds.includes(cachedModule.id))) {
188
+ this.paths.set(cachedModule.id, cachedModule.cachePath);
189
+ }
190
+ }
191
+ }
192
+
193
+ return this.paths;
194
+ }
195
+ }
196
+
197
+ module.exports = { CustomModules };