claude-dev-env 1.37.1 → 1.38.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 (94) hide show
  1. package/CLAUDE.md +3 -0
  2. package/_shared/pr-loop/audit-contract.md +4 -3
  3. package/_shared/pr-loop/fix-protocol.md +2 -0
  4. package/_shared/pr-loop/gh-payloads.md +38 -37
  5. package/_shared/pr-loop/scripts/README.md +0 -1
  6. package/_shared/pr-loop/scripts/preflight.py +2 -1
  7. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +2 -2
  8. package/_shared/pr-loop/scripts/tests/test_preflight.py +22 -0
  9. package/_shared/pr-loop/state-schema.md +10 -10
  10. package/agents/clean-coder.md +4 -0
  11. package/agents/code-quality-agent.md +23 -85
  12. package/agents/groq-coder.md +8 -6
  13. package/hooks/blocking/__init__.py +0 -0
  14. package/hooks/blocking/code_rules_enforcer.py +93 -32
  15. package/hooks/blocking/hedging_language_blocker.py +2 -2
  16. package/hooks/blocking/state_description_blocker.py +243 -0
  17. package/hooks/blocking/tdd_enforcer.py +94 -0
  18. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +158 -0
  19. package/hooks/blocking/test_hedging_language_blocker.py +1 -1
  20. package/hooks/blocking/test_state_description_blocker.py +618 -0
  21. package/hooks/blocking/test_tdd_enforcer.py +152 -0
  22. package/hooks/config/state_description_blocker_constants.py +130 -0
  23. package/hooks/hooks.json +10 -0
  24. package/package.json +1 -1
  25. package/rules/no-historical-clutter.md +31 -10
  26. package/scripts/config/groq_bugteam_config.py +13 -5
  27. package/skills/bugteam/CONSTRAINTS.md +20 -27
  28. package/skills/bugteam/EXAMPLES.md +1 -1
  29. package/skills/bugteam/PROMPTS.md +60 -31
  30. package/skills/bugteam/SKILL.md +47 -47
  31. package/skills/bugteam/SKILL_EVALS.md +8 -8
  32. package/skills/bugteam/reference/github-pr-reviews.md +31 -31
  33. package/skills/bugteam/reference/team-setup.md +1 -1
  34. package/skills/bugteam/reference/teardown-publish-permissions.md +4 -4
  35. package/skills/copilot-review/SKILL.md +7 -14
  36. package/skills/findbugs/SKILL.md +2 -2
  37. package/skills/fixbugs/SKILL.md +1 -1
  38. package/skills/monitor-open-prs/SKILL.md +6 -6
  39. package/skills/pr-converge/SKILL.md +7 -6
  40. package/skills/pr-converge/reference/convergence-gates.md +28 -30
  41. package/skills/pr-converge/reference/examples.md +4 -4
  42. package/skills/pr-converge/reference/fix-protocol.md +6 -8
  43. package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -10
  44. package/skills/pr-converge/reference/per-tick.md +18 -33
  45. package/skills/pr-converge/reference/stop-conditions.md +7 -7
  46. package/skills/pr-converge/scripts/README.md +65 -117
  47. package/skills/pr-review-responder/EXAMPLES.md +2 -2
  48. package/skills/pr-review-responder/PRINCIPLES.md +2 -8
  49. package/skills/pr-review-responder/README.md +7 -48
  50. package/skills/pr-review-responder/SKILL.md +2 -3
  51. package/skills/pr-review-responder/TESTING.md +8 -65
  52. package/skills/qbug/SKILL.md +10 -16
  53. package/_shared/pr-loop/scripts/config/gh_util_constants.py +0 -31
  54. package/_shared/pr-loop/scripts/gh_util.py +0 -193
  55. package/_shared/pr-loop/scripts/tests/test_gh_util.py +0 -257
  56. package/_shared/pr-loop/scripts/tests/test_gh_util_constants.py +0 -61
  57. package/skills/pr-converge/scripts/check_pr_mergeability.py +0 -78
  58. package/skills/pr-converge/scripts/config/pr_converge_constants.py +0 -134
  59. package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +0 -152
  60. package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +0 -70
  61. package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +0 -57
  62. package/skills/pr-converge/scripts/fetch_claude_inline_comments.py +0 -70
  63. package/skills/pr-converge/scripts/fetch_claude_reviews.py +0 -61
  64. package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +0 -70
  65. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +0 -61
  66. package/skills/pr-converge/scripts/mark_pr_ready.py +0 -54
  67. package/skills/pr-converge/scripts/post-bugbot-run.helpers.ps1 +0 -49
  68. package/skills/pr-converge/scripts/post-bugbot-run.ps1 +0 -33
  69. package/skills/pr-converge/scripts/reply_to_inline_comment.py +0 -84
  70. package/skills/pr-converge/scripts/request_copilot_review.py +0 -71
  71. package/skills/pr-converge/scripts/resolve_pr_head.py +0 -58
  72. package/skills/pr-converge/scripts/review_field_helpers.py +0 -43
  73. package/skills/pr-converge/scripts/reviewer_fetch_core.py +0 -153
  74. package/skills/pr-converge/scripts/reviewer_specs.py +0 -98
  75. package/skills/pr-converge/scripts/test_check_pr_mergeability.py +0 -126
  76. package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +0 -443
  77. package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +0 -299
  78. package/skills/pr-converge/scripts/test_fetch_claude_inline_comments.py +0 -485
  79. package/skills/pr-converge/scripts/test_fetch_claude_reviews.py +0 -368
  80. package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +0 -440
  81. package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +0 -366
  82. package/skills/pr-converge/scripts/test_mark_pr_ready.py +0 -69
  83. package/skills/pr-converge/scripts/test_post_bugbot_run.py +0 -195
  84. package/skills/pr-converge/scripts/test_reply_to_inline_comment.py +0 -159
  85. package/skills/pr-converge/scripts/test_request_copilot_review.py +0 -101
  86. package/skills/pr-converge/scripts/test_resolve_pr_head.py +0 -79
  87. package/skills/pr-converge/scripts/test_review_field_helpers.py +0 -80
  88. package/skills/pr-converge/scripts/test_reviewer_fetch_core.py +0 -448
  89. package/skills/pr-converge/scripts/test_reviewer_specs.py +0 -107
  90. package/skills/pr-converge/scripts/test_trigger_bugbot.py +0 -139
  91. package/skills/pr-converge/scripts/test_view_pr_context.py +0 -155
  92. package/skills/pr-converge/scripts/trigger_bugbot.py +0 -77
  93. package/skills/pr-converge/scripts/view_pr_context.py +0 -78
  94. package/skills/pr-review-responder/scripts/respond_to_reviews.py +0 -376
