Skill Tracer
Skills › Compound Engineering › ce-resolve-pr-feedback › get-thread-for-comment

get-thread-for-comment

84 lines · skills/ce-resolve-pr-feedback/scripts/get-thread-for-comment at 3449230

1#!/usr/bin/env bash
2
3# Maps a PR review comment node ID to its parent thread.
4# Fetches all review threads (paginated) and their comments, then returns the
5# thread whose comments contain the target ID.
6
7set -e
8
9if [ $# -lt 2 ]; then
10 echo "Usage: get-thread-for-comment PR_NUMBER COMMENT_NODE_ID [OWNER/REPO]"
11 echo "Example: get-thread-for-comment 378 PRRC_kwDOP_gZVc6ySv89"
12 exit 1
13fi
15PR_NUMBER=$1
16COMMENT_NODE_ID=$2
18if [ -n "$3" ]; then
19 OWNER=$(echo "$3" | cut -d/ -f1)
20 REPO=$(echo "$3" | cut -d/ -f2)
21else
22 # `|| true` is load-bearing: under `set -e`, a failed command substitution
23 # in an assignment aborts the script immediately. Run outside a git repo,
24 # `gh repo view` exits 1, its stderr is swallowed by `2>/dev/null`, and the
25 # script would die here with rc=1 and no output -- making the friendly error
26 # below unreachable. Keep `|| true` so detection failure falls through to it.
27 OWNER=$(gh repo view --json owner -q .owner.login 2>/dev/null || true)
28 REPO=$(gh repo view --json name -q .name 2>/dev/null || true)
29fi
31if [ -z "$OWNER" ] || [ -z "$REPO" ]; then
32 echo "Error: could not resolve owner/repo. Run get-thread-for-comment from inside the target git repository, or pass OWNER/REPO as the third argument (e.g., get-thread-for-comment $PR_NUMBER $COMMENT_NODE_ID EveryInc/cora)." >&2
33 exit 1
34fi
36# Pagination (issue #798): paginate the reviewThreads connection so PRs with
37# more than one page of threads can still resolve a comment to its parent
38# thread. Per-thread comments are still capped at 100 -- threads exceeding
39# that depth are not paginated here.
40tmp_threads=$(mktemp "${TMPDIR:-/tmp}/ce-pr-threads.XXXXXX")
41trap 'rm -f "$tmp_threads"' EXIT
43gh api graphql --paginate --slurp \
44 -f owner="$OWNER" -f repo="$REPO" -F pr="$PR_NUMBER" \
45 -f query='
46query Threads($owner: String!, $repo: String!, $pr: Int!, $endCursor: String) {
47 repository(owner: $owner, name: $repo) {
48 pullRequest(number: $pr) {
49 reviewThreads(first: 100, after: $endCursor) {
50 nodes {
51 id
52 isResolved
53 isOutdated
54 path
55 line
56 originalLine
57 startLine
58 originalStartLine
59 comments(first: 100) {
60 nodes {
61 id
62 author { login }
63 body
64 createdAt
65 url
66 }
67 }
68 }
69 pageInfo { hasNextPage endCursor }
70 }
71 }
72 }
73}' > "$tmp_threads"
75jq -e -n --slurpfile pages "$tmp_threads" --arg cid "$COMMENT_NODE_ID" '
76 [$pages[0][].data.repository.pullRequest.reviewThreads.nodes[]
77 | select(any(.comments.nodes[]; .id == $cid))]
78 | if length == 0 then error("No thread found for comment \($cid)") else .[0] end
79 | . + {
80 root_comment_id: (.comments.nodes[0].url
81 | capture("discussion_r(?<id>[0-9]+)").id
82 | tonumber)
83 }