claude-dev-env 1.36.2 → 1.37.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 (70) hide show
  1. package/_shared/pr-loop/scripts/config/preflight_constants.py +29 -8
  2. package/_shared/pr-loop/scripts/preflight.py +242 -20
  3. package/_shared/pr-loop/scripts/tests/test_preflight.py +362 -25
  4. package/_shared/pr-loop/scripts/tests/test_preflight_constants.py +9 -14
  5. package/hooks/blocking/code_rules_enforcer.py +269 -23
  6. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +157 -1
  7. package/hooks/config/test_unused_module_import_constants.py +48 -0
  8. package/hooks/config/unused_module_import_constants.py +41 -0
  9. package/package.json +1 -1
  10. package/skills/bg-agent/SKILL.md +69 -0
  11. package/skills/bugteam/CONSTRAINTS.md +10 -19
  12. package/skills/bugteam/PROMPTS.md +3 -3
  13. package/skills/bugteam/SKILL.md +103 -202
  14. package/skills/bugteam/SKILL_EVALS.md +75 -114
  15. package/skills/bugteam/reference/README.md +2 -4
  16. package/skills/bugteam/reference/design-rationale.md +3 -8
  17. package/skills/bugteam/reference/team-setup.md +11 -19
  18. package/skills/bugteam/reference/teardown-publish-permissions.md +2 -14
  19. package/skills/bugteam/scripts/config/__init__.py +0 -0
  20. package/skills/bugteam/scripts/config/reflow_skill_md_constants.py +12 -0
  21. package/skills/bugteam/scripts/reflow_skill_md.py +51 -47
  22. package/skills/bugteam/sources.md +1 -25
  23. package/skills/bugteam/test_skill_additions.py +4 -13
  24. package/skills/fresh-branch/SKILL.md +71 -0
  25. package/skills/gotcha/SKILL.md +73 -0
  26. package/skills/monitor-open-prs/SKILL.md +4 -37
  27. package/skills/monitor-open-prs/test_skill_contract.py +0 -5
  28. package/skills/pr-converge/SKILL.md +60 -1298
  29. package/skills/pr-converge/reference/convergence-gates.md +118 -0
  30. package/skills/pr-converge/reference/examples.md +76 -0
  31. package/skills/pr-converge/reference/fix-protocol.md +54 -0
  32. package/skills/pr-converge/reference/ground-rules.md +13 -0
  33. package/skills/pr-converge/reference/multi-pr-orchestration.md +204 -0
  34. package/skills/pr-converge/reference/per-tick.md +201 -0
  35. package/skills/pr-converge/reference/state-schema.md +19 -0
  36. package/skills/pr-converge/reference/stop-conditions.md +26 -0
  37. package/skills/pr-converge/scripts/README.md +36 -9
  38. package/skills/pr-converge/scripts/check_pr_mergeability.py +1 -2
  39. package/skills/pr-converge/scripts/config/pr_converge_constants.py +58 -5
  40. package/skills/pr-converge/scripts/config/reflow_skill_md_constants.py +13 -0
  41. package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +0 -24
  42. package/skills/pr-converge/scripts/cursor-agents-continue.ahk +22 -2
  43. package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +19 -59
  44. package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +15 -61
  45. package/skills/pr-converge/scripts/fetch_claude_inline_comments.py +70 -0
  46. package/skills/pr-converge/scripts/fetch_claude_reviews.py +61 -0
  47. package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +19 -61
  48. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +14 -74
  49. package/skills/pr-converge/scripts/reflow_skill_md.py +71 -50
  50. package/skills/pr-converge/scripts/reviewer_fetch_core.py +153 -0
  51. package/skills/pr-converge/scripts/reviewer_specs.py +98 -0
  52. package/skills/pr-converge/scripts/test_cursor_agents_continue.py +65 -0
  53. package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +107 -6
  54. package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +85 -6
  55. package/skills/pr-converge/scripts/test_fetch_claude_inline_comments.py +485 -0
  56. package/skills/pr-converge/scripts/test_fetch_claude_reviews.py +368 -0
  57. package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +74 -6
  58. package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +94 -8
  59. package/skills/pr-converge/scripts/test_reflow_skill_md.py +162 -0
  60. package/skills/pr-converge/scripts/test_reviewer_fetch_core.py +448 -0
  61. package/skills/pr-converge/scripts/test_reviewer_specs.py +107 -0
  62. package/skills/pr-converge/workflows/schedule-wakeup-loop.md +24 -22
  63. package/skills/bugteam/reference/workflow-path-a-orchestrated-teams.md +0 -113
  64. package/skills/bugteam/reference/workflow-path-b-task-harness.md +0 -48
  65. package/skills/bugteam/test_team_lifecycle.py +0 -103
  66. package/skills/monitor-open-prs/test_team_lifecycle.py +0 -46
  67. package/skills/pr-converge/scripts/open_followup_copilot_pr.py +0 -136
  68. package/skills/pr-converge/scripts/test_open_followup_copilot_pr.py +0 -236
  69. package/skills/pr-converge/test_team_lifecycle.py +0 -56
  70. package/skills/pr-converge/workflows/ahk-auto-continue-loop.md +0 -108
