@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 +21 -0
- package/README.md +6 -5
- package/README.zh-CN.md +1 -1
- package/docs/recipes.md +52 -6
- package/package.json +2 -1
- package/recipes/changelog-draft/recipe.toml +2 -1
- package/recipes/changelog-draft/release.md +20 -5
- package/recipes/ci-sweeper/README.md +9 -0
- package/recipes/ci-sweeper/recipe.toml +12 -0
- package/recipes/ci-sweeper/sweep.md +70 -0
- package/recipes/daily-digest/README.md +10 -0
- package/recipes/daily-digest/digest.md +64 -0
- package/recipes/daily-digest/recipe.toml +12 -0
- package/recipes/ecosystem/README.md +9 -0
- package/recipes/ecosystem/recipe.toml +12 -0
- package/recipes/ecosystem/scan.md +59 -0
- package/recipes/pr-watch/README.md +9 -0
- package/recipes/pr-watch/recipe.toml +12 -0
- package/recipes/pr-watch/watch.md +70 -0
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.
|
|
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
|
|
142
|
-
that two loops turn, `/recipe add autoresearch` runs one experiment per run against a
|
|
143
|
-
|
|
144
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
`
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
##
|
|
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.
|
|
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
|
-
|
|
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.
|
|
32
|
-
|
|
33
|
-
|
|
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.
|