@@ -493,7 +493,7 @@ export class CustomModesManager {
493493 }
494494 }
495495
496- public async consolidateRulesForMode ( slug : string ) : Promise < { success : boolean ; error ?: string } > {
496+ public async exportModeWithRules ( slug : string ) : Promise < { success : boolean ; yaml ?: string ; error ?: string } > {
497497 try {
498498 // Get all current modes
499499 const allModes = await this . getCustomModes ( )
@@ -536,73 +536,108 @@ export class CustomModesManager {
536536 // Check for .roo/rules-{slug}/ directory
537537 const modeRulesDir = path . join ( workspacePath , ".roo" , `rules-${ slug } ` )
538538
539+ let rulesFiles : Array < { relativePath : string ; content : string } > = [ ]
539540 try {
540541 const stats = await fs . stat ( modeRulesDir )
541- if ( ! stats . isDirectory ( ) ) {
542- return { success : false , error : "Rules directory not found" }
543- }
544- } catch ( error ) {
545- return { success : false , error : "Rules directory not found" }
546- }
547-
548- // Extract content specific to this mode by looking for the mode-specific rules
549- let modeSpecificRules = ""
550- try {
551- const entries = await fs . readdir ( modeRulesDir , { withFileTypes : true , recursive : true } )
552- const files : Array < { filename : string ; content : string } > = [ ]
553-
554- for ( const entry of entries ) {
555- if ( entry . isFile ( ) ) {
556- const filePath = path . join ( entry . parentPath || modeRulesDir , entry . name )
557- const content = await fs . readFile ( filePath , "utf-8" )
558- if ( content . trim ( ) ) {
559- files . push ( { filename : filePath , content : content . trim ( ) } )
542+ if ( stats . isDirectory ( ) ) {
543+ // Extract content specific to this mode by looking for the mode-specific rules
544+ const entries = await fs . readdir ( modeRulesDir , { withFileTypes : true , recursive : true } )
545+
546+ for ( const entry of entries ) {
547+ if ( entry . isFile ( ) ) {
548+ const filePath = path . join ( entry . parentPath || modeRulesDir , entry . name )
549+ const content = await fs . readFile ( filePath , "utf-8" )
550+ if ( content . trim ( ) ) {
551+ // Calculate relative path from .roo directory
552+ const relativePath = path . relative ( path . join ( workspacePath , ".roo" ) , filePath )
553+ rulesFiles . push ( { relativePath, content : content . trim ( ) } )
554+ }
560555 }
561556 }
562557 }
558+ } catch ( error ) {
559+ // Directory doesn't exist, which is fine - mode might not have rules
560+ }
563561
564- if ( files . length === 0 ) {
565- return { success : false , error : "No rule files found in the directory" }
566- }
562+ // Create an export mode with rules files preserved
563+ const exportMode : ModeConfig & { rulesFiles ?: Array < { relativePath : string ; content : string } > } = {
564+ ...mode ,
565+ // Remove source property for export
566+ source : undefined as any ,
567+ }
567568
568- // Format the content without filename headers to avoid confusing the LLM
569- modeSpecificRules = files . map ( ( file ) => file . content ) . join ( "\n\n" )
570- } catch ( error ) {
571- return { success : false , error : "Failed to read rule files" }
569+ // Add rules files if any exist
570+ if ( rulesFiles . length > 0 ) {
571+ exportMode . rulesFiles = rulesFiles
572572 }
573573
574- if ( ! modeSpecificRules ) {
575- return { success : false , error : "No rule content found" }
574+ // Generate YAML
575+ const exportData = {
576+ customModes : [ exportMode ] ,
576577 }
577578
578- // Combine existing custom instructions with the consolidated rules
579- const existingInstructions = mode . customInstructions || ""
580- const separator = existingInstructions ? "\n\n" : ""
581- const newInstructions = existingInstructions + separator + modeSpecificRules
579+ const yamlContent = yaml . stringify ( exportData )
582580
583- // Update the mode with the new instructions
584- const updatedMode : ModeConfig = {
585- ...mode ,
586- customInstructions : newInstructions ,
581+ return { success : true , yaml : yamlContent }
582+ } catch ( error ) {
583+ const errorMessage = error instanceof Error ? error . message : String ( error )
584+ logger . error ( "Failed to export mode with rules" , { slug, error : errorMessage } )
585+ return { success : false , error : errorMessage }
586+ }
587+ }
588+
589+ public async importModeWithRules ( yamlContent : string ) : Promise < { success : boolean ; error ?: string } > {
590+ try {
591+ // Parse the YAML content
592+ const importData = yaml . parse ( yamlContent )
593+
594+ if (
595+ ! importData ?. customModes ||
596+ ! Array . isArray ( importData . customModes ) ||
597+ importData . customModes . length === 0
598+ ) {
599+ return { success : false , error : "Invalid import format: no custom modes found" }
600+ }
601+
602+ const workspacePath = getWorkspacePath ( )
603+ if ( ! workspacePath ) {
604+ return { success : false , error : "No workspace found" }
587605 }
588606
589- await this . updateCustomMode ( slug , updatedMode )
607+ // Process each mode in the import
608+ for ( const importMode of importData . customModes ) {
609+ const { rulesFiles, ...modeConfig } = importMode
590610
591- // Remove the source directory after successful consolidation
592- try {
593- await fs . rm ( modeRulesDir , { recursive : true , force : true } )
594- } catch ( removeError ) {
595- // Log the error but don't fail the operation since consolidation was successful
596- logger . error ( "Warning: Could not remove rules directory after consolidation" , {
597- directory : modeRulesDir ,
598- error : removeError instanceof Error ? removeError . message : String ( removeError ) ,
611+ // Import the mode configuration
612+ await this . updateCustomMode ( importMode . slug , {
613+ ...modeConfig ,
614+ source : "project" , // Always import as project mode
599615 } )
616+
617+ // Import rules files if they exist
618+ if ( rulesFiles && Array . isArray ( rulesFiles ) ) {
619+ for ( const ruleFile of rulesFiles ) {
620+ if ( ruleFile . relativePath && ruleFile . content ) {
621+ const targetPath = path . join ( workspacePath , ".roo" , ruleFile . relativePath )
622+
623+ // Ensure directory exists
624+ const targetDir = path . dirname ( targetPath )
625+ await fs . mkdir ( targetDir , { recursive : true } )
626+
627+ // Write the file
628+ await fs . writeFile ( targetPath , ruleFile . content , "utf-8" )
629+ }
630+ }
631+ }
600632 }
601633
634+ // Refresh the modes after import
635+ await this . refreshMergedState ( )
636+
602637 return { success : true }
603638 } catch ( error ) {
604639 const errorMessage = error instanceof Error ? error . message : String ( error )
605- logger . error ( "Failed to consolidate rules for mode " , { slug , error : errorMessage } )
640+ logger . error ( "Failed to import mode with rules " , { error : errorMessage } )
606641 return { success : false , error : errorMessage }
607642 }
608643 }
0 commit comments