claude-dev-env 1.37.1 → 1.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CLAUDE.md +3 -0
  2. package/_shared/pr-loop/audit-contract.md +4 -3
  3. package/_shared/pr-loop/fix-protocol.md +2 -0
  4. package/_shared/pr-loop/gh-payloads.md +38 -37
  5. package/_shared/pr-loop/scripts/README.md +0 -1
  6. package/_shared/pr-loop/scripts/preflight.py +2 -1
  7. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +2 -2
  8. package/_shared/pr-loop/scripts/tests/test_preflight.py +22 -0
  9. package/_shared/pr-loop/state-schema.md +10 -10
  10. package/agents/clean-coder.md +4 -0
  11. package/agents/code-quality-agent.md +23 -85
  12. package/agents/groq-coder.md +8 -6
  13. package/hooks/blocking/__init__.py +0 -0
  14. package/hooks/blocking/hedging_language_blocker.py +2 -2
  15. package/hooks/blocking/state_description_blocker.py +243 -0
  16. package/hooks/blocking/tdd_enforcer.py +94 -0
  17. package/hooks/blocking/test_hedging_language_blocker.py +1 -1
  18. package/hooks/blocking/test_state_description_blocker.py +618 -0
  19. package/hooks/blocking/test_tdd_enforcer.py +152 -0
  20. package/hooks/config/state_description_blocker_constants.py +130 -0
  21. package/hooks/hooks.json +10 -0
  22. package/package.json +1 -1
  23. package/rules/no-historical-clutter.md +31 -10
  24. package/scripts/config/groq_bugteam_config.py +13 -5
  25. package/skills/bugteam/CONSTRAINTS.md +20 -27
  26. package/skills/bugteam/EXAMPLES.md +1 -1
  27. package/skills/bugteam/PROMPTS.md +60 -31
  28. package/skills/bugteam/SKILL.md +47 -47
  29. package/skills/bugteam/SKILL_EVALS.md +8 -8
  30. package/skills/bugteam/reference/github-pr-reviews.md +31 -31
  31. package/skills/bugteam/reference/team-setup.md +1 -1
  32. package/skills/bugteam/reference/teardown-publish-permissions.md +4 -4
  33. package/skills/copilot-review/SKILL.md +7 -14
  34. package/skills/findbugs/SKILL.md +2 -2
  35. package/skills/fixbugs/SKILL.md +1 -1
  36. package/skills/monitor-open-prs/SKILL.md +6 -6
  37. package/skills/pr-converge/SKILL.md +7 -6
  38. package/skills/pr-converge/reference/convergence-gates.md +28 -30
  39. package/skills/pr-converge/reference/examples.md +4 -4
  40. package/skills/pr-converge/reference/fix-protocol.md +6 -8
  41. package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -10
  42. package/skills/pr-converge/reference/per-tick.md +18 -33
  43. package/skills/pr-converge/reference/stop-conditions.md +7 -7
  44. package/skills/pr-converge/scripts/README.md +65 -117
  45. package/skills/pr-review-responder/EXAMPLES.md +2 -2
  46. package/skills/pr-review-responder/PRINCIPLES.md +2 -8
  47. package/skills/pr-review-responder/README.md +7 -48
  48. package/skills/pr-review-responder/SKILL.md +2 -3
  49. package/skills/pr-review-responder/TESTING.md +8 -65
  50. package/skills/qbug/SKILL.md +10 -16
  51. package/_shared/pr-loop/scripts/config/gh_util_constants.py +0 -31
  52. package/_shared/pr-loop/scripts/gh_util.py +0 -193
  53. package/_shared/pr-loop/scripts/tests/test_gh_util.py +0 -257
  54. package/_shared/pr-loop/scripts/tests/test_gh_util_constants.py +0 -61
  55. package/skills/pr-converge/scripts/check_pr_mergeability.py +0 -78
  56. package/skills/pr-converge/scripts/config/pr_converge_constants.py +0 -134
  57. package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +0 -152
  58. package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +0 -70
  59. package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +0 -57
  60. package/skills/pr-converge/scripts/fetch_claude_inline_comments.py +0 -70
  61. package/skills/pr-converge/scripts/fetch_claude_reviews.py +0 -61
  62. package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +0 -70
  63. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +0 -61
  64. package/skills/pr-converge/scripts/mark_pr_ready.py +0 -54
  65. package/skills/pr-converge/scripts/post-bugbot-run.helpers.ps1 +0 -49
  66. package/skills/pr-converge/scripts/post-bugbot-run.ps1 +0 -33
  67. package/skills/pr-converge/scripts/reply_to_inline_comment.py +0 -84
  68. package/skills/pr-converge/scripts/request_copilot_review.py +0 -71
  69. package/skills/pr-converge/scripts/resolve_pr_head.py +0 -58
  70. package/skills/pr-converge/scripts/review_field_helpers.py +0 -43
  71. package/skills/pr-converge/scripts/reviewer_fetch_core.py +0 -153
  72. package/skills/pr-converge/scripts/reviewer_specs.py +0 -98
  73. package/skills/pr-converge/scripts/test_check_pr_mergeability.py +0 -126
  74. package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +0 -443
  75. package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +0 -299
  76. package/skills/pr-converge/scripts/test_fetch_claude_inline_comments.py +0 -485
  77. package/skills/pr-converge/scripts/test_fetch_claude_reviews.py +0 -368
  78. package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +0 -440
  79. package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +0 -366
  80. package/skills/pr-converge/scripts/test_mark_pr_ready.py +0 -69
  81. package/skills/pr-converge/scripts/test_post_bugbot_run.py +0 -195
  82. package/skills/pr-converge/scripts/test_reply_to_inline_comment.py +0 -159
  83. package/skills/pr-converge/scripts/test_request_copilot_review.py +0 -101
  84. package/skills/pr-converge/scripts/test_resolve_pr_head.py +0 -79
  85. package/skills/pr-converge/scripts/test_review_field_helpers.py +0 -80
  86. package/skills/pr-converge/scripts/test_reviewer_fetch_core.py +0 -448
  87. package/skills/pr-converge/scripts/test_reviewer_specs.py +0 -107
  88. package/skills/pr-converge/scripts/test_trigger_bugbot.py +0 -139
  89. package/skills/pr-converge/scripts/test_view_pr_context.py +0 -155
  90. package/skills/pr-converge/scripts/trigger_bugbot.py +0 -77
  91. package/skills/pr-converge/scripts/view_pr_context.py +0 -78
  92. package/skills/pr-review-responder/scripts/respond_to_reviews.py +0 -376
@@ -1,172 +1,120 @@
1
- # pr-converge scripts
1
+ # pr-converge MCP tool references
2
2
 
3
- Thin Python wrappers around the gh CLI calls the skill makes per tick. Centralizing them lets the skill body reference one script path per action and keeps the gh-paginate and gh-body-file rules enforced in one place.
3
+ The GitHub MCP server exposes pull request operations through the tools listed below. Pagination is handled server-side; callers receive the complete result set in a single response.
4
4
 
5
- Scripts that target a specific repository are invoked as `python "${CLAUDE_SKILL_DIR}/scripts/<name>.py" --owner OWNER --repo REPO --number NUMBER ...`. `view_pr_context.py` relies on `gh`'s default repository context and takes no `--owner` / `--repo` / `--number` flags. Scripts return non-zero on subprocess failure and surface gh's stderr through `subprocess.CalledProcessError`.
5
+ ## MCP tools
6
6
 
7
- ## Scripts
7
+ ### PR context: `pull_request_read(method="get")`
8
8
 
9
- ### `view_pr_context.py`
9
+ Read PR metadata:
10
10
 
11
- Returns the per-tick PR context as JSON.
11
+ pull_request_read(method="get", pullNumber=NUMBER, owner=OWNER, repo=REPO)
12
12
 
13
- ```bash
14
- python "${CLAUDE_SKILL_DIR}/scripts/view_pr_context.py"
15
- ```
13
+ Fields: `.number`, `.url`, `.head.sha`, `.base.ref`, `.head.ref`, `.isDraft`.
16
14
 
17
- Output: `{"number", "url", "headRefOid", "baseRefName", "headRefName", "isDraft"}`. Wraps `gh pr view --json number,url,headRefOid,baseRefName,headRefName,isDraft`.
15
+ ### Bugbot reviews: `pull_request_read(method="get_reviews")`
18
16
 
19
- ### `fetch_bugbot_reviews.py`
17
+ Fetch all reviews and filter for `cursor[bot]`:
20
18
 
21
- Fetches every Cursor Bugbot review on the PR newest-first, classified per body content.
19
+ pull_request_read(method="get_reviews", pullNumber=NUMBER, owner=OWNER, repo=REPO)
22
20
 
23
- ```bash
24
- python "${CLAUDE_SKILL_DIR}/scripts/fetch_bugbot_reviews.py" \
25
- --owner <OWNER> --repo <REPO> --number <NUMBER>
26
- ```
21
+ Classification: clean if review body has no findings count; dirty if body matches "found N potential issue".
27
22
 
28
- Output: JSON array of `{review_id, commit_id, submitted_at, state, body, classification}` where `classification` is `"dirty"` (body matches `Cursor Bugbot has reviewed your changes and found <N> potential issue`) or `"clean"`. Uses `--paginate --slurp` and flattens pages in Python — required by `../../../rules/gh-paginate.md` because `gh --paginate --jq` runs the filter per-page (gh CLI issue 10459). Login filter is a case-insensitive substring match on `cursor` (handles login-shape divergence between review-level and inline-comment endpoints).
23
+ ### Bugbot inline comments: `pull_request_read(method="get_review_comments")`
29
24
 
30
- ### `fetch_bugbot_inline_comments.py`
25
+ Fetch all review comments and filter for `cursor[bot]`, matching `pull_request_review_id` to the newest Bugbot review on the target commit:
31
26
 
32
- Fetches unaddressed Cursor Bugbot inline comments for the **newest submitted Bugbot review** on the requested ``--commit`` SHA (matches ``pull_request_review_id`` to the review returned by ``fetch_bugbot_reviews.py`` so stale inline threads from an older Bugbot review on the same SHA are ignored).
27
+ pull_request_read(method="get_review_comments", pullNumber=NUMBER, owner=OWNER, repo=REPO)
33
28
 
34
- ```bash
35
- python "${CLAUDE_SKILL_DIR}/scripts/fetch_bugbot_inline_comments.py" \
36
- --owner <OWNER> --repo <REPO> --number <NUMBER> --commit <CURRENT_HEAD>
37
- ```
29
+ ### PR head SHA: `pull_request_read(method="get")`
38
30
 
