diff --git a/tools/bmad-npx-wrapper.js b/tools/bmad-npx-wrapper.js index 0cf6ff95..126349c1 100755 --- a/tools/bmad-npx-wrapper.js +++ b/tools/bmad-npx-wrapper.js @@ -18,17 +18,17 @@ async function promptVersionSelection() { const chalk = require('chalk'); console.log( - chalk.bold.cyan(` -██████╗ ███╗ ███╗ █████╗ ██████╗ ███╗ ███╗███████╗████████╗██╗ ██╗ ██████╗ ██████╗ -██╔══██╗████╗ ████║██╔══██╗██╔══██╗ ████╗ ████║██╔════╝╚══██╔══╝██║ ██║██╔═══██╗██╔══██╗ -██████╔╝██╔████╔██║███████║██║ ██║█████╗██╔████╔██║█████╗ ██║ ███████║██║ ██║██║ ██║ -██╔══██╗██║╚██╔╝██║██╔══██║██║ ██║╚════╝██║╚██╔╝██║██╔══╝ ██║ ██╔══██║██║ ██║██║ ██║ -██████╔╝██║ ╚═╝ ██║██║ ██║██████╔╝ ██║ ╚═╝ ██║███████╗ ██║ ██║ ██║╚██████╔╝██████╔╝ -╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ + chalk.cyan(` + ██████╗ ███╗ ███╗ █████╗ ██████╗ ™ + ██╔══██╗████╗ ████║██╔══██╗██╔══██╗ + ██████╔╝██╔████╔██║███████║██║ ██║ + ██╔══██╗██║╚██╔╝██║██╔══██║██║ ██║ + ██████╔╝██║ ╚═╝ ██║██║ ██║██████╔╝ + ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ `), ); - console.log(chalk.bold.magenta('🚀 Universal AI Agent Framework for Any Domain\n')); + console.log(chalk.dim(' Build More, Architect Dreams\n')); const answers = await inquirer.prompt([ { diff --git a/tools/cli/installers/lib/ide/opencode.js b/tools/cli/installers/lib/ide/opencode.js index f9b2de7e..b4fbbe86 100644 --- a/tools/cli/installers/lib/ide/opencode.js +++ b/tools/cli/installers/lib/ide/opencode.js @@ -24,19 +24,13 @@ class OpenCodeSetup extends BaseIdeSetup { 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'); + const commandsBaseDir = path.join(baseDir, this.commandsDir); - await this.ensureDir(agentsBaseDir); await this.ensureDir(commandsBaseDir); - // Install primary agents + // Install primary agents with flat naming: bmad-agent-{module}-{name}.md + // OpenCode puts agents in the command folder, not a separate agent folder 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) { @@ -46,40 +40,40 @@ class OpenCodeSetup extends BaseIdeSetup { }); const agentContent = this.createAgentContent(processed, agent); - const targetPath = path.join(agentsBaseDir, agent.module, `${agent.name}.md`); + // Flat structure in command folder: bmad-agent-{module}-{name}.md + const targetPath = path.join(commandsBaseDir, `bmad-agent-${agent.module}-${agent.name}.md`); await this.writeFile(targetPath, agentContent); agentCount++; } - // Install workflow commands + // Install workflow commands with flat naming: bmad-workflow-{module}-{name}.md 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++; + if (artifact.type === 'workflow-command') { + const commandContent = artifact.content; + // Flat structure: bmad-workflow-{module}-{name}.md + // artifact.relativePath is like: bmm/workflows/plan-project.md + const workflowName = path.basename(artifact.relativePath, '.md'); + const targetPath = path.join(commandsBaseDir, `bmad-workflow-${artifact.module}-${workflowName}.md`); + await this.writeFile(targetPath, commandContent); + workflowCommandCount++; + } + // Skip workflow launcher READMEs as they're not needed in flat structure } - // Install task and tool commands - const taskToolGen = new TaskToolCommandGenerator(); - const taskToolResult = await taskToolGen.generateTaskToolCommands(projectDir, bmadDir, commandsBaseDir); + // Install task and tool commands with flat naming + const { tasks, tools } = await this.generateFlatTaskToolCommands(bmadDir, commandsBaseDir); console.log(chalk.green(`✓ ${this.name} configured:`)); - console.log(chalk.dim(` - ${agentCount} agents installed to .opencode/agent/bmad/`)); + console.log(chalk.dim(` - ${agentCount} agents installed to .opencode/command/`)); if (workflowCommandCount > 0) { - console.log(chalk.dim(` - ${workflowCommandCount} workflow commands generated to .opencode/command/bmad/`)); + console.log(chalk.dim(` - ${workflowCommandCount} workflows installed to .opencode/command/`)); } - if (taskToolResult.generated > 0) { - console.log( - chalk.dim( - ` - ${taskToolResult.generated} task/tool commands generated (${taskToolResult.tasks} tasks, ${taskToolResult.tools} tools)`, - ), - ); + if (tasks + tools > 0) { + console.log(chalk.dim(` - ${tasks + tools} tasks/tools installed to .opencode/command/ (${tasks} tasks, ${tools} tools)`)); } return { @@ -90,6 +84,39 @@ class OpenCodeSetup extends BaseIdeSetup { }; } + /** + * Generate flat task and tool commands for OpenCode + * OpenCode doesn't support nested command directories + */ + async generateFlatTaskToolCommands(bmadDir, commandsBaseDir) { + const taskToolGen = new TaskToolCommandGenerator(); + const tasks = await taskToolGen.loadTaskManifest(bmadDir); + const tools = await taskToolGen.loadToolManifest(bmadDir); + + // Filter to only standalone items + const standaloneTasks = tasks ? tasks.filter((t) => t.standalone === 'true' || t.standalone === true) : []; + const standaloneTools = tools ? tools.filter((t) => t.standalone === 'true' || t.standalone === true) : []; + + // Generate command files for tasks with flat naming: bmad-task-{module}-{name}.md + for (const task of standaloneTasks) { + const commandContent = taskToolGen.generateCommandContent(task, 'task'); + const targetPath = path.join(commandsBaseDir, `bmad-task-${task.module}-${task.name}.md`); + await this.writeFile(targetPath, commandContent); + } + + // Generate command files for tools with flat naming: bmad-tool-{module}-{name}.md + for (const tool of standaloneTools) { + const commandContent = taskToolGen.generateCommandContent(tool, 'tool'); + const targetPath = path.join(commandsBaseDir, `bmad-tool-${tool.module}-${tool.name}.md`); + await this.writeFile(targetPath, commandContent); + } + + return { + tasks: standaloneTasks.length, + tools: standaloneTools.length, + }; + } + async readAndProcess(filePath, metadata) { const content = await fs.readFile(filePath, 'utf8'); return this.processContent(content, metadata);