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.
Files changed (26) hide show
  1. package/.agents/skills/context-audit/SKILL.md +48 -0
  2. package/.agents/skills/context-audit/references/depth-modes.md +67 -0
  3. package/.agents/skills/context-audit/scripts/context_audit.py +109 -0
  4. package/.agents/skills/context-audit/scripts/context_audit_constants/__init__.py +1 -0
  5. package/.agents/skills/context-audit/scripts/context_audit_constants/config/__init__.py +1 -0
  6. package/.agents/skills/context-audit/scripts/context_audit_constants/config/constants.py +179 -0
  7. package/.agents/skills/context-audit/scripts/context_inventory.py +362 -0
  8. package/.agents/skills/context-audit/scripts/context_report.py +176 -0
  9. package/.agents/skills/context-audit/scripts/context_sources.py +252 -0
  10. package/.agents/skills/context-audit/scripts/test_context_audit.py +217 -0
  11. package/.agents/skills/search-before-acting/SKILL.md +42 -0
  12. package/.agents/skills/search-before-acting/playbooks/build.md +18 -0
  13. package/.agents/skills/search-before-acting/playbooks/delegate.md +9 -0
  14. package/.agents/skills/search-before-acting/playbooks/plan-or-log.md +9 -0
  15. package/.agents/skills/search-before-acting/references/report-shape.md +14 -0
  16. package/.agents/skills/search-before-acting/references/search-places.md +26 -0
  17. package/bin/ever-shipped-skills.mjs +2 -0
  18. package/bin/ever-shipped-skills.test.mjs +4 -0
  19. package/docs/rule-guides/explore-thoroughly.md +10 -0
  20. package/hooks/blocking/pr_lifecycle_skill_gate.py +5 -2
  21. package/hooks/blocking/pull_request_proof.py +50 -8
  22. package/hooks/blocking/test_pr_lifecycle_skill_gate.py +4 -1
  23. package/hooks/blocking/test_pull_request_proof.py +45 -2
  24. package/hooks/hooks_constants/pull_request_proof_constants.py +14 -0
  25. package/package.json +1 -1
  26. package/rules/explore-thoroughly.md +4 -4
@@ -1,4 +1,4 @@
1
- """Deny a new pull request whose body carries no 'Proof in practice' section."""
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
- all_lines = body.splitlines()
62
- for each_index, each_line in enumerate(all_lines):
63
- heading_level = _heading_level(each_line, PROOF_HEADING_PATTERN)
64
- if heading_level is not None:
65
- all_section_lines = _lines_until_heading(all_lines[each_index + 1 :], heading_level)
66
- return LINE_SEPARATOR.join(all_section_lines).strip()
67
- return None
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 = "Follow-up to #1731\n\n## Proof in practice\nRan `python probe.py`.\n"
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 check on a new pull request."""
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 MISSING_BODY_REASON, MISSING_PROOF_REASON
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,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.62.1",
3
+ "version": "8.64.0",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,9 +1,9 @@
1
1
  # Explore thoroughly
2
2
 
3
- **When:** Choosing an implementation approach or recommending an architectural direction.
3
+ **When:** Before you log, plan, delegate, or build an ask, or choose an approach.
4
4
 
5
- Read relevant files, map local patterns, and identify constraints before committing to an approach. Scale exploration to risk: inspect nearby files for a small change, broader examples for a new feature, and the full landscape for an architectural decision. Once the files, constraints, and success condition are known, act without repeating settled research.
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:** none, the agent applies it.
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 when setting exploration depth.
9
+ **Full text:** [`docs/rule-guides/explore-thoroughly.md`](../docs/rule-guides/explore-thoroughly.md). Read it for search scope and depth.