编写 adapter
adapter 告诉 HarnessKit 某个 agent 把文件放在哪里,以及如何读写这些文件。每个 agent 在 crates/hk-core/src/adapter/ 下有一个 Rust 文件。新增一个 agent,就是编写这个文件并注册它,再在应用中为它设置名称、颜色和吉祥物。
- 先创建 issue。说明要添加哪个 agent,附上它的文档或源码链接,在编写代码之前先商定要覆盖的范围。
- 用真实安装测试。agent 的文档与实际行为常常不一致。请将每种扩展都安装一次,并执行启用、禁用和删除,再新建一个会话,确认 agent 识别到了改动。
- 写明测试内容。在 PR 中注明测试所用的 agent 版本和平台。
adapter 是什么
Section titled “adapter 是什么”adapter 实现 crates/hk-core/src/adapter/mod.rs 中的 AgentAdapter trait。每个 adapter 都必须实现以下方法:
name():agent 的 id,例如pi。base_dir()和detect():agent 的安装位置,以及是否已安装。skill_dirs():全局 skill 文件夹。HarnessKit 会安装到第一个。mcp_config_path()、read_mcp_servers()、hook_config_path()、read_hooks()和plugin_dirs()。
其余方法都有默认实现,而大多数默认实现的含义是「这个 agent 没有这类内容」。遗漏覆盖时,代码照样能编译,测试也能通过,只是相应功能不显示任何内容。最常需要覆盖的方法有:
mcp_format()和remote_mcp_schema():用于 MCP 文件不是 Claude Code 那种mcpServersJSON,或 agent 支持远程 server 的情况。hook_format():默认为 Claude Code 的文件结构。没有 shell 命令 hook 的 agent 返回HookFormat::None。translate_hook_event():默认将 Claude Code 的事件名原样传递。见第 4 步。read_plugins():列出全局 plugin。默认不列出任何内容,因此只实现plugin_dirs()不会显示任何 plugin。project_skill_dirs()、project_mcp_config_relpath()、project_hook_config_relpath()和project_plugin_dirs():用于项目范围。除 skill 外,每个项目路径都必须配备对应的读取方法:read_mcp_servers_from()、read_hooks_from()和read_plugins_from()。它们默认不返回任何内容,因此若只声明路径而不实现读取方法,项目中将无法列出任何内容。MCP 和 hook 的路径仍会作为安装目标出现,于是 HarnessKit 会向其中写入,却不显示写入的内容。project_markers():哪些文件或文件夹表明一个目录是该 agent 的项目。若 adapter 未声明,scanner.rs中的一个测试会失败,除非它在该测试中被列为仅有全局范围。global_rules_files()和project_rules_patterns():供智能体页使用。agent 的其他文件也有对应的方法:global_memory_files()和project_memory_patterns()、global_settings_files()和project_settings_patterns()、global_subagent_files()和project_subagent_patterns()、project_ignore_patterns(),以及global_workflow_files()和project_workflow_patterns()。
应用提供哪些安装目标,完全由这些声明决定,没有单独的能力清单。
在 crates/hk-core/src/ 中:
- 路径。编写 adapter。每个路径都要对照 agent 的文档或源码核实,并在文件顶部的注释中注明出处。
- 共享 skill。若 agent 会读取
~/.agents/skills/,请把该目录加入skill_dirs(),排在 agent 自己的文件夹之后。 - MCP 格式。若现有的
McpFormat都不适用,请新增一个,并在deployer.rs中实现对应的写入逻辑。 - hook 事件。若 agent 支持 shell 命令 hook,请在
adapter/hook_events.rs中添加一张事件表,以 Claude Code 的事件名为键,再覆盖translate_hook_event(),使其查询这张表。若不覆盖,从其他 agent 安装过来的 hook 会保留 Claude Code 的事件名,你的 agent 永远不会触发它。 - 禁用开关。确定每种扩展的禁用方式,见下文的原生禁用开关。
- 单文件 plugin。若 plugin 是单个
.ts或.js文件,请在manager.rs中列出 OpenCode、Oh My Pi 和 Pi 的位置也加上这个 agent。否则,禁用的 plugin 将无法重新启用。 - 注册。在
adapter/mod.rs顶部添加pub mod <id>;,把 adapter 加入all_adapters(),并在同一文件中列出所有 agent 的测试里加上它,包括能力矩阵测试。
应用部分:
- 顺序和名称。把 id 加入
src/lib/types.ts的AGENT_ORDER,位置与all_adapters()中一致,并在同一文件中添加显示名。更新src/lib/__tests__/types.test.ts中的数量。 - 颜色。在
src/index.css的三套主题(默认、claude和tiesen)中各添加一个--agent-<id>,浅色和深色各一份。再在@theme inline块中把它映射为--color-agent-<id>。在extension-filters.tsx中添加筛选标签。 - 吉祥物。在
src/components/shared/agent-mascot/下新建<id>-mascot.tsx,在agent-mascot.tsx中注册,在mascot.css中编写动画。 - 新手引导。在
onboarding.tsx的FLOAT_DELAYS和SCATTER_POSITIONS中各添加一项,遗漏任何一项,TypeScript 都会报错。若点击动画超过 600 ms,还需要在agent-card.tsx的CLICK_DURATIONS中添加一项。
文档部分:
- README。在
README.md和README.zh-CN.md的支持表格和 agent 列表中添加这个 agent。 - 本站。网站是本仓库的
website/目录。你的 PR 合并后,由维护者把这个 agent 添加到网站上,你在 PR 中注明的路径和版本会写入支持的 agent 和文件位置。也可以在同一个 PR 中自行添加,见编写文档。
最后运行 cargo fmt --all、npm test、npm run build 和 cargo test --workspace。
原生禁用开关
Section titled “原生禁用开关”默认情况下,HarnessKit 用自己的方式禁用扩展:为 skill 的 SKILL.md 改名;把 MCP server 或 hook 从配置中移除,并在数据库中保留一份快照;为 plugin 的 manifest 改名。若 agent 自带禁用开关,则改用 agent 的开关。每种扩展的接入位置不同:
- skill。让
supports_native_skill_toggle()返回 true,在natively_disabled_skills()中读回 agent 自己的列表,并在manager.rs的toggle_skill中添加一个分支。若不把该 agent 加入test_supports_native_skill_toggle_only_hermes,该测试会失败。 - MCP server。让
supports_native_mcp_toggle()返回 true,并在manager.rs的toggle_mcp中添加一个分支,状态由read_mcp_servers()读回。若遗漏这个分支,禁用 server 会失败。请把该 agent 加入test_supports_native_mcp_toggle_only_native_agents。 - hook。hook 没有对应的开关方法。请在
manager.rs的toggle_hook中添加一个分支,放在「移除并保留快照」那条路径之前,Grok Build 和 Kiro 就是这样实现的。 - plugin。在
manager.rs的toggle_plugin中添加一个分支。若没有该分支,通用的兜底逻辑会把 plugin 的 manifest 改名为.disabled,以其他方式加载 plugin 的 agent 会因此出现问题。删除 plugin 时,agent 配置中的对应条目也需要一并删除:请在service.rs的delete_extension中添加一个分支。若 agent 有自己的卸载命令,或自带无法删除的 plugin,则改为实现plugin_removal()。
PR #157 加入了 Pi:
| 文件 | 改动内容 |
|---|---|
adapter/pi.rs | adapter 本身:skill 文件夹、mcp.json、extension、上下文文件。 |
adapter/mod.rs、adapter/omp.rs | 注册 Pi,并将其加入测试。为不含 SSE 的 Streamable HTTP 添加了远程 schema。把 Oh My Pi 读取 .ts extension 的代码移入 mod.rs 共用。 |
deployer.rs、manager.rs | 复用 Oh My Pi 写入 enabled 标志的逻辑,拒绝 SSE server,并添加 Pi 的开关分支。 |
src/lib/types.ts 及其测试、src/index.css、extension-filters.tsx、agent-card.tsx、onboarding.tsx | 顺序、名字、颜色、筛选标签、点击时长、新手引导。 |
pi-mascot.tsx、agent-mascot.tsx、mascot.css | 吉祥物。 |
README.md、README.zh-CN.md | 表格行和 agent 列表。 |
Pi 没有 shell 命令 hook,因此未修改 hook_events.rs。
crates/hk-core/src/adapter/mod.rs定义 trait、它的默认实现和all_adapters()。crates/hk-core/src/adapter/pi.rs是一个较新、篇幅较短的 adapter,适合作为参考。crates/hk-core/src/adapter/hook_events.rs存放 hook 事件表。crates/hk-core/src/manager.rs把每个原生禁用开关交给对应的写入逻辑。crates/hk-core/src/service.rs负责删除扩展,包括各 agent 删除 plugin 后的清理。