diff --git a/tools/cli/installers/lib/ide/qwen.js b/tools/cli/installers/lib/ide/qwen.js index cf16952d..3daa7906 100644 --- a/tools/cli/installers/lib/ide/qwen.js +++ b/tools/cli/installers/lib/ide/qwen.js @@ -4,13 +4,14 @@ const chalk = require('chalk'); /** * Qwen Code setup handler - * Creates concatenated QWEN.md file in .qwen/bmad-method/ (similar to Gemini) + * Creates TOML command files in .qwen/commands/BMad/ */ class QwenSetup extends BaseIdeSetup { constructor() { super('qwen', 'Qwen Code'); this.configDir = '.qwen'; - this.bmadDir = 'bmad-method'; + this.commandsDir = 'commands'; + this.bmadDir = 'BMad'; } /** @@ -22,54 +23,90 @@ class QwenSetup extends BaseIdeSetup { async setup(projectDir, bmadDir, options = {}) { console.log(chalk.cyan(`Setting up ${this.name}...`)); - // Create .qwen/bmad-method directory + // Create .qwen/commands/BMad directory structure const qwenDir = path.join(projectDir, this.configDir); - const bmadMethodDir = path.join(qwenDir, this.bmadDir); - await this.ensureDir(bmadMethodDir); + const commandsDir = path.join(qwenDir, this.commandsDir); + const bmadCommandsDir = path.join(commandsDir, this.bmadDir); + const agentsDir = path.join(bmadCommandsDir, 'agents'); + const tasksDir = path.join(bmadCommandsDir, 'tasks'); + + await this.ensureDir(agentsDir); + await this.ensureDir(tasksDir); // Update existing settings.json if present await this.updateSettings(qwenDir); - // Clean up old agents directory if exists - await this.cleanupOldAgents(qwenDir); + // Clean up old configuration if exists + await this.cleanupOldConfig(qwenDir); - // Get agents + // Get agents and tasks const agents = await this.getAgents(bmadDir); + const tasks = await this.getTasks(bmadDir); - // Create concatenated content for all agents + // Create TOML files for each agent + let agentCount = 0; + for (const agent of agents) { + const content = await this.readFile(agent.path); + const tomlContent = this.createAgentToml(agent, content, projectDir); + const tomlPath = path.join(agentsDir, `${agent.name}.toml`); + await this.writeFile(tomlPath, tomlContent); + agentCount++; + console.log(chalk.green(` ✓ Added agent: /BMad:agents:${agent.name}`)); + } + + // Create TOML files for each task + let taskCount = 0; + for (const task of tasks) { + const content = await this.readFile(task.path); + const tomlContent = this.createTaskToml(task, content, projectDir); + const tomlPath = path.join(tasksDir, `${task.name}.toml`); + await this.writeFile(tomlPath, tomlContent); + taskCount++; + console.log(chalk.green(` ✓ Added task: /BMad:tasks:${task.name}`)); + } + + // Create concatenated QWEN.md for reference let concatenatedContent = `# BMAD Method - Qwen Code Configuration -This file contains all BMAD agents configured for use with Qwen Code. -Agents can be activated by typing \`*{agent-name}\` in your prompts. +This file contains all BMAD agents and tasks configured for use with Qwen Code. + +## Agents +Agents can be activated using: \`/BMad:agents:\` + +## Tasks +Tasks can be executed using: \`/BMad:tasks:\` --- `; - let agentCount = 0; for (const agent of agents) { const content = await this.readFile(agent.path); const agentSection = this.createAgentSection(agent, content, projectDir); - concatenatedContent += agentSection; concatenatedContent += '\n\n---\n\n'; - agentCount++; - - console.log(chalk.green(` ✓ Added agent: *${agent.name}`)); } - // Write QWEN.md - const qwenMdPath = path.join(bmadMethodDir, 'QWEN.md'); + for (const task of tasks) { + const content = await this.readFile(task.path); + const taskSection = this.createTaskSection(task, content, projectDir); + concatenatedContent += taskSection; + concatenatedContent += '\n\n---\n\n'; + } + + const qwenMdPath = path.join(bmadCommandsDir, 'QWEN.md'); await this.writeFile(qwenMdPath, concatenatedContent); console.log(chalk.green(`✓ ${this.name} configured:`)); console.log(chalk.dim(` - ${agentCount} agents configured`)); - console.log(chalk.dim(` - Configuration file: ${path.relative(projectDir, qwenMdPath)}`)); - console.log(chalk.dim(` - Agents activated with: *{agent-name}`)); + console.log(chalk.dim(` - ${taskCount} tasks configured`)); + console.log(chalk.dim(` - Agents activated with: /BMad:agents:`)); + console.log(chalk.dim(` - Tasks activated with: /BMad:tasks:`)); return { success: true, agents: agentCount, + tasks: taskCount, }; } @@ -89,7 +126,9 @@ Agents can be activated by typing \`*{agent-name}\` in your prompts. // Remove agent file references from contextFileName if (settings.contextFileName && Array.isArray(settings.contextFileName)) { const originalLength = settings.contextFileName.length; - settings.contextFileName = settings.contextFileName.filter((fileName) => !fileName.startsWith('agents/')); + settings.contextFileName = settings.contextFileName.filter( + (fileName) => !fileName.startsWith('agents/') && !fileName.startsWith('bmad-method/'), + ); if (settings.contextFileName.length !== originalLength) { updated = true; @@ -107,16 +146,72 @@ Agents can be activated by typing \`*{agent-name}\` in your prompts. } /** - * Clean up old agents directory + * Clean up old configuration directories */ - async cleanupOldAgents(qwenDir) { + async cleanupOldConfig(qwenDir) { const fs = require('fs-extra'); const agentsDir = path.join(qwenDir, 'agents'); + const bmadMethodDir = path.join(qwenDir, 'bmad-method'); if (await fs.pathExists(agentsDir)) { await fs.remove(agentsDir); console.log(chalk.green(' ✓ Removed old agents directory')); } + + if (await fs.pathExists(bmadMethodDir)) { + await fs.remove(bmadMethodDir); + console.log(chalk.green(' ✓ Removed old bmad-method directory')); + } + } + + /** + * Create TOML file for agent + */ + createAgentToml(agent, content, projectDir) { + const titleMatch = content.match(/title="([^"]+)"/); + const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name); + const yamlMatch = content.match(/```ya?ml\r?\n([\s\S]*?)```/); + const yamlContent = yamlMatch ? yamlMatch[1] : content; + const relativePath = path.relative(projectDir, agent.path).replaceAll('\\', '/'); + + return `# ${title} Agent +name = "${agent.name}" +description = """ +${title} agent from BMAD ${agent.module.toUpperCase()} module. + +CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode: + +\`\`\`yaml +${yamlContent} +\`\`\` + +File: ${relativePath} +"""`; + } + + /** + * Create TOML file for task + */ + createTaskToml(task, content, projectDir) { + const titleMatch = content.match(/title="([^"]+)"/); + const title = titleMatch ? titleMatch[1] : this.formatTitle(task.name); + const yamlMatch = content.match(/```ya?ml\r?\n([\s\S]*?)```/); + const yamlContent = yamlMatch ? yamlMatch[1] : content; + const relativePath = path.relative(projectDir, task.path).replaceAll('\\', '/'); + + return `# ${title} Task +name = "${task.name}" +description = """ +${title} task from BMAD ${task.module.toUpperCase()} module. + +Execute this task by following the instructions in the YAML configuration: + +\`\`\`yaml +${yamlContent} +\`\`\` + +File: ${relativePath} +"""`; } /** @@ -136,7 +231,7 @@ Agents can be activated by typing \`*{agent-name}\` in your prompts. let section = `# ${agent.name.toUpperCase()} Agent Rule -This rule is triggered when the user types \`*${agent.name}\` and activates the ${title} agent persona. +This rule is triggered when the user types \`/BMad:agents:${agent.name}\` and activates the ${title} agent persona. ## Agent Activation @@ -152,7 +247,7 @@ The complete agent definition is available in [${relativePath}](${relativePath}) ## Usage -When the user types \`*${agent.name}\`, activate this ${title} persona and follow all instructions defined in the YAML configuration above. +When the user types \`/BMad:agents:${agent.name}\`, activate this ${title} persona and follow all instructions defined in the YAML configuration above. ## Module @@ -161,6 +256,43 @@ Part of the BMAD ${agent.module.toUpperCase()} module.`; return section; } + /** + * Create task section for concatenated file + */ + createTaskSection(task, content, projectDir) { + const titleMatch = content.match(/title="([^"]+)"/); + const title = titleMatch ? titleMatch[1] : this.formatTitle(task.name); + const yamlMatch = content.match(/```ya?ml\r?\n([\s\S]*?)```/); + const yamlContent = yamlMatch ? yamlMatch[1] : content; + const relativePath = path.relative(projectDir, task.path).replaceAll('\\', '/'); + + let section = `# ${task.name.toUpperCase()} Task + +This task is triggered when the user types \`/BMad:tasks:${task.name}\` and executes the ${title} task. + +## Task Execution + +Execute this task by following the instructions in the YAML configuration: + +\`\`\`yaml +${yamlContent} +\`\`\` + +## File Reference + +The complete task definition is available in [${relativePath}](${relativePath}). + +## Usage + +When the user types \`/BMad:tasks:${task.name}\`, execute this ${title} task and follow all instructions defined in the YAML configuration above. + +## Module + +Part of the BMAD ${task.module.toUpperCase()} module.`; + + return section; + } + /** * Format name as title */ @@ -176,12 +308,18 @@ Part of the BMAD ${agent.module.toUpperCase()} module.`; */ async cleanup(projectDir) { const fs = require('fs-extra'); - const bmadMethodDir = path.join(projectDir, this.configDir, this.bmadDir); + const bmadCommandsDir = path.join(projectDir, this.configDir, this.commandsDir, this.bmadDir); + const oldBmadMethodDir = path.join(projectDir, this.configDir, 'bmad-method'); - if (await fs.pathExists(bmadMethodDir)) { - await fs.remove(bmadMethodDir); + if (await fs.pathExists(bmadCommandsDir)) { + await fs.remove(bmadCommandsDir); console.log(chalk.dim(`Removed BMAD configuration from Qwen Code`)); } + + if (await fs.pathExists(oldBmadMethodDir)) { + await fs.remove(oldBmadMethodDir); + console.log(chalk.dim(`Removed old BMAD configuration from Qwen Code`)); + } } } diff --git a/v6-open-items.md b/v6-open-items.md index 62b8a65a..8443bfef 100644 --- a/v6-open-items.md +++ b/v6-open-items.md @@ -11,7 +11,9 @@ Aside from stability and bug fixes found during the alpha period - the main focu - DONE: Game Agents comms style WAY to over the top - reduced a bit. - need to nest subagents for better organization. - DONE: Quick note on BMM v6 Flow -- DONE: CC SubAgents installed to subfolders now. +- DONE: CC SubAgents installed to sub-folders now. +- DONE: Qwen TOML update. +- DONE: Diagram alpha BMM flow. - IN PROGRESS - Team Web Bundler functional - IN PROGRESS - bmm `testarch` integrated into the BMM workflow's after aligned with the rest of bmad method flow. - IN PROGRESS - Document new agent workflows. @@ -26,6 +28,7 @@ Aside from stability and bug fixes found during the alpha period - the main focu - github pipelines, branch protection, vulnerability scanners - improved subagent injections - bmm existing project scanning and integration with workflow phase 0-4 improvements +- BTA Module coming soon! ## Needed before Beta → v0 release