claude-dev-env 2.27.0 → 2.28.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 (109) hide show
  1. package/.agents/skills/hitl/SKILL.md +45 -0
  2. package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
  3. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +16 -29
  4. package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
  5. package/bin/ever-shipped-skills.mjs +1 -0
  6. package/docs/CODE_RULES.md +3 -3
  7. package/docs/agent-spawn-protocol.md +1 -1
  8. package/docs/references/prose-style-enforcement.md +5 -9
  9. package/hooks/blocking/AGENTS.md +3 -13
  10. package/hooks/blocking/code_rules_docstrings.py +10 -2138
  11. package/hooks/blocking/code_rules_enforcer.py +0 -121
  12. package/hooks/blocking/code_rules_imports_logging.py +1 -236
  13. package/hooks/blocking/code_rules_shared.py +23 -0
  14. package/hooks/blocking/code_rules_test_layout.py +8 -8
  15. package/hooks/blocking/config/prose_style_enforcement_constants.py +4 -2
  16. package/hooks/blocking/config/test_prose_style_enforcement_constants.py +5 -1
  17. package/hooks/blocking/precommit_code_rules_gate.py +8 -43
  18. package/hooks/blocking/state_description_blocker.py +1 -7
  19. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
  20. package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +0 -8
  21. package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +11 -112
  22. package/hooks/blocking/test_code_rules_enforcer_narrow_edit.py +0 -1
  23. package/hooks/blocking/test_code_rules_enforcer_split_entry_1.py +1 -18
  24. package/hooks/blocking/test_code_rules_shared.py +12 -0
  25. package/hooks/blocking/test_precommit_code_rules_gate.py +32 -179
  26. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -1
  27. package/hooks/blocking/test_state_description_blocker.py +6 -4
  28. package/hooks/blocking/test_stop_dispatcher.py +5 -29
  29. package/hooks/hooks_constants/AGENTS.md +1 -6
  30. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -1
  31. package/hooks/hooks_constants/code_rules_enforcer_constants.py +3 -0
  32. package/hooks/hooks_constants/messages.py +0 -2
  33. package/hooks/hooks_constants/precommit_code_rules_gate_constants.py +3 -17
  34. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -2
  35. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -1
  36. package/hooks/hooks_constants/test_code_rules_enforcer_constants.py +7 -0
  37. package/hooks/hooks_constants/test_messages.py +5 -3
  38. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -2
  39. package/package.json +1 -1
  40. package/rules/AGENTS.md +3 -6
  41. package/rules/claims-as-quotes.md +0 -10
  42. package/rules/code-standards.md +6 -6
  43. package/rules/explore-thoroughly.md +0 -1
  44. package/rules/failure-blast-radius.md +0 -8
  45. package/rules/falsify-before-green.md +0 -8
  46. package/rules/file-global-constants.md +2 -2
  47. package/rules/filesystem-search.md +1 -1
  48. package/rules/git-workflow.md +1 -9
  49. package/rules/hedging-claims.md +2 -6
  50. package/rules/long-horizon-autonomy.md +1 -1
  51. package/rules/measurement-denominators.md +0 -9
  52. package/rules/research-mode.md +0 -6
  53. package/rules/verify-before-asking.md +0 -5
  54. package/rules/verify-runtime-state.md +0 -5
  55. package/scripts/codex_compat_materializer.py +99 -24
  56. package/scripts/sync_to_cursor/rules.py +0 -10
  57. package/scripts/tests/test_codex_compat_materializer.py +70 -19
  58. package/scripts/tests/test_engine.py +0 -1
  59. package/scripts/tests/test_rules.py +0 -1
  60. package/scripts/tests/test_sync_to_cursor.py +0 -1
  61. package/system-prompts/software-engineer.xml +3 -3
  62. package/hooks/blocking/code_rules_dead_argparse_argument.py +0 -554
  63. package/hooks/blocking/code_rules_dead_config_field.py +0 -568
  64. package/hooks/blocking/code_rules_dead_dataclass_field.py +0 -348
  65. package/hooks/blocking/code_rules_dead_module_constant.py +0 -757
  66. package/hooks/blocking/code_rules_dead_split_branch.py +0 -225
  67. package/hooks/blocking/code_rules_mock_completeness.py +0 -295
  68. package/hooks/blocking/code_rules_scope_binding.py +0 -151
  69. package/hooks/blocking/code_rules_unused_imports.py +0 -197
  70. package/hooks/blocking/hedging_language_blocker.py +0 -221
  71. package/hooks/blocking/intent_only_ending_blocker.py +0 -148
  72. package/hooks/blocking/test_code_rules_enforcer_dead_argparse_argument.py +0 -534
  73. package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +0 -846
  74. package/hooks/blocking/test_code_rules_enforcer_dead_dataclass_field.py +0 -507
  75. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +0 -679
  76. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_alias.py +0 -133
  77. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant_read_cap.py +0 -103
  78. package/hooks/blocking/test_code_rules_enforcer_dead_split_branch.py +0 -105
  79. package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +0 -425
  80. package/hooks/blocking/test_code_rules_enforcer_docstring_cardinal_family.py +0 -176
  81. package/hooks/blocking/test_code_rules_enforcer_docstring_delegation_summary.py +0 -385
  82. package/hooks/blocking/test_code_rules_enforcer_docstring_fallback_branch.py +0 -398
  83. package/hooks/blocking/test_code_rules_enforcer_docstring_field_runmode_outcome.py +0 -129
  84. package/hooks/blocking/test_code_rules_enforcer_docstring_inline_literal_claim.py +0 -93
  85. package/hooks/blocking/test_code_rules_enforcer_docstring_length_constant_superlative.py +0 -198
  86. package/hooks/blocking/test_code_rules_enforcer_docstring_mark_glyph_enumeration.py +0 -262
  87. package/hooks/blocking/test_code_rules_enforcer_docstring_no_consumer.py +0 -93
  88. package/hooks/blocking/test_code_rules_enforcer_docstring_no_network.py +0 -115
  89. package/hooks/blocking/test_code_rules_enforcer_docstring_raises_largezipfile.py +0 -226
  90. package/hooks/blocking/test_code_rules_enforcer_docstring_returns_plural_cardinality.py +0 -207
  91. package/hooks/blocking/test_code_rules_enforcer_docstring_step_dispatch.py +0 -262
  92. package/hooks/blocking/test_code_rules_enforcer_docstring_type_checking_gate.py +0 -164
  93. package/hooks/blocking/test_code_rules_enforcer_docstring_unguarded_payload.py +0 -188
  94. package/hooks/blocking/test_code_rules_enforcer_import_block_sort.py +0 -157
  95. package/hooks/blocking/test_code_rules_enforcer_split_mocks_1.py +0 -303
  96. package/hooks/blocking/test_code_rules_enforcer_split_mocks_2.py +0 -111
  97. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +0 -656
  98. package/hooks/blocking/test_hedging_language_blocker.py +0 -261
  99. package/hooks/blocking/test_intent_only_ending_blocker.py +0 -209
  100. package/hooks/hooks_constants/dead_argparse_argument_constants.py +0 -28
  101. package/hooks/hooks_constants/dead_config_field_constants.py +0 -39
  102. package/hooks/hooks_constants/dead_dataclass_field_constants.py +0 -25
  103. package/hooks/hooks_constants/dead_module_constant_constants.py +0 -30
  104. package/hooks/hooks_constants/hedging_uncertainty_constants.py +0 -42
  105. package/hooks/hooks_constants/test_dispatcher_constants_docstrings.py +0 -44
  106. package/rules/conservative-action.md +0 -17
  107. package/rules/context7.md +0 -8
  108. package/rules/few-words.md +0 -3
  109. package/rules/parallel-tools.md +0 -23
