claude-dev-env 1.42.0 → 1.44.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.
Files changed (208) hide show
  1. package/_shared/pr-loop/scripts/_claude_permissions_common.py +1 -5
  2. package/_shared/pr-loop/scripts/code_rules_gate.py +293 -8
  3. package/_shared/pr-loop/scripts/fix_hookspath.py +96 -5
  4. package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +3 -16
  5. package/_shared/pr-loop/scripts/post_audit_thread.py +4 -4
  6. package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/claude_permissions_constants.py +1 -1
  7. package/_shared/pr-loop/scripts/preflight.py +13 -31
  8. package/_shared/pr-loop/scripts/reviews_disabled.py +2 -16
  9. package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +3 -16
  10. package/_shared/pr-loop/scripts/tests/conftest.py +1 -51
  11. package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +4 -4
  12. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +4 -2
  13. package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +4 -2
  14. package/_shared/pr-loop/scripts/tests/test_claude_settings_keys_constants.py +4 -2
  15. package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +4 -2
  16. package/_shared/pr-loop/scripts/tests/test_fix_hookspath_constants.py +6 -2
  17. package/_shared/pr-loop/scripts/tests/test_grant_project_claude_permissions.py +2 -2
  18. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +1 -2
  19. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +4 -2
  20. package/_shared/pr-loop/scripts/tests/test_preflight.py +17 -52
  21. package/_shared/pr-loop/scripts/tests/test_preflight_constants.py +6 -2
  22. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +2 -2
  23. package/agents/pr-description-writer.md +50 -140
  24. package/docs/PR_DESCRIPTION_GUIDE.md +101 -102
  25. package/hooks/_gh_pr_author_swap_utils.py +1 -1
  26. package/hooks/blocking/bot_mention_comment_blocker.py +4 -10
  27. package/hooks/blocking/code_rules_enforcer.py +217 -99
  28. package/hooks/blocking/code_rules_path_utils.py +8 -1
  29. package/hooks/blocking/destructive_command_blocker.py +1 -1
  30. package/hooks/blocking/es_exe_path_rewriter.py +7 -13
  31. package/hooks/blocking/gh_body_arg_blocker.py +6 -1
  32. package/hooks/blocking/gh_pr_author_enforcer.py +5 -5
  33. package/hooks/blocking/gh_pr_author_restore.py +5 -5
  34. package/hooks/blocking/hedging_language_blocker.py +4 -10
  35. package/hooks/blocking/md_path_exemptions.py +205 -0
  36. package/hooks/blocking/md_to_html_blocker.py +48 -20
  37. package/hooks/blocking/pr_converge_bugteam_enforcer.py +5 -11
  38. package/hooks/blocking/pr_description_enforcer.py +626 -41
  39. package/hooks/blocking/question_to_user_enforcer.py +4 -10
  40. package/hooks/blocking/state_description_blocker.py +6 -12
  41. package/hooks/blocking/tdd_enforcer.py +1 -1
  42. package/hooks/blocking/test_bot_mention_comment_blocker.py +1 -1
  43. package/hooks/blocking/test_code_rules_enforcer.py +3 -3
  44. package/hooks/blocking/test_code_rules_enforcer_any_exempt_files.py +1 -1
  45. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -2
  46. package/hooks/blocking/test_code_rules_enforcer_comment_string_awareness.py +184 -0
  47. package/hooks/blocking/test_code_rules_enforcer_type_checking_scope.py +82 -0
  48. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +29 -29
  49. package/hooks/blocking/test_gh_body_arg_blocker.py +7 -8
  50. package/hooks/blocking/test_gh_pr_author_enforcer.py +1 -1
  51. package/hooks/blocking/test_gh_pr_author_restore.py +1 -1
  52. package/hooks/blocking/test_hedging_language_blocker.py +2 -2
  53. package/hooks/blocking/test_md_to_html_blocker.py +463 -8
  54. package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +1 -1
  55. package/hooks/blocking/test_pr_description_enforcer.py +1210 -13
  56. package/hooks/blocking/test_question_to_user_enforcer.py +1 -1
  57. package/hooks/blocking/windows_rmtree_blocker.py +5 -11
  58. package/hooks/diagnostic/hook_log_extractor.py +1 -1
  59. package/hooks/diagnostic/hook_log_init.py +1 -1
  60. package/hooks/diagnostic/hook_log_stop_wrapper.py +1 -1
  61. package/hooks/diagnostic/test_hook_log_extractor.py +1 -1
  62. package/hooks/diagnostic/test_hook_log_init.py +2 -2
  63. package/hooks/diagnostic/test_hook_log_stop_wrapper.py +1 -1
  64. package/hooks/git-hooks/gate_utils.py +1 -1
  65. package/hooks/git-hooks/pre_commit.py +1 -1
  66. package/hooks/git-hooks/pre_push.py +1 -1
  67. package/hooks/git-hooks/test_config.py +5 -5
  68. package/hooks/git-hooks/test_pre_push.py +6 -6
  69. package/hooks/{config → hooks_constants}/code_rules_enforcer_constants.py +37 -0
  70. package/hooks/hooks_constants/code_rules_path_utils_constants.py +28 -0
  71. package/hooks/hooks_constants/md_to_html_blocker_constants.py +82 -0
  72. package/hooks/{config → hooks_constants}/pr_converge_bugteam_enforcer_state.py +1 -1
  73. package/hooks/hooks_constants/pr_description_enforcer_constants.py +154 -0
  74. package/hooks/{config → hooks_constants}/pre_tool_use_stdin.py +1 -1
  75. package/hooks/{config → hooks_constants}/project_paths_reader.py +2 -2
  76. package/hooks/{config → hooks_constants}/test_banned_identifiers_constants.py +1 -1
  77. package/hooks/{config → hooks_constants}/test_dynamic_stderr_handler.py +1 -1
  78. package/hooks/{config → hooks_constants}/test_hardcoded_user_path_constants.py +1 -1
  79. package/hooks/{config → hooks_constants}/test_hook_log_extractor_constants.py +2 -2
  80. package/hooks/hooks_constants/test_md_to_html_blocker_constants.py +110 -0
  81. package/hooks/{config → hooks_constants}/test_messages.py +2 -6
  82. package/hooks/{config → hooks_constants}/test_path_rewriter_constants.py +1 -1
  83. package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +292 -0
  84. package/hooks/{config → hooks_constants}/test_pre_tool_use_stdin.py +2 -2
  85. package/hooks/{config → hooks_constants}/test_project_paths_reader.py +3 -3
  86. package/hooks/{config → hooks_constants}/test_session_env_cleanup_constants.py +1 -1
  87. package/hooks/{config → hooks_constants}/test_setup_project_paths_constants.py +2 -2
  88. package/hooks/{config → hooks_constants}/test_unused_module_import_constants.py +1 -1
  89. package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +5 -11
  90. package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +1 -1
  91. package/hooks/session/gh_pr_author_session_cleanup.py +5 -6
  92. package/hooks/session/session_env_cleanup.py +4 -10
  93. package/hooks/session/test_gh_pr_author_session_cleanup.py +1 -1
  94. package/hooks/session/test_untracked_repo_detector.py +2 -2
  95. package/hooks/session/untracked_repo_detector.py +6 -12
  96. package/hooks/test__gh_pr_author_swap_utils.py +1 -1
  97. package/hooks/validators/run_all_validators.py +16 -5
  98. package/hooks/validators/test_output_formatter.py +46 -0
  99. package/hooks/workflow/doc_gist_auto_publish.py +1 -1
  100. package/hooks/workflow/md_to_html_companion.py +8 -15
  101. package/hooks/workflow/test_md_to_html_companion.py +184 -23
  102. package/package.json +1 -1
  103. package/rules/ask-user-question-required.md +1 -1
  104. package/rules/vault-context.md +1 -1
  105. package/scripts/{config → dev_env_scripts_constants}/timing.py +1 -1
  106. package/scripts/setup_project_paths.py +49 -11
  107. package/scripts/sweep_empty_dirs.py +10 -1
  108. package/scripts/test_setup_project_paths.py +2 -2
  109. package/scripts/test_sweep_empty_dirs.py +2 -6
  110. package/skills/_shared/pr-loop/scripts/_path_resolver.py +1 -1
  111. package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +1 -1
  112. package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +1 -1
  113. package/skills/_shared/pr-loop/scripts/init_loop_state.py +1 -1
  114. package/skills/_shared/pr-loop/scripts/teardown_worktrees.py +1 -1
  115. package/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +2 -2
  116. package/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +2 -2
  117. package/skills/bugteam/PROMPTS.md +1 -1
  118. package/skills/bugteam/SKILL.md +1 -1
  119. package/skills/bugteam/reference/github-pr-reviews.md +1 -1
  120. package/skills/bugteam/scripts/{_claude_permissions_common.py → _bugteam_permissions_common.py} +1 -13
  121. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +1 -13
  122. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +1 -16
  123. package/skills/bugteam/scripts/bugteam_preflight.py +1 -13
  124. package/skills/bugteam/scripts/grant_project_claude_permissions.py +2 -8
  125. package/skills/bugteam/scripts/probe_code_rules_enforcer_check.py +1 -1
  126. package/skills/bugteam/scripts/reflow_skill_md.py +1 -1
  127. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +2 -8
  128. package/skills/bugteam/scripts/{test__claude_permissions_common.py → test__bugteam_permissions_common.py} +4 -4
  129. package/skills/bugteam/scripts/test_agent_config_carveout.py +2 -2
  130. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -26
  131. package/skills/bugteam/scripts/{test_claude_permissions_common.py → test_bugteam_permissions_common.py} +3 -66
  132. package/skills/bugteam/scripts/test_bugteam_preflight.py +2 -27
  133. package/skills/bugteam/scripts/windows_safe_rmtree.py +1 -1
  134. package/skills/doc-gist/SKILL.md +1 -1
  135. package/skills/doc-gist/scripts/gist_upload.py +1 -1
  136. package/skills/implement/SKILL.md +2 -2
  137. package/skills/implement/scripts/append_note.py +1 -1
  138. package/skills/pr-converge/pr_converge_skill_constants/__init__.py +0 -0
  139. package/skills/pr-converge/{config → pr_converge_skill_constants}/constants.py +1 -1
  140. package/skills/pr-converge/scripts/check_bugbot_ci.py +1 -1
  141. package/skills/pr-converge/scripts/check_convergence.py +11 -4
  142. package/skills/pr-converge/scripts/check_pending_reviews.py +1 -1
  143. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +1 -1
  144. package/skills/pr-converge/scripts/post_fix_reply.py +1 -1
  145. package/skills/pr-converge/scripts/pr_converge_scripts_constants/__init__.py +0 -0
  146. package/skills/pr-converge/scripts/{config → pr_converge_scripts_constants}/pr_converge_constants.py +1 -1
  147. package/skills/pr-converge/scripts/reflow_skill_md.py +90 -16
  148. package/skills/pr-converge/scripts/test_check_convergence.py +18 -0
  149. package/skills/pr-converge/scripts/test_reflow_skill_md.py +0 -31
  150. package/skills/pre-compact/SKILL.md +114 -0
  151. package/skills/session-log/SKILL.md +98 -233
  152. package/hooks/config/pr_description_enforcer_constants.py +0 -19
  153. package/hooks/config/test_pr_description_enforcer_constants.py +0 -82
  154. package/skills/bugteam/scripts/test_grant_project_claude_permissions.py +0 -55
  155. package/skills/bugteam/scripts/test_revoke_project_claude_permissions.py +0 -55
  156. package/skills/pr-converge/scripts/conftest.py +0 -60
  157. package/skills/pr-converge/scripts/evict_cached_config_modules.py +0 -20
  158. package/skills/pr-converge/scripts/test_evict_cached_config_modules.py +0 -22
  159. /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/__init__.py +0 -0
  160. /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/claude_settings_keys_constants.py +0 -0
  161. /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/code_rules_gate_constants.py +0 -0
  162. /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/fix_hookspath_constants.py +0 -0
  163. /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/post_audit_thread_constants.py +0 -0
  164. /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/preflight_constants.py +0 -0
  165. /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/reviews_disabled_constants.py +0 -0
  166. /package/hooks/git-hooks/{config.py → git_hooks_constants/__init__.py} +0 -0
  167. /package/hooks/{config → hooks_constants}/__init__.py +0 -0
  168. /package/hooks/{config → hooks_constants}/any_type_config.py +0 -0
  169. /package/hooks/{config → hooks_constants}/banned_identifiers_constants.py +0 -0
  170. /package/hooks/{config → hooks_constants}/blocking_check_limits.py +0 -0
  171. /package/hooks/{config → hooks_constants}/bot_mention_comment_blocker_constants.py +0 -0
  172. /package/hooks/{config → hooks_constants}/convergence_branch_constants.py +0 -0
  173. /package/hooks/{config → hooks_constants}/doc_gist_auto_publish_constants.py +0 -0
  174. /package/hooks/{config → hooks_constants}/dynamic_stderr_handler.py +0 -0
  175. /package/hooks/{config → hooks_constants}/gh_pr_author_swap_constants.py +0 -0
  176. /package/hooks/{config → hooks_constants}/hardcoded_user_path_constants.py +0 -0
  177. /package/hooks/{config → hooks_constants}/hook_log_extractor_constants.py +0 -0
  178. /package/hooks/{config → hooks_constants}/html_companion_constants.py +0 -0
  179. /package/hooks/{config → hooks_constants}/inline_tuple_string_magic_constants.py +0 -0
  180. /package/hooks/{config → hooks_constants}/messages.py +0 -0
  181. /package/hooks/{config → hooks_constants}/path_rewriter_constants.py +0 -0
  182. /package/hooks/{config → hooks_constants}/pr_converge_bugteam_enforcer_constants.py +0 -0
  183. /package/hooks/{config → hooks_constants}/session_env_cleanup_constants.py +0 -0
  184. /package/hooks/{config → hooks_constants}/setup_project_paths_constants.py +0 -0
  185. /package/hooks/{config → hooks_constants}/state_description_blocker_constants.py +0 -0
  186. /package/hooks/{config → hooks_constants}/stuttering_check_config.py +0 -0
  187. /package/hooks/{config → hooks_constants}/stuttering_import_binding_constants.py +0 -0
  188. /package/hooks/{config → hooks_constants}/sys_path_insert_constants.py +0 -0
  189. /package/hooks/{config → hooks_constants}/unused_module_import_constants.py +0 -0
  190. /package/hooks/{config → hooks_constants}/windows_rmtree_blocker_constants.py +0 -0
  191. /package/{skills/_shared/pr-loop/scripts/config → hooks/lifecycle}/__init__.py +0 -0
  192. /package/{skills/bugteam/scripts/config → hooks/session}/__init__.py +0 -0
  193. /package/scripts/{config → dev_env_scripts_constants}/__init__.py +0 -0
  194. /package/skills/{doc-gist/scripts/config → _shared/pr-loop/scripts/skills_pr_loop_constants}/__init__.py +0 -0
  195. /package/skills/_shared/pr-loop/scripts/{config → skills_pr_loop_constants}/path_resolver_constants.py +0 -0
  196. /package/skills/{implement/scripts/config → bugteam/scripts/bugteam_scripts_constants}/__init__.py +0 -0
  197. /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_code_rules_gate_constants.py +0 -0
  198. /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_fix_hookspath_constants.py +0 -0
  199. /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_preflight_constants.py +0 -0
  200. /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/claude_permissions_common_constants.py +0 -0
  201. /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/probe_code_rules_enforcer_check_constants.py +0 -0
  202. /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/reflow_skill_md_constants.py +0 -0
  203. /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/windows_safe_rmtree_constants.py +0 -0
  204. /package/skills/{pr-converge/config → doc-gist/scripts/doc_gist_scripts_constants}/__init__.py +0 -0
  205. /package/skills/doc-gist/scripts/{config → doc_gist_scripts_constants}/gist_upload_constants.py +0 -0
  206. /package/skills/{pr-converge/scripts/config → implement/scripts/implement_scripts_constants}/__init__.py +0 -0
  207. /package/skills/implement/scripts/{config → implement_scripts_constants}/notes_constants.py +0 -0
  208. /package/skills/pr-converge/scripts/{config → pr_converge_scripts_constants}/reflow_skill_md_constants.py +0 -0
