From 44b9d7bcb5cbb6de5a15d8f2ec7918d186ac9576 Mon Sep 17 00:00:00 2001 From: "hieu.sats" <44203931+hieusats@users.noreply.github.com> Date: Thu, 26 Jun 2025 09:33:58 +0700 Subject: [PATCH] feat: Add Gemini CLI Integration (#271) --- tools/installer/bin/bmad.js | 5 +- tools/installer/config/install.config.yml | 10 ++++ tools/installer/lib/ide-setup.js | 59 +++++++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/tools/installer/bin/bmad.js b/tools/installer/bin/bmad.js index 97415dc6..fc3c590d 100755 --- a/tools/installer/bin/bmad.js +++ b/tools/installer/bin/bmad.js @@ -50,7 +50,7 @@ program .option('-t, --team ', 'Install specific team with required agents and dependencies') .option('-x, --expansion-only', 'Install only expansion packs (no bmad-core)') .option('-d, --directory ', 'Installation directory (default: .bmad-core)') - .option('-i, --ide ', 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, roo, cline, other)') + .option('-i, --ide ', 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, roo, cline, gemini, other)') .option('-e, --expansion-packs ', 'Install specific expansion packs (can specify multiple)') .action(async (options) => { try { @@ -314,7 +314,8 @@ async function promptInstallation() { { name: 'Claude Code', value: 'claude-code' }, { name: 'Windsurf', value: 'windsurf' }, { name: 'Roo Code', value: 'roo' }, - { name: 'Cline', value: 'cline' } + { name: 'Cline', value: 'cline' }, + { name: 'Gemini CLI', value: 'gemini' } ] } ]); diff --git a/tools/installer/config/install.config.yml b/tools/installer/config/install.config.yml index 1e6bd7ca..2c34aeb3 100644 --- a/tools/installer/config/install.config.yml +++ b/tools/installer/config/install.config.yml @@ -101,6 +101,16 @@ ide-configurations: # 2. Type @agent-name (e.g., "@dev", "@pm", "@architect") # 3. The agent will adopt that persona for the conversation # 4. Rules are stored in .clinerules/ directory in your project + gemini: + name: Gemini CLI + rule-dir: .gemini/agents/ + format: context-files + instructions: | + # To use BMAD agents with the Gemini CLI: + # 1. The installer creates a .gemini/ directory in your project. + # 2. It also configures .gemini/settings.json to load all agent files. + # 3. Simply mention the agent in your prompt (e.g., "As @dev, ..."). + # 4. The Gemini CLI will automatically have the context for that agent. available-agents: - id: analyst name: Business Analyst diff --git a/tools/installer/lib/ide-setup.js b/tools/installer/lib/ide-setup.js index 6ad79c54..09065886 100644 --- a/tools/installer/lib/ide-setup.js +++ b/tools/installer/lib/ide-setup.js @@ -33,6 +33,8 @@ class IdeSetup { return this.setupRoo(installDir, selectedAgent); case "cline": return this.setupCline(installDir, selectedAgent); + case "gemini": + return this.setupGeminiCli(installDir, selectedAgent); default: console.log(chalk.yellow(`\nIDE ${ide} not yet supported`)); return false; @@ -411,6 +413,63 @@ class IdeSetup { return true; } + + async setupGeminiCli(installDir, selectedAgent) { + await initializeModules(); + const geminiDir = path.join(installDir, ".gemini"); + const agentsContextDir = path.join(geminiDir, "agents"); + await fileManager.ensureDirectory(agentsContextDir); + + // Get all available agents + const agents = await this.getAllAgentIds(installDir); + const agentContextFiles = []; + + for (const agentId of agents) { + // Find the source agent file + let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`); + if (!(await fileManager.pathExists(agentPath))) { + agentPath = path.join(installDir, "agents", `${agentId}.md`); + } + + if (await fileManager.pathExists(agentPath)) { + const agentContent = await fileManager.readFile(agentPath); + const contextFilePath = path.join(agentsContextDir, `${agentId}.md`); + + // Copy the agent content directly into its own context file + await fileManager.writeFile(contextFilePath, agentContent); + + // Store the relative path for settings.json + const relativePath = path.relative(geminiDir, contextFilePath); + agentContextFiles.push(relativePath.replace(/\\/g, '/')); // Ensure forward slashes for consistency + console.log(chalk.green(`āœ“ Created context file for @${agentId}`)); + } + } + + console.log(chalk.green(`\nāœ“ Created individual agent context files in ${agentsContextDir}`)); + + // Create or update settings.json + const settingsPath = path.join(geminiDir, "settings.json"); + let settings = {}; + + if (await fileManager.pathExists(settingsPath)) { + try { + const existingSettings = await fileManager.readFile(settingsPath); + settings = JSON.parse(existingSettings); + console.log(chalk.yellow("Found existing .gemini/settings.json. Merging settings...")); + } catch (e) { + console.error(chalk.red("Error parsing existing settings.json. It will be overwritten."), e); + settings = {}; + } + } + + // Set contextFileName to our new array of files + settings.contextFileName = agentContextFiles; + + await fileManager.writeFile(settingsPath, JSON.stringify(settings, null, 2)); + console.log(chalk.green(`āœ“ Configured .gemini/settings.json to load all agent context files.`)); + + return true; + } } module.exports = new IdeSetup();