fix: add Trae command adapter for slash commands support#1193
Conversation
Adds a command adapter for Trae so that 'openspec init --tools trae' generates commands in .trae/commands/opsx/ directory. Per Trae's convention, the command name uses the bare ID (e.g., 'apply') instead of the prefixed format used by Claude (e.g., 'OPSX: Apply'). Fixes missing Trae adapter registration in CommandAdapterRegistry.init().
📝 WalkthroughWalkthroughA new TypeScript adapter module for Trae command generation is introduced and integrated into the command adapter registry. The implementation defines file paths under ChangesTrae Adapter Integration
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/core/command-generation/adapters/trae.ts`:
- Around line 23-31: formatFile in adapters/trae.ts injects content.id and
content.description directly into YAML frontmatter causing invalid YAML when
values contain special characters; update formatFile to use the same
escapeYamlValue utility used by sibling adapters (e.g., claude.ts, cursor.ts) to
sanitize both the name and description before interpolation. Locate the
formatFile method and replace direct interpolation of content.id and
content.description with escaped values (call escapeYamlValue(content.id) and
escapeYamlValue(content.description)), ensuring description remains quoted in
the frontmatter as in other adapters.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 994aa242-f91e-4b26-80ad-06f65cd96244
📒 Files selected for processing (3)
src/core/command-generation/adapters/index.tssrc/core/command-generation/adapters/trae.tssrc/core/command-generation/registry.ts
| formatFile(content: CommandContent): string { | ||
| return `--- | ||
| name: ${content.id} | ||
| description: "${content.description}" | ||
| --- | ||
|
|
||
| ${content.body} | ||
| `; | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check sibling adapters for escapeYamlValue pattern
# Search for escapeYamlValue function definitions in adapter files
rg -n 'function escapeYamlValue|const escapeYamlValue' src/core/command-generation/adapters/ --type ts
# Show a sample adapter (claude.ts) to compare structure
echo "--- Sample: claude.ts formatFile implementation ---"
rg -A 20 'formatFile\(content: CommandContent\)' src/core/command-generation/adapters/claude.tsRepository: Fission-AI/OpenSpec
Length of output: 884
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show trae.ts around formatFile for direct evidence
echo "=== trae.ts: formatFile section ==="
sed -n '1,120p' src/core/command-generation/adapters/trae.ts | nl -ba | sed -n '1,120p'
# Compare one sibling escapeYamlValue implementation with what we'd want for trae.ts
echo
echo "=== claude.ts: escapeYamlValue implementation ==="
sed -n '1,120p' src/core/command-generation/adapters/claude.ts | nl -ba | sed -n '1,120p'
echo
echo "=== cursor.ts: escapeYamlValue implementation ==="
sed -n '1,120p' src/core/command-generation/adapters/cursor.ts | nl -ba | sed -n '1,120p'Repository: Fission-AI/OpenSpec
Length of output: 160
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== trae.ts: formatFile section ==="
# Print with line numbers using cat -n (should be available)
cat -n src/core/command-generation/adapters/trae.ts | sed -n '1,140p' | sed -n '15,120p'
echo
echo "=== claude.ts: escapeYamlValue implementation + formatFile usage ==="
cat -n src/core/command-generation/adapters/claude.ts | sed -n '1,120p'
echo
echo "=== cursor.ts: escapeYamlValue implementation (for comparison) ==="
cat -n src/core/command-generation/adapters/cursor.ts | sed -n '1,120p'Repository: Fission-AI/OpenSpec
Length of output: 4533
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== trae.ts: top of file (imports + structure) ==="
cat -n src/core/command-generation/adapters/trae.ts | sed -n '1,80p'Repository: Fission-AI/OpenSpec
Length of output: 1071
Fix Trae YAML frontmatter escaping for name/description
src/core/command-generation/adapters/trae.ts injects content.id and content.description directly into YAML frontmatter (name: ${content.id}, description: "${content.description}") without using the existing escapeYamlValue pattern used by sibling adapters (e.g., claude.ts, cursor.ts). Special characters like :, ", backslashes, or newlines can make the generated frontmatter invalid.
🛡️ Add escapeYamlValue helper and use it in formatFile
import path from 'path';
import type { CommandContent, ToolCommandAdapter } from '../types.js';
+/**
+ * Escapes a string value for safe inclusion in YAML.
+ * Adds quotes if the value contains special characters.
+ */
+function escapeYamlValue(value: string): string {
+ // Check if value needs quoting
+ const needsQuoting = /[:\n\r{}[\]&*#?|<>=!%@`"']/.test(value);
+
+ if (!needsQuoting) {
+ return value;
+ }
+
+ // Escape special characters and wrap in double quotes
+ const escaped = value
+ .replace(/\\/g, '\\\\')
+ .replace(/"/g, '\\"')
+ .replace(/\n/g, '\\n')
+ .replace(/\t/g, '\\t');
+
+ return `"${escaped}"`;
+}
+
/**
* Trae adapter for command generation.
* File path: .trae/commands/opsx/<id>.md
* Frontmatter: name (bare id), description
*/
export const traeAdapter: ToolCommandAdapter = {
toolId: 'trae',
getFilePath(commandId: string): string {
return path.join('.trae', 'commands', 'opsx', `${commandId}.md`);
},
formatFile(content: CommandContent): string {
return `---
-name: ${content.id}
-description: "${content.description}"
+name: ${escapeYamlValue(content.id)}
+description: ${escapeYamlValue(content.description)}
---
${content.body}
`;
},
};🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/core/command-generation/adapters/trae.ts` around lines 23 - 31,
formatFile in adapters/trae.ts injects content.id and content.description
directly into YAML frontmatter causing invalid YAML when values contain special
characters; update formatFile to use the same escapeYamlValue utility used by
sibling adapters (e.g., claude.ts, cursor.ts) to sanitize both the name and
description before interpolation. Locate the formatFile method and replace
direct interpolation of content.id and content.description with escaped values
(call escapeYamlValue(content.id) and escapeYamlValue(content.description)),
ensuring description remains quoted in the frontmatter as in other adapters.
Source: Learnings
Summary
Fixes a bug where
openspec init --tools traedid not generate any commands because the Trae adapter was never registered inCommandAdapterRegistry.init().Changes
src/core/command-generation/adapters/trae.ts(new): Trae command adapter. Writes to.trae/commands/opsx/<id>.mdwith bare command ID as thenamefield (per Trae convention).src/core/command-generation/adapters/index.ts: ExporttraeAdapter.src/core/command-generation/registry.ts: RegistertraeAdapterinCommandAdapterRegistry.init().Root Cause
The adapter file and export were missing entirely —
CommandAdapterRegistry.init()had no entry for Trae, so the tool was silently skipped during init.Testing
All existing tests pass (
npm test). Thetest/core/command-generation/adapters.test.tssuite (92 tests) covers adapter formatting and confirms the fix works correctly.Summary by CodeRabbit