RTK 的 Copilot 集成详解:PreToolUse 双格式钩子如何透明改写命令并节省 Token
本篇指南聚焦 rtk-awareness.md 所描述的 GitHub Copilot(VS Code Copilot Chat + Copilot CLI)集成方案:通过 .github/copilot-instructions.md 指令文件与 rtk hook copilot 这个跨平台 Rust 钩子二进制,把 Agent 执行的原始 shell 命令自动改写为 rtk 前缀形式,在不改变开发者工作流的前提下将进入 LLM 上下文的 bash 输出削减最高 90%。读完本文,你将掌握安装验证、两种输入格式的自动检测原理、updatedInput 透明改写与 deny-with-suggestion 回退机制,并能用仓库自带测试脚本逐条验证钩子行为。
一、双层自动机制:指令文件 + PreToolUse 钩子
RTK 对 Copilot 的集成由两层互补机制构成,二者均自动生效:
1. .github/copilot-instructions.md(提示层)
该文件在会话启动时被 Copilot CLI 和 VS Code Copilot Chat 加载,指示 Copilot 自动为命令添加 rtk 前缀。它属于"提示级引导"——依赖模型遵循指令,无法强制拦截。
2. .github/hooks/rtk-rewrite.json(强制层)
该钩子配置通过 rtk hook 注册了一个 PreToolUse 安全网:一个跨平台 Rust 二进制拦截原始 bash 工具调用并进行改写。没有 shell 脚本依赖、没有 jq 依赖,在 Windows 上原生工作。
从源码看,这两个文件正是 rtk init --copilot 写入的产物。src/hooks/init.rs 中的 run_copilot_at 依次执行两步:
- Upsert 指令标记块:将带
<!-- rtk-instructions v2 -->标记的 RTK 指令块写入.github/copilot-instructions.md,保留用户已有内容(见 COPILOT_INSTRUCTIONS 常量); - 写入钩子配置:写入
.github/hooks/rtk-rewrite.json,内容为单条 PascalCasePreToolUse注册:
{
"version": 1,
"hooks": {
"PreToolUse": [
{
"type": "command",
"command": "rtk hook copilot",
"cwd": ".",
"timeout": 5
}
]
}
}
相关文件名的来源可在 src/hooks/constants.rs 中确认:.github 目录、钩子文件 rtk-rewrite.json、指令文件 copilot-instructions.md,以及全局安装目录 .copilot。
值得注意的是 init.rs 中的注释:早期版本曾为 Copilot CLI 的原生 schema 额外注册一条 camelCase preToolUse 条目,但实测发现 Copilot CLI 会把两个 key 当作独立钩子顺序执行,导致每次工具调用都产生一次冗余的进程启动。自 Copilot CLI 1.0.73+ 起,CLI 会把自身的原生 bash/powershell 运行时工具映射为 tool_name: "Bash" 的 PascalCase schema 并直接尊重 updatedInput,因此新安装只保留单条 PreToolUse 注册即可。
二、Meta 命令(直接使用,勿改写)
以下元命令应被 Agent 直接调用,钩子不会对其二次改写:
rtk gain # Token savings dashboard for this session
rtk gain --history # Per-command history with savings %
rtk discover # Scan session history for missed rtk opportunities
rtk proxy <cmd> # Run raw (no filtering) but still track it
这与 src/hooks/init.rs 中写入 copilot-instructions.md 的 "Meta commands (use directly)" 区块完全一致。源码层面的防重入保障位于 hook_cmd.rs 的 get_rewritten:改写函数调用 src/discover/registry.rs 的 rewrite_command 后,若返回值与原命令相同(例如命令已经带有 rtk 前缀),钩子判定为"无需改写",静默放行——这就是不会产生 rtk rtk git status 的原因。
三、安装验证
rtk --version # Should print: rtk X.Y.Z
rtk gain # Should show a dashboard (not "command not found")
which rtk # Verify correct binary path
名称冲突警告:如果
rtk gain失败,你安装的可能是reachingforthejack/rtk(Rust Type Kit)而非本项目的 rtk。请通过which rtk核对二进制路径后重新安装本项目的 rtk。
四、钩子工作原理:rtk hook 如何读取 stdin 并响应
rtk hook 从 stdin 读取 PreToolUse JSON,自动识别 Agent 格式后作出响应。文档描述的两种主流程:
VS Code Copilot Chat(支持 updatedInput —— 透明改写,无拒绝感)
- Agent 执行
git status→rtk hook通过PreToolUse拦截 rtk hook检测到 VS Code 格式(tool_name/tool_input键,snake_case)- 返回
hookSpecificOutput.updatedInput.command = "rtk git status" - Agent 静默执行改写后的命令——无拒绝、无重试
GitHub Copilot CLI(deny-with-suggestion —— 旧版原生 schema 下 CLI 忽略 updatedInput)
- Agent 执行
git status→rtk hook通过PreToolUse拦截 rtk hook检测到 Copilot CLI 格式(toolName/toolArgs键,camelCase)- 返回带建议的拒绝响应,reason 形如
"Token savings: use 'rtk git status' instead" - Copilot 读取 reason 并重新执行
rtk git status
文档同时指出:一旦 Copilot CLI 全面支持 updatedInput,只需更新 rtk hook 本身,钩子配置文件无需任何改动。从源码结构看,这一设计确实成立——格式分派集中在单一 Rust 函数内,配置侧只有一条命令注册。
4.1 源码级细节:输入读取与格式检测
run_copilot 是入口函数,处理链条如下:
- 限流读取:stdin 上限 1 MiB(STDIN_CAP),防止异常输入耗尽内存;
- BOM 剥离:部分 Windows 宿主会在钩子 stdin 前附加 UTF-8 BOM,strip_leading_bom 先行清理,否则
serde_json解析直接失败; - JSON 解析失败不阻塞:解析错误仅向 stderr 输出警告并
Ok(())返回——这遵循 hooks/README.md 中定义的退出码契约:钩子在任何错误路径(二进制缺失、坏 JSON、改写失败)都必须退出 0,因为非零退出会阻止用户命令执行; - stdout 协议洁癖:模块头注释特别指出使用
writeln!(stdout, ...)且不允许任何杂散输出污染 stdout,因为 JSON 协议之外的任何字节都会破坏宿主对钩子响应的解析。
detect_format 实现了双格式自动检测,其分派逻辑映射到 HookFormat 枚举 的四个分支:
| 分支 | 触发条件 | 响应形态 |
|---|---|---|
VsCode |
tool_name 为 runTerminalCommand / run_in_terminal / Bash / bash,且 tool_input.command 非空 |
updatedInput 透明改写 |
CopilotCli |
toolName 为 bash/powershell,toolArgs 为 JSON 字符串(旧 camelCase schema) |
保留宿主元数据的 modifiedArgs 改写 |
CopilotIde |
toolName 为 run_in_terminal(JetBrains/IntelliJ Copilot 插件) |
deny-with-suggestion(该宿主只尊重顶层 deny 决策) |
PassThrough |
非 bash 工具、已是 rtk 命令或未知格式 | 静默放行(无输出) |
其中 run_in_terminal 出现在 VS Code 分支并非笔误:源码注释说明这是 VS Code Copilot Chat 真实终端工具名(通过实际负载捕获确认),若缺失该匹配,钩子对 VS Code Copilot Chat 将永不触发。
4.2 三种响应构建器
- VS Code / Copilot CLI PascalCase 路径(vscode_response_from_decision):构建
hookSpecificOutput,包含hookEventName: "PreToolUse"、permissionDecisionReason: "RTK auto-rewrite"与updatedInput.command。一个精细之处是:permissionDecision: "allow"仅在命中用户显式 Allow 规则时才输出;Default 或 Ask 判定的改写会省略该字段,把权限流程交还给宿主自身的原生 prompt/allowlist 机制(源码注释引用了断言"ask"曾导致 Copilot CLI 1.0.66+ 弹出不可记忆的阻塞对话框的回归事故); - 旧 Copilot CLI camelCase 路径(copilot_cli_response_from_decision):克隆完整的
toolArgs对象、仅替换其中的command字段后放入modifiedArgs返回——保留宿主提供的 description、initial_wait、mode 等工具必需的元数据; - JetBrains 路径(copilot_ide_response_from_decision):由于该宿主只尊重顶层 deny 决策,改写统一以 deny + reason 形式表达,reason 中用反引号包裹完整的 rtk 命令供 Agent 复跑。
4.3 改写决策链与权限闸门
进入响应构建之前,decide_hook_action 会先做权限与结构检查:
- 若 src/hooks/permissions.rs 判定为 Deny → 记审计日志后放行宿主处理(返回
None,即无输出); - 若命令含"不可证明构造"(contains_unattestable_construct 检查)→
Defer,不介入; - 再调用
get_rewritten:跳过 heredoc(has_heredoc),加载~/.config/rtk/config.toml中的exclude_commands与transparent_prefixes配置,最终委托给 src/discover/registry.rs 的模式注册表完成实际改写。
这意味着钩子的行为可以被用户配置精细控制:RTK_DISABLED=1 git status 可临时旁路单条命令,exclude_commands 可永久排除特定命令(支持子命令模式与 ^ 开头的正则)。
4.4 遗留配置自愈
heal_legacy_copilot_configs 是一个有意思的健壮性设计:当检测到旧版本 rtk init --copilot 写下的 camelCase preToolUse 遗留条目、且 PascalCase PreToolUse 条目仍然存在时,钩子会在运行时原子性地重写 .github/hooks/rtk-rewrite.json(项目级)与 ~/.copilot/hooks/rtk-rewrite.json(全局级),删除冗余条目并只保留用户自己的附加项,同时写入 self_heal 审计日志。用户无需手动清理旧配置。
五、集成方式横向对比
原文档给出的集成对照表(已按仓库路径校正 OpenCode 插件的实际位置 hooks/opencode/rtk.ts):
| Tool | Mechanism | Hook output | File |
|---|---|---|---|
| Claude Code | PreToolUse hook with updatedInput |
Transparent rewrite | hooks/claude/rtk-rewrite.sh |
| VS Code Copilot Chat | PreToolUse hook with updatedInput |
Transparent rewrite | .github/hooks/rtk-rewrite.json |
| GitHub Copilot CLI | PreToolUse deny-with-suggestion |
Denial + retry | .github/hooks/rtk-rewrite.json |
| OpenCode | Plugin tool.execute.before |
Transparent rewrite | hooks/opencode/rtk.ts |
| (any) | Custom instructions | Prompt-level guidance | .github/copilot-instructions.md |
从 hooks/README.md 的完整 Agent 支持矩阵可看到 Copilot 集成的定位:它是少数使用 Rust 二进制(而非 shell 脚本)实现 Full hook 层的 Agent 之一,与 Claude Code、Cursor 同级,但机制上支持两种宿主行为(透明改写 + 拒绝重试),维护成本高于纯规则文件类集成(Cline、Windsurf、Codex 走 prompt 级引导)。
六、本地验证:用测试脚本复现全部行为
仓库自带测试脚本 hooks/copilot/test-rtk-rewrite.sh(配合 hooks/copilot/README.md 说明使用),它向 rtk hook 投喂模拟的 PreToolUse JSON 并断言决策结果:
bash hooks/copilot/test-rtk-rewrite.sh
脚本覆盖四类断言:
- Copilot CLI 拦截(
test_deny):git status、git log --oneline -10、git diff HEAD、cargo test、cargo clippy --all-targets、grep -rn pattern src/、gh pr list等命令应返回permissionDecision: deny,且 reason 中包含对应的rtk ...命令; - VS Code 改写(
test_vscode_rewrite):git status、cargo test、gh pr list应返回hookSpecificOutput.permissionDecision: allow且updatedInput.command以rtk开头; - 透传(
test_allow):已是 rtk 的命令(rtk git status)、heredoc、未知命令(htop、echo)、非 bash 工具(view、edit、editFiles)均应无任何输出且退出码 0; - 输出格式:Copilot CLI 输出为合法 JSON 且 reason 含反引号包裹的 rtk 命令;VS Code 输出为合法 JSON、决策为
allow、updatedInput.command以rtk开头。
全部用例通过时脚本打印 ALL N TESTS PASSED,退出码为失败用例数。
七、适用前提与限制小结
- 适用宿主:VS Code Copilot Chat 与 GitHub Copilot CLI;
rtk hook copilot同时兼容 Copilot CLI 1.0.73+(PascalCasePreToolUse+updatedInput)与旧安装残留的 camelCase schema,以及 JetBrains/IntelliJ Copilot 插件的run_in_terminal形式; - 安装位置:项目级安装写入当前目录
.github/;全局安装可落在~/.copilot/(COPILOT_HOME可覆盖),copilot_user_dir 在自愈逻辑中即引用该解析函数; - 非阻塞承诺:所有错误路径(坏 JSON、stdin 超限、改写失败)均以退出 0 结束,命令按原样执行——钩子永远不会成为开发流程的单点故障;
- 改写范围:由 src/discover/registry.rs 的模式注册表统一裁决(70+ 条模式,覆盖测试运行器、构建工具、VCS、lint、包管理器等类别),钩子本身不内置任何过滤逻辑,属于"薄委托"设计,保证全部 Agent 集成共享单一改写事实源;
- 版本敏感点:Copilot CLI 是否走透明改写路径取决于其版本是否尊重 PascalCase schema 的
updatedInput;文档所述 "CLI 忽略 updatedInput、走 deny-with-suggestion" 是针对旧版原生 camelCase schema 的行为,当前 COPILOT_HOOK_JSON 已只注册 PascalCase 单条目。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00