@alphacoder-v0/pi-loops 0.19.3 → 0.20.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,27 @@
3
3
  All notable changes to pi-loops are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow SemVer.
5
5
 
6
+ ## [0.20.0] - 2026-09-14
7
+
8
+ ### Added
9
+ - **Five more recipes.** `daily-digest` files one finding a morning saying what needs a look —
10
+ open items, a red default branch, a loop that failed its last two runs — or none on a quiet day.
11
+ `pr-watch` gives every open pull request one state and reports only what changed, or a stall
12
+ that lasted another day; under `propose` it may leave one comment per stall. `ci-sweeper` turns
13
+ a red default branch into a finding, or under `propose` a fix in a worktree and a pull request,
14
+ with the failure signature as memory and a stop after two failed attempts. `ecosystem` finds who
15
+ uses or forks the project and drafts the one reply or invitation worth sending, every draft a
16
+ finding a person sends by claiming it, its only level `propose`. `changelog-draft` gains
17
+ `propose`: the entry and the version bump in a release pull request. None of these touch
18
+ `src/`; `test/recipe.test.ts` holds every packaged manifest and playbook to the same rules.
19
+
20
+ ### Fixed
21
+ - `@earendil-works/pi-ai` is listed in `peerDependencies`: two files import a type from it, and
22
+ pi's packaging rules ask that every bundled package imported be listed.
23
+ - The "escape hatch" front-end test read the fake pi's log unconditionally and failed on runs
24
+ where nothing had reached pi yet — which is the fact it was asserting. A missing log now reads
25
+ as empty.
26
+
6
27
  ## [0.19.3] - 2026-09-14
7
28
 
8
29
  ### Fixed
package/README.md CHANGED
@@ -38,7 +38,7 @@ the first sign of credentials that do not work should not be an empty inbox tomo
38
38
 
39
39
  ```bash
40
40
  pi install npm:@alphacoder-v0/pi-loops # from npm, following new releases
41
- pi install git:github.com/alphacoder-v0/pi-loops@v0.19.3 # or from GitHub, at a pinned tag
41
+ pi install git:github.com/alphacoder-v0/pi-loops@v0.20.0 # or from GitHub, at a pinned tag
42
42
  pi install /path/to/pi-loops # or a local checkout — `pi install .` in this repo
43
43
  pi -e /path/to/pi-loops # or none of them: try it for one run, installing nothing
44
44
  ```
@@ -138,10 +138,11 @@ A job records its directory and model at creation, so it is not tied to the wind
138
138
  `--cwd` runs it in another checkout (absolute, or relative to this project — no shell, so nothing
139
139
  expands `~`), and `--model` pins it whatever this session is on (`/cron set <ref> --model -` unpins).
140
140
 
141
- And two you do not write at all: `/recipe add issue-loop` runs the issue tracker as a state machine
142
- that two loops turn, `/recipe add autoresearch` runs one experiment per run against a contract you
143
- wrote — each a directory of playbooks copied into the project, installed with one question and one
144
- confirmation ([docs/recipes.md](docs/recipes.md)).
141
+ And seven you do not write at all: `/recipe add issue-loop` runs the issue tracker as a state
142
+ machine that two loops turn, `/recipe add autoresearch` runs one experiment per run against a
143
+ contract you wrote, and `daily-digest`, `pr-watch`, `ci-sweeper`, `changelog-draft` and
144
+ `ecosystem` do what their names say — each a directory of playbooks copied into the project,
145
+ installed with one question and one confirmation ([docs/recipes.md](docs/recipes.md)).
145
146
 
146
147
  Two that are not loops:
147
148
 
package/README.zh-CN.md CHANGED
@@ -45,7 +45,7 @@ pi-loops 自己没有任何运行时依赖。
45
45
 
46
46
  ```bash
47
47
  pi install npm:@alphacoder-v0/pi-loops # 从 npm 装,跟着新版本走
48
- pi install git:github.com/alphacoder-v0/pi-loops@v0.19.3 # 或者从 GitHub 装,钉住一个 tag
48
+ pi install git:github.com/alphacoder-v0/pi-loops@v0.20.0 # 或者从 GitHub 装,钉住一个 tag
49
49
  pi install /path/to/pi-loops # 或者本地检出;本仓库里就是 pi install .
50
50
  ```