@@ -9,23 +9,10 @@ the changes applied. No-op when the entries already exist.
9
9
  import sys
10
10
  from pathlib import Path
11
11
 
12
- parent_directory = str(Path(__file__).absolute().parent)
13
- try:
14
- sys.path.remove(parent_directory)
15
- except ValueError:
16
- pass
12
+ parent_directory = str(Path(__file__).resolve().parent)
17
13
  if parent_directory not in sys.path:
18
14
  sys.path.insert(0, parent_directory)
19
15
 
20
- for each_cached_module_name in [
21
- each_module_key
22
- for each_module_key in list(sys.modules)
23
- if each_module_key == "config"
24
- or each_module_key.startswith("config.")
25
- or each_module_key == "_claude_permissions_common"
26
- ]:
27
- sys.modules.pop(each_cached_module_name, None)
28
-
29
16
  from _claude_permissions_common import ( # noqa: E402
30
17
  append_if_missing,
31
18
  build_agent_config_deny_rules,
@@ -40,7 +27,7 @@ from _claude_permissions_common import ( # noqa: E402
40
27
  remove_matching_entries_from_list,
41
28
  save_settings,
42
29
  )
43
- from config.claude_permissions_constants import ( # noqa: E402
30
+ from pr_loop_shared_constants.claude_permissions_constants import (
44
31
  ALL_AGENT_CONFIG_DENY_TOOLS,
45
32
  ALL_AGENT_CONFIG_PATH_PATTERNS,
46
33
  ALL_PERMISSION_ALLOW_TOOLS,
@@ -48,7 +35,7 @@ from config.claude_permissions_constants import ( # noqa: E402
48
35
  AUTO_MODE_ENVIRONMENT_ENTRY_TEMPLATE,
49
36
  get_claude_user_settings_path,
50
37
  )
51
- from config.claude_settings_keys_constants import ( # noqa: E402
38
+ from pr_loop_shared_constants.claude_settings_keys_constants import (
52
39
  CLAUDE_SETTINGS_ADDITIONAL_DIRECTORIES_KEY,
53
40
  CLAUDE_SETTINGS_ALLOW_KEY,
54
41
  CLAUDE_SETTINGS_AUTO_MODE_KEY,
@@ -32,11 +32,11 @@ import urllib.request
32
32
  from pathlib import Path
33
33
  from typing import NoReturn
34
34
 
35
- sys.modules.pop("config", None)
36
- if str(Path(__file__).absolute().parent) not in sys.path:
37
- sys.path.insert(0, str(Path(__file__).absolute().parent))
35
+ parent_directory = str(Path(__file__).resolve().parent)
36
+ if parent_directory not in sys.path:
37
+ sys.path.insert(0, parent_directory)
38
38
 
39
- from config.post_audit_thread_constants import (
39
+ from pr_loop_shared_constants.post_audit_thread_constants import ( # noqa: E402
40
40
  ALL_GH_API_COMMAND_PARTS,
41
41
  ALL_GH_API_USER_COMMAND_PARTS,
42
42
  ALL_GH_AUTH_STATUS_COMMAND_PARTS,
@@ -2,7 +2,7 @@
2
2
 
3
3
  from pathlib import Path
4
4
 
5
- from config.preflight_constants import GIT_DIRECTORY_NAME
5
+ from pr_loop_shared_constants.preflight_constants import GIT_DIRECTORY_NAME
6
6
 
7
7
  __all__ = (
8
8
  "ALL_AGENT_CONFIG_DENY_TOOLS",
@@ -4,38 +4,20 @@ import subprocess
4
4
  import sys
5
5
  from pathlib import Path
6
6
 
7
- sys.modules.pop("config", None)
8
- _script_directory_resolved = Path(__file__).resolve().parent
9
- _script_directory_absolute = Path(__file__).absolute().parent
10
-
7
+ parent_directory = str(Path(__file__).resolve().parent)
8
+ sys.path[:] = [
9
+ each_existing_entry
10
+ for each_existing_entry in sys.path
11
+ if not (
12
+ os.path.exists(each_existing_entry)
13
+ and os.path.samefile(each_existing_entry, parent_directory)
14
+ )
15
+ ]
16
+ if parent_directory not in sys.path:
17
+ sys.path.insert(0, parent_directory)
11
18
 
12
- def _entry_points_at_preflight_script_directory(each_path_entry: str) -> bool:
13
- if each_path_entry in (
14
- str(_script_directory_resolved),
15
- str(_script_directory_absolute),
16
- ):
17
- return True
18
- try:
19
- candidate_path = Path(each_path_entry)
20
- except (OSError, ValueError):
21
- return False
22
- if candidate_path.exists():
23
- try:
24
- return os.path.samefile(candidate_path, _script_directory_resolved)
25
- except OSError:
26
- return False
27
- return False
28
-
29
-
30
- for each_index in range(len(sys.path) - 1, -1, -1):
31
- if _entry_points_at_preflight_script_directory(sys.path[each_index]):
32
- sys.path.pop(each_index)
33
- _preflight_scripts_path_entry = str(_script_directory_absolute)
34
- if _preflight_scripts_path_entry not in sys.path:
35
- sys.path.insert(0, _preflight_scripts_path_entry)
36
-
37
- from config.fix_hookspath_constants import HOOKS_PATH_VERIFICATION_SUFFIX
38
- from config.preflight_constants import (
19
+ from pr_loop_shared_constants.fix_hookspath_constants import HOOKS_PATH_VERIFICATION_SUFFIX # noqa: E402
20
+ from pr_loop_shared_constants.preflight_constants import (
39
21
  ALL_GIT_CONFIG_GET_CORE_HOOKS_PATH_SUBCOMMAND,
40
22
  ALL_GIT_DIFF_NAME_ONLY_SUBCOMMAND,
41
23
  ALL_GIT_LS_FILES_TEST_DISCOVERY_SUBCOMMAND,
@@ -8,22 +8,8 @@ rules and disabled-token taxonomy live in exactly one place.
8
8
  from __future__ import annotations
9
9
 
10
10
  import os
11
- import sys
12
- from pathlib import Path
13
-
14
- for each_cached_module_name in [
15
- each_module_key
16
- for each_module_key in list(sys.modules)
17
- if each_module_key == "config" or each_module_key.startswith("config.")
18
- ]:
19
- sys.modules.pop(each_cached_module_name, None)
20
- _shared_pr_loop_scripts_directory = str(Path(__file__).absolute().parent)
21
- while _shared_pr_loop_scripts_directory in sys.path:
22
- sys.path.remove(_shared_pr_loop_scripts_directory)
23
- if _shared_pr_loop_scripts_directory not in sys.path:
24
- sys.path.insert(0, _shared_pr_loop_scripts_directory)
25
-
26
- from config.reviews_disabled_constants import (
11
+
12
+ from pr_loop_shared_constants.reviews_disabled_constants import (
27
13
  CLAUDE_REVIEWS_DISABLED_BUGTEAM_TOKEN,
28
14
  CLAUDE_REVIEWS_DISABLED_ENV_VAR_NAME,
29
15
  CLAUDE_REVIEWS_DISABLED_TOKEN_SEPARATOR,
@@ -10,23 +10,10 @@ autoMode sections so repeated grant/revoke cycles leave no dead structure.
10
10
  import sys
11
11
  from pathlib import Path
12
12
 
13
- parent_directory = str(Path(__file__).absolute().parent)
14
- try:
15
- sys.path.remove(parent_directory)
16
- except ValueError:
17
- pass
13
+ parent_directory = str(Path(__file__).resolve().parent)
18
14
  if parent_directory not in sys.path:
19
15
  sys.path.insert(0, parent_directory)
20
16
 
21
- for each_cached_module_name in [
22
- each_module_key
23
- for each_module_key in list(sys.modules)
24
- if each_module_key == "config"
25
- or each_module_key.startswith("config.")
26
- or each_module_key == "_claude_permissions_common"
27
- ]:
28
- sys.modules.pop(each_cached_module_name, None)
29
-
30
17
  from _claude_permissions_common import ( # noqa: E402
31
18
  build_agent_config_deny_rules,
32
19
  build_permission_rules,
@@ -39,14 +26,14 @@ from _claude_permissions_common import ( # noqa: E402
39
26
  remove_matching_entries_from_list,
40
27
  save_settings,
41
28
  )
42
- from config.claude_permissions_constants import ( # noqa: E402
29
+ from pr_loop_shared_constants.claude_permissions_constants import (
43
30
  ALL_AGENT_CONFIG_DENY_TOOLS,
44
31
  ALL_AGENT_CONFIG_PATH_PATTERNS,
45
32
  ALL_PERMISSION_ALLOW_TOOLS,
46
33
  AUTO_MODE_ENVIRONMENT_ENTRY_PREFIX,
47
34
  get_claude_user_settings_path,
48
35
  )
49
- from config.claude_settings_keys_constants import ( # noqa: E402
36
+ from pr_loop_shared_constants.claude_settings_keys_constants import (
50
37
  CLAUDE_SETTINGS_ADDITIONAL_DIRECTORIES_KEY,
51
38
  CLAUDE_SETTINGS_ALLOW_KEY,
52
39
  CLAUDE_SETTINGS_AUTO_MODE_KEY,
@@ -1,51 +1 @@
1
- """Test fixtures for _shared/pr-loop/scripts/.
2
-
3
- Two unrelated Python packages live under the name ``config`` in this repo:
4
- - ``_shared/pr-loop/scripts/config/`` (constants for grant/revoke/gate/preflight scripts)
5
- - ``hooks/config/`` (constants for the code-rules enforcer and other hooks)
6
-
7
- When tests under this directory exercise the gate (which loads
8
- ``hooks/blocking/code_rules_enforcer.py``) and also load the grant/revoke
9
- scripts in the same pytest process, ``sys.modules['config']`` and
10
- ``sys.modules['config.<submodule>']`` cache entries from one package leak
11
- into the other. The next ``from config.<submodule> import ...`` then fails
12
- with ``ModuleNotFoundError`` because the cached parent package does not
13
- expose that submodule.
14
-
15
- Independently, several scripts in this folder do
16
- ``Path(__file__).resolve()`` then prepend the resulting directory to
17
- ``sys.path``. On Windows when the working tree lives under a mapped drive
18
- backed by a UNC share (``Y:`` -> ``\\\\server\\share\\...``), ``.resolve()``
19
- returns the UNC form, and Python's import machinery on this host cannot
20
- locate ``config`` packages from a UNC ``sys.path`` entry. The Y:-form entry
21
- gets pushed to a later index by subsequent inserts, making ``from
22
- config.<submodule> import ...`` fail.
23
-
24
- This autouse fixture restores both invariants before each test:
25
- 1. evict every ``config`` and ``config.*`` entry from ``sys.modules``
26
- 2. prepend the drive-letter (``.absolute()``) form of the scripts
27
- directory to ``sys.path`` so package resolution always has a
28
- non-UNC path to search first
29
- """
30
-
31
- from __future__ import annotations
32
-
33
- import sys
34
- from pathlib import Path
35
-
36
- import pytest
37
-
38
- SCRIPTS_DIRECTORY_DRIVE_LETTER_FORM = str(Path(__file__).absolute().parent.parent)
39
-
40
-
41
- @pytest.fixture(autouse=True)
42
- def _evict_config_namespace_between_tests() -> None:
43
- for each_module_name in [
44
- each_key
45
- for each_key in list(sys.modules)
46
- if each_key == "config" or each_key.startswith("config.")
47
- ]:
48
- sys.modules.pop(each_module_name, None)
49
- if SCRIPTS_DIRECTORY_DRIVE_LETTER_FORM in sys.path:
50
- sys.path.remove(SCRIPTS_DIRECTORY_DRIVE_LETTER_FORM)
51
- sys.path.insert(0, SCRIPTS_DIRECTORY_DRIVE_LETTER_FORM)
1
+ """Test fixtures for _shared/pr-loop/scripts/tests/."""
@@ -49,7 +49,6 @@ def _load_grant_module() -> ModuleType:
49
49
  scripts_directory_str = str(scripts_directory.resolve())
50
50
  if scripts_directory_str not in sys.path:
51
51
  sys.path.insert(0, scripts_directory_str)
52
- sys.modules.pop("config", None)
53
52
  return _load_module_from_path(
54
53
  "grant_project_claude_permissions",
55
54
  scripts_directory / "grant_project_claude_permissions.py",
@@ -61,7 +60,6 @@ def _load_revoke_module() -> ModuleType:
61
60
  scripts_directory_str = str(scripts_directory.resolve())
62
61
  if scripts_directory_str not in sys.path:
63
62
  sys.path.insert(0, scripts_directory_str)
64
- sys.modules.pop("config", None)
65
63
  return _load_module_from_path(
66
64
  "revoke_project_claude_permissions",
67
65
  scripts_directory / "revoke_project_claude_permissions.py",
@@ -70,8 +68,10 @@ def _load_revoke_module() -> ModuleType:
70
68
 
71
69
  def _load_constants_module() -> ModuleType:
72
70
  return _load_module_from_path(
73
- "config.claude_permissions_constants",
74
- _scripts_directory() / "config" / "claude_permissions_constants.py",
71
+ "pr_loop_shared_constants.claude_permissions_constants",
72
+ _scripts_directory()
73
+ / "pr_loop_shared_constants"
74
+ / "claude_permissions_constants.py",
75
75
  )
76
76
 
77
77
 
@@ -138,10 +138,12 @@ def test_save_settings_temp_suffix_includes_pid_and_random_token(
138
138
 
139
139
  def test_text_file_encoding_sourced_from_config() -> None:
140
140
  config_module_path = (
141
- Path(__file__).parent.parent / "config" / "claude_permissions_constants.py"
141
+ Path(__file__).parent.parent
142
+ / "pr_loop_shared_constants"
143
+ / "claude_permissions_constants.py"
142
144
  )
143
145
  specification = importlib.util.spec_from_file_location(
144
- "config.claude_permissions_constants", config_module_path
146
+ "pr_loop_shared_constants.claude_permissions_constants", config_module_path
145
147
  )
146
148
  assert specification is not None
147
149
  assert specification.loader is not None
@@ -7,10 +7,12 @@ from types import ModuleType
7
7
 
8
8
  def _load_constants_module() -> ModuleType:
9
9
  module_path = (
10
- Path(__file__).parent.parent / "config" / "claude_permissions_constants.py"
10
+ Path(__file__).parent.parent
11
+ / "pr_loop_shared_constants"
12
+ / "claude_permissions_constants.py"
11
13
  )
12
14
  specification = importlib.util.spec_from_file_location(
13
- "config.claude_permissions_constants", module_path
15
+ "pr_loop_shared_constants.claude_permissions_constants", module_path
14
16
  )
15
17
  assert specification is not None
16
18
  assert specification.loader is not None
@@ -7,10 +7,12 @@ from types import ModuleType
7
7
 
8
8
  def _load_constants_module() -> ModuleType:
9
9
  module_path = (
10
- Path(__file__).parent.parent / "config" / "claude_settings_keys_constants.py"
10
+ Path(__file__).parent.parent
11
+ / "pr_loop_shared_constants"
12
+ / "claude_settings_keys_constants.py"
11
13
  )
12
14
  specification = importlib.util.spec_from_file_location(
13
- "config.claude_settings_keys_constants", module_path
15
+ "pr_loop_shared_constants.claude_settings_keys_constants", module_path
14
16
  )
15
17
  assert specification is not None
16
18
  assert specification.loader is not None
@@ -7,10 +7,12 @@ from types import ModuleType
7
7
 
8
8
  def _load_constants_module() -> ModuleType:
9
9
  module_path = (
10
- Path(__file__).parent.parent / "config" / "code_rules_gate_constants.py"
10
+ Path(__file__).parent.parent
11
+ / "pr_loop_shared_constants"
12
+ / "code_rules_gate_constants.py"
11
13
  )
12
14
  specification = importlib.util.spec_from_file_location(
13
- "config.code_rules_gate_constants", module_path
15
+ "pr_loop_shared_constants.code_rules_gate_constants", module_path
14
16
  )
15
17
  assert specification is not None
16
18
  assert specification.loader is not None
@@ -11,9 +11,13 @@ from types import ModuleType
11
11
 
12
12
 
13
13
  def _load_constants_module() -> ModuleType:
14
- module_path = Path(__file__).parent.parent / "config" / "fix_hookspath_constants.py"
14
+ module_path = (
15
+ Path(__file__).parent.parent
16
+ / "pr_loop_shared_constants"
17
+ / "fix_hookspath_constants.py"
18
+ )
15
19
  specification = importlib.util.spec_from_file_location(
16
- "config.fix_hookspath_constants", module_path
20
+ "pr_loop_shared_constants.fix_hookspath_constants", module_path
17
21
  )
18
22
  assert specification is not None
19
23
  assert specification.loader is not None
@@ -1,7 +1,8 @@
1
1
  """Smoke tests for grant_project_claude_permissions wiring.
2
2
 
3
3
  Confirms the module imports cleanly with the constants now sourced from
4
- config/claude_permissions_constants.py and config/claude_settings_keys_constants.py.
4
+ pr_loop_shared_constants/claude_permissions_constants.py and
5
+ pr_loop_shared_constants/claude_settings_keys_constants.py.
5
6
  """
6
7
 
7
8
  from __future__ import annotations
@@ -17,7 +18,6 @@ def _load_grant_module() -> ModuleType:
17
18
  parent_directory = str(scripts_directory.resolve())
18
19
  if parent_directory not in sys.path:
19
20
  sys.path.insert(0, parent_directory)
20
- sys.modules.pop("config", None)
21
21
  module_path = scripts_directory / "grant_project_claude_permissions.py"
22
22
  specification = importlib.util.spec_from_file_location(
23
23
  "grant_project_claude_permissions", module_path
@@ -36,11 +36,10 @@ from typing import Any
36
36
  THIS_FILE_DIRECTORY = Path(__file__).resolve().parent
37
37
  SCRIPT_DIRECTORY = THIS_FILE_DIRECTORY.parent
38
38
 
39
- sys.modules.pop("config", None)
40
39
  if str(SCRIPT_DIRECTORY) not in sys.path:
41
40
  sys.path.insert(0, str(SCRIPT_DIRECTORY))
42
41
 
43
- from config.post_audit_thread_constants import ( # noqa: E402
42
+ from pr_loop_shared_constants.post_audit_thread_constants import ( # noqa: E402
44
43
  ALL_GH_AUTH_TOKEN_COMMAND_PARTS,
45
44
  ALL_RETRY_BACKOFF_SECONDS,
46
45
  BUGTEAM_REVIEWER_ACCOUNT_ENV_VAR_NAME,
@@ -7,10 +7,12 @@ from types import ModuleType
7
7
 
8
8
  def _load_constants_module() -> ModuleType:
9
9
  module_path = (
10
- Path(__file__).parent.parent / "config" / "post_audit_thread_constants.py"
10
+ Path(__file__).parent.parent
11
+ / "pr_loop_shared_constants"
12
+ / "post_audit_thread_constants.py"
11
13
  )
12
14
  specification = importlib.util.spec_from_file_location(
13
- "config.post_audit_thread_constants", module_path
15
+ "pr_loop_shared_constants.post_audit_thread_constants", module_path
14
16
  )
15
17
  assert specification is not None
16
18
  assert specification.loader is not None
@@ -31,7 +31,7 @@ def _load_preflight_module() -> ModuleType:
31
31
 
32
32
  preflight = _load_preflight_module()
33
33
 
34
- from config.preflight_constants import ( # noqa: E402
34
+ from pr_loop_shared_constants.preflight_constants import ( # noqa: E402
35
35
  PYTEST_INI_FILENAME,
36
36
  PYTEST_NO_TESTS_COLLECTED_EXIT_CODE,
37
37
  )
@@ -204,16 +204,19 @@ def test_should_exit_nonzero_when_subprocess_run_raises_os_error(
204
204
 
205
205
 
206
206
  def test_preflight_uses_shared_hooks_path_suffix_constant() -> None:
207
- """Preflight's expected suffix must come from config.fix_hookspath_constants
208
- so the canonical hooks directory is defined in exactly one place."""
207
+ """Preflight's expected suffix must come from
208
+ pr_loop_shared_constants.fix_hookspath_constants so the canonical hooks
209
+ directory is defined in exactly one place."""
209
210
  scripts_directory = str(Path(__file__).parent.parent.resolve())
210
211
  if scripts_directory not in sys.path:
211
212
  sys.path.insert(0, scripts_directory)
212
213
  constants_module_path = (
213
- Path(__file__).parent.parent / "config" / "fix_hookspath_constants.py"
214
+ Path(__file__).parent.parent
215
+ / "pr_loop_shared_constants"
216
+ / "fix_hookspath_constants.py"
214
217
  )
215
218
  constants_specification = importlib.util.spec_from_file_location(
216
- "config.fix_hookspath_constants",
219
+ "pr_loop_shared_constants.fix_hookspath_constants",
217
220
  constants_module_path,
218
221
  )
219
222
  assert constants_specification is not None
@@ -234,15 +237,18 @@ def test_preflight_skip_uses_shared_env_var_constant(
234
237
  capsys: pytest.CaptureFixture[str],
235
238
  monkeypatch: pytest.MonkeyPatch,
236
239
  ) -> None:
237
- """The preflight skip env-var name must come from config/preflight_constants.py."""
240
+ """The preflight skip env-var name must come from
241
+ pr_loop_shared_constants/preflight_constants.py."""
238
242
  scripts_directory = str(Path(__file__).parent.parent.resolve())
239
243
  if scripts_directory not in sys.path:
240
244
  sys.path.insert(0, scripts_directory)
241
245
  constants_module_path = (
242
- Path(__file__).parent.parent / "config" / "preflight_constants.py"
246
+ Path(__file__).parent.parent
247
+ / "pr_loop_shared_constants"
248
+ / "preflight_constants.py"
243
249
  )
244
250
  constants_specification = importlib.util.spec_from_file_location(
245
- "config.preflight_constants",
251
+ "pr_loop_shared_constants.preflight_constants",
246
252
  constants_module_path,
247
253
  )
248
254
  assert constants_specification is not None
@@ -306,8 +312,9 @@ def test_preflight_does_not_import_unused_repository_root_marker_constant() -> N
306
312
 
307
313
  def test_pytest_no_tests_collected_helper_returns_named_constant() -> None:
308
314
  """The pytest "no tests collected" exit code must be sourced from the
309
- named constant in config/preflight_constants.py rather than the bare
310
- literal 5 inside the function body (CODE_RULES magic-values rule)."""
315
+ named constant in pr_loop_shared_constants/preflight_constants.py rather
316
+ than the bare literal 5 inside the function body (CODE_RULES magic-values
317
+ rule)."""
311
318
  assert preflight._pytest_exit_code_no_tests_collected() == (
312
319
  PYTEST_NO_TESTS_COLLECTED_EXIT_CODE
313
320
  )
@@ -317,30 +324,6 @@ def test_pytest_no_tests_collected_helper_returns_named_constant() -> None:
317
324
  )
318
325
 
319
326
 
320
- def test_preflight_bootstrap_moves_script_directory_to_front() -> None:
321
- """Import bootstrap keeps exactly one script directory entry at the front."""
322
- module_path = Path(__file__).parent.parent / "preflight.py"
323
- script_directory_resolved = str(module_path.parent.resolve())
324
- script_directory_absolute = str(module_path.parent.absolute())
325
- original_sys_path = list(sys.path)
326
- try:
327
- sys.path.insert(0, script_directory_resolved)
328
- sys.path.insert(0, script_directory_resolved)
329
- sys.path.insert(0, str(module_path.parents[4]))
330
- _load_preflight_module()
331
- assert os.path.samefile(sys.path[0], script_directory_resolved)
332
- equivalent_count = sum(
333
- 1
334
- for each_entry in sys.path
335
- if os.path.exists(each_entry)
336
- and os.path.samefile(each_entry, script_directory_resolved)
337
- )
338
- assert equivalent_count == 1
339
- assert sys.path[0] == script_directory_absolute
340
- finally:
341
- sys.path[:] = original_sys_path
342
-
343
-
344
327
  def test_main_uses_correct_changed_files_function_name() -> None:
345
328
  """main() must call get_changed_files, not the undefined get_all_changed_files."""
346
329
  main_source = inspect.getsource(preflight.main)
@@ -528,24 +511,6 @@ def test_explicit_scope_all_with_base_ref_should_not_call_get_changed_files(
528
511
  mock_get_changed.assert_not_called()
529
512
 
530
513
 
531
- def test_preflight_bootstrap_matches_code_rules_sys_path_pattern() -> None:
532
- """Bootstrap must clear duplicate script_directory entries, then guard insert."""
533
- module_path = Path(__file__).parent.parent / "preflight.py"
534
- source = module_path.read_text(encoding="utf-8")
535
- assert "_entry_points_at_preflight_script_directory" in source, (
536
- "Bootstrap must remove script_directory entries using path equivalence"
537
- )
538
- assert "for each_index in range(len(sys.path) - 1, -1, -1):" in source, (
539
- "Bootstrap must walk sys.path to drop duplicate script directory entries"
540
- )
541
- assert "_preflight_scripts_path_entry not in sys.path:" in source, (
542
- "Bootstrap insert must be guarded for code_rules_gate compliance"
543
- )
544
- assert "sys.path.insert(0, _preflight_scripts_path_entry)" in source, (
545
- "Bootstrap must insert the absolute script directory at index 0"
546
- )
547
-
548
-
549
514
  def test_has_discoverable_tests_should_include_untracked_test_files(
550
515
  tmp_path: Path,
551
516
  ) -> None:
@@ -6,9 +6,13 @@ from types import ModuleType
6
6
 
7
7
 
8
8
  def _load_constants_module() -> ModuleType:
9
- module_path = Path(__file__).parent.parent / "config" / "preflight_constants.py"
9
+ module_path = (
10
+ Path(__file__).parent.parent
11
+ / "pr_loop_shared_constants"
12
+ / "preflight_constants.py"
13
+ )
10
14
  specification = importlib.util.spec_from_file_location(
11
- "config.preflight_constants", module_path
15
+ "pr_loop_shared_constants.preflight_constants", module_path
12
16
  )
13
17
  assert specification is not None
14
18
  assert specification.loader is not None
@@ -1,7 +1,8 @@
1
1
  """Smoke tests for revoke_project_claude_permissions wiring.
2
2
 
3
3
  Confirms the module imports cleanly with the constants now sourced from
4
- config/claude_permissions_constants.py and config/claude_settings_keys_constants.py.
4
+ pr_loop_shared_constants/claude_permissions_constants.py and
5
+ pr_loop_shared_constants/claude_settings_keys_constants.py.
5
6
  """
6
7
 
7
8
  from __future__ import annotations
@@ -17,7 +18,6 @@ def _load_revoke_module() -> ModuleType:
17
18
  parent_directory = str(scripts_directory.resolve())
18
19
  if parent_directory not in sys.path:
19
20
  sys.path.insert(0, parent_directory)
20
- sys.modules.pop("config", None)
21
21
  module_path = scripts_directory / "revoke_project_claude_permissions.py"
22
22
  specification = importlib.util.spec_from_file_location(
23
23
  "revoke_project_claude_permissions", module_path