claude-dev-env 1.36.1 → 1.37.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 (101) hide show
  1. package/_shared/pr-loop/audit-contract.md +159 -0
  2. package/_shared/pr-loop/code-rules-gate.md +64 -0
  3. package/_shared/pr-loop/fix-protocol.md +37 -0
  4. package/_shared/pr-loop/gh-payloads.md +85 -0
  5. package/_shared/pr-loop/scripts/README.md +20 -0
  6. package/_shared/pr-loop/scripts/_claude_permissions_common.py +234 -0
  7. package/_shared/pr-loop/scripts/code_rules_gate.py +975 -0
  8. package/_shared/pr-loop/scripts/config/__init__.py +0 -0
  9. package/_shared/pr-loop/scripts/config/claude_permissions_constants.py +36 -0
  10. package/_shared/pr-loop/scripts/config/claude_settings_keys_constants.py +11 -0
  11. package/_shared/pr-loop/scripts/config/code_rules_gate_constants.py +56 -0
  12. package/_shared/pr-loop/scripts/config/fix_hookspath_constants.py +25 -0
  13. package/_shared/pr-loop/scripts/config/gh_util_constants.py +31 -0
  14. package/_shared/pr-loop/scripts/config/preflight_constants.py +68 -0
  15. package/_shared/pr-loop/scripts/fix_hookspath.py +260 -0
  16. package/_shared/pr-loop/scripts/gh_util.py +193 -0
  17. package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +130 -0
  18. package/_shared/pr-loop/scripts/preflight.py +449 -0
  19. package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +156 -0
  20. package/_shared/pr-loop/scripts/tests/conftest.py +51 -0
  21. package/_shared/pr-loop/scripts/tests/test__claude_permissions_common.py +135 -0
  22. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +169 -0
  23. package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +58 -0
  24. package/_shared/pr-loop/scripts/tests/test_claude_settings_keys_constants.py +50 -0
  25. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +917 -0
  26. package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +102 -0
  27. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +374 -0
  28. package/_shared/pr-loop/scripts/tests/test_fix_hookspath_constants.py +47 -0
  29. package/_shared/pr-loop/scripts/tests/test_gh_util.py +257 -0
  30. package/_shared/pr-loop/scripts/tests/test_gh_util_constants.py +61 -0
  31. package/_shared/pr-loop/scripts/tests/test_grant_project_claude_permissions.py +49 -0
  32. package/_shared/pr-loop/scripts/tests/test_preflight.py +670 -0
  33. package/_shared/pr-loop/scripts/tests/test_preflight_constants.py +77 -0
  34. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +49 -0
  35. package/_shared/pr-loop/state-schema.md +81 -0
  36. package/hooks/blocking/code_rules_enforcer.py +269 -23
  37. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +157 -1
  38. package/hooks/config/test_unused_module_import_constants.py +48 -0
  39. package/hooks/config/unused_module_import_constants.py +41 -0
  40. package/package.json +2 -1
  41. package/skills/bg-agent/SKILL.md +69 -0
  42. package/skills/bugteam/CONSTRAINTS.md +10 -19
  43. package/skills/bugteam/PROMPTS.md +3 -3
  44. package/skills/bugteam/SKILL.md +103 -202
  45. package/skills/bugteam/SKILL_EVALS.md +75 -114
  46. package/skills/bugteam/reference/README.md +2 -4
  47. package/skills/bugteam/reference/design-rationale.md +3 -8
  48. package/skills/bugteam/reference/team-setup.md +11 -19
  49. package/skills/bugteam/reference/teardown-publish-permissions.md +2 -14
  50. package/skills/bugteam/scripts/config/__init__.py +0 -0
  51. package/skills/bugteam/scripts/config/reflow_skill_md_constants.py +12 -0
  52. package/skills/bugteam/scripts/reflow_skill_md.py +51 -47
  53. package/skills/bugteam/sources.md +1 -25
  54. package/skills/bugteam/test_skill_additions.py +4 -13
  55. package/skills/fresh-branch/SKILL.md +71 -0
  56. package/skills/gotcha/SKILL.md +73 -0
  57. package/skills/monitor-open-prs/SKILL.md +4 -37
  58. package/skills/monitor-open-prs/test_skill_contract.py +0 -5
  59. package/skills/pr-converge/SKILL.md +60 -1298
  60. package/skills/pr-converge/reference/convergence-gates.md +118 -0
  61. package/skills/pr-converge/reference/examples.md +76 -0
  62. package/skills/pr-converge/reference/fix-protocol.md +54 -0
  63. package/skills/pr-converge/reference/ground-rules.md +13 -0
  64. package/skills/pr-converge/reference/multi-pr-orchestration.md +204 -0
  65. package/skills/pr-converge/reference/per-tick.md +201 -0
  66. package/skills/pr-converge/reference/state-schema.md +19 -0
  67. package/skills/pr-converge/reference/stop-conditions.md +26 -0
  68. package/skills/pr-converge/scripts/README.md +36 -9
  69. package/skills/pr-converge/scripts/check_pr_mergeability.py +1 -2
  70. package/skills/pr-converge/scripts/config/pr_converge_constants.py +58 -5
  71. package/skills/pr-converge/scripts/config/reflow_skill_md_constants.py +13 -0
  72. package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +0 -24
  73. package/skills/pr-converge/scripts/cursor-agents-continue.ahk +22 -2
  74. package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +19 -59
  75. package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +15 -61
  76. package/skills/pr-converge/scripts/fetch_claude_inline_comments.py +70 -0
  77. package/skills/pr-converge/scripts/fetch_claude_reviews.py +61 -0
  78. package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +19 -61
  79. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +14 -74
  80. package/skills/pr-converge/scripts/reflow_skill_md.py +71 -50
  81. package/skills/pr-converge/scripts/reviewer_fetch_core.py +153 -0
  82. package/skills/pr-converge/scripts/reviewer_specs.py +98 -0
  83. package/skills/pr-converge/scripts/test_cursor_agents_continue.py +65 -0
  84. package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +107 -6
  85. package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +85 -6
  86. package/skills/pr-converge/scripts/test_fetch_claude_inline_comments.py +485 -0
  87. package/skills/pr-converge/scripts/test_fetch_claude_reviews.py +368 -0
  88. package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +74 -6
  89. package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +94 -8
  90. package/skills/pr-converge/scripts/test_reflow_skill_md.py +162 -0
  91. package/skills/pr-converge/scripts/test_reviewer_fetch_core.py +448 -0
  92. package/skills/pr-converge/scripts/test_reviewer_specs.py +107 -0
  93. package/skills/pr-converge/workflows/schedule-wakeup-loop.md +24 -22
  94. package/skills/bugteam/reference/workflow-path-a-orchestrated-teams.md +0 -113
  95. package/skills/bugteam/reference/workflow-path-b-task-harness.md +0 -48
  96. package/skills/bugteam/test_team_lifecycle.py +0 -103
  97. package/skills/monitor-open-prs/test_team_lifecycle.py +0 -46
  98. package/skills/pr-converge/scripts/open_followup_copilot_pr.py +0 -136
  99. package/skills/pr-converge/scripts/test_open_followup_copilot_pr.py +0 -236
  100. package/skills/pr-converge/test_team_lifecycle.py +0 -56
  101. package/skills/pr-converge/workflows/ahk-auto-continue-loop.md +0 -108
