fapony 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -156
- package/package.json +5 -6
- package/skill/define-convention/SKILL.md +3 -3
- package/skill/lookup-before-edit/SKILL.md +2 -1
- package/skill/move-to-done/SKILL.md +18 -22
- package/skill/plan-with-pony/SKILL.md +4 -4
- package/skill/review-pony/SKILL.md +10 -12
- package/src/adapters/cli.ts +11 -52
- package/src/adapters/hooks/index.ts +0 -67
- package/src/adapters/hooks/mv-guard.ts +2 -2
- package/src/analyze/discover.ts +1 -1
- package/src/analyze/index.ts +0 -1
- package/src/commands.ts +8 -39
- package/src/core/config.ts +0 -8
- package/src/core/fapony-dir.ts +71 -0
- package/src/core/hook-helpers.ts +2 -11
- package/src/debt/load.ts +3 -5
- package/src/debt/promotion.ts +2 -2
- package/src/debt/scan.ts +1 -1
- package/src/digest/collect.ts +6 -7
- package/src/fael.ts +139 -0
- package/src/hook.ts +1 -81
- package/src/init.ts +30 -90
- package/src/install/antigravity.ts +8 -99
- package/src/install/claude.ts +66 -200
- package/src/install/codex.ts +6 -170
- package/src/install/detect.ts +4 -14
- package/src/install/opencode.ts +50 -486
- package/src/install/types.ts +1 -39
- package/src/install/zcode.ts +5 -91
- package/src/install.ts +9 -35
- package/src/memory.ts +10 -69
- package/src/plan/index.ts +34 -0
- package/src/plan/next.ts +181 -0
- package/src/plan/store.ts +41 -0
- package/src/{mem/commands/plan.ts → plan/sweep.ts} +66 -46
- package/src/seed/plan-seed.ts +10 -9
- package/src/setup.ts +4 -22
- package/src/update.ts +1 -1
- package/templates/PLAN.md +1 -1
- package/src/adapters/hooks/bug-markers.ts +0 -60
- package/src/adapters/hooks/context-data.ts +0 -211
- package/src/adapters/hooks/edit-hint.ts +0 -215
- package/src/adapters/hooks/read-hint.ts +0 -400
- package/src/adapters/hooks/session-start.ts +0 -112
- package/src/adapters/hooks/stop.ts +0 -768
- package/src/adapters/mcp/tools/index.ts +0 -149
- package/src/adapters/mcp/tools/mem.ts +0 -258
- package/src/adapters/mcp/transport.ts +0 -147
- package/src/analyze/cache.ts +0 -162
- package/src/core/mem-log.ts +0 -394
- package/src/init-mem.ts +0 -143
- package/src/install/cursor.ts +0 -167
- package/src/install/utils.ts +0 -29
- package/src/mem/commands/read.ts +0 -792
- package/src/mem/commands/rotate.ts +0 -59
- package/src/mem/commands/where.ts +0 -56
- package/src/mem/commands/write.ts +0 -284
- package/src/mem/engine.ts +0 -329
- package/src/mem/index.ts +0 -201
- package/src/mem/key-registry.ts +0 -116
- package/src/mem/render.ts +0 -66
- package/src/mem/selectors.ts +0 -227
- package/src/mem/store.ts +0 -356
package/README.md
CHANGED
|
@@ -4,12 +4,85 @@
|
|
|
4
4
|
|
|
5
5
|
# fapony
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/fapony) [](https://www.npmjs.com/package/fapony) [](https://github.com/inonix-dev/fapony)
|
|
8
8
|
|
|
9
|
-
**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
**The dev workflow for writing code with agents** — plans cut into one-session chunks, lookups
|
|
10
|
+
that cost a fraction of reading the files, convention debt you can count, and what it all cost in
|
|
11
|
+
tokens. It is one developer's daily flow made into commands; adopting fapony means adopting that
|
|
12
|
+
flow. Memory — decisions, bugs, notes — is [fael](https://github.com/inonix-dev/fael)'s, never
|
|
13
|
+
fapony's.
|
|
14
|
+
|
|
15
|
+
Why chunks: a long plan run in one unbroken session only accumulates context. Every chunk here is
|
|
16
|
+
its own session that opens with just the facts it needs and stops when the chunk lands.
|
|
17
|
+
|
|
18
|
+
## The workflow — fapony + fael
|
|
19
|
+
|
|
20
|
+
Two tools, one loop, no overlap. **fapony is the workflow** — plans cut into chunks, convention
|
|
21
|
+
debt, cheap lookups, what it all cost. **[fael](https://github.com/inonix-dev/fael) is the memory** — the
|
|
22
|
+
decisions, bugs and notes the next session must see. Each is useful alone; together they close
|
|
23
|
+
the loop: fapony says *what's next*, fael says *what the last session learned*.
|
|
24
|
+
|
|
25
|
+
| | fapony — workflow | fael — memory |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| Owns | plans + chunk loop, `debt`, `lint-baseline`, `review-seed` / `analyze`, usage | decisions, issues, notes (`add` / `find` / `close`) |
|
|
28
|
+
| Agent surface | plan-mv guard hook, skills — **no MCP server** | MCP tools + SessionStart / read / Stop hooks |
|
|
29
|
+
| Writes | plan files, only when told (`plan sweep --apply`) | its log under `.fael/` in your repo |
|
|
30
|
+
| Install | `npm i -g fapony && fapony install` | `npm i -g @inonix/fael && fael install` |
|
|
31
|
+
|
|
32
|
+
One chunk, one session:
|
|
33
|
+
|
|
34
|
+
```mermaid
|
|
35
|
+
flowchart TD
|
|
36
|
+
S([new session]) --> K["fael kickoff — SessionStart hook<br/>open decisions + issues"]
|
|
37
|
+
K --> P["fapony plan PLAN-x.md<br/>unchecked chunks + handoff notes from fael"]
|
|
38
|
+
P --> L["fapony review-seed --files …<br/>exports + importers instead of whole-file reads"]
|
|
39
|
+
L --> E["edit<br/>fael read hook: rows about that file"]
|
|
40
|
+
E --> C["tick the chunk with its sha → commit"]
|
|
41
|
+
C --> N["fael add note 'what chunk N+1 must know'<br/>--files f1,f2,PLAN-x.md"]
|
|
42
|
+
N --> X([stop — don't drag the transcript along])
|
|
43
|
+
X -->|next chunk| S
|
|
44
|
+
C -->|last chunk| W["fapony plan sweep PLAN-x.md --apply<br/>git mv into .fapony/done/"]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Who reads what:
|
|
48
|
+
|
|
49
|
+
```mermaid
|
|
50
|
+
flowchart LR
|
|
51
|
+
CC[Claude Code] --> F[fapony]
|
|
52
|
+
OC[OpenCode] --> F
|
|
53
|
+
ZC[ZCode] --> F
|
|
54
|
+
CX[Codex] --> F
|
|
55
|
+
AG[Antigravity] --> F
|
|
56
|
+
F --> U[usage — tokens & cost]
|
|
57
|
+
F --> P[plans — next chunk, sweep, check]
|
|
58
|
+
F --> D[debt — how far the move has gone]
|
|
59
|
+
M[(fael — memory)] -. read-only .-> F
|
|
60
|
+
CC & OC & CX --> M
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
fapony is opinionated: the loop above is the product, and the commands exist to make each step
|
|
64
|
+
cheap. Plans and debt are per-project (`fapony init`); usage needs no setup at all.
|
|
65
|
+
|
|
66
|
+
## The pieces
|
|
67
|
+
|
|
68
|
+
**Plans, one chunk at a time.** `fapony plan` shows every active plan, its progress and next
|
|
69
|
+
unchecked chunk; `fapony plan PLAN-x.md` opens one chunk with just the facts it needs — the
|
|
70
|
+
unchecked boxes, whether the last ticked chunk's commit really exists, and the notes the previous
|
|
71
|
+
session left in fael — instead of dragging the old transcript along.
|
|
72
|
+
|
|
73
|
+
**Lookups instead of whole-file reads.** `fapony review-seed --files <f>` gives exports with line
|
|
74
|
+
numbers and every importer for roughly a thirtieth of the tokens reading those files costs.
|
|
75
|
+
|
|
76
|
+
**Convention debt.** `fapony debt` answers the question nothing else does: *we decided this six
|
|
77
|
+
months ago — how far along is the move?* ESLint says this line is wrong; nothing says 11 of 47
|
|
78
|
+
files have migrated. Dead code and duplication it deliberately leaves to knip and friends —
|
|
79
|
+
they already do that better.
|
|
80
|
+
|
|
81
|
+
## What it cost — usage
|
|
82
|
+
|
|
83
|
+
fapony reads the session logs Claude Code, Codex, OpenCode and ZCode already write, and puts them
|
|
84
|
+
all on one yardstick — tokens, cost and time per model, per client, per workflow. Nothing to
|
|
85
|
+
instrument: it runs on the history already sitting on your disk.
|
|
13
86
|
|
|
14
87
|
<p align="center">
|
|
15
88
|
<img src="images/summary.webp" width="800" alt="fapony usage-web summary cards">
|
|
@@ -40,47 +113,13 @@ Raw facts from logs are hard to argue with — a vendor can dispute a verdict as
|
|
|
40
113
|
dispute their own token count. That is the whole measurement layer: tokens and cost, nothing
|
|
41
114
|
self-graded.
|
|
42
115
|
|
|
43
|
-
## Past day one
|
|
44
|
-
|
|
45
|
-
Two more layers, both optional, both compounding:
|
|
46
|
-
|
|
47
|
-
**Memory — the mem log.** An agent has no memory of pain across sessions: it writes the 37th
|
|
48
|
-
hand-rolled `try/catch` as cheerfully as the first, because every session starts new. Wrappers
|
|
49
|
-
and shared libraries get built by *people* who were hurt often enough to remember. fapony
|
|
50
|
-
remembers instead: one MCP call per unit of work (`mem_add`) records the decision, bug or note
|
|
51
|
-
with the files it touched, and `mem_find` answers *"what was ever decided about this file?"*
|
|
52
|
-
before the next agent touches it. The log lives in your repo (`.fapony/.memory/`), so it crosses
|
|
53
|
-
machines over git for free.
|
|
54
|
-
|
|
55
|
-
**Convention debt.** `fapony debt` answers the question nothing else does: *we decided this six
|
|
56
|
-
months ago — how far along is the move?* ESLint says this line is wrong; nothing says 11 of 47
|
|
57
|
-
files have migrated. Dead code and duplication it deliberately leaves to knip and friends —
|
|
58
|
-
they already do that better.
|
|
59
|
-
|
|
60
|
-
```mermaid
|
|
61
|
-
flowchart LR
|
|
62
|
-
A[Claude Code] --> F[fapony]
|
|
63
|
-
B[OpenCode] --> F
|
|
64
|
-
C[ZCode] --> F
|
|
65
|
-
D[Codex] --> F
|
|
66
|
-
E[Cursor] --> F
|
|
67
|
-
G[Antigravity] --> F
|
|
68
|
-
F --> U[usage — tokens & cost]
|
|
69
|
-
F --> M[mem log — what was decided here]
|
|
70
|
-
F --> D[debt — how far the move has gone]
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Adopting it doesn't change your workflow: install it, point your agent at it, read the reports.
|
|
74
|
-
Both layers above are per-project (`fapony init`) and worthless on run 1 — they get more useful
|
|
75
|
-
every run after, which is exactly why they're retention, not the reason to install.
|
|
76
|
-
|
|
77
116
|
## Quick start
|
|
78
117
|
|
|
79
118
|
```bash
|
|
80
119
|
# 1. Install (needs Bun — https://bun.sh)
|
|
81
120
|
npm install -g fapony
|
|
82
121
|
# from source instead:
|
|
83
|
-
# git clone https://github.com/
|
|
122
|
+
# git clone https://github.com/inonix-dev/fapony.git && cd fapony && bun install && bun link
|
|
84
123
|
# (`bun link` claims the global `fapony` bin by package name, not path — re-run it in the
|
|
85
124
|
# checkout you want to be the one)
|
|
86
125
|
|
|
@@ -96,17 +135,16 @@ fapony install --all # skip the prompt, wire everything detected
|
|
|
96
135
|
# claude/opencode also symlink skill/<name>/ into ~/.claude/skills — an existing
|
|
97
136
|
# skill of the same name is reported, never overwritten
|
|
98
137
|
|
|
99
|
-
# 4. Turn on
|
|
138
|
+
# 4. Turn on plans + debt (per project you want them in)
|
|
100
139
|
fapony init /path/to/your-worktree
|
|
101
|
-
# creates .fapony/ —
|
|
140
|
+
# creates .fapony/ — plan/ done/ spec/, evidence.json for `fapony report`,
|
|
102
141
|
# and conventions.json for `fapony debt` (shared rules: commit them),
|
|
103
|
-
# then offers to write the
|
|
104
|
-
# (none yet = AGENTS.md + a CLAUDE.md that imports it)
|
|
105
|
-
# what the rules they already read tell them to
|
|
142
|
+
# then offers to write the plan-loop rules into CLAUDE.md / AGENTS.md
|
|
143
|
+
# (none yet = AGENTS.md + a CLAUDE.md that imports it)
|
|
106
144
|
fapony init /path/to/your-worktree --rules --yes # repo already set up: rules only, no prompt
|
|
107
|
-
```
|
|
108
145
|
|
|
109
|
-
|
|
146
|
+
# 5. Memory: install fael (npm i -g @inonix/fael && fael install)
|
|
147
|
+
```
|
|
110
148
|
|
|
111
149
|
## What fapony is not
|
|
112
150
|
|
|
@@ -114,69 +152,36 @@ Stated up front, because the gap between these two things is where most tooling
|
|
|
114
152
|
|
|
115
153
|
- **It does not run your test suite.** The evidence collector runs an allowlist *you* write in
|
|
116
154
|
`.fapony/evidence.json`, never a command an agent proposes. No allowlist, no evidence.
|
|
117
|
-
- **It does not judge your code
|
|
118
|
-
fapony is the memory, not the judge.
|
|
155
|
+
- **It does not judge your code** — and it holds no memory of its own; that is fael's.
|
|
119
156
|
- **It checks conformance, not correctness** — that a claim lines up with git facts and that
|
|
120
157
|
uncertainty was declared, not that the code works.
|
|
121
|
-
- **Almost nothing blocks.** The one exception is the
|
|
122
|
-
|
|
158
|
+
- **Almost nothing blocks.** The one exception is the plan-mv guard on Claude Code, which denies a
|
|
159
|
+
raw `git mv` of a plan into done/ and points at `fapony plan sweep --apply`; nothing else touches a
|
|
160
|
+
tool call.
|
|
123
161
|
- **Model attribution is inferred, not declared** — reports label it `inferred`; read it as such.
|
|
124
|
-
- **The knowledge layer is empty on run 1** — worth something around run 5, more every run after.
|
|
125
162
|
|
|
126
163
|
## What runs where
|
|
127
164
|
|
|
128
|
-
`fapony install` wires
|
|
129
|
-
the
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
|
137
|
-
|
|
|
138
|
-
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
evolving, and `usage-scan` can't read its session log yet. The hints live on hooks rather than MCP
|
|
149
|
-
on purpose — they must fire mid-turn without the agent deciding to call anything.
|
|
150
|
-
|
|
151
|
-
## The ledger — one habit, 3 tools
|
|
152
|
-
|
|
153
|
-
One habit feeds it: record a mem row when a unit of work ends. Everything else on this page is
|
|
154
|
-
optional around that. The **Stop hook** is the only thing fapony *blocks* — once per turn, when a
|
|
155
|
-
commit lands with no new mem row. It never judges what deserves recording. The hints only
|
|
156
|
-
annotate: a big-file read points at `review-seed`, a repeat read of an unchanged file points at
|
|
157
|
-
grep, an edit names the file's importer count before you change its shape.
|
|
158
|
-
|
|
159
|
-
| Tool | Purpose |
|
|
160
|
-
|------|---------|
|
|
161
|
-
| `mem_find` | Search the project's mem log read-only — matched on the row's `files[]` (text substring for older rows), `text`, `kind` (no default filter), `since` |
|
|
162
|
-
| `mem_add` | Append a mem row (decision/bug/note/next/hold) with `files[]` required and rejected when empty — the write half of `mem_find` |
|
|
163
|
-
| `mem_close` | Close a mem row by id with a tombstone message — a separate tool because a close row carries no `files[]` |
|
|
164
|
-
|
|
165
|
-
**A tool earns its schema by being called mid-task without being asked.** Everything you invoke
|
|
166
|
-
deliberately is a CLI command instead: a tool schema is paid as input tokens in every session of
|
|
167
|
-
every client whether or not it is used, while a CLI command costs nothing until it runs. That is
|
|
168
|
-
why `stats`, `report`, `usage-web` and friends are CLI-only, and why four tools left the MCP
|
|
169
|
-
surface in 2026-09 — the 3 mem tools keep their schemas because nobody is going to type them at
|
|
170
|
-
the right moment.
|
|
171
|
-
|
|
172
|
-
## The work side — conveniences, not the contract
|
|
173
|
-
|
|
174
|
-
Read-only, deterministic, none of it writes anything. Skip this side entirely and fapony still
|
|
175
|
-
works. **Nothing here is a precondition for anything above.**
|
|
176
|
-
|
|
177
|
-
- `fapony review-seed --files src/thing/` — exports, importers, untested, for roughly a thirtieth
|
|
178
|
-
of the tokens reading those files costs. Before touching an unfamiliar file, fire this and Read
|
|
179
|
-
only the line ranges it points at. Directories work too.
|
|
165
|
+
`fapony install` wires five clients (Claude Code, OpenCode, ZCode, Codex, Antigravity) — skills
|
|
166
|
+
everywhere, plus the plan-mv guard on Claude Code. It also removes hooks fapony no longer ships (the
|
|
167
|
+
edit hint, cut 2026-09-26: measured over two windows, it never moved an agent to migrate a file).
|
|
168
|
+
Memory hooks and MCP tools are fael's (`fael install`); fapony has no MCP server.
|
|
169
|
+
|
|
170
|
+
| | Claude Code | OpenCode | ZCode | Codex | Antigravity |
|
|
171
|
+
|---|---|---|---|---|---|
|
|
172
|
+
| Plan-mv guard — deny raw `git mv` of a plan into done/ | ✅ | — | — | — | — |
|
|
173
|
+
| Skills symlinked into `~/.claude/skills` | ✅ | ✅ | — | — | — |
|
|
174
|
+
| Skills symlinked into `~/.agents/skills` | — | — | ✅ | ✅ | ✅ |
|
|
175
|
+
| `usage-scan` reads this client's session log | ✅ | ✅ | ✅ | ✅ | — |
|
|
176
|
+
|
|
177
|
+
`—` means not wired, not impossible.
|
|
178
|
+
|
|
179
|
+
## Lookups, digest and skills
|
|
180
|
+
|
|
181
|
+
Read-only and deterministic — none of it writes anything.
|
|
182
|
+
|
|
183
|
+
- `fapony review-seed --files src/thing/` — exports, importers, untested. Before touching an
|
|
184
|
+
unfamiliar file, fire this and Read only the line ranges it points at. Directories work too.
|
|
180
185
|
- `fapony digest` — decisions, open bugs, in-flight plans, cost, on one page, from what's already
|
|
181
186
|
on disk.
|
|
182
187
|
|
|
@@ -188,8 +193,8 @@ expects, so a client can symlink the directory rather than copy the file:
|
|
|
188
193
|
| Skill | Purpose | Trigger |
|
|
189
194
|
|-------|---------|---------|
|
|
190
195
|
| `skill/plan-with-pony/` | Draft plan + spec from "what's in your head" via conversation | `/plan-with-pony` |
|
|
191
|
-
| `skill/review-pony/` | Review as verification
|
|
192
|
-
| `skill/lookup-before-edit/` | Look up unfamiliar files (`review-seed --files` +
|
|
196
|
+
| `skill/review-pony/` | Review as verification: scope facts before (`review-seed`), a fael row after when findings survive | `/review-pony` |
|
|
197
|
+
| `skill/lookup-before-edit/` | Look up unfamiliar files (`review-seed --files` + fael + debt) before reading/editing them | `/lookup-before-edit` |
|
|
193
198
|
| `skill/define-convention/` | Turn a not-yet-migrated pattern into a tracked convention (interview + dry-run `debt`) | `/define-convention` |
|
|
194
199
|
| `skill/move-to-done/` | Archive a shipped PLAN into .fapony/done/ | `/move-to-done` |
|
|
195
200
|
| `skill/git-commit-conventional/` | Commit split by concern + conventional message | `/git-commit` |
|
|
@@ -197,63 +202,59 @@ expects, so a client can symlink the directory rather than copy the file:
|
|
|
197
202
|
|
|
198
203
|
`plan-with-pony` is vendor-neutral — the SKILL.md *is* the prompt, so pipe it to any agent:
|
|
199
204
|
`cat skill/plan-with-pony/SKILL.md | claude -p` (or `opencode run`, or anything that reads stdin).
|
|
200
|
-
Example plans it produced: [examples/](https://github.com/
|
|
205
|
+
Example plans it produced: [examples/](https://github.com/inonix-dev/fapony/tree/main/examples).
|
|
201
206
|
|
|
202
207
|
### Plans your agent can answer questions about
|
|
203
208
|
|
|
204
209
|
Plans stay markdown files in your repo — nothing moves into a database. Four optional frontmatter
|
|
205
210
|
keys (`kind` / `status` / `blocked_by` / `blocks`) make a folder of them queryable; plans with no
|
|
206
211
|
frontmatter still work, because the unchecked checkboxes are enough. Open the next session with
|
|
207
|
-
`fapony
|
|
208
|
-
|
|
212
|
+
`fapony plan` — it prints every active plan with its progress and first unchecked chunk without
|
|
213
|
+
reading a single 100KB plan body into context:
|
|
209
214
|
|
|
210
215
|
```
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
+
# .fapony/plan/ — 3 active plan(s)
|
|
217
|
+
|
|
218
|
+
- PLAN-calendar.md — 1/4 chunks · priority:high
|
|
219
|
+
next: chunk 2 — move overdue out
|
|
220
|
+
- PLAN-billing.md — 0/3 chunks · blocked_by: PLAN-calendar.md
|
|
221
|
+
next: chunk 1 — money type
|
|
216
222
|
```
|
|
217
223
|
|
|
224
|
+
`fapony plan PLAN-calendar.md` then shows that plan's unchecked chunks, whether the last ticked
|
|
225
|
+
chunk's sha is really in git, and the open fael notes about it — close a chunk with
|
|
226
|
+
`fael add note "<what chunk N+1 must know>" --files <f>,<PLAN path>` and the next session finds it.
|
|
227
|
+
|
|
218
228
|
**There is no `MASTER.md`** — every line above is derived from the plan files themselves, so it
|
|
219
|
-
cannot drift; a hand-kept master file always does. `fapony
|
|
229
|
+
cannot drift; a hand-kept master file always does. `fapony plan check` verifies ticked chunk
|
|
220
230
|
shas against git history (a ticked box with no sha to check is a claim, not a close) and flags
|
|
221
|
-
dangling `blocked_by` refs; `fapony
|
|
231
|
+
dangling `blocked_by` refs; `fapony plan sweep PLAN-x.md --apply` archives a shipped plan with `git mv`
|
|
222
232
|
into `.fapony/done/` — same name, same depth, so every relative link inside the file survives the
|
|
223
233
|
move. Specs live in `.fapony/spec/` and are never archived.
|
|
224
234
|
|
|
225
235
|
## CLI
|
|
226
236
|
|
|
227
237
|
```bash
|
|
228
|
-
# core:
|
|
229
|
-
fapony
|
|
230
|
-
fapony
|
|
231
|
-
fapony
|
|
232
|
-
fapony mem kickoff [<plan.md>] [--pick <n>] # open a session + a next-up list
|
|
233
|
-
fapony mem where # show the resolved mem dir and which step won
|
|
234
|
-
fapony mem done | stale | claim | release | synced | plan-sweep | plan-check | rotate
|
|
238
|
+
# core: plans + debt (memory — decisions, bugs, notes — lives in fael)
|
|
239
|
+
fapony plan [<PLAN.md>] # active plans + next chunk; one plan: unchecked chunks + open fael rows
|
|
240
|
+
fapony plan sweep [<PLAN.md>] [--apply] # archive shipped plans into done/ + rewrite links
|
|
241
|
+
fapony plan check [--quiet] # deps, broken links, ticked-chunk shas (exit 1 on issues)
|
|
235
242
|
fapony debt [--id a,b] [--where <path>] # which files haven't migrated to a declared convention (live, read-only)
|
|
236
243
|
fapony lint-baseline [--cmd ...] [--diff] # separate "already red" from "I made it red"
|
|
237
|
-
fapony init-mem # delete legacy .memory/ dirs + warn call sites still referencing them
|
|
238
244
|
fapony digest [--since 7d|YYYY-MM-DD] [--format text|html] [--json] [--out FILE] # single-page summary from what's on disk
|
|
239
245
|
|
|
240
|
-
# usage
|
|
246
|
+
# usage — what it cost
|
|
241
247
|
fapony usage-scan # scan session logs → cache (incremental, progress bar)
|
|
242
248
|
fapony price-scan # fetch model price table → prices.json (cache; query never fetches)
|
|
243
249
|
fapony usage-web [port] # usage comparison dashboard from cache
|
|
244
250
|
|
|
245
251
|
# lookup (read-only, never touches state)
|
|
246
252
|
fapony analyze [path] # live repo graph: hubs, orphans, cycles, changed-untested (TS/JS + Python .py/.pyi; stdlib→external, no sys.path)
|
|
247
|
-
fapony review-seed [--staged|--commit <sha>|--range <a...b>|--files f1,f2,dir|--plan <PLAN.md>] # scope facts for a review
|
|
253
|
+
fapony review-seed [--staged|--commit <sha>|--range <a...b>|--files f1,f2,dir|--plan <PLAN.md>] [--body sym[,sym]] [--callers sym[,sym]] # scope facts for a review
|
|
248
254
|
fapony plan-seed <name> [--spec] [--scope <path>[,<path>]]... # write PLAN (+SPEC): frontmatter, capped sections, prior-art list
|
|
249
255
|
|
|
250
|
-
# hooks
|
|
251
|
-
fapony mcp # MCP server (stdio JSON-RPC — 3 tools)
|
|
252
|
-
fapony hook-stop # Stop hook: block turns with commits but no mem row
|
|
253
|
-
fapony hook-read-hint # read/re-read annotations
|
|
254
|
-
fapony hook-edit-hint # importer count before editing shape
|
|
256
|
+
# hooks (wired by `fapony install`, not run by hand)
|
|
255
257
|
fapony hook-mv-guard # deny raw git mv of plan files into done/
|
|
256
|
-
fapony hook-session-start # SessionStart: kickoff into context
|
|
257
258
|
|
|
258
259
|
# frozen ledger (reads history only — the grading tool left the MCP surface in 2026-09)
|
|
259
260
|
fapony stats [--mode verdict [--regime code|fix|review|plan|inquiry|test]] # KPIs from old graded runs
|
|
@@ -261,8 +262,8 @@ fapony report <run-id> # verification report for a run
|
|
|
261
262
|
fapony report-web [file] # static HTML report page
|
|
262
263
|
|
|
263
264
|
# setup & maintenance
|
|
264
|
-
fapony init <path> # scaffold .fapony/ (plan/spec/
|
|
265
|
-
fapony install [--all|--platform <name>|--dry-run] # wire
|
|
265
|
+
fapony init <path> # scaffold .fapony/ (plan/done/spec/evidence)
|
|
266
|
+
fapony install [--all|--platform <name>|--dry-run] # wire skills + plan-mv guard into clients
|
|
266
267
|
fapony setup # interactive wizard: config + scaffold in one step
|
|
267
268
|
fapony update # self-update via git pull
|
|
268
269
|
fapony telemetry show|send # opt-in only, default off — see TELEMETRY.md
|
|
@@ -271,43 +272,30 @@ fapony telemetry show|send # opt-in only, default off — see TE
|
|
|
271
272
|
`fapony report <run-id>` prints the full report for a frozen-ledger run — git facts, handoff
|
|
272
273
|
conformance, allowlisted evidence, the stored verdict, cost — with anything the agent claimed but
|
|
273
274
|
couldn't prove marked as such. Reports are stamped with the producing build's `server_sha`; after
|
|
274
|
-
editing fapony, compare the stamp against `git log -1` before trusting a report
|
|
275
|
-
long-lived MCP server. Each allowlisted command gets `timeout_ms` (default 30s), the whole report
|
|
275
|
+
editing fapony, compare the stamp against `git log -1` before trusting a report. Each allowlisted command gets `timeout_ms` (default 30s), the whole report
|
|
276
276
|
capped at 180s — a command that doesn't fit reports as `timeout`, never as a pass. If your
|
|
277
277
|
`.gitignore` ignores `.fapony/` wholesale, re-include the file: `**/.fapony/*`, then
|
|
278
278
|
`!**/.fapony/evidence.json`.
|
|
279
279
|
|
|
280
|
-
*When* to call `mem add` is your project's call, not fapony's — write it in your own
|
|
281
|
-
`AGENTS.md`/`CLAUDE.md`. A starting point:
|
|
282
|
-
|
|
283
|
-
```markdown
|
|
284
|
-
## Memory
|
|
285
|
-
- Found a bug while working (not just user-reported)? Log it before fixing:
|
|
286
|
-
mem_add { kind: "bug", worktree: "<absolute app dir>", files: [...], text: "..." }
|
|
287
|
-
- `text` must stand alone — read months later with no chat context: what/where/repro/status.
|
|
288
|
-
- Don't fold the fix into the same chunk — log first, fix as its own next/chunk if you do.
|
|
289
|
-
```
|
|
290
|
-
|
|
291
280
|
## Config
|
|
292
281
|
|
|
293
282
|
`fapony.config.json` lives in the fapony checkout and is gitignored (it's per-machine). Copy
|
|
294
|
-
[fapony.config.example.json](https://github.com/
|
|
283
|
+
[fapony.config.example.json](https://github.com/inonix-dev/fapony/blob/main/fapony.config.example.json)
|
|
295
284
|
for a complete working reference; every section is optional. Key fields: `worktrees`
|
|
296
|
-
(name → path), `memory` (shell commands
|
|
285
|
+
(name → path), `memory` (shell commands the frozen ledger runs; off unless set), `paths` / `safety`,
|
|
297
286
|
`usageWeb { port, hostname }`. Env overrides: `FAPONY_CONFIG`, `FAPONY_STATE_DIR` (state DB;
|
|
298
|
-
default `~/.config/fapony/`)
|
|
287
|
+
default `~/.config/fapony/`).
|
|
299
288
|
|
|
300
289
|
## Scope
|
|
301
290
|
|
|
302
|
-
**Supported:**
|
|
303
|
-
per-
|
|
291
|
+
**Supported:** cross-client usage on one yardstick · per-project plans + convention debt ·
|
|
292
|
+
per-client hooks ([matrix above](#what-runs-where)) ·
|
|
304
293
|
vendor-neutral skills (anything that reads stdin) · opt-in telemetry, off by default
|
|
305
|
-
([TELEMETRY.md](https://github.com/
|
|
294
|
+
([TELEMETRY.md](https://github.com/inonix-dev/fapony/blob/main/TELEMETRY.md) lists exactly what
|
|
306
295
|
leaves the machine) · Bun-only; run state in SQLite via `bun:sqlite` (WAL mode).
|
|
307
296
|
|
|
308
|
-
**Not supported (yet):**
|
|
309
|
-
|
|
310
|
-
hook surface is still evolving. A hosted or shared ledger —
|
|
297
|
+
**Not supported (yet):** the plan-mv guard outside Claude Code. Memory of any
|
|
298
|
+
kind — that is [fael](https://github.com/inonix-dev/fael). A hosted or shared ledger —
|
|
311
299
|
`FAPONY_STATE_DIR` on a synced folder works as an experiment only; SQLite's WAL mode does not
|
|
312
300
|
tolerate concurrent writers over NFS/Dropbox/iCloud Drive and can corrupt the db under real
|
|
313
301
|
contention.
|
package/package.json
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fapony",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"description": "Token usage across Claude Code, OpenCode, Codex & ZCode on one yardstick — plus
|
|
3
|
+
"version": "0.7.1",
|
|
4
|
+
"description": "Token usage across Claude Code, OpenCode, Codex & ZCode on one yardstick — plus plans run chunk by chunk and a convention-debt tracker. No server, your data stays local",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "delamind (https://github.com/kire21b)",
|
|
7
|
-
"homepage": "https://github.com/
|
|
7
|
+
"homepage": "https://github.com/inonix-dev/fapony#readme",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/
|
|
10
|
+
"url": "git+https://github.com/inonix-dev/fapony.git"
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
|
-
"mcp",
|
|
14
|
-
"mcp-server",
|
|
15
13
|
"agent",
|
|
14
|
+
"token-usage",
|
|
16
15
|
"claude-code",
|
|
17
16
|
"opencode",
|
|
18
17
|
"coding-agent"
|
|
@@ -8,8 +8,8 @@ description: Turn "files that haven't migrated yet" into a tracked convention
|
|
|
8
8
|
One convention = the pattern to use (`ok`) + the pattern meaning not-yet-migrated
|
|
9
9
|
(`stale`) + scope (`where`) + an optional file condition (`guard`). The output is
|
|
10
10
|
one row in `<worktree>/.fapony/conventions.json` (`{"conventions": [...]}`), in the
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
nearest `.fapony/` at or above the code — the same dir `fapony plan` lists plans
|
|
12
|
+
from (app-scoped in a monorepo). No file there yet = create it;
|
|
13
13
|
a file with rows = append only, never rewrite other rows.
|
|
14
14
|
|
|
15
15
|
## Phase 1 — One question, then the checker question
|
|
@@ -68,7 +68,7 @@ Read it literally — every outcome names its fix:
|
|
|
68
68
|
Phase 3.
|
|
69
69
|
- `0 convention(s)` after `--id` → that id does not exist (misspelling — a dropped
|
|
70
70
|
row still prints its `⚠`). Only `no conventions.json in <dir>` means you wrote
|
|
71
|
-
to the wrong `.fapony/` (
|
|
71
|
+
to the wrong `.fapony/` (the nearest one above the code — `fapony plan` names it).
|
|
72
72
|
|
|
73
73
|
## Later
|
|
74
74
|
|
|
@@ -38,7 +38,8 @@ per file, the rest as `(+N)`, so the total is still readable. That is your entry
|
|
|
38
38
|
|
|
39
39
|
## History + debt (same paths, two calls)
|
|
40
40
|
|
|
41
|
-
- `
|
|
41
|
+
- fael `find` with `files: [<same paths>]` (or `fael find --files <paths>`) — "what was ever decided about
|
|
42
|
+
this file". fael already attaches these rows when you Read a file, so call it only for paths you skip reading.
|
|
42
43
|
- `fapony debt --where <dir|file>` — conventions this path still violates; empty until `conventions.json` exists.
|
|
43
44
|
|
|
44
45
|
## After the lookup
|
|
@@ -37,26 +37,26 @@ You are about to move a PLAN that has been shipped to the archive.
|
|
|
37
37
|
A plan that is merely *waiting* (on a person, a customer, a decision) is **not** dead and does
|
|
38
38
|
not move — mark it `status: blocked` + `blocked_by: <what you are waiting for>` and leave it in
|
|
39
39
|
`plan/` — the frontmatter is for the next person reading the folder, and the plan stays out
|
|
40
|
-
of `done/`, which is what `plan
|
|
40
|
+
of `done/`, which is what `fapony plan` and `plan sweep` go by. Never `--apply` a blocked file;
|
|
41
41
|
a blocked file with all chunks ticked is deferred doc debt — ask the user: ship it or keep
|
|
42
42
|
waiting.
|
|
43
43
|
|
|
44
|
-
1c. **Check the dep graph before moving** — `fapony
|
|
44
|
+
1c. **Check the dep graph before moving** — `fapony plan check` reads `blocked_by`/`blocks`
|
|
45
45
|
and says what a human would miss: a `blocked_by` pointing at a file that is not in `plan/`
|
|
46
46
|
or `done/`, a blocker already in `done/` while the dependent is still `status: blocked`,
|
|
47
47
|
a waiter cycle, and a blocked plan with all chunks ticked. Fix its issues first — a move
|
|
48
48
|
on top of a broken graph just relocates the confusion.
|
|
49
49
|
|
|
50
|
-
2. **Run `plan
|
|
50
|
+
2. **Run `fapony plan sweep --apply`** — this does the `git mv`, rewrites markdown links inside the
|
|
51
51
|
file and inbound links from every `.md` under `.fapony/` (`plan/`, `done/`, `spec/`),
|
|
52
52
|
warns about plain-text mentions and about tracked files outside `.fapony/` that still
|
|
53
53
|
name the file, prints a `🔓 <shipped> — <waiter> lists it as blocker` line when the ship
|
|
54
54
|
unblocks a waiting plan (copy that line into your summary — the waiter keeps
|
|
55
|
-
`status: blocked` until its owner clears it)
|
|
55
|
+
`status: blocked` until its owner clears it) — all in one call:
|
|
56
56
|
```bash
|
|
57
|
-
fapony
|
|
57
|
+
fapony plan sweep <PLAN-foo.md> --apply
|
|
58
58
|
```
|
|
59
|
-
It refuses if the file lacks a shipped header or has open
|
|
59
|
+
It refuses if the file lacks a shipped header or fael still has an open issue about it (notes and decisions travel with the plan).
|
|
60
60
|
If git refuses ("not under version control" — `.fapony/` is gitignored in this repo), plain
|
|
61
61
|
`mv` instead; there's nothing to commit for an untracked path, so skip step 4 in that case.
|
|
62
62
|
The filename gets no date prefix — the ship date is already in the header (step 1).
|
|
@@ -71,20 +71,17 @@ You are about to move a PLAN that has been shipped to the archive.
|
|
|
71
71
|
chore(plan): archive PLAN-foo.md (shipped <hash>)
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
5. **Leave a note when the ship taught something** —
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
`mem_add` MCP tool (fapony) once:
|
|
74
|
+
5. **Leave a note when the ship taught something** — the move itself is in git, so a
|
|
75
|
+
clean ship needs nothing more. When the plan hit something a reader could not get
|
|
76
|
+
from the diff, call fael's `add` tool (or `fael add`) once:
|
|
78
77
|
- `kind`: `note`
|
|
79
78
|
- `text`: what the symptom looked like, where the cause actually was, and the
|
|
80
79
|
rule that follows. Standalone prose — it is read months later with no access
|
|
81
80
|
to this conversation. Write one only then — "clean ship" files nothing, and a
|
|
82
81
|
note that repeats the diff teaches the next session nothing
|
|
83
82
|
- `files`: repo-relative paths this plan touched (`git diff --name-only <base>..HEAD`)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
fapony tool scopes by absolute path too; a bare repo name won't match them
|
|
87
|
-
Skip only if fapony's MCP tools aren't available in this session — don't block the archive on it.
|
|
83
|
+
plus the archived plan's path (post-move, e.g. `.fapony/done/PLAN-foo.md`)
|
|
84
|
+
Skip only if fael isn't available in this session — don't block the archive on it.
|
|
88
85
|
|
|
89
86
|
## Example
|
|
90
87
|
|
|
@@ -92,25 +89,24 @@ You are about to move a PLAN that has been shipped to the archive.
|
|
|
92
89
|
Input: .fapony/plan/PLAN-kickoff.md, no shipped header yet
|
|
93
90
|
Steps:
|
|
94
91
|
1. stamp header: > ✅ **shipped 2026-09-13** (a1b2c3)
|
|
95
|
-
2. fapony
|
|
96
|
-
→ moved, links rewritten
|
|
92
|
+
2. fapony plan sweep .fapony/plan/PLAN-kickoff.md --apply
|
|
93
|
+
→ moved, links rewritten
|
|
97
94
|
3. spec: untouched, stays in .fapony/spec/
|
|
98
95
|
4. commit
|
|
99
|
-
5. (clean ship —
|
|
96
|
+
5. (clean ship — the move is in git, nothing more to file)
|
|
100
97
|
```
|
|
101
98
|
|
|
102
99
|
A ship worth a note looks like this instead:
|
|
103
100
|
|
|
104
101
|
```
|
|
105
|
-
5.
|
|
102
|
+
5. add(kind="note",
|
|
106
103
|
text="sheet scroll reset on open, not close — the restore hook was on the wrong side; the router's own scrollRestoration resets on every navigate(). Check the router option before writing a restore hook.",
|
|
107
|
-
files=["src/routes/expenses/index.tsx"
|
|
108
|
-
worktree="/Users/you/Project/vela")
|
|
104
|
+
files=["src/routes/expenses/index.tsx", ".fapony/done/PLAN-quick-nav.md"])
|
|
109
105
|
```
|
|
110
106
|
|
|
111
107
|
## If fail
|
|
112
108
|
|
|
113
109
|
- No git repo / no commits (can't derive a shipped hash) → tell user: "Add header > ✅ **shipped** (<hash>) first"
|
|
114
110
|
- Stamped the header yourself → always say which hash you used
|
|
115
|
-
- plan
|
|
116
|
-
- Too many inbound links → plan
|
|
111
|
+
- plan sweep refuses (open fael issue) → fix and `fael close` it, or `MEM_FORCE=1`
|
|
112
|
+
- Too many inbound links → plan sweep reports them; too many to fix → report the list
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: plan-with-pony
|
|
3
|
-
description: Draft a plan + spec from "what's in your head" — one question, then a draft you correct. Vendor-neutral — works with Claude Code, OpenCode, Codex, ZCode. Seeds the factual sections from the code and
|
|
3
|
+
description: Draft a plan + spec from "what's in your head" — one question, then a draft you correct. Vendor-neutral — works with Claude Code, OpenCode, Codex, ZCode. Seeds the factual sections from the code and fael memory when the CLI is wired up. Trigger on /plan-with-pony and when the user asks to plan or brainstorm a feature.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# plan-with-pony — start from what's in your head
|
|
@@ -88,7 +88,7 @@ fapony plan-seed <feature> --spec --scope <path>
|
|
|
88
88
|
One command, no MCP round trip. It writes `<planDir>/PLAN-<feature>.md` +
|
|
89
89
|
`<specDir>/SPEC-<feature>.md` — the frontmatter, the 8 empty sections, a `## 8. References` list
|
|
90
90
|
of shipped plans that already touched this scope, and a `## Context (fapony)` block under the
|
|
91
|
-
TL;DR (recent
|
|
91
|
+
TL;DR (recent fael decisions plus what is already in scope — one line per scope file with its
|
|
92
92
|
exports; needs `--scope` to list anything). No ledger-ranking line: the ledger is frozen and
|
|
93
93
|
cross-model ranking claims are off the table, so the seed does not point at them. SPEC chunks carry
|
|
94
94
|
verbatim signatures, hard-capped (PLAN ≤ ~60 / SPEC ≤ 200 lines), and capped lines say what was
|
|
@@ -149,7 +149,7 @@ normal — writing to the default there scatters plans into a directory nobody r
|
|
|
149
149
|
by hand, this check is yours.)
|
|
150
150
|
|
|
151
151
|
**Editing a plan someone is executing right now is a different job from drafting one.** Ask the
|
|
152
|
-
dev, or run `fapony
|
|
152
|
+
dev, or run `fapony plan` — it reads the same plan files and names each plan's first unchecked
|
|
153
153
|
chunk, so a plan already in flight is the one you are about to edit under someone. When that is the case:
|
|
154
154
|
|
|
155
155
|
- **Anything you add is an instruction, not a note.** A measured fact parked under "don't do"
|
|
@@ -189,7 +189,7 @@ only place that ordering stays true.
|
|
|
189
189
|
|
|
190
190
|
**The TL;DR is 15 lines, hard cap, and is the only part that changes while the work is in flight**
|
|
191
191
|
(tick a box, stamp a short sha). Everything below it is the agreement. A TL;DR allowed to grow
|
|
192
|
-
becomes a second copy of the plan, and then neither copy can be trusted. `fapony
|
|
192
|
+
becomes a second copy of the plan, and then neither copy can be trusted. `fapony plan` reads the
|
|
193
193
|
checkboxes in the **first `##` section only**, so section 6 stays detail rather than status.
|
|
194
194
|
|
|
195
195
|
Section 6 — every step must be verifiable. Section 8 — must link back to anything it came from. **A step that needs something the system does not store yet** ("the month the accountant has seen",
|