feat: add qwen-code ide support to bmad installer. (#392)

Co-authored-by: Djanghao <hstnz>
This commit is contained in:
Houston Zhang
2025-08-03 21:24:09 -04:00
committed by GitHub
parent 55f834954f
commit a72b790f3b
3 changed files with 117 additions and 2 deletions

View File

@@ -41,7 +41,7 @@ program
.option('-f, --full', 'Install complete BMad Method')
.option('-x, --expansion-only', 'Install only expansion packs (no bmad-core)')
.option('-d, --directory <path>', 'Installation directory')
.option('-i, --ide <ide...>', 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, github-copilot, other)')
.option('-i, --ide <ide...>', 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, other)')
.option('-e, --expansion-packs <packs...>', 'Install specific expansion packs (can specify multiple)')
.action(async (options) => {
try {
@@ -314,6 +314,7 @@ async function promptInstallation() {
{ name: 'Kilo Code', value: 'kilo' },
{ name: 'Cline', value: 'cline' },
{ name: 'Gemini CLI', value: 'gemini' },
{ name: 'Qwen Code', value: 'qwen-code' },
{ name: 'Github Copilot', value: 'github-copilot' }
]
}

View File

@@ -98,4 +98,16 @@ ide-configurations:
# To use BMAD agents in Kilo Code:
# 1. Open the mode selector in VSCode
# 2. Select a bmad-{agent} mode (e.g. "bmad-dev")
# 3. The AI adopts that agent's persona and capabilities
# 3. The AI adopts that agent's persona and capabilities
qwen-code:
name: Qwen Code
rule-dir: .qwen/bmad-method/
format: single-file
command-suffix: .md
instructions: |
# To use BMad agents with Qwen Code:
# 1. The installer creates a .qwen/bmad-method/ directory in your project.
# 2. It concatenates all agent files into a single QWEN.md file.
# 3. Simply mention the agent in your prompt (e.g., "As *dev, ...").
# 4. The Qwen Code CLI will automatically have the context for that agent.

View File

@@ -59,6 +59,8 @@ class IdeSetup extends BaseIdeSetup {
return this.setupGeminiCli(installDir, selectedAgent);
case "github-copilot":
return this.setupGitHubCopilot(installDir, selectedAgent, spinner, preConfiguredSettings);
case "qwen-code":
return this.setupQwenCode(installDir, selectedAgent);
default:
console.log(chalk.yellow(`\nIDE ${ide} not yet supported`));
return false;
@@ -977,6 +979,106 @@ class IdeSetup extends BaseIdeSetup {
return true;
}
async setupQwenCode(installDir, selectedAgent) {
const qwenDir = path.join(installDir, ".qwen");
const bmadMethodDir = path.join(qwenDir, "bmad-method");
await fileManager.ensureDirectory(bmadMethodDir);
// Update logic for existing settings.json
const settingsPath = path.join(qwenDir, "settings.json");
if (await fileManager.pathExists(settingsPath)) {
try {
const settingsContent = await fileManager.readFile(settingsPath);
const settings = JSON.parse(settingsContent);
let updated = false;
// Handle contextFileName property
if (settings.contextFileName && Array.isArray(settings.contextFileName)) {
const originalLength = settings.contextFileName.length;
settings.contextFileName = settings.contextFileName.filter(
(fileName) => !fileName.startsWith("agents/")
);
if (settings.contextFileName.length !== originalLength) {
updated = true;
}
}
if (updated) {
await fileManager.writeFile(
settingsPath,
JSON.stringify(settings, null, 2)
);
console.log(chalk.green("✓ Updated .qwen/settings.json - removed agent file references"));
}
} catch (error) {
console.warn(
chalk.yellow("Could not update .qwen/settings.json"),
error
);
}
}
// Remove old agents directory
const agentsDir = path.join(qwenDir, "agents");
if (await fileManager.pathExists(agentsDir)) {
await fileManager.removeDirectory(agentsDir);
console.log(chalk.green("✓ Removed old .qwen/agents directory"));
}
// Get all available agents
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
let concatenatedContent = "";
for (const agentId of agents) {
// Find the source agent file
const agentPath = await this.findAgentPath(agentId, installDir);
if (agentPath) {
const agentContent = await fileManager.readFile(agentPath);
// Create properly formatted agent rule content (similar to gemini)
let agentRuleContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`;
agentRuleContent += `This rule is triggered when the user types \`*${agentId}\` and activates the ${await this.getAgentTitle(
agentId,
installDir
)} agent persona.\n\n`;
agentRuleContent += "## Agent Activation\n\n";
agentRuleContent +=
"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:\n\n";
agentRuleContent += "```yaml\n";
// Extract just the YAML content from the agent file
const yamlContent = extractYamlFromAgent(agentContent);
if (yamlContent) {
agentRuleContent += yamlContent;
}
else {
// If no YAML found, include the whole content minus the header
agentRuleContent += agentContent.replace(/^#.*$/m, "").trim();
}
agentRuleContent += "\n```\n\n";
agentRuleContent += "## File Reference\n\n";
const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
agentRuleContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
agentRuleContent += "## Usage\n\n";
agentRuleContent += `When the user types \`*${agentId}\`, activate this ${await this.getAgentTitle(
agentId,
installDir
)} persona and follow all instructions defined in the YAML configuration above.\n`;
// Add to concatenated content with separator
concatenatedContent += agentRuleContent + "\n\n---\n\n";
console.log(chalk.green(`✓ Added context for *${agentId}`));
}
}
// Write the concatenated content to QWEN.md
const qwenMdPath = path.join(bmadMethodDir, "QWEN.md");
await fileManager.writeFile(qwenMdPath, concatenatedContent);
console.log(chalk.green(`\n✓ Created QWEN.md in ${bmadMethodDir}`));
return true;
}
async setupGitHubCopilot(installDir, selectedAgent, spinner = null, preConfiguredSettings = null) {
// Configure VS Code workspace settings first to avoid UI conflicts with loading spinners
await this.configureVsCodeSettings(installDir, spinner, preConfiguredSettings);