@@ -1,133 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import importlib.util
4
- import os
5
- import shutil
6
- import stat
7
- import tempfile
8
- from collections.abc import Callable, Iterator
9
- from pathlib import Path
10
-
11
- import pytest
12
-
13
- ENFORCER_PATH = Path(__file__).resolve().parent / "code_rules_enforcer.py"
14
- specification = importlib.util.spec_from_file_location("code_rules_enforcer", ENFORCER_PATH)
15
- assert specification is not None and specification.loader is not None
16
- code_rules_enforcer = importlib.util.module_from_spec(specification)
17
- specification.loader.exec_module(code_rules_enforcer)
18
-
19
- CONSTANTS_BODY = 'MEDIUM_TERMINAL = "terminal"\nMEDIUM_CODE = "code"\nMEDIUM_TEXT = "text"\n'
20
-
21
-
22
- def _strip_read_only_and_retry(
23
- removal_function: Callable[[str], object],
24
- target_path: str,
25
- _exc_info: BaseException,
26
- ) -> None:
27
- try:
28
- os.chmod(target_path, stat.S_IWRITE)
29
- removal_function(target_path)
30
- except OSError:
31
- pass
32
-
33
-
34
- @pytest.fixture
35
- def neutral_root() -> Iterator[Path]:
36
- """Yield a temp directory whose path carries no ``test_`` segment.
37
-
38
- ::
39
-
40
- tmp_path (pytest default) -> embeds "test_..." in its name
41
- -> is_test_file() misreads the path
42
- mkdtemp(prefix="deadconst-") -> no "test_" segment -> reads as
43
- a production path ok
44
-
45
- A ``.git`` marker is planted at the root so the cross-tree widening
46
- resolves the repository root to this synthetic tree.
47
- """
48
- neutral_directory = Path(tempfile.mkdtemp(prefix="deadconst-")).resolve()
49
- (neutral_directory / ".git").mkdir()
50
- try:
51
- yield neutral_directory
52
- finally:
53
- shutil.rmtree(neutral_directory, onexc=_strip_read_only_and_retry)
54
-
55
-
56
- def _check(source: str, file_path: str) -> list[str]:
57
- return code_rules_enforcer.check_dead_module_constants(source, file_path)
58
-
59
-
60
- def _build_constants_package(
61
- workflow_directory: Path,
62
- constants_body: str,
63
- consumer_body: str,
64
- ) -> Path:
65
- constants_package = workflow_directory / "report_constants"
66
- constants_package.mkdir(parents=True)
67
- (constants_package / "__init__.py").write_text("", encoding="utf-8")
68
- constants_path = constants_package / "render_report_constants.py"
69
- constants_path.write_text(constants_body, encoding="utf-8")
70
- (workflow_directory / "render_report.py").write_text(consumer_body, encoding="utf-8")
71
- return constants_path
72
-
73
-
74
- def _assert_alias_read_constants_live(issues: list[str]) -> None:
75
- assert not any(
76
- "MEDIUM_TERMINAL" in each_issue or "MEDIUM_CODE" in each_issue for each_issue in issues
77
- ), f"A constant read as attribute of an imported module stays live, got: {issues}"
78
- assert any("MEDIUM_TEXT" in each_issue for each_issue in issues), (
79
- f"A constant read by no module must still be flagged, got: {issues}"
80
- )
81
-
82
-
83
- def test_does_not_flag_constant_read_via_from_import_module_alias_attribute(
84
- neutral_root: Path,
85
- ) -> None:
86
- consumer_body = (
87
- "from report_constants import render_report_constants as constants\n"
88
- "\n"
89
- "def panel_class(medium: str) -> str:\n"
90
- " if medium == constants.MEDIUM_TERMINAL:\n"
91
- " return 'terminal'\n"
92
- " return 'code' if medium == constants.MEDIUM_CODE else 'other'\n"
93
- )
94
- constants_path = _build_constants_package(
95
- neutral_root / "workflow", CONSTANTS_BODY, consumer_body
96
- )
97
- _assert_alias_read_constants_live(_check(CONSTANTS_BODY, str(constants_path)))
98
-
99
-
100
- def test_does_not_flag_constant_read_via_import_as_alias_attribute(
101
- neutral_root: Path,
102
- ) -> None:
103
- consumer_body = (
104
- "import report_constants.render_report_constants as constants\n"
105
- "\n"
106
- "def label(medium: str) -> str:\n"
107
- " if medium == constants.MEDIUM_TERMINAL:\n"
108
- " return 'terminal'\n"
109
- " return 'code' if medium == constants.MEDIUM_CODE else 'other'\n"
110
- )
111
- constants_path = _build_constants_package(
112
- neutral_root / "workflow", CONSTANTS_BODY, consumer_body
113
- )
114
- _assert_alias_read_constants_live(_check(CONSTANTS_BODY, str(constants_path)))
115
-
116
-
117
- def test_does_not_flag_constant_read_via_plain_module_name_attribute(
118
- neutral_root: Path,
119
- ) -> None:
120
- consumer_body = (
121
- "from report_constants import render_report_constants\n"
122
- "\n"
123
- "def label(medium: str) -> str:\n"
124
- " if medium == render_report_constants.MEDIUM_TERMINAL:\n"
125
- " return 'terminal'\n"
126
- " if medium == render_report_constants.MEDIUM_CODE:\n"
127
- " return 'code'\n"
128
- " return 'other'\n"
129
- )
130
- constants_path = _build_constants_package(
131
- neutral_root / "workflow", CONSTANTS_BODY, consumer_body
132
- )
133
- _assert_alias_read_constants_live(_check(CONSTANTS_BODY, str(constants_path)))
@@ -1,103 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import importlib.util
4
- import os
5
- import shutil
6
- import stat
7
- import tempfile
8
- from collections.abc import Callable, Iterator
9
- from pathlib import Path
10
-
11
- import pytest
12
-
13
- ENFORCER_PATH = Path(__file__).resolve().parent / "code_rules_enforcer.py"
14
- specification = importlib.util.spec_from_file_location("code_rules_enforcer", ENFORCER_PATH)
15
- assert specification is not None and specification.loader is not None
16
- code_rules_enforcer = importlib.util.module_from_spec(specification)
17
- specification.loader.exec_module(code_rules_enforcer)
18
-
19
-
20
- def _strip_read_only_and_retry(
21
- removal_function: Callable[[str], object],
22
- target_path: str,
23
- _exc_info: BaseException,
24
- ) -> None:
25
- try:
26
- os.chmod(target_path, stat.S_IWRITE)
27
- removal_function(target_path)
28
- except OSError:
29
- pass
30
-
31
-
32
- @pytest.fixture
33
- def neutral_root() -> Iterator[Path]:
34
- """Yield a temp directory whose path carries no ``test_`` segment.
35
-
36
- ::
37
-
38
- tmp_path (pytest default) -> embeds "test_..." in its name
39
- -> is_test_file() misreads every
40
- synthetic constants path
41
- mkdtemp(prefix="deadconst-") -> no "test_" segment
42
- -> reads as a production path ok
43
-
44
- A ``.git`` marker is planted at the root so the cross-tree widening
45
- resolves the repository root to this synthetic tree.
46
- """
47
- neutral_directory = Path(tempfile.mkdtemp(prefix="deadconst-")).resolve()
48
- (neutral_directory / ".git").mkdir()
49
- try:
50
- yield neutral_directory
51
- finally:
52
- shutil.rmtree(neutral_directory, onexc=_strip_read_only_and_retry)
53
-
54
-
55
- def _check(source: str, file_path: str) -> list[str]:
56
- return code_rules_enforcer.check_dead_module_constants(source, file_path)
57
-
58
-
59
- def _write_noise_modules(directory: Path, count: int) -> None:
60
- directory.mkdir(parents=True)
61
- for each_index in range(count):
62
- (directory / f"mod_{each_index}.py").write_text(
63
- "def noop() -> int:\n return 1\n", encoding="utf-8"
64
- )
65
-
66
-
67
- def test_widened_scan_bounds_total_disk_reads_on_noncandidate_flood(
68
- neutral_root: Path, monkeypatch: pytest.MonkeyPatch
69
- ) -> None:
70
- """A read-attempt cap stops the widened pass from reading every sibling.
71
-
72
- ::
73
-
74
- MAX_SCAN_ROOT_READ_COUNT = 3, 50 non-candidate noise files
75
- ok: read_text() called a bounded number of times (< 50)
76
- flag: read_text() called once per noise file (== 50)
77
-
78
- Without the cap, ``_read_candidate_source`` opens and reads every
79
- repository ``.py`` file's full text before testing it for the module
80
- stem, so disk-read cost scales with repository size regardless of how
81
- quickly the file cap would otherwise stop the parse-and-collect work.
82
- """
83
- monkeypatch.setattr("code_rules_dead_module_constant.MAX_SCAN_ROOT_READ_COUNT", 3)
84
- package_directory = neutral_root / "pkg"
85
- package_directory.mkdir(parents=True)
86
- constants_body = 'DEAD_EXPORTED = "x"\n__all__ = ["DEAD_EXPORTED"]\n'
87
- constants_path = package_directory / "settings_constants.py"
88
- constants_path.write_text(constants_body, encoding="utf-8")
89
- _write_noise_modules(neutral_root / "noise", 50)
90
- read_count = 0
91
- original_read_text = Path.read_text
92
-
93
- def counting_read_text(self: Path, *positional: object, **keyword: object) -> str:
94
- nonlocal read_count
95
- read_count += 1
96
- return original_read_text(self, *positional, **keyword) # type: ignore[arg-type] # forwards positional/keyword args mypy cannot resolve
97
-
98
- monkeypatch.setattr(Path, "read_text", counting_read_text)
99
- _check(constants_body, str(constants_path))
100
- assert read_count < 50, (
101
- "A read-count cap must stop the widened pass from reading every "
102
- f"non-candidate sibling's full text, got {read_count} reads across 50 noise files"
103
- )
@@ -1,105 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import importlib.util
4
- from pathlib import Path
5
-
6
- ENFORCER_PATH = Path(__file__).resolve().parent / "code_rules_enforcer.py"
7
- specification = importlib.util.spec_from_file_location("code_rules_enforcer", ENFORCER_PATH)
8
- assert specification is not None and specification.loader is not None
9
- code_rules_enforcer = importlib.util.module_from_spec(specification)
10
- specification.loader.exec_module(code_rules_enforcer)
11
-
12
- PRODUCTION_PATH = "C:/project/pkg/menu_info_colors.py"
13
-
14
-
15
- def _check(source: str) -> list[str]:
16
- return code_rules_enforcer.check_dead_split_truthiness_branch(source, PRODUCTION_PATH)
17
-
18
-
19
- def test_flags_split_truthiness_else_arm() -> None:
20
- source = (
21
- "def _extract_uid_prefix(uid: str) -> str:\n"
22
- ' all_parts = uid.split("_")\n'
23
- " if len(all_parts) > 2:\n"
24
- ' return "_".join(all_parts[:3])\n'
25
- " return all_parts[0] if all_parts else uid\n"
26
- )
27
- issues = _check(source)
28
- assert len(issues) == 1
29
- assert "all_parts" in issues[0]
30
- assert "Line 5" in issues[0]
31
-
32
-
33
- def test_flags_negated_guard_dead_body() -> None:
34
- source = (
35
- "def first_segment(path: str) -> str:\n"
36
- ' all_segments = path.split("/")\n'
37
- " if not all_segments:\n"
38
- " return path\n"
39
- " return all_segments[0]\n"
40
- )
41
- issues = _check(source)
42
- assert len(issues) == 1
43
- assert "all_segments" in issues[0]
44
-
45
-
46
- def test_does_not_flag_split_without_separator() -> None:
47
- source = (
48
- "def first_token(text: str) -> str:\n"
49
- " all_parts = text.split()\n"
50
- " return all_parts[0] if all_parts else text\n"
51
- )
52
- assert _check(source) == []
53
-
54
-
55
- def test_does_not_flag_length_comparison_guard() -> None:
56
- source = (
57
- "def prefix(uid: str) -> str:\n"
58
- ' all_parts = uid.split("_")\n'
59
- " if len(all_parts) > 1:\n"
60
- " return all_parts[0]\n"
61
- " return uid\n"
62
- )
63
- assert _check(source) == []
64
-
65
-
66
- def test_does_not_flag_when_name_reassigned() -> None:
67
- source = (
68
- "def normalize(uid: str) -> str:\n"
69
- ' all_parts = uid.split("_")\n'
70
- " all_parts = [each_part.strip() for each_part in all_parts]\n"
71
- " return all_parts[0] if all_parts else uid\n"
72
- )
73
- assert _check(source) == []
74
-
75
-
76
- def test_does_not_flag_when_name_is_parameter() -> None:
77
- source = (
78
- "def shape(all_parts: list[str]) -> str:\n"
79
- ' all_parts = "x".split("_")\n'
80
- ' return all_parts[0] if all_parts else ""\n'
81
- )
82
- assert _check(source) == []
83
-
84
-
85
- def test_does_not_flag_attribute_receiver_split() -> None:
86
- source = (
87
- "def first(s: object) -> object:\n"
88
- ' parts = s.str.split(",")\n'
89
- " return parts[0] if parts else 0\n"
90
- )
91
- assert _check(source) == []
92
-
93
-
94
- def test_does_not_flag_in_test_file() -> None:
95
- source = (
96
- "def helper(uid: str) -> str:\n"
97
- ' all_parts = uid.split("_")\n'
98
- " return all_parts[0] if all_parts else uid\n"
99
- )
100
- assert (
101
- code_rules_enforcer.check_dead_split_truthiness_branch(
102
- source, "C:/project/pkg/test_thing.py"
103
- )
104
- == []
105
- )