@@ -12,12 +12,12 @@ description: >-
12
12
 
13
13
  # Monitor Open PRs
14
14
 
15
- **Core principle:** One sweep covers every open PR across both owner scopes. Claude discovers PRs live via `gh search prs`, dispatches `/bugteam` per PR with `BUGTEAM_FIX_IMPLEMENTER=groq-coder` and `--bugbot-retrigger`, then polls Cursor's bugbot replies until each PR is quiet for a full backoff cycle.
15
+ **Core principle:** One sweep covers every open PR across both owner scopes. Claude discovers PRs live via `scripts/discover_open_prs.py` which shells out to `gh search prs --owner <owner> --state open --json ...`, dispatches `/bugteam` per PR with `BUGTEAM_FIX_IMPLEMENTER=groq-coder` and `--bugbot-retrigger`, then polls Cursor's bugbot replies until each PR is quiet for a full backoff cycle.
16
16
 
17
17
  ## Contents
18
18
 
19
19
  - When this skill applies — refusal cases and trigger conditions
20
- - Discovery — live `gh search prs` across both owner scopes
20
+ - Discovery — `scripts/discover_open_prs.py` queries via `gh search prs` across both owner scopes
21
21
  - Wrapping — `bws run` for GROQ_API_KEY injection
22
22
  - Dispatch — `/bugteam --bugbot-retrigger <pr_numbers...>` with groq-coder + retrigger
23
23
  - Post-convergence polling — bugbot replies and re-invocation
@@ -30,13 +30,13 @@ description: >-
30
30
  Refusals — first match wins; respond with the quoted line exactly and stop:
31
31
 
32
32
  - **bws not on PATH.** `bws not installed. /monitor-open-prs injects GROQ_API_KEY via Bitwarden Secrets Manager.`
33
- - **gh not authenticated.** `gh auth status failed. /monitor-open-prs relies on existing gh credentials.`
33
+ - **GitHub API not accessible.** `get_me failed. /monitor-open-prs needs active GitHub MCP credentials.`
34
34
  - **Dirty tree on the caller's repo.** `Uncommitted changes detected. Stash, commit, or revert before /monitor-open-prs.`
35
35
  - **Required subagents missing.** Confirm `code-quality-agent`, `clean-coder`, and `groq-coder` exist. Else: `Required subagent type <name> not installed.`
36
36
 
37
37
  ## Discovery
38
38
 
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.
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 shells out to `gh search prs --owner <owner> --state open --json number,repository,url,headRefName,baseRefName` for each owner scope 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.
40
40
 
41
41
  ## Secret Wrapping
42
42
 
@@ -50,7 +50,7 @@ bws run \
50
50
  /bugteam --bugbot-retrigger <pr_numbers...>
