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
@@ -30,6 +30,7 @@ assert _hook_spec.loader is not None
30
30
  _hook_module = importlib.util.module_from_spec(_hook_spec)
31
31
  _hook_spec.loader.exec_module(_hook_module)
32
32
  check_unused_module_level_imports = _hook_module.check_unused_module_level_imports
33
+ _reconstruct_post_edit_file_content = _hook_module._reconstruct_post_edit_file_content
33
34
 
34
35
 
35
36
  PRODUCTION_FILE_PATH = "packages/app/services/loader.py"
@@ -297,6 +298,163 @@ def test_should_skip_when_noqa_is_bare() -> None:
297
298
  assert issues == [], f"Bare noqa must suppress unused import, got: {issues}"
298
299
 
299
300
 
301
+ def test_should_not_flag_imports_referenced_only_in_full_file_content() -> None:
302
+ fragment = "from config.constants import NEW_NAME\n"
303
+ full_file = (
304
+ "from config.constants import NEW_NAME\n"
305
+ "\n"
306
+ "def existing_function() -> str:\n"
307
+ " return NEW_NAME\n"
308
+ )
309
+ issues = check_unused_module_level_imports(
310
+ fragment, PRODUCTION_FILE_PATH, full_file_content=full_file,
311
+ )
312
+ assert issues == [], (
313
+ f"When the post-edit file references the import, it must not flag, got: {issues}"
314
+ )
315
+
316
+
317
+ def test_should_flag_imports_unused_in_full_file_content() -> None:
318
+ fragment = "from config.constants import TRULY_UNUSED\n"
319
+ full_file = (
320
+ "from config.constants import TRULY_UNUSED\n"
321
+ "\n"
322
+ "def existing_function() -> None:\n"
323
+ " return None\n"
324
+ )
325
+ issues = check_unused_module_level_imports(
326
+ fragment, PRODUCTION_FILE_PATH, full_file_content=full_file,
327
+ )
328
+ assert any("TRULY_UNUSED" in each_issue for each_issue in issues), (
329
+ f"Imports unused in the post-edit file must still flag, got: {issues}"
330
+ )
331
+
332
+
333
+ def test_should_only_flag_imports_in_fragment_not_full_file() -> None:
334
+ fragment = "from config.constants import FRAGMENT_IMPORT\n"
335
+ full_file = (
336
+ "from config.other import PRE_EXISTING_UNUSED\n"
337
+ "from config.constants import FRAGMENT_IMPORT\n"
338
+ "\n"
339
+ "def existing_function() -> str:\n"
340
+ " return FRAGMENT_IMPORT\n"
341
+ )
342
+ issues = check_unused_module_level_imports(
343
+ fragment, PRODUCTION_FILE_PATH, full_file_content=full_file,
344
+ )
345
+ assert issues == [], (
346
+ "Pre-existing imports outside the fragment must not be flagged on Edit; "
347
+ f"got: {issues}"
348
+ )
349
+
350
+
351
+ def test_should_skip_when_full_file_declares_dunder_all() -> None:
352
+ fragment = "from config.constants import NEW_NAME\n"
353
+ full_file = (
354
+ "from config.constants import NEW_NAME\n"
355
+ "\n"
356
+ "__all__ = ['NEW_NAME']\n"
357
+ )
358
+ issues = check_unused_module_level_imports(
359
+ fragment, PRODUCTION_FILE_PATH, full_file_content=full_file,
360
+ )
361
+ assert issues == [], (
362
+ "__all__ in the post-edit file must skip the scan even when the fragment "
363
+ f"itself does not contain __all__, got: {issues}"
364
+ )
365
+
366
+
367
+ def test_should_skip_when_full_file_uses_type_checking_gate() -> None:
368
+ fragment = "from config.constants import NEW_NAME\n"
369
+ full_file = (
370
+ "from typing import TYPE_CHECKING\n"
371
+ "from config.constants import NEW_NAME\n"
372
+ "\n"
373
+ "if TYPE_CHECKING:\n"
374
+ " from somewhere import OtherName\n"
375
+ "\n"
376
+ "def run() -> None:\n"
377
+ " return None\n"
378
+ )
379
+ issues = check_unused_module_level_imports(
380
+ fragment, PRODUCTION_FILE_PATH, full_file_content=full_file,
381
+ )
382
+ assert issues == [], (
383
+ "TYPE_CHECKING gate in the post-edit file must skip the scan, "
384
+ f"got: {issues}"
385
+ )
386
+
387
+
388
+ def test_should_fall_back_to_content_when_full_file_content_is_none() -> None:
389
+ source = (
390
+ "from config.constants import VENV_DIRECTORY_NAME\n"
391
+ "\n"
392
+ "def run() -> None:\n"
393
+ " return None\n"
394
+ )
395
+ issues = check_unused_module_level_imports(source, PRODUCTION_FILE_PATH, full_file_content=None)
396
+ assert any("VENV_DIRECTORY_NAME" in each_issue for each_issue in issues), (
397
+ f"Backward-compat: with full_file_content=None, behavior must match "
398
+ f"the existing single-argument scan, got: {issues}"
399
+ )
400
+
401
+
402
+ def test_should_fall_back_when_full_file_content_has_syntax_error() -> None:
403
+ fragment = "from config.constants import NEW_NAME\n"
404
+ full_file_with_syntax_error = "from config import (\n not python\n"
405
+ issues = check_unused_module_level_imports(
406
+ fragment, PRODUCTION_FILE_PATH, full_file_content=full_file_with_syntax_error,
407
+ )
408
+ assert issues == [], (
409
+ "When the reconstructed post-edit content cannot be parsed, return empty "
410
+ f"rather than raising, got: {issues}"
411
+ )
412
+
413
+
414
+ def test_reconstruct_post_edit_returns_replaced_content(tmp_path: pathlib.Path) -> None:
415
+ target_file = tmp_path / "module.py"
416
+ target_file.write_text("ALPHA = 1\nBETA = 2\n", encoding="utf-8")
417
+ post_edit = _reconstruct_post_edit_file_content(
418
+ str(target_file), "BETA = 2", "BETA = 22\nGAMMA = 3",
419
+ )
420
+ assert post_edit == "ALPHA = 1\nBETA = 22\nGAMMA = 3\n", (
421
+ f"Helper must return the file body with the first occurrence replaced, got: {post_edit!r}"
422
+ )
423
+
424
+
425
+ def test_reconstruct_post_edit_returns_none_when_file_missing(tmp_path: pathlib.Path) -> None:
426
+ missing_file = tmp_path / "does_not_exist.py"
427
+ post_edit = _reconstruct_post_edit_file_content(
428
+ str(missing_file), "any_old", "any_new",
429
+ )
430
+ assert post_edit is None, (
431
+ f"Missing file must yield None so the caller treats it as 'no full-file context', got: {post_edit!r}"
432
+ )
433
+
434
+
435
+ def test_reconstruct_post_edit_returns_none_when_old_string_absent(tmp_path: pathlib.Path) -> None:
436
+ target_file = tmp_path / "module.py"
437
+ target_file.write_text("ALPHA = 1\n", encoding="utf-8")
438
+ post_edit = _reconstruct_post_edit_file_content(
439
+ str(target_file), "ZETA = 9", "OMEGA = 0",
440
+ )
441
+ assert post_edit is None, (
442
+ f"Absent old_string means the Edit will not apply cleanly — return None, got: {post_edit!r}"
443
+ )
444
+
445
+
446
+ def test_reconstruct_post_edit_replaces_only_first_occurrence(tmp_path: pathlib.Path) -> None:
447
+ target_file = tmp_path / "module.py"
448
+ target_file.write_text("X = 1\nX = 1\n", encoding="utf-8")
449
+ post_edit = _reconstruct_post_edit_file_content(
450
+ str(target_file), "X = 1", "X = 2",
451
+ )
452
+ assert post_edit == "X = 2\nX = 1\n", (
453
+ "Edit replaces only the first occurrence; helper must mirror that, got: "
454
+ f"{post_edit!r}"
455
+ )
456
+
457
+
300
458
  def test_should_flag_import_when_only_shadowed_local_name_is_loaded() -> None:
301
459
  source = (
302
460
  "import json\n"
@@ -97,7 +97,7 @@ def test_hedging_reason_contains_not_installed_notice_when_skill_absent():
97
97
 
98
98
  assert parsed_response["decision"] == "block"
99
99
  assert "no research-mode skill installed" in parsed_response["reason"]
100
- assert "verify with sources or reply" in parsed_response["reason"]
100
+ assert "verify with sources or prompt the user via AskUserQuestion" in parsed_response["reason"]
101
101
  assert "SKILL.md" not in parsed_response["reason"]
102
102
  assert RESEARCH_MODE_SKILL_BODY_MARKER not in parsed_response["reason"]
103
103