@reforma/agentflow 0.0.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/LICENSE +21 -0
- package/README.md +252 -0
- package/bin/agentflow.js +189 -0
- package/package.json +55 -0
- package/skills/code-review/SKILL.md +117 -0
- package/skills/grill/SKILL.md +57 -0
- package/skills/handoff/SKILL.md +63 -0
- package/skills/plan/SKILL.md +81 -0
- package/skills/research/SKILL.md +133 -0
- package/skills/tdd/SKILL.md +43 -0
- package/skills/tdd/mocking.md +50 -0
- package/skills/tdd/tests.md +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Reforma contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# AgentFlow
|
|
2
|
+
|
|
3
|
+
An opinionated workflow for producing high-quality, production-ready changes
|
|
4
|
+
with coding agents.
|
|
5
|
+
|
|
6
|
+
Medium and large tasks are difficult to carry through in a single chat without
|
|
7
|
+
losing quality. The scope grows during implementation, earlier requirements
|
|
8
|
+
disappear into the history, and unanswered questions are left for the agent to
|
|
9
|
+
decide. Those decisions are often wrong. The longer the chat runs, the less
|
|
10
|
+
reliable its context becomes, while compacting it or moving the work to a new
|
|
11
|
+
chat loses the reasoning behind earlier decisions.
|
|
12
|
+
|
|
13
|
+
For anything bigger than a quick fix, we usually start by exploring the existing
|
|
14
|
+
feature, code, or technology. Once the agent knows the ground, we grill the idea
|
|
15
|
+
and talk through the tricky parts before touching code. Then we usually write a
|
|
16
|
+
plan and split the work into PR-sized chunks. Small tasks can skip the plan and
|
|
17
|
+
go straight to implementation.
|
|
18
|
+
|
|
19
|
+
Work through the chunks in order, however you prefer: stay in the same chat,
|
|
20
|
+
summarize it between PRs, start a fresh chat, or write the code yourself. Review
|
|
21
|
+
and commit each PR before moving to the next one, while the diff is still small
|
|
22
|
+
enough to clean up properly. When the context gets noisy, summarize it or write
|
|
23
|
+
a handoff to disk before continuing.
|
|
24
|
+
|
|
25
|
+
The core loop is:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
Research → Grill → Plan → PR → Review → Commit
|
|
29
|
+
↑ │
|
|
30
|
+
└──── Next PR ─────┘
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Research is optional, small tasks can skip the plan, and a handoff is only
|
|
34
|
+
needed when you move the work to a fresh context.
|
|
35
|
+
|
|
36
|
+
Reusable agent skills are published from `skills/`:
|
|
37
|
+
|
|
38
|
+
- `/research` — save technology or landscape research for later chats
|
|
39
|
+
- `/grill` — question an idea until the important decisions are clear
|
|
40
|
+
- `/plan` — split the work into PR-sized slices
|
|
41
|
+
- `/code-review` — review each slice for reuse and unnecessary complexity
|
|
42
|
+
- `/handoff` — save context before moving to a fresh chat
|
|
43
|
+
- `/tdd` — work through one red-green slice at a time
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## The Loop
|
|
48
|
+
|
|
49
|
+
Research helps when the agent does not know the area, and most larger tasks also need a plan. But almost every task goes through Grill. Small, obvious changes can go straight from Grill to implementation.
|
|
50
|
+
|
|
51
|
+
1. Learn how it works (optional)
|
|
52
|
+
2. Sharpen the idea with Grill
|
|
53
|
+
3. Slice the work into PRs
|
|
54
|
+
4. Implement one PR
|
|
55
|
+
5. Agent review
|
|
56
|
+
6. Your review, then commit
|
|
57
|
+
7. Refresh the context when needed or start the new chat
|
|
58
|
+
|
|
59
|
+
Repeat steps 4–7 until every PR in the plan is complete.
|
|
60
|
+
|
|
61
|
+
### Step 1. Learn how it works (optional)
|
|
62
|
+
|
|
63
|
+
Research does not require a skill. Even a simple prompt like "Find out how
|
|
64
|
+
authentication works in this project" can make the implementation much easier.
|
|
65
|
+
|
|
66
|
+
This step is optional, but highly recommended when the agent has not worked in
|
|
67
|
+
the area before. It lets the agent understand what it will be changing before
|
|
68
|
+
the implementation starts.
|
|
69
|
+
|
|
70
|
+
Use `/research` when the findings should survive the current chat. It saves the
|
|
71
|
+
research artifacts under `.agentflow/<feature>/research/`, ready to attach after
|
|
72
|
+
context compaction or in a new chat.
|
|
73
|
+
|
|
74
|
+
Even if you skip this step, Grill will fill in any gaps.
|
|
75
|
+
|
|
76
|
+
### Step 2. Sharpen the idea with Grill
|
|
77
|
+
|
|
78
|
+
Grill is the core of AgentFlow and the one step used for the most tasks.
|
|
79
|
+
|
|
80
|
+
Run `/grill` after research, or start with it when research is not needed.
|
|
81
|
+
|
|
82
|
+
The agent explains how it understands the task, surfaces assumptions and
|
|
83
|
+
decision points, and recommends an answer for each one. You confirm or correct
|
|
84
|
+
that understanding. If your answers open new questions, the agent continues
|
|
85
|
+
until nothing important is left unclear.
|
|
86
|
+
|
|
87
|
+
This gives the implementation an agreed starting point instead of leaving the
|
|
88
|
+
agent to fill in gaps on its own.
|
|
89
|
+
|
|
90
|
+
### Step 3. Plan and slice the work into PRs
|
|
91
|
+
|
|
92
|
+
For a larger task, planning usually follows Grill in the same chat. If you asked
|
|
93
|
+
Grill to plan next, it loads `/plan` automatically. You can also run `/plan`
|
|
94
|
+
yourself.
|
|
95
|
+
|
|
96
|
+
In a normal chat, `/plan` saves the result to `.agentflow/<slug>/plan.md`. In
|
|
97
|
+
Native Plan mode, the agent uses its built-in planning flow and native plan
|
|
98
|
+
artifact instead.
|
|
99
|
+
|
|
100
|
+
The plan breaks the feature into PRs that can be shipped one by one. A PR is the
|
|
101
|
+
smallest complete change with one coherent outcome and a clear way to verify it,
|
|
102
|
+
not a fixed number of files or lines.
|
|
103
|
+
|
|
104
|
+
Larger work is usually sliced from foundations to consumers. A full-stack
|
|
105
|
+
feature might start with behavior-preserving refactoring, continue with shared
|
|
106
|
+
types and backend work, and finish with the frontend. A frontend feature might
|
|
107
|
+
move from reusable components, through shared runtime or wiring, to the
|
|
108
|
+
user-facing interface.
|
|
109
|
+
|
|
110
|
+
These are examples, not a required template. A small feature stays as one
|
|
111
|
+
vertical slice. Feature-local components and wiring stay with the interface
|
|
112
|
+
that first uses them instead of becoming placeholder PRs.
|
|
113
|
+
|
|
114
|
+
Keep the plan updated as the work changes.
|
|
115
|
+
|
|
116
|
+
**Done when:** someone can open the plan, find the next PR, and see how to
|
|
117
|
+
verify it.
|
|
118
|
+
|
|
119
|
+
### Step 4. Implement one PR
|
|
120
|
+
|
|
121
|
+
Implement the first unchecked PR and nothing beyond it. Without a plan, keep the
|
|
122
|
+
change small enough to review. You can stay in the current chat, start a fresh
|
|
123
|
+
one, or write the code yourself. Bring the plan and latest handoff when moving
|
|
124
|
+
to another chat. Use `/tdd` for test-first work.
|
|
125
|
+
|
|
126
|
+
Load any relevant skills named in `AGENTS.md`. If work spills into a later PR,
|
|
127
|
+
update the plan instead of silently expanding the current one.
|
|
128
|
+
|
|
129
|
+
**Done when:** the PR does what the plan promised, and the plan matches the work.
|
|
130
|
+
|
|
131
|
+
### Step 5. Agent review
|
|
132
|
+
|
|
133
|
+
Run `/code-review` on the completed PR. It looks for code to reuse, unnecessary
|
|
134
|
+
wrappers, and avoidable complexity. It fixes small local problems and returns a
|
|
135
|
+
verdict: keep, shrink, or burn.
|
|
136
|
+
|
|
137
|
+
**Done when:** the review has run and its local fixes are applied.
|
|
138
|
+
|
|
139
|
+
### Step 6. Your review, then commit
|
|
140
|
+
|
|
141
|
+
Read the diff yourself, then commit it using the project's normal workflow.
|
|
142
|
+
|
|
143
|
+
**Done when:** the PR is reviewed and committed.
|
|
144
|
+
|
|
145
|
+
### Step 7. Refresh the context when needed
|
|
146
|
+
|
|
147
|
+
Keep the current chat if it still has useful context. When it gets noisy,
|
|
148
|
+
summarize it or start a fresh one. Run `/handoff` to save what shipped, what
|
|
149
|
+
changed, and which PR comes next.
|
|
150
|
+
|
|
151
|
+
Attach the plan and handoff to a fresh chat when they exist.
|
|
152
|
+
|
|
153
|
+
**Done when:** you can start the next PR without reconstructing the previous
|
|
154
|
+
work. Return to step 4.
|
|
155
|
+
|
|
156
|
+
## Install
|
|
157
|
+
|
|
158
|
+
Install the complete workflow in a project:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npx @reforma/agentflow init
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The command installs every AgentFlow skill, writes `AGENTFLOW.md`, and ignores
|
|
165
|
+
the local `.agentflow/` artifacts. It prints a short pointer you can add to
|
|
166
|
+
`AGENTS.md` when you want agents in that project to follow the full loop.
|
|
167
|
+
|
|
168
|
+
Install only one skill without the workflow:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npx skills add reforma-dev/agentflow --skill plan
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Update the complete workflow:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npx @reforma/agentflow@latest update
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Or update one independently installed skill:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npx skills update plan
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Why AgentFlow?
|
|
189
|
+
|
|
190
|
+
Coding agents can handle large changes, but chat history is a poor place to
|
|
191
|
+
keep requirements, scope, and decisions. AgentFlow moves that information into
|
|
192
|
+
small files and gives each PR a clear boundary.
|
|
193
|
+
|
|
194
|
+
- **Learn the ground before estimating the work** — the agent sees the existing
|
|
195
|
+
behavior and code before discussing a solution.
|
|
196
|
+
- **Resolve decisions before coding** — `/grill` exposes assumptions while they
|
|
197
|
+
are still cheap to change.
|
|
198
|
+
- **Keep changes reviewable** — the plan divides a large feature into PR-sized
|
|
199
|
+
slices that are implemented and reviewed in order.
|
|
200
|
+
- **Preserve context between sessions** — the review artifacts, plan and handoff tell the next agent what is true and what comes next.
|
|
201
|
+
- **Use an opinionated workflow without heavy artifacts** — the sequence is
|
|
202
|
+
fixed, while the documentation is limited to one plan and one current
|
|
203
|
+
handoff.
|
|
204
|
+
|
|
205
|
+
### How we compare
|
|
206
|
+
|
|
207
|
+
OpenSpec and Spec Kit try to cover most of spec-driven development with their
|
|
208
|
+
own commands, templates, and artifacts. AgentFlow does not try to be an
|
|
209
|
+
all-in-one system. It adds a small, opinionated loop to the way you already
|
|
210
|
+
build software.
|
|
211
|
+
|
|
212
|
+
**vs. [OpenSpec](https://github.com/Fission-AI/OpenSpec)** — OpenSpec manages
|
|
213
|
+
proposals, requirements, designs, tasks, and completed changes. That works when
|
|
214
|
+
specs are the center of the process, but it also means adopting OpenSpec's
|
|
215
|
+
artifact system. AgentFlow does not require a spec tree. Most work needs one
|
|
216
|
+
plan and a handoff only when the context moves to a new chat.
|
|
217
|
+
|
|
218
|
+
**vs. [Spec Kit](https://github.com/github/spec-kit)** — Spec Kit provides a
|
|
219
|
+
thorough, phase-based process with a constitution, specifications, plans, and
|
|
220
|
+
task lists. AgentFlow gives you an order of work without asking you to move the
|
|
221
|
+
rest of your development process into the framework.
|
|
222
|
+
|
|
223
|
+
**vs. an unstructured chat** — Working directly in chat is enough for a small
|
|
224
|
+
fix. On larger changes, AgentFlow keeps decisions out of transient history,
|
|
225
|
+
limits scope to one reviewable slice, and gives the next session a reliable
|
|
226
|
+
starting point.
|
|
227
|
+
|
|
228
|
+
## Releasing
|
|
229
|
+
|
|
230
|
+
Add a changeset with every publishable change:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
bun run changeset
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
When that changeset reaches `main`, the publish workflow tests the package,
|
|
237
|
+
updates its version and changelog, publishes it to npm through trusted
|
|
238
|
+
publishing, and commits the release files back to `main`. A separate job then
|
|
239
|
+
publishes the matching versioned Agent Skills release on GitHub.
|
|
240
|
+
|
|
241
|
+
The npm trusted publisher for `@reforma/agentflow` must point at
|
|
242
|
+
`reforma-dev/agentflow/.github/workflows/publish_npm.yml`.
|
|
243
|
+
|
|
244
|
+
Validate a release locally without publishing:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
gh skill publish --dry-run
|
|
248
|
+
bun run test
|
|
249
|
+
bun publish --dry-run
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
AgentFlow is available under the [MIT License](LICENSE).
|
package/bin/agentflow.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
5
|
+
import { dirname as dirname2, resolve as resolve3 } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
// src/project.ts
|
|
9
|
+
import {
|
|
10
|
+
existsSync,
|
|
11
|
+
readFileSync,
|
|
12
|
+
renameSync,
|
|
13
|
+
writeFileSync
|
|
14
|
+
} from "node:fs";
|
|
15
|
+
import { resolve } from "node:path";
|
|
16
|
+
var GENERATED_MARKER = "<!-- Generated by AgentFlow CLI. Run `npx @reforma/agentflow@latest update` to refresh. -->";
|
|
17
|
+
var AGENTS_SNIPPET = "**Agent loop:** [AGENTFLOW.md](AGENTFLOW.md) — optional research → grill → usually `/plan` → sequential PRs (implement → `/code-review` → commit) → refresh context when needed.";
|
|
18
|
+
function generatedWorkflow(workflow) {
|
|
19
|
+
return `${GENERATED_MARKER}
|
|
20
|
+
|
|
21
|
+
${workflow.trimEnd()}
|
|
22
|
+
`;
|
|
23
|
+
}
|
|
24
|
+
function isGeneratedWorkflow(path) {
|
|
25
|
+
return existsSync(path) && readFileSync(path, "utf8").startsWith(GENERATED_MARKER);
|
|
26
|
+
}
|
|
27
|
+
function writeAtomically(path, contents) {
|
|
28
|
+
const temporaryPath = `${path}.agentflow-tmp`;
|
|
29
|
+
writeFileSync(temporaryPath, contents);
|
|
30
|
+
renameSync(temporaryPath, path);
|
|
31
|
+
}
|
|
32
|
+
function ensureIgnored(cwd) {
|
|
33
|
+
const path = resolve(cwd, ".gitignore");
|
|
34
|
+
const current = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
35
|
+
const alreadyIgnored = current.split(/\r?\n/).some((line) => /^\/?\.agentflow\/?$/.test(line.trim()));
|
|
36
|
+
if (alreadyIgnored) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const separator = current.length === 0 || current.endsWith(`
|
|
40
|
+
`) ? "" : `
|
|
41
|
+
`;
|
|
42
|
+
writeAtomically(path, `${current}${separator}.agentflow/
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/skills.ts
|
|
47
|
+
import { spawnSync } from "node:child_process";
|
|
48
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
49
|
+
import { createRequire } from "node:module";
|
|
50
|
+
import { dirname, resolve as resolve2 } from "node:path";
|
|
51
|
+
var AGENTFLOW_SKILLS = [
|
|
52
|
+
"research",
|
|
53
|
+
"grill",
|
|
54
|
+
"plan",
|
|
55
|
+
"code-review",
|
|
56
|
+
"handoff",
|
|
57
|
+
"tdd"
|
|
58
|
+
];
|
|
59
|
+
var SKILLS_SOURCE = "reforma-dev/agentflow";
|
|
60
|
+
var runSkills = (args) => {
|
|
61
|
+
const require2 = createRequire(import.meta.url);
|
|
62
|
+
const packagePath = require2.resolve("skills/package.json");
|
|
63
|
+
const packageJson = JSON.parse(readFileSync2(packagePath, "utf8"));
|
|
64
|
+
const relativeBin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin.skills;
|
|
65
|
+
if (!relativeBin) {
|
|
66
|
+
throw new Error("The installed skills package does not expose a skills bin.");
|
|
67
|
+
}
|
|
68
|
+
const result = spawnSync(process.execPath, [resolve2(dirname(packagePath), relativeBin), ...args], { stdio: "inherit" });
|
|
69
|
+
if (result.error) {
|
|
70
|
+
throw result.error;
|
|
71
|
+
}
|
|
72
|
+
return result.status ?? 1;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// src/cli.ts
|
|
76
|
+
var packageRoot = resolve3(dirname2(fileURLToPath(import.meta.url)), "..");
|
|
77
|
+
var packageJson = JSON.parse(readFileSync3(resolve3(packageRoot, "package.json"), "utf8"));
|
|
78
|
+
function parseOptions(args, command) {
|
|
79
|
+
const forwarded = [];
|
|
80
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
81
|
+
const argument = args[index];
|
|
82
|
+
if (argument === "--agent" || argument === "-a") {
|
|
83
|
+
if (command !== "init") {
|
|
84
|
+
throw new Error(`${argument} is only supported by init.`);
|
|
85
|
+
}
|
|
86
|
+
const agent = args[index + 1];
|
|
87
|
+
if (!agent || agent.startsWith("-")) {
|
|
88
|
+
throw new Error(`${argument} requires an agent name.`);
|
|
89
|
+
}
|
|
90
|
+
forwarded.push(argument, agent);
|
|
91
|
+
index += 1;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (argument === "--global" || argument === "-g") {
|
|
95
|
+
forwarded.push(argument);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (argument === "--project" || argument === "-p") {
|
|
99
|
+
if (command !== "update") {
|
|
100
|
+
throw new Error(`${argument} is only supported by update.`);
|
|
101
|
+
}
|
|
102
|
+
forwarded.push(argument);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (argument === "--yes" || argument === "-y") {
|
|
106
|
+
forwarded.push(argument);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
throw new Error(`Unknown option: ${argument}`);
|
|
110
|
+
}
|
|
111
|
+
return forwarded;
|
|
112
|
+
}
|
|
113
|
+
function help() {
|
|
114
|
+
return `AgentFlow ${packageJson.version}
|
|
115
|
+
|
|
116
|
+
Usage:
|
|
117
|
+
agentflow init [--agent <name>] [--global] [--yes]
|
|
118
|
+
agentflow update [--global | --project] [--yes]
|
|
119
|
+
agentflow --version
|
|
120
|
+
|
|
121
|
+
Commands:
|
|
122
|
+
init Install all AgentFlow skills and write AGENTFLOW.md
|
|
123
|
+
update Update installed AgentFlow skills and refresh AGENTFLOW.md
|
|
124
|
+
|
|
125
|
+
AgentFlow prints the AGENTS.md snippet for you to add manually.
|
|
126
|
+
`;
|
|
127
|
+
}
|
|
128
|
+
function runCli(args, {
|
|
129
|
+
cwd = process.cwd(),
|
|
130
|
+
stdout = process.stdout,
|
|
131
|
+
stderr = process.stderr,
|
|
132
|
+
runSkills: executeSkills = runSkills,
|
|
133
|
+
workflow = readFileSync3(resolve3(packageRoot, "README.md"), "utf8")
|
|
134
|
+
} = {}) {
|
|
135
|
+
const [command, ...optionArgs] = args;
|
|
136
|
+
if (!command || command === "--help" || command === "-h") {
|
|
137
|
+
stdout.write(help());
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
if (command === "--version" || command === "-v") {
|
|
141
|
+
stdout.write(`${packageJson.version}
|
|
142
|
+
`);
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
if (command !== "init" && command !== "update") {
|
|
146
|
+
stderr.write(`Unknown command: ${command}
|
|
147
|
+
|
|
148
|
+
${help()}`);
|
|
149
|
+
return 1;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const forwarded = parseOptions(optionArgs, command);
|
|
153
|
+
const workflowPath = resolve3(cwd, "AGENTFLOW.md");
|
|
154
|
+
if (existsSync2(workflowPath) && !isGeneratedWorkflow(workflowPath)) {
|
|
155
|
+
throw new Error("AGENTFLOW.md is not managed by AgentFlow CLI. Move it or merge it manually before continuing.");
|
|
156
|
+
}
|
|
157
|
+
if (command === "update" && !existsSync2(workflowPath)) {
|
|
158
|
+
throw new Error("AgentFlow is not initialized here. Run `agentflow init`.");
|
|
159
|
+
}
|
|
160
|
+
const skillsArgs = command === "init" ? ["add", SKILLS_SOURCE, "--skill", "*", ...forwarded] : ["update", ...AGENTFLOW_SKILLS, ...forwarded];
|
|
161
|
+
const status = executeSkills(skillsArgs);
|
|
162
|
+
if (status !== 0) {
|
|
163
|
+
stderr.write(`Agent skills ${command} failed with exit code ${status}.
|
|
164
|
+
`);
|
|
165
|
+
return status;
|
|
166
|
+
}
|
|
167
|
+
writeAtomically(workflowPath, generatedWorkflow(workflow));
|
|
168
|
+
ensureIgnored(cwd);
|
|
169
|
+
if (command === "init") {
|
|
170
|
+
stdout.write(`AgentFlow initialized.
|
|
171
|
+
|
|
172
|
+
Add this line to AGENTS.md:
|
|
173
|
+
|
|
174
|
+
${AGENTS_SNIPPET}
|
|
175
|
+
`);
|
|
176
|
+
} else {
|
|
177
|
+
stdout.write(`AgentFlow updated.
|
|
178
|
+
`);
|
|
179
|
+
}
|
|
180
|
+
return 0;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
183
|
+
`);
|
|
184
|
+
return 1;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/bin.ts
|
|
189
|
+
process.exitCode = runCli(process.argv.slice(2));
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reforma/agentflow",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "An opinionated workflow for shipping production-ready changes with coding agents.",
|
|
5
|
+
"author": "Reforma, Inc. <dev@reforma.ai>",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"agentflow": "bin/agentflow.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"skills",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "bun build src/bin.ts --target=node --outfile=bin/agentflow.js --external skills",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"changeset": "bunx @changesets/cli",
|
|
20
|
+
"version-packages": "bunx @changesets/cli version",
|
|
21
|
+
"release": "bunx @changesets/cli publish",
|
|
22
|
+
"test": "bun test",
|
|
23
|
+
"validate:skills": "gh skill publish --dry-run",
|
|
24
|
+
"prepublishOnly": "bun run typecheck && bun run test && bun run build"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/reforma-dev/agentflow.git"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/reforma-dev/agentflow#readme",
|
|
31
|
+
"bugs": "https://github.com/reforma-dev/agentflow/issues",
|
|
32
|
+
"keywords": [
|
|
33
|
+
"agent-skills",
|
|
34
|
+
"coding-agents",
|
|
35
|
+
"cursor",
|
|
36
|
+
"claude-code",
|
|
37
|
+
"codex"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"skills": "1.5.23"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@changesets/cli": "3.0.1",
|
|
51
|
+
"@types/bun": "1.4.0",
|
|
52
|
+
"@types/node": "26.4.0",
|
|
53
|
+
"typescript": "7.0.2"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review
|
|
3
|
+
description: >-
|
|
4
|
+
Strict review of reuse, wrappers, and architecture. Reviewers collect, then
|
|
5
|
+
you apply fixes. Verdict is keep, shrink, or burn.
|
|
6
|
+
license: MIT
|
|
7
|
+
disable-model-invocation: true
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# code-review
|
|
11
|
+
|
|
12
|
+
Reviewers collect; **you apply fixes** — including obvious local defects, without asking. Done when production code is **smaller and less wrapped**, calling what already exists. Verdict: `keep` / `shrink` / `burn`.
|
|
13
|
+
|
|
14
|
+
Bugs are incidental: whoever already read the files reports ones they saw. Do not launch a bot to hunt bugs.
|
|
15
|
+
|
|
16
|
+
## Scope
|
|
17
|
+
|
|
18
|
+
User-named paths / symbols / area win. Named fixed point (branch, tag, `main`, PR) → `git diff <fixed>...HEAD` (three-dot), still honor a path allowlist inside it.
|
|
19
|
+
|
|
20
|
+
Otherwise: `git diff --no-color` and `git diff --cached --no-color`. No local diff → conversation files. Still nothing → `git show --stat --patch --no-color HEAD`.
|
|
21
|
+
|
|
22
|
+
Do not broaden past that scope except to match existing patterns. Preserve unrelated user changes.
|
|
23
|
+
|
|
24
|
+
**Allowlist** = those files. Every bot and every edit stays inside it (imports from an allowlisted file are ok).
|
|
25
|
+
|
|
26
|
+
## 1. Triage
|
|
27
|
+
|
|
28
|
+
Read the allowlisted diff. Write **one** launch line, then collect or skip to fix.
|
|
29
|
+
|
|
30
|
+
| Launch line | When |
|
|
31
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| `parent-only` | Default. The whole diff fits in one read. |
|
|
33
|
+
| `one: <bot> — <reason>` | One row matches **and** the parent cannot finish the verdict from this read. Reason names that gap. |
|
|
34
|
+
| `army: <bots>` | User said full / strict / army. One wave of matching rows except bugbot. |
|
|
35
|
+
| named bots | User named them. Those names only. Includes `bugbot` if they said it. |
|
|
36
|
+
|
|
37
|
+
**One-bot pick** (not army, not named): first match only.
|
|
38
|
+
|
|
39
|
+
1. **reuse** — may have reinvented or wrapped something that already exists, **and** that existing API is not obvious from this read (outside the allowlist, or too many files to hunt). A new file on a small diff is not enough.
|
|
40
|
+
2. **smell** — roughly ≥4 production files and wrappers / pass-throughs / muddy shape; reuse did not match.
|
|
41
|
+
3. **security** — actual trust surface (auth, secrets, injection, user input, sandbox, network); reuse and smell did not match.
|
|
42
|
+
|
|
43
|
+
No match → `parent-only`. A `.ts` file, “several files”, or “tests in the allowlist” is not a launch.
|
|
44
|
+
|
|
45
|
+
| Bot | Type | Launch |
|
|
46
|
+
| -------- | ----------------- | ------------------------------------------ |
|
|
47
|
+
| Reuse | `reuse-review` | Pick 1, army if that row matches, or named |
|
|
48
|
+
| Smells | `smell-review` | Pick 2, army if that row matches, or named |
|
|
49
|
+
| Security | `security-review` | Pick 3, army if that row matches, or named |
|
|
50
|
+
| Bugbot | `bugbot` | User named `bugbot` only |
|
|
51
|
+
|
|
52
|
+
Parent always does reuse / wrappers / verdict itself (nearest `AGENTS.md`; React / MobX / copy / tests → matching craft skill). A bot is extra eyes, not a replacement.
|
|
53
|
+
|
|
54
|
+
## 2. Collect
|
|
55
|
+
|
|
56
|
+
`parent-only` → skip this step.
|
|
57
|
+
|
|
58
|
+
Launch **only** the bots on the launch line. Army: one parallel wave. Otherwise exactly one. They must not edit. `run_in_background: false`, no `resume`.
|
|
59
|
+
|
|
60
|
+
**Brief** (every bot, plus the allowlist):
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
Primary: reuse (existing APIs vs new wrappers), architecture, cleanliness.
|
|
64
|
+
Verdict required: keep | shrink | burn — one line, why.
|
|
65
|
+
Incidental: bugs you already saw while reading. Do not hunt bugs.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Reuse / smells** — `description` exactly `Reuse Review` / `Smell Review`. They inspect git. Prompt: allowlist + brief.
|
|
69
|
+
|
|
70
|
+
**Security / bugbot** — `description` exactly `Security Review` / `Bugbot`. They compute the diff. Prompt:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
Full Repository Path: <workspace root>
|
|
74
|
+
Diff: uncommitted changes
|
|
75
|
+
Custom Instructions: Review ONLY these files (ignore other uncommitted changes):
|
|
76
|
+
- <allowlist path>
|
|
77
|
+
|
|
78
|
+
Primary: reuse (existing APIs vs new wrappers), architecture, cleanliness.
|
|
79
|
+
Verdict required: keep | shrink | burn — one line, why.
|
|
80
|
+
Incidental: bugs you already saw while reading. Do not hunt bugs.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Empty diff on committed HEAD/branch work → retry once with `Diff: branch changes`. Bugbot still empty → retry once with `Diff: natural language` and a per-file Change Description. Wrong prompt: retry once, then stop that bot and say so.
|
|
84
|
+
|
|
85
|
+
**Spec (you):** user-passed path, or issue/PR in commit messages (Linear, GitHub), or the plan in this conversation. Do not hunt a specs tree the user did not name. Missing/partial/scope-creep → findings for Fix. No source → skip.
|
|
86
|
+
|
|
87
|
+
## 3. Fix
|
|
88
|
+
|
|
89
|
+
Normalize: `severity | confidence | file:line | finding | fix`. Drop `confidence: low`. Dedup.
|
|
90
|
+
|
|
91
|
+
Bar (even if a bot under-reported):
|
|
92
|
+
|
|
93
|
+
1. **Reuse** — existing helper/component/API does the job → call it. No parallel wrapper. Duplicate implementations in the allowlist → keep the better one, retarget imports, delete the rest.
|
|
94
|
+
2. **Wrappers** — pass-throughs, extra HTML/JSX, one-off barrels/helpers: inline or delete. Do not wrap a wrapper.
|
|
95
|
+
3. **Shrink** — net-fewer prod lines (tests excluded) by deleting wrappers, dupes, and orphans — not by packing lines. Nested ternaries, dense one-liners, and mashed concerns are not shrink. Clarity wins when they conflict. Preserve behavior: only how, not what. A new file/helper is wrong unless it deletes more than it adds. If prod cannot shrink, say why.
|
|
96
|
+
4. **Orphans** — unused imports, locals, helpers, exports, files, or commented-out blocks **this allowlisted change** made dead. Before delete: Grep real uses, including dynamic `import()` / string path lookups. Zero uses → delete. Package public export / cross-app API without monorepo Grep proof → defer. Pre-existing dead outside the allowlist → leave. Unsure → defer (no knip/depcheck/ts-prune sweeps).
|
|
97
|
+
5. **Verdict** — local `burn`/`shrink` (inline, delete the new file, call the existing API) → do it. Whole-shape `burn` → say so and defer; do not nibble.
|
|
98
|
+
6. **Tests in the allowlist** — keep real behavior tests; delete mock-theater / dupes / empty / greenwash. Do not invent tests for a prod-only change. Do not reshape prod to please a weak test.
|
|
99
|
+
7. **Obvious** — a named local defect (broken emit, silent wrong path, leftover after a failed write) → fix this turn. Do not ask.
|
|
100
|
+
|
|
101
|
+
Incidental bugs: fix if local; else defer. Do not hunt.
|
|
102
|
+
|
|
103
|
+
Default-accept reuse/smell findings that serve the bar when the fix is local. Skip only with `needs product call` / `this is the contract` / `too large`.
|
|
104
|
+
|
|
105
|
+
Tie-break: existing helper > inline > new helper. You fix — do not delegate. No edits outside the allowlist. No re-hunt. Small in-scope shrink → do it; larger → defer. Do not rerun collectors unless a fix likely created a new bug.
|
|
106
|
+
|
|
107
|
+
## 4. Verify
|
|
108
|
+
|
|
109
|
+
No pass / done / clean claim without a command you ran in **this** turn. Identify the command → run it full → read exit and failures → then claim. A previous run, “should pass”, or lint-for-compile is not evidence.
|
|
110
|
+
|
|
111
|
+
Targeted: the project's test and lint for the allowlist (nearest `AGENTS.md`).
|
|
112
|
+
|
|
113
|
+
Bug you fixed with no covering test → add a regression test or list `no test: …`.
|
|
114
|
+
|
|
115
|
+
## Output
|
|
116
|
+
|
|
117
|
+
Launch line; **verdict** (`keep` / `shrink` / `burn` + one line); what changed; prod smaller or why not; wrappers gone / APIs reused; orphans deleted or deferred (Grep gap / public API); incidental bugs fixed or deferred; tests judged or `no tests in allowlist`; spec gaps or `no spec`; deferred (`severity | file:line | reason`); tests/lint **this turn** (command + exit). No “fix or skip?” for local findings.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: grill
|
|
3
|
+
description: Relentless interview to sharpen a plan or design until every branch is resolved.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Grill
|
|
8
|
+
|
|
9
|
+
Interview until you reach a shared understanding. Map the work as a **design tree**: every decision branches into the decisions that hang off it.
|
|
10
|
+
|
|
11
|
+
Use this for non-trivial product/architecture calls. Do not grill a one-line fix.
|
|
12
|
+
|
|
13
|
+
Do **not** create `CONTEXT.md`, ADRs, or tickets as you go. Capture settled decisions in the reply; if a lasting convention belongs in an `AGENTS.md`, say so and wait for the user to ask you to write it.
|
|
14
|
+
|
|
15
|
+
## Rounds
|
|
16
|
+
|
|
17
|
+
Start by restating the task, the decisions already implied by the request, and
|
|
18
|
+
any assumptions you would otherwise make. If there are no open questions, ask
|
|
19
|
+
the user to confirm this reading.
|
|
20
|
+
|
|
21
|
+
Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait.
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
❓ **Q1** - **<title>**: <body, including choices>
|
|
25
|
+
|
|
26
|
+
➡️ <your recommended answer>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
❓ **Q2** - **<title>**: <body>
|
|
31
|
+
|
|
32
|
+
➡️ <your recommended answer>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Each round of answers reshapes the tree. Recompute the frontier. A question that depends on another still open in this round belongs to a _later_ round.
|
|
36
|
+
|
|
37
|
+
## Facts vs decisions
|
|
38
|
+
|
|
39
|
+
Finding _facts_ is your job. When a frontier question needs something from the repo, look it up (nearest `AGENTS.md`) — don't ask the user for anything you can read. A running lookup is an unsettled prerequisite: ask the rest of the frontier now.
|
|
40
|
+
|
|
41
|
+
The _decisions_ are the user's. Put each to them and wait.
|
|
42
|
+
|
|
43
|
+
Push back when a simpler approach fits. Name blockers instead of designing around them. Recommend the project's default (nearest `AGENTS.md`: surgical, inline, no new files) unless the user has a reason not to.
|
|
44
|
+
|
|
45
|
+
## Done
|
|
46
|
+
|
|
47
|
+
The frontier is empty, every branch has been visited, and the user has confirmed
|
|
48
|
+
the final reading. Nothing is silently assumed. Do not implement before this
|
|
49
|
+
confirmation.
|
|
50
|
+
|
|
51
|
+
After confirmation:
|
|
52
|
+
|
|
53
|
+
- In Plan mode, continue with the agent's native planning flow.
|
|
54
|
+
- If the user asked for a plan in a normal mode, load the `plan` skill and write
|
|
55
|
+
it in the same chat.
|
|
56
|
+
- Otherwise, let the user choose the next step. A small task may continue
|
|
57
|
+
directly to implementation.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: handoff
|
|
3
|
+
description: >-
|
|
4
|
+
Save AGENTFLOW context to disk before continuing in a fresh chat.
|
|
5
|
+
license: MIT
|
|
6
|
+
disable-model-invocation: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Handoff
|
|
10
|
+
|
|
11
|
+
Use this after an implementation PR when the next slice will continue in a
|
|
12
|
+
fresh chat. Write a file the next chat can `@`-attach. The handoff + plan are
|
|
13
|
+
the durable source of truth.
|
|
14
|
+
|
|
15
|
+
Loop: the repo-root loop doc (`README.md` or `AGENTFLOW.md`).
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
1. **Find the plan.** Path the user named, the last handoff, or the plan in this
|
|
20
|
+
thread.
|
|
21
|
+
2. **This PR.** What shipped (paths, commit hash if any). Done-when met or not.
|
|
22
|
+
3. **Bleed.** Work that belongs to a later plan row but landed, or should have.
|
|
23
|
+
Fold it into the plan: check off this PR, rewrite later rows. Do not leave
|
|
24
|
+
the next chat to discover leftover files.
|
|
25
|
+
4. **Next PR.** Title + done-when from the updated plan. One row only.
|
|
26
|
+
5. **Write** `.agentflow/<slug>/handoff.md` (create dirs). Reuse the same slug
|
|
27
|
+
for the feature so the next chat overwrites this file. Not OS temp.
|
|
28
|
+
6. **Print** the path and what the fresh chat should attach.
|
|
29
|
+
|
|
30
|
+
Redact secrets. Do not paste diffs or OpenSpec bodies — point at paths.
|
|
31
|
+
|
|
32
|
+
If the user passed a focus, that is the next chat’s Next PR.
|
|
33
|
+
|
|
34
|
+
## File
|
|
35
|
+
|
|
36
|
+
```markdown
|
|
37
|
+
# Handoff: <slug>
|
|
38
|
+
|
|
39
|
+
> Next chat: read this file and the plan. Implement **only** Next PR.
|
|
40
|
+
> Do not resurrect discarded approaches from Cursor summarize.
|
|
41
|
+
|
|
42
|
+
## Plan
|
|
43
|
+
|
|
44
|
+
path: <file the next chat can open>
|
|
45
|
+
sync: updated | unchanged
|
|
46
|
+
|
|
47
|
+
## This PR
|
|
48
|
+
|
|
49
|
+
<title>
|
|
50
|
+
done when: <criterion> — met | not met
|
|
51
|
+
shipped: <paths, commit if any>
|
|
52
|
+
|
|
53
|
+
## Bleed
|
|
54
|
+
|
|
55
|
+
<what leaked into / out of later PRs, and how the plan changed>
|
|
56
|
+
none
|
|
57
|
+
|
|
58
|
+
## Next PR
|
|
59
|
+
|
|
60
|
+
<title>
|
|
61
|
+
done when: <criterion>
|
|
62
|
+
suggested: tdd / code-review / craft skills for the next slice
|
|
63
|
+
```
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan
|
|
3
|
+
description: >-
|
|
4
|
+
Use when the user asks to write or update an implementation plan split into
|
|
5
|
+
PR-sized slices after decisions are settled. Not the interview (grill) and
|
|
6
|
+
not the end-of-PR baton (handoff).
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Plan
|
|
11
|
+
|
|
12
|
+
Write **one** plan that another developer or agent can pick up. Do not implement. Do not write a handoff.
|
|
13
|
+
|
|
14
|
+
If important decisions are still open, load `grill` first. Resume the plan after
|
|
15
|
+
the user confirms the final reading. One-line or obvious scope can skip the
|
|
16
|
+
plan.
|
|
17
|
+
|
|
18
|
+
## Where
|
|
19
|
+
|
|
20
|
+
- **Plan mode:** plan artifact / reply only. No `.agentflow/` files.
|
|
21
|
+
- **Agent mode:** `.agentflow/<slug>/plan.md` (kebab-case from the feature; reuse the slug). Print the path.
|
|
22
|
+
- Another machine or person: OpenSpec or a `*.md` on the branch.
|
|
23
|
+
|
|
24
|
+
## Shape
|
|
25
|
+
|
|
26
|
+
```markdown
|
|
27
|
+
# <Feature>
|
|
28
|
+
|
|
29
|
+
**Goal:** one sentence
|
|
30
|
+
**Approach:** 2–3 sentences — the chosen reading
|
|
31
|
+
**Reuse:** existing APIs this plan calls (`path`)
|
|
32
|
+
|
|
33
|
+
## Files
|
|
34
|
+
|
|
35
|
+
- `path` — what changes (or `create`)
|
|
36
|
+
|
|
37
|
+
- [ ] PR 1 — <title>
|
|
38
|
+
done when: <observable>
|
|
39
|
+
verify: <command>
|
|
40
|
+
|
|
41
|
+
- [ ] PR 2 — …
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Each row is the smallest complete, independently shippable change with one
|
|
45
|
+
coherent outcome and one verification story. Order rows from foundations to
|
|
46
|
+
their consumers.
|
|
47
|
+
|
|
48
|
+
## Slicing
|
|
49
|
+
|
|
50
|
+
Common shapes:
|
|
51
|
+
|
|
52
|
+
- **Full-stack feature:** behavior-preserving refactoring, then shared types and
|
|
53
|
+
backend, then frontend integration.
|
|
54
|
+
- **Frontend feature:** reusable components, then shared runtime or wiring, then
|
|
55
|
+
user-facing interfaces.
|
|
56
|
+
- **Small feature:** one complete vertical slice.
|
|
57
|
+
|
|
58
|
+
Give a foundation its own row when it is independently useful. Keep
|
|
59
|
+
feature-local components and wiring with their first consumer. Split distinct
|
|
60
|
+
outcomes or architectural decisions that can ship separately.
|
|
61
|
+
|
|
62
|
+
## Rules
|
|
63
|
+
|
|
64
|
+
- Ground every row in files you read. Cite a path → it exists, or mark `create`.
|
|
65
|
+
- One approach. No menu, no TBD, no “handle edge cases”, no “similar to PR n”.
|
|
66
|
+
- File map before rows. Reuse before create.
|
|
67
|
+
- Tight: settled model + rows. Cut prose, keep paths.
|
|
68
|
+
- `done when` is observable. `verify` is the command the implementer runs.
|
|
69
|
+
|
|
70
|
+
## Self-check
|
|
71
|
+
|
|
72
|
+
1. Every settled decision has a row or is named out of scope.
|
|
73
|
+
2. Every path exists or is marked `create`.
|
|
74
|
+
3. No placeholders.
|
|
75
|
+
4. Another developer or agent can open the file and ship PR 1 without this conversation.
|
|
76
|
+
5. Every row leaves the repository working and does not need the next row to
|
|
77
|
+
justify its code.
|
|
78
|
+
|
|
79
|
+
## Output
|
|
80
|
+
|
|
81
|
+
Print the path. The implementer starts with the first unchecked row, in this context or a new one.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: research
|
|
3
|
+
description: >-
|
|
4
|
+
Research a codebase, feature, technology, or external landscape and save a
|
|
5
|
+
reusable map under .agentflow/<feature>/research/. Use when later work should
|
|
6
|
+
not repeat the same investigation. Not for a one-file lookup.
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Research
|
|
11
|
+
|
|
12
|
+
Learn how an area works and leave a map that another chat can use without
|
|
13
|
+
repeating the investigation. Research only. Do not plan or implement the change.
|
|
14
|
+
|
|
15
|
+
The user can ask for research in a regular prompt. Use this skill when the
|
|
16
|
+
findings should survive context compaction or move to another chat.
|
|
17
|
+
|
|
18
|
+
## Output
|
|
19
|
+
|
|
20
|
+
Write to:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
.agentflow/<slug>/research/
|
|
24
|
+
├── index.md
|
|
25
|
+
└── <topic>.md
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Use a short kebab-case slug for the feature or question. Reuse the directory
|
|
29
|
+
when continuing the same research.
|
|
30
|
+
|
|
31
|
+
`index.md` is a short map, not the full report. It contains:
|
|
32
|
+
|
|
33
|
+
- the question and scope
|
|
34
|
+
- a short summary
|
|
35
|
+
- links to every topic page
|
|
36
|
+
- the important files, modules, or sources
|
|
37
|
+
- open questions that research could not answer
|
|
38
|
+
|
|
39
|
+
Split detailed findings by domain, module, flow, or independent question. Each
|
|
40
|
+
topic page owns one coherent area and must make sense when attached to a chat on
|
|
41
|
+
its own. If a later chat could use one part without the others, put that part in
|
|
42
|
+
its own file.
|
|
43
|
+
|
|
44
|
+
Keep a narrow investigation in `index.md` instead of creating empty pages. For
|
|
45
|
+
larger research, keep details out of `index.md` and link to the topic pages.
|
|
46
|
+
|
|
47
|
+
Write the notes in the user's language.
|
|
48
|
+
|
|
49
|
+
## Research the code
|
|
50
|
+
|
|
51
|
+
When the question is about an existing project:
|
|
52
|
+
|
|
53
|
+
1. Read the nearest `AGENTS.md` and the repository's exploration rules.
|
|
54
|
+
2. Try the current behavior when possible.
|
|
55
|
+
3. Find the entry points, public interfaces, modules, and data flow.
|
|
56
|
+
4. Follow the important path end to end. Verify call graph guesses with text
|
|
57
|
+
search.
|
|
58
|
+
5. Record exact file paths and symbol names with one line explaining each role.
|
|
59
|
+
6. Capture constraints, gotchas, and existing APIs that later work should reuse.
|
|
60
|
+
|
|
61
|
+
Do not dump search results or source code. Explain how the pieces connect.
|
|
62
|
+
Separate what you observed from what you inferred.
|
|
63
|
+
|
|
64
|
+
## Research a technology
|
|
65
|
+
|
|
66
|
+
When the question is about a library, platform, or approach:
|
|
67
|
+
|
|
68
|
+
1. Check the version and setup used by the project.
|
|
69
|
+
2. Search the web and read current primary documentation and official sources.
|
|
70
|
+
3. Find the APIs and constraints relevant to the question.
|
|
71
|
+
4. Connect the findings back to the project's files and current architecture.
|
|
72
|
+
5. Record dated source links.
|
|
73
|
+
|
|
74
|
+
The result should explain what applies here, not summarize the entire
|
|
75
|
+
technology.
|
|
76
|
+
|
|
77
|
+
## Research a landscape
|
|
78
|
+
|
|
79
|
+
When comparing tools, competitors, or options:
|
|
80
|
+
|
|
81
|
+
1. State the question and the filter used to keep or reject candidates.
|
|
82
|
+
2. Record what the project already has.
|
|
83
|
+
3. Open current primary sources and date the snapshot.
|
|
84
|
+
4. Explain why each kept option fits the question.
|
|
85
|
+
5. Name rejected options and the reason.
|
|
86
|
+
6. End with ranked next moves when the user asked what to adopt or build.
|
|
87
|
+
|
|
88
|
+
A catalog dump is not research. Every item must help answer the question.
|
|
89
|
+
|
|
90
|
+
## Suggested page shape
|
|
91
|
+
|
|
92
|
+
```markdown
|
|
93
|
+
# <Topic>
|
|
94
|
+
|
|
95
|
+
## Summary
|
|
96
|
+
|
|
97
|
+
<What a later chat needs to know>
|
|
98
|
+
|
|
99
|
+
## How it works
|
|
100
|
+
|
|
101
|
+
<Behavior or flow>
|
|
102
|
+
|
|
103
|
+
## Relevant files and modules
|
|
104
|
+
|
|
105
|
+
- `path/to/file.ts` — role
|
|
106
|
+
|
|
107
|
+
## Constraints
|
|
108
|
+
|
|
109
|
+
- <constraint or gotcha>
|
|
110
|
+
|
|
111
|
+
## Sources
|
|
112
|
+
|
|
113
|
+
- <dated primary source>
|
|
114
|
+
|
|
115
|
+
## Open questions
|
|
116
|
+
|
|
117
|
+
- <unanswered question>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use only the sections that fit the topic.
|
|
121
|
+
|
|
122
|
+
## Done
|
|
123
|
+
|
|
124
|
+
Research is complete when a fresh chat can answer these questions from the
|
|
125
|
+
notes:
|
|
126
|
+
|
|
127
|
+
1. How does this area work?
|
|
128
|
+
2. Where are the important files, modules, or sources?
|
|
129
|
+
3. What constraints will affect the work?
|
|
130
|
+
4. What is still unknown?
|
|
131
|
+
|
|
132
|
+
Print the path to `index.md` and a short takeaway. Do not paste the full notes
|
|
133
|
+
into the reply.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd
|
|
3
|
+
description: >-
|
|
4
|
+
Use when the user wants to build features or fix bugs test-first,
|
|
5
|
+
mentions "red-green-refactor", or wants integration tests.
|
|
6
|
+
license: MIT
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Test-Driven Development
|
|
10
|
+
|
|
11
|
+
TDD is the red → green loop. This skill is the reference that makes that loop produce tests worth keeping: what a good test is, where tests go, the anti-patterns, and the rules of the loop. Every section applies on every cycle — consult them before and during the loop, not after.
|
|
12
|
+
|
|
13
|
+
When exploring the codebase, read the nearest `AGENTS.md` so test names and interface vocabulary match the area's language. Explore per root `AGENTS.md`.
|
|
14
|
+
|
|
15
|
+
## What a good test is
|
|
16
|
+
|
|
17
|
+
Tests verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't. A good test reads like a specification — "user can checkout with valid cart" tells you exactly what capability exists — and survives refactors because it doesn't care about internal structure.
|
|
18
|
+
|
|
19
|
+
See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
|
|
20
|
+
|
|
21
|
+
## Seams — where tests go
|
|
22
|
+
|
|
23
|
+
A **seam** is the public boundary you test at: the interface where you observe behavior without reaching inside. Tests live at seams, never against internals.
|
|
24
|
+
|
|
25
|
+
**Test only at pre-agreed seams.** Before writing any test, write down the seams under test and confirm them with the user. No test is written at an unconfirmed seam. You can't test everything — agreeing the seams up front is how testing effort lands on the critical paths and complex logic instead of every edge case.
|
|
26
|
+
|
|
27
|
+
Ask: "What's the public interface, and which seams should we test?"
|
|
28
|
+
|
|
29
|
+
Root `AGENTS.md` wins: do not add a file, port, or wrapper so a test can reach inside. One real adapter is not a seam — two (production + test, or two backends) is. Don't invent `IFooService` for an in-process collaborator you own.
|
|
30
|
+
|
|
31
|
+
## Anti-patterns
|
|
32
|
+
|
|
33
|
+
- **Implementation-coupled** — mocks internal collaborators, tests private methods, or verifies through a side channel (querying the database instead of using the interface). The tell: the test breaks when you refactor but behavior hasn't changed.
|
|
34
|
+
- **Tautological** — the assertion recomputes the expected value the way the code does (`expect(add(a, b)).toBe(a + b)`, a snapshot derived by hand the same way, a constant asserted equal to itself), so it passes by construction and can never disagree with the code. Expected values must come from an independent source of truth — a known-good literal, a worked example, the spec.
|
|
35
|
+
- **Horizontal slicing** — writing all tests first, then all implementation. Bulk tests verify _imagined_ behavior: you test the _shape_ of things rather than user-facing behavior, the tests go insensitive to real changes, and you commit to test structure before understanding the implementation. Work in **vertical slices** instead — one test → one implementation → repeat, each test a **tracer bullet** that responds to what the last cycle taught you.
|
|
36
|
+
|
|
37
|
+
## Rules of the loop
|
|
38
|
+
|
|
39
|
+
- **Red before green.** Write the failing test first, then only enough code to pass it. Don't anticipate future tests or add speculative features.
|
|
40
|
+
- **One slice at a time.** One seam, one test, one minimal implementation per cycle.
|
|
41
|
+
- **Watch the color.** Red is not “wrote a test”. Run it. Confirm it fails because the behavior is missing — not a typo, import error, or existing pass. Passes immediately → you tested existing behavior; fix the test. Errors → fix the harness until it fails correctly, then implement. Green: run the same command; claim pass only from that output.
|
|
42
|
+
- **Name the break.** Before the test body, name the production change that should make it fail. Cannot name one → wrong seam.
|
|
43
|
+
- **Refactoring is not part of the loop.** It belongs to the review stage (see the `code-review` skill), not the red → green implementation cycle.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# When to Mock
|
|
2
|
+
|
|
3
|
+
Mock at **system boundaries** only:
|
|
4
|
+
|
|
5
|
+
- External APIs (payment, email, third-party)
|
|
6
|
+
- Time / randomness
|
|
7
|
+
- Databases and filesystems — sometimes; prefer a real test DB / tmp dir when cheap
|
|
8
|
+
|
|
9
|
+
Don't mock:
|
|
10
|
+
|
|
11
|
+
- Your own classes / modules
|
|
12
|
+
- Internal collaborators
|
|
13
|
+
- Anything you control
|
|
14
|
+
|
|
15
|
+
Use the project's test runner (nearest `AGENTS.md`). Vitest: `vi.fn`, `vi.mock`.
|
|
16
|
+
|
|
17
|
+
## Designing for mockability
|
|
18
|
+
|
|
19
|
+
**1. Pass the boundary in** — only when a second adapter is real (production + test, or two backends).
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// Easy to mock
|
|
23
|
+
function processPayment(order: Order, paymentClient: PaymentClient) {
|
|
24
|
+
return paymentClient.charge(order.total);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Hard to mock
|
|
28
|
+
function processPayment(order: Order) {
|
|
29
|
+
const client = new StripeClient(process.env.STRIPE_KEY);
|
|
30
|
+
return client.charge(order.total);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Don't invent `IFoo` for an in-process collaborator you own just so a test can mock it.
|
|
35
|
+
|
|
36
|
+
**2. Prefer SDK-style interfaces over a generic fetcher**
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// GOOD: each function is independently mockable
|
|
40
|
+
const api = {
|
|
41
|
+
getUser: (id: string) => fetch(`/users/${id}`),
|
|
42
|
+
createOrder: (data: OrderInput) =>
|
|
43
|
+
fetch("/orders", { method: "POST", body: JSON.stringify(data) }),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// BAD: mock needs conditional logic
|
|
47
|
+
const api = {
|
|
48
|
+
fetch: (endpoint: string, options?: RequestInit) => fetch(endpoint, options),
|
|
49
|
+
};
|
|
50
|
+
```
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Good and Bad Tests
|
|
2
|
+
|
|
3
|
+
## Good tests
|
|
4
|
+
|
|
5
|
+
Test through real interfaces, not mocks of internal parts.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// GOOD: observable behavior
|
|
9
|
+
it("should confirm checkout for a valid cart", async () => {
|
|
10
|
+
const cart = createCart();
|
|
11
|
+
cart.add(product);
|
|
12
|
+
const result = await checkout(cart, paymentMethod);
|
|
13
|
+
expect(result.status).toBe("confirmed");
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- Behavior callers care about
|
|
18
|
+
- Public API only
|
|
19
|
+
- Survives internal refactors
|
|
20
|
+
- Describes WHAT, not HOW
|
|
21
|
+
- One logical assertion per test
|
|
22
|
+
|
|
23
|
+
## Bad tests
|
|
24
|
+
|
|
25
|
+
**Implementation-detail** — coupled to internal structure.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// BAD
|
|
29
|
+
it("should call paymentService.process", async () => {
|
|
30
|
+
const process = vi.fn();
|
|
31
|
+
await checkout(cart, { process });
|
|
32
|
+
expect(process).toHaveBeenCalledWith(cart.total);
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Red flags: mocking internal collaborators, private methods, call counts/order, test name describes HOW, verifying through a side channel instead of the interface.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// BAD: bypasses interface
|
|
40
|
+
it("should save the user to the database", async () => {
|
|
41
|
+
await createUser({ name: "Alice" });
|
|
42
|
+
const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
|
|
43
|
+
expect(row).toBeDefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// GOOD: verifies through interface
|
|
47
|
+
it("should make a created user retrievable", async () => {
|
|
48
|
+
const user = await createUser({ name: "Alice" });
|
|
49
|
+
const retrieved = await getUser(user.id);
|
|
50
|
+
expect(retrieved.name).toBe("Alice");
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Tautological** — expected value restates the implementation.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// BAD
|
|
58
|
+
it("should sum line items", () => {
|
|
59
|
+
const items = [{ price: 10 }, { price: 5 }];
|
|
60
|
+
const expected = items.reduce((sum, i) => sum + i.price, 0);
|
|
61
|
+
expect(calculateTotal(items)).toBe(expected);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// GOOD: independent literal
|
|
65
|
+
it("should sum line items", () => {
|
|
66
|
+
expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Low-value mapping tests** — asserting `.map` / array shape with no behavior. Skip or drop them rather than warping production code for reachability (root `AGENTS.md`).
|
|
71
|
+
|
|
72
|
+
## Mutation check
|
|
73
|
+
|
|
74
|
+
A realistic production mutation (wrong branch, missing side effect, empty return) should fail at least one test. Nothing fails → tautological or untested.
|