Fix Trae output format and add tests

This commit is contained in:
shog86
2026-03-22 12:12:24 +08:00
parent 3f05d8b6a7
commit 9faa690047
4 changed files with 120 additions and 18 deletions
+7 -8
View File
@@ -77,7 +77,7 @@ source/ → dist/
kiro/.kiro/skills/{name}/SKILL.md
opencode/.opencode/skills/{name}/SKILL.md
pi/.pi/skills/{name}/SKILL.md
trae/.trae-cn/builtin_skills/{name}/SKILL.md
trae/.trae-cn/skills/{name}/SKILL.md
```
## Provider Transformations
@@ -123,13 +123,13 @@ All providers output skills to `dist/{provider}/.{config}/skills/{name}/SKILL.md
- Frontmatter: name, description, license, compatibility, metadata
### Trae (Full Featured)
- Output: `dist/trae/.trae-cn/builtin_skills/{name}/SKILL.md` (China version)
- Output: `dist/trae/.trae/builtin_skills/{name}/SKILL.md` (International version)
- Frontmatter: name, description, user-invokable, args, license, compatibility, metadata, allowed-tools
- Output: `dist/trae/.trae-cn/skills/{name}/SKILL.md` (China version)
- Output: `dist/trae/.trae/skills/{name}/SKILL.md` (International version)
- Frontmatter: name, description, user-invocable, args, license, compatibility, metadata, allowed-tools
- Same format as Claude Code
- Two variants are generated automatically:
- **Trae China**: Skills installed to `~/.trae-cn/builtin_skills/`
- **Trae International**: Skills installed to `~/.trae/builtin_skills/`
- **Trae China**: Skills installed to `~/.trae-cn/skills/`
- **Trae International**: Skills installed to `~/.trae/skills/`
## Adding New Content
@@ -184,7 +184,7 @@ The build system uses a modular architecture under `scripts/`:
- `transformKiro()`: Basic frontmatter with license/compatibility/metadata
- `transformOpenCode()`: Full metadata (same as Claude Code)
- `transformPi()`: Basic frontmatter with license/compatibility/metadata
- `transformTrae()`: Full metadata (same as Claude Code), outputs to both `.trae-cn/builtin_skills/` and `.trae/builtin_skills/`
- `transformTrae()`: Full metadata (same as Claude Code), outputs to both `.trae-cn/skills/` and `.trae/skills/`
## Best Practices
@@ -275,4 +275,3 @@ impeccable/
## Questions?
Open an issue or submit a PR!
+4 -4
View File
@@ -128,15 +128,15 @@ cp -r dist/codex/.codex/* ~/.codex/
**Trae:**
```bash
# Trae China (domestic version)
cp -r dist/trae/.trae-cn/builtin_skills/* ~/.trae-cn/builtin_skills/
cp -r dist/trae/.trae-cn/skills/* ~/.trae-cn/skills/
# Trae International
cp -r dist/trae/.trae/builtin_skills/* ~/.trae/builtin_skills/
cp -r dist/trae/.trae/skills/* ~/.trae/skills/
```
> **Note:** Trae has two versions with different config directories:
> - **Trae China**: `~/.trae-cn/builtin_skills/`
> - **Trae International**: `~/.trae/builtin_skills/`
> - **Trae China**: `~/.trae-cn/skills/`
> - **Trae International**: `~/.trae/skills/`
>
> After copying, restart Trae IDE to activate the skills.
+6 -6
View File
@@ -5,8 +5,8 @@ import { cleanDir, ensureDir, writeFile, generateYamlFrontmatter, replacePlaceho
* Trae Transformer (Skills Only)
*
* Outputs skills for both Trae China and Trae International versions:
* - .trae-cn/builtin_skills/{name}/SKILL.md (China version)
* - .trae/builtin_skills/{name}/SKILL.md (International version)
* - .trae-cn/skills/{name}/SKILL.md (China version)
* - .trae/skills/{name}/SKILL.md (International version)
*
* Trae uses a similar format to Claude Code with full metadata support.
*
@@ -27,8 +27,8 @@ export function transformTrae(skills, distDir, patterns = null, options = {}) {
const commandNames = skills.filter(s => s.userInvokable).map(s => `${prefix}${s.name}`);
const variants = [
{ name: 'China', configDir: '.trae-cn/builtin_skills' },
{ name: 'International', configDir: '.trae/builtin_skills' }
{ name: 'China', configDir: '.trae-cn/skills' },
{ name: 'International', configDir: '.trae/skills' }
];
let totalRefCount = 0;
@@ -47,7 +47,7 @@ export function transformTrae(skills, distDir, patterns = null, options = {}) {
description: skill.description,
};
if (skill.userInvokable) frontmatterObj['user-invokable'] = true;
if (skill.userInvokable) frontmatterObj['user-invocable'] = true;
if (skill.args && skill.args.length > 0) frontmatterObj.args = skill.args;
if (skill.license) frontmatterObj.license = skill.license;
if (skill.compatibility) frontmatterObj.compatibility = skill.compatibility;
@@ -78,5 +78,5 @@ export function transformTrae(skills, distDir, patterns = null, options = {}) {
const userInvokableCount = skills.filter(s => s.userInvokable).length;
const refInfo = totalRefCount > 0 ? ` (${totalRefCount / 2} reference files per variant)` : '';
const prefixInfo = prefix ? ` [${prefix}prefixed]` : '';
console.log(`✓ Trae${prefixInfo}: ${skills.length} skills (${userInvokableCount} user-invokable) for 2 variants${refInfo}`);
console.log(`✓ Trae${prefixInfo}: ${skills.length} skills (${userInvokableCount} user-invocable) for 2 variants${refInfo}`);
}
+103
View File
@@ -0,0 +1,103 @@
import { describe, test, expect, beforeEach, afterEach, mock } from 'bun:test';
import fs from 'fs';
import path from 'path';
import { transformTrae } from '../../../scripts/lib/transformers/trae.js';
const TEST_DIR = path.join(process.cwd(), 'test-tmp-trae');
describe('transformTrae', () => {
beforeEach(() => {
if (fs.existsSync(TEST_DIR)) {
fs.rmSync(TEST_DIR, { recursive: true, force: true });
}
});
afterEach(() => {
if (fs.existsSync(TEST_DIR)) {
fs.rmSync(TEST_DIR, { recursive: true, force: true });
}
});
test('should create correct directory structure for both variants', () => {
transformTrae([], TEST_DIR);
expect(fs.existsSync(path.join(TEST_DIR, 'trae/.trae-cn/skills'))).toBe(true);
expect(fs.existsSync(path.join(TEST_DIR, 'trae/.trae/skills'))).toBe(true);
});
test('should create skill with frontmatter and body', () => {
const skills = [
{
name: 'test-skill',
description: 'A test skill',
license: 'MIT',
userInvokable: true,
body: 'Skill instructions here.'
}
];
transformTrae(skills, TEST_DIR);
const chinaPath = path.join(TEST_DIR, 'trae/.trae-cn/skills/test-skill/SKILL.md');
const intlPath = path.join(TEST_DIR, 'trae/.trae/skills/test-skill/SKILL.md');
expect(fs.existsSync(chinaPath)).toBe(true);
expect(fs.existsSync(intlPath)).toBe(true);
const content = fs.readFileSync(chinaPath, 'utf-8');
expect(content).toContain('---');
expect(content).toContain('name: test-skill');
expect(content).toContain('description: A test skill');
expect(content).toContain('license: MIT');
expect(content).toContain('user-invocable: true');
expect(content).toContain('Skill instructions here.');
});
test('should copy reference files', () => {
const skills = [
{
name: 'frontend-design',
description: 'Design skill',
license: 'MIT',
body: 'Design instructions.',
references: [
{ name: 'typography', content: 'Typography reference', filePath: '/fake/path/typography.md' }
]
}
];
transformTrae(skills, TEST_DIR);
expect(fs.existsSync(path.join(TEST_DIR, 'trae/.trae-cn/skills/frontend-design/reference/typography.md'))).toBe(true);
expect(fs.existsSync(path.join(TEST_DIR, 'trae/.trae/skills/frontend-design/reference/typography.md'))).toBe(true);
});
test('should support prefix option', () => {
const skills = [
{ name: 'audit', description: 'Audit', license: '', body: 'Audit body' }
];
transformTrae(skills, TEST_DIR, null, { prefix: 'i-', outputSuffix: '-prefixed' });
expect(fs.existsSync(path.join(TEST_DIR, 'trae-prefixed/.trae-cn/skills/i-audit/SKILL.md'))).toBe(true);
expect(fs.existsSync(path.join(TEST_DIR, 'trae-prefixed/.trae/skills/i-audit/SKILL.md'))).toBe(true);
});
test('should log correct summary', () => {
const consoleMock = mock(() => {});
const originalLog = console.log;
console.log = consoleMock;
const skills = [
{ name: 'skill1', description: '', license: '', userInvokable: true, body: 'body1' },
{ name: 'skill2', description: '', license: '', userInvokable: false, body: 'body2' }
];
transformTrae(skills, TEST_DIR);
console.log = originalLog;
expect(consoleMock).toHaveBeenCalledWith(expect.stringContaining('✓ Trae:'));
expect(consoleMock).toHaveBeenCalledWith(expect.stringContaining('2 skills'));
expect(consoleMock).toHaveBeenCalledWith(expect.stringContaining('1 user-invocable'));
});
});