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.
Files changed (148) hide show
  1. package/agents/clean-coder.md +109 -1
  2. package/agents/docs-agent.md +1 -1
  3. package/agents/project-docs-analyzer.md +0 -1
  4. package/agents/skill-to-agent-converter.md +0 -1
  5. package/bin/install.mjs +28 -8
  6. package/bin/install.test.mjs +9 -1
  7. package/commands/initialize.md +0 -1
  8. package/commands/readability-review.md +4 -4
  9. package/commands/review-plan.md +2 -4
  10. package/commands/stubcheck.md +1 -2
  11. package/docs/CODE_RULES.md +3 -0
  12. package/docs/agents-md-alignment-plan.md +123 -0
  13. package/hooks/blocking/code_rules_enforcer.py +686 -60
  14. package/hooks/blocking/es_exe_path_rewriter.py +10 -4
  15. package/hooks/blocking/test_code_rules_enforcer.py +273 -39
  16. package/hooks/blocking/test_code_rules_enforcer_annotations.py +97 -0
  17. package/hooks/blocking/test_code_rules_enforcer_banned_identifier.py +106 -0
  18. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +173 -0
  19. package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +328 -0
  20. package/hooks/blocking/test_code_rules_enforcer_config_path.py +0 -20
  21. package/hooks/blocking/test_code_rules_enforcer_constant_equality.py +33 -11
  22. package/hooks/blocking/test_code_rules_enforcer_existence_checks.py +0 -18
  23. package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +291 -0
  24. package/hooks/blocking/test_code_rules_enforcer_inline_literal_collections.py +155 -0
  25. package/hooks/blocking/test_code_rules_enforcer_loop_variable_naming.py +194 -0
  26. package/hooks/blocking/test_code_rules_enforcer_naming_pattern.py +49 -13
  27. package/hooks/blocking/test_code_rules_enforcer_skip_decorators.py +0 -26
  28. package/hooks/blocking/test_code_rules_enforcer_string_magic.py +234 -0
  29. package/hooks/blocking/test_code_rules_enforcer_sys_path_insert.py +157 -0
  30. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +244 -0
  31. package/hooks/blocking/test_es_exe_path_rewriter.py +81 -3
  32. package/hooks/blocking/test_windows_rmtree_blocker.py +120 -8
  33. package/hooks/blocking/windows_rmtree_blocker.py +23 -6
  34. package/hooks/config/banned_identifiers_constants.py +24 -0
  35. package/hooks/config/hardcoded_user_path_constants.py +12 -0
  36. package/hooks/config/hook_log_extractor_constants.py +1 -1
  37. package/hooks/config/pre_tool_use_stdin.py +48 -0
  38. package/hooks/config/setup_project_paths_constants.py +4 -0
  39. package/hooks/config/stuttering_check_config.py +14 -0
  40. package/hooks/config/stuttering_import_binding_constants.py +11 -0
  41. package/hooks/config/sys_path_insert_constants.py +4 -0
  42. package/hooks/config/test_banned_identifiers_constants.py +48 -0
  43. package/hooks/config/test_hardcoded_user_path_constants.py +78 -0
  44. package/hooks/config/test_hook_log_extractor_constants.py +3 -3
  45. package/hooks/config/test_pre_tool_use_stdin.py +80 -0
  46. package/hooks/config/unused_module_import_constants.py +7 -0
  47. package/hooks/config/windows_rmtree_blocker_constants.py +3 -0
  48. package/hooks/diagnostic/hook_log_stop_wrapper.py +7 -4
  49. package/hooks/git-hooks/config.py +3 -3
  50. package/hooks/git-hooks/test_gate_utils.py +10 -10
  51. package/hooks/mypy.ini +2 -0
  52. package/package.json +1 -1
  53. package/rules/gh-paginate.md +125 -0
  54. package/skills/bugteam/CONSTRAINTS.md +12 -6
  55. package/skills/bugteam/PROMPTS.md +0 -39
  56. package/skills/bugteam/SKILL.md +93 -125
  57. package/skills/bugteam/SKILL_EVALS.md +25 -23
  58. package/skills/bugteam/reference/README.md +2 -0
  59. package/skills/bugteam/reference/audit-and-teammates.md +2 -2
  60. package/skills/bugteam/reference/copilot-gap-analysis.md +12 -0
  61. package/skills/bugteam/reference/teardown-publish-permissions.md +1 -1
  62. package/skills/bugteam/reference/workflow-path-a-orchestrated-teams.md +113 -0
  63. package/skills/bugteam/reference/workflow-path-b-task-harness.md +48 -0
  64. package/skills/bugteam/test_skill_additions.py +13 -4
  65. package/skills/bugteam/test_team_lifecycle.py +94 -0
  66. package/skills/findbugs/SKILL.md +3 -3
  67. package/skills/fixbugs/SKILL.md +4 -4
  68. package/skills/monitor-open-prs/SKILL.md +32 -2
  69. package/skills/monitor-open-prs/test_team_lifecycle.py +46 -0
  70. package/skills/pr-converge/SKILL.md +576 -95
  71. package/skills/pr-converge/scripts/README.md +145 -0
  72. package/skills/pr-converge/scripts/caller-window-pid.ps1 +86 -0
  73. package/skills/pr-converge/scripts/check_pr_mergeability.py +79 -0
  74. package/skills/pr-converge/scripts/config/pr_converge_constants.py +65 -0
  75. package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +176 -0
  76. package/skills/pr-converge/scripts/cursor-agents-continue-caller.cmd +9 -0
  77. package/skills/pr-converge/scripts/cursor-agents-continue-stop-others.ps1 +16 -0
  78. package/skills/pr-converge/scripts/cursor-agents-continue.ahk +172 -0
  79. package/skills/pr-converge/scripts/cursor-agents-continue.cmd +2 -0
  80. package/skills/pr-converge/scripts/evict_cached_config_modules.py +20 -0
  81. package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +110 -0
  82. package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +103 -0
  83. package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +112 -0
  84. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +121 -0
  85. package/skills/pr-converge/scripts/mark_pr_ready.py +54 -0
  86. package/skills/pr-converge/scripts/open_followup_copilot_pr.py +136 -0
  87. package/skills/pr-converge/scripts/post-bugbot-run.helpers.ps1 +49 -0
  88. package/skills/pr-converge/scripts/post-bugbot-run.ps1 +33 -0
  89. package/skills/pr-converge/scripts/reply_to_inline_comment.py +84 -0
  90. package/skills/pr-converge/scripts/request_copilot_review.py +71 -0
  91. package/skills/pr-converge/scripts/resolve_pr_head.py +58 -0
  92. package/skills/pr-converge/scripts/review_field_helpers.py +43 -0
  93. package/skills/pr-converge/scripts/test_check_pr_mergeability.py +126 -0
  94. package/skills/pr-converge/scripts/test_evict_cached_config_modules.py +22 -0
  95. package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +342 -0
  96. package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +220 -0
  97. package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +372 -0
  98. package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +280 -0
  99. package/skills/pr-converge/scripts/test_mark_pr_ready.py +69 -0
  100. package/skills/pr-converge/scripts/test_open_followup_copilot_pr.py +236 -0
  101. package/skills/pr-converge/scripts/test_post_bugbot_run.py +195 -0
  102. package/skills/pr-converge/scripts/test_reply_to_inline_comment.py +159 -0
  103. package/skills/pr-converge/scripts/test_request_copilot_review.py +101 -0
  104. package/skills/pr-converge/scripts/test_resolve_pr_head.py +79 -0
  105. package/skills/pr-converge/scripts/test_review_field_helpers.py +80 -0
  106. package/skills/pr-converge/scripts/test_trigger_bugbot.py +139 -0
  107. package/skills/pr-converge/scripts/test_view_pr_context.py +111 -0
  108. package/skills/pr-converge/scripts/trigger_bugbot.py +77 -0
  109. package/skills/pr-converge/scripts/view_pr_context.py +47 -0
  110. package/skills/pr-converge/test_team_lifecycle.py +47 -0
  111. package/skills/pr-converge/workflows/ahk-auto-continue-loop.md +108 -0
  112. package/skills/pr-converge/workflows/schedule-wakeup-loop.md +37 -0
  113. package/skills/qbug/SKILL.md +4 -4
  114. package/skills/qbug/test_qbug_skill_post_fix_audit.py +2 -2
  115. package/skills/resume-review/SKILL.md +261 -0
  116. package/agents/agent-writer.md +0 -157
  117. package/agents/config-centralizer.md +0 -686
  118. package/agents/config-extraction-agent.md +0 -225
  119. package/agents/doc-orchestrator.md +0 -47
  120. package/agents/docx-agent.md +0 -211
  121. package/agents/magic-value-eliminator-agent.md +0 -72
  122. package/agents/mandatory-agent-workflow-agent.md +0 -88
  123. package/agents/parallel-workflow-coordinator.md +0 -779
  124. package/agents/pdf-agent.md +0 -302
  125. package/agents/project-context-loader.md +0 -238
  126. package/agents/readability-review-agent.md +0 -76
  127. package/agents/refactoring-specialist.md +0 -69
  128. package/agents/right-sized-engineer.md +0 -129
  129. package/agents/session-continuity-manager.md +0 -53
  130. package/agents/stub-detector-agent.md +0 -140
  131. package/agents/tdd-test-writer.md +0 -62
  132. package/agents/test-data-builder.md +0 -68
  133. package/agents/tooling-builder.md +0 -78
  134. package/agents/validation-expert.md +0 -71
  135. package/agents/xlsx-agent.md +0 -169
  136. package/skills/bugteam/scripts/README.md +0 -58
  137. package/skills/bugteam/scripts/_claude_permissions_common.py +0 -219
  138. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +0 -633
  139. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +0 -260
  140. package/skills/bugteam/scripts/bugteam_preflight.py +0 -201
  141. package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +0 -17
  142. package/skills/bugteam/scripts/grant_project_claude_permissions.py +0 -109
  143. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +0 -135
  144. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +0 -271
  145. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -267
  146. package/skills/bugteam/scripts/test_bugteam_preflight.py +0 -189
  147. package/skills/bugteam/scripts/test_claude_permissions_common.py +0 -44
  148. /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
- skill_text = _read_skill_text()
15
- assert "BUGTEAM_FIX_IMPLEMENTER" in skill_text
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
- skill_text = _read_skill_text()
25
- assert "groq-coder" in skill_text
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
@@ -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 spawn clean-coder to fix the P0/P1 findings?`
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 spawn clean-coder to fix the P0/P1 findings?
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 spawn clean-coder to fix the P0 + P1s?`
180
+ `Want me to run /fixbugs for the P0 + P1s?`
181
181
  </example>
182
182
 
183
183
  <example>
@@ -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 clean-coder agent to implement every fix in one
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 clean-coder background agent (model: sonnet) to implement all fixes
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 clean-coder spawns]
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 above.
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)