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
|
@@ -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
|
-
)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pre-compact
|
|
3
|
+
description: >-
|
|
4
|
+
Composes a focus directive for `/compact [instructions]` and copies the full
|
|
5
|
+
`/compact <directive>` string to the operator's clipboard so the next prompt
|
|
6
|
+
is a single paste. The directive pins the session's load-bearing identifiers
|
|
7
|
+
(branch, PR, HEAD, worktree, in-flight work, decisions, blockers, files in
|
|
8
|
+
play, follow-ups) and lists the redundant tool outputs the summarizer should
|
|
9
|
+
drop. Use when the user says `/pre-compact`, asks to prep for compaction, or
|
|
10
|
+
asks to compose a focus directive for `/compact`.
|
|
11
|
+
disable-model-invocation: true
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Pre-Compact
|
|
15
|
+
|
|
16
|
+
`/compact [instructions]` accepts a focus directive that steers the
|
|
17
|
+
compaction-summary LLM toward high-signal content. This skill writes that
|
|
18
|
+
directive from the live session and copies the full `/compact <directive>`
|
|
19
|
+
string to the operator's clipboard.
|
|
20
|
+
|
|
21
|
+
**Announce at start:** "I'm composing your compact focus directive."
|
|
22
|
+
|
|
23
|
+
## Step 1 — Read the live session
|
|
24
|
+
|
|
25
|
+
Pull the load-bearing identifiers from the current conversation. Run
|
|
26
|
+
`git status`, `git rev-parse --short HEAD`, or `gh pr view` when values are
|
|
27
|
+
not already in context.
|
|
28
|
+
|
|
29
|
+
| Field | What to capture | Source |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `branch` | Active branch name | `git branch --show-current` |
|
|
32
|
+
| `pr` | Active PR number, when one exists | `gh pr view --json number` |
|
|
33
|
+
| `head` | Short HEAD SHA (whatever `git rev-parse --short` outputs) | `git rev-parse --short HEAD` |
|
|
34
|
+
| `worktree` | Absolute path to the working directory | `pwd` |
|
|
35
|
+
| `in_flight` | One sentence describing what is being worked on right now | conversation |
|
|
36
|
+
| `decisions` | Architectural choices, library picks, tradeoffs settled this session | conversation |
|
|
37
|
+
| `blockers` | Failures observed, root causes identified, fixes pending | conversation |
|
|
38
|
+
| `files` | Paths the operator is iterating on (edited or read more than once) | conversation |
|
|
39
|
+
| `follow_ups` | What the user asked to be remembered or revisited | conversation |
|
|
40
|
+
|
|
41
|
+
A field whose value cannot be stated as a concrete identifier is omitted
|
|
42
|
+
from the directive.
|
|
43
|
+
|
|
44
|
+
## Step 2 — Render the directive
|
|
45
|
+
|
|
46
|
+
Render this exact shape, populating only the fields with concrete values:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Preserve:
|
|
50
|
+
- Branch: <name>
|
|
51
|
+
- PR: #<number>
|
|
52
|
+
- HEAD: <short-sha>
|
|
53
|
+
- Worktree: <path>
|
|
54
|
+
- In-flight: <one sentence>
|
|
55
|
+
- Decisions: <bullet per decision>
|
|
56
|
+
- Blockers: <bullet per blocker>
|
|
57
|
+
- Files: <path>, <path>, <path>
|
|
58
|
+
- Follow-ups: <bullet per follow-up>
|
|
59
|
+
|
|
60
|
+
Drop:
|
|
61
|
+
- Tool outputs already applied to files
|
|
62
|
+
- Per-tick progress narration
|
|
63
|
+
- Resolved findings and superseded SHAs
|
|
64
|
+
- Listing/grep output whose conclusion appears above
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The `Preserve:` block leads so the summarizer maximizes recall first. The
|
|
68
|
+
`Drop:` block lists the lightest-touch removals — raw tool outputs are the
|
|
69
|
+
safest content to drop because the work they produced lives in the files
|
|
70
|
+
and commits.
|
|
71
|
+
|
|
72
|
+
Source: [Effective context engineering for AI agents](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents)
|
|
73
|
+
— "start by maximizing recall to ensure your compaction prompt captures
|
|
74
|
+
every relevant piece of information from the trace, then iterate to improve
|
|
75
|
+
precision by eliminating superfluous content."
|
|
76
|
+
|
|
77
|
+
## Step 3 — Copy `/compact <directive>` to the clipboard
|
|
78
|
+
|
|
79
|
+
Write the full `/compact <directive>` string to a temporary file via the
|
|
80
|
+
Write tool, then copy the file contents to the clipboard with PowerShell:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
pwsh -NoProfile -Command "Get-Content -LiteralPath '<temp file path>' -Raw | Set-Clipboard"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`Get-Content -LiteralPath … -Raw` reads the file as a single string with
|
|
87
|
+
no wildcard expansion, and `Set-Clipboard` writes it verbatim. The intermediate file keeps the directive content out
|
|
88
|
+
of any shell-parsing path: session text passes through `Get-Content`
|
|
89
|
+
unmodified regardless of which characters it contains.
|
|
90
|
+
|
|
91
|
+
A reasonable temp path under `$env:TEMP` (Windows) or `$TMPDIR` (POSIX)
|
|
92
|
+
works; clean it up after the `Set-Clipboard` call returns.
|
|
93
|
+
|
|
94
|
+
## Step 4 — Hand off
|
|
95
|
+
|
|
96
|
+
Print this confirmation line to the operator:
|
|
97
|
+
|
|
98
|
+
> Copied `/compact …` to your clipboard. Paste it as your next prompt to
|
|
99
|
+
> compact this conversation with focus.
|
|
100
|
+
|
|
101
|
+
Then list up to the first three `Preserve:` bullets (or fewer when the
|
|
102
|
+
directive omits fields) and the first `Drop:` bullet inline so the
|
|
103
|
+
operator can spot-check before pasting.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## References
|
|
108
|
+
|
|
109
|
+
- `/compact [instructions]` — [Claude Code commands](https://code.claude.com/docs/en/commands)
|
|
110
|
+
- Compaction strategy — [Effective context engineering for AI agents](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents)
|
|
111
|
+
|
|
112
|
+
## Folder map
|
|
113
|
+
|
|
114
|
+
- `SKILL.md` — hub. No companions.
|