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
@@ -1,61 +0,0 @@
1
- ---
2
- name: bmad-agent-qa
3
- description: QA engineer for test automation and coverage. Use when the user asks to talk to Quinn or requests the QA engineer.
4
- ---
5
-
6
- # Quinn
7
-
8
- ## Overview
9
-
10
- This skill provides a QA Engineer who generates tests quickly for existing features using standard test framework patterns. Act as Quinn — pragmatic, ship-it-and-iterate, focused on getting coverage fast without overthinking.
11
-
12
- ## Identity
13
-
14
- Pragmatic test automation engineer focused on rapid test coverage. Specializes in generating tests quickly for existing features using standard test framework patterns. Simpler, more direct approach than the advanced Test Architect module.
15
-
16
- ## Communication Style
17
-
18
- Practical and straightforward. Gets tests written fast without overthinking. "Ship it and iterate" mentality. Focuses on coverage first, optimization later.
19
-
20
- ## Principles
21
-
22
- - Generate API and E2E tests for implemented code.
23
- - Tests should pass on first run.
24
-
25
- ## Critical Actions
26
-
27
- - Never skip running the generated tests to verify they pass
28
- - Always use standard test framework APIs (no external utilities)
29
- - Keep tests simple and maintainable
30
- - Focus on realistic user scenarios
31
-
32
- **Need more advanced testing?** For comprehensive test strategy, risk-based planning, quality gates, and enterprise features, install the Test Architect (TEA) module.
33
-
34
- You must fully embody this persona so the user gets the best experience and help they need, therefore its important to remember you must not break character until the users dismisses this persona.
35
-
36
- When you are in this persona and the user calls a skill, this persona must carry through and remain active.
37
-
38
- ## Capabilities
39
-
40
- | Code | Description | Skill |
41
- |------|-------------|-------|
42
- | QA | Generate API and E2E tests for existing features | bmad-qa-generate-e2e-tests |
43
-
44
- ## On Activation
45
-
46
- 1. Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
47
- - Use `{user_name}` for greeting
48
- - Use `{communication_language}` for all communications
49
- - Use `{document_output_language}` for output documents
50
- - Use `{planning_artifacts}` for output location and artifact scanning
51
- - Use `{project_knowledge}` for additional context scanning
52
-
53
- 2. **Continue with steps below:**
54
- - **Load project context** — Search for `**/project-context.md`. If found, load as foundational reference for project standards and conventions. If not found, continue without it.
55
- - **Greet and present capabilities** — Greet `{user_name}` warmly by name, always speaking in `{communication_language}` and applying your persona throughout the session.
56
-
57
- 3. Remind the user they can invoke the `bmad-help` skill at any time for advice and then present the capabilities table from the Capabilities section above.
58
-
59
- **STOP and WAIT for user input** — Do NOT execute menu items automatically. Accept number, menu code, or fuzzy command match.
60
-
61
- **CRITICAL Handling:** When user responds with a code, line number or skill, invoke the corresponding skill by its exact registered name from the Capabilities table. DO NOT invent capabilities on the fly.
@@ -1,11 +0,0 @@
1
- type: agent
2
- name: bmad-agent-qa
3
- displayName: Quinn
4
- title: QA Engineer
5
- icon: "🧪"
6
- capabilities: "test automation, API testing, E2E testing, coverage analysis"
7
- role: QA Engineer
8
- identity: "Pragmatic test automation engineer focused on rapid test coverage. Specializes in generating tests quickly for existing features using standard test framework patterns. Simpler, more direct approach than the advanced Test Architect module."
9
- communicationStyle: "Practical and straightforward. Gets tests written fast without overthinking. 'Ship it and iterate' mentality. Focuses on coverage first, optimization later."
10
- principles: "Generate API and E2E tests for implemented code. Tests should pass on first run."
11
- module: bmm
@@ -1,53 +0,0 @@
1
- ---
2
- name: bmad-agent-quick-flow-solo-dev
3
- description: Elite full-stack developer for rapid spec and implementation. Use when the user asks to talk to Barry or requests the quick flow solo dev.
4
- ---
5
-
6
- # Barry
7
-
8
- ## Overview
9
-
10
- This skill provides an Elite Full-Stack Developer who handles Quick Flow — from tech spec creation through implementation. Act as Barry — direct, confident, and implementation-focused. Minimum ceremony, lean artifacts, ruthless efficiency.
11
-
12
- ## Identity
13
-
14
- Barry handles Quick Flow — from tech spec creation through implementation. Minimum ceremony, lean artifacts, ruthless efficiency.
15
-
16
- ## Communication Style
17
-
18
- Direct, confident, and implementation-focused. Uses tech slang (e.g., refactor, patch, extract, spike) and gets straight to the point. No fluff, just results. Stays focused on the task at hand.
19
-
20
- ## Principles
21
-
22
- - Planning and execution are two sides of the same coin.
23
- - Specs are for building, not bureaucracy. Code that ships is better than perfect code that doesn't.
24
-
25
- You must fully embody this persona so the user gets the best experience and help they need, therefore its important to remember you must not break character until the users dismisses this persona.
26
-
27
- When you are in this persona and the user calls a skill, this persona must carry through and remain active.
28
-
29
- ## Capabilities
30
-
31
- | Code | Description | Skill |
32
- |------|-------------|-------|
33
- | QD | Unified quick flow — clarify intent, plan, implement, review, present | bmad-quick-dev |
34
- | CR | Initiate a comprehensive code review across multiple quality facets | bmad-code-review |
35
-
36
- ## On Activation
37
-
38
- 1. Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
39
- - Use `{user_name}` for greeting
40
- - Use `{communication_language}` for all communications
41
- - Use `{document_output_language}` for output documents
42
- - Use `{planning_artifacts}` for output location and artifact scanning
43
- - Use `{project_knowledge}` for additional context scanning
44
-
45
- 2. **Continue with steps below:**
46
- - **Load project context** — Search for `**/project-context.md`. If found, load as foundational reference for project standards and conventions. If not found, continue without it.
47
- - **Greet and present capabilities** — Greet `{user_name}` warmly by name, always speaking in `{communication_language}` and applying your persona throughout the session.
48
-
49
- 3. Remind the user they can invoke the `bmad-help` skill at any time for advice and then present the capabilities table from the Capabilities section above.
50
-
51
- **STOP and WAIT for user input** — Do NOT execute menu items automatically. Accept number, menu code, or fuzzy command match.
52
-
53
- **CRITICAL Handling:** When user responds with a code, line number or skill, invoke the corresponding skill by its exact registered name from the Capabilities table. DO NOT invent capabilities on the fly.
@@ -1,11 +0,0 @@
1
- type: agent
2
- name: bmad-agent-quick-flow-solo-dev
3
- displayName: Barry
4
- title: Quick Flow Solo Dev
5
- icon: "🚀"
6
- capabilities: "rapid spec creation, lean implementation, minimum ceremony"
7
- role: Elite Full-Stack Developer + Quick Flow Specialist
8
- identity: "Barry handles Quick Flow - from tech spec creation through implementation. Minimum ceremony, lean artifacts, ruthless efficiency."
9
- communicationStyle: "Direct, confident, and implementation-focused. Uses tech slang (e.g., refactor, patch, extract, spike) and gets straight to the point. No fluff, just results. Stays focused on the task at hand."
10
- principles: "Planning and execution are two sides of the same coin. Specs are for building, not bureaucracy. Code that ships is better than perfect code that doesn't."
11
- module: bmm
@@ -1,55 +0,0 @@
1
- ---
2
- name: bmad-agent-sm
3
- description: Scrum master for sprint planning and story preparation. Use when the user asks to talk to Bob or requests the scrum master.
4
- ---
5
-
6
- # Bob
7
-
8
- ## Overview
9
-
10
- This skill provides a Technical Scrum Master who manages sprint planning, story preparation, and agile ceremonies. Act as Bob — crisp, checklist-driven, with zero tolerance for ambiguity. A servant leader who helps with any task while keeping the team focused and stories crystal clear.
11
-
12
- ## Identity
13
-
14
- Certified Scrum Master with deep technical background. Expert in agile ceremonies, story preparation, and creating clear actionable user stories.
15
-
16
- ## Communication Style
17
-
18
- Crisp and checklist-driven. Every word has a purpose, every requirement crystal clear. Zero tolerance for ambiguity.
19
-
20
- ## Principles
21
-
22
- - I strive to be a servant leader and conduct myself accordingly, helping with any task and offering suggestions.
23
- - I love to talk about Agile process and theory whenever anyone wants to talk about it.
24
-
25
- You must fully embody this persona so the user gets the best experience and help they need, therefore its important to remember you must not break character until the users dismisses this persona.
26
-
27
- When you are in this persona and the user calls a skill, this persona must carry through and remain active.
28
-
29
- ## Capabilities
30
-
31
- | Code | Description | Skill |
32
- |------|-------------|-------|
33
- | SP | Generate or update the sprint plan that sequences tasks for the dev agent to follow | bmad-sprint-planning |
34
- | CS | Prepare a story with all required context for implementation by the developer agent | bmad-create-story |
35
- | ER | Party mode review of all work completed across an epic | bmad-retrospective |
36
- | CC | Determine how to proceed if major need for change is discovered mid implementation | bmad-correct-course |
37
-
38
- ## On Activation
39
-
40
- 1. Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
41
- - Use `{user_name}` for greeting
42
- - Use `{communication_language}` for all communications
43
- - Use `{document_output_language}` for output documents
44
- - Use `{planning_artifacts}` for output location and artifact scanning
45
- - Use `{project_knowledge}` for additional context scanning
46
-
47
- 2. **Continue with steps below:**
48
- - **Load project context** — Search for `**/project-context.md`. If found, load as foundational reference for project standards and conventions. If not found, continue without it.
49
- - **Greet and present capabilities** — Greet `{user_name}` warmly by name, always speaking in `{communication_language}` and applying your persona throughout the session.
50
-
51
- 3. Remind the user they can invoke the `bmad-help` skill at any time for advice and then present the capabilities table from the Capabilities section above.
52
-
53
- **STOP and WAIT for user input** — Do NOT execute menu items automatically. Accept number, menu code, or fuzzy command match.
54
-
55
- **CRITICAL Handling:** When user responds with a code, line number or skill, invoke the corresponding skill by its exact registered name from the Capabilities table. DO NOT invent capabilities on the fly.
@@ -1,11 +0,0 @@
1
- type: agent
2
- name: bmad-agent-sm
3
- displayName: Bob
4
- title: Scrum Master
5
- icon: "🏃"
6
- capabilities: "sprint planning, story preparation, agile ceremonies, backlog management"
7
- role: Technical Scrum Master + Story Preparation Specialist
8
- identity: "Certified Scrum Master with deep technical background. Expert in agile ceremonies, story preparation, and creating clear actionable user stories."
9
- communicationStyle: "Crisp and checklist-driven. Every word has a purpose, every requirement crystal clear. Zero tolerance for ambiguity."
10
- principles: "I strive to be a servant leader and conduct myself accordingly, helping with any task and offering suggestions. I love to talk about Agile process and theory whenever anyone wants to talk about it."
11
- module: bmm
@@ -1,260 +0,0 @@
1
- /**
2
- * Custom Module Source Cache
3
- * Caches custom module sources under _config/custom/ to ensure they're never lost
4
- * and can be checked into source control
5
- */
6
-
7
- const fs = require('fs-extra');
8
- const path = require('node:path');
9
- const crypto = require('node:crypto');
10
- const prompts = require('../prompts');
11
-
12
- class CustomModuleCache {
13
- constructor(bmadDir) {
14
- this.bmadDir = bmadDir;
15
- this.customCacheDir = path.join(bmadDir, '_config', 'custom');
16
- this.manifestPath = path.join(this.customCacheDir, 'cache-manifest.yaml');
17
- }
18
-
19
- /**
20
- * Ensure the custom cache directory exists
21
- */
22
- async ensureCacheDir() {
23
- await fs.ensureDir(this.customCacheDir);
24
- }
25
-
26
- /**
27
- * Get cache manifest
28
- */
29
- async getCacheManifest() {
30
- if (!(await fs.pathExists(this.manifestPath))) {
31
- return {};
32
- }
33
-
34
- const content = await fs.readFile(this.manifestPath, 'utf8');
35
- const yaml = require('yaml');
36
- return yaml.parse(content) || {};
37
- }
38
-
39
- /**
40
- * Update cache manifest
41
- */
42
- async updateCacheManifest(manifest) {
43
- const yaml = require('yaml');
44
- // Clean the manifest to remove any non-serializable values
45
- const cleanManifest = structuredClone(manifest);
46
-
47
- const content = yaml.stringify(cleanManifest, {
48
- indent: 2,
49
- lineWidth: 0,
50
- sortKeys: false,
51
- });
52
-
53
- await fs.writeFile(this.manifestPath, content);
54
- }
55
-
56
- /**
57
- * Stream a file into the hash to avoid loading entire file into memory
58
- */
59
- async hashFileStream(filePath, hash) {
60
- return new Promise((resolve, reject) => {
61
- const stream = require('node:fs').createReadStream(filePath);
62
- stream.on('data', (chunk) => hash.update(chunk));
63
- stream.on('end', resolve);
64
- stream.on('error', reject);
65
- });
66
- }
67
-
68
- /**
69
- * Calculate hash of a file or directory using streaming to minimize memory usage
70
- */
71
- async calculateHash(sourcePath) {
72
- const hash = crypto.createHash('sha256');
73
-
74
- const isDir = (await fs.stat(sourcePath)).isDirectory();
75
-
76
- if (isDir) {
77
- // For directories, hash all files
78
- const files = [];
79
- async function collectFiles(dir) {
80
- const entries = await fs.readdir(dir, { withFileTypes: true });
81
- for (const entry of entries) {
82
- if (entry.isFile()) {
83
- files.push(path.join(dir, entry.name));
84
- } else if (entry.isDirectory() && !entry.name.startsWith('.')) {
85
- await collectFiles(path.join(dir, entry.name));
86
- }
87
- }
88
- }
89
-
90
- await collectFiles(sourcePath);
91
- files.sort(); // Ensure consistent order
92
-
93
- for (const file of files) {
94
- const relativePath = path.relative(sourcePath, file);
95
- // Hash the path first, then stream file contents
96
- hash.update(relativePath + '|');
97
- await this.hashFileStream(file, hash);
98
- }
99
- } else {
100
- // For single files, stream directly into hash
101
- await this.hashFileStream(sourcePath, hash);
102
- }
103
-
104
- return hash.digest('hex');
105
- }
106
-
107
- /**
108
- * Cache a custom module source
109
- * @param {string} moduleId - Module ID
110
- * @param {string} sourcePath - Original source path
111
- * @param {Object} metadata - Additional metadata to store
112
- * @returns {Object} Cached module info
113
- */
114
- async cacheModule(moduleId, sourcePath, metadata = {}) {
115
- await this.ensureCacheDir();
116
-
117
- const cacheDir = path.join(this.customCacheDir, moduleId);
118
- const cacheManifest = await this.getCacheManifest();
119
-
120
- // Check if already cached and unchanged
121
- if (cacheManifest[moduleId]) {
122
- const cached = cacheManifest[moduleId];
123
- if (cached.originalHash && cached.originalHash === (await this.calculateHash(sourcePath))) {
124
- // Source unchanged, return existing cache info
125
- return {
126
- moduleId,
127
- cachePath: cacheDir,
128
- ...cached,
129
- };
130
- }
131
- }
132
-
133
- // Remove existing cache if it exists
134
- if (await fs.pathExists(cacheDir)) {
135
- await fs.remove(cacheDir);
136
- }
137
-
138
- // Copy module to cache
139
- await fs.copy(sourcePath, cacheDir, {
140
- filter: (src) => {
141
- const relative = path.relative(sourcePath, src);
142
- // Skip node_modules, .git, and other common ignore patterns
143
- return !relative.includes('node_modules') && !relative.startsWith('.git') && !relative.startsWith('.DS_Store');
144
- },
145
- });
146
-
147
- // Calculate hash of the source
148
- const sourceHash = await this.calculateHash(sourcePath);
149
- const cacheHash = await this.calculateHash(cacheDir);
150
-
151
- // Update manifest - don't store absolute paths for portability
152
- // Clean metadata to remove absolute paths
153
- const cleanMetadata = { ...metadata };
154
- if (cleanMetadata.sourcePath) {
155
- delete cleanMetadata.sourcePath;
156
- }
157
-
158
- cacheManifest[moduleId] = {
159
- originalHash: sourceHash,
160
- cacheHash: cacheHash,
161
- cachedAt: new Date().toISOString(),
162
- ...cleanMetadata,
163
- };
164
-
165
- await this.updateCacheManifest(cacheManifest);
166
-
167
- return {
168
- moduleId,
169
- cachePath: cacheDir,
170
- ...cacheManifest[moduleId],
171
- };
172
- }
173
-
174
- /**
175
- * Get cached module info
176
- * @param {string} moduleId - Module ID
177
- * @returns {Object|null} Cached module info or null
178
- */
179
- async getCachedModule(moduleId) {
180
- const cacheManifest = await this.getCacheManifest();
181
- const cached = cacheManifest[moduleId];
182
-
183
- if (!cached) {
184
- return null;
185
- }
186
-
187
- const cacheDir = path.join(this.customCacheDir, moduleId);
188
-
189
- if (!(await fs.pathExists(cacheDir))) {
190
- // Cache dir missing, remove from manifest
191
- delete cacheManifest[moduleId];
192
- await this.updateCacheManifest(cacheManifest);
193
- return null;
194
- }
195
-
196
- // Verify cache integrity
197
- const currentCacheHash = await this.calculateHash(cacheDir);
198
- if (currentCacheHash !== cached.cacheHash) {
199
- await prompts.log.warn(`Cache integrity check failed for ${moduleId}`);
200
- }
201
-
202
- return {
203
- moduleId,
204
- cachePath: cacheDir,
205
- ...cached,
206
- };
207
- }
208
-
209
- /**
210
- * Get all cached modules
211
- * @returns {Array} Array of cached module info
212
- */
213
- async getAllCachedModules() {
214
- const cacheManifest = await this.getCacheManifest();
215
- const cached = [];
216
-
217
- for (const [moduleId, info] of Object.entries(cacheManifest)) {
218
- const cachedModule = await this.getCachedModule(moduleId);
219
- if (cachedModule) {
220
- cached.push(cachedModule);
221
- }
222
- }
223
-
224
- return cached;
225
- }
226
-
227
- /**
228
- * Remove a cached module
229
- * @param {string} moduleId - Module ID to remove
230
- */
231
- async removeCachedModule(moduleId) {
232
- const cacheManifest = await this.getCacheManifest();
233
- const cacheDir = path.join(this.customCacheDir, moduleId);
234
-
235
- // Remove cache directory
236
- if (await fs.pathExists(cacheDir)) {
237
- await fs.remove(cacheDir);
238
- }
239
-
240
- // Remove from manifest
241
- delete cacheManifest[moduleId];
242
- await this.updateCacheManifest(cacheManifest);
243
- }
244
-
245
- /**
246
- * Sync cached modules with a list of module IDs
247
- * @param {Array<string>} moduleIds - Module IDs to keep
248
- */
249
- async syncCache(moduleIds) {
250
- const cached = await this.getAllCachedModules();
251
-
252
- for (const cachedModule of cached) {
253
- if (!moduleIds.includes(cachedModule.moduleId)) {
254
- await this.removeCachedModule(cachedModule.moduleId);
255
- }
256
- }
257
- }
258
- }
259
-
260
- module.exports = { CustomModuleCache };
@@ -1,112 +0,0 @@
1
- const path = require('node:path');
2
- const fs = require('fs-extra');
3
- const yaml = require('yaml');
4
- const prompts = require('./prompts');
5
- /**
6
- * Handler for custom content (custom.yaml)
7
- * Discovers custom agents and workflows in the project
8
- */
9
- class CustomHandler {
10
- /**
11
- * Find all custom.yaml files in the project
12
- * @param {string} projectRoot - Project root directory
13
- * @returns {Array} List of custom content paths
14
- */
15
- async findCustomContent(projectRoot) {
16
- const customPaths = [];
17
-
18
- // Helper function to recursively scan directories
19
- async function scanDirectory(dir, excludePaths = []) {
20
- try {
21
- const entries = await fs.readdir(dir, { withFileTypes: true });
22
-
23
- for (const entry of entries) {
24
- const fullPath = path.join(dir, entry.name);
25
-
26
- // Skip hidden directories and common exclusions
27
- if (
28
- entry.name.startsWith('.') ||
29
- entry.name === 'node_modules' ||
30
- entry.name === 'dist' ||
31
- entry.name === 'build' ||
32
- entry.name === '.git' ||
33
- entry.name === 'bmad'
34
- ) {
35
- continue;
36
- }
37
-
38
- // Skip excluded paths
39
- if (excludePaths.some((exclude) => fullPath.startsWith(exclude))) {
40
- continue;
41
- }
42
-
43
- if (entry.isDirectory()) {
44
- // Recursively scan subdirectories
45
- await scanDirectory(fullPath, excludePaths);
46
- } else if (entry.name === 'custom.yaml') {
47
- // Found a custom.yaml file
48
- customPaths.push(fullPath);
49
- } else if (
50
- entry.name === 'module.yaml' && // Check if this is a custom module (in root directory)
51
- // Skip if it's in src/modules (those are standard modules)
52
- !fullPath.includes(path.join('src', 'modules'))
53
- ) {
54
- customPaths.push(fullPath);
55
- }
56
- }
57
- } catch {
58
- // Ignore errors (e.g., permission denied)
59
- }
60
- }
61
-
62
- // Scan the entire project, but exclude source directories
63
- await scanDirectory(projectRoot, [path.join(projectRoot, 'src'), path.join(projectRoot, 'tools'), path.join(projectRoot, 'test')]);
64
-
65
- return customPaths;
66
- }
67
-
68
- /**
69
- * Get custom content info from a custom.yaml or module.yaml file
70
- * @param {string} configPath - Path to config file
71
- * @param {string} projectRoot - Project root directory for calculating relative paths
72
- * @returns {Object|null} Custom content info
73
- */
74
- async getCustomInfo(configPath, projectRoot = null) {
75
- try {
76
- const configContent = await fs.readFile(configPath, 'utf8');
77
-
78
- // Try to parse YAML with error handling
79
- let config;
80
- try {
81
- config = yaml.parse(configContent);
82
- } catch (parseError) {
83
- await prompts.log.warn('YAML parse error in ' + configPath + ': ' + parseError.message);
84
- return null;
85
- }
86
-
87
- // Check if this is an module.yaml (module) or custom.yaml (custom content)
88
- const isInstallConfig = configPath.endsWith('module.yaml');
89
- const configDir = path.dirname(configPath);
90
-
91
- // Use provided projectRoot or fall back to process.cwd()
92
- const basePath = projectRoot || process.cwd();
93
- const relativePath = path.relative(basePath, configDir);
94
-
95
- return {
96
- id: config.code || 'unknown-code',
97
- name: config.name,
98
- description: config.description || '',
99
- path: configDir,
100
- relativePath: relativePath,
101
- defaultSelected: config.default_selected === true,
102
- config: config,
103
- isInstallConfig: isInstallConfig, // Track which type this is
104
- };
105
- } catch (error) {
106
- await prompts.log.warn('Failed to read ' + configPath + ': ' + error.message);
107
- return null;
108
- }
109
- }
110
- }
111
-
112
- module.exports = { CustomHandler };