feat: improve resolve-pr-feedback skill (#379)
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: get-pr-comments PR_NUMBER [OWNER/REPO]"
|
||||
echo "Example: get-pr-comments 123"
|
||||
echo "Example: get-pr-comments 123 EveryInc/cora"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PR_NUMBER=$1
|
||||
|
||||
if [ -n "$2" ]; then
|
||||
OWNER=$(echo "$2" | cut -d/ -f1)
|
||||
REPO=$(echo "$2" | cut -d/ -f2)
|
||||
else
|
||||
OWNER=$(gh repo view --json owner -q .owner.login 2>/dev/null)
|
||||
REPO=$(gh repo view --json name -q .name 2>/dev/null)
|
||||
fi
|
||||
|
||||
if [ -z "$OWNER" ] || [ -z "$REPO" ]; then
|
||||
echo "Error: Could not detect repository. Pass OWNER/REPO as second argument."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Fetch review threads, regular PR comments, and review bodies in one query.
|
||||
# Output is a JSON object with three keys:
|
||||
# review_threads - unresolved, non-outdated inline code review threads
|
||||
# pr_comments - top-level PR conversation comments (excludes PR author)
|
||||
# review_bodies - review submissions with non-empty body text (excludes PR author)
|
||||
gh api graphql -f owner="$OWNER" -f repo="$REPO" -F pr="$PR_NUMBER" -f query='
|
||||
query FetchPRFeedback($owner: String!, $repo: String!, $pr: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
pullRequest(number: $pr) {
|
||||
author { login }
|
||||
reviewThreads(first: 100) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
isResolved
|
||||
isOutdated
|
||||
path
|
||||
line
|
||||
comments(first: 50) {
|
||||
nodes {
|
||||
id
|
||||
author { login }
|
||||
body
|
||||
createdAt
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
comments(first: 100) {
|
||||
nodes {
|
||||
id
|
||||
author { login }
|
||||
body
|
||||
createdAt
|
||||
url
|
||||
}
|
||||
}
|
||||
reviews(first: 50) {
|
||||
nodes {
|
||||
id
|
||||
author { login }
|
||||
body
|
||||
state
|
||||
createdAt
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' | jq '.data.repository.pullRequest as $pr | {
|
||||
review_threads: [$pr.reviewThreads.edges[]
|
||||
| select(.node.isResolved == false and .node.isOutdated == false)],
|
||||
pr_comments: [$pr.comments.nodes[]
|
||||
| select(.author.login != $pr.author.login)
|
||||
| select(.body | test("^\\s*$") | not)],
|
||||
review_bodies: [$pr.reviews.nodes[]
|
||||
| select(.body != null and .body != "")
|
||||
| select(.author.login != $pr.author.login)]
|
||||
}'
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Maps a PR review comment node ID to its parent thread.
|
||||
# Fetches thread IDs and first comment IDs to find the match,
|
||||
# then returns the matching thread with full comment details.
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "Usage: get-thread-for-comment PR_NUMBER COMMENT_NODE_ID [OWNER/REPO]"
|
||||
echo "Example: get-thread-for-comment 378 PRRC_kwDOP_gZVc6ySv89"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PR_NUMBER=$1
|
||||
COMMENT_NODE_ID=$2
|
||||
|
||||
if [ -n "$3" ]; then
|
||||
OWNER=$(echo "$3" | cut -d/ -f1)
|
||||
REPO=$(echo "$3" | cut -d/ -f2)
|
||||
else
|
||||
OWNER=$(gh repo view --json owner -q .owner.login 2>/dev/null)
|
||||
REPO=$(gh repo view --json name -q .name 2>/dev/null)
|
||||
fi
|
||||
|
||||
if [ -z "$OWNER" ] || [ -z "$REPO" ]; then
|
||||
echo "Error: Could not detect repository. Pass OWNER/REPO as third argument."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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 {
|
||||
id
|
||||
isResolved
|
||||
path
|
||||
line
|
||||
comments(first: 100) {
|
||||
nodes {
|
||||
id
|
||||
author { login }
|
||||
body
|
||||
createdAt
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' | jq -e --arg cid "$COMMENT_NODE_ID" '
|
||||
[.data.repository.pullRequest.reviewThreads.nodes[]
|
||||
| select(.comments.nodes | map(.id) | index($cid))]
|
||||
| if length == 0 then error("No thread found for comment \($cid)") else .[0] end
|
||||
'
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Replies to a PR review thread. Body is read from stdin to avoid
|
||||
# shell escaping issues with markdown (quotes, newlines, etc.).
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: echo 'reply body' | reply-to-pr-thread THREAD_ID"
|
||||
echo "Example: echo 'Addressed: added null check' | reply-to-pr-thread PRRT_kwDOABC123"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
THREAD_ID=$1
|
||||
BODY=$(cat)
|
||||
|
||||
if [ -z "$BODY" ]; then
|
||||
echo "Error: No body provided on stdin."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gh api graphql -f threadId="$THREAD_ID" -f body="$BODY" -f query='
|
||||
mutation ReplyToReviewThread($threadId: ID!, $body: String!) {
|
||||
addPullRequestReviewThreadReply(input: {
|
||||
pullRequestReviewThreadId: $threadId
|
||||
body: $body
|
||||
}) {
|
||||
comment {
|
||||
id
|
||||
url
|
||||
}
|
||||
}
|
||||
}'
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: resolve-pr-thread THREAD_ID"
|
||||
echo "Example: resolve-pr-thread PRRT_kwDOABC123"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
THREAD_ID=$1
|
||||
|
||||
gh api graphql -f threadId="$THREAD_ID" -f query='
|
||||
mutation ResolveReviewThread($threadId: ID!) {
|
||||
resolveReviewThread(input: {threadId: $threadId}) {
|
||||
thread {
|
||||
id
|
||||
isResolved
|
||||
path
|
||||
line
|
||||
}
|
||||
}
|
||||
}'
|
||||
Reference in New Issue
Block a user