@clfhhc/bmad-methods-skills 0.0.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.
- package/LICENSE +21 -0
- package/README.md +48 -0
- package/bin/bmad-skills.js +217 -0
- package/config.json +25 -0
- package/convert.js +381 -0
- package/docs/development.md +53 -0
- package/docs/getting-started.md +129 -0
- package/docs/technical-reference.md +116 -0
- package/package.json +46 -0
- package/skills/bootstrap-bmad-skills/SKILL.md +84 -0
- package/skills/enhance-bmad-skills/SKILL.md +149 -0
- package/skills/enhance-bmad-skills/data/path-patterns.md +95 -0
- package/skills/enhance-bmad-skills/data/skills-registry.md +86 -0
- package/src/converters/agent-converter.js +298 -0
- package/src/converters/workflow-converter.js +671 -0
- package/src/utils/bmad-fetcher.js +97 -0
- package/src/utils/file-finder.js +220 -0
- package/src/utils/path-rewriter.js +273 -0
- package/src/utils/skill-writer.js +108 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 David Chen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# BMAD-Methods-Skills
|
|
2
|
+
|
|
3
|
+
Automatically convert BMAD-METHOD agents and workflows to Claude Skills format.
|
|
4
|
+
|
|
5
|
+
## Distribution (New Projects)
|
|
6
|
+
|
|
7
|
+
To install the BMAD bootstrap skill into a new project, you can use `npx`:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @clfhhc/bmad-methods-skills init
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This will:
|
|
14
|
+
1. Detect your AI tool (.agent, .cursor, or .claude)
|
|
15
|
+
2. Install the `bootstrap-bmad-skills` and `enhance-bmad-skills` into your project
|
|
16
|
+
3. Enable the `BS` command to fetch and install all other BMAD skills via `npx`
|
|
17
|
+
|
|
18
|
+
## Documentation
|
|
19
|
+
|
|
20
|
+
For full documentation on development, manual usage, and technical details, please see:
|
|
21
|
+
|
|
22
|
+
- **[Getting Started](docs/getting-started.md)**: Installation, Usage, and Configuration
|
|
23
|
+
- **[Technical Reference](docs/technical-reference.md)**: Output structure and conversion details
|
|
24
|
+
- **[Development](docs/development.md)**: Project structure, contributing, and troubleshooting
|
|
25
|
+
|
|
26
|
+
## License
|
|
27
|
+
|
|
28
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
29
|
+
|
|
30
|
+
## Credits
|
|
31
|
+
|
|
32
|
+
- **BMAD-METHOD**: Created by [BMAD Code Organization](https://github.com/bmad-code-org/BMAD-METHOD)
|
|
33
|
+
- **Claude Skills**: Format by [Anthropic](https://github.com/anthropics/skills)
|
|
34
|
+
|
|
35
|
+
## Contributing
|
|
36
|
+
|
|
37
|
+
Contributions welcome! Please:
|
|
38
|
+
|
|
39
|
+
1. Fork the repository
|
|
40
|
+
2. Create a feature branch
|
|
41
|
+
3. Make your changes
|
|
42
|
+
4. Test thoroughly
|
|
43
|
+
5. Submit a pull request
|
|
44
|
+
|
|
45
|
+
## Related Projects
|
|
46
|
+
|
|
47
|
+
- [BMAD-METHOD](https://github.com/bmad-code-org/BMAD-METHOD) - Original BMAD methodology
|
|
48
|
+
- [Claude Skills](https://github.com/anthropics/skills) - Claude Skills specification and examples
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import readline from 'node:readline';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const pkgRoot = path.resolve(__dirname, '../');
|
|
11
|
+
|
|
12
|
+
async function run() {
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const command = args[0];
|
|
15
|
+
|
|
16
|
+
if (command === 'init') {
|
|
17
|
+
await init(args);
|
|
18
|
+
} else if (command === 'install') {
|
|
19
|
+
await install(args);
|
|
20
|
+
} else if (command === '--help' || command === '-h' || args.includes('--help') || args.includes('-h')) {
|
|
21
|
+
printHelp();
|
|
22
|
+
} else {
|
|
23
|
+
// Proxy to convert.js logic
|
|
24
|
+
await import('../convert.js');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Install specific skills from a source directory to the project's skill directory
|
|
32
|
+
*/
|
|
33
|
+
async function install(args) {
|
|
34
|
+
const sourceArg = args.find(a => a.startsWith('--from='))?.split('=')[1];
|
|
35
|
+
const force = args.includes('--force');
|
|
36
|
+
|
|
37
|
+
if (!sourceArg) {
|
|
38
|
+
console.error('ā Missing required argument: --from=<path>');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const sourcePath = path.resolve(process.cwd(), sourceArg);
|
|
43
|
+
if (!(await fs.pathExists(sourcePath))) {
|
|
44
|
+
console.error(`ā Source path does not exist: ${sourcePath}`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Detect tool
|
|
49
|
+
const toolInfo = await detectTool(args);
|
|
50
|
+
if (!toolInfo) return;
|
|
51
|
+
|
|
52
|
+
console.log(`š¦ Installation Target: ${toolInfo.name} (${toolInfo.path})`);
|
|
53
|
+
console.log(`š Source: ${sourcePath}`);
|
|
54
|
+
|
|
55
|
+
// Get skills from source
|
|
56
|
+
const skills = await fs.readdir(sourcePath);
|
|
57
|
+
const validSkills = [];
|
|
58
|
+
|
|
59
|
+
for (const skill of skills) {
|
|
60
|
+
if ((await fs.stat(path.join(sourcePath, skill))).isDirectory()) {
|
|
61
|
+
validSkills.push(skill);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (validSkills.length === 0) {
|
|
66
|
+
console.warn('ā ļø No skill directories found in source.');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log(`\nFound ${validSkills.length} skills to install.`);
|
|
71
|
+
|
|
72
|
+
// Install
|
|
73
|
+
for (const skillName of validSkills) {
|
|
74
|
+
await installSkill(
|
|
75
|
+
skillName,
|
|
76
|
+
path.join(sourcePath, skillName),
|
|
77
|
+
path.join(process.cwd(), toolInfo.path, skillName),
|
|
78
|
+
force
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log(`\nā
Installation complete.`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function init(args) {
|
|
86
|
+
console.log('š BMAD Skills Installer\n');
|
|
87
|
+
|
|
88
|
+
const toolInfo = await detectTool(args);
|
|
89
|
+
if (!toolInfo) return;
|
|
90
|
+
|
|
91
|
+
console.log(`š¦ Installing Bootstrap Skills for ${toolInfo.name}...`);
|
|
92
|
+
|
|
93
|
+
// Dynamically find skills in package
|
|
94
|
+
const skillsDir = path.join(pkgRoot, 'skills');
|
|
95
|
+
if (!(await fs.pathExists(skillsDir))) {
|
|
96
|
+
console.error('ā Critical Error: Package skills directory not found.');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const skills = await fs.readdir(skillsDir);
|
|
101
|
+
const skillsToInstall = [];
|
|
102
|
+
|
|
103
|
+
for (const skill of skills) {
|
|
104
|
+
// Only install directories as skills
|
|
105
|
+
if ((await fs.stat(path.join(skillsDir, skill))).isDirectory()) {
|
|
106
|
+
skillsToInstall.push(skill);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (skillsToInstall.length === 0) {
|
|
111
|
+
console.warn('ā ļø No skills found in package to install.');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const force = args.includes('--force');
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
for (const skillName of skillsToInstall) {
|
|
119
|
+
const sourceDir = path.join(skillsDir, skillName);
|
|
120
|
+
const targetDir = path.resolve(process.cwd(), toolInfo.path, skillName);
|
|
121
|
+
|
|
122
|
+
await installSkill(skillName, sourceDir, targetDir, force);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log(`\nā
Successfully initialized in: ${toolInfo.path}/`);
|
|
126
|
+
console.log('\nNext steps:');
|
|
127
|
+
console.log(`1. Open your AI chat (${toolInfo.name}).`);
|
|
128
|
+
console.log('2. Type "BS" or "bootstrap-skills" to fetch and install the full BMAD method suite.');
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error(`\nā Installation failed: ${error.message}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Helpher to copy skill with checks
|
|
136
|
+
*/
|
|
137
|
+
async function installSkill(name, source, target, force) {
|
|
138
|
+
if (await fs.pathExists(target)) {
|
|
139
|
+
if (!force) {
|
|
140
|
+
console.warn(` ā Skill '${name}' already exists. Skipping.`);
|
|
141
|
+
console.warn(' (Use --force to overwrite)');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
console.log(` ā» Updating ${name}...`);
|
|
145
|
+
} else {
|
|
146
|
+
console.log(` + Installing ${name}...`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
await fs.ensureDir(path.dirname(target));
|
|
150
|
+
await fs.copy(source, target, { overwrite: true });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Helper to detect AI tool
|
|
155
|
+
*/
|
|
156
|
+
async function detectTool(args) {
|
|
157
|
+
const toolArg = args.find(a => a.startsWith('--tool='))?.split('=')[1];
|
|
158
|
+
const force = args.includes('--force');
|
|
159
|
+
|
|
160
|
+
const tools = [
|
|
161
|
+
{ name: 'Antigravity', path: '.agent/skills', active: await fs.pathExists('.agent') },
|
|
162
|
+
{ name: 'Cursor', path: '.cursor/skills', active: await fs.pathExists('.cursor') },
|
|
163
|
+
{ name: 'Claude Code (Local)', path: '.claude/skills', active: await fs.pathExists('.claude') },
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
let selectedTool = tools.find(t => t.active);
|
|
167
|
+
|
|
168
|
+
if (toolArg) {
|
|
169
|
+
selectedTool = tools.find(t => t.name.toLowerCase().includes(toolArg.toLowerCase()));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!selectedTool) {
|
|
173
|
+
if (force) {
|
|
174
|
+
// Default to antigravity if forced and not found
|
|
175
|
+
return tools[0];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
console.log('ā No AI tool directory detected (.agent, .cursor, .claude).');
|
|
179
|
+
console.log(' Use --tool=<name> to force installation or ensure you are in the project root.');
|
|
180
|
+
console.log(' Available tools: antigravity, cursor, claude');
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return selectedTool;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function printHelp() {
|
|
188
|
+
console.log(`
|
|
189
|
+
Usage: npx @clfhhc/bmad-methods-skills [command] [options]
|
|
190
|
+
|
|
191
|
+
Commands:
|
|
192
|
+
init Install bootstrap skills into the current project
|
|
193
|
+
install Install skills from a local directory
|
|
194
|
+
[no command] Run the BMAD-to-Skills converter (proxy to convert.js)
|
|
195
|
+
|
|
196
|
+
Options (for init/install):
|
|
197
|
+
--tool=<name> Specify tool (antigravity, cursor, claude)
|
|
198
|
+
--force Overwrite existing skills / Force installation
|
|
199
|
+
--from=<path> (install only) Source directory containing skills
|
|
200
|
+
|
|
201
|
+
Options (for conversion):
|
|
202
|
+
--repo <url> Override BMAD repository URL
|
|
203
|
+
--branch <name> Override BMAD branch
|
|
204
|
+
--output-dir <path> Custom output directory
|
|
205
|
+
--identity-limit <n> Character limit for identity
|
|
206
|
+
--[no-]examples Enable/disable examples
|
|
207
|
+
--[no-]best-practices Enable/disable best practices
|
|
208
|
+
... (see convert.js --help for full list)
|
|
209
|
+
|
|
210
|
+
-h, --help Show this help
|
|
211
|
+
`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
run().catch(err => {
|
|
215
|
+
console.error(err);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
});
|
package/config.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bmadRepo": "https://github.com/bmad-code-org/BMAD-METHOD.git",
|
|
3
|
+
"bmadBranch": "main",
|
|
4
|
+
"outputDir": "./skills",
|
|
5
|
+
"tempDir": "./.temp/bmad-method",
|
|
6
|
+
"modules": ["bmm", "bmb", "cis", "core"],
|
|
7
|
+
"agentPaths": [
|
|
8
|
+
"src/core/agents",
|
|
9
|
+
"src/modules/*/agents"
|
|
10
|
+
],
|
|
11
|
+
"workflowPaths": [
|
|
12
|
+
"src/core/workflows",
|
|
13
|
+
"src/modules/*/workflows"
|
|
14
|
+
],
|
|
15
|
+
"enhancements": {
|
|
16
|
+
"optional": {
|
|
17
|
+
"addExamples": true,
|
|
18
|
+
"addBestPractices": true,
|
|
19
|
+
"addTroubleshooting": false,
|
|
20
|
+
"addRelatedSkills": true,
|
|
21
|
+
"generateMetaDocs": false
|
|
22
|
+
},
|
|
23
|
+
"identityCharLimit": null
|
|
24
|
+
}
|
|
25
|
+
}
|
package/convert.js
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { fetchBmadRepo } from './src/utils/bmad-fetcher.js';
|
|
5
|
+
import { findAgentsAndWorkflows } from './src/utils/file-finder.js';
|
|
6
|
+
import { convertAgentToSkill } from './src/converters/agent-converter.js';
|
|
7
|
+
import { convertWorkflowToSkill } from './src/converters/workflow-converter.js';
|
|
8
|
+
import { writeSkill } from './src/utils/skill-writer.js';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse command line arguments
|
|
15
|
+
*/
|
|
16
|
+
function parseArgs() {
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
const options = {
|
|
19
|
+
outputDir: null,
|
|
20
|
+
repoUrl: null,
|
|
21
|
+
branch: null,
|
|
22
|
+
identityCharLimit: null,
|
|
23
|
+
addExamples: null,
|
|
24
|
+
addBestPractices: null,
|
|
25
|
+
addTroubleshooting: null,
|
|
26
|
+
addRelatedSkills: null,
|
|
27
|
+
generateMetaDocs: null,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < args.length; i++) {
|
|
31
|
+
const arg = args[i];
|
|
32
|
+
|
|
33
|
+
if (arg === '--output-dir' && i + 1 < args.length) {
|
|
34
|
+
options.outputDir = args[++i];
|
|
35
|
+
} else if (arg === '--repo' && i + 1 < args.length) {
|
|
36
|
+
options.repoUrl = args[++i];
|
|
37
|
+
} else if (arg === '--branch' && i + 1 < args.length) {
|
|
38
|
+
options.branch = args[++i];
|
|
39
|
+
} else if (arg === '--identity-limit' && i + 1 < args.length) {
|
|
40
|
+
const limit = Number.parseInt(args[++i], 10);
|
|
41
|
+
options.identityCharLimit = Number.isNaN(limit) ? null : limit;
|
|
42
|
+
} else if (arg === '--no-examples') {
|
|
43
|
+
options.addExamples = false;
|
|
44
|
+
} else if (arg === '--examples') {
|
|
45
|
+
options.addExamples = true;
|
|
46
|
+
} else if (arg === '--no-best-practices') {
|
|
47
|
+
options.addBestPractices = false;
|
|
48
|
+
} else if (arg === '--best-practices') {
|
|
49
|
+
options.addBestPractices = true;
|
|
50
|
+
} else if (arg === '--no-troubleshooting') {
|
|
51
|
+
options.addTroubleshooting = false;
|
|
52
|
+
} else if (arg === '--troubleshooting') {
|
|
53
|
+
options.addTroubleshooting = true;
|
|
54
|
+
} else if (arg === '--no-related-skills') {
|
|
55
|
+
options.addRelatedSkills = false;
|
|
56
|
+
} else if (arg === '--related-skills') {
|
|
57
|
+
options.addRelatedSkills = true;
|
|
58
|
+
} else if (arg === '--no-meta-docs') {
|
|
59
|
+
options.generateMetaDocs = false;
|
|
60
|
+
} else if (arg === '--meta-docs') {
|
|
61
|
+
options.generateMetaDocs = true;
|
|
62
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
63
|
+
printHelp();
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return options;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Print help message
|
|
73
|
+
*/
|
|
74
|
+
function printHelp() {
|
|
75
|
+
console.log(`
|
|
76
|
+
BMAD to Skills Converter
|
|
77
|
+
|
|
78
|
+
Usage: pnpm convert [options]
|
|
79
|
+
|
|
80
|
+
Options:
|
|
81
|
+
--output-dir <path> Custom output directory (default: ./skills)
|
|
82
|
+
Use a non-version-controlled folder for custom configs
|
|
83
|
+
|
|
84
|
+
--repo <url> Override BMAD repository URL
|
|
85
|
+
--branch <name> Override BMAD branch (default: main)
|
|
86
|
+
|
|
87
|
+
--identity-limit <num> Character limit for identity in description
|
|
88
|
+
(default: no limit, use --identity-limit 200 to enable old behavior)
|
|
89
|
+
|
|
90
|
+
Optional Enhancements (override config.json defaults):
|
|
91
|
+
--examples / --no-examples
|
|
92
|
+
--best-practices / --no-best-practices
|
|
93
|
+
--troubleshooting / --no-troubleshooting
|
|
94
|
+
--related-skills / --no-related-skills
|
|
95
|
+
--meta-docs / --no-meta-docs
|
|
96
|
+
|
|
97
|
+
-h, --help Show this help message
|
|
98
|
+
|
|
99
|
+
Examples:
|
|
100
|
+
# Use default config (outputs to ./skills, version controlled)
|
|
101
|
+
pnpm convert
|
|
102
|
+
|
|
103
|
+
# Output to custom directory with different settings
|
|
104
|
+
pnpm convert --output-dir ./custom-skills --identity-limit 200 --no-examples
|
|
105
|
+
|
|
106
|
+
# Enable troubleshooting for this run
|
|
107
|
+
pnpm convert --troubleshooting
|
|
108
|
+
`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Parse CLI arguments
|
|
112
|
+
const cliOptions = parseArgs();
|
|
113
|
+
|
|
114
|
+
// Load configuration
|
|
115
|
+
const configPath = path.join(__dirname, 'config.json');
|
|
116
|
+
let config;
|
|
117
|
+
try {
|
|
118
|
+
if (!(await fs.pathExists(configPath))) {
|
|
119
|
+
throw new Error(`Configuration file not found: ${configPath}`);
|
|
120
|
+
}
|
|
121
|
+
const configContent = await fs.readFile(configPath, 'utf-8');
|
|
122
|
+
config = JSON.parse(configContent);
|
|
123
|
+
|
|
124
|
+
// Validate required config fields
|
|
125
|
+
const requiredFields = ['bmadRepo', 'bmadBranch', 'outputDir', 'tempDir'];
|
|
126
|
+
for (const field of requiredFields) {
|
|
127
|
+
if (!config[field]) {
|
|
128
|
+
throw new Error(`Missing required configuration field: ${field}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Merge CLI options with config
|
|
133
|
+
if (cliOptions.outputDir) {
|
|
134
|
+
config.outputDir = cliOptions.outputDir;
|
|
135
|
+
}
|
|
136
|
+
if (cliOptions.repoUrl) {
|
|
137
|
+
config.bmadRepo = cliOptions.repoUrl;
|
|
138
|
+
console.log(`ā¹ļø Overriding BMAD Repo: ${config.bmadRepo}`);
|
|
139
|
+
}
|
|
140
|
+
if (cliOptions.branch) {
|
|
141
|
+
config.bmadBranch = cliOptions.branch;
|
|
142
|
+
console.log(`ā¹ļø Overriding BMAD Branch: ${config.bmadBranch}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Initialize enhancements config if not present
|
|
146
|
+
if (!config.enhancements) {
|
|
147
|
+
config.enhancements = {};
|
|
148
|
+
}
|
|
149
|
+
if (!config.enhancements.optional) {
|
|
150
|
+
config.enhancements.optional = {
|
|
151
|
+
addExamples: true,
|
|
152
|
+
addBestPractices: true,
|
|
153
|
+
addTroubleshooting: false,
|
|
154
|
+
addRelatedSkills: true,
|
|
155
|
+
generateMetaDocs: false,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Override with CLI options
|
|
160
|
+
if (cliOptions.identityCharLimit !== null) {
|
|
161
|
+
config.enhancements.identityCharLimit = cliOptions.identityCharLimit;
|
|
162
|
+
}
|
|
163
|
+
if (cliOptions.addExamples !== null) {
|
|
164
|
+
config.enhancements.optional.addExamples = cliOptions.addExamples;
|
|
165
|
+
}
|
|
166
|
+
if (cliOptions.addBestPractices !== null) {
|
|
167
|
+
config.enhancements.optional.addBestPractices = cliOptions.addBestPractices;
|
|
168
|
+
}
|
|
169
|
+
if (cliOptions.addTroubleshooting !== null) {
|
|
170
|
+
config.enhancements.optional.addTroubleshooting = cliOptions.addTroubleshooting;
|
|
171
|
+
}
|
|
172
|
+
if (cliOptions.addRelatedSkills !== null) {
|
|
173
|
+
config.enhancements.optional.addRelatedSkills = cliOptions.addRelatedSkills;
|
|
174
|
+
}
|
|
175
|
+
if (cliOptions.generateMetaDocs !== null) {
|
|
176
|
+
config.enhancements.optional.generateMetaDocs = cliOptions.generateMetaDocs;
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error(`ā Failed to load configuration: ${error.message}`);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Statistics
|
|
184
|
+
const stats = {
|
|
185
|
+
agents: { total: 0, converted: 0, errors: 0 },
|
|
186
|
+
workflows: { total: 0, converted: 0, errors: 0 },
|
|
187
|
+
errors: [],
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Main conversion function
|
|
192
|
+
*/
|
|
193
|
+
async function main() {
|
|
194
|
+
console.log('š BMAD to Skills Converter\n');
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
// Step 1: Fetch BMAD repository
|
|
198
|
+
console.log('š„ Fetching BMAD-METHOD repository...');
|
|
199
|
+
const bmadRoot = await fetchBmadRepo(
|
|
200
|
+
config.bmadRepo,
|
|
201
|
+
config.bmadBranch,
|
|
202
|
+
path.resolve(process.cwd(), config.tempDir),
|
|
203
|
+
);
|
|
204
|
+
console.log(`ā Repository ready at: ${bmadRoot}\n`);
|
|
205
|
+
|
|
206
|
+
// Step 2: Discover agents and workflows
|
|
207
|
+
console.log('š Discovering agents and workflows...');
|
|
208
|
+
const { agents, workflows } = await findAgentsAndWorkflows(
|
|
209
|
+
bmadRoot,
|
|
210
|
+
config.agentPaths,
|
|
211
|
+
config.workflowPaths,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
stats.agents.total = agents.length;
|
|
215
|
+
stats.workflows.total = workflows.length;
|
|
216
|
+
|
|
217
|
+
console.log(
|
|
218
|
+
`ā Found ${agents.length} agents and ${workflows.length} workflows\n`,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// Step 3: Prepare output directory
|
|
222
|
+
const outputDir = path.resolve(process.cwd(), config.outputDir);
|
|
223
|
+
await fs.ensureDir(outputDir);
|
|
224
|
+
console.log(`š Output directory: ${outputDir}\n`);
|
|
225
|
+
|
|
226
|
+
// Step 4: Convert agents
|
|
227
|
+
if (agents.length > 0) {
|
|
228
|
+
console.log('š¤ Converting agents...');
|
|
229
|
+
const agentOptions = {
|
|
230
|
+
identityCharLimit: config.enhancements.identityCharLimit ?? null,
|
|
231
|
+
allAgents: agents,
|
|
232
|
+
allWorkflows: workflows,
|
|
233
|
+
};
|
|
234
|
+
for (const agent of agents) {
|
|
235
|
+
try {
|
|
236
|
+
const skillContent = await convertAgentToSkill(agent.path, {
|
|
237
|
+
...agentOptions,
|
|
238
|
+
currentModule: agent.module,
|
|
239
|
+
});
|
|
240
|
+
await writeSkill(
|
|
241
|
+
outputDir,
|
|
242
|
+
agent.module,
|
|
243
|
+
agent.name,
|
|
244
|
+
skillContent,
|
|
245
|
+
);
|
|
246
|
+
stats.agents.converted++;
|
|
247
|
+
console.log(` ā ${agent.module}/${agent.name}`);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
stats.agents.errors++;
|
|
250
|
+
stats.errors.push({
|
|
251
|
+
type: 'agent',
|
|
252
|
+
path: agent.path,
|
|
253
|
+
error: error.message,
|
|
254
|
+
});
|
|
255
|
+
console.error(
|
|
256
|
+
` ā ${agent.module}/${agent.name}: ${error.message}`,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
console.log();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Step 5: Convert workflows
|
|
264
|
+
if (workflows.length > 0) {
|
|
265
|
+
console.log('āļø Converting workflows...');
|
|
266
|
+
const workflowOptions = {};
|
|
267
|
+
for (const workflow of workflows) {
|
|
268
|
+
try {
|
|
269
|
+
const skillContent = await convertWorkflowToSkill(
|
|
270
|
+
workflow.path,
|
|
271
|
+
workflow.instructionsPath,
|
|
272
|
+
workflow.workflowDir,
|
|
273
|
+
workflow.instructionsType,
|
|
274
|
+
{
|
|
275
|
+
...workflowOptions,
|
|
276
|
+
isMarkdown: workflow.isMarkdown || false,
|
|
277
|
+
},
|
|
278
|
+
);
|
|
279
|
+
await writeSkill(
|
|
280
|
+
outputDir,
|
|
281
|
+
workflow.module,
|
|
282
|
+
workflow.name,
|
|
283
|
+
skillContent,
|
|
284
|
+
{ workflowDir: workflow.workflowDir },
|
|
285
|
+
);
|
|
286
|
+
stats.workflows.converted++;
|
|
287
|
+
console.log(` ā ${workflow.module}/${workflow.name}`);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
stats.workflows.errors++;
|
|
290
|
+
stats.errors.push({
|
|
291
|
+
type: 'workflow',
|
|
292
|
+
path: workflow.path,
|
|
293
|
+
error: error.message,
|
|
294
|
+
});
|
|
295
|
+
console.error(
|
|
296
|
+
` ā ${workflow.module}/${workflow.name}: ${error.message}`,
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
console.log();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Step 6: Generate summary
|
|
304
|
+
await printSummary();
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error(`\nā Fatal error: ${error.message}`);
|
|
307
|
+
console.error(error.stack);
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Prints conversion summary
|
|
314
|
+
*/
|
|
315
|
+
async function printSummary() {
|
|
316
|
+
console.log('š Conversion Summary\n');
|
|
317
|
+
console.log('Agents:');
|
|
318
|
+
console.log(` Total: ${stats.agents.total}`);
|
|
319
|
+
console.log(` Converted: ${stats.agents.converted}`);
|
|
320
|
+
console.log(` Errors: ${stats.agents.errors}`);
|
|
321
|
+
console.log();
|
|
322
|
+
console.log('Workflows:');
|
|
323
|
+
console.log(` Total: ${stats.workflows.total}`);
|
|
324
|
+
console.log(` Converted: ${stats.workflows.converted}`);
|
|
325
|
+
console.log(` Errors: ${stats.workflows.errors}`);
|
|
326
|
+
console.log();
|
|
327
|
+
|
|
328
|
+
const totalConverted =
|
|
329
|
+
stats.agents.converted + stats.workflows.converted;
|
|
330
|
+
const totalErrors = stats.agents.errors + stats.workflows.errors;
|
|
331
|
+
|
|
332
|
+
if (totalErrors > 0) {
|
|
333
|
+
console.log('ā ļø Errors encountered:');
|
|
334
|
+
for (const err of stats.errors) {
|
|
335
|
+
console.log(` - ${err.type}: ${err.path}`);
|
|
336
|
+
console.log(` ${err.error}`);
|
|
337
|
+
}
|
|
338
|
+
console.log();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
console.log(`ā
Successfully converted ${totalConverted} skills`);
|
|
342
|
+
console.log(
|
|
343
|
+
`š Output directory: ${path.resolve(process.cwd(), config.outputDir)}`,
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
// Show configuration info
|
|
347
|
+
if (config.outputDir !== './skills') {
|
|
348
|
+
console.log('\nš” Note: Output directory is not the default (./skills)');
|
|
349
|
+
console.log(' This directory is not version controlled.');
|
|
350
|
+
console.log(' To use default settings, run without --output-dir flag.');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (config.enhancements.identityCharLimit !== null) {
|
|
354
|
+
console.log(`\nš” Note: Identity character limit is set to ${config.enhancements.identityCharLimit}`);
|
|
355
|
+
console.log(' Default behavior (no limit) is recommended for better content.');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Print per-module breakdown
|
|
359
|
+
if (totalConverted > 0) {
|
|
360
|
+
console.log('\nš¦ Per-module breakdown:');
|
|
361
|
+
|
|
362
|
+
// Count by module from output structure
|
|
363
|
+
const outputDir = path.resolve(process.cwd(), config.outputDir);
|
|
364
|
+
if (await fs.pathExists(outputDir)) {
|
|
365
|
+
const modules = await fs.readdir(outputDir);
|
|
366
|
+
for (const module of modules) {
|
|
367
|
+
const modulePath = path.join(outputDir, module);
|
|
368
|
+
if ((await fs.stat(modulePath)).isDirectory()) {
|
|
369
|
+
const skills = await fs.readdir(modulePath);
|
|
370
|
+
console.log(` ${module}: ${skills.length} skills`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Run the conversion
|
|
378
|
+
main().catch((error) => {
|
|
379
|
+
console.error('Unhandled error:', error);
|
|
380
|
+
process.exit(1);
|
|
381
|
+
});
|