@@ -80,6 +80,22 @@ async function showMainMenu() {
8080async function createClaudeConfig ( options = { } ) {
8181 const targetDir = options . directory || process . cwd ( ) ;
8282
83+ // Handle individual component installation
84+ if ( options . agent ) {
85+ await installIndividualAgent ( options . agent , targetDir , options ) ;
86+ return ;
87+ }
88+
89+ if ( options . command ) {
90+ await installIndividualCommand ( options . command , targetDir , options ) ;
91+ return ;
92+ }
93+
94+ if ( options . mcp ) {
95+ await installIndividualMCP ( options . mcp , targetDir , options ) ;
96+ return ;
97+ }
98+
8399 // Handle command stats analysis (both singular and plural)
84100 if ( options . commandStats || options . commandsStats ) {
85101 await runCommandStats ( options ) ;
@@ -253,4 +269,171 @@ async function createClaudeConfig(options = {}) {
253269 }
254270}
255271
272+ // Individual component installation functions
273+ async function installIndividualAgent ( agentName , targetDir , options ) {
274+ console . log ( chalk . blue ( `🤖 Installing agent: ${ agentName } ` ) ) ;
275+
276+ try {
277+ // Check if components directory exists
278+ const componentsPath = path . join ( __dirname , '..' , 'components' , 'agents' ) ;
279+ const agentFile = path . join ( componentsPath , `${ agentName } .md` ) ;
280+
281+ if ( ! await fs . pathExists ( agentFile ) ) {
282+ console . log ( chalk . red ( `❌ Agent "${ agentName } " not found` ) ) ;
283+ console . log ( chalk . yellow ( 'Available agents:' ) ) ;
284+
285+ // List available agents
286+ if ( await fs . pathExists ( componentsPath ) ) {
287+ const agents = await fs . readdir ( componentsPath ) ;
288+ agents . filter ( f => f . endsWith ( '.md' ) ) . forEach ( agent => {
289+ console . log ( chalk . cyan ( ` - ${ agent . replace ( '.md' , '' ) } ` ) ) ;
290+ } ) ;
291+ }
292+ return ;
293+ }
294+
295+ // For agents, they are typically part of templates, so we need to determine
296+ // the appropriate language/framework and install the complete template
297+ const agentContent = await fs . readFile ( agentFile , 'utf8' ) ;
298+ const language = extractLanguageFromAgent ( agentContent , agentName ) ;
299+ const framework = extractFrameworkFromAgent ( agentContent , agentName ) ;
300+
301+ console . log ( chalk . yellow ( `📝 Agent "${ agentName } " is part of ${ language } /${ framework } template` ) ) ;
302+ console . log ( chalk . blue ( '🚀 Installing complete template with this agent...' ) ) ;
303+
304+ // Install the template that contains this agent (avoid recursion)
305+ const setupOptions = {
306+ ...options ,
307+ language,
308+ framework,
309+ yes : true ,
310+ targetDirectory : targetDir ,
311+ agent : null // Remove agent to avoid recursion
312+ } ;
313+ delete setupOptions . agent ;
314+ await createClaudeConfig ( setupOptions ) ;
315+
316+ console . log ( chalk . green ( `✅ Agent "${ agentName } " installed successfully!` ) ) ;
317+
318+ } catch ( error ) {
319+ console . log ( chalk . red ( `❌ Error installing agent: ${ error . message } ` ) ) ;
320+ }
321+ }
322+
323+ async function installIndividualCommand ( commandName , targetDir , options ) {
324+ console . log ( chalk . blue ( `⚡ Installing command: ${ commandName } ` ) ) ;
325+
326+ try {
327+ // Check if components directory exists
328+ const componentsPath = path . join ( __dirname , '..' , 'components' , 'commands' ) ;
329+ const commandFile = path . join ( componentsPath , `${ commandName } .md` ) ;
330+
331+ if ( ! await fs . pathExists ( commandFile ) ) {
332+ console . log ( chalk . red ( `❌ Command "${ commandName } " not found` ) ) ;
333+ console . log ( chalk . yellow ( 'Available commands:' ) ) ;
334+
335+ // List available commands
336+ if ( await fs . pathExists ( componentsPath ) ) {
337+ const commands = await fs . readdir ( componentsPath ) ;
338+ commands . filter ( f => f . endsWith ( '.md' ) ) . forEach ( command => {
339+ console . log ( chalk . cyan ( ` - ${ command . replace ( '.md' , '' ) } ` ) ) ;
340+ } ) ;
341+ }
342+ return ;
343+ }
344+
345+ // Create .claude/commands directory if it doesn't exist
346+ const commandsDir = path . join ( targetDir , '.claude' , 'commands' ) ;
347+ await fs . ensureDir ( commandsDir ) ;
348+
349+ // Copy the command file
350+ const targetFile = path . join ( commandsDir , `${ commandName } .md` ) ;
351+ await fs . copy ( commandFile , targetFile ) ;
352+
353+ console . log ( chalk . green ( `✅ Command "${ commandName } " installed successfully!` ) ) ;
354+ console . log ( chalk . cyan ( `📁 Installed to: ${ path . relative ( targetDir , targetFile ) } ` ) ) ;
355+
356+ } catch ( error ) {
357+ console . log ( chalk . red ( `❌ Error installing command: ${ error . message } ` ) ) ;
358+ }
359+ }
360+
361+ async function installIndividualMCP ( mcpName , targetDir , options ) {
362+ console . log ( chalk . blue ( `🔌 Installing MCP: ${ mcpName } ` ) ) ;
363+
364+ try {
365+ // Check if components directory exists
366+ const componentsPath = path . join ( __dirname , '..' , 'components' , 'mcps' ) ;
367+ const mcpFile = path . join ( componentsPath , `${ mcpName } .json` ) ;
368+
369+ if ( ! await fs . pathExists ( mcpFile ) ) {
370+ console . log ( chalk . red ( `❌ MCP "${ mcpName } " not found` ) ) ;
371+ console . log ( chalk . yellow ( 'Available MCPs:' ) ) ;
372+
373+ // List available MCPs
374+ if ( await fs . pathExists ( componentsPath ) ) {
375+ const mcps = await fs . readdir ( componentsPath ) ;
376+ mcps . filter ( f => f . endsWith ( '.json' ) ) . forEach ( mcp => {
377+ console . log ( chalk . cyan ( ` - ${ mcp . replace ( '.json' , '' ) } ` ) ) ;
378+ } ) ;
379+ }
380+ return ;
381+ }
382+
383+ // Read the MCP configuration
384+ const mcpConfig = await fs . readJson ( mcpFile ) ;
385+
386+ // Check if .mcp.json exists in target directory
387+ const targetMcpFile = path . join ( targetDir , '.mcp.json' ) ;
388+ let existingConfig = { } ;
389+
390+ if ( await fs . pathExists ( targetMcpFile ) ) {
391+ existingConfig = await fs . readJson ( targetMcpFile ) ;
392+ console . log ( chalk . yellow ( '📝 Existing .mcp.json found, merging configurations...' ) ) ;
393+ }
394+
395+ // Merge configurations
396+ const mergedConfig = {
397+ ...existingConfig ,
398+ ...mcpConfig
399+ } ;
400+
401+ // Write the merged configuration
402+ await fs . writeJson ( targetMcpFile , mergedConfig , { spaces : 2 } ) ;
403+
404+ console . log ( chalk . green ( `✅ MCP "${ mcpName } " installed successfully!` ) ) ;
405+ console . log ( chalk . cyan ( `📁 Configuration merged into: ${ path . relative ( targetDir , targetMcpFile ) } ` ) ) ;
406+
407+ } catch ( error ) {
408+ console . log ( chalk . red ( `❌ Error installing MCP: ${ error . message } ` ) ) ;
409+ }
410+ }
411+
412+ // Helper functions to extract language/framework from agent content
413+ function extractLanguageFromAgent ( content , agentName ) {
414+ // Try to determine language from agent content or filename
415+ if ( agentName . includes ( 'react' ) || content . includes ( 'React' ) ) return 'javascript-typescript' ;
416+ if ( agentName . includes ( 'django' ) || content . includes ( 'Django' ) ) return 'python' ;
417+ if ( agentName . includes ( 'fastapi' ) || content . includes ( 'FastAPI' ) ) return 'python' ;
418+ if ( agentName . includes ( 'flask' ) || content . includes ( 'Flask' ) ) return 'python' ;
419+ if ( agentName . includes ( 'rails' ) || content . includes ( 'Rails' ) ) return 'ruby' ;
420+ if ( agentName . includes ( 'api-security' ) || content . includes ( 'API security' ) ) return 'javascript-typescript' ;
421+ if ( agentName . includes ( 'database' ) || content . includes ( 'database' ) ) return 'javascript-typescript' ;
422+
423+ // Default to javascript-typescript for general agents
424+ return 'javascript-typescript' ;
425+ }
426+
427+ function extractFrameworkFromAgent ( content , agentName ) {
428+ // Try to determine framework from agent content or filename
429+ if ( agentName . includes ( 'react' ) || content . includes ( 'React' ) ) return 'react' ;
430+ if ( agentName . includes ( 'django' ) || content . includes ( 'Django' ) ) return 'django' ;
431+ if ( agentName . includes ( 'fastapi' ) || content . includes ( 'FastAPI' ) ) return 'fastapi' ;
432+ if ( agentName . includes ( 'flask' ) || content . includes ( 'Flask' ) ) return 'flask' ;
433+ if ( agentName . includes ( 'rails' ) || content . includes ( 'Rails' ) ) return 'rails' ;
434+
435+ // For general agents, return none to install the base template
436+ return 'none' ;
437+ }
438+
256439module . exports = { createClaudeConfig, showMainMenu } ;
0 commit comments