claude-dev-env 1.41.0 → 1.43.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.
- package/CLAUDE.md +8 -0
- package/_shared/pr-loop/scripts/_claude_permissions_common.py +232 -8
- package/_shared/pr-loop/scripts/code_rules_gate.py +293 -8
- package/_shared/pr-loop/scripts/fix_hookspath.py +96 -5
- package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +124 -20
- package/_shared/pr-loop/scripts/post_audit_thread.py +4 -4
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +90 -0
- package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/claude_settings_keys_constants.py +2 -0
- package/_shared/pr-loop/scripts/preflight.py +13 -31
- package/_shared/pr-loop/scripts/reviews_disabled.py +2 -16
- package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +76 -33
- package/_shared/pr-loop/scripts/tests/conftest.py +1 -51
- package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +385 -0
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +37 -2
- package/_shared/pr-loop/scripts/tests/test_claude_settings_keys_constants.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath_constants.py +6 -2
- package/_shared/pr-loop/scripts/tests/test_grant_project_claude_permissions.py +2 -2
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +1 -2
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_preflight.py +17 -52
- package/_shared/pr-loop/scripts/tests/test_preflight_constants.py +6 -2
- package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +5 -3
- package/agents/pr-description-writer.md +50 -140
- package/docs/PR_DESCRIPTION_GUIDE.md +101 -102
- package/hooks/_gh_pr_author_swap_utils.py +1 -1
- package/hooks/blocking/bot_mention_comment_blocker.py +4 -10
- package/hooks/blocking/code_rules_enforcer.py +217 -99
- package/hooks/blocking/code_rules_path_utils.py +8 -1
- package/hooks/blocking/destructive_command_blocker.py +1 -1
- package/hooks/blocking/es_exe_path_rewriter.py +7 -13
- package/hooks/blocking/gh_body_arg_blocker.py +6 -1
- package/hooks/blocking/gh_pr_author_enforcer.py +5 -5
- package/hooks/blocking/gh_pr_author_restore.py +5 -5
- package/hooks/blocking/hedging_language_blocker.py +4 -10
- package/hooks/blocking/md_path_exemptions.py +205 -0
- package/hooks/blocking/md_to_html_blocker.py +48 -20
- package/hooks/blocking/pr_converge_bugteam_enforcer.py +5 -11
- package/hooks/blocking/pr_description_enforcer.py +626 -41
- package/hooks/blocking/question_to_user_enforcer.py +4 -10
- package/hooks/blocking/state_description_blocker.py +6 -12
- package/hooks/blocking/tdd_enforcer.py +1 -1
- package/hooks/blocking/test_bot_mention_comment_blocker.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer.py +3 -3
- package/hooks/blocking/test_code_rules_enforcer_any_exempt_files.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -2
- package/hooks/blocking/test_code_rules_enforcer_comment_string_awareness.py +184 -0
- package/hooks/blocking/test_code_rules_enforcer_type_checking_scope.py +82 -0
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +29 -29
- package/hooks/blocking/test_gh_body_arg_blocker.py +7 -8
- package/hooks/blocking/test_gh_pr_author_enforcer.py +1 -1
- package/hooks/blocking/test_gh_pr_author_restore.py +1 -1
- package/hooks/blocking/test_hedging_language_blocker.py +2 -2
- package/hooks/blocking/test_md_to_html_blocker.py +463 -8
- package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +1 -1
- package/hooks/blocking/test_pr_description_enforcer.py +1210 -13
- package/hooks/blocking/test_question_to_user_enforcer.py +1 -1
- package/hooks/blocking/windows_rmtree_blocker.py +5 -11
- package/hooks/diagnostic/hook_log_extractor.py +1 -1
- package/hooks/diagnostic/hook_log_init.py +1 -1
- package/hooks/diagnostic/hook_log_stop_wrapper.py +1 -1
- package/hooks/diagnostic/test_hook_log_extractor.py +1 -1
- package/hooks/diagnostic/test_hook_log_init.py +2 -2
- package/hooks/diagnostic/test_hook_log_stop_wrapper.py +1 -1
- package/hooks/git-hooks/gate_utils.py +1 -1
- package/hooks/git-hooks/pre_commit.py +1 -1
- package/hooks/git-hooks/pre_push.py +1 -1
- package/hooks/git-hooks/test_config.py +5 -5
- package/hooks/git-hooks/test_pre_push.py +6 -6
- package/hooks/{config → hooks_constants}/code_rules_enforcer_constants.py +37 -0
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +28 -0
- package/hooks/hooks_constants/md_to_html_blocker_constants.py +82 -0
- package/hooks/{config → hooks_constants}/pr_converge_bugteam_enforcer_state.py +1 -1
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +154 -0
- package/hooks/{config → hooks_constants}/pre_tool_use_stdin.py +1 -1
- package/hooks/{config → hooks_constants}/project_paths_reader.py +2 -2
- package/hooks/{config → hooks_constants}/test_banned_identifiers_constants.py +1 -1
- package/hooks/{config → hooks_constants}/test_dynamic_stderr_handler.py +1 -1
- package/hooks/{config → hooks_constants}/test_hardcoded_user_path_constants.py +1 -1
- package/hooks/{config → hooks_constants}/test_hook_log_extractor_constants.py +2 -2
- package/hooks/hooks_constants/test_md_to_html_blocker_constants.py +110 -0
- package/hooks/{config → hooks_constants}/test_messages.py +2 -6
- package/hooks/{config → hooks_constants}/test_path_rewriter_constants.py +1 -1
- package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +292 -0
- package/hooks/{config → hooks_constants}/test_pre_tool_use_stdin.py +2 -2
- package/hooks/{config → hooks_constants}/test_project_paths_reader.py +3 -3
- package/hooks/{config → hooks_constants}/test_session_env_cleanup_constants.py +1 -1
- package/hooks/{config → hooks_constants}/test_setup_project_paths_constants.py +2 -2
- package/hooks/{config → hooks_constants}/test_unused_module_import_constants.py +1 -1
- package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +5 -11
- package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +1 -1
- package/hooks/session/gh_pr_author_session_cleanup.py +5 -6
- package/hooks/session/session_env_cleanup.py +4 -10
- package/hooks/session/test_gh_pr_author_session_cleanup.py +1 -1
- package/hooks/session/test_untracked_repo_detector.py +2 -2
- package/hooks/session/untracked_repo_detector.py +6 -12
- package/hooks/test__gh_pr_author_swap_utils.py +1 -1
- package/hooks/validators/run_all_validators.py +16 -5
- package/hooks/validators/test_output_formatter.py +46 -0
- package/hooks/workflow/doc_gist_auto_publish.py +1 -1
- package/hooks/workflow/md_to_html_companion.py +8 -15
- package/hooks/workflow/test_md_to_html_companion.py +184 -23
- package/package.json +1 -1
- package/rules/ask-user-question-required.md +1 -1
- package/rules/vault-context.md +1 -1
- package/scripts/{config → dev_env_scripts_constants}/timing.py +1 -1
- package/scripts/setup_project_paths.py +49 -11
- package/scripts/sweep_empty_dirs.py +10 -1
- package/scripts/test_setup_project_paths.py +2 -2
- package/scripts/test_sweep_empty_dirs.py +2 -6
- package/skills/_shared/pr-loop/scripts/_path_resolver.py +1 -1
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/init_loop_state.py +1 -1
- package/skills/_shared/pr-loop/scripts/teardown_worktrees.py +1 -1
- package/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +2 -2
- package/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +2 -2
- package/skills/bugteam/PROMPTS.md +1 -1
- package/skills/bugteam/SKILL.md +1 -1
- package/skills/bugteam/reference/github-pr-reviews.md +1 -1
- package/skills/bugteam/scripts/{_claude_permissions_common.py → _bugteam_permissions_common.py} +110 -13
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +1 -13
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +1 -16
- package/skills/bugteam/scripts/bugteam_preflight.py +1 -13
- package/skills/bugteam/scripts/bugteam_scripts_constants/claude_permissions_common_constants.py +69 -0
- package/skills/bugteam/scripts/grant_project_claude_permissions.py +117 -12
- package/skills/bugteam/scripts/probe_code_rules_enforcer_check.py +1 -1
- package/skills/bugteam/scripts/reflow_skill_md.py +1 -1
- package/skills/bugteam/scripts/revoke_project_claude_permissions.py +71 -25
- package/skills/bugteam/scripts/{test__claude_permissions_common.py → test__bugteam_permissions_common.py} +4 -4
- package/skills/bugteam/scripts/test_agent_config_carveout.py +356 -0
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -26
- package/skills/bugteam/scripts/{test_claude_permissions_common.py → test_bugteam_permissions_common.py} +3 -66
- package/skills/bugteam/scripts/test_bugteam_preflight.py +2 -27
- package/skills/bugteam/scripts/windows_safe_rmtree.py +1 -1
- package/skills/doc-gist/SKILL.md +1 -1
- package/skills/doc-gist/scripts/gist_upload.py +1 -1
- package/skills/implement/SKILL.md +66 -0
- package/skills/implement/scripts/append_note.py +133 -0
- package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
- package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +12 -0
- package/skills/implement/scripts/test_append_note.py +191 -0
- package/skills/pr-converge/pr_converge_skill_constants/__init__.py +0 -0
- package/skills/pr-converge/{config → pr_converge_skill_constants}/constants.py +6 -1
- package/skills/pr-converge/scripts/check_bugbot_ci.py +2 -2
- package/skills/pr-converge/scripts/check_convergence.py +175 -29
- package/skills/pr-converge/scripts/check_pending_reviews.py +2 -2
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +2 -2
- package/skills/pr-converge/scripts/post_fix_reply.py +2 -2
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/__init__.py +0 -0
- package/skills/pr-converge/scripts/{config → pr_converge_scripts_constants}/pr_converge_constants.py +1 -1
- package/skills/pr-converge/scripts/reflow_skill_md.py +90 -16
- package/skills/pr-converge/scripts/test_check_bugbot_ci.py +1 -1
- package/skills/pr-converge/scripts/test_check_convergence.py +324 -0
- package/skills/pr-converge/scripts/test_reflow_skill_md.py +0 -31
- package/skills/refine/SKILL.md +257 -0
- package/skills/refine/templates/implementation-notes-template.html +56 -0
- package/skills/refine/templates/plan-template.md +60 -0
- package/skills/session-log/SKILL.md +98 -233
- package/_shared/pr-loop/scripts/config/claude_permissions_constants.py +0 -36
- package/hooks/config/pr_description_enforcer_constants.py +0 -19
- package/hooks/config/test_pr_description_enforcer_constants.py +0 -82
- package/skills/bugteam/scripts/config/claude_permissions_common_constants.py +0 -20
- package/skills/bugteam/scripts/test_grant_project_claude_permissions.py +0 -55
- package/skills/bugteam/scripts/test_revoke_project_claude_permissions.py +0 -55
- package/skills/pr-converge/scripts/evict_cached_config_modules.py +0 -20
- package/skills/pr-converge/scripts/test_evict_cached_config_modules.py +0 -22
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/__init__.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/code_rules_gate_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/fix_hookspath_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/post_audit_thread_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/preflight_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/reviews_disabled_constants.py +0 -0
- /package/hooks/git-hooks/{config.py → git_hooks_constants/__init__.py} +0 -0
- /package/hooks/{config → hooks_constants}/__init__.py +0 -0
- /package/hooks/{config → hooks_constants}/any_type_config.py +0 -0
- /package/hooks/{config → hooks_constants}/banned_identifiers_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/blocking_check_limits.py +0 -0
- /package/hooks/{config → hooks_constants}/bot_mention_comment_blocker_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/convergence_branch_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/doc_gist_auto_publish_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/dynamic_stderr_handler.py +0 -0
- /package/hooks/{config → hooks_constants}/gh_pr_author_swap_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/hardcoded_user_path_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/hook_log_extractor_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/html_companion_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/inline_tuple_string_magic_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/messages.py +0 -0
- /package/hooks/{config → hooks_constants}/path_rewriter_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/pr_converge_bugteam_enforcer_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/session_env_cleanup_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/setup_project_paths_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/state_description_blocker_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/stuttering_check_config.py +0 -0
- /package/hooks/{config → hooks_constants}/stuttering_import_binding_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/sys_path_insert_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/unused_module_import_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/windows_rmtree_blocker_constants.py +0 -0
- /package/{skills/_shared/pr-loop/scripts/config → hooks/lifecycle}/__init__.py +0 -0
- /package/{skills/bugteam/scripts/config → hooks/session}/__init__.py +0 -0
- /package/scripts/{config → dev_env_scripts_constants}/__init__.py +0 -0
- /package/skills/{doc-gist/scripts/config → _shared/pr-loop/scripts/skills_pr_loop_constants}/__init__.py +0 -0
- /package/skills/_shared/pr-loop/scripts/{config → skills_pr_loop_constants}/path_resolver_constants.py +0 -0
- /package/skills/{pr-converge/config → bugteam/scripts/bugteam_scripts_constants}/__init__.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_code_rules_gate_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_fix_hookspath_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_preflight_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/probe_code_rules_enforcer_check_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/reflow_skill_md_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/windows_safe_rmtree_constants.py +0 -0
- /package/skills/{pr-converge/scripts/config → doc-gist/scripts/doc_gist_scripts_constants}/__init__.py +0 -0
- /package/skills/doc-gist/scripts/{config → doc_gist_scripts_constants}/gist_upload_constants.py +0 -0
- /package/skills/pr-converge/scripts/{config → pr_converge_scripts_constants}/reflow_skill_md_constants.py +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Shared exemption rules for the .md blocker and its post-write companion.
|
|
2
|
+
|
|
3
|
+
Both `md_to_html_blocker.py` (PreToolUse) and `md_to_html_companion.py`
|
|
4
|
+
(PostToolUse) must agree on which file paths bypass the .md → .html policy.
|
|
5
|
+
This module is the single source of truth for that decision.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path, PureWindowsPath
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_hooks_directory = str(Path(__file__).resolve().parent.parent)
|
|
14
|
+
if _hooks_directory not in sys.path:
|
|
15
|
+
sys.path.insert(0, _hooks_directory)
|
|
16
|
+
|
|
17
|
+
from hooks_constants.md_to_html_blocker_constants import ( # noqa: E402
|
|
18
|
+
ALL_CLAUDE_CODE_SOURCE_TOP_DIRECTORIES,
|
|
19
|
+
ALL_EXEMPT_ANYWHERE_FILENAMES_LOWER,
|
|
20
|
+
ALL_EXEMPT_HOME_DIRECTORY_PATH_PREFIXES,
|
|
21
|
+
ALL_EXEMPT_PLUGIN_DIRECTORY_SEGMENTS,
|
|
22
|
+
ALL_EXEMPT_ROOT_FILENAMES_LOWER,
|
|
23
|
+
CLAUDE_DEV_ENV_REPO_NAME_SEGMENT,
|
|
24
|
+
CLAUDE_DIRECTORY_PATH_PREFIX,
|
|
25
|
+
CLAUDE_DIRECTORY_SEGMENT_MARKER,
|
|
26
|
+
MINIMUM_SEGMENT_COUNT_TO_MATCH_INDICATOR,
|
|
27
|
+
PACKAGES_TOP_LEVEL_SEGMENT,
|
|
28
|
+
PLUGIN_DIRECTORY_PATH_PREFIX,
|
|
29
|
+
PLUGIN_DIRECTORY_SEGMENT_MARKER,
|
|
30
|
+
PLUGIN_ROOT_MARKER_DIRECTORY_NAME,
|
|
31
|
+
REPO_ROOT_MARKER_NAME,
|
|
32
|
+
RESOLVED_HOME_DIRECTORY_LOWER,
|
|
33
|
+
RESOLVED_TEMP_DIRECTORY_PATH_PREFIX,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def is_exempt_path(file_path: str) -> bool:
|
|
38
|
+
"""Return True when the .md file path is exempt from the blocker policy.
|
|
39
|
+
|
|
40
|
+
Exemption sources, in order of evaluation:
|
|
41
|
+
- Any segment under `.claude/` or `.claude-plugin/` (case-insensitive)
|
|
42
|
+
- Basename in `ALL_EXEMPT_ANYWHERE_FILENAMES` (e.g. SKILL.md)
|
|
43
|
+
- Anchored under `packages/claude-dev-env/<one of
|
|
44
|
+
ALL_CLAUDE_CODE_SOURCE_TOP_DIRECTORIES>/...` (docs, rules,
|
|
45
|
+
system-prompts source files in this repo)
|
|
46
|
+
- Path segment in `ALL_EXEMPT_PLUGIN_DIRECTORY_SEGMENTS` (agents/skills/commands)
|
|
47
|
+
- Canonical path under a home-relative exempt directory
|
|
48
|
+
(`ALL_EXEMPT_HOME_RELATIVE_DIRECTORIES`)
|
|
49
|
+
- Canonical path under the OS temp directory
|
|
50
|
+
- Ancestor directory contains `.claude-plugin/` (plugin-root marker walk)
|
|
51
|
+
- Basename in `ALL_EXEMPT_ROOT_FILENAMES` and directory is a repo root
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
file_path: Raw file path from the hook payload. May contain tilde,
|
|
55
|
+
backslashes, or be relative.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
True when the path is exempt, False when the policy applies.
|
|
59
|
+
"""
|
|
60
|
+
expanded_path = os.path.expanduser(file_path)
|
|
61
|
+
normalized = os.path.normpath(expanded_path).replace("\\", "/")
|
|
62
|
+
lower_normalized = normalized.lower()
|
|
63
|
+
if (
|
|
64
|
+
CLAUDE_DIRECTORY_SEGMENT_MARKER in lower_normalized
|
|
65
|
+
or lower_normalized.startswith(CLAUDE_DIRECTORY_PATH_PREFIX)
|
|
66
|
+
):
|
|
67
|
+
return True
|
|
68
|
+
if (
|
|
69
|
+
PLUGIN_DIRECTORY_SEGMENT_MARKER in lower_normalized
|
|
70
|
+
or lower_normalized.startswith(PLUGIN_DIRECTORY_PATH_PREFIX)
|
|
71
|
+
):
|
|
72
|
+
return True
|
|
73
|
+
basename_lower = os.path.basename(normalized).lower()
|
|
74
|
+
if basename_lower in ALL_EXEMPT_ANYWHERE_FILENAMES_LOWER:
|
|
75
|
+
return True
|
|
76
|
+
if _is_under_claude_dev_env_source_subdirectory(expanded_path, lower_normalized):
|
|
77
|
+
return True
|
|
78
|
+
if _has_plugin_directory_segment(lower_normalized):
|
|
79
|
+
return True
|
|
80
|
+
canonical_normalized_path = os.path.realpath(expanded_path).replace("\\", "/")
|
|
81
|
+
canonical_lower_path = canonical_normalized_path.lower()
|
|
82
|
+
if _is_under_exempt_home_directory(canonical_lower_path):
|
|
83
|
+
return True
|
|
84
|
+
if canonical_lower_path.startswith(RESOLVED_TEMP_DIRECTORY_PATH_PREFIX):
|
|
85
|
+
return True
|
|
86
|
+
if _is_under_plugin_root_marker(canonical_normalized_path):
|
|
87
|
+
return True
|
|
88
|
+
if basename_lower in ALL_EXEMPT_ROOT_FILENAMES_LOWER:
|
|
89
|
+
absolute_directory = _resolve_absolute_directory(normalized)
|
|
90
|
+
if _is_repo_root_directory(absolute_directory):
|
|
91
|
+
return True
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _resolve_absolute_directory(normalized_path: str) -> str:
|
|
96
|
+
directory = os.path.dirname(normalized_path)
|
|
97
|
+
if not directory or directory == ".":
|
|
98
|
+
return os.getcwd()
|
|
99
|
+
if os.path.isabs(directory):
|
|
100
|
+
return directory
|
|
101
|
+
return os.path.abspath(directory)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _has_plugin_directory_segment(lower_normalized_path: str) -> bool:
|
|
105
|
+
for each_directory_segment in ALL_EXEMPT_PLUGIN_DIRECTORY_SEGMENTS:
|
|
106
|
+
segment_marker = f"/{each_directory_segment}/"
|
|
107
|
+
if segment_marker in lower_normalized_path:
|
|
108
|
+
return True
|
|
109
|
+
if lower_normalized_path.startswith(f"{each_directory_segment}/"):
|
|
110
|
+
return True
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _is_absolute_path_cross_platform(file_path: str) -> bool:
|
|
115
|
+
"""Detect absolute paths in both POSIX and Windows drive-letter forms.
|
|
116
|
+
|
|
117
|
+
``os.path.isabs`` is platform-dependent: on Linux/macOS it classifies a
|
|
118
|
+
Windows drive-letter path like ``Y:\\repo\\foo`` as relative. The anchored
|
|
119
|
+
source-subdirectory exemption must scan every starting segment for
|
|
120
|
+
absolute paths regardless of host OS, so a path's absoluteness must be
|
|
121
|
+
decided cross-platform.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
file_path: Tilde-expanded file path.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
True when the path is absolute under POSIX rules or carries a Windows
|
|
128
|
+
drive-letter root (``[A-Za-z]:[\\\\/]...``).
|
|
129
|
+
"""
|
|
130
|
+
if os.path.isabs(file_path):
|
|
131
|
+
return True
|
|
132
|
+
return PureWindowsPath(file_path).is_absolute()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _is_under_claude_dev_env_source_subdirectory(
|
|
136
|
+
expanded_file_path: str, lower_normalized_path: str
|
|
137
|
+
) -> bool:
|
|
138
|
+
"""Anchored exemption for ``packages/claude-dev-env/<source-dir>/...``.
|
|
139
|
+
|
|
140
|
+
The match requires segment-anchored matching at the start of the path
|
|
141
|
+
(relative) or at the root of an absolute path. A nested path like
|
|
142
|
+
``notes/packages/claude-dev-env/docs/foo.md`` is NOT exempt — only the
|
|
143
|
+
full three-segment anchor matches.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
expanded_file_path: Tilde-expanded file path; ``os.path.isabs`` on
|
|
147
|
+
this form classifies the path as absolute or relative on the
|
|
148
|
+
current platform.
|
|
149
|
+
lower_normalized_path: Same path lowercased and with separators
|
|
150
|
+
normalized to forward slashes.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
True when the path is anchored under
|
|
154
|
+
``packages/claude-dev-env/<one of
|
|
155
|
+
ALL_CLAUDE_CODE_SOURCE_TOP_DIRECTORIES>/``.
|
|
156
|
+
"""
|
|
157
|
+
all_segments = [
|
|
158
|
+
each_segment
|
|
159
|
+
for each_segment in lower_normalized_path.split("/")
|
|
160
|
+
if each_segment
|
|
161
|
+
]
|
|
162
|
+
if not all_segments:
|
|
163
|
+
return False
|
|
164
|
+
if _is_absolute_path_cross_platform(expanded_file_path):
|
|
165
|
+
starting_segment_index_options = list(range(len(all_segments)))
|
|
166
|
+
else:
|
|
167
|
+
starting_segment_index_options = [0]
|
|
168
|
+
for each_starting_index in starting_segment_index_options:
|
|
169
|
+
if (
|
|
170
|
+
len(all_segments) >= each_starting_index + MINIMUM_SEGMENT_COUNT_TO_MATCH_INDICATOR
|
|
171
|
+
and all_segments[each_starting_index] == PACKAGES_TOP_LEVEL_SEGMENT
|
|
172
|
+
and all_segments[each_starting_index + 1] == CLAUDE_DEV_ENV_REPO_NAME_SEGMENT
|
|
173
|
+
and all_segments[each_starting_index + 2] in ALL_CLAUDE_CODE_SOURCE_TOP_DIRECTORIES
|
|
174
|
+
):
|
|
175
|
+
return True
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _is_under_plugin_root_marker(normalized_path: str) -> bool:
|
|
180
|
+
directory = os.path.dirname(normalized_path)
|
|
181
|
+
visited_directories: set[str] = set()
|
|
182
|
+
while directory and directory not in visited_directories:
|
|
183
|
+
visited_directories.add(directory)
|
|
184
|
+
marker_path = os.path.join(directory, PLUGIN_ROOT_MARKER_DIRECTORY_NAME)
|
|
185
|
+
if os.path.isdir(marker_path):
|
|
186
|
+
return True
|
|
187
|
+
parent_directory = os.path.dirname(directory)
|
|
188
|
+
if parent_directory == directory:
|
|
189
|
+
break
|
|
190
|
+
directory = parent_directory
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _is_under_exempt_home_directory(lower_normalized_path: str) -> bool:
|
|
195
|
+
if not RESOLVED_HOME_DIRECTORY_LOWER:
|
|
196
|
+
return False
|
|
197
|
+
for each_exempt_path_prefix in ALL_EXEMPT_HOME_DIRECTORY_PATH_PREFIXES:
|
|
198
|
+
if lower_normalized_path.startswith(each_exempt_path_prefix):
|
|
199
|
+
return True
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _is_repo_root_directory(directory_path: str) -> bool:
|
|
204
|
+
git_marker_path = os.path.join(directory_path, REPO_ROOT_MARKER_NAME)
|
|
205
|
+
return os.path.exists(git_marker_path)
|
|
@@ -6,27 +6,44 @@ that markdown flattens. See https://thariqs.github.io/html-effectiveness/
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
|
-
import os
|
|
10
9
|
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
11
|
from typing import TextIO
|
|
12
12
|
|
|
13
|
+
_hooks_dir = str(Path(__file__).resolve().parent.parent)
|
|
14
|
+
if _hooks_dir not in sys.path:
|
|
15
|
+
sys.path.insert(0, _hooks_dir)
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
_blocking_directory = str(Path(__file__).resolve().parent)
|
|
18
|
+
if _blocking_directory not in sys.path:
|
|
19
|
+
sys.path.insert(0, _blocking_directory)
|
|
20
|
+
|
|
21
|
+
from hooks_constants.md_to_html_blocker_constants import ( # noqa: E402
|
|
22
|
+
ALL_CLAUDE_CODE_SOURCE_TOP_DIRECTORIES,
|
|
23
|
+
ALL_EXEMPT_ANYWHERE_FILENAMES,
|
|
24
|
+
ALL_EXEMPT_HOME_RELATIVE_DIRECTORIES,
|
|
25
|
+
ALL_EXEMPT_PLUGIN_DIRECTORY_SEGMENTS,
|
|
26
|
+
CLAUDE_DEV_ENV_REPO_NAME_SEGMENT,
|
|
27
|
+
CLAUDE_DIRECTORY_NAME,
|
|
28
|
+
PACKAGES_TOP_LEVEL_SEGMENT,
|
|
29
|
+
PLUGIN_ROOT_MARKER_DIRECTORY_NAME,
|
|
30
|
+
)
|
|
31
|
+
from md_path_exemptions import is_exempt_path # noqa: E402
|
|
17
32
|
|
|
18
33
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
_markdown_extension = ".md"
|
|
35
|
+
_html_effectiveness_url = "https://thariqs.github.io/html-effectiveness/"
|
|
36
|
+
_exempt_anywhere_filenames_summary = ", ".join(ALL_EXEMPT_ANYWHERE_FILENAMES)
|
|
37
|
+
_exempt_plugin_segments_summary = ", ".join(
|
|
38
|
+
f"{each_segment}/" for each_segment in ALL_EXEMPT_PLUGIN_DIRECTORY_SEGMENTS
|
|
39
|
+
)
|
|
40
|
+
_exempt_home_directories_summary = ", ".join(
|
|
41
|
+
f"~/{each_directory}/" for each_directory in ALL_EXEMPT_HOME_RELATIVE_DIRECTORIES
|
|
42
|
+
)
|
|
43
|
+
_claude_dev_env_source_directories_summary = (
|
|
44
|
+
f"{PACKAGES_TOP_LEVEL_SEGMENT}/{CLAUDE_DEV_ENV_REPO_NAME_SEGMENT}/"
|
|
45
|
+
f"{{{','.join(sorted(ALL_CLAUDE_CODE_SOURCE_TOP_DIRECTORIES))}}}/"
|
|
46
|
+
)
|
|
30
47
|
|
|
31
48
|
|
|
32
49
|
def _block_reason(file_path: str) -> str:
|
|
@@ -46,8 +63,14 @@ def _block_context() -> str:
|
|
|
46
63
|
"Reference for HTML effectiveness patterns:\n"
|
|
47
64
|
f"{_html_effectiveness_url}\n"
|
|
48
65
|
"Exceptions (.md still allowed):\n"
|
|
49
|
-
"- Files inside
|
|
50
|
-
"-
|
|
66
|
+
f"- Files inside {CLAUDE_DIRECTORY_NAME}/ or {PLUGIN_ROOT_MARKER_DIRECTORY_NAME}/ directories\n"
|
|
67
|
+
f"- {_exempt_anywhere_filenames_summary} anywhere\n"
|
|
68
|
+
f"- Files under {_exempt_plugin_segments_summary} directories\n"
|
|
69
|
+
f"- Files under {_claude_dev_env_source_directories_summary} source directories\n"
|
|
70
|
+
f"- Files under any directory whose ancestor contains {PLUGIN_ROOT_MARKER_DIRECTORY_NAME}/\n"
|
|
71
|
+
"- README.md and CHANGELOG.md at any repo root\n"
|
|
72
|
+
f"- Files under {_exempt_home_directories_summary}\n"
|
|
73
|
+
"- Files under the OS temp directory"
|
|
51
74
|
)
|
|
52
75
|
|
|
53
76
|
|
|
@@ -55,8 +78,13 @@ def _block_system_message() -> str:
|
|
|
55
78
|
return (
|
|
56
79
|
".md files are blocked in this project — generate a self-contained .html "
|
|
57
80
|
f"file instead. See {_html_effectiveness_url} for "
|
|
58
|
-
"design patterns and examples. Exemptions:
|
|
59
|
-
"
|
|
81
|
+
f"design patterns and examples. Exemptions: {CLAUDE_DIRECTORY_NAME}/ and "
|
|
82
|
+
f"{PLUGIN_ROOT_MARKER_DIRECTORY_NAME}/ infrastructure, "
|
|
83
|
+
f"{_exempt_anywhere_filenames_summary} anywhere, {_exempt_plugin_segments_summary} trees, "
|
|
84
|
+
f"{_claude_dev_env_source_directories_summary} source trees, "
|
|
85
|
+
f"files under a {PLUGIN_ROOT_MARKER_DIRECTORY_NAME}/ root, "
|
|
86
|
+
f"README.md/CHANGELOG.md at any repo root, {_exempt_home_directories_summary}, "
|
|
87
|
+
"and the OS temp directory."
|
|
60
88
|
)
|
|
61
89
|
|
|
62
90
|
|
|
@@ -92,7 +120,7 @@ def main() -> None:
|
|
|
92
120
|
if not file_path.lower().endswith(_markdown_extension):
|
|
93
121
|
sys.exit(0)
|
|
94
122
|
|
|
95
|
-
if
|
|
123
|
+
if is_exempt_path(file_path):
|
|
96
124
|
sys.exit(0)
|
|
97
125
|
|
|
98
126
|
block_payload = {
|
|
@@ -25,17 +25,11 @@ import sys
|
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
from typing import TextIO
|
|
27
27
|
|
|
28
|
+
_hooks_dir = str(Path(__file__).resolve().parent.parent)
|
|
29
|
+
if _hooks_dir not in sys.path:
|
|
30
|
+
sys.path.insert(0, _hooks_dir)
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
hooks_tree = Path(__file__).resolve().parent.parent
|
|
31
|
-
hooks_tree_string = str(hooks_tree)
|
|
32
|
-
if hooks_tree_string not in sys.path:
|
|
33
|
-
sys.path.insert(0, hooks_tree_string)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
_insert_hooks_tree_for_imports()
|
|
37
|
-
|
|
38
|
-
from config.pr_converge_bugteam_enforcer_constants import (
|
|
32
|
+
from hooks_constants.pr_converge_bugteam_enforcer_constants import ( # noqa: E402
|
|
39
33
|
AGENT_TOOL_NAME,
|
|
40
34
|
ALL_AUDIT_PROMPT_SUBSTRINGS,
|
|
41
35
|
BUGTEAM_PHASE,
|
|
@@ -47,7 +41,7 @@ from config.pr_converge_bugteam_enforcer_constants import (
|
|
|
47
41
|
STATE_FIELD_PHASE,
|
|
48
42
|
STATE_FIELD_TICK_COUNT,
|
|
49
43
|
)
|
|
50
|
-
from
|
|
44
|
+
from hooks_constants.pr_converge_bugteam_enforcer_state import ( # noqa: E402
|
|
51
45
|
load_state_dictionary,
|
|
52
46
|
resolve_state_path,
|
|
53
47
|
)
|