claude-dev-env 1.37.1 → 1.38.1
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.
- package/CLAUDE.md +3 -0
- package/_shared/pr-loop/audit-contract.md +4 -3
- package/_shared/pr-loop/fix-protocol.md +2 -0
- package/_shared/pr-loop/gh-payloads.md +38 -37
- package/_shared/pr-loop/scripts/README.md +0 -1
- package/_shared/pr-loop/scripts/preflight.py +2 -1
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +2 -2
- package/_shared/pr-loop/scripts/tests/test_preflight.py +22 -0
- package/_shared/pr-loop/state-schema.md +10 -10
- package/agents/clean-coder.md +4 -0
- package/agents/code-quality-agent.md +23 -85
- package/agents/groq-coder.md +8 -6
- package/hooks/blocking/__init__.py +0 -0
- package/hooks/blocking/code_rules_enforcer.py +93 -32
- package/hooks/blocking/hedging_language_blocker.py +2 -2
- package/hooks/blocking/state_description_blocker.py +243 -0
- package/hooks/blocking/tdd_enforcer.py +94 -0
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +158 -0
- package/hooks/blocking/test_hedging_language_blocker.py +1 -1
- package/hooks/blocking/test_state_description_blocker.py +618 -0
- package/hooks/blocking/test_tdd_enforcer.py +152 -0
- package/hooks/config/state_description_blocker_constants.py +130 -0
- package/hooks/hooks.json +10 -0
- package/package.json +1 -1
- package/rules/no-historical-clutter.md +31 -10
- package/scripts/config/groq_bugteam_config.py +13 -5
- package/skills/bugteam/CONSTRAINTS.md +20 -27
- package/skills/bugteam/EXAMPLES.md +1 -1
- package/skills/bugteam/PROMPTS.md +60 -31
- package/skills/bugteam/SKILL.md +47 -47
- package/skills/bugteam/SKILL_EVALS.md +8 -8
- package/skills/bugteam/reference/github-pr-reviews.md +31 -31
- package/skills/bugteam/reference/team-setup.md +1 -1
- package/skills/bugteam/reference/teardown-publish-permissions.md +4 -4
- package/skills/copilot-review/SKILL.md +7 -14
- package/skills/findbugs/SKILL.md +2 -2
- package/skills/fixbugs/SKILL.md +1 -1
- package/skills/monitor-open-prs/SKILL.md +6 -6
- package/skills/pr-converge/SKILL.md +7 -6
- package/skills/pr-converge/reference/convergence-gates.md +28 -30
- package/skills/pr-converge/reference/examples.md +4 -4
- package/skills/pr-converge/reference/fix-protocol.md +6 -8
- package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -10
- package/skills/pr-converge/reference/per-tick.md +18 -33
- package/skills/pr-converge/reference/stop-conditions.md +7 -7
- package/skills/pr-converge/scripts/README.md +65 -117
- package/skills/pr-review-responder/EXAMPLES.md +2 -2
- package/skills/pr-review-responder/PRINCIPLES.md +2 -8
- package/skills/pr-review-responder/README.md +7 -48
- package/skills/pr-review-responder/SKILL.md +2 -3
- package/skills/pr-review-responder/TESTING.md +8 -65
- package/skills/qbug/SKILL.md +10 -16
- package/_shared/pr-loop/scripts/config/gh_util_constants.py +0 -31
- package/_shared/pr-loop/scripts/gh_util.py +0 -193
- package/_shared/pr-loop/scripts/tests/test_gh_util.py +0 -257
- package/_shared/pr-loop/scripts/tests/test_gh_util_constants.py +0 -61
- package/skills/pr-converge/scripts/check_pr_mergeability.py +0 -78
- package/skills/pr-converge/scripts/config/pr_converge_constants.py +0 -134
- package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +0 -152
- package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +0 -70
- package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +0 -57
- package/skills/pr-converge/scripts/fetch_claude_inline_comments.py +0 -70
- package/skills/pr-converge/scripts/fetch_claude_reviews.py +0 -61
- package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +0 -70
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +0 -61
- package/skills/pr-converge/scripts/mark_pr_ready.py +0 -54
- package/skills/pr-converge/scripts/post-bugbot-run.helpers.ps1 +0 -49
- package/skills/pr-converge/scripts/post-bugbot-run.ps1 +0 -33
- package/skills/pr-converge/scripts/reply_to_inline_comment.py +0 -84
- package/skills/pr-converge/scripts/request_copilot_review.py +0 -71
- package/skills/pr-converge/scripts/resolve_pr_head.py +0 -58
- package/skills/pr-converge/scripts/review_field_helpers.py +0 -43
- package/skills/pr-converge/scripts/reviewer_fetch_core.py +0 -153
- package/skills/pr-converge/scripts/reviewer_specs.py +0 -98
- package/skills/pr-converge/scripts/test_check_pr_mergeability.py +0 -126
- package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +0 -443
- package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +0 -299
- package/skills/pr-converge/scripts/test_fetch_claude_inline_comments.py +0 -485
- package/skills/pr-converge/scripts/test_fetch_claude_reviews.py +0 -368
- package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +0 -440
- package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +0 -366
- package/skills/pr-converge/scripts/test_mark_pr_ready.py +0 -69
- package/skills/pr-converge/scripts/test_post_bugbot_run.py +0 -195
- package/skills/pr-converge/scripts/test_reply_to_inline_comment.py +0 -159
- package/skills/pr-converge/scripts/test_request_copilot_review.py +0 -101
- package/skills/pr-converge/scripts/test_resolve_pr_head.py +0 -79
- package/skills/pr-converge/scripts/test_review_field_helpers.py +0 -80
- package/skills/pr-converge/scripts/test_reviewer_fetch_core.py +0 -448
- package/skills/pr-converge/scripts/test_reviewer_specs.py +0 -107
- package/skills/pr-converge/scripts/test_trigger_bugbot.py +0 -139
- package/skills/pr-converge/scripts/test_view_pr_context.py +0 -155
- package/skills/pr-converge/scripts/trigger_bugbot.py +0 -77
- package/skills/pr-converge/scripts/view_pr_context.py +0 -78
- package/skills/pr-review-responder/scripts/respond_to_reviews.py +0 -376
|
@@ -1,172 +1,120 @@
|
|
|
1
|
-
# pr-converge
|
|
1
|
+
# pr-converge MCP tool references
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
5
|
+
## MCP tools
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### PR context: `pull_request_read(method="get")`
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Read PR metadata:
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
pull_request_read(method="get", pullNumber=NUMBER, owner=OWNER, repo=REPO)
|
|
12
12
|
|
|
13
|
-
|
|
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
|
-
|
|
15
|
+
### Bugbot reviews: `pull_request_read(method="get_reviews")`
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
Fetch all reviews and filter for `cursor[bot]`:
|
|
20
18
|
|
|
21
|
-
|
|
19
|
+
pull_request_read(method="get_reviews", pullNumber=NUMBER, owner=OWNER, repo=REPO)
|
|
22
20
|
|
|
23
|
-
|
|
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
|
-
|
|
23
|
+
### Bugbot inline comments: `pull_request_read(method="get_review_comments")`
|
|
29
24
|
|
|
30
|
-
|
|
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
|
-
|
|
27
|
+
pull_request_read(method="get_review_comments", pullNumber=NUMBER, owner=OWNER, repo=REPO)
|
|
33
28
|
|
|
34
|
-
|
|
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
|
-
|
|
31
|
+
Read the current HEAD SHA from the PR:
|
|
40
32
|
|
|
41
|
-
|
|
33
|
+
pull_request_read(method="get", pullNumber=NUMBER, owner=OWNER, repo=REPO)
|
|
42
34
|
|
|
43
|
-
|
|
35
|
+
Access `.head.sha` from the response.
|
|
44
36
|
|
|
45
|
-
|
|
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
|
-
|
|
39
|
+
Post the bugbot re-trigger comment:
|
|
51
40
|
|
|
52
|
-
|
|
41
|
+
add_issue_comment(owner="OWNER", repo="REPO", issueNumber=NUMBER, body="bugbot run")
|
|
53
42
|
|
|
54
|
-
|
|
43
|
+
`bugbot run` is the only recognized re-trigger phrase; alternative phrasings silently no-op.
|
|
55
44
|
|
|
56
|
-
|
|
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
|
-
|
|
47
|
+
Mark a draft PR as ready for review:
|
|
62
48
|
|
|
63
|
-
|
|
49
|
+
update_pull_request(pullNumber=NUMBER, owner=OWNER, repo=REPO, draft=false)
|
|
64
50
|
|
|
65
|
-
|
|
51
|
+
### Reply to inline comment: `add_reply_to_pull_request_comment`
|
|
66
52
|
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
+
### Mergeability: `pull_request_read(method="get")`
|
|
75
64
|
|
|
76
|
-
|
|
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
|
-
|
|
67
|
+
pull_request_read(method="get", pullNumber=NUMBER, owner=OWNER, repo=REPO)
|
|
83
68
|
|
|
84
|
-
|
|
69
|
+
Fields: `.mergeable`, `.mergeable_state`, `.head.sha`.
|
|
85
70
|
|
|
86
|
-
|
|
71
|
+
### Copilot reviews: `pull_request_read(method="get_reviews")`
|
|
87
72
|
|
|
88
|
-
|
|
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
|
-
|
|
75
|
+
pull_request_read(method="get_reviews", pullNumber=NUMBER, owner=OWNER, repo=REPO)
|
|
94
76
|
|
|
95
|
-
|
|
77
|
+
Classification: `APPROVED` → clean, `CHANGES_REQUESTED` → dirty, `COMMENTED` with non-empty body → dirty.
|
|
96
78
|
|
|
97
|
-
|
|
79
|
+
### Copilot inline comments: `pull_request_read(method="get_review_comments")`
|
|
98
80
|
|
|
99
|
-
|
|
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
|
-
|
|
83
|
+
pull_request_read(method="get_review_comments", pullNumber=NUMBER, owner=OWNER, repo=REPO)
|
|
105
84
|
|
|
106
|
-
### `
|
|
85
|
+
### Request Copilot review: `add_issue_comment` or `request_copilot_review`
|
|
107
86
|
|
|
108
|
-
|
|
87
|
+
Two options:
|
|
109
88
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
98
|
+
The `[bot]` suffix is load-bearing per `../../copilot-review/SKILL.md`.
|
|
116
99
|
|
|
117
|
-
### `
|
|
100
|
+
### Claude reviews: `pull_request_read(method="get_reviews")`
|
|
118
101
|
|
|
119
|
-
|
|
102
|
+
Fetch all reviews and filter for login containing `claude`:
|
|
120
103
|
|
|
121
|
-
|
|
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
|
-
|
|
106
|
+
Classification: same state-based rules as Copilot reviews.
|
|
127
107
|
|
|
128
|
-
### `
|
|
108
|
+
### Claude inline comments: `pull_request_read(method="get_review_comments")`
|
|
129
109
|
|
|
130
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
351
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
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: `
|
|
134
|
-
-
|
|
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
|
-
-
|
|
142
|
-
-
|
|
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
|
-
-
|
|
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.
|
|
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: `
|
|
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
|
-
-
|
|
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
|
-
|
|
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
|
|
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
|
-
- [ ]
|
|
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
|
|
328
|
+
If MCP calls fail:
|
|
379
329
|
|
|
380
|
-
1. **Check
|
|
381
|
-
|
|
382
|
-
|
|
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:
|
package/skills/qbug/SKILL.md
CHANGED
|
@@ -66,24 +66,19 @@ Pre-flight checks (in order):
|
|
|
66
66
|
|
|
67
67
|
## Step 1: Resolve PR scope (lead)
|
|
68
68
|
|
|
69
|
-
1. `
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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" —
|
|
208
|
-
the
|
|
209
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
)
|