51
51
  ```
52
52
 
53
- The `bws run` subshell resolves the project's secrets and exports them for the wrapped command. The `GROQ_API_KEY` secret's UUID inside that project is `b7e99a7f-2ecc-42b3-99a5-b434010622f9`. GitHub auth is not sourced through `bws` — existing `gh auth` credentials carry the session.
53
+ The `bws run` subshell resolves the project's secrets and exports them for the wrapped command. The `GROQ_API_KEY` secret's UUID inside that project is `b7e99a7f-2ecc-42b3-99a5-b434010622f9`. GitHub auth is not sourced through `bws` — existing MCP `get_me` credentials carry the session.
54
54
 
55
55
  ## Dispatch
56
56
 
@@ -68,7 +68,7 @@ For each discovered PR:
68
68
  After a `/bugteam` invocation returns (converged, cap reached, stuck, or error), the PR may accumulate new Cursor bugbot comments within minutes. Poll for them:
69
69
 
70
70
  1. Baseline: capture `since_timestamp` as the PR's last commit timestamp.
71
- 2. Every 60 seconds, run `gh pr view <pr_number> --json comments --jq '.comments[] | select(.createdAt > "<since_timestamp>") | select(.author.login | test("bugbot|cursor";"i"))'`.
71
+ 2. Every 60 seconds, call `pull_request_read(method="get", pullNumber=<pr_number>, owner=<owner>, repo=<repo>)` and filter the response's `comments` array for entries whose `.author.login` matches `"bugbot"` or `"cursor"` and `.createdAt` is after `<since_timestamp>`.
72
72
  3. Back off: 60s → 120s → 240s → 480s → 960s. If five successive polls return empty, exit polling for this PR.
73
73
  4. If bugbot posts a new finding in any poll, re-invoke `/bugteam <pr_number>` via the same `bws run` wrapper with the bugbot finding text seeded into the invocation's `bugs_to_fix` preamble. Reset the backoff.
74
74
 
@@ -41,10 +41,12 @@ post a fresh PR in a fresh branch based on origin main to the user.
41
41
  - **Duplicate `bugbot run` while review queued** — skip Step 3 when the
42
42
  latest `bugbot run` PR comment has an `:eyes:` or `:+1:` reaction;
43
43
  wait for review or HEAD change before re-triggering.
44
- - **Bot logins differ between review-level and inline-comment endpoints** —
45
- Copilot reviews come from `copilot-pull-request-reviewer[bot]`, but its
46
- inline comments are authored by `Copilot`. Always use case-insensitive
47
- substring matching on `user.login`, never strict equality.
44
+ - **Bot login fields differ by endpoint** `get_reviews` returns
45
+ `.user.login` (object), but `get_review_comments` returns `.author`
46
+ (string, not an object). Threads use `is_outdated` (not `commit_id`) to
47
+ indicate staleness. Always check the correct fields and use
48
+ case-insensitive substring matching on login values, never strict
49
+ equality.
48
50
 
49
51
  ## First tick of a session
50
52
 
@@ -62,7 +64,7 @@ Read [`reference/state-schema.md`](reference/state-schema.md),
62
64
  | Multi-PR session — `state.json` exists at `<TMPDIR>/pr-converge-<session_id>/` | [`reference/multi-pr-orchestration.md`](reference/multi-pr-orchestration.md) |
63
65
  | Scheduling the next wakeup | [`workflows/schedule-wakeup-loop.md`](workflows/schedule-wakeup-loop.md) |
64
66
  | Hard blocker, convergence reached, or user stops loop | [`reference/stop-conditions.md`](reference/stop-conditions.md) |
65
- | Need to invoke a Python script (bugbot trigger, copilot fetch, mergeability check, …) | [`scripts/README.md`](scripts/README.md) |
67
+ | All GitHub interactions use `plugin:github:github` MCP tools | [`reference/per-tick.md`](reference/per-tick.md) |
66
68
  | Tick is ambiguous against the spokes above | [`reference/examples.md`](reference/examples.md) |
67
69
 
68
70
  ## Folder map
@@ -70,4 +72,3 @@ Read [`reference/state-schema.md`](reference/state-schema.md),
70
72
  - `SKILL.md` — this hub.
71
73
  - `reference/` — workflow detail per situation.
72
74
  - `workflows/` — pacing implementations.
73
- - `scripts/` — Python wrappers for `gh` API calls plus their tests.
@@ -11,12 +11,13 @@ Fetch latest Copilot reviewer (`copilot-pull-request-reviewer[bot]`) review
11
11
  plus inline comments anchored to most recent Copilot review on
12
12
  `current_head`:
13
13
 
14
- ```bash
15
- python "${CLAUDE_SKILL_DIR}/scripts/fetch_copilot_reviews.py" \
16
- --owner <OWNER> --repo <REPO> --number <NUMBER>
14
+ ```
15
+ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_reviews")
16
+ filter `.user.login` for copilot (case-insensitive substring "copilot")
17
+ → sort by `.submitted_at` descending
17
18
 
18
- python "${CLAUDE_SKILL_DIR}/scripts/fetch_copilot_inline_comments.py" \
19
- --owner <OWNER> --repo <REPO> --number <NUMBER> --commit "$current_head"
19
+ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_review_comments")
20
+ filter threads where `is_outdated == false` AND any comment has `.author` matching Copilot (case-insensitive substring "copilot")
20
21
  ```
21
22
 
22
23
  Decide (four branches; match first whose predicate holds):
@@ -24,7 +25,7 @@ Decide (four branches; match first whose predicate holds):
24
25
  - **`classification == "dirty"` with non-empty inline comments matching
25
26
  `pull_request_review_id`:** Fix protocol input (same shape as bugbot
26
27
  dirty). Spawn Agent (subagent_type: clean-coder) to implement → push → reply inline on each thread via
