2025-06-14 15:06:41 -05:00
const path = require ( "path" ) ;
2025-06-28 02:22:57 -05:00
const fs = require ( "fs-extra" ) ;
const yaml = require ( "js-yaml" ) ;
2025-06-14 15:06:41 -05:00
const fileManager = require ( "./file-manager" ) ;
const configLoader = require ( "./config-loader" ) ;
2025-06-15 14:07:25 -05:00
// Dynamic import for ES module
let chalk ;
// Initialize ES modules
async function initializeModules ( ) {
if ( ! chalk ) {
chalk = ( await import ( "chalk" ) ) . default ;
}
}
2025-06-12 22:38:24 -05:00
class IdeSetup {
2025-06-28 02:22:57 -05:00
constructor ( ) {
this . ideAgentConfig = null ;
}
async loadIdeAgentConfig ( ) {
if ( this . ideAgentConfig ) return this . ideAgentConfig ;
try {
const configPath = path . join ( _ _dirname , '..' , 'config' , 'ide-agent-config.yml' ) ;
const configContent = await fs . readFile ( configPath , 'utf8' ) ;
this . ideAgentConfig = yaml . load ( configContent ) ;
return this . ideAgentConfig ;
} catch ( error ) {
console . warn ( 'Failed to load IDE agent configuration, using defaults' ) ;
return {
'roo-permissions' : { } ,
'cline-order' : { }
} ;
}
}
2025-06-12 22:38:24 -05:00
async setup ( ide , installDir , selectedAgent = null ) {
2025-06-15 14:07:25 -05:00
await initializeModules ( ) ;
2025-06-12 22:38:24 -05:00
const ideConfig = await configLoader . getIdeConfiguration ( ide ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
if ( ! ideConfig ) {
console . log ( chalk . yellow ( ` \n No configuration available for ${ ide } ` ) ) ;
return false ;
}
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
switch ( ide ) {
2025-06-14 15:06:41 -05:00
case "cursor" :
2025-06-12 22:38:24 -05:00
return this . setupCursor ( installDir , selectedAgent ) ;
2025-06-14 15:06:41 -05:00
case "claude-code" :
2025-06-12 22:38:24 -05:00
return this . setupClaudeCode ( installDir , selectedAgent ) ;
2025-06-14 15:06:41 -05:00
case "windsurf" :
2025-06-12 22:38:24 -05:00
return this . setupWindsurf ( installDir , selectedAgent ) ;
2025-06-14 15:06:41 -05:00
case "roo" :
return this . setupRoo ( installDir , selectedAgent ) ;
2025-06-24 04:47:21 +02:00
case "cline" :
return this . setupCline ( installDir , selectedAgent ) ;
2025-06-26 09:33:58 +07:00
case "gemini" :
return this . setupGeminiCli ( installDir , selectedAgent ) ;
2025-06-12 22:38:24 -05:00
default :
console . log ( chalk . yellow ( ` \n IDE ${ ide } not yet supported ` ) ) ;
return false ;
}
}
async setupCursor ( installDir , selectedAgent ) {
2025-06-14 15:06:41 -05:00
const cursorRulesDir = path . join ( installDir , ".cursor" , "rules" ) ;
2025-06-19 12:55:16 -05:00
const agents = selectedAgent ? [ selectedAgent ] : await this . getAllAgentIds ( installDir ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
await fileManager . ensureDirectory ( cursorRulesDir ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
for ( const agentId of agents ) {
2025-06-28 02:22:57 -05:00
// Find the agent file
const agentPath = await this . findAgentPath ( agentId , installDir ) ;
2025-06-14 15:06:41 -05:00
2025-06-28 02:22:57 -05:00
if ( agentPath ) {
2025-06-12 22:38:24 -05:00
const agentContent = await fileManager . readFile ( agentPath ) ;
const mdcPath = path . join ( cursorRulesDir , ` ${ agentId } .mdc ` ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
// Create MDC content with proper format
2025-06-14 15:06:41 -05:00
let mdcContent = "---\n" ;
mdcContent += "description: \n" ;
mdcContent += "globs: []\n" ;
mdcContent += "alwaysApply: false\n" ;
mdcContent += "---\n\n" ;
2025-06-12 22:38:24 -05:00
mdcContent += ` # ${ agentId . toUpperCase ( ) } Agent Rule \n \n ` ;
2025-06-28 01:01:26 -05:00
mdcContent += ` This rule is triggered when the user types \` @ ${ agentId } \` and activates the ${ await this . getAgentTitle (
agentId ,
installDir
2025-06-14 15:06:41 -05:00
) } agent persona . \ n \ n ` ;
mdcContent += "## Agent Activation\n\n" ;
mdcContent +=
"CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n" ;
mdcContent += "```yml\n" ;
2025-06-12 22:38:24 -05:00
// Extract just the YAML content from the agent file
const yamlMatch = agentContent . match ( /```ya?ml\n([\s\S]*?)```/ ) ;
if ( yamlMatch ) {
mdcContent += yamlMatch [ 1 ] . trim ( ) ;
} else {
// If no YAML found, include the whole content minus the header
2025-06-14 15:06:41 -05:00
mdcContent += agentContent . replace ( /^#.*$/m , "" ) . trim ( ) ;
2025-06-12 22:38:24 -05:00
}
2025-06-14 15:06:41 -05:00
mdcContent += "\n```\n\n" ;
mdcContent += "## File Reference\n\n" ;
2025-06-28 02:22:57 -05:00
const relativePath = path . relative ( installDir , agentPath ) . replace ( /\\/g , '/' ) ;
mdcContent += ` The complete agent definition is available in [ ${ relativePath } ](mdc: ${ relativePath } ). \n \n ` ;
2025-06-14 15:06:41 -05:00
mdcContent += "## Usage\n\n" ;
2025-06-28 01:01:26 -05:00
mdcContent += ` When the user types \` @ ${ agentId } \` , activate this ${ await this . getAgentTitle (
agentId ,
installDir
2025-06-14 15:06:41 -05:00
) } persona and follow all instructions defined in the YML configuration above . \ n ` ;
2025-06-12 22:38:24 -05:00
await fileManager . writeFile ( mdcPath , mdcContent ) ;
console . log ( chalk . green ( ` ✓ Created rule: ${ agentId } .mdc ` ) ) ;
}
}
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
console . log ( chalk . green ( ` \n ✓ Created Cursor rules in ${ cursorRulesDir } ` ) ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
return true ;
}
async setupClaudeCode ( installDir , selectedAgent ) {
2025-06-14 15:06:41 -05:00
const commandsDir = path . join ( installDir , ".claude" , "commands" ) ;
2025-06-19 12:55:16 -05:00
const agents = selectedAgent ? [ selectedAgent ] : await this . getAllAgentIds ( installDir ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
await fileManager . ensureDirectory ( commandsDir ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
for ( const agentId of agents ) {
2025-06-28 02:22:57 -05:00
// Find the agent file
const agentPath = await this . findAgentPath ( agentId , installDir ) ;
2025-06-12 22:38:24 -05:00
const commandPath = path . join ( commandsDir , ` ${ agentId } .md ` ) ;
2025-06-14 15:06:41 -05:00
2025-06-28 02:22:57 -05:00
if ( agentPath ) {
2025-06-12 22:38:24 -05:00
// Create command file with agent content
const agentContent = await fileManager . readFile ( agentPath ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
// Add command header
let commandContent = ` # / ${ agentId } Command \n \n ` ;
commandContent += ` When this command is used, adopt the following agent persona: \n \n ` ;
commandContent += agentContent ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
await fileManager . writeFile ( commandPath , commandContent ) ;
console . log ( chalk . green ( ` ✓ Created command: / ${ agentId } ` ) ) ;
}
}
2025-06-14 15:06:41 -05:00
2025-06-19 12:55:16 -05:00
console . log ( chalk . green ( ` \n ✓ Created Claude Code commands in ${ commandsDir } ` ) ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
return true ;
}
async setupWindsurf ( installDir , selectedAgent ) {
2025-06-14 15:06:41 -05:00
const windsurfRulesDir = path . join ( installDir , ".windsurf" , "rules" ) ;
2025-06-19 12:55:16 -05:00
const agents = selectedAgent ? [ selectedAgent ] : await this . getAllAgentIds ( installDir ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
await fileManager . ensureDirectory ( windsurfRulesDir ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
for ( const agentId of agents ) {
2025-06-28 02:22:57 -05:00
// Find the agent file
const agentPath = await this . findAgentPath ( agentId , installDir ) ;
2025-06-14 15:06:41 -05:00
2025-06-28 02:22:57 -05:00
if ( agentPath ) {
2025-06-12 22:38:24 -05:00
const agentContent = await fileManager . readFile ( agentPath ) ;
const mdPath = path . join ( windsurfRulesDir , ` ${ agentId } .md ` ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
// Create MD content (similar to Cursor but without frontmatter)
let mdContent = ` # ${ agentId . toUpperCase ( ) } Agent Rule \n \n ` ;
2025-06-28 01:01:26 -05:00
mdContent += ` This rule is triggered when the user types \` @ ${ agentId } \` and activates the ${ await this . getAgentTitle (
agentId ,
installDir
2025-06-14 15:06:41 -05:00
) } agent persona . \ n \ n ` ;
mdContent += "## Agent Activation\n\n" ;
mdContent +=
"CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n" ;
mdContent += "```yml\n" ;
2025-06-12 22:38:24 -05:00
// Extract just the YAML content from the agent file
const yamlMatch = agentContent . match ( /```ya?ml\n([\s\S]*?)```/ ) ;
if ( yamlMatch ) {
mdContent += yamlMatch [ 1 ] . trim ( ) ;
} else {
// If no YAML found, include the whole content minus the header
2025-06-14 15:06:41 -05:00
mdContent += agentContent . replace ( /^#.*$/m , "" ) . trim ( ) ;
2025-06-12 22:38:24 -05:00
}
2025-06-14 15:06:41 -05:00
mdContent += "\n```\n\n" ;
mdContent += "## File Reference\n\n" ;
2025-06-28 02:22:57 -05:00
const relativePath = path . relative ( installDir , agentPath ) . replace ( /\\/g , '/' ) ;
mdContent += ` The complete agent definition is available in [ ${ relativePath } ]( ${ relativePath } ). \n \n ` ;
2025-06-14 15:06:41 -05:00
mdContent += "## Usage\n\n" ;
2025-06-28 01:01:26 -05:00
mdContent += ` When the user types \` @ ${ agentId } \` , activate this ${ await this . getAgentTitle (
agentId ,
installDir
2025-06-14 15:06:41 -05:00
) } persona and follow all instructions defined in the YML configuration above . \ n ` ;
2025-06-12 22:38:24 -05:00
await fileManager . writeFile ( mdPath , mdContent ) ;
console . log ( chalk . green ( ` ✓ Created rule: ${ agentId } .md ` ) ) ;
}
}
2025-06-14 15:06:41 -05:00
2025-06-19 12:55:16 -05:00
console . log ( chalk . green ( ` \n ✓ Created Windsurf rules in ${ windsurfRulesDir } ` ) ) ;
2025-06-14 15:06:41 -05:00
2025-06-12 22:38:24 -05:00
return true ;
}
2025-06-28 02:22:57 -05:00
async findAgentPath ( agentId , installDir ) {
// Try to find the agent file in various locations
const possiblePaths = [
path . join ( installDir , ".bmad-core" , "agents" , ` ${ agentId } .md ` ) ,
path . join ( installDir , "agents" , ` ${ agentId } .md ` )
] ;
// Also check expansion pack directories
const glob = require ( "glob" ) ;
const expansionDirs = glob . sync ( ".*/agents" , { cwd : installDir } ) ;
for ( const expDir of expansionDirs ) {
possiblePaths . push ( path . join ( installDir , expDir , ` ${ agentId } .md ` ) ) ;
}
for ( const agentPath of possiblePaths ) {
if ( await fileManager . pathExists ( agentPath ) ) {
return agentPath ;
}
}
return null ;
}
2025-06-12 22:38:24 -05:00
async getAllAgentIds ( installDir ) {
2025-06-28 02:22:57 -05:00
const glob = require ( "glob" ) ;
const allAgentIds = [ ] ;
// Check core agents in .bmad-core or root
2025-06-14 15:06:41 -05:00
let agentsDir = path . join ( installDir , ".bmad-core" , "agents" ) ;
if ( ! ( await fileManager . pathExists ( agentsDir ) ) ) {
agentsDir = path . join ( installDir , "agents" ) ;
2025-06-12 22:38:24 -05:00
}
2025-06-28 02:22:57 -05:00
if ( await fileManager . pathExists ( agentsDir ) ) {
const agentFiles = glob . sync ( "*.md" , { cwd : agentsDir } ) ;
allAgentIds . push ( ... agentFiles . map ( ( file ) => path . basename ( file , ".md" ) ) ) ;
}
// Also check for expansion pack agents in dot folders
const expansionDirs = glob . sync ( ".*/agents" , { cwd : installDir } ) ;
for ( const expDir of expansionDirs ) {
const fullExpDir = path . join ( installDir , expDir ) ;
const expAgentFiles = glob . sync ( "*.md" , { cwd : fullExpDir } ) ;
allAgentIds . push ( ... expAgentFiles . map ( ( file ) => path . basename ( file , ".md" ) ) ) ;
}
// Remove duplicates
return [ ... new Set ( allAgentIds ) ] ;
2025-06-12 22:38:24 -05:00
}
2025-06-28 01:01:26 -05:00
async getAgentTitle ( agentId , installDir ) {
2025-06-28 02:22:57 -05:00
// Try to find the agent file in various locations
const possiblePaths = [
path . join ( installDir , ".bmad-core" , "agents" , ` ${ agentId } .md ` ) ,
path . join ( installDir , "agents" , ` ${ agentId } .md ` )
] ;
// Also check expansion pack directories
const glob = require ( "glob" ) ;
const expansionDirs = glob . sync ( ".*/agents" , { cwd : installDir } ) ;
for ( const expDir of expansionDirs ) {
possiblePaths . push ( path . join ( installDir , expDir , ` ${ agentId } .md ` ) ) ;
2025-06-28 01:01:26 -05:00
}
2025-06-28 02:22:57 -05:00
for ( const agentPath of possiblePaths ) {
if ( await fileManager . pathExists ( agentPath ) ) {
try {
const agentContent = await fileManager . readFile ( agentPath ) ;
const yamlMatch = agentContent . match ( /```ya?ml\n([\s\S]*?)```/ ) ;
if ( yamlMatch ) {
const yaml = yamlMatch [ 1 ] ;
const titleMatch = yaml . match ( /title:\s*(.+)/ ) ;
if ( titleMatch ) {
return titleMatch [ 1 ] . trim ( ) ;
}
2025-06-28 01:01:26 -05:00
}
2025-06-28 02:22:57 -05:00
} catch ( error ) {
console . warn ( ` Failed to read agent title for ${ agentId } : ${ error . message } ` ) ;
2025-06-28 01:01:26 -05:00
}
}
}
// Fallback to formatted agent ID
return agentId . split ( '-' ) . map ( word =>
word . charAt ( 0 ) . toUpperCase ( ) + word . slice ( 1 )
) . join ( ' ' ) ;
2025-06-12 22:38:24 -05:00
}
2025-06-14 15:06:41 -05:00
async setupRoo ( installDir , selectedAgent ) {
2025-06-19 12:55:16 -05:00
const agents = selectedAgent ? [ selectedAgent ] : await this . getAllAgentIds ( installDir ) ;
2025-06-14 15:06:41 -05:00
2025-06-17 17:51:52 +02:00
// Check for existing .roomodes file in project root
const roomodesPath = path . join ( installDir , ".roomodes" ) ;
2025-06-14 15:06:41 -05:00
let existingModes = [ ] ;
let existingContent = "" ;
if ( await fileManager . pathExists ( roomodesPath ) ) {
existingContent = await fileManager . readFile ( roomodesPath ) ;
// Parse existing modes to avoid duplicates
const modeMatches = existingContent . matchAll ( /- slug: ([\w-]+)/g ) ;
for ( const match of modeMatches ) {
existingModes . push ( match [ 1 ] ) ;
}
2025-06-19 12:55:16 -05:00
console . log ( chalk . yellow ( ` Found existing .roomodes file with ${ existingModes . length } modes ` ) ) ;
2025-06-14 15:06:41 -05:00
}
// Create new modes content
let newModesContent = "" ;
2025-06-28 02:22:57 -05:00
// Load dynamic agent permissions from configuration
const config = await this . loadIdeAgentConfig ( ) ;
const agentPermissions = config [ 'roo-permissions' ] || { } ;
2025-06-14 16:38:37 -05:00
2025-06-14 15:06:41 -05:00
for ( const agentId of agents ) {
// Skip if already exists
if ( existingModes . includes ( ` bmad- ${ agentId } ` ) ) {
2025-06-19 12:55:16 -05:00
console . log ( chalk . dim ( ` Skipping ${ agentId } - already exists in .roomodes ` ) ) ;
2025-06-14 15:06:41 -05:00
continue ;
}
// Read agent file to extract all information
2025-06-28 02:22:57 -05:00
const agentPath = await this . findAgentPath ( agentId , installDir ) ;
2025-06-14 15:06:41 -05:00
2025-06-28 02:22:57 -05:00
if ( agentPath ) {
2025-06-14 15:06:41 -05:00
const agentContent = await fileManager . readFile ( agentPath ) ;
// Extract YAML content
const yamlMatch = agentContent . match ( /```ya?ml\n([\s\S]*?)```/ ) ;
if ( yamlMatch ) {
const yaml = yamlMatch [ 1 ] ;
// Extract agent info from YAML
const titleMatch = yaml . match ( /title:\s*(.+)/ ) ;
const iconMatch = yaml . match ( /icon:\s*(.+)/ ) ;
const whenToUseMatch = yaml . match ( /whenToUse:\s*"(.+)"/ ) ;
2025-06-14 16:38:37 -05:00
const roleDefinitionMatch = yaml . match ( /roleDefinition:\s*"(.+)"/ ) ;
2025-06-14 15:06:41 -05:00
2025-06-28 01:01:26 -05:00
const title = titleMatch ? titleMatch [ 1 ] . trim ( ) : await this . getAgentTitle ( agentId , installDir ) ;
2025-06-14 15:06:41 -05:00
const icon = iconMatch ? iconMatch [ 1 ] . trim ( ) : "🤖" ;
2025-06-19 12:55:16 -05:00
const whenToUse = whenToUseMatch ? whenToUseMatch [ 1 ] . trim ( ) : ` Use for ${ title } tasks ` ;
2025-06-14 16:38:37 -05:00
const roleDefinition = roleDefinitionMatch
? roleDefinitionMatch [ 1 ] . trim ( )
: ` You are a ${ title } specializing in ${ title . toLowerCase ( ) } tasks and responsibilities. ` ;
2025-06-14 18:11:16 -05:00
// Build mode entry with proper formatting (matching exact indentation)
2025-06-14 16:38:37 -05:00
newModesContent += ` - slug: bmad- ${ agentId } \n ` ;
newModesContent += ` name: ' ${ icon } ${ title } ' \n ` ;
newModesContent += ` roleDefinition: ${ roleDefinition } \n ` ;
newModesContent += ` whenToUse: ${ whenToUse } \n ` ;
2025-06-28 02:22:57 -05:00
// Get relative path from installDir to agent file
const relativePath = path . relative ( installDir , agentPath ) . replace ( /\\/g , '/' ) ;
newModesContent += ` customInstructions: CRITICAL Read the full YML from ${ relativePath } start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode \n ` ;
2025-06-14 16:38:37 -05:00
newModesContent += ` groups: \n ` ;
newModesContent += ` - read \n ` ;
2025-06-17 17:51:52 +02:00
2025-06-14 16:38:37 -05:00
// Add permissions based on agent type
const permissions = agentPermissions [ agentId ] ;
if ( permissions ) {
newModesContent += ` - - edit \n ` ;
newModesContent += ` - fileRegex: ${ permissions . fileRegex } \n ` ;
newModesContent += ` description: ${ permissions . description } \n ` ;
} else {
newModesContent += ` - edit \n ` ;
}
2025-06-14 15:06:41 -05:00
2025-06-19 12:55:16 -05:00
console . log ( chalk . green ( ` ✓ Added mode: bmad- ${ agentId } ( ${ icon } ${ title } ) ` ) ) ;
2025-06-14 15:06:41 -05:00
}
}
}
// Build final roomodes content
let roomodesContent = "" ;
if ( existingContent ) {
// If there's existing content, append new modes to it
roomodesContent = existingContent . trim ( ) + "\n" + newModesContent ;
} else {
// Create new .roomodes file with proper YAML structure
2025-06-14 16:38:37 -05:00
roomodesContent = "customModes:\n" + newModesContent ;
2025-06-14 15:06:41 -05:00
}
// Write .roomodes file
await fileManager . writeFile ( roomodesPath , roomodesContent ) ;
2025-06-17 17:51:52 +02:00
console . log ( chalk . green ( "✓ Created .roomodes file in project root" ) ) ;
2025-06-14 15:06:41 -05:00
console . log ( chalk . green ( ` \n ✓ Roo Code setup complete! ` ) ) ;
2025-06-19 12:55:16 -05:00
console . log ( chalk . dim ( "Custom modes will be available when you open this project in Roo Code" ) ) ;
2025-06-14 15:06:41 -05:00
return true ;
}
2025-06-24 04:47:21 +02:00
async setupCline ( installDir , selectedAgent ) {
const clineRulesDir = path . join ( installDir , ".clinerules" ) ;
const agents = selectedAgent ? [ selectedAgent ] : await this . getAllAgentIds ( installDir ) ;
await fileManager . ensureDirectory ( clineRulesDir ) ;
2025-06-28 02:22:57 -05:00
// Load dynamic agent ordering from configuration
const config = await this . loadIdeAgentConfig ( ) ;
const agentOrder = config [ 'cline-order' ] || { } ;
2025-06-24 04:47:21 +02:00
for ( const agentId of agents ) {
2025-06-28 02:22:57 -05:00
// Find the agent file
const agentPath = await this . findAgentPath ( agentId , installDir ) ;
2025-06-24 04:47:21 +02:00
2025-06-28 02:22:57 -05:00
if ( agentPath ) {
2025-06-24 04:47:21 +02:00
const agentContent = await fileManager . readFile ( agentPath ) ;
// Get numeric prefix for ordering
const order = agentOrder [ agentId ] || 99 ;
const prefix = order . toString ( ) . padStart ( 2 , '0' ) ;
const mdPath = path . join ( clineRulesDir , ` ${ prefix } - ${ agentId } .md ` ) ;
// Create MD content for Cline (focused on project standards and role)
2025-06-28 01:01:26 -05:00
let mdContent = ` # ${ await this . getAgentTitle ( agentId , installDir ) } Agent \n \n ` ;
mdContent += ` This rule defines the ${ await this . getAgentTitle ( agentId , installDir ) } persona and project standards. \n \n ` ;
2025-06-24 04:47:21 +02:00
mdContent += "## Role Definition\n\n" ;
mdContent +=
"When the user types `@" + agentId + "`, adopt this persona and follow these guidelines:\n\n" ;
mdContent += "```yml\n" ;
// Extract just the YAML content from the agent file
const yamlMatch = agentContent . match ( /```ya?ml\n([\s\S]*?)```/ ) ;
if ( yamlMatch ) {
mdContent += yamlMatch [ 1 ] . trim ( ) ;
} else {
// If no YAML found, include the whole content minus the header
mdContent += agentContent . replace ( /^#.*$/m , "" ) . trim ( ) ;
}
mdContent += "\n```\n\n" ;
mdContent += "## Project Standards\n\n" ;
mdContent += ` - Always maintain consistency with project documentation in .bmad-core/ \n ` ;
mdContent += ` - Follow the agent's specific guidelines and constraints \n ` ;
mdContent += ` - Update relevant project files when making changes \n ` ;
2025-06-28 02:22:57 -05:00
const relativePath = path . relative ( installDir , agentPath ) . replace ( /\\/g , '/' ) ;
mdContent += ` - Reference the complete agent definition in [ ${ relativePath } ]( ${ relativePath } ) \n \n ` ;
2025-06-24 04:47:21 +02:00
mdContent += "## Usage\n\n" ;
2025-06-28 01:01:26 -05:00
mdContent += ` Type \` @ ${ agentId } \` to activate this ${ await this . getAgentTitle ( agentId , installDir ) } persona. \n ` ;
2025-06-24 04:47:21 +02:00
await fileManager . writeFile ( mdPath , mdContent ) ;
console . log ( chalk . green ( ` ✓ Created rule: ${ prefix } - ${ agentId } .md ` ) ) ;
}
}
console . log ( chalk . green ( ` \n ✓ Created Cline rules in ${ clineRulesDir } ` ) ) ;
return true ;
}
2025-06-26 09:33:58 +07:00
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
2025-06-28 02:22:57 -05:00
const agentPath = await this . findAgentPath ( agentId , installDir ) ;
2025-06-26 09:33:58 +07:00
2025-06-28 02:22:57 -05:00
if ( agentPath ) {
2025-06-26 09:33:58 +07:00
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 } ` ) ) ;
2025-06-28 01:26:28 +02:00
// Add GEMINI.md to the context files array
agentContextFiles . push ( "GEMINI.md" ) ;
2025-06-26 09:33:58 +07:00
// 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 ;
}
2025-06-12 22:38:24 -05:00
}
2025-06-14 15:06:41 -05:00
module . exports = new IdeSetup ( ) ;