39
- Output: JSON array of `{comment_id, commit_id, path, line, body}`. Uses the same `--paginate --slurp` pattern as `fetch_bugbot_reviews.py`.
31
+ Read the current HEAD SHA from the PR:
40
32
 
41
- ### `resolve_pr_head.py`
33
+ pull_request_read(method="get", pullNumber=NUMBER, owner=OWNER, repo=REPO)
42
34
 
43
- Returns the current HEAD SHA of the PR. Wraps the single-object endpoint `repos/<owner>/<repo>/pulls/<number>` which is not paginated, so `gh`'s built-in `--jq .head.sha` is safe here (see "Single-object endpoints" in `../../../rules/gh-paginate.md`).
35
+ Access `.head.sha` from the response.
44
36
 
45
- ```bash
46
- python "${CLAUDE_SKILL_DIR}/scripts/resolve_pr_head.py" \
47
- --owner <OWNER> --repo <REPO> --number <NUMBER>
48
- ```
37
+ ### Trigger bugbot: `add_issue_comment`
49
38
 
50
- Output: the SHA on stdout, trailing newline.
39
+ Post the bugbot re-trigger comment:
51
40
 
52
- ### `trigger_bugbot.py`
41
+ add_issue_comment(owner="OWNER", repo="REPO", issueNumber=NUMBER, body="bugbot run")
53
42
 
54
- Posts the literal `bugbot run` re-trigger comment via `gh pr comment --body-file` (per `../../../rules/gh-body-file.md` passing the body inline can corrupt backticks). Writes and removes the temp body file internally.
43
+ `bugbot run` is the only recognized re-trigger phrase; alternative phrasings silently no-op.
55
44
 
56
- ```bash
57
- python "${CLAUDE_SKILL_DIR}/scripts/trigger_bugbot.py" \
58
- --owner <OWNER> --repo <REPO> --number <NUMBER>
59
- ```
45
+ ### Mark PR ready: `update_pull_request(draft=false)`
60
46
 
61
- Output: the comment URL from gh on stdout.
47
+ Mark a draft PR as ready for review:
62
48
 
63
- ### `mark_pr_ready.py`
49
+ update_pull_request(pullNumber=NUMBER, owner=OWNER, repo=REPO, draft=false)
64
50
 
65
- Marks a draft PR as ready for review. Convergence action invoked when both bugbot and bugteam are clean against the same HEAD.
51
+ ### Reply to inline comment: `add_reply_to_pull_request_comment`
66
52
 
67
- ```bash
68
- python "${CLAUDE_SKILL_DIR}/scripts/mark_pr_ready.py" \
69
- --owner <OWNER> --repo <REPO> --number <NUMBER>
70
- ```
53
+ Reply to an inline review comment thread:
71
54
 
72
- ### `reply_to_inline_comment.py`
55
+ add_reply_to_pull_request_comment(
56
+ commentId=COMMENT_ID,
57
+ body=REPLY_BODY,
58
+ owner=OWNER,
59
+ repo=REPO,
60
+ pullNumber=NUMBER
61
+ )
73
62
 
74
- Posts an inline reply to a PR review comment. Reply body is sourced from a caller-supplied file via `gh api ... -F body=@<path>` (per `../../../rules/gh-body-file.md`).
63
+ ### Mergeability: `pull_request_read(method="get")`
75
64
 
76
- ```bash
77
- python "${CLAUDE_SKILL_DIR}/scripts/reply_to_inline_comment.py" \
78
- --owner <OWNER> --repo <REPO> --number <NUMBER> \
79
- --comment-id <COMMENT_ID> --body-file <PATH_TO_REPLY_MD>
80
- ```
65
+ Read mergeability fields from the PR:
81
66
 
82
- Output: the new reply id from gh's JSON response, on stdout.
67
+ pull_request_read(method="get", pullNumber=NUMBER, owner=OWNER, repo=REPO)
83
68
 
84
- ### `check_pr_mergeability.py`
69
+ Fields: `.mergeable`, `.mergeable_state`, `.head.sha`.
85
70
 
86
- Returns the mergeability state of the current PR as JSON. Wraps `gh pr view --json mergeable,mergeStateStatus,headRefOid` (single-object endpoint — no pagination needed). Used by the convergence gate to detect base-branch conflicts (`mergeStateStatus == "DIRTY"` / `mergeable == "CONFLICTING"`) before flipping the PR ready.
71
+ ### Copilot reviews: `pull_request_read(method="get_reviews")`
87
72
 
88
- ```bash
89
- python "${CLAUDE_SKILL_DIR}/scripts/check_pr_mergeability.py" \
90
- --owner <OWNER> --repo <REPO> --number <NUMBER>
91
- ```
73
+ Fetch all reviews and filter for `copilot-pull-request-reviewer[bot]`:
92
74
 
93
- Output: `{"mergeable", "mergeStateStatus", "headRefOid"}`.
75
+ pull_request_read(method="get_reviews", pullNumber=NUMBER, owner=OWNER, repo=REPO)
94
76
 
95
- ### `fetch_copilot_reviews.py`
77
+ Classification: `APPROVED` → clean, `CHANGES_REQUESTED` → dirty, `COMMENTED` with non-empty body → dirty.
96
78
 
