claude-dev-env 1.49.1 → 1.50.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.
@@ -252,6 +252,44 @@ def test_blocks_changelog_not_at_root():
252
252
  assert output["hookSpecificOutput"]["permissionDecision"] == "deny"
253
253
 
254
254
 
255
+ def test_passes_claude_md_at_root():
256
+ result = _run_hook(
257
+ "Write",
258
+ {"file_path": "CLAUDE.md", "content": "# CLAUDE"},
259
+ )
260
+ assert result.returncode == 0
261
+ assert result.stdout == ""
262
+
263
+
264
+ def test_passes_agents_md_at_root():
265
+ result = _run_hook(
266
+ "Write",
267
+ {"file_path": "AGENTS.md", "content": "# AGENTS"},
268
+ )
269
+ assert result.returncode == 0
270
+ assert result.stdout == ""
271
+
272
+
273
+ def test_blocks_claude_md_not_at_root():
274
+ result = _run_hook(
275
+ "Write",
276
+ {"file_path": "docs/CLAUDE.md", "content": "# CLAUDE"},
277
+ )
278
+ assert result.returncode == 0
279
+ output = json.loads(result.stdout)
280
+ assert output["hookSpecificOutput"]["permissionDecision"] == "deny"
281
+
282
+
283
+ def test_blocks_agents_md_not_at_root():
284
+ result = _run_hook(
285
+ "Write",
286
+ {"file_path": "sub/AGENTS.md", "content": "# AGENTS"},
287
+ )
288
+ assert result.returncode == 0
289
+ output = json.loads(result.stdout)
290
+ assert output["hookSpecificOutput"]["permissionDecision"] == "deny"
291
+
292
+
255
293
  def test_blocks_relative_readme_when_cwd_is_not_repo_root():
256
294
  sandbox_parent = _get_sandbox_parent_directory()
257
295
  non_repo_cwd = os.path.join(sandbox_parent, "not-a-repo")
@@ -16,6 +16,8 @@ MAX_BARE_EXCEPT_ISSUES: int = 3
16
16
  MAX_BOUNDARY_TYPE_ISSUES: int = 5
17
17
  ALL_BANNED_PREFIX_NAMES: tuple[str, ...] = ("handle_", "process_", "manage_", "do_")
18
18
  MAX_DOCSTRING_FORMAT_ISSUES: int = 5
19
+ MAX_DOCSTRING_ARGS_SIGNATURE_ISSUES: int = 5
20
+ MAX_IGNORED_MUST_CHECK_RETURN_ISSUES: int = 5
19
21
  MAX_TYPE_ESCAPE_HATCH_ISSUES: int = 5
20
22
  MAX_THIN_WRAPPER_ISSUES: int = 1
21
23
  DOCSTRING_TRIVIAL_FUNCTION_BODY_LINE_LIMIT: int = 3
@@ -24,9 +24,23 @@ ALL_MIGRATION_PATH_PATTERNS = {"/migrations/", "\\migrations\\"}
24
24
  ADVISORY_LINE_THRESHOLD_SOFT = 400
25
25
  ADVISORY_LINE_THRESHOLD_HARD = 1000
26
26
 
27
- ALL_BOOLEAN_NAME_PREFIXES: tuple[str, ...] = ("is_", "has_", "should_", "can_")
27
+ ALL_BOOLEAN_NAME_PREFIXES: tuple[str, ...] = ("is_", "has_", "should_", "can_", "was_", "did_")
28
28
  UPPER_SNAKE_CONSTANT_PATTERN = re.compile(r"^[A-Z][A-Z0-9_]*$")
29
29
 
30
+ ALL_MUST_CHECK_RETURN_FUNCTION_NAMES: frozenset[str] = frozenset({"find_and_click", "write_outcome"})
31
+
32
+ DOCSTRING_ARG_ENTRY_PATTERN: re.Pattern[str] = re.compile(r"^([A-Za-z_][A-Za-z0-9_]*)\s*[:(]")
33
+ ALL_DOCSTRING_ARGS_SECTION_HEADERS: tuple[str, ...] = ("Args:", "Arguments:")
34
+ ALL_DOCSTRING_TERMINATING_SECTION_HEADERS: frozenset[str] = frozenset({
35
+ "Returns:",
36
+ "Yields:",
37
+ "Raises:",
38
+ "Examples:",
39
+ "Example:",
40
+ "Note:",
41
+ "Notes:",
42
+ })
43
+
30
44
 
31
45
  TYPE_CHECKING_BLOCK_PATTERN = re.compile(r"^(?P<indent>\s*)if\s+(typing\.)?TYPE_CHECKING\s*:\s*$")
32
46
  ALL_IMPORT_STATEMENT_PREFIXES: tuple[str, ...] = ("import ", "from ")
@@ -19,7 +19,7 @@ MINIMUM_SEGMENT_COUNT_TO_MATCH_INDICATOR: int = 4
19
19
  ALL_EXEMPT_ANYWHERE_FILENAMES: tuple[str, ...] = ("SKILL.md",)
20
20
  ALL_EXEMPT_PLUGIN_DIRECTORY_SEGMENTS: tuple[str, ...] = ("agents", "skills", "commands")
21
21
  ALL_EXEMPT_HOME_RELATIVE_DIRECTORIES: tuple[str, ...] = ("SessionLog",)
22
- ALL_EXEMPT_ROOT_FILENAMES: tuple[str, ...] = ("readme.md", "changelog.md")
22
+ ALL_EXEMPT_ROOT_FILENAMES: tuple[str, ...] = ("readme.md", "changelog.md", "claude.md", "agents.md")
23
23
  REPO_ROOT_MARKER_NAME: str = ".git"
24
24
  CLAUDE_DIRECTORY_NAME: str = ".claude"
25
25
  PLUGIN_ROOT_MARKER_DIRECTORY_NAME: str = ".claude-plugin"
@@ -82,10 +82,17 @@ def test_exempt_home_relative_directories_include_session_log() -> None:
82
82
  assert "ALL_EXEMPT_HOME_RELATIVE_DIRECTORIES" in constants_module.__all__
83
83
 
84
84
 
85
- def test_exempt_root_filenames_cover_readme_and_changelog() -> None:
86
- """README.md and CHANGELOG.md at a repo root are universally exempt;
87
- every repo with a `.git` marker satisfies the root check."""
88
- assert constants_module.ALL_EXEMPT_ROOT_FILENAMES == ("readme.md", "changelog.md")
85
+ def test_exempt_root_filenames_cover_readme_changelog_claude_and_agents() -> None:
86
+ """README.md, CHANGELOG.md, CLAUDE.md, and AGENTS.md at a repo root are
87
+ universally exempt; every repo with a `.git` marker satisfies the root
88
+ check. CLAUDE.md and AGENTS.md are functional agent-instruction files that
89
+ Claude Code loads by name and must stay Markdown."""
90
+ assert constants_module.ALL_EXEMPT_ROOT_FILENAMES == (
91
+ "readme.md",
92
+ "changelog.md",
93
+ "claude.md",
94
+ "agents.md",
95
+ )
89
96
  assert "ALL_EXEMPT_ROOT_FILENAMES" in constants_module.__all__
90
97
 
91
98
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "1.49.1",
3
+ "version": "1.50.0",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {