首页
/ claw-code 原生 Git 上下文工具:GitStatus / GitDiff / GitLog / GitShow / GitBlame 五个工具的设计与源码解析

claw-code 原生 Git 上下文工具:GitStatus / GitDiff / GitLog / GitShow / GitBlame 五个工具的设计与源码解析

2026-09-04 13:09:22作者:虞亚竹Luna

在 claw-code 的 Rust 工具层(rust/crates/tools)中,仓库为模型提供了五个原生的只读 Git 上下文工具,用结构化、可发现的工具定义替代了通过 bash 拼凑临时 git 命令的旧模式。本文以 rust/crates/tools/GIT_TOOLS_README.md 为主体,完整覆盖五个工具的全部参数、输入输出示例与使用场景,并结合 工具 crate 源码 中真实的参数拼装逻辑、权限模型与测试用例,说明每个工具在底层是如何构造 git 命令、处理错误以及受权限门控约束的。读完本文,你既能直接按文档使用这五个工具,也能理解它们的实现边界与限制。

为什么需要原生 Git 工具

在引入这组工具之前,模型若要读取仓库状态,只能通过 bash 工具执行 ad-hoc 的 git 命令。文档明确指出这存在四个问题:

  • 无结构化输出:Bash 返回原始文本,需要模型自行解析;
  • 权限过宽:即使是只读的 git status,Bash 也需要 DangerFullAccess 级别权限;
  • 不可发现:模型无法通过 ToolSearch 检索到具备 git 能力的工具;
  • 不一致:每次调用可能使用不同的参数与格式。

换成原生工具后,五个工具全部声明为 ReadOnly 权限模式,在受限权限模式下安全可用;输出为结构化的 JSON;可通过 ToolSearch 用 "git"、"diff"、"blame" 等关键词发现;每个工具的描述文字都会向模型解释何时应优先于 bash 使用。这些声明均可在源码的静态工具表中逐一印证——五个 ToolSpecrequired_permission 字段全部为 PermissionMode::ReadOnly,见 mvp_tool_specs 中的 Git 工具定义

架构:五工具统一的四层实现模式

文档将五个工具的共同架构归纳为四个层次,这一模式与源码完全一致:

  1. ToolSpec —— 定义工具名、描述、JSON 输入 schema 以及 PermissionMode::ReadOnly。在源码中,五个工具依次注册于 工具静态表。值得注意的是,每个 schema 都带有 "additionalProperties": false,即严格拒绝未声明的多余字段,这是比文档更进一步的输入契约;
  2. Input struct —— 派生 Deserialize,可选字段使用 #[serde(default)]。五个对应的结构体定义于 输入结构体区块,例如 GitStatusInput 只有一个可选的 short: Option<bool> 字段;
  3. Run 函数 —— 拼装 git 参数、调用 git_stdout() 辅助函数、再用 to_pretty_json() 将结果包装为 JSON。实现位于 run_git_* 函数区
  4. Dispatch —— 与其他工具一样在 execute_tool_with_enforcer() 中按工具名匹配分发,见 分发匹配块
"GitStatus" => from_value::<GitStatusInput>(input).and_then(run_git_status),
"GitDiff"   => from_value::<GitDiffInput>(input).and_then(run_git_diff),
"GitLog"    => from_value::<GitLogInput>(input).and_then(run_git_log),
"GitShow"   => from_value::<GitShowInput>(input).and_then(run_git_show),
"GitBlame"  => from_value::<GitBlameInput>(input).and_then(run_git_blame),

分发前的权限校验由 enforce_permission_check 完成:每次工具调用都会经过权限门控,被拒绝时返回带原因的错误。按照 tools crate 的 AGENTS.md 约定,所有工具分发都不得绕过权限检查,工具边界签名统一为 Result<String, String>

git_stdout 辅助函数

所有五个工具最终都委托给同一个辅助函数 git_stdout(args: &[&str]) -> Option<String>,其语义值得注意:

fn git_stdout(args: &[&str]) -> Option<String> {
    let output = Command::new("git").args(args).output().ok()?;
    if !output.status.success() {
        return None;
    }
    let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
    (!stdout.is_empty()).then_some(stdout)
}