@@ -17,14 +17,15 @@ from __future__ import annotations
17
17
 
18
18
  import re
19
19
  import textwrap
20
- from pathlib import Path
21
20
 
22
- MAX_WIDTH = 80
23
- SKILL_PATH = Path(__file__).resolve().parent.parent / "SKILL.md"
24
-
25
- ORDERED_RE = re.compile(r"^(\s*)(\d+\.\s)(.*)$")
26
- BULLET_RE = re.compile(r"^(\s*)([-*]\s)(.*)$")
27
- UNFINISHED_MD_LINK_TARGET = re.compile(r"\]\([^)]*$")
21
+ from config.reflow_skill_md_constants import (
22
+ BASH_CONTINUATION_MARKER_WIDTH,
23
+ BULLET_LIST_ITEM_PATTERN as BULLET_RE,
24
+ MAXIMUM_LINE_WIDTH as MAX_WIDTH,
25
+ ORDERED_LIST_ITEM_PATTERN as ORDERED_RE,
26
+ TARGET_SKILL_PATH as SKILL_PATH,
27
+ UNFINISHED_MARKDOWN_LINK_TARGET_PATTERN as UNFINISHED_MD_LINK_TARGET,
28
+ )
28
29
 
29
30
 
30
31
  def wrap_paragraph_plain(text: str) -> list[str]:
