
rtk 的 Cursor 集成实战preToolUse 钩子如何透明改写 Agent 命令以削减 Token 消耗【免费下载链接】rtkCLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies项目地址: https://gitcode.com/GitHub_Trending/rtk4/rtk本文基于 rtkRust Token Killer仓库中 Cursor 钩子文档 及其配套实现展开讲解 rtk 如何借助 Cursor 的preToolUse钩子在 Agent 执行 Shell 命令前将其透明改写为rtk等价命令例如git status→rtk git status从而让 LLM 看到最多削减 90% 的 bash 输出字节。读完本文你将掌握 Cursor 钩子的 JSON 协议格式permission/updated_input、原生二进制钩子rtk hook cursor的完整处理链、遗留 shell 脚本rtk-rewrite.sh的委派机制以及通过rtk init --global --agent cursor安装/卸载钩子的全部细节。1. Cursor 集成的整体架构在 rtk 的 Agent 钩子总览 中所有 Agent 集成都被明确划分为三层各司其职层位置职责部署产物Deployed artifactshooks/cursor/安装在用户机器上的钩子文件本体安装/卸载器src/hooks/init.rsrtk init/rtk uninstall的写入、补丁、备份与原子写逻辑原生运行时src/hooks/hook_cmd.rsrtk hook cursor子命令的 JSON 协议处理与响应构造整体数据流来自 hooks/README.mdAgent 执行命令例如 cargo test --nocapture - 钩子拦截Cursor preToolUse - 读取 stdin 上的 JSON提取命令字符串 - 调用改写注册表src/discover/registry.rs - 返回 rtk cargo test --nocapture - 钩子以 Cursor 专属 JSON 格式输出响应 - Agent 实际执行改写后的命令 - 经过滤的输出进入 LLM 上下文bash 输出字节最多减少 90%关键点所有改写规则70 条模式集中在 Rust 侧的注册表 src/discover/registry.rs钩子脚本只做“解析 Agent 专属 JSON 调用改写逻辑 格式化响应”三件事自身不包含任何过滤逻辑。Cursor 集成有两个历史形态本文都会覆盖原生二进制钩子当前默认rtk init --global --agent cursor在~/.cursor/hooks.json中注册rtk hook cursor命令由 Rust 二进制直接处理 stdin/stdout遗留 shell 脚本hooks/cursor/rtk-rewrite.sh仓库中保留的薄委派脚本依赖jq和rtk 0.23.0作为旧版安装的兼容形态保留。2. Cursor 钩子的 JSON 协议Cursor 钩子说明 强调了两个与 Claude Code 钩子的核心差异这是理解整个集成协议的基础2.1 响应字段命名不同Cursor 使用permission/updated_input而 Claude Code 使用hookSpecificOutput/updatedInput。输入格式两者相同输入stdin与 Claude Code 一致{ tool_name: Bash, tool_input: { command: git status } }输出stdout发生改写时{ permission: allow, updated_input: { command: rtk git status } }2.2 所有路径都必须输出 JSON这是 Cursor 独有的契约当没有任何改写适用时钩子必须返回{}空 JSON 对象而不是像其他 Agent 钩子那样输出空字符串。Cursor 要求钩子在任何执行路径上都要给出合法的 JSON 输出否则协议会解析失败。这一契约在原生实现中被严格兑现。src/hooks/hook_cmd.rs 中的run_cursor()覆盖了所有边界情况空 stdin输出{}JSON 解析失败输出{}解析成功但tool_input.command缺失或为空输出{}权限判定为Deny命中 deny 规则或Defer无改写/不可证明构造同样输出{}把原命令交还给 Cursor 原生流程处理。只有在判定为AllowRewrite或AskRewrite时才输出带updated_input的响应分别对应两种权限值// AllowRewrite命中 Cursor 的 allow 规则 { continue: true, permission: allow, updated_input: { command: rtk git status } } // AskRewrite默认判定未配置 allow 规则 { continue: true, permission: ask, updated_input: { command: rtk git status } }这两个响应的构造分别由cursor_allow()与cursor_ask()完成hook_cmd.rs。permission: ask意味着把改写后的命令交给 Cursor 的用户确认流程——这保证了 rtk 的自动放行集合永远是宿主权限模型的子集不会比 Cursor 自身更宽松。3. 原生钩子 run_cursor 的完整处理链rtk hook cursor子命令在 src/main.rs 中分发到hooks::hook_cmd::run_cursor()。其处理链每一步都有明确的防御设计3.1 stdin 读取1 MiB 上限const STDIN_CAP: usize 1_048_576; // 1 MiBread_stdin_limited()限制单次读取上限为 1 MiB超限即报错返回防止恶意或异常的超大 payload 撑爆进程内存hook_cmd.rs。3.2 BOM 剥离针对 Windows 宿主的实测修复let input strip_leading_bom(input).trim();源码注释明确指出部分 Windows 宿主会给钩子 stdin 前置 UTF-8 BOM已在 Cursor 上确认而serde_json会拒绝带 BOM 的输入。测试用例甚至覆盖了“双重 BOM”场景tracer 包装rtk hook cursor时出现Cursor 3.2.x 实测见 hook_cmd.rs 测试。3.3 命令提取与判定命令通过 JSON 指针/tool_input/command提取随后进入decide_hook_action(cmd, permissions::Host::Cursor)hook_cmd.rs。判定逻辑分四档判定触发条件响应AllowRewrite改写成功 命中 Cursor 的Shell(...)allow 规则permission: allowAskRewrite改写成功 默认判定无规则permission: askDeny命中 Cursor deny 规则{}不阻断交还宿主Defer无可改写项 / 含不可证明构造{}原样透传3.4 不可证明构造unattestable constructs一律透传decide_from_verdict()hook_cmd.rs在改写前调用contains_unattestable_construct(cmd)做安全检查。对包含反引号替换、$(...)命令替换、文件重定向等构造的命令即使权限判定是 Allow 也直接 Defer——因为 rtk 无法在改写前证明这些动态展开部分的最终行为。对应测试test_cursor_substitution_defers_even_when_allowedgit status \rm -rf /tmp/x和git status $(rm -rf /tmp/x)都返回{}src/hooks/rewrite_cmd.rs 测试 同样验证了反引号、$()、双引号内替换、文件重定向git log /tmp/out.txt均透传而 fd 复制重定向git status 21仍可改写。3.5 改写注册表与覆盖控制实际改写由 src/discover/registry.rs 的rewrite_command()完成注册表按类别覆盖测试框架vitest/pytest/cargo test 等90-99% 节省、构建工具70-90%、VCSgit status/log/diff70-80%、语言服务tsc/mypy80-83%、Lint 器80-85%、包管理器75-80%与文件操作60-75%等模式类别与节省比例见 hooks/README.md 的注册表表格。三类覆盖机制对 Cursor 钩子同样生效RTK_DISABLED1环境变量前缀按命令禁用改写例如RTK_DISABLED1 git status原样执行。注册表在 env 前缀中检测到RTK_DISABLED时跳过改写registry.rsexclude_commands在~/.config/rtk/config.toml中列出永不改写的命令支持子命令模式git push同时排除git push origin main和^开头的正则模式Already-RTK 幂等rtk git status保持原样绝不会产生rtk rtk git。测试test_cursor_already_rtk_passthrough验证此时输出{}hook_cmd.rs。复合命令方面注册表处理、||、;、|、|、管道中仅改写 pipeline-safe 的末段/||/;两侧独立改写——例如cargo fmt --all cargo test变为rtk cargo fmt --all rtk cargo test测试test_cursor_compound_rewrite_includes_continue验证了复合改写响应同样携带continue: true。3.6 权限规则来源~/.cursor/cli-config.jsonpermissions::Host::Cursor的规则加载逻辑在 src/hooks/permissions.rs 的load_cursor_rules()读取全局~/.cursor/cli-config.json中的permissions.deny与permissions.allow数组只识别Shell(前缀的规则并剥离包装裸Shell规则映射为*。源码注释解释了设计约束只读全局配置因为 RTK 的自动放行集合必须是宿主信任集合的子集宿主还会叠加项目级配置与 folder-trustRTK 绝不能比 Cursor 更宽松。测试矩阵hook_cmd.rs 测试区覆盖了该集成的全部关键路径测试验证点test_cursor_rewrite_flat_formatallow 规则下git status输出扁平格式无hookSpecificOutput包裹test_cursor_default_verdict_rewrites无规则时默认判定走ask而非allowtest_cursor_unallowed_segment_asksgit status rm -rf /tmp/x中未授权段导致asktest_cursor_passthrough_empty_json未支持命令htop返回{}test_cursor_empty_input_empty_json空输入返回{}test_cursor_heredoc_passthroughheredoc 命令透传返回{}test_cursor_deny_blocks_rewritedeny 规则阻断改写3.7 审计日志设置RTK_HOOK_AUDIT1后每次 rewrite/ask/deny 都会追加写入~/.local/share/rtk/hook-audit.log格式为时间戳 | 动作 | 原命令 | 改写后命令字段做了反斜杠/竖线/换行转义以防日志注入hook_cmd.rs。这是排查“某条命令为什么被改写/没被改写”的第一手依据。4. 遗留 shell 脚本 rtk-rewrite.sh薄委派模式hooks/cursor/rtk-rewrite.sh67 行是仓库保留的旧版安装产物脚本头注释声明兼容 Cursor 编辑器与 cursor-cli两者共享~/.cursor/hooks.json。它体现了 rtk 钩子设计的“薄委派”原则——自身零过滤逻辑全部改写决策委托给rtk rewrite子进程INPUT$(cat) CMD$(echo $INPUT | jq -r .tool_input.command // empty) if [ -z $CMD ]; then echo {} exit 0 fi # 委托给 Rust 二进制。 # 退出码0 允许改写, 1 不改写透传, 2 拒绝, 3 询问 REWRITTEN$(rtk rewrite $CMD 2/dev/null) RC$?4.1 前置守卫Guards脚本开头有三层守卫任一层失败都只向 stderr 打警告并exit 0jq缺失警告并退出jq是提取命令字段的唯一依赖rtk不在 PATH警告并退出版本守卫rtk rewrite子命令自 0.23.0 引入脚本解析rtk --version输出若MAJOR0 MINOR23则警告并退出。4.2 rtk rewrite 的退出码契约rtk rewrite的退出码协议在 src/hooks/rewrite_cmd.rs 有权威定义退出码stdout含义0改写后命令允许改写钩子可自动放行1空无 RTK 等价项钩子原样透传2空命中 deny 规则交还宿主原生拒绝流程3改写后命令命中 ask 规则改写但由宿主发起用户确认shell 脚本对该契约的解读是只有 0 或 3 才继续构造响应RC 为 3 时把permission置为ask脚本注释说明 Cursor 当前未强制执行 ask 语义此处属于“面向未来的预留”。4.3 优雅降级永不阻断命令执行hooks/README.md 的“Exit Code Contract”规定了所有钩子的一条铁律任何错误路径缺二进制、坏 JSON、改写失败都必须退出 0否则 Agent 的命令会被阻断。脚本中“改写结果与原命令相同则输出{}”的分支第 52-55 行也保证了幂等场景零副作用。需要注意的是当前新安装走的是原生二进制路径见下一节该脚本主要作为遗留安装的存在保留rtk init --global --agent cursor会主动清理它。5. 安装与卸载rtk init / rtk uninstall5.1 安装命令rtk init --global --agent cursor # 等价简写rtk init -g --agent cursorCursor 钩子是global-only不带--global时rtk init会直接报错Cursor hooks are global-only. Use: rtk init -g --agent cursorinit.rs。5.2 安装流程install_cursor_hooksinstall_cursor_hooks()init.rs执行四步第一步迁移清理。若存在旧版脚本~/.cursor/hooks/rtk-rewrite.sh删除它并顺带清掉hooks.json中指向该脚本的陈旧条目remove_legacy_cursor_hooks_json_entries。第二步幂等检查。cursor_hook_already_present()检查hooks.preToolUse数组中是否已有 command 包含rtk-rewrite.sh或等于rtk hook cursor的条目已存在则跳过。第三步写入条目。insert_cursor_hook_entry()init.rs向~/.cursor/hooks.json追加{ version: 1, hooks: { preToolUse: [ { command: rtk hook cursor, matcher: Shell } ] } }其中matcher: Shell把钩子触发范围限定在 Shell 工具上命令常量CURSOR_HOOK_COMMAND rtk hook cursor定义在 src/hooks/constants.rs。第四步备份 原子写。写前把原文件复制为hooks.json.bak写入采用“同目录临时文件 rename”的原子模式atomic_write崩溃也不会留下半写文件。完成后打印提示Cursor reloads hooks.json automatically——Cursor 会自动重新加载hooks.json无需重启。安装路径常量CURSOR_DIR .cursor、HOOKS_JSON hooks.jsonconstants.rs。5.3 卸载rtk uninstall --global --agent cursorremove_cursor_hooks()init.rs做三件事删除遗留脚本文件从hooks.json中精确移除 RTK 条目同时匹配遗留脚本路径与rtk hook cursor命令其他用户自定义钩子不受影响修改前同样先备份为hooks.json.bak再原子写。支持--dry-run预览全部动作而不落盘。5.4 验证安装后可以直接查看~/.cursor/hooks.json确认条目存在日常调试建议开启审计日志观察真实改写行为export RTK_HOOK_AUDIT1 # 在 Cursor Agent 中让 Agent 执行 git status 等命令 cat ~/.local/share/rtk/hook-audit.log日志中会出现形如2026-09-06T13:00:00 | rewrite | git status | rtk git status的记录或ask/deny/skip:defer等动作标记。6. 适用前提与注意事项综合 Cursor 钩子说明 与源码使用该集成时需要注意以下边界版本要求原生二进制钩子需要rtk支持rtk hook cursor子命令rtk rewrite自 0.23.0 引入遗留 shell 脚本路径则额外要求jq全局作用域安装/卸载都必须带--global钩子注册在用户级~/.cursor/hooks.json对所有 Cursor 会话生效编辑器与 cursor-cli 共用JSON 全路径契约所有代码路径含错误路径都输出 JSON这是与其他 Agent 钩子最大的协议差异二次开发时不可“输出空字符串代替{}”权限模型不放大RTK 只自动放行命中~/.cursor/cli-config.json中Shell(...)allow 规则的命令其余改写一律走ask对不可证明构造命令替换、重定向、heredoc一律透传不碰可观测性RTK_HOOK_AUDIT1提供逐次改写的审计日志rtk gain可查看 token 节省统计节省比例数字来自仓库文档的标注区间实际节省取决于命令类别与输出量。7. 小结rtk 的 Cursor 集成展示了“单一事实源 多宿主薄适配”的工程设计70 条改写规则集中在 src/discover/registry.rsCursor 侧仅需处理两件独有的事——permission/updated_input字段命名与“全路径输出 JSON含{}”契约。原生实现 run_cursor() 通过 1 MiB stdin 上限、BOM 剥离、权限判定四档分流与审计日志把“透明改写”做到既不改变用户工作流、也不放大宿主权限模型的程度rtk-rewrite.sh 则作为兼容遗留安装的薄委派脚本保留。对希望在 Cursor Agent 会话中降低 LLM 上下文开销的开发者rtk init --global --agent cursor一条命令即可完成接入。【免费下载链接】rtkCLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies项目地址: https://gitcode.com/GitHub_Trending/rtk4/rtk创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考