bmm-opencode 1.4.2 → 1.4.3
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/.opencode/agents/bmad-bmad-master.md +11 -0
- package/.opencode/agents/bmm-analyst.md +10 -32
- package/.opencode/agents/bmm-architect.md +6 -34
- package/.opencode/agents/bmm-dev.md +6 -32
- package/.opencode/agents/bmm-pm.md +10 -41
- package/.opencode/agents/bmm-qa.md +5 -31
- package/.opencode/agents/bmm-quick-flow-solo-dev.md +7 -32
- package/.opencode/agents/bmm-sm.md +8 -32
- package/.opencode/agents/bmm-tech-writer.md +12 -0
- package/.opencode/agents/bmm-ux-designer.md +5 -37
- package/.opencode/commands/bmad-bmm-check-implementation-readiness.md +7 -0
- package/.opencode/commands/bmad-bmm-code-review.md +7 -0
- package/.opencode/commands/bmad-bmm-correct-course.md +7 -0
- package/.opencode/commands/bmad-bmm-create-architecture.md +7 -0
- package/.opencode/commands/bmad-bmm-create-epics-and-stories.md +7 -0
- package/.opencode/commands/bmad-bmm-create-prd.md +7 -0
- package/.opencode/commands/bmad-bmm-create-product-brief.md +7 -0
- package/.opencode/commands/bmad-bmm-create-story.md +7 -0
- package/.opencode/commands/bmad-bmm-create-ux-design.md +7 -0
- package/.opencode/commands/bmad-bmm-dev-story.md +7 -0
- package/.opencode/commands/bmad-bmm-document-project.md +7 -0
- package/.opencode/commands/bmad-bmm-domain-research.md +7 -0
- package/.opencode/commands/bmad-bmm-edit-prd.md +7 -0
- package/.opencode/commands/bmad-bmm-generate-project-context.md +5 -0
- package/.opencode/commands/bmad-bmm-market-research.md +7 -0
- package/.opencode/commands/bmad-bmm-quick-dev.md +7 -0
- package/.opencode/commands/bmad-bmm-quick-spec.md +7 -0
- package/.opencode/commands/bmad-bmm-retrospective.md +7 -0
- package/.opencode/commands/bmad-bmm-sprint-planning.md +7 -0
- package/.opencode/commands/bmad-bmm-sprint-status.md +5 -0
- package/.opencode/commands/bmad-bmm-technical-research.md +7 -0
- package/.opencode/commands/bmad-bmm-validate-prd.md +7 -0
- package/.opencode/commands/bmad-brainstorming.md +7 -0
- package/.opencode/commands/bmad-editorial-review-prose.md +5 -0
- package/.opencode/commands/bmad-editorial-review-structure.md +5 -0
- package/.opencode/commands/bmad-help.md +5 -0
- package/.opencode/commands/bmad-index-docs.md +5 -0
- package/.opencode/commands/bmad-review-adversarial-general.md +5 -0
- package/.opencode/commands/bmad-shard-doc.md +5 -0
- package/.opencode/skills/bmad-bmad-master/SKILL.md +56 -0
- package/.opencode/skills/bmad-bmm-analyst/SKILL.md +65 -38
- package/.opencode/skills/bmad-bmm-architect/SKILL.md +49 -38
- package/.opencode/skills/bmad-bmm-check-implementation-readiness/SKILL.md +1092 -24
- package/.opencode/skills/bmad-bmm-code-review/SKILL.md +45 -13
- package/.opencode/skills/bmad-bmm-correct-course/SKILL.md +56 -94
- package/.opencode/skills/bmad-bmm-create-architecture/SKILL.md +2391 -27
- package/.opencode/skills/bmad-bmm-create-epics-and-stories/SKILL.md +927 -23
- package/.opencode/skills/bmad-bmm-create-prd/SKILL.md +9 -26
- package/.opencode/skills/bmad-bmm-create-product-brief/SKILL.md +1358 -22
- package/.opencode/skills/bmad-bmm-create-story/SKILL.md +61 -24
- package/.opencode/skills/bmad-bmm-create-ux-design/SKILL.md +3275 -26
- package/.opencode/skills/bmad-bmm-dev/SKILL.md +57 -43
- package/.opencode/skills/bmad-bmm-dev-story/SKILL.md +20 -13
- package/.opencode/skills/bmad-bmm-document-project/SKILL.md +22 -81
- package/.opencode/skills/bmad-bmm-domain-research/SKILL.md +53 -37
- package/.opencode/skills/bmad-bmm-edit-prd/SKILL.md +10 -27
- package/.opencode/skills/bmad-bmm-generate-project-context/SKILL.md +797 -28
- package/.opencode/skills/bmad-bmm-market-research/SKILL.md +53 -37
- package/.opencode/skills/bmad-bmm-pm/SKILL.md +60 -39
- package/.opencode/skills/bmad-bmm-qa/SKILL.md +77 -35
- package/.opencode/skills/bmad-bmm-qa-automate/SKILL.md +47 -129
- package/.opencode/skills/bmad-bmm-quick-dev/SKILL.md +802 -30
- package/.opencode/skills/bmad-bmm-quick-flow-solo-dev/SKILL.md +57 -36
- package/.opencode/skills/bmad-bmm-quick-spec/SKILL.md +684 -27
- package/.opencode/skills/bmad-bmm-retrospective/SKILL.md +55 -200
- package/.opencode/skills/bmad-bmm-sm/SKILL.md +57 -36
- package/.opencode/skills/bmad-bmm-sprint-planning/SKILL.md +51 -52
- package/.opencode/skills/bmad-bmm-sprint-status/SKILL.md +30 -99
- package/.opencode/skills/bmad-bmm-tech-writer/SKILL.md +70 -0
- package/.opencode/skills/bmad-bmm-technical-research/SKILL.md +53 -37
- package/.opencode/skills/bmad-bmm-ux-designer/SKILL.md +48 -37
- package/.opencode/skills/bmad-bmm-validate-prd/SKILL.md +10 -27
- package/.opencode/skills/bmad-brainstorming/SKILL.md +2048 -0
- package/.opencode/skills/bmad-editorial-review-prose/SKILL.md +107 -0
- package/.opencode/skills/bmad-editorial-review-structure/SKILL.md +214 -0
- package/.opencode/skills/bmad-help/SKILL.md +82 -0
- package/.opencode/skills/bmad-index-docs/SKILL.md +70 -0
- package/.opencode/skills/bmad-party-mode/SKILL.md +682 -0
- package/.opencode/skills/bmad-review-adversarial-general/SKILL.md +53 -0
- package/.opencode/skills/bmad-shard-doc/SKILL.md +113 -0
- package/README.md +28 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +350 -47
- package/package.json +4 -3
- package/.opencode/agents/bmm-tech-writer-tech-writer.md +0 -44
- package/.opencode/agents/cis-brainstorming-coach.md +0 -38
- package/.opencode/agents/cis-creative-problem-solver.md +0 -38
- package/.opencode/agents/cis-design-thinking-coach.md +0 -38
- package/.opencode/agents/cis-innovation-strategist.md +0 -38
- package/.opencode/agents/cis-presentation-master.md +0 -54
- package/.opencode/agents/cis-storyteller-storyteller.md +0 -38
- package/.opencode/agents/core-bmad-master.md +0 -39
- package/.opencode/agents/gen-subagent.md +0 -311
- package/.opencode/agents/party-mode.md +0 -812
- package/.opencode/agents/tea-tea.md +0 -48
- package/.opencode/skills/bmad-bmm-dev-team-mode/SKILL.md +0 -300
- package/.opencode/skills/bmad-bmm-tech-writer-tech-writer/SKILL.md +0 -51
- package/.opencode/skills/bmad-cis-brainstorming-coach/SKILL.md +0 -46
- package/.opencode/skills/bmad-cis-creative-problem-solver/SKILL.md +0 -46
- package/.opencode/skills/bmad-cis-design-thinking/SKILL.md +0 -156
- package/.opencode/skills/bmad-cis-design-thinking-coach/SKILL.md +0 -46
- package/.opencode/skills/bmad-cis-innovation-strategist/SKILL.md +0 -46
- package/.opencode/skills/bmad-cis-innovation-strategy/SKILL.md +0 -238
- package/.opencode/skills/bmad-cis-presentation-master/SKILL.md +0 -52
- package/.opencode/skills/bmad-cis-problem-solving/SKILL.md +0 -212
- package/.opencode/skills/bmad-cis-storyteller-storyteller/SKILL.md +0 -48
- package/.opencode/skills/bmad-cis-storytelling/SKILL.md +0 -290
- package/.opencode/skills/bmad-core-bmad-master/SKILL.md +0 -48
- package/.opencode/skills/bmad-core-brainstorming/SKILL.md +0 -74
- package/.opencode/skills/bmad-core-party-mode/SKILL.md +0 -241
- package/.opencode/skills/bmad-core-task-editorial-review-prose/SKILL.md +0 -74
- package/.opencode/skills/bmad-core-task-editorial-review-structure/SKILL.md +0 -151
- package/.opencode/skills/bmad-core-task-help/SKILL.md +0 -100
- package/.opencode/skills/bmad-core-task-index-docs/SKILL.md +0 -46
- package/.opencode/skills/bmad-core-task-review-adversarial-general/SKILL.md +0 -36
- package/.opencode/skills/bmad-core-task-shard-doc/SKILL.md +0 -80
- package/.opencode/skills/bmad-tea-tea/SKILL.md +0 -57
- package/.opencode/skills/bmad-tea-teach-me-testing/SKILL.md +0 -106
- package/.opencode/skills/bmad-tea-testarch-atdd/SKILL.md +0 -62
- package/.opencode/skills/bmad-tea-testarch-automate/SKILL.md +0 -67
- package/.opencode/skills/bmad-tea-testarch-ci/SKILL.md +0 -62
- package/.opencode/skills/bmad-tea-testarch-framework/SKILL.md +0 -62
- package/.opencode/skills/bmad-tea-testarch-nfr/SKILL.md +0 -60
- package/.opencode/skills/bmad-tea-testarch-test-design/SKILL.md +0 -76
- package/.opencode/skills/bmad-tea-testarch-test-review/SKILL.md +0 -60
- package/.opencode/skills/bmad-tea-testarch-trace/SKILL.md +0 -60
package/dist/index.js
CHANGED
|
@@ -1,43 +1,13 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
2
|
-
import { readFileSync, readdirSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
2
|
+
import { readFileSync, readdirSync, existsSync, mkdirSync, writeFileSync, statSync } from "fs";
|
|
3
3
|
import { join, dirname } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { homedir } from "os";
|
|
6
|
-
import { convert } from "bmad-opencode-converter";
|
|
7
6
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
7
|
const packageRoot = join(__dirname, "..");
|
|
9
8
|
const bundledAgentsDir = join(packageRoot, ".opencode", "agents");
|
|
10
9
|
const bundledSkillsDir = join(packageRoot, ".opencode", "skills");
|
|
11
|
-
const
|
|
12
|
-
let conversionCache = null;
|
|
13
|
-
const CACHE_TTL_MS = 60000;
|
|
14
|
-
function hasBmadSource() {
|
|
15
|
-
return existsSync(join(bmadSourceDir, "_config", "agent-manifest.csv"));
|
|
16
|
-
}
|
|
17
|
-
async function getConvertedResources() {
|
|
18
|
-
if (conversionCache && Date.now() - conversionCache.timestamp < CACHE_TTL_MS) {
|
|
19
|
-
return { agents: conversionCache.agents, skills: conversionCache.skills };
|
|
20
|
-
}
|
|
21
|
-
if (hasBmadSource()) {
|
|
22
|
-
try {
|
|
23
|
-
const result = await convert({
|
|
24
|
-
sourceDir: bmadSourceDir,
|
|
25
|
-
outputDir: "",
|
|
26
|
-
verbose: false,
|
|
27
|
-
});
|
|
28
|
-
conversionCache = {
|
|
29
|
-
agents: result.agents,
|
|
30
|
-
skills: result.skills,
|
|
31
|
-
timestamp: Date.now(),
|
|
32
|
-
};
|
|
33
|
-
return { agents: result.agents, skills: result.skills };
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
console.error("Failed to convert from BMAD source:", err);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return readBundledFiles();
|
|
40
|
-
}
|
|
10
|
+
const bundledWorkflowsDir = join(packageRoot, "_bmad", "bmm", "workflows");
|
|
41
11
|
function readBundledFiles() {
|
|
42
12
|
const agents = [];
|
|
43
13
|
const skills = [];
|
|
@@ -53,6 +23,8 @@ function readBundledFiles() {
|
|
|
53
23
|
mode: frontmatter.mode,
|
|
54
24
|
model: frontmatter.model || undefined,
|
|
55
25
|
tools: frontmatter.tools,
|
|
26
|
+
workflows: frontmatter.workflows || extractWorkflowsFromBody(body),
|
|
27
|
+
permittedSkills: extractPermittedSkills(content),
|
|
56
28
|
},
|
|
57
29
|
prompt: body,
|
|
58
30
|
});
|
|
@@ -127,6 +99,129 @@ function parseFrontmatter(content) {
|
|
|
127
99
|
}
|
|
128
100
|
return { frontmatter, body: match[2] };
|
|
129
101
|
}
|
|
102
|
+
function extractPermittedSkills(content) {
|
|
103
|
+
const skills = [];
|
|
104
|
+
const lines = content.split('\n');
|
|
105
|
+
let inPermission = false;
|
|
106
|
+
let inSkill = false;
|
|
107
|
+
for (const line of lines) {
|
|
108
|
+
if (line === '---' && skills.length > 0)
|
|
109
|
+
break;
|
|
110
|
+
if (line.match(/^permission:\s*$/)) {
|
|
111
|
+
inPermission = true;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (inPermission && line.match(/^\s+skill:\s*$/)) {
|
|
115
|
+
inSkill = true;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (inSkill) {
|
|
119
|
+
const skillMatch = line.match(/^\s+"([^"]+)":\s*allow/);
|
|
120
|
+
if (skillMatch) {
|
|
121
|
+
skills.push(skillMatch[1]);
|
|
122
|
+
}
|
|
123
|
+
else if (line.match(/^\s+\w+:/) && !line.match(/^\s+"/)) {
|
|
124
|
+
inSkill = false;
|
|
125
|
+
inPermission = false;
|
|
126
|
+
}
|
|
127
|
+
else if (line.match(/^\w/)) {
|
|
128
|
+
inSkill = false;
|
|
129
|
+
inPermission = false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return skills;
|
|
134
|
+
}
|
|
135
|
+
function extractWorkflowsFromBody(body) {
|
|
136
|
+
const workflows = [];
|
|
137
|
+
const lines = body.split('\n');
|
|
138
|
+
let inWorkflowSection = false;
|
|
139
|
+
for (const line of lines) {
|
|
140
|
+
if (line.includes('You have access to the following workflows')) {
|
|
141
|
+
inWorkflowSection = true;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (inWorkflowSection) {
|
|
145
|
+
const match = line.match(/^-\s+([\w-]+)/);
|
|
146
|
+
if (match) {
|
|
147
|
+
workflows.push(match[1]);
|
|
148
|
+
}
|
|
149
|
+
else if (line.trim() && !line.startsWith('-')) {
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return workflows;
|
|
155
|
+
}
|
|
156
|
+
function readWorkflows(workflowsDir) {
|
|
157
|
+
const workflows = [];
|
|
158
|
+
const searchDir = workflowsDir || bundledWorkflowsDir;
|
|
159
|
+
if (!existsSync(searchDir)) {
|
|
160
|
+
return workflows;
|
|
161
|
+
}
|
|
162
|
+
function scanDirectory(dir) {
|
|
163
|
+
for (const item of readdirSync(dir)) {
|
|
164
|
+
const fullPath = join(dir, item);
|
|
165
|
+
const stat = statSync(fullPath);
|
|
166
|
+
if (stat.isDirectory()) {
|
|
167
|
+
scanDirectory(fullPath);
|
|
168
|
+
}
|
|
169
|
+
else if (item === 'workflow.yaml') {
|
|
170
|
+
try {
|
|
171
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
172
|
+
const lines = content.split('\n');
|
|
173
|
+
let name = '';
|
|
174
|
+
let description = '';
|
|
175
|
+
let author = '';
|
|
176
|
+
for (const line of lines) {
|
|
177
|
+
const nameMatch = line.match(/^name:\s*["']?([^"'\n]+)["']?/);
|
|
178
|
+
const descMatch = line.match(/^description:\s*["']?([^"'\n]+)["']?/);
|
|
179
|
+
const authorMatch = line.match(/^author:\s*["']?([^"'\n]+)["']?/);
|
|
180
|
+
if (nameMatch)
|
|
181
|
+
name = nameMatch[1];
|
|
182
|
+
if (descMatch)
|
|
183
|
+
description = descMatch[1];
|
|
184
|
+
if (authorMatch)
|
|
185
|
+
author = authorMatch[1];
|
|
186
|
+
}
|
|
187
|
+
if (name) {
|
|
188
|
+
workflows.push({
|
|
189
|
+
name,
|
|
190
|
+
description,
|
|
191
|
+
path: fullPath,
|
|
192
|
+
author,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
// Skip invalid YAML files
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
scanDirectory(searchDir);
|
|
203
|
+
return workflows;
|
|
204
|
+
}
|
|
205
|
+
function getAgentWorkflowMappings() {
|
|
206
|
+
const { agents } = readBundledFiles();
|
|
207
|
+
const workflows = readWorkflows();
|
|
208
|
+
return agents.map(agent => {
|
|
209
|
+
const agentName = agent.filename.replace('.md', '');
|
|
210
|
+
const agentWorkflows = agent.frontmatter.workflows || [];
|
|
211
|
+
const workflowDetails = agentWorkflows
|
|
212
|
+
.map(workflowName => {
|
|
213
|
+
const workflow = workflows.find(w => w.name === workflowName ||
|
|
214
|
+
w.name.endsWith(workflowName) ||
|
|
215
|
+
workflowName.includes(w.name));
|
|
216
|
+
return workflow ? `${workflowName}${workflow.description ? ` - ${workflow.description}` : ''}` : workflowName;
|
|
217
|
+
});
|
|
218
|
+
return {
|
|
219
|
+
agent: agentName,
|
|
220
|
+
workflows: agentWorkflows,
|
|
221
|
+
description: agent.frontmatter.description
|
|
222
|
+
};
|
|
223
|
+
}).filter(mapping => mapping.workflows.length > 0);
|
|
224
|
+
}
|
|
130
225
|
function writeAgentFile(targetDir, agent) {
|
|
131
226
|
const agentDir = join(targetDir, "agents");
|
|
132
227
|
mkdirSync(agentDir, { recursive: true });
|
|
@@ -144,7 +239,15 @@ function writeAgentFile(targetDir, agent) {
|
|
|
144
239
|
}
|
|
145
240
|
}
|
|
146
241
|
frontmatterLines.push("---");
|
|
147
|
-
|
|
242
|
+
let promptContent = agent.prompt;
|
|
243
|
+
if (agent.frontmatter.workflows && agent.frontmatter.workflows.length > 0) {
|
|
244
|
+
const workflows = readWorkflows();
|
|
245
|
+
const workflowSection = buildWorkflowSection(agent.frontmatter.workflows, workflows);
|
|
246
|
+
if (!promptContent.includes("You have access to the following workflows")) {
|
|
247
|
+
promptContent = promptContent.trim() + "\n\n" + workflowSection;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const content = frontmatterLines.join("\n") + "\n\n" + promptContent;
|
|
148
251
|
writeFileSync(join(agentDir, agent.filename), content);
|
|
149
252
|
}
|
|
150
253
|
function writeSkillFile(targetDir, skill) {
|
|
@@ -167,7 +270,7 @@ function writeSkillFile(targetDir, skill) {
|
|
|
167
270
|
const content = frontmatterLines.join("\n") + "\n\n" + skill.content;
|
|
168
271
|
writeFileSync(join(skillDir, "SKILL.md"), content);
|
|
169
272
|
}
|
|
170
|
-
function formatAgentContent(agent) {
|
|
273
|
+
function formatAgentContent(agent, injectWorkflows = true) {
|
|
171
274
|
const frontmatterLines = ["---"];
|
|
172
275
|
frontmatterLines.push(`description: ${JSON.stringify(agent.frontmatter.description)}`);
|
|
173
276
|
if (agent.frontmatter.mode)
|
|
@@ -181,8 +284,112 @@ function formatAgentContent(agent) {
|
|
|
181
284
|
frontmatterLines.push(` ${toolName}: ${enabled}`);
|
|
182
285
|
}
|
|
183
286
|
}
|
|
287
|
+
if (agent.frontmatter.workflows && agent.frontmatter.workflows.length > 0) {
|
|
288
|
+
frontmatterLines.push("workflows:");
|
|
289
|
+
for (const workflow of agent.frontmatter.workflows) {
|
|
290
|
+
frontmatterLines.push(` - ${workflow}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
frontmatterLines.push("---");
|
|
294
|
+
let promptContent = agent.prompt;
|
|
295
|
+
if (injectWorkflows && agent.frontmatter.workflows && agent.frontmatter.workflows.length > 0) {
|
|
296
|
+
const workflows = readWorkflows();
|
|
297
|
+
const workflowSection = buildWorkflowSection(agent.frontmatter.workflows, workflows);
|
|
298
|
+
if (!promptContent.includes("You have access to the following workflows")) {
|
|
299
|
+
promptContent = promptContent.trim() + "\n\n" + workflowSection;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return frontmatterLines.join("\n") + "\n\n" + promptContent;
|
|
303
|
+
}
|
|
304
|
+
function buildWorkflowSection(agentWorkflows, allWorkflows) {
|
|
305
|
+
const lines = ["You have access to the following workflows and tasks:"];
|
|
306
|
+
for (const workflowName of agentWorkflows) {
|
|
307
|
+
const workflow = allWorkflows.find(w => w.name === workflowName ||
|
|
308
|
+
w.name.endsWith(workflowName) ||
|
|
309
|
+
workflowName.includes(w.name));
|
|
310
|
+
if (workflow && workflow.description) {
|
|
311
|
+
lines.push(`- **${workflowName}**: ${workflow.description}`);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
lines.push(`- ${workflowName}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return lines.join("\n");
|
|
318
|
+
}
|
|
319
|
+
function getCommandMappings() {
|
|
320
|
+
const { agents, skills } = readBundledFiles();
|
|
321
|
+
const mappings = [];
|
|
322
|
+
const mappedSkills = new Set();
|
|
323
|
+
const agentBaseSkills = new Set();
|
|
324
|
+
for (const a of agents) {
|
|
325
|
+
const name = a.filename.replace('.md', '');
|
|
326
|
+
agentBaseSkills.add(`bmad-${name}`);
|
|
327
|
+
agentBaseSkills.add(name);
|
|
328
|
+
}
|
|
329
|
+
const skipSkills = new Set(['bmad-party-mode']);
|
|
330
|
+
for (const agent of agents) {
|
|
331
|
+
const agentName = agent.filename.replace('.md', '');
|
|
332
|
+
const permitted = agent.frontmatter.permittedSkills || [];
|
|
333
|
+
const workflows = agent.frontmatter.workflows || [];
|
|
334
|
+
const allSkillNames = [...new Set([...permitted, ...workflows])];
|
|
335
|
+
for (const skillName of allSkillNames) {
|
|
336
|
+
if (agentBaseSkills.has(skillName) || skipSkills.has(skillName))
|
|
337
|
+
continue;
|
|
338
|
+
if (mappedSkills.has(skillName))
|
|
339
|
+
continue;
|
|
340
|
+
const skill = skills.find(s => s.name === skillName || s.folder === skillName);
|
|
341
|
+
if (!skill)
|
|
342
|
+
continue;
|
|
343
|
+
mappings.push({
|
|
344
|
+
commandName: skillName,
|
|
345
|
+
skillName: skillName,
|
|
346
|
+
agentName: agentName,
|
|
347
|
+
description: skill.frontmatter.description,
|
|
348
|
+
});
|
|
349
|
+
mappedSkills.add(skillName);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const standaloneSkills = [
|
|
353
|
+
'bmad-help',
|
|
354
|
+
'bmad-index-docs',
|
|
355
|
+
'bmad-shard-doc',
|
|
356
|
+
'bmad-editorial-review-prose',
|
|
357
|
+
'bmad-editorial-review-structure',
|
|
358
|
+
'bmad-review-adversarial-general',
|
|
359
|
+
'bmad-bmm-generate-project-context',
|
|
360
|
+
'bmad-bmm-sprint-status',
|
|
361
|
+
];
|
|
362
|
+
for (const skillName of standaloneSkills) {
|
|
363
|
+
if (mappedSkills.has(skillName))
|
|
364
|
+
continue;
|
|
365
|
+
const skill = skills.find(s => s.name === skillName || s.folder === skillName);
|
|
366
|
+
if (!skill)
|
|
367
|
+
continue;
|
|
368
|
+
mappings.push({
|
|
369
|
+
commandName: skillName,
|
|
370
|
+
skillName: skillName,
|
|
371
|
+
agentName: '',
|
|
372
|
+
description: skill.frontmatter.description,
|
|
373
|
+
});
|
|
374
|
+
mappedSkills.add(skillName);
|
|
375
|
+
}
|
|
376
|
+
return mappings;
|
|
377
|
+
}
|
|
378
|
+
function writeCommandFile(targetDir, mapping) {
|
|
379
|
+
const commandsDir = join(targetDir, "commands");
|
|
380
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
381
|
+
const frontmatterLines = ["---"];
|
|
382
|
+
frontmatterLines.push(`description: ${JSON.stringify(mapping.description)}`);
|
|
383
|
+
if (mapping.agentName) {
|
|
384
|
+
frontmatterLines.push(`agent: ${mapping.agentName}`);
|
|
385
|
+
frontmatterLines.push(`subtask: true`);
|
|
386
|
+
}
|
|
184
387
|
frontmatterLines.push("---");
|
|
185
|
-
|
|
388
|
+
const prompt = mapping.agentName
|
|
389
|
+
? `Load the skill "${mapping.skillName}" and follow its instructions. $ARGUMENTS`
|
|
390
|
+
: `Load the skill "${mapping.skillName}" and follow its instructions. $ARGUMENTS`;
|
|
391
|
+
const content = frontmatterLines.join("\n") + "\n\n" + prompt + "\n";
|
|
392
|
+
writeFileSync(join(commandsDir, `${mapping.commandName}.md`), content);
|
|
186
393
|
}
|
|
187
394
|
function formatSkillContent(skill) {
|
|
188
395
|
const frontmatterLines = ["---"];
|
|
@@ -205,13 +412,14 @@ export const BMMPlugin = async () => {
|
|
|
205
412
|
return {
|
|
206
413
|
tool: {
|
|
207
414
|
bmm_list: tool({
|
|
208
|
-
description: "List all available BMAD-METHOD agents and
|
|
415
|
+
description: "List all available BMAD-METHOD agents, skills, and commands from bmm-opencode",
|
|
209
416
|
args: {},
|
|
210
417
|
async execute() {
|
|
211
|
-
const { agents, skills } =
|
|
212
|
-
const
|
|
418
|
+
const { agents, skills } = readBundledFiles();
|
|
419
|
+
const commands = getCommandMappings();
|
|
420
|
+
const agentCommands = commands.filter(c => c.agentName);
|
|
421
|
+
const standaloneCommands = commands.filter(c => !c.agentName);
|
|
213
422
|
return `# BMM-OpenCode Resources
|
|
214
|
-
Source: ${source}
|
|
215
423
|
|
|
216
424
|
## Agents (${agents.length})
|
|
217
425
|
${agents.map((a) => `- ${a.filename.replace(".md", "")}: ${a.frontmatter.description}`).join("\n")}
|
|
@@ -219,11 +427,20 @@ ${agents.map((a) => `- ${a.filename.replace(".md", "")}: ${a.frontmatter.descrip
|
|
|
219
427
|
## Skills (${skills.length})
|
|
220
428
|
${skills.map((s) => `- ${s.name}: ${s.frontmatter.description}`).join("\n")}
|
|
221
429
|
|
|
430
|
+
## Commands (${commands.length})
|
|
431
|
+
After \`bmm_install\`, type \`/\` in the TUI to see these workflow commands:
|
|
432
|
+
|
|
433
|
+
### Agent Workflows (${agentCommands.length})
|
|
434
|
+
${agentCommands.map((c) => `- \`/${c.commandName}\` → @${c.agentName}: ${c.description}`).join("\n")}
|
|
435
|
+
|
|
436
|
+
### Standalone Utilities (${standaloneCommands.length})
|
|
437
|
+
${standaloneCommands.map((c) => `- \`/${c.commandName}\`: ${c.description}`).join("\n")}
|
|
438
|
+
|
|
222
439
|
## Usage
|
|
223
440
|
- Use \`bmm_agent\` tool to get agent definition
|
|
224
441
|
- Use \`bmm_skill\` tool to get skill instructions
|
|
225
|
-
- Use \`bmm_install\`
|
|
226
|
-
-
|
|
442
|
+
- Use \`bmm_install\` to install agents, skills, and commands
|
|
443
|
+
- After install, use \`/command-name\` to invoke workflows directly`;
|
|
227
444
|
},
|
|
228
445
|
}),
|
|
229
446
|
bmm_agent: tool({
|
|
@@ -232,14 +449,32 @@ ${skills.map((s) => `- ${s.name}: ${s.frontmatter.description}`).join("\n")}
|
|
|
232
449
|
name: tool.schema.string().describe("Agent name (e.g., bmm-dev, bmm-pm)"),
|
|
233
450
|
},
|
|
234
451
|
async execute(args) {
|
|
235
|
-
const { agents } =
|
|
452
|
+
const { agents } = readBundledFiles();
|
|
236
453
|
const agent = agents.find((a) => a.filename === `${args.name}.md` ||
|
|
237
454
|
a.filename.replace(".md", "") === args.name);
|
|
238
455
|
if (!agent) {
|
|
239
456
|
const available = agents.map((a) => a.filename.replace(".md", "")).join(", ");
|
|
240
457
|
return `Agent "${args.name}" not found.\n\nAvailable agents: ${available}`;
|
|
241
458
|
}
|
|
242
|
-
|
|
459
|
+
let output = formatAgentContent(agent);
|
|
460
|
+
if (agent.frontmatter.workflows && agent.frontmatter.workflows.length > 0) {
|
|
461
|
+
const workflows = readWorkflows();
|
|
462
|
+
output += `\n\n## Available Workflows\n\n`;
|
|
463
|
+
output += `This agent has access to ${agent.frontmatter.workflows.length} workflow(s):\n\n`;
|
|
464
|
+
for (const workflowName of agent.frontmatter.workflows) {
|
|
465
|
+
const workflow = workflows.find(w => w.name === workflowName ||
|
|
466
|
+
w.name.endsWith(workflowName) ||
|
|
467
|
+
workflowName.includes(w.name));
|
|
468
|
+
if (workflow) {
|
|
469
|
+
output += `- **${workflowName}**: ${workflow.description}\n`;
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
output += `- ${workflowName}\n`;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
output += `\n**Tip**: Use \`bmm_suggest_workflows({ agent: "${args.name}" })\` for detailed workflow information.\n`;
|
|
476
|
+
}
|
|
477
|
+
return output;
|
|
243
478
|
},
|
|
244
479
|
}),
|
|
245
480
|
bmm_skill: tool({
|
|
@@ -248,7 +483,7 @@ ${skills.map((s) => `- ${s.name}: ${s.frontmatter.description}`).join("\n")}
|
|
|
248
483
|
name: tool.schema.string().describe("Skill name (e.g., bmad-bmm-create-prd)"),
|
|
249
484
|
},
|
|
250
485
|
async execute(args) {
|
|
251
|
-
const { skills } =
|
|
486
|
+
const { skills } = readBundledFiles();
|
|
252
487
|
const skill = skills.find((s) => s.name === args.name || s.folder === args.name);
|
|
253
488
|
if (!skill) {
|
|
254
489
|
const available = skills.map((s) => s.name).join(", ");
|
|
@@ -330,7 +565,7 @@ To avoid confusion, consider removing one installation.`;
|
|
|
330
565
|
|
|
331
566
|
Use \`force=true\` to overwrite, or remove existing files first.`;
|
|
332
567
|
}
|
|
333
|
-
const { agents, skills } =
|
|
568
|
+
const { agents, skills } = readBundledFiles();
|
|
334
569
|
let agentsCopied = 0;
|
|
335
570
|
for (const agent of agents) {
|
|
336
571
|
writeAgentFile(targetBase, agent);
|
|
@@ -341,23 +576,91 @@ Use \`force=true\` to overwrite, or remove existing files first.`;
|
|
|
341
576
|
writeSkillFile(targetBase, skill);
|
|
342
577
|
skillsCopied++;
|
|
343
578
|
}
|
|
579
|
+
const commandMappings = getCommandMappings();
|
|
580
|
+
let commandsCopied = 0;
|
|
581
|
+
for (const mapping of commandMappings) {
|
|
582
|
+
writeCommandFile(targetBase, mapping);
|
|
583
|
+
commandsCopied++;
|
|
584
|
+
}
|
|
344
585
|
const isGlobal = targetBase === globalConfigDir;
|
|
345
586
|
const installType = isGlobal ? "globally" : "to project";
|
|
346
|
-
const source =
|
|
587
|
+
const source = "from bundled files";
|
|
588
|
+
const targetCommands = join(targetBase, "commands");
|
|
347
589
|
const autoNote = autoDetected ? `\n\nNote: Auto-detected existing global installation at ${globalConfigDir}` : "";
|
|
348
590
|
return `Successfully installed BMM-OpenCode ${installType} (${targetBase}):
|
|
349
591
|
- ${agentsCopied} agents copied to ${targetAgents}
|
|
350
592
|
- ${skillsCopied} skills copied to ${targetSkills}
|
|
593
|
+
- ${commandsCopied} commands copied to ${targetCommands}
|
|
351
594
|
|
|
352
595
|
Source: ${source}${autoNote}
|
|
353
596
|
|
|
354
|
-
|
|
597
|
+
After restart, type \`/\` to see all available workflow commands (e.g. \`/bmad-bmm-create-prd\`, \`/bmad-bmm-dev-story\`).
|
|
598
|
+
Each command auto-routes to the correct agent and loads the workflow skill.
|
|
599
|
+
|
|
600
|
+
Restart OpenCode to use the new agents, skills, and commands.`;
|
|
355
601
|
}
|
|
356
602
|
catch (error) {
|
|
357
603
|
return `Installation failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
358
604
|
}
|
|
359
605
|
},
|
|
360
606
|
}),
|
|
607
|
+
bmm_agent_workflows: tool({
|
|
608
|
+
description: "List all BMM agents with their available workflows for @agent autocomplete suggestions",
|
|
609
|
+
args: {},
|
|
610
|
+
async execute() {
|
|
611
|
+
const mappings = getAgentWorkflowMappings();
|
|
612
|
+
if (mappings.length === 0) {
|
|
613
|
+
return "No agent-workflow mappings found. Make sure BMM agents are properly configured.";
|
|
614
|
+
}
|
|
615
|
+
let output = "# BMM Agent Workflow Mappings\n\n";
|
|
616
|
+
output += "Use this information to suggest workflows when users @mention an agent.\n\n";
|
|
617
|
+
for (const mapping of mappings) {
|
|
618
|
+
output += `## @${mapping.agent}\n`;
|
|
619
|
+
output += `**Role**: ${mapping.description}\n\n`;
|
|
620
|
+
output += `**Available Workflows**:\n`;
|
|
621
|
+
for (const workflow of mapping.workflows) {
|
|
622
|
+
output += `- ${workflow}\n`;
|
|
623
|
+
}
|
|
624
|
+
output += '\n';
|
|
625
|
+
}
|
|
626
|
+
return output;
|
|
627
|
+
},
|
|
628
|
+
}),
|
|
629
|
+
bmm_suggest_workflows: tool({
|
|
630
|
+
description: "Get workflow suggestions for a specific BMM agent (useful for @agent autocomplete)",
|
|
631
|
+
args: {
|
|
632
|
+
agent: tool.schema.string().describe("Agent name (e.g., bmm-dev, bmm-pm, bmm-qa)"),
|
|
633
|
+
},
|
|
634
|
+
async execute(args) {
|
|
635
|
+
const mappings = getAgentWorkflowMappings();
|
|
636
|
+
const agentName = args.agent.replace(/^@/, '').replace(/\.md$/, '');
|
|
637
|
+
const mapping = mappings.find(m => m.agent === agentName ||
|
|
638
|
+
m.agent === `bmm-${agentName}` ||
|
|
639
|
+
m.agent === `bmad-${agentName}`);
|
|
640
|
+
if (!mapping) {
|
|
641
|
+
const available = mappings.map(m => m.agent).join(", ");
|
|
642
|
+
return `Agent "${args.agent}" not found or has no workflows configured.\n\nAvailable agents with workflows: ${available}`;
|
|
643
|
+
}
|
|
644
|
+
let output = `# Workflows for @${mapping.agent}\n\n`;
|
|
645
|
+
output += `**Role**: ${mapping.description}\n\n`;
|
|
646
|
+
output += `**Available Workflows** (${mapping.workflows.length}):\n`;
|
|
647
|
+
const workflows = readWorkflows();
|
|
648
|
+
for (const workflowName of mapping.workflows) {
|
|
649
|
+
const workflow = workflows.find(w => w.name === workflowName ||
|
|
650
|
+
w.name.endsWith(workflowName) ||
|
|
651
|
+
workflowName.includes(w.name));
|
|
652
|
+
if (workflow) {
|
|
653
|
+
output += `- **${workflowName}**: ${workflow.description}\n`;
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
output += `- ${workflowName}\n`;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
output += `\n## Usage\n`;
|
|
660
|
+
output += `\`\`\`\n@${mapping.agent} [use workflow: ${mapping.workflows[0] || 'workflow-name'}]\n\`\`\`\n`;
|
|
661
|
+
return output;
|
|
662
|
+
},
|
|
663
|
+
}),
|
|
361
664
|
},
|
|
362
665
|
};
|
|
363
666
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bmm-opencode",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
4
4
|
"description": "BMAD-METHOD agents and skills for OpenCode - AI agent framework with 19 specialized agents and 62 workflow skills",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -32,14 +32,15 @@
|
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsc",
|
|
34
34
|
"dev": "tsc --watch",
|
|
35
|
+
"dev:test": "bash scripts/dev-test.sh",
|
|
35
36
|
"test": "node --experimental-vm-modules --test tests/*.test.js",
|
|
36
37
|
"prepublishOnly": "npm run build && npm test"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@opencode-ai/plugin": "^1.1.51"
|
|
40
|
-
"bmad-opencode-converter": "^1.0.0"
|
|
40
|
+
"@opencode-ai/plugin": "^1.1.51"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
+
"bmad-opencode-converter": "^1.3.0",
|
|
43
44
|
"@types/node": "^22.0.0",
|
|
44
45
|
"typescript": "^5.0.0"
|
|
45
46
|
},
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Technical Writer"
|
|
3
|
-
mode: subagent
|
|
4
|
-
model: "google/gemini-2.5-flash"
|
|
5
|
-
tools:
|
|
6
|
-
write: true
|
|
7
|
-
edit: true
|
|
8
|
-
bash: true
|
|
9
|
-
read: true
|
|
10
|
-
glob: true
|
|
11
|
-
grep: true
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
📚 **Technical Writer** - Paige
|
|
15
|
-
|
|
16
|
-
## Role
|
|
17
|
-
Technical Documentation Specialist + Knowledge Curator
|
|
18
|
-
|
|
19
|
-
## Identity
|
|
20
|
-
Experienced technical writer expert in CommonMark, DITA, OpenAPI. Master of clarity - transforms complex concepts into accessible structured documentation.
|
|
21
|
-
|
|
22
|
-
## Communication Style
|
|
23
|
-
Patient educator who explains like teaching a friend. Uses analogies that make complex simple, celebrates clarity when it shines.
|
|
24
|
-
|
|
25
|
-
## Principles
|
|
26
|
-
- Every Technical Document I touch helps someone accomplish a task. Thus I strive for Clarity above all, and every word and phrase serves a purpose without being overly wordy.
|
|
27
|
-
- I believe a picture/diagram is worth 1000s works and will include diagrams over drawn out text.
|
|
28
|
-
- I understand the intended audience or will clarify with the user so I know when to simplify vs when to be detailed.
|
|
29
|
-
- I will always strive to follow `_bmad/_memory/tech
|
|
30
|
-
- writer
|
|
31
|
-
- sidecar/documentation
|
|
32
|
-
- standards.md` best practices.
|
|
33
|
-
|
|
34
|
-
## Rules
|
|
35
|
-
- ALWAYS communicate in {communication_language} UNLESS contradicted by communication_style.
|
|
36
|
-
- Stay in character until exit selected
|
|
37
|
-
- Display Menu items as the item dictates and in the order given.
|
|
38
|
-
- Load files ONLY when executing a user chosen workflow or a command requires it, EXCEPTION: agent activation step 2 config.yaml
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## Model Configuration
|
|
43
|
-
- **Default**: `google/gemini-2.5-flash`
|
|
44
|
-
- **Alternatives**: `anthropic/claude-haiku-3-5-20241022`, `openai/gpt-4o-mini`
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Elite Brainstorming Specialist"
|
|
3
|
-
mode: subagent
|
|
4
|
-
model: "openai/gpt-4o"
|
|
5
|
-
tools:
|
|
6
|
-
write: true
|
|
7
|
-
edit: true
|
|
8
|
-
bash: true
|
|
9
|
-
read: true
|
|
10
|
-
glob: true
|
|
11
|
-
grep: true
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
🧠 **Elite Brainstorming Specialist** - Carson
|
|
15
|
-
|
|
16
|
-
## Role
|
|
17
|
-
Master Brainstorming Facilitator + Innovation Catalyst
|
|
18
|
-
|
|
19
|
-
## Identity
|
|
20
|
-
Elite facilitator with 20+ years leading breakthrough sessions. Expert in creative techniques, group dynamics, and systematic innovation.
|
|
21
|
-
|
|
22
|
-
## Communication Style
|
|
23
|
-
Talks like an enthusiastic improv coach - high energy, builds on ideas with YES AND, celebrates wild thinking
|
|
24
|
-
|
|
25
|
-
## Principles
|
|
26
|
-
- Psychological safety unlocks breakthroughs. Wild ideas today become innovations tomorrow. Humor and play are serious innovation tools.
|
|
27
|
-
|
|
28
|
-
## Rules
|
|
29
|
-
- ALWAYS communicate in {communication_language} UNLESS contradicted by communication_style.
|
|
30
|
-
- Stay in character until exit selected
|
|
31
|
-
- Display Menu items as the item dictates and in the order given.
|
|
32
|
-
- Load files ONLY when executing a user chosen workflow or a command requires it, EXCEPTION: agent activation step 2 config.yaml
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## Model Configuration
|
|
37
|
-
- **Default**: `openai/gpt-4o`
|
|
38
|
-
- **Alternatives**: `anthropic/claude-sonnet-4-20250514`, `google/gemini-2.5-flash`
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Master Problem Solver"
|
|
3
|
-
mode: subagent
|
|
4
|
-
model: "anthropic/claude-opus-4-20250514"
|
|
5
|
-
tools:
|
|
6
|
-
write: true
|
|
7
|
-
edit: true
|
|
8
|
-
bash: true
|
|
9
|
-
read: true
|
|
10
|
-
glob: true
|
|
11
|
-
grep: true
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
🔬 **Master Problem Solver** - Dr. Quinn
|
|
15
|
-
|
|
16
|
-
## Role
|
|
17
|
-
Systematic Problem-Solving Expert + Solutions Architect
|
|
18
|
-
|
|
19
|
-
## Identity
|
|
20
|
-
Renowned problem-solver who cracks impossible challenges. Expert in TRIZ, Theory of Constraints, Systems Thinking. Former aerospace engineer turned puzzle master.
|
|
21
|
-
|
|
22
|
-
## Communication Style
|
|
23
|
-
Speaks like Sherlock Holmes mixed with a playful scientist - deductive, curious, punctuates breakthroughs with AHA moments
|
|
24
|
-
|
|
25
|
-
## Principles
|
|
26
|
-
- Every problem is a system revealing weaknesses. Hunt for root causes relentlessly. The right question beats a fast answer.
|
|
27
|
-
|
|
28
|
-
## Rules
|
|
29
|
-
- ALWAYS communicate in {communication_language} UNLESS contradicted by communication_style.
|
|
30
|
-
- Stay in character until exit selected
|
|
31
|
-
- Display Menu items as the item dictates and in the order given.
|
|
32
|
-
- Load files ONLY when executing a user chosen workflow or a command requires it, EXCEPTION: agent activation step 2 config.yaml
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## Model Configuration
|
|
37
|
-
- **Default**: `anthropic/claude-opus-4-20250514`
|
|
38
|
-
- **Alternatives**: `openai/o3`, `google/gemini-2.5-pro`
|