claude-dev-env 1.26.3 → 1.26.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.
|
@@ -219,7 +219,10 @@ def test_read_body_file_rejects_relative_path_traversal(tmp_path) -> None:
|
|
|
219
219
|
import os, pytest
|
|
220
220
|
sentinel_file = tmp_path / 'secret.txt'
|
|
221
221
|
sentinel_file.write_text('secret')
|
|
222
|
-
|
|
222
|
+
try:
|
|
223
|
+
rel_path = os.path.relpath(str(sentinel_file))
|
|
224
|
+
except ValueError:
|
|
225
|
+
pytest.skip('tmp_path on different drive than cwd; relpath undefined on Windows')
|
|
223
226
|
if '..' not in rel_path:
|
|
224
227
|
pytest.skip('file is under cwd, not a traversal case')
|
|
225
228
|
with pytest.raises(m.PathTraversalError):
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
- **Agent teams required, not parallel subagents.** The skill MUST use Claude Code's agent teams feature (`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`). Spawning `code-quality-agent` and `clean-coder` as parallel subagents from the lead's context = fail; the clean-room property requires independent teammate sessions.
|
|
6
6
|
- **Orchestrator-only `TeamCreate`.** Only the lead session (this session, when `/bugteam` is invoked) calls `TeamCreate`. Teammates never call `TeamCreate` — if a teammate's spawn prompt instructs it to, that is a skill defect. When additional parallel work is needed (e.g., parallel auditors from loop 4 onward, supplementary audit of adjacent files), the lead spawns additional teammates into the EXISTING team by passing the current `team_name` to every `Agent(...)` call. Multiple teammate "sets" live inside one team under one orchestrator. The runtime enforces this: `TeamCreate` called while the session already leads a team returns the error `Already leading team "<name>". A leader can only manage one team at a time. Use TeamDelete to end the current team before creating a new one.` — direct quote from the runtime's response when this invariant is violated.
|
|
7
|
+
- **One team per invocation, multi-PR supported.** All PRs in a single /bugteam invocation share one team created by the orchestrator. Per-PR identity lives in the teammate name prefix (`bugfind-pr<N>-loop<L>` / `bugfix-pr<N>-loop<L>`) and the `<team_temp_dir>/pr-<N>/` subfolder containing that PR's git worktree, diff patches, and outcome XML files.
|
|
7
8
|
- **Grant before any spawn, revoke before any return.** Step 0 grants project `.claude/**` permissions; Step 5 revokes. Both are mandatory. Revoke runs on every exit path including error, cap-reached, and stuck.
|
|
8
9
|
- **Fresh teammate per loop.** Both bugfind and bugfix are spawned new each loop and shut down after their action. Reusing a teammate across loops accumulates context inside that teammate's window — defeats clean-room.
|
|
9
10
|
- **One up-front confirmation = whole cycle.** The `/bugteam` invocation authorizes the entire cycle; every subsequent decision runs on that single authorization.
|
|
@@ -11,10 +11,14 @@ Keep the spawn prompt self-contained: reference only the PR scope, audit rubric,
|
|
|
11
11
|
<base_branch>base ref</base_branch>
|
|
12
12
|
<pr_url>full URL</pr_url>
|
|
13
13
|
<loop>N</loop>
|
|
14
|
+
<pr_number>N</pr_number>
|
|
15
|
+
<worktree_path>absolute path from Step 1 per-PR workspace</worktree_path>
|
|
14
16
|
</context>
|
|
15
17
|
|
|
18
|
+
cd into `<worktree_path>` before any git, gh, or file operation.
|
|
19
|
+
|
|
16
20
|
<scope>
|
|
17
|
-
<diff_path>Absolute path to the
|
|
21
|
+
<diff_path>Absolute path to the per-PR patch file: <team_temp_dir>/pr-<N>/loop-<L>.patch (same path as gh pr diff redirect in AUDIT)</diff_path>
|
|
18
22
|
<scope_rule>Audit only lines added or modified in the diff. Pre-existing code on untouched lines is out of scope.</scope_rule>
|
|
19
23
|
</scope>
|
|
20
24
|
|
|
@@ -72,8 +76,8 @@ Keep the spawn prompt self-contained: reference only the PR scope, audit rubric,
|
|
|
72
76
|
</comment_posting>
|
|
73
77
|
|
|
74
78
|
<output_format>
|
|
75
|
-
Write the outcome XML below to .bugteam-loop
|
|
76
|
-
|
|
79
|
+
Write the outcome XML below to .bugteam-pr<N>-loop<L>.outcomes.xml inside
|
|
80
|
+
the PR's worktree directory (<worktree_path>). Return only that path on stdout. The schema:
|
|
77
81
|
</output_format>
|
|
78
82
|
```
|
|
79
83
|
|
|
@@ -100,7 +104,7 @@ Keep the spawn prompt self-contained: reference only the PR scope, audit rubric,
|
|
|
100
104
|
</bugteam_audit>
|
|
101
105
|
```
|
|
102
106
|
|
|
103
|
-
After the teammate writes the XML and returns, the lead reads `.bugteam-loop
|
|
107
|
+
After the teammate writes the XML and returns, the lead reads `.bugteam-pr<N>-loop<L>.outcomes.xml` from the PR's worktree directory with the `Read` tool, parses it, and populates `loop_comment_index` from `<finding>` elements.
|
|
104
108
|
|
|
105
109
|
## FIX spawn-prompt XML (bugfix teammate)
|
|
106
110
|
|
|
@@ -111,8 +115,12 @@ After the teammate writes the XML and returns, the lead reads `.bugteam-loop-<N>
|
|
|
111
115
|
<base_branch>base</base_branch>
|
|
112
116
|
<pr_url>url</pr_url>
|
|
113
117
|
<loop>N</loop>
|
|
118
|
+
<pr_number>N</pr_number>
|
|
119
|
+
<worktree_path>absolute path from Step 1 per-PR workspace</worktree_path>
|
|
114
120
|
</context>
|
|
115
121
|
|
|
122
|
+
cd into `<worktree_path>` before any git, gh, or file operation.
|
|
123
|
+
|
|
116
124
|
<bugs_to_fix>
|
|
117
125
|
[for each P0/P1/P2 finding from last_findings:]
|
|
118
126
|
<bug
|
|
@@ -144,7 +152,7 @@ After the teammate writes the XML and returns, the lead reads `.bugteam-loop-<N>
|
|
|
144
152
|
- "Could not address this loop: <one-line reason>" if you skipped or failed it
|
|
145
153
|
- "Hook blocked the fix commit: <one-line summary>" if the commit was hook-blocked
|
|
146
154
|
Use the Fix reply CLI shape from Step 2.5 (`jq -Rs | gh api .../comments/<id>/replies --input -`). Write every reply body to a temp file first.
|
|
147
|
-
7. Write `.bugteam-loop
|
|
155
|
+
7. Write `.bugteam-pr<N>-loop<L>.outcomes.xml` inside `<worktree_path>` (schema below) and return its path.
|
|
148
156
|
</execution>
|
|
149
157
|
|
|
150
158
|
<outcome_xml_schema>
|
package/skills/bugteam/SKILL.md
CHANGED
|
@@ -51,6 +51,7 @@ Refusals — first match wins; respond with the quoted line exactly and stop:
|
|
|
51
51
|
- **No PR or upstream diff.** `No PR or upstream diff. /bugteam needs a target.`
|
|
52
52
|
- **Dirty tree.** `Uncommitted changes detected. Stash, commit, or revert before /bugteam.`
|
|
53
53
|
- **Missing subagents.** Before Step 0, confirm `code-quality-agent` and `clean-coder` exist. Else: `Required subagent type <name> not installed. /bugteam needs both code-quality-agent and clean-coder available.`
|
|
54
|
+
- **Lead role must be held by the orchestrator.** Run /bugteam in the session that received the user's command. The orchestrator session calls TeamCreate directly. Runtime confirms a single lead per team: `Already leading team "<name>". A leader can only manage one team at a time.`
|
|
54
55
|
|
|
55
56
|
## Utility scripts
|
|
56
57
|
|
|
@@ -89,17 +90,23 @@ python "${CLAUDE_SKILL_DIR}/scripts/grant_project_claude_permissions.py"
|
|
|
89
90
|
|
|
90
91
|
### Step 1: Resolve PR scope (once)
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
1. `gh pr view --json number,baseRefName,headRefName,url`
|
|
95
|
-
2. Else `git merge-base HEAD origin/<default>` then `git diff <merge-base>...HEAD`
|
|
96
|
-
3. Else refuse above.
|
|
93
|
+
Accept one or more PR numbers from the invocation. For each PR, run `gh pr view --json number,baseRefName,headRefName,url` (falling back to the merge-base diff path when no PR exists). Capture `all_prs = [{number, owner, repo, baseRef, headRef, url}, ...]`. A single-PR invocation produces a one-element list and follows the same downstream rules.
|
|
97
94
|
|
|
98
95
|
Keep: owner/repo, branches, PR number, URL — for all loops.
|
|
99
96
|
|
|
97
|
+
#### Per-PR workspace
|
|
98
|
+
|
|
99
|
+
For each PR in all_prs:
|
|
100
|
+
|
|
101
|
+
1. Create `<team_temp_dir>/pr-<N>/`.
|
|
102
|
+
2. Run `git worktree add "<team_temp_dir>/pr-<N>/worktree" origin/<headRef>`.
|
|
103
|
+
3. Record the absolute worktree path alongside the PR's other fields.
|
|
104
|
+
|
|
105
|
+
Teammates spawned for a PR operate inside that PR's worktree. Step 4 teardown runs `git worktree remove "<team_temp_dir>/pr-<N>/worktree"` for each PR before `TeamDelete`.
|
|
106
|
+
|
|
100
107
|
### Step 2: Create the agent team
|
|
101
108
|
|
|
102
|
-
|
|
109
|
+
**This session is the lead.** The orchestrator calls `TeamCreate` directly:
|
|
103
110
|
|
|
104
111
|
```
|
|
105
112
|
TeamCreate(
|
|
@@ -109,7 +116,7 @@ TeamCreate(
|
|
|
109
116
|
)
|
|
110
117
|
```
|
|
111
118
|
|
|
112
|
-
**Team name:** `bugteam-pr-<number>-<YYYYMMDDHHMMSS
|
|
119
|
+
**Team name:** For a single-PR invocation use `bugteam-pr-<number>-<YYYYMMDDHHMMSS>`. For a multi-PR invocation use `bugteam-<YYYYMMDDHHMMSS>`. The timestamp is captured once at team-creation time. Apply the no-PR fallback (`bugteam-<sanitized-head>-<YYYYMMDDHHMMSS>`) only when no PR resolves at all. `TeamCreate` implements natural-language team creation ([`sources.md`](sources.md) § Team creation in natural language).
|
|
113
120
|
|
|
114
121
|
**Sanitize head branch (no-PR only):** replace characters outside `[A-Za-z0-9._-]` with `-` (e.g. `feat/foo*bar` → `feat-foo-bar`). Apply once; reuse everywhere below.
|
|
115
122
|
|
|
@@ -186,7 +193,9 @@ jq -n \
|
|
|
186
193
|
|
|
187
194
|
### Step 3: The cycle
|
|
188
195
|
|
|
189
|
-
|
|
196
|
+
Run the AUDIT-FIX cycle for each PR in all_prs, reusing the same team across PRs. The 10-loop cap applies per PR. Exit reasons (converged, cap reached, stuck, error) are tracked per PR; the final report lists one outcome line per PR.
|
|
197
|
+
|
|
198
|
+
**Gate:** `validate_content` / `hooks/blocking/code_rules_enforcer.py` on PR-scoped files before every AUDIT (`bugteam_code_rules_gate.py`). Lead runs gate; clean-coder clears failures; then bugfind audits.
|
|
190
199
|
|
|
191
200
|
1. From `last_action` / `last_findings`:
|
|
192
201
|
- `last_action == "audited"` and `last_findings.total == 0` → exit `converged`
|
|
@@ -213,29 +222,29 @@ First pass: pre-audit → AUDIT. After a FIX, the next pass runs pre-audit again
|
|
|
213
222
|
### AUDIT action
|
|
214
223
|
|
|
215
224
|
```bash
|
|
216
|
-
mkdir -p "<team_temp_dir>"
|
|
217
|
-
gh pr diff <
|
|
225
|
+
mkdir -p "<team_temp_dir>/pr-<N>"
|
|
226
|
+
gh pr diff <N> -R <owner>/<repo> > "<team_temp_dir>/pr-<N>/loop-<L>.patch"
|
|
218
227
|
```
|
|
219
228
|
|
|
220
229
|
```
|
|
221
230
|
Agent(
|
|
222
231
|
subagent_type="code-quality-agent",
|
|
223
|
-
name="bugfind",
|
|
232
|
+
name="bugfind-pr<N>-loop<L>",
|
|
224
233
|
team_name="<team_name>",
|
|
225
234
|
model="sonnet",
|
|
226
|
-
description="Bugfind audit
|
|
235
|
+
description="Bugfind audit PR <N> loop <L>",
|
|
227
236
|
prompt="<audit XML; see PROMPTS.md>"
|
|
228
237
|
)
|
|
229
238
|
```
|
|
230
239
|
|
|
231
|
-
Fresh `Agent` each loop; teammate context excludes lead history ([`sources.md`](sources.md) § Teammate context isolation). [`PROMPTS.md`](PROMPTS.md): XML + outcome schema. Lead reads `.bugteam-loop
|
|
240
|
+
Fresh `Agent` each loop; teammate context excludes lead history ([`sources.md`](sources.md) § Teammate context isolation). [`PROMPTS.md`](PROMPTS.md): XML + outcome schema. Lead reads `.bugteam-pr<N>-loop<L>.outcomes.xml`, fills `loop_comment_index`.
|
|
232
241
|
|
|
233
242
|
**Shutdown:** If `Agent` returned and the teammate already ended, skip. Otherwise:
|
|
234
243
|
|
|
235
244
|
```
|
|
236
245
|
SendMessage(
|
|
237
|
-
to="bugfind",
|
|
238
|
-
message={"type": "shutdown_request", "reason": "audit
|
|
246
|
+
to="bugfind-pr<N>-loop<L>",
|
|
247
|
+
message={"type": "shutdown_request", "reason": "audit PR <N> loop <L> complete; outcome XML captured"}
|
|
239
248
|
)
|
|
240
249
|
```
|
|
241
250
|
|
|
@@ -243,24 +252,24 @@ SendMessage(
|
|
|
243
252
|
|
|
244
253
|
`last_action = "audited"`; append audit line to `audit_log`.
|
|
245
254
|
|
|
246
|
-
**Parallel auditors (`loop_count >= 4`):** gate passes immediately before; after three full audit/fix rounds without convergence, issue three `Agent` calls in one assistant message (parallel). `-a` posts the review and merges outcomes from `-b`/`-c` (read `.bugteam-loop
|
|
255
|
+
**Parallel auditors (`loop_count >= 4`):** gate passes immediately before; after three full audit/fix rounds without convergence, issue three `Agent` calls in one assistant message (parallel). `-a` posts the review and merges outcomes from `-b`/`-c` (read `.bugteam-pr<N>-loop<L>.outcomes.xml` plus `<team_temp_dir>/pr-<N>/loop-<L>-b.outcomes.xml` and `...-c...`); merge key `(file, line, category_letter)`; re-id `loopN-K`. `-b`/`-c` write sibling XML only; prompts must pass literal absolute sibling paths. Shutdown: parallel `SendMessage` to `b` and `c`, then `a`.
|
|
247
256
|
|
|
248
257
|
### FIX action
|
|
249
258
|
|
|
250
259
|
```
|
|
251
260
|
Agent(
|
|
252
261
|
subagent_type="clean-coder",
|
|
253
|
-
name="bugfix",
|
|
262
|
+
name="bugfix-pr<N>-loop<L>",
|
|
254
263
|
team_name="<team_name>",
|
|
255
264
|
model="sonnet",
|
|
256
|
-
description="Bugfix
|
|
265
|
+
description="Bugfix PR <N> loop <L>",
|
|
257
266
|
prompt="<fix XML; see PROMPTS.md>"
|
|
258
267
|
)
|
|
259
268
|
```
|
|
260
269
|
|
|
261
270
|
Pass finding comment URLs/ids from `loop_comment_index` in XML. Replies: `Fixed in <sha>` or `Could not address this loop: <reason>`.
|
|
262
271
|
|
|
263
|
-
**Shutdown:** same as bugfind; else `SendMessage(to="bugfix", message={"type": "shutdown_request", "reason": "fix
|
|
272
|
+
**Shutdown:** same as bugfind; else `SendMessage(to="bugfix-pr<N>-loop<L>", message={"type": "shutdown_request", "reason": "fix PR <N> loop <L> complete; commit <sha7> pushed"})`. `approve: false` → `error: bugfix teammate refused shutdown` → Step 4 then 5.
|
|
264
273
|
|
|
265
274
|
[`PROMPTS.md`](PROMPTS.md): fix XML + schema. Verify: `git rev-parse HEAD` advanced; `git fetch origin <branch> && git rev-parse origin/<branch>` matches `HEAD`. Unchanged HEAD → `stuck — bugfix teammate could not address findings`.
|
|
266
275
|
|