From 5ddc3c28e0edcaac189d8b06f3017fa8ce6f71b6 Mon Sep 17 00:00:00 2001 From: james-haytko_nwx Date: Mon, 23 Feb 2026 17:03:24 -0600 Subject: [PATCH] inline suggestions --- .../workflows/claude-documentation-fixer.yml | 118 ++++++++++++++++-- .../claude-documentation-reviewer.yml | 2 +- 2 files changed, 111 insertions(+), 9 deletions(-) diff --git a/.github/workflows/claude-documentation-fixer.yml b/.github/workflows/claude-documentation-fixer.yml index 415d43fdef..b1e1352f1d 100644 --- a/.github/workflows/claude-documentation-fixer.yml +++ b/.github/workflows/claude-documentation-fixer.yml @@ -12,33 +12,135 @@ jobs: github.event.issue.pull_request && contains(github.event.comment.body, '@claude') permissions: - contents: write + contents: read pull-requests: write issues: write id-token: write actions: read steps: - - name: Get PR branch + - name: Get PR info id: pr-info env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - PR_DATA=$(gh pr view ${{ github.event.issue.number }} --repo ${{ github.repository }} --json headRefName,headRefOid) - echo "branch=$(echo "$PR_DATA" | jq -r '.headRefName')" >> "$GITHUB_OUTPUT" + PR_DATA=$(gh pr view ${{ github.event.issue.number }} --repo ${{ github.repository }} --json headRefOid) + echo "sha=$(echo "$PR_DATA" | jq -r '.headRefOid')" >> "$GITHUB_OUTPUT" - name: Checkout repository uses: actions/checkout@v4 with: - # Check out the branch by name so git push works. - ref: ${{ steps.pr-info.outputs.branch }} fetch-depth: 0 - - name: Apply fixes + - name: Fetch PR head + run: | + git fetch origin pull/${{ github.event.issue.number }}/head:pr-fix-branch + git checkout pr-fix-branch + + - name: Generate fixes uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} show_full_output: true + prompt: | + ${{ github.event.comment.body }} + + Apply the requested fixes to the documentation files. Edit files directly using the Write and Edit tools. + Do NOT run git commit, git push, or git add. Only edit the files. claude_args: | --model claude-sonnet-4-5-20250929 - --allowedTools "Read,Write,Edit,Bash(gh pr view:*),Bash(gh pr diff:*),Bash(gh pr comment:*),Bash(git config:*),Bash(git add:*),Bash(git commit:*),Bash(git push:*),Bash(git status:*),Bash(git diff:*)" + --allowedTools "Read,Write,Edit,Bash(gh pr view:*),Bash(gh pr diff:*),Bash(git status:*),Bash(git diff:*)" + + - name: Post inline suggestions + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.issue.number }} + HEAD_SHA: ${{ steps.pr-info.outputs.sha }} + REPO: ${{ github.repository }} + run: | + python3 << 'PYEOF' + import subprocess, json, os, re, sys + + def parse_diff(diff_text): + suggestions = [] + current_file = None + lines = diff_text.split('\n') + i = 0 + while i < len(lines): + line = lines[i] + if line.startswith('diff --git'): + i += 1 + continue + elif line.startswith('--- a/'): + current_file = line[6:] + i += 1 + continue + elif line.startswith('+++ b/'): + i += 1 + continue + elif line.startswith('@@ '): + m = re.match(r'@@ -(\d+)(?:,\d+)? \+\d+(?:,\d+)? @@', line) + if not m: + i += 1 + continue + old_line = int(m.group(1)) + i += 1 + while i < len(lines) and not lines[i].startswith('@@') and not lines[i].startswith('diff '): + if lines[i].startswith('-') or lines[i].startswith('+'): + removed, added = [], [] + start = old_line + while i < len(lines) and (lines[i].startswith('-') or lines[i].startswith('+')): + if lines[i].startswith('-'): + removed.append(lines[i][1:]) + old_line += 1 + else: + added.append(lines[i][1:]) + i += 1 + if removed: + suggestions.append({'file': current_file, 'start': start, 'end': old_line - 1, 'new': added}) + elif lines[i].startswith(' '): + old_line += 1 + i += 1 + else: + i += 1 + else: + i += 1 + return suggestions + + diff = subprocess.run(['git', 'diff'], capture_output=True, text=True).stdout + if not diff.strip(): + print("No changes to suggest") + sys.exit(0) + + suggestions = parse_diff(diff) + if not suggestions: + print("No line-replacement suggestions to post") + sys.exit(0) + + comments = [] + for s in suggestions: + body = '```suggestion\n' + '\n'.join(s['new']) + '\n```' + c = {'path': s['file'], 'line': s['end'], 'side': 'RIGHT', 'body': body} + if s['start'] != s['end']: + c['start_line'] = s['start'] + c['start_side'] = 'RIGHT' + comments.append(c) + + review_data = { + 'commit_id': os.environ['HEAD_SHA'], + 'body': f"Here are the suggested fixes ({len(comments)} suggestion(s)). Click 'Apply suggestion' on each one to accept it.", + 'event': 'COMMENT', + 'comments': comments + } + + r = subprocess.run( + ['gh', 'api', f"repos/{os.environ['REPO']}/pulls/{os.environ['PR_NUMBER']}/reviews", + '--method', 'POST', '--input', '-'], + input=json.dumps(review_data).encode(), + capture_output=True + ) + if r.returncode != 0: + print(f"Error posting suggestions: {r.stderr.decode()}") + sys.exit(1) + print(f"Posted {len(comments)} inline suggestion(s)") + PYEOF diff --git a/.github/workflows/claude-documentation-reviewer.yml b/.github/workflows/claude-documentation-reviewer.yml index a889d43a3f..31fdc18a7a 100644 --- a/.github/workflows/claude-documentation-reviewer.yml +++ b/.github/workflows/claude-documentation-reviewer.yml @@ -102,5 +102,5 @@ jobs: run: | echo "" >> /tmp/review.md echo "To apply fixes, reply with \`@claude\` followed by your instructions (e.g. \`@claude fix all issues\` or \`@claude fix only the spelling errors\`)." >> /tmp/review.md - echo "Note: automated fixes are only available for branches in this repository, not forks." >> /tmp/review.md + echo "Note: fixes are posted as inline suggestions. Click 'Apply suggestion' on each one to accept it." >> /tmp/review.md gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/review.md