@@ -55,11 +56,14 @@ def wrap_list_item(lead_ws: str, marker: str, body: str) -> list[str]:
55
56
  ).splitlines()
56
57
 
57
58
 
58
- def reflow_yaml_description_block(lines: list[str], body_start: int) -> tuple[list[str], int]:
59
+ def reflow_yaml_description_block(
60
+ all_lines: list[str],
61
+ body_start: int,
62
+ ) -> tuple[list[str], int]:
59
63
  body_parts: list[str] = []
60
64
  index = body_start
61
- while index < len(lines):
62
- line = lines[index]
65
+ while index < len(all_lines):
66
+ line = all_lines[index]
63
67
  if line.strip() == "---":
64
68
  index += 1
65
69
  break
@@ -118,30 +122,30 @@ def merge_without_space(buffer: str, continuation: str) -> bool:
118
122
  return False
119
123
 
120
124
 
121
- def merge_soft_breaks(lines: list[str]) -> list[str]:
122
- output: list[str] = []
125
+ def merge_soft_breaks(all_lines: list[str]) -> list[str]:
126
+ reflowed_lines: list[str] = []
123
127
  index = 0
124
- in_fence = False
125
- while index < len(lines):
126
- raw = lines[index]
128
+ is_inside_fence = False
129
+ while index < len(all_lines):
130
+ raw = all_lines[index]
127
131
  line = raw.rstrip("\n")
128
132
  if line.lstrip().startswith("```"):
129
- in_fence = not in_fence
130
- output.append(line)
133
+ is_inside_fence = not is_inside_fence
134
+ reflowed_lines.append(line)
131
135
  index += 1
132
136
  continue
133
- if in_fence:
134
- output.append(line)
137
+ if is_inside_fence:
138
+ reflowed_lines.append(line)
135
139
  index += 1
136
140
  continue
137
141
  if line.strip() == "":
138
- output.append(line)
142
+ reflowed_lines.append(line)
139
143
  index += 1
140
144
  continue
141
145
  buffer_line = line
142
146
  index += 1
143
- while index < len(lines):
144
- next_raw = lines[index].rstrip("\n")
147
+ while index < len(all_lines):
148
+ next_raw = all_lines[index].rstrip("\n")
145
149
  if next_raw.strip() == "":
146
150
  break
147
151
  if next_raw.lstrip().startswith("```"):
@@ -154,8 +158,8 @@ def merge_soft_breaks(lines: list[str]) -> list[str]:
154
158
  else:
155
159
  buffer_line = f"{buffer_line.rstrip()} {stripped_next}"
156
160
  index += 1
157
- output.append(buffer_line)
158
- return output
161
+ reflowed_lines.append(buffer_line)
162
+ return reflowed_lines
159
163
 
160
164
 
161
165
  def reflow_merged_line(line: str) -> list[str]:
@@ -212,39 +216,39 @@ def reflow_merged_line(line: str) -> list[str]:
212
216
  return wrap_paragraph_plain(stripped)
213
217
 
214
218
 
215
- def reflow_markdown_body(lines: list[str]) -> list[str]:
216
- merged = merge_soft_breaks(lines)
217
- output: list[str] = []
219
+ def reflow_markdown_body(all_lines: list[str]) -> list[str]:
220
+ merged = merge_soft_breaks(all_lines)
221
+ reflowed_lines: list[str] = []
218
222
  for each_line in merged:
219
223
  if each_line.strip() == "":
