claude-dev-env 1.27.0 → 1.28.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "1.27.0",
3
+ "version": "1.28.0",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,318 @@
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
+ description="qbug audit/fix cycle for PR <number>",
98
+ prompt="<filled cycle XML; see § Subagent cycle prompt>",
99
+ run_in_background=False
100
+ )
101
+ ```
102
+
103
+ 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`).
104
+
105
+ ## Subagent cycle prompt
106
+
107
+ 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).
108
+
109
+ ```xml
110
+ <role>
111
+ You are the lone audit-fix worker for this pull request. Run the full
112
+ audit → fix → commit → push cycle in this one subagent session. The
113
+ lead has already resolved scope and pre-flight; your job is to take
114
+ the cycle to an exit state and report back.
115
+ </role>
116
+
117
+ <context>
118
+ <repo>{{OWNER_REPO}}</repo>
119
+ <branch>{{HEAD_REF}}</branch>
120
+ <base_branch>{{BASE_REF}}</base_branch>
121
+ <pr_url>{{PR_URL}}</pr_url>
122
+ <starting_sha>{{STARTING_SHA}}</starting_sha>
123
+ <qbug_temp_dir>{{QBUG_TEMP_DIR}}</qbug_temp_dir>
124
+ <gate_script>{{GATE_SCRIPT}}</gate_script>
125
+ <categories_file>{{CATEGORIES_FILE}}</categories_file>
126
+ </context>
127
+
128
+ <exit_conditions>
129
+ The cycle stops when ONE of these is true. Check on every iteration:
130
+ - converged: most recent AUDIT returned zero findings.
131
+ - stuck: most recent FIX left `git rev-parse HEAD` unchanged.
132
+ - error: three consecutive pre-audit gate rounds failed (three is
133
+ chosen because two is within normal clean-coder variance; four
134
+ rounds typically indicates a gate defect rather than fixable
135
+ violations).
136
+ There is no loop-count cap. A pathological diff with ever-changing
137
+ findings will still exit via `stuck` once a FIX produces no commit.
138
+ </exit_conditions>
139
+
140
+ <cycle>
141
+ Maintain inline across iterations:
142
+ loop_count = 0
143
+ last_action = "fresh" # fresh | audited | fixed
144
+ last_findings = {p0, p1, p2, total}
145
+ loop_comment_index = {}
146
+ audit_log = []
147
+
148
+ Iteration:
149
+ 1. Dispatch on last_action:
150
+ - "audited" and last_findings.total == 0 → exit "converged"
151
+ - "fixed" and `git rev-parse HEAD` equals the sha captured
152
+ immediately before FIX → exit "stuck"
153
+ - "fresh" or "fixed" → run pre-audit, then AUDIT
154
+ - "audited" and last_findings.total > 0 → run FIX
155
+
156
+ 2. Pre-audit (before every AUDIT):
157
+ Run the gate script at <gate_script> with `--base origin/<base_branch>`.
158
+ Non-zero exit → fix the reported violations inline and re-run the
159
+ same command. Count consecutive failures. Three failed rounds →
160
+ exit "error: code rules gate failed pre-audit".
161
+ On exit 0: increment loop_count, proceed to AUDIT.
162
+
163
+ 3. AUDIT:
164
+ Run: `gh pr diff <pr_number> -R <owner>/<repo> > <qbug_temp_dir>/loop-<loop_count>.patch`
165
+
166
+ - Read the patch file.
167
+ - Audit only added/modified lines. Read <categories_file> for the
168
+ A–J category definitions; investigate each category explicitly
169
+ and return either at least one finding or a verified-clean
170
+ entry with cleared evidence.
171
+ - Assign each finding a stable id of the form `loop<N>-<K>`
172
+ (N=loop_count, K=1-based within this loop).
173
+ - Partition into anchored (line appears in the diff) vs
174
+ unanchored (line does not).
175
+
176
+ Post ONE review per loop. Use the payload shape from
177
+ <categories_file>'s sibling SKILL.md § "PR comments" — build
178
+ the JSON with jq `--rawfile` / `-Rs` reading per-finding body
179
+ files, pipe to
180
+ `gh api repos/<owner>/<repo>/pulls/<pr_number>/reviews -X POST --input -`.
181
+ Review body first line: `## /qbug loop <N> audit: <P0>P0 / <P1>P1 / <P2>P2`.
182
+ If the review POST fails, fall back to one issue comment on
183
+ `/issues/<pr_number>/comments` carrying the full body; mark every
184
+ finding `used_fallback=true`.
185
+
186
+ Harvest `html_url` for the parent review and each child comment
187
+ into loop_comment_index[finding_id].
188
+
189
+ Update state: last_action="audited", last_findings=counts.
190
+ Append `<N> audit: <P0>P0 / <P1>P1 / <P2>P2` to audit_log.
191
+
192
+ 4. FIX:
193
+ Capture the pre-FIX sha: `pre_fix_sha = git rev-parse HEAD`.
194
+
195
+ Apply each fix. Read every file before editing. Preserve existing
196
+ comments on lines you do not modify. Add type hints on every
197
+ signature you touch.
198
+
199
+ Validate each modified Python file with `python -m py_compile`
200
+ (or the language-equivalent compile check).
201
+
202
+ Stage each modified path by explicit name: `git add <path>`.
203
+ Create one commit summarizing the fixed findings. Let every git
204
+ hook run. If a hook blocks the commit, capture its stderr, mark
205
+ every finding in this loop `status=hook_blocked`, and move on to
206
+ the next iteration without retrying this loop.
207
+
208
+ Push with a plain fast-forward: `git push`.
209
+
210
+ Reply to each finding at loop_comment_index[finding_id].finding_comment_id
211
+ using the reply CLI shape (jq `-Rs` → `gh api .../comments/<id>/replies --input -`).
212
+ Reply body is one of:
213
+ - `Fixed in <short_sha>`
214
+ - `Could not address this loop: <one-line reason>`
215
+ - `Hook blocked the fix commit: <one-line summary>`
216
+
217
+ Update state: last_action="fixed". Append
218
+ `<N> fix: <short_sha> — <fixed>/<could_not_address>/<hook_blocked>`
219
+ to audit_log.
220
+
221
+ 5. Return to step 1.
222
+ </cycle>
223
+
224
+ <example_finding_body>
225
+ **[P1] race condition on shared cache write**
226
+ Category: I (concurrency hazards)
227
+ Two writers can both pass the existence check at line 88 before either
228
+ commits the write at line 91 — whichever writes second overwrites the
229
+ first under contention. Either hold the cache lock across the check
230
+ and the write, or use a compare-and-swap primitive.
231
+
232
+ _From /qbug audit loop 2._
233
+ </example_finding_body>
234
+
235
+ <constraints>
236
+ - Edit only files reachable from the PR diff's scope.
237
+ - Keep the branch linear: append one new commit per FIX loop and push
238
+ fast-forward only.
239
+ - Preserve existing comments on lines you do not modify.
240
+ - Every signature you touch has complete type hints.
241
+ - Every file is read before you edit it (investigate before answering).
242
+ - Complete the entire cycle in this one subagent session using your
243
+ available tools directly; keep all audit and fix work inside this
244
+ session.
245
+ </constraints>
246
+
247
+ <output_format>
248
+ Return to the lead with exactly these fields:
249
+ - exit_reason: "converged" | "stuck" | "error: <detail>"
250
+ - loop_count: integer
251
+ - final_commit_sha: `git rev-parse HEAD` at exit
252
+ - audit_log: ordered list of per-loop lines
253
+ - unresolved: array of {file, line, severity, title, reason}
254
+ (present only when exit_reason == "stuck")
255
+ </output_format>
256
+ ```
257
+
258
+ ## Step 3: PR description refresh (lead)
259
+
260
+ 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`.
261
+
262
+ On error exit paths: best-effort; log the failure in the final report and continue.
263
+
264
+ ## Step 4: Final report (lead)
265
+
266
+ Use the same shape as [`bugteam/SKILL.md` Step 6](../bugteam/SKILL.md#step-6-final-report) with two deltas:
267
+
268
+ - Header substitutes `/qbug` for `/bugteam`.
269
+ - Exit states are `converged | stuck | error` (no `cap reached` state, since `/qbug` has no loop cap).
270
+
271
+ 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.
272
+
273
+ ## Constraints
274
+
275
+ - **One subagent, not a team.** Lead spawns a single `clean-coder` via the Agent tool. No `TeamCreate`. The subagent does not spawn further subagents.
276
+ - **No loop cap.** Cycle runs until `converged`, `stuck`, or `error`. User can interrupt.
277
+ - **Code rules gate before every AUDIT.** Same `validate_content` logic as /bugteam.
278
+ - **One commit per FIX action.** Linear branch, fast-forward push only.
279
+ - **Categories A–J.** Same rubric as [`bugteam/PROMPTS.md`](../bugteam/PROMPTS.md).
280
+ - **One review per loop.** Anchored findings as `comments[]`; unanchored listed under "Findings without a diff anchor" in the review body.
281
+ - **PR description rewrite on every exit**, same as /bugteam Step 4.5.
282
+ - **Temp file cleanup on every exit path.**
283
+ - **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.
284
+
285
+ ## Examples
286
+
287
+ <example>
288
+ User: `/qbug`
289
+ Lead: [preflight, resolves PR #42, spawns ONE clean-coder subagent with the cycle prompt]
290
+ Subagent: [runs loops internally, returns]
291
+
292
+ `Loop 1 audit: 1P0 / 2P1 / 0P2`
293
+ `Loop 1 fix: commit a1b2c3d (3 files, +18/-7) — 3 fixed, 0 skipped`
294
+ `Loop 2 audit: 0P0 / 1P1 / 0P2`
295
+ `Loop 2 fix: commit e4f5g6h (1 file, +5/-2) — 1 fixed, 0 skipped`
296
+ `Loop 3 audit: 0P0 / 0P1 / 0P2 → converged`
297
+
298
+ `/qbug exit: converged`
299
+ `Loops: 3`
300
+ `Starting commit: 9d8c7b6`
301
+ `Final commit: e4f5g6h`
302
+ `Net change: 4 files, +23/-9`
303
+ </example>
304
+
305
+ <example>
306
+ User: `/qbug`
307
+ Subagent: [loop 4 fix produces no commit despite findings]
308
+
309
+ `Loop 4 fix: no changes — could not address remaining 2 findings`
310
+ `/qbug exit: stuck`
311
+ `Unresolved: src/cache.py:88 (P0 race condition); src/parser.py:44 (P1 unbound reference)`
312
+ </example>
313
+
314
+ <example>
315
+ User: `/qbug` (no PR or upstream diff)
316
+ Lead: `No PR or upstream diff. /qbug needs a target.`
317
+ </example>
318
+