Skip to content

Commit e66d85b

Browse files
committed
Added Windsurf and Cline support
1 parent 358933c commit e66d85b

File tree

2 files changed

+157
-40
lines changed

2 files changed

+157
-40
lines changed

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,9 @@ roo/
4545

4646
.DS_Store
4747
RooFlow.zip
48-
RooFlow/
48+
RooFlow/
49+
.clinerules-architect
50+
.clinerules-ask
51+
.clinerules-code
52+
.clinerules-test
53+
.windsurfrules

installer.js

Lines changed: 151 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
252294
function 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
*/
435477
function 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
*/
479521
async 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
717814
process.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

Comments
 (0)