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
|
@@ -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
|
|
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
|
|