@muggleai/works 4.11.4 → 4.12.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 (96) hide show
  1. package/dist/{chunk-E52757MK.js → chunk-2DVZ2LYO.js} +5 -0
  2. package/dist/cli.js +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  5. package/dist/plugin/.cursor-plugin/plugin.json +1 -1
  6. package/dist/plugin/scripts/ensure-electron-app.sh +29 -4
  7. package/dist/plugin/skills/_shared/github-cli-recipes/line-comments-for-review.md +8 -0
  8. package/dist/plugin/skills/_shared/github-cli-recipes/loop-user-identity.md +9 -0
  9. package/dist/plugin/skills/_shared/github-cli-recipes/pr-edit.md +8 -0
  10. package/dist/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +12 -0
  11. package/dist/plugin/skills/_shared/github-cli-recipes/push-to-branch.md +10 -0
  12. package/dist/plugin/skills/_shared/github-cli-recipes/reply-line-comment.md +10 -0
  13. package/dist/plugin/skills/_shared/github-cli-recipes/submitted-reviews.md +15 -0
  14. package/dist/plugin/skills/_shared/github-cli-recipes/top-level-comment.md +7 -0
  15. package/dist/plugin/skills/_shared/github-cli-recipes/unresolved-threads.md +33 -0
  16. package/dist/plugin/skills/_shared/github-cli-recipes/verify-working-tree.md +17 -0
  17. package/dist/plugin/skills/_shared/github-cli-recipes.md +20 -0
  18. package/dist/plugin/skills/_shared/pr-followup-helpers/allow-list.md +53 -0
  19. package/dist/plugin/skills/_shared/pr-followup-helpers/classify.md +48 -0
  20. package/dist/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +40 -0
  21. package/dist/plugin/skills/_shared/pr-followup-helpers.md +8 -146
  22. package/dist/plugin/skills/_shared/telemetry-events/muggle-do-cycle.md +25 -0
  23. package/dist/plugin/skills/_shared/telemetry-events/muggle-do-escalation.md +19 -0
  24. package/dist/plugin/skills/_shared/telemetry-events/muggle-do-resolve-reminder.md +19 -0
  25. package/dist/plugin/skills/_shared/telemetry-events/pr-followup-bootstrap.md +20 -0
  26. package/dist/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +23 -0
  27. package/dist/plugin/skills/_shared/telemetry-events.md +15 -0
  28. package/dist/plugin/skills/do/address-reviews.md +137 -0
  29. package/dist/plugin/skills/do/build.md +5 -5
  30. package/dist/plugin/skills/do/open-prs/forward.md +80 -0
  31. package/dist/plugin/skills/do/open-prs/update.md +54 -0
  32. package/dist/plugin/skills/do/open-prs.md +6 -102
  33. package/dist/plugin/skills/do/per-comment-replies.md +61 -0
  34. package/dist/plugin/skills/do/resolve-reminder.md +70 -0
  35. package/dist/plugin/skills/muggle-do/SKILL.md +41 -45
  36. package/dist/plugin/skills/muggle-pr-followup/CLAUDE.md +25 -0
  37. package/dist/plugin/skills/muggle-pr-followup/SKILL.md +21 -43
  38. package/dist/plugin/skills/muggle-pr-followup/bootstrap.md +88 -0
  39. package/dist/plugin/skills/muggle-pr-followup/contract.md +44 -161
  40. package/dist/plugin/skills/muggle-pr-followup/output-templates/bootstrap.md +42 -0
  41. package/dist/plugin/skills/muggle-pr-followup/output-templates/escalation.md +32 -0
  42. package/dist/plugin/skills/muggle-pr-followup/output-templates/help.md +17 -0
  43. package/dist/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +17 -0
  44. package/dist/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +12 -0
  45. package/dist/plugin/skills/muggle-pr-followup/output-templates/watcher-log.md +21 -0
  46. package/dist/plugin/skills/muggle-pr-followup/output-templates.md +14 -0
  47. package/dist/plugin/skills/muggle-pr-followup/state-schemas.md +104 -0
  48. package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +3 -3
  49. package/dist/release-manifest.json +4 -4
  50. package/package.json +6 -6
  51. package/plugin/.claude-plugin/plugin.json +1 -1
  52. package/plugin/.cursor-plugin/plugin.json +1 -1
  53. package/plugin/scripts/ensure-electron-app.sh +29 -4
  54. package/plugin/skills/_shared/github-cli-recipes/line-comments-for-review.md +8 -0
  55. package/plugin/skills/_shared/github-cli-recipes/loop-user-identity.md +9 -0
  56. package/plugin/skills/_shared/github-cli-recipes/pr-edit.md +8 -0
  57. package/plugin/skills/_shared/github-cli-recipes/pr-metadata.md +12 -0
  58. package/plugin/skills/_shared/github-cli-recipes/push-to-branch.md +10 -0
  59. package/plugin/skills/_shared/github-cli-recipes/reply-line-comment.md +10 -0
  60. package/plugin/skills/_shared/github-cli-recipes/submitted-reviews.md +15 -0
  61. package/plugin/skills/_shared/github-cli-recipes/top-level-comment.md +7 -0
  62. package/plugin/skills/_shared/github-cli-recipes/unresolved-threads.md +33 -0
  63. package/plugin/skills/_shared/github-cli-recipes/verify-working-tree.md +17 -0
  64. package/plugin/skills/_shared/github-cli-recipes.md +20 -0
  65. package/plugin/skills/_shared/pr-followup-helpers/allow-list.md +53 -0
  66. package/plugin/skills/_shared/pr-followup-helpers/classify.md +48 -0
  67. package/plugin/skills/_shared/pr-followup-helpers/reply-routing.md +40 -0
  68. package/plugin/skills/_shared/pr-followup-helpers.md +8 -146
  69. package/plugin/skills/_shared/telemetry-events/muggle-do-cycle.md +25 -0
  70. package/plugin/skills/_shared/telemetry-events/muggle-do-escalation.md +19 -0
  71. package/plugin/skills/_shared/telemetry-events/muggle-do-resolve-reminder.md +19 -0
  72. package/plugin/skills/_shared/telemetry-events/pr-followup-bootstrap.md +20 -0
  73. package/plugin/skills/_shared/telemetry-events/pr-followup-tick.md +23 -0
  74. package/plugin/skills/_shared/telemetry-events.md +15 -0
  75. package/plugin/skills/do/address-reviews.md +137 -0
  76. package/plugin/skills/do/build.md +5 -5
  77. package/plugin/skills/do/open-prs/forward.md +80 -0
  78. package/plugin/skills/do/open-prs/update.md +54 -0
  79. package/plugin/skills/do/open-prs.md +6 -102
  80. package/plugin/skills/do/per-comment-replies.md +61 -0
  81. package/plugin/skills/do/resolve-reminder.md +70 -0
  82. package/plugin/skills/muggle-do/SKILL.md +41 -45
  83. package/plugin/skills/muggle-pr-followup/CLAUDE.md +25 -0
  84. package/plugin/skills/muggle-pr-followup/SKILL.md +21 -43
  85. package/plugin/skills/muggle-pr-followup/bootstrap.md +88 -0
  86. package/plugin/skills/muggle-pr-followup/contract.md +44 -161
  87. package/plugin/skills/muggle-pr-followup/output-templates/bootstrap.md +42 -0
  88. package/plugin/skills/muggle-pr-followup/output-templates/escalation.md +32 -0
  89. package/plugin/skills/muggle-pr-followup/output-templates/help.md +17 -0
  90. package/plugin/skills/muggle-pr-followup/output-templates/inline-reply.md +17 -0
  91. package/plugin/skills/muggle-pr-followup/output-templates/resolve-reminder.md +12 -0
  92. package/plugin/skills/muggle-pr-followup/output-templates/watcher-log.md +21 -0
  93. package/plugin/skills/muggle-pr-followup/output-templates.md +14 -0
  94. package/plugin/skills/muggle-pr-followup/state-schemas.md +104 -0
  95. package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +3 -3
  96. package/scripts/postinstall.mjs +36 -5
