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
@@ -1,236 +0,0 @@
1
- """Tests for open_followup_copilot_pr.
2
-
3
- Covers:
4
- - branch name follows COPILOT_FOLLOWUP_BRANCH_TEMPLATE with the short SHA
5
- - subprocess sequence: gh pr view (base ref) -> git fetch -> git switch -c -> git push -> gh pr create
6
- - gh pr create uses --draft, --base, --head, --title, --body-file (per gh-body-file rule)
7
- - the returned PR URL is the trimmed stdout from gh pr create
8
- - subprocess errors propagate
9
- """
10
-
11
- from __future__ import annotations
12
-
13
- import importlib.util
14
- import json
15
- import subprocess
16
- from pathlib import Path
17
- from types import ModuleType
18
- from unittest.mock import MagicMock, patch
19
-
20
- import pytest
21
-
22
-
23
- def _load_module() -> ModuleType:
24
- module_path = Path(__file__).parent / "open_followup_copilot_pr.py"
25
- spec = importlib.util.spec_from_file_location(
26
- "open_followup_copilot_pr", module_path
27
- )
28
- assert spec is not None
29
- assert spec.loader is not None
30
- module = importlib.util.module_from_spec(spec)
31
- spec.loader.exec_module(module)
32
- return module
33
-
34
-
35
- open_followup_copilot_pr_module = _load_module()
36
-
37
-
38
- def _completed(stdout: str) -> subprocess.CompletedProcess:
39
- process = MagicMock(spec=subprocess.CompletedProcess)
40
- process.stdout = stdout
41
- process.returncode = 0
42
- return process
43
-
44
-
45
- def _scripted_subprocess_runs(
46
- *,
47
- base_ref_payload: str,
48
- new_pr_url: str,
49
- ) -> list[subprocess.CompletedProcess]:
50
- return [
51
- _completed(base_ref_payload),
52
- _completed(""),
53
- _completed(""),
54
- _completed(""),
55
- _completed(new_pr_url),
56
- ]
57
-
58
-
59
- def test_should_build_branch_name_from_parent_number_and_short_sha(
60
- tmp_path: Path,
61
- ) -> None:
62
- findings_file = tmp_path / "findings.md"
63
- findings_file.write_text("- Item 1\n", encoding="utf-8")
64
- payload_sequence = _scripted_subprocess_runs(
65
- base_ref_payload=json.dumps({"baseRefName": "main"}),
66
- new_pr_url="https://github.com/acme/widget/pull/313\n",
67
- )
68
- with patch("subprocess.run", side_effect=payload_sequence) as mock_run:
69
- open_followup_copilot_pr_module.open_followup_copilot_pr(
70
- owner="acme",
71
- repo="widget",
72
- parent_number=312,
73
- head="abc12345deadbeefcafe",
74
- findings_file=findings_file,
75
- )
76
- git_switch_argv = mock_run.call_args_list[2][0][0]
77
- expected_branch = "chore/copilot-followup-312-abc12345"
78
- assert expected_branch in git_switch_argv
79
-
80
-
81
- def test_should_invoke_subprocess_call_sequence_in_documented_order(
82
- tmp_path: Path,
83
- ) -> None:
84
- findings_file = tmp_path / "findings.md"
85
- findings_file.write_text("- Item\n", encoding="utf-8")
86
- payload_sequence = _scripted_subprocess_runs(
87
- base_ref_payload=json.dumps({"baseRefName": "main"}),
88
- new_pr_url="https://github.com/acme/widget/pull/313\n",
89
- )
90
- with patch("subprocess.run", side_effect=payload_sequence) as mock_run:
91
- open_followup_copilot_pr_module.open_followup_copilot_pr(
92
- owner="acme",
93
- repo="widget",
94
- parent_number=312,
95
- head="abc12345deadbeefcafe",
96
- findings_file=findings_file,
97
- )
98
- invoked_command_sequence = [
99
- each_call[0][0] for each_call in mock_run.call_args_list
100
- ]
101
- assert invoked_command_sequence[0][0:3] == ["gh", "pr", "view"]
102
- assert invoked_command_sequence[1][0:2] == ["git", "fetch"]
103
- assert invoked_command_sequence[2][0:3] == ["git", "switch", "-c"]
104
- assert invoked_command_sequence[3][0:2] == ["git", "push"]
105
- assert invoked_command_sequence[4][0:3] == ["gh", "pr", "create"]
106
-
107
-
108
- def test_should_invoke_gh_pr_create_with_draft_and_body_file_flags(
109
- tmp_path: Path,
110
- ) -> None:
111
- findings_file = tmp_path / "findings.md"
112
- findings_file.write_text("- Finding A\n", encoding="utf-8")
113
- payload_sequence = _scripted_subprocess_runs(
114
- base_ref_payload=json.dumps({"baseRefName": "develop"}),
115
- new_pr_url="https://github.com/acme/widget/pull/444\n",
116
- )
117
- with patch("subprocess.run", side_effect=payload_sequence) as mock_run:
118
- open_followup_copilot_pr_module.open_followup_copilot_pr(
119
- owner="acme",
120
- repo="widget",
121
- parent_number=312,
122
- head="abc12345deadbeef",
123
- findings_file=findings_file,
124
- )
125
- pr_create_argv = mock_run.call_args_list[4][0][0]
126
- assert pr_create_argv[0:3] == ["gh", "pr", "create"]
127
- assert "--draft" in pr_create_argv
128
- assert "--base" in pr_create_argv
129
- assert "develop" in pr_create_argv
130
- assert "--head" in pr_create_argv
131
- assert "--title" in pr_create_argv
132
- assert "--body-file" in pr_create_argv
133
- body_file_argv = pr_create_argv[pr_create_argv.index("--body-file") + 1]
134
- assert body_file_argv == str(findings_file)
135
-
136
-
137
- def test_should_render_pr_title_via_named_template_constant(tmp_path: Path) -> None:
138
- findings_file = tmp_path / "findings.md"
139
- findings_file.write_text("- Finding\n", encoding="utf-8")
140
- payload_sequence = _scripted_subprocess_runs(
141
- base_ref_payload=json.dumps({"baseRefName": "main"}),
142
- new_pr_url="https://github.com/acme/widget/pull/513\n",
143
- )
144
- with patch("subprocess.run", side_effect=payload_sequence) as mock_run:
145
- open_followup_copilot_pr_module.open_followup_copilot_pr(
146
- owner="acme",
147
- repo="widget",
148
- parent_number=312,
149
- head="abc12345deadbeef",
150
- findings_file=findings_file,
151
- )
152
- pr_create_argv = mock_run.call_args_list[4][0][0]
153
- title_index = pr_create_argv.index("--title")
154
- title_value = pr_create_argv[title_index + 1]
155
- assert title_value == "chore: address Copilot findings from PR #312"
156
-
157
-
158
- def test_should_return_trimmed_pr_url(tmp_path: Path) -> None:
159
- findings_file = tmp_path / "findings.md"
160
- findings_file.write_text("- Finding\n", encoding="utf-8")
161
- payload_sequence = _scripted_subprocess_runs(
162
- base_ref_payload=json.dumps({"baseRefName": "main"}),
163
- new_pr_url=" https://github.com/acme/widget/pull/313 \n",
164
- )
165
- with patch("subprocess.run", side_effect=payload_sequence):
166
- new_pr_url = open_followup_copilot_pr_module.open_followup_copilot_pr(
167
- owner="acme",
168
- repo="widget",
169
- parent_number=312,
170
- head="abc12345deadbeef",
171
- findings_file=findings_file,
172
- )
173
- assert new_pr_url == "https://github.com/acme/widget/pull/313"
174
-
175
-
176
- def test_should_pass_repo_arg_to_gh_pr_view_for_base_ref(tmp_path: Path) -> None:
177
- findings_file = tmp_path / "findings.md"
178
- findings_file.write_text("- Finding\n", encoding="utf-8")
179
- payload_sequence = _scripted_subprocess_runs(
180
- base_ref_payload=json.dumps({"baseRefName": "main"}),
181
- new_pr_url="https://github.com/acme/widget/pull/313\n",
182
- )
183
- with patch("subprocess.run", side_effect=payload_sequence) as mock_run:
184
- open_followup_copilot_pr_module.open_followup_copilot_pr(
185
- owner="acme",
186
- repo="widget",
187
- parent_number=312,
188
- head="abc12345deadbeef",
189
- findings_file=findings_file,
190
- )
191
- pr_view_argv = mock_run.call_args_list[0][0][0]
192
- assert pr_view_argv[0:3] == ["gh", "pr", "view"]
193
- assert "--repo" in pr_view_argv
194
- repo_arg_value = pr_view_argv[pr_view_argv.index("--repo") + 1]
195
- assert repo_arg_value == "acme/widget"
196
-
197
-
198
- def test_should_pass_repo_arg_to_gh_pr_create_for_followup_pr(
199
- tmp_path: Path,
200
- ) -> None:
201
- findings_file = tmp_path / "findings.md"
202
- findings_file.write_text("- Finding\n", encoding="utf-8")
203
- payload_sequence = _scripted_subprocess_runs(
204
- base_ref_payload=json.dumps({"baseRefName": "main"}),
205
- new_pr_url="https://github.com/acme/widget/pull/313\n",
206
- )
207
- with patch("subprocess.run", side_effect=payload_sequence) as mock_run:
208
- open_followup_copilot_pr_module.open_followup_copilot_pr(
209
- owner="acme",
210
- repo="widget",
211
- parent_number=312,
212
- head="abc12345deadbeef",
213
- findings_file=findings_file,
214
- )
215
- pr_create_argv = mock_run.call_args_list[4][0][0]
216
- assert pr_create_argv[0:3] == ["gh", "pr", "create"]
217
- assert "--repo" in pr_create_argv
218
- repo_arg_value = pr_create_argv[pr_create_argv.index("--repo") + 1]
219
- assert repo_arg_value == "acme/widget"
220
-
221
-
222
- def test_should_raise_when_subprocess_fails(tmp_path: Path) -> None:
223
- findings_file = tmp_path / "findings.md"
224
- findings_file.write_text("- Finding\n", encoding="utf-8")
225
- failure = subprocess.CalledProcessError(
226
- returncode=1, cmd=["gh"], stderr="auth failure"
227
- )
228
- with patch("subprocess.run", side_effect=failure):
229
- with pytest.raises(subprocess.CalledProcessError):
230
- open_followup_copilot_pr_module.open_followup_copilot_pr(
231
- owner="acme",
232
- repo="widget",
233
- parent_number=312,
234
- head="abc12345",
235
- findings_file=findings_file,
236
- )
@@ -1,56 +0,0 @@
1
- """Markdown assertion tests for pr-converge orchestrator team lifecycle.
2
-
3
- Locks in the contract that pr-converge multi-PR orchestration must:
4
- - own a single long-lived team for the whole sweep
5
- - pass that team to every bugteam invocation via attach mode
6
- - tear down only when every PR reaches `converged` or `blocked`
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_documents_orchestrator_owned_team_in_multi_pr_mode():
20
- skill_text = _skill_text()
21
- assert "team_name" in skill_text
22
- assert "TeamCreate" in skill_text
23
- assert "orchestrator" in skill_text.lower()
24
-
25
-
26
- def test_skill_passes_attach_mode_to_bugteam_invocations():
27
- skill_text = _skill_text()
28
- assert "BUGTEAM_TEAM_LIFECYCLE" in skill_text
29
- assert "attach" in skill_text
30
- assert "BUGTEAM_TEAM_NAME" in skill_text
31
-
32
-
33
- def test_skill_tears_down_team_only_on_full_convergence():
34
- skill_text = _skill_text()
35
- assert "TeamDelete" in skill_text
36
- convergence_phrases = [
37
- "every PR",
38
- "all PRs",
39
- "fully converged",
40
- "every prs[",
41
- ]
42
- assert any(phrase in skill_text for phrase in convergence_phrases)
43
-
44
-
45
- def test_state_schema_includes_team_name_field():
46
- skill_text = _skill_text()
47
- assert '"team_name"' in skill_text or "team_name:" in skill_text
48
-
49
-
50
- def test_skill_md_physical_lines_fit_eighty_column_limit():
51
- skill_text = _skill_text()
52
- for each_line_number, each_physical_line in enumerate(skill_text.splitlines(), 1):
53
- assert len(each_physical_line) <= 80, (
54
- "SKILL.md line %s exceeds 80 columns (%s chars)"
55
- % (each_line_number, len(each_physical_line))
56
- )
@@ -1,108 +0,0 @@
1
- # AHK auto-continue loop pacing (pr-converge)
2
-
3
- Load this document when **`ScheduleWakeup` is not available** in this session (orchestrated teams disabled, restricted tool registry, Cursor
4
- without that primitive, or the user wants a visibly-running pacer). Follow it for **every** instruction below that depends on that choice.
5
- Shared bugbot / bugteam / Fix protocol steps stay in the main `SKILL.md`.
6
-
7
- ## Session behavior
8
-
9
- Keep ticks in the **same** window the auto-typer targets so each `continue` re-enters here and reads the same state line and `gh` context.
10
-
11
- ## Why this path exists
12
-
13
- It is not a separate "mode" the user must remember — bare `/pr-converge` already implies loop-until-done; when the primary wakeup tool is
14
- missing, fall through to AHK automatically. The per-tick work is unchanged; what changes is who fires the next tick. Instead of
15
- `ScheduleWakeup` re-entering the skill, an external AutoHotkey utility auto-types `continue` into the active Claude Code window every 5
16
- minutes, and the model treats each `continue` as the next tick trigger.
17
-
18
- **AHK is loop pacing only:** every `phase == BUGTEAM` tick still runs **`/bugteam`** via the bugteam skill per Step 2 of the main skill —
19
- nothing here replaces that audit.
20
-
21
- **Fix protocol** commits use **`Task`** with **`subagent_type: "generalPurpose"`** and the **clean-coder preamble** from the main
22
- [`SKILL.md` Fix protocol](../SKILL.md#fix-protocol) section (same as ScheduleWakeup pacing — Cursor has no `clean-coder` `subagent_type`).
23
-
24
- Ensure `~/.claude/agents/clean-coder.md` exists (Windows: `%USERPROFILE%\.claude\agents\clean-coder.md`). Optionally also copy it to
25
- `.cursor/agents/clean-coder.md` in the repo when you want the file co-located with the checkout; the spawn **prompt** must still name the
26
- absolute path the subagent should **Read** first.
27
-
28
- ### One-time setup at the start of the loop
29
-
30
- The skill bundles its driver scripts under `scripts/` and resolves them at runtime via `$HOME\.claude\skills\pr-converge\scripts\…` (the same
31
- convention `/logifix` uses). The bundled `.cmd` launchers locate their siblings via `%~dp0`, so they need no path arguments — only the AHK
32
- target PID.
33
-
34
- Run these two commands in order (PowerShell-friendly Bash escaping):
35
-
36
- 1. Resolve the PID of the GUI ancestor that hosts this Claude Code session:
37
- ```bash
38
- pwsh -NoProfile -ExecutionPolicy Bypass -File "$HOME\.claude\skills\pr-converge\scripts\caller-window-pid.ps1"
39
- ```
40
- Capture the printed integer as `caller_pid`. Verify it points at the right window before continuing:
41
- ```bash
42
- pwsh -NoProfile -Command "Get-Process -Id $caller_pid | Select-Object Id,ProcessName,MainWindowTitle"
43
- ```
44
- 2. Launch the auto-typer attached to that PID with auto-start enabled. The bundled launcher accepts the PID as its first arg and the
45
- `--start-on` flag is forwarded to the AHK script:
46
- ```bash
47
- "$HOME\.claude\skills\pr-converge\scripts\cursor-agents-continue.cmd" $caller_pid --start-on
48
- ```
49
- AutoHotkey v2 must be installed at `C:\Program Files\AutoHotkey\v2\AutoHotkey64.exe`.
50
-
51
- ### Per-tick behavior under this driver
52
-
53
- - Run Steps 1–3 of **Per-tick work** in the main `SKILL.md` exactly as written.
54
- - In **Step 4**, do **not** call `ScheduleWakeup` — the auto-typer is the pacer (this is the fallback branch of Step 4 in the main skill).
55
- - End every assistant response with the literal sentence `Awaiting next "continue" tick.` so the next iteration is unambiguously identifiable
56
- in the transcript.
57
- - When the next user message is `continue` (auto-typed by AHK) or any close paraphrase, treat it as the next tick of default-loop
58
- `/pr-converge` and re-enter from Step 1 against the freshest PR state.
59
-
60
- ### Convergence cleanup
61
-
62
- On back-to-back clean (the existing convergence rule in the main skill), run `gh pr ready`, then kill the auto-typer when this session used
63
- this AHK pacing path:
64
-
65
- ```bash
66
- pwsh -NoProfile -Command "Get-CimInstance Win32_Process -Filter \"Name='AutoHotkey64.exe'\" | Where-Object CommandLine -like '*cursor-agents-continue.ahk*' | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }"
67
- ```
68
-
69
- Report convergence in the same one-sentence shape as the standard flow, plus a second sentence noting the auto-typer was stopped. The skill
70
- returns; no next tick fires.
71
-
72
- ### Gotchas
73
-
74
- - **Resolver fallback semantics matter.** `caller-window-pid.ps1` walks up the parent process chain, terminates at `explorer.exe`, and falls
75
- back to the foreground window when no GUI ancestor is found. Always verify `MainWindowTitle` after capture — if it isn't the Claude Code
76
- session, the auto-typer will fire `continue` into the wrong window and the loop stalls silently.
77
- - **Tick-duration vs. 5-minute cadence.** The auto-typer fires every 5 minutes regardless of model activity. A tick that runs longer than 5
78
- minutes will receive a queued `continue` while still in flight; Claude Code processes these sequentially, so there's no corruption, but the
79
- loop pace becomes irregular. Don't try to "fix" this by shortening the AHK interval — the `bugbot run` cadence already has its own pacing
80
- baked into the standard flow.
81
- - **AHK runs as `#SingleInstance Force`.** Re-running the launcher replaces the prior instance silently. Safe to re-issue if the loop appears
82
- stalled.
83
- - **`Stop-Process -Force` on `AutoHotkey64` is broad.** It kills every AHK instance, not just the one this skill started. When the user has
84
- unrelated AHK utilities running, scope the kill by command-line match instead:
85
- ```bash
86
- pwsh -NoProfile -Command "Get-CimInstance Win32_Process -Filter \"Name='AutoHotkey64.exe'\" | Where-Object CommandLine -like '*cursor-agents-continue.ahk*' | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }"
87
- ```
88
- - **State-line responsibility is unchanged.** The state line (phase, bugbot_clean_at, inline_lag_streak, tick_count) is still emitted at the
89
- end of every tick — it's how the next tick reads prior state. The auto-typer only fires `continue`; it does not preserve state for you.
90
- - **No `tick_count` ceiling.** `tick_count` is observability-only (same as the main skill and `state-schema.md`). This path ends on convergence or **Stop conditions** in `SKILL.md`, not on a tick counter.
91
- - **`/bugteam` is not optional for BUGTEAM ticks.** AHK only paces **when** the next tick runs; it does not replace the bugteam skill. Skipping
92
- **`/bugteam`** after a clean Bugbot review breaks the back-to-back contract.
93
- - **Fix protocol:** use **`Task` + `generalPurpose`** with the clean-coder **Read** preamble from the main [`SKILL.md` Fix protocol](../SKILL.md#fix-protocol)
94
- (never a bare `generalPurpose` production edit). Ensure the clean-coder agent markdown exists at `~/.claude/agents/clean-coder.md` (Windows:
95
- `%USERPROFILE%\.claude\agents\clean-coder.md`); copy into `.cursor/agents/` only if you want a repo-local duplicate.
96
-
97
- ## BUGBOT inline-lag (this path only)
98
-
99
- When Step 2 BUGBOT branch c routes to API lag and you are on **this** pacing path: complete Step 4 per **Per-tick behavior under this driver**
100
- above (fixed AHK cadence — there is no 60s shortcut). The inline comments should appear on the next tick.
101
-
102
- ## Convergence
103
-
104
- On back-to-back clean: stop the auto-typer per **Convergence cleanup** above; omit `ScheduleWakeup` (not used on this path).
105
-
106
- ## Stop / safety (this path)
107
-
108
- On hard blockers or user stop: omit loop pacing and stop the AHK auto-typer if it was started, per main skill **Stop conditions**. Use the same **scoped** `Get-CimInstance` / `Stop-Process` command as **Convergence cleanup** (command-line match on `cursor-agents-continue.ahk`) so unrelated AutoHotkey instances are not killed.