claude-dev-env 8.46.4 → 8.46.6
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/agents-archived/clean-coder.md +1 -1
- package/.agents/agents-archived/code-quality-agent.md +1 -1
- package/.agents/skills/grok-spawn/reference/worker-briefs.md +1 -1
- package/AGENTS.md +1 -1
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/code_rules_comments.py +4 -4
- package/hooks/blocking/pr_lifecycle_skill_gate.py +22 -1
- package/hooks/blocking/test_pr_lifecycle_skill_gate.py +43 -0
- package/hooks/hooks_constants/pr_lifecycle_skill_gate_constants.py +5 -0
- package/package.json +1 -1
- package/system-prompts/software-engineer.xml +1 -1
|
@@ -135,7 +135,7 @@ Consult the advisor the spawn ticket names. When the ticket names the orchestrat
|
|
|
135
135
|
[3] Parameters and returns typed; no Any / no type-ignore directives?
|
|
136
136
|
[4] Do not add code comments. Preserve existing comments. Docstrings remain allowed.
|
|
137
137
|
[5] When a change touches code that an existing comment describes or is attached to, remove that comment in the same change and carry its meaning through clear names and structure. Leave comments tied to untouched code unchanged. Keep comment cleanup inside the requested task.
|
|
138
|
-
[6] Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed
|
|
138
|
+
[6] Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed. Do not add or justify them.
|
|
139
139
|
[7] Magic values and UPPER_SNAKE live in config/ where required?
|
|
140
140
|
[8] Function short; one job; guards over else-chains?
|
|
141
141
|
[9] Pre-check --check clean for the real destination path?
|
|
@@ -63,7 +63,7 @@ Tradeoff for category-restricted mode: parallel category invocation loses cross-
|
|
|
63
63
|
|
|
64
64
|
Leave comments tied to untouched code unchanged. Findings on production and test code report only on new code added by the diff; comments on lines that remain otherwise unchanged stay outside the audit's scope. New inline comments added by this PR's diff are themselves a category J finding (code self-documents through naming).
|
|
65
65
|
When a change touches code that an existing comment describes or is attached to, remove that comment in the same change and carry its meaning through clear names and structure. Leave comments tied to untouched code unchanged. Keep comment cleanup inside the requested task.
|
|
66
|
-
Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed
|
|
66
|
+
Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed. Do not add or justify them.
|
|
67
67
|
|
|
68
68
|
## Read-Only Stance
|
|
69
69
|
|
|
@@ -63,7 +63,7 @@ You edit code and run tests for one closed task. You never commit, push, or call
|
|
|
63
63
|
|
|
64
64
|
Do not add code comments. Preserve existing comments. Docstrings remain allowed.
|
|
65
65
|
When a change touches code that an existing comment describes or is attached to, remove that comment in the same change and carry its meaning through clear names and structure. Leave comments tied to untouched code unchanged. Keep comment cleanup inside the requested task.
|
|
66
|
-
Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed
|
|
66
|
+
Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed. Do not add or justify them.
|
|
67
67
|
|
|
68
68
|
## Scope
|
|
69
69
|
|
package/AGENTS.md
CHANGED
|
@@ -68,7 +68,7 @@ Match requested status, paths, links, and format. Name files, checks, blockers,
|
|
|
68
68
|
|
|
69
69
|
Do not add code comments. Preserve existing comments. Docstrings remain allowed.
|
|
70
70
|
When a change touches code that an existing comment describes or is attached to, remove that comment in the same change and carry its meaning through clear names and structure. Leave comments tied to untouched code unchanged. Keep comment cleanup inside the requested task.
|
|
71
|
-
Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed
|
|
71
|
+
Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed. Do not add or justify them.
|
|
72
72
|
|
|
73
73
|
Banned word: real
|
|
74
74
|
|
package/docs/CODE_RULES.md
CHANGED
|
@@ -7,7 +7,7 @@ Use this index for edits. [`.cursor/BUGBOT.md`](../../../.cursor/BUGBOT.md) poin
|
|
|
7
7
|
Do not add code comments. Preserve existing comments. Docstrings remain allowed.
|
|
8
8
|
|
|
9
9
|
When a change touches code that an existing comment describes or is attached to, remove that comment in the same change and carry its meaning through clear names and structure. Leave comments tied to untouched code unchanged. Keep comment cleanup inside the requested task.
|
|
10
|
-
Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed
|
|
10
|
+
Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed. Do not add or justify them.
|
|
11
11
|
|
|
12
12
|
A keep marker is the one comment that may be added and kept: a comment that opens with a prefix the repository lists under `comment_keep_markers` in `.claude/policy-lint.json`. Open [details](code-rules/comment-preservation.md) when editing comments.
|
|
13
13
|
|
|
@@ -201,10 +201,10 @@ def _is_keep_occurrence(comment_text: str, all_keep_markers: tuple[str, ...]) ->
|
|
|
201
201
|
"""Return True for a comment that opens with an accepted keep marker.
|
|
202
202
|
|
|
203
203
|
A keep marker is meant to be freely added and to survive edits to the
|
|
204
|
-
line it sits on
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
deliberately still blocks on add.
|
|
204
|
+
line it sits on. AGENTS.md requires ``noqa`` and ``TODO`` comments to be
|
|
205
|
+
removed and forbids adding or justifying them. This check excludes keep
|
|
206
|
+
markers itself, outside the shared exempt-marker set that
|
|
207
|
+
``check_comment_changes`` deliberately still blocks on add.
|
|
208
208
|
"""
|
|
209
209
|
return comment_text.startswith("#") and comment_text[1:].lstrip().startswith(
|
|
210
210
|
all_keep_markers
|
|
@@ -13,6 +13,8 @@ if hooks_directory not in sys.path:
|
|
|
13
13
|
sys.path.insert(0, hooks_directory)
|
|
14
14
|
|
|
15
15
|
from hooks_constants.pr_lifecycle_skill_gate_constants import (
|
|
16
|
+
AGENT_ID_FIELD,
|
|
17
|
+
AGENT_ID_PATTERN,
|
|
16
18
|
ALL_API_ACTION_NAMES,
|
|
17
19
|
ALL_COMMAND_PREFIX_WORDS,
|
|
18
20
|
ALL_GITHUB_MCP_TOOL_SUFFIXES,
|
|
@@ -40,8 +42,11 @@ from hooks_constants.pr_lifecycle_skill_gate_constants import (
|
|
|
40
42
|
PERMISSION_DECISION_REASON_KEY,
|
|
41
43
|
PULL_REQUEST_SCRIPT_NAME,
|
|
42
44
|
PYTHON_OPTIONS_WITH_VALUE,
|
|
45
|
+
SESSION_TRANSCRIPT_PATH_FIELD,
|
|
43
46
|
SHELL_TOOL_NAMES,
|
|
44
47
|
SKILL_NAME,
|
|
48
|
+
SUBAGENT_TRANSCRIPT_DIRECTORY_NAME,
|
|
49
|
+
SUBAGENT_TRANSCRIPT_FILE_TEMPLATE,
|
|
45
50
|
)
|
|
46
51
|
from hooks_constants.pre_tool_use_stdin import read_hook_input_dictionary_from_stdin
|
|
47
52
|
from hooks_constants.setup_project_paths_constants import DECODE_ERRORS_POLICY, UTF8_ENCODING
|
|
@@ -174,6 +179,21 @@ def _transcript_load_status(path: str) -> bool | None:
|
|
|
174
179
|
return None
|
|
175
180
|
|
|
176
181
|
|
|
182
|
+
def _subagent_transcript_path(all_payload_fields: dict[str, object]) -> str | None:
|
|
183
|
+
session_path = all_payload_fields.get(SESSION_TRANSCRIPT_PATH_FIELD)
|
|
184
|
+
agent_id = all_payload_fields.get(AGENT_ID_FIELD)
|
|
185
|
+
if not isinstance(session_path, str) or not session_path:
|
|
186
|
+
return None
|
|
187
|
+
if not isinstance(agent_id, str) or not re.fullmatch(AGENT_ID_PATTERN, agent_id):
|
|
188
|
+
return None
|
|
189
|
+
subagent_path = (
|
|
190
|
+
Path(session_path).with_suffix("")
|
|
191
|
+
/ SUBAGENT_TRANSCRIPT_DIRECTORY_NAME
|
|
192
|
+
/ SUBAGENT_TRANSCRIPT_FILE_TEMPLATE.format(agent_id=agent_id)
|
|
193
|
+
)
|
|
194
|
+
return str(subagent_path) if subagent_path.is_file() else None
|
|
195
|
+
|
|
196
|
+
|
|
177
197
|
def decision_for(all_payload_fields: dict[str, object]) -> dict[str, object] | None:
|
|
178
198
|
"""Return a deny for a governed action with readable, unloaded transcripts.
|
|
179
199
|
|
|
@@ -183,7 +203,8 @@ def decision_for(all_payload_fields: dict[str, object]) -> dict[str, object] | N
|
|
|
183
203
|
if not _is_governed_action(all_payload_fields):
|
|
184
204
|
return None
|
|
185
205
|
paths = [all_payload_fields.get(field) for field in ALL_TRANSCRIPT_PATH_FIELDS]
|
|
186
|
-
|
|
206
|
+
paths.append(_subagent_transcript_path(all_payload_fields))
|
|
207
|
+
readable_paths = list(dict.fromkeys(path for path in paths if isinstance(path, str) and path))
|
|
187
208
|
if not readable_paths:
|
|
188
209
|
return None
|
|
189
210
|
statuses = [_transcript_load_status(path) for path in readable_paths]
|
|
@@ -82,6 +82,49 @@ def test_agent_transcript_can_supply_invocation(tmp_path: Path) -> None:
|
|
|
82
82
|
assert gate.decision_for(payload) is None
|
|
83
83
|
|
|
84
84
|
|
|
85
|
+
def _subagent_transcript(tmp_path: Path, agent_id: str, skill_name: str | None) -> Path:
|
|
86
|
+
subagent_directory = tmp_path / "session" / "subagents"
|
|
87
|
+
subagent_directory.mkdir(parents=True)
|
|
88
|
+
subagent_path = subagent_directory / f"agent-{agent_id}.jsonl"
|
|
89
|
+
lines = [json.dumps({"type": "user", "message": {"content": "Task."}})]
|
|
90
|
+
if skill_name:
|
|
91
|
+
lines.append(json.dumps({"type": "assistant", "message": {"content": [{"type": "tool_use", "name": "Skill", "input": {"skill": skill_name}}]}}))
|
|
92
|
+
subagent_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
93
|
+
return subagent_path
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_subagent_transcript_located_by_agent_id_can_supply_invocation(tmp_path: Path) -> None:
|
|
97
|
+
session_path = _transcript(tmp_path)
|
|
98
|
+
_subagent_transcript(tmp_path, "a8865a3f3c71595ce", "pr-lifecycle")
|
|
99
|
+
payload = _payload("git commit", session_path)
|
|
100
|
+
payload["agent_id"] = "a8865a3f3c71595ce"
|
|
101
|
+
assert _run_main(payload) == (0, "")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_subagent_without_invocation_is_denied(tmp_path: Path) -> None:
|
|
105
|
+
session_path = _transcript(tmp_path)
|
|
106
|
+
_subagent_transcript(tmp_path, "a8865a3f3c71595ce", None)
|
|
107
|
+
payload = _payload("git push", session_path)
|
|
108
|
+
payload["agent_id"] = "a8865a3f3c71595ce"
|
|
109
|
+
_assert_denied(payload)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_missing_subagent_transcript_keeps_session_deny(tmp_path: Path) -> None:
|
|
113
|
+
payload = _payload("git push", _transcript(tmp_path))
|
|
114
|
+
payload["agent_id"] = "a8865a3f3c71595ce"
|
|
115
|
+
_assert_denied(payload)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_agent_id_with_path_separator_is_not_followed(tmp_path: Path) -> None:
|
|
119
|
+
session_path = _transcript(tmp_path)
|
|
120
|
+
escaped_path = tmp_path / "session" / "agent-x.jsonl"
|
|
121
|
+
escaped_path.parent.mkdir(parents=True)
|
|
122
|
+
escaped_path.write_text(json.dumps({"type": "assistant", "message": {"content": [{"type": "tool_use", "name": "Skill", "input": {"skill": "pr-lifecycle"}}]}}) + "\n", encoding="utf-8")
|
|
123
|
+
payload = _payload("git push", session_path)
|
|
124
|
+
payload["agent_id"] = "../agent-x"
|
|
125
|
+
_assert_denied(payload)
|
|
126
|
+
|
|
127
|
+
|
|
85
128
|
def test_unreadable_or_missing_transcript_allows_silently(tmp_path: Path) -> None:
|
|
86
129
|
payload = _payload("gh pr create", tmp_path / "missing.jsonl")
|
|
87
130
|
assert _run_main(payload) == (0, "")
|
|
@@ -18,6 +18,11 @@ ALL_GITHUB_MCP_TOOL_SUFFIXES = (
|
|
|
18
18
|
"__update_pull_request",
|
|
19
19
|
)
|
|
20
20
|
ALL_TRANSCRIPT_PATH_FIELDS = ("transcript_path", "agent_transcript_path")
|
|
21
|
+
SESSION_TRANSCRIPT_PATH_FIELD = "transcript_path"
|
|
22
|
+
AGENT_ID_FIELD = "agent_id"
|
|
23
|
+
AGENT_ID_PATTERN = r"[A-Za-z0-9_-]+"
|
|
24
|
+
SUBAGENT_TRANSCRIPT_DIRECTORY_NAME = "subagents"
|
|
25
|
+
SUBAGENT_TRANSCRIPT_FILE_TEMPLATE = "agent-{agent_id}.jsonl"
|
|
21
26
|
COMMAND_SEPARATORS = ";|&\n()`"
|
|
22
27
|
COMMAND_WHITESPACE = " \t\r"
|
|
23
28
|
OPTION_AND_VALUE_WORD_COUNT = 2
|
package/package.json
CHANGED
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
- Preserve existing comments.
|
|
117
117
|
- Do not add code comments.
|
|
118
118
|
- When a change touches code that an existing comment describes or is attached to, remove that comment in the same change and carry its meaning through clear names and structure. Leave comments tied to untouched code unchanged. Keep comment cleanup inside the requested task.
|
|
119
|
-
- Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed
|
|
119
|
+
- Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed. Do not add or justify them.
|
|
120
120
|
- Express new explanations through self-documenting names
|
|
121
121
|
- Docstrings remain allowed.
|
|
122
122
|
|