@open-tomato/rafa 0.26.0 → 0.31.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/README.md +220 -14
- package/dist/bundled/skills/dev-planner/SKILL.md +4 -4
- package/dist/cli.js +26094 -18698
- package/dist/{describe-vbwqxj1f.js → describe-b2kfwphn.js} +3 -3
- package/dist/effort/store/index.js +4 -4
- package/dist/{index-azptzvp3.js → index-13wzpgc1.js} +2248 -269
- package/dist/{index-p1265xgj.js → index-89j1m0f6.js} +3 -3
- package/dist/{index-2yd8bber.js → index-b766n1aw.js} +3417 -2752
- package/dist/{index-sf2brh4w.js → index-dnwcx1cs.js} +2 -1
- package/dist/{index-4j1e3h26.js → index-e10rwa4g.js} +1 -1
- package/dist/{index-xwr54rzt.js → index-p0njjkjj.js} +2 -2
- package/dist/{index-59x3zsh4.js → index-szgyrz6j.js} +6974 -2599
- package/dist/{index-rv7y8y1j.js → index-t6q4k1tq.js} +113 -26
- package/dist/index.js +12 -10
- package/dist/plan/index.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -153,9 +153,9 @@ the next one, so you rarely have to remember it.
|
|
|
153
153
|
stored, one commit. A task that reports `blocked` is marked and the
|
|
154
154
|
run stops with the reason; fix what it names and start again, and the
|
|
155
155
|
blocked task goes first. After the last task a wrap-up session syncs
|
|
156
|
-
with the base,
|
|
157
|
-
|
|
158
|
-
repair sessions on a red one.
|
|
156
|
+
with the base, commits a release fragment when the project has them
|
|
157
|
+
(the plan's level and notes), pushes, opens the pull request and waits
|
|
158
|
+
for CI, spending repair sessions on a red one.
|
|
159
159
|
|
|
160
160
|
From another terminal: `rafa loop status`, `rafa loop pause` (after the
|
|
161
161
|
running task), `rafa loop stop` (now).
|
|
@@ -163,23 +163,73 @@ the next one, so you rarely have to remember it.
|
|
|
163
163
|
5. **Land it and look at what it cost.**
|
|
164
164
|
|
|
165
165
|
```bash
|
|
166
|
-
rafa pr current
|
|
167
|
-
rafa pr triage
|
|
168
|
-
rafa pr merge [--skip-checks] # asks y/N, merges, switches to the base, pulls, deletes
|
|
169
|
-
rafa release
|
|
166
|
+
rafa pr current # number, title, checks, URL
|
|
167
|
+
rafa pr triage # why is it red, and is the fix simple (🪙 only with --resolve)
|
|
168
|
+
rafa pr merge [--skip-checks] # asks y/N, merges, switches to the base, pulls, deletes branches
|
|
169
|
+
rafa release settle # folds fragments to a version and pushes
|
|
170
|
+
rafa release tag # tag the released version (optional, depends on config)
|
|
170
171
|
rafa effort collect && rafa effort report
|
|
171
172
|
```
|
|
172
173
|
|
|
173
|
-
Then step 2 again, or `rafa plan create --next`.
|
|
174
|
+
Then step 2 again, or `rafa plan create --next`. When `rafa next` is used,
|
|
175
|
+
it runs settle and optionally tag as part of the workflow.
|
|
176
|
+
|
|
177
|
+
### Release fragments and settling
|
|
178
|
+
|
|
179
|
+
A branch no longer owns a version number. Instead, the wrap-up commits a
|
|
180
|
+
release fragment (a small markdown file naming the plan, level and notes),
|
|
181
|
+
and `rafa release settle` assigns the version on the base branch after
|
|
182
|
+
merges. This lets several branches merge in any order without a version
|
|
183
|
+
collision: whichever machine settles first releases all waiting fragments
|
|
184
|
+
under one version.
|
|
185
|
+
|
|
186
|
+
The pull request shows a forecast of what version the branch would get if
|
|
187
|
+
merged now. The fragment is stored under `release.fragments` (default
|
|
188
|
+
`.changes/`), one file per plan. `rafa doctor` warns when fragments are
|
|
189
|
+
waiting and suggests running settle. `rafa pr list` marks forecasts as
|
|
190
|
+
`(base moved)` if the base branch changed since the forecast was written.
|
|
191
|
+
|
|
192
|
+
Every project starts with fragments settling to the base branch and tagging
|
|
193
|
+
by hand. You can configure settle to open a pending release pull request
|
|
194
|
+
instead (for protected base branches), and have settle tag the commit
|
|
195
|
+
automatically (for CI ownership of releases). Four keys, all optional:
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
# Empty config: fragments in .changes/, semver-by-level, settle pushes, tags by hand
|
|
199
|
+
# (This is the default for every new project.)
|
|
200
|
+
|
|
201
|
+
release:
|
|
202
|
+
tag: settle # settle tags the commit it pushed
|
|
203
|
+
|
|
204
|
+
release:
|
|
205
|
+
settle: pr # protected base branch: one pending release PR
|
|
206
|
+
pr:
|
|
207
|
+
versionCollision: refuse
|
|
208
|
+
|
|
209
|
+
release:
|
|
210
|
+
settle: pr
|
|
211
|
+
tag: settle
|
|
212
|
+
pr:
|
|
213
|
+
versionCollision: refuse
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The keys are: `release.fragments` (path, default `.changes/`), `release.strategy`
|
|
217
|
+
(fold strategy, default `semver-by-level`), `release.settle` (`push` or `pr`,
|
|
218
|
+
default `push`), `release.tag` (`manual` or `settle`, default `manual`).
|
|
219
|
+
`pr.versionCollision` controls what happens when a merge would collide
|
|
220
|
+
(default `report`). Read `context/release.md` for the full design,
|
|
221
|
+
`docs/ci-release-settle.md` for automating settle in CI, and
|
|
222
|
+
`rafa release settle --help` for the settle command.
|
|
174
223
|
|
|
175
224
|
### Tracking effort and learning from skills
|
|
176
225
|
|
|
177
226
|
Each run records what it cost in time, tokens and work, and which skills
|
|
178
|
-
were offered, invoked and worked.
|
|
227
|
+
were offered, invoked and worked. Key commands for the effort record:
|
|
179
228
|
|
|
180
229
|
```bash
|
|
181
230
|
rafa effort collect # gather and store session logs
|
|
182
231
|
rafa effort report # per-plan summary: sessions, tasks, costs
|
|
232
|
+
rafa effort import <file> # merge another device's store into this one
|
|
183
233
|
```
|
|
184
234
|
|
|
185
235
|
`rafa effort report` shows the per-plan tables (sessions by status and
|
|
@@ -210,6 +260,92 @@ The report opens with a fixed line explaining that it shows co-occurrence,
|
|
|
210
260
|
not causation: a skill can be invoked and its failure recur for reasons the
|
|
211
261
|
skill does not cover.
|
|
212
262
|
|
|
263
|
+
To see whether task sessions are getting more expensive over time, and
|
|
264
|
+
whether that follows the plan or the calendar:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
rafa effort report --trend # last 3 days against the 14 before, then the loops
|
|
268
|
+
rafa effort report --trend --loops=10 --by=effort
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The trend report has two parts. **Trend** compares the recent task
|
|
272
|
+
sessions with a baseline: median and p90 of minutes, output tokens,
|
|
273
|
+
cache-read tokens and turns per task, one row per day, and the sessions
|
|
274
|
+
over the outlier line (baseline median plus three robust standard
|
|
275
|
+
deviations). **Loops** lists one row per plan's loop, newest first: its
|
|
276
|
+
wall-clock and summed task time, number of tasks, minutes per task (min,
|
|
277
|
+
max, average, median), and average tokens and turns per task. Under the
|
|
278
|
+
rows, a drift reading tests each per-task figure across the loops in the
|
|
279
|
+
order they ran (the Mann-Kendall trend test). `no steady trend` means the
|
|
280
|
+
cost moves with the plan; `rising` means it grows whatever the plan.
|
|
281
|
+
|
|
282
|
+
To see everything at once, the loops running now included:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
rafa effort collect && rafa effort dashboard
|
|
286
|
+
rafa effort dashboard --output=json # one key per widget, for a dashboard
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The dashboard prints the running loops with their tasks done over total
|
|
290
|
+
and three estimates of the time left: **by task** (the plan's average task
|
|
291
|
+
session), **by progress** (the time since the plan first started, per task
|
|
292
|
+
done), and **this session** (what `rafa loop status` prints). By progress
|
|
293
|
+
counts the time between tasks too, so it is the one to compare against
|
|
294
|
+
the clock. Then come the trend, the loops, each plan's skills M1 and M2,
|
|
295
|
+
and the totals of every stored session.
|
|
296
|
+
|
|
297
|
+
### Choosing how the effort store travels
|
|
298
|
+
|
|
299
|
+
An effort store can travel between a project's devices in different ways, each
|
|
300
|
+
suited to different setups. Set `effort.sync` in `.rafa/config.yaml`:
|
|
301
|
+
|
|
302
|
+
| Setup | Strategy | Why |
|
|
303
|
+
|---|---|---|
|
|
304
|
+
| One laptop | `local` | Nothing to sync |
|
|
305
|
+
| Two personal devices, occasional | `file` | No server, no account; copy a file |
|
|
306
|
+
| Personal projects, frequent | `git` | History and access from git, works offline |
|
|
307
|
+
| A company or several teams | `service` | One hub, live status, shared rules |
|
|
308
|
+
| Advanced, exploratory | `p2p` | Devices talk directly (spike) |
|
|
309
|
+
|
|
310
|
+
Configuration examples:
|
|
311
|
+
|
|
312
|
+
```yaml
|
|
313
|
+
# Default: no sync, each device keeps its own store
|
|
314
|
+
# (nothing set, or effort: {sync: local})
|
|
315
|
+
|
|
316
|
+
# File exchange with no server
|
|
317
|
+
effort:
|
|
318
|
+
sync: file
|
|
319
|
+
|
|
320
|
+
# Hub-based sync (requires module and service URL)
|
|
321
|
+
effort:
|
|
322
|
+
sync: service
|
|
323
|
+
syncUrl: https://hub.example.com
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Only `local` and `file` ship with rafa; `git`, `service` and `p2p` need a module.
|
|
327
|
+
|
|
328
|
+
### Starting a second device
|
|
329
|
+
|
|
330
|
+
To run rafa on a second device for the same project, bring the effort store over
|
|
331
|
+
consistently using `rafa effort copy` and `rafa effort import`:
|
|
332
|
+
|
|
333
|
+
1. On the new device, check for an existing `.rafa/effort/` store and set
|
|
334
|
+
it aside (rename or back up).
|
|
335
|
+
2. From the primary device, make a consistent copy:
|
|
336
|
+
```bash
|
|
337
|
+
rafa effort copy --to=<path>
|
|
338
|
+
```
|
|
339
|
+
Or use `sqlite3 .rafa/effort/effort.sqlite ".backup <path>"`, but never
|
|
340
|
+
a plain file copy during a write.
|
|
341
|
+
3. Transfer the copied store and `.rafa/config.yaml` to the new device's
|
|
342
|
+
`.rafa/` directory. **The exported file holds private text — task
|
|
343
|
+
descriptions, findings, and notes — meant only for your own devices.**
|
|
344
|
+
4. After both devices have recorded effort, bring them back together with:
|
|
345
|
+
```bash
|
|
346
|
+
rafa effort import <copied-store-file>
|
|
347
|
+
```
|
|
348
|
+
|
|
213
349
|
### Which agents and skills are involved
|
|
214
350
|
|
|
215
351
|
A task line may end with a declaration, for example
|
|
@@ -282,11 +418,13 @@ with its section line, to change it.
|
|
|
282
418
|
### `effort`
|
|
283
419
|
|
|
284
420
|
One setting says how long a command waits for another rafa process that
|
|
285
|
-
is writing the effort store before it gives up with `SQLITE_BUSY
|
|
421
|
+
is writing the effort store before it gives up with `SQLITE_BUSY`, and
|
|
422
|
+
another names how the store travels between a project's devices.
|
|
286
423
|
|
|
287
424
|
| Key | Default | What it sets |
|
|
288
425
|
|---|---|---|
|
|
289
426
|
| `effort.busyTimeoutMs` | `5000` | the wait in milliseconds, a whole number from 1 to 60000 |
|
|
427
|
+
| `effort.sync` | `local` | how the store travels: `local`, `file`, `git`, `service` or `p2p` |
|
|
290
428
|
|
|
291
429
|
There is no unlimited wait, since a lock wait with no end can hang a
|
|
292
430
|
loop; `0`, a fraction and a quoted `"5000"` are refused.
|
|
@@ -331,6 +469,56 @@ status:
|
|
|
331
469
|
notice: false
|
|
332
470
|
```
|
|
333
471
|
|
|
472
|
+
### `loop` and the guard
|
|
473
|
+
|
|
474
|
+
A loop guards itself: every turn, it watches the checkout's branch and HEAD
|
|
475
|
+
and halts if either changes externally (a branch switch in another terminal,
|
|
476
|
+
a pull that moved the base). The work stays committed and nothing is lost;
|
|
477
|
+
without the guard the edits would move with the checkout. The guard runs on
|
|
478
|
+
every loop and never interferes with the loop's own commits.
|
|
479
|
+
|
|
480
|
+
**Worktree use cases.** `loop start --as-worktree` runs the loop in a new
|
|
481
|
+
git worktree alongside your main checkout, so you stay on `main` in your
|
|
482
|
+
terminal and can keep working — reviewing, pulling or merging other PRs —
|
|
483
|
+
while the loop runs beside you. This is a git worktree, the second desk on
|
|
484
|
+
the same repository, with its own branch and its own working directory. Both
|
|
485
|
+
share one `.rafa/` and one effort store. The worktree is created under
|
|
486
|
+
`.rafa/worktrees/` by default, or in the directory `loop.worktreeDir` names
|
|
487
|
+
when configured. A loop without `--as-worktree` runs in the current checkout,
|
|
488
|
+
which is unchanged. The guard stops a loop if you switch away from it; with
|
|
489
|
+
a worktree, you are free to switch the main checkout to anything, and the
|
|
490
|
+
loop stays on its branch. Two loops run in two worktrees, each with its own
|
|
491
|
+
branch, both under the same `.rafa/`, allowing parallel work on two epics at
|
|
492
|
+
once. Merging a PR while a loop runs no longer offers `rafa self-update` right
|
|
493
|
+
away; `pr merge` names the running loop and asks you to update after it stops,
|
|
494
|
+
since the binary is what the loop spawns its sessions from, and swapping it
|
|
495
|
+
mid-run changes the engine while driving.
|
|
496
|
+
|
|
497
|
+
**Edge case: the 2026-09-29 incident.** A loop switched the main checkout to
|
|
498
|
+
its branch; 48 seconds later, `git checkout main` and `git pull` in another
|
|
499
|
+
terminal moved it back, and the edits followed. Both branches were at the
|
|
500
|
+
same commit, so `git switch <loop-branch>` recovered it. With the guard the
|
|
501
|
+
loop halts instead, keeping the work and the branch where it is while you
|
|
502
|
+
sort out what happened.
|
|
503
|
+
|
|
504
|
+
| Key | Default | What it sets |
|
|
505
|
+
|---|---|---|
|
|
506
|
+
| `loop.worktreeDir` | `.rafa/worktrees` | the directory where `--as-worktree` creates worktrees, absolute or relative to the project root |
|
|
507
|
+
| `dangerous.selfUpdateDuringLoop` | `false` | whether `rafa self-update` runs while a loop of this project is live |
|
|
508
|
+
|
|
509
|
+
Both take a string and a boolean as written; quoted values are refused.
|
|
510
|
+
|
|
511
|
+
```yaml
|
|
512
|
+
# empty: loops run in the current checkout, guarded
|
|
513
|
+
# --as-worktree puts them in .rafa/worktrees (the default)
|
|
514
|
+
|
|
515
|
+
loop:
|
|
516
|
+
worktreeDir: ../rafa-loops # worktrees beside the repository
|
|
517
|
+
|
|
518
|
+
dangerous:
|
|
519
|
+
selfUpdateDuringLoop: true # update the binary even while a loop runs
|
|
520
|
+
```
|
|
521
|
+
|
|
334
522
|
## Specs, issues and the roadmap
|
|
335
523
|
|
|
336
524
|
You can plan from a local file and never touch a board. When you want
|
|
@@ -376,6 +564,19 @@ from GitHub Issues:
|
|
|
376
564
|
file `.rafa/position.json` holds your current place, where you were, and
|
|
377
565
|
where you came from — the same two-slot pattern as `cd -` and
|
|
378
566
|
`git checkout -`, plus a home anchor.
|
|
567
|
+
- **Hopping between epics with `rafa next --roadmap`.** When your epic's
|
|
568
|
+
work is blocked by an issue in another epic, `rafa next --roadmap`
|
|
569
|
+
reaches for that blocker, works it, and comes home. You hold two slots:
|
|
570
|
+
home (your anchor) and current (the work in hand). Reaching for a third
|
|
571
|
+
would mean letting go of home, so rafa halts if a blocker is itself
|
|
572
|
+
blocked. The pattern keeps unattended work safe: a hop into another
|
|
573
|
+
team's epic asks for that team's review before it changes their code.
|
|
574
|
+
Solo projects see a dry hop — the useful part without the permission
|
|
575
|
+
check. One team hops between its own epics. Several teams open a pull
|
|
576
|
+
request when a hop crosses to another board, and `rafa status` says
|
|
577
|
+
`waiting on #C (owner review)` until the team reviews and approves. The
|
|
578
|
+
hop record (`.rafa/hop.json`) tracks where home is and what rafa works.
|
|
579
|
+
When `rafa next` finishes, it deletes the record and returns home.
|
|
379
580
|
- **Other trackers.** GitHub Issues is what works today. Linear support
|
|
380
581
|
is being ported from the project rafa grew out of, as an optional
|
|
381
582
|
add-on in a later version. For anything else, open or upvote a request
|
|
@@ -425,10 +626,13 @@ of `~/.bun/bin`; both warn when it is not, and `rafa doctor` checks it.
|
|
|
425
626
|
Both exit 1 before building anything while a plan tracker in `plan.dir`
|
|
426
627
|
(`.rafa/plans` unless `.rafa/config.yaml` names another) still holds an
|
|
427
628
|
open or blocked task, and name every such tracker. `rafa self-update`
|
|
428
|
-
runs only inside a project, so `rafa init` the checkout first
|
|
629
|
+
runs only inside a project, so `rafa init` the checkout first, and it
|
|
630
|
+
also exits 1 while a loop of the project is live, naming each loop's
|
|
631
|
+
branch and pid, unless `dangerous.selfUpdateDuringLoop` is true in the
|
|
632
|
+
config; `--force` does not override that wait. They exit
|
|
429
633
|
2 when they could not run: a `package.json` that is not rafa's or cannot
|
|
430
|
-
be read, a config or tracker they could not read,
|
|
431
|
-
link that failed.
|
|
634
|
+
be read, a config or tracker they could not read, a loop record
|
|
635
|
+
`rafa self-update` could not read, or a build, copy or link that failed.
|
|
432
636
|
|
|
433
637
|
A version is installed once. Both also exit 1, before building, when
|
|
434
638
|
`~/.rafa/runtime/<version>/` is already there, naming that directory and
|
|
@@ -485,7 +689,9 @@ and a line here is ticked by the change that finishes the feature.
|
|
|
485
689
|
- ✅ Plan straight from the issue board, or from whatever is next on the roadmap
|
|
486
690
|
- ✅ Review, merge and clean up pull requests from the command line
|
|
487
691
|
- ✅ A failing pull request is diagnosed, and fixed when the fix is simple
|
|
488
|
-
- ✅
|
|
692
|
+
- ✅ Release fragments on branches, settled to a version on the base
|
|
693
|
+
branch after merges, with forecasts in the PR and protection for
|
|
694
|
+
multiple concurrent branches
|
|
489
695
|
- ✅ Start a plan from the main branch and rafa makes the branch for you
|
|
490
696
|
- ✅ One command takes you to the next step: merge, clean up, plan,
|
|
491
697
|
branch, start
|
|
@@ -155,7 +155,7 @@ Recognized fields:
|
|
|
155
155
|
- `stub` — The plan identifier (string, following the `rafa-<n>-<slug>` format). Used to organize findings and dedupe rows across runs.
|
|
156
156
|
- `issue` — Optional GitHub issue number (integer, e.g., `123`). Links the work back to the `open-tomato/rafa` board.
|
|
157
157
|
- `spec` — Optional path to the specification document that guided the plan.
|
|
158
|
-
- `release` — Optional release level for the version bump this plan's pull request ships: `patch`, `minor`, `major` or `none`. Anything else is reported as `unusable-field` and read as if unset.
|
|
158
|
+
- `release` — Optional release level for the version bump this plan's pull request ships: `patch`, `minor`, `major` or `none`. Anything else is reported as `unusable-field` and read as if unset. When absent, the level is the highest severity among the plan's collected change notes. The wrap-up writes this level to a release fragment file (under `release.fragments`, default `.changes/`) that `rafa release settle` will process to compute the version bump after the branch merges.
|
|
159
159
|
|
|
160
160
|
Unknown keys are retained and ignored by the loop; they do not cause parsing to fail.
|
|
161
161
|
|
|
@@ -270,7 +270,7 @@ Report fields:
|
|
|
270
270
|
| `skills_used` | list of strings | Names of skills referenced or applied |
|
|
271
271
|
| `blockers` | list of objects | What blocked the task. Any entry marks the task `[BLOCKED]` whatever `status` says, so write `[]` when nothing did |
|
|
272
272
|
| `out_of_scope_bugs` | list of objects | Bugs found that are outside this task's scope |
|
|
273
|
-
| `changes` | list of objects |
|
|
273
|
+
| `changes` | list of objects | Release notes for this task's own diff (contributing to the release fragment), one entry per user-visible change (see below). An absent list is empty with no issue; a task whose diff a user would notice nothing of writes one entry at level `none`. The wrap-up collects these from all tasks and writes them to the fragment that `rafa release settle` processes after the branch merges. |
|
|
274
274
|
|
|
275
275
|
Finding entry fields:
|
|
276
276
|
|
|
@@ -414,13 +414,13 @@ The second example above has no `skills=` and is still a valid task declaration.
|
|
|
414
414
|
invariant sweeps), live-seam runs, migrations, and close-out — these
|
|
415
415
|
preserve resumability where a halt is most likely.
|
|
416
416
|
* The RUNNER owns the push, the pull request, the merge with the base,
|
|
417
|
-
the wait for CI, and the
|
|
417
|
+
the wait for CI, and the release fragment creation. After the last
|
|
418
418
|
task it runs a wrap-up session that promotes findings, compacts
|
|
419
419
|
`progress.txt`, merges `origin/main`, commits, pushes and opens (or
|
|
420
420
|
updates) the PR — and then polls that PR's checks, spending repair sessions
|
|
421
421
|
on a red or conflicting result. So a plan must NOT carry a task that opens
|
|
422
422
|
a PR, resolves a merge conflict, waits on CI, compacts `progress.txt`,
|
|
423
|
-
or writes a
|
|
423
|
+
or writes a release fragment. Two openers race: measured,
|
|
424
424
|
one run cut a second branch and opened a second PR for a single plan. A
|
|
425
425
|
close-out task SHOULD still take the mergeability reading (`git merge-tree
|
|
426
426
|
--write-tree origin/main HEAD`) and assemble the body material — the gate
|