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.
Files changed (94) 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/code_rules_enforcer.py +93 -32
  15. package/hooks/blocking/hedging_language_blocker.py +2 -2
  16. package/hooks/blocking/state_description_blocker.py +243 -0
  17. package/hooks/blocking/tdd_enforcer.py +94 -0
  18. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +158 -0
  19. package/hooks/blocking/test_hedging_language_blocker.py +1 -1
  20. package/hooks/blocking/test_state_description_blocker.py +618 -0
  21. package/hooks/blocking/test_tdd_enforcer.py +152 -0
  22. package/hooks/config/state_description_blocker_constants.py +130 -0
  23. package/hooks/hooks.json +10 -0
  24. package/package.json +1 -1
  25. package/rules/no-historical-clutter.md +31 -10
  26. package/scripts/config/groq_bugteam_config.py +13 -5
  27. package/skills/bugteam/CONSTRAINTS.md +20 -27
  28. package/skills/bugteam/EXAMPLES.md +1 -1
  29. package/skills/bugteam/PROMPTS.md +60 -31
  30. package/skills/bugteam/SKILL.md +47 -47
  31. package/skills/bugteam/SKILL_EVALS.md +8 -8
  32. package/skills/bugteam/reference/github-pr-reviews.md +31 -31
  33. package/skills/bugteam/reference/team-setup.md +1 -1
  34. package/skills/bugteam/reference/teardown-publish-permissions.md +4 -4
  35. package/skills/copilot-review/SKILL.md +7 -14
  36. package/skills/findbugs/SKILL.md +2 -2
  37. package/skills/fixbugs/SKILL.md +1 -1
  38. package/skills/monitor-open-prs/SKILL.md +6 -6
  39. package/skills/pr-converge/SKILL.md +7 -6
  40. package/skills/pr-converge/reference/convergence-gates.md +28 -30
  41. package/skills/pr-converge/reference/examples.md +4 -4
  42. package/skills/pr-converge/reference/fix-protocol.md +6 -8
  43. package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -10
  44. package/skills/pr-converge/reference/per-tick.md +18 -33
  45. package/skills/pr-converge/reference/stop-conditions.md +7 -7
  46. package/skills/pr-converge/scripts/README.md +65 -117
  47. package/skills/pr-review-responder/EXAMPLES.md +2 -2
  48. package/skills/pr-review-responder/PRINCIPLES.md +2 -8
  49. package/skills/pr-review-responder/README.md +7 -48
  50. package/skills/pr-review-responder/SKILL.md +2 -3
  51. package/skills/pr-review-responder/TESTING.md +8 -65
  52. package/skills/qbug/SKILL.md +10 -16
  53. package/_shared/pr-loop/scripts/config/gh_util_constants.py +0 -31
  54. package/_shared/pr-loop/scripts/gh_util.py +0 -193
  55. package/_shared/pr-loop/scripts/tests/test_gh_util.py +0 -257
  56. package/_shared/pr-loop/scripts/tests/test_gh_util_constants.py +0 -61
  57. package/skills/pr-converge/scripts/check_pr_mergeability.py +0 -78
  58. package/skills/pr-converge/scripts/config/pr_converge_constants.py +0 -134
  59. package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +0 -152
  60. package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +0 -70
  61. package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +0 -57
  62. package/skills/pr-converge/scripts/fetch_claude_inline_comments.py +0 -70
  63. package/skills/pr-converge/scripts/fetch_claude_reviews.py +0 -61
  64. package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +0 -70
  65. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +0 -61
  66. package/skills/pr-converge/scripts/mark_pr_ready.py +0 -54
  67. package/skills/pr-converge/scripts/post-bugbot-run.helpers.ps1 +0 -49
  68. package/skills/pr-converge/scripts/post-bugbot-run.ps1 +0 -33
  69. package/skills/pr-converge/scripts/reply_to_inline_comment.py +0 -84
  70. package/skills/pr-converge/scripts/request_copilot_review.py +0 -71
  71. package/skills/pr-converge/scripts/resolve_pr_head.py +0 -58
  72. package/skills/pr-converge/scripts/review_field_helpers.py +0 -43
  73. package/skills/pr-converge/scripts/reviewer_fetch_core.py +0 -153
  74. package/skills/pr-converge/scripts/reviewer_specs.py +0 -98
  75. package/skills/pr-converge/scripts/test_check_pr_mergeability.py +0 -126
  76. package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +0 -443
  77. package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +0 -299
  78. package/skills/pr-converge/scripts/test_fetch_claude_inline_comments.py +0 -485
  79. package/skills/pr-converge/scripts/test_fetch_claude_reviews.py +0 -368
  80. package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +0 -440
  81. package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +0 -366
  82. package/skills/pr-converge/scripts/test_mark_pr_ready.py +0 -69
  83. package/skills/pr-converge/scripts/test_post_bugbot_run.py +0 -195
  84. package/skills/pr-converge/scripts/test_reply_to_inline_comment.py +0 -159
  85. package/skills/pr-converge/scripts/test_request_copilot_review.py +0 -101
  86. package/skills/pr-converge/scripts/test_resolve_pr_head.py +0 -79
  87. package/skills/pr-converge/scripts/test_review_field_helpers.py +0 -80
  88. package/skills/pr-converge/scripts/test_reviewer_fetch_core.py +0 -448
  89. package/skills/pr-converge/scripts/test_reviewer_specs.py +0 -107
  90. package/skills/pr-converge/scripts/test_trigger_bugbot.py +0 -139
  91. package/skills/pr-converge/scripts/test_view_pr_context.py +0 -155
  92. package/skills/pr-converge/scripts/trigger_bugbot.py +0 -77
  93. package/skills/pr-converge/scripts/view_pr_context.py +0 -78
  94. package/skills/pr-review-responder/scripts/respond_to_reviews.py +0 -376
