diff --git a/scripts/profiles/index.js b/scripts/profiles/index.js index f624aaee..f6eedd48 100644 --- a/scripts/profiles/index.js +++ b/scripts/profiles/index.js @@ -2,4 +2,5 @@ export * as clineProfile from './cline.js'; export * as cursorProfile from './cursor.js'; export * as rooProfile from './roo.js'; +export * as traeProfile from './trae.js'; export * as windsurfProfile from './windsurf.js'; diff --git a/scripts/profiles/trae.js b/scripts/profiles/trae.js new file mode 100644 index 00000000..1310f959 --- /dev/null +++ b/scripts/profiles/trae.js @@ -0,0 +1,32 @@ +// Trae conversion profile for rule-transformer +import { createProfile, COMMON_TOOL_MAPPINGS } from './base-profile.js'; + +// Create trae profile using the base factory +const traeProfile = createProfile({ + name: 'trae', + displayName: 'Trae', + url: 'trae.ai', + docsUrl: 'docs.trae.ai', + profileDir: '.trae', + rulesDir: '.trae/rules', + mcpConfig: false, + mcpConfigName: 'trae_mcp_settings.json', + fileExtension: '.mdc', + targetExtension: '.md', + toolMappings: COMMON_TOOL_MAPPINGS.STANDARD // Trae uses standard tool names +}); + +// Export all the standard profile properties +export const { + conversionConfig, + fileMap, + globalReplacements, + profileName, + displayName, + profileDir, + rulesDir, + mcpConfig, + mcpConfigName, + mcpConfigPath, + getTargetRuleFilename +} = traeProfile; diff --git a/src/constants/profiles.js b/src/constants/profiles.js index f3bea327..de7034ff 100644 --- a/src/constants/profiles.js +++ b/src/constants/profiles.js @@ -1,5 +1,5 @@ /** - * @typedef {'cline' | 'cursor' | 'roo' | 'windsurf'} RulesProfile + * @typedef {'cline' | 'cursor' | 'roo' | 'trae' | 'windsurf'} RulesProfile */ /** @@ -13,6 +13,7 @@ * - cline: Cline IDE rules * - cursor: Cursor IDE rules (default) * - roo: Roo Code IDE rules + * - trae: Trae IDE rules * - windsurf: Windsurf IDE rules * * To add a new rule profile: @@ -20,7 +21,7 @@ * 2. Create a profile file in scripts/profiles/{profile}.js * 3. Export it as {profile}Profile in scripts/profiles/index.js */ -export const RULE_PROFILES = ['cline', 'cursor', 'roo', 'windsurf']; +export const RULE_PROFILES = ['cline', 'cursor', 'roo', 'trae', 'windsurf']; /** * Check if a given rule profile is valid diff --git a/tests/integration/trae-init-functionality.test.js b/tests/integration/trae-init-functionality.test.js new file mode 100644 index 00000000..86b0fb35 --- /dev/null +++ b/tests/integration/trae-init-functionality.test.js @@ -0,0 +1,46 @@ +import fs from 'fs'; +import path from 'path'; + +describe('Trae Profile Initialization Functionality', () => { + let traeProfileContent; + + beforeAll(() => { + const traeJsPath = path.join( + process.cwd(), + 'scripts', + 'profiles', + 'trae.js' + ); + traeProfileContent = fs.readFileSync(traeJsPath, 'utf8'); + }); + + test('trae.js uses factory pattern with correct configuration', () => { + expect(traeProfileContent).toContain("name: 'trae'"); + expect(traeProfileContent).toContain("displayName: 'Trae'"); + expect(traeProfileContent).toContain("rulesDir: '.trae/rules'"); + expect(traeProfileContent).toContain("profileDir: '.trae'"); + }); + + test('trae.js configures .mdc to .md extension mapping', () => { + expect(traeProfileContent).toContain("fileExtension: '.mdc'"); + expect(traeProfileContent).toContain("targetExtension: '.md'"); + }); + + test('trae.js uses standard tool mappings', () => { + expect(traeProfileContent).toContain('COMMON_TOOL_MAPPINGS.STANDARD'); + // Should contain comment about standard tool names + expect(traeProfileContent).toContain('standard tool names'); + }); + + test('trae.js contains correct URL configuration', () => { + expect(traeProfileContent).toContain("url: 'trae.ai'"); + expect(traeProfileContent).toContain("docsUrl: 'docs.trae.ai'"); + }); + + test('trae.js has MCP configuration disabled', () => { + expect(traeProfileContent).toContain('mcpConfig: false'); + expect(traeProfileContent).toContain( + "mcpConfigName: 'trae_mcp_settings.json'" + ); + }); +}); diff --git a/tests/unit/mcp-config-validation.test.js b/tests/unit/mcp-config-validation.test.js index 9e3b34a9..838b942a 100644 --- a/tests/unit/mcp-config-validation.test.js +++ b/tests/unit/mcp-config-validation.test.js @@ -23,6 +23,12 @@ describe('MCP Configuration Validation', () => { expectedConfigName: 'mcp.json', expectedPath: '.roo/mcp.json' }, + trae: { + shouldHaveMcp: false, + expectedDir: '.trae', + expectedConfigName: 'trae_mcp_settings.json', + expectedPath: '.trae/trae_mcp_settings.json' + }, cline: { shouldHaveMcp: false, expectedDir: '.clinerules', @@ -100,8 +106,11 @@ describe('MCP Configuration Validation', () => { }); test('should use profile-specific config name for non-MCP profiles', () => { - const profile = getRulesProfile('cline'); - expect(profile.mcpConfigName).toBe('cline_mcp_settings.json'); + const clineProfile = getRulesProfile('cline'); + expect(clineProfile.mcpConfigName).toBe('cline_mcp_settings.json'); + + const traeProfile = getRulesProfile('trae'); + expect(traeProfile.mcpConfigName).toBe('trae_mcp_settings.json'); }); }); @@ -134,6 +143,7 @@ describe('MCP Configuration Validation', () => { expect(mcpEnabledProfiles).toContain('windsurf'); expect(mcpEnabledProfiles).toContain('roo'); expect(mcpEnabledProfiles).not.toContain('cline'); + expect(mcpEnabledProfiles).not.toContain('trae'); }); test('should provide all necessary information for MCP config creation', () => { diff --git a/tests/unit/rule-transformer-trae.test.js b/tests/unit/rule-transformer-trae.test.js new file mode 100644 index 00000000..7dd34a05 --- /dev/null +++ b/tests/unit/rule-transformer-trae.test.js @@ -0,0 +1,113 @@ +import fs from 'fs'; +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'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +describe('Trae Rule Transformer', () => { + const testDir = path.join(__dirname, 'temp-test-dir'); + + beforeAll(() => { + // Create test directory + if (!fs.existsSync(testDir)) { + fs.mkdirSync(testDir, { recursive: true }); + } + }); + + afterAll(() => { + // Clean up test directory + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true, force: true }); + } + }); + + it('should correctly convert basic terms', () => { + // Create a test Cursor rule file with basic terms + const testCursorRule = path.join(testDir, 'basic-terms.mdc'); + const testContent = `--- +description: Test Cursor rule for basic terms +globs: **/* +alwaysApply: true +--- + +This is a Cursor rule that references cursor.so and uses the word Cursor multiple times. +Also has references to .mdc files.`; + + fs.writeFileSync(testCursorRule, testContent); + + // Convert it + const testTraeRule = path.join(testDir, 'basic-terms.md'); + convertRuleToProfileRule(testCursorRule, testTraeRule, traeProfile); + + // Read the converted file + const convertedContent = fs.readFileSync(testTraeRule, 'utf8'); + + // Verify transformations + expect(convertedContent).toContain('Trae'); + expect(convertedContent).toContain('trae.ai'); + expect(convertedContent).toContain('.md'); + expect(convertedContent).not.toContain('cursor.so'); + expect(convertedContent).not.toContain('Cursor rule'); + }); + + it('should correctly convert tool references', () => { + // Create a test Cursor rule file with tool references + const testCursorRule = path.join(testDir, 'tool-refs.mdc'); + const testContent = `--- +description: Test Cursor rule for tool references +globs: **/* +alwaysApply: true +--- + +- Use the search tool to find code +- The edit_file tool lets you modify files +- run_command executes terminal commands +- use_mcp connects to external services`; + + fs.writeFileSync(testCursorRule, testContent); + + // Convert it + const testTraeRule = path.join(testDir, 'tool-refs.md'); + convertRuleToProfileRule(testCursorRule, testTraeRule, traeProfile); + + // Read the converted file + const convertedContent = fs.readFileSync(testTraeRule, 'utf8'); + + // Verify transformations (Trae uses standard tool names, so no transformation) + expect(convertedContent).toContain('search tool'); + expect(convertedContent).toContain('edit_file tool'); + expect(convertedContent).toContain('run_command'); + expect(convertedContent).toContain('use_mcp'); + }); + + it('should correctly update file references', () => { + // Create a test Cursor rule file with file references + const testCursorRule = path.join(testDir, 'file-refs.mdc'); + const testContent = `--- +description: Test Cursor rule for file references +globs: **/* +alwaysApply: true +--- + +This references [dev_workflow.mdc](mdc:.cursor/rules/dev_workflow.mdc) and +[taskmaster.mdc](mdc:.cursor/rules/taskmaster.mdc).`; + + fs.writeFileSync(testCursorRule, testContent); + + // Convert it + const testTraeRule = path.join(testDir, 'file-refs.md'); + convertRuleToProfileRule(testCursorRule, testTraeRule, traeProfile); + + // Read the converted file + const convertedContent = fs.readFileSync(testTraeRule, 'utf8'); + + // Verify transformations + expect(convertedContent).toContain('(.trae/rules/dev_workflow.md)'); + expect(convertedContent).toContain('(.trae/rules/taskmaster.md)'); + expect(convertedContent).not.toContain('(mdc:.cursor/rules/'); + }); +}); diff --git a/tests/unit/rule-transformer.test.js b/tests/unit/rule-transformer.test.js index 4d588d5c..92fde13f 100644 --- a/tests/unit/rule-transformer.test.js +++ b/tests/unit/rule-transformer.test.js @@ -12,7 +12,7 @@ describe('Rule Transformer - General', () => { expect(RULE_PROFILES.length).toBeGreaterThan(0); // Verify expected profiles are present - const expectedProfiles = ['cline', 'cursor', 'roo', 'windsurf']; + const expectedProfiles = ['cline', 'cursor', 'roo', 'trae', 'windsurf']; expectedProfiles.forEach((profile) => { expect(RULE_PROFILES).toContain(profile); }); @@ -153,6 +153,11 @@ describe('Rule Transformer - General', () => { mcpConfigName: 'mcp.json', expectedPath: '.roo/mcp.json' }, + trae: { + mcpConfig: false, + mcpConfigName: 'trae_mcp_settings.json', + expectedPath: '.trae/trae_mcp_settings.json' + }, cline: { mcpConfig: false, mcpConfigName: 'cline_mcp_settings.json',