claude-dev-env 8.62.1 → 8.64.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/.agents/skills/context-audit/SKILL.md +48 -0
- package/.agents/skills/context-audit/references/depth-modes.md +67 -0
- package/.agents/skills/context-audit/scripts/context_audit.py +109 -0
- package/.agents/skills/context-audit/scripts/context_audit_constants/__init__.py +1 -0
- package/.agents/skills/context-audit/scripts/context_audit_constants/config/__init__.py +1 -0
- package/.agents/skills/context-audit/scripts/context_audit_constants/config/constants.py +179 -0
- package/.agents/skills/context-audit/scripts/context_inventory.py +362 -0
- package/.agents/skills/context-audit/scripts/context_report.py +176 -0
- package/.agents/skills/context-audit/scripts/context_sources.py +252 -0
- package/.agents/skills/context-audit/scripts/test_context_audit.py +217 -0
- package/.agents/skills/search-before-acting/SKILL.md +42 -0
- package/.agents/skills/search-before-acting/playbooks/build.md +18 -0
- package/.agents/skills/search-before-acting/playbooks/delegate.md +9 -0
- package/.agents/skills/search-before-acting/playbooks/plan-or-log.md +9 -0
- package/.agents/skills/search-before-acting/references/report-shape.md +14 -0
- package/.agents/skills/search-before-acting/references/search-places.md +26 -0
- package/bin/ever-shipped-skills.mjs +2 -0
- package/bin/ever-shipped-skills.test.mjs +4 -0
- package/docs/rule-guides/explore-thoroughly.md +10 -0
- package/hooks/blocking/pr_lifecycle_skill_gate.py +5 -2
- package/hooks/blocking/pull_request_proof.py +50 -8
- package/hooks/blocking/test_pr_lifecycle_skill_gate.py +4 -1
- package/hooks/blocking/test_pull_request_proof.py +45 -2
- package/hooks/hooks_constants/pull_request_proof_constants.py +14 -0
- package/package.json +1 -1
- package/rules/explore-thoroughly.md +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Deny a new pull request whose body
|
|
1
|
+
"""Deny a new pull request whose body lacks a 'Proof in practice' or an 'Existing work' section."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -18,8 +18,10 @@ from hooks_constants.pull_request_proof_constants import (
|
|
|
18
18
|
ALL_PYTHON_PROGRAM_NAMES,
|
|
19
19
|
COMMAND_MARKER,
|
|
20
20
|
CREATE_PULL_REQUEST_TOOL_SUFFIX,
|
|
21
|
+
EXISTING_WORK_HEADING_PATTERN,
|
|
21
22
|
LINE_SEPARATOR,
|
|
22
23
|
MISSING_BODY_REASON,
|
|
24
|
+
MISSING_EXISTING_WORK_REASON,
|
|
23
25
|
MISSING_PROOF_REASON,
|
|
24
26
|
NEXT_HEADING_PATTERN,
|
|
25
27
|
PROOF_HEADING_PATTERN,
|
|
@@ -48,6 +50,16 @@ def _lines_until_heading(all_lines: list[str], heading_level: int) -> list[str]:
|
|
|
48
50
|
return all_lines
|
|
49
51
|
|
|
50
52
|
|
|
53
|
+
def _section_text(body: str, heading_pattern: str) -> str | None:
|
|
54
|
+
all_lines = body.splitlines()
|
|
55
|
+
for each_index, each_line in enumerate(all_lines):
|
|
56
|
+
heading_level = _heading_level(each_line, heading_pattern)
|
|
57
|
+
if heading_level is not None:
|
|
58
|
+
all_section_lines = _lines_until_heading(all_lines[each_index + 1 :], heading_level)
|
|
59
|
+
return LINE_SEPARATOR.join(all_section_lines).strip()
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
|
|
51
63
|
def proof_section(body: str) -> str | None:
|
|
52
64
|
"""Return the text under the body's 'Proof in practice' heading, or None.
|
|
53
65
|
|
|
@@ -58,13 +70,20 @@ def proof_section(body: str) -> str | None:
|
|
|
58
70
|
|
|
59
71
|
The section runs to the next heading of the same or a higher level.
|
|
60
72
|
"""
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
return _section_text(body, PROOF_HEADING_PATTERN)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def section_under_existing_work(body: str) -> str | None:
|
|
77
|
+
"""Return the text under the body's 'Existing work' heading, or None.
|
|
78
|
+
|
|
79
|
+
::
|
|
80
|
+
|
|
81
|
+
"## Existing work\\n`a.py:4` runs it\\n## Notes" -> "`a.py:4` runs it"
|
|
82
|
+
"## Summary\\nno existing work heading" -> None
|
|
83
|
+
|
|
84
|
+
The section runs to the next heading of the same or a higher level.
|
|
85
|
+
"""
|
|
86
|
+
return _section_text(body, EXISTING_WORK_HEADING_PATTERN)
|
|
68
87
|
|
|
69
88
|
|
|
70
89
|
def _script_arguments(program: str, all_arguments: list[str]) -> list[str] | None:
|
|
@@ -139,3 +158,26 @@ def missing_proof_reason(all_payload_fields: Mapping[str, object]) -> str | None
|
|
|
139
158
|
if not section or COMMAND_MARKER not in section:
|
|
140
159
|
return MISSING_PROOF_REASON
|
|
141
160
|
return None
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def missing_existing_work_reason(all_payload_fields: Mapping[str, object]) -> str | None:
|
|
164
|
+
"""Return a deny reason when a new pull request's body has no existing-work section, else None.
|
|
165
|
+
|
|
166
|
+
::
|
|
167
|
+
|
|
168
|
+
mcp__github__create_pull_request, body has the section -> None
|
|
169
|
+
mcp__github__create_pull_request, body without it -> MISSING_EXISTING_WORK_REASON
|
|
170
|
+
gh pr create --fill -> None
|
|
171
|
+
gh pr edit, git push, any other tool -> None
|
|
172
|
+
|
|
173
|
+
The section is an 'Existing work' heading with text under it. A create
|
|
174
|
+
command whose body this gate cannot read passes here, because
|
|
175
|
+
missing_proof_reason denies it first.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
all_payload_fields: The parsed PreToolUse input.
|
|
179
|
+
"""
|
|
180
|
+
for each_body in _create_bodies(all_payload_fields):
|
|
181
|
+
if each_body is not None and not section_under_existing_work(each_body):
|
|
182
|
+
return MISSING_EXISTING_WORK_REASON
|
|
183
|
+
return None
|
|
@@ -15,7 +15,10 @@ from blocking import pr_lifecycle_skill_gate as gate
|
|
|
15
15
|
from hooks_constants.pr_lifecycle_skill_gate_constants import DENY_REASON
|
|
16
16
|
from hooks_constants.pull_request_proof_constants import MISSING_PROOF_REASON
|
|
17
17
|
|
|
18
|
-
PROVEN_FOLLOWUP_BODY =
|
|
18
|
+
PROVEN_FOLLOWUP_BODY = (
|
|
19
|
+
"Follow-up to #1731\n\n## Existing work\nNothing found in open or merged pull requests.\n\n"
|
|
20
|
+
"## Proof in practice\nRan `python probe.py`.\n"
|
|
21
|
+
)
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
def _transcript(tmp_path: Path, skill_name: str | None = None, compact: bool = False) -> Path:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Behavior tests for the proof-in-practice
|
|
1
|
+
"""Behavior tests for the proof-in-practice and existing-work checks on a new pull request."""
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
4
|
from pathlib import Path
|
|
@@ -8,10 +8,19 @@ if str(HOOKS_DIRECTORY) not in sys.path:
|
|
|
8
8
|
sys.path.insert(0, str(HOOKS_DIRECTORY))
|
|
9
9
|
|
|
10
10
|
from blocking import pull_request_proof as gate
|
|
11
|
-
from hooks_constants.pull_request_proof_constants import
|
|
11
|
+
from hooks_constants.pull_request_proof_constants import (
|
|
12
|
+
MISSING_BODY_REASON,
|
|
13
|
+
MISSING_EXISTING_WORK_REASON,
|
|
14
|
+
MISSING_PROOF_REASON,
|
|
15
|
+
)
|
|
12
16
|
|
|
13
17
|
PROVEN_BODY = "## Summary\nAdds a gate.\n\n## Proof in practice\nRan `python run.py`.\n> denied\n"
|
|
14
18
|
UNPROVEN_BODY = "## Summary\nAdds a gate.\n\n## Verification\nUnit tests pass.\n"
|
|
19
|
+
SEARCHED_BODY = (
|
|
20
|
+
"## Summary\nAdds a gate.\n\n## Existing work\n`gate.py:12` runs the check; this adds a flag.\n\n"
|
|
21
|
+
"## Proof in practice\nRan `python run.py`.\n"
|
|
22
|
+
)
|
|
23
|
+
EMPTY_EXISTING_WORK_BODY = "## Existing work\n\n## Proof in practice\nRan `python run.py`.\n"
|
|
15
24
|
|
|
16
25
|
|
|
17
26
|
def _shell(command: str, working_directory: Path | None = None) -> dict[str, object]:
|
|
@@ -90,3 +99,37 @@ def test_other_pull_request_commands_should_pass() -> None:
|
|
|
90
99
|
def test_other_tools_should_pass() -> None:
|
|
91
100
|
payload = {"tool_name": "mcp__github__update_pull_request", "tool_input": {"body": "x"}}
|
|
92
101
|
assert gate.missing_proof_reason(payload) is None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_existing_work_section_should_stop_at_a_heading_of_the_same_level() -> None:
|
|
105
|
+
assert gate.section_under_existing_work(SEARCHED_BODY) == "`gate.py:12` runs the check; this adds a flag."
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_mcp_create_without_existing_work_should_be_denied() -> None:
|
|
109
|
+
assert gate.missing_existing_work_reason(_mcp_create(PROVEN_BODY)) == MISSING_EXISTING_WORK_REASON
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_mcp_create_with_an_empty_existing_work_section_should_be_denied() -> None:
|
|
113
|
+
payload = _mcp_create(EMPTY_EXISTING_WORK_BODY)
|
|
114
|
+
assert gate.missing_existing_work_reason(payload) == MISSING_EXISTING_WORK_REASON
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_mcp_create_with_existing_work_should_pass() -> None:
|
|
118
|
+
assert gate.missing_existing_work_reason(_mcp_create(SEARCHED_BODY)) is None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_gh_create_body_file_without_existing_work_should_be_denied(tmp_path: Path) -> None:
|
|
122
|
+
(tmp_path / "body.md").write_text(PROVEN_BODY, encoding="utf-8")
|
|
123
|
+
payload = _shell("gh pr create --draft --body-file body.md", tmp_path)
|
|
124
|
+
assert gate.missing_existing_work_reason(payload) == MISSING_EXISTING_WORK_REASON
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_unreadable_body_should_leave_the_deny_to_the_proof_check() -> None:
|
|
128
|
+
payload = _shell("gh pr create --fill")
|
|
129
|
+
assert gate.missing_existing_work_reason(payload) is None
|
|
130
|
+
assert gate.missing_proof_reason(payload) == MISSING_BODY_REASON
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_other_pull_request_commands_should_pass_the_existing_work_check() -> None:
|
|
134
|
+
for each_command in ("gh pr edit 3 --body x", "git push -u origin b", "gh pr view"):
|
|
135
|
+
assert gate.missing_existing_work_reason(_shell(each_command)) is None
|
|
@@ -25,3 +25,17 @@ MISSING_PROOF_REASON = (
|
|
|
25
25
|
" prove the change, leave the pull request unopened and tell the user what you could"
|
|
26
26
|
" not run. Read " + PROOF_GUIDE_PATH + " for the full rule."
|
|
27
27
|
)
|
|
28
|
+
EXISTING_WORK_HEADING_PATTERN = r"^(#{1,6})[ \t]+existing work[ \t]*#*[ \t]*$"
|
|
29
|
+
EXISTING_WORK_GUIDE_PATH = "~/.claude/docs/rule-guides/explore-thoroughly.md"
|
|
30
|
+
MISSING_EXISTING_WORK_REASON = (
|
|
31
|
+
"This pull request body has no 'Existing work' section. Before you open a pull"
|
|
32
|
+
" request, search for what already does the change in full or in part: repository"
|
|
33
|
+
" code, open and merged pull requests, hooks, rules, skills, workflows, trackers and"
|
|
34
|
+
" production config. Add an 'Existing work' heading to the body. Under it, list each"
|
|
35
|
+
" piece you found with its link or file:line and what this pull request adds on top"
|
|
36
|
+
" of it, or state that nothing was found and name the places you searched. When the"
|
|
37
|
+
" search finds a piece that already does the change, stop and report it to the user"
|
|
38
|
+
" before you open the pull request. Run the search-before-acting skill and read "
|
|
39
|
+
+ EXISTING_WORK_GUIDE_PATH
|
|
40
|
+
+ " for the full rule."
|
|
41
|
+
)
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Explore thoroughly
|
|
2
2
|
|
|
3
|
-
**When:**
|
|
3
|
+
**When:** Before you log, plan, delegate, or build an ask, or choose an approach.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Search for what already does the ask, in full or in part. When something exists, stop and report it to the user in four points; run the `search-before-acting` skill. Then read files, patterns, and constraints, scaled to risk. Once the files, constraints, and success condition are known, act without repeating settled research.
|
|
6
6
|
|
|
7
|
-
**Enforcement:**
|
|
7
|
+
**Enforcement:** the pull request gate denies a body with no "Existing work" section.
|
|
8
8
|
|
|
9
|
-
**Full text:** [`docs/rule-guides/explore-thoroughly.md`](../docs/rule-guides/explore-thoroughly.md). Read it
|
|
9
|
+
**Full text:** [`docs/rule-guides/explore-thoroughly.md`](../docs/rule-guides/explore-thoroughly.md). Read it for search scope and depth.
|