mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-17 08:36:25 +03:00
Fix Trae output format and add tests
This commit is contained in:
+7
-8
@@ -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!
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
|
||||
@@ -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'));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user