2025-06-12 22:38:24 -05:00
const path = require ( 'path' ) ;
const fileManager = require ( './file-manager' ) ;
const configLoader = require ( './config-loader' ) ;
const chalk = require ( 'chalk' ) ;
class IdeSetup {
async setup ( ide , installDir , selectedAgent = null ) {
const ideConfig = await configLoader . getIdeConfiguration ( ide ) ;
if ( ! ideConfig ) {
console . log ( chalk . yellow ( ` \n No configuration available for ${ ide } ` ) ) ;
return false ;
}
switch ( ide ) {
case 'cursor' :
return this . setupCursor ( installDir , selectedAgent ) ;
case 'claude-code' :
return this . setupClaudeCode ( installDir , selectedAgent ) ;
case 'windsurf' :
return this . setupWindsurf ( installDir , selectedAgent ) ;
default :
console . log ( chalk . yellow ( ` \n IDE ${ ide } not yet supported ` ) ) ;
return false ;
}
}
async setupCursor ( installDir , selectedAgent ) {
const cursorRulesDir = path . join ( installDir , '.cursor' , 'rules' ) ;
const agents = selectedAgent ? [ selectedAgent ] : await this . getAllAgentIds ( installDir ) ;
await fileManager . ensureDirectory ( cursorRulesDir ) ;
for ( const agentId of agents ) {
2025-06-13 19:11:17 -05:00
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
let agentPath = path . join ( installDir , '.bmad-core' , 'agents' , ` ${ agentId } .md ` ) ;
2025-06-12 22:38:24 -05:00
if ( ! await fileManager . pathExists ( agentPath ) ) {
agentPath = path . join ( installDir , 'agents' , ` ${ agentId } .md ` ) ;
}
if ( await fileManager . pathExists ( agentPath ) ) {
const agentContent = await fileManager . readFile ( agentPath ) ;
const mdcPath = path . join ( cursorRulesDir , ` ${ agentId } .mdc ` ) ;
// Create MDC content with proper format
let mdcContent = '\n---\n' ;
mdcContent += 'description: \n' ;
mdcContent += 'globs: []\n' ;
mdcContent += 'alwaysApply: false\n' ;
mdcContent += '---\n\n' ;
mdcContent += ` # ${ agentId . toUpperCase ( ) } Agent Rule \n \n ` ;
mdcContent += ` This rule is triggered when the user types \` @ ${ agentId } \` and activates the ${ this . getAgentTitle ( agentId ) } 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' ;
// 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
mdcContent += agentContent . replace ( /^#.*$/m , '' ) . trim ( ) ;
}
mdcContent += '\n```\n\n' ;
mdcContent += '## File Reference\n\n' ;
2025-06-13 19:11:17 -05:00
mdcContent += ` The complete agent definition is available in [.bmad-core/agents/ ${ agentId } .md](mdc:.bmad-core/agents/ ${ agentId } .md). \n \n ` ;
2025-06-12 22:38:24 -05:00
mdcContent += '## Usage\n\n' ;
mdcContent += ` When the user types \` @ ${ agentId } \` , activate this ${ this . getAgentTitle ( agentId ) } persona and follow all instructions defined in the YML configuration above. \n ` ;
await fileManager . writeFile ( mdcPath , mdcContent ) ;
console . log ( chalk . green ( ` ✓ Created rule: ${ agentId } .mdc ` ) ) ;
}
}
console . log ( chalk . green ( ` \n ✓ Created Cursor rules in ${ cursorRulesDir } ` ) ) ;
return true ;
}
async setupClaudeCode ( installDir , selectedAgent ) {
const commandsDir = path . join ( installDir , '.claude' , 'commands' ) ;
const agents = selectedAgent ? [ selectedAgent ] : await this . getAllAgentIds ( installDir ) ;
await fileManager . ensureDirectory ( commandsDir ) ;
for ( const agentId of agents ) {
2025-06-13 19:11:17 -05:00
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
let agentPath = path . join ( installDir , '.bmad-core' , 'agents' , ` ${ agentId } .md ` ) ;
2025-06-12 22:38:24 -05:00
if ( ! await fileManager . pathExists ( agentPath ) ) {
agentPath = path . join ( installDir , 'agents' , ` ${ agentId } .md ` ) ;
}
const commandPath = path . join ( commandsDir , ` ${ agentId } .md ` ) ;
if ( await fileManager . pathExists ( agentPath ) ) {
// Create command file with agent content
const agentContent = await fileManager . readFile ( agentPath ) ;
// Add command header
let commandContent = ` # / ${ agentId } Command \n \n ` ;
commandContent += ` When this command is used, adopt the following agent persona: \n \n ` ;
commandContent += agentContent ;
await fileManager . writeFile ( commandPath , commandContent ) ;
console . log ( chalk . green ( ` ✓ Created command: / ${ agentId } ` ) ) ;
}
}
console . log ( chalk . green ( ` \n ✓ Created Claude Code commands in ${ commandsDir } ` ) ) ;
return true ;
}
async setupWindsurf ( installDir , selectedAgent ) {
const windsurfRulesDir = path . join ( installDir , '.windsurf' , 'rules' ) ;
const agents = selectedAgent ? [ selectedAgent ] : await this . getAllAgentIds ( installDir ) ;
await fileManager . ensureDirectory ( windsurfRulesDir ) ;
for ( const agentId of agents ) {
2025-06-13 19:11:17 -05:00
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
let agentPath = path . join ( installDir , '.bmad-core' , 'agents' , ` ${ agentId } .md ` ) ;
2025-06-12 22:38:24 -05:00
if ( ! await fileManager . pathExists ( agentPath ) ) {
agentPath = path . join ( installDir , 'agents' , ` ${ agentId } .md ` ) ;
}
if ( await fileManager . pathExists ( agentPath ) ) {
const agentContent = await fileManager . readFile ( agentPath ) ;
const mdPath = path . join ( windsurfRulesDir , ` ${ agentId } .md ` ) ;
// Create MD content (similar to Cursor but without frontmatter)
let mdContent = ` # ${ agentId . toUpperCase ( ) } Agent Rule \n \n ` ;
mdContent += ` This rule is triggered when the user types \` @ ${ agentId } \` and activates the ${ this . getAgentTitle ( agentId ) } 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' ;
// 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 += '## File Reference\n\n' ;
2025-06-13 19:11:17 -05:00
mdContent += ` The complete agent definition is available in [.bmad-core/agents/ ${ agentId } .md](.bmad-core/agents/ ${ agentId } .md). \n \n ` ;
2025-06-12 22:38:24 -05:00
mdContent += '## Usage\n\n' ;
mdContent += ` When the user types \` @ ${ agentId } \` , activate this ${ this . getAgentTitle ( agentId ) } persona and follow all instructions defined in the YML configuration above. \n ` ;
await fileManager . writeFile ( mdPath , mdContent ) ;
console . log ( chalk . green ( ` ✓ Created rule: ${ agentId } .md ` ) ) ;
}
}
console . log ( chalk . green ( ` \n ✓ Created Windsurf rules in ${ windsurfRulesDir } ` ) ) ;
return true ;
}
async getAllAgentIds ( installDir ) {
2025-06-13 19:11:17 -05:00
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
let agentsDir = path . join ( installDir , '.bmad-core' , 'agents' ) ;
2025-06-12 22:38:24 -05:00
if ( ! await fileManager . pathExists ( agentsDir ) ) {
agentsDir = path . join ( installDir , 'agents' ) ;
}
const glob = require ( 'glob' ) ;
const agentFiles = glob . sync ( '*.md' , { cwd : agentsDir } ) ;
return agentFiles . map ( file => path . basename ( file , '.md' ) ) ;
}
getAgentTitle ( agentId ) {
const agentTitles = {
'analyst' : 'Business Analyst' ,
'architect' : 'Solution Architect' ,
'bmad-master' : 'BMAD Master' ,
'bmad-orchestrator' : 'BMAD Orchestrator' ,
'dev' : 'Developer' ,
'pm' : 'Product Manager' ,
'po' : 'Product Owner' ,
'qa' : 'QA Specialist' ,
'sm' : 'Scrum Master' ,
'ux-expert' : 'UX Expert'
} ;
return agentTitles [ agentId ] || agentId ;
}
}
module . exports = new IdeSetup ( ) ;