220
- output.append("")
224
+ reflowed_lines.append("")
221
225
  continue
222
- output.extend(reflow_merged_line(each_line))
223
- return output
226
+ reflowed_lines.extend(reflow_merged_line(each_line))
227
+ return reflowed_lines
224
228
 
225
229
 
226
- def wrap_long_bash_fence_lines(lines: list[str]) -> list[str]:
230
+ def wrap_long_bash_fence_lines(all_lines: list[str]) -> list[str]:
227
231
  """Hard-wrap only ```bash fence bodies that still exceed MAX_WIDTH."""
228
- output: list[str] = []
229
- in_bash_fence = False
230
- for line in lines:
231
- stripped = line.lstrip()
232
+ wrapped_lines: list[str] = []
233
+ is_inside_bash_fence = False
234
+ for each_line in all_lines:
235
+ stripped = each_line.lstrip()
232
236
  if stripped.startswith("```"):
233
- if not in_bash_fence:
237
+ if not is_inside_bash_fence:
234
238
  lang = stripped[3:].strip().lower()
235
- in_bash_fence = lang == "bash"
239
+ is_inside_bash_fence = lang == "bash"
236
240
  else:
237
- in_bash_fence = False
238
- output.append(line)
241
+ is_inside_bash_fence = False
242
+ wrapped_lines.append(each_line)
239
243
  continue
240
- if in_bash_fence and len(line) > MAX_WIDTH:
241
- indent_len = len(line) - len(line.lstrip())
242
- indent = line[:indent_len]
243
- content = line.lstrip()
244
+ if is_inside_bash_fence and len(each_line) > MAX_WIDTH:
245
+ indent_len = len(each_line) - len(each_line.lstrip())
246
+ indent = each_line[:indent_len]
247
+ content = each_line.lstrip()
244
248
  wrapped_segments: list[str] = []
245
249
  rest = content
246
250
  while len(rest) > MAX_WIDTH - len(indent):
247
- room = MAX_WIDTH - len(indent) - 2
251
+ room = MAX_WIDTH - len(indent) - BASH_CONTINUATION_MARKER_WIDTH
248
252
  window = rest[:room]
249
253
  break_at = window.rfind(" ")
250
254
  if break_at <= 0:
@@ -254,10 +258,10 @@ def wrap_long_bash_fence_lines(lines: list[str]) -> list[str]:
254
258
  wrapped_segments.append(indent + piece + " \\")
255
259
  if rest:
256
260
  wrapped_segments.append(indent + (" " if wrapped_segments else "") + rest)
257
- output.extend(wrapped_segments)
261
+ wrapped_lines.extend(wrapped_segments)
258
262
  else:
259
- output.append(line)
260
- return output
263
+ wrapped_lines.append(each_line)
264
+ return wrapped_lines
261
265
 
262
266
 
263
267
  def main() -> None:
@@ -26,15 +26,7 @@ Direct quote:
26
26
 
27
27
  **Skill use:** Subagents return into the lead context (accumulates across loops); agent-team teammates do not pollute the lead. This skill needs the independent-context property.
28
28
 
29
- ### Team creation in natural language
30
-
31
- Direct quote:
32
-
33
- > "tell Claude to create an agent team and describe the task and the team structure you want in natural language. Claude creates the team, spawns teammates, and coordinates work based on your prompt."
34
-
35
- **Skill use:** Maps to the `TeamCreate` tool step in the process section.
36
-
37
- ### Referencing subagent types when spawning teammates
29
+ ### Referencing subagent types when spawning subagents
38
30
 
39
31
  Direct quote:
40
32
 
@@ -42,22 +34,6 @@ Direct quote:
42
34
 
43
35
  **Skill use:** Bugfind / bugfix roles reference `code-quality-agent` and `clean-coder` by subagent type name.
44
36
 