27
- `reply_to_inline_comment.py` → Step 3 in same tick (see
28
+ `add_reply_to_pull_request_comment` MCP → Step 3 in same tick (see
28
29
  [Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow) for
29
30
  full contract).
30
31
  Reset `bugbot_clean_at = null` AND `copilot_clean_at = null`, `phase =
@@ -34,9 +35,7 @@ Decide (four branches; match first whose predicate holds):
34
35
  `pull_request_review_id`:** Copilot posted findings only in review body
35
36
  (`CHANGES_REQUESTED` or `COMMENTED` with non-empty body, no inline
36
37
  threads). Parse body for actionable findings. Spawn Agent (subagent_type: clean-coder) to implement → push → post
37
- top-level review reply citing new HEAD SHA → Step 3 in same tick (see
38
- [Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow) for
39
- full contract).
38
+ top-level review reply using `pull_request_review_write(method="create", event="COMMENT", body)` citing new HEAD SHA → Step 3 in same tick.
40
39
  Reset
41
40
  `bugbot_clean_at = null` AND
42
41
  `copilot_clean_at = null`, `phase = BUGBOT`, Step 3 on new HEAD,
@@ -51,16 +50,16 @@ Decide (four branches; match first whose predicate holds):
51
50
 
52
51
  Resolve PR's mergeability state:
53
52
 
54
- ```bash
55
- python "${CLAUDE_SKILL_DIR}/scripts/check_pr_mergeability.py" \
56
- --owner <OWNER> --repo <REPO> --number <NUMBER>
53
+ ```
54
+ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get")
55
+ `.mergeable_state`, `.mergeable`
57
56
  ```
58
57
 
59
- Persist `mergeStateStatus` into `merge_state_status`. Decide:
58
+ Persist `mergeable_state` into `merge_state_status`. Decide:
60
59
 
61
- - **`mergeStateStatus == "CLEAN"` AND `mergeable == "MERGEABLE"`:**
60
+ - **`mergeable_state == "clean"` AND `mergeable == true`:**
62
61
  Continue to gate (c).
63
- - **`mergeStateStatus == "DIRTY"` (or `mergeable == "CONFLICTING"`):** Do
62
+ - **`mergeable_state == "dirty"` (or `mergeable == false`):** Do
64
63
  **not** mark ready. Invoke **`rebase`** skill
65
64
  ([`../../rebase/SKILL.md`](../../rebase/SKILL.md)) Phase 1–4 against PR's
66
65
  base ref. After rebase + force-with-lease push, new HEAD invalidates
@@ -68,22 +67,23 @@ Persist `mergeStateStatus` into `merge_state_status`. Decide:
68
67
  `copilot_clean_at = null`, `merge_state_status = null`, `phase = BUGBOT`,
69
68
  Step 3 on new HEAD, schedule next wakeup, return. Loop re-runs from
70
69
  scratch on new HEAD.
71
- - **`mergeStateStatus` is `BLOCKED`, `BEHIND`, or `UNKNOWN` for
70
+ - **`mergeable_state` is `"blocked"`, `"behind"`, or `"unknown"` for
72
71
  non-conflict reasons** (required checks pending, branch behind base
73
72
  without conflicts GitHub cannot auto-resolve): **hard blocker** per
74
73
  [stop-conditions.md](stop-conditions.md) — do not invent a fix. Report specific
75
- `mergeStateStatus`, omit loop pacing.
74
+ `mergeable_state`, omit loop pacing.
76
75
 
77
76
  ## (c) Post-convergence Copilot review request
78
77
 
79
78
  Once gates (a) and (b) both pass (Copilot clean at `current_head` *or* no
80
- Copilot review yet, AND `mergeStateStatus == "CLEAN"`), request Copilot
79
+ Copilot review yet, AND `mergeable_state == "clean"`), request Copilot
81
80
  review:
82
81
 
83
- ```bash
84
- python "${CLAUDE_SKILL_DIR}/scripts/request_copilot_review.py" \
85
- --owner <OWNER> --repo <REPO> --number <NUMBER>
86
82
  ```
83
+ request_copilot_review(owner=OWNER, repo=REPO, pullNumber=NUMBER)
84
+ ```
85
+
86
+ When the `request_copilot_review` MCP tool is unavailable, use `add_issue_comment` as fallback: `add_issue_comment(owner=OWNER, repo=REPO, issueNumber=NUMBER, body="@copilot review")`.
87
87
 
88
88
  After request, schedule next wakeup and return — next tick checks response.
89
89
 
@@ -91,7 +91,7 @@ Next tick with `phase == BUGTEAM` and prior state preserved → re-run gate
91
91
  (a) first. Decide:
92
92
 
93
93
  - **Copilot review `clean` (state `APPROVED`):** Set `copilot_clean_at =
94
- current_head`. Mark PR ready (`mark_pr_ready.py`), report convergence
94
+ current_head`. Mark PR ready (`update_pull_request(pullNumber=NUMBER, owner=OWNER, repo=REPO, draft=false)`), report convergence
95
95
  per §(d), terminate per [stop-conditions.md](stop-conditions.md) / Convergence.
