Code Buddy:实时监控AI编程助手状态,提升开发效率与掌控感
1. 项目概述如果你和我一样日常开发重度依赖 Claude Code、Cursor 这类 AI 编程助手那你肯定遇到过这个场景你让 AI 去执行一个复杂的find或grep命令然后切到浏览器查资料或者去回个消息。几分钟后回来你完全不知道 AI 刚才到底干了啥——它是在“思考”中卡住了还是在默默地读写文件你只能切回终端盯着那个可能已经结束也可能还在运行的会话心里没底。Code Buddy 就是为了解决这个“信息黑盒”问题而生的。它是一个轻量级的 macOS 菜单栏应用能实时显示你所有 AI 编码助手会话的状态。想象一下你的菜单栏上有一个常驻的小窗口清晰地告诉你Claude 正在“执行”一个 Shell 命令Gemini 在“读取”项目文件而 Cursor 刚刚“完成”了一次重构。这种对后台进程的“感知力”能极大地提升你与 AI 协同编程时的掌控感和效率。它支持 Claude Code、Gemini CLI、Cursor 和 OpenCode 等多款主流工具并且可以同时监控多个会话让你一目了然。2. 核心设计思路与架构解析2.1 核心需求统一的事件捕获与状态呈现开发这类工具首要挑战是标准化。每款 AI 工具的输出格式、事件触发机制都不同。Claude Code 和 Gemini CLI 提供了官方的钩子Hooks接口Cursor 有自己的 Shell 执行钩子而 OpenCode 则可能需要插件机制。Code Buddy 的设计核心就是建立一个轻量的事件中转中心。它的架构非常清晰采用了经典的生产者-消费者模型并针对不同工具适配了相应的“生产者”。事件生产者Hooks/Wrappers/Plugin这是与各个 AI 工具对接的一层。对于 Claude Code、Gemini CLI 和 Cursor项目提供了 Python 编写的code-buddy-hook.py脚本。这些工具在特定生命周期如会话开始、工具使用前/后、Shell 执行前/后会调用对应的钩子命令该脚本负责捕获这些事件。对于 OpenCode则是一个 JavaScript 插件直接集成到其运行时中。对于 Gemini CLI 和 OpenCode还额外提供了 Shell 包装脚本gemini-buddy.sh,opencode-buddy.sh以确保能准确捕获到会话的开始和结束事件因为它们的原生钩子可能不包含这些边界事件。事件中转中心本地服务器Code Buddy 应用本身在启动时会在localhost:8765端口同时开启一个WebSocket 服务器和一个HTTP 服务器。这是一个巧妙的设计WebSocket 用于处理来自 Claude、Gemini、Cursor 钩子脚本的持续、双向的实时事件流而 HTTP POST 接口则用于接收来自 OpenCode 插件这类的一次性事件通知。这种混合模式以最小开销兼容了不同工具的通信习惯。事件消费者与状态呈现SwiftUI 应用菜单栏应用作为消费者订阅 WebSocket 消息或监听 HTTP 请求。它解析收到的标准化事件如{“tool”: “claude”, “status”: “executing”, “session_id”: “abc123”}然后更新内存中的会话状态表。最后通过 SwiftUI 将状态实时渲染到那个始终置顶的浮动窗口上。为什么选择 WebSocket HTTP 混合纯 HTTP 对于频繁的状态更新如“思考”-“执行”-“写入”有连接开销而纯 WebSocket 需要对某些工具进行更复杂的集成。混合方案在实现复杂度和性能之间取得了最佳平衡。WebSocket 保持长连接适合高频事件HTTP 作为补充方便插件快速“投递”事件。2.2 技术选型背后的考量语言Swift 为主Python/Shell/JS 为辅核心应用使用 Swift 和 SwiftUI 开发这是构建原生、高性能 macOS 菜单栏应用最自然的选择能完美集成 SF Symbols 和系统外观深色/浅色模式。钩子脚本选用 Python是因为 uv 工具链能确保依赖一致且启动快速同时 Python 处理 JSON 等数据结构非常方便。包装脚本用 Shell是为了以最小侵入性包裹原有命令行工具。OpenCode 插件用 JS则是为了匹配其插件生态。通信本地环回接口所有通信严格限制在localhost:8765。这保证了数据不会离开你的电脑满足了隐私和安全的基本要求也避免了复杂的网络配置。状态管理会话为中心应用内部以session_id为核心管理状态。每个 AI 工具的一次启动到退出被视为一个会话同一个工具可以并行多个会话例如开了两个 Claude Code 窗口。这比单纯以工具类型来管理要精确得多。3. 详细安装与配置指南官方提供了一键安装脚本但对于想了解细节或遇到问题的开发者手动走一遍流程会更有帮助。这里我以手动安装为例带你深入每个步骤。3.1 环境准备与源码构建首先确保你的系统是 macOS 13 或更高版本并安装了 Xcode Command Line Tools。# 检查 Swift 编译器 swift --version # 如果没有安装 Xcode CLT xcode-select --install接下来获取源码并构建。我建议在~/Developer目录下操作保持环境整洁。cd ~/Developer git clone https://github.com/runkids/code-buddy.git cd code-buddy # 使用 release 模式构建优化性能 swift build -c release构建完成后产物位于.build/release/目录下核心是一个名为CodeBuddy的可执行文件。3.2 文件部署与路径规划Unix 哲学讲究“各就各位”。我们将应用主程序、脚本和插件分别放到合适的目录。# 创建用户本地二进制目录如果不存在 mkdir -p ~/.local/bin # 1. 部署主程序 cp .build/release/CodeBuddy ~/.local/bin/ # 2. 部署核心钩子脚本Python cp Scripts/code-buddy-hook.py ~/.local/bin/ # 3. 部署包装脚本 cp Scripts/gemini-buddy.sh ~/.local/bin/gemini-buddy cp Scripts/opencode-buddy.sh ~/.local/bin/opencode-buddy # 赋予执行权限 chmod x ~/.local/bin/CodeBuddy chmod x ~/.local/bin/code-buddy-hook.py chmod x ~/.local/bin/gemini-buddy chmod x ~/.local/bin/opencode-buddy # 4. 部署 OpenCode 插件 mkdir -p ~/.config/opencode/plugins cp Scripts/code-buddy-opencode-plugin.js ~/.config/opencode/plugins/注意~/.local/bin是一个常见的用户级软件安装目录。确保它已加入你的PATH环境变量。检查echo $PATH如果没有需要在~/.zshrc中添加export PATH$HOME/.local/bin:$PATH并执行source ~/.zshrc。3.3 配置各 AI 工具的钩子这是最关键的一步让 AI 工具知道在何时去调用我们的钩子脚本。1. 配置 Claude CodeClaude Code 的配置在~/.claude/settings.json。如果文件不存在先启动一次 Claude Code 它会自动生成。我们需要编辑其hooks部分。{ hooks: { SessionStart: [ { hooks: [ { type: command, command: ~/.local/bin/code-buddy-hook.py SessionStart --source claude } ] } ], SessionEnd: [ { hooks: [ { type: command, command: ~/.local/bin/code-buddy-hook.py SessionEnd --source claude } ] } ], PreToolUse: [ { matcher: *, hooks: [ { type: command, command: ~/.local/bin/code-buddy-hook.py PreToolUse --source claude } ] } ], PostToolUse: [ { matcher: *, hooks: [ { type: command, command: ~/.local/bin/code-buddy-hook.py PostToolUse --source claude } ] } ] } }关键点解析SessionStart/End捕获整个会话的生命周期。PreToolUse/PostToolUse在 AI 执行任何工具如 Bash、读写文件之前和之后触发。“matcher”: “*”表示匹配所有工具。--source claude参数至关重要它告诉钩子脚本事件的来源以便正确标识会话。2. 配置 Gemini CLIGemini CLI 的配置类似文件位于~/.gemini/settings.json。{ hooks: { BeforeTool: [ { matcher: *, hooks: [ { type: command, command: ~/.local/bin/code-buddy-hook.py BeforeTool --source gemini } ] } ], AfterTool: [ { matcher: *, hooks: [ { type: command, command: ~/.local/bin/code-buddy-hook.py AfterTool --source gemini } ] } ] } }特别注意Gemini CLI 的钩子可能无法直接捕获会话开始。这就是gemini-buddy包装脚本存在的意义。我们需要创建一个别名alias让每次输入gemini命令时实际上是先通过包装脚本启动报告“会话开始”然后再调用真正的gemini程序。在~/.zshrc中添加alias geminigemini-buddy3. 配置 CursorCursor 的钩子配置在~/.cursor/hooks.json。它主要针对 Shell 命令的执行。{ version: 1, hooks: { beforeShellExecution: [ { command: ~/.local/bin/code-buddy-hook.py beforeShellExecution --source cursor } ], afterShellExecution: [ { command: ~/.local/bin/code-buddy-hook.py afterShellExecution --source cursor } ] } }4. 配置 OpenCodeOpenCode 的插件已经复制到位它会自动被加载。同样为了捕获完整的会话我们也需要为opencode命令设置包装别名。在~/.zshrc中添加alias opencodeopencode-buddy3.4 启动与自启动配置完成所有配置后首先手动启动一次 Code Buddy 来测试。# 在后台启动 Code Buddy并将日志输出到临时文件 nohup ~/.local/bin/CodeBuddy /tmp/code-buddy.log 21 此时你应该能在菜单栏右侧看到 Code Buddy 的图标。点击它会显示一个浮动窗口初始状态可能是“Waiting for AI tools...”。为了让 Code Buddy 在每次登录时自动启动将以下内容添加到~/.zshrc文件的末尾# 启动 Code Buddy (如果尚未运行) pgrep -x CodeBuddy /dev/null || nohup ~/.local/bin/CodeBuddy /tmp/code-buddy.log 21 这条命令先检查CodeBuddy进程是否存在如果不存在则启动它。nohup和确保它在后台运行即使终端关闭也不受影响。4. 使用体验与状态解读启动并配置好所有工具后真正的乐趣开始了。打开你的 Claude Code 或 Cursor开始一次普通的编码对话。你会发现菜单栏上的 Code Buddy 窗口不再显示等待信息而是变成了一个动态的状态看板。以下是一些典型状态的含义和使用技巧Idle所有配置的 AI 工具均无活跃会话。这是初始状态。Starting当你启动一个 AI 工具如通过gemini别名命令包装脚本首先发送此事件表示会话开始建立。ThinkingAI 正在处理你的请求生成回答。这是最常见的“工作”状态之一。Executing非常有用当 AI 决定运行一个 Bash 命令时状态会立刻变为“Executing”。这时你可以知道 AI 正在操作你的文件系统如果命令长时间未返回你可能需要去终端查看一下。Reading / WritingAI 在读或写文件。这让你对 AI 的文件操作活动有了直观感知避免它“悄悄”修改了重要文件而你却不知道。SearchingAI 在执行grep、find或文件通配符操作。Done!当前工具动作成功完成通常是一个短暂的提示状态随后可能回到“Thinking”或“Idle”。Failed工具执行失败。这是一个重要的警示提示你需要关注 AI 刚才尝试的操作为何出错。多会话管理如果你同时打开了两个 Claude Code 窗口和一个 CursorCode Buddy 的窗口会显示多行状态分别标识不同的会话通常以工具名和简短ID区分。你可以一眼看清哪个工具正在忙碌哪个已经空闲。浮动窗口特性这个窗口被设置为“始终置顶”这意味着即使你全屏切换桌面或应用只要你想看它都在那里。你可以拖动它到屏幕的任何角落我个人喜欢把它放在右上角既不遮挡代码编辑器的主要区域又方便随时瞥一眼。5. 故障排除与调试实战即使按照步骤操作也可能会遇到问题。别担心我们可以一步步排查。5.1 常见问题速查表问题现象可能原因排查步骤菜单栏显示“Waiting for AI tools...”1. Code Buddy 未运行2. 服务器端口未监听3. 钩子配置错误或未生效1.pgrep -x CodeBuddy检查进程。2.lsof -i :8765检查端口。3. 重启 AI 工具重载配置。某个工具状态不更新如 Claude该工具的钩子配置有误1. 检查对应的settings.json或hooks.json文件路径和语法。2. 检查钩子脚本路径~/.local/bin/code-buddy-hook.py是否存在且可执行。3. 查看该工具的调试日志如果有。Gemini/OpenCode 只显示结束状态未使用包装脚本别名1. 确认~/.zshrc中已添加alias geminigemini-buddy和alias opencodeopencode-buddy。2. 执行source ~/.zshrc或新开终端。3. 使用which gemini确认别名生效。状态更新延迟或丢失1. 钩子脚本执行出错2. 网络环回接口异常1. 启用调试日志在终端执行CODE_BUDDY_DEBUG1然后运行钩子命令测试。2. 查看/tmp/code-buddy-hook.log。应用崩溃或无响应程序本身 Bug 或资源冲突1. 查看主日志/tmp/code-buddy.log。2. 尝试重启应用pkill -x CodeBuddy然后重新启动。5.2 深度调试查看日志日志是定位问题的第一手资料。1. 查看 Code Buddy 主应用日志tail -f /tmp/code-buddy.log启动 AI 工具观察这里是否有 WebSocket 连接建立或 HTTP 请求收到的记录。如果什么都没有说明事件根本没有发送到服务器问题出在钩子配置或脚本上。2. 查看钩子脚本调试日志钩子脚本默认可能不输出详细日志。你可以手动测试它。首先设置环境变量并直接运行脚本模拟一个事件export CODE_BUDDY_DEBUG1 # 模拟一个 Claude 会话开始事件 ~/.local/bin/code-buddy-hook.py SessionStart --source claude然后查看脚本生成的日志tail -f /tmp/code-buddy-hook.log这个日志会显示脚本是否成功连接到localhost:8765发送了什么数据以及服务器的响应。如果连接失败会明确报错。3. 检查端口监听确保 Code Buddy 正确绑定了端口。lsof -i -P | grep LISTEN | grep 8765应该能看到CodeBuddy进程在监听8765端口。5.3 实操心得配置生效的秘诀配置文件的生效时机Claude Code、Gemini CLI 和 Cursor 通常是在启动时读取它们的配置文件。这意味着你修改了settings.json或hooks.json后必须完全退出并重启该 AI 应用新的钩子配置才会被加载。仅仅重启终端标签页是不够的。Shell 别名的陷阱alias只在交互式 Shell 中有效。如果你通过某个 IDE 的内置终端或菜单启动工具它可能不会读取你的~/.zshrc。确保你的启动环境是继承了你配置的 Shell 环境。路径问题所有钩子命令中使用的路径都必须是绝对路径。~/.local/bin/code-buddy-hook.py是可行的因为 Shell 会将其展开为绝对路径。但为了绝对保险有时使用绝对路径/Users/你的用户名/.local/bin/code-buddy-hook.py更能避免意外。6. 进阶理解包装脚本的工作原理为什么 Gemini CLI 和 OpenCode 需要包装脚本这是解决“会话边界”问题的经典模式。让我们拆解一下gemini-buddy.sh这个脚本通常内容类似#!/bin/bash # 1. 首先发送一个“会话开始”事件给 Code Buddy 服务器 curl -s -X POST http://localhost:8765/event \ -H Content-Type: application/json \ -d {tool:gemini,status:starting,session_id:some_unique_id} /dev/null 21 # 2. 然后执行真正的原始命令并捕获其退出码 original_gemini_command $ EXIT_CODE$? # 3. 最后根据退出码发送“会话结束”或“失败”事件 if [ $EXIT_CODE -eq 0 ]; then STATUSgoodbye else STATUSfailed fi curl -s -X POST http://localhost:8765/event \ -H Content-Type: application/json \ -d {\tool\:\gemini\,\status\:\$STATUS\,\session_id\:\some_unique_id\} /dev/null 21 # 4. 将原始命令的退出码传递出去 exit $EXIT_CODE核心逻辑拦截用户输入geminiShell 根据别名找到gemini-buddy脚本。通知开始脚本在真正调用gemini程序之前先向 Code Buddy 发送 HTTP 请求报告会话开始。执行原命令调用真正的gemini程序这里假设它被重命名或通过路径调用为original_gemini_command并传递所有参数 (“$”)。通知结束等待原命令执行完毕根据其退出码判断成功与否再发送会话结束事件。透明传递脚本以原命令的退出码退出使得整个包装过程对用户和后续脚本透明。这种包装器模式非常强大它可以在不修改原始工具任何代码的情况下为其注入新的能力如状态监控、日志记录等。7. 开发与贡献指南如果你对这个项目感兴趣想修复 Bug 或添加新功能比如支持 GitHub Copilot Chat、Windscope 等其他工具以下是本地开发环境设置。# 克隆你的 fork 仓库 git clone https://github.com/你的用户名/code-buddy.git cd code-buddy # 使用 Swift Package Manager 进行开发构建 swift build # 运行调试版本 swift run # 运行测试如果项目有测试的话 swift test # 生成 Xcode 工程以便在 IDE 中开发可选 swift package generate-xcodeproj项目结构概览Sources/CodeBuddy/Swift 应用核心代码包含服务器逻辑、状态管理和 UI。Scripts/所有钩子脚本、包装脚本和安装脚本。Package.swiftSwift 包管理清单文件定义了依赖项。添加对新工具的支持研究目标工具首先确认该工具是否有公开的钩子、插件系统或能否通过包装器拦截。实现事件生产者在Scripts/下创建新的钩子脚本或包装器。扩展状态机在 Swift 代码中可能需要为新的工具类型定义标识符和对应的状态映射逻辑。更新安装脚本修改install.sh将新工具的配置步骤集成进去。测试务必进行充分测试确保事件能准确捕获和显示。整个开发流程遵循标准的 GitHub Fork Pull Request 模式。在提交 PR 前请确保代码风格一致并通过基础的测试。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2596654.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!