--- description: Add line-specific review comments to pull requests using GitHub CLI API argument-hint: PR number or URL (optional - can work with current branch) --- # How to Attach Line-Specific Review Comments to Pull Requests This guide explains how to add line-specific review comments to pull requests using the GitHub CLI (`gh`) API, similar to how the GitHub UI allows commenting on specific lines of code. ## Overview While `gh pr review` provides basic review functionality (approve, request changes, general comments), it **does not support line-specific comments directly**. To add comments on specific lines of code, you must use the lower-level `gh api` command to call GitHub's REST API directly. ## Prerequisites 1. GitHub CLI installed and authenticated: ```bash gh auth status ``` 2. Access to the repository and pull request you want to review ## Understanding GitHub's Review Comment System GitHub has two types of PR comments: 1. **Issue Comments** - General comments on the PR conversation 2. **Review Comments** - Line-specific comments on code changes Review comments can be added in two ways: - **Single comment** - Using the `/pulls/{pr}/comments` endpoint - **Review with multiple comments** - Using the `/pulls/{pr}/reviews` endpoint ## Adding a Single Line-Specific Comment ### Basic Syntax ```bash gh api repos/{owner}/{repo}/pulls/{pr_number}/comments \ -f body='Your comment text here' \ -f commit_id='' \ -f path='path/to/file.js' \ -F line=42 \ -f side='RIGHT' ``` ### Parameters Explained | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `body` | string | Yes | The text of the review comment (supports Markdown) | | `commit_id` | string | Yes | The SHA of the commit to comment on | | `path` | string | Yes | Relative path to the file being commented on | | `line` | integer | Yes | The line number in the diff (use `-F` for integers) | | `side` | string | Yes | `RIGHT` for new/modified lines, `LEFT` for deleted lines | | `start_line` | integer | No | For multi-line comments, the starting line | | `start_side` | string | No | For multi-line comments, the starting side | ### Parameter Flags - `-f` (--field) - For string values - `-F` (--field) - For integer values (note the capital F) ### Complete Example ```bash # First, get the latest commit SHA for the PR gh api repos/NeoLabHQ/learning-platform-app/pulls/4 --jq '.head.sha' # Then add your comment gh api repos/NeoLabHQ/learning-platform-app/pulls/4/comments \ -f body='Consider adding error handling here. Should we confirm the lesson was successfully marked as completed before navigating away?' \ -f commit_id='e152d0dd6cf498467eadbeb638bf05abe11c64d4' \ -f path='src/components/LessonNavigationButtons.tsx' \ -F line=26 \ -f side='RIGHT' ``` ### Understanding Line Numbers The `line` parameter refers to the **position in the diff**, not the absolute line number in the file: - For **new files**: Line numbers match the file's line numbers - For **modified files**: Use the line number as it appears in the "Files changed" tab - For **multi-line comments**: Use `start_line` and `line` to specify the range ### Response On success, returns a JSON object with comment details: ```json { "id": 2532291222, "pull_request_review_id": 3470545909, "path": "src/components/LessonNavigationButtons.tsx", "line": 26, "body": "Consider adding error handling here...", "html_url": "https://github.com/NeoLabHQ/learning-platform-app/pull/4#discussion_r2532291222", "created_at": "2025-11-16T22:40:46Z" } ``` ## Adding Multiple Line-Specific Comments Together To add multiple comments across different files in a single review, use the `/reviews` endpoint with JSON input. ### Why Use Reviews for Multiple Comments? - **Atomic operation** - All comments are added together - **Single notification** - Doesn't spam with multiple notifications - **Better UX** - Appears as one cohesive review - **Same mechanism as GitHub UI** - "Start a review" → "Finish review" ### Basic Syntax ```bash cat <<'EOF' | gh api repos/{owner}/{repo}/pulls/{pr_number}/reviews --input - { "event": "COMMENT", "body": "Overall review summary (optional)", "comments": [ { "path": "file1.tsx", "body": "Comment on file 1", "side": "RIGHT", "line": 15 }, { "path": "file2.tsx", "body": "Comment on file 2", "side": "RIGHT", "line": 30 } ] } EOF ``` ### Review Event Types | Event | Description | When to Use | |-------|-------------|-------------| | `COMMENT` | General review comment | Just leaving feedback without approval | | `APPROVE` | Approve the PR | Changes look good, ready to merge | | `REQUEST_CHANGES` | Request changes | Issues that must be fixed before merge | ### Complete Example ```bash cat <<'EOF' | gh api repos/NeoLabHQ/learning-platform-app/pulls/4/reviews --input - { "event": "COMMENT", "body": "Testing multiple line-specific comments via gh api", "comments": [ { "path": "src/components/CourseCard.tsx", "body": "Test comment generated by Claude", "side": "RIGHT", "line": 15 }, { "path": "src/components/CourseProgressWidget.tsx", "body": "Test comment generated by Claude", "side": "RIGHT", "line": 30 }, { "path": "src/components/LessonProgressTracker.tsx", "body": "Test comment generated by Claude", "side": "RIGHT", "line": 20 } ] } EOF ``` ### Response ```json { "id": 3470546747, "state": "COMMENTED", "html_url": "https://github.com/NeoLabHQ/learning-platform-app/pull/4#pullrequestreview-3470546747", "submitted_at": "2025-11-16T22:42:43Z", "commit_id": "e152d0dd6cf498467eadbeb638bf05abe11c64d4" } ``` ## Common Issues and Solutions ### Issue 1: "user_id can only have one pending review per pull request" **Error Message:** ``` gh: Validation Failed (HTTP 422) {"message":"Validation Failed","errors":[{"resource":"PullRequestReview","code":"custom","field":"user_id","message":"user_id can only have one pending review per pull request"}]} ``` **Cause:** GitHub only allows one pending (unsubmitted) review per user per PR. If you previously started a review through the UI or API and didn't submit it, it blocks new review creation. **Solution 1: Submit the pending review** ```bash # Check for pending reviews gh api repos/{owner}/{repo}/pulls/{pr_number}/reviews | jq '.[] | select(.state=="PENDING")' # Submit it through the UI or ask the user to submit it ``` **Solution 2: Use the single comment endpoint instead** ```bash # Add individual comments without creating a review gh api repos/{owner}/{repo}/pulls/{pr_number}/comments \ -f body='Comment text' \ -f commit_id='' \ -f path='file.tsx' \ -F line=26 \ -f side='RIGHT' ``` ### Issue 2: Array syntax not working with --raw-field **Failed Attempt:** ```bash # This does NOT work - GitHub API receives an object, not an array gh api repos/{owner}/{repo}/pulls/{pr}/reviews \ --raw-field 'comments[0][path]=file1.tsx' \ --raw-field 'comments[0][line]=15' \ --raw-field 'comments[1][path]=file2.tsx' \ --raw-field 'comments[1][line]=30' ``` **Error:** ``` Invalid request. For 'properties/comments', {"0" => {...}, "1" => {...}} is not an array. ``` **Solution:** Use JSON input via heredoc: ```bash cat <<'EOF' | gh api repos/{owner}/{repo}/pulls/{pr}/reviews --input - { "comments": [...] } EOF ``` ### Issue 3: Invalid line number **Error Message:** ``` Pull request review thread line must be part of the diff ``` **Cause:** The line number doesn't exist in the diff for this file. **Solutions:** - Verify the file was actually changed in this PR - Check the "Files changed" tab to see actual line numbers in the diff - Ensure you're using the correct `commit_id` (the latest commit in the PR) ### Issue 4: Wrong commit_id **Error Message:** ``` commit_sha is not part of the pull request ``` **Solution:** Get the latest commit SHA: ```bash gh api repos/{owner}/{repo}/pulls/{pr_number} --jq '.head.sha' ``` ## Best Practices ### 1. Get PR Information First Before adding comments, gather necessary information: ```bash # Get PR details gh pr view {pr_number} --json headRefOid,files # Or use the API gh api repos/{owner}/{repo}/pulls/{pr_number} --jq '{commit: .head.sha, files: [.changed_files]}' # List files changed gh api repos/{owner}/{repo}/pulls/{pr_number}/files --jq '.[] | {filename: .filename, additions: .additions, deletions: .deletions}' ``` ### 2. Check for Pending Reviews ```bash # Check if you have a pending review gh api repos/{owner}/{repo}/pulls/{pr_number}/reviews \ --jq '.[] | select(.state=="PENDING" and .user.login=="YOUR_USERNAME")' ``` ### 3. Use Meaningful Comment Text - Be specific and constructive - Reference documentation or best practices - Suggest alternatives when requesting changes - Use code blocks for code suggestions: ```markdown Consider using async/await: \`\`\`typescript async function getData() { const result = await fetch(url); return result.json(); } \`\`\` ``` ### 4. Batch Related Comments Use the review endpoint to group related comments: - All comments for a single file/area - All comments for a specific concern (security, performance, etc.) - Complete review session ### 5. Choose the Right Event Type ```bash # For feedback during development "event": "COMMENT" # When approving "event": "APPROVE" # When blocking merge "event": "REQUEST_CHANGES" ``` ## Workflow Examples ### Example 1: Quick Single Comment ```bash #!/bin/bash OWNER="NeoLabHQ" REPO="learning-platform-app" PR=4 # Get latest commit COMMIT=$(gh api repos/$OWNER/$REPO/pulls/$PR --jq '.head.sha') # Add comment gh api repos/$OWNER/$REPO/pulls/$PR/comments \ -f body='Consider extracting this into a separate function for better testability' \ -f commit_id="$COMMIT" \ -f path='src/utils/validation.ts' \ -F line=45 \ -f side='RIGHT' ``` ### Example 2: Comprehensive Review ```bash #!/bin/bash OWNER="NeoLabHQ" REPO="learning-platform-app" PR=4 # Create review with multiple comments cat <