97
- Fetches every Copilot reviewer (`copilot-pull-request-reviewer[bot]`) review on the PR newest-first, classified per the review's `state` field.
79
+ ### Copilot inline comments: `pull_request_read(method="get_review_comments")`
98
80
 
99
- ```bash
100
- python "${CLAUDE_SKILL_DIR}/scripts/fetch_copilot_reviews.py" \
101
- --owner <OWNER> --repo <REPO> --number <NUMBER>
102
- ```
81
+ Fetch all review comments and filter for `copilot-pull-request-reviewer[bot]`, matching `pull_request_review_id` to the newest Copilot review on the target commit:
103
82
 
104
- Output: JSON array of `{review_id, commit_id, submitted_at, state, body, classification}` where `classification` is `"clean"` for `state == "APPROVED"`, `"dirty"` for `state == "CHANGES_REQUESTED"`, and `"dirty"` for `state == "COMMENTED"` with a non-empty body. Uses `--paginate --slurp` and flattens pages in Python — required by `../../../rules/gh-paginate.md`. Login filter is a case-insensitive substring match on `copilot` (handles the divergence where Copilot reviews come from `copilot-pull-request-reviewer[bot]` but its inline comments are authored by `Copilot`).
83
+ pull_request_read(method="get_review_comments", pullNumber=NUMBER, owner=OWNER, repo=REPO)
105
84
 
106
- ### `fetch_copilot_inline_comments.py`
85
+ ### Request Copilot review: `add_issue_comment` or `request_copilot_review`
107
86
 
108
- Fetches unaddressed Copilot inline comments for the **newest submitted Copilot review** on the requested ``--commit`` SHA (matches ``pull_request_review_id`` to the review returned by ``fetch_copilot_reviews.py`` so stale inline threads from an older Copilot review on the same SHA are ignored).
87
+ Two options:
109
88
 
110
- ```bash
111
- python "${CLAUDE_SKILL_DIR}/scripts/fetch_copilot_inline_comments.py" \
112
- --owner <OWNER> --repo <REPO> --number <NUMBER> --commit <CURRENT_HEAD>
113
- ```
89
+ 1. Comment-based trigger:
90
+ ```
91
+ add_issue_comment(owner=OWNER, repo=REPO, issueNumber=NUMBER, body="@copilot review")
92
+ ```
93
+ 2. Dedicated MCP tool (when available):
94
+ ```
95
+ request_copilot_review(owner=OWNER, repo=REPO, pullNumber=NUMBER)
96
+ ```
114
97
 
115
- Output: JSON array of `{comment_id, commit_id, path, line, body}`. Uses the same `--paginate --slurp` pattern as `fetch_copilot_reviews.py`.
98
+ The `[bot]` suffix is load-bearing per `../../copilot-review/SKILL.md`.
116
99
 
117
- ### `request_copilot_review.py`
100
+ ### Claude reviews: `pull_request_read(method="get_reviews")`
118
101
 
119
- Requests a Copilot review on the current PR via `gh api -X POST repos/{owner}/{repo}/pulls/{number}/requested_reviewers -f 'reviewers[]=copilot-pull-request-reviewer[bot]'`. The `[bot]` suffix is load-bearing per `../../copilot-review/SKILL.md` — `Copilot`, `copilot`, and `github-copilot` all silently no-op.
102
+ Fetch all reviews and filter for login containing `claude`:
120
103
 
121
- ```bash
122
- python "${CLAUDE_SKILL_DIR}/scripts/request_copilot_review.py" \
123
- --owner <OWNER> --repo <REPO> --number <NUMBER>
124
- ```
104
+ pull_request_read(method="get_reviews", pullNumber=NUMBER, owner=OWNER, repo=REPO)
125
105
 
