feat: add Claude Code integration and unified agent settings - #37
Conversation
📝 WalkthroughWalkthrough项目新增 Claude Code 集成。Codex 与 Claude Code 现在共享智能体适配器、Hook 服务和设置界面,同时保留独立配置、事件、验证状态和操作流程。新增失败事件处理、回合生命周期处理、测试、文档和图标说明。 ChangesClaude Code 集成
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant ClaudeCode
participant hook_server
participant live_status
participant settings
ClaudeCode->>hook_server: 发送智能体事件
hook_server->>live_status: 转发规范化事件
live_status->>settings: 更新运行状态
settings-->>ClaudeCode: 显示连接与事件状态
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (7)
src-tauri/src/hook_installer/claude_code.rs (1)
16-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value建议去掉
expect,用显式错误代替。当前分支在
settings_path返回None时调用expect。虽然此处home一定是Some,但该不变量依赖settings_path的内部实现。若后续修改settings_path,此处会 panic。可用ok_or_else返回错误,保持函数的Result契约。♻️ 建议的重构
pub(super) fn spec() -> Result<HookSpec, String> { let config_dir = std::env::var_os("CLAUDE_CONFIG_DIR"); - let path = if let Some(path) = settings_path(config_dir.as_deref(), None) { - path - } else { - settings_path(None, Some(&config::home_dir()?)).expect("home path is present") - }; + let path = match settings_path(config_dir.as_deref(), None) { + Some(path) => path, + None => settings_path(None, Some(&config::home_dir()?)) + .ok_or_else(|| "无法确定 Claude Code 配置路径".to_string())?, + };🤖 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-tauri/src/hook_installer/claude_code.rs` around lines 16 - 22, Update spec’s fallback settings_path handling to remove expect("home path is present") and convert a None result into an explicit Err via ok_or_else, preserving the function’s Result<HookSpec, String> contract and returning a descriptive error instead of panicking.src/settings.ts (3)
391-398: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win建议复用
integrationIds常量。这两处重复写出
["codex", "claude-code"] as const,而 Line 24 已定义integrationIds。若以后新增智能体,需要修改三处。请改为遍历integrationIds。♻️ 建议的改动
- for (const agent of ["codex", "claude-code"] as const) { + for (const agent of integrationIds) { const definition = integrationDefinitions[agent];-for (const agent of ["codex", "claude-code"] as const) { +for (const agent of integrationIds) { const definition = integrationDefinitions[agent];Also applies to: 683-700
🤖 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/settings.ts` around lines 391 - 398, Reuse the existing integrationIds constant in both affected loops instead of repeating the literal ["codex", "claude-code"] as const. Update the loops around integrationConfig(agent) and the corresponding block near the alternate occurrence so adding an agent only requires changing integrationIds.
825-837: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win建议对
refreshHookStatus增加去抖。事件监听器对每个到达的智能体事件调用
refreshHookStatus。每次调用发起hook_status与hook_runtime_status两个 IPC 请求。后端hook_installer::status会读取并解析该智能体的 Hook 配置文件。在活跃任务期间,
PreToolUse与PostToolUse事件频繁到达。加上 Line 834 的 5 秒定时刷新,磁盘读取会累积。建议对每个智能体加入短去抖窗口(例如 300ms),合并连续事件。🤖 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/settings.ts` around lines 825 - 837, 为 refreshHookStatus 增加按智能体独立的短去抖机制(约 300ms),使 AGENT_EVENT_CHANNEL 监听器和 5 秒 healthTimer 在窗口内的连续刷新请求合并执行;保留事件对 codex 与 claude-code 的现有筛选和最终状态刷新行为,并在 refreshHookStatus 附近复用统一的去抖调度逻辑。
543-550: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value建议用
displayName替换硬编码的 "Claude Code" 文案。这些分支对任意
agent执行,但文案固定写 "Claude Code"。当前后端只对 Claude Code 返回globallyDisabled: true,所以显示结果正确。该正确性依赖后端实现,属隐式耦合。若以后其他智能体也支持全局禁用,Codex 卡片会显示 Claude Code 文案。建议改用已有的
displayName变量,例如${displayName} 已全局禁用所有 Hooks。同样适用于 Line 747、779、798 的提示文案。Also applies to: 565-569
🤖 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/settings.ts` around lines 543 - 550, Replace the hard-coded “Claude Code” text in the hook status messages assigned to linkControl.title and hookElement.textContent with the existing displayName interpolation, including the corresponding messages at the other referenced status-message branches. Preserve the existing global-disable behavior and wording while making each agent’s UI use its own displayName.e2e/readme-screenshots.screenshot.ts (1)
42-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win建议让 mock 按
agent参数返回数据。
hook_statusmock 忽略agent参数,对两个智能体都返回~/.codex/hooks.json与11/11。Claude Code 的真实期望事件数为 13。同时 Line 42 把
claudeCode.hooksEnabled设为false。结合已安装的 mock 状态,refreshHookStatus会让 Claude Code 卡片进入"已暂停"分支(见src/settings.tsLine 570)。请确认 README 截图需要该状态。若需要"已连接",请把hooksEnabled设为true,并按 agent 返回对应路径与事件数。Also applies to: 80-88
🤖 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 `@e2e/readme-screenshots.screenshot.ts` at line 42, Update the hook_status mock used by the README screenshot to branch on the agent parameter, returning the Codex hook path and 11/11 counts for Codex and the Claude hook path and 13/13 counts for Claude Code. In the claudeCode configuration, set hooksEnabled to true if the screenshot should show the connected state; otherwise confirm and preserve the intended paused state.src-tauri/src/hook_server.rs (1)
374-382: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win建议缓存验证指纹,减少每事件的磁盘读取。
emit_event对每个真实事件调用hook_verification::record。record会调用verification_fingerprint(agent),该函数读取并解析该 agent 的 Hook 配置文件,随后record再读取一次验证状态文件。该工作发生在 Hook 接收线程上。本次改动使两个 agent 都进入这条路径,事件量随之上升。
PreToolUse与PostToolUse频率较高,磁盘读取会累积。建议在指纹已验证后跳过重复读取,或按 agent 缓存指纹并按文件 mtime 失效。🤖 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-tauri/src/hook_server.rs` around lines 374 - 382, 优化 emit_event 调用的 hook_verification::record 与 verification_fingerprint 流程:按 agent 缓存已验证的指纹,避免每个真实事件重复读取和解析 Hook 配置及验证状态文件;当配置文件 mtime 变化时使缓存失效并重新验证,保持现有验证结果与错误处理行为。src-tauri/src/hook_verification.rs (1)
41-48: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win建议校验
agent后再拼接文件名。
path把agent直接插入文件名。agent源自 Tauri 命令参数。目前所有调用方(verified_at、record、clear)都先经过hook_installer::verification_fingerprint或hook_installer::install,这两者会对未知 agent 返回错误,因此路径遍历当前不可达。该保护是隐式的。若以后新增直接调用
path的入口,含../或路径分隔符的 agent 会写到配置目录之外。建议在path内做一次显式校验。🛡️ 建议的防御性改动
fn path(agent: &str) -> Result<std::path::PathBuf, String> { + if !matches!(agent, hook_installer::CODEX | hook_installer::CLAUDE_CODE) { + return Err(format!("不支持的 Agent:{agent}")); + } let name = if agent == hook_installer::CODEX {🤖 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-tauri/src/hook_verification.rs` around lines 41 - 48, 在 path 函数内部显式校验 agent,仅允许受支持且不会包含路径遍历片段或路径分隔符的值;对无效 agent 返回错误后再拼接文件名。保留 hook_installer::CODEX 的特殊文件名及其他合法 agent 的现有命名规则。
🤖 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/terminal-event-ledger.ts`:
- Around line 47-50: Update the terminalSession.turnEnded branch in the
event-filtering logic to validate payload.timestamp before allowing
startsSession, UserPromptSubmit, or SessionEnd events without a turnId; reject
stale events while preserving the existing handling for current events. Add
regression coverage in terminal-event-ledger.test.ts for late UserPromptSubmit
and SessionStart events, ensuring they cannot clear the ended session and allow
subsequent stale PostToolUse events.
---
Nitpick comments:
In `@e2e/readme-screenshots.screenshot.ts`:
- Line 42: Update the hook_status mock used by the README screenshot to branch
on the agent parameter, returning the Codex hook path and 11/11 counts for Codex
and the Claude hook path and 13/13 counts for Claude Code. In the claudeCode
configuration, set hooksEnabled to true if the screenshot should show the
connected state; otherwise confirm and preserve the intended paused state.
In `@src-tauri/src/hook_installer/claude_code.rs`:
- Around line 16-22: Update spec’s fallback settings_path handling to remove
expect("home path is present") and convert a None result into an explicit Err
via ok_or_else, preserving the function’s Result<HookSpec, String> contract and
returning a descriptive error instead of panicking.
In `@src-tauri/src/hook_server.rs`:
- Around line 374-382: 优化 emit_event 调用的 hook_verification::record 与
verification_fingerprint 流程:按 agent 缓存已验证的指纹,避免每个真实事件重复读取和解析 Hook
配置及验证状态文件;当配置文件 mtime 变化时使缓存失效并重新验证,保持现有验证结果与错误处理行为。
In `@src-tauri/src/hook_verification.rs`:
- Around line 41-48: 在 path 函数内部显式校验 agent,仅允许受支持且不会包含路径遍历片段或路径分隔符的值;对无效 agent
返回错误后再拼接文件名。保留 hook_installer::CODEX 的特殊文件名及其他合法 agent 的现有命名规则。
In `@src/settings.ts`:
- Around line 391-398: Reuse the existing integrationIds constant in both
affected loops instead of repeating the literal ["codex", "claude-code"] as
const. Update the loops around integrationConfig(agent) and the corresponding
block near the alternate occurrence so adding an agent only requires changing
integrationIds.
- Around line 825-837: 为 refreshHookStatus 增加按智能体独立的短去抖机制(约 300ms),使
AGENT_EVENT_CHANNEL 监听器和 5 秒 healthTimer 在窗口内的连续刷新请求合并执行;保留事件对 codex 与
claude-code 的现有筛选和最终状态刷新行为,并在 refreshHookStatus 附近复用统一的去抖调度逻辑。
- Around line 543-550: Replace the hard-coded “Claude Code” text in the hook
status messages assigned to linkControl.title and hookElement.textContent with
the existing displayName interpolation, including the corresponding messages at
the other referenced status-message branches. Preserve the existing
global-disable behavior and wording while making each agent’s UI use its own
displayName.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 017fd08c-c1f2-4ee8-9618-2e8b6de88db4
⛔ Files ignored due to path filters (2)
assets/agent-icons/claude-code.svgis excluded by!**/*.svgassets/agent-icons/codex.svgis excluded by!**/*.svg
📒 Files selected for processing (29)
README.mdREADME.zh-CN.mdassets/agent-icons/LICENSE.lobe-iconsassets/agent-icons/README.mde2e/readme-screenshots.screenshot.tse2e/settings.e2e.tssrc-tauri/src/config.rssrc-tauri/src/hook_installer.rssrc-tauri/src/hook_installer/claude_code.rssrc-tauri/src/hook_installer/codex.rssrc-tauri/src/hook_installer/unix.rssrc-tauri/src/hook_installer/windows.rssrc-tauri/src/hook_server.rssrc-tauri/src/hook_verification.rssrc-tauri/src/lib.rssrc/agents/claude-code.tssrc/agents/registry.test.tssrc/agents/registry.tssrc/agents/types.tssrc/live-status.test.tssrc/live-status.tssrc/reaction-controller.test.tssrc/reaction-controller.tssrc/settings.htmlsrc/settings.tssrc/styles.csssrc/terminal-event-ledger.test.tssrc/terminal-event-ledger.tssrc/types.ts
|
@monkeyscan review |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/terminal-event-ledger.test.ts (1)
84-87: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win让测试覆盖
recordActivity的状态转换。这两个用例只调用
shouldIgnore。它们没有模拟事件入口在结果为false时调用recordActivity。因此,测试没有直接验证迟到事件被错误放行后会清除turnEnded,以及后续PostToolUse是否仍被拒绝。请按生产顺序处理事件:调用
shouldIgnore,仅在返回false时调用recordActivity,然后断言后续PostToolUse仍被忽略。建议的最小修改
- expect(ledger.shouldIgnore(claudeEvent("UserPromptSubmit", 1))).toBe(true); + const stalePrompt = claudeEvent("UserPromptSubmit", 1); + const promptIgnored = ledger.shouldIgnore(stalePrompt); + if (!promptIgnored) ledger.recordActivity(stalePrompt); expect(ledger.shouldIgnore(claudeEvent("PostToolUse", 2))).toBe(true); + expect(promptIgnored).toBe(true); - expect(ledger.shouldIgnore(claudeEvent("SessionStart", 1))).toBe(true); + const staleSessionStart = claudeEvent("SessionStart", 1); + const sessionStartIgnored = ledger.shouldIgnore(staleSessionStart); + if (!sessionStartIgnored) ledger.recordActivity(staleSessionStart); expect(ledger.shouldIgnore(claudeEvent("PostToolUse", 2))).toBe(true); + expect(sessionStartIgnored).toBe(true);Also applies to: 99-102
🤖 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/terminal-event-ledger.test.ts` around lines 84 - 87, Update the test around ledger.shouldIgnore and recordActivity to process events in production order: call shouldIgnore for each event and invoke recordActivity only when it returns false. Assert that the late UserPromptSubmit does not clear the ended-turn state and that the subsequent PostToolUse remains ignored.
🤖 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.
Nitpick comments:
In `@src/terminal-event-ledger.test.ts`:
- Around line 84-87: Update the test around ledger.shouldIgnore and
recordActivity to process events in production order: call shouldIgnore for each
event and invoke recordActivity only when it returns false. Assert that the late
UserPromptSubmit does not clear the ended-turn state and that the subsequent
PostToolUse remains ignored.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: b3428762-8336-413d-9201-354a2b209ac8
📒 Files selected for processing (2)
src/terminal-event-ledger.test.tssrc/terminal-event-ledger.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/terminal-event-ledger.ts
|
@monkeyscan review 下这个 PR |
收到,我正在处理中,完成后会更新结果。 |
|
PR Title: feat: add Claude Code integration and unified agen... Commit: 本次 PR 为 Agent Cat 引入 Claude Code 集成,与现有 Codex 集成并行运行。主要变更包括:
整体评估:架构设计合理,将 Agent 相关逻辑抽象为 |
已触发 PR Review,完成后会在当前 PR 中发布评审结果。 |
Summary
Claude Code integration
Review fixes
Validation
Summary by CodeRabbit
新功能
文档
测试