51
51
 
package/docs/recipes.md CHANGED
@@ -14,16 +14,17 @@ anything a person could not have typed.
14
14
  /recipe remove issue-loop its jobs go; the files stay unless you say --purge
15
15
  ```
16
16
 
17
- Two recipes ship in this version, and the shape is meant to be copied:
17
+ Seven recipes ship, and the shape is meant to be copied:
18
18
 
19
19
  | recipe | jobs | what it runs | levels |
20
20
  |---|---|---|---|
21
21
  | [issue-loop](../recipes/issue-loop/) | `issue-triage` every 30 min, `issue-implement` every 30 min offset | the issue tracker as a state machine: triage into agent briefs, build accepted issues in a worktree, open pull requests | `propose`, `act` |
22
22
  | [autoresearch](../recipes/autoresearch/) | `autoresearch` every hour, with `--verify` | one experiment per run against a contract you wrote, a ledger of every attempt, promotion only on held-out evidence | `propose`, `act` |
23
-
24
- `changelog-draft` (a nightly changelog entry and version proposal) is packaged with its playbook
25
- and manifest but is still the release-side half of the issue loop; it and four more — a daily
26
- digest, a pull-request watch, a CI sweeper, an ecosystem scan — are the next batch.
23
+ | [daily-digest](../recipes/daily-digest/) | `daily-digest` at 09:00 | one finding saying what needs a look today — open items, a red CI, a loop that keeps failing — or none | `report` |
24
+ | [pr-watch](../recipes/pr-watch/) | `pr-watch` every 15 min | the open pull requests, and only what changed about them: a red check, a conflict, a review waiting, an author who answered | `report`, `propose` |
25
+ | [ci-sweeper](../recipes/ci-sweeper/) | `ci-sweeper` every 15 min, 40-minute timeout | a red default branch reported, or repaired in a worktree and put up as a pull request; the same failure twice unfixed is a stop | `report`, `propose` |
26
+ | [changelog-draft](../recipes/changelog-draft/) | `changelog-draft` weekdays at 18:00 | when the default branch is ahead of the last tag, the changelog entry and a version, drafted — or put in a release pull request | `report`, `propose` |
27
+ | [ecosystem](../recipes/ecosystem/) | `ecosystem` Mondays at 10:00 | who uses or forks this project, and the one reply or upstream invitation worth sending, drafted as a finding a person sends | `propose` |
27
28
 
28
29
  ## What `/recipe add` does, in order
29
30
 
@@ -199,7 +200,52 @@ Specific to this repository: the labels are on
199
200
  command is `npm run ci`, and a release needs a one-time password — so the release side can only
200
201
  propose, which is what `changelog-draft` does everywhere.
201
202
 
202
- ## autoresearch
203
+ ## daily-digest
204
+
205
+ One job at nine, one level. It reads the tracker, the latest run on the default branch, the last
206
+ day's commits, and the automation's own health — a loop that failed its last two runs, a finding
207
+ that has sat in the inbox for three days — and files **one** finding, the day first, then a line
208
+ per thing that needs a look. A quiet day files nothing, and a thing reported yesterday and
209
+ unchanged is not repeated: the notes carry a watermark and yesterday's lines. The loop writes
210
+ nowhere but the inbox.
211
+
212
+ ## pr-watch
213
+
214
+ Every fifteen minutes, each open pull request gets one state — `green`, `red-check`, `conflict`,
215
+ `awaiting-review`, `changes-requested`, `answered` — and a finding is filed only when a state
216
+ changed since the last run, or a stall has lasted another whole day. Under `propose` the loop may
217
+ leave one comment on a pull request that has waited on the same person for more than two days,
218
+ once per stall, remembered in its notes. It never merges, approves, rebases or pushes.
219
+
220
+ ## ci-sweeper
221
+
222
+ Every fifteen minutes, the latest run on the default branch. Green: nothing. Red: a **failure
223
+ signature** (job, step, first error line, normalised) is the memory — under `report` it is one
224
+ finding per signature and one more when the branch is green again; under `propose` the loop
225
+ reproduces it in a worktree, fixes the cause, runs the project's check command and opens a pull
226
+ request, and the same signature attempted twice without a green check is a stop with a finding
227
+ that says so. A test is never skipped or deleted to get to green, and the workflow file is never
228
+ edited to make it pass.
229
+
230
+ ## changelog-draft
231
+
232
+ Weekdays at six, if the default branch is ahead of the last tag by anything but chores: the
233
+ changelog entry in the project's own style and a version picked the way its history picks them.
234
+ Under `report` the draft is the finding; under `propose` it is also a branch, the entry written
235
+ in, the version bumped, and a pull request whose link leads the finding — a person merges it and
236
+ tags. Tagging and publishing are never the loop's: they need a key, a one-time password and a
237
+ decision.
238
+
239
+ ## ecosystem
240
+
241
+ Mondays, once. It searches for repositories that use, mention or fork this project, sorts them
242
+ into integrations, derivatives, mentions and noise, and for each integration or derivative looks
243
+ for **one** thing worth a word: a problem they hit that is fixed upstream, a fork commit worth
244
+ bringing back, an unanswered question. Each draft is a finding with the text ready to post;
245
+ claiming the finding is what sends it, as the person, from their chat. The loop itself never
246
+ posts, comments or opens anything anywhere, which is why its only level is `propose`.
247
+
248
+ ## Another tracker
203
249
 
204
250
  The shape Karpathy's autoresearch and Arbor made familiar, without a kernel: a **research
205
251
  contract** a person writes, a **ledger** of every attempt including the failed ones, one isolated
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alphacoder-v0/pi-loops",
3
- "version": "0.19.3",
3
+ "version": "0.20.0",
4
4
  "description": "An automation layer for pi: cron + stateful loops with a triage inbox, maker/checker verification, dynamic triggers, MCP notifications and tools, lifecycle hooks, session archives. A non-invasive pi extension.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,6 +53,7 @@
53
53
  "check:scripts": "node --check src/web.mjs && node --check src/cli-entry.mjs && node --check src/host-entry.mjs && node --check src/ts-entry.mjs && node --check src/pi-resolver.mjs && node --check src/register-pi.mjs && node --check examples/mcp-notify-server.mjs"
54
54
  },
55
55
  "peerDependencies": {
56
+ "@earendil-works/pi-ai": "*",
56
57
  "@earendil-works/pi-coding-agent": "*",
57
58
  "@earendil-works/pi-tui": "*",
58
59
  "typebox": "*"
@@ -1,7 +1,8 @@
1
1
  name = "changelog-draft"
2
2
  summary = "When the default branch is ahead of the last tag, draft the changelog entry and propose a version; a person tags."
3
3
  needs_tracker = false
4
- levels = ["report"]
4
+ levels = ["report", "propose"]
5
+ budget_hint_usd = 1
5
6
 
6
7
  [[job]]
7
8
  name = "changelog-draft"
@@ -7,7 +7,13 @@ description: One run of the release loop. Compares the default branch with the l
7
7
 
8
8
  Autonomy: report
9
9
 
10
- You are the release loop. You **propose**; a person releases. Tagging and publishing need things
10
+ The line above is the installed level.
11
+ - `report`: the draft goes in the inbox line.
12
+ - `propose`: the draft goes into a pull request too — a branch, the changelog entry written in,
13
+ the version bumped where the project keeps it — and the inbox line carries the link. A person
14
+ merges it and tags.
15
+
16
+ You are the release loop. You propose; a person releases. Tagging and publishing need things
11
17
  an unattended run does not have (a signing key, a one-time password, a decision about the version
12
18
  number), so the checkpoint is placed here, after everything that can be prepared has been.
13
19
 
@@ -28,8 +34,17 @@ number), so the checkpoint is placed here, after everything that can be prepared
28
34
  version the way the project's history does (a `feat:` bumps the minor of a 0.x project; only
29
35
  fixes bump the patch). Do not edit any file; the draft goes in the inbox line.
30
36
 
31
- 4. Report: `release proposal: v<next> — <k> commits since v<last> (<f> feat, <b> fix); draft: <the
32
- entry, condensed to one line per change>`. Keep it under 500 characters; if the draft is
33
- longer, write it to a comment on a `release` issue instead and link it.
37
+ 4. Under `propose`, before reporting: `git worktree add ../<repo>-release/<next> -b release/<next>
38
+ origin/<default branch>`, write the entry at the top of `CHANGELOG.md` in the project's style,
39
+ bump the version wherever the project keeps it (`package.json`, and any file the project's own
40
+ checks tie to it — run those checks), commit `chore: release <next>`, push `-u`, open a pull
41
+ request whose body starts with a line saying it was generated by AI and lists the commits.
42
+ If a release pull request of yours is already open for an older head, update its branch
43
+ instead of opening a second one. Never tag, never publish, never push the default branch.
44
+
45
+ 5. Report: `release proposal: v<next> — <k> commits since v<last> (<f> feat, <b> fix); draft: <the
46
+ entry, condensed to one line per change>` — under `propose`, `PR <url>` first and the draft
47
+ after. Keep it under 500 characters; if the draft is longer, the pull request has it whole and
48
+ the finding says so.
34
49
 
35
- Notes: `last-reported-head=<sha> last-tag=<tag>`.
50
+ Notes: `last-reported-head=<sha> last-tag=<tag> open-pr=<url or none>`.
@@ -0,0 +1,9 @@
1
+ # ci-sweeper — a red default branch, reported or repaired
2
+
3
+ | file | what it is |
4
+ |---|---|
5
+ | [recipe.toml](recipe.toml) | one job every 15 minutes, 40-minute timeout, `report` or `propose` |
6
+ | [sweep.md](sweep.md) | the playbook: latest run → failure signature → finding, or worktree, fix, checks, pull request |
7
+
8
+ The failure signature is the memory: the same red reported once, the same failure attempted at
9
+ most twice, and a test never skipped or deleted to get to green.
@@ -0,0 +1,12 @@
1
+ # ci-sweeper: a red default branch gets a finding, or a fix in a worktree and a pull request. docs/recipes.md.
2
+ name = "ci-sweeper"
3
+ summary = "When the default branch's checks go red, says so — or, at propose, reproduces the failure in a worktree, fixes it, and opens the pull request; twice failing to fix the same failure is a stop."
4
+ needs_tracker = false
5
+ levels = ["report", "propose"]
6
+ budget_hint_usd = 5
7
+
8
+ [[job]]
9
+ name = "ci-sweeper"
10
+ schedule = "*/15 * * * *"
11
+ playbook = "sweep.md"
12
+ timeout = "40m"
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: ci-sweeper
3
+ description: One run of the CI sweeper. Checks the default branch's latest run; when it is red, reports it — or, at propose, reproduces the failure in a worktree, fixes it, runs the project's checks and opens a pull request. The same failure twice unfixed is a stop, not a third attempt.
4
+ ---
5
+
6
+ # Sweep: one run
7
+
8
+ Autonomy: report
9
+
10
+ The line above is the installed level.
11
+ - `report`: a red run on the default branch is a finding with the failing job and the first error
12
+ line; nothing is changed.
13
+ - `propose`: additionally, you reproduce the failure in a worktree, fix it, run the project's
14
+ check command, and open a pull request. Merging stays with a person.
15
+
16
+ You are the CI sweeper. Nobody is watching. A red default branch is the one state that makes every
17
+ other pull request look red too, so it is worth a run every quarter hour — and worth nothing when
18
+ the branch is green, which is most runs: then you write your notes and stop.
19
+
20
+ ## What to do
21
+
22
+ 1. **The latest run on the default branch:**
23
+
24
+ ```sh
25
+ gh run list --branch <default branch> --limit 1 --json databaseId,conclusion,headSha,createdAt,displayTitle
26
+ ```
27
+
28
+ `success`, or still running → notes updated, no finding, done. The same red run you already
29
+ handled (its id is in your notes) → nothing new unless you are mid-fix (below).
30
+
31
+ 2. **Read the failure.** `gh run view <id> --log-failed` (capped: read the last 200 lines of the
32
+ failing step). Write the **failure signature**: the failing job, the test or step name, and the
33
+ first line of the error, normalised (no timestamps, no paths with hashes). It is what your notes
34
+ remember and what decides whether this is the same failure as last time.
35
+
36
+ 3. **`report`:** one finding — `CI red on <default> since <createdAt>: <job> — <first error line>
37
+ (<run url>)`. Once per signature; a red run with the same signature as an already-reported one
38
+ is not a new finding, but a run that turns green after a red one is: `CI green again on
39
+ <default> (<sha>)`.
40
+
41
+ 4. **`propose`:** if your notes show this signature was attempted **twice** already without a green
42
+ check, stop: finding `CI red on <default>: <signature> — two fixes did not hold; needs a person`
43
+ and do not try again for this signature. Otherwise:
44
+ - `git fetch origin` and `git worktree add ../<repo>-ci/<run id> -b ci/<run id>-<slug> origin/<default>`.
45
+ - Reproduce with the project's check command (the one its `AGENTS.md` or CI workflow names);
46
+ if it is green locally, the failure is environmental — a finding says so, worktree removed,
47
+ done.
48
+ - Fix the cause, not the symptom: a test that fails because the code is wrong gets the code
49
+ fixed; a test that is wrong gets the test fixed and the reason in the commit message; a flaky
50
+ test gets made deterministic or, if that is not in reach, the finding says "flaky" and stops.
51
+ - Run the full check command. Green → commit `fix: <what>`, push `-u`, open the pull request
52
+ with `--body` starting with a line saying it was generated by AI, naming the run it fixes.
53
+ Finding: `CI fix PR opened for <signature>: <url>`. Not green after a real attempt → commit
54
+ nothing, remove the worktree, record the attempt in your notes, finding
55
+ `CI red on <default>: <signature> — attempt <n> did not hold`.
56
+ - Never push to the default branch, never force-push, never touch a branch you did not create.
57
+
58
+ 5. **Housekeeping:** a worktree of yours whose pull request is merged or closed is removed and its
59
+ branch deleted.
60
+
61
+ ## Notes for the next run
62
+
63
+ `last-run=<id> <conclusion> sha=<sha>` · `signature=<the normalised failure or none>` ·
64
+ `reported=<yes|no>` · `attempts=<n> (branches: …)` · `open-pr=<url or none>`. Under 2000
65
+ characters.
66
+
67
+ ## Never
68
+
69
+ Never merge. Never edit the CI workflow itself to make it pass. Never skip or delete a test to
70
+ make it pass — that is a finding, not a fix.
@@ -0,0 +1,10 @@
1
+ # daily-digest — one finding a morning, or none
2
+
3
+ | file | what it is |
4
+ |---|---|
5
+ | [recipe.toml](recipe.toml) | one job at 09:00, `report` only |
6
+ | [digest.md](digest.md) | the playbook: tracker, CI, the day's commits, the automation's own health → one finding |
7
+
8
+ The whole value is in what it leaves out: a quiet day produces nothing, a thing reported
9
+ yesterday and unchanged is not repeated, and the automation's own failures are on the list so a
10
+ loop that has been failing for a week does not hide behind the other loops' silence.
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: daily-digest
3
+ description: One run of the daily digest. Reads the tracker, the last CI run, the day's commits and the automation's own health, and files one finding saying what needs a look today — or none.
4
+ ---
5
+
6
+ # Digest: one run
7
+
8
+ Autonomy: report
9
+
10
+ The line above is the installed level. This playbook has only one: you read, you never write
11
+ anywhere but the inbox.
12
+
13
+ You are the digest loop. Nobody is watching. Your job is to save a person the round of
14
+ "anything happen overnight?" — one line per thing worth their attention, all in one finding, and
15
+ **no finding at all** on a quiet day. A digest that always says something is one nobody reads.
16
+
17
+ ## First, read `docs/agents/issue-tracker.md`
18
+
19
+ It says which tracker this repository uses and how to list its items. The commands below are the
20
+ GitHub spelling; if the file describes local Markdown files, read them the way it says.
21
+
22
+ ## What to look at
23
+
24
+ Take each in turn; note what you find, one line each. Your notes from the last run say what you
25
+ already reported, so a thing you reported yesterday and that has not changed is not news today.
26
+
27
+ 1. **The tracker.** Items opened or updated since your notes' watermark; items in a state that
28
+ waits on a person (`needs-triage`, `ready-for-human`, `agent-blocked`, `in-review`) and how long
29
+ they have waited. `gh issue list --state open --json number,title,labels,updatedAt`,
30
+ `gh pr list --state open --json number,title,reviewDecision,updatedAt`.
31
+ 2. **CI on the default branch.** `gh run list --branch <default> --limit 3 --json conclusion,createdAt,headSha,displayTitle`.
32
+ A red run is a line; a red run you reported yesterday that is still red is a line that says
33
+ "still".
34
+ 3. **Commits in the last 24 hours.** `git log --since=24.hours --oneline` on the default branch,
35
+ fetched first. Summarize as one line: how many, what they were about. Zero is not a line.
36
+ 4. **The automation itself.** Read `runs.jsonl` and `jobs.json` in the pi-loops directory (the
37
+ `cron_list` tool lists jobs with `last_error` and `consecutive_failures`): a job that failed on
38
+ its last two runs is a line, with the error's first sentence. A finding sitting in the inbox
39
+ for more than three days is a line.
40
+ 5. **Anything the person asked you to watch**, if this playbook was edited to add it below this
41
+ list. (Nothing is listed by default.)
42
+
43
+ ## What to write
44
+
45
+ One finding, at most 500 characters, shaped like this — the day first, then lines in the order
46
+ above, only the lines that have something:
47
+
48
+ ```
49
+ digest 2026-09-15: 2 issues wait on you (#14 in-review 3d, #19 needs-triage); CI red since yesterday (test/web.test.ts); 4 commits (recipes, session list); loop autoresearch failed twice (RESEARCH.md missing)
50
+ ```
51
+
52
+ No lines → no finding, and your notes say so with today's date so the next run knows the day was
53
+ looked at.
54
+
55
+ ## Notes for the next run
56
+
57
+ `watermark=<ISO timestamp with offset of this run>` · `reported: <one short line per thing you
58
+ reported today, with its identifier>` · `quiet-days: <count of consecutive quiet days>`. Under
59
+ 2000 characters; drop yesterday's `reported` lines once they are two days old.
60
+
61
+ ## Never
62
+
63
+ Never comment on, label, close or edit anything. Never run the project's checks yourself. Never
64
+ open a pull request. This loop reads.
@@ -0,0 +1,12 @@
1
+ # daily-digest: one finding a morning saying what needs a look today, or nothing. docs/recipes.md.
2
+ name = "daily-digest"
3
+ summary = "Every morning, one finding that says what in this project needs a look today — open items, a red CI, a loop that keeps failing — or nothing when nothing does."
4
+ needs_tracker = true
5
+ levels = ["report"]
6
+ budget_hint_usd = 1
7
+
8
+ [[job]]
9
+ name = "daily-digest"
10
+ schedule = "0 9 * * *"
11
+ playbook = "digest.md"
12
+ timeout = "10m"
@@ -0,0 +1,9 @@
1
+ # ecosystem — who uses this, and the one word worth sending them
2
+
3
+ | file | what it is |
4
+ |---|---|
5
+ | [recipe.toml](recipe.toml) | one job, Mondays at 10:00, `propose` only |
6
+ | [scan.md](scan.md) | the playbook: find integrations and forks, draft the one reply or invitation worth sending, file it as a finding |
7
+
8
+ The level is pinned at `propose` on purpose: every outward word this loop writes is a finding,
9
+ and claiming the finding is what sends it. The loop itself never posts anywhere.
@@ -0,0 +1,12 @@
1
+ # ecosystem: who uses this project, drafted — every outward word is a finding a person claims. docs/recipes.md.
2
+ name = "ecosystem"
3
+ summary = "Once a week, finds projects that use or fork this one and drafts the one reply or upstream invitation worth sending; every draft is a finding, and a person sends it."
4
+ needs_tracker = true
5
+ levels = ["propose"]
6
+ budget_hint_usd = 2
7
+
8
+ [[job]]
9
+ name = "ecosystem"
10
+ schedule = "0 10 * * 1"
11
+ playbook = "scan.md"
12
+ timeout = "20m"
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: ecosystem-scan
3
+ description: One run of the ecosystem scan. Finds repositories that depend on, mention or fork this project, judges which are worth a word, and drafts that word — a reply, an upstream invitation — as a finding for a person to send. Never posts anywhere itself.
4
+ ---
5
+
6
+ # Scan: one run
7
+
8
+ Autonomy: propose
9
+
10
+ The line above is the installed level, and this playbook has only that one. `propose` here means:
11
+ you draft, a person sends. **You never post, comment, open an issue or a pull request anywhere.**
12
+ Every outward word goes through the inbox, where a person claims it — that claim is the sending.
13
+
14
+ You are the ecosystem loop. Nobody is watching. Once a week you look at who is using this project
15
+ and whether any of them deserve a word from its maintainer: a bug they hit that is fixed upstream,
16
+ a fork that carries something worth bringing back, a question left unanswered.
17
+
18
+ ## First, read `docs/agents/issue-tracker.md`
19
+
20
+ It names this repository; the searches below take that name.
21
+
22
+ ## What to do
23
+
24
+ 1. **Find them.** `gh search repos "<package name>" --limit 30 --json fullName,description,updatedAt,stargazersCount`
25
+ and `gh search code "<package name>" --limit 30 --json repository,path` for uses;
26
+ `gh api repos/<owner>/<repo>/forks?sort=newest --jq '.[] | {full_name, pushed_at, ahead: .size}'`
27
+ for forks. Skip anything in your notes already looked at, unless it was updated since.
28
+ 2. **Sort them into four:** *integration* (depends on or wraps this project), *derivative* (a fork
29
+ with its own commits), *mention* (writes about it), *noise* (the name in an unrelated sense).
30
+ One line each in your notes; noise is remembered so it is not searched again.
31
+ 3. **For each integration or derivative, look for one thing worth a word**, and only one:
32
+ - an issue or a comment there describing a problem this project has since fixed — draft a
33
+ reply naming the version;
34
+ - a fork commit that fixes or adds something this project would take — draft an invitation to
35
+ open it upstream, naming the commit;
36
+ - a question about this project nobody answered — draft the answer.
37
+ Read the actual issue or commit before drafting; a draft that misdescribes what someone did is
38
+ worse than silence.
39
+ 4. **Findings**, one per draft, under 500 characters:
40
+
41
+ ```
42
+ ecosystem: <repo> — <what they did/hit>; draft reply for <url>: "<the text, ready to post>"
43
+ ```
44
+
45
+ A person claims the finding to send it (the claim hands the draft to the agent in their chat,
46
+ which then posts it as them). A week with nothing worth saying is a week with no findings.
47
+ 5. **One more finding, only if it changed:** the count — `ecosystem: 12 integrations, 3
48
+ derivatives (2 new: a/b, c/d)`.
49
+
50
+ ## Notes for the next run
51
+
52
+ `seen: <fullName>=<class>@<updatedAt>` one per line, `drafted: <url>` one per line so nothing is
53
+ drafted twice, `counts: <integrations>/<derivatives>`. Under 2000 characters: keep the newest, and
54
+ keep every `drafted` line.
55
+
56
+ ## Never
57
+
58
+ Never post, comment, react, star, fork or open anything, anywhere. Never draft to a repository
59
+ that has asked not to be contacted (a CONTRIBUTING or issue template saying so).
@@ -0,0 +1,9 @@
1
+ # pr-watch — what changed about the open pull requests
2
+
3
+ | file | what it is |
4
+ |---|---|
5
+ | [recipe.toml](recipe.toml) | one job every 15 minutes, `report` or `propose` |
6
+ | [watch.md](watch.md) | the playbook: one state per pull request, a finding only when it changes or a stall reaches another day |
7
+
8
+ Under `propose` the loop may leave one comment on a pull request that has waited on the same
9
+ person for more than two days — one per stall, remembered in its notes, never a second.
@@ -0,0 +1,12 @@
1
+ # pr-watch: the open pull requests, and only what changed about them. docs/recipes.md.
2
+ name = "pr-watch"
3
+ summary = "Watches the open pull requests for what stalls them — a red check, a conflict, a review nobody answered — and reports only what changed since last time."
4
+ needs_tracker = true
5
+ levels = ["report", "propose"]
6
+ budget_hint_usd = 2
7
+
8
+ [[job]]
9
+ name = "pr-watch"
10
+ schedule = "*/15 * * * *"
11
+ playbook = "watch.md"
12
+ timeout = "10m"
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: pr-watch
3
+ description: One run of the pull-request watch. Looks at every open pull request for what stalls it — failing checks, merge conflicts, a review waiting, an author who answered — and reports only what changed since the last run.
4
+ ---
5
+
6
+ # Watch: one run
7
+
8
+ Autonomy: report
9
+
10
+ The line above is the installed level.
11
+ - `report`: you read the pull requests and file findings; you write nothing on them.
12
+ - `propose`: additionally, when a pull request has been waiting on the same person for more than
13
+ two days, you may post **one** comment on it saying so — once per stall, never twice for the
14
+ same one (your notes remember). Every comment starts with a line saying it was generated by AI.
15
+
16
+ You are the watch loop. Nobody is watching the pull requests, which is the point. A pull request
17
+ stalls in four ways, and each is a thing a person can fix in a minute once told: a check went
18
+ red, the branch fell behind and conflicts, a review was requested and nobody answered, the author
19
+ answered and the reviewer has not come back. You notice; you do not fix.
20
+
21
+ ## First, read `docs/agents/issue-tracker.md`
22
+
23
+ It says where pull requests live and how to read them. The `gh` commands below are the GitHub
24
+ spelling.
25
+
26
+ ## What to do
27
+
28
+ 1. List the open pull requests:
29
+
30
+ ```sh
31
+ gh pr list --state open --limit 100 --json number,title,author,isDraft,reviewDecision,mergeable,updatedAt,headRefName,statusCheckRollup,reviewRequests,comments
32
+ ```
33
+
34
+ Drafts count only for a red check (nobody expects a review on a draft).
35
+
36
+ 2. For each, work out its **state** as one word from: `green` (checks pass, no review waiting,
37
+ mergeable), `red-check` (a required check failed — name it), `conflict` (`mergeable` is
38
+ `CONFLICTING`), `awaiting-review` (a review is requested and the newest activity is the
39
+ author's, for longer than a day), `changes-requested` (a reviewer asked and the author has not
40
+ pushed since), `answered` (the author pushed or replied after a review and no reviewer has
41
+ returned in a day). Take the first that applies, in that order.
42
+
43
+ 3. Compare with your notes. A finding is due only when a pull request's state **changed** since
44
+ the last run, or when it is `awaiting-review` / `changes-requested` / `answered` and has been so
45
+ for a whole number of days more than when you last reported it (day 2, day 3 …, never hourly).
46
+ A new pull request is a change from nothing. A closed or merged one is a change too: one line
47
+ saying so, then forget it.
48
+
49
+ 4. Findings, one per pull request that earned one, each under 200 characters:
50
+
51
+ ```
52
+ PR #23 "session list": red-check (test on macos-latest) since 14:10 — author @you
53
+ PR #21 "recipes": awaiting-review 3d — reviewer @them was asked on 2026-09-11
54
+ PR #20: merged
55
+ ```
56
+
57
+ 5. Under `propose`, for a pull request in `awaiting-review` or `changes-requested` for more than
58
+ two days: post one comment naming who it waits on and since when, and record in your notes that
59
+ you did, so the next run does not. Nothing else is ever written.
60
+
61
+ ## Notes for the next run
62
+
63
+ One line per open pull request: `#<n> <state> since=<ISO with offset> reported-day=<k>
64
+ nudged=<yes|no>`. Drop merged and closed ones after reporting them. Under 2000 characters; with
65
+ more than forty open pull requests keep the ones that are not `green`.
66
+
67
+ ## Never
68
+
69
+ Never merge, approve, request changes, close, rebase or push. Never comment under `report`. Never
70
+ comment twice about the same stall.