claude-dev-env 1.27.0 → 1.28.1
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 +1 -1
- package/skills/qbug/SKILL.md +319 -0
package/package.json
CHANGED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qbug
|
|
3
|
+
description: >-
|
|
4
|
+
Required baseline review for every new PR. Runs the /bugteam audit → fix →
|
|
5
|
+
commit → push cycle via one clean-coder subagent (not a full team), looping
|
|
6
|
+
until convergence or stuck. Uses the same CODE_RULES gate, A–J category
|
|
7
|
+
rubric, and per-loop PR review shape as /bugteam — without TeamCreate,
|
|
8
|
+
teammates, per-loop clean-room, or a loop cap. Invoke /bugteam instead for
|
|
9
|
+
larger PRs that need per-loop bias isolation or a hard loop cap. Triggers:
|
|
10
|
+
'/qbug', 'quick bug audit', 'solo bug audit', 'baseline PR review',
|
|
11
|
+
'bugteam without a team'.
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# qbug
|
|
15
|
+
|
|
16
|
+
**Core principle:** One `clean-coder` subagent loops audit → fix → commit → push until converged or stuck. The subagent's context persists across loops (no per-loop clean-room) — that is the explicit trade vs /bugteam.
|
|
17
|
+
|
|
18
|
+
**When to reach for /qbug vs /bugteam:** `/qbug` is the required baseline review for every new PR (fastest path from "ready" to "merged-safe"). Escalate to `/bugteam` when the PR is large enough that anchoring-bias across loops becomes a convergence risk, or when a hard loop cap is required for cost control.
|
|
19
|
+
|
|
20
|
+
Shared artifacts with /bugteam are referenced below by path, using the `${CLAUDE_SKILL_DIR}` host-substitution convention (both skills land under `~/.claude/skills/` after install):
|
|
21
|
+
|
|
22
|
+
- Pre-flight script: `${CLAUDE_SKILL_DIR}/../bugteam/scripts/bugteam_preflight.py`
|
|
23
|
+
- Code-rules gate script: `${CLAUDE_SKILL_DIR}/../bugteam/scripts/bugteam_code_rules_gate.py`
|
|
24
|
+
- Bug category rubric A–J: [`bugteam/PROMPTS.md`](../bugteam/PROMPTS.md#audit-spawn-prompt-xml-bugfind-teammate)
|
|
25
|
+
- PR comment lifecycle shape: [`bugteam/SKILL.md`](../bugteam/SKILL.md#step-25-pr-comments-one-review-per-loop)
|
|
26
|
+
|
|
27
|
+
## When this skill applies
|
|
28
|
+
|
|
29
|
+
`/qbug` once authorizes the full cycle (no loop cap — runs until `converged` or `stuck`; user can interrupt at any time).
|
|
30
|
+
|
|
31
|
+
Refusals — first match wins; respond with the quoted line exactly and stop:
|
|
32
|
+
|
|
33
|
+
- **No PR or upstream diff.** `No PR or upstream diff. /qbug needs a target.`
|
|
34
|
+
- **Dirty tree.** `Uncommitted changes detected. Stash, commit, or revert before /qbug.`
|
|
35
|
+
- **Missing subagent.** Before Step 2, confirm `clean-coder` exists. Else: `Required subagent type clean-coder not installed. /qbug needs clean-coder available.`
|
|
36
|
+
|
|
37
|
+
## Progress checklist
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
[ ] Step 0: pre-flight clean
|
|
41
|
+
[ ] Step 1: PR scope resolved
|
|
42
|
+
[ ] Step 2: subagent cycle complete (converged | stuck | error)
|
|
43
|
+
[ ] Step 3: PR description refreshed
|
|
44
|
+
[ ] Step 4: final report printed
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Step 0: Pre-flight
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
python "${CLAUDE_SKILL_DIR}/../bugteam/scripts/bugteam_preflight.py"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`${CLAUDE_SKILL_DIR}` is host-substituted before the shell runs. Non-zero → fix before continuing. `BUGTEAM_PREFLIGHT_SKIP=1` is emergency only. Add `--pre-commit` when `.pre-commit-config.yaml` exists.
|
|
54
|
+
|
|
55
|
+
## Step 1: Resolve PR scope (lead)
|
|
56
|
+
|
|
57
|
+
1. `gh pr view --json number,baseRefName,headRefName,url`
|
|
58
|
+
2. Else `git merge-base HEAD origin/<default>` then `git diff <merge-base>...HEAD`
|
|
59
|
+
3. Else refuse per § When this skill applies.
|
|
60
|
+
|
|
61
|
+
Capture: `owner/repo`, `baseRefName`, `headRefName`, PR `number`, `url`, `starting_sha = git rev-parse HEAD`.
|
|
62
|
+
|
|
63
|
+
Resolve the scoped temp directory once, lead-side, before spawning the subagent. Use Python's `tempfile.gettempdir()` so the path is correct on macOS, Linux, Windows cmd.exe, and PowerShell — do not hardcode `/tmp/` because Windows runners do not honor it. Capture the resolved absolute path as `<qbug_temp_dir>` and pass that literal path to the subagent:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import json
|
|
67
|
+
import subprocess
|
|
68
|
+
import tempfile
|
|
69
|
+
from pathlib import Path
|
|
70
|
+
|
|
71
|
+
pr_view: dict = json.loads(subprocess.check_output(
|
|
72
|
+
["gh", "pr", "view", "--json", "number,baseRefName,headRefName,url"]
|
|
73
|
+
))
|
|
74
|
+
pr_number: int = pr_view["number"]
|
|
75
|
+
qbug_temp_dir: Path = Path(tempfile.gettempdir()) / f"qbug-pr-{pr_number}"
|
|
76
|
+
qbug_temp_dir.mkdir(parents=True, exist_ok=True)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Step 2: Spawn the single subagent
|
|
80
|
+
|
|
81
|
+
Before calling `Agent`, the lead resolves the three absolute paths the subagent needs and substitutes them into the prompt template (the `<gate_script>`, `<categories_file>`, and `<qbug_temp_dir>` placeholders in § Subagent cycle prompt):
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import os
|
|
85
|
+
from pathlib import Path
|
|
86
|
+
|
|
87
|
+
skill_dir = Path(os.environ["CLAUDE_SKILL_DIR"])
|
|
88
|
+
gate_script_path = (skill_dir / ".." / "bugteam" / "scripts" / "bugteam_code_rules_gate.py").resolve()
|
|
89
|
+
categories_file_path = (skill_dir / ".." / "bugteam" / "PROMPTS.md").resolve()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then call `Agent`:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
Agent(
|
|
96
|
+
subagent_type="clean-coder",
|
|
97
|
+
model="sonnet",
|
|
98
|
+
description="qbug audit/fix cycle for PR <number>",
|
|
99
|
+
prompt="<filled cycle XML; see § Subagent cycle prompt>",
|
|
100
|
+
run_in_background=False
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
One subagent, not a team. No `TeamCreate`, no `team_name`, no teammate shutdown protocol. The subagent returns when it has exited the cycle (`converged`, `stuck`, or `error`).
|
|
105
|
+
|
|
106
|
+
## Subagent cycle prompt
|
|
107
|
+
|
|
108
|
+
The subagent receives this prompt and loops internally — the lead does not re-invoke between loops. The prompt is self-contained: it restates the bug-category rubric by path rather than assuming prior context, and it states its full scope upfront. Before passing the prompt to `Agent()`, the lead substitutes every `{{UPPER_SNAKE}}` slot: `{{OWNER_REPO}}`, `{{HEAD_REF}}`, `{{BASE_REF}}`, `{{PR_URL}}`, `{{STARTING_SHA}}` (from Step 1) and `{{QBUG_TEMP_DIR}}`, `{{GATE_SCRIPT}}`, `{{CATEGORIES_FILE}}` (resolved in Step 2).
|
|
109
|
+
|
|
110
|
+
```xml
|
|
111
|
+
<role>
|
|
112
|
+
You are the lone audit-fix worker for this pull request. Run the full
|
|
113
|
+
audit → fix → commit → push cycle in this one subagent session. The
|
|
114
|
+
lead has already resolved scope and pre-flight; your job is to take
|
|
115
|
+
the cycle to an exit state and report back.
|
|
116
|
+
</role>
|
|
117
|
+
|
|
118
|
+
<context>
|
|
119
|
+
<repo>{{OWNER_REPO}}</repo>
|
|
120
|
+
<branch>{{HEAD_REF}}</branch>
|
|
121
|
+
<base_branch>{{BASE_REF}}</base_branch>
|
|
122
|
+
<pr_url>{{PR_URL}}</pr_url>
|
|
123
|
+
<starting_sha>{{STARTING_SHA}}</starting_sha>
|
|
124
|
+
<qbug_temp_dir>{{QBUG_TEMP_DIR}}</qbug_temp_dir>
|
|
125
|
+
<gate_script>{{GATE_SCRIPT}}</gate_script>
|
|
126
|
+
<categories_file>{{CATEGORIES_FILE}}</categories_file>
|
|
127
|
+
</context>
|
|
128
|
+
|
|
129
|
+
<exit_conditions>
|
|
130
|
+
The cycle stops when ONE of these is true. Check on every iteration:
|
|
131
|
+
- converged: most recent AUDIT returned zero findings.
|
|
132
|
+
- stuck: most recent FIX left `git rev-parse HEAD` unchanged.
|
|
133
|
+
- error: three consecutive pre-audit gate rounds failed (three is
|
|
134
|
+
chosen because two is within normal clean-coder variance; four
|
|
135
|
+
rounds typically indicates a gate defect rather than fixable
|
|
136
|
+
violations).
|
|
137
|
+
There is no loop-count cap. A pathological diff with ever-changing
|
|
138
|
+
findings will still exit via `stuck` once a FIX produces no commit.
|
|
139
|
+
</exit_conditions>
|
|
140
|
+
|
|
141
|
+
<cycle>
|
|
142
|
+
Maintain inline across iterations:
|
|
143
|
+
loop_count = 0
|
|
144
|
+
last_action = "fresh" # fresh | audited | fixed
|
|
145
|
+
last_findings = {p0, p1, p2, total}
|
|
146
|
+
loop_comment_index = {}
|
|
147
|
+
audit_log = []
|
|
148
|
+
|
|
149
|
+
Iteration:
|
|
150
|
+
1. Dispatch on last_action:
|
|
151
|
+
- "audited" and last_findings.total == 0 → exit "converged"
|
|
152
|
+
- "fixed" and `git rev-parse HEAD` equals the sha captured
|
|
153
|
+
immediately before FIX → exit "stuck"
|
|
154
|
+
- "fresh" or "fixed" → run pre-audit, then AUDIT
|
|
155
|
+
- "audited" and last_findings.total > 0 → run FIX
|
|
156
|
+
|
|
157
|
+
2. Pre-audit (before every AUDIT):
|
|
158
|
+
Run the gate script at <gate_script> with `--base origin/<base_branch>`.
|
|
159
|
+
Non-zero exit → fix the reported violations inline and re-run the
|
|
160
|
+
same command. Count consecutive failures. Three failed rounds →
|
|
161
|
+
exit "error: code rules gate failed pre-audit".
|
|
162
|
+
On exit 0: increment loop_count, proceed to AUDIT.
|
|
163
|
+
|
|
164
|
+
3. AUDIT:
|
|
165
|
+
Run: `gh pr diff <pr_number> -R <owner>/<repo> > <qbug_temp_dir>/loop-<loop_count>.patch`
|
|
166
|
+
|
|
167
|
+
- Read the patch file.
|
|
168
|
+
- Audit only added/modified lines. Read <categories_file> for the
|
|
169
|
+
A–J category definitions; investigate each category explicitly
|
|
170
|
+
and return either at least one finding or a verified-clean
|
|
171
|
+
entry with cleared evidence.
|
|
172
|
+
- Assign each finding a stable id of the form `loop<N>-<K>`
|
|
173
|
+
(N=loop_count, K=1-based within this loop).
|
|
174
|
+
- Partition into anchored (line appears in the diff) vs
|
|
175
|
+
unanchored (line does not).
|
|
176
|
+
|
|
177
|
+
Post ONE review per loop. Use the payload shape from
|
|
178
|
+
<categories_file>'s sibling SKILL.md § "PR comments" — build
|
|
179
|
+
the JSON with jq `--rawfile` / `-Rs` reading per-finding body
|
|
180
|
+
files, pipe to
|
|
181
|
+
`gh api repos/<owner>/<repo>/pulls/<pr_number>/reviews -X POST --input -`.
|
|
182
|
+
Review body first line: `## /qbug loop <N> audit: <P0>P0 / <P1>P1 / <P2>P2`.
|
|
183
|
+
If the review POST fails, fall back to one issue comment on
|
|
184
|
+
`/issues/<pr_number>/comments` carrying the full body; mark every
|
|
185
|
+
finding `used_fallback=true`.
|
|
186
|
+
|
|
187
|
+
Harvest `html_url` for the parent review and each child comment
|
|
188
|
+
into loop_comment_index[finding_id].
|
|
189
|
+
|
|
190
|
+
Update state: last_action="audited", last_findings=counts.
|
|
191
|
+
Append `<N> audit: <P0>P0 / <P1>P1 / <P2>P2` to audit_log.
|
|
192
|
+
|
|
193
|
+
4. FIX:
|
|
194
|
+
Capture the pre-FIX sha: `pre_fix_sha = git rev-parse HEAD`.
|
|
195
|
+
|
|
196
|
+
Apply each fix. Read every file before editing. Preserve existing
|
|
197
|
+
comments on lines you do not modify. Add type hints on every
|
|
198
|
+
signature you touch.
|
|
199
|
+
|
|
200
|
+
Validate each modified Python file with `python -m py_compile`
|
|
201
|
+
(or the language-equivalent compile check).
|
|
202
|
+
|
|
203
|
+
Stage each modified path by explicit name: `git add <path>`.
|
|
204
|
+
Create one commit summarizing the fixed findings. Let every git
|
|
205
|
+
hook run. If a hook blocks the commit, capture its stderr, mark
|
|
206
|
+
every finding in this loop `status=hook_blocked`, and move on to
|
|
207
|
+
the next iteration without retrying this loop.
|
|
208
|
+
|
|
209
|
+
Push with a plain fast-forward: `git push`.
|
|
210
|
+
|
|
211
|
+
Reply to each finding at loop_comment_index[finding_id].finding_comment_id
|
|
212
|
+
using the reply CLI shape (jq `-Rs` → `gh api .../comments/<id>/replies --input -`).
|
|
213
|
+
Reply body is one of:
|
|
214
|
+
- `Fixed in <short_sha>`
|
|
215
|
+
- `Could not address this loop: <one-line reason>`
|
|
216
|
+
- `Hook blocked the fix commit: <one-line summary>`
|
|
217
|
+
|
|
218
|
+
Update state: last_action="fixed". Append
|
|
219
|
+
`<N> fix: <short_sha> — <fixed>/<could_not_address>/<hook_blocked>`
|
|
220
|
+
to audit_log.
|
|
221
|
+
|
|
222
|
+
5. Return to step 1.
|
|
223
|
+
</cycle>
|
|
224
|
+
|
|
225
|
+
<example_finding_body>
|
|
226
|
+
**[P1] race condition on shared cache write**
|
|
227
|
+
Category: I (concurrency hazards)
|
|
228
|
+
Two writers can both pass the existence check at line 88 before either
|
|
229
|
+
commits the write at line 91 — whichever writes second overwrites the
|
|
230
|
+
first under contention. Either hold the cache lock across the check
|
|
231
|
+
and the write, or use a compare-and-swap primitive.
|
|
232
|
+
|
|
233
|
+
_From /qbug audit loop 2._
|
|
234
|
+
</example_finding_body>
|
|
235
|
+
|
|
236
|
+
<constraints>
|
|
237
|
+
- Edit only files reachable from the PR diff's scope.
|
|
238
|
+
- Keep the branch linear: append one new commit per FIX loop and push
|
|
239
|
+
fast-forward only.
|
|
240
|
+
- Preserve existing comments on lines you do not modify.
|
|
241
|
+
- Every signature you touch has complete type hints.
|
|
242
|
+
- Every file is read before you edit it (investigate before answering).
|
|
243
|
+
- Complete the entire cycle in this one subagent session using your
|
|
244
|
+
available tools directly; keep all audit and fix work inside this
|
|
245
|
+
session.
|
|
246
|
+
</constraints>
|
|
247
|
+
|
|
248
|
+
<output_format>
|
|
249
|
+
Return to the lead with exactly these fields:
|
|
250
|
+
- exit_reason: "converged" | "stuck" | "error: <detail>"
|
|
251
|
+
- loop_count: integer
|
|
252
|
+
- final_commit_sha: `git rev-parse HEAD` at exit
|
|
253
|
+
- audit_log: ordered list of per-loop lines
|
|
254
|
+
- unresolved: array of {file, line, severity, title, reason}
|
|
255
|
+
(present only when exit_reason == "stuck")
|
|
256
|
+
</output_format>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Step 3: PR description refresh (lead)
|
|
260
|
+
|
|
261
|
+
Delegate body composition to the `pr-description-writer` agent (the mandatory-pr-description hook requires it for `gh pr edit` to succeed). Feed it the final PR diff and the original body. Apply via `gh pr edit <number> -R <owner>/<repo> --body-file .qbug-final-body.md`.
|
|
262
|
+
|
|
263
|
+
On error exit paths: best-effort; log the failure in the final report and continue.
|
|
264
|
+
|
|
265
|
+
## Step 4: Final report (lead)
|
|
266
|
+
|
|
267
|
+
Use the same shape as [`bugteam/SKILL.md` Step 6](../bugteam/SKILL.md#step-6-final-report) with two deltas:
|
|
268
|
+
|
|
269
|
+
- Header substitutes `/qbug` for `/bugteam`.
|
|
270
|
+
- Exit states are `converged | stuck | error` (no `cap reached` state, since `/qbug` has no loop cap).
|
|
271
|
+
|
|
272
|
+
Delete the resolved `<qbug_temp_dir>` tree and any `.qbug-*.md` temp files in the working directory. The lead captured the dir as an absolute path via `tempfile.gettempdir()` in Step 1; reuse that literal for cleanup.
|
|
273
|
+
|
|
274
|
+
## Constraints
|
|
275
|
+
|
|
276
|
+
- **One subagent, not a team.** Lead spawns a single `clean-coder` via the Agent tool. No `TeamCreate`. The subagent does not spawn further subagents.
|
|
277
|
+
- **No loop cap.** Cycle runs until `converged`, `stuck`, or `error`. User can interrupt.
|
|
278
|
+
- **Code rules gate before every AUDIT.** Same `validate_content` logic as /bugteam.
|
|
279
|
+
- **One commit per FIX action.** Linear branch, fast-forward push only.
|
|
280
|
+
- **Categories A–J.** Same rubric as [`bugteam/PROMPTS.md`](../bugteam/PROMPTS.md).
|
|
281
|
+
- **One review per loop.** Anchored findings as `comments[]`; unanchored listed under "Findings without a diff anchor" in the review body.
|
|
282
|
+
- **PR description rewrite on every exit**, same as /bugteam Step 4.5.
|
|
283
|
+
- **Temp file cleanup on every exit path.**
|
|
284
|
+
- **No per-loop clean-room.** The single subagent's context accumulates across loops — that is the explicit trade vs /bugteam. For convergence-critical audits where bias isolation matters, use /bugteam.
|
|
285
|
+
|
|
286
|
+
## Examples
|
|
287
|
+
|
|
288
|
+
<example>
|
|
289
|
+
User: `/qbug`
|
|
290
|
+
Lead: [preflight, resolves PR #42, spawns ONE clean-coder subagent with the cycle prompt]
|
|
291
|
+
Subagent: [runs loops internally, returns]
|
|
292
|
+
|
|
293
|
+
`Loop 1 audit: 1P0 / 2P1 / 0P2`
|
|
294
|
+
`Loop 1 fix: commit a1b2c3d (3 files, +18/-7) — 3 fixed, 0 skipped`
|
|
295
|
+
`Loop 2 audit: 0P0 / 1P1 / 0P2`
|
|
296
|
+
`Loop 2 fix: commit e4f5g6h (1 file, +5/-2) — 1 fixed, 0 skipped`
|
|
297
|
+
`Loop 3 audit: 0P0 / 0P1 / 0P2 → converged`
|
|
298
|
+
|
|
299
|
+
`/qbug exit: converged`
|
|
300
|
+
`Loops: 3`
|
|
301
|
+
`Starting commit: 9d8c7b6`
|
|
302
|
+
`Final commit: e4f5g6h`
|
|
303
|
+
`Net change: 4 files, +23/-9`
|
|
304
|
+
</example>
|
|
305
|
+
|
|
306
|
+
<example>
|
|
307
|
+
User: `/qbug`
|
|
308
|
+
Subagent: [loop 4 fix produces no commit despite findings]
|
|
309
|
+
|
|
310
|
+
`Loop 4 fix: no changes — could not address remaining 2 findings`
|
|
311
|
+
`/qbug exit: stuck`
|
|
312
|
+
`Unresolved: src/cache.py:88 (P0 race condition); src/parser.py:44 (P1 unbound reference)`
|
|
313
|
+
</example>
|
|
314
|
+
|
|
315
|
+
<example>
|
|
316
|
+
User: `/qbug` (no PR or upstream diff)
|
|
317
|
+
Lead: `No PR or upstream diff. /qbug needs a target.`
|
|
318
|
+
</example>
|
|
319
|
+
|