126
- Output: none on success (gh's stdout is suppressed); `subprocess.CalledProcessError` on failure.
106
+ Classification: same state-based rules as Copilot reviews.
127
107
 
128
- ### `fetch_claude_reviews.py`
108
+ ### Claude inline comments: `pull_request_read(method="get_review_comments")`
129
109
 
130
- Fetches every Claude reviewer-bot review on the PR newest-first, classified per the review's `state` field. Mirror of `fetch_copilot_reviews.py` for an Anthropic Claude PR review bot (e.g. `claude[bot]`, `claude-code[bot]`, or any login containing `claude`).
110
+ Fetch all review comments and filter for login containing `claude`, matching `pull_request_review_id` to the newest Claude review on the target commit:
131
111
 
132
- ```bash
133
- python "${CLAUDE_SKILL_DIR}/scripts/fetch_claude_reviews.py" \
134
- --owner <OWNER> --repo <REPO> --number <NUMBER>
135
- ```
136
-
137
- Output: JSON array of `{review_id, commit_id, submitted_at, state, body, classification}` — same shape and same state-based classification rules as `fetch_copilot_reviews.py`. Login filter is a case-insensitive substring match on `claude`.
138
-
139
- ### `fetch_claude_inline_comments.py`
140
-
141
- Fetches unaddressed Claude inline comments for the **newest submitted Claude review** on the requested `--commit` SHA (matches `pull_request_review_id` to the review returned by `fetch_claude_reviews.py` so stale inline threads from an older Claude review on the same SHA are ignored).
142
-
143
- ```bash
144
- python "${CLAUDE_SKILL_DIR}/scripts/fetch_claude_inline_comments.py" \
145
- --owner <OWNER> --repo <REPO> --number <NUMBER> --commit <CURRENT_HEAD>
146
- ```
147
-
148
- Output: JSON array of `{comment_id, commit_id, path, line, body}`. Same `--paginate --slurp` pattern.
112
+ pull_request_read(method="get_review_comments", pullNumber=NUMBER, owner=OWNER, repo=REPO)
149
113
 
150
114
  ## Shared modules
151
115
 
152
- The reviewer fetch scripts share their fetch and classification logic via two internal modules. Entry-point scripts (`fetch_bugbot_reviews.py`, `fetch_copilot_reviews.py`, `fetch_claude_reviews.py` and their inline-comment counterparts) are thin wrappers they import a per-reviewer spec, call the shared core, and shape argparse / JSON output.
153
-
154
- ### `reviewer_specs.py`
155
-
156
- Defines the `ReviewerSpec` frozen dataclass (two fields: `login_filter_substring` and `classify_review`) plus three module-level instances: `bugbot_spec`, `copilot_spec`, `claude_spec`. The state-based classifier used by Copilot and Claude is built via the shared `_make_state_based_classifier` factory; Bugbot has its own body-regex classifier because Bugbot uses `state == "COMMENTED"` for both clean and dirty reviews and only the body distinguishes them.
157
-
158
- Spec instances use lowercase names because they are frozen dataclass values rather than scalar configuration constants — keeps them out of the `UPPER_SNAKE` constants-location rule that requires module-level constants outside `config/` to be hoisted there.
159
-
160
- ### `reviewer_fetch_core.py`
161
-
162
- Exports `fetch_reviewer_reviews(spec, ...)` and `fetch_reviewer_inline_comments(spec, ..., all_reviews=...)`. The inline-comments function takes pre-fetched reviews as an argument rather than fetching them internally, so each entry-point script keeps its own patchable `fetch_X_reviews` function for tests that mock the reviews fetch on the entry-point module.
163
-
164
- The core enforces the gh-paginate contract (`--paginate --slurp` + Python JSON flattening, never `gh --jq` for cross-page operations) and the case-insensitive substring login filter in one place.
116
+ No shared fetch core modules are needed. Each review or comment fetch is a single MCP tool call with client-side filtering by login.
165
117
 
166
118
  ## Tests
167
119
 
168
- Each script has a sibling `test_<name>.py`. Run them all with:
169
-
170
- ```bash
171
- python -m pytest packages/claude-dev-env/skills/pr-converge/scripts/ -v
172
- ```
120
+ Unit tests for gh wrapper scripts are removed. Reviewer interaction is tested through the MCP server contract.
@@ -347,8 +347,8 @@ Found 1 general PR comment (not inline):
347
347
  This is a general comment, not tied to specific code lines.
348
348
  Cannot auto-respond to general comments.
349
349
 
350
- Suggestion: Reply manually on GitHub or use gh CLI:
351
- gh pr comment 91 -b "✅ **Added**: Cache invalidation tests in tests/test_cache.py"
350
+ Suggestion: Reply manually on GitHub or use the MCP tool:
351
+ `add_issue_comment(owner=owner, repo=repo, issueNumber=91, body="✅ **Added**: Cache invalidation tests in tests/test_cache.py")`
352
352
  ```
353
353
 
354
354
  ---
@@ -242,11 +242,7 @@ One-to-one mapping, clear context.
242
242
  **GitHub location**: File diffs, attached to specific line numbers
243
243
 
244
244
  **How to respond**:
245
- ```bash
246
- gh api repos/{owner}/{repo}/pulls/comments/{comment_id}/replies \
247
- -X POST \
248
- -f body="✅ **Fixed**: [description]"
249
- ```
245
+ `add_reply_to_pull_request_comment(commentId={comment_id}, body="✅ **Fixed**: [description]", owner="{owner}", repo="{repo}", pullNumber={pr_number})`
250
246
 
251
247
  **Skill behavior**: Automatically detects and responds to these
252
248
 
@@ -257,9 +253,7 @@ gh api repos/{owner}/{repo}/pulls/comments/{comment_id}/replies \
257
253
  **GitHub location**: PR conversation tab
258
254
 
259
255
  **How to respond**:
260
- ```bash
261
- gh pr comment {pr_number} -b "✅ **Fixed**: [description]"
262
- ```
256
+ `add_issue_comment(owner=owner, repo=repo, issueNumber={pr_number}, body="✅ **Fixed**: [description]")`
263
257
 
264
258
  **Skill behavior**: Detects but cannot auto-respond (lacks line context)
265
259
 
@@ -24,7 +24,6 @@ Use this skill:
24
24
  - **SKILL.md**: Core instructions and step-by-step workflow for Claude
25
25
  - **EXAMPLES.md**: 8 real-world examples with different scenarios
26
26
  - **PRINCIPLES.md**: Theory and best practices for PR communication
27
- - **scripts/respond_to_reviews.py**: Standalone automation script
28
27
  - **README.md**: This file
29
28
 
30
29
  ## Quick Start
@@ -44,19 +43,6 @@ git commit -m "fix: address PR feedback"
44
43
  git push
45
44
  ```
46
45
 
47
- ### Using Standalone Script
48
-
49
- ```bash
50
- # Auto-detect PR and respond to comments
51
- python scripts/respond_to_reviews.py
52
-
53
- # Specify PR number
54
- python scripts/respond_to_reviews.py --pr 123
55
-
56
- # Auto-approve responses (no confirmation)
57
- python scripts/respond_to_reviews.py --auto-approve
58
- ```
59
-
60
46
  ## Testing
61
47
 
62
48
  Test the skill with these queries:
@@ -74,23 +60,13 @@ Test the skill with these queries:
74
60
 
75
61
  ## Requirements
76
62
 
77
- - `gh` CLI installed and authenticated (`gh auth login`)
63
+ - GitHub MCP server (`plugin:github:github`) authenticated
78
64
  - Git repository with GitHub remote
79
65
  - Current branch with an open PR
80
66
  - Changes committed (not just staged)
81
- - Python 3.8+ (for standalone script)
82
67
 
83
68
  ## Integration Options
84
69
 
85
- ### As Pre-Push Hook
86
-
87
- Add to `.git/hooks/pre-push`:
88
- ```bash
89
- #!/bin/bash
90
- echo "Checking for PR review comments..."
91
- python .claude/skills/pr-review-responder/scripts/respond_to_reviews.py
92
- ```
93
-
94
70
  ### As Slash Command
95
71
 
96
72
  Add to `.claude/settings.json`:
@@ -104,19 +80,6 @@ Add to `.claude/settings.json`:
104
80
 
105
81
  Usage: `/respond`
106
82
 
107
- ### As npm Script
108
-
109
- Add to `package.json`:
110
- ```json
111
- {
112
- "scripts": {
113
- "respond": "python .claude/skills/pr-review-responder/scripts/respond_to_reviews.py"
114
- }
115
- }
116
- ```
117
-
118
- Usage: `npm run respond`
119
-
120
83
  ## Example Response Format
121
84
 
122
85
  ```
@@ -130,16 +93,16 @@ Usage: `npm run respond`
130
93
  ## Troubleshooting
131
94
 
132
95
  **"No PR found for this branch"**:
133
- - Create PR first: `gh pr create`
134
- - Or specify PR: `--pr NUMBER`
96
+ - Create PR first using MCP: `create_pull_request(owner=owner, repo=repo, title=title, body=body, head=head, base=base)`
97
+ - Ensure the branch has an associated open PR
135
98
 
136
99
  **"No review comments found"**:
137
100
  - PR might not have reviews yet
138
101
  - Or all comments already responded to
139
102
 
140
103
  **"Permission denied"**:
141
- - Run: `gh auth refresh -h github.com -s repo`
142
- - Ensure write access to repository
104
+ - Ensure the GitHub MCP server has write-scoped authentication
105
+ - Check repository write access
143
106
 
144
107
  **"Cannot find matching changes"**:
145
108
  - Review comments don't match files you modified
@@ -180,9 +143,6 @@ See [PRINCIPLES.md](PRINCIPLES.md) for:
180
143
  ```bash
181
144
  # Test skill activation
182
145
  "Respond to PR review comments"
183
-
184
- # Test standalone script
185
- python scripts/respond_to_reviews.py --help
186
146
  ```
187
147
 
188
148
  ## Version History
@@ -194,15 +154,14 @@ python scripts/respond_to_reviews.py --help
194
154
  - Matching changes to comments
195
155
  - Drafting responses with standard format
196
156
  - Posting via GitHub API
197
- - Standalone Python script
157
+ - MCP-based GitHub tool integration
198
158
 
199
159
  ## Contributing
200
160
 
201
161
  To improve this skill:
202
162
  1. Add new examples to EXAMPLES.md
203
163
  2. Document anti-patterns in PRINCIPLES.md
204
- 3. Enhance matching logic in scripts/respond_to_reviews.py
205
- 4. Test with various PR scenarios
164
+ 3. Test with various PR scenarios
206
165
 
207
166
  ## License
208
167
 
@@ -36,8 +36,7 @@ Before doing ANYTHING:
36
36
 
37
37
  - [ ] FORBIDDEN: Assuming you know what the comments say
38
38
  - [ ] FORBIDDEN: Using default pagination (30 results, causes missed comments)
39
- - [x] REQUIRED: `gh api repos/{owner}/{repo}/pulls/{pr_number}/comments?per_page=100`
40
- - [x] REQUIRED: Check for additional pages if 100+ comments
39
+ - [x] REQUIRED: `pull_request_read(method="get_review_comments", pullNumber={pr_number}, owner="{owner}", repo="{repo}")`
41
40
 
42
41
  **WHY:** Missing comments forces extra review rounds. Default pagination (30) silently drops comments.
43
42
 
@@ -194,7 +193,7 @@ Cannot add review responses to merged PRs. Report error.
194
193
 
195
194
  ## Requirements
196
195
 
197
- - `gh` CLI installed and authenticated
196
+ - GitHub MCP server (`plugin:github:github`) authenticated
198
197
  - Current branch must have an open PR
199
198
  - Git repository with remote on GitHub
200
199
  - Write access to repository
@@ -213,62 +213,12 @@ git commit -m "fix: latest feedback"
213
213
  - Doesn't duplicate on already-replied comments
214
214
  - Tracks which were skipped
215
215
 
216
- ## Standalone Script Tests
217
-
218
- ### Test 7: Script with Auto-Approve
219
-
220
- **Setup**:
221
- ```bash
222
- # PR with review comments
223
- # Changes committed
224
- ```
225
-
226
- **Test**:
227
- ```bash
228
- python scripts/respond_to_reviews.py --auto-approve
229
- ```
230
-
231
- **Expected behavior**:
232
- - Finds comments
233
- - Drafts responses
234
- - Posts WITHOUT asking for confirmation
235
- - Shows success report
236
-
237
- **Validation**:
238
- - No interactive prompts
239
- - Responses posted automatically
240
- - Exit code 0
241
-
242
- ### Test 8: Script with Specific PR Number
243
-
244
- **Setup**:
245
- ```bash
246
- # Multiple PRs open
247
- # Specify which one
248
- ```
249
-
250
- **Test**:
251
- ```bash
252
- python scripts/respond_to_reviews.py --pr 123
253
- ```
254
-
255
- **Expected behavior**:
256
- - Uses PR #123 (not auto-detect)
257
- - Processes that PR's comments
258
- - Posts responses
259
-
260
- **Validation**:
261
- - Correct PR processed
262
- - Doesn't use current branch's PR
263
-
264
216
  ## Edge Cases
265
217
 
266
218
  ### Test 9: Authentication Failure
267
219
 
268
220
  **Setup**:
269
- ```bash
270
- gh auth logout
271
- ```
221
+ - GitHub MCP server authentication is not configured or has expired
272
222
 
273
223
  **Test**:
274
224
  ```
@@ -277,9 +227,9 @@ gh auth logout
277
227
 
278
228
  **Expected behavior**:
279
229
  - Attempts to fetch PR
280
- - Gets auth error
230
+ - Gets auth error from MCP server
281
231
  - Shows clear error message
282
- - Suggests: `gh auth login`
232
+ - Suggests re-authenticating the GitHub MCP server
283
233
 
284
234
  ### Test 10: Merged PR
285
235
 
@@ -347,7 +297,7 @@ Before considering skill production-ready:
347
297
  - [ ] Happy path works end-to-end
348
298
  - [ ] Edge cases handled gracefully
349
299
  - [ ] Error messages are clear and actionable
350
- - [ ] Standalone script works independently
300
+ - [ ] GitHub MCP tools respond correctly for all operations
351
301
  - [ ] Documentation is comprehensive
352
302
  - [ ] Examples cover common scenarios
353
303
 
@@ -375,24 +325,17 @@ If skill doesn't activate:
375
325
  - Does it include "respond", "reply", "post"?
376
326
  - Are trigger words present?
377
327
 
378
- If script fails:
328
+ If MCP calls fail:
379
329
 
380
- 1. **Check gh CLI**:
381
- ```bash
382
- gh --version
383
- gh auth status
384
- ```
330
+ 1. **Check GitHub MCP server**:
331
+ - Verify `plugin:github:github` is listed in available MCP servers
332
+ - Check authentication status within the MCP server configuration
385
333
 
386
334
  2. **Check git repository**:
387
335
  ```bash
388
336
  git remote -v
389
337
  ```
390
338
 
391
- 3. **Run with error output**:
392
- ```bash
393
- python scripts/respond_to_reviews.py 2>&1 | tee debug.log
394
- ```
395
-
396
339
  ## Success Metrics
397
340
 
398
341
  Skill is working correctly if:
@@ -66,24 +66,19 @@ Pre-flight checks (in order):
66
66
 
67
67
  ## Step 1: Resolve PR scope (lead)
68
68
 
69
- 1. `gh pr view --json number,baseRefName,headRefName,url`
69
+ 1. Call `pull_request_read(method="get", pullNumber=N, owner=O, repo=R)` via the lead's available MCP tools (`N` comes from the parent skill's PR context, or fall back to `search_issues` MCP with the current branch name to recover the PR number). Extract `number`, `baseRefName`, `headRefName`, `url` from the response.
70
70
  2. Else `git merge-base HEAD origin/<default>` then `git diff <merge-base>...HEAD`
