@muggleai/works 4.10.1 → 4.11.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.
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/skills/_shared/pr-followup-helpers.md +151 -0
- package/dist/plugin/skills/do/build.md +51 -0
- package/dist/plugin/skills/do/e2e-acceptance.md +2 -2
- package/dist/plugin/skills/do/impact-analysis.md +2 -2
- package/dist/plugin/skills/do/open-prs.md +67 -78
- package/dist/plugin/skills/do/pre-flight.md +3 -3
- package/dist/plugin/skills/do/requirements.md +2 -2
- package/dist/plugin/skills/do/unit-tests.md +2 -2
- package/dist/plugin/skills/muggle-do/SKILL.md +34 -77
- package/dist/plugin/skills/muggle-feedback/SKILL.md +2 -1
- package/dist/plugin/skills/muggle-feedback/ops/submit.md +16 -3
- package/dist/plugin/skills/muggle-pr-followup/SKILL.md +59 -0
- package/dist/plugin/skills/muggle-pr-followup/contract.md +190 -0
- package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +14 -0
- package/dist/release-manifest.json +4 -4
- package/package.json +6 -6
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/skills/_shared/pr-followup-helpers.md +151 -0
- package/plugin/skills/do/build.md +51 -0
- package/plugin/skills/do/e2e-acceptance.md +2 -2
- package/plugin/skills/do/impact-analysis.md +2 -2
- package/plugin/skills/do/open-prs.md +67 -78
- package/plugin/skills/do/pre-flight.md +3 -3
- package/plugin/skills/do/requirements.md +2 -2
- package/plugin/skills/do/unit-tests.md +2 -2
- package/plugin/skills/muggle-do/SKILL.md +34 -77
- package/plugin/skills/muggle-feedback/SKILL.md +2 -1
- package/plugin/skills/muggle-feedback/ops/submit.md +16 -3
- package/plugin/skills/muggle-pr-followup/SKILL.md +59 -0
- package/plugin/skills/muggle-pr-followup/contract.md +190 -0
- package/plugin/skills/muggle-test-feature-local/SKILL.md +14 -0
- package/dist/plugin/skills/do/pr-followup.md +0 -225
- package/dist/plugin/skills/do/validate-code.md +0 -38
- package/dist/plugin/skills/muggle-do-pr-followup/SKILL.md +0 -37
- package/plugin/skills/do/pr-followup.md +0 -225
- package/plugin/skills/do/validate-code.md +0 -38
- package/plugin/skills/muggle-do-pr-followup/SKILL.md +0 -37
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
# PR follow-up agent (Stage 8/8)
|
|
2
|
-
|
|
3
|
-
You are babysitting one or more open pull requests opened by stage 7. Each invocation of this stage is **one polling tick** dispatched by `/loop 5m /muggle:muggle-do-pr-followup <slug>`. The tick is short, idempotent, and addresses **at most one item per PR**.
|
|
4
|
-
|
|
5
|
-
The loop ends when every PR in the session is merged or closed.
|
|
6
|
-
|
|
7
|
-
## Turn preamble
|
|
8
|
-
|
|
9
|
-
Start the turn with:
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
**Stage 8/8 — PR follow-up** — polling <N> PR(s), tick #<K>.
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Resolve `<N>` from `prs.json` (non-terminal entries only) and `<K>` from the tick counter in `state.md`.
|
|
16
|
-
|
|
17
|
-
## Stage-8 exception to the no-mid-cycle-questions rule
|
|
18
|
-
|
|
19
|
-
Stages 2–7 never ask the user mid-cycle. **Stage 8 may escalate** when a reviewer comment is ambiguous (see [Decision rule: classify](#decision-rule-classify) below). This is deliberate — the user has already walked away by the time stage 8 starts, and forcing a guess on an ambiguous design comment is worse than pausing.
|
|
20
|
-
|
|
21
|
-
Escalation is the **only** user-facing path in stage 8. Anything else — directives, questions, CI failures, retries — runs silently.
|
|
22
|
-
|
|
23
|
-
## Inputs
|
|
24
|
-
|
|
25
|
-
Read these from `.muggle-do/sessions/<slug>/`:
|
|
26
|
-
|
|
27
|
-
- `state.md` — current tick counter, session metadata, the pre-flight answers (for context when classifying comments).
|
|
28
|
-
- `prs.json` — list of `{repo, number, url, head_sha, state}`. Entries with `state: "merged"` or `state: "closed"` are skipped.
|
|
29
|
-
- `last_seen.json` — per-PR cursor: `{commentId, reviewId, checkRunCompletedAt, last_pushed_sha, idle_tick_count, escalated_comment_ids[]}`.
|
|
30
|
-
|
|
31
|
-
If any of these don't exist, the tick is a no-op — log an error to `followup.log` and exit.
|
|
32
|
-
|
|
33
|
-
## Per-tick contract
|
|
34
|
-
|
|
35
|
-
Do these steps in order. **Do not batch — at most one actionable item per PR.**
|
|
36
|
-
|
|
37
|
-
### Step 1: Refresh PR states
|
|
38
|
-
|
|
39
|
-
For each PR in `prs.json` not already terminal:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
gh pr view <number> --repo <repo> --json state,mergedAt,closedAt,headRefOid
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
If `state` is `MERGED` or `CLOSED`, mark the entry terminal in `prs.json`. Update `head_sha` if it changed.
|
|
46
|
-
|
|
47
|
-
### Step 2: Termination check
|
|
48
|
-
|
|
49
|
-
If every entry in `prs.json` is now terminal:
|
|
50
|
-
|
|
51
|
-
1. Write `result.md` with one section per PR (URL, final state, count of items addressed, count escalated, final commit SHA).
|
|
52
|
-
2. **Do not schedule the next tick.** End the turn with no `ScheduleWakeup`-equivalent — `/loop` ends naturally.
|
|
53
|
-
3. Emit the final tick-summary telemetry event (see [Telemetry](#telemetry)) with `prs_terminal == pr_count`.
|
|
54
|
-
|
|
55
|
-
### Step 3: Resolve the reviewer allow-list (every tick)
|
|
56
|
-
|
|
57
|
-
For each non-terminal PR, compute the set of GitHub logins allowed to drive changes:
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
gh pr view <number> --repo <repo> --json reviewRequests,author
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Add requested reviewers. Add CODEOWNERS by parsing `.github/CODEOWNERS` (or `CODEOWNERS` / `docs/CODEOWNERS`) from the PR's head branch. Remove the PR author and any bot accounts (logins ending in `[bot]`, plus the standard list: `dependabot`, `github-actions`, `renovate`).
|
|
64
|
-
|
|
65
|
-
This is per-tick by design (decision 9): reviewers added or removed after the PR opened take effect on the next poll.
|
|
66
|
-
|
|
67
|
-
### Step 4: Pull new actionable items
|
|
68
|
-
|
|
69
|
-
For each non-terminal PR, fetch items newer than the cursor in `last_seen.json`:
|
|
70
|
-
|
|
71
|
-
- **Line-level review comments**:
|
|
72
|
-
```bash
|
|
73
|
-
gh api repos/<owner>/<repo>/pulls/<number>/comments --paginate
|
|
74
|
-
```
|
|
75
|
-
Filter to comments with `id > last_seen.commentId` AND `user.login` in the allow-list AND not already in `escalated_comment_ids` (avoid re-escalating).
|
|
76
|
-
|
|
77
|
-
- **CHANGES_REQUESTED review bodies** (only when the review has a non-empty body and no associated line comments):
|
|
78
|
-
```bash
|
|
79
|
-
gh api repos/<owner>/<repo>/pulls/<number>/reviews --paginate
|
|
80
|
-
```
|
|
81
|
-
Filter to reviews with `id > last_seen.reviewId` AND `state == "CHANGES_REQUESTED"` AND `user.login` in the allow-list AND `body` is non-empty.
|
|
82
|
-
|
|
83
|
-
- **Failing CI checks** (apply the `head_sha` guard, decision 11):
|
|
84
|
-
```bash
|
|
85
|
-
gh pr checks <number> --repo <repo> --json name,state,completedAt,detailsUrl,workflow
|
|
86
|
-
```
|
|
87
|
-
Filter to checks where `state == "FAILURE"` AND `completedAt > last_seen.checkRunCompletedAt`. **Skip any check whose target SHA equals `last_pushed_sha`** — CI is still digesting our last push, addressing it again would double-handle.
|
|
88
|
-
|
|
89
|
-
### Step 5: Pick one item per PR
|
|
90
|
-
|
|
91
|
-
If a PR has zero actionable items, increment `idle_tick_count` for that PR. Otherwise sort the PR's items by timestamp ascending and take the **oldest one**. Reset `idle_tick_count` to 0 for that PR.
|
|
92
|
-
|
|
93
|
-
If every PR has zero actionable items this tick:
|
|
94
|
-
- Append a one-line heartbeat to `followup.log`: `<ts> tick #<K> idle (PRs: #A, #B, ...)`.
|
|
95
|
-
- If `idle_tick_count >= 12` for any PR, also rewrite `state.md` with `idle since <ts>, last poll <ts>` for that PR (decision 12).
|
|
96
|
-
- Emit tick-summary telemetry, exit the turn (next tick fires in 5 min via `/loop`).
|
|
97
|
-
|
|
98
|
-
### Step 6: Classify and route
|
|
99
|
-
|
|
100
|
-
For each picked item, classify it:
|
|
101
|
-
|
|
102
|
-
#### Decision rule: classify
|
|
103
|
-
|
|
104
|
-
| Class | Signals | Action |
|
|
105
|
-
| :---- | :------ | :----- |
|
|
106
|
-
| **directive** | Imperative verb on a concrete target: "rename X to Y", "extract this", "add a null check", "remove this branch", "use `const` here", "this should be `async`", "delete this comment". Includes review-body summaries that read as a list of changes. | Fix → commit → push → reply `Done in <sha> — <one-line>`. |
|
|
107
|
-
| **question** | Ends with `?` and is not a rhetorical disguise. "Why this approach?", "Is this called from X?", "Does this need to handle Z?". | Reply inline with the answer. No code change. No push. |
|
|
108
|
-
| **CI failure** | Source is a failing check, not a comment. | Read the failing job log, fix, commit, push. No reply. |
|
|
109
|
-
| **ambiguous** (default) | Proposes an alternative without instructing ("I think we should use Z instead", "Have you considered Y?"), conflicts with a deliberate choice in the PR description or design doc, or is a multi-part comment mixing question and change request. | **Escalate.** See [Step 7: Escalate](#step-7-escalate). |
|
|
110
|
-
|
|
111
|
-
When the comment matches neither **directive** nor **question** cleanly, default to **ambiguous**. Do not guess. The cost of escalating a directive that could have been auto-handled is small; the cost of pushing a wrong change because we guessed is large.
|
|
112
|
-
|
|
113
|
-
#### Decision rule: reply text (adaptive, decision 6)
|
|
114
|
-
|
|
115
|
-
- **directive**: short, one-line. `Done in <sha> — renamed \`fooBar\` to \`foo_bar\` per request.` Use the [reply-routing helper](#reply-routing) to hit the correct endpoint.
|
|
116
|
-
- **question**: answer inline. Pull surrounding-code context if needed. Reply length matches the question's complexity — don't write three paragraphs to answer a yes/no.
|
|
117
|
-
- **CI failure**: no comment to reply to. The fix commit is the response. The commit message should reference the failing check by name (e.g. `fix(ci): typecheck — narrow type of foo`).
|
|
118
|
-
- **ambiguous**: no reply written by the bot. The escalation goes to the user, who replies to the comment themselves.
|
|
119
|
-
|
|
120
|
-
### Step 7: Escalate
|
|
121
|
-
|
|
122
|
-
When the picked item is **ambiguous**:
|
|
123
|
-
|
|
124
|
-
1. Add the comment id to `last_seen.escalated_comment_ids` so it isn't re-picked next tick.
|
|
125
|
-
2. Append an entry to `followup.log` describing the comment and why it was classified ambiguous.
|
|
126
|
-
3. Pause this PR's loop by writing `escalated: true` against the PR's entry in `prs.json`. Subsequent ticks skip this PR until the user clears the escalation.
|
|
127
|
-
4. End the turn with a **single terminal message** to the user:
|
|
128
|
-
|
|
129
|
-
```
|
|
130
|
-
**Stage 8 escalation — <repo>#<number>**
|
|
131
|
-
|
|
132
|
-
<reviewer-login> left an ambiguous comment on <file>:<line>:
|
|
133
|
-
|
|
134
|
-
> <quoted comment body>
|
|
135
|
-
|
|
136
|
-
Classifying it as a directive would mean: <one-line interpretation>
|
|
137
|
-
Classifying it as a question would mean: <one-line alternative>
|
|
138
|
-
|
|
139
|
-
Reply to that GitHub comment yourself, or tell me which way to go. I'll resume polling once the comment is either resolved or has a follow-up from you.
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
The user clears the escalation by either resolving the GitHub thread (the next tick sees it resolved and removes it from `escalated_comment_ids`) or by replying in this terminal session with a directive that the next tick will pick up.
|
|
143
|
-
|
|
144
|
-
### Step 8: Update cursors and push
|
|
145
|
-
|
|
146
|
-
After addressing a non-escalated item:
|
|
147
|
-
|
|
148
|
-
- Advance `last_seen.commentId` / `last_seen.reviewId` / `last_seen.checkRunCompletedAt` past the addressed item.
|
|
149
|
-
- If a push happened, set `last_seen.last_pushed_sha` to the new HEAD SHA. This arms the `head_sha` guard for the next tick (decision 11).
|
|
150
|
-
- Emit per-item telemetry (see [Telemetry](#telemetry)).
|
|
151
|
-
|
|
152
|
-
### Step 9: Emit tick-summary telemetry and exit
|
|
153
|
-
|
|
154
|
-
Emit one `muggle-local-telemetry-skill-emit` event per tick (see [Telemetry](#telemetry)). Exit the turn. Next tick fires in 5 min via `/loop`.
|
|
155
|
-
|
|
156
|
-
## Reply routing
|
|
157
|
-
|
|
158
|
-
GitHub's PR comment APIs are not uniform. Route by parent type:
|
|
159
|
-
|
|
160
|
-
- **Reply to a line-level review comment** (most common): `POST /repos/{owner}/{repo}/pulls/{n}/comments/{comment_id}/replies` with `{"body": "..."}`. The reply lands in the same review thread.
|
|
161
|
-
- **Reply to a CHANGES_REQUESTED review body** (no inline comment to reply to): post a fresh top-level PR comment via `gh pr comment <number> --body "..."` referencing the review. There is no "reply to review body" endpoint.
|
|
162
|
-
- **Failing CI**: no reply. The fix commit is the response.
|
|
163
|
-
|
|
164
|
-
Never post the same reply twice — the cursor in `last_seen.json` is the only re-entry guard.
|
|
165
|
-
|
|
166
|
-
## Telemetry
|
|
167
|
-
|
|
168
|
-
Two telemetry shapes per tick (decision 14):
|
|
169
|
-
|
|
170
|
-
**Per-item** (one event per addressed/escalated item):
|
|
171
|
-
|
|
172
|
-
```json
|
|
173
|
-
{
|
|
174
|
-
"skill": "muggle-do-pr-followup",
|
|
175
|
-
"event": "item",
|
|
176
|
-
"session_slug": "<slug>",
|
|
177
|
-
"repo": "<repo>",
|
|
178
|
-
"pr_number": <n>,
|
|
179
|
-
"item_type": "directive|question|ci_failure|ambiguous",
|
|
180
|
-
"outcome": "fixed_and_pushed|replied|escalated",
|
|
181
|
-
"comment_id": <id-or-null>,
|
|
182
|
-
"head_sha": "<sha-or-null>"
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
**Per-tick summary** (always one, even on idle ticks):
|
|
187
|
-
|
|
188
|
-
```json
|
|
189
|
-
{
|
|
190
|
-
"skill": "muggle-do-pr-followup",
|
|
191
|
-
"event": "tick",
|
|
192
|
-
"session_slug": "<slug>",
|
|
193
|
-
"tick": <K>,
|
|
194
|
-
"pr_count": <total>,
|
|
195
|
-
"prs_terminal": <count>,
|
|
196
|
-
"items_seen": <count>,
|
|
197
|
-
"items_addressed": <count>,
|
|
198
|
-
"items_escalated": <count>,
|
|
199
|
-
"pushed": true|false,
|
|
200
|
-
"tick_duration_ms": <ms>
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
## Output
|
|
205
|
-
|
|
206
|
-
This stage produces no console output beyond:
|
|
207
|
-
- The turn preamble (always).
|
|
208
|
-
- An escalation terminal message (only when an item is classified ambiguous).
|
|
209
|
-
- The final `result.md` summary (only on the terminating tick — written to disk, not printed).
|
|
210
|
-
|
|
211
|
-
Everything else lives in `followup.log` and `last_seen.json`.
|
|
212
|
-
|
|
213
|
-
## Self-check before ending the turn
|
|
214
|
-
|
|
215
|
-
Before exiting, confirm:
|
|
216
|
-
|
|
217
|
-
- [ ] `last_seen.json` was advanced for every item handled.
|
|
218
|
-
- [ ] `prs.json` reflects current PR states (terminal entries marked).
|
|
219
|
-
- [ ] `followup.log` has at minimum a heartbeat or per-item line for this tick.
|
|
220
|
-
- [ ] Telemetry events were emitted (per-item + per-tick).
|
|
221
|
-
- [ ] If pushed, `last_pushed_sha` is set.
|
|
222
|
-
- [ ] If escalated, `escalated_comment_ids` contains the comment id.
|
|
223
|
-
- [ ] If terminal, the loop is NOT continued (do not schedule another tick).
|
|
224
|
-
|
|
225
|
-
If any are missing, fix before exit — a dropped cursor causes double-handling next tick.
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Code Validation Agent (Stage 4/7)
|
|
2
|
-
|
|
3
|
-
You are validating that each repository's git state is ready for the dev cycle pipeline.
|
|
4
|
-
|
|
5
|
-
## Turn preamble
|
|
6
|
-
|
|
7
|
-
Start the turn with:
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
**Stage 4/7 — Validate code** — checking branch and commit state for each repo with changes.
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Input
|
|
14
|
-
|
|
15
|
-
You receive:
|
|
16
|
-
- A list of repos with changes (from impact analysis), including their paths and branch names
|
|
17
|
-
|
|
18
|
-
## Your Job
|
|
19
|
-
|
|
20
|
-
For each repo:
|
|
21
|
-
|
|
22
|
-
1. **Verify the branch is a feature branch** (not main/master/the default branch). This should already be validated by impact analysis, but double-check.
|
|
23
|
-
2. **Check for uncommitted changes:** Run `git status --porcelain` in the repo. If there are uncommitted changes, warn the user — uncommitted changes won't be included in PRs.
|
|
24
|
-
3. **Get the branch diff:** Run `git diff <default-branch>...HEAD --stat` for a summary of changes.
|
|
25
|
-
4. **Verify commits exist on the branch:** Run `git log <default-branch>..HEAD --oneline` to confirm there are commits to push.
|
|
26
|
-
|
|
27
|
-
## Output
|
|
28
|
-
|
|
29
|
-
Per repo:
|
|
30
|
-
|
|
31
|
-
**Repo: (name)**
|
|
32
|
-
- Branch: (name)
|
|
33
|
-
- Commits on branch: (count and one-line summaries)
|
|
34
|
-
- Uncommitted changes: yes/no (with warning if yes)
|
|
35
|
-
- Diff stat: (file change summary)
|
|
36
|
-
- Status: READY | WARNING | ERROR
|
|
37
|
-
|
|
38
|
-
**Overall:** READY to proceed / BLOCKED (with reasons)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: muggle-do-pr-followup
|
|
3
|
-
description: One polling tick of /muggle-do stage 8 — addresses reviewer comments and CI failures on PRs opened by an earlier /muggle-do session. Dispatched by `/loop 5m /muggle:muggle-do-pr-followup <slug>` after stage 7; also re-runnable by hand on any session slug.
|
|
4
|
-
disable-model-invocation: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Muggle Test Do — PR follow-up
|
|
8
|
-
|
|
9
|
-
> Telemetry first step: see [`_shared/telemetry-emit.md`](../_shared/telemetry-emit.md). Use `skillName: "muggle-do-pr-followup"`.
|
|
10
|
-
|
|
11
|
-
This skill is the **dispatch entry** for stage 8 of /muggle-do. It is intentionally thin — the actual per-tick logic lives in [`../do/pr-followup.md`](../do/pr-followup.md), the single source of truth.
|
|
12
|
-
|
|
13
|
-
## Input routing
|
|
14
|
-
|
|
15
|
-
`$ARGUMENTS` is the **session slug** (the directory name under `.muggle-do/sessions/`).
|
|
16
|
-
|
|
17
|
-
- Empty / `help` / `?` → list available session slugs from `.muggle-do/sessions/` that have a non-empty `prs.json` with at least one non-terminal entry, then exit. Do not start a poll.
|
|
18
|
-
- Otherwise → resolve `.muggle-do/sessions/<slug>/`. If the directory or its `prs.json` is missing, log the error to `followup.log` (creating it if needed) and exit. Do **not** ask the user.
|
|
19
|
-
|
|
20
|
-
## Run one tick
|
|
21
|
-
|
|
22
|
-
With a valid session slug, follow [`../do/pr-followup.md`](../do/pr-followup.md) exactly. That file owns:
|
|
23
|
-
|
|
24
|
-
- The turn preamble.
|
|
25
|
-
- The 9-step per-tick contract.
|
|
26
|
-
- The classify rule (directive / question / CI failure / ambiguous → escalate).
|
|
27
|
-
- Reply routing.
|
|
28
|
-
- Telemetry shape.
|
|
29
|
-
- The self-check before exit.
|
|
30
|
-
|
|
31
|
-
This skill file adds nothing beyond the slug resolution above.
|
|
32
|
-
|
|
33
|
-
## Why a separate entry exists
|
|
34
|
-
|
|
35
|
-
- `/loop 5m /muggle:muggle-do-pr-followup <slug>` (dispatched by stage 7 of /muggle-do) needs a slash-addressable target.
|
|
36
|
-
- Manual re-attach: if `/loop` was killed and the user wants to resume, they can run `/muggle:muggle-do-pr-followup <slug>` once to drive a single tick, or re-dispatch the `/loop` themselves.
|
|
37
|
-
- Isolated debugging: a single tick is reproducible against a real session dir.
|