它负责启动 git 子进程,返回去除首尾空白后的 stdout;当 git 进程启动失败、退出码非零、或 stdout 为空时返回 None。因此每个 run_git_* 函数在拿到 None 时都会返回针对该命令定制的中文错误提示,例如 "git status failed. Ensure the current directory is inside a git repository."。这意味着工具成功执行时始终返回 {"output": "..."} 这样的 JSON 对象,失败时返回可诊断的错误字符串,而不是空值。

GitStatus:查看工作区状态

展示工作树状态(分支、已暂存、未暂存、未跟踪文件),等价于 git status --short --branch

参数 类型 必填 默认值 说明
short boolean true 使用 --short --branch 格式输出简洁结果

示例输入:

{}

示例输出:

{
  "output": "## feat/git-aware-tools...upstream/main [ahead 1]\nM rust/crates/tools/src/lib.rs"
}

源码实现见 run_git_status:当 shorttrue(未提供时也按 true 处理)时追加 --short --branch;显式传 false 则回退为完整的 git status 输出。

GitDiff:查看差异

展示提交、索引与工作树之间的变化,支持已暂存变更、指定路径、提交范围以及两个提交之间的比较。

参数 类型 必填 默认值 说明
staged boolean false 显示已暂存变更(git diff --cached
commit string 与之对比的提交哈希、标签或分支
commit2 string 范围对比的第二个提交(commit...commit2
path string 限定 diff 到某个文件路径

示例输入:

{}
{ "staged": true }
{ "commit": "HEAD~3", "path": "rust/crates/tools/src/lib.rs" }
{ "commit": "main", "commit2": "feat/git-aware-tools" }

run_git_diff 的参数拼装 可以看到几个精确行为:

  • stagedtrue 时插入 --cached
  • commitcommit2 同时提供时,拼成 "{commit}...{commit2}" 三点范围语法;只提供 commit 时直接作为对比基线;
  • path 存在时会插入 -- 分隔符再跟路径,避免以 - 开头的文件名被误解析为选项。

GitLog:查看提交历史

展示提交历史,支持数量限制、按作者/日期/路径过滤以及 oneline 格式,默认返回最近 20 条提交。

参数 类型 必填 默认值 说明
count integer 20 返回的最大提交数
oneline boolean false 使用 --oneline 格式(仅哈希 + 标题)
author string 按作者模式过滤提交
since string 起始日期过滤(如 "2024-01-01""2.weeks"
until string 截止日期过滤
path string 按文件或目录路径过滤提交

示例输入:

{ "count": 5, "oneline": true }
{ "author": "alice", "since": "1.week", "path": "src/main.rs" }

源码层面(run_git_log),count 会拼成 -n{count} 形式追加到参数最前,author/since/until 分别对应 --author=--since=--until=path 同样以 -- 分隔。工具 schema 中还声明了 "count": { "type": "integer", "minimum": 1 },保证至少返回一条记录。

GitShow:查看提交、标签或树对象

展示某个提交、标签或树对象及其 diff,支持查看某提交下的特定文件(commit:path 语法),并支持仅显示统计信息。

参数 类型 必填 默认值 说明
commit string 要显示的提交哈希、标签或分支引用
path string 仅显示该提交下的此文件(commit:path 语法)
stat boolean false 显示 diffstat 摘要而非完整 diff

示例输入:

{ "commit": "HEAD" }
{ "commit": "abc1234", "stat": true }
{ "commit": "main", "path": "src/lib.rs" }

源码实现 run_git_show 比文档表格更进一步:当前代码在 stat 之外还引入了 format 参数(取值为 "patch"(默认)、"stat""metadata",见 GitShow 的 ToolSpec schema),其语义为:

  • format: "metadata" 会追加 --format=medium --no-patch,只输出提交元信息而不含 diff;
  • format 一旦提供即优先于 stat 布尔值,stat: true 被保留为向后兼容的旧式写法;
  • 传入未知取值会返回明确错误:unknown GitShow format: "..."
  • metadata 格式不能与 path 组合——metadata 描述的是提交对象而非某个 blob,源码直接拒绝该组合并提示改用 patch/stat 或省略 path
  • 提供 path 时,工具自动拼为 {commit}:{path} 语法传给 git show

这些行为均有对应测试覆盖,例如 git_show 相关测试用例 验证了 schema 暴露 format 枚举、patch/stat/metadata 三种格式均能执行、stat: true 的旧式写法兼容、以及非法格式值会返回 "unknown GitShow format" 错误。

GitBlame:逐行溯源

展示文件的每一行最近一次由哪个修订和哪个作者修改,支持行范围过滤。

参数 类型 必填 默认值 说明
path string 要 blame 的文件路径
start_line integer 行范围起点(1 基)
end_line integer 行范围终点(1 基)

示例输入:

{ "path": "src/main.rs" }
{ "path": "src/main.rs", "start_line": 100, "end_line": 150 }

run_git_blame 的实现看,行范围会拼为 git 原生的 -L{start},{end} 选项;一个文档未明说的细节是:start_lineend_line 必须同时提供才会生效(源码中用 if let (Some(start), Some(end)) 配对判断),只提供其一会被静默忽略,因此实际使用时应成对传入。schema 层同样以 minimum: 1 约束行号从 1 开始。

权限模型与分发链路

每个工具被调用时,完整链路是:模型发出带 JSON 输入的工具调用 → execute_tool_with_enforcer() 按工具名匹配分发 → 分发前先经 PermissionEnforcer 做权限检查(enforce_permission_check 将检查结果映射为 Ok 或携带拒绝原因的 Err)→ from_value 将 JSON 反序列化为对应的 Input struct(多余字段因 additionalProperties: false 被 schema 拒绝)→ run_git_* 拼装参数并调用 git_stdout() → 成功时经 to_pretty_json 返回 {"output": ...},失败时返回包含操作上下文的错误消息。

由于五个工具都是 ReadOnly 权限,它们可以在受限权限模式下直接运行;这与 bash 中即使是只读 git 命令也需要更高权限形成对比。对照 tools crate 的 AGENTS.md 中记录的约定:所有工具分发都必须经过权限门控,工具边界签名统一为 Result<String, String>,测试命名遵循 BDD 风格的 given_x_when_y_then_z

构建与测试验证

文档给出的验证命令如下:

cd rust
cargo build --release
cargo test -p tools

其中 cargo test -p tools 针对 tools crate 运行全部测试,包括上节提到的 GitShow format 枚举、三种输出格式、legacy stat 兼容与非法格式拒绝等用例。文档同时说明:该 crate 中存在 3 个与本次改动无关的既有测试失败(agent_fake_runneragent_persists_handoffworker_create_merges_config),原因是本地 settings.json 配置不兼容,判断测试结果时应将这 3 个失败排除在外。

使用前提与限制

综合文档与源码,使用这五个工具时需要注意以下边界:

  • 工作目录必须在 git 仓库内git_stdout() 基于子进程的 stdout 判断成败,不在仓库内执行时各工具会返回形如 "Ensure the current directory is inside a git repository" 的错误提示,而不是静默返回空;
  • 空结果视为失败git_stdout() 在 stdout 为空时返回 None,因此"命令成功但没有可显示内容"(例如 git diff 无差异)会以错误形式返回,调用方需要理解这一语义;
  • 严格只读:五个工具只覆盖 status/diff/log/show/blame 等只读子命令,且 schema 拒绝任何未声明参数,无法借此执行写操作;
  • GitShowformatpath 存在组合约束metadata 格式不可与 path 同时使用;
  • GitBlame 行范围必须成对提供start_lineend_line 单独提供不生效。

这五个工具把最常用的仓库只读查询收敛为模型可直接发现、可直接调用、权限最小化的结构化接口,其参数设计与错误处理均可在 rust/crates/tools/src/lib.rs 中逐行对照验证,适合作为在 Agent 工具层中集成领域命令的典型参考。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384