71
71
  3. Else refuse per § When this skill applies.
72
72
 
73
73
  Capture: `owner/repo`, `baseRefName`, `headRefName`, PR `number`, `url`, `starting_sha = git rev-parse HEAD`.
74
74
 
75
- Resolve the scoped temp directory once, lead-side, before spawning the subagent. Use Python's `tempfile.gettempdir()` so the path is correct on macOS, Linux, Windows cmd.exe, and PowerShell — do not hardcode `/tmp/` because Windows runners do not honor it. Capture the resolved absolute path as `<qbug_temp_dir>` and pass that literal path to the subagent:
75
+ Resolve the scoped temp directory once, lead-side, before spawning the subagent. Use Python's `tempfile.gettempdir()` so the path is correct on macOS, Linux, Windows cmd.exe, and PowerShell — do not hardcode `/tmp/` because Windows runners do not honor it. Pass the resolved absolute path along with the PR metadata to the subagent:
76
76
 
77
77
  ```python
78
- import json
79
- import subprocess
80
78
  import tempfile
81
79
  from pathlib import Path
82
80
 
83
- pr_view: dict = json.loads(subprocess.check_output(
84
- ["gh", "pr", "view", "--json", "number,baseRefName,headRefName,url"]
85
- ))
86
- pr_number: int = pr_view["number"]
81
+ pr_number: int = ... # from pull_request_read MCP response above
87
82
  qbug_temp_dir: Path = Path(tempfile.gettempdir()) / f"qbug-pr-{pr_number}"
88
83
  qbug_temp_dir.mkdir(parents=True, exist_ok=True)
89
84
  ```
