combine to /src/utils/profiles.js; add codex and claude code profiles
This commit is contained in:
@@ -1126,7 +1126,9 @@ describe('rules command', () => {
|
||||
expect.stringMatching(/removing rules for profile: roo/i)
|
||||
);
|
||||
expect(mockConsoleLog).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/completed removal for profile: roo/i)
|
||||
expect.stringMatching(
|
||||
/Summary for roo: (Rules directory removed|Skipped \(default or protected files\))/i
|
||||
)
|
||||
);
|
||||
// Should not exit with error
|
||||
expect(mockExit).not.toHaveBeenCalledWith(1);
|
||||
|
||||
@@ -117,16 +117,38 @@ describe('MCP Configuration Validation', () => {
|
||||
describe('Profile Directory Structure', () => {
|
||||
test('should ensure each profile has a unique directory', () => {
|
||||
const profileDirs = new Set();
|
||||
// Simple profiles that use root directory (can share the same directory)
|
||||
const simpleProfiles = ['claude', 'codex'];
|
||||
|
||||
RULE_PROFILES.forEach((profileName) => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
|
||||
// Simple profiles can share the root directory
|
||||
if (simpleProfiles.includes(profileName)) {
|
||||
expect(profile.profileDir).toBe('.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Full profiles should have unique directories
|
||||
expect(profileDirs.has(profile.profileDir)).toBe(false);
|
||||
profileDirs.add(profile.profileDir);
|
||||
});
|
||||
});
|
||||
|
||||
test('should ensure profile directories follow expected naming convention', () => {
|
||||
// Simple profiles that use root directory
|
||||
const simpleProfiles = ['claude', 'codex'];
|
||||
|
||||
RULE_PROFILES.forEach((profileName) => {
|
||||
const profile = getRulesProfile(profileName);
|
||||
|
||||
// Simple profiles use root directory
|
||||
if (simpleProfiles.includes(profileName)) {
|
||||
expect(profile.profileDir).toBe('.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Full profiles should follow the .name pattern
|
||||
expect(profile.profileDir).toMatch(/^\.[\w-]+$/);
|
||||
});
|
||||
});
|
||||
@@ -144,6 +166,8 @@ describe('MCP Configuration Validation', () => {
|
||||
expect(mcpEnabledProfiles).toContain('roo');
|
||||
expect(mcpEnabledProfiles).not.toContain('cline');
|
||||
expect(mcpEnabledProfiles).not.toContain('trae');
|
||||
expect(mcpEnabledProfiles).not.toContain('claude');
|
||||
expect(mcpEnabledProfiles).not.toContain('codex');
|
||||
});
|
||||
|
||||
test('should provide all necessary information for MCP config creation', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import { convertRuleToProfileRule } from '../../src/utils/rule-transformer.js';
|
||||
import * as clineProfile from '../../scripts/profiles/cline.js';
|
||||
import { clineProfile } from '../../scripts/profiles/cline.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -41,11 +41,7 @@ Also has references to .mdc files.`;
|
||||
|
||||
// Convert it
|
||||
const testClineRule = path.join(testDir, 'basic-terms.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testClineRule,
|
||||
clineProfile.clineProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testClineRule, clineProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testClineRule, 'utf8');
|
||||
@@ -76,11 +72,7 @@ alwaysApply: true
|
||||
|
||||
// Convert it
|
||||
const testClineRule = path.join(testDir, 'tool-refs.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testClineRule,
|
||||
clineProfile.clineProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testClineRule, clineProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testClineRule, 'utf8');
|
||||
@@ -108,11 +100,7 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
|
||||
|
||||
// Convert it
|
||||
const testClineRule = path.join(testDir, 'file-refs.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testClineRule,
|
||||
clineProfile.clineProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testClineRule, clineProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testClineRule, 'utf8');
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
convertRuleToProfileRule,
|
||||
getRulesProfile
|
||||
} from '../../src/utils/rule-transformer.js';
|
||||
import * as cursorProfile from '../../scripts/profiles/cursor.js';
|
||||
import { cursorProfile } from '../../scripts/profiles/cursor.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -45,11 +45,7 @@ Also has references to .mdc files.`;
|
||||
|
||||
// Convert it
|
||||
const testCursorOut = path.join(testDir, 'basic-terms.mdc');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testCursorOut,
|
||||
cursorProfile.cursorProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testCursorOut, cursorProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testCursorOut, 'utf8');
|
||||
@@ -80,11 +76,7 @@ alwaysApply: true
|
||||
|
||||
// Convert it
|
||||
const testCursorOut = path.join(testDir, 'tool-refs.mdc');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testCursorOut,
|
||||
cursorProfile.cursorProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testCursorOut, cursorProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testCursorOut, 'utf8');
|
||||
@@ -114,11 +106,7 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
|
||||
|
||||
// Convert it
|
||||
const testCursorOut = path.join(testDir, 'file-refs.mdc');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testCursorOut,
|
||||
cursorProfile.cursorProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testCursorOut, cursorProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testCursorOut, 'utf8');
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
convertRuleToProfileRule,
|
||||
getRulesProfile
|
||||
} from '../../src/utils/rule-transformer.js';
|
||||
import * as rooProfile from '../../scripts/profiles/roo.js';
|
||||
import { rooProfile } from '../../scripts/profiles/roo.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -45,11 +45,7 @@ Also has references to .mdc files.`;
|
||||
|
||||
// Convert it
|
||||
const testRooRule = path.join(testDir, 'basic-terms.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testRooRule,
|
||||
rooProfile.rooProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testRooRule, rooProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testRooRule, 'utf8');
|
||||
@@ -80,11 +76,7 @@ alwaysApply: true
|
||||
|
||||
// Convert it
|
||||
const testRooRule = path.join(testDir, 'tool-refs.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testRooRule,
|
||||
rooProfile.rooProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testRooRule, rooProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testRooRule, 'utf8');
|
||||
@@ -112,11 +104,7 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
|
||||
|
||||
// Convert it
|
||||
const testRooRule = path.join(testDir, 'file-refs.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testRooRule,
|
||||
rooProfile.rooProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testRooRule, rooProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testRooRule, 'utf8');
|
||||
@@ -134,7 +122,7 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
|
||||
const assetRule = path.join(assetsRulesDir, 'dev_workflow.mdc');
|
||||
fs.writeFileSync(assetRule, 'dummy');
|
||||
// Should create .roo/rules and call post-processing
|
||||
convertAllRulesToProfileRules(testDir, rooProfile.rooProfile);
|
||||
convertAllRulesToProfileRules(testDir, rooProfile);
|
||||
// Check for post-processing artifacts, e.g., rules-* folders or extra files
|
||||
const rooDir = path.join(testDir, '.roo');
|
||||
const found = fs.readdirSync(rooDir).some((f) => f.startsWith('rules-'));
|
||||
|
||||
@@ -3,7 +3,7 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import { convertRuleToProfileRule } from '../../src/utils/rule-transformer.js';
|
||||
import * as traeProfile from '../../scripts/profiles/trae.js';
|
||||
import { traeProfile } from '../../scripts/profiles/trae.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -41,11 +41,7 @@ Also has references to .mdc files.`;
|
||||
|
||||
// Convert it
|
||||
const testTraeRule = path.join(testDir, 'basic-terms.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testTraeRule,
|
||||
traeProfile.traeProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testTraeRule, traeProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testTraeRule, 'utf8');
|
||||
@@ -76,11 +72,7 @@ alwaysApply: true
|
||||
|
||||
// Convert it
|
||||
const testTraeRule = path.join(testDir, 'tool-refs.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testTraeRule,
|
||||
traeProfile.traeProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testTraeRule, traeProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testTraeRule, 'utf8');
|
||||
@@ -108,11 +100,7 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
|
||||
|
||||
// Convert it
|
||||
const testTraeRule = path.join(testDir, 'file-refs.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testTraeRule,
|
||||
traeProfile.traeProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testTraeRule, traeProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testTraeRule, 'utf8');
|
||||
|
||||
@@ -3,7 +3,7 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import { convertRuleToProfileRule } from '../../src/utils/rule-transformer.js';
|
||||
import * as windsurfProfile from '../../scripts/profiles/windsurf.js';
|
||||
import { windsurfProfile } from '../../scripts/profiles/windsurf.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -41,11 +41,7 @@ Also has references to .mdc files.`;
|
||||
|
||||
// Convert it
|
||||
const testWindsurfRule = path.join(testDir, 'basic-terms.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testWindsurfRule,
|
||||
windsurfProfile.windsurfProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testWindsurfRule, windsurfProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testWindsurfRule, 'utf8');
|
||||
@@ -76,11 +72,7 @@ alwaysApply: true
|
||||
|
||||
// Convert it
|
||||
const testWindsurfRule = path.join(testDir, 'tool-refs.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testWindsurfRule,
|
||||
windsurfProfile.windsurfProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testWindsurfRule, windsurfProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testWindsurfRule, 'utf8');
|
||||
@@ -108,11 +100,7 @@ This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and
|
||||
|
||||
// Convert it
|
||||
const testWindsurfRule = path.join(testDir, 'file-refs.md');
|
||||
convertRuleToProfileRule(
|
||||
testCursorRule,
|
||||
testWindsurfRule,
|
||||
windsurfProfile.windsurfProfile
|
||||
);
|
||||
convertRuleToProfileRule(testCursorRule, testWindsurfRule, windsurfProfile);
|
||||
|
||||
// Read the converted file
|
||||
const convertedContent = fs.readFileSync(testWindsurfRule, 'utf8');
|
||||
|
||||
@@ -12,7 +12,15 @@ describe('Rule Transformer - General', () => {
|
||||
expect(RULE_PROFILES.length).toBeGreaterThan(0);
|
||||
|
||||
// Verify expected profiles are present
|
||||
const expectedProfiles = ['cline', 'cursor', 'roo', 'trae', 'windsurf'];
|
||||
const expectedProfiles = [
|
||||
'claude',
|
||||
'cline',
|
||||
'codex',
|
||||
'cursor',
|
||||
'roo',
|
||||
'trae',
|
||||
'windsurf'
|
||||
];
|
||||
expectedProfiles.forEach((profile) => {
|
||||
expect(RULE_PROFILES).toContain(profile);
|
||||
});
|
||||
@@ -31,7 +39,7 @@ describe('Rule Transformer - General', () => {
|
||||
expect(isValidProfile(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return correct rules profile with getRulesProfile', () => {
|
||||
it('should return correct rule profile with getRulesProfile', () => {
|
||||
// Test valid profiles
|
||||
RULE_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
@@ -46,6 +54,9 @@ describe('Rule Transformer - General', () => {
|
||||
|
||||
describe('Profile Structure', () => {
|
||||
it('should have all required properties for each profile', () => {
|
||||
// Simple profiles that only copy files (no rule transformation)
|
||||
const simpleProfiles = ['claude', 'codex'];
|
||||
|
||||
RULE_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
|
||||
@@ -56,7 +67,15 @@ describe('Rule Transformer - General', () => {
|
||||
expect(profileConfig).toHaveProperty('rulesDir');
|
||||
expect(profileConfig).toHaveProperty('profileDir');
|
||||
|
||||
// Check that conversionConfig has required structure
|
||||
// Simple profiles have minimal structure
|
||||
if (simpleProfiles.includes(profile)) {
|
||||
// For simple profiles, conversionConfig and fileMap can be empty
|
||||
expect(typeof profileConfig.conversionConfig).toBe('object');
|
||||
expect(typeof profileConfig.fileMap).toBe('object');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that conversionConfig has required structure for full profiles
|
||||
expect(profileConfig.conversionConfig).toHaveProperty('profileTerms');
|
||||
expect(profileConfig.conversionConfig).toHaveProperty('toolNames');
|
||||
expect(profileConfig.conversionConfig).toHaveProperty('toolContexts');
|
||||
@@ -89,6 +108,9 @@ describe('Rule Transformer - General', () => {
|
||||
'taskmaster.mdc'
|
||||
];
|
||||
|
||||
// Simple profiles that only copy files (no rule transformation)
|
||||
const simpleProfiles = ['claude', 'codex'];
|
||||
|
||||
RULE_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
|
||||
@@ -97,7 +119,12 @@ describe('Rule Transformer - General', () => {
|
||||
expect(typeof profileConfig.fileMap).toBe('object');
|
||||
expect(profileConfig.fileMap).not.toBeNull();
|
||||
|
||||
// Check that fileMap is not empty
|
||||
// Simple profiles can have empty fileMap since they don't transform rules
|
||||
if (simpleProfiles.includes(profile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that fileMap is not empty for full profiles
|
||||
const fileMapKeys = Object.keys(profileConfig.fileMap);
|
||||
expect(fileMapKeys.length).toBeGreaterThan(0);
|
||||
|
||||
@@ -116,6 +143,9 @@ describe('Rule Transformer - General', () => {
|
||||
|
||||
describe('MCP Configuration Properties', () => {
|
||||
it('should have all required MCP properties for each profile', () => {
|
||||
// Simple profiles that only copy files (no MCP configuration)
|
||||
const simpleProfiles = ['claude', 'codex'];
|
||||
|
||||
RULE_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
|
||||
@@ -124,7 +154,15 @@ describe('Rule Transformer - General', () => {
|
||||
expect(profileConfig).toHaveProperty('mcpConfigName');
|
||||
expect(profileConfig).toHaveProperty('mcpConfigPath');
|
||||
|
||||
// Check types
|
||||
// Simple profiles have no MCP configuration
|
||||
if (simpleProfiles.includes(profile)) {
|
||||
expect(profileConfig.mcpConfig).toBe(false);
|
||||
expect(profileConfig.mcpConfigName).toBe(null);
|
||||
expect(profileConfig.mcpConfigPath).toBe(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check types for full profiles
|
||||
expect(typeof profileConfig.mcpConfig).toBe('boolean');
|
||||
expect(typeof profileConfig.mcpConfigName).toBe('string');
|
||||
expect(typeof profileConfig.mcpConfigPath).toBe('string');
|
||||
@@ -138,6 +176,16 @@ describe('Rule Transformer - General', () => {
|
||||
|
||||
it('should have correct MCP configuration for each profile', () => {
|
||||
const expectedConfigs = {
|
||||
claude: {
|
||||
mcpConfig: false,
|
||||
mcpConfigName: null,
|
||||
expectedPath: null
|
||||
},
|
||||
codex: {
|
||||
mcpConfig: false,
|
||||
mcpConfigName: null,
|
||||
expectedPath: null
|
||||
},
|
||||
cursor: {
|
||||
mcpConfig: true,
|
||||
mcpConfigName: 'mcp.json',
|
||||
@@ -176,9 +224,18 @@ describe('Rule Transformer - General', () => {
|
||||
});
|
||||
|
||||
it('should have consistent profileDir and mcpConfigPath relationship', () => {
|
||||
// Simple profiles that only copy files (no MCP configuration)
|
||||
const simpleProfiles = ['claude', 'codex'];
|
||||
|
||||
RULE_PROFILES.forEach((profile) => {
|
||||
const profileConfig = getRulesProfile(profile);
|
||||
|
||||
// Simple profiles have null mcpConfigPath
|
||||
if (simpleProfiles.includes(profile)) {
|
||||
expect(profileConfig.mcpConfigPath).toBe(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// The mcpConfigPath should start with the profileDir
|
||||
expect(profileConfig.mcpConfigPath).toMatch(
|
||||
new RegExp(
|
||||
@@ -201,8 +258,11 @@ describe('Rule Transformer - General', () => {
|
||||
return profileConfig.profileDir;
|
||||
});
|
||||
|
||||
// Note: Claude and Codex both use "." (root directory) so we expect some duplication
|
||||
const uniqueProfileDirs = [...new Set(profileDirs)];
|
||||
expect(uniqueProfileDirs).toHaveLength(profileDirs.length);
|
||||
// We should have fewer unique directories than total profiles due to simple profiles using root
|
||||
expect(uniqueProfileDirs.length).toBeLessThanOrEqual(profileDirs.length);
|
||||
expect(uniqueProfileDirs.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should have unique MCP config paths', () => {
|
||||
@@ -211,8 +271,13 @@ describe('Rule Transformer - General', () => {
|
||||
return profileConfig.mcpConfigPath;
|
||||
});
|
||||
|
||||
// Note: Claude and Codex both have null mcpConfigPath so we expect some duplication
|
||||
const uniqueMcpConfigPaths = [...new Set(mcpConfigPaths)];
|
||||
expect(uniqueMcpConfigPaths).toHaveLength(mcpConfigPaths.length);
|
||||
// We should have fewer unique paths than total profiles due to simple profiles having null
|
||||
expect(uniqueMcpConfigPaths.length).toBeLessThanOrEqual(
|
||||
mcpConfigPaths.length
|
||||
);
|
||||
expect(uniqueMcpConfigPaths.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
getInstalledProfiles,
|
||||
wouldRemovalLeaveNoProfiles
|
||||
} from '../../src/utils/profile-detection.js';
|
||||
} from '../../src/utils/profiles.js';
|
||||
import { rulesDirect } from '../../mcp-server/src/core/direct-functions/rules.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
Reference in New Issue
Block a user