45
- ### Lead cleanup and active teammates
46
-
47
- Direct quote:
48
-
49
- > "When the lead runs cleanup, it checks for active teammates and fails if any are still running, so shut them down first."
50
-
51
- **Skill use:** Step 4 shutdown sequence before `TeamDelete`.
52
-
53
- ### Ending the team
54
-
55
- Direct quote:
56
-
57
- > "When you're done, ask the lead to clean up: 'Clean up the team'."
58
-
59
- **Skill use:** Maps to calling `TeamDelete()` after shutdown messages.
60
-
61
37
  ---
62
38
 
63
39
  ## Claude — Agent skills best practices
@@ -10,16 +10,9 @@ 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
-
20
13
  def test_skill_references_fix_implementer_env_var():
21
- workflow_text = _read_path_a_workflow_text()
22
- assert "BUGTEAM_FIX_IMPLEMENTER" in workflow_text
14
+ skill_text = _read_skill_text()
15
+ assert "BUGTEAM_FIX_IMPLEMENTER" in skill_text
23
16
 
24
17
 
25
18
  def test_skill_names_default_implementer_subagent_type():
@@ -28,12 +21,10 @@ def test_skill_names_default_implementer_subagent_type():
28
21
 
29
22
 
30
23
  def test_skill_names_optional_groq_implementer_subagent_type():
31
- workflow_text = _read_path_a_workflow_text()
32
- assert "groq-coder" in workflow_text
24
+ skill_text = _read_skill_text()
25
+ assert "groq-coder" in skill_text
33
26
 
34
27
 
35
28
  def test_skill_documents_bugbot_retrigger_flag():
36
29
  skill_text = _read_skill_text()
37
- workflow_text = _read_path_a_workflow_text()
38
30
  assert "--bugbot-retrigger" in skill_text
