update MCP responses, centralize rules profiles & helpers
This commit is contained in:
@@ -73,6 +73,7 @@ import {
|
||||
getApiKeyStatusReport
|
||||
} from './task-manager/models.js';
|
||||
import { findProjectRoot } from './utils.js';
|
||||
import { convertAllRulesToBrandRules, removeBrandRules, BRAND_NAMES, isValidBrand, getBrandProfile } from './rule-transformer.js';
|
||||
|
||||
/**
|
||||
* Runs the interactive setup process for model configuration.
|
||||
@@ -511,38 +512,24 @@ function registerCommands(programInstance) {
|
||||
const expandedBrands = brands
|
||||
.flatMap((b) => b.split(',').map((s) => s.trim()))
|
||||
.filter(Boolean);
|
||||
|
||||
const removalResults = [];
|
||||
|
||||
for (const brand of expandedBrands) {
|
||||
let profile;
|
||||
try {
|
||||
// Use pathToFileURL for correct ESM dynamic import
|
||||
const { pathToFileURL } = await import('url');
|
||||
const profilePath = path.resolve(
|
||||
process.cwd(),
|
||||
'scripts',
|
||||
'profiles',
|
||||
`${brand}.js`
|
||||
);
|
||||
const profileModule = await import(pathToFileURL(profilePath).href);
|
||||
profile = profileModule.default || profileModule;
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Rules profile for brand "${brand}" not found. Skipping.`
|
||||
);
|
||||
console.warn(`Import error: ${e && e.message ? e.message : e}`);
|
||||
if (!isValidBrand(brand)) {
|
||||
console.warn(`Rules profile for brand "${brand}" not found. Valid brands: ${BRAND_NAMES.join(', ')}. Skipping.`);
|
||||
continue;
|
||||
}
|
||||
const profile = getBrandProfile(brand);
|
||||
|
||||
if (action === 'add') {
|
||||
const { convertAllRulesToBrandRules } = await import(
|
||||
'./rule-transformer.js'
|
||||
);
|
||||
convertAllRulesToBrandRules(projectDir, profile);
|
||||
if (typeof profile.onAddBrandRules === 'function') {
|
||||
profile.onAddBrandRules(projectDir);
|
||||
}
|
||||
} else if (action === 'remove') {
|
||||
const { removeBrandRules } = await import('./rule-transformer.js');
|
||||
removeBrandRules(projectDir, profile);
|
||||
const result = removeBrandRules(projectDir, profile);
|
||||
removalResults.push(result);
|
||||
if (typeof profile.onRemoveBrandRules === 'function') {
|
||||
profile.onRemoveBrandRules(projectDir);
|
||||
}
|
||||
@@ -551,6 +538,25 @@ function registerCommands(programInstance) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary for removals
|
||||
if (action === 'remove') {
|
||||
const successes = removalResults.filter(r => r.success).map(r => r.brandName);
|
||||
const skipped = removalResults.filter(r => r.skipped).map(r => r.brandName);
|
||||
const errors = removalResults.filter(r => r.error && !r.success && !r.skipped);
|
||||
|
||||
if (successes.length > 0) {
|
||||
console.log(chalk.green(`Successfully removed rules: ${successes.join(', ')}`));
|
||||
}
|
||||
if (skipped.length > 0) {
|
||||
console.log(chalk.yellow(`Skipped (default or protected): ${skipped.join(', ')}`));
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
errors.forEach(r => {
|
||||
console.log(chalk.red(`Error removing ${r.brandName}: ${r.error}`));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// parse-prd command
|
||||
|
||||
@@ -12,6 +12,27 @@ import { log } from './utils.js';
|
||||
// Import the shared MCP configuration helper
|
||||
import { setupMCPConfiguration } from './mcp-utils.js';
|
||||
|
||||
// --- Centralized Brand Helpers ---
|
||||
export const BRAND_NAMES = ['cursor', 'roo', 'windsurf'];
|
||||
|
||||
import * as cursorProfile from '../profiles/cursor.js';
|
||||
import * as rooProfile from '../profiles/roo.js';
|
||||
import * as windsurfProfile from '../profiles/windsurf.js';
|
||||
|
||||
export const BRAND_PROFILES = {
|
||||
cursor: cursorProfile,
|
||||
roo: rooProfile,
|
||||
windsurf: windsurfProfile
|
||||
};
|
||||
|
||||
export function isValidBrand(brand) {
|
||||
return BRAND_NAMES.includes(brand);
|
||||
}
|
||||
|
||||
export function getBrandProfile(brand) {
|
||||
return BRAND_PROFILES[brand];
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace basic Cursor terms with brand equivalents
|
||||
*/
|
||||
@@ -223,6 +244,7 @@ function convertAllRulesToBrandRules(projectDir, profile) {
|
||||
|
||||
return { success, failed };
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a brand's rules directory and, if empty, the parent brand folder (except .cursor)
|
||||
* @param {string} projectDir - The root directory of the project
|
||||
@@ -233,41 +255,63 @@ function removeBrandRules(projectDir, profile) {
|
||||
const { brandName, rulesDir } = profile;
|
||||
const brandRulesDir = path.join(projectDir, rulesDir);
|
||||
const brandDir = path.dirname(brandRulesDir);
|
||||
// Also remove the mcp.json file if it exists in the brand directory
|
||||
const mcpPath = path.join(brandDir, 'mcp.json');
|
||||
|
||||
const result = {
|
||||
brandName,
|
||||
mcpConfigRemoved: false,
|
||||
rulesDirRemoved: false,
|
||||
brandFolderRemoved: false,
|
||||
skipped: false,
|
||||
error: null,
|
||||
success: false // Overall success for this brand
|
||||
};
|
||||
|
||||
if (fs.existsSync(mcpPath)) {
|
||||
try {
|
||||
fs.unlinkSync(mcpPath);
|
||||
log('info', `Removed MCP configuration: ${mcpPath}`);
|
||||
result.mcpConfigRemoved = true;
|
||||
} catch (e) {
|
||||
log(
|
||||
'warn',
|
||||
`Failed to remove MCP configuration at ${mcpPath}: ${e.message}`
|
||||
);
|
||||
const errorMessage = `Failed to remove MCP configuration at ${mcpPath}: ${e.message}`;
|
||||
log('warn', errorMessage);
|
||||
result.error = result.error ? `${result.error}; ${errorMessage}` : errorMessage;
|
||||
}
|
||||
}
|
||||
// Do not allow removal of the default Cursor rules directory
|
||||
|
||||
if (brandName.toLowerCase() === 'cursor') {
|
||||
log('warn', 'Cannot remove default Cursor rules directory. Skipping.');
|
||||
return false;
|
||||
const skipMessage = 'Cannot remove default Cursor rules directory. Skipping.';
|
||||
log('warn', skipMessage);
|
||||
result.skipped = true;
|
||||
result.error = skipMessage;
|
||||
return result; // Early exit for cursor brand
|
||||
}
|
||||
|
||||
if (fs.existsSync(brandRulesDir)) {
|
||||
fs.rmSync(brandRulesDir, { recursive: true, force: true });
|
||||
log('info', `Removed rules directory: ${brandRulesDir}`);
|
||||
// Check if parent brand folder is empty
|
||||
if (
|
||||
fs.existsSync(brandDir) &&
|
||||
path.basename(brandDir) !== '.cursor' &&
|
||||
fs.readdirSync(brandDir).length === 0
|
||||
) {
|
||||
fs.rmdirSync(brandDir);
|
||||
log('info', `Removed empty brand folder: ${brandDir}`);
|
||||
try {
|
||||
fs.rmSync(brandRulesDir, { recursive: true, force: true });
|
||||
result.rulesDirRemoved = true;
|
||||
|
||||
if (
|
||||
fs.existsSync(brandDir) &&
|
||||
path.basename(brandDir) !== '.cursor' &&
|
||||
fs.readdirSync(brandDir).length === 0
|
||||
) {
|
||||
fs.rmdirSync(brandDir);
|
||||
result.brandFolderRemoved = true;
|
||||
}
|
||||
result.success = true; // Mark overall success if rules dir was removed
|
||||
} catch (e) {
|
||||
const errorMessage = `Failed to remove rules directory ${brandRulesDir} or brand folder ${brandDir}: ${e.message}`;
|
||||
log('error', errorMessage); // Log as error since this is a primary operation failing
|
||||
result.error = result.error ? `${result.error}; ${errorMessage}` : errorMessage;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
log('warn', `Rules directory not found: ${brandRulesDir}`);
|
||||
return false;
|
||||
const warnMessage = `Rules directory not found: ${brandRulesDir}`;
|
||||
log('warn', warnMessage);
|
||||
result.error = result.error ? `${result.error}; ${warnMessage}` : warnMessage;
|
||||
// success remains false as the primary target (rulesDir) was not found
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { fileURLToPath } from 'url';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { isSilentMode } from '../modules/utils.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -136,12 +137,12 @@ export function onAddBrandRules(targetDir) {
|
||||
if (fs.existsSync(roomodesSrc)) {
|
||||
try {
|
||||
fs.copyFileSync(roomodesSrc, roomodesDest);
|
||||
console.log(`[Roo] Copied .roomodes to ${roomodesDest}`);
|
||||
if (!isSilentMode()) console.log(`[Roo] Copied .roomodes to ${roomodesDest}`);
|
||||
} catch (err) {
|
||||
console.warn(`[Roo] Failed to copy .roomodes: ${err.message}`);
|
||||
if (!isSilentMode()) console.warn(`[Roo] Failed to copy .roomodes: ${err.message}`);
|
||||
}
|
||||
} else {
|
||||
console.warn(`[Roo] .roomodes not found at ${roomodesSrc}`);
|
||||
if (!isSilentMode()) console.warn(`[Roo] .roomodes not found at ${roomodesSrc}`);
|
||||
}
|
||||
|
||||
// Copy each <mode>-rules file into the corresponding .roo/rules-<mode>/ folder
|
||||
@@ -154,12 +155,12 @@ export function onAddBrandRules(targetDir) {
|
||||
const destDir = path.dirname(dest);
|
||||
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
||||
fs.copyFileSync(src, dest);
|
||||
console.log(`[Roo] Copied ${src} to ${dest}`);
|
||||
if (!isSilentMode()) console.log(`[Roo] Copied ${src} to ${dest}`);
|
||||
} catch (err) {
|
||||
console.warn(`[Roo] Failed to copy ${src} to ${dest}: ${err.message}`);
|
||||
if (!isSilentMode()) console.warn(`[Roo] Failed to copy ${src} to ${dest}: ${err.message}`);
|
||||
}
|
||||
} else {
|
||||
console.warn(`[Roo] Roo rule file not found for mode '${mode}': ${src}`);
|
||||
if (!isSilentMode()) console.warn(`[Roo] Roo rule file not found for mode '${mode}': ${src}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user