claude-dev-env 1.50.3 → 1.51.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/CLAUDE.md +0 -8
- package/_shared/pr-loop/audit-contract.md +3 -3
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/preflight_self_heal_constants.py +28 -0
- package/_shared/pr-loop/scripts/preflight.py +18 -6
- package/_shared/pr-loop/scripts/preflight_self_heal.py +164 -0
- package/_shared/pr-loop/scripts/tests/test_preflight.py +39 -0
- package/_shared/pr-loop/scripts/tests/test_preflight_self_heal.py +273 -0
- package/agents/clean-coder.md +1 -1
- package/agents/code-quality-agent.md +7 -5
- package/audit-rubrics/category_rubrics/category-a-api-contracts.md +3 -0
- package/audit-rubrics/category_rubrics/category-f-silent-failures.md +3 -0
- package/audit-rubrics/category_rubrics/category-k-codebase-conflicts.md +8 -2
- package/audit-rubrics/category_rubrics/category-n-test-name-scenario-verifier.md +3 -0
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +39 -0
- package/audit-rubrics/category_rubrics/category-p-name-vs-behavior-contract.md +40 -0
- package/audit-rubrics/prompts/category-a-api-contracts.md +11 -4
- package/audit-rubrics/prompts/category-b-selector-engine-compat.md +2 -2
- package/audit-rubrics/prompts/category-c-resource-cleanup.md +1 -1
- package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +1 -1
- package/audit-rubrics/prompts/category-e-dead-code.md +1 -1
- package/audit-rubrics/prompts/category-f-silent-failures.md +13 -2
- package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +1 -1
- package/audit-rubrics/prompts/category-h-security-boundaries.md +1 -1
- package/audit-rubrics/prompts/category-i-concurrency.md +1 -1
- package/audit-rubrics/prompts/category-j-code-rules-compliance.md +1 -1
- package/audit-rubrics/prompts/category-k-codebase-conflicts.md +15 -5
- package/audit-rubrics/prompts/category-l-behavior-equivalence.md +1 -1
- package/audit-rubrics/prompts/category-m-producer-consumer-cardinality.md +1 -1
- package/audit-rubrics/prompts/category-n-test-name-scenario-verifier.md +10 -3
- package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +74 -0
- package/audit-rubrics/prompts/category-p-name-vs-behavior-contract.md +75 -0
- package/docs/CODE_RULES.md +24 -346
- package/package.json +1 -1
- package/rules/ask-user-question-required.md +2 -41
- package/rules/confirm-implementation-forks.md +3 -44
- package/rules/gh-body-file.md +2 -78
- package/rules/gh-paginate.md +2 -78
- package/rules/plain-language.md +2 -41
- package/rules/prompt-workflow-context-controls.md +9 -38
- package/rules/shell-invocation-policy.md +2 -141
- package/rules/testing.md +10 -0
- package/rules/vault-context.md +3 -32
- package/rules/windows-filesystem-safe.md +3 -87
- package/scripts/sync_to_cursor/rules.py +201 -79
- package/scripts/tests/test_sync_to_cursor.py +122 -26
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +2 -0
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +51 -4
- package/skills/auditing-claude-config/SKILL.md +6 -1
- package/skills/bugteam/CONSTRAINTS.md +1 -1
- package/skills/bugteam/PROMPTS.md +8 -6
- package/skills/bugteam/SKILL.md +5 -5
- package/skills/bugteam/reference/audit-and-teammates.md +1 -1
- package/skills/bugteam/reference/audit-contract.md +4 -4
- package/skills/bugteam/reference/design-rationale.md +1 -1
- package/skills/bugteam/reference/obstacles/audit-walk-categories.md +1 -1
- package/skills/bugteam/reference/team-setup.md +17 -5
- package/skills/bugteam/scripts/bugteam_preflight.py +22 -10
- package/skills/bugteam/scripts/test_bugteam_preflight.py +32 -0
- package/skills/copilot-review/SKILL.md +5 -8
- package/skills/doc-gist/SKILL.md +5 -8
- package/skills/fixbugs/SKILL.md +1 -1
- package/skills/gh-paginate/SKILL.md +84 -0
- package/skills/pr-converge/SKILL.md +28 -1
- package/skills/pr-converge/reference/per-tick.md +24 -8
- package/skills/pre-compact/SKILL.md +4 -9
- package/skills/refine/SKILL.md +8 -2
- package/skills/structure-prompt/SKILL.md +5 -10
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: auditing-claude-config
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
Audits a Claude Code setup (user CLAUDE.md, ~/.claude/rules/, project .claude/) for
|
|
5
|
+
context-budget waste — duplicate @-imports, always-on rules to path-scope or convert
|
|
6
|
+
to skills, oversized files — and produces a migration table with savings. Use when
|
|
7
|
+
reviewing the startup/instruction load, when /memory shows surprising loads, when
|
|
8
|
+
adding new rules, or for periodic config hygiene.
|
|
4
9
|
---
|
|
5
10
|
|
|
6
11
|
# Auditing Claude Config
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Constraints
|
|
4
4
|
|
|
5
|
-
- **Full A–
|
|
5
|
+
- **Full A–P audit every loop, no exceptions.** PR size, "focused audit," "team overhead," "CODE_RULES already passed" — not valid reasons. Empty `<findings/>` for any category is a valid result. The audit agent walks all A–P rubrics each loop.
|
|
6
6
|
- **One run per invocation, multi-PR supported.** All PRs in a single /bugteam invocation share one `run_temp_dir`. Per-PR identity lives in the subagent name prefix (`bugfind-pr<N>-loop<L>` / `bugfix-pr<N>-loop<L>`) and the `<run_temp_dir>/pr-<N>/` subfolder containing that PR's git worktree, diff patches, and outcome XML files.
|
|
7
7
|
- **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
8
|
- **Fresh subagent per loop.** Both bugfind and bugfix are spawned new each loop. Reusing a subagent across loops accumulates context inside that subagent's window — defeats clean-room.
|
|
@@ -24,7 +24,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
24
24
|
</scope>
|
|
25
25
|
|
|
26
26
|
<bug_categories>
|
|
27
|
-
Investigate each of the
|
|
27
|
+
Investigate each of the sixteen categories (A–P) explicitly. For each,
|
|
28
28
|
return either at least one finding OR a verified-clean entry with the
|
|
29
29
|
evidence backing the verdict. A category is verified-clean only when one
|
|
30
30
|
complete execution path through the changed code has been traced from
|
|
@@ -37,7 +37,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
37
37
|
When evidence contains any of these phrases, the category is not
|
|
38
38
|
verified-clean -- re-audit with a concrete trace.
|
|
39
39
|
|
|
40
|
-
Categories A–
|
|
40
|
+
Categories A–P (one-line summary; full rubric and sub-bucket
|
|
41
41
|
decomposition for each is in
|
|
42
42
|
`$HOME/.claude/audit-rubrics/category_rubrics/`; ready-to-send Variant
|
|
43
43
|
C prompts — each with a PR/repo-independent generalized skeleton above
|
|
@@ -58,6 +58,8 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
58
58
|
L. Behavior-equivalence for refactors
|
|
59
59
|
M. Producer/consumer cardinality vs collection-type contract
|
|
60
60
|
N. Test-name scenario verifier
|
|
61
|
+
O. Docstring / fixture-prose vs implementation drift
|
|
62
|
+
P. Name / regex / word-list vs behavior-contract precision
|
|
61
63
|
</bug_categories>
|
|
62
64
|
|
|
63
65
|
<rubric_reference>
|
|
@@ -77,7 +79,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
77
79
|
</constraints>
|
|
78
80
|
|
|
79
81
|
<comment_posting>
|
|
80
|
-
Load all A–
|
|
82
|
+
Load all A–P rubrics from
|
|
81
83
|
`$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/`. The prompt file
|
|
82
84
|
is a template for output shape, not a straitjacket — reorganize when the
|
|
83
85
|
diff demands it. The diff supplies the findings; the rubric supplies the
|
|
@@ -88,7 +90,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
88
90
|
done.
|
|
89
91
|
|
|
90
92
|
<self_audit_checklist>
|
|
91
|
-
[ ] Walk all
|
|
93
|
+
[ ] Walk all 16 categories (A–P), each with Shape A or Shape B
|
|
92
94
|
[ ] Assign finding IDs (loop<L>-<K>)
|
|
93
95
|
[ ] Capture excerpts, validate anchors, format finding bodies
|
|
94
96
|
[ ] Build findings JSON, invoke post_audit_thread.py, capture html_url
|
|
@@ -96,7 +98,7 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
96
98
|
[ ] Write outcome XML
|
|
97
99
|
</self_audit_checklist>
|
|
98
100
|
|
|
99
|
-
1. Audit the diff against the
|
|
101
|
+
1. Audit the diff against the 16 categories above. Buffer the findings
|
|
100
102
|
in memory; all posting happens at step 4 once anchors are validated.
|
|
101
103
|
2. Assign each finding a stable finding_id of exactly the form `loop<L>-<K>`
|
|
102
104
|
where <K> is 1-based within this loop.
|
|
@@ -227,7 +229,7 @@ attributes.
|
|
|
227
229
|
</bugteam_audit>
|
|
228
230
|
```
|
|
229
231
|
|
|
230
|
-
Verified-clean evidence per A–
|
|
232
|
+
Verified-clean evidence per A–P category is surfaced in the agent's text-mode
|
|
231
233
|
final report, not in this outcome XML (the writer accepts a flat findings list
|
|
232
234
|
only).
|
|
233
235
|
|
package/skills/bugteam/SKILL.md
CHANGED
|
@@ -11,10 +11,10 @@ description: >-
|
|
|
11
11
|
# Bugteam
|
|
12
12
|
|
|
13
13
|
Audit–fix until convergence. Bugfind: `code-quality-agent`, fresh context each
|
|
14
|
-
loop, auditing all A–
|
|
14
|
+
loop, auditing all A–P categories. Bugfix: `clean-coder`. Hard cap: 20 audit
|
|
15
15
|
loops. Grant `.claude/**` at start, revoke always at end.
|
|
16
16
|
|
|
17
|
-
The audit agent loads the A–
|
|
17
|
+
The audit agent loads the A–P category rubrics from
|
|
18
18
|
`$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/` alongside
|
|
19
19
|
[`PROMPTS.md`](PROMPTS.md) and produces a single outcome XML per loop.
|
|
20
20
|
|
|
@@ -146,7 +146,7 @@ end-to-end mental model before starting Step 0.
|
|
|
146
146
|
| Posting the end-of-pass audit review via `post_audit_thread.py` (APPROVE on CLEAN — the request event; GitHub stores it as `state=APPROVED` — REQUEST_CHANGES with inline anchored comments on DIRTY) | [§ Audit posting](#audit-posting) |
|
|
147
147
|
| Posting per-finding fix replies via GitHub MCP `add_reply_to_pull_request_comment` (rendered with the unified template at [`_shared/pr-loop/audit-reply-template.md`](../../_shared/pr-loop/audit-reply-template.md)) | [reference/github-pr-reviews.md](reference/github-pr-reviews.md) |
|
|
148
148
|
| Teardown, PR description rewrite via `pr-description-writer`, permission revoke, final report | [reference/teardown-publish-permissions.md](reference/teardown-publish-permissions.md) |
|
|
149
|
-
| Spawn-prompt XML, A–
|
|
149
|
+
| Spawn-prompt XML, A–P category bindings, outcome XML schemas | [PROMPTS.md](PROMPTS.md) |
|
|
150
150
|
| Per-category audit content (sub-buckets, decision criteria, ready-to-send Variant C templates) | `$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/` |
|
|
151
151
|
| Invariants and design rationale | [CONSTRAINTS.md](CONSTRAINTS.md), [reference/design-rationale.md](reference/design-rationale.md) |
|
|
152
152
|
| Audit-contract finding shape (Shape A / B), Haiku secondary, post-fix self-audit | [reference/audit-contract.md](reference/audit-contract.md) |
|
|
@@ -159,11 +159,11 @@ end-to-end mental model before starting Step 0.
|
|
|
159
159
|
- `SKILL.md` — this hub.
|
|
160
160
|
- `reference/` — workflow detail per situation.
|
|
161
161
|
- `scripts/` — utility scripts executed, not loaded as primary context.
|
|
162
|
-
- `PROMPTS.md` — spawn XML, A–
|
|
162
|
+
- `PROMPTS.md` — spawn XML, A–P category bindings, outcome schemas.
|
|
163
163
|
- `CONSTRAINTS.md` — invariants.
|
|
164
164
|
- `EXAMPLES.md` — exit scenarios.
|
|
165
165
|
- `sources.md` — doc URLs and verbatim quotes.
|
|
166
166
|
- `~/.claude/audit-rubrics/` — installed by `npx claude-dev-env` from
|
|
167
|
-
`packages/claude-dev-env/audit-rubrics/`; the audit agent reads all A–
|
|
167
|
+
`packages/claude-dev-env/audit-rubrics/`; the audit agent reads all A–P
|
|
168
168
|
rubrics under `category_rubrics/` and prompts under `prompts/`. Required
|
|
169
169
|
at audit time alongside `PROMPTS.md`.
|
|
@@ -21,7 +21,7 @@ Each finding an audit produces MUST be one of exactly two shapes.
|
|
|
21
21
|
"id": "loop<L>-<K>",
|
|
22
22
|
"file": "path/relative/to/repo/root.py",
|
|
23
23
|
"line": 123,
|
|
24
|
-
"category": "A | B | C | D | E | F | G | H | I | J | K | L | M | N",
|
|
24
|
+
"category": "A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P",
|
|
25
25
|
"severity": "P0 | P1 | P2",
|
|
26
26
|
"excerpt": "verbatim code snippet from the offending line(s)",
|
|
27
27
|
"failure_mode": "one sentence describing what goes wrong and when",
|
|
@@ -37,7 +37,7 @@ Used when an audit investigates a category and does NOT find a bug. Bare "verifi
|
|
|
37
37
|
|
|
38
38
|
```json
|
|
39
39
|
{
|
|
40
|
-
"category": "A | B | C | D | E | F | G | H | I | J | K | L | M | N",
|
|
40
|
+
"category": "A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P",
|
|
41
41
|
"files_opened": ["file1.py", "file2.py"],
|
|
42
42
|
"lines_quoted": [
|
|
43
43
|
{"file": "file1.py", "line": 88, "text": "verbatim line content"}
|
|
@@ -89,7 +89,7 @@ After the primary finding list is complete, every audit runs a second pass again
|
|
|
89
89
|
|
|
90
90
|
The audit must either produce new Shape A findings citing new file:line references not present in the first pass, or cite explicit Shape B adversarial-probe entries for each category it re-examined. An adversarial pass that returns "nothing new, confident first pass was complete" is REJECTED — produce evidence or findings, not confidence.
|
|
91
91
|
|
|
92
|
-
For `/bugteam`, the single audit agent provides per-category coverage by walking all A–
|
|
92
|
+
For `/bugteam`, the single audit agent provides per-category coverage by walking all A–P rubrics in one invocation.
|
|
93
93
|
|
|
94
94
|
## Merge rules
|
|
95
95
|
|
|
@@ -113,7 +113,7 @@ Sequence:
|
|
|
113
113
|
3. Run `py_compile` (or language-equivalent) on each modified file.
|
|
114
114
|
4. Compute `fix_diff` against pre-fix contents for the modified set.
|
|
115
115
|
5. Run `bugteam_code_rules_gate.py` with explicit paths for every modified file.
|
|
116
|
-
6. Spawn a scoped audit of `fix_diff` with full A–
|
|
116
|
+
6. Spawn a scoped audit of `fix_diff` with full A–P rigor, Shape A/B contract, adversarial pass, AND Haiku secondary in parallel (paranoid mode on post-fix).
|
|
117
117
|
7. Any new findings become same-loop fix-targets. Internal iteration count increments by one.
|
|
118
118
|
8. After 3 internal iterations with fresh findings each time, exit `stuck: post-fix audit not converging`.
|
|
119
119
|
9. Only when `gate_findings` empty AND `post_fix_findings` empty: `git add`, commit, push.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Core principle (expanded)
|
|
4
4
|
|
|
5
|
-
One audit agent (`code-quality-agent`, opus) walks all A–
|
|
5
|
+
One audit agent (`code-quality-agent`, opus) walks all A–P categories per loop. One fix agent (`clean-coder`, opus) addresses the audit's findings.
|
|
6
6
|
|
|
7
7
|
Fresh-spawn clean-room isolation: each `Agent` call creates a new subagent with its own context window and no access to prior conversation. After the subagent writes its outcome XML and self-terminates, the lead reads the file. Results never accumulate in the lead’s context beyond the XML artifact. Verbatim Anthropic quotes and URLs: [`../sources.md`](../sources.md).
|
|
8
8
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Walk categories
|
|
2
2
|
|
|
3
|
-
Audit every category A through
|
|
3
|
+
Audit every category A through P. Each returns Shape A finding or Shape B verified-clean. No skipped categories.
|
|
4
4
|
|
|
5
5
|
## Self-population
|
|
6
6
|
|
|
@@ -17,11 +17,23 @@ python "${CLAUDE_SKILL_DIR}/scripts/bugteam_preflight.py"
|
|
|
17
17
|
Non-zero → fix before grant. `BUGTEAM_PREFLIGHT_SKIP=1` emergency only.
|
|
18
18
|
`--pre-commit` if `.pre-commit-config.yaml` exists.
|
|
19
19
|
|
|
20
|
-
**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
**Self-heal for stale local-scope `core.hooksPath`:** preflight silently
|
|
21
|
+
unsets ALL local-scope `core.hooksPath` entries on the active repository when
|
|
22
|
+
two conditions both hold: at least one entry does not end in the canonical
|
|
23
|
+
`hooks/git-hooks` suffix, AND a canonical global `core.hooksPath` is already
|
|
24
|
+
configured. The check clears the `<repo>/.git/hooks` entry git can seed into a
|
|
25
|
+
worktree's local config when `extensions.worktreeConfig` is set or seeding
|
|
26
|
+
tooling is in use, so the canonical global setting takes effect without Claude
|
|
27
|
+
needing to surface a failure or invoke the fix script. When the global is
|
|
28
|
+
unset or non-canonical, the self-heal stands down so the downstream
|
|
29
|
+
`core.hooksPath is '<path>'` diagnostic stays informative.
|
|
30
|
+
|
|
31
|
+
**Auto-remediation for `core.hooksPath`:** when preflight reports stderr
|
|
32
|
+
containing `core.hooksPath` (the message starts with
|
|
33
|
+
`bugteam_preflight: core.hooksPath is`, or `Git-side CODE_RULES enforcement is
|
|
34
|
+
not active`) — which surfaces when the global setting is missing or points
|
|
35
|
+
elsewhere — Claude must auto-invoke the fix script. Do not fall through to
|
|
36
|
+
`AskUserQuestion`, do not punt to the user, do not ask for confirmation:
|
|
25
37
|
|
|
26
38
|
```bash
|
|
27
39
|
python "${CLAUDE_SKILL_DIR}/scripts/bugteam_fix_hookspath.py"
|
|
@@ -36,7 +36,8 @@ _shared_pr_loop_scripts_directory = (
|
|
|
36
36
|
if str(_shared_pr_loop_scripts_directory) not in sys.path:
|
|
37
37
|
sys.path.insert(0, str(_shared_pr_loop_scripts_directory))
|
|
38
38
|
|
|
39
|
-
from
|
|
39
|
+
from preflight_self_heal import silently_clear_stale_local_hooks_path_override # noqa: E402
|
|
40
|
+
from reviews_disabled import ( # noqa: E402
|
|
40
41
|
CLAUDE_REVIEWS_DISABLED_BUGTEAM_TOKEN,
|
|
41
42
|
CLAUDE_REVIEWS_DISABLED_ENV_VAR_NAME,
|
|
42
43
|
EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV,
|
|
@@ -47,9 +48,17 @@ from reviews_disabled import (
|
|
|
47
48
|
def verify_git_hooks_path(repository_root: Path | None) -> int:
|
|
48
49
|
"""Check that core.hooksPath resolves to the claude-dev-env git-hooks directory.
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
Silently clears any stale, non-canonical local-scope core.hooksPath
|
|
52
|
+
override before querying the effective config, so a worktree-seeded local
|
|
53
|
+
entry cannot shadow a correctly configured global setting. When
|
|
54
|
+
*repository_root* is provided, queries the effective config for that
|
|
55
|
+
repository (``git -C <root> config --get``). When a canonical global
|
|
56
|
+
``core.hooksPath`` is already configured, the preceding self-heal step
|
|
57
|
+
clears non-canonical local-scope entries, so repo-level overrides such
|
|
58
|
+
as Husky or lefthook at local scope are silently removed in favor of
|
|
59
|
+
the canonical global; when the global is unset or non-canonical, the
|
|
60
|
+
self-heal stands down and the ``--get`` query still surfaces those
|
|
61
|
+
overrides through the failure path. Falls back to the current working
|
|
53
62
|
directory's effective config when *repository_root* is None.
|
|
54
63
|
|
|
55
64
|
Args:
|
|
@@ -60,12 +69,15 @@ def verify_git_hooks_path(repository_root: Path | None) -> int:
|
|
|
60
69
|
Zero when the configured path ends with the expected hooks suffix.
|
|
61
70
|
Non-zero and prints a correction message when unset or pointing elsewhere.
|
|
62
71
|
"""
|
|
72
|
+
silently_clear_stale_local_hooks_path_override(
|
|
73
|
+
repository_root, EXPECTED_HOOKS_PATH_SUFFIX
|
|
74
|
+
)
|
|
63
75
|
git_command: list[str] = ["git"]
|
|
64
76
|
if repository_root is not None:
|
|
65
77
|
git_command.extend(["-C", str(repository_root)])
|
|
66
78
|
git_command.extend(list(ALL_GIT_CONFIG_HOOKS_PATH_ARGUMENTS))
|
|
67
79
|
try:
|
|
68
|
-
|
|
80
|
+
query_completed_process = subprocess.run(
|
|
69
81
|
git_command,
|
|
70
82
|
capture_output=True,
|
|
71
83
|
text=True,
|
|
@@ -87,13 +99,13 @@ def verify_git_hooks_path(repository_root: Path | None) -> int:
|
|
|
87
99
|
file=sys.stderr,
|
|
88
100
|
)
|
|
89
101
|
return EXIT_CODE_HOOKS_PATH_CHECK_FAILED
|
|
90
|
-
if
|
|
102
|
+
if query_completed_process.returncode != 0:
|
|
91
103
|
print(
|
|
92
104
|
f"{BUGTEAM_PREFLIGHT_PREFIX}{ENFORCEMENT_ABSENT_MESSAGE}",
|
|
93
105
|
file=sys.stderr,
|
|
94
106
|
)
|
|
95
107
|
return EXIT_CODE_HOOKS_PATH_CHECK_FAILED
|
|
96
|
-
configured_path =
|
|
108
|
+
configured_path = query_completed_process.stdout.strip().replace("\\", "/").rstrip("/")
|
|
97
109
|
if not configured_path.endswith(EXPECTED_HOOKS_PATH_SUFFIX):
|
|
98
110
|
print(
|
|
99
111
|
f"{BUGTEAM_PREFLIGHT_PREFIX}core.hooksPath is '{configured_path}' — "
|
|
@@ -178,20 +190,20 @@ def _pytest_exit_code_no_tests_collected() -> int:
|
|
|
178
190
|
return PYTEST_EXIT_CODE_NO_TESTS_COLLECTED
|
|
179
191
|
|
|
180
192
|
|
|
181
|
-
def run_pytest(repository_root: Path,
|
|
193
|
+
def run_pytest(repository_root: Path, is_verbose: bool) -> int:
|
|
182
194
|
"""Run pytest in the repository root and return the exit code.
|
|
183
195
|
|
|
184
196
|
Treats the "no tests collected" exit code as a pass (exit 0).
|
|
185
197
|
|
|
186
198
|
Args:
|
|
187
199
|
repository_root: The repository root for running pytest.
|
|
188
|
-
|
|
200
|
+
is_verbose: When True, pass no -q flag (shows individual test names).
|
|
189
201
|
|
|
190
202
|
Returns:
|
|
191
203
|
The pytest exit code, or 0 when no tests were collected.
|
|
192
204
|
"""
|
|
193
205
|
command = [sys.executable, "-m", "pytest"]
|
|
194
|
-
if not
|
|
206
|
+
if not is_verbose:
|
|
195
207
|
command.append("-q")
|
|
196
208
|
completed = subprocess.run(
|
|
197
209
|
command,
|
|
@@ -282,3 +282,35 @@ def test_main_should_halt_when_env_var_contains_uppercase_or_whitespace_bugteam_
|
|
|
282
282
|
monkeypatch.delenv("BUGTEAM_PREFLIGHT_SKIP", raising=False)
|
|
283
283
|
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
284
284
|
assert exit_code == bugteam_preflight.EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _was_called_with_argument(
|
|
288
|
+
mock_subprocess_run: MagicMock, argument_token: str
|
|
289
|
+
) -> bool:
|
|
290
|
+
return any(
|
|
291
|
+
argument_token in each_call_args[0][0]
|
|
292
|
+
for each_call_args in mock_subprocess_run.call_args_list
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def test_verify_git_hooks_path_invokes_self_heal_before_effective_query(
|
|
297
|
+
tmp_path: Path,
|
|
298
|
+
) -> None:
|
|
299
|
+
"""verify_git_hooks_path must delegate to the shared self-heal helper before --get."""
|
|
300
|
+
canonical_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
301
|
+
canonical_hooks_path.mkdir(parents=True)
|
|
302
|
+
with patch("subprocess.run") as mock_run:
|
|
303
|
+
mock_run.return_value = _make_completed_process(
|
|
304
|
+
str(canonical_hooks_path) + "\n", returncode=0
|
|
305
|
+
)
|
|
306
|
+
bugteam_preflight.verify_git_hooks_path(tmp_path)
|
|
307
|
+
assert _was_called_with_argument(mock_run, "--get-all"), (
|
|
308
|
+
"verify_git_hooks_path must run the --local --get-all read for self-heal"
|
|
309
|
+
)
|
|
310
|
+
assert _was_called_with_argument(mock_run, "--get"), (
|
|
311
|
+
"verify_git_hooks_path must still run the effective --get verification"
|
|
312
|
+
)
|
|
313
|
+
first_called_command = mock_run.call_args_list[0][0][0]
|
|
314
|
+
assert "--get-all" in first_called_command, (
|
|
315
|
+
"Self-heal must run BEFORE the effective config query, not after"
|
|
316
|
+
)
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: copilot-review
|
|
3
3
|
description: >-
|
|
4
|
-
Spawns a background subagent that babysits the GitHub Copilot reviewer on the
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
terminates on convergence (clean review against HEAD) and reports back.
|
|
10
|
-
Triggers: '/copilot-review', 'watch copilot', 'babysit copilot review',
|
|
11
|
-
'loop copilot reviews', 're-request copilot', 'keep re-requesting copilot'.
|
|
4
|
+
Spawns a background subagent that babysits the GitHub Copilot reviewer on the current
|
|
5
|
+
PR: each ~5-min tick it fetches the latest copilot-pull-request-reviewer[bot] review,
|
|
6
|
+
fixes unaddressed findings against HEAD (commit, push, inline replies), re-requests
|
|
7
|
+
review, and exits on convergence. Triggers: '/copilot-review', 'watch copilot',
|
|
8
|
+
'babysit copilot review', 'keep re-requesting copilot'.
|
|
12
9
|
---
|
|
13
10
|
|
|
14
11
|
# Copilot Review
|
package/skills/doc-gist/SKILL.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: doc-gist
|
|
3
3
|
description: >-
|
|
4
|
-
Use when the user asks to share, publish, preview, or open as a webpage any
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
the `gist_upload` transport script, an auto-publish hook keyed off the
|
|
10
|
-
`<!-- @publish-as-gist -->` HTML comment, and a 20-file gallery of HTML
|
|
11
|
-
artifact patterns to draw from when designing fresh.
|
|
4
|
+
Use when the user asks to share, publish, preview, or open as a webpage any HTML doc,
|
|
5
|
+
writeup, report, plan, runbook, or interactive artifact. Triggers on /doc-gist,
|
|
6
|
+
"publish this", "share as a gist", "open this as a webpage", "make me a writeup", or
|
|
7
|
+
any request ending in a shareable HTML preview URL. Provides the gist_upload transport
|
|
8
|
+
script, the <!-- @publish-as-gist --> auto-publish hook, and an HTML pattern gallery.
|
|
12
9
|
---
|
|
13
10
|
|
|
14
11
|
# doc-gist
|
package/skills/fixbugs/SKILL.md
CHANGED
|
@@ -33,7 +33,7 @@ Locate the most recent `/findbugs` output in the current conversation. For each
|
|
|
33
33
|
|
|
34
34
|
- Severity (`P0` / `P1` / `P2`)
|
|
35
35
|
- `file:line`
|
|
36
|
-
- Category (the A–
|
|
36
|
+
- Category (the A–P letter or category name `/findbugs` reported)
|
|
37
37
|
- One-sentence description as `/findbugs` wrote it
|
|
38
38
|
|
|
39
39
|
Apply the severity filter from `$ARGUMENTS` if present:
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gh-paginate
|
|
3
|
+
description: Safe pagination for gh api reads of paginated GitHub list endpoints (PR reviews/comments/files, issue comments, pulls, issues) — --paginate --slurp piped to external jq, single-page bounds, newest-first walks. Use before composing any gh api list-endpoint call or any cross-page jq operation (sort_by | last, reverse).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gh API Pagination Rule
|
|
7
|
+
|
|
8
|
+
**Root cause:** GitHub REST API list endpoints paginate by default. Without `--paginate --slurp`, callers see only the oldest page, and cross-page jq operations (e.g., `sort_by | last`) operate within a single page — producing wrong-but-confident results.
|
|
9
|
+
|
|
10
|
+
**Rule:** All `gh api` calls that read `pulls/<number>/reviews`, `pulls/<number>/comments`, `issues/<number>/comments`, or any other paginated GitHub list endpoint **must** request the full set of pages AND apply any cross-page jq operation through external `jq`, not through `gh`'s built-in `--jq`. Use `--paginate --slurp | jq` (preferred — see [Safe patterns](#safe-patterns)). Never call these endpoints with their default pagination, and never use `gh`'s `--jq` for cross-page operations like `sort_by | last` or `| reverse | .[0]`.
|
|
11
|
+
|
|
12
|
+
## Two defects, one rule
|
|
13
|
+
|
|
14
|
+
This rule guards against two distinct silent-truncation defects that compound:
|
|
15
|
+
|
|
16
|
+
1. **Default page truncation.** Without `--paginate`, only the first page is fetched.
|
|
17
|
+
2. **`--jq` runs per-page, not on the concatenated result.** Per [GitHub CLI #10459](https://github.com/cli/cli/issues/10459), `gh api --paginate --jq '<filter>'` applies `<filter>` to each page **separately** and emits one output per page. Cross-page operations like `sort_by(.submitted_at) | last` therefore operate within each page independently, not across the merged result set.
|
|
18
|
+
|
|
19
|
+
The safe patterns below fix both defects together: `--paginate --slurp` walks every page AND emits a single merged structure, and an **external** `jq` then runs cross-page operations on that merged structure.
|
|
20
|
+
|
|
21
|
+
## Affected endpoints
|
|
22
|
+
|
|
23
|
+
The rule applies to every paginated read from the GitHub REST API. Common offenders in PR-loop skills:
|
|
24
|
+
|
|
25
|
+
- `gh api repos/<owner>/<repo>/pulls/<number>/reviews`
|
|
26
|
+
- `gh api repos/<owner>/<repo>/pulls/<number>/comments`
|
|
27
|
+
- `gh api repos/<owner>/<repo>/pulls/<number>/files`
|
|
28
|
+
- `gh api repos/<owner>/<repo>/issues/<number>/comments`
|
|
29
|
+
- `gh api repos/<owner>/<repo>/pulls`
|
|
30
|
+
- `gh api repos/<owner>/<repo>/issues`
|
|
31
|
+
|
|
32
|
+
The same rule applies to any other endpoint documented as paginated by GitHub (see [GitHub REST API pagination](https://docs.github.com/en/rest/using-the-rest-api/using-pagination-in-the-rest-api)).
|
|
33
|
+
|
|
34
|
+
Single-object endpoints (e.g., `repos/<owner>/<repo>/pulls/<number>` returning one PR object) are not paginated — `?per_page=...` is silently ignored, and neither `--paginate` nor external `jq` is required. Use `gh`'s `--jq` directly on those endpoints.
|
|
35
|
+
|
|
36
|
+
## Safe patterns
|
|
37
|
+
|
|
38
|
+
### Preferred — `--paginate --slurp` piped to external `jq`
|
|
39
|
+
|
|
40
|
+
`gh api ... --paginate --slurp` walks every page and emits a single merged JSON array of page-arrays (`[[page1_items...], [page2_items...], ...]`). Pipe to external `jq` to flatten and filter across the full result set:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
gh api 'repos/<owner>/<repo>/pulls/<number>/reviews?per_page=100' --paginate --slurp \
|
|
44
|
+
| jq '[.[][] | select(.user.login=="cursor[bot]")] | sort_by(.submitted_at) | last'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The `.[][]` flattens the array-of-pages into one stream of items before the cross-page operators (`sort_by`, `last`, `reverse`) run. Combine with `?per_page=100` to reduce round-trips on long PRs.
|
|
48
|
+
|
|
49
|
+
`gh`'s `--jq` flag and `--slurp` flag are mutually exclusive (gh CLI rejects `--paginate --slurp --jq` with `the --slurp option is not supported with --jq or --template`), which is why the filter must run in an external `jq` invocation.
|
|
50
|
+
|
|
51
|
+
### Acceptable — single-page bound on a paginated list endpoint when result fits
|
|
52
|
+
|
|
53
|
+
When you have an explicit reason to read at most one page from a **paginated** list endpoint (e.g., a known-small list), document the bound in a comment and use `?per_page=100` without `--paginate`. Cross-page operators are not in play here, so `gh`'s `--jq` is safe:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Bound: a freshly created issue is expected to have <= 100 comments.
|
|
57
|
+
gh api 'repos/<owner>/<repo>/issues/<number>/comments?per_page=100' \
|
|
58
|
+
--jq '[.[] | select(.user.login=="cursor[bot]")] | length'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This pattern is only safe when the endpoint is confirmed to return a list smaller than 100 entries. Lists that grow over the PR's lifetime (reviews, comments) must use `--paginate --slurp` plus external `jq`.
|
|
62
|
+
|
|
63
|
+
### Single-object endpoints — no pagination needed
|
|
64
|
+
|
|
65
|
+
Endpoints that return a single object (e.g., `pulls/<number>`, `issues/<number>`) are not paginated. `?per_page=...`, `--paginate`, and `--slurp` are all unnecessary. Use `gh`'s built-in `--jq` directly:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
gh api 'repos/<owner>/<repo>/pulls/<number>' --jq '.head.sha'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Newest-first walk
|
|
72
|
+
|
|
73
|
+
Pair pagination with explicit reverse-sort so the consumer reads newest-first regardless of the API's internal order:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
gh api 'repos/<owner>/<repo>/pulls/<number>/reviews?per_page=100' --paginate --slurp \
|
|
77
|
+
| jq '[.[][] | select(.user.login=="cursor[bot]")] | sort_by(.submitted_at) | reverse'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This is the canonical pattern for the bugbot <-> bugteam convergence loop: walk newest-first, stop at the first clean review.
|
|
81
|
+
|
|
82
|
+
## Enforcement
|
|
83
|
+
|
|
84
|
+
This rule is documentation-only at present. A future PreToolUse hook may pattern-match `Bash` invocations of `gh api repos/.../pulls/<n>/(reviews|comments)` without `--paginate --slurp` (or with `--paginate --jq` doing cross-page operations) and return a corrective message. Until that hook lands, treat this rule as binding by review and rely on it during skill authoring.
|
|
@@ -105,6 +105,15 @@ For each unresolved thread, verify the concern against current HEAD;
|
|
|
105
105
|
either fix-and-resolve, or reply-with-note-and-resolve when the concern
|
|
106
106
|
no longer applies.
|
|
107
107
|
|
|
108
|
+
**Full-PR-diff rule: every CODE-REVIEW round (Step 5) and every BUGTEAM
|
|
109
|
+
round (Step 6) covers the FULL `origin/main...HEAD` diff — every file
|
|
110
|
+
the PR touches.** A round that scopes to a subset — only the last commit,
|
|
111
|
+
only files touched since the prior clean SHA, only bugbot-flagged paths,
|
|
112
|
+
or any other delta cut — does not satisfy the gate, and a "clean" verdict
|
|
113
|
+
against a partial diff is not a valid clean. Re-run the round against the
|
|
114
|
+
full diff before recording `code_review_clean_at` or treating the bugteam
|
|
115
|
+
round as converged. This rule holds every tick, every loop, every PR.
|
|
116
|
+
|
|
108
117
|
- [ ] **Step 0: Grant project permissions**
|
|
109
118
|
`python "$HOME/.claude/skills/bugteam/scripts/grant_project_claude_permissions.py"`
|
|
110
119
|
|
|
@@ -159,12 +168,22 @@ no longer applies.
|
|
|
159
168
|
|
|
160
169
|
Pre-condition: `bugbot_clean_at == current_head` (or `bugbot_down == true`).
|
|
161
170
|
|
|
162
|
-
Run Claude Code's built-in `/code-review --fix` on the
|
|
171
|
+
Run Claude Code's built-in `/code-review --fix` on the full
|
|
172
|
+
`origin/main...HEAD` diff —
|
|
163
173
|
the [local diff review](https://code.claude.com/docs/en/code-review#review-a-diff-locally)
|
|
164
174
|
— so it reviews the diff and applies its findings to the working
|
|
165
175
|
tree. Pass no effort argument, so the review uses the session's
|
|
166
176
|
current effort.
|
|
167
177
|
|
|
178
|
+
**Scope: the FULL `origin/main...HEAD` diff every tick** — every file
|
|
179
|
+
the PR touches. Do not delta-scope to commits added since the prior
|
|
180
|
+
clean SHA, do not scope to a single file, do not scope to bugbot's
|
|
181
|
+
flagged paths. Before running, confirm the working tree is on the
|
|
182
|
+
PR's HEAD with no uncommitted edits, then invoke `/code-review --fix`
|
|
183
|
+
with no path arguments so it audits the whole branch diff against
|
|
184
|
+
`origin/main`. A partial-scope round does not count and cannot set
|
|
185
|
+
`code_review_clean_at`.
|
|
186
|
+
|
|
168
187
|
- [ ] **fixes applied** (working tree changed) →
|
|
169
188
|
- [ ] Commit the applied fixes (one commit) → push
|
|
170
189
|
- [ ] reset `bugbot_clean_at = null`, `code_review_clean_at = null`
|
|
@@ -187,6 +206,14 @@ no longer applies.
|
|
|
187
206
|
`pr_converge_bugteam_enforcer` hook blocks it. `qbug` is NOT an accepted
|
|
188
207
|
substitute; `bugteam` is the only allowed skill at this step.
|
|
189
208
|
|
|
209
|
+
**Scope: the FULL `origin/main...HEAD` diff every tick** — every file
|
|
210
|
+
the PR touches. Pass the PR URL as the sole argument so bugteam audits
|
|
211
|
+
the whole branch diff against `origin/main`. Do not pass a file list,
|
|
212
|
+
a path filter, a commit range, or any "just the new commits since
|
|
213
|
+
last clean" cut — bugteam owns its own discovery on the full PR diff.
|
|
214
|
+
A partial-scope round does not count and cannot satisfy the
|
|
215
|
+
converged-on-current-HEAD condition below.
|
|
216
|
+
|
|
190
217
|
After bugteam completes, re-resolve HEAD.
|
|
191
218
|
|
|
192
219
|
- [ ] **bugteam pushed new commits** →
|
|
@@ -117,14 +117,23 @@ c. Decide (four branches; match first whose predicate holds):
|
|
|
117
117
|
|
|
118
118
|
Local correctness/quality pass between BUGBOT clean and BUGTEAM. Enters
|
|
119
119
|
after BUGBOT reports clean on `current_head` (or `bugbot_down == true`).
|
|
120
|
-
Runs Claude Code's built-in `/code-review --fix` on the
|
|
121
|
-
produces no GitHub review artifact, so there
|
|
122
|
-
resolve.
|
|
123
|
-
|
|
124
|
-
a. Run Claude Code's built-in `/code-review --fix` on the
|
|
125
|
-
the
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
Runs Claude Code's built-in `/code-review --fix` on the full
|
|
121
|
+
`origin/main...HEAD` diff; it produces no GitHub review artifact, so there
|
|
122
|
+
are no code-review threads to resolve.
|
|
123
|
+
|
|
124
|
+
a. Run Claude Code's built-in `/code-review --fix` on the FULL
|
|
125
|
+
`origin/main...HEAD` diff — every file the PR touches — via the
|
|
126
|
+
[local diff review](https://code.claude.com/docs/en/code-review#review-a-diff-locally).
|
|
127
|
+
It reviews the diff and applies its findings to the working tree.
|
|
128
|
+
|
|
129
|
+
Before running, confirm the working tree sits on the PR's HEAD with no
|
|
130
|
+
uncommitted edits, then invoke `/code-review --fix` with no path
|
|
131
|
+
arguments so it audits the whole branch diff against `origin/main`. Do
|
|
132
|
+
not delta-scope to commits added since the prior clean SHA, do not
|
|
133
|
+
scope to a single file, do not scope to bugbot's flagged paths. A
|
|
134
|
+
partial-scope round does not count and cannot set
|
|
135
|
+
`code_review_clean_at`. Pass no effort argument, so the review uses
|
|
136
|
+
the session's current effort.
|
|
128
137
|
|
|
129
138
|
b. Decide (two branches; match first whose predicate holds):
|
|
130
139
|
|
|
@@ -144,6 +153,13 @@ b. Decide (two branches; match first whose predicate holds):
|
|
|
144
153
|
|
|
145
154
|
a. Run **bugteam** on current PR.
|
|
146
155
|
|
|
156
|
+
Pass the PR URL as the sole argument so bugteam audits the FULL
|
|
157
|
+
`origin/main...HEAD` diff — every file the PR touches. Bugteam owns
|
|
158
|
+
its own discovery on the full PR diff. Do not pass a file list, a
|
|
159
|
+
path filter, a commit range, or any "just the new commits since last
|
|
160
|
+
clean" cut. A partial-scope round does not count and cannot satisfy
|
|
161
|
+
the converged-on-current-HEAD condition in step (d).
|
|
162
|
+
|
|
147
163
|
- **`Skill` invokable**: invoke bugteam
|
|
148
164
|
with `Skill`.
|
|
149
165
|
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pre-compact
|
|
3
3
|
description: >-
|
|
4
|
-
Composes a focus directive for
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
play, follow-ups) the next steps depend on, so the summarizer keeps them with
|
|
9
|
-
high fidelity. It confirms the operator's intent for the next chat through a
|
|
10
|
-
structured question first, then validates each identifier against its live
|
|
11
|
-
source before stating it. Use when the user says `/pre-compact`, asks to prep for compaction, or
|
|
12
|
-
asks to compose a focus directive for `/compact`.
|
|
4
|
+
Composes a focus directive for /compact and copies the full "/compact <directive>"
|
|
5
|
+
string to the clipboard, pinning the session's load-bearing identifiers (branch, PR,
|
|
6
|
+
HEAD, worktree, in-flight work, decisions, blockers, files in play) after validating
|
|
7
|
+
each against its live source. Use on /pre-compact or any ask to prep for compaction.
|
|
13
8
|
disable-model-invocation: true
|
|
14
9
|
---
|
|
15
10
|
|