claude-dev-env 8.46.3 → 8.46.4
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/package.json
CHANGED
|
@@ -11,7 +11,7 @@ paths:
|
|
|
11
11
|
|
|
12
12
|
# Flag non-breaking findings
|
|
13
13
|
|
|
14
|
-
**When:**
|
|
14
|
+
**When:** Writing a local gate or changing a check's severity.
|
|
15
15
|
|
|
16
16
|
Give every check ID a breaking or smell severity row; style and structure findings are smells. Fail the gate and stop for a bug, secret, broken test, syntax error, or instruction file that cannot load. Record a smell in `.claude/followups/smells.jsonl`, proceed, and fix it in a later pull request; ledger failure leaves the gate decision unchanged. Treat an unclassified check as breaking.
|
|
17
17
|
|
|
@@ -5,7 +5,7 @@ paths:
|
|
|
5
5
|
|
|
6
6
|
# Rules prose names only hooks that run
|
|
7
7
|
|
|
8
|
-
**When:**
|
|
8
|
+
**When:** Describing a hook or retiring a gate.
|
|
9
9
|
|
|
10
10
|
Verify the module and its hook registration or dispatcher roster before claiming it runs; describe a retired hook in past tense or name the check that carries its work now. When retiring a gate, remove the forced agent calls, tokens, extra steps, thresholds, audit rubrics, and review prompts it required across every instruction lane. Add a retired hook path to the installer roster when deleting its module.
|
|
11
11
|
|
|
@@ -18,11 +18,15 @@ from codex_compat_materializer import (
|
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
FRONTMATTER_DELIMITER = "---"
|
|
21
|
+
FENCE_MARKER = "```"
|
|
21
22
|
FULL_TEXT_LINK_PATTERN = re.compile(
|
|
22
23
|
r"^\*\*Full text:\*\*.*?\]\((\.\./docs/rule-guides/[^)#\s]+\.md)\)", re.MULTILINE
|
|
23
24
|
)
|
|
24
25
|
TITLE_PATTERN = re.compile(r"^# \S", re.MULTILINE)
|
|
25
26
|
WHEN_LINE_PATTERN = re.compile(r"^\*\*When(?: this applies)?:\*\* \S", re.MULTILINE)
|
|
27
|
+
DOUBLED_WHEN_LEAD_IN_PATTERN = re.compile(
|
|
28
|
+
r"^\*\*When(?: this applies)?:\*\* When\b", re.MULTILINE
|
|
29
|
+
)
|
|
26
30
|
|
|
27
31
|
MAXIMUM_ENTRY_BYTES = 1_500
|
|
28
32
|
MAXIMUM_ALWAYS_ON_BYTES = 12_000
|
|
@@ -90,16 +94,74 @@ def test_each_entry_opens_with_a_title_after_its_frontmatter() -> None:
|
|
|
90
94
|
assert untitled == []
|
|
91
95
|
|
|
92
96
|
|
|
97
|
+
def _entry_body_before_full_text(entry_text: str) -> str:
|
|
98
|
+
_, entry_body = _split_frontmatter(entry_text)
|
|
99
|
+
all_kept_lines: list[str] = []
|
|
100
|
+
is_inside_fence = False
|
|
101
|
+
for each_line in entry_body.splitlines():
|
|
102
|
+
if FULL_TEXT_LINK_PATTERN.match(each_line):
|
|
103
|
+
break
|
|
104
|
+
if each_line.lstrip().startswith(FENCE_MARKER):
|
|
105
|
+
is_inside_fence = not is_inside_fence
|
|
106
|
+
continue
|
|
107
|
+
if not is_inside_fence:
|
|
108
|
+
all_kept_lines.append(each_line)
|
|
109
|
+
return "\n".join(all_kept_lines)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _states_when_it_applies(entry_text: str) -> bool:
|
|
113
|
+
return bool(WHEN_LINE_PATTERN.search(_entry_body_before_full_text(entry_text)))
|
|
114
|
+
|
|
115
|
+
|
|
93
116
|
def test_each_entry_states_when_it_applies() -> None:
|
|
94
117
|
without_when_line = [
|
|
95
118
|
each_path.name
|
|
96
119
|
for each_path in _entry_paths()
|
|
97
120
|
if each_path.name not in INDEX_SHAPE_EXEMPT_ENTRY_NAMES
|
|
98
|
-
and not
|
|
121
|
+
and not _states_when_it_applies(_entry_text(each_path))
|
|
99
122
|
]
|
|
100
123
|
assert without_when_line == []
|
|
101
124
|
|
|
102
125
|
|
|
126
|
+
def test_when_line_inside_a_fenced_block_does_not_count() -> None:
|
|
127
|
+
entry_text = (
|
|
128
|
+
"# Example rule\n\n"
|
|
129
|
+
"```markdown\n**When:** Writing an example.\n```\n\n"
|
|
130
|
+
"**Full text:** [`docs/rule-guides/example.md`]"
|
|
131
|
+
"(../docs/rule-guides/example.md).\n"
|
|
132
|
+
)
|
|
133
|
+
assert not _states_when_it_applies(entry_text)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_when_line_below_the_full_text_link_does_not_count() -> None:
|
|
137
|
+
entry_text = (
|
|
138
|
+
"# Example rule\n\n"
|
|
139
|
+
"**Full text:** [`docs/rule-guides/example.md`]"
|
|
140
|
+
"(../docs/rule-guides/example.md).\n\n"
|
|
141
|
+
"**When:** Writing an example.\n"
|
|
142
|
+
)
|
|
143
|
+
assert not _states_when_it_applies(entry_text)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_when_line_in_the_entry_body_counts() -> None:
|
|
147
|
+
entry_text = (
|
|
148
|
+
"---\npaths:\n - \"**/*.py\"\n---\n\n# Example rule\n\n"
|
|
149
|
+
"**When:** Writing an example.\n\n"
|
|
150
|
+
"**Full text:** [`docs/rule-guides/example.md`]"
|
|
151
|
+
"(../docs/rule-guides/example.md).\n"
|
|
152
|
+
)
|
|
153
|
+
assert _states_when_it_applies(entry_text)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_no_entry_repeats_when_after_its_when_label() -> None:
|
|
157
|
+
doubled_lead_in = [
|
|
158
|
+
each_path.name
|
|
159
|
+
for each_path in _entry_paths()
|
|
160
|
+
if DOUBLED_WHEN_LEAD_IN_PATTERN.search(_entry_text(each_path))
|
|
161
|
+
]
|
|
162
|
+
assert doubled_lead_in == []
|
|
163
|
+
|
|
164
|
+
|
|
103
165
|
def _full_text_link_problems(entry_path: Path) -> list[str]:
|
|
104
166
|
all_guide_names = _linked_guide_names(_entry_text(entry_path))
|
|
105
167
|
if not all_guide_names:
|