39
- assert "--bugbot-retrigger" in workflow_text
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: fresh-branch
3
+ description: Creates a fresh branch for the current repo based on origin main. Always fetches actual origin main rather than relying on local main. Suggests possible branch names via AskUserQuestion when context is available, or prompts the user to provide a name directly. Triggers on "fresh branch", "new branch from main", "/fresh-branch", "start fresh".
4
+ ---
5
+
6
+ # fresh-branch
7
+
8
+ ## Overview
9
+
10
+ Creates a new branch from `origin/main` (always fresh-fetched, never stale local main). Designed as a shared primitive: other skills (e.g. gotcha) invoke `/fresh-branch` to create a clean branch for their own PR workflows.
11
+
12
+ **Announce at start:** "Creating a fresh branch from origin/main."
13
+
14
+ ## Instructions
15
+
16
+ ### Phase 1 — Fetch origin main
17
+
18
+ Always fetch `origin/main` directly. Do not rely on the local `main` branch, which may be stale.
19
+
20
+ ```
21
+ git fetch origin main
22
+ ```
23
+
24
+ Confirm the fetch succeeded. If it fails (no network, no remote), report the error and stop.
25
+
26
+ ### Phase 2 — Determine branch name
27
+
28
+ Branch names follow the repo's convention: lowercase, hyphen-separated, descriptive prefix.
29
+
30
+ **When context is available (the caller or prior conversation provides a topic):**
31
+
32
+ Suggest 2–4 branch names via `AskUserQuestion`. The suggestions should be short, descriptive, and follow the `prefix/description` or `description` convention visible in recent `git log` output.
33
+
34
+ Poll recent branch naming patterns to inform suggestions:
35
+
36
+ ```
37
+ git branch -r --sort=-committerdate | head -20
38
+ ```
39
+
40
+ **When no context is available:**
41
+
42
+ Ask the user to provide a branch name directly via `AskUserQuestion` with `multiSelect: false` and a free-text option.
43
+
44
+ ### Phase 3 — Create the branch
45
+
46
+ ```
47
+ git checkout -b <branch-name> origin/main
48
+ ```
49
+
50
+ The `-b` flag creates the branch and checks it out. Basing on `origin/main` (not local `main`) guarantees the branch starts from the true latest state.
51
+
52
+ Confirm success:
53
+
54
+ ```
55
+ git rev-parse --abbrev-ref HEAD
56
+ git log --oneline -1
57
+ ```
58
+
59
+ ### Phase 4 — Report
60
+
61
+ State the new branch name and its base commit. If invoked as a subroutine by another skill, return the branch name so the caller can proceed (e.g., creating a PR from it).
62
+
63
+ ## What this skill does NOT do
64
+
65
+ - Does not push the branch. The caller decides when and whether to push.
66
+ - Does not create a PR. Use `gh pr create` separately.
67
+ - Does not switch an existing branch. It always creates a new one.
68
+
69
+ ## Gotchas
70
+
71
+ See the gotcha reference at the bottom of this file. When a new gotcha is discovered during use, invoke `/gotcha` to add it here.
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: gotcha
3
+ description: When a skill encounters an obstacle requiring user intervention, this skill invokes bg-agent to add the gotcha to the skill file's gotcha section and creates a PR via fresh-branch. Triggers on "/gotcha", "add a gotcha", "document this gotcha", "record this obstacle".
4
+ ---
5
+
6
+ # gotcha
7
+
8
+ ## Overview
9
+
10
+ When a skill is executed and hits an obstacle that requires user intervention to resolve, `/gotcha` captures that knowledge so future invocations avoid the same trap. It delegates the mechanical work to `bg-agent`, which writes the gotcha entry and opens a PR using `fresh-branch`.
11
+
12
+ **Announce at start:** "Recording this gotcha and opening a PR."
13
+
14
+ ## Instructions
15
+
16
+ ### Step 1 — Collect the gotcha
17
+
18
+ The caller provides the obstacle context. Distill it into:
19
+
20
+ - **Which skill file** hit the obstacle (full path, e.g. `packages/claude-dev-env/skills/rebase/SKILL.md`).
21
+ - **What happened** — the specific failure or blocker, in one or two sentences.
22
+ - **What the user had to do** to resolve it.
23
+ - **What to do differently next time** (the actionable gotcha).
24
+
25
+ Determine which repo the skill lives in. Usually this is `claude-code-config`, but if the skill is in another repo (e.g. a project-specific `.claude/skills/` directory), use that repo instead.
26
+
27
+ ### Step 2 — Delegate to bg-agent
28
+
29
+ Invoke `/bg-agent` with a task like:
30
+
31
+ ```
32
+ bg-agent add a gotcha to <skill-file-path> in repo <repo-name>. The gotcha is: "<one-line summary>". Details: "<what happened, what the user did, and what to do next time>."
33
+
34
+ Steps:
35
+ 1. Use /fresh-branch to create a branch named gotcha/<short-slug>.
36
+ 2. In <skill-file-path>, add the gotcha entry under a ## Gotchas section (create the section at the bottom of the file if it does not exist).
37
+ 3. Format each gotcha as a bullet: "- **<title>:** <description>."
38
+ 4. Commit with message "fix(<skill-name>): add gotcha — <title>".
39
+ 5. Push and create a draft PR.
40
+ 6. Report the PR URL.
41
+ ```
42
+
43
+ The bg-agent will pick a suitable agent type and run the full workflow in the background.
44
+
45
+ ### Step 3 — Confirm
46
+
47
+ Tell the user: "Recording gotcha in the background. You will be notified when the PR is ready."
48
+
49
+ ## Gotcha entry format
50
+
51
+ Every gotcha entry follows this format:
52
+
53
+ ```markdown
54
+ ## Gotchas
55
+
56
+ - **<title>:** <what happens>. <what to do instead>.
57
+ ```
58
+
59
+ If a `## Gotchas` section already exists, append to it. If not, create it at the bottom of the skill file.
60
+
61
+ ## Which repo
62
+
63
+ | Skill location | Repo |
64
+ |---|---|
65
+ | `packages/claude-dev-env/skills/<name>/` | `jl-cmd/claude-code-config` |
66
+ | `~/.claude/skills/<name>/` | `jl-cmd/claude-code-config` (user skills) |
67
+ | Project `.claude/skills/<name>/` | That project's repo |
68
+
69
+ When unsure, ask the user which repo. Otherwise, default to `claude-code-config`.
70
+
71
+ ## Gotchas
72
+
73
+ - **The bg-agent prompt must be self-contained.** The background agent has no access to this conversation. Include the skill file path, the gotcha text, the repo, and the exact workflow steps in the prompt.
@@ -7,8 +7,7 @@ description: >-
7
7
  re-trigger flag (--bugbot-retrigger), wrap the session in `bws run`