@@ -0,0 +1,32 @@
1
+ # Escalation messages
2
+
3
+ Emitted by `/muggle-do` in address-reviews mode. One per invocation, at most. Anchored here so wording stays consistent.
4
+
5
+ ## Ambiguous escalation
6
+
7
+ ```
8
+ **Review-followup escalation — <owner>/<repo>#<n>**
9
+
10
+ I can't act on <count> review(s) without your input. Listed below; reply on GitHub by submitting a new review with clearer direction, or tell me here which way to go.
11
+
12
+ Review #<id> from <login>:
13
+ > <body or "(no body)">
14
+
15
+ Comments:
16
+ - <file>:<line> — <body>
17
+ - ...
18
+
19
+ [Repeat per ambiguous review in the batch.]
20
+ ```
21
+
22
+ ## Design-adjustment escalation
23
+
24
+ ```
25
+ **Design-adjustment needed — <owner>/<repo>#<n>**
26
+
27
+ While addressing review #<id>, the cycle surfaced a conflict with the current design:
28
+
29
+ <one-paragraph description of the conflict>
30
+
31
+ This is beyond a routine code change. Confirm the design intent (reply here, or update the requirements doc) before I retry.
32
+ ```
@@ -0,0 +1,17 @@
1
+ # Help output
2
+
3
+ For `/muggle:muggle-pr-followup` with no args, or with `help` / `?`:
4
+
5
+ ```
6
+ muggle-pr-followup — watcher loop for PR review follow-ups
7
+
8
+ Active loops:
9
+ <slug> → <owner>/<repo>#<n> (cursor @ review #<id>, <N> cycles)
10
+ ...
11
+ (or "no active loops")
12
+
13
+ Usage:
14
+ /muggle:muggle-pr-followup <pr-url> → bootstrap a new loop
15
+ /muggle:muggle-pr-followup <slug> <pr-number> → run one tick (called by /loop)
16
+ /muggle:muggle-pr-followup <pr-number> → run one tick (slug inferred from on-disk state)
17
+ ```
@@ -0,0 +1,17 @@
1
+ # Per-comment inline reply
2
+
3
+ Posted via `gh api .../comments/<comment-id>/replies` per cycle, one per line comment.
4
+
5
+ ```
6
+ Addressed in <short-sha>: <one-line summary of the change made for THIS comment>.
7
+ ```
8
+
9
+ `<short-sha>` is the first 7 chars of the new SHA. The body must contain that substring — the resolve-reminder stage greps for it to classify threads as addressed-by-loop.
10
+
11
+ ## Top-level fallback (review-body-only)
12
+
13
+ When an actionable review has a non-empty body but zero line comments, GitHub has no `/replies` endpoint for the body. Post a top-level PR comment instead, at most once per such review:
14
+
15
+ ```
16
+ Re: review #<review_id> — addressed in <short-sha>: <one-line summary>.
17
+ ```
@@ -0,0 +1,12 @@
1
+ # Resolve-reminder top-level PR comment
2
+
3
+ Posted via `gh pr comment` after the resolve-reminder stage scans threads. Only when at least one addressed-by-loop thread exists.
4
+
5
+ ```
6
+ I addressed these threads in <short-sha> — mark them resolved when satisfied:
7
+ - #<thread-id-1>
8
+ - #<thread-id-2>
9
+ - ...
10
+ ```
11
+
12
+ If `addressed_by_loop == 0`, no comment is posted (silent).
@@ -0,0 +1,21 @@
1
+ # Watcher tick log lines
2
+
3
+ The watcher does **not** print to the user during normal operation. It only appends to `followup.log`. All user-facing escalations come from `/muggle-do`.
4
+
5
+ ## Idle tick
6
+
7
+ ```
8
+ <ISO-8601> tick pr=<n> reviews_seen=0 idle
9
+ ```
10
+
11
+ ## Dispatching tick
12
+
13
+ ```
14
+ <ISO-8601> tick pr=<n> reviews_seen=<count> dispatched=<id1>,<id2>,...
15
+ ```
16
+
17
+ ## Terminal tick
18
+
19
+ ```
20
+ <ISO-8601> tick pr=<n> terminal=<merged|closed> result.md written
21
+ ```
@@ -0,0 +1,14 @@
1
+ # Output templates
2
+
3
+ All user-facing message text used by the watcher, bootstrap, and `/muggle-do` in address-reviews mode. Each template group is its own file — load only what the current step emits.
4
+
5
+ ## Index
6
+
7
+ | Group | Use case |
8
+ | :---- | :------- |
9
+ | [`bootstrap`](output-templates/bootstrap.md) | Bootstrap success summary + all bootstrap aborts. |
10
+ | [`watcher-log`](output-templates/watcher-log.md) | The three `followup.log` line shapes (idle, dispatching, terminal). |
11
+ | [`escalation`](output-templates/escalation.md) | `/muggle-do` terminal escalation messages (ambiguous, design-adjustment). |
12
+ | [`inline-reply`](output-templates/inline-reply.md) | Per-comment inline reply + top-level fallback for body-only reviews. |
13
+ | [`resolve-reminder`](output-templates/resolve-reminder.md) | Top-level PR comment for the resolve-reminder stage. |
14
+ | [`help`](output-templates/help.md) | Skill help output (no args / `help` / `?`). |
@@ -0,0 +1,104 @@
1
+ # Session State Schemas
2
+
3
+ Canonical shapes for the JSON files in a PR-follow-up session slot. The slot path is `.muggle-do/sessions/<slug>/` (the caller's session dir; `muggle-do` is the current and only caller).
4
+
5
+ All files are atomic writes — the caller rewrites the whole file each time, never mutates in place. Use a temp file + rename if the platform supports it.
6
+
7
+ ## `prs.json`
8
+
9
+ A list of one entry. (Historical: the file is an array for forward-compat with the original session-wide model. Today, each PR has its own session slot, so the array always has exactly one entry.)
10
+
11
+ ```json
12
+ [
13
+ {
14
+ "repo": "<owner>/<repo>",
15
+ "number": <int>,
16
+ "url": "https://github.com/<owner>/<repo>/pull/<number>",
17
+ "head_sha": "<40-char-hex-sha>",
18
+ "state": "open" | "merged" | "closed"
19
+ }
20
+ ]
21
+ ```
22
+
23
+ - `state` is the **observed** state from the last `gh pr view`. The watcher refreshes it each tick.
24
+ - Terminal states (`merged`, `closed`) are sticky — once set, the watcher writes `result.md` and exits without rescheduling.
25
+
26
+ ## `last_seen.json`
27
+
28
+ Keyed by `"<owner>/<repo>#<n>"`. One key per PR in the slot.
29
+
30
+ ```json
31
+ {
32
+ "<owner>/<repo>#<n>": {
33
+ "reviewId": <int>,
34
+ "last_pushed_sha": "<sha-or-null>",
35
+ "idle_tick_count": <int>,
36
+ "cycles_completed": <int>,
37
+ "escalated_review_ids": [<int>, ...],
38
+ "pushed_shas": ["<sha>", ...]
39
+ }
40
+ }
41
+ ```
42
+
43
+ - `reviewId`: the cursor. The watcher fetches reviews with `id > reviewId`. Bootstrap pins this to the highest existing submitted review id (or `0` if none).
44
+ - `last_pushed_sha`: most recent SHA `/muggle-do` pushed in this PR's life; `null` until the first push.
45
+ - `idle_tick_count`: incremented each tick that sees zero new reviews. Reset to 0 on any tick that dispatches `/muggle-do`. Diagnostic only — does not gate behavior.
46
+ - `cycles_completed`: incremented each time `/muggle-do` completes an address-reviews invocation (regardless of actionable/ambiguous/mixed).
47
+ - `escalated_review_ids`: review ids classified as ambiguous by `/muggle-do`. The watcher excludes these from future review fetches so the same ambiguous review is never re-dispatched.
48
+ - `pushed_shas`: every SHA `/muggle-do` has pushed for this PR. Append-only. Used by the resolve-reminder stage to recognize threads addressed by the loop.
49
+
50
+ ## `state.md`
51
+
52
+ Free-form markdown. No required schema beyond a few well-known fields the caller-agnostic code reads:
53
+
54
+ ```markdown
55
+ # Session — <slug>
56
+
57
+ **PR:** <url>
58
+ **Slug:** <slug>
59
+ **Loop user:** <github-login> ← cached from `gh api user`
60
+ **Created:** <ISO-8601>
61
+ **Bootstrapped from URL:** <yes | no>
62
+
63
+ ...free-form notes added by /muggle-do and bootstrap...
64
+ ```
65
+
66
+ The watcher does **not** read or write `state.md`. Only bootstrap, `/muggle-do`, and the caller's stages touch it.
67
+
68
+ ## `followup.log`
69
+
70
+ Append-only line-per-tick log. One line per watcher tick, plus one line per `/muggle-do` invocation. Format is loose, but each line starts with an ISO-8601 timestamp:
71
+
72
+ ```
73
+ 2026-05-20T12:34:56Z tick pr=154 reviews_seen=0 idle
74
+ 2026-05-20T12:35:56Z tick pr=154 reviews_seen=1 dispatched=4295962800
75
+ 2026-05-20T12:36:14Z muggle-do cycle review_ids=[4295962800] outcome=pushed head_sha=abc1234
76
+ ```
77
+
78
+ Used for forensics only — never read back by skills.
79
+
80
+ ## `result.md`
81
+
82
+ Written exactly once when the PR's watcher exits terminally (PR merged or closed). Free-form markdown summarizing the PR's life under this loop:
83
+
84
+ ```markdown
85
+ # Result — <slug>
86
+
87
+ **PR:** <url>
88
+ **Final state:** merged | closed
89
+ **Cycles completed:** <int>
90
+ **Pushed SHAs:** <comma-separated list>
91
+ **Escalated review ids:** <comma-separated list or "none">
92
+
93
+ ## Timeline
94
+
95
+ - <ISO-8601> bootstrap (cursor pinned at <reviewId>)
96
+ - <ISO-8601> review <id> from <login> — actionable, pushed <sha>
97
+ - <ISO-8601> review <id> from <login> — ambiguous, escalated
98
+ - ...
99
+ - <ISO-8601> PR <merged|closed> — watcher terminal
100
+ ```
101
+
102
+ ## Not in the slot
103
+
104
+ `cycle.json` and `requirements.md` are not seeded or read. `/muggle-do` reads reviews off GitHub each invocation.
@@ -180,15 +180,15 @@ Bulk regen does not run replays, so the section B (replay) router does not apply
180
180
 
181
181
  ### Step 8 — Open the Dashboard
182
182
 
183
- Open the Muggle AI dashboard so the user can watch progress visually:
183
+ Open the Muggle AI dashboard so the user can watch progress visually. Point them at the **scripts** page (where DRAFT → GENERATING → ACTIVE transitions are visible), not `/runs` (which is for replay batches and shows nothing for a regen):
184
184
 
185
185
  ```bash
186
- open "https://www.muggle-ai.com/muggleTestV0/dashboard/projects/{projectId}/runs"
186
+ open "https://www.muggle-ai.com/muggleTestV0/dashboard/projects/{projectId}/scripts"
187
187
  ```
188
188
 
189
189
  Tell them:
190
190
 
191
- > "I've opened the project's runs page. Generation jobs typically take a few minutes each — they'll appear here as they progress. Your test cases will move into `ACTIVE` status as scripts complete."
191
+ > "I've opened the project's scripts page. Generation jobs typically take a few minutes each — your test cases will move from `DRAFT` through `GENERATING` to `ACTIVE` as scripts complete."
192
192
 
193
193
  ### Step 9 (optional) — Poll Status
194
194
 
@@ -1,7 +1,7 @@
1
1
  {
2
- "release": "4.11.4",
3
- "buildId": "run-37-1",
4
- "commitSha": "36a1f8499178f2361b5a683ddd5420de28558c23",
5
- "buildTime": "2026-05-20T22:12:49Z",
2
+ "release": "4.12.0",
3
+ "buildId": "run-38-1",
4
+ "commitSha": "8a01015dc5a95c7b5a34e982132e8ca4a1c0d9f1",
5
+ "buildTime": "2026-05-21T18:38:53Z",
6
6
  "serviceName": "muggle-ai-works-mcp"
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@muggleai/works",
3
3
  "mcpName": "io.github.multiplex-ai/muggle",
4
- "version": "4.11.4",
4
+ "version": "4.12.0",
5
5
  "description": "Ship quality products with AI-powered E2E acceptance testing that validates your web app like a real user — from Claude Code and Cursor to PR.",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -41,14 +41,14 @@
41
41
  "test:watch": "vitest"
42
42
  },
43
43
  "muggleConfig": {
44
- "electronAppVersion": "1.0.88",
44
+ "electronAppVersion": "1.0.90",
45
45
  "downloadBaseUrl": "https://github.com/multiplex-ai/muggle-ai-works/releases/download",
46
46
  "runtimeTargetDefault": "production",
47
47
  "checksums": {
48
- "darwin-arm64": "dbcefc10f2cdeabe8be74490354ed83f600eb18d8f411b3f1dec266e5eb2e95b",
49
- "darwin-x64": "ac4430824e18d15476ed91a6bbe81d0880239b758006cbf721d8e7d58a0c3676",
50
- "win32-x64": "16119743fb344990f2b67fa2a9844a50a46d70f6fbf3f72435b5ce1d7a635cd9",
51
- "linux-x64": "4e43f3946c80918f972a4c587a5f83873ccaeb5338f6eb8b5296022ba096f059"
48
+ "darwin-arm64": "13fc830ede44af479761302e3477c4f2516be142885445c8c5baf574041a7069",
49
+ "darwin-x64": "02007eb996d2fa2cf5be6546b6a1a01de769f1b12c7e3b2af1e93eb1b4deda0e",
50
+ "win32-x64": "5ea49f43ce6c432eac42dd10179873c78067ff1fc1328d8f664ce2c81da38f49",
51
+ "linux-x64": "f92c350bde46c95fb35ceab8c1c115352663a53d44d210ea98316fe1a92cf41e"
52
52
  }
53
53
  },
54
54
  "dependencies": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "muggle",
3
3
  "description": "Run real-browser end-to-end (E2E) acceptance tests on your web app from any AI coding agent. Generate test scripts from plain English, replay them on localhost, capture screenshots, and validate user flows like signup, checkout, and dashboards. Works across Claude Code, Cursor, Codex, and Windsurf.",
4
- "version": "4.11.4",
4
+ "version": "4.12.0",
5
5
  "author": {
6
6
  "name": "Muggle AI",
7
7
  "email": "support@muggle-ai.com"
@@ -2,7 +2,7 @@
2
2
  "name": "muggle",
3
3
  "displayName": "Muggle AI",
4
4
  "description": "Ship quality products with AI-powered end-to-end (E2E) acceptance testing that validates your web app like a real user — from Claude Code and Cursor to PR.",
5
- "version": "4.11.4",
5
+ "version": "4.12.0",
6
6
  "author": {
7
7
  "name": "Muggle AI",
8
8
  "email": "support@muggle-ai.com"
@@ -3,10 +3,35 @@
3
3
  set -euo pipefail
4
4
 
5
5
  # Ensure the Electron browser test runner is installed/up to date (silent, best-effort).
6
- if command -v muggle >/dev/null 2>&1; then
7
- muggle setup >/dev/null 2>&1 || true
8
- else
9
- npx -y @muggleai/works setup >/dev/null 2>&1 || true
6
+ #
7
+ # Bounded + cached: this script runs from a SessionStart hook on every Claude
8
+ # session, so a hung `muggle setup` (e.g. one blocked by host security policy
9
+ # on Windows) must not leak a ~100 MB orphan per session start. We cap the
10
+ # attempt with `timeout` and skip the call entirely if we already checked
11
+ # within the last day.
12
+ ensure_marker_dir="${HOME}/.cache/muggle"
13
+ ensure_marker="${ensure_marker_dir}/electron-app-checked"
14
+ ensure_ttl=$((24 * 60 * 60))
15
+
16
+ ensure_now=$(date +%s)
17
+ ensure_last=0
18
+ if [ -f "${ensure_marker}" ]; then
19
+ ensure_last=$(stat -f %m "${ensure_marker}" 2>/dev/null || stat -c %Y "${ensure_marker}" 2>/dev/null || echo 0)
20
+ fi
21
+
22
+ if [ $((ensure_now - ensure_last)) -ge "${ensure_ttl}" ]; then
23
+ if command -v timeout >/dev/null 2>&1; then
24
+ ensure_timeout="timeout -k 5 60"
25
+ else
26
+ ensure_timeout=""
27
+ fi
28
+ if command -v muggle >/dev/null 2>&1; then
29
+ ${ensure_timeout} muggle setup >/dev/null 2>&1 || true
30
+ else
31
+ ${ensure_timeout} npx -y @muggleai/works setup >/dev/null 2>&1 || true
32
+ fi
33
+ mkdir -p "${ensure_marker_dir}" 2>/dev/null || true
34
+ touch "${ensure_marker}" 2>/dev/null || true
10
35
  fi
11
36
 
12
37
  # --- Context injection ---
@@ -0,0 +1,8 @@
1
+ # Line comments for a specific review
2
+
3
+ For per-comment reply routing in `/muggle-do`.
4
+
5
+ ```bash
6
+ gh api repos/<owner>/<repo>/pulls/<n>/comments --paginate \
7
+ --jq '[.[] | select(.pull_request_review_id == <review-id>)]'
8
+ ```
@@ -0,0 +1,9 @@
1
+ # Identify the loop user
2
+
3
+ The GitHub identity that owns the authenticated `gh` token. Resolve-reminder thread classification and reply attribution need this.
4
+
5
+ ```bash
6
+ gh api user --jq '.login'
7
+ ```
8
+
9
+ Cache in `state.md` under `Loop user:`; re-resolve only when missing.
@@ -0,0 +1,8 @@
1
+ # Refresh the PR title or body
2
+
3
+ For `open-prs/update.md` when E2E state flips (passing↔failing) or validation strategy changes.
4
+
5
+ ```bash
6
+ gh pr edit <pr-number> --repo <owner>/<repo> --title "<new-title>"
7
+ gh pr edit <pr-number> --repo <owner>/<repo> --body-file <file>
8
+ ```
@@ -0,0 +1,12 @@
1
+ # PR metadata snapshot
2
+
3
+ Fetch the fields the watcher and bootstrap need in one call.
4
+
5
+ ```bash
6
+ gh pr view <pr-number> --repo <owner>/<repo> \
7
+ --json url,number,headRefOid,headRefName,baseRefName,state,mergedAt,closedAt,body,title,author
8
+ ```
9
+
10
+ - `state` is one of `OPEN`, `MERGED`, `CLOSED`.
11
+ - `headRefOid` is the current head SHA — store as `head_sha` in `prs.json`.
12
+ - `headRefName` is the branch — must match the working tree's branch in bootstrap.
@@ -0,0 +1,10 @@
1
+ # Push to the PR branch
2
+
3
+ After the address-reviews cycle's work.
4
+
5
+ ```bash
6
+ git -C <repo-path> push origin <head-ref-name>
7
+ git -C <repo-path> rev-parse HEAD
8
+ ```
9
+
10
+ Append the new SHA to `last_seen.pushed_shas[]` so resolve-reminder can recognize it.
@@ -0,0 +1,10 @@
1
+ # Reply to a line comment (threaded)
2
+
3
+ Used by `/muggle-do` per-comment inline replies.
4
+
5
+ ```bash
6
+ gh api --method POST \
7
+ -H "Accept: application/vnd.github+json" \
8
+ repos/<owner>/<repo>/pulls/<n>/comments/<comment-id>/replies \
9
+ -f body="<reply-text>"
10
+ ```
@@ -0,0 +1,15 @@
1
+ # Submitted reviews past a cursor
2
+
3
+ For the watcher's poll and the address-reviews fetch.
4
+
5
+ ```bash
6
+ gh api repos/<owner>/<repo>/pulls/<n>/reviews --paginate
7
+ ```
8
+
9
+ Filter client-side:
10
+
11
+ - `submitted_at != null` (skip PENDING drafts)
12
+ - `id > last_seen.reviewId`
13
+ - `id` not in `last_seen.escalated_review_ids`
14
+ - `user.login` in the resolved allow-list
15
+ - `state` in `{CHANGES_REQUESTED, COMMENTED}`, OR `APPROVED` with a non-empty body or at least one line comment
@@ -0,0 +1,7 @@
1
+ # Top-level PR comment
2
+
3
+ For the resolve-reminder stage and any non-threaded notice.
4
+
5
+ ```bash
6
+ gh pr comment <pr-number> --repo <owner>/<repo> --body "<text>"
7
+ ```
@@ -0,0 +1,33 @@
1
+ # Unresolved comment threads
2
+
3
+ For the resolve-reminder stage. GraphQL only — REST does not expose `isResolved`.
4
+
5
+ ```bash
6
+ gh api graphql -F owner=<owner> -F name=<repo> -F number=<n> -f query='
7
+ query($owner: String!, $name: String!, $number: Int!) {
8
+ repository(owner: $owner, name: $name) {
9
+ pullRequest(number: $number) {
10
+ reviewThreads(first: 100) {
11
+ nodes {
12
+ id
13
+ isResolved
14
+ comments(first: 100) {
15
+ nodes {
16
+ databaseId
17
+ author { login }
18
+ body
19
+ createdAt
20
+ }
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }'
27
+ ```
28
+
29
+ Filter client-side to `isResolved == false`. Classify each thread by inspecting its comments:
30
+
31
+ - **Addressed by loop** — at least one comment authored by the loop user citing a SHA in `last_seen.pushed_shas[]`.
32
+ - **Addressed by human** — at least one comment authored by a non-loop user after the original, and no addressed-by-loop signal.
33
+ - **Not addressed** — otherwise.
@@ -0,0 +1,17 @@
1
+ # Verify the working tree matches the PR
2
+
3
+ Bootstrap's environment check.
4
+
5
+ ```bash
6
+ git rev-parse --show-toplevel # cwd is a git working tree
7
+ git remote get-url origin # remote matches <owner>/<repo>
8
+ git rev-parse --abbrev-ref HEAD # current branch matches PR's headRefName
9
+ ```
10
+
11
+ Accept any remote URL form for `<owner>/<repo>` (with or without trailing `.git`):
12
+
13
+ - `https://github.com/<owner>/<repo>`
14
+ - `git@github.com:<owner>/<repo>`
15
+ - `ssh://git@github.com/<owner>/<repo>`
16
+
17
+ Any mismatch → bootstrap aborts using the wrong-checkout template in [`../../muggle-pr-followup/output-templates/bootstrap.md`](../../muggle-pr-followup/output-templates/bootstrap.md).
@@ -0,0 +1,20 @@
1
+ # GitHub CLI Recipes
2
+
3
+ Reusable `gh` / `git` snippets used by `muggle-pr-followup` (watcher + bootstrap) and `/muggle-do` (address-reviews + open-prs). Each recipe is one file — load only what you need.
4
+
5
+ Skills assume a working `gh auth status`. Auth errors surface verbatim from `gh`.
6
+
7
+ ## Index
8
+
9
+ | Recipe | Use case |
10
+ | :----- | :------- |
11
+ | [`pr-metadata`](github-cli-recipes/pr-metadata.md) | Snapshot PR state, head SHA, branch — watcher + bootstrap. |
12
+ | [`submitted-reviews`](github-cli-recipes/submitted-reviews.md) | Fetch reviews past a cursor — watcher's poll. |
13
+ | [`line-comments-for-review`](github-cli-recipes/line-comments-for-review.md) | Pull a review's line comments — `/muggle-do` per-comment routing. |
14
+ | [`unresolved-threads`](github-cli-recipes/unresolved-threads.md) | GraphQL fetch of unresolved comment threads — resolve-reminder. |
15
+ | [`reply-line-comment`](github-cli-recipes/reply-line-comment.md) | POST a threaded reply on a line comment. |
16
+ | [`top-level-comment`](github-cli-recipes/top-level-comment.md) | POST a top-level PR comment — resolve-reminder + overflow. |
17
+ | [`push-to-branch`](github-cli-recipes/push-to-branch.md) | Push + capture new SHA after address-reviews work. |
18
+ | [`verify-working-tree`](github-cli-recipes/verify-working-tree.md) | Three checks bootstrap runs before seeding state. |
19
+ | [`pr-edit`](github-cli-recipes/pr-edit.md) | Refresh title or body when address-reviews mode flips state. |
20
+ | [`loop-user-identity`](github-cli-recipes/loop-user-identity.md) | Resolve the GitHub login of the loop user. |
@@ -0,0 +1,53 @@
1
+ # Reviewer allow-list
2
+
3
+ The address-reviews flow only acts on reviews submitted by users in the **allow-list** = (requested reviewers ∪ CODEOWNERS) − bots − PR author. Re-resolve every invocation — never cache across cycles.
4
+
5
+ ## Step 1: requested reviewers
6
+
7
+ ```bash
8
+ gh pr view <number> --repo <owner>/<repo> --json reviewRequests,author
9
+ ```
10
+
11
+ `reviewRequests` is an array of `{ login? , slug? }`. User reviewers have `login`; team reviewers have `slug` (and `name`). Expand teams to member logins:
12
+
13
+ ```bash
14
+ gh api orgs/<org>/teams/<slug>/members --jq '.[].login'
15
+ ```
16
+
17
+ Record `prAuthor = author.login` for the exclusion step.
18
+
19
+ ## Step 2: CODEOWNERS
20
+
21
+ Look for the file in this order — first hit wins:
22
+
23
+ 1. `.github/CODEOWNERS`
24
+ 2. `CODEOWNERS`
25
+ 3. `docs/CODEOWNERS`
26
+
27
+ Read from the PR's **head branch** (not master) — a PR that adds CODEOWNERS should be informational while open, load-bearing once merged:
28
+
29
+ ```bash
30
+ gh api repos/<owner>/<repo>/contents/.github/CODEOWNERS?ref=<head_sha> --jq '.content' | base64 -d
31
+ ```
32
+
33
+ Parse line-by-line:
34
+
35
+ - Skip blank lines and lines starting with `#`.
36
+ - Each line is `<pattern> <owner1> <owner2> ...`.
37
+ - Owners are either `@user` or `@org/team`. Strip the leading `@`.
38
+ - Collect the union of all owners across all lines (don't match patterns against changed files — repo-level membership is enough).
39
+
40
+ Expand `@org/team` to member logins via the orgs/teams/members endpoint.
41
+
42
+ If no CODEOWNERS file exists in any location, the CODEOWNERS contribution is empty. Don't fail.
43
+
44
+ ## Step 3: filter
45
+
46
+ Allow-list = (requested reviewers ∪ CODEOWNERS) − `{prAuthor}` − bot logins.
47
+
48
+ Bot logins:
49
+
50
+ - Ends with `[bot]` (e.g. `dependabot[bot]`)
51
+ - Exact match: `dependabot`, `github-actions`, `renovate`, `mergify`
52
+
53
+ A comment author not in the allow-list is silently ignored — do not reply, do not address.
@@ -0,0 +1,48 @@
1
+ # Classify
2
+
3
+ Classify the **review as a unit** — but reply per line comment (threaded), not per review.
4
+
5
+ | Class | Signal | Action |
6
+ | :---- | :----- | :----- |
7
+ | **actionable** | Review names at least one concrete change or asks an answerable question. Soft phrasing counts when there's a concrete referent. | Treat as amended requirements; run **one** implementation cycle for the whole review; reply **threaded per line comment** referencing the new SHA (top-level only when the review is body-only). |
8
+ | **ambiguous** | No actionable signal — pure vibes, contradictory, or depends on knowledge the loop can't access. | Escalate once with two interpretations; pause the PR. |
9
+
10
+ Default to **actionable**. CI catches wrong attempts; reviewers correct on the next round. Escalation is a round-trip with an absent user — reserve it.
11
+
12
+ Reply shape (all replies for one review reference the same SHA):
13
+
14
+ - **threaded** (default): `Done in <sha> — <attribution>. (Review #<review_id>, cycle <status>.)`
15
+ - **top-level** (fallback, body-only reviews): `Re: review #<review_id> — addressed in <sha>, cycle <status>.`
16
+ - **ambiguous**: no bot reply.
17
+
18
+ ## Worked examples — Actionable
19
+
20
+ | Review (summarized) | Why actionable |
21
+ | :------------------ | :------------- |
22
+ | 3 comments: "rename `fooBar` to `foo_bar`", "use `const` here", "fix this typo" | Three concrete edits |
23
+ | 1 comment: "could the procedure be simpler?" | Soft-phrased but the intent is clear — simplify |
24
+ | Review body: "Two things: extract validation into a helper, add a null check before the lookup." + 0 line comments | Two concrete directives in the body |
25
+ | 4 comments: "why this approach?", "is this called from X?", "does this need to handle empty array?", "what's the perf here?" | All questions, each answerable |
26
+ | 1 comment: "rewrite this module — the architecture doesn't match the spec" | Substantive rebuild, but direction is clear |
27
+ | 1 comment: "I'd lean toward the bar.ts pattern" | Concrete referent (bar.ts) — apply that pattern |
28
+
29
+ ## Worked examples — Ambiguous
30
+
31
+ | Review (summarized) | Why ambiguous |
32
+ | :------------------ | :------------ |
33
+ | 1 comment: "👀" / "hmm" / ":thinking:" | No signal at all |
34
+ | 1 comment: "this is wrong" with no target or direction | Asserts a problem but doesn't propose a fix |
35
+ | 2 comments: "use X" + "but actually don't use X" | Self-contradicting |
36
+ | 1 comment: "we discussed this offline — please address" | References context the loop doesn't have |
37
+ | 1 comment: "won't this break the prod migration we did last week?" | Implicit change request gated on knowledge the loop can't access |
38
+ | Mixed: 2 concrete directives + 1 "rethink the whole approach" | The "rethink" subverts the others; escalate to confirm scope |
39
+
40
+ Escalate per the caller's procedure (add the review id to the cursor's escalated set, emit one terminal message, pause the PR).
41
+
42
+ ## Borderline rule
43
+
44
+ If you can paraphrase the review's intent as **"do X"** (X concrete) → actionable. Pick the best interpretation; dispatch.
45
+
46
+ If you can only paraphrase as **"the reviewer is dissatisfied but I can't tell with what"** → ambiguous.
47
+
48
+ Mixed ("3 concrete + 1 dissatisfaction") usually splits: action the concrete; ask about the dissatisfaction in the reply summary. Pure ambiguity means *nothing* in the review is actionable.