fapony 0.1.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/LICENSE +21 -0
- package/README.md +473 -0
- package/fapony.ts +78 -0
- package/package.json +42 -0
- package/skill/git-commit-conventional/SKILL.md +68 -0
- package/skill/git-ship/SKILL.md +144 -0
- package/skill/move-to-done/SKILL.md +126 -0
- package/skill/plan-with-pony/SKILL.md +263 -0
- package/skill/review-pony/SKILL.md +254 -0
- package/src/analyze.ts +517 -0
- package/src/context/index.ts +11 -0
- package/src/context/projectHealth.ts +359 -0
- package/src/conventions-seed.ts +420 -0
- package/src/db/defaults.ts +26 -0
- package/src/db/getters.ts +33 -0
- package/src/db/index.ts +7 -0
- package/src/db/load.ts +57 -0
- package/src/db/store.ts +286 -0
- package/src/db/types.ts +79 -0
- package/src/debt.ts +667 -0
- package/src/digest/cli.ts +75 -0
- package/src/digest/collect.ts +625 -0
- package/src/digest/html.ts +208 -0
- package/src/digest/text.ts +191 -0
- package/src/gate.ts +153 -0
- package/src/gates.ts +194 -0
- package/src/hook.ts +436 -0
- package/src/init-mem.ts +71 -0
- package/src/init.ts +237 -0
- package/src/install/claude.ts +361 -0
- package/src/install/codex.ts +61 -0
- package/src/install/cursor.ts +167 -0
- package/src/install/detect.ts +78 -0
- package/src/install/opencode.ts +234 -0
- package/src/install/skills.ts +106 -0
- package/src/install/types.ts +69 -0
- package/src/install/utils.ts +29 -0
- package/src/install/zcode.ts +120 -0
- package/src/install.ts +176 -0
- package/src/lint-baseline.ts +260 -0
- package/src/map.ts +320 -0
- package/src/math.ts +13 -0
- package/src/mcp/evidence.ts +332 -0
- package/src/mcp/primitives.ts +316 -0
- package/src/mcp/tools/check.ts +243 -0
- package/src/mcp/tools/collect.ts +157 -0
- package/src/mcp/tools/context.ts +66 -0
- package/src/mcp/tools/index.ts +309 -0
- package/src/mcp/tools/mem.ts +95 -0
- package/src/mcp/tools/plans.ts +255 -0
- package/src/mcp/tools/report.ts +285 -0
- package/src/mcp/tools/stats.ts +96 -0
- package/src/mcp/tools/usage.ts +211 -0
- package/src/mcp/tools/verdict.ts +148 -0
- package/src/mcp/transport.ts +241 -0
- package/src/mcp/types.ts +54 -0
- package/src/mcp/worktree.ts +27 -0
- package/src/memory.ts +264 -0
- package/src/parse.ts +71 -0
- package/src/plan-seed.ts +599 -0
- package/src/price/fetch.ts +146 -0
- package/src/price/index.ts +8 -0
- package/src/price/resolve.ts +213 -0
- package/src/report/cli.ts +92 -0
- package/src/report/format.ts +37 -0
- package/src/report/index.ts +4 -0
- package/src/report/render.ts +206 -0
- package/src/review-seed.ts +932 -0
- package/src/safety.ts +18 -0
- package/src/session/activeSession.ts +153 -0
- package/src/session/claude-code.ts +412 -0
- package/src/session/codex.ts +347 -0
- package/src/session/findModel.ts +376 -0
- package/src/session/helpers.ts +640 -0
- package/src/session/index.ts +31 -0
- package/src/session/opencode.ts +167 -0
- package/src/session/registry.ts +45 -0
- package/src/session/types.ts +128 -0
- package/src/session/zcode.ts +151 -0
- package/src/setup.ts +242 -0
- package/src/stats/cli.ts +44 -0
- package/src/stats/data.ts +1019 -0
- package/src/stats/format.ts +584 -0
- package/src/stats/index.ts +19 -0
- package/src/telemetry.ts +364 -0
- package/src/test.ts +2 -0
- package/src/update.ts +212 -0
- package/src/usage/cache.ts +125 -0
- package/src/usage/cli.ts +120 -0
- package/src/usage/format.ts +29 -0
- package/src/usage/index.ts +4 -0
- package/src/usage/render.ts +523 -0
- package/src/usage/scan.ts +161 -0
- package/src/util.ts +32 -0
- package/src/web/html.ts +33 -0
- package/templates/PLAN.md +90 -0
- package/templates/SPEC.md +30 -0
- package/templates/mem/commands/plan.ts +360 -0
- package/templates/mem/commands/read.ts +194 -0
- package/templates/mem/commands/rotate.ts +59 -0
- package/templates/mem/commands/selftest.ts +450 -0
- package/templates/mem/commands/write.ts +214 -0
- package/templates/mem/mem.ts +68 -0
- package/templates/mem/render.ts +63 -0
- package/templates/mem/selectors.ts +144 -0
- package/templates/mem/store.ts +285 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-pony
|
|
3
|
+
description: Review a plan, PR, diff, or design doc as a verification rather than an opinion — scope first, walk the real path, break it on paper, cite everything. Takes optional effort (low|medium|high|max, widens the walk only, never skips a pass) and --fix (apply CONFIRMED blocker/major findings after the report). Records the verdict to fapony after the report. Trigger on /review-pony and proactively whenever the user asks to review, audit, scrutinize, sanity-check, or get a second opinion on a plan, PR, diff, design doc, or proposed code change.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Review Pony
|
|
7
|
+
|
|
8
|
+
**A review is a verification, not an opinion.** Anything you cannot trace, run, or cite is
|
|
9
|
+
something you feel about the code — and feelings are what make reviews long and useless.
|
|
10
|
+
|
|
11
|
+
Four passes. Run them in order. Each one is allowed to end the review early.
|
|
12
|
+
|
|
13
|
+
**Args:** `effort` (`low|medium|high|max`, default `medium`) — widens or narrows pass 2's walk
|
|
14
|
+
only. Passes 1, 3, and 4 run in full at every level; effort never skips verification, it only
|
|
15
|
+
changes how far you walk before writing findings down. There is no `ultra` here — that's
|
|
16
|
+
multi-agent cloud review; point the user at `/code-review ultra` instead.
|
|
17
|
+
`--fix` — after the report, apply every `blocker`/`major` `CONFIRMED` fix (never nits, never
|
|
18
|
+
`PLAUSIBLE`) — details in the `--fix` section below. `--comment` is not supported — PR-posting
|
|
19
|
+
is another tool's job.
|
|
20
|
+
|
|
21
|
+
## The four passes
|
|
22
|
+
|
|
23
|
+
1. **Scope is a finding.** Does this need to exist, and does it need to be this big?
|
|
24
|
+
2. **Claims are not facts.** Walk the real path. Run what can be run.
|
|
25
|
+
3. **A finding needs a failing input.** Cannot write one? Not a finding.
|
|
26
|
+
4. **Facts carry a citation.** `file:line`, output, or trace step — or it doesn't ship.
|
|
27
|
+
|
|
28
|
+
Carry them. Do not post them, and do not narrate them — the reader wants what you found, not
|
|
29
|
+
proof that you looked. Start at pass 1.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Before: scope facts (fapony, optional)
|
|
34
|
+
|
|
35
|
+
Run `fapony review-seed` with the scope flag matching what you're reviewing (default = uncommitted,
|
|
36
|
+
`--commit <sha>`, `--range <a...b>`, `--files f1,f2,dir`, `--plan <PLAN.md>`). The output is where to
|
|
37
|
+
enter, never coverage — walk it, run it, kill your findings as normal. No fapony CLI or the call
|
|
38
|
+
errors → skip silently and review anyway — a hint, not a gate. Mid-walk, `--files <f> --body <sym>
|
|
39
|
+
--callers <sym>` answers "what does this do / who calls it" without reading the file.
|
|
40
|
+
|
|
41
|
+
**`--plan` on an already-shipped plan comes back "nothing in this scope" — that's the wrong scope,
|
|
42
|
+
not no scope.** A shipped plan has nothing left in the working tree to diff. If its header cites
|
|
43
|
+
commit shas (`> Status: shipped ... Commits: <sha1> ... <shaN>`), re-run against those — one
|
|
44
|
+
`--range <first-sha>^...<last-sha>` covering them, or `--commit <sha>` per commit — and walk that
|
|
45
|
+
instead. Only treat the plan as prose-only, no code to walk, when its header cites no commits.
|
|
46
|
+
|
|
47
|
+
## Pass 1 — Scope is a finding
|
|
48
|
+
|
|
49
|
+
Say what the change is for in one sentence, in your own words. If you can't, the artifact is
|
|
50
|
+
underspecified. That is the review. Report it and stop.
|
|
51
|
+
|
|
52
|
+
Then take one pass down the ladder, and stop at the first rung that reaches the same goal:
|
|
53
|
+
|
|
54
|
+
1. **Nothing.** Is the problem load-bearing, or is this a fix for a hypothetical?
|
|
55
|
+
2. **What's already here.** A function, a flag, a stdlib call the author didn't know about.
|
|
56
|
+
3. **A native mechanism.** A DB constraint over app logic, config over code, build over runtime.
|
|
57
|
+
4. **90% for 10%.** The narrow change that solves the real case and drops the exotic ones.
|
|
58
|
+
|
|
59
|
+
If a rung holds, name it **before** any line-by-line notes. A scope finding is worth more than
|
|
60
|
+
every other finding combined, and it is worth nothing once the author has already rewritten the
|
|
61
|
+
code to answer your nits.
|
|
62
|
+
|
|
63
|
+
Mandatory even on small changes. Skip only if the user says "don't question scope".
|
|
64
|
+
|
|
65
|
+
## Pass 2 — Claims are not facts
|
|
66
|
+
|
|
67
|
+
The diff is where you enter, not what you review.
|
|
68
|
+
|
|
69
|
+
- **Walk the path end-to-end**: entry point → call sites → branches taken → state mutated → exit
|
|
70
|
+
or side effect. Read the *unchanged* code on both sides. A diff is correct in isolation far
|
|
71
|
+
more often than it is correct in place.
|
|
72
|
+
- **Read who calls this.** A signature change is fine until the third caller passes the old shape.
|
|
73
|
+
- **Run what can be run.** Reading cannot see a command that exits 0 without doing anything, a
|
|
74
|
+
timeout budget no real suite fits, or a server still serving last week's build. A green result
|
|
75
|
+
you produced outranks a green result you inferred, every time.
|
|
76
|
+
- **For a plan or design doc**, walk the proposed flow against the system that exists — where
|
|
77
|
+
does it touch reality, and what does it assume that isn't true today?
|
|
78
|
+
|
|
79
|
+
Write down every place the walk surprises you. Surprises outrank style; chase them first.
|
|
80
|
+
|
|
81
|
+
**Effort controls how far this walk goes, nothing else:**
|
|
82
|
+
|
|
83
|
+
- `low` — direct callers only, one hop. No test reading unless the diff touches a test file.
|
|
84
|
+
- `medium` (default) — as written above: full path, callers, tests on the path.
|
|
85
|
+
- `high` — also second-degree callers, and read the tests that exercise them, not just the path.
|
|
86
|
+
- `max` — also run `get_impact_radius_tool` (or grep if the graph isn't wired) on every changed
|
|
87
|
+
file and re-open every `deferred` line from the last review of this scope, if fapony has one.
|
|
88
|
+
|
|
89
|
+
Whatever level stopped you, say so in the one-line coverage note (rule below) — "walked to 1 hop"
|
|
90
|
+
is honest, "walked" alone at `low` is not.
|
|
91
|
+
|
|
92
|
+
## Pass 3 — A finding needs a failing input
|
|
93
|
+
|
|
94
|
+
Before a finding reaches the report, try to kill it yourself.
|
|
95
|
+
|
|
96
|
+
- **Write the failure scenario**: concrete inputs or state → the wrong output, crash, or
|
|
97
|
+
corruption. Can't write that sentence? You have a preference. Drop it.
|
|
98
|
+
- **Look again for the guard you missed.** This is where most findings deserve to die: the
|
|
99
|
+
validation lives in the caller, the branch is unreachable, the type already excludes it.
|
|
100
|
+
- **Label what survives.** `CONFIRMED` — you traced or ran it. `PLAUSIBLE` — the mechanism is
|
|
101
|
+
real but you could not reach the failing state. Never let the second wear the first's clothes.
|
|
102
|
+
- **Don't flag by pattern.** "Should use dependency injection" is taste. "Calls `fetch` in a loop
|
|
103
|
+
whose length comes from user input" is a finding.
|
|
104
|
+
|
|
105
|
+
## Pass 4 — Facts carry a citation
|
|
106
|
+
|
|
107
|
+
Every claim points at a `file:line`, a command's output, or the step in the walk that exposed it.
|
|
108
|
+
No citation, no report line.
|
|
109
|
+
|
|
110
|
+
Keep the claim and the verification in separate sentences. "The PR says it retries twice" and
|
|
111
|
+
"I traced it to `client.ts:88` and the retry is unreachable" are different statements; merging
|
|
112
|
+
them is how a review launders an assumption into a fact.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Report
|
|
117
|
+
|
|
118
|
+
The reader has the diff and is deciding what to do next. Nothing else belongs here.
|
|
119
|
+
|
|
120
|
+
**Verdict first, then at most 3 findings, at most 4 lines each, then one deferred line.**
|
|
121
|
+
Severity order: blocker → major → nit, and cut the nits entirely when anything structural
|
|
122
|
+
survived — they dilute the only thing worth reading.
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
<ship | fix-then-ship | rework | reject> — the single biggest reason, one sentence.
|
|
126
|
+
|
|
127
|
+
1. <blocker|major|nit> <CONFIRMED|PLAUSIBLE> — what breaks, one line
|
|
128
|
+
<file:line> — the mechanism, one line
|
|
129
|
+
repro: <input or state> ⇒ <wrong result vs. right one>
|
|
130
|
+
fix: <the minimal change>
|
|
131
|
+
|
|
132
|
+
deferred: <thing> (<where it was specified>) · <thing>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Four lines is a ceiling, not a quota — a finding that fits in two ships in two. Drop `repro:`
|
|
136
|
+
only when the finding is the absence of something (no test, no guard); never drop the citation.
|
|
137
|
+
|
|
138
|
+
**Cut on sight:** the four passes as headings or prose · what you walked, ran, or ruled out ·
|
|
139
|
+
anything restating the diff, the plan, or the author's reasoning · a nit riding along under a
|
|
140
|
+
blocker · hedging that does not change the verdict.
|
|
141
|
+
|
|
142
|
+
Finding nothing is a valid result. Then the whole report is the verdict line plus one line
|
|
143
|
+
naming what you walked, so the reader can judge the coverage — not a tour of it.
|
|
144
|
+
|
|
145
|
+
## --fix (optional)
|
|
146
|
+
|
|
147
|
+
Only with the `--fix` arg, only after the report is shown. For each `blocker`/`major` finding
|
|
148
|
+
labeled `CONFIRMED`: apply the `fix:` line to the working tree. Skip every nit and every
|
|
149
|
+
`PLAUSIBLE` — a fix you weren't sure was a bug is a bug you're introducing on purpose.
|
|
150
|
+
|
|
151
|
+
Report what happened in one line per finding, no more:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
fixed: 1, 2 · skipped: 3 (PLAUSIBLE — could not reach the failing state)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Fixing changes what actually shipped, not what the review found — re-run pass 4's citation
|
|
158
|
+
check on the new state before calling it done, but don't re-run the whole review. Submit the
|
|
159
|
+
verdict on what you found, not on the post-fix state (`verdict_submit`'s `note` can say the fix
|
|
160
|
+
was applied).
|
|
161
|
+
|
|
162
|
+
## After: record the verdict (fapony)
|
|
163
|
+
|
|
164
|
+
Call `verdict_submit` once, after the report is shown. Don't block the report on it, and don't
|
|
165
|
+
let it change the report's content. Pass `regime="review"` — required, and a review is what this
|
|
166
|
+
was; it is what puts this run in the `regime × model` table.
|
|
167
|
+
|
|
168
|
+
| Report verdict | `verdict` |
|
|
169
|
+
|---|---|
|
|
170
|
+
| ship, 0 findings | `pass-excellent` |
|
|
171
|
+
| ship, nit-only findings | `pass-good` |
|
|
172
|
+
| fix-then-ship | `pass-adequate` |
|
|
173
|
+
| rework / reject | `fail` |
|
|
174
|
+
| could not walk enough to have a verdict | `uncertain` |
|
|
175
|
+
|
|
176
|
+
`uncertain` is not a softer `fail`. It is the honest answer when the walk never
|
|
177
|
+
reached the thing under review — the branch wouldn't build, the path is behind a
|
|
178
|
+
service you cannot run, every finding came out `PLAUSIBLE`. Say so in the report
|
|
179
|
+
too. Guessing `pass` there is the one outcome that makes the ledger lie.
|
|
180
|
+
|
|
181
|
+
`reason_code` — the *lead* (most severe) finding, not a generic bucket:
|
|
182
|
+
|
|
183
|
+
- **0 findings, or a clean pass → `none`** — never `other`. `other` means "a real
|
|
184
|
+
finding that none of these buckets name", so filing clean passes there puts them
|
|
185
|
+
in the recurring-fail-reasons list, where they crowd out the reasons that mean
|
|
186
|
+
something. It is the one value in this table that costs other people accuracy.
|
|
187
|
+
- missing or weak test coverage on the path you walked → `missing_test`
|
|
188
|
+
- change is narrower or wider than the plan / PR description claims → `scope_mismatch`
|
|
189
|
+
- a shell/eval/deploy command runs without the guard it needs → `unsafe_command`
|
|
190
|
+
- the plan or spec didn't cover a case the walk exposed → `spec_gap`
|
|
191
|
+
- the change stops short of what it set out to do → `incomplete`
|
|
192
|
+
- a real finding none of the above names → `other`, and then `note` is **required**
|
|
193
|
+
|
|
194
|
+
Always attach a one-line `note` — the only field a later review can act on. Say what broke or
|
|
195
|
+
was walked, not that a review happened.
|
|
196
|
+
|
|
197
|
+
Args: `verdict`, `reason_code`, `note`, `regime`, `worktree` — **absolute path** via
|
|
198
|
+
`git rev-parse --show-toplevel`, never a bare name (`runs.worktree` is free text; a bare name
|
|
199
|
+
writes where no query reads it and every fapony tool misses the run), `plan` (the PLAN file
|
|
200
|
+
path under review, omitted for a bare PR/diff), and `files` — the repo-relative paths you
|
|
201
|
+
actually walked. **Always send `files`.** It is the only input to per-file risk history; a
|
|
202
|
+
verdict without it tells the next session that something failed but not where. No `run_id` —
|
|
203
|
+
fapony reuses the latest still-open run for the same worktree+plan (so round 2+ counts toward
|
|
204
|
+
the round cap), creating a row only when none is open. `session_id` (optional) — the client
|
|
205
|
+
session id, only if the client exposes it; attribute the model, never block the submit on it.
|
|
206
|
+
If `verdict_submit` errors, say so in one line and move on — never re-run a review because
|
|
207
|
+
storage failed.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Rules
|
|
212
|
+
|
|
213
|
+
The four passes are the rules. These three are what they fail on in practice:
|
|
214
|
+
|
|
215
|
+
- **Order is not optional.** No line-by-line notes before pass 1, no finding before passes 2-3
|
|
216
|
+
earned it, nothing stated as fact that pass 4 cannot cite.
|
|
217
|
+
- **The budget is binding.** Verdict, ≤3 findings, ≤4 lines each, one deferred line. Over budget
|
|
218
|
+
means you are reporting process. "LGTM" is not an output either — finding nothing ships as the
|
|
219
|
+
verdict line plus one line naming what you walked.
|
|
220
|
+
- **Forget who wrote it.** The author's reasoning is context, never evidence.
|
|
221
|
+
|
|
222
|
+
## Example
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
1-4. scope holds; walked the new gate branch; ran the evidence command — it exits 0
|
|
226
|
+
without running the suite (CONFIRMED: `bun test` with no test dir exits 0)
|
|
227
|
+
post. verdict_submit(verdict="pass-adequate", reason_code="other", regime="review",
|
|
228
|
+
note="evidence entry `bun test` exits 0 while running zero tests — real entry is `bun run test`",
|
|
229
|
+
worktree="/Users/you/Project/fapony/wt-fapony",
|
|
230
|
+
plan=".fapony/plan/PLAN-verdict-notes.md")
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
A report in budget — same review that, narrated, ran five paragraphs:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
rework — Finding 1 corrupts the ledger this feature exists to keep.
|
|
237
|
+
|
|
238
|
+
1. blocker CONFIRMED — incremental scan replaces cached history with a delta
|
|
239
|
+
cache.ts:81 overwrites by client; claude-code.ts:191 returns only new lines
|
|
240
|
+
repro: line(1000 tok) → scan → +line(500) → scan ⇒ cache 500, truth 1500
|
|
241
|
+
fix: merge per session_id (plan §5), or drop incremental and always full-scan
|
|
242
|
+
|
|
243
|
+
2. major CONFIRMED — usage-scan crashes on a machine with no state dir (day 1)
|
|
244
|
+
cache.ts:68 writes the tmp file; writeCache never mkdirs, store.ts does
|
|
245
|
+
repro: FAPONY_STATE_DIR=/tmp/nonexistent bun fapony.ts usage-scan ⇒ ENOENT
|
|
246
|
+
fix: mkdirSync(faponyDir(config), { recursive: true }) before the write
|
|
247
|
+
|
|
248
|
+
3. major CONFIRMED — SQLite `since` compares seconds to ms, so it never filters
|
|
249
|
+
helpers.ts:46 — max(time_created)=1789144099203 vs now_s=1789145895
|
|
250
|
+
repro: opencode all-time 1494 sessions == cached 1494; the filter cannot bite
|
|
251
|
+
fix: pass since*1000 — but only after 1, or these two clients corrupt too
|
|
252
|
+
|
|
253
|
+
deferred: bytes_by_tool (plan step 7) · per-client watermark (plan §6.3)
|
|
254
|
+
```
|