From fc94022ed04b7dbba1d4687e2278ab116a18bf75 Mon Sep 17 00:00:00 2001 From: ayamir <61657399+ayamir@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:57:04 +0800 Subject: [PATCH] feat(agent): add TraeCode CLI support (#807) * feat(agent): add TraeCode CLI support * fix(settings): index TraeCode agent hooks --- README.md | 5 +-- README.zh-CN.md | 5 +-- assets/icons/agents/traecli.svg | 4 +++ crates/tty7-core/src/core/agent_hooks.rs | 34 ++++++++++++++++++++- crates/tty7-core/src/core/cli_agent.rs | 39 ++++++++++++++++++++++-- docs/agents/overview.mdx | 7 +++-- docs/agents/sessions.mdx | 1 + docs/agents/status.mdx | 2 +- docs/getting-started/first-launch.mdx | 6 ++-- docs/index.mdx | 2 +- src/ui/assets.rs | 1 + src/ui/i18n/en.rs | 4 +++ src/ui/i18n/ja.rs | 4 +++ src/ui/i18n/mod.rs | 3 ++ src/ui/i18n/zh.rs | 4 +++ src/ui/settings.rs | 5 +++ src/ui/tab_strip.rs | 9 +++++- 17 files changed, 119 insertions(+), 16 deletions(-) create mode 100644 assets/icons/agents/traecli.svg diff --git a/README.md b/README.md index a4c6c7c8..fa649748 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Native builds for macOS, Windows, and Linux on [**Releases**](https://github.com | | | |---|---| -| **Agent-aware** | per-pane detection (19 CLIs) · status dot · notifications · branch + diff · tray icon when input is needed · resume after reboot · tab sidebar grouped by repository | +| **Agent-aware** | per-pane detection (20 CLIs) · status dot · notifications · branch + diff · tray icon when input is needed · resume after reboot · tab sidebar grouped by repository | | **CLI + Skills** | bundled `tty7` CLI · [agent skill](skills/tty7/SKILL.md) · `run` streams a command and exits with its code · `split` · `send` · `wait --until free` · `capture` | | **Editor-grade input** | ghost suggestions from history · explained tab completion · syntax highlighting · multi-line editing · click places the caret · ⌃ R fuzzy history | | **Window** | tabs & splits · ⌘ P palette · ⌘ F scrollback search · ⌘ J panel with process tree and listening ports · 13 themes, your own YAML, iTerm2 import · IME | @@ -69,12 +69,13 @@ after a reboot. **Fork** needs both — the agent's own fork command, and the ho that tells tty7 which session to fork.
-The full support matrix, all nineteen +The full support matrix, all twenty | Agent | Detected | Status · resume | Fork | |---|:-:|:-:|:-:| | **Claude Code** | ✓ | ✓ | ✓ | | **Codex** | ✓ | ✓ | ✓ | +| **TraeCode** | ✓ | ✓ | ✓ | | **Grok** | ✓ | ✓ | ✓ | | **OpenCode** | ✓ | ✓ | ✓ | | **Oh My Pi** | ✓ | ✓ | ✓ | diff --git a/README.zh-CN.md b/README.zh-CN.md index 014ba97a..b782628c 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -50,7 +50,7 @@ macOS、Windows、Linux 的原生构建都在 [**Releases**](https://github.com/ | | | |---|---| -| **Agent 感知** | 逐 pane 识别 19 个 CLI agent · 状态点 · 通知 · 分支 + diff · 需要输入时托盘图标提醒 · 重启后续上会话 · 侧边栏按仓库分组 | +| **Agent 感知** | 逐 pane 识别 20 个 CLI agent · 状态点 · 通知 · 分支 + diff · 需要输入时托盘图标提醒 · 重启后续上会话 · 侧边栏按仓库分组 | | **CLI + Skills** | 安装包自带 `tty7` CLI · [agent skill](skills/tty7/SKILL.md) · `run` 转发命令输出并原样返回退出码 · `split` · `send` · `wait --until free` · `capture` | | **编辑器级输入** | 从历史推出影子建议 · Tab 补全附带说明 · 语法高亮 · 多行编辑 · 点击定位光标 · ⌃ R 模糊搜索历史 | | **窗口** | 标签页与分屏 · ⌘ P 命令面板 · ⌘ F 回滚搜索 · ⌘ J 侧栏列出进程树和监听端口 · 13 套主题,也能写自己的 YAML 或导入 iTerm2 配色 · 输入法 | @@ -66,12 +66,13 @@ macOS、Windows、Linux 的原生构建都在 [**Releases**](https://github.com/ **Fork** 两个条件都要:agent 自己提供 fork 命令,且 hook 已装——tty7 得知道 fork 的是哪个会话。
-19 个 agent 的完整支持矩阵 +20 个 agent 的完整支持矩阵 | Agent | 识别 | 状态 · 重启恢复 | Fork | |---|:-:|:-:|:-:| | **Claude Code** | ✓ | ✓ | ✓ | | **Codex** | ✓ | ✓ | ✓ | +| **TraeCode** | ✓ | ✓ | ✓ | | **Grok** | ✓ | ✓ | ✓ | | **OpenCode** | ✓ | ✓ | ✓ | | **Oh My Pi** | ✓ | ✓ | ✓ | diff --git a/assets/icons/agents/traecli.svg b/assets/icons/agents/traecli.svg new file mode 100644 index 00000000..d18ff324 --- /dev/null +++ b/assets/icons/agents/traecli.svg @@ -0,0 +1,4 @@ + + + + diff --git a/crates/tty7-core/src/core/agent_hooks.rs b/crates/tty7-core/src/core/agent_hooks.rs index 6458c227..63298afb 100644 --- a/crates/tty7-core/src/core/agent_hooks.rs +++ b/crates/tty7-core/src/core/agent_hooks.rs @@ -246,6 +246,7 @@ fn ancestor_pids(procs: &[crate::daemon::winproc::Proc]) -> Vec { pub enum HookAgent { Claude, Codex, + TraeCode, Copilot, OpenCode, Pi, @@ -259,9 +260,10 @@ pub enum HookAgent { } impl HookAgent { - pub const ALL: [HookAgent; 12] = [ + pub const ALL: [HookAgent; 13] = [ HookAgent::Claude, HookAgent::Codex, + HookAgent::TraeCode, HookAgent::Copilot, HookAgent::OpenCode, HookAgent::Pi, @@ -283,6 +285,7 @@ impl HookAgent { match agent { CLIAgent::Claude => Some(HookAgent::Claude), CLIAgent::Codex => Some(HookAgent::Codex), + CLIAgent::TraeCode => Some(HookAgent::TraeCode), CLIAgent::Copilot => Some(HookAgent::Copilot), CLIAgent::OpenCode => Some(HookAgent::OpenCode), CLIAgent::Pi => Some(HookAgent::Pi), @@ -310,6 +313,7 @@ impl HookAgent { match self { HookAgent::Claude => Some(CLAUDE_HOOK_EVENTS), HookAgent::Codex => Some(CODEX_HOOK_EVENTS), + HookAgent::TraeCode => Some(TRAE_CODE_HOOK_EVENTS), HookAgent::Gemini => Some(GEMINI_HOOK_EVENTS), HookAgent::Droid => Some(DROID_HOOK_EVENTS), HookAgent::Qwen => Some(QWEN_HOOK_EVENTS), @@ -337,6 +341,7 @@ impl HookAgent { match self { HookAgent::Claude => "claude", HookAgent::Codex => "codex", + HookAgent::TraeCode => "traecli", HookAgent::Copilot => "copilot", HookAgent::OpenCode => "opencode", HookAgent::Pi => "pi", @@ -354,6 +359,7 @@ impl HookAgent { match self { HookAgent::Claude => "Claude Code", HookAgent::Codex => "Codex", + HookAgent::TraeCode => "TraeCode", HookAgent::Copilot => "Copilot CLI", HookAgent::OpenCode => "OpenCode", HookAgent::Pi => "Pi", @@ -375,6 +381,7 @@ impl HookAgent { match self { HookAgent::Claude => target.claude_settings_path(), HookAgent::Codex => target.under_home(&[".codex", "hooks.json"]), + HookAgent::TraeCode => target.traecli_hooks_path(), HookAgent::Copilot => target.under_home(&[".copilot", "hooks", OWNED_FILE_STEM_JSON]), HookAgent::OpenCode => target.under( &target.xdg_config_dir(), @@ -487,6 +494,18 @@ impl<'a> HookTarget<'a> { self.under_home(&[".kimi-code", "config.toml"]) } + fn traecli_hooks_path(&self) -> PathBuf { + if self.is_local() { + if let Some(dir) = std::env::var_os("TRAECLI_HOME").filter(|d| !d.is_empty()) { + return PathBuf::from(dir).join("hooks.json"); + } + if let Some(dir) = std::env::var_os("TRAE_HOME").filter(|d| !d.is_empty()) { + return PathBuf::from(dir).join("cli").join("hooks.json"); + } + } + self.under_home(&[".trae", "cli", "hooks.json"]) + } + fn hook_command(&self, agent: HookAgent, event: &str) -> String { if let Some(exe) = self.hook_command_exe() { return format!("{exe} agent-hook {} {event}", agent.slug()); @@ -697,6 +716,15 @@ const CODEX_HOOK_EVENTS: &[(&str, &str)] = &[ ("Stop", "stop"), ]; +const TRAE_CODE_HOOK_EVENTS: &[(&str, &str)] = &[ + ("SessionStart", "session-start"), + ("UserPromptSubmit", "prompt-submit"), + ("PermissionRequest", "permission-request"), + ("PostToolUse", "tool-complete"), + ("Stop", "stop"), + ("SessionEnd", "session-end"), +]; + /// Gemini names the turn boundaries after the agent rather than the user, and /// omitting `matcher` matches everything (`hookPlanner.ts`, `!entry.matcher`), /// so the bare entries [`hook_map_install`] already writes are enough. @@ -1106,6 +1134,7 @@ fn owned_file_content(target: &HookTarget, agent: HookAgent) -> Option { HookAgent::Goose => goose_hooks_json(target), HookAgent::Claude | HookAgent::Codex + | HookAgent::TraeCode | HookAgent::Gemini | HookAgent::Droid | HookAgent::Qwen @@ -1538,6 +1567,7 @@ mod tests { let mut events: Vec<&str> = CLAUDE_HOOK_EVENTS .iter() .chain(CODEX_HOOK_EVENTS) + .chain(TRAE_CODE_HOOK_EVENTS) .chain(GEMINI_HOOK_EVENTS) .chain(DROID_HOOK_EVENTS) .chain(QWEN_HOOK_EVENTS) @@ -1571,6 +1601,7 @@ mod tests { (HookAgent::Gemini, "/home/me/.gemini/settings.json"), (HookAgent::Droid, "/home/me/.factory/settings.json"), (HookAgent::Qwen, "/home/me/.qwen/settings.json"), + (HookAgent::TraeCode, "/home/me/.trae/cli/hooks.json"), ( HookAgent::Goose, "/home/me/.agents/plugins/tty7/hooks/hooks.json", @@ -1862,6 +1893,7 @@ mod tests { for (agent, expected) in [ (HookAgent::Claude, "/home/me/.claude/settings.json"), (HookAgent::Codex, "/home/me/.codex/hooks.json"), + (HookAgent::TraeCode, "/home/me/.trae/cli/hooks.json"), (HookAgent::Copilot, "/home/me/.copilot/hooks/tty7.json"), ( HookAgent::OpenCode, diff --git a/crates/tty7-core/src/core/cli_agent.rs b/crates/tty7-core/src/core/cli_agent.rs index c4c67ea0..712ecf10 100644 --- a/crates/tty7-core/src/core/cli_agent.rs +++ b/crates/tty7-core/src/core/cli_agent.rs @@ -23,12 +23,16 @@ pub enum CLIAgent { Qwen, OhMyPi, Kimi, + // Keep new variants at the end: daemon messages serialize this enum and + // moving an existing discriminant would break mixed-version clients. + TraeCode, } impl CLIAgent { - pub const ALL: [CLIAgent; 19] = [ + pub const ALL: [CLIAgent; 20] = [ CLIAgent::Claude, CLIAgent::Codex, + CLIAgent::TraeCode, CLIAgent::Gemini, CLIAgent::Aider, CLIAgent::Amp, @@ -52,6 +56,7 @@ impl CLIAgent { match self { CLIAgent::Claude => &["claude", "claude-code"], CLIAgent::Codex => &["codex", "codex-cli"], + CLIAgent::TraeCode => &["traecli", "traex"], CLIAgent::Gemini => &["gemini", "gemini-cli"], CLIAgent::Aider => &["aider", "aider-chat"], CLIAgent::Amp => &["amp"], @@ -85,6 +90,7 @@ impl CLIAgent { match self { CLIAgent::Claude => "claude", CLIAgent::Codex => "codex", + CLIAgent::TraeCode => "traecli", CLIAgent::Gemini => "gemini", CLIAgent::Aider => "aider", CLIAgent::Amp => "amp", @@ -114,6 +120,7 @@ impl CLIAgent { match self { CLIAgent::Claude => "Claude Code", CLIAgent::Codex => "Codex", + CLIAgent::TraeCode => "TraeCode", CLIAgent::Gemini => "Gemini", CLIAgent::Aider => "Aider", CLIAgent::Amp => "Amp", @@ -146,6 +153,7 @@ impl CLIAgent { match self { CLIAgent::Claude => Some(format!("claude{flags} --resume {session_id}")), CLIAgent::Codex => Some(format!("codex resume {session_id}{flags}")), + CLIAgent::TraeCode => Some(format!("traecli resume {session_id}{flags}")), CLIAgent::Gemini => Some(format!("gemini{flags} --resume {session_id}")), CLIAgent::OpenCode => Some(format!("opencode{flags} --session {session_id}")), CLIAgent::Amp => Some(format!("amp threads continue {session_id}{flags}")), @@ -189,6 +197,7 @@ impl CLIAgent { let flags = self.session_command_flags(session_id, launch_argv)?; match self { CLIAgent::Codex => Some(format!("codex fork {session_id}{flags}")), + CLIAgent::TraeCode => Some(format!("traecli fork {session_id}{flags}")), CLIAgent::Claude => Some(format!( "claude{flags} --resume {session_id} --fork-session" )), @@ -213,6 +222,7 @@ impl CLIAgent { match self { CLIAgent::Claude | CLIAgent::Codex + | CLIAgent::TraeCode | CLIAgent::Grok | CLIAgent::OpenCode | CLIAgent::OhMyPi @@ -260,7 +270,9 @@ impl CLIAgent { let named = argv.iter().position(|t| names_self(t))?; let mut tail: Vec<&str> = argv[named + 1..].iter().map(String::as_str).collect(); - if self == CLIAgent::Codex && matches!(tail.first(), Some(&"resume") | Some(&"fork")) { + if matches!(self, CLIAgent::Codex | CLIAgent::TraeCode) + && matches!(tail.first(), Some(&"resume") | Some(&"fork")) + { tail.remove(0); if tail.first().is_some_and(|t| !t.starts_with('-')) { tail.remove(0); @@ -339,6 +351,7 @@ impl CLIAgent { CLIAgent::Antigravity => &["--conversation", "--continue", "-c"], CLIAgent::OpenCode => &["--session", "-s", "--continue", "-c", "--fork"], CLIAgent::Codex => &["--last"], + CLIAgent::TraeCode => &["--last", "--resume", "--session-id"], CLIAgent::Pi => &[ "--session", "--session-id", @@ -422,6 +435,9 @@ impl CLIAgent { match self { CLIAgent::Claude => 0xD97757, CLIAgent::Codex => 0x000000, + // The brand mark carries its own green foreground on a black + // field, so the surrounding tab avatar needs to stay black too. + CLIAgent::TraeCode => 0x000000, CLIAgent::Gemini => 0x4285F4, CLIAgent::Aider => 0x14B014, CLIAgent::Amp => 0xF34E3F, @@ -448,6 +464,7 @@ impl CLIAgent { match self { CLIAgent::Claude => "icons/agents/claude.svg", CLIAgent::Codex => "icons/agents/codex.svg", + CLIAgent::TraeCode => "icons/agents/traecli.svg", CLIAgent::Gemini => "icons/agents/gemini.svg", CLIAgent::Amp => "icons/agents/amp.svg", CLIAgent::OpenCode => "icons/agents/opencode.svg", @@ -771,6 +788,14 @@ mod tests { CLIAgent::detect_from_argv(&argv(&["/opt/homebrew/bin/codex", "--model", "o3"])), Some(CLIAgent::Codex) ); + assert_eq!( + CLIAgent::detect_from_argv(&argv(&["/Users/me/.local/bin/traecli"])), + Some(CLIAgent::TraeCode) + ); + assert_eq!( + CLIAgent::detect_from_argv(&argv(&["traex"])), + Some(CLIAgent::TraeCode) + ); assert_eq!( CLIAgent::detect_from_argv(&argv(&["/usr/local/bin/gemini"])), Some(CLIAgent::Gemini) @@ -1192,6 +1217,12 @@ mod tests { CLIAgent::Codex.resume_command("th_read.9", None).as_deref(), Some("codex resume th_read.9") ); + assert_eq!( + CLIAgent::TraeCode + .resume_command("019c-123", None) + .as_deref(), + Some("traecli resume 019c-123") + ); assert_eq!( CLIAgent::Pi .resume_command("0199c3f2-1b0e-7c3a-9f21-6d4b8e2a5c17", None) @@ -1522,6 +1553,10 @@ mod tests { CLIAgent::Codex.fork_command("abc-123", None).as_deref(), Some("codex fork abc-123") ); + assert_eq!( + CLIAgent::TraeCode.fork_command("019c-123", None).as_deref(), + Some("traecli fork 019c-123") + ); assert_eq!( CLIAgent::Claude.fork_command("abc-123", None).as_deref(), Some("claude --resume abc-123 --fork-session") diff --git a/docs/agents/overview.mdx b/docs/agents/overview.mdx index 5888288c..e0e21f5b 100644 --- a/docs/agents/overview.mdx +++ b/docs/agents/overview.mdx @@ -1,6 +1,6 @@ --- title: "Coding agents" -description: "What tty7 does around Claude Code, Codex, and 17 others — without ever wrapping them." +description: "What tty7 does around Claude Code, Codex, TraeCode, and 17 others — without ever wrapping them." --- tty7 recognises coding agents running in a pane and builds around them. It does @@ -15,12 +15,13 @@ need, and what changed. ## Which agents -Nineteen CLIs are recognised on sight, by the command running in the pane: +Twenty CLIs are recognised on sight, by the command running in the pane: | Agent | Command | |---|---| | Claude Code | `claude`, `claude-code` | | Codex | `codex`, `codex-cli` | +| TraeCode | `traecli`, `traex` | | Gemini | `gemini`, `gemini-cli` | | Copilot | `copilot` | | Cursor | `cursor-agent` | @@ -58,7 +59,7 @@ If you launch agents through a wrapper script, map its name to an agent in ``` The key is your command's name; the value is one of the slugs above (`claude`, -`codex`, `gemini`, `aider`, `amp`, `opencode`, `copilot`, `cursor`, `goose`, +`codex`, `traecli`, `gemini`, `aider`, `amp`, `opencode`, `copilot`, `cursor`, `goose`, `droid`, `pi`, `auggie`, `hermes`, `vibe`, `antigravity`, `grok`, `qwen`, `omp`, `kimi`). diff --git a/docs/agents/sessions.mdx b/docs/agents/sessions.mdx index 61d4fba8..90703839 100644 --- a/docs/agents/sessions.mdx +++ b/docs/agents/sessions.mdx @@ -39,6 +39,7 @@ or right-click the **tab or sidebar row** to open the fork in a new tab. |---|---| | Claude Code | `claude --resume --fork-session` | | Codex | `codex fork ` | +| TraeCode | `traecli fork ` | | Grok | `grok --resume --fork-session` | | OpenCode | `opencode --session --fork` | | Oh My Pi | `omp --fork ` | diff --git a/docs/agents/status.mdx b/docs/agents/status.mdx index f4fb2a87..256e7a87 100644 --- a/docs/agents/status.mdx +++ b/docs/agents/status.mdx @@ -14,7 +14,7 @@ the agent say which one it is. | Agent | | |---|---| -| Claude Code · Codex · Copilot CLI · OpenCode · Pi · Grok Build · Oh My Pi · Gemini · Droid · Qwen Code · Goose · Kimi Code | Hooks available | +| Claude Code · Codex · TraeCode · Copilot CLI · OpenCode · Pi · Grok Build · Oh My Pi · Gemini · Droid · Qwen Code · Goose · Kimi Code | Hooks available | | Aider · Amp · Cursor · Auggie · Hermes · Vibe · Antigravity | Detected and labelled, but no status channel yet | Installing writes into that agent's own configuration directory. Once installed diff --git a/docs/getting-started/first-launch.mdx b/docs/getting-started/first-launch.mdx index 71f88c9d..80eb645a 100644 --- a/docs/getting-started/first-launch.mdx +++ b/docs/getting-started/first-launch.mdx @@ -54,13 +54,13 @@ leave it off if you type accented characters. ## 4. If you use coding agents, install the hooks -**Settings → Agents.** tty7 detects 19 coding CLIs by process name on its own — +**Settings → Agents.** tty7 detects 20 coding CLIs by process name on its own — you get brand avatars and tab labels for free. The *status dots*, the "needs your permission" notifications, and `tty7 wait` all need one more thing: a small hook the agent calls to report what it is doing. -Click **Install** next to Claude Code, Codex, Copilot CLI, OpenCode, Pi, Grok -Build, or Oh My Pi. It writes into that agent's own config directory and can be +Click **Install** next to Claude Code, Codex, TraeCode, Copilot CLI, OpenCode, +Pi, Grok Build, or Oh My Pi. It writes into that agent's own config directory and can be removed from the same row. [More about agents →](/agents/status) diff --git a/docs/index.mdx b/docs/index.mdx index d1f0d721..a298a154 100644 --- a/docs/index.mdx +++ b/docs/index.mdx @@ -31,7 +31,7 @@ something floods the screen. syntax highlighting, click-to-place-caret, real multi-line editing. - 19 coding CLIs are recognised on sight. Per-pane status dots, notifications + 20 coding CLIs are recognised on sight. Per-pane status dots, notifications when one needs you, git context, and session resume after a reboot. diff --git a/src/ui/assets.rs b/src/ui/assets.rs index 94a7bb1f..801694f7 100644 --- a/src/ui/assets.rs +++ b/src/ui/assets.rs @@ -49,6 +49,7 @@ fn agent_icon(path: &str) -> Option<&'static [u8]> { "icons/refresh.svg" => include_bytes!("../../assets/icons/refresh.svg"), "icons/agents/claude.svg" => include_bytes!("../../assets/icons/agents/claude.svg"), "icons/agents/codex.svg" => include_bytes!("../../assets/icons/agents/codex.svg"), + "icons/agents/traecli.svg" => include_bytes!("../../assets/icons/agents/traecli.svg"), "icons/agents/gemini.svg" => include_bytes!("../../assets/icons/agents/gemini.svg"), "icons/agents/amp.svg" => include_bytes!("../../assets/icons/agents/amp.svg"), "icons/agents/opencode.svg" => include_bytes!("../../assets/icons/agents/opencode.svg"), diff --git a/src/ui/i18n/en.rs b/src/ui/i18n/en.rs index fea7d64f..b9af2001 100644 --- a/src/ui/i18n/en.rs +++ b/src/ui/i18n/en.rs @@ -738,6 +738,7 @@ pub fn translate_en(key: L10nKey) -> &'static str { } L10nKey::SettingsAgentClaudeCode => "Claude Code", L10nKey::SettingsAgentCodex => "Codex", + L10nKey::SettingsAgentTraeCode => "TraeCode", L10nKey::SettingsAgentCopilotCli => "Copilot CLI", L10nKey::SettingsAgentOpencode => "OpenCode", L10nKey::SettingsAgentPi => "Pi", @@ -768,6 +769,9 @@ pub fn translate_en(key: L10nKey) -> &'static str { "agent integration hooks install uninstall status rich session working waiting tab bar sidebar badge claude" } L10nKey::SettingsSearchCodexKeywords => "agent integration hooks install openai codex", + L10nKey::SettingsSearchTraeCodeKeywords => { + "agent integration hooks install trae code traecli traex" + } L10nKey::SettingsSearchCommandLineToolKeywords => { "command line tool cli tty7 path shell command install symlink terminal iterm agent script" } diff --git a/src/ui/i18n/ja.rs b/src/ui/i18n/ja.rs index 50a29245..03ddc490 100644 --- a/src/ui/i18n/ja.rs +++ b/src/ui/i18n/ja.rs @@ -747,6 +747,7 @@ pub fn translate_ja(key: L10nKey) -> Option<&'static str> { } L10nKey::SettingsAgentClaudeCode => "Claude Code", L10nKey::SettingsAgentCodex => "Codex", + L10nKey::SettingsAgentTraeCode => "TraeCode", L10nKey::SettingsAgentCopilotCli => "Copilot CLI", L10nKey::SettingsAgentOpencode => "OpenCode", L10nKey::SettingsAgentPi => "Pi", @@ -787,6 +788,9 @@ pub fn translate_ja(key: L10nKey) -> Option<&'static str> { L10nKey::SettingsSearchCodexKeywords => { "エージェント 統合 フック インストール openai codex agent integration hooks install" } + L10nKey::SettingsSearchTraeCodeKeywords => { + "エージェント 統合 フック インストール trae code traecli traex agent integration hooks install" + } L10nKey::SettingsSearchCommandLineToolKeywords => { "cli tty7 パス シェル コマンド インストール シンボリックリンク ターミナル iterm エージェント スクリプト command line tool" } diff --git a/src/ui/i18n/mod.rs b/src/ui/i18n/mod.rs index c7bd1d96..25425c07 100644 --- a/src/ui/i18n/mod.rs +++ b/src/ui/i18n/mod.rs @@ -570,6 +570,7 @@ l10n_keys! { SettingsAppHttpProxyInvalid, SettingsAgentClaudeCode, SettingsAgentCodex, + SettingsAgentTraeCode, SettingsAgentCopilotCli, SettingsAgentOpencode, SettingsAgentPi, @@ -593,6 +594,7 @@ l10n_keys! { SettingsSearchBoldFontKeywords, SettingsSearchClaudeCodeKeywords, SettingsSearchCodexKeywords, + SettingsSearchTraeCodeKeywords, SettingsSearchCommandLineToolKeywords, SettingsSearchCommandLineToolTitle, SettingsSearchCopilotCliKeywords, @@ -1532,6 +1534,7 @@ mod tests { // Product names. L10nKey::SettingsAgentClaudeCode, L10nKey::SettingsAgentCodex, + L10nKey::SettingsAgentTraeCode, L10nKey::SettingsAgentCopilotCli, L10nKey::SettingsAgentDroid, L10nKey::SettingsAgentGemini, diff --git a/src/ui/i18n/zh.rs b/src/ui/i18n/zh.rs index 9ea57025..c83ea1ca 100644 --- a/src/ui/i18n/zh.rs +++ b/src/ui/i18n/zh.rs @@ -656,6 +656,7 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::SettingsAppHttpProxyInvalid => "不是有效的代理地址,该值未保存。", L10nKey::SettingsAgentClaudeCode => "Claude Code", L10nKey::SettingsAgentCodex => "Codex", + L10nKey::SettingsAgentTraeCode => "TraeCode", L10nKey::SettingsAgentCopilotCli => "Copilot CLI", L10nKey::SettingsAgentOpencode => "OpenCode", L10nKey::SettingsAgentPi => "Pi", @@ -694,6 +695,9 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::SettingsSearchCodexKeywords => { "Codex agent 集成 hook 安装 OpenAI codex agent integration hooks install" } + L10nKey::SettingsSearchTraeCodeKeywords => { + "TraeCode traecli traex agent 集成 hook 安装 agent integration hooks install" + } L10nKey::SettingsSearchCommandLineToolKeywords => { "命令行工具 cli tty7 路径 shell 命令 安装 符号链接 terminal command line tool" } diff --git a/src/ui/settings.rs b/src/ui/settings.rs index 3cfe35c2..6ec891ce 100644 --- a/src/ui/settings.rs +++ b/src/ui/settings.rs @@ -601,6 +601,11 @@ fn settings_search_entries() -> &'static [SearchEntry] { title: SettingsAgentCodex, keywords: SettingsSearchCodexKeywords, }, + SearchEntry { + section: Agents, + title: SettingsAgentTraeCode, + keywords: SettingsSearchTraeCodeKeywords, + }, SearchEntry { section: Agents, title: SettingsAgentCopilotCli, diff --git a/src/ui/tab_strip.rs b/src/ui/tab_strip.rs index 160b7f5c..82b90fff 100644 --- a/src/ui/tab_strip.rs +++ b/src/ui/tab_strip.rs @@ -1301,7 +1301,14 @@ impl Tty7App { gpui::svg() .path(agent.icon_path()) .size(px(size * 0.54)) - .text_color(gpui::white()), + // SVG assets are rendered as a single-colour mask. + // TraeCode's black field comes from the avatar, and + // its brand mark uses the official green. + .text_color(if agent == crate::core::cli_agent::CLIAgent::TraeCode { + gpui::rgb(0x32F08C) + } else { + gpui::rgb(0xFFFFFF) + }), ) .when_some(dot, |b, dot| b.child(dot)) .tooltip(move |window, cx| {