.agents/skills/kbn-github/references/review.md
Detailed reference for creating, verifying, and submitting PR reviews via gh api. See the main skill for the core rules on pending vs published reviews.
--input <file> pointing at a JSON file. The strip-review-event hook denies every -f event=... / -F event=... / --field event=... flag shape on the creation endpoint because the only safe sanitisation is parsing the JSON file.--input path must not contain $VAR, $(...), backticks, or a leading ~; the hook reads the literal path and cannot expand shell metacharacters. Pass an absolute or already-expanded path.--input - (stdin) is denied — the hook can't inspect or rewrite stdin.comments array in the same request.PENDING review per user per PR. Pending comments are not editable via API — do not try to PATCH them or attach new comments via the PR comments endpoint (it won't accept pull_request_review_id). If you need to change bodies or anchors, delete the pending review and recreate it.subject_type=file) are immediately visible; they are not part of a pending review.Prefer line/side anchoring over position (less error-prone):
line (the file line number on the right side) + side: "RIGHT".side: "LEFT" + the old-file line number.start_line + start_side.line/side approach uses absolute file line numbers (visible in the GitHub diff UI), so there is no off-by-one math to get wrong.Avoid position unless line/side cannot express the anchor. GitHub's REST docs describe position as closing down, and say it is "the number of lines down from the first @@ hunk header" where the line just below that header is position 1.
patch from GET /repos/{o}/{r}/pulls/{n}/files.@@ hunk header using GitHub's definition; do not treat source-file line numbers as positions.path, line, side, and position match the intended anchor.After creating a review:
gh api repos/elastic/kibana/pulls/NUM/reviews --jq '.[] | {id,state}'gh api repos/elastic/kibana/pulls/NUM/reviews/REVIEW_ID/comments --jq 'length'gh api repos/elastic/kibana/pulls/NUM/comments --jq 'length'After submitting a review:
Inline review comment (line or range; supports GitHub suggestion blocks):
gh api repos/elastic/kibana/pulls/NUM/comments -f body=$'Text.\n\n```suggestion\ncode\n```' -f commit_id=SHA -f path=FILE -f side=RIGHT -f line=LINE
For multi-line, add: -f start_line=START -f start_side=RIGHT.
File-level review comment (file-scoped, immediately visible):
gh api repos/elastic/kibana/pulls/NUM/comments \
-f body=$'Text.' \
-f commit_id=SHA -f path=FILE -f subject_type=file
Reply in an existing review thread:
gh api repos/elastic/kibana/pulls/NUM/comments \
-f body=$'Text.' \
-F in_reply_to=COMMENT_ID
in_reply_to (integer). The response field is in_reply_to_id.in_reply_to_id in the request; it may create a new top-level comment instead of a reply.commit_id, and GitHub rejects it as "commit_id is not part of the pull request", use the commit_id from the target review comment you're replying to.PR-level timeline comment (use sparingly):
gh pr comment NUM -b "<text>"
Run payload creation and review creation as separate shell commands. The
strip-review-event hook reads the --input file to strip any stray event
key before gh runs, so the payload file must already exist on disk when the
review-creation command executes.
cat > /tmp/review-payload.json <<'JSON'
{
"commit_id": "HEAD_SHA",
"body": "",
"comments": [
{ "path": "path/to/file.ts", "line": 42, "side": "RIGHT", "body": "Comment text." },
{ "path": "path/to/file.ts", "line": 78, "side": "RIGHT", "body": "Another comment." }
]
}
JSON
gh api repos/elastic/kibana/pulls/NUM/reviews -X POST --input /tmp/review-payload.json
# Submit later (include body explicitly if you want a summary):
gh api repos/elastic/kibana/pulls/NUM/reviews/REVIEW_ID/events -X POST -f event=APPROVE -f body=$'Looks good.'