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.
- package/.claude-plugin/marketplace.json +0 -3
- package/README.md +8 -9
- package/README_CN.md +1 -1
- package/README_VN.md +110 -0
- package/package.json +1 -1
- package/removals.txt +17 -0
- package/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/steps/step-13-responsive-accessibility.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/data/prd-purpose.md +197 -0
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01-discovery.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01b-legacy-conversion.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-02-review.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-03-edit.md +1 -1
- package/src/bmm-skills/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md +1 -3
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-01-document-discovery.md +1 -1
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-02-prd-analysis.md +1 -1
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/steps/step-03-epic-coverage-validation.md +1 -1
- package/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/workflow.md +1 -1
- package/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/workflow.md +1 -1
- package/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +5 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/SKILL.md +29 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/generate-trail.md +38 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-01-orientation.md +105 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-02-walkthrough.md +89 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-03-detail-pass.md +106 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-04-testing.md +74 -0
- package/src/bmm-skills/4-implementation/bmad-checkpoint-preview/step-05-wrapup.md +24 -0
- package/src/bmm-skills/4-implementation/bmad-code-review/steps/step-01-gather-context.md +38 -15
- package/src/bmm-skills/4-implementation/bmad-correct-course/checklist.md +2 -2
- package/src/bmm-skills/4-implementation/bmad-correct-course/workflow.md +8 -8
- package/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/checklist.md +1 -1
- package/src/bmm-skills/4-implementation/bmad-quick-dev/compile-epic-context.md +62 -0
- package/src/bmm-skills/4-implementation/bmad-quick-dev/spec-template.md +1 -1
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-01-clarify-and-route.md +33 -6
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-02-plan.md +20 -8
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-03-implement.md +2 -0
- package/src/bmm-skills/4-implementation/bmad-quick-dev/step-oneshot.md +16 -4
- package/src/bmm-skills/4-implementation/bmad-quick-dev/workflow.md +1 -5
- package/src/bmm-skills/4-implementation/bmad-retrospective/workflow.md +134 -134
- package/src/bmm-skills/4-implementation/bmad-sprint-planning/sprint-status-template.yaml +1 -1
- package/src/bmm-skills/4-implementation/bmad-sprint-planning/workflow.md +3 -3
- package/src/bmm-skills/4-implementation/bmad-sprint-status/workflow.md +2 -2
- package/src/bmm-skills/module-help.csv +2 -0
- package/src/core-skills/bmad-help/SKILL.md +4 -2
- package/src/core-skills/bmad-party-mode/SKILL.md +8 -6
- package/src/core-skills/module-help.csv +1 -0
- package/tools/installer/cli-utils.js +18 -9
- package/tools/installer/commands/install.js +1 -1
- package/tools/installer/core/existing-install.js +2 -8
- package/tools/installer/core/install-paths.js +0 -3
- package/tools/installer/core/installer.js +180 -463
- package/tools/installer/core/manifest-generator.js +8 -14
- package/tools/installer/core/manifest.js +94 -102
- package/tools/installer/ide/_config-driven.js +149 -38
- package/tools/installer/ide/shared/skill-manifest.js +1 -16
- package/tools/installer/install-messages.yaml +19 -26
- package/tools/installer/modules/community-manager.js +377 -0
- package/tools/installer/modules/custom-module-manager.js +644 -0
- package/tools/installer/modules/external-manager.js +65 -49
- package/tools/installer/modules/official-modules.js +117 -65
- package/tools/installer/modules/plugin-resolver.js +398 -0
- package/tools/installer/modules/registry-client.js +66 -0
- package/tools/installer/{external-official-modules.yaml → modules/registry-fallback.yaml} +3 -12
- package/tools/installer/ui.js +549 -666
- package/src/bmm-skills/4-implementation/bmad-agent-qa/SKILL.md +0 -61
- package/src/bmm-skills/4-implementation/bmad-agent-qa/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/4-implementation/bmad-agent-quick-flow-solo-dev/SKILL.md +0 -53
- package/src/bmm-skills/4-implementation/bmad-agent-quick-flow-solo-dev/bmad-skill-manifest.yaml +0 -11
- package/src/bmm-skills/4-implementation/bmad-agent-sm/SKILL.md +0 -55
- package/src/bmm-skills/4-implementation/bmad-agent-sm/bmad-skill-manifest.yaml +0 -11
- package/tools/installer/core/custom-module-cache.js +0 -260
- package/tools/installer/custom-handler.js +0 -112
- 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.
|
package/src/bmm-skills/4-implementation/bmad-agent-quick-flow-solo-dev/bmad-skill-manifest.yaml
DELETED
|
@@ -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 };
|