add Trae support

This commit is contained in:
Joe Danziger
2025-05-27 13:26:48 -04:00
parent ba18ccbcab
commit 93bd8f0f30
7 changed files with 213 additions and 5 deletions

View File

@@ -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';

32
scripts/profiles/trae.js Normal file
View File

@@ -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;

View File

@@ -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

View File

@@ -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'"
);
});
});

View File

@@ -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', () => {

View File

@@ -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/');
});
});

View File

@@ -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',