claude-dev-env 1.34.1 → 1.36.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/agents/clean-coder.md +109 -1
- package/agents/docs-agent.md +1 -1
- package/agents/project-docs-analyzer.md +0 -1
- package/agents/skill-to-agent-converter.md +0 -1
- package/bin/install.mjs +28 -8
- package/bin/install.test.mjs +9 -1
- package/commands/initialize.md +0 -1
- package/commands/readability-review.md +4 -4
- package/commands/review-plan.md +2 -4
- package/commands/stubcheck.md +1 -2
- package/docs/CODE_RULES.md +3 -0
- package/docs/agents-md-alignment-plan.md +123 -0
- package/hooks/blocking/code_rules_enforcer.py +686 -60
- package/hooks/blocking/es_exe_path_rewriter.py +10 -4
- package/hooks/blocking/test_code_rules_enforcer.py +273 -39
- package/hooks/blocking/test_code_rules_enforcer_annotations.py +97 -0
- package/hooks/blocking/test_code_rules_enforcer_banned_identifier.py +106 -0
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +173 -0
- package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +328 -0
- package/hooks/blocking/test_code_rules_enforcer_config_path.py +0 -20
- package/hooks/blocking/test_code_rules_enforcer_constant_equality.py +33 -11
- package/hooks/blocking/test_code_rules_enforcer_existence_checks.py +0 -18
- package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +291 -0
- package/hooks/blocking/test_code_rules_enforcer_inline_literal_collections.py +155 -0
- package/hooks/blocking/test_code_rules_enforcer_loop_variable_naming.py +194 -0
- package/hooks/blocking/test_code_rules_enforcer_naming_pattern.py +49 -13
- package/hooks/blocking/test_code_rules_enforcer_skip_decorators.py +0 -26
- package/hooks/blocking/test_code_rules_enforcer_string_magic.py +234 -0
- package/hooks/blocking/test_code_rules_enforcer_sys_path_insert.py +157 -0
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +244 -0
- package/hooks/blocking/test_es_exe_path_rewriter.py +81 -3
- package/hooks/blocking/test_windows_rmtree_blocker.py +120 -8
- package/hooks/blocking/windows_rmtree_blocker.py +23 -6
- package/hooks/config/banned_identifiers_constants.py +24 -0
- package/hooks/config/hardcoded_user_path_constants.py +12 -0
- package/hooks/config/hook_log_extractor_constants.py +1 -1
- package/hooks/config/pre_tool_use_stdin.py +48 -0
- package/hooks/config/setup_project_paths_constants.py +4 -0
- package/hooks/config/stuttering_check_config.py +14 -0
- package/hooks/config/stuttering_import_binding_constants.py +11 -0
- package/hooks/config/sys_path_insert_constants.py +4 -0
- package/hooks/config/test_banned_identifiers_constants.py +48 -0
- package/hooks/config/test_hardcoded_user_path_constants.py +78 -0
- package/hooks/config/test_hook_log_extractor_constants.py +3 -3
- package/hooks/config/test_pre_tool_use_stdin.py +80 -0
- package/hooks/config/unused_module_import_constants.py +7 -0
- package/hooks/config/windows_rmtree_blocker_constants.py +3 -0
- package/hooks/diagnostic/hook_log_stop_wrapper.py +7 -4
- package/hooks/git-hooks/config.py +3 -3
- package/hooks/git-hooks/test_gate_utils.py +10 -10
- package/hooks/mypy.ini +2 -0
- package/package.json +1 -1
- package/rules/gh-paginate.md +125 -0
- package/skills/bugteam/CONSTRAINTS.md +12 -6
- package/skills/bugteam/PROMPTS.md +0 -39
- package/skills/bugteam/SKILL.md +93 -125
- package/skills/bugteam/SKILL_EVALS.md +25 -23
- package/skills/bugteam/reference/README.md +2 -0
- package/skills/bugteam/reference/audit-and-teammates.md +2 -2
- package/skills/bugteam/reference/copilot-gap-analysis.md +12 -0
- package/skills/bugteam/reference/teardown-publish-permissions.md +1 -1
- package/skills/bugteam/reference/workflow-path-a-orchestrated-teams.md +113 -0
- package/skills/bugteam/reference/workflow-path-b-task-harness.md +48 -0
- package/skills/bugteam/test_skill_additions.py +13 -4
- package/skills/bugteam/test_team_lifecycle.py +94 -0
- package/skills/findbugs/SKILL.md +3 -3
- package/skills/fixbugs/SKILL.md +4 -4
- package/skills/monitor-open-prs/SKILL.md +32 -2
- package/skills/monitor-open-prs/test_team_lifecycle.py +46 -0
- package/skills/pr-converge/SKILL.md +576 -95
- package/skills/pr-converge/scripts/README.md +145 -0
- package/skills/pr-converge/scripts/caller-window-pid.ps1 +86 -0
- package/skills/pr-converge/scripts/check_pr_mergeability.py +79 -0
- package/skills/pr-converge/scripts/config/pr_converge_constants.py +65 -0
- package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +176 -0
- package/skills/pr-converge/scripts/cursor-agents-continue-caller.cmd +9 -0
- package/skills/pr-converge/scripts/cursor-agents-continue-stop-others.ps1 +16 -0
- package/skills/pr-converge/scripts/cursor-agents-continue.ahk +172 -0
- package/skills/pr-converge/scripts/cursor-agents-continue.cmd +2 -0
- package/skills/pr-converge/scripts/evict_cached_config_modules.py +20 -0
- package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +110 -0
- package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +103 -0
- package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +112 -0
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +121 -0
- package/skills/pr-converge/scripts/mark_pr_ready.py +54 -0
- package/skills/pr-converge/scripts/open_followup_copilot_pr.py +136 -0
- package/skills/pr-converge/scripts/post-bugbot-run.helpers.ps1 +49 -0
- package/skills/pr-converge/scripts/post-bugbot-run.ps1 +33 -0
- package/skills/pr-converge/scripts/reply_to_inline_comment.py +84 -0
- package/skills/pr-converge/scripts/request_copilot_review.py +71 -0
- package/skills/pr-converge/scripts/resolve_pr_head.py +58 -0
- package/skills/pr-converge/scripts/review_field_helpers.py +43 -0
- package/skills/pr-converge/scripts/test_check_pr_mergeability.py +126 -0
- package/skills/pr-converge/scripts/test_evict_cached_config_modules.py +22 -0
- package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +342 -0
- package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +220 -0
- package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +372 -0
- package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +280 -0
- package/skills/pr-converge/scripts/test_mark_pr_ready.py +69 -0
- package/skills/pr-converge/scripts/test_open_followup_copilot_pr.py +236 -0
- package/skills/pr-converge/scripts/test_post_bugbot_run.py +195 -0
- package/skills/pr-converge/scripts/test_reply_to_inline_comment.py +159 -0
- package/skills/pr-converge/scripts/test_request_copilot_review.py +101 -0
- package/skills/pr-converge/scripts/test_resolve_pr_head.py +79 -0
- package/skills/pr-converge/scripts/test_review_field_helpers.py +80 -0
- package/skills/pr-converge/scripts/test_trigger_bugbot.py +139 -0
- package/skills/pr-converge/scripts/test_view_pr_context.py +111 -0
- package/skills/pr-converge/scripts/trigger_bugbot.py +77 -0
- package/skills/pr-converge/scripts/view_pr_context.py +47 -0
- package/skills/pr-converge/test_team_lifecycle.py +47 -0
- package/skills/pr-converge/workflows/ahk-auto-continue-loop.md +108 -0
- package/skills/pr-converge/workflows/schedule-wakeup-loop.md +37 -0
- package/skills/qbug/SKILL.md +4 -4
- package/skills/qbug/test_qbug_skill_post_fix_audit.py +2 -2
- package/skills/resume-review/SKILL.md +261 -0
- package/agents/agent-writer.md +0 -157
- package/agents/config-centralizer.md +0 -686
- package/agents/config-extraction-agent.md +0 -225
- package/agents/doc-orchestrator.md +0 -47
- package/agents/docx-agent.md +0 -211
- package/agents/magic-value-eliminator-agent.md +0 -72
- package/agents/mandatory-agent-workflow-agent.md +0 -88
- package/agents/parallel-workflow-coordinator.md +0 -779
- package/agents/pdf-agent.md +0 -302
- package/agents/project-context-loader.md +0 -238
- package/agents/readability-review-agent.md +0 -76
- package/agents/refactoring-specialist.md +0 -69
- package/agents/right-sized-engineer.md +0 -129
- package/agents/session-continuity-manager.md +0 -53
- package/agents/stub-detector-agent.md +0 -140
- package/agents/tdd-test-writer.md +0 -62
- package/agents/test-data-builder.md +0 -68
- package/agents/tooling-builder.md +0 -78
- package/agents/validation-expert.md +0 -71
- package/agents/xlsx-agent.md +0 -169
- package/skills/bugteam/scripts/README.md +0 -58
- package/skills/bugteam/scripts/_claude_permissions_common.py +0 -219
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +0 -633
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +0 -260
- package/skills/bugteam/scripts/bugteam_preflight.py +0 -201
- package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +0 -17
- package/skills/bugteam/scripts/grant_project_claude_permissions.py +0 -109
- package/skills/bugteam/scripts/revoke_project_claude_permissions.py +0 -135
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +0 -271
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -267
- package/skills/bugteam/scripts/test_bugteam_preflight.py +0 -189
- package/skills/bugteam/scripts/test_claude_permissions_common.py +0 -44
- /package/skills/{bugteam → pr-converge}/scripts/config/__init__.py +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Bugteam — Path A workflow (orchestrated teams)
|
|
2
|
+
|
|
3
|
+
Load when bugteam `SKILL.md` **Path routing** selects **Path A** (`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` equals **`1`** after trim). Execute **after** shared `SKILL.md` steps through **Step 2 loop state**; then use this file for **harness-only** steps. Shared gate scripts, Step 3 cycle numbering, Step 2.5 `jq`/`gh` payload shapes, `PROMPTS.md`, and outcome XML rules remain in **`SKILL.md`**.
|
|
4
|
+
|
|
5
|
+
## Step 2 harness — lifecycle, `TeamCreate`, and team metadata
|
|
6
|
+
|
|
7
|
+
**This session is the lead.** Step 2 first resolves the team lifecycle from the [Team lifecycle](../SKILL.md#team-lifecycle-path-a-only) table in `SKILL.md`, then either creates or attaches to a team.
|
|
8
|
+
|
|
9
|
+
**Lifecycle resolution (read once, then apply):**
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
|
|
15
|
+
mode = os.environ.get("BUGTEAM_TEAM_LIFECYCLE", "auto").strip().lower() or "auto"
|
|
16
|
+
attached_team_name = os.environ.get("BUGTEAM_TEAM_NAME", "").strip()
|
|
17
|
+
|
|
18
|
+
if mode == "attach" and not attached_team_name:
|
|
19
|
+
raise RuntimeError(
|
|
20
|
+
"BUGTEAM_TEAM_LIFECYCLE=attach requires BUGTEAM_TEAM_NAME to name an existing team."
|
|
21
|
+
)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The mode then drives Step 2's `TeamCreate` decision:
|
|
25
|
+
|
|
26
|
+
- **`mode == "attach"`** — skip `TeamCreate` entirely. Set `team_name = attached_team_name`, `team_owned = false`. Continue to teammate spawns using that `team_name`.
|
|
27
|
+
|
|
28
|
+
- **`mode == "owned"`** — call `TeamCreate(team_name=<computed_team_name>, ...)` (form below). On the runtime error matching `Already leading team "(.+)"\.` → **fail** with `Already leading team <existing>; rerun with BUGTEAM_TEAM_LIFECYCLE=attach BUGTEAM_TEAM_NAME=<existing>`. On success, set `team_owned = true`.
|
|
29
|
+
|
|
30
|
+
- **`mode == "auto"`** (default) — call `TeamCreate(team_name=<computed_team_name>, ...)`. On the same runtime error, parse the existing team name with the regex `r'Already leading team "([^"]+)"'`, set `team_name = <existing>`, `team_owned = false`, and continue without retrying `TeamCreate`. On success, set `team_owned = true`. Both branches honor §Team name below.
|
|
31
|
+
|
|
32
|
+
**`TeamCreate` call shape (only when `mode != "attach"` and the auto branch did not already attach):**
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
TeamCreate(
|
|
36
|
+
team_name="<computed_team_name>",
|
|
37
|
+
description="Bugteam audit/fix loop for PR <number> (<owner>/<repo>)",
|
|
38
|
+
agent_type="team-lead"
|
|
39
|
+
)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**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).
|
|
43
|
+
|
|
44
|
+
**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.
|
|
45
|
+
|
|
46
|
+
**`<team_temp_dir>`:** `Path(tempfile.gettempdir()) / team_name` (lead resolves once to an absolute path; every shell gets that literal string).
|
|
47
|
+
|
|
48
|
+
**Roles (spawned per loop, not here):** bugfind → `code-quality-agent` opus (4.7) at xhigh effort; bugfix → `clean-coder` opus (4.7) at xhigh effort. `model="opus"` resolves to Opus 4.7 on the Anthropic API and runs at the model's default `xhigh` effort level — see [`CONSTRAINTS.md`](../CONSTRAINTS.md) § **Opus 4.7 at xhigh effort for both teammates** for rationale. **Display:** inherit `teammateMode` from `~/.claude.json`. Reference subagent types by name when spawning teammates ([`sources.md`](../sources.md) § Referencing subagent types when spawning teammates).
|
|
49
|
+
|
|
50
|
+
**Optional Groq-backed FIX path (explicit opt-in only):** when the user explicitly sets `BUGTEAM_FIX_IMPLEMENTER=groq-coder` before invocation, spawn the FIX teammate with `subagent_type="groq-coder"`. Before Step 3, `groq_bugteam.py` loads `packages/claude-dev-env/.env` when that file exists (gitignored; start from `packages/claude-dev-env/.env.example`). If `GROQ_API_KEY` is still unset after that load, stop and prompt the user to create `packages/claude-dev-env/.env` from the example path above—do not continue the Groq path without a key. Any other `BUGTEAM_FIX_IMPLEMENTER` value (or unset) keeps `clean-coder` on Opus. The FIX spawn XML in [`PROMPTS.md`](../PROMPTS.md) is identical for both implementers.
|
|
51
|
+
|
|
52
|
+
**`--bugbot-retrigger` flag:** when present on the `/bugteam` invocation, after every successful FIX push in Step 3, post an additional `bugbot run` issue comment via the Step 2.5 issue-comments fallback endpoint (`POST .../issues/{issue}/comments`) to re-trigger Cursor's bugbot on the new commit. Omit when the flag is absent.
|
|
53
|
+
|
|
54
|
+
## Step 2.5 — who posts (Path A)
|
|
55
|
+
|
|
56
|
+
**Bugfind** posts one `POST .../pulls/<n>/reviews` per loop after audit. **Bugfix** posts `.../comments/<id>/replies` after push. The **lead’s** only PR write before Step 4.5 is unchanged from `SKILL.md` (Step 4.5 body edit).
|
|
57
|
+
|
|
58
|
+
## AUDIT spawn (Path A)
|
|
59
|
+
|
|
60
|
+
After shared setup in `SKILL.md` (`mkdir`, `gh pr diff`):
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
Agent(
|
|
64
|
+
subagent_type="code-quality-agent",
|
|
65
|
+
name="bugfind-pr<N>-loop<L>",
|
|
66
|
+
team_name="<team_name>",
|
|
67
|
+
model="opus",
|
|
68
|
+
description="Bugfind audit PR <N> loop <L>",
|
|
69
|
+
prompt="<audit XML; see PROMPTS.md>"
|
|
70
|
+
)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
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` per `SKILL.md`.
|
|
74
|
+
|
|
75
|
+
**Shutdown:** If `Agent` returned and the teammate already ended, skip. Otherwise:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
SendMessage(
|
|
79
|
+
to="bugfind-pr<N>-loop<L>",
|
|
80
|
+
message={"type": "shutdown_request", "reason": "audit PR <N> loop <L> complete; outcome XML captured"}
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`approve: false` → `error: bugfind teammate refused shutdown` → Step 4 then 5 per `SKILL.md`.
|
|
85
|
+
|
|
86
|
+
**Parallel auditors (`loop_count >= 4`):** after three full audit/fix rounds without convergence, issue three `Agent` calls in one assistant message with `team_name="<team_name>"` per `SKILL.md` § parallel variant naming (`-a` / `-b` / `-c`). Shutdown: parallel `SendMessage` to `b` and `c`, then `a`.
|
|
87
|
+
|
|
88
|
+
## FIX spawn (Path A)
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
Agent(
|
|
92
|
+
subagent_type="clean-coder",
|
|
93
|
+
name="bugfix-pr<N>-loop<L>",
|
|
94
|
+
team_name="<team_name>",
|
|
95
|
+
model="opus",
|
|
96
|
+
description="Bugfix PR <N> loop <L>",
|
|
97
|
+
prompt="<fix XML; see PROMPTS.md>"
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Shutdown:** same pattern as AUDIT; `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.
|
|
102
|
+
|
|
103
|
+
Verify and outcome handling: unchanged from `SKILL.md` § FIX action (**Verify**, stuck message, [`PROMPTS.md`](../PROMPTS.md)).
|
|
104
|
+
|
|
105
|
+
## Step 4 harness — after worktree remove, before shared `rmtree`
|
|
106
|
+
|
|
107
|
+
Run **after** `SKILL.md` § Step 4 step 1 (`git worktree remove` for each PR).
|
|
108
|
+
|
|
109
|
+
1. For each live teammate **spawned by this invocation**: `SendMessage(to="<name>", message={"type": "shutdown_request", "reason": "bugteam cycle ending"})`. `approve: false` on cleanup → log and continue. Teammates that pre-existed an attached team (mode `attach` or `auto` after the attach branch) are owned by the orchestrator, not this invocation — leave them alone.
|
|
110
|
+
|
|
111
|
+
2. `TeamDelete()` — **only when `team_owned=true`** (set in Step 2 lifecycle resolution). When `team_owned=false`, **skip `TeamDelete`** and log `cleanup: skipped TeamDelete (team not owned by this invocation; lifecycle=<mode>)`. The orchestrator that originally created the team owns teardown — see [Team lifecycle](../SKILL.md#team-lifecycle-path-a-only).
|
|
112
|
+
|
|
113
|
+
Then continue with `SKILL.md` § Step 4 step 3 (`<team_temp_dir>` cleanup, also gated on `team_owned`).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Bugteam — Path B workflow (Task harness)
|
|
2
|
+
|
|
3
|
+
Load when bugteam `SKILL.md` **Path routing** selects **Path B** (`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` is not exactly **`1`** after trim — typical Cursor IDE). Execute **after** shared `SKILL.md` steps through **Step 2 loop state**. **Do not** run `TeamCreate` or `TeamDelete`. Harness substitutions vs Path A:
|
|
4
|
+
|
|
5
|
+
| Path A | Path B |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `TeamCreate(...)` | **Omit.** |
|
|
8
|
+
| `Agent(..., team_name=..., ...)` | **`Task`** (or host-equivalent) with the same `model` and prompt contracts as Path A — **omit** `team_name`. `subagent_type` follows this file's **AUDIT** / **FIX spawn** sections (Claude Code: `code-quality-agent` / `clean-coder`; Cursor: `code-quality-agent` / `generalPurpose` + mandatory `clean-coder.md` **Read** on FIX when the enum rejects `clean-coder`). |
|
|
9
|
+
| `SendMessage` shutdown | **Omit.** Await Task completion. |
|
|
10
|
+
| `TeamDelete()` | **Omit.** |
|
|
11
|
+
| Three parallel `Agent` (`loop_count >= 4`) | Three parallel **`Task`** with `subagent_type="code-quality-agent"`; merge outcomes in the lead like Path A `-a`/`-b`/`-c`. |
|
|
12
|
+
|
|
13
|
+
## Step 2 harness — Path B
|
|
14
|
+
|
|
15
|
+
No `TeamCreate`. After shared `SKILL.md` **Step 1** completes (PR scope **and** `<team_temp_dir>/pr-<N>/` per Step 1 items 1–3 there), use the same `team_name` string only as a **logical label** for paths under that `<team_temp_dir>`; do not pass `team_name` into spawns.
|
|
16
|
+
|
|
17
|
+
**`--bugbot-retrigger` flag:** same as Path A [`workflow-path-a-orchestrated-teams.md`](workflow-path-a-orchestrated-teams.md) § Step 2 harness — the **lead** posts the issue comment after each successful FIX push when the flag is present.
|
|
18
|
+
|
|
19
|
+
## Step 2.5 — who posts (Path B)
|
|
20
|
+
|
|
21
|
+
The **lead** runs the **same** `gh api` / `jq` sequences as `SKILL.md` **Step 2.5** after reading each **`Task`** handoff / outcome XML — same JSON shapes and anchor rules; only **who executes the shell** changes.
|
|
22
|
+
|
|
23
|
+
## AUDIT spawn (Path B)
|
|
24
|
+
|
|
25
|
+
After shared setup in `SKILL.md` (`mkdir`, `gh pr diff`):
|
|
26
|
+
|
|
27
|
+
`Task` with `subagent_type="code-quality-agent"`, **omit** `team_name`, same `model`, `description`, and `prompt="<audit XML; see PROMPTS.md>"` as Path A [`workflow-path-a-orchestrated-teams.md`](workflow-path-a-orchestrated-teams.md) § AUDIT spawn.
|
|
28
|
+
|
|
29
|
+
Fresh **Task** each loop (clean-room intent: do not reuse prior Task transcript as audit input). Lead reads `.bugteam-pr<N>-loop<L>.outcomes.xml`, fills `loop_comment_index` per `SKILL.md`.
|
|
30
|
+
|
|
31
|
+
**Parallel auditors (`loop_count >= 4`):** three **`Task`** calls with `subagent_type="code-quality-agent"` in one assistant message; merge outcomes in the lead exactly as Path A documents for variants `-a`/`-b`/`-c`. Await all three Tasks — no `SendMessage`.
|
|
32
|
+
|
|
33
|
+
## FIX spawn (Path B)
|
|
34
|
+
|
|
35
|
+
**Hosts that accept `clean-coder` as a `Task` subtype (typical Claude Code):** `Task` with `subagent_type="clean-coder"` (or `subagent_type="groq-coder"` when `BUGTEAM_FIX_IMPLEMENTER=groq-coder` per Path A optional Groq rules), **omit** `team_name`, same fields otherwise as Path A [`workflow-path-a-orchestrated-teams.md`](workflow-path-a-orchestrated-teams.md) § FIX spawn. Await Task completion — no `SendMessage`.
|
|
36
|
+
|
|
37
|
+
**Cursor and other hosts with a fixed `Task` enum (no `clean-coder` value):** use `Task` with `subagent_type: "generalPurpose"` and put the **same** FIX obligations from Path A into the **`prompt`**, after a mandatory first step to **Read** the clean-coder agent markdown: macOS/Linux `$HOME/.claude/agents/clean-coder.md`, Windows `%USERPROFILE%\.claude\agents\clean-coder.md`. State that file is binding for naming, TDD when behavior changes, hooks, one commit, and scope. Do **not** use a bare `generalPurpose` prompt without that Read. Same bundle as [`../../pr-converge/SKILL.md`](../../pr-converge/SKILL.md) Fix protocol **Implement**. If `Task` cannot run, stop and notify the user.
|
|
38
|
+
|
|
39
|
+
Verify and outcome handling: unchanged from `SKILL.md` § FIX action.
|
|
40
|
+
## Step 4 harness — after worktree remove, before shared `rmtree`
|
|
41
|
+
|
|
42
|
+
Run **after** `SKILL.md` § Step 4 step 1 (`git worktree remove` for each PR).
|
|
43
|
+
|
|
44
|
+
**Omit** teammate `SendMessage` rounds and **`TeamDelete()`**. Then continue with `SKILL.md` § Step 4 step 3 (shared `rmtree` on `<team_temp_dir>`).
|
|
45
|
+
|
|
46
|
+
## Clean-room note
|
|
47
|
+
|
|
48
|
+
Path B approximates Path A isolation by spawning a **new** Task per AUDIT (and per FIX) with the same prompt contract as Path A, without reusing prior Task context as audit input.
|
|
@@ -10,9 +10,16 @@ def _read_skill_text() -> str:
|
|
|
10
10
|
return skill_path.read_text(encoding="utf-8")
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def _read_path_a_workflow_text() -> str:
|
|
14
|
+
workflow_path = (
|
|
15
|
+
pathlib.Path(__file__).parent / "reference" / "workflow-path-a-orchestrated-teams.md"
|
|
16
|
+
)
|
|
17
|
+
return workflow_path.read_text(encoding="utf-8")
|
|
18
|
+
|
|
19
|
+
|
|
13
20
|
def test_skill_references_fix_implementer_env_var():
|
|
14
|
-
|
|
15
|
-
assert "BUGTEAM_FIX_IMPLEMENTER" in
|
|
21
|
+
workflow_text = _read_path_a_workflow_text()
|
|
22
|
+
assert "BUGTEAM_FIX_IMPLEMENTER" in workflow_text
|
|
16
23
|
|
|
17
24
|
|
|
18
25
|
def test_skill_names_default_implementer_subagent_type():
|
|
@@ -21,10 +28,12 @@ def test_skill_names_default_implementer_subagent_type():
|
|
|
21
28
|
|
|
22
29
|
|
|
23
30
|
def test_skill_names_optional_groq_implementer_subagent_type():
|
|
24
|
-
|
|
25
|
-
assert "groq-coder" in
|
|
31
|
+
workflow_text = _read_path_a_workflow_text()
|
|
32
|
+
assert "groq-coder" in workflow_text
|
|
26
33
|
|
|
27
34
|
|
|
28
35
|
def test_skill_documents_bugbot_retrigger_flag():
|
|
29
36
|
skill_text = _read_skill_text()
|
|
37
|
+
workflow_text = _read_path_a_workflow_text()
|
|
30
38
|
assert "--bugbot-retrigger" in skill_text
|
|
39
|
+
assert "--bugbot-retrigger" in workflow_text
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Markdown assertion tests for bugteam team-lifecycle decoupling.
|
|
2
|
+
|
|
3
|
+
Locks in the contract that the bugteam skill must:
|
|
4
|
+
- support three team lifecycle modes (`owned`, `attach`, `auto`)
|
|
5
|
+
- default to `auto` (back-compat for solo invocations, safe for nested ones)
|
|
6
|
+
- skip `TeamDelete` when the invocation did not create the team
|
|
7
|
+
- parse the runtime's `Already leading team "<name>"` error and attach
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import pathlib
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _read(relative_path: str) -> str:
|
|
16
|
+
here = pathlib.Path(__file__).parent
|
|
17
|
+
return (here / relative_path).read_text(encoding="utf-8")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _skill_text() -> str:
|
|
21
|
+
return _read("SKILL.md")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _path_a_text() -> str:
|
|
25
|
+
return _read("reference/workflow-path-a-orchestrated-teams.md")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _constraints_text() -> str:
|
|
29
|
+
return _read("CONSTRAINTS.md")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_skill_documents_three_team_lifecycle_modes():
|
|
33
|
+
skill_text = _skill_text()
|
|
34
|
+
assert "BUGTEAM_TEAM_LIFECYCLE" in skill_text
|
|
35
|
+
assert "owned" in skill_text
|
|
36
|
+
assert "attach" in skill_text
|
|
37
|
+
assert "auto" in skill_text
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_skill_documents_auto_as_default_lifecycle():
|
|
41
|
+
skill_text = _skill_text()
|
|
42
|
+
assert "default" in skill_text.lower()
|
|
43
|
+
assert "BUGTEAM_TEAM_LIFECYCLE" in skill_text
|
|
44
|
+
auto_default_phrases = [
|
|
45
|
+
"default: `auto`",
|
|
46
|
+
"default `auto`",
|
|
47
|
+
"defaults to `auto`",
|
|
48
|
+
"defaults to auto",
|
|
49
|
+
"default to `auto`",
|
|
50
|
+
]
|
|
51
|
+
assert any(phrase in skill_text for phrase in auto_default_phrases)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_skill_documents_BUGTEAM_TEAM_NAME_env_for_attach_mode():
|
|
55
|
+
skill_text = _skill_text()
|
|
56
|
+
assert "BUGTEAM_TEAM_NAME" in skill_text
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_path_a_workflow_handles_already_leading_team_error():
|
|
60
|
+
workflow_text = _path_a_text()
|
|
61
|
+
assert 'Already leading team "' in workflow_text
|
|
62
|
+
assert "team_owned" in workflow_text
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_path_a_workflow_step_4_skips_team_delete_when_not_owned():
|
|
66
|
+
workflow_text = _path_a_text()
|
|
67
|
+
assert "TeamDelete" in workflow_text
|
|
68
|
+
assert "team_owned" in workflow_text
|
|
69
|
+
skip_phrases = [
|
|
70
|
+
"skip `TeamDelete`",
|
|
71
|
+
"skip TeamDelete",
|
|
72
|
+
"omit `TeamDelete`",
|
|
73
|
+
"omit TeamDelete",
|
|
74
|
+
"do not call `TeamDelete`",
|
|
75
|
+
"do not call TeamDelete",
|
|
76
|
+
]
|
|
77
|
+
assert any(phrase in workflow_text for phrase in skip_phrases)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_path_a_workflow_documents_attach_mode_reuses_team_name():
|
|
81
|
+
workflow_text = _path_a_text()
|
|
82
|
+
assert "BUGTEAM_TEAM_NAME" in workflow_text
|
|
83
|
+
assert "attach" in workflow_text
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_constraints_lead_only_cleanup_includes_team_owned():
|
|
87
|
+
constraints_text = _constraints_text()
|
|
88
|
+
assert "team_owned" in constraints_text
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_constraints_warn_against_owned_mode_inside_orchestrator():
|
|
92
|
+
constraints_text = _constraints_text()
|
|
93
|
+
assert "orchestrator" in constraints_text.lower()
|
|
94
|
+
assert "attach" in constraints_text
|
package/skills/findbugs/SKILL.md
CHANGED
|
@@ -121,7 +121,7 @@ When the agent returns, report concisely:
|
|
|
121
121
|
- The cleared categories so the user can see coverage breadth
|
|
122
122
|
- Any open questions the agent could not resolve from the diff alone
|
|
123
123
|
|
|
124
|
-
Offer the next step without auto-executing it: `Want me to
|
|
124
|
+
Offer the next step without auto-executing it: `Want me to run /fixbugs for the P0/P1 findings?`
|
|
125
125
|
|
|
126
126
|
Delete the scoped temp diff at `<diff_temp_path>` after the audit completes (or moves to a fix flow). Temporary diff files do not belong in the working tree.
|
|
127
127
|
|
|
@@ -143,7 +143,7 @@ Verified clean: <category>, <category>, <category>
|
|
|
143
143
|
Open questions:
|
|
144
144
|
<if any>
|
|
145
145
|
|
|
146
|
-
Want me to
|
|
146
|
+
Want me to run /fixbugs for the P0/P1 findings?
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
## Constraints
|
|
@@ -177,7 +177,7 @@ Claude: [resolves PR #42 from current branch, fetches full diff, spawns code-qua
|
|
|
177
177
|
|
|
178
178
|
`Open questions: none`
|
|
179
179
|
|
|
180
|
-
`Want me to
|
|
180
|
+
`Want me to run /fixbugs for the P0 + P1s?`
|
|
181
181
|
</example>
|
|
182
182
|
|
|
183
183
|
<example>
|
package/skills/fixbugs/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: fixbugs
|
|
|
3
3
|
description: >-
|
|
4
4
|
Fixes the bugs surfaced by the most recent /findbugs invocation by handing
|
|
5
5
|
the findings to /agent-prompt, which authors a structured XML prompt and
|
|
6
|
-
spawns a background sonnet
|
|
6
|
+
spawns a background sonnet implementer (via /agent-prompt) to apply every fix in one
|
|
7
7
|
commit on the existing branch. Default scope: all severities. Optional
|
|
8
8
|
argument filters by severity (e.g. /fixbugs P0, /fixbugs P0+P1).
|
|
9
9
|
Triggers: '/fixbugs', 'fix all the bugs', 'apply the audit fixes',
|
|
@@ -66,8 +66,8 @@ Fix the following bugs surfaced by /findbugs on
|
|
|
66
66
|
[for each filtered finding, one bullet:]
|
|
67
67
|
- [<severity>] <file:line> (<category>): <description>
|
|
68
68
|
|
|
69
|
-
Deploy a
|
|
70
|
-
in one commit on the existing branch and push. Constraints:
|
|
69
|
+
Deploy a background implementer (model: sonnet) to implement all fixes
|
|
70
|
+
in one commit on the existing branch and push. The implementer must **Read** the clean-coder agent file first (`~/.claude/agents/clean-coder.md`; Windows `%USERPROFILE%\.claude\agents\clean-coder.md`) and treat it as binding; on Cursor use `Task` + `generalPurpose` with that Read in the prompt when `clean-coder` is not a valid `Task` subtype. Constraints:
|
|
71
71
|
- Modify only the files referenced in the bug list above.
|
|
72
72
|
- Do NOT change the PR base, do NOT rebase, do NOT amend, do NOT --force.
|
|
73
73
|
- Do NOT skip git hooks (no --no-verify, no --no-gpg-sign).
|
|
@@ -106,7 +106,7 @@ When `/fixbugs` short-circuits (no findings, no PR, empty filter, zero bugs), th
|
|
|
106
106
|
<example>
|
|
107
107
|
User: `/findbugs` → returns `1 P0 / 2 P1 / 0 P2`
|
|
108
108
|
User: `/fixbugs`
|
|
109
|
-
Claude: [recovers all 3 findings, resolves PR scope, invokes /agent-prompt with a goal targeting all 3 bugs; /agent-prompt presents the XML + Outcome digest + AskUserQuestion; on Launch it, the background sonnet
|
|
109
|
+
Claude: [recovers all 3 findings, resolves PR scope, invokes /agent-prompt with a goal targeting all 3 bugs; /agent-prompt presents the XML + Outcome digest + AskUserQuestion; on Launch it, the background sonnet implementer spawns]
|
|
110
110
|
</example>
|
|
111
111
|
|
|
112
112
|
<example>
|
|
@@ -19,6 +19,7 @@ description: >-
|
|
|
19
19
|
|
|
20
20
|
- When this skill applies — refusal cases and trigger conditions
|
|
21
21
|
- Discovery — live `gh search prs` across both owner scopes
|
|
22
|
+
- Team lifecycle — single long-lived team for the whole sweep
|
|
22
23
|
- Wrapping — `bws run` for GROQ_API_KEY injection
|
|
23
24
|
- Dispatch — `/bugteam <numbers...>` with groq-coder + retrigger
|
|
24
25
|
- Post-convergence polling — bugbot replies and re-invocation
|
|
@@ -40,6 +41,35 @@ Refusals — first match wins; respond with the quoted line exactly and stop:
|
|
|
40
41
|
|
|
41
42
|
Call `scripts/discover_open_prs.discover_open_prs(all_owners=["jl-cmd", "JonEcho"])` to merge the live open-PR list across both scopes. The helper runs `gh search prs --owner <owner> --state open --json number,repository,url,headRefName,baseRefName` once per owner and flattens the result to a uniform dict shape with keys `number`, `owner`, `repo`, `head_ref`, `base_ref`, `url`. Empty scopes contribute empty lists; an entirely empty sweep returns `[]` and exits cleanly.
|
|
42
43
|
|
|
44
|
+
## Team Lifecycle
|
|
45
|
+
|
|
46
|
+
`/monitor-open-prs` dispatches one `/bugteam` per discovered PR — often more than ten in a sweep. Per-invocation `TeamCreate` / `TeamDelete` from each `/bugteam` would either collide on the runtime's `Already leading team` rule or tear down the wrong team mid-sweep, depending on dispatch order. The sweep instead owns a **single long-lived team for the whole sweep** and passes that team to every `/bugteam` invocation in attach mode. See [bugteam Team lifecycle](../bugteam/SKILL.md#team-lifecycle-path-a-only).
|
|
47
|
+
|
|
48
|
+
**Before the first dispatch:**
|
|
49
|
+
|
|
50
|
+
1. Capture `sweep_id = <YYYYMMDDHHMMSS>` once at sweep start. This is the same convention as bugteam's team-name timestamp.
|
|
51
|
+
2. Compute `team_name = "bugteam-monitor-<sweep_id>"`.
|
|
52
|
+
3. `TeamCreate(team_name=<team_name>, description="monitor-open-prs sweep <sweep_id>", agent_type="team-lead")`. The orchestrator (this session) becomes the lead.
|
|
53
|
+
|
|
54
|
+
**For every `/bugteam` dispatch** (parallel fan-out or serial), prepend the two attach-mode env vars to the wrapped command so bugteam reuses the orchestrator's team and skips its own `TeamCreate` / `TeamDelete`:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bws run \
|
|
58
|
+
--project-id c69cedc5-aea1-4aa8-b350-b4300145d978 \
|
|
59
|
+
-- \
|
|
60
|
+
env BUGTEAM_TEAM_LIFECYCLE=attach \
|
|
61
|
+
BUGTEAM_TEAM_NAME=<team_name> \
|
|
62
|
+
BUGTEAM_FIX_IMPLEMENTER=groq-coder \
|
|
63
|
+
/bugteam --bugbot-retrigger <pr_numbers...>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Teardown — only after every PR has exited polling** (see §Post-Convergence Polling): the sweep is done; no further `/bugteam` invocation will run. At that point, and only at that point:
|
|
67
|
+
|
|
68
|
+
1. `TeamDelete()` (orchestrator is the lead; no arguments).
|
|
69
|
+
2. Print one cleanup line into the §Final Report: `team teardown: TeamDelete <team_name>` (success) or the error message on failure.
|
|
70
|
+
|
|
71
|
+
If a hard blocker or user stop ends the sweep early, the orchestrator is shutting down: still call `TeamDelete()` once after polling stops for the in-flight PRs.
|
|
72
|
+
|
|
43
73
|
## Secret Wrapping
|
|
44
74
|
|
|
45
75
|
Every `/bugteam` dispatch runs inside `bws run` so `GROQ_API_KEY` is injected from Bitwarden Secrets Manager without touching the filesystem. The project and secret UUIDs are fixed for this skill:
|
|
@@ -59,9 +89,9 @@ The `bws run` subshell resolves the project's secrets and exports them for the w
|
|
|
59
89
|
For each discovered PR:
|
|
60
90
|
|
|
61
91
|
1. Resolve the PR's repo checkout (existing worktree or fresh `git clone`).
|
|
62
|
-
2. From that checkout, invoke `/bugteam <pr_number>` under the `bws run` wrapper
|
|
92
|
+
2. From that checkout, invoke `/bugteam <pr_number>` under the `bws run` wrapper from §Team Lifecycle (the wrapper already carries `BUGTEAM_TEAM_LIFECYCLE=attach` and `BUGTEAM_TEAM_NAME=<team_name>` so bugteam reuses the sweep-owned team).
|
|
63
93
|
3. The `BUGTEAM_FIX_IMPLEMENTER=groq-coder` env var routes the FIX role to the `groq-coder` subagent. The `--bugbot-retrigger` flag tells bugteam to post `bugbot run` as an issue comment after every successful FIX push so Cursor's bugbot re-evaluates the new commit.
|
|
64
|
-
4. Bugteam runs its own 10-loop audit/fix cycle per PR; this skill waits for each bugteam invocation to return before dispatching the next (or fanning out — see below).
|
|
94
|
+
4. Bugteam runs its own 10-loop audit/fix cycle per PR; this skill waits for each bugteam invocation to return before dispatching the next (or fanning out — see below). Each invocation skips its own `TeamCreate` / `TeamDelete` because of attach mode — only the sweep owns team teardown (§Team Lifecycle).
|
|
65
95
|
|
|
66
96
|
**Fan-out (optional):** when the discovered list has more than one PR, the skill may spawn `/bugteam` dispatches in parallel by issuing multiple `Agent` calls in a single assistant message. Each dispatch operates in its own per-PR worktree (bugteam Step 1.1). Serialize when the caller sets an explicit `--serial` flag.
|
|
67
97
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Markdown assertion tests for monitor-open-prs team lifecycle.
|
|
2
|
+
|
|
3
|
+
Locks in the contract that the sweep must:
|
|
4
|
+
- own a single long-lived team for every dispatched /bugteam
|
|
5
|
+
- pass attach mode + the team name into each per-PR /bugteam dispatch
|
|
6
|
+
- tear down only after all PR polling completes
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import pathlib
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _skill_text() -> str:
|
|
15
|
+
here = pathlib.Path(__file__).parent
|
|
16
|
+
return (here / "SKILL.md").read_text(encoding="utf-8")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_skill_creates_one_team_for_the_whole_sweep():
|
|
20
|
+
skill_text = _skill_text()
|
|
21
|
+
assert "TeamCreate" in skill_text
|
|
22
|
+
sweep_phrases = [
|
|
23
|
+
"one team for the whole sweep",
|
|
24
|
+
"single team for the sweep",
|
|
25
|
+
"single long-lived team",
|
|
26
|
+
]
|
|
27
|
+
assert any(phrase in skill_text for phrase in sweep_phrases)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_skill_passes_attach_lifecycle_to_each_bugteam_dispatch():
|
|
31
|
+
skill_text = _skill_text()
|
|
32
|
+
assert "BUGTEAM_TEAM_LIFECYCLE" in skill_text
|
|
33
|
+
assert "attach" in skill_text
|
|
34
|
+
assert "BUGTEAM_TEAM_NAME" in skill_text
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_skill_tears_down_team_after_polling_completes():
|
|
38
|
+
skill_text = _skill_text()
|
|
39
|
+
assert "TeamDelete" in skill_text
|
|
40
|
+
teardown_phrases = [
|
|
41
|
+
"after every PR has exited polling",
|
|
42
|
+
"after polling completes",
|
|
43
|
+
"after the sweep",
|
|
44
|
+
"after all polling",
|
|
45
|
+
]
|
|
46
|
+
assert any(phrase in skill_text for phrase in teardown_phrases)
|