@@ -0,0 +1,65 @@
1
+ """Tests for the Cursor Agents AutoHotkey pacer."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+
8
+ def _script_text() -> str:
9
+ return (Path(__file__).resolve().parent / "cursor-agents-continue.ahk").read_text(
10
+ encoding="utf-8"
11
+ )
12
+
13
+
14
+ def _function_body(function_name: str, next_marker: str) -> str:
15
+ script_text = _script_text()
16
+ function_start = script_text.index(function_name)
17
+ function_end = script_text.index(next_marker, function_start)
18
+ return script_text[function_start:function_end]
19
+
20
+
21
+ def test_should_fallback_when_pwsh_is_unavailable() -> None:
22
+ script_text = _script_text()
23
+ terminate_body = _function_body(
24
+ "terminate_other_script_instances() {",
25
+ "\n}\n\nrun_stop_script_with_shell",
26
+ )
27
+ helper_body = _function_body(
28
+ "run_stop_script_with_shell(shell_name, stop_script) {",
29
+ "\n}\n\nterminate_other_script_instances()",
30
+ )
31
+
32
+ first_shell_check = (
33
+ "if run_stop_script_with_shell(POWERSHELL_CORE_SHELL_NAME, stop_script)"
34
+ )
35
+ fallback_shell_check = (
36
+ "if run_stop_script_with_shell(WINDOWS_POWERSHELL_SHELL_NAME, stop_script)"
37
+ )
38
+
39
+ assert 'POWERSHELL_CORE_SHELL_NAME := "pwsh"' in script_text
40
+ assert 'WINDOWS_POWERSHELL_SHELL_NAME := "powershell.exe"' in script_text
41
+ assert "STOP_SCRIPT_ARGUMENTS_FORMAT :=" in script_text
42
+ assert 'RUN_WAIT_WINDOW_OPTION := "Hide"' in script_text
43
+ assert first_shell_check in terminate_body
44
+ assert fallback_shell_check in terminate_body
45
+ assert "throw Error(Format(STOP_SCRIPT_FAILURE_MESSAGE_FORMAT" in terminate_body
46
+ assert terminate_body.index(first_shell_check) < terminate_body.index(
47
+ fallback_shell_check
48
+ )
49
+ assert terminate_body.index(fallback_shell_check) < terminate_body.index(
50
+ "throw Error"
51
+ )
52
+ assert '"pwsh"' not in terminate_body
53
+ assert '"powershell.exe"' not in terminate_body
54
+ assert "try {" in helper_body
55
+ assert "catch {" in helper_body
56
+ assert (
57
+ "Format(STOP_SCRIPT_ARGUMENTS_FORMAT, stop_script, ProcessExist())"
58
+ in helper_body
59
+ )
60
+ assert (
61
+ "RunWait(shell_name stop_command_arguments, , RUN_WAIT_WINDOW_OPTION)"
62
+ in helper_body
63
+ )
64
+ assert "-NoProfile" not in helper_body
65
+ assert '"Hide"' not in helper_body
@@ -286,12 +286,113 @@ def test_should_flatten_across_pages() -> None:
286
286
  ]
287
287
 
288
288
 
289
- def test_should_reference_cursor_bot_login_constant_directly_without_local_alias() -> None:
290
- source_text = (
291
- Path(__file__).resolve().parent / "fetch_bugbot_inline_comments.py"
292
- ).read_text(encoding="utf-8")
293
- assert "cursor_bot_login = CURSOR_BOT_LOGIN" not in source_text
294
- assert "CURSOR_BOT_LOGIN" in source_text
289
+ def test_should_match_login_case_insensitively() -> None:
290
+ pages_payload = json.dumps(
291
+ [
292
+ [
293
+ {
294
+ "id": 300,
295
+ "user": {"login": "Cursor"},
296
+ "commit_id": "abc123",
297
+ "pull_request_review_id": 1,
298
+ "body": "uppercase login",
299
+ "path": "x.py",
300
+ "line": 5,
301
+ },
302
+ {
303
+ "id": 301,
304
+ "user": {"login": "CURSOR[bot]"},
305
+ "commit_id": "abc123",
306
+ "pull_request_review_id": 1,
307
+ "body": "screaming login",
308
+ "path": "x.py",
309
+ "line": 6,
310
+ },
311
+ ]
312
+ ]
313
+ )
314
+ with patch.object(
315
+ fetch_bugbot_inline_comments_module,
316
+ "fetch_bugbot_reviews",
317
+ return_value=_default_review_for_head(commit="abc123", review_id=1),
318
+ ), patch("subprocess.run") as mock_run:
319
+ mock_run.return_value = _completed(pages_payload)
320
+ all_inline_comments = (
321
+ fetch_bugbot_inline_comments_module.fetch_bugbot_inline_comments(
322
+ owner="acme", repo="widget", number=42, current_head="abc123"
323
+ )
324
+ )
325
+ assert {each_comment["comment_id"] for each_comment in all_inline_comments} == {300, 301}
326
+
327
+
328
+ def test_should_match_login_containing_cursor_substring() -> None:
329
+ pages_payload = json.dumps(
330
+ [
331
+ [
332
+ {
333
+ "id": 400,
334
+ "user": {"login": "internal-cursor-fork[bot]"},
335
+ "commit_id": "abc123",
336
+ "pull_request_review_id": 1,
337
+ "body": "non-canonical login still matches",
338
+ "path": "x.py",
339
+ "line": 5,
340
+ }
341
+ ]
342
+ ]
343
+ )
344
+ with patch.object(
345
+ fetch_bugbot_inline_comments_module,
346
+ "fetch_bugbot_reviews",
347
+ return_value=_default_review_for_head(commit="abc123", review_id=1),
348
+ ), patch("subprocess.run") as mock_run:
349
+ mock_run.return_value = _completed(pages_payload)
350
+ all_inline_comments = (
351
+ fetch_bugbot_inline_comments_module.fetch_bugbot_inline_comments(
352
+ owner="acme", repo="widget", number=42, current_head="abc123"
353
+ )
354
+ )
355
+ assert len(all_inline_comments) == 1
356
+ assert all_inline_comments[0]["comment_id"] == 400
357
+
358
+
359
+ def test_should_exclude_login_without_cursor_substring() -> None:
360
+ pages_payload = json.dumps(
361
+ [
362
+ [
363
+ {
364
+ "id": 500,
365
+ "user": {"login": "copilot-pull-request-reviewer[bot]"},
366
+ "commit_id": "abc123",
367
+ "pull_request_review_id": 1,
368
+ "body": "copilot finding",
369
+ "path": "x.py",
370
+ "line": 5,
371
+ },
372
+ {
373
+ "id": 501,
374
+ "user": {"login": "dependabot[bot]"},
375
+ "commit_id": "abc123",
376
+ "pull_request_review_id": 1,
377
+ "body": "dependency bump",
378
+ "path": "x.py",
379
+ "line": 6,
380
+ },
381
+ ]
382
+ ]
383
+ )
384
+ with patch.object(
385
+ fetch_bugbot_inline_comments_module,
386
+ "fetch_bugbot_reviews",
387
+ return_value=_default_review_for_head(commit="abc123", review_id=1),
388
+ ), patch("subprocess.run") as mock_run:
389
+ mock_run.return_value = _completed(pages_payload)
390
+ all_inline_comments = (
391
+ fetch_bugbot_inline_comments_module.fetch_bugbot_inline_comments(
392
+ owner="acme", repo="widget", number=42, current_head="abc123"
393
+ )
394
+ )
395
+ assert all_inline_comments == []
295
396
 
296
397
 
297
398
  def test_should_raise_when_gh_subprocess_fails() -> None:
@@ -176,12 +176,91 @@ def test_should_classify_clean_review_when_body_lacks_findings_pattern() -> None
176
176
  assert all_reviews[0]["classification"] == "clean"
177
177
 
178
178
 
179
- def test_should_reference_cursor_bot_login_constant_directly_without_local_alias() -> None:
180
- source_text = (
181
- Path(__file__).resolve().parent / "fetch_bugbot_reviews.py"
182
- ).read_text(encoding="utf-8")
183
- assert "cursor_bot_login = CURSOR_BOT_LOGIN" not in source_text
184
- assert "CURSOR_BOT_LOGIN" in source_text
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 == []
185
264
 
186
265
 
187
266
  def test_should_raise_when_gh_subprocess_fails() -> None: