BMAD-METHOD/.patch/820/pr-820.patch

280 lines
11 KiB
Diff

From f291a525f2308e76cb19f3acff5cb875b13d1ff1 Mon Sep 17 00:00:00 2001
From: cpitt <me@cpitt.com>
Date: Sat, 25 Oct 2025 13:29:39 -0700
Subject: [PATCH] Add Opencode IDE installer
- Added docs/ide-info/opencode.md
- Added tool/cli/installers/lib/ide/opencode.js
- Modified tools/installers/lib/ide/core/detector.js to include
detection for opencode command dir
- Modified tools/cli/platform-codes.yaml to include opencode config
- Modified tools/cli/installers/lib/ide/workflow-command-template.md to
include frontmatter with description as opencode requires this for
commands and adding it to the template by default does not seem to
impact other IDEs
- Modified src/modules/bmm/workflows/workflow-status/workflow.yaml
description so that it properly escapes quotes when interpolated in the
teplate
---
docs/ide-info/opencode.md | 24 ++++
.../workflows/workflow-status/workflow.yaml | 2 +-
tools/cli/README.md | 3 +-
tools/cli/installers/lib/core/detector.js | 5 +-
tools/cli/installers/lib/ide/opencode.js | 134 ++++++++++++++++++
.../lib/ide/workflow-command-template.md | 4 +
tools/platform-codes.yaml | 6 +
7 files changed, 174 insertions(+), 4 deletions(-)
create mode 100644 docs/ide-info/opencode.md
create mode 100644 tools/cli/installers/lib/ide/opencode.js
diff --git a/docs/ide-info/opencode.md b/docs/ide-info/opencode.md
new file mode 100644
index 000000000..eb9b69129
--- /dev/null
+++ b/docs/ide-info/opencode.md
@@ -0,0 +1,24 @@
+# BMAD Method - OpenCode Instructions
+
+## Activating Agents
+
+BMAD agents are installed as OpenCode agents in `.opencode/agent/BMAD/{module_name}` and workflow commands in `.opencode/command/BMAD/{module_name}`.
+
+### How to Use
+
+1. **Switch Agents**: Press **Tab** to cycle through primary agents or select using the `/agents`
+2. **Activate Agent**: Once the Agent is selected say `hello` or any prompt to activate that agent persona
+3. **Execute Commands**: Type `/bmad` to see and execute bmad workflow commands (commands allow for fuzzy matching)
+
+### Examples
+
+```
+/agents - to see a list of agents and switch between them
+/bmad/bmm/workflows/workflow-init - Activate the workflow-init command
+```
+
+### Notes
+
+- Press **Tab** to switch between primary agents (Analyst, Architect, Dev, etc.)
+- Commands are autocompleted when you type `/` and allow for fuzzy matching
+- Workflow commands execute in current agent context, make sure you have the right agent activated before running a command
diff --git a/src/modules/bmm/workflows/workflow-status/workflow.yaml b/src/modules/bmm/workflows/workflow-status/workflow.yaml
index c8098e4a8..ce6308797 100644
--- a/src/modules/bmm/workflows/workflow-status/workflow.yaml
+++ b/src/modules/bmm/workflows/workflow-status/workflow.yaml
@@ -1,6 +1,6 @@
# Workflow Status - Master Router and Status Tracker
name: workflow-status
-description: "Lightweight status checker - answers 'what should I do now?' for any agent. Reads simple key-value status file for instant parsing. Use workflow-init for new projects."
+description: 'Lightweight status checker - answers "what should I do now?" for any agent. Reads simple key-value status file for instant parsing. Use workflow-init for new projects.'
author: "BMad"
# Critical variables from config
diff --git a/tools/cli/README.md b/tools/cli/README.md
index b0ce430d7..fd66209c8 100644
--- a/tools/cli/README.md
+++ b/tools/cli/README.md
@@ -126,7 +126,7 @@ The installer is a multi-stage system that handles agent compilation, IDE integr
### IDE Support
-The installer supports **14 IDE environments** through a base-derived architecture. Each IDE handler extends `BaseIDE` and implements IDE-specific artifact generation.
+The installer supports **15 IDE environments** through a base-derived architecture. Each IDE handler extends `BaseIDE` and implements IDE-specific artifact generation.
**Supported IDEs** (as of v6-alpha):
@@ -134,6 +134,7 @@ The installer supports **14 IDE environments** through a base-derived architectu
| ---------------- | ----------------- | ---------------------- |
| `codex` | Claude Code | `.claude/commands/` |
| `claude-code` | Claude Code (alt) | `.claude/commands/` |
+| `opencode` | OpenCode | `.opencode` |
| `windsurf` | Windsurf | `.windsurf/workflows/` |
| `cursor` | Cursor | `.cursor/rules/` |
| `cline` | Cline | `.clinerules/` |
diff --git a/tools/cli/installers/lib/core/detector.js b/tools/cli/installers/lib/core/detector.js
index c94b81bd6..d3e090af7 100644
--- a/tools/cli/installers/lib/core/detector.js
+++ b/tools/cli/installers/lib/core/detector.js
@@ -211,10 +211,11 @@ class Detector {
// Check inside various IDE command folders for legacy bmad folders
// List of IDE config folders that might have commands directories
- const ideConfigFolders = ['.claude', '.crush', '.continue', '.cursor', '.windsurf', '.cline', '.roo-cline'];
+ const ideConfigFolders = ['.opencode', '.claude', '.crush', '.continue', '.cursor', '.windsurf', '.cline', '.roo-cline'];
for (const ideFolder of ideConfigFolders) {
- const commandsPath = path.join(projectDir, ideFolder, 'commands');
+ const commandsDirName = ideFolder === '.opencode' ? 'command' : 'commands';
+ const commandsPath = path.join(projectDir, ideFolder, commandsDirName);
if (await fs.pathExists(commandsPath)) {
try {
const commandEntries = await fs.readdir(commandsPath, { withFileTypes: true });
diff --git a/tools/cli/installers/lib/ide/opencode.js b/tools/cli/installers/lib/ide/opencode.js
new file mode 100644
index 000000000..1e4d49ac1
--- /dev/null
+++ b/tools/cli/installers/lib/ide/opencode.js
@@ -0,0 +1,134 @@
+const path = require('node:path');
+const fs = require('fs-extra');
+const os = require('node:os');
+const chalk = require('chalk');
+const yaml = require('js-yaml');
+const { BaseIdeSetup } = require('./_base-ide');
+const { WorkflowCommandGenerator } = require('./workflow-command-generator');
+
+const { getAgentsFromBmad } = require('./shared/bmad-artifacts');
+
+/**
+ * OpenCode IDE setup handler
+ */
+class OpenCodeSetup extends BaseIdeSetup {
+ constructor() {
+ super('opencode', 'OpenCode', false);
+ this.configDir = '.opencode';
+ this.commandsDir = 'command';
+ this.agentsDir = 'agent';
+ }
+
+ async setup(projectDir, bmadDir, options = {}) {
+ console.log(chalk.cyan(`Setting up ${this.name}...`));
+
+ const baseDir = path.join(projectDir, this.configDir);
+ const agentsBaseDir = path.join(baseDir, this.agentsDir, 'bmad');
+ const commandsBaseDir = path.join(baseDir, this.commandsDir, 'bmad');
+
+ await this.ensureDir(agentsBaseDir);
+ await this.ensureDir(commandsBaseDir);
+
+ // Install primary agents
+ const agents = await getAgentsFromBmad(bmadDir, options.selectedModules || []);
+ const modules = new Set(agents.map((agent) => agent.module));
+
+ for (const module of modules) {
+ await this.ensureDir(path.join(agentsBaseDir, module));
+ }
+
+ let agentCount = 0;
+ for (const agent of agents) {
+ const processed = await this.readAndProcess(agent.path, {
+ module: agent.module,
+ name: agent.name,
+ });
+
+ const agentContent = this.createAgentContent(processed, agent);
+ const targetPath = path.join(agentsBaseDir, agent.module, `${agent.name}.md`);
+ await this.writeFile(targetPath, agentContent);
+ agentCount++;
+ }
+
+ // Install workflow commands
+ const workflowGenerator = new WorkflowCommandGenerator();
+ const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir);
+
+ let workflowCommandCount = 0;
+ for (const artifact of workflowArtifacts) {
+ // Workflow artifacts already have proper frontmatter from the template
+ // No need to wrap with additional frontmatter
+ const commandContent = artifact.content;
+ const targetPath = path.join(commandsBaseDir, artifact.relativePath);
+ await this.writeFile(targetPath, commandContent);
+ workflowCommandCount++;
+ }
+
+ console.log(chalk.green(`✓ ${this.name} configured:`));
+ console.log(chalk.dim(` - ${agentCount} agents installed to .opencode/agent/bmad/`));
+ if (workflowCommandCount > 0) {
+ console.log(chalk.dim(` - ${workflowCommandCount} workflow commands generated to .opencode/command/bmad/`));
+ }
+
+ return {
+ success: true,
+ agents: agentCount,
+ workflows: workflowCommandCount,
+ workflowCounts,
+ };
+ }
+
+ async readAndProcess(filePath, metadata) {
+ const content = await fs.readFile(filePath, 'utf8');
+ return this.processContent(content, metadata);
+ }
+
+ createAgentContent(content, metadata) {
+ const { frontmatter = {}, body } = this.parseFrontmatter(content);
+
+ frontmatter.description =
+ frontmatter.description && String(frontmatter.description).trim().length > 0
+ ? frontmatter.description
+ : `BMAD ${metadata.module} agent: ${metadata.name}`;
+
+ // OpenCode agents use: 'primary' mode for main agents
+ frontmatter.mode = 'primary';
+
+ const frontmatterString = this.stringifyFrontmatter(frontmatter);
+
+ return `${frontmatterString}\n${body}`;
+ }
+
+ parseFrontmatter(content) {
+ const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/);
+ if (!match) {
+ return { data: {}, body: content };
+ }
+
+ const body = content.slice(match[0].length);
+
+ let frontmatter = {};
+ try {
+ frontmatter = yaml.load(match[1]) || {};
+ } catch {
+ frontmatter = {};
+ }
+
+ return { frontmatter, body };
+ }
+
+ stringifyFrontmatter(frontmatter) {
+ const yamlText = yaml
+ .dump(frontmatter, {
+ indent: 2,
+ lineWidth: -1,
+ noRefs: true,
+ sortKeys: false,
+ })
+ .trimEnd();
+
+ return `---\n${yamlText}\n---`;
+ }
+}
+
+module.exports = { OpenCodeSetup };
diff --git a/tools/cli/installers/lib/ide/workflow-command-template.md b/tools/cli/installers/lib/ide/workflow-command-template.md
index 4199c2f6c..8755d882f 100644
--- a/tools/cli/installers/lib/ide/workflow-command-template.md
+++ b/tools/cli/installers/lib/ide/workflow-command-template.md
@@ -1,3 +1,7 @@
+---
+description: '{{description}}'
+---
+
# {{name}}
IT IS CRITICAL THAT YOU FOLLOW THESE STEPS - while staying in character as the current agent persona you may have loaded:
diff --git a/tools/platform-codes.yaml b/tools/platform-codes.yaml
index 3114d12a0..702d3a542 100644
--- a/tools/platform-codes.yaml
+++ b/tools/platform-codes.yaml
@@ -37,6 +37,12 @@ platforms:
category: ide
description: "AI coding assistant"
+ opencode:
+ name: "OpenCode"
+ preferred: false
+ category: ide
+ description: "OpenCode terminal coding assistant"
+
auggie:
name: "Auggie"
preferred: false