claude-dev-env 1.50.1 → 1.50.2
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/hooks/blocking/code_rules_annotations_length.py +167 -0
- package/hooks/blocking/code_rules_banned_identifiers.py +385 -0
- package/hooks/blocking/code_rules_boolean_mustcheck.py +350 -0
- package/hooks/blocking/code_rules_comments.py +337 -0
- package/hooks/blocking/code_rules_constants_config.py +252 -0
- package/hooks/blocking/code_rules_docstrings.py +308 -0
- package/hooks/blocking/code_rules_enforcer.py +98 -5807
- package/hooks/blocking/code_rules_imports_logging.py +276 -0
- package/hooks/blocking/code_rules_magic_values.py +180 -0
- package/hooks/blocking/code_rules_mock_completeness.py +295 -0
- package/hooks/blocking/code_rules_naming_collection.py +264 -0
- package/hooks/blocking/code_rules_optional_params.py +288 -0
- package/hooks/blocking/code_rules_paths_syspath.py +186 -0
- package/hooks/blocking/code_rules_probe_chains.py +305 -0
- package/hooks/blocking/code_rules_probe_detection.py +257 -0
- package/hooks/blocking/code_rules_probe_recording.py +225 -0
- package/hooks/blocking/code_rules_scope_binding.py +151 -0
- package/hooks/blocking/code_rules_shared.py +301 -0
- package/hooks/blocking/code_rules_string_magic.py +207 -0
- package/hooks/blocking/code_rules_test_assertions.py +226 -0
- package/hooks/blocking/code_rules_test_branching_except.py +181 -0
- package/hooks/blocking/code_rules_test_isolation.py +341 -0
- package/hooks/blocking/code_rules_type_escape.py +341 -0
- package/hooks/blocking/code_rules_typeddict_stub.py +305 -0
- package/hooks/blocking/code_rules_unused_imports.py +256 -0
- package/hooks/blocking/tdd_enforcer.py +31 -0
- package/hooks/blocking/test_code_rules_constants_config.py +26 -0
- package/hooks/blocking/test_code_rules_enforcer_banned_noun_word.py +5 -2
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -5
- package/hooks/blocking/test_code_rules_enforcer_comment_string_awareness.py +21 -15
- package/hooks/blocking/test_code_rules_enforcer_config_path.py +20 -16
- package/hooks/blocking/test_code_rules_enforcer_exempt_marker_chained.py +4 -2
- package/hooks/blocking/test_code_rules_enforcer_function_length.py +18 -13
- package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +1 -2
- package/hooks/blocking/test_code_rules_enforcer_ignored_must_check_return.py +22 -12
- package/hooks/blocking/test_code_rules_enforcer_split_annotations_length.py +55 -0
- package/hooks/blocking/test_code_rules_enforcer_split_banned.py +170 -0
- package/hooks/blocking/test_code_rules_enforcer_split_comments.py +60 -0
- package/hooks/blocking/test_code_rules_enforcer_split_config_path.py +52 -0
- package/hooks/blocking/test_code_rules_enforcer_split_constants_config.py +236 -0
- package/hooks/blocking/test_code_rules_enforcer_split_entry_1.py +296 -0
- package/hooks/blocking/test_code_rules_enforcer_split_entry_2.py +238 -0
- package/hooks/blocking/test_code_rules_enforcer_split_isolation_1.py +271 -0
- package/hooks/blocking/test_code_rules_enforcer_split_isolation_2.py +283 -0
- package/hooks/blocking/test_code_rules_enforcer_split_isolation_3.py +268 -0
- package/hooks/blocking/test_code_rules_enforcer_split_isolation_4.py +85 -0
- package/hooks/blocking/test_code_rules_enforcer_split_mocks_1.py +303 -0
- package/hooks/blocking/test_code_rules_enforcer_split_mocks_2.py +111 -0
- package/hooks/blocking/test_code_rules_enforcer_split_mustcheck.py +87 -0
- package/hooks/blocking/test_code_rules_enforcer_split_naming.py +107 -0
- package/hooks/blocking/test_code_rules_enforcer_split_optional_params.py +325 -0
- package/hooks/blocking/test_code_rules_enforcer_split_paths_syspath.py +110 -0
- package/hooks/blocking/test_code_rules_enforcer_split_shared.py +44 -0
- package/hooks/blocking/test_code_rules_enforcer_split_string_magic.py +55 -0
- package/hooks/blocking/test_code_rules_enforcer_split_test_assertions.py +56 -0
- package/hooks/blocking/test_code_rules_enforcer_todo_markers.py +21 -15
- package/hooks/blocking/test_code_rules_paths_syspath.py +26 -0
- package/hooks/blocking/test_tdd_enforcer.py +116 -0
- package/hooks/hooks_constants/blocking_check_limits.py +3 -0
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +8 -0
- package/hooks/hooks_constants/sys_path_insert_constants.py +1 -0
- package/package.json +1 -1
- package/hooks/blocking/test_code_rules_enforcer.py +0 -2669
|
@@ -574,3 +574,119 @@ def test_is_inside_dotclaude_segment_helper_matches_only_exact_segments() -> Non
|
|
|
574
574
|
assert _PRODUCTION_MODULE._is_inside_dotclaude_segment("C:\\Users\\dev\\.claude\\agent.py") is True
|
|
575
575
|
assert _PRODUCTION_MODULE._is_inside_dotclaude_segment("/src/my.claude.helpers.py") is False
|
|
576
576
|
assert _PRODUCTION_MODULE._is_inside_dotclaude_segment("/src/app/service.py") is False
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def test_should_offer_split_family_test_files_as_candidates_for_code_rules_module(
|
|
580
|
+
tmp_path: Path,
|
|
581
|
+
) -> None:
|
|
582
|
+
sandbox = _sandbox(tmp_path)
|
|
583
|
+
production_module = sandbox / "code_rules_magic_values.py"
|
|
584
|
+
string_magic_family_test = sandbox / "test_code_rules_enforcer_split_string_magic.py"
|
|
585
|
+
string_magic_family_test.write_text("def test_string_magic(): pass\n")
|
|
586
|
+
banned_family_test = sandbox / "test_code_rules_enforcer_split_banned.py"
|
|
587
|
+
banned_family_test.write_text("def test_banned(): pass\n")
|
|
588
|
+
|
|
589
|
+
all_candidates = _PRODUCTION_MODULE.candidate_test_paths_for(production_module)
|
|
590
|
+
|
|
591
|
+
assert string_magic_family_test in all_candidates
|
|
592
|
+
assert banned_family_test in all_candidates
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def test_should_keep_plain_stem_candidates_first_for_code_rules_module(
|
|
596
|
+
tmp_path: Path,
|
|
597
|
+
) -> None:
|
|
598
|
+
sandbox = _sandbox(tmp_path)
|
|
599
|
+
production_module = sandbox / "code_rules_magic_values.py"
|
|
600
|
+
family_test = sandbox / "test_code_rules_enforcer_split_string_magic.py"
|
|
601
|
+
family_test.write_text("def test_string_magic(): pass\n")
|
|
602
|
+
|
|
603
|
+
all_candidates = _PRODUCTION_MODULE.candidate_test_paths_for(production_module)
|
|
604
|
+
|
|
605
|
+
assert all_candidates[0] == sandbox / "test_code_rules_magic_values.py"
|
|
606
|
+
assert all_candidates[1] == sandbox / "code_rules_magic_values_test.py"
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
def test_should_not_offer_split_family_candidates_for_non_code_rules_module(
|
|
610
|
+
tmp_path: Path,
|
|
611
|
+
) -> None:
|
|
612
|
+
sandbox = _sandbox(tmp_path)
|
|
613
|
+
production_module = sandbox / "orders.py"
|
|
614
|
+
family_test = sandbox / "test_code_rules_enforcer_split_string_magic.py"
|
|
615
|
+
family_test.write_text("def test_string_magic(): pass\n")
|
|
616
|
+
|
|
617
|
+
all_candidates = _PRODUCTION_MODULE.candidate_test_paths_for(production_module)
|
|
618
|
+
|
|
619
|
+
assert family_test not in all_candidates
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def test_should_add_no_split_family_candidates_when_directory_has_none(
|
|
623
|
+
tmp_path: Path,
|
|
624
|
+
) -> None:
|
|
625
|
+
sandbox = _sandbox(tmp_path)
|
|
626
|
+
production_module = sandbox / "code_rules_magic_values.py"
|
|
627
|
+
|
|
628
|
+
all_candidates = _PRODUCTION_MODULE.candidate_test_paths_for(production_module)
|
|
629
|
+
|
|
630
|
+
expected_stem_candidates = [
|
|
631
|
+
sandbox / "test_code_rules_magic_values.py",
|
|
632
|
+
sandbox / "code_rules_magic_values_test.py",
|
|
633
|
+
]
|
|
634
|
+
assert all_candidates == expected_stem_candidates
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def test_should_not_offer_family_candidates_for_code_ruleset_stem(
|
|
638
|
+
tmp_path: Path,
|
|
639
|
+
) -> None:
|
|
640
|
+
sandbox = _sandbox(tmp_path)
|
|
641
|
+
production_module = sandbox / "code_ruleset.py"
|
|
642
|
+
family_test = sandbox / "test_code_rules_enforcer_split_example.py"
|
|
643
|
+
family_test.write_text("def test_detects_example(): pass\n")
|
|
644
|
+
|
|
645
|
+
all_candidates = _PRODUCTION_MODULE.candidate_test_paths_for(production_module)
|
|
646
|
+
|
|
647
|
+
expected_stem_candidates = [
|
|
648
|
+
sandbox / "test_code_ruleset.py",
|
|
649
|
+
sandbox / "code_ruleset_test.py",
|
|
650
|
+
]
|
|
651
|
+
assert all_candidates == expected_stem_candidates
|
|
652
|
+
assert family_test not in all_candidates
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def test_should_allow_code_rules_edit_when_fresh_split_family_sibling_exists(
|
|
656
|
+
tmp_path: Path,
|
|
657
|
+
) -> None:
|
|
658
|
+
sandbox = _sandbox(tmp_path)
|
|
659
|
+
production_module = sandbox / "code_rules_example.py"
|
|
660
|
+
production_module.write_text("def detect() -> None:\n return None\n")
|
|
661
|
+
family_test = sandbox / "test_code_rules_enforcer_split_example_concern.py"
|
|
662
|
+
family_test.write_text("def test_detects_example(): pass\n")
|
|
663
|
+
|
|
664
|
+
payload = _make_edit_payload(
|
|
665
|
+
production_module,
|
|
666
|
+
old_string="return None",
|
|
667
|
+
new_string="return None # adjusted",
|
|
668
|
+
)
|
|
669
|
+
completed = _run_hook_with_payload(payload)
|
|
670
|
+
|
|
671
|
+
assert _decision_from(completed) == "allow"
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def test_should_deny_code_rules_edit_when_split_family_sibling_is_stale(
|
|
675
|
+
tmp_path: Path,
|
|
676
|
+
) -> None:
|
|
677
|
+
sandbox = _sandbox(tmp_path)
|
|
678
|
+
production_module = sandbox / "code_rules_example.py"
|
|
679
|
+
production_module.write_text("def detect() -> None:\n return None\n")
|
|
680
|
+
family_test = sandbox / "test_code_rules_enforcer_split_example_concern.py"
|
|
681
|
+
family_test.write_text("def test_detects_example(): pass\n")
|
|
682
|
+
stale_timestamp = time.time() - STALE_MTIME_OFFSET_SECONDS
|
|
683
|
+
os.utime(family_test, (stale_timestamp, stale_timestamp))
|
|
684
|
+
|
|
685
|
+
payload = _make_edit_payload(
|
|
686
|
+
production_module,
|
|
687
|
+
old_string="return None",
|
|
688
|
+
new_string="return None # adjusted",
|
|
689
|
+
)
|
|
690
|
+
completed = _run_hook_with_payload(payload)
|
|
691
|
+
|
|
692
|
+
assert _decision_from(completed) == "deny"
|
|
@@ -20,6 +20,9 @@ MAX_DOCSTRING_ARGS_SIGNATURE_ISSUES: int = 5
|
|
|
20
20
|
MAX_IGNORED_MUST_CHECK_RETURN_ISSUES: int = 5
|
|
21
21
|
MAX_TYPE_ESCAPE_HATCH_ISSUES: int = 5
|
|
22
22
|
MAX_THIN_WRAPPER_ISSUES: int = 1
|
|
23
|
+
MAX_LOGGING_FSTRING_ISSUES: int = 3
|
|
24
|
+
MAX_WINDOWS_API_NONE_ISSUES: int = 3
|
|
25
|
+
MAX_E2E_TEST_NAMING_ISSUES: int = 3
|
|
23
26
|
DOCSTRING_TRIVIAL_FUNCTION_BODY_LINE_LIMIT: int = 3
|
|
24
27
|
|
|
25
28
|
ALL_BARE_EXCEPT_BANNED_HANDLER_NAMES: frozenset[str] = frozenset({"Exception", "BaseException"})
|
|
@@ -79,6 +79,14 @@ NOT_INSIDE_TYPE_CHECKING_BLOCK = -1
|
|
|
79
79
|
TRIPLE_QUOTE_PARITY_DIVISOR = 2
|
|
80
80
|
TRIPLE_DOUBLE_QUOTE_DELIMITER = '"""'
|
|
81
81
|
TRIPLE_SINGLE_QUOTE_DELIMITER = "'''"
|
|
82
|
+
MAX_MAGIC_VALUE_ISSUES = 3
|
|
83
|
+
STRING_LITERAL_QUOTE_PAIR_LENGTH = 2
|
|
84
|
+
MINIMUM_FSTRING_LITERAL_LENGTH = 2
|
|
85
|
+
MAX_FSTRING_STRUCTURAL_LITERAL_ISSUES = 100
|
|
86
|
+
ALL_ALLOWED_MAGIC_NUMBER_LITERALS: frozenset[str] = frozenset({"0", "1", "-1", "0.0", "1.0"})
|
|
87
|
+
ALL_NON_MAGIC_FSTRING_STRIPPED_VALUES: frozenset[str] = frozenset({"", "True", "False"})
|
|
88
|
+
DUPLICATED_FORMAT_MINIMUM_REPETITION_COUNT = 3
|
|
89
|
+
DUPLICATED_FORMAT_MINIMUM_LITERAL_CHARACTER_COUNT = 5
|
|
82
90
|
FILE_GLOBAL_UPPER_SNAKE_PATTERN = re.compile(r"^_?[A-Z][A-Z0-9_]*$")
|
|
83
91
|
|
|
84
92
|
ALL_COLLECTION_TYPE_NAMES: frozenset[str] = frozenset({
|