280 lines
11 KiB
Diff
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
|