96
96
  - **Copilot review `dirty`:** Treat identically to gate (a) dirty path —
97
97
  spawn Agent (subagent_type: clean-coder) to fix in same PR, restart convergence from BUGBOT. Follow [Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow).
@@ -107,16 +107,14 @@ Next tick with `phase == BUGTEAM` and prior state preserved → re-run gate
107
107
  ## (d) Mark ready and report
108
108
 
109
109
  Only when all four gates pass — bugbot CLEAN ∧ bugteam CLEAN ∧
110
- `mergeStateStatus == "CLEAN"` ∧ Copilot CLEAN at HEAD — run:
110
+ `mergeable_state == "clean"` ∧ Copilot CLEAN at HEAD — run:
111
111
 
112
- ```bash
113
- python "${CLAUDE_SKILL_DIR}/scripts/mark_pr_ready.py" \
114
- --owner <OWNER> --repo <REPO> --number <NUMBER>
115
- ```
112
+ Use the `update_pull_request` MCP tool:
113
+
114
+ update_pull_request(pullNumber=NUMBER, owner=OWNER, repo=REPO, draft=false)
116
115
 
117
- When scripts unavailable, `gh pr ready <NUMBER> --repo <OWNER>/<REPO>` is
118
- equivalent. With `state.json`, append convergence row to
116
+ With `state.json`, append convergence row to
119
117
  `<TMPDIR>/pr-converge-<session_id>/converged.log` per `multi-pr-orchestration.md` §Memory; else skip.
120
118
  Report: `PR #<NUMBER> converged: bugbot CLEAN at <SHA>, bugteam CLEAN at
121
- <SHA>, mergeStateStatus CLEAN, copilot CLEAN at <SHA>; marked ready for
119
+ <SHA>, mergeable_state CLEAN, copilot CLEAN at <SHA>; marked ready for
122
120
  review`. **Omit loop pacing** per **Convergence** of active pacing workflow.
@@ -25,7 +25,7 @@ convergence or stop]
25
25
  </example>
26
26
 
