@@ -49,7 +49,7 @@ const AI_IDE_EXTENSIONS = {
4949 { src : path . join ( projectRoot , "roo" , "RooFlow-main" , "config" , ".roo" , "system-prompt-debug" ) , destFolder : path . join ( ".roo" , "system-prompt-debug.yaml" ) } ,
5050 { src : path . join ( projectRoot , "roo" , "RooFlow-main" , "config" , ".roo" , "system-prompt-test" ) , destFolder : path . join ( ".roo" , "system-prompt-test.yaml" ) } ,
5151
52- { src : path . join ( projectRoot , "roo" , "RooFlow-main" , "default-system-prompt.md" ) , destFolder : path . join ( ".roo" , "default- system-prompt.md" ) } ,
52+ { src : path . join ( projectRoot , "roo" , "RooFlow-main" , "default-system-prompt.md" ) , destFolder : path . join ( ".roo" , "system-prompt-default .md" ) } ,
5353
5454 { src : path . join ( projectRoot , "roo" , "RooFlow-main" , "config" , "default-mode" , "cline_custom_modes.json" ) , destFolder : path . join ( ".roo" , "cline_custom_modes.json" ) } ,
5555 { src : path . join ( projectRoot , "roo" , "RooFlow-main" , "config" , "default-mode" , "custom-instructions.yaml" ) , destFolder : path . join ( ".roo" , "custom-instructions.yaml" ) } ,
@@ -65,15 +65,25 @@ const AI_IDE_EXTENSIONS = {
6565 ] ,
6666 } ,
6767 ".cline" : {
68- url : null ,
69- templateDir : null , // Placeholder for future template directory
70- filesToCopy : [ ] ,
68+ url : "https://codeload.github.com/GreatScottyMac/roo-code-memory-bank/zip/refs/heads/main" ,
69+ templateDir : path . join ( projectRoot , "cline" , "roo-code-memory-bank-main" ) ,
70+ filesToCopy : [
71+ { src : path . join ( projectRoot , "cline" , "roo-code-memory-bank-main" , ".clinerules-architect" ) , destFolder : path . join ( ".clinerules-architect" ) } ,
72+ { src : path . join ( projectRoot , "cline" , "roo-code-memory-bank-main" , ".clinerules-code" ) , destFolder : path . join ( ".clinerules-code" ) } ,
73+ { src : path . join ( projectRoot , "cline" , "roo-code-memory-bank-main" , ".clinerules-ask" ) , destFolder : path . join ( ".clinerules-ask" ) } ,
74+ { src : path . join ( projectRoot , "clione" , "roo-code-memory-bank-main" , ".clinerules-debug" ) , destFolder : path . join ( ".clinerules-debug" ) } ,
75+ { src : path . join ( projectRoot , "cline" , "roo-code-memory-bank-main" , ".clinerules-test" ) , destFolder : path . join ( ".clinerules-test" ) } ,
76+ { src : path . join ( projectRoot , "cline" , "roo-code-memory-bank-main" , ".roomodes" ) , destFolder : path . join ( ".roomodes" ) } ,
77+ ] ,
7178 additionalFilesToCopy : [ ] ,
7279 } ,
7380 ".windsurf" : {
74- url : null ,
75- templateDir : null , // Placeholder for future template directory
76- filesToCopy : [ ] ,
81+ url : "https://codeload.github.com/GreatScottyMac/cascade-memory-bank/zip/refs/heads/main" ,
82+ templateDir : path . join ( projectRoot , "windsurf" , "cascade-memory-bank-main" ) ,
83+ filesToCopy : [
84+ { src : path . join ( projectRoot , "windsurf" , "cascade-memory-bank-main" , ".windsurfrules" ) , destFolder : path . join ( ".windsurfrules" ) } ,
85+ { src : path . join ( projectRoot , "windsurf" , "cascade-memory-bank-main" , "global_rules.md" ) , dest : path . join ( os . homedir ( ) , ".codeium" , "windsurf" , "memories" , "global_rules.md" ) , isAbsolutePath : true } ,
86+ ] ,
7787 additionalFilesToCopy : [ ] ,
7888 } ,
7989 ".cursor" : {
@@ -133,14 +143,17 @@ function getFilesToCopy() {
133143 }
134144
135145 // Add filesToCopy for each tool
136- filesToCopy . forEach ( ( { src, destFolder } ) => {
146+ filesToCopy . forEach ( ( { src, dest , destFolder, isAbsolutePath } ) => {
137147 if ( fs . existsSync ( src ) ) {
138- if ( destFolder ) {
139- // Construct full destination path using projectRoot
140- const fullDestPath = destFolder ;
141- files . push ( { src, dest : fullDestPath } ) ;
148+ if ( dest ) {
149+ // If dest is directly provided, use it
150+ files . push ( { src, dest, isAbsolutePath } ) ;
151+ } else if ( destFolder ) {
152+ // For backward compatibility with destFolder
153+ console . log ( `${ YELLOW } Note${ RESET } : Using deprecated 'destFolder' property for: ${ src } ` ) ;
154+ files . push ( { src, dest : destFolder , isAbsolutePath } ) ;
142155 } else {
143- console . log ( `${ YELLOW } Warning${ RESET } : Destination folder is undefined for source file: ${ src } ` ) ;
156+ console . log ( `${ YELLOW } Warning${ RESET } : Destination is undefined for source file: ${ src } ` ) ;
144157 }
145158 } else {
146159 console . log ( `${ YELLOW } Warning${ RESET } : Source file does not exist: ${ src } for tool: ${ dir } ` ) ;
@@ -200,7 +213,21 @@ function findProjectRoot() {
200213 * @param {string } destPath - Destination path in the project
201214 * @returns {Promise } Promise that resolves to true if successful, false if file not found
202215 */
203- function copyFile ( srcPath , destPath ) {
216+ /**
217+ * Copies a file from template directory to the project or an absolute path
218+ * ------------------------------------------------------
219+ * This function handles copying a file from the local template directory
220+ * to the appropriate location in the project or to an absolute path.
221+ * It creates any necessary directories and handles various error conditions.
222+ * If the destination file already exists, it will not overwrite it.
223+ *
224+ * @param {string } srcPath - Source path in the template directory
225+ * @param {string } destPath - Destination path (relative to projectRoot or absolute)
226+ * @param {boolean } isAbsolutePath - Whether destPath is an absolute path
227+ * @param {boolean } forceOverwrite - Whether to overwrite existing files (default: false)
228+ * @returns {Promise } Promise that resolves to true if successful, false if file not found or skipped
229+ */
230+ function copyFile ( srcPath , destPath , isAbsolutePath = false , forceOverwrite = false ) {
204231 return new Promise ( ( resolve , reject ) => {
205232 // Check if source file exists
206233 if ( ! fs . existsSync ( srcPath ) ) {
@@ -211,21 +238,36 @@ function copyFile(srcPath, destPath) {
211238 return ;
212239 }
213240
214- console . log ( ` Copying ${ BLUE } ${ srcPath } ${ RESET } to ${ destPath } ...` ) ;
241+ // Determine the full destination path based on whether it's absolute or relative
242+ const destFullPath = isAbsolutePath ? destPath : path . join ( projectRoot , destPath ) ;
243+
244+ // Check if destination file already exists
245+ if ( fs . existsSync ( destFullPath ) && ! forceOverwrite ) {
246+ console . log ( ` ${ BLUE } Skipping${ RESET } : File already exists at ${ destFullPath } ` ) ;
247+ console . log ( ` ${ YELLOW } Note${ RESET } : Preserving user modifications` ) ;
248+ resolve ( true ) ; // Still return true as this is an expected condition
249+ return ;
250+ }
215251
216- const destFullPath = path . join ( projectRoot , destPath ) ;
252+ console . log ( ` Copying ${ BLUE } ${ srcPath } ${ RESET } to ${ destPath } ...` ) ;
217253
218254 // Create directories if they don't exist
219255 const destDir = path . dirname ( destFullPath ) ;
220256 if ( ! fs . existsSync ( destDir ) ) {
221- fs . mkdirSync ( destDir , { recursive : true } ) ;
257+ try {
258+ fs . mkdirSync ( destDir , { recursive : true } ) ;
259+ console . log ( ` Created directory: ${ destDir } ` ) ;
260+ } catch ( dirError ) {
261+ console . error ( ` ${ RED } Error${ RESET } : Failed to create directory ${ destDir } : ${ dirError . message } ` ) ;
262+ resolve ( false ) ;
263+ return ;
264+ }
222265 }
223266
224- console . log ( ` Copying ${ BLUE } ${ destPath } ${ RESET } ...` ) ;
225-
226267 try {
227268 // Perform the file copy
228269 fs . copyFileSync ( srcPath , destFullPath ) ;
270+ console . log ( ` Successfully copied to ${ destFullPath } ` ) ;
229271 resolve ( true ) ;
230272 } catch ( error ) {
231273 reject (
@@ -251,7 +293,7 @@ function copyFile(srcPath, destPath) {
251293 */
252294function runInsertVariablesScript ( ) {
253295 console . log (
254- "\nRunning insert-variables script to configure system variables..."
296+ "\nRunning insert-variables script to configure system variables ..."
255297 ) ;
256298
257299 const isWindows = process . platform === "win32" ;
@@ -400,7 +442,7 @@ function validateAllToolConfigurations() {
400442
401443 // First attempt to fix any duplicate capabilities sections
402444 // This is a common issue when updating from older versions
403- console . log ( "\nChecking and fixing system prompt files..." ) ;
445+ console . log ( "\nChecking and fixing system prompt files ..." ) ;
404446 const fixResult = sanitizerScript . fixDuplicateCapabilities ( findProjectRoot ( ) ) ;
405447
406448 if ( ! fixResult ) {
@@ -411,7 +453,7 @@ function validateAllToolConfigurations() {
411453 }
412454
413455 // Then validate YAML structure across all files
414- console . log ( "\nValidating YAML structure in all configuration files..." ) ;
456+ console . log ( "\nValidating YAML structure in all configuration files ..." ) ;
415457 const validationSuccess = sanitizerScript . validateFilesWithYaml (
416458 findProjectRoot ( )
417459 ) ;
@@ -433,7 +475,7 @@ function validateAllToolConfigurations() {
433475 * @returns {boolean } True if template directories exist
434476 */
435477function verifyTemplateDirectories ( ) {
436- console . log ( "Verifying template directories..." ) ;
478+ console . log ( "Verifying template directories ..." ) ;
437479 let valid = true ;
438480
439481 // Check tool-specific templates
@@ -477,7 +519,7 @@ function verifyTemplateDirectories() {
477519 * @returns {Promise<void> }
478520 */
479521async function downloadAndUnpackAIExtension ( extension ) {
480- const { url, configPath } = AI_IDE_EXTENSIONS [ extension ] ;
522+ const { url } = AI_IDE_EXTENSIONS [ extension ] ;
481523
482524 // Skip processing if the URL is null
483525 if ( ! url ) {
@@ -490,7 +532,7 @@ async function downloadAndUnpackAIExtension(extension) {
490532 const zipPath = path . join ( projectRoot , `${ extension . substring ( 1 ) } .zip` ) ;
491533 const extractPath = path . join ( projectRoot , extension . substring ( 1 ) ) ;
492534
493- console . log ( `Downloading and unpacking ${ extension } ...` ) ;
535+ console . log ( `Downloading and unpacking ${ extension } ...` ) ;
494536
495537 try {
496538 console . log ( `Downloading from ${ url } to ${ zipPath } ` ) ;
@@ -551,15 +593,20 @@ async function downloadAndUnpackAIExtension(extension) {
551593 } ) ;
552594
553595 console . log ( `Extracting ${ extension } .zip to ${ extractPath } ` ) ;
554- const zip = new AdmZip ( zipPath ) ;
555- zip . extractAllTo ( extractPath , true ) ;
556- console . log (
557- `Unpacked ${ extension } .zip to ${ extension . substring (
558- 1
559- ) } folder successfully`
560- ) ;
561- fs . unlinkSync ( zipPath ) ;
562- console . log ( `Deleted ${ extension } .zip successfully` ) ;
596+ try {
597+ const zip = new AdmZip ( zipPath ) ;
598+ zip . extractAllTo ( extractPath , true ) ;
599+ console . log ( `Unpacked ${ extension } .zip to ${ extension . substring ( 1 ) } folder successfully` ) ;
600+
601+ // Delete the zip file if it exists
602+ if ( fs . existsSync ( zipPath ) ) {
603+ fs . unlinkSync ( zipPath ) ;
604+ console . log ( `Deleted ${ extension } .zip successfully` ) ;
605+ }
606+ } catch ( extractError ) {
607+ console . error ( `Error extracting ${ extension } .zip: ${ extractError . message } ` ) ;
608+ // Don't rethrow, let the outer catch handle it
609+ }
563610 } catch ( error ) {
564611 console . error (
565612 `Error downloading and unpacking ${ extension } : ${ error . message } `
@@ -571,8 +618,12 @@ async function downloadAndUnpackAIExtension(extension) {
571618 * Main installation function
572619 * --------------------------
573620 * Updated to handle multiple AI IDE extensions dynamically.
621+ *
622+ * @param {Object } options - Installation options
623+ * @param {boolean } options.forceOverwrite - Whether to force overwrite existing files
624+ * @returns {Promise<boolean> } - Whether installation was successful
574625 */
575- async function install ( ) {
626+ async function install ( options = { forceOverwrite : false } ) {
576627 console . log ( `${ BLUE } RooFlow Installer${ RESET } ` ) ;
577628
578629 // Print installation context information (useful for debugging)
@@ -599,13 +650,13 @@ async function install() {
599650 const FILES = getFilesToCopy ( ) ;
600651
601652 // Copy all files from templates
602- console . log ( "Copying template files..." ) ;
653+ console . log ( "Copying template files ..." ) ;
603654 let copySuccess = true ;
604655
605656 try {
606657 // Process each file in the copy list
607658 for ( const file of FILES ) {
608- const success = await copyFile ( file . src , file . dest ) ;
659+ const success = await copyFile ( file . src , file . dest , file . isAbsolutePath , options . forceOverwrite ) ;
609660
610661 if ( ! success ) {
611662 console . log ( ` ${ YELLOW } Warning: Failed to copy ${ file . src } ${ RESET } ` ) ;
@@ -713,19 +764,80 @@ async function install() {
713764 }
714765}
715766
767+ /**
768+ * Parse command line arguments
769+ * ----------------------------
770+ * Parses command line arguments to determine installation options.
771+ *
772+ * @returns {Object } Options object with parsed command line arguments
773+ */
774+ function parseCommandLineArgs ( ) {
775+ const args = process . argv . slice ( 2 ) ;
776+ const options = {
777+ forceOverwrite : false ,
778+ help : false
779+ } ;
780+
781+ for ( const arg of args ) {
782+ if ( arg === '--force' || arg === '-f' ) {
783+ options . forceOverwrite = true ;
784+ } else if ( arg === '--help' || arg === '-h' ) {
785+ options . help = true ;
786+ }
787+ }
788+
789+ return options ;
790+ }
791+
792+ /**
793+ * Display help information
794+ * -----------------------
795+ * Shows usage information and available command line options.
796+ */
797+ function showHelp ( ) {
798+ console . log ( `
799+ ${ BLUE } RooFlow Installer Help${ RESET }
800+ Usage: node installer.js [options]
801+
802+ Options:
803+ -f, --force Force overwrite of existing files (by default, existing files are preserved)
804+ -h, --help Display this help message
805+
806+ Examples:
807+ node installer.js # Normal installation (preserves existing files)
808+ node installer.js --force # Force overwrite all files
809+ ` ) ;
810+ process . exit ( 0 ) ;
811+ }
812+
716813// Add error handler for better diagnostic information
717814process . on ( "uncaughtException" , ( error ) => {
718815 console . error ( `${ RED } Installer encountered an error:${ RESET } ` ) ;
719816 console . error ( error ) ;
720817 console . error ( "\nIf installation failed, you can run it manually:" ) ;
721818 console . error ( "- npm run setup" ) ;
722819 console . error ( "- node node_modules/vibecode/installer.js" ) ;
820+ console . error ( "- Add --force to overwrite existing files: node node_modules/vibecode/installer.js --force" ) ;
723821 process . exit ( 1 ) ;
724822} ) ;
725823
824+ // Parse command line arguments
825+ const options = parseCommandLineArgs ( ) ;
826+
827+ // Show help if requested
828+ if ( options . help ) {
829+ showHelp ( ) ;
830+ }
831+
726832// Run the installation
727- console . log ( "Installer running..." ) ;
728- install ( ) . catch ( ( error ) => {
833+ console . log ( "Installer running ..." ) ;
834+ if ( options . forceOverwrite ) {
835+ console . log ( `${ YELLOW } Force overwrite mode enabled${ RESET } - Existing files will be overwritten` ) ;
836+ } else {
837+ console . log ( `${ BLUE } Preserve mode enabled${ RESET } - Existing files will be kept` ) ;
838+ }
839+
840+ install ( options ) . catch ( ( error ) => {
729841 console . error ( `${ RED } Installation failed:${ RESET } ` , error ) ;
730842 process . exit ( 1 ) ;
731843} ) ;
0 commit comments