关于 fetch-pr-feedback
fetch-pr-feedback 是用于收集拉取请求评论并交由 receive-feedback 技能处理的技能。它按审阅者整理反馈,默认排除 PR 作者、当前用户及已解决讨论串中的行内评论。
主要功能
- 01获取问题评论与审查评论
- 02指定 PR 编号或当前分支的 PR
- 03默认排除作者和当前用户
- 04可选择纳入已解决讨论串评论
- 05按审阅者整理反馈
适用场景
- 01收集当前分支 PR 的反馈
- 02查看指定 PR 的评论
- 03选择纳入作者或已解决讨论串评论
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ existential-birds/ beagle --skill fetch-pr-feedback安装后在 Claude Code 中输入/fetch-pr-feedback确认已加载。
详细介绍
获取 PR 反馈
获取当前 PR 中所有评审者的评论,对其进行格式化,并使用 receive-feedback 技能进行评估。默认排除 PR 作者和当前用户。默认也排除属于已解决评审讨论串的行级评论。
用法
调用 fetch-pr-feedback 技能,可选传入以下标志:
fetch-pr-feedback [--pr <number>] [--include-author] [--include-resolved]
标志:
--pr <number>- 目标 PR 编号(默认:当前分支的 PR)--include-author- 包含 PR 作者自己的评论(默认:排除)--include-resolved- 包含来自已解决评审讨论串的行级评论(默认:排除)
说明
门槛(按顺序执行;不得跳过)
仅当满足每项 通过条件 后,才能继续。
- PR 上下文 — 通过条件:
$PR_NUMBER已设置为正整数,且针对该 PR 的gh pr view/gh api执行完成,退出代码为 0;或者在 获取 PR 上下文 步骤中停止,并且只显示该步骤中给出的失败信息(“当前分支未找到 PR…”)。 - 获取评论 — 通过条件: 已解决讨论串的 GraphQL 调用(若指定
--include-resolved则跳过)以及两次分页的gh api … | jq -s -f …执行(Issue 评论 + 评审评论)均以退出代码 0 结束,且解析为 JSON(空数组[]也有效)。若退出代码非零或 jq 出错,则停止;显示命令的 stderr,不得编造评论。 - 格式化结果 — 通过条件: 输出必须是以下两种情况之一:(a) 符合 格式化反馈文档 的 Markdown(标题为
# PR #$PR_NUMBER Review Feedback,每位评审者均有## Reviewer: …,并包含 Summary / Line-Specific 小节);或 (b) 完全等于:No review comments found on this PR (excluding PR author, current user, and resolved threads). - 加载 receive-feedback — 通过条件: 已加载 receive-feedback 技能;只有此时才能针对该格式化文档运行该技能的 verify → evaluate → execute 循环。
1. 解析参数
从 $ARGUMENTS 提取标志:
--pr <number>或从当前分支检测--include-author标志(布尔值,默认 false)--include-resolved标志(布尔值,默认 false)
2. 获取 PR 上下文
# If --pr was specified, use that number directly
# Otherwise, get PR for current branch:
gh pr view --json number,headRefName,url,author --jq '{number, headRefName, url, author: .author.login}'
# Get repo owner/name
gh repo view --json owner,name --jq '{owner: .owner.login, name: .name}'
# Get current authenticated user
gh api user --jq '.login'
保存为 $PR_NUMBER、$PR_AUTHOR、$OWNER、$REPO、$CURRENT_USER。
注意: $OWNER、$REPO 等是占位符。请使用前面步骤中的实际值替换。
如果当前分支不存在 PR,则失败并显示:“当前分支未找到 PR。使用 --pr 指定 PR 编号。”
3. 获取评论
获取这两类评论,并排除 $PR_AUTHOR 和 $CURRENT_USER(除非设置了 --include-author)。使用 --paginate 和 jq -s 将分页后的 JSON 数组合并为一个数组。
除非设置了 --include-resolved,否则先获取属于已解决评审讨论串的每条评审评论的 databaseId。Issue 评论(摘要/演练说明)不属于评审讨论串,因此这只会影响行级评审评论。
已解决讨论串的评论 ID(如果设置了 --include-resolved,则完全跳过此块;改为设置 RESOLVED_IDS='[]'):
RESOLVED_IDS=$(gh api graphql \
-F owner="$OWNER" -F repo="$REPO" -F pr="$PR_NUMBER" \
-f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
isResolved
comments(first: 100) { nodes { databaseId } }
}
}
}
}
}
' \
--jq '[.data.repository.pullRequest.reviewThreads.nodes[]
| select(.isResolved)
| .comments.nodes[].databaseId
| select(. != null)]')
first: 100 的限制足以覆盖一般 PR。对于非常大的 PR(超过 100 个讨论串,或单个讨论串中超过 100 条评论),需要添加讨论串/评论级分页;目前这是已知限制。
使用单引号分隔符的 heredoc 将 jq 过滤器写入临时文件(避免 !=、正则表达式模式和尖括号导致的 shell 转义问题):
Issue 评论(摘要/演练说明):
cat > /tmp/issue_comments.jq << 'JQEOF'
def clean_body:
gsub(".*?"; ""; "s")
| gsub(""; ""; "s")
| gsub("<details>\\s*<summary>\\s*🧩 Analysis chain[\\s\\S]*?</details>"; ""; "s")
| gsub("<details>\\s*<summary>\\s*🤖 Prompt for AI Agents[\\s\\S]*?</details>"; ""; "s")
| gsub("<details>\\s*<summary>\\s*📝 Committable suggestion[\\s\\S]*?</details>"; ""; "s")
| gsub("<details>\\s*<summary>Past reviewee.*?</details>"; ""; "s")
| gsub("<details>\\s*<summary>Recent review details[\\s\\S]*?</details>"; ""; "s")
| gsub("<details>\\s*<summary>\\s*Tips\\b.*?</details>"; ""; "s")
| gsub("\\n?\\n---\\n+(?=\\s*(?:✨\\s*Finishing Touches|🪧\\s*Tips?|🤖\\s*Generated|<sub>|_Generated by|Generated by Claude|Generated with))[\\s\\S]*$"; ""; "s")
| gsub("^\\s+|\\s+$"; "")
| if length > 4000 then .[:4000] + "\n\n[comment truncated]" else . end
;
[(add // []) | .[] | select(
.user.login != $pr_author and
.user.login != $current_user
)] |
map({id, user: .user.login, body: (.body | clean_body), created_at})
JQEOF
gh api --paginate "repos/$OWNER/$REPO/issues/$PR_NUMBER/comments" | \
jq -s --arg pr_author "$PR_AUTHOR" --arg current_user "$CURRENT_USER" \
-f /tmp/issue_comments.jq
评审评论(行级):
cat > /tmp/review_comments.jq << 'JQEOF'
def clean_body:
gsub(".*?"; ""; "s")
| gsub(""; ""; "s")
| gsub("<details>\\s*<summary>\\s*🧩 Analysis chain[\\s\\S]*?</details>"; ""; "s")
| gsub("<details>\\s*<summary>\\s*🤖 Prompt for AI Agents[\\s\\S]*?</details>"; ""; "s")
| gsub("<details>\\s*<summary>\\s*📝 Committable suggestion[\\s\\S]*?</details>"; ""; "s")
| gsub("<details>\\s*<summary>Past reviewee.*?</details>"; ""; "s")
| gsub("<details>\\s*<summary>Recent review details[\\s\\S]*?</details>"; ""; "s")
| gsub("<details>\\s*<summary>\\s*Tips\\b.*?</details>"; ""; "s")
| gsub("\\n?\\n---\\n+(?=\\s*(?:✨\\s*Finishing Touches|🪧\\s*Tips?|🤖\\s*Generated|<sub>|_Generated by|Generated by Claude|Generated with))[\\s\\S]*$"; ""; "s")
| gsub("^\\s+|\\s+$"; "")
| if length > 4000 then .[:4000] + "\n\n[comment truncated]" else . end
;
[(add // []) | .[] | select(
.user.login != $pr_author and
.user.login != $current_user and
(.id as $comment_id | ($resolved_ids | index($comment_id) | not))
)] |
map({
id,
user: .user.login,
path,
line_display: (
.line as $end | .start_line as $start |
if $start and $start != $end then "\($start)-\($end)"
else "\($end // .original_line)" end
),
body: (.body | clean_body),
created_at
})
JQEOF
gh api --paginate "repos/$OWNER/$REPO/pulls/$PR_NUMBER/comments" | \
jq -s --arg pr_author "$PR_AUTHOR" --arg current_user "$CURRENT_USER" \
--argjson resolved_ids "$RESOLVED_IDS" \
-f /tmp/review_comments.jq
如果设置了 --include-author,则从这两个 jq 过滤器文件中省略 --arg pr_author 参数和 .user.login != $pr_author 条件。无论哪种情况,都保留 $current_user 排除条件。当 RESOLVED_IDS='[]'(即 --include-resolved 路径)时,$resolved_ids 过滤器自然不起作用,因此保留即可。
4. 格式化反馈文档
剔除噪声——由第 3 步中的 clean_body jq 函数处理。顺序很重要:首先移除 ... 代码块,然后移除剩余的 HTML 注释,接着移除已知的噪声 <details> 块(Analysis chain、Prompt for AI Agents、Committable suggestion、Past reviewee、Recent review details、Tips),最后移除 --- 页脚模板。必须在 --- 页脚模式之前剔除 <details> 块,因为机器人分析链中包含 --- 分隔符,否则会截断实际的问题。实质性的 <details> 块(例如“Suggested fix”“Proposed fix”)会保留。剔除后超过 4000 个字符的评论会被截断,并添加 [comment truncated] 标记。
按审阅者分组——按审阅者的用户名整理格式化输出:
# PR #$PR_NUMBER Review Feedback
## Reviewer: coderabbitai[bot]
### Summary Comments
[Issue comments from this reviewer, each separated by ---]
### Line-Specific Comments
[Review comments from this reviewer, each formatted as:]
**File: `path/to/file.ts:42`**
[cleaned comment body]
---
## Reviewer: another-reviewer
### Summary Comments
...
### Line-Specific Comments
...
如果没有找到任何审阅者的评论,则输出:“No review comments found on this PR (excluding PR author, current user, and resolved threads).”
5. 使用 receive-feedback 评估
加载 receive-feedback 技能。
然后处理格式化后的反馈文档:
- 从格式化后的文档中解析每个可执行项
- 对每个项依次执行 verify → evaluate → execute
- 生成结构化的响应摘要
示例
# Fetch unresolved reviewer comments on current branch's PR (default)
fetch-pr-feedback
# Fetch from a specific PR
fetch-pr-feedback --pr 123
# Include PR author's own comments
fetch-pr-feedback --include-author
# Include line-specific comments from resolved review threads
fetch-pr-feedback --include-resolved
# Combined
fetch-pr-feedback --pr 456 --include-author --include-resolved