27
27
  <example> BUGTEAM phase, bugteam reports convergence and `bugbot_clean_at
28
- == current_head`. Claude: [runs `gh pr ready <NUMBER>`, reports "PR
28
+ == current_head`. Claude: [runs `update_pull_request(pullNumber=NUMBER, owner=OWNER, repo=REPO, draft=false)`, reports "PR
29
29
  converged: bugbot CLEAN at <SHA>, bugteam CLEAN at <SHA>; marked ready for
30
30
  review", applies **Convergence** from `workflows/schedule-wakeup-loop.md`]
31
31
  </example>
@@ -58,11 +58,11 @@ HEAD, schedules next wakeup]
58
58
  </example>
59
59
 
60
60
  <example> Back-to-back clean, mergeability CLEAN, no Copilot review on
61
- `current_head`. Claude requests Copilot via `request_copilot_review.py`,
61
+ `current_head`. Claude requests Copilot via `add_issue_comment(owner=OWNER, repo=REPO, issueNumber=NUMBER, body="@copilot review")`,
62
62
  waits one tick. Next tick: Copilot review `state: APPROVED`. Claude: [sets
63
- `copilot_clean_at = current_head`; runs `mark_pr_ready.py`; reports "PR
63
+ `copilot_clean_at = current_head`; runs `update_pull_request(pullNumber=NUMBER, owner=OWNER, repo=REPO, draft=false)`; reports "PR
64
64
  #N converged: bugbot CLEAN at <SHA>, bugteam CLEAN at <SHA>,
65
- mergeStateStatus CLEAN, copilot CLEAN; marked ready for review"]
65
+ mergeable_state clean, copilot CLEAN; marked ready for review"]
66
66
  </example>
67
67
 
68
68
  <example> Back-to-back clean, mergeability CLEAN, post-convergence Copilot
@@ -8,8 +8,8 @@ per [ground-rules.md](ground-rules.md).
8
8
  **Multi-PR (`state.json`) teammate obligations** (plus TDD, commit, push):
9
9
 
10
10
  - Replies inline on each addressed finding via
11
- `reply_to_inline_comment.py` (what changed + commit identifier),
12
- matching §Audit result → fix worker step 4 — **before** writing
11
+ `add_reply_to_pull_request_comment(owner, repo, pullNumber, commentId, body)`
12
+ (what changed + commit identifier), matching §Audit result → fix worker step 4 — **before** writing
13
13
  `state.json` and going idle.
14
14
  - Writes `last_action: "fix_pushed"`, `current_head: <new SHA>`,
15
15
  `bugbot_clean_at: null`, `phase: "BUGBOT"`, `status: "awaiting_bugbot"`,
@@ -40,12 +40,10 @@ git push origin <BRANCH>
40
40
  ```
41
41
  **Pre-push gate:** honor hooks; full-stop on bypass. Capture new HEAD
42
42
  only after both gates pass; set `current_head`, `bugbot_clean_at = null`.
43
- - Reply inline on each addressed comment thread using `--body-file` (per
44
- gh-body-file rule):
45
- ```bash
46
- python "${CLAUDE_SKILL_DIR}/scripts/reply_to_inline_comment.py" \
47
- --owner <OWNER> --repo <REPO> --number <NUMBER> \
48
- --comment-id <COMMENT_ID> --body-file <path/to/reply.md>
43
+ - Reply inline on each addressed comment thread using the `add_reply_to_pull_request_comment` MCP tool:
44
+
45
+ ```
46
+ add_reply_to_pull_request_comment(owner=OWNER, repo=REPO, pullNumber=NUMBER, commentId=COMMENT_ID, body="Fixed in <SHA> — <what changed>")
49
47
  ```
50
48
  - **After pushing a fix, always run Step 3 (`bugbot run`) in the same
51
49
  tick** regardless of phase. New commit **resets full convergence cycle**:
@@ -25,7 +25,7 @@ Create once at session start. Each teammate writes result before going idle.
25
25
 
26
26
  **Directory lifecycle:** Keep `<TMPDIR>/pr-converge-<session_id>/` until every
27
27
  `prs[...]` is `converged` or `blocked`, or user stops. Then delete folder.
28
- `mark_pr_ready.py` / `gh pr ready` on GitHub is canonical record. See
28
+ `update_pull_request(draft=false)` on GitHub is canonical record. See
29
29
  [Memory](#memory) for optional append-only log.
30
30
 
31
31
  **Barebones schema:**
@@ -107,7 +107,7 @@ Bugfind subagent completes (findings or clean):
107
107
  2. Applies TDD fixes (test first, then production).
108
108
  3. Commits, pushes one fix commit.
109
109
  4. Replies inline on each addressed finding via
110
- `reply_to_inline_comment.py`.
110
+ `add_reply_to_pull_request_comment(owner, repo, pullNumber, commentId, body)`.
111
111
  5. Writes `state.json` (per §Concurrency): `last_action: "fix_pushed"`,
112
112
  `current_head: <new SHA>`, `bugbot_clean_at: null`, `phase:
113
113
  "BUGBOT"`, `status: "awaiting_bugbot"`, `last_updated` ISO-8601 UTC.
@@ -116,7 +116,7 @@ Bugfind subagent completes (findings or clean):
116
116
  - **PRs with zero findings:** spawn one `general-purpose` subagent per PR via
117
117
  `Agent(subagent_type="general-purpose", run_in_background=true)`. Subagent:
118
118
  1. `bugbot_clean_at == current_head` (back-to-back clean): run
119
- `mark_pr_ready.py`, append convergence row to
119
+ `update_pull_request(pullNumber=PR_NUMBER, owner=OWNER, repo=REPO, draft=false)`, append convergence row to
120
120
  `<TMPDIR>/pr-converge-<session_id>/converged.log` per §Memory, then
121
121
  write `state.json` (per §Concurrency) with `status: "converged"`,
122
122
  `last_action: "converged"` (or `marked_ready`), `phase: "BUGBOT"`,
@@ -125,7 +125,7 @@ Bugfind subagent completes (findings or clean):
125
125
  duplicate work.
126
126
  2. Else: update `state.json` (per §Concurrency) with `last_action:
127
127
  "audit_clean"`, `status: "awaiting_bugbot"`, `phase: "BUGBOT"`, then
128
- trigger bugbot via `trigger_bugbot.py`.
128
+ trigger bugbot via `add_issue_comment(owner, repo, issueNumber, body="bugbot run")`.
129
129
  3. Goes idle.
130
130
 
131
131
  ### Fix result → general-purpose per PR
@@ -135,11 +135,11 @@ When bugfix (clean-coder) subagent completes after push:
135
135
  - Spawn one `general-purpose` subagent per PR via
136
136
  `Agent(subagent_type="general-purpose", run_in_background=true)`. Subagent:
137
137
  1. Reads `state.json` for its PR.
138
- 2. Triggers bugbot via `trigger_bugbot.py`.
139
- 3. Polls `fetch_bugbot_reviews.py` every 60s (up to 10 polls) until review
140
- anchored to `current_head` appears.
138
+ 2. Triggers bugbot via `add_issue_comment(owner, repo, issueNumber, body="bugbot run")`.
139
+ 3. Polls `pull_request_read(method="get_reviews")` every 60s (up to 10 polls) until review
140
+ anchored to `current_head` appears with `commit_id == current_head`.
141
141
  4. **Poll / classify loop** (repeat from 4a whenever 4c retries):
142
- - **4a.** Fetch inline comments via `fetch_bugbot_inline_comments.py`.
142
+ - **4a.** Fetch inline comments via `pull_request_read(method="get_review_comments")` filtered by review ID and `commit_id == current_head`.
143
143
  - **4b.** Classify — three outcomes (same as `SKILL.md` Step 2 BUGBOT):
144
144
  - **`clean`:** review body clean, zero unaddressed inline findings.
145
145
  - **`dirty`:** ≥1 unaddressed inline finding for `current_head`
@@ -186,7 +186,7 @@ When bugfix (clean-coder) subagent completes after push:
186
186
  Run directory `<TMPDIR>/pr-converge-<session_id>/` holds `state.json` and
187
187
  optional `converged.log`. Keep from first create until every PR under `prs`
188
188
  is `converged` or `blocked`, or **Stop conditions** ends loop. Safe to
189
- delete folder after — `mark_pr_ready.py` / `gh pr ready` on GitHub is
189
+ delete folder after — `update_pull_request(draft=false)` on GitHub is
190
190
  canonical record. Folder skill, not a plugin package; do **not** rely
191
191
  on `${CLAUDE_PLUGIN_DATA}`. OS/disk cleanup of `<TMPDIR>` (reboot, policy)
192
192
  can remove files mid-run — environmental risk.
@@ -196,7 +196,7 @@ can remove files mid-run — environmental risk.
196
196
  - **Path:** sibling of `state.json`.
197
197
  - **Format:** one tab-separated row per converged PR: ISO8601 UTC,
198
198
  owner/repo#number, bugbot SHA, bugteam SHA.
199
- - **Append site:** agent running `mark_pr_ready.py`. Append **before**
199
+ - **Append site:** agent running `update_pull_request(pullNumber=PR_NUMBER, owner=OWNER, repo=REPO, draft=false)`. Append **before**
200
200
  locked `state.json` publish so log row survives failed merge.
201
201
  - **Never read inside loop.** User / follow-up tooling only.
202
202
 
@@ -37,12 +37,12 @@ state line when **no** `state.json` (single-PR only). With `state.json`, do
37
37
  **not** increment here — orchestrator's per-tick bump is sole increment.
38
38
 
39
39
  ```bash
40
- python "${CLAUDE_SKILL_DIR}/scripts/view_pr_context.py" --owner <OWNER> --repo <REPO> --number <NUMBER>
40
+ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get") → `.head.sha`
41
41
  ```
42
42
 
43
- If owner/repo/number are not yet known, extract them from the PR URL or run without flags in a repo checkout.
43
+ If owner/repo/number are not yet known, extract them from the PR URL.
44
44
 
45
- Capture `number`, `headRefOid` (= `current_head`), owner/repo, branch.
45
+ Capture `number`, `head.sha` (= `current_head`), owner/repo, branch.
46
46
 
47
47
  ## Step 2: Branch on `phase`
48
48
 
@@ -50,15 +50,14 @@ Capture `number`, `headRefOid` (= `current_head`), owner/repo, branch.
50
50
 
51
51
  a. Fetch Cursor Bugbot reviews newest-first, walk back until first clean:
52
52
 
53
- ```bash
54
- python "${CLAUDE_SKILL_DIR}/scripts/fetch_bugbot_reviews.py" \
55
- --owner <OWNER> --repo <REPO> --number <NUMBER>
53
+ ```
54
+ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_reviews")
55
+ filter `.user.login` for cursor/bugbot, sort by `.submitted_at` descending
56
56
  ```
57
57
 
58
- Track dirty entries in a temp file; Fix protocol reads it back later
59
- this tick.
58
+ Track dirty entries (review body contains `BUGBOT_REVIEW` markers with finding content); Fix protocol reads them back later this tick.
60
59
 
61
- Iterate from index 0 (most recent) toward older:
60
+ Iterate from index 0 (most recent) toward older:
62
61
 
63
62
  - Dirty review → append JSON line with `{review_id, commit_id,
64
63
  submitted_at, body}`.
@@ -71,14 +70,12 @@ review for decisions below. When branch routes to **Fix protocol**, address
71
70
  **every** entry in `$dirty_reviews_path` — not just index 0.
72
71
 
73
72
  b. Fetch unaddressed inline comments from `cursor[bot]` for newest Bugbot
74
- review on `current_head`. Script uses same `--paginate --slurp` pattern,
75
- resolves review via reviews list, returns only inline rows whose
76
- `pull_request_review_id` matches that review (excludes stale threads from
77
- older reviews on same SHA).
73
+ review on `current_head`:
78
74
 
79
- ```bash
80
- python "${CLAUDE_SKILL_DIR}/scripts/fetch_bugbot_inline_comments.py" \
81
- --owner <OWNER> --repo <REPO> --number <NUMBER> --commit "$current_head"
75
+ ```
76
+ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_review_comments")
77
+ filter threads where `is_outdated == false` AND `is_resolved == false`
78
+ AND any comment has `.author` matching cursor/bugbot (case-insensitive substring)
82
79
  ```
83
80
 
84
81
  c. Decide (four branches; match first whose predicate holds):
@@ -96,7 +93,7 @@ c. Decide (four branches; match first whose predicate holds):
96
93
  `state.json`, goes idle; Step 3 on new HEAD runs after via
97
94
  orchestrator-spawned follow-up agent (§Fix result → general-purpose).
98
95
  No `state.json` (single-PR): spawn Agent (subagent_type: clean-coder) to implement → push → reply inline on each thread
99
- via `reply_to_inline_comment.py` → Step 3 in same tick (see
96
+ via `add_reply_to_pull_request_comment` MCP → Step 3 in same tick (see
100
97
  [Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow) for
101
98
  full contract).
102
99
  Schedule next wakeup, return.
@@ -125,8 +122,7 @@ Skill({skill: "bugteam", args:
125
122
  b. **Re-resolve current HEAD** — bugteam may have pushed commits during
126
123
  its run. `current_head` from Step 1 is potentially stale:
127
124
  ```bash
128
- new_head=$(python "${CLAUDE_SKILL_DIR}/scripts/resolve_pr_head.py" \
129
- --owner <OWNER> --repo <REPO> --number <NUMBER>)
125
+ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get") → `.head.sha`
130
126
  ```
131
127
  If `new_head != current_head`, set `current_head = new_head` AND
132
128
  `bugbot_clean_at = null`. New commits invalidate bugbot's prior clean.
@@ -146,25 +142,14 @@ never falsely terminates:
146
142
  **omit loop pacing** per **Convergence** of active pacing workflow.
147
143
  - **Convergence BUT `bugbot_clean_at != current_head` (no push):**
148
144
  `phase = BUGBOT`, schedule next wakeup, return.
149
- - **Findings without committed fixes:** spawn Agent (subagent_type: clean-coder) to implement fixes and push, then reply inline via `reply_to_inline_comment.py`, following [Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow).
145
+ - **Findings without committed fixes:** spawn Agent (subagent_type: clean-coder) to implement fixes and push, then reply inline via `add_reply_to_pull_request_comment` MCP, following [Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow).
150
146
  `phase = BUGBOT`, schedule next wakeup, return.
151
147
 
152
148
  ## Step 3: Re-trigger bugbot
153
149
 
154
- Prefer portable script (temp body file, `gh pr comment --body-file`):
155
-
156
- ```bash
157
- python "${CLAUDE_SKILL_DIR}/scripts/trigger_bugbot.py" \
158
- --owner <OWNER> --repo <REPO> --number <NUMBER>
159
- ```
160
-
161
- **Bundled PowerShell alternative** (same gh-body-file contract):
150
+ Use the `add_issue_comment` MCP tool:
162
151
 
163
- ```bash
164
- POST_BUGBOT_RUN="$HOME/.claude/skills/pr-converge/scripts/post-bugbot-run.ps1"
165
- pwsh -NoProfile -ExecutionPolicy Bypass -File "$POST_BUGBOT_RUN" \
166
- "https://github.com/<OWNER>/<REPO>/pull/<NUMBER>"
167
- ```
152
+ add_issue_comment(owner="OWNER", repo="REPO", issueNumber=NUMBER, body="bugbot run")
168
153
 
169
154
  `bugbot run` is empirically the only re-trigger Cursor Bugbot recognizes;
170
155
  alternative phrasings (`re-review`, `bugbot please`, etc.) silently no-op.
@@ -1,9 +1,9 @@
1
1
  # Stop conditions
2
2
 
3
3
  - **Convergence** (back-to-back clean ∧ no outstanding Copilot findings
4
- on `current_head` ∧ `mergeStateStatus == "CLEAN"` with `mergeable ==
5
- "MERGEABLE"` ∧ post-convergence Copilot request returned `clean` at
6
- `current_head`): prefer `mark_pr_ready.py`; else `gh pr ready`. With
4
+ on `current_head` ∧ `mergeable_state == "clean"` with `mergeable ==
5
+ true` ∧ post-convergence Copilot request returned `clean` at
6
+ `current_head`): use `update_pull_request(pullNumber=NUMBER, owner=OWNER, repo=REPO, draft=false)`. With
7
7
  `state.json`, append convergence row to
8
8
  `<TMPDIR>/pr-converge-<session_id>/converged.log` per `multi-pr-orchestration.md` §Memory; else
9
9
  skip. Report [convergence-gates.md](convergence-gates.md) (d) summary, then **omit loop pacing**
@@ -16,11 +16,11 @@
16
16
  `current_head` after three consecutive wakeups. Report specific
17
17
  blocker and diagnosis, **omit loop pacing** per
18
18
  `../workflows/schedule-wakeup-loop.md`.
19
- - **Hard blocker (`mergeStateStatus` non-CLEAN non-DIRTY):**
20
- `mergeStateStatus` is `BLOCKED`, `UNKNOWN`, or `BEHIND` (required
19
+ - **Hard blocker (`mergeable_state` non-clean non-dirty):**
20
+ `mergeable_state` is `"blocked"`, `"unknown"`, or `"behind"` (required
21
21
  checks pending, branch behind base without textual conflicts, or
22
22
  GitHub indeterminate). Investigate before retrying; `rebase` skill
23
- handles `DIRTY` (textual conflicts) only. Report specific
24
- `mergeStateStatus`, **omit loop pacing**.
23
+ handles `"dirty"` (textual conflicts) only. Report specific
24
+ `mergeable_state`, **omit loop pacing**.
25
25
  - **User stops loop:** "stop the converge loop" → **omit loop pacing**
26
26
  per `../workflows/schedule-wakeup-loop.md`.