claude-dev-env 1.35.0 → 1.36.1

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 (115) hide show
  1. package/agents/clean-coder.md +109 -1
  2. package/bin/install.mjs +28 -8
  3. package/bin/install.test.mjs +9 -1
  4. package/docs/CODE_RULES.md +3 -0
  5. package/docs/agents-md-alignment-plan.md +123 -0
  6. package/hooks/blocking/code_rules_enforcer.py +451 -39
  7. package/hooks/blocking/es_exe_path_rewriter.py +10 -4
  8. package/hooks/blocking/test_code_rules_enforcer.py +182 -0
  9. package/hooks/blocking/test_code_rules_enforcer_banned_identifier.py +106 -0
  10. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +173 -0
  11. package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +191 -0
  12. package/hooks/blocking/test_code_rules_enforcer_constant_equality.py +40 -0
  13. package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +291 -0
  14. package/hooks/blocking/test_code_rules_enforcer_loop_variable_naming.py +87 -3
  15. package/hooks/blocking/test_code_rules_enforcer_naming_pattern.py +49 -0
  16. package/hooks/blocking/test_code_rules_enforcer_sys_path_insert.py +157 -0
  17. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +244 -0
  18. package/hooks/blocking/test_es_exe_path_rewriter.py +81 -3
  19. package/hooks/blocking/test_windows_rmtree_blocker.py +120 -8
  20. package/hooks/blocking/windows_rmtree_blocker.py +23 -6
  21. package/hooks/config/banned_identifiers_constants.py +24 -0
  22. package/hooks/config/hardcoded_user_path_constants.py +12 -0
  23. package/hooks/config/hook_log_extractor_constants.py +1 -1
  24. package/hooks/config/pre_tool_use_stdin.py +48 -0
  25. package/hooks/config/setup_project_paths_constants.py +4 -0
  26. package/hooks/config/stuttering_check_config.py +14 -0
  27. package/hooks/config/stuttering_import_binding_constants.py +11 -0
  28. package/hooks/config/sys_path_insert_constants.py +4 -0
  29. package/hooks/config/test_banned_identifiers_constants.py +48 -0
  30. package/hooks/config/test_hardcoded_user_path_constants.py +78 -0
  31. package/hooks/config/test_hook_log_extractor_constants.py +3 -3
  32. package/hooks/config/test_pre_tool_use_stdin.py +80 -0
  33. package/hooks/config/unused_module_import_constants.py +7 -0
  34. package/hooks/config/windows_rmtree_blocker_constants.py +3 -0
  35. package/hooks/diagnostic/hook_log_stop_wrapper.py +7 -4
  36. package/hooks/git-hooks/config.py +3 -3
  37. package/hooks/git-hooks/test_gate_utils.py +10 -10
  38. package/hooks/mypy.ini +2 -0
  39. package/package.json +1 -1
  40. package/rules/gh-paginate.md +125 -0
  41. package/skills/bugteam/CONSTRAINTS.md +12 -6
  42. package/skills/bugteam/SKILL.md +364 -154
  43. package/skills/bugteam/SKILL_EVALS.md +25 -23
  44. package/skills/bugteam/reference/README.md +2 -0
  45. package/skills/bugteam/reference/audit-and-teammates.md +2 -2
  46. package/skills/bugteam/reference/teardown-publish-permissions.md +1 -1
  47. package/skills/bugteam/reference/workflow-path-a-orchestrated-teams.md +113 -0
  48. package/skills/bugteam/reference/workflow-path-b-task-harness.md +48 -0
  49. package/skills/bugteam/scripts/reflow_skill_md.py +298 -0
  50. package/skills/bugteam/test_skill_additions.py +13 -4
  51. package/skills/bugteam/test_team_lifecycle.py +103 -0
  52. package/skills/findbugs/SKILL.md +3 -3
  53. package/skills/fixbugs/SKILL.md +4 -4
  54. package/skills/monitor-open-prs/SKILL.md +32 -2
  55. package/skills/monitor-open-prs/test_team_lifecycle.py +46 -0
  56. package/skills/pr-converge/SKILL.md +1206 -131
  57. package/skills/pr-converge/scripts/README.md +145 -0
  58. package/skills/pr-converge/scripts/caller-window-pid.ps1 +86 -0
  59. package/skills/pr-converge/scripts/check_pr_mergeability.py +79 -0
  60. package/skills/pr-converge/scripts/config/pr_converge_constants.py +65 -0
  61. package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +176 -0
  62. package/skills/pr-converge/scripts/cursor-agents-continue-caller.cmd +9 -0
  63. package/skills/pr-converge/scripts/cursor-agents-continue-stop-others.ps1 +16 -0
  64. package/skills/pr-converge/scripts/cursor-agents-continue.ahk +172 -0
  65. package/skills/pr-converge/scripts/cursor-agents-continue.cmd +2 -0
  66. package/skills/pr-converge/scripts/evict_cached_config_modules.py +20 -0
  67. package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +110 -0
  68. package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +103 -0
  69. package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +112 -0
  70. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +121 -0
  71. package/skills/pr-converge/scripts/mark_pr_ready.py +54 -0
  72. package/skills/pr-converge/scripts/open_followup_copilot_pr.py +136 -0
  73. package/skills/pr-converge/scripts/post-bugbot-run.helpers.ps1 +49 -0
  74. package/skills/pr-converge/scripts/post-bugbot-run.ps1 +33 -0
  75. package/skills/pr-converge/scripts/reflow_skill_md.py +288 -0
  76. package/skills/pr-converge/scripts/reply_to_inline_comment.py +84 -0
  77. package/skills/pr-converge/scripts/request_copilot_review.py +71 -0
  78. package/skills/pr-converge/scripts/resolve_pr_head.py +58 -0
  79. package/skills/pr-converge/scripts/review_field_helpers.py +43 -0
  80. package/skills/pr-converge/scripts/test_check_pr_mergeability.py +126 -0
  81. package/skills/pr-converge/scripts/test_evict_cached_config_modules.py +22 -0
  82. package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +342 -0
  83. package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +220 -0
  84. package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +372 -0
  85. package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +280 -0
  86. package/skills/pr-converge/scripts/test_mark_pr_ready.py +69 -0
  87. package/skills/pr-converge/scripts/test_open_followup_copilot_pr.py +236 -0
  88. package/skills/pr-converge/scripts/test_post_bugbot_run.py +195 -0
  89. package/skills/pr-converge/scripts/test_reply_to_inline_comment.py +159 -0
  90. package/skills/pr-converge/scripts/test_request_copilot_review.py +101 -0
  91. package/skills/pr-converge/scripts/test_resolve_pr_head.py +79 -0
  92. package/skills/pr-converge/scripts/test_review_field_helpers.py +80 -0
  93. package/skills/pr-converge/scripts/test_trigger_bugbot.py +139 -0
  94. package/skills/pr-converge/scripts/test_view_pr_context.py +111 -0
  95. package/skills/pr-converge/scripts/trigger_bugbot.py +77 -0
  96. package/skills/pr-converge/scripts/view_pr_context.py +47 -0
  97. package/skills/pr-converge/test_team_lifecycle.py +56 -0
  98. package/skills/pr-converge/workflows/ahk-auto-continue-loop.md +108 -0
  99. package/skills/pr-converge/workflows/schedule-wakeup-loop.md +37 -0
  100. package/skills/qbug/SKILL.md +4 -4
  101. package/skills/qbug/test_qbug_skill_post_fix_audit.py +2 -2
  102. package/skills/resume-review/SKILL.md +261 -0
  103. package/skills/bugteam/scripts/README.md +0 -58
  104. package/skills/bugteam/scripts/_claude_permissions_common.py +0 -219
  105. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +0 -633
  106. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +0 -260
  107. package/skills/bugteam/scripts/bugteam_preflight.py +0 -201
  108. package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +0 -17
  109. package/skills/bugteam/scripts/grant_project_claude_permissions.py +0 -109
  110. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +0 -135
  111. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +0 -271
  112. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -267
  113. package/skills/bugteam/scripts/test_bugteam_preflight.py +0 -189
  114. package/skills/bugteam/scripts/test_claude_permissions_common.py +0 -44
  115. /package/skills/{bugteam → pr-converge}/scripts/config/__init__.py +0 -0
@@ -0,0 +1,103 @@
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
95
+
96
+
97
+ def test_skill_md_physical_lines_fit_eighty_column_limit():
98
+ skill_text = _skill_text()
99
+ for each_line_number, each_physical_line in enumerate(skill_text.splitlines(), 1):
100
+ assert len(each_physical_line) <= 80, (
101
+ "SKILL.md line %s exceeds 80 columns (%s chars)"
102
+ % (each_line_number, len(each_physical_line))
103
+ )
@@ -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)