claude-dev-env 1.42.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/_shared/pr-loop/scripts/_claude_permissions_common.py +1 -5
- 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 +3 -16
- package/_shared/pr-loop/scripts/post_audit_thread.py +4 -4
- package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/claude_permissions_constants.py +1 -1
- 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 +3 -16
- package/_shared/pr-loop/scripts/tests/conftest.py +1 -51
- package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +4 -4
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +4 -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 +2 -2
- 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} +1 -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/grant_project_claude_permissions.py +2 -8
- 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 +2 -8
- 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 +2 -2
- 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 +2 -2
- package/skills/implement/scripts/append_note.py +1 -1
- package/skills/pr-converge/pr_converge_skill_constants/__init__.py +0 -0
- package/skills/pr-converge/{config → pr_converge_skill_constants}/constants.py +1 -1
- package/skills/pr-converge/scripts/check_bugbot_ci.py +1 -1
- package/skills/pr-converge/scripts/check_convergence.py +11 -4
- package/skills/pr-converge/scripts/check_pending_reviews.py +1 -1
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +1 -1
- package/skills/pr-converge/scripts/post_fix_reply.py +1 -1
- 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_convergence.py +18 -0
- package/skills/pr-converge/scripts/test_reflow_skill_md.py +0 -31
- package/skills/session-log/SKILL.md +98 -233
- 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/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/conftest.py +0 -60
- 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}/claude_settings_keys_constants.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/{implement/scripts/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}/claude_permissions_common_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/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 → implement/scripts/implement_scripts_constants}/__init__.py +0 -0
- /package/skills/implement/scripts/{config → implement_scripts_constants}/notes_constants.py +0 -0
- /package/skills/pr-converge/scripts/{config → pr_converge_scripts_constants}/reflow_skill_md_constants.py +0 -0
|
@@ -40,43 +40,43 @@ if _HOOKS_DIR not in sys.path:
|
|
|
40
40
|
sys.path.insert(0, _HOOKS_DIR)
|
|
41
41
|
|
|
42
42
|
from code_rules_path_utils import is_config_file # noqa: E402
|
|
43
|
-
from
|
|
43
|
+
from hooks_constants.banned_identifiers_constants import ( # noqa: E402
|
|
44
44
|
ALL_BANNED_IDENTIFIERS,
|
|
45
45
|
BANNED_IDENTIFIER_MESSAGE_SUFFIX,
|
|
46
46
|
BANNED_IDENTIFIER_SKIP_ADVISORY,
|
|
47
47
|
MAX_BANNED_IDENTIFIER_ISSUES,
|
|
48
48
|
)
|
|
49
|
-
from
|
|
49
|
+
from hooks_constants.hardcoded_user_path_constants import ( # noqa: E402
|
|
50
50
|
HARDCODED_USER_PATH_GUIDANCE,
|
|
51
51
|
HARDCODED_USER_PATH_PATTERN,
|
|
52
52
|
MAX_HARDCODED_USER_PATH_ISSUES,
|
|
53
53
|
)
|
|
54
|
-
from
|
|
54
|
+
from hooks_constants.inline_tuple_string_magic_constants import ( # noqa: E402
|
|
55
55
|
ALL_SNAKE_CASE_KEYWORD_EXEMPTIONS,
|
|
56
56
|
EXPECTED_TUPLE_PAIR_LENGTH,
|
|
57
57
|
INLINE_TUPLE_STRING_MAGIC_MESSAGE_SUFFIX,
|
|
58
58
|
MAX_INLINE_TUPLE_STRING_MAGIC_ISSUES,
|
|
59
59
|
SNAKE_CASE_LITERAL_PATTERN,
|
|
60
60
|
)
|
|
61
|
-
from
|
|
61
|
+
from hooks_constants.stuttering_check_config import ( # noqa: E402
|
|
62
62
|
MAX_STUTTERING_PREFIX_ISSUES,
|
|
63
63
|
STUTTERING_ALL_PREFIX_PATTERN,
|
|
64
64
|
)
|
|
65
|
-
from
|
|
66
|
-
from
|
|
65
|
+
from hooks_constants.sys_path_insert_constants import MAX_SYS_PATH_INSERT_ISSUES, SYS_PATH_INSERT_GUIDANCE # noqa: E402
|
|
66
|
+
from hooks_constants.unused_module_import_constants import ( # noqa: E402
|
|
67
67
|
ALL_TYPING_MODULE_NAMES,
|
|
68
68
|
MAX_UNUSED_IMPORT_ISSUES,
|
|
69
69
|
TYPE_CHECKING_IDENTIFIER,
|
|
70
70
|
UNUSED_IMPORT_GUIDANCE,
|
|
71
71
|
line_suppresses_unused_import_via_noqa,
|
|
72
72
|
)
|
|
73
|
-
from
|
|
73
|
+
from hooks_constants.stuttering_import_binding_constants import ( # noqa: E402
|
|
74
74
|
AST_LINENO_ATTRIBUTE,
|
|
75
75
|
MODULE_PATH_SEPARATOR,
|
|
76
76
|
WILDCARD_IMPORT_SENTINEL,
|
|
77
77
|
)
|
|
78
|
-
from
|
|
79
|
-
from
|
|
78
|
+
from hooks_constants.any_type_config import ALL_ANY_ALLOWED_PATTERNS # noqa: E402
|
|
79
|
+
from hooks_constants.blocking_check_limits import ( # noqa: E402
|
|
80
80
|
ALL_BANNED_PREFIX_NAMES,
|
|
81
81
|
ALL_BARE_EXCEPT_BANNED_HANDLER_NAMES,
|
|
82
82
|
ALL_BOUNDARY_TYPE_EXEMPT_FILENAMES,
|
|
@@ -95,7 +95,7 @@ from config.blocking_check_limits import ( # noqa: E402
|
|
|
95
95
|
MAX_THIN_WRAPPER_ISSUES,
|
|
96
96
|
)
|
|
97
97
|
|
|
98
|
-
from
|
|
98
|
+
from hooks_constants.code_rules_enforcer_constants import ( # noqa: E402
|
|
99
99
|
ADVISORY_LINE_THRESHOLD_HARD,
|
|
100
100
|
ADVISORY_LINE_THRESHOLD_SOFT,
|
|
101
101
|
ALL_CODE_EXTENSIONS,
|
|
@@ -109,9 +109,14 @@ from config.code_rules_enforcer_constants import ( # noqa: E402
|
|
|
109
109
|
ALL_SUBSCRIPT_ONLY_COLLECTION_TYPE_NAMES,
|
|
110
110
|
DOTTED_SEGMENT_PATTERN,
|
|
111
111
|
EACH_PREFIX,
|
|
112
|
+
ALL_EXEMPT_PYTHON_COMMENT_BODIES,
|
|
113
|
+
ALL_JAVASCRIPT_EXEMPT_COMMENT_PREFIXES,
|
|
114
|
+
ALL_JAVASCRIPT_EXEMPT_INLINE_COMMENT_PREFIXES,
|
|
115
|
+
ALL_PYTHON_TOKENIZE_FAILURE_EXCEPTIONS,
|
|
112
116
|
FILE_GLOBAL_UPPER_SNAKE_PATTERN,
|
|
113
117
|
ALL_HOOK_INFRASTRUCTURE_PATTERNS,
|
|
114
118
|
ALL_IMPORT_STATEMENT_PREFIXES,
|
|
119
|
+
MAX_COMMENT_ISSUES,
|
|
115
120
|
INLINE_COLLECTION_MIN_LENGTH,
|
|
116
121
|
ALL_JAVASCRIPT_EXTENSIONS,
|
|
117
122
|
LOGGING_FSTRING_PATTERN,
|
|
@@ -121,6 +126,9 @@ from config.code_rules_enforcer_constants import ( # noqa: E402
|
|
|
121
126
|
ALL_PYTHON_EXTENSIONS,
|
|
122
127
|
ALL_SELF_AND_CLS_PARAMETER_NAMES,
|
|
123
128
|
ALL_TEST_PATH_PATTERNS,
|
|
129
|
+
TRIPLE_DOUBLE_QUOTE_DELIMITER,
|
|
130
|
+
TRIPLE_QUOTE_PARITY_DIVISOR,
|
|
131
|
+
TRIPLE_SINGLE_QUOTE_DELIMITER,
|
|
124
132
|
TYPE_CHECKING_BLOCK_PATTERN,
|
|
125
133
|
ALL_UNION_TYPING_NAMES,
|
|
126
134
|
UPPER_SNAKE_CONSTANT_PATTERN,
|
|
@@ -168,54 +176,29 @@ def is_spec_file(file_path: str) -> bool:
|
|
|
168
176
|
|
|
169
177
|
|
|
170
178
|
def check_comments_python(content: str) -> list[str]:
|
|
171
|
-
"""Check for comments in Python code.
|
|
179
|
+
"""Check for comments in Python code.
|
|
180
|
+
|
|
181
|
+
Uses ``tokenize.generate_tokens`` to find true ``COMMENT`` tokens.
|
|
182
|
+
Hash characters that appear inside string literals (hex color codes,
|
|
183
|
+
URL fragments, and the hash inside an f-string interpolation pattern)
|
|
184
|
+
are correctly skipped because the tokenizer recognizes them as parts
|
|
185
|
+
of string tokens rather than comment tokens.
|
|
186
|
+
|
|
187
|
+
When the tokenizer cannot parse the file (partial content during
|
|
188
|
+
Edit, invalid syntax), the check returns no findings rather than
|
|
189
|
+
falling back to a line-walker scan — false negatives on
|
|
190
|
+
syntactically-invalid drafts are preferable to false positives that
|
|
191
|
+
mis-classify string-interior hash characters as comments.
|
|
192
|
+
"""
|
|
172
193
|
issues = []
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
for line_number, line in enumerate(lines, 1):
|
|
176
|
-
stripped = line.strip()
|
|
177
|
-
|
|
178
|
-
if not stripped:
|
|
179
|
-
continue
|
|
180
|
-
|
|
181
|
-
if stripped.startswith("#!"):
|
|
182
|
-
continue
|
|
183
|
-
|
|
184
|
-
if stripped.startswith("# type:"):
|
|
194
|
+
for each_comment_token in _comment_tokens(content):
|
|
195
|
+
if _is_exempt_python_comment(each_comment_token):
|
|
185
196
|
continue
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if
|
|
191
|
-
continue
|
|
192
|
-
|
|
193
|
-
if stripped.startswith("# pragma:"):
|
|
194
|
-
continue
|
|
195
|
-
|
|
196
|
-
if stripped.startswith(("# TODO", "# FIXME", "# HACK", "# XXX")):
|
|
197
|
-
continue
|
|
198
|
-
|
|
199
|
-
comment_index = line.find("#")
|
|
200
|
-
if comment_index != -1:
|
|
201
|
-
before_comment = line[:comment_index]
|
|
202
|
-
if not before_comment.strip().startswith(("'", '"')):
|
|
203
|
-
is_in_string = False
|
|
204
|
-
quote_char = None
|
|
205
|
-
for i, each_char in enumerate(before_comment):
|
|
206
|
-
if each_char in ("'", '"') and (i == 0 or before_comment[i - 1] != "\\"):
|
|
207
|
-
if not is_in_string:
|
|
208
|
-
is_in_string = True
|
|
209
|
-
quote_char = each_char
|
|
210
|
-
elif each_char == quote_char:
|
|
211
|
-
is_in_string = False
|
|
212
|
-
|
|
213
|
-
if not is_in_string:
|
|
214
|
-
comment_text = line[comment_index + 1 :].strip()
|
|
215
|
-
if comment_text and not comment_text.startswith(("type:", "noqa", "pylint:", "pragma:", "TODO", "FIXME", "HACK", "XXX")):
|
|
216
|
-
issues.append(f"Line {line_number}: Comment found - refactor to self-documenting code")
|
|
217
|
-
|
|
218
|
-
if len(issues) >= 3:
|
|
197
|
+
line_number = each_comment_token.start[0]
|
|
198
|
+
issues.append(
|
|
199
|
+
f"Line {line_number}: Comment found - refactor to self-documenting code"
|
|
200
|
+
)
|
|
201
|
+
if len(issues) >= MAX_COMMENT_ISSUES:
|
|
219
202
|
break
|
|
220
203
|
|
|
221
204
|
return issues
|
|
@@ -245,10 +228,10 @@ def check_comments_javascript(content: str) -> list[str]:
|
|
|
245
228
|
continue
|
|
246
229
|
|
|
247
230
|
if stripped.startswith("//"):
|
|
248
|
-
if not stripped.startswith(
|
|
231
|
+
if not stripped.startswith(ALL_JAVASCRIPT_EXEMPT_COMMENT_PREFIXES):
|
|
249
232
|
issues.append(f"Line {each_line_number}: Comment found - refactor to self-documenting code")
|
|
250
233
|
|
|
251
|
-
if len(issues) >=
|
|
234
|
+
if len(issues) >= MAX_COMMENT_ISSUES:
|
|
252
235
|
break
|
|
253
236
|
|
|
254
237
|
return issues
|
|
@@ -268,36 +251,13 @@ def extract_comment_texts(content: str, file_path: str) -> tuple[set[str], set[s
|
|
|
268
251
|
if not content:
|
|
269
252
|
return inline_comments, standalone_comments
|
|
270
253
|
|
|
271
|
-
lines = content.split("\n")
|
|
272
|
-
|
|
273
254
|
if extension in ALL_PYTHON_EXTENSIONS:
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if not stripped:
|
|
277
|
-
continue
|
|
278
|
-
if stripped.startswith("#"):
|
|
279
|
-
if stripped.startswith(("#!", "# type:", "# noqa", "# pylint:", "# pragma:", "# TODO", "# FIXME", "# HACK", "# XXX")):
|
|
280
|
-
continue
|
|
281
|
-
standalone_comments.add(stripped)
|
|
282
|
-
elif "#" in line:
|
|
283
|
-
comment_index = line.find("#")
|
|
284
|
-
before_comment = line[:comment_index]
|
|
285
|
-
if not before_comment.strip().startswith(("'", '"')):
|
|
286
|
-
is_in_string = False
|
|
287
|
-
quote_char = None
|
|
288
|
-
for i, char in enumerate(before_comment):
|
|
289
|
-
if char in ("'", '"') and (i == 0 or before_comment[i - 1] != "\\"):
|
|
290
|
-
if not is_in_string:
|
|
291
|
-
is_in_string = True
|
|
292
|
-
quote_char = char
|
|
293
|
-
elif char == quote_char:
|
|
294
|
-
is_in_string = False
|
|
295
|
-
if not is_in_string:
|
|
296
|
-
comment_text = line[comment_index + 1 :].strip()
|
|
297
|
-
if comment_text and not comment_text.startswith(("type:", "noqa", "pylint:", "pragma:", "TODO", "FIXME", "HACK", "XXX")):
|
|
298
|
-
inline_comments.add(line[comment_index:].strip())
|
|
255
|
+
inline_comments, standalone_comments, _ = _extract_python_comment_sets(content)
|
|
256
|
+
return inline_comments, standalone_comments
|
|
299
257
|
|
|
300
|
-
|
|
258
|
+
lines = content.split("\n")
|
|
259
|
+
|
|
260
|
+
if extension in ALL_JAVASCRIPT_EXTENSIONS:
|
|
301
261
|
is_in_multiline = False
|
|
302
262
|
for line in lines:
|
|
303
263
|
stripped = line.strip()
|
|
@@ -313,14 +273,14 @@ def extract_comment_texts(content: str, file_path: str) -> tuple[set[str], set[s
|
|
|
313
273
|
standalone_comments.add(stripped)
|
|
314
274
|
continue
|
|
315
275
|
if stripped.startswith("//"):
|
|
316
|
-
if not stripped.startswith(
|
|
276
|
+
if not stripped.startswith(ALL_JAVASCRIPT_EXEMPT_COMMENT_PREFIXES):
|
|
317
277
|
standalone_comments.add(stripped)
|
|
318
278
|
elif "//" in line:
|
|
319
279
|
before_slash = line[:line.index("//")]
|
|
320
280
|
if before_slash.strip():
|
|
321
281
|
comment_start = stripped.index("//")
|
|
322
282
|
comment_text = stripped[comment_start + 2 :].strip()
|
|
323
|
-
if not comment_text.startswith(
|
|
283
|
+
if not comment_text.startswith(ALL_JAVASCRIPT_EXEMPT_INLINE_COMMENT_PREFIXES):
|
|
324
284
|
inline_comments.add(stripped[comment_start:])
|
|
325
285
|
|
|
326
286
|
return inline_comments, standalone_comments
|
|
@@ -332,11 +292,26 @@ def check_comment_changes(old_content: str, new_content: str, file_path: str) ->
|
|
|
332
292
|
Inline comments (after code on same line): BLOCK when added.
|
|
333
293
|
Standalone comment lines: NUDGE (print advisory) when added.
|
|
334
294
|
Existing comments being removed: BLOCK (comment preservation principle).
|
|
295
|
+
|
|
296
|
+
When the file is Python and either *old_content* or *new_content* cannot
|
|
297
|
+
be tokenized (common for mid-edit Edit fragments), the comparison is
|
|
298
|
+
indeterminate: the per-side tokenize failure would empty one set and
|
|
299
|
+
misrepresent every comment on the other side as either added or
|
|
300
|
+
removed. The check returns no issues in that case — false negatives on
|
|
301
|
+
syntactically-invalid drafts are preferable to false positives that
|
|
302
|
+
flag legitimate comments as deleted.
|
|
335
303
|
"""
|
|
336
304
|
issues: list[str] = []
|
|
337
305
|
|
|
338
|
-
|
|
339
|
-
|
|
306
|
+
extension = get_file_extension(file_path)
|
|
307
|
+
if extension in ALL_PYTHON_EXTENSIONS:
|
|
308
|
+
old_inline, old_standalone, old_tokenize_ok = _extract_python_comment_sets(old_content)
|
|
309
|
+
new_inline, new_standalone, new_tokenize_ok = _extract_python_comment_sets(new_content)
|
|
310
|
+
if not (old_tokenize_ok and new_tokenize_ok):
|
|
311
|
+
return issues
|
|
312
|
+
else:
|
|
313
|
+
old_inline, old_standalone = extract_comment_texts(old_content, file_path)
|
|
314
|
+
new_inline, new_standalone = extract_comment_texts(new_content, file_path)
|
|
340
315
|
|
|
341
316
|
added_inline = new_inline - old_inline
|
|
342
317
|
if added_inline:
|
|
@@ -382,14 +357,29 @@ def check_imports_at_top(content: str) -> list[str]:
|
|
|
382
357
|
simpler single-level tracking is preferred over maintaining a stack of indent
|
|
383
358
|
levels. The pinned behavior is covered by
|
|
384
359
|
``test_should_track_only_innermost_type_checking_block``.
|
|
360
|
+
|
|
361
|
+
Triple-quoted-string interior lines are skipped. Once a line opens a
|
|
362
|
+
multi-line triple-double-quote or triple-single-quote string (odd count
|
|
363
|
+
of the delimiter), every subsequent line is treated as docstring content
|
|
364
|
+
and exempt from the import-prefix scan until the matching delimiter
|
|
365
|
+
closes the string. Without this tracking, docstring sentences that
|
|
366
|
+
happen to start with ``from `` or ``import `` after stripping (a common
|
|
367
|
+
pattern in narrative docstrings) would fire a false positive.
|
|
385
368
|
"""
|
|
386
369
|
issues: list[str] = []
|
|
387
370
|
lines = content.split("\n")
|
|
388
371
|
is_inside_function = False
|
|
389
372
|
function_indent = 0
|
|
390
373
|
type_checking_block_indent = NOT_INSIDE_TYPE_CHECKING_BLOCK
|
|
374
|
+
active_triple_quote_delimiter: str | None = None
|
|
391
375
|
|
|
392
376
|
for line_number, each_line in enumerate(lines, 1):
|
|
377
|
+
if active_triple_quote_delimiter is not None:
|
|
378
|
+
active_triple_quote_delimiter = _update_triple_quote_state_for_line(
|
|
379
|
+
each_line, active_triple_quote_delimiter
|
|
380
|
+
)
|
|
381
|
+
continue
|
|
382
|
+
|
|
393
383
|
stripped = each_line.strip()
|
|
394
384
|
|
|
395
385
|
if not stripped:
|
|
@@ -404,12 +394,18 @@ def check_imports_at_top(content: str) -> list[str]:
|
|
|
404
394
|
type_checking_match = TYPE_CHECKING_BLOCK_PATTERN.match(each_line)
|
|
405
395
|
if type_checking_match:
|
|
406
396
|
type_checking_block_indent = len(type_checking_match.group("indent"))
|
|
397
|
+
active_triple_quote_delimiter = _update_triple_quote_state_for_line(
|
|
398
|
+
each_line, active_triple_quote_delimiter
|
|
399
|
+
)
|
|
407
400
|
continue
|
|
408
401
|
|
|
409
402
|
function_match = re.match(r"^(\s*)(async\s+)?def\s+\w+", each_line)
|
|
410
403
|
if function_match:
|
|
411
404
|
is_inside_function = True
|
|
412
405
|
function_indent = len(function_match.group(1)) if function_match.group(1) else 0
|
|
406
|
+
active_triple_quote_delimiter = _update_triple_quote_state_for_line(
|
|
407
|
+
each_line, active_triple_quote_delimiter
|
|
408
|
+
)
|
|
413
409
|
continue
|
|
414
410
|
|
|
415
411
|
if is_inside_function:
|
|
@@ -421,9 +417,51 @@ def check_imports_at_top(content: str) -> list[str]:
|
|
|
421
417
|
if stripped.startswith(ALL_IMPORT_STATEMENT_PREFIXES):
|
|
422
418
|
issues.append(f"Line {line_number}: Import inside function - move to top of file")
|
|
423
419
|
|
|
420
|
+
active_triple_quote_delimiter = _update_triple_quote_state_for_line(
|
|
421
|
+
each_line, active_triple_quote_delimiter
|
|
422
|
+
)
|
|
423
|
+
|
|
424
424
|
return issues
|
|
425
425
|
|
|
426
426
|
|
|
427
|
+
def _update_triple_quote_state_for_line(
|
|
428
|
+
line_text: str, current_delimiter: str | None
|
|
429
|
+
) -> str | None:
|
|
430
|
+
"""Return the triple-quote delimiter that remains active after the line.
|
|
431
|
+
|
|
432
|
+
Naively counts triple-double-quote and triple-single-quote occurrences.
|
|
433
|
+
An odd count of either delimiter toggles the active state: ``None``
|
|
434
|
+
becomes that delimiter, the same delimiter becomes ``None``. Even counts
|
|
435
|
+
mean the line opens and closes the same delimiter in place (single-line
|
|
436
|
+
docstring or balanced pair) and the active state is unchanged.
|
|
437
|
+
|
|
438
|
+
Known limitation: the counter does not distinguish triple quotes that
|
|
439
|
+
appear inside other string contexts (for example, a raw f-string
|
|
440
|
+
containing the literal substring of triple quotes). Such constructs are
|
|
441
|
+
rare in docstring-bearing code; the false-negative risk is acceptable
|
|
442
|
+
to keep the line-walker simple and dependency-free.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
line_text: The raw source line whose triple-quote balance is being
|
|
446
|
+
integrated into the running state.
|
|
447
|
+
current_delimiter: The active delimiter at the start of this line,
|
|
448
|
+
or ``None`` when no multi-line string is open.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
The delimiter that remains active after this line, or ``None`` when
|
|
452
|
+
no string is open.
|
|
453
|
+
"""
|
|
454
|
+
if current_delimiter is not None:
|
|
455
|
+
if line_text.count(current_delimiter) % TRIPLE_QUOTE_PARITY_DIVISOR == 1:
|
|
456
|
+
return None
|
|
457
|
+
return current_delimiter
|
|
458
|
+
if line_text.count(TRIPLE_DOUBLE_QUOTE_DELIMITER) % TRIPLE_QUOTE_PARITY_DIVISOR == 1:
|
|
459
|
+
return TRIPLE_DOUBLE_QUOTE_DELIMITER
|
|
460
|
+
if line_text.count(TRIPLE_SINGLE_QUOTE_DELIMITER) % TRIPLE_QUOTE_PARITY_DIVISOR == 1:
|
|
461
|
+
return TRIPLE_SINGLE_QUOTE_DELIMITER
|
|
462
|
+
return None
|
|
463
|
+
|
|
464
|
+
|
|
427
465
|
def check_logging_fstrings(content: str) -> list[str]:
|
|
428
466
|
"""Check for f-strings in logging calls."""
|
|
429
467
|
issues = []
|
|
@@ -739,16 +777,96 @@ def _find_any_annotation_lines(source: str) -> list[int]:
|
|
|
739
777
|
return offending_line_numbers
|
|
740
778
|
|
|
741
779
|
|
|
742
|
-
def
|
|
743
|
-
"""
|
|
780
|
+
def _python_tokens(source: str) -> Iterator[tokenize.TokenInfo]:
|
|
781
|
+
"""Yield Python tokens from *source* one at a time.
|
|
782
|
+
|
|
783
|
+
Centralizes the ``tokenize.generate_tokens`` entry-point so a future
|
|
784
|
+
change to the API lands in exactly one place. Iteration may raise
|
|
785
|
+
any of ``ALL_PYTHON_TOKENIZE_FAILURE_EXCEPTIONS`` when the source is
|
|
786
|
+
not valid Python (mid-edit Edit fragments, unterminated strings,
|
|
787
|
+
mismatched indentation) — callers handle the exception according to
|
|
788
|
+
their own contract (silently stop, return an indeterminate flag, etc.).
|
|
789
|
+
"""
|
|
790
|
+
yield from tokenize.generate_tokens(io.StringIO(source).readline)
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
def _comment_tokens(source: str) -> Iterator[tokenize.TokenInfo]:
|
|
794
|
+
"""Yield COMMENT tokens from *source* one at a time.
|
|
795
|
+
|
|
796
|
+
Streams from ``_python_tokens`` so consumers that early-exit (e.g.
|
|
797
|
+
``check_comments_python`` caps at ``MAX_COMMENT_ISSUES``) avoid
|
|
798
|
+
materializing the entire token list. Silently stops on tokenize
|
|
799
|
+
failure so callers receive only valid comment tokens — no
|
|
800
|
+
indeterminate signal is exposed at this layer because the consumers
|
|
801
|
+
that need it (``_extract_python_comment_sets``) bypass this helper.
|
|
802
|
+
"""
|
|
744
803
|
try:
|
|
745
|
-
|
|
746
|
-
each_token
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
804
|
+
for each_token in _python_tokens(source):
|
|
805
|
+
if each_token.type == tokenize.COMMENT:
|
|
806
|
+
yield each_token
|
|
807
|
+
except ALL_PYTHON_TOKENIZE_FAILURE_EXCEPTIONS:
|
|
808
|
+
return
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
def _is_exempt_python_comment(comment_token: tokenize.TokenInfo) -> bool:
|
|
812
|
+
"""Return True for shebangs and tooling-directive comments.
|
|
813
|
+
|
|
814
|
+
The shebang exemption applies only when the comment token starts
|
|
815
|
+
at line 1, column 0 — matching the OS-level convention that a
|
|
816
|
+
shebang line is meaningful only as the first line of an executable
|
|
817
|
+
file. An inline shebang-lookalike later in the file (an
|
|
818
|
+
after-code occurrence on any line, or a standalone occurrence on
|
|
819
|
+
line 2 or later) is NOT a real shebang and remains subject to the
|
|
820
|
+
no-comments rule.
|
|
821
|
+
|
|
822
|
+
Matches any prefix listed in ``ALL_EXEMPT_PYTHON_COMMENT_BODIES``
|
|
823
|
+
regardless of whether the directive sits flush against the leading
|
|
824
|
+
hash character or carries one or more whitespace characters (space
|
|
825
|
+
or tab) between the hash and the directive body. The pre-tokenize
|
|
826
|
+
implementation accepted both forms via its line-slice-then-strip
|
|
827
|
+
step; this helper preserves that behavior on top of the
|
|
828
|
+
tokenize-based scan.
|
|
829
|
+
"""
|
|
830
|
+
comment_string = comment_token.string
|
|
831
|
+
if comment_string.startswith("#!") and comment_token.start == (1, 0):
|
|
832
|
+
return True
|
|
833
|
+
directive_body = comment_string[1:].lstrip()
|
|
834
|
+
if not directive_body:
|
|
835
|
+
return True
|
|
836
|
+
return directive_body.startswith(ALL_EXEMPT_PYTHON_COMMENT_BODIES)
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
def _extract_python_comment_sets(content: str) -> tuple[set[str], set[str], bool]:
|
|
840
|
+
"""Return (inline_comments, standalone_comments, tokenize_succeeded).
|
|
841
|
+
|
|
842
|
+
Streams *content* once via ``_python_tokens``. A tokenize failure
|
|
843
|
+
(mid-edit fragment, syntax error) returns empty sets and ``False``
|
|
844
|
+
so callers can treat the situation as indeterminate rather than as
|
|
845
|
+
"no comments present". Inline vs standalone is decided by inspecting
|
|
846
|
+
the column offset of each ``COMMENT`` token against its source
|
|
847
|
+
line: an all-whitespace prefix means standalone.
|
|
848
|
+
"""
|
|
849
|
+
inline_comments: set[str] = set()
|
|
850
|
+
standalone_comments: set[str] = set()
|
|
851
|
+
lines = content.split("\n")
|
|
852
|
+
try:
|
|
853
|
+
for each_token in _python_tokens(content):
|
|
854
|
+
if each_token.type != tokenize.COMMENT:
|
|
855
|
+
continue
|
|
856
|
+
if _is_exempt_python_comment(each_token):
|
|
857
|
+
continue
|
|
858
|
+
line_number = each_token.start[0]
|
|
859
|
+
column_offset = each_token.start[1]
|
|
860
|
+
source_line = lines[line_number - 1] if line_number - 1 < len(lines) else ""
|
|
861
|
+
text_before_comment = source_line[:column_offset]
|
|
862
|
+
normalized_comment_text = each_token.string.strip()
|
|
863
|
+
if not text_before_comment.strip():
|
|
864
|
+
standalone_comments.add(normalized_comment_text)
|
|
865
|
+
else:
|
|
866
|
+
inline_comments.add(normalized_comment_text)
|
|
867
|
+
except ALL_PYTHON_TOKENIZE_FAILURE_EXCEPTIONS:
|
|
868
|
+
return set(), set(), False
|
|
869
|
+
return inline_comments, standalone_comments, True
|
|
752
870
|
|
|
753
871
|
|
|
754
872
|
def _find_unjustified_type_ignore_lines(source: str) -> list[int]:
|
|
@@ -11,8 +11,15 @@ the canonical implementation lives here.
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
+
import sys
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
|
|
17
|
+
hooks_root_directory = str(Path(__file__).resolve().parent.parent)
|
|
18
|
+
if hooks_root_directory not in sys.path:
|
|
19
|
+
sys.path.insert(0, hooks_root_directory)
|
|
20
|
+
|
|
21
|
+
from hooks_constants.code_rules_path_utils_constants import ALL_CONFIG_DIRECTORY_NAMES # noqa: E402
|
|
22
|
+
|
|
16
23
|
|
|
17
24
|
def is_config_file(file_path: str) -> bool:
|
|
18
25
|
"""Return True when the path points to a config file.
|
|
@@ -28,4 +35,4 @@ def is_config_file(file_path: str) -> bool:
|
|
|
28
35
|
if normalized.endswith("/settings.py") or normalized == "settings.py":
|
|
29
36
|
return True
|
|
30
37
|
path_parts = Path(normalized).parts
|
|
31
|
-
return
|
|
38
|
+
return any(directory_segment in ALL_CONFIG_DIRECTORY_NAMES for directory_segment in path_parts[:-1])
|
|
@@ -13,7 +13,7 @@ _hooks_dir = str(Path(__file__).resolve().parent.parent)
|
|
|
13
13
|
if _hooks_dir not in sys.path:
|
|
14
14
|
sys.path.insert(0, _hooks_dir)
|
|
15
15
|
|
|
16
|
-
from
|
|
16
|
+
from hooks_constants.convergence_branch_constants import ( # noqa: E402
|
|
17
17
|
ALL_CONVERGENCE_BRANCH_PREFIXES,
|
|
18
18
|
CONVERGENCE_BRANCH_SUFFIX_PATTERN,
|
|
19
19
|
CONVERGENCE_FORCE_PUSH_DETECTION_PATTERN,
|
|
@@ -15,25 +15,19 @@ import re
|
|
|
15
15
|
import sys
|
|
16
16
|
from pathlib import Path, PurePosixPath, PureWindowsPath
|
|
17
17
|
|
|
18
|
+
_hooks_dir = str(Path(__file__).resolve().parent.parent)
|
|
19
|
+
if _hooks_dir not in sys.path:
|
|
20
|
+
sys.path.insert(0, _hooks_dir)
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if hooks_tree_string not in sys.path:
|
|
23
|
-
sys.path.insert(0, hooks_tree_string)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
_insert_hooks_tree_for_imports()
|
|
27
|
-
|
|
28
|
-
from config.dynamic_stderr_handler import DynamicStderrHandler
|
|
29
|
-
from config.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
|
|
30
|
-
from config.path_rewriter_constants import (
|
|
22
|
+
from hooks_constants.dynamic_stderr_handler import DynamicStderrHandler # noqa: E402
|
|
23
|
+
from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin # noqa: E402
|
|
24
|
+
from hooks_constants.path_rewriter_constants import ( # noqa: E402
|
|
31
25
|
BASH_TOOL_NAME,
|
|
32
26
|
HOOK_EVENT_NAME,
|
|
33
27
|
PERMISSION_ALLOW,
|
|
34
28
|
PLACEHOLDER_TOKEN_PATTERN,
|
|
35
29
|
)
|
|
36
|
-
from
|
|
30
|
+
from hooks_constants.project_paths_reader import load_registry # noqa: E402
|
|
37
31
|
|
|
38
32
|
_ES_EXE_TRIGGER_PATTERN = re.compile(
|
|
39
33
|
r"(?i)(?<![\w.])(?:Everything[/\\])?es\.exe(?![\w.])",
|
|
@@ -22,8 +22,13 @@ pattern this hook must catch); otherwise approves unparseable input.
|
|
|
22
22
|
import json
|
|
23
23
|
import re
|
|
24
24
|
import sys
|
|
25
|
+
from pathlib import Path
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
_hooks_dir = str(Path(__file__).resolve().parent.parent)
|
|
28
|
+
if _hooks_dir not in sys.path:
|
|
29
|
+
sys.path.insert(0, _hooks_dir)
|
|
30
|
+
|
|
31
|
+
from blocking._gh_body_arg_utils import ( # noqa: E402
|
|
27
32
|
_is_bash_continuation,
|
|
28
33
|
all_body_flags,
|
|
29
34
|
all_body_flag_prefixes,
|
|
@@ -33,11 +33,11 @@ import subprocess
|
|
|
33
33
|
import sys
|
|
34
34
|
from pathlib import Path
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
if
|
|
38
|
-
sys.path.insert(0,
|
|
36
|
+
hooks_parent_directory = str(Path(__file__).resolve().parent.parent)
|
|
37
|
+
if hooks_parent_directory not in sys.path:
|
|
38
|
+
sys.path.insert(0, hooks_parent_directory)
|
|
39
39
|
|
|
40
|
-
from _gh_pr_author_swap_utils import ( # noqa: E402
|
|
40
|
+
from _gh_pr_author_swap_utils import ( # noqa: E402
|
|
41
41
|
_all_gh_pr_create_segments,
|
|
42
42
|
_command_invokes_gh_pr_create_in_stripped,
|
|
43
43
|
_delete_state_file,
|
|
@@ -47,7 +47,7 @@ from _gh_pr_author_swap_utils import ( # noqa: E402 # sys.path shim above must
|
|
|
47
47
|
_switch_gh_account,
|
|
48
48
|
_write_line,
|
|
49
49
|
)
|
|
50
|
-
from
|
|
50
|
+
from hooks_constants.gh_pr_author_swap_constants import (
|
|
51
51
|
ALL_GH_API_USER_COMMAND,
|
|
52
52
|
BASH_TOOL_NAME,
|
|
53
53
|
GH_API_USER_TIMEOUT_SECONDS,
|
|
@@ -31,11 +31,11 @@ import json
|
|
|
31
31
|
import sys
|
|
32
32
|
from pathlib import Path
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
if
|
|
36
|
-
sys.path.insert(0,
|
|
34
|
+
hooks_parent_directory = str(Path(__file__).resolve().parent.parent)
|
|
35
|
+
if hooks_parent_directory not in sys.path:
|
|
36
|
+
sys.path.insert(0, hooks_parent_directory)
|
|
37
37
|
|
|
38
|
-
from _gh_pr_author_swap_utils import ( # noqa: E402
|
|
38
|
+
from _gh_pr_author_swap_utils import ( # noqa: E402
|
|
39
39
|
_command_invokes_gh_pr_create_in_stripped,
|
|
40
40
|
_delete_state_file,
|
|
41
41
|
_preprocess_command_for_matching,
|
|
@@ -45,7 +45,7 @@ from _gh_pr_author_swap_utils import ( # noqa: E402 # sys.path shim above must
|
|
|
45
45
|
_switch_gh_account,
|
|
46
46
|
_write_line,
|
|
47
47
|
)
|
|
48
|
-
from
|
|
48
|
+
from hooks_constants.gh_pr_author_swap_constants import BASH_TOOL_NAME # noqa: E402
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
def main() -> None:
|
|
@@ -12,17 +12,11 @@ import re
|
|
|
12
12
|
import sys
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
|
+
_hooks_dir = str(Path(__file__).resolve().parent.parent)
|
|
16
|
+
if _hooks_dir not in sys.path:
|
|
17
|
+
sys.path.insert(0, _hooks_dir)
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
hooks_tree = Path(__file__).resolve().parent.parent
|
|
18
|
-
hooks_tree_string = str(hooks_tree)
|
|
19
|
-
if hooks_tree_string not in sys.path:
|
|
20
|
-
sys.path.insert(0, hooks_tree_string)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
_insert_hooks_tree_for_imports()
|
|
24
|
-
|
|
25
|
-
from config.messages import USER_FACING_NOTICE
|
|
19
|
+
from hooks_constants.messages import USER_FACING_NOTICE # noqa: E402
|
|
26
20
|
|
|
27
21
|
PLUGIN_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
28
22
|
|