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
|
@@ -33,7 +33,7 @@ _pr_converge_dir = Path(__file__).absolute().parent.parent
|
|
|
33
33
|
if str(_pr_converge_dir) not in sys.path:
|
|
34
34
|
sys.path.insert(0, str(_pr_converge_dir))
|
|
35
35
|
|
|
36
|
-
from
|
|
36
|
+
from pr_converge_skill_constants.constants import (
|
|
37
37
|
ALL_BUGBOT_CHECK_RUN_ACTIVE_STATUSES,
|
|
38
38
|
ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS,
|
|
39
39
|
BUGBOT_CHECK_RUN_COMPLETED_STATUS,
|
|
@@ -23,7 +23,7 @@ _pr_converge_dir = Path(__file__).absolute().parent.parent
|
|
|
23
23
|
if str(_pr_converge_dir) not in sys.path:
|
|
24
24
|
sys.path.insert(0, str(_pr_converge_dir))
|
|
25
25
|
|
|
26
|
-
from
|
|
26
|
+
from pr_converge_skill_constants.constants import (
|
|
27
27
|
ALL_COPILOT_DIRTY_REVIEW_STATES,
|
|
28
28
|
ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS,
|
|
29
29
|
BUGBOT_CHECK_RUN_NAME_SUBSTRING,
|
|
@@ -495,9 +495,16 @@ def check_all(*, owner: str, repo: str, number: int, bugbot_down: bool) -> int:
|
|
|
495
495
|
|
|
496
496
|
Returns:
|
|
497
497
|
``0`` when every gate reports PASS, ``1`` when at least one gate
|
|
498
|
-
reports FAIL.
|
|
499
|
-
|
|
500
|
-
|
|
498
|
+
reports FAIL. Per-gate ``gh api`` transport failures surface as
|
|
499
|
+
gate FAIL lines in the printed output and contribute to the ``1``
|
|
500
|
+
exit code.
|
|
501
|
+
|
|
502
|
+
Raises:
|
|
503
|
+
SystemExit: Propagated by the initial ``_get_pr_head_sha`` call
|
|
504
|
+
with ``EXIT_CODE_GH_ERROR`` when the PR-head-SHA fetch fails
|
|
505
|
+
before any gate runs. The function does not catch this
|
|
506
|
+
exception; the caller is responsible for converting it into
|
|
507
|
+
an exit code.
|
|
501
508
|
"""
|
|
502
509
|
head_sha = _get_pr_head_sha(owner=owner, repo=repo, number=number)
|
|
503
510
|
print(f"HEAD: {head_sha[:7]}\n")
|
|
@@ -21,7 +21,7 @@ _pr_converge_dir = Path(__file__).absolute().parent.parent
|
|
|
21
21
|
if str(_pr_converge_dir) not in sys.path:
|
|
22
22
|
sys.path.insert(0, str(_pr_converge_dir))
|
|
23
23
|
|
|
24
|
-
from
|
|
24
|
+
from pr_converge_skill_constants.constants import (
|
|
25
25
|
EXIT_CODE_GH_ERROR,
|
|
26
26
|
GH_REVIEWS_PATH_TEMPLATE,
|
|
27
27
|
REVIEWS_PER_PAGE,
|
|
@@ -19,7 +19,7 @@ _pr_converge_dir = Path(__file__).absolute().parent.parent
|
|
|
19
19
|
if str(_pr_converge_dir) not in sys.path:
|
|
20
20
|
sys.path.insert(0, str(_pr_converge_dir))
|
|
21
21
|
|
|
22
|
-
from
|
|
22
|
+
from pr_converge_skill_constants.constants import (
|
|
23
23
|
COPILOT_LOGIN_FILTER_SUBSTRING,
|
|
24
24
|
GH_REVIEWS_PATH_TEMPLATE,
|
|
25
25
|
REVIEWS_PER_PAGE,
|
|
@@ -24,7 +24,7 @@ _pr_converge_dir = Path(__file__).absolute().parent.parent
|
|
|
24
24
|
if str(_pr_converge_dir) not in sys.path:
|
|
25
25
|
sys.path.insert(0, str(_pr_converge_dir))
|
|
26
26
|
|
|
27
|
-
from
|
|
27
|
+
from pr_converge_skill_constants.constants import (
|
|
28
28
|
EXIT_CODE_GH_ERROR,
|
|
29
29
|
GH_INLINE_COMMENT_REPLY_PATH_TEMPLATE,
|
|
30
30
|
GH_ISSUE_COMMENT_CREATE_PATH_TEMPLATE,
|
|
File without changes
|
package/skills/pr-converge/scripts/{config → pr_converge_scripts_constants}/pr_converge_constants.py
RENAMED
|
@@ -11,7 +11,7 @@ settings) live here.
|
|
|
11
11
|
import re
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
|
|
14
|
-
from
|
|
14
|
+
from pr_converge_skill_constants.constants import ( # noqa: F401
|
|
15
15
|
ALL_BUGBOT_CHECK_RUN_ACTIVE_STATUSES,
|
|
16
16
|
ALL_CLAUDE_DIRTY_REVIEW_STATES,
|
|
17
17
|
ALL_COPILOT_DIRTY_REVIEW_STATES,
|
|
@@ -9,22 +9,9 @@ Run: python3 packages/claude-dev-env/skills/pr-converge/scripts/reflow_skill_md.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
import sys
|
|
13
12
|
import textwrap
|
|
14
|
-
from pathlib import Path
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
while script_directory in sys.path:
|
|
19
|
-
sys.path.remove(script_directory)
|
|
20
|
-
if script_directory not in sys.path:
|
|
21
|
-
sys.path.insert(0, script_directory)
|
|
22
|
-
|
|
23
|
-
from evict_cached_config_modules import evict_cached_config_modules
|
|
24
|
-
|
|
25
|
-
evict_cached_config_modules()
|
|
26
|
-
|
|
27
|
-
from config.reflow_skill_md_constants import (
|
|
14
|
+
from pr_converge_scripts_constants.reflow_skill_md_constants import (
|
|
28
15
|
BASH_CONTINUATION_MARKER_WIDTH,
|
|
29
16
|
BULLET_LIST_ITEM_PATTERN as BULLET_RE,
|
|
30
17
|
MARKDOWN_REFERENCE_DEFINITION_PATTERN as REF_DEF_RE,
|
|
@@ -36,6 +23,14 @@ from config.reflow_skill_md_constants import (
|
|
|
36
23
|
|
|
37
24
|
|
|
38
25
|
def wrap_paragraph_plain(text: str) -> list[str]:
|
|
26
|
+
"""Wrap a plain paragraph to MAX_WIDTH after collapsing whitespace.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
text: Paragraph text with internal whitespace runs to collapse.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Wrapped lines; empty list when the input collapses to nothing.
|
|
33
|
+
"""
|
|
39
34
|
collapsed = " ".join(text.split())
|
|
40
35
|
if not collapsed:
|
|
41
36
|
return []
|
|
@@ -48,6 +43,17 @@ def wrap_paragraph_plain(text: str) -> list[str]:
|
|
|
48
43
|
|
|
49
44
|
|
|
50
45
|
def wrap_list_item(lead_ws: str, marker: str, body: str) -> list[str]:
|
|
46
|
+
"""Wrap a list item, preserving the leading marker and indentation.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
lead_ws: Leading whitespace before the list marker.
|
|
50
|
+
marker: List marker such as a hyphen or numeric prefix.
|
|
51
|
+
body: Item body text to wrap.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Wrapped lines with the marker on the first line and matching
|
|
55
|
+
indentation on subsequent lines.
|
|
56
|
+
"""
|
|
51
57
|
collapsed = " ".join(body.split())
|
|
52
58
|
if not collapsed:
|
|
53
59
|
return [lead_ws + marker.rstrip()]
|
|
@@ -67,6 +73,16 @@ def reflow_yaml_description_block(
|
|
|
67
73
|
all_lines: list[str],
|
|
68
74
|
body_start: int,
|
|
69
75
|
) -> tuple[list[str], int]:
|
|
76
|
+
"""Reflow the YAML description block until the closing fence.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
all_lines: Full SKILL.md lines.
|
|
80
|
+
body_start: Index of the first description body line.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Tuple of wrapped description lines and the index just past the
|
|
84
|
+
closing fence.
|
|
85
|
+
"""
|
|
70
86
|
body_parts: list[str] = []
|
|
71
87
|
index = body_start
|
|
72
88
|
while index < len(all_lines):
|
|
@@ -95,6 +111,16 @@ def is_table_line(line: str) -> bool:
|
|
|
95
111
|
|
|
96
112
|
|
|
97
113
|
def is_new_logical_line(stripped: str) -> bool:
|
|
114
|
+
"""Decide whether ``stripped`` starts a new logical line.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
stripped: Candidate line with leading whitespace already removed.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
True when the line begins a new markdown construct (fence, heading,
|
|
121
|
+
table row, list item, reference definition, or example tag) and
|
|
122
|
+
therefore must not be merged into the prior buffer.
|
|
123
|
+
"""
|
|
98
124
|
if not stripped:
|
|
99
125
|
return False
|
|
100
126
|
if stripped.startswith("```"):
|
|
@@ -115,7 +141,16 @@ def is_new_logical_line(stripped: str) -> bool:
|
|
|
115
141
|
|
|
116
142
|
|
|
117
143
|
def merge_without_space(buffer: str, continuation: str) -> bool:
|
|
118
|
-
"""Join without space only for split markdown link URL paths.
|
|
144
|
+
"""Join without space only for split markdown link URL paths.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
buffer: Accumulated line preceding the candidate continuation.
|
|
148
|
+
continuation: Next line to evaluate for joining.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
True when continuation is the tail of a split markdown link target
|
|
152
|
+
and buffer ends inside an unfinished link target.
|
|
153
|
+
"""
|
|
119
154
|
base = buffer.rstrip()
|
|
120
155
|
stripped = continuation.lstrip()
|
|
121
156
|
if not base or not stripped:
|
|
@@ -126,6 +161,15 @@ def merge_without_space(buffer: str, continuation: str) -> bool:
|
|
|
126
161
|
|
|
127
162
|
|
|
128
163
|
def merge_soft_breaks(all_lines: list[str]) -> list[str]:
|
|
164
|
+
"""Merge soft line breaks across non-fence markdown paragraphs.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
all_lines: Raw SKILL.md lines.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Lines with each paragraph collapsed to a single buffer line; fences
|
|
171
|
+
and blank lines are preserved verbatim.
|
|
172
|
+
"""
|
|
129
173
|
reflowed_lines: list[str] = []
|
|
130
174
|
index = 0
|
|
131
175
|
is_inside_fence = False
|
|
@@ -166,6 +210,15 @@ def merge_soft_breaks(all_lines: list[str]) -> list[str]:
|
|
|
166
210
|
|
|
167
211
|
|
|
168
212
|
def reflow_merged_line(line: str) -> list[str]:
|
|
213
|
+
"""Reflow a single merged buffer line into MAX_WIDTH-bounded lines.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
line: Buffer line produced by merge_soft_breaks.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Wrapped lines; structural constructs (fences, tables, separators)
|
|
220
|
+
are returned unchanged.
|
|
221
|
+
"""
|
|
169
222
|
stripped = line.strip()
|
|
170
223
|
if stripped == "":
|
|
171
224
|
return [""]
|
|
@@ -221,6 +274,14 @@ def reflow_merged_line(line: str) -> list[str]:
|
|
|
221
274
|
|
|
222
275
|
|
|
223
276
|
def reflow_markdown_body(all_lines: list[str]) -> list[str]:
|
|
277
|
+
"""Merge soft breaks then reflow every line of the SKILL.md body.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
all_lines: Raw SKILL.md body lines following the YAML front matter.
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
Reflowed body lines bounded by MAX_WIDTH.
|
|
284
|
+
"""
|
|
224
285
|
merged = merge_soft_breaks(all_lines)
|
|
225
286
|
reflowed_lines: list[str] = []
|
|
226
287
|
for each_line in merged:
|
|
@@ -232,7 +293,15 @@ def reflow_markdown_body(all_lines: list[str]) -> list[str]:
|
|
|
232
293
|
|
|
233
294
|
|
|
234
295
|
def wrap_long_bash_fence_lines(all_lines: list[str]) -> list[str]:
|
|
235
|
-
"""Hard-wrap
|
|
296
|
+
"""Hard-wrap bash fence bodies that still exceed MAX_WIDTH.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
all_lines: SKILL.md body lines after paragraph reflow.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Lines with overlong bash-fence bodies split on whitespace with a
|
|
303
|
+
trailing backslash continuation marker.
|
|
304
|
+
"""
|
|
236
305
|
wrapped_lines: list[str] = []
|
|
237
306
|
is_inside_bash_fence = False
|
|
238
307
|
for each_line in all_lines:
|
|
@@ -272,6 +341,11 @@ def wrap_long_bash_fence_lines(all_lines: list[str]) -> list[str]:
|
|
|
272
341
|
|
|
273
342
|
|
|
274
343
|
def main() -> None:
|
|
344
|
+
"""Read SKILL.md, reflow it to MAX_WIDTH, and write the result back.
|
|
345
|
+
|
|
346
|
+
Raises:
|
|
347
|
+
SystemExit: When the file does not start with YAML front matter.
|
|
348
|
+
"""
|
|
275
349
|
raw = SKILL_PATH.read_text(encoding="utf-8")
|
|
276
350
|
lines = raw.splitlines()
|
|
277
351
|
if not lines or lines[0].strip() != "---":
|
|
@@ -304,3 +304,21 @@ def should_bypass_bugbot_gates_when_bugbot_down_is_true(
|
|
|
304
304
|
assert "_check_bugbot_not_dirty" not in all_invocation_names
|
|
305
305
|
assert "bypassed (bugbot_down)" in captured_stdout
|
|
306
306
|
assert exit_code == 0
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def should_propagate_systemexit_from_get_pr_head_sha(
|
|
310
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
311
|
+
) -> None:
|
|
312
|
+
def stub_get_pr_head_sha_raising_systemexit(
|
|
313
|
+
*, owner: str, repo: str, number: int
|
|
314
|
+
) -> str:
|
|
315
|
+
raise SystemExit(check_convergence.EXIT_CODE_GH_ERROR)
|
|
316
|
+
|
|
317
|
+
monkeypatch.setattr(
|
|
318
|
+
check_convergence, "_get_pr_head_sha", stub_get_pr_head_sha_raising_systemexit
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
322
|
+
check_convergence.check_all(owner="o", repo="r", number=1, bugbot_down=False)
|
|
323
|
+
|
|
324
|
+
assert exc_info.value.code == check_convergence.EXIT_CODE_GH_ERROR
|
|
@@ -108,27 +108,6 @@ def test_reflow_merged_line_preserves_long_markdown_reference_definition() -> No
|
|
|
108
108
|
assert reflow_module.reflow_merged_line(line) == [stripped_line]
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
def test_reflow_bootstrap_moves_script_directory_ahead_of_shadow_config(
|
|
112
|
-
tmp_path: Path,
|
|
113
|
-
) -> None:
|
|
114
|
-
"""sys.path bootstrap must move the script directory ahead of shadow config packages."""
|
|
115
|
-
shadow_config_directory = tmp_path / "shadow" / "config"
|
|
116
|
-
shadow_config_directory.mkdir(parents=True)
|
|
117
|
-
(shadow_config_directory / "__init__.py").write_text("", encoding="utf-8")
|
|
118
|
-
(shadow_config_directory / "pr_converge_constants.py").write_text(
|
|
119
|
-
"BROKEN = True\n", encoding="utf-8"
|
|
120
|
-
)
|
|
121
|
-
original_sys_path = list(sys.path)
|
|
122
|
-
try:
|
|
123
|
-
sys.path.insert(0, str(tmp_path / "shadow"))
|
|
124
|
-
loaded_module = _load_module()
|
|
125
|
-
assert loaded_module.MAX_WIDTH == 80
|
|
126
|
-
assert sys.path[0] == str(_SCRIPTS_DIRECTORY)
|
|
127
|
-
assert sys.path.count(str(_SCRIPTS_DIRECTORY)) == 1
|
|
128
|
-
finally:
|
|
129
|
-
sys.path[:] = original_sys_path
|
|
130
|
-
|
|
131
|
-
|
|
132
111
|
def test_wrap_long_bash_fence_lines_uses_continuation_marker_for_long_lines() -> None:
|
|
133
112
|
"""Wrapped continuation lines use the bash continuation marker."""
|
|
134
113
|
long_line = "echo " + "word " * 20
|
|
@@ -150,13 +129,3 @@ def test_reflow_uses_config_constant_for_continuation_marker_width() -> None:
|
|
|
150
129
|
"reflow_skill_md.py must import BASH_CONTINUATION_MARKER_WIDTH from config"
|
|
151
130
|
)
|
|
152
131
|
|
|
153
|
-
def test_reflow_bootstrap_matches_code_rules_sys_path_pattern() -> None:
|
|
154
|
-
"""Bootstrap must guard insert with a membership check."""
|
|
155
|
-
module_path = _SCRIPTS_DIRECTORY / "reflow_skill_md.py"
|
|
156
|
-
source = module_path.read_text(encoding="utf-8")
|
|
157
|
-
assert "while script_directory in sys.path:" in source, (
|
|
158
|
-
"Bootstrap must dedup script_directory entries before insert"
|
|
159
|
-
)
|
|
160
|
-
assert "sys.path.insert(0, script_directory)" in source, (
|
|
161
|
-
"Bootstrap must insert script_directory at index 0"
|
|
162
|
-
)
|