@@ -183,7 +178,7 @@ The subagent receives this prompt and loops internally — the lead does not re-
183
178
  On exit 0: increment loop_count, proceed to AUDIT.
184
179
 
185
180
  3. AUDIT:
186
- Run: `gh pr diff <pr_number> -R <owner>/<repo> > <qbug_temp_dir>/loop-<loop_count>.patch`
181
+ Call `pull_request_read(method="get_diff", pullNumber=<pr_number>, owner=<owner>, repo=<repo>)` via MCP and save the diff to `<qbug_temp_dir>/loop-<loop_count>.patch`
187
182
 
188
183
  - Read the patch file.
189
184
  - Audit only added/modified lines. Read <categories_file> for the
@@ -204,13 +199,12 @@ The subagent receives this prompt and loops internally — the lead does not re-
204
199
  persistence schema.
205
200
 
206
201
  Post ONE review per loop. Use the payload shape from
207
- <categories_file>'s sibling SKILL.md § "PR comments" — build
208
- the JSON with jq `--rawfile` / `-Rs` reading per-finding body
209
- files, pipe to
210
- `gh api repos/<owner>/<repo>/pulls/<pr_number>/reviews -X POST --input -`.
202
+ <categories_file>'s sibling SKILL.md § "PR comments" — pass
203
+ the review body as a direct string parameter (not jq/piped files) to
204
+ `pull_request_review_write(method="create", event="COMMENT", body=<body>, owner=<owner>, repo=<repo>, pullNumber=<pr_number>, commitID=<head_sha>)`.
211
205
  Review body first line: `## /qbug loop <N> audit: <P0>P0 / <P1>P1 / <P2>P2`.