8
8
  to inject GROQ_API_KEY, and poll Cursor's bugbot replies after
9
9
  convergence so any post-Groq findings loop back through /bugteam.
10
- Requires CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1. Triggers:
11
- '/monitor-open-prs', 'sweep the open PRs', 'groq-bugteam the backlog'.
10
+ Triggers: '/monitor-open-prs', 'sweep the open PRs', 'groq-bugteam the backlog'.
12
11
  ---
13
12
 
14
13
  # Monitor Open PRs
@@ -19,9 +18,8 @@ description: >-
19
18
 
20
19
  - When this skill applies — refusal cases and trigger conditions
21
20
  - Discovery — live `gh search prs` across both owner scopes
22
- - Team lifecycle — single long-lived team for the whole sweep
23
21
  - Wrapping — `bws run` for GROQ_API_KEY injection
24
- - Dispatch — `/bugteam <numbers...>` with groq-coder + retrigger
22
+ - Dispatch — `/bugteam --bugbot-retrigger <pr_numbers...>` with groq-coder + retrigger
25
23
  - Post-convergence polling — bugbot replies and re-invocation
26
24
  - `scripts/discover_open_prs.py` — the discovery helper
27
25
 
@@ -31,7 +29,6 @@ description: >-
31
29
 
32
30
  Refusals — first match wins; respond with the quoted line exactly and stop:
33
31
 
34
- - **Agent teams not enabled.** Check `claude config get env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` and `~/.claude/settings.json`. If neither is `"1"`: `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 not set. /monitor-open-prs requires the agent teams feature.`
35
32
  - **bws not on PATH.** `bws not installed. /monitor-open-prs injects GROQ_API_KEY via Bitwarden Secrets Manager.`
36
33
  - **gh not authenticated.** `gh auth status failed. /monitor-open-prs relies on existing gh credentials.`
37
34
  - **Dirty tree on the caller's repo.** `Uncommitted changes detected. Stash, commit, or revert before /monitor-open-prs.`
@@ -41,35 +38,6 @@ Refusals — first match wins; respond with the quoted line exactly and stop:
41
38
 
42
39
  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.
43
40
 
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
-
73
41
  ## Secret Wrapping
74
42
 
75
43
  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:
@@ -89,9 +57,9 @@ The `bws run` subshell resolves the project's secrets and exports them for the w
89
57
  For each discovered PR:
90
58
 
91
59
  1. Resolve the PR's repo checkout (existing worktree or fresh `git clone`).
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).
60
+ 2. From that checkout, invoke `/bugteam --bugbot-retrigger <pr_number>` under the `bws run` wrapper from §Secret Wrapping.
93
61
  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.
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).
62
+ 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).
95
63
 
96
64
  **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.
97
65
 
@@ -127,7 +95,6 @@ Total Groq tokens consumed: <approx from /bugteam outcome summaries>
127
95
 
128
96
  ## Non-Negotiable Guardrails
129
97
 
130
- - Never run `/bugteam` without `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` active.
131
98
  - Never source secrets outside `bws run` — no `.env` files, no shell history, no logs.
132
99
  - Never pass `--no-verify` or `--no-gpg-sign` to git in any dispatched bugteam run.
133
100
  - Never open a PR from this skill; only comment on existing ones.
@@ -16,11 +16,6 @@ def test_skill_has_frontmatter_name():
16
16
  assert "name: monitor-open-prs" in skill_text
17
17
 
18
18
 
19
- def test_skill_requires_agent_teams_env_var():
20
- skill_text = _read_skill_text()
21
- assert "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS" in skill_text
22
-
23
-
24
19
  def test_skill_invokes_bugteam_with_groq_implementer():
25
20
  skill_text = _read_skill_text()
26
21
  assert "BUGTEAM_FIX_IMPLEMENTER" in skill_text