fix: 同步上游 D 块 —— worktree 清理静默空转 + Gemini 子智能体支持勘误(#19)

## 1) finishing-a-development-branch:worktree 清理静默空转(上游 0b47219)

真 bug,我们这边与上游同样存在。步骤 6 在步骤 5 已经 cd 到主仓库根之后才用
`git rev-parse --show-toplevel` 重算 WORKTREE_PATH,于是拿到主根路径 ——
`.worktrees/` 溯源判断永远匹配不上,清理静默空转,随后分支删除还会因为
worktree 仍挂着而失败。上游记录说测试对象不得不偏离 skill 原文才能跑通。

修法(4 处):
- 步骤 2 趁还在工作区内就捕获 WORKTREE_PATH
- 步骤 6 改为消费步骤 2 的值,并加显式警告说明为什么不能在此重算
- 步骤 6 去掉冗余的 MAIN_ROOT 推导与 cd(两个调用方都已切好目录)
- 选项 2 补上菜单已声称的分离 HEAD 推送变体

已实际复现验证(临时仓库 + .worktrees/feature):
- 旧逻辑:WORKTREE_PATH 算得 "main" -> 溯源未命中 -> 清理空转
- 新逻辑:捕获到 "main/.worktrees/feature" -> 命中 -> worktree remove 成功、
  branch -D 成功(旧逻辑下会因 worktree 仍挂着而失败)

上游第 4 处改动(rationalization 表里"测试早先通过"那行的措辞)本次不适用 ——
我们还是「红线」清单,那张表属 C 块。

## 2) gemini-tools.md:我们错写成「不支持子 Agent」

我们的版本称 Gemini CLI 没有 Task 等价物、依赖子智能体的 skill「退化为
executing-plans 单会话执行」。实际上 Gemini CLI 通过 `invoke_agent`
(`agent_name: "generalist"`,也可用 `@generalist` 聊天语法)支持子智能体,
并且支持在同一响应里发多个调用做并行分派。

这个错误会让 Gemini CLI 用户的 subagent-driven-development /
dispatching-parallel-agents / requesting-code-review 全部瘸腿。

按上游新版重写(33 行 -> 62 行),补齐:动作导向的映射表、指令文件
(GEMINI.md 层级加载)、个人 skills 目录(~/.gemini/skills 与 ~/.agents/skills
的优先级)、子智能体支持与模板填写、并行分派,以及 tracker_* / complete_task /
update_topic / read_mcp_resource 等此前缺失的 20 个工具名。

已全仓扫描,确认没有别处重复这个错误说法。

## 3) audit.sh 结构漂移度量修正(顺带发现的假阳性来源)

3c/3d 用 `grep -cE '^#{1,4} '` 数标题,但这会把 ``` 围栏内的 shell 注释
(`# 运行测试`)当成 markdown 标题 —— 一个 skill 里多几行 bash 注释就能
凭空造出「结构漂移」。

用正确口径(排除围栏)重算 14 个 skill,3 条告警里 2 条是假阳性:
- executing-plans            9/16 WARN -> 9/11  pass
- finishing-a-development-branch 21/31 WARN -> 14/17 pass
- using-git-worktrees        21/28 WARN -> 14/21 WARN(真漂移,保留)

这点很要紧:#19 里引用的上游欠账数字此前是被虚高的。

改为 awk 逐行跟踪围栏状态。双向验证:给 brainstorming 加 5 个真标题会触发
告警(7 vs 13),加 5 行围栏内 shell 注释不触发。

回归:audit.sh 152 pass / 1 warn / 0 fail(原 150 pass / 3 warn)
      verify-release.sh 82 pass / 0 fail
This commit is contained in:
AI不止语
2026-08-07 23:10:24 +08:00
parent eadc83b64b
commit a662295898
3 changed files with 72 additions and 35 deletions

View File

@@ -153,6 +153,14 @@ else
done
# 3c. 14 翻译 skill 结构层级H1-H4 标题数)
#
# 必须排除 ``` 围栏内的行shell 注释(`# 运行测试`)同样匹配 ^#{1,4}
# 会被当成 markdown 标题数进去。用 grep 直接数的话,一个 skill 里多几行
# bash 注释就能凭空造出「结构漂移」—— executing-plans 与
# finishing-a-development-branch 两条告警此前就是这么来的假阳性。
count_headings() { # 读 stdin只数围栏之外的 H1-H4
awk '/^```/{fence = !fence; next} !fence && /^#{1,4} /{n++} END{print n+0}'
}
declare -a SKILLS=(brainstorming dispatching-parallel-agents executing-plans \
finishing-a-development-branch receiving-code-review requesting-code-review \
subagent-driven-development systematic-debugging test-driven-development \
@@ -160,8 +168,8 @@ else
writing-plans writing-skills)
for s in "${SKILLS[@]}"; do
up=$(git show upstream/main:skills/$s/SKILL.md 2>/dev/null | grep -cE '^#{1,4} ' || echo 0)
our=$(grep -cE '^#{1,4} ' "skills/$s/SKILL.md" 2>/dev/null || echo 0)
up=$(git show upstream/main:skills/$s/SKILL.md 2>/dev/null | count_headings || echo 0)
our=$(count_headings < "skills/$s/SKILL.md" 2>/dev/null || echo 0)
diff=$((up - our))
abs=${diff#-}
# 允许 3 个 header 差异(翻译造成的合并/拆分小幅波动)
@@ -173,8 +181,8 @@ else
done
# 3d. requesting-code-review/code-reviewer.md 结构v5.1.0 self-contained
up=$(git show upstream/main:skills/requesting-code-review/code-reviewer.md 2>/dev/null | grep -cE '^#{1,3} ' || echo 0)
our=$(grep -cE '^#{1,3} ' skills/requesting-code-review/code-reviewer.md)
up=$(git show upstream/main:skills/requesting-code-review/code-reviewer.md 2>/dev/null | count_headings || echo 0)
our=$(count_headings < skills/requesting-code-review/code-reviewer.md)
diff=$((up - our))
abs=${diff#-}
if [ "$abs" -le "2" ]; then

View File

@@ -50,6 +50,9 @@ npm test / cargo test / pytest / go test ./...
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
# 现在就捕获 —— 此刻还在工作区里面。步骤 5 会切换目录,
# 而清理(步骤 6需要这个值
WORKTREE_PATH=$(git rev-parse --show-toplevel)
```
这决定了展示哪种菜单、以及清理方式:
@@ -129,6 +132,8 @@ git branch -d <feature-branch>
```bash
# 推送分支
git push -u origin <feature-branch>
# 从分离 HEAD 出发时,在远端指定新分支名:
# git push origin HEAD:refs/heads/<new-branch>
# 创建 PR
gh pr create --title "<title>" --body "$(cat <<'EOF'
@@ -179,21 +184,15 @@ git branch -D <feature-branch>
### 步骤 6清理工作区
**只对选项 1 和 4 执行。** 选项 2 和 3 始终保留 worktree。
**只对选项 1 和 4 执行。** 选项 2 和 3 始终保留 worktree。两个调用方都已经切到主仓库根目录了 —— 移除 worktree 必须从 worktree 外面执行 —— 因此这里使用**步骤 2 里捕获的** `GIT_DIR` / `GIT_COMMON` / `WORKTREE_PATH`,也就是那次目录切换之前的值。
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
WORKTREE_PATH=$(git rev-parse --show-toplevel)
```
> ⚠️ **不要在这里重新计算这些值。** 此刻 `git rev-parse --show-toplevel` 返回的是主仓库根目录,不是 worktree 路径 —— 溯源判断会永远匹配不上,清理会静默空转,随后分支删除还会因为 worktree 仍挂着而失败。
**如果 `GIT_DIR == GIT_COMMON`** 普通仓库,无 worktree 可清理。结束。
**如果 worktree 路径在 `.worktrees/` 或 `worktrees/` 之下:** 这是 Superpowers 创建的 worktree —— 我们负责清理。
**如果 `WORKTREE_PATH` 在 `.worktrees/` 或 `worktrees/` 之下:** 这是 Superpowers 创建的 worktree —— 我们负责清理。
```bash
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
cd "$MAIN_ROOT"
git worktree remove "$WORKTREE_PATH"
git worktree prune # 自愈:清理任何过期的注册记录
```

View File

@@ -1,33 +1,63 @@
# Gemini CLI 工具映射
Skills 使用 Claude Code 的工具名称。在 Gemini CLI 中遇到这些名称时,请使用对应的平台等价工具
Skills 说的是动作("分派一个子智能体"、"建一条待办"、"读一个文件")。在 Gemini CLI 上,这些动作对应下面这些工具
| Skill 中的引用 | Gemini CLI 等价工具 |
|---------------|-------------------|
| `Read`读取文件 | `read_file` |
| `Write`(创建文件 | `write_file` |
| `Edit`(编辑文件 | `replace` |
| `Bash`(执行命令) | `run_shell_command` |
| `Grep`(搜索文件内容) | `grep_search` |
| `Glob`(按名称搜索文件 | `glob` |
| `TodoWrite`(任务跟踪) | `write_todos` |
| `Skill` 工具(调用 skill | `activate_skill` |
| `WebSearch` | `google_web_search` |
| `WebFetch` | `web_fetch` |
| `Task` 工具(派遣子 agent | 无等价工具——Gemini CLI 不支持子 agent |
| Skill 请求的动作 | Gemini CLI 等价工具 |
|----------------|-------------------|
| 读取一个文件 | `read_file` |
| 一次读取多个文件 | `read_many_files` |
| 创建新文件 | `write_file` |
| 编辑文件 | `replace` |
| 执行 shell 命令 | `run_shell_command` |
| 搜索文件内容 | `grep_search` |
| 按名称查找文件 | `glob` |
| 列出文件和子目录 | `list_directory` |
| 抓取 URL | `web_fetch` |
| 搜索网页 | `google_web_search` |
| 调用一个 skill | `activate_skill` |
| 分派子智能体(`Subagent (general-purpose):` 模板) | `invoke_agent``agent_name: "generalist"`(也可用 `@generalist` 聊天语法调用——见[子智能体支持](#子智能体支持) |
| 多个并行分派 | 同一条响应里发多个 `invoke_agent` 调用 |
| 任务跟踪("建一条待办"、"标记完成" | `write_todos`状态pending、in_progress、completed、cancelled、blocked |
## 不支持子 Agent
## 指令文件
Gemini CLI 没有 Claude Code `Task` 工具的等价物。依赖子 agent 派遣的 skills`subagent-driven-development``dispatching-parallel-agents`)将退化为通过 `executing-plans` 进行单会话执行
当某个 skill 提到"你的指令文件"时,在 Gemini CLI 上指的是 **`GEMINI.md`**。Gemini CLI 按层级加载 `GEMINI.md`:全局的在 `~/.gemini/GEMINI.md`,项目级的在工作区目录及其各级父目录里,另外当某个工具访问子目录中的文件时,该子目录下的 `GEMINI.md` 也会被加载
## 个人 skills 目录
用户级 skills 放在 **`~/.gemini/skills/`****`~/.agents/skills/`** 是跨运行时的别名目录(与 Codex、Copilot CLI 共用)。当同一层级下两个目录都存在时,`.agents/skills/` 优先。每个 skill 是一个子目录,里面有一份带 `name``description` frontmatter 的 `SKILL.md`
## 子智能体支持
Gemini CLI 通过 `invoke_agent` 工具分派子智能体,该工具接收 `agent_name``prompt` 两个参数。同一个分派动作也有聊天语法快捷方式:输入 `@generalist <prompt>` 等价于以 `agent_name: "generalist"` 调用 `invoke_agent`。内置的 agent 名包括 `generalist``cli_help``codebase_investigator`,以及(启用浏览器工具后的)`browser_agent`
Skills 用 `Subagent (general-purpose):` 来分派,并且要么引用一个提示词模板文件(例如 `superpowers:subagent-driven-development``./implementer-prompt.md`),要么直接给出内联提示词。在 Gemini CLI 上:
| Skill 里的分派形式 | Gemini CLI 等价做法 |
|------------------|-------------------|
| 引用某个 `*-prompt.md` 模板implementer、task-reviewer、code-reviewer 等) | 把模板填好,然后以 `agent_name: "generalist"` 和填好的提示词调用 `invoke_agent` |
| 引用 `superpowers:requesting-code-review``./code-reviewer.md` | 以 `agent_name: "generalist"` 和填好的审查模板调用 `invoke_agent` |
| 内联提示词(没有引用模板) | 以 `agent_name: "generalist"` 和你的内联提示词调用 `invoke_agent` |
### 填写提示词
Skills 提供的提示词模板里有 `{WHAT_WAS_IMPLEMENTED}``[FULL TEXT of task]` 这类占位符。把所有占位符都填好,再把完整提示词交给 `invoke_agent`。模板本身就包含了该 agent 的角色、审查标准和期望的输出格式——子智能体会照着它执行。
### 并行分派
Gemini CLI 支持并行分派子智能体。在同一条响应里发出多个 `invoke_agent` 调用(或在一个提示词里写多个 `@generalist` 调用),即可让相互独立的子智能体工作并行跑。有依赖关系的任务保持串行,但**不要**为了让历史记录简单一点就把相互独立的子智能体任务串起来。
## Gemini CLI 额外工具
以下工具 Gemini CLI 中可用,但 Claude Code 中没有对应工具
以下工具 Gemini CLI 独有的
| 工具 | 用途 |
|------|------|
| `list_directory` | 列出文件和子目录 |
| `save_memory` | 将信息持久化到 GEMINI.md跨会话保留 |
| `ask_user` | 向用户请求结构化输入 |
| `tracker_create_task` | 丰富的任务管理(创建、更新、列表、可视化) |
| `enter_plan_mode` / `exit_plan_mode` | 切换到只读研究模式,在修改前先调研 |
| `save_memory`(旧版) | 当 `experimental.memoryV2 = false` 时,跨会话持久化事实 |
| `get_internal_docs` | 查阅 Gemini CLI 自带的文档 |
| `ask_user` | 向用户提出结构化问题(文本 / 单选 / 多选) |
| `enter_plan_mode` / `exit_plan_mode` | 进入和退出只读的计划模式 |
| `update_topic` | 更新当前会话的主题 / 战略意图元数据 |
| `complete_task` | 表示某个 Gemini 子智能体已完成,并把结果返回给父 agent |
| `tracker_create_task``tracker_update_task``tracker_get_task``tracker_list_tasks``tracker_add_dependency``tracker_visualize` | 功能完整的任务跟踪器,支持依赖关系与可视化 |
| `read_mcp_resource``list_mcp_resources` | 访问 MCP 资源 |