212
206
  If the review POST fails, fall back to one issue comment on
213
- `/issues/<pr_number>/comments` carrying the full body; mark every
207
+ `add_issue_comment(owner=<owner>, repo=<repo>, issueNumber=<pr_number>, body=<body>)` carrying the full body; mark every
214
208
  finding `used_fallback=true`.
215
209
 
216
210
  Harvest `html_url` for the parent review and each child comment
@@ -252,7 +246,7 @@ The subagent receives this prompt and loops internally — the lead does not re-
252
246
  contract's diagnostics schema (all eight keys required).
253
247
 
254
248
  Reply to each finding at loop_comment_index[finding_id].finding_comment_id
255
- using the reply CLI shape (jq `-Rs` `gh api .../comments/<id>/replies --input -`).
249
+ using `add_reply_to_pull_request_comment(commentId=<id>, body=<body>, owner=<owner>, repo=<repo>, pullNumber=<pr_number>)`.
256
250
  Reply body is one of:
257
251
  - `Fixed in <short_sha>`
258
252
  - `Could not address this loop: <one-line reason>`
@@ -301,7 +295,7 @@ _From /qbug audit loop 2._
301
295
 
302
296
  ## Step 3: PR description refresh (lead)
303
297
 
304
- Delegate body composition to the `pr-description-writer` agent (the mandatory-pr-description hook requires it for `gh pr edit` to succeed). Feed it the final PR diff and the original body. Apply via `gh pr edit <number> -R <owner>/<repo> --body-file .qbug-final-body.md`.
298
+ Delegate body composition to the `pr-description-writer` agent (the mandatory-pr-description hook requires it). Feed the agent the final PR diff and the original body. Apply via `update_pull_request(pullNumber=<number>, owner=<owner>, repo=<repo>, body=<new_body>)`.
305
299
 
306
300
  On error exit paths: best-effort; log the failure in the final report and continue.
307
301
 
@@ -1,31 +0,0 @@
1
- """Constants for gh_util.py per CODE_RULES centralized-config rule."""
2
-
3
- DEFAULT_TIMEOUT_SECONDS: int = 30
4
- DEFAULT_RETRIES: int = 2
5
- DEFAULT_BACKOFF_SECONDS: float = 1.0
6
- EXPONENTIAL_BACKOFF_BASE: int = 2
7
- GH_TIMEOUT_RETURN_CODE: int = 124
8
- INLINE_REVIEW_COMMENTS_PATH_TEMPLATE: str = (
9
- "/repos/{owner}/{repo}/pulls/{pull_number}/comments"
10
- )
11
-
12
- ALL_TRANSIENT_ERROR_MARKERS: tuple[str, ...] = (
13
- "connection reset",
14
- "connection refused",
15
- "timeout",
16
- "timed out",
17
- "temporarily unavailable",
18
- "502",
19
- "503",
20
- "504",
21
- "rate limit",
22
- )
23
-
24
- ALL_AUTH_ERROR_MARKERS: tuple[str, ...] = (
25
- "gh auth login",
26
- "authentication failed",
27
- "http 401",
28
- "http 403",
29
- "forbidden",
30
- "resource not accessible",
31
- )