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.
- 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/pre-compact/SKILL.md +114 -0
- 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
|
@@ -16,7 +16,7 @@ import os
|
|
|
16
16
|
import sys
|
|
17
17
|
import time
|
|
18
18
|
|
|
19
|
-
from
|
|
19
|
+
from dev_env_scripts_constants.timing import DEFAULT_AGE_SECONDS, DEFAULT_POLL_INTERVAL
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def _positive_int(raw_argument: str) -> int:
|
|
@@ -42,6 +42,15 @@ def sweep(root: str, min_age_seconds: int) -> list[str]:
|
|
|
42
42
|
Walks bottom-up so nested empty directories are cleaned from the leaves
|
|
43
43
|
inward. Relies on os.rmdir to fail harmlessly for non-empty directories
|
|
44
44
|
instead of checking snapshotted subdirectory lists.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
root: Root directory to walk; directories below the root are
|
|
48
|
+
candidates for removal.
|
|
49
|
+
min_age_seconds: Minimum age in seconds for a directory to qualify
|
|
50
|
+
for removal.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Sorted list of directory paths that were removed during the sweep.
|
|
45
54
|
"""
|
|
46
55
|
|
|
47
56
|
all_removed: list[str] = []
|
|
@@ -22,8 +22,8 @@ for each_sys_path_entry in (
|
|
|
22
22
|
|
|
23
23
|
import setup_project_paths as setup
|
|
24
24
|
import untracked_repo_detector as detector_module
|
|
25
|
-
from
|
|
26
|
-
from
|
|
25
|
+
from hooks_constants.project_paths_reader import registry_file_path
|
|
26
|
+
from hooks_constants.setup_project_paths_constants import (
|
|
27
27
|
ABORTED_NOTHING_WRITTEN_MESSAGE,
|
|
28
28
|
CONFIRMATION_PROMPT_TEXT,
|
|
29
29
|
ES_EXE_FOLDERS_ONLY_QUERY_ARGUMENTS,
|
|
@@ -15,10 +15,6 @@ _SCRIPTS_DIR = Path(os.path.abspath(__file__)).parent
|
|
|
15
15
|
if str(_SCRIPTS_DIR) not in sys.path:
|
|
16
16
|
sys.path.insert(0, str(_SCRIPTS_DIR))
|
|
17
17
|
|
|
18
|
-
for _cached in list(sys.modules):
|
|
19
|
-
if _cached == "config" or _cached.startswith("config."):
|
|
20
|
-
del sys.modules[_cached]
|
|
21
|
-
|
|
22
18
|
from sweep_empty_dirs import _build_parser, _positive_int, sweep # noqa: E402
|
|
23
19
|
|
|
24
20
|
_OLD_TIMESTAMP = time.time() - 300
|
|
@@ -53,7 +49,7 @@ def test_positive_int_rejects_non_integer() -> None:
|
|
|
53
49
|
|
|
54
50
|
|
|
55
51
|
def test_build_parser_sets_age_default_from_timing_config() -> None:
|
|
56
|
-
"""_build_parser uses DEFAULT_AGE_SECONDS from
|
|
52
|
+
"""_build_parser uses DEFAULT_AGE_SECONDS from dev_env_scripts_constants.timing as --age default."""
|
|
57
53
|
parser = _build_parser()
|
|
58
54
|
default_age = parser.get_default("age")
|
|
59
55
|
assert isinstance(default_age, int)
|
|
@@ -61,7 +57,7 @@ def test_build_parser_sets_age_default_from_timing_config() -> None:
|
|
|
61
57
|
|
|
62
58
|
|
|
63
59
|
def test_build_parser_sets_interval_default_from_timing_config() -> None:
|
|
64
|
-
"""_build_parser uses DEFAULT_POLL_INTERVAL from
|
|
60
|
+
"""_build_parser uses DEFAULT_POLL_INTERVAL from dev_env_scripts_constants.timing as --interval default."""
|
|
65
61
|
parser = _build_parser()
|
|
66
62
|
default_interval = parser.get_default("interval")
|
|
67
63
|
assert isinstance(default_interval, int)
|
|
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
import tempfile
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
|
-
from
|
|
12
|
+
from skills_pr_loop_constants.path_resolver_constants import (
|
|
13
13
|
DIFF_PATCH_TEMPLATE,
|
|
14
14
|
FIX_OUTCOME_XML_TEMPLATE,
|
|
15
15
|
MULTI_PR_SLUG_TEMPLATE,
|
|
@@ -19,7 +19,7 @@ if str(_self_dir) not in sys.path:
|
|
|
19
19
|
sys.path.insert(0, str(_self_dir))
|
|
20
20
|
|
|
21
21
|
from _xml_utils import emit_pretty_xml
|
|
22
|
-
from
|
|
22
|
+
from skills_pr_loop_constants.path_resolver_constants import (
|
|
23
23
|
ALL_AUDIT_CATEGORY_ENTRIES,
|
|
24
24
|
ALL_AUDIT_CONSTRAINT_TEXTS,
|
|
25
25
|
)
|
|
@@ -21,7 +21,7 @@ if str(_self_dir) not in sys.path:
|
|
|
21
21
|
|
|
22
22
|
from _cli_utils import require_file
|
|
23
23
|
from _xml_utils import emit_pretty_xml
|
|
24
|
-
from
|
|
24
|
+
from skills_pr_loop_constants.path_resolver_constants import (
|
|
25
25
|
ALL_FIX_CONSTRAINT_TEXTS,
|
|
26
26
|
ALL_FIX_EXECUTION_STEPS,
|
|
27
27
|
)
|
|
@@ -20,7 +20,7 @@ from _path_resolver import (
|
|
|
20
20
|
per_pr_workspace,
|
|
21
21
|
resolve_run_temp_dir,
|
|
22
22
|
)
|
|
23
|
-
from
|
|
23
|
+
from skills_pr_loop_constants.path_resolver_constants import LOOP_STATE_JSON_INDENT
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def create_loop_state(
|
|
@@ -24,7 +24,7 @@ if str(_self_dir) not in sys.path:
|
|
|
24
24
|
sys.path.insert(0, str(_self_dir))
|
|
25
25
|
|
|
26
26
|
from _path_resolver import per_pr_workspace
|
|
27
|
-
from
|
|
27
|
+
from skills_pr_loop_constants.path_resolver_constants import ALL_PYTHON_ONEXC_VERSION
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def _remove_readonly_attribute(
|
|
@@ -19,7 +19,7 @@ if str(_self_dir) not in sys.path:
|
|
|
19
19
|
from _cli_utils import require_file
|
|
20
20
|
from _path_resolver import outcome_xml_path
|
|
21
21
|
from _xml_utils import emit_pretty_xml
|
|
22
|
-
from
|
|
22
|
+
from skills_pr_loop_constants.path_resolver_constants import ALL_FINDING_BODY_ELEMENT_KEYS
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def build_audit_xml(
|
|
@@ -70,7 +70,7 @@ def _populate_findings(parent: Element, findings_data: list[dict[str, object]])
|
|
|
70
70
|
|
|
71
71
|
Scalar finding fields become XML attributes on `<finding>`; the
|
|
72
72
|
body fields named in `ALL_FINDING_BODY_ELEMENT_KEYS` (defined in
|
|
73
|
-
`packages/claude-dev-env/skills/_shared/pr-loop/scripts/
|
|
73
|
+
`packages/claude-dev-env/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py`
|
|
74
74
|
and currently `("title", "excerpt", "description")`) become child elements.
|
|
75
75
|
Nested dicts or lists in scalar slots are flattened to string form
|
|
76
76
|
so attribute serialization stays well-defined.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Validate status enum values and write <bugteam_fix> XML at the canonical path.
|
|
2
2
|
|
|
3
3
|
Status enum (canonical source: `ALL_VALID_FIX_STATUSES` in
|
|
4
|
-
`packages/claude-dev-env/skills/_shared/pr-loop/scripts/
|
|
4
|
+
`packages/claude-dev-env/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py`):
|
|
5
5
|
fixed | could_not_address | hook_blocked | unverified_fixed.
|
|
6
6
|
|
|
7
7
|
Each outcome's scalar fields become XML attributes on `<outcome>`; the
|
|
@@ -27,7 +27,7 @@ if str(_self_dir) not in sys.path:
|
|
|
27
27
|
from _cli_utils import require_file
|
|
28
28
|
from _path_resolver import fix_outcome_xml_path
|
|
29
29
|
from _xml_utils import emit_pretty_xml
|
|
30
|
-
from
|
|
30
|
+
from skills_pr_loop_constants.path_resolver_constants import (
|
|
31
31
|
ALL_FIX_OUTCOME_BODY_ELEMENT_KEYS,
|
|
32
32
|
ALL_VALID_FIX_STATUSES,
|
|
33
33
|
)
|
|
@@ -108,7 +108,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
108
108
|
teammate does NOT author the inline-comment body directly:
|
|
109
109
|
`post_audit_thread.py` renders every body from
|
|
110
110
|
`INLINE_COMMENT_BODY_TEMPLATE` (defined in
|
|
111
|
-
[`_shared/pr-loop/scripts/
|
|
111
|
+
[`_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py`](../../_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py))
|
|
112
112
|
— the template prepends `**[<severity>] <Skill> audit finding**`
|
|
113
113
|
and renders the suggested-fix block, so a teammate who hand-formats
|
|
114
114
|
a title or footer wastes the work.
|
package/skills/bugteam/SKILL.md
CHANGED
|
@@ -103,7 +103,7 @@ narrative) becomes `description`, and the suffix starting at `Fix:`
|
|
|
103
103
|
When the agent omits the `Fix:` heading on a given finding, write the
|
|
104
104
|
full `failure_mode` text to BOTH `description` and `fix_summary` so the
|
|
105
105
|
script's body template (`INLINE_COMMENT_BODY_TEMPLATE` in
|
|
106
|
-
[`_shared/pr-loop/scripts/
|
|
106
|
+
[`_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py`](../../_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py))
|
|
107
107
|
still renders coherently. Set `side="RIGHT"` for every entry. On CLEAN,
|
|
108
108
|
pass an empty array (`[]`) so the script posts an APPROVE review
|
|
109
109
|
(GitHub stores it as `state=APPROVED`) with a "no findings" summary and
|
|
@@ -22,7 +22,7 @@ python "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/post_audit_thread.py"
|
|
|
22
22
|
|
|
23
23
|
Capture `<head_sha>` via `git rev-parse HEAD` in the subagent cwd immediately before this call so the review attaches to the commit the audit actually scoped.
|
|
24
24
|
|
|
25
|
-
`--findings-json` points to a JSON file whose root is a list of objects shaped `{path, line, side, severity, description, fix_summary}`. Build it from the merged Shape A findings: finding `file` → `path`. Each finding's `failure_mode` carries the full audit-to-fix handoff text per [`agents/code-quality-agent.md`](../../../agents/code-quality-agent.md); split `failure_mode` at the literal `Fix:` heading so the failure narrative becomes `description` and the suffix beginning at `Fix:` (including the trailing `Validation:` clause) becomes `fix_summary`. When a finding's `failure_mode` omits the `Fix:` heading, write the full text to BOTH `description` and `fix_summary` so the script's body template (`INLINE_COMMENT_BODY_TEMPLATE` in [`packages/claude-dev-env/_shared/pr-loop/scripts/
|
|
25
|
+
`--findings-json` points to a JSON file whose root is a list of objects shaped `{path, line, side, severity, description, fix_summary}`. Build it from the merged Shape A findings: finding `file` → `path`. Each finding's `failure_mode` carries the full audit-to-fix handoff text per [`agents/code-quality-agent.md`](../../../agents/code-quality-agent.md); split `failure_mode` at the literal `Fix:` heading so the failure narrative becomes `description` and the suffix beginning at `Fix:` (including the trailing `Validation:` clause) becomes `fix_summary`. When a finding's `failure_mode` omits the `Fix:` heading, write the full text to BOTH `description` and `fix_summary` so the script's body template (`INLINE_COMMENT_BODY_TEMPLATE` in [`packages/claude-dev-env/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py`](../../../_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py)) renders coherently. Set `side="RIGHT"` for every entry. On CLEAN the list is empty (`[]`); on DIRTY the list carries one entry per finding.
|
|
26
26
|
|
|
27
27
|
The script handles retries internally — 1s / 4s / 16s backoff across four attempts (one initial plus three retries). Exit codes:
|
|
28
28
|
|
package/skills/bugteam/scripts/{_claude_permissions_common.py → _bugteam_permissions_common.py}
RENAMED
|
@@ -16,25 +16,13 @@ from collections.abc import Callable
|
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
from typing import NoReturn
|
|
18
18
|
|
|
19
|
-
|
|
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" or each_module_key.startswith("config.")
|
|
24
|
-
]:
|
|
25
|
-
_previously_cached_config[each_cached_module_name] = sys.modules.pop(
|
|
26
|
-
each_cached_module_name
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
from config.claude_permissions_common_constants import (
|
|
19
|
+
from bugteam_scripts_constants.claude_permissions_common_constants import (
|
|
30
20
|
ALL_TRUST_ENTRY_PROJECT_PATH_BOUNDARY_QUOTE_CHARACTERS,
|
|
31
21
|
ATOMIC_WRITE_TEMPORARY_SUFFIX,
|
|
32
22
|
DEFAULT_SETTINGS_FILE_MODE,
|
|
33
23
|
TEXT_FILE_ENCODING,
|
|
34
24
|
)
|
|
35
25
|
|
|
36
|
-
sys.modules.update(_previously_cached_config)
|
|
37
|
-
|
|
38
26
|
|
|
39
27
|
def exit_with_error(message: str) -> NoReturn:
|
|
40
28
|
print(f"Error: {message}", file=sys.stderr)
|
|
@@ -11,17 +11,7 @@ from pathlib import Path
|
|
|
11
11
|
|
|
12
12
|
ValidateContentCallable = Callable[..., list[str]]
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
for each_cached_module_name in [
|
|
16
|
-
each_module_key
|
|
17
|
-
for each_module_key in list(sys.modules)
|
|
18
|
-
if each_module_key == "config" or each_module_key.startswith("config.")
|
|
19
|
-
]:
|
|
20
|
-
_previously_cached_config[each_cached_module_name] = sys.modules.pop(
|
|
21
|
-
each_cached_module_name
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
from config.bugteam_code_rules_gate_constants import (
|
|
14
|
+
from bugteam_scripts_constants.bugteam_code_rules_gate_constants import (
|
|
25
15
|
ALL_CODE_FILE_EXTENSIONS,
|
|
26
16
|
ALL_COLUMN_MAGIC_FALSE_VALUES,
|
|
27
17
|
ALL_GIT_DIFF_CACHED_ARGS,
|
|
@@ -34,8 +24,6 @@ from config.bugteam_code_rules_gate_constants import (
|
|
|
34
24
|
VIOLATION_LINE_RAW_PATTERN,
|
|
35
25
|
)
|
|
36
26
|
|
|
37
|
-
sys.modules.update(_previously_cached_config)
|
|
38
|
-
|
|
39
27
|
|
|
40
28
|
def hunk_header_pattern() -> re.Pattern[str]:
|
|
41
29
|
return re.compile(HUNK_HEADER_RAW_PATTERN)
|
|
@@ -5,22 +5,7 @@ import subprocess
|
|
|
5
5
|
import sys
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
try:
|
|
10
|
-
sys.path.remove(parent_directory)
|
|
11
|
-
except ValueError:
|
|
12
|
-
pass
|
|
13
|
-
if parent_directory not in sys.path:
|
|
14
|
-
sys.path.insert(0, parent_directory)
|
|
15
|
-
|
|
16
|
-
for each_cached_module_name in [
|
|
17
|
-
each_module_key
|
|
18
|
-
for each_module_key in list(sys.modules)
|
|
19
|
-
if each_module_key == "config" or each_module_key.startswith("config.")
|
|
20
|
-
]:
|
|
21
|
-
sys.modules.pop(each_cached_module_name, None)
|
|
22
|
-
|
|
23
|
-
from config.bugteam_fix_hookspath_constants import (
|
|
8
|
+
from bugteam_scripts_constants.bugteam_fix_hookspath_constants import (
|
|
24
9
|
ALL_CANONICAL_HOOKS_DIRECTORY_COMPONENTS,
|
|
25
10
|
ALL_GLOBAL_HOOKS_PATH_ARGUMENTS,
|
|
26
11
|
ALL_HOME_ENV_VAR_NAMES,
|
|
@@ -6,19 +6,13 @@ import subprocess
|
|
|
6
6
|
import sys
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
|
-
for each_cached_module_name in [
|
|
10
|
-
each_module_key
|
|
11
|
-
for each_module_key in list(sys.modules)
|
|
12
|
-
if each_module_key == "config" or each_module_key.startswith("config.")
|
|
13
|
-
]:
|
|
14
|
-
sys.modules.pop(each_cached_module_name, None)
|
|
15
9
|
_bugteam_scripts_directory = str(Path(__file__).absolute().parent)
|
|
16
10
|
while _bugteam_scripts_directory in sys.path:
|
|
17
11
|
sys.path.remove(_bugteam_scripts_directory)
|
|
18
12
|
if _bugteam_scripts_directory not in sys.path:
|
|
19
13
|
sys.path.insert(0, _bugteam_scripts_directory)
|
|
20
14
|
|
|
21
|
-
from
|
|
15
|
+
from bugteam_scripts_constants.bugteam_preflight_constants import (
|
|
22
16
|
ALL_DISCOVERY_IGNORE_DIRECTORIES,
|
|
23
17
|
ALL_GIT_CONFIG_HOOKS_PATH_ARGUMENTS,
|
|
24
18
|
ALL_PRE_COMMIT_ARGUMENTS,
|
|
@@ -35,12 +29,6 @@ from config.bugteam_preflight_constants import (
|
|
|
35
29
|
PYTEST_INI_FILENAME,
|
|
36
30
|
)
|
|
37
31
|
|
|
38
|
-
for each_cached_module_name in [
|
|
39
|
-
each_module_key
|
|
40
|
-
for each_module_key in list(sys.modules)
|
|
41
|
-
if each_module_key == "config" or each_module_key.startswith("config.")
|
|
42
|
-
]:
|
|
43
|
-
sys.modules.pop(each_cached_module_name, None)
|
|
44
32
|
_shared_pr_loop_scripts_directory = (
|
|
45
33
|
Path(__file__).absolute().parent
|
|
46
34
|
/ ".." / ".." / ".." / "_shared" / "pr-loop" / "scripts"
|
|
@@ -9,17 +9,11 @@ the changes applied. No-op when the entries already exist.
|
|
|
9
9
|
import sys
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
|
-
for each_cached_module_name in [
|
|
13
|
-
each_module_key
|
|
14
|
-
for each_module_key in list(sys.modules)
|
|
15
|
-
if each_module_key == "config" or each_module_key.startswith("config.")
|
|
16
|
-
]:
|
|
17
|
-
sys.modules.pop(each_cached_module_name, None)
|
|
18
12
|
parent_directory = str(Path(__file__).resolve().parent)
|
|
19
13
|
if parent_directory not in sys.path:
|
|
20
14
|
sys.path.insert(0, parent_directory)
|
|
21
15
|
|
|
22
|
-
from
|
|
16
|
+
from _bugteam_permissions_common import ( # noqa: E402
|
|
23
17
|
append_if_missing,
|
|
24
18
|
build_agent_config_deny_rules,
|
|
25
19
|
build_permission_rules,
|
|
@@ -32,7 +26,7 @@ from _claude_permissions_common import ( # noqa: E402
|
|
|
32
26
|
remove_matching_entries_from_list,
|
|
33
27
|
save_settings,
|
|
34
28
|
)
|
|
35
|
-
from
|
|
29
|
+
from bugteam_scripts_constants.claude_permissions_common_constants import ( # noqa: E402
|
|
36
30
|
ALL_AGENT_CONFIG_DENY_TOOLS,
|
|
37
31
|
ALL_AGENT_CONFIG_PATH_PATTERNS,
|
|
38
32
|
ALL_PERMISSION_ALLOW_TOOLS,
|
|
@@ -19,7 +19,7 @@ import sys
|
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
from types import ModuleType
|
|
21
21
|
|
|
22
|
-
from
|
|
22
|
+
from bugteam_scripts_constants.probe_code_rules_enforcer_check_constants import (
|
|
23
23
|
DEFAULT_REPORTED_PATH,
|
|
24
24
|
ENFORCER_MODULE_NAME,
|
|
25
25
|
ENFORCER_RELATIVE_PATH,
|
|
@@ -18,7 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
import re
|
|
19
19
|
import textwrap
|
|
20
20
|
|
|
21
|
-
from
|
|
21
|
+
from bugteam_scripts_constants.reflow_skill_md_constants import (
|
|
22
22
|
BASH_CONTINUATION_MARKER_WIDTH,
|
|
23
23
|
BULLET_LIST_ITEM_PATTERN as BULLET_RE,
|
|
24
24
|
MAXIMUM_LINE_WIDTH as MAX_WIDTH,
|
|
@@ -10,17 +10,11 @@ autoMode sections so repeated grant/revoke cycles leave no dead structure.
|
|
|
10
10
|
import sys
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
|
|
13
|
-
for each_cached_module_name in [
|
|
14
|
-
each_module_key
|
|
15
|
-
for each_module_key in list(sys.modules)
|
|
16
|
-
if each_module_key == "config" or each_module_key.startswith("config.")
|
|
17
|
-
]:
|
|
18
|
-
sys.modules.pop(each_cached_module_name, None)
|
|
19
13
|
parent_directory = str(Path(__file__).resolve().parent)
|
|
20
14
|
if parent_directory not in sys.path:
|
|
21
15
|
sys.path.insert(0, parent_directory)
|
|
22
16
|
|
|
23
|
-
from
|
|
17
|
+
from _bugteam_permissions_common import ( # noqa: E402
|
|
24
18
|
build_agent_config_deny_rules,
|
|
25
19
|
build_permission_rules,
|
|
26
20
|
exit_with_error,
|
|
@@ -31,7 +25,7 @@ from _claude_permissions_common import ( # noqa: E402
|
|
|
31
25
|
remove_matching_entries_from_list,
|
|
32
26
|
save_settings,
|
|
33
27
|
)
|
|
34
|
-
from
|
|
28
|
+
from bugteam_scripts_constants.claude_permissions_common_constants import ( # noqa: E402
|
|
35
29
|
ALL_AGENT_CONFIG_DENY_TOOLS,
|
|
36
30
|
ALL_AGENT_CONFIG_PATH_PATTERNS,
|
|
37
31
|
ALL_PERMISSION_ALLOW_TOOLS,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"""TDD-pair tests for the underscore-prefixed
|
|
1
|
+
"""TDD-pair tests for the underscore-prefixed _bugteam_permissions_common module.
|
|
2
2
|
|
|
3
3
|
The TDD enforcer matches a production filename ``X.py`` to ``test_X.py``;
|
|
4
|
-
``
|
|
4
|
+
``_bugteam_permissions_common.py`` carries a leading underscore that the
|
|
5
5
|
enforcer treats as part of the name. This file's tests are the canonical
|
|
6
6
|
match. The broader behavioral suite continues to live alongside, in
|
|
7
|
-
``
|
|
7
|
+
``test_bugteam_permissions_common.py``.
|
|
8
8
|
"""
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
@@ -18,7 +18,7 @@ _script_directory = str(Path(__file__).resolve().parent)
|
|
|
18
18
|
if _script_directory not in sys.path:
|
|
19
19
|
sys.path.insert(0, _script_directory)
|
|
20
20
|
|
|
21
|
-
import
|
|
21
|
+
import _bugteam_permissions_common as common_module
|
|
22
22
|
import grant_project_claude_permissions as grant_module
|
|
23
23
|
import revoke_project_claude_permissions as revoke_module
|
|
24
24
|
|
|
@@ -21,10 +21,10 @@ _script_directory = str(Path(__file__).resolve().parent)
|
|
|
21
21
|
if _script_directory not in sys.path:
|
|
22
22
|
sys.path.insert(0, _script_directory)
|
|
23
23
|
|
|
24
|
-
import
|
|
24
|
+
import _bugteam_permissions_common as common_module
|
|
25
25
|
import grant_project_claude_permissions as grant_module
|
|
26
26
|
import revoke_project_claude_permissions as revoke_module
|
|
27
|
-
from
|
|
27
|
+
from bugteam_scripts_constants.claude_permissions_common_constants import (
|
|
28
28
|
ALL_AGENT_CONFIG_DENY_TOOLS,
|
|
29
29
|
ALL_AGENT_CONFIG_PATH_PATTERNS,
|
|
30
30
|
ALL_PERMISSION_ALLOW_TOOLS,
|
|
@@ -356,29 +356,3 @@ def test_read_global_core_hooks_path_returns_empty_when_key_unset() -> None:
|
|
|
356
356
|
with patch.object(subprocess, "run", fake_run):
|
|
357
357
|
result = bugteam_fix_hookspath.read_global_core_hooks_path(None)
|
|
358
358
|
assert result == ""
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
def test_module_import_evicts_cached_config_submodules() -> None:
|
|
362
|
-
"""Importing bugteam_fix_hookspath must evict cached `config.*` submodules.
|
|
363
|
-
|
|
364
|
-
Regression for loop1-1: without a defensive cache pop above sys.path.insert,
|
|
365
|
-
a previously-cached `config` package shadows scripts/config/ and the
|
|
366
|
-
from-import raises ModuleNotFoundError.
|
|
367
|
-
"""
|
|
368
|
-
fake_submodule_name = "config.bugteam_fix_hookspath_constants"
|
|
369
|
-
fake_parent_name = "config"
|
|
370
|
-
sentinel_module_a = ModuleType(fake_parent_name)
|
|
371
|
-
sentinel_module_b = ModuleType(fake_submodule_name)
|
|
372
|
-
sys.modules[fake_parent_name] = sentinel_module_a
|
|
373
|
-
sys.modules[fake_submodule_name] = sentinel_module_b
|
|
374
|
-
try:
|
|
375
|
-
_load_fix_module()
|
|
376
|
-
finally:
|
|
377
|
-
sys.modules.pop(fake_parent_name, None)
|
|
378
|
-
sys.modules.pop(fake_submodule_name, None)
|
|
379
|
-
assert sys.modules.get(fake_parent_name) is not sentinel_module_a, (
|
|
380
|
-
"parent `config` cache entry must be evicted on module import"
|
|
381
|
-
)
|
|
382
|
-
assert sys.modules.get(fake_submodule_name) is not sentinel_module_b, (
|
|
383
|
-
"cached `config.<submodule>` entries must be evicted on module import"
|
|
384
|
-
)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import importlib
|
|
2
1
|
import sys
|
|
3
2
|
from pathlib import Path
|
|
4
|
-
from types import ModuleType
|
|
5
3
|
from unittest.mock import patch
|
|
6
4
|
|
|
7
5
|
import pytest
|
|
@@ -10,14 +8,14 @@ _script_directory = str(Path(__file__).resolve().parent)
|
|
|
10
8
|
if _script_directory not in sys.path:
|
|
11
9
|
sys.path.insert(0, _script_directory)
|
|
12
10
|
|
|
13
|
-
import
|
|
14
|
-
from
|
|
11
|
+
import _bugteam_permissions_common as common_module
|
|
12
|
+
from _bugteam_permissions_common import (
|
|
15
13
|
build_permission_rule,
|
|
16
14
|
get_current_project_path,
|
|
17
15
|
path_contains_glob_metacharacters,
|
|
18
16
|
save_settings,
|
|
19
17
|
)
|
|
20
|
-
from
|
|
18
|
+
from bugteam_scripts_constants.claude_permissions_common_constants import DEFAULT_SETTINGS_FILE_MODE
|
|
21
19
|
import grant_project_claude_permissions as grant_module
|
|
22
20
|
import revoke_project_claude_permissions as revoke_module
|
|
23
21
|
|
|
@@ -140,64 +138,3 @@ def test_is_valid_project_root_exported_from_consumer_modules(
|
|
|
140
138
|
assert revoke_module.is_valid_project_root(bare_directory) is False
|
|
141
139
|
|
|
142
140
|
|
|
143
|
-
def _reload_with_stale_config_cache(module_name: str) -> ModuleType:
|
|
144
|
-
fake_submodule_name = "config.claude_permissions_common_constants"
|
|
145
|
-
fake_parent_name = "config"
|
|
146
|
-
sentinel_module_a = ModuleType(fake_parent_name)
|
|
147
|
-
sentinel_module_b = ModuleType(fake_submodule_name)
|
|
148
|
-
sys.modules[fake_parent_name] = sentinel_module_a
|
|
149
|
-
sys.modules[fake_submodule_name] = sentinel_module_b
|
|
150
|
-
try:
|
|
151
|
-
target_module = sys.modules.get(module_name)
|
|
152
|
-
if target_module is None:
|
|
153
|
-
target_module = importlib.import_module(module_name)
|
|
154
|
-
else:
|
|
155
|
-
target_module = importlib.reload(target_module)
|
|
156
|
-
finally:
|
|
157
|
-
sys.modules.pop(fake_parent_name, None)
|
|
158
|
-
sys.modules.pop(fake_submodule_name, None)
|
|
159
|
-
return target_module
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def test_grant_module_import_evicts_cached_config_submodules(
|
|
163
|
-
tmp_path: Path,
|
|
164
|
-
) -> None:
|
|
165
|
-
"""grant_project_claude_permissions must evict cached `config.*` on import.
|
|
166
|
-
|
|
167
|
-
Regression for loop1-2: without a defensive cache pop above sys.path.insert,
|
|
168
|
-
a cached `config` package shadows scripts/config/ and the from-import raises.
|
|
169
|
-
Calls the rebound `is_valid_project_root` to confirm the real implementation
|
|
170
|
-
survived the cache eviction (a stale shadow would either raise on import or
|
|
171
|
-
bind a placeholder that returns the wrong value).
|
|
172
|
-
"""
|
|
173
|
-
reloaded_module = _reload_with_stale_config_cache(
|
|
174
|
-
"grant_project_claude_permissions"
|
|
175
|
-
)
|
|
176
|
-
real_project_root = tmp_path / "project_root"
|
|
177
|
-
(real_project_root / ".claude").mkdir(parents=True)
|
|
178
|
-
bare_directory = tmp_path / "no_claude_marker"
|
|
179
|
-
bare_directory.mkdir()
|
|
180
|
-
assert reloaded_module.is_valid_project_root(real_project_root) is True
|
|
181
|
-
assert reloaded_module.is_valid_project_root(bare_directory) is False
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def test_revoke_module_import_evicts_cached_config_submodules(
|
|
185
|
-
tmp_path: Path,
|
|
186
|
-
) -> None:
|
|
187
|
-
"""revoke_project_claude_permissions must evict cached `config.*` on import.
|
|
188
|
-
|
|
189
|
-
Regression for loop1-3: without a defensive cache pop above sys.path.insert,
|
|
190
|
-
a cached `config` package shadows scripts/config/ and the from-import raises.
|
|
191
|
-
Calls the rebound `is_valid_project_root` to confirm the real implementation
|
|
192
|
-
survived the cache eviction (a stale shadow would either raise on import or
|
|
193
|
-
bind a placeholder that returns the wrong value).
|
|
194
|
-
"""
|
|
195
|
-
reloaded_module = _reload_with_stale_config_cache(
|
|
196
|
-
"revoke_project_claude_permissions"
|
|
197
|
-
)
|
|
198
|
-
real_project_root = tmp_path / "project_root"
|
|
199
|
-
(real_project_root / ".claude").mkdir(parents=True)
|
|
200
|
-
bare_directory = tmp_path / "no_claude_marker"
|
|
201
|
-
bare_directory.mkdir()
|
|
202
|
-
assert reloaded_module.is_valid_project_root(real_project_root) is True
|
|
203
|
-
assert reloaded_module.is_valid_project_root(bare_directory) is False
|
|
@@ -210,38 +210,13 @@ def test_should_exit_nonzero_when_subprocess_run_raises_os_error(
|
|
|
210
210
|
)
|
|
211
211
|
|
|
212
212
|
|
|
213
|
-
def test_module_import_evicts_cached_config_submodules() -> None:
|
|
214
|
-
"""Importing bugteam_preflight must evict cached `config.*` submodules.
|
|
215
|
-
|
|
216
|
-
Regression for loop1-4: a single `sys.modules.pop("config", None)` only
|
|
217
|
-
removes the parent key, leaving stale `config.<submodule>` entries that
|
|
218
|
-
satisfy the next from-import with the wrong bindings.
|
|
219
|
-
"""
|
|
220
|
-
fake_submodule_name = "config.bugteam_preflight_constants"
|
|
221
|
-
fake_parent_name = "config"
|
|
222
|
-
sentinel_module_a = ModuleType(fake_parent_name)
|
|
223
|
-
sentinel_module_b = ModuleType(fake_submodule_name)
|
|
224
|
-
sys.modules[fake_parent_name] = sentinel_module_a
|
|
225
|
-
sys.modules[fake_submodule_name] = sentinel_module_b
|
|
226
|
-
try:
|
|
227
|
-
_load_preflight_module()
|
|
228
|
-
finally:
|
|
229
|
-
sys.modules.pop(fake_parent_name, None)
|
|
230
|
-
sys.modules.pop(fake_submodule_name, None)
|
|
231
|
-
assert sys.modules.get(fake_parent_name) is not sentinel_module_a, (
|
|
232
|
-
"parent `config` cache entry must be evicted on module import"
|
|
233
|
-
)
|
|
234
|
-
assert sys.modules.get(fake_submodule_name) is not sentinel_module_b, (
|
|
235
|
-
"cached `config.<submodule>` entries must be evicted on module import"
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
|
|
239
213
|
def test_has_pytest_configuration_finds_pytest_ini(tmp_path: Path) -> None:
|
|
240
214
|
"""has_pytest_configuration must detect pytest.ini at the repo root.
|
|
241
215
|
|
|
242
216
|
Regression for loop1-17/loop1-18: the literals "pytest.ini",
|
|
243
217
|
"pyproject.toml", and "[tool.pytest" were inlined in production function
|
|
244
|
-
bodies; centralizing them in
|
|
218
|
+
bodies; centralizing them in bugteam_scripts_constants and importing here
|
|
219
|
+
pins the contract.
|
|
245
220
|
"""
|
|
246
221
|
repository_root = tmp_path / "repo"
|
|
247
222
|
repository_root.mkdir()
|
|
@@ -16,7 +16,7 @@ import stat
|
|
|
16
16
|
import sys
|
|
17
17
|
from collections.abc import Callable
|
|
18
18
|
|
|
19
|
-
from
|
|
19
|
+
from bugteam_scripts_constants.windows_safe_rmtree_constants import (
|
|
20
20
|
EXIT_CODE_REMOVE_TREE_FAILURE,
|
|
21
21
|
EXIT_CODE_USAGE_ERROR,
|
|
22
22
|
EXPECTED_ARGUMENT_COUNT,
|
package/skills/doc-gist/SKILL.md
CHANGED
|
@@ -94,6 +94,6 @@ Read the matching example for the artifact you're designing. Crib palette, typog
|
|
|
94
94
|
|
|
95
95
|
- `SKILL.md` — this file.
|
|
96
96
|
- `skills/doc-gist/scripts/gist_upload.py` — transport: HTML in, gist + preview URLs out.
|
|
97
|
-
- `skills/doc-gist/scripts/
|
|
97
|
+
- `skills/doc-gist/scripts/doc_gist_scripts_constants/gist_upload_constants.py` — the URL prefixes and template strings.
|
|
98
98
|
- `references/examples/` — Thariq's 20 html-effectiveness prototypes.
|
|
99
99
|
- (PostToolUse hook lives in `packages/claude-dev-env/hooks/workflow/doc_gist_auto_publish.py` — wired into the plugin's `hooks.json`.)
|
|
@@ -30,7 +30,7 @@ _script_directory = str(Path(__file__).resolve().parent)
|
|
|
30
30
|
if _script_directory not in sys.path:
|
|
31
31
|
sys.path.insert(0, _script_directory)
|
|
32
32
|
|
|
33
|
-
from
|
|
33
|
+
from doc_gist_scripts_constants.gist_upload_constants import ( # noqa: E402
|
|
34
34
|
GIST_DEFAULT_FILENAME,
|
|
35
35
|
GIST_HOST_PREFIX,
|
|
36
36
|
MINIMUM_GIST_URL_PARTS,
|
|
@@ -62,5 +62,5 @@ Append entries as decisions are made — do not batch them until the end.
|
|
|
62
62
|
| File | Purpose |
|
|
63
63
|
|---|---|
|
|
64
64
|
| `SKILL.md` | This hub |
|
|
65
|
-
| `scripts/append_note.py` | CLI to append one entry to a section |
|
|
66
|
-
| `scripts/
|
|
65
|
+
| `packages/claude-dev-env/skills/implement/scripts/append_note.py` | CLI to append one entry to a section |
|
|
66
|
+
| `packages/claude-dev-env/skills/implement/scripts/implement_scripts_constants/notes_constants.py` | Section slugs → headings and default filename |
|
|
@@ -16,7 +16,7 @@ import html
|
|
|
16
16
|
import sys
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
|
-
from
|
|
19
|
+
from implement_scripts_constants.notes_constants import DEFAULT_NOTES_FILENAME, HEADING_BY_SLUG
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def _build_skeleton() -> str:
|
|
File without changes
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
All runtime and API constants live here. Script-specific constants
|
|
4
4
|
(CLI args, markdown patterns, reflow settings) stay in
|
|
5
|
-
``packages/claude-dev-env/skills/pr-converge/scripts/
|
|
5
|
+
``packages/claude-dev-env/skills/pr-converge/scripts/pr_converge_scripts_constants/pr_converge_constants.py``,
|
|
6
6
|
which imports from here.
|
|
7
7
|
"""
|
|
8
8
|
|