@@ -1,299 +0,0 @@
1
- """Tests for fetch_bugbot_reviews.
2
-
3
- Covers:
4
- - gh command uses --paginate --slurp (per gh-paginate rule)
5
- - per-page filter happens in Python after fetching all pages
6
- - only cursor[bot] reviews are returned
7
- - reviews are sorted newest-first by submitted_at
8
- - review bodies matching the bugbot dirty-pattern are classified "dirty"
9
- - review bodies without the pattern are classified "clean"
10
- - subprocess errors propagate with stderr context
11
- """
12
-
13
- from __future__ import annotations
14
-
15
- import importlib.util
16
- import json
17
- import subprocess
18
- from pathlib import Path
19
- from types import ModuleType
20
- from unittest.mock import MagicMock, patch
21
-
22
- import pytest
23
-
24
-
25
- def _load_module() -> ModuleType:
26
- module_path = Path(__file__).parent / "fetch_bugbot_reviews.py"
27
- spec = importlib.util.spec_from_file_location("fetch_bugbot_reviews", module_path)
28
- assert spec is not None
29
- assert spec.loader is not None
30
- module = importlib.util.module_from_spec(spec)
31
- spec.loader.exec_module(module)
32
- return module
33
-
34
-
35
- fetch_bugbot_reviews_module = _load_module()
36
-
37
-
38
- def _completed(stdout: str) -> subprocess.CompletedProcess:
39
- process = MagicMock(spec=subprocess.CompletedProcess)
40
- process.stdout = stdout
41
- process.returncode = 0
42
- return process
43
-
44
-
45
- def test_should_invoke_gh_with_paginate_slurp_against_reviews_endpoint() -> None:
46
- pages_payload = json.dumps([[]])
47
- with patch("subprocess.run") as mock_run:
48
- mock_run.return_value = _completed(pages_payload)
49
- fetch_bugbot_reviews_module.fetch_bugbot_reviews(
50
- owner="acme", repo="widget", number=42
51
- )
52
- invoked_argv = mock_run.call_args[0][0]
53
- assert invoked_argv[0] == "gh"
54
- assert invoked_argv[1] == "api"
55
- assert "repos/acme/widget/pulls/42/reviews?per_page=100" in invoked_argv[2]
56
- assert "--paginate" in invoked_argv
57
- assert "--slurp" in invoked_argv
58
-
59
-
60
- def test_should_filter_to_cursor_bot_only() -> None:
61
- pages_payload = json.dumps(
62
- [
63
- [
64
- {
65
- "id": 1,
66
- "user": {"login": "copilot-pull-request-reviewer[bot]"},
67
- "commit_id": "abc",
68
- "submitted_at": "2026-01-01T00:00:00Z",
69
- "body": "copilot stuff",
70
- },
71
- {
72
- "id": 2,
73
- "user": {"login": "cursor[bot]"},
74
- "commit_id": "abc",
75
- "submitted_at": "2026-01-02T00:00:00Z",
76
- "body": "Cursor Bugbot has reviewed your changes and found 1 potential issue.",
77
- },
78
- ]
79
- ]
80
- )
81
- with patch("subprocess.run") as mock_run:
82
- mock_run.return_value = _completed(pages_payload)
83
- all_reviews = fetch_bugbot_reviews_module.fetch_bugbot_reviews(
84
- owner="acme", repo="widget", number=42
85
- )
86
- assert len(all_reviews) == 1
87
- assert all_reviews[0]["review_id"] == 2
88
-
89
-
90
- def test_should_return_reviews_newest_first_across_pages() -> None:
91
- pages_payload = json.dumps(
92
- [
93
- [
94
- {
95
- "id": 10,
96
- "user": {"login": "cursor[bot]"},
97
- "commit_id": "old",
98
- "submitted_at": "2026-01-01T00:00:00Z",
99
- "body": "Bugbot reviewed your changes and found no new issues!",
100
- }
101
- ],
102
- [
103
- {
104
- "id": 11,
105
- "user": {"login": "cursor[bot]"},
106
- "commit_id": "new",
107
- "submitted_at": "2026-01-03T00:00:00Z",
108
- "body": "Cursor Bugbot has reviewed your changes and found 2 potential issues.",
109
- },
110
- {
111
- "id": 12,
112
- "user": {"login": "cursor[bot]"},
113
- "commit_id": "mid",
114
- "submitted_at": "2026-01-02T00:00:00Z",
115
- "body": "Bugbot reviewed your changes and found no new issues!",
116
- },
117
- ],
118
- ]
119
- )
120
- with patch("subprocess.run") as mock_run:
121
- mock_run.return_value = _completed(pages_payload)
122
- all_reviews = fetch_bugbot_reviews_module.fetch_bugbot_reviews(
123
- owner="acme", repo="widget", number=42
124
- )
125
- submitted_at_sequence = [each_review["submitted_at"] for each_review in all_reviews]
126
- assert submitted_at_sequence == [
127
- "2026-01-03T00:00:00Z",
128
- "2026-01-02T00:00:00Z",
129
- "2026-01-01T00:00:00Z",
130
- ]
131
-
132
-
133
- def test_should_classify_dirty_review_when_body_matches_bugbot_findings_pattern() -> (
134
- None
135
- ):
136
- pages_payload = json.dumps(
137
- [
138
- [
139
- {
140
- "id": 1,
141
- "user": {"login": "cursor[bot]"},
142
- "commit_id": "abc",
143
- "submitted_at": "2026-01-01T00:00:00Z",
144
- "body": "Cursor Bugbot has reviewed your changes and found 3 potential issues.",
145
- }
146
- ]
147
- ]
148
- )
149
- with patch("subprocess.run") as mock_run:
150
- mock_run.return_value = _completed(pages_payload)
151
- all_reviews = fetch_bugbot_reviews_module.fetch_bugbot_reviews(
152
- owner="acme", repo="widget", number=42
153
- )
154
- assert all_reviews[0]["classification"] == "dirty"
155
-
156
-
157
- def test_should_classify_clean_review_when_body_lacks_findings_pattern() -> None:
158
- pages_payload = json.dumps(
159
- [
160
- [
161
- {
162
- "id": 1,
163
- "user": {"login": "cursor[bot]"},
164
- "commit_id": "abc",
165
- "submitted_at": "2026-01-01T00:00:00Z",
166
- "body": "Bugbot reviewed your changes and found no new issues!",
167
- }
168
- ]
169
- ]
170
- )
171
- with patch("subprocess.run") as mock_run:
172
- mock_run.return_value = _completed(pages_payload)
173
- all_reviews = fetch_bugbot_reviews_module.fetch_bugbot_reviews(
174
- owner="acme", repo="widget", number=42
175
- )
176
- assert all_reviews[0]["classification"] == "clean"
177
-
178
-
179
- def test_should_match_login_case_insensitively() -> None:
180
- pages_payload = json.dumps(
181
- [
182
- [
183
- {
184
- "id": 1,
185
- "user": {"login": "Cursor"},
186
- "commit_id": "abc",
187
- "submitted_at": "2026-01-01T00:00:00Z",
188
- "body": "Cursor Bugbot has reviewed your changes and found 1 potential issue.",
189
- },
190
- {
191
- "id": 2,
192
- "user": {"login": "CURSOR[bot]"},
193
- "commit_id": "abc",
194
- "submitted_at": "2026-01-02T00:00:00Z",
195
- "body": "Bugbot reviewed your changes and found no new issues!",
196
- },
197
- ]
198
- ]
199
- )
200
- with patch("subprocess.run") as mock_run:
201
- mock_run.return_value = _completed(pages_payload)
202
- all_reviews = fetch_bugbot_reviews_module.fetch_bugbot_reviews(
203
- owner="acme", repo="widget", number=42
204
- )
205
- assert {each_review["review_id"] for each_review in all_reviews} == {1, 2}
206
-
207
-
208
- def test_should_match_login_containing_cursor_substring() -> None:
209
- pages_payload = json.dumps(
210
- [
211
- [
212
- {
213
- "id": 1,
214
- "user": {"login": "cursor[bot]"},
215
- "commit_id": "abc",
216
- "submitted_at": "2026-01-01T00:00:00Z",
217
- "body": "Cursor Bugbot has reviewed your changes and found 1 potential issue.",
218
- },
219
- {
220
- "id": 2,
221
- "user": {"login": "internal-cursor-fork[bot]"},
222
- "commit_id": "abc",
223
- "submitted_at": "2026-01-02T00:00:00Z",
224
- "body": "Cursor Bugbot has reviewed your changes and found 2 potential issues.",
225
- },
226
- ]
227
- ]
228
- )
229
- with patch("subprocess.run") as mock_run:
230
- mock_run.return_value = _completed(pages_payload)
231
- all_reviews = fetch_bugbot_reviews_module.fetch_bugbot_reviews(
232
- owner="acme", repo="widget", number=42
233
- )
234
- assert {each_review["review_id"] for each_review in all_reviews} == {1, 2}
235
-
236
-
237
- def test_should_exclude_login_without_cursor_substring() -> None:
238
- pages_payload = json.dumps(
239
- [
240
- [
241
- {
242
- "id": 1,
243
- "user": {"login": "copilot-pull-request-reviewer[bot]"},
244
- "commit_id": "abc",
245
- "submitted_at": "2026-01-01T00:00:00Z",
246
- "body": "copilot finding",
247
- },
248
- {
249
- "id": 2,
250
- "user": {"login": "dependabot[bot]"},
251
- "commit_id": "abc",
252
- "submitted_at": "2026-01-02T00:00:00Z",
253
- "body": "dependency bump",
254
- },
255
- ]
256
- ]
257
- )
258
- with patch("subprocess.run") as mock_run:
259
- mock_run.return_value = _completed(pages_payload)
260
- all_reviews = fetch_bugbot_reviews_module.fetch_bugbot_reviews(
261
- owner="acme", repo="widget", number=42
262
- )
263
- assert all_reviews == []
264
-
265
-
266
- def test_should_raise_when_gh_subprocess_fails() -> None:
267
- failure = subprocess.CalledProcessError(
268
- returncode=1, cmd=["gh"], stderr="auth failure"
269
- )
270
- with patch("subprocess.run", side_effect=failure):
271
- with pytest.raises(subprocess.CalledProcessError):
272
- fetch_bugbot_reviews_module.fetch_bugbot_reviews(
273
- owner="acme", repo="widget", number=42
274
- )
275
-
276
-
277
- def test_should_return_entries_whose_keys_are_strings() -> None:
278
- pages_payload = json.dumps(
279
- [
280
- [
281
- {
282
- "id": 1,
283
- "user": {"login": "cursor[bot]"},
284
- "commit_id": "abc",
285
- "submitted_at": "2026-01-01T00:00:00Z",
286
- "body": "Bugbot reviewed your changes and found no new issues!",
287
- }
288
- ]
289
- ]
290
- )
291
- with patch("subprocess.run") as mock_run:
292
- mock_run.return_value = _completed(pages_payload)
293
- all_reviews = fetch_bugbot_reviews_module.fetch_bugbot_reviews(
294
- owner="acme", repo="widget", number=42
295
- )
296
- assert len(all_reviews) == 1
297
- first_review_entry = all_reviews[0]
298
- assert isinstance(first_review_entry, dict)
299
- assert all(isinstance(each_key, str) for each_key in first_review_entry.keys())