@reforma/agentflow 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTFLOW.md ADDED
@@ -0,0 +1,85 @@
1
+ # AgentFlow
2
+
3
+ Use this loop for changes larger than an obvious quick fix:
4
+
5
+ ```text
6
+ Research → Grill → Plan → PR → Review → Commit
7
+ ↑ │
8
+ └──── Next PR ─────┘
9
+ ```
10
+
11
+ Research is optional. Small tasks can skip the plan. Write a handoff only when
12
+ the work moves to a fresh context.
13
+
14
+ ## 1. Learn how it works
15
+
16
+ Explore the existing feature, code, or technology before proposing a change.
17
+ Use `/research` when the findings must survive the current chat. Save reusable
18
+ findings under `.agentflow/<feature>/research/`.
19
+
20
+ Skip this step when the area and required change are already clear.
21
+
22
+ ## 2. Sharpen the task
23
+
24
+ Run `/grill`. List assumptions, unresolved decisions, what is in and out of the
25
+ task, and how the result will be checked.
26
+
27
+ Continue until no open decision can change how the work will be done. Then
28
+ proceed directly for a small task or load `/plan` for larger work.
29
+
30
+ ## 3. Plan PR-sized slices
31
+
32
+ Save the plan to `.agentflow/<feature>/plan.md`.
33
+
34
+ Each row is the smallest complete, independently shippable change that does one
35
+ useful thing and has one clear way to test it. Put lower-level pieces before the
36
+ code that uses them.
37
+
38
+ Common shapes:
39
+
40
+ - Full-stack feature: behavior-preserving refactoring, then shared types and
41
+ backend, then frontend integration.
42
+ - Frontend feature: reusable components, then shared runtime or wiring, then
43
+ user-facing interfaces.
44
+ - Small feature: one complete vertical slice.
45
+
46
+ Give a foundation its own row when it is independently useful. Keep
47
+ feature-local components and wiring with their first consumer. Split distinct
48
+ outcomes or architectural decisions that can ship separately.
49
+
50
+ Keep the plan current as implementation changes. Record scope changes and new
51
+ decisions in the plan instead of letting the active PR grow silently.
52
+
53
+ ## 4. Implement one PR
54
+
55
+ Implement the first unchecked row and nothing beyond it. Without a plan, keep
56
+ the change small enough to review as one useful result.
57
+
58
+ Load the relevant project skills. Use `/tdd` when the work should proceed
59
+ test-first.
60
+
61
+ Verify the slice using the checks named in the plan. Check its row only after
62
+ the implementation and verification are complete, then update later rows to
63
+ match what remains.
64
+
65
+ ## 5. Review
66
+
67
+ Run `/code-review` against the completed slice. Fix local findings and reduce
68
+ the change when the review verdict requires it.
69
+
70
+ Read the final diff yourself after the agent review.
71
+
72
+ ## 6. Commit
73
+
74
+ Commit the verified slice using the project's normal workflow. Then return to
75
+ the first unchecked row.
76
+
77
+ ## 7. Refresh context when needed
78
+
79
+ Continue in the current chat while its context remains useful. When it becomes
80
+ noisy, summarize it or start a fresh chat.
81
+
82
+ Run `/handoff` before moving unfinished work to a fresh context. Save the result
83
+ to `.agentflow/<feature>/handoff.md` with what shipped, what changed, and which
84
+ PR comes next. Bring the plan and handoff into the new context, then resume from
85
+ the first unchecked row.
package/README.md CHANGED
@@ -58,7 +58,12 @@ Research helps when the agent does not know the area, and most larger tasks also
58
58
 
59
59
  Repeat steps 4–7 until every PR in the plan is complete.
60
60
 
61
- ### Step 1. 🔍 Learn how it works (optional)
61
+ AgentFlow keeps research, plans, handoffs, and local setup state under
62
+ `.agentflow/`. Version control is your choice: ignore the directory for a
63
+ private workflow, or commit it to share the work with your team like an
64
+ OpenSpec workspace.
65
+
66
+ ### 🔍 Step 1. Learn how it works (optional)
62
67
 
63
68
  Research does not require a skill. Even a simple prompt like "Find out how
64
69
  authentication works in this project" can make the implementation much easier.
@@ -73,7 +78,7 @@ context compaction or in a new chat.
73
78
 
74
79
  Even if you skip this step, Grill will fill in any gaps.
75
80
 
76
- ### Step 2. 🔥 Sharpen the idea with Grill
81
+ ### 🔥 Step 2. Sharpen the idea with Grill
77
82
 
78
83
  Grill is the core of AgentFlow and the one step used for the most tasks.
79
84
 
@@ -84,10 +89,9 @@ decision points, and recommends an answer for each one. You confirm or correct
84
89
  that understanding. If your answers open new questions, the agent continues
85
90
  until nothing important is left unclear.
86
91
 
87
- This gives the implementation an agreed starting point instead of leaving the
88
- agent to fill in gaps on its own.
92
+ Now the agent does not have to fill in the gaps while coding.
89
93
 
90
- ### Step 3. 🗂️ Plan and slice the work into PRs
94
+ ### 🗂️ Step 3. Plan and slice the work into PRs
91
95
 
92
96
  For a larger task, planning usually follows Grill in the same chat. If you asked
93
97
  Grill to plan next, it loads `/plan` automatically. You can also run `/plan`
@@ -98,14 +102,14 @@ Native Plan mode, the agent uses its built-in planning flow and native plan
98
102
  artifact instead.
99
103
 
100
104
  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.
105
+ smallest complete change that does one useful thing and has a clear way to check
106
+ it, not a fixed number of files or lines.
103
107
 
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.
108
+ Larger work usually starts with lower-level pieces and moves to the code that
109
+ uses them. A full-stack feature might start with behavior-preserving
110
+ refactoring, continue with shared types and backend work, and finish with the
111
+ frontend. A frontend feature might move from reusable components, through
112
+ shared runtime or wiring, to the user-facing interface.
109
113
 
110
114
  These are examples, not a required template. A small feature stays as one
111
115
  vertical slice. Feature-local components and wiring stay with the interface
@@ -113,7 +117,7 @@ that first uses them instead of becoming placeholder PRs.
113
117
 
114
118
  Keep the plan updated as the work changes.
115
119
 
116
- ### Step 4. 🛠️ Implement one PR
120
+ ### 🛠️ Step 4. Implement one PR
117
121
 
118
122
  Implement the first unchecked PR and nothing beyond it. Without a plan, keep the
119
123
  change small enough to review. You can stay in the current chat, start a fresh
@@ -123,20 +127,20 @@ to another chat. Use `/tdd` for test-first work.
123
127
  Load any relevant skills named in `AGENTS.md`. If work spills into a later PR,
124
128
  update the plan instead of silently expanding the current one.
125
129
 
126
- Before review, update `plan.md`: check the completed PR only after its
127
- done-when and verification pass, then apply any scope changes to later rows.
130
+ Before review, update `plan.md`: check the completed PR only after its checks
131
+ pass, then apply any scope changes to later rows.
128
132
 
129
- ### Step 5. 🤖 Agent review
133
+ ### 🤖 Step 5. Agent review
130
134
 
131
135
  Run `/code-review` on the completed PR. It looks for code to reuse, unnecessary
132
136
  wrappers, and avoidable complexity. It fixes small local problems and returns a
133
137
  verdict: keep, shrink, or burn.
134
138
 
135
- ### Step 6. Your review, then commit
139
+ ### Step 6. Your review, then commit
136
140
 
137
141
  Read the diff yourself, then commit it using the project's normal workflow.
138
142
 
139
- ### Step 7. 🔄 Refresh the context when needed
143
+ ### 🔄 Step 7. Refresh the context when needed
140
144
 
141
145
  Keep the current chat if it still has useful context. When it gets noisy,
142
146
  summarize it or start a fresh one. Run `/handoff` to save what shipped, what
@@ -152,9 +156,22 @@ Install the complete workflow in a project:
152
156
  npx @reforma/agentflow init
153
157
  ```
154
158
 
155
- The command installs every AgentFlow skill, writes `AGENTFLOW.md`, and ignores
156
- the local `.agentflow/` artifacts. It prints a short pointer you can add to
157
- `AGENTS.md` when you want agents in that project to follow the full loop.
159
+ AgentFlow first asks which parts to install: the skills, the concise
160
+ `AGENTFLOW.md` loop, and a one-line pointer in `AGENTS.md`. When you choose the
161
+ skills, the `skills` CLI handles the skill picker, target agents, project or
162
+ global scope, and installation method.
163
+
164
+ Use its standard AgentFlow flags when the destination is already known:
165
+
166
+ ```bash
167
+ npx @reforma/agentflow init --global --agent cursor
168
+ ```
169
+
170
+ For automation, accept every default without prompts:
171
+
172
+ ```bash
173
+ npx @reforma/agentflow init --yes
174
+ ```
158
175
 
159
176
  Install only one skill without the workflow:
160
177
 
@@ -168,6 +185,10 @@ Update the complete workflow:
168
185
  npx @reforma/agentflow@latest update
169
186
  ```
170
187
 
188
+ Updates are non-interactive after setup and refresh only the AgentFlow parts
189
+ selected during initialization. In a project without AgentFlow, `update`
190
+ starts the same setup as `init`.
191
+
171
192
  Or update one independently installed skill:
172
193
 
173
194
  ```bash
@@ -176,24 +197,7 @@ npx skills update plan
176
197
 
177
198
  ---
178
199
 
179
- ## 💡 Why AgentFlow?
180
-
181
- Coding agents can handle large changes, but chat history is a poor place to
182
- keep requirements, scope, and decisions. AgentFlow moves that information into
183
- small files and gives each PR a clear boundary.
184
-
185
- - **Learn the ground before estimating the work** — the agent sees the existing
186
- behavior and code before discussing a solution.
187
- - **Resolve decisions before coding** — `/grill` exposes assumptions while they
188
- are still cheap to change.
189
- - **Keep changes reviewable** — the plan divides a large feature into PR-sized
190
- slices that are implemented and reviewed in order.
191
- - **Preserve context between sessions** — the review artifacts, plan and handoff tell the next agent what is true and what comes next.
192
- - **Use an opinionated workflow without heavy artifacts** — the sequence is
193
- fixed, while the documentation is limited to one plan and one current
194
- handoff.
195
-
196
- ### ⚖️ How we compare
200
+ ## ⚖️ How we compare
197
201
 
198
202
  OpenSpec and Spec Kit try to cover most of spec-driven development with their
199
203
  own commands, templates, and artifacts. AgentFlow does not try to be an
@@ -212,9 +216,9 @@ task lists. AgentFlow gives you an order of work without asking you to move the
212
216
  rest of your development process into the framework.
213
217
 
214
218
  **vs. an unstructured chat** — Working directly in chat is enough for a small
215
- fix. On larger changes, AgentFlow keeps decisions out of transient history,
216
- limits scope to one reviewable slice, and gives the next session a reliable
217
- starting point.
219
+ fix. On larger changes, AgentFlow keeps decisions out of chat history, limits
220
+ scope to one reviewable slice, and gives the next session enough context to
221
+ continue.
218
222
 
219
223
  ## 🚀 Releasing
220
224
 
package/bin/agentflow.js CHANGED
@@ -1,20 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
+ import { cancel, isCancel, multiselect } from "@clack/prompts";
4
5
  import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
5
- import { dirname as dirname2, resolve as resolve3 } from "node:path";
6
+ import { dirname as dirname3, resolve as resolve3 } from "node:path";
6
7
  import { fileURLToPath } from "node:url";
7
8
 
8
9
  // src/project.ts
9
10
  import {
10
11
  existsSync,
12
+ mkdirSync,
11
13
  readFileSync,
12
14
  renameSync,
13
15
  writeFileSync
14
16
  } from "node:fs";
15
- import { resolve } from "node:path";
17
+ import { dirname, resolve } from "node:path";
16
18
  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.";
19
+ var AGENTS_POINTER = "Check @AGENTFLOW.md to follow the development workflow.";
20
+ var SETUP_COMPONENTS = ["skills", "workflow", "agents"];
18
21
  function generatedWorkflow(workflow) {
19
22
  return `${GENERATED_MARKER}
20
23
 
@@ -25,21 +28,37 @@ function isGeneratedWorkflow(path) {
25
28
  return existsSync(path) && readFileSync(path, "utf8").startsWith(GENERATED_MARKER);
26
29
  }
27
30
  function writeAtomically(path, contents) {
31
+ mkdirSync(dirname(path), { recursive: true });
28
32
  const temporaryPath = `${path}.agentflow-tmp`;
29
33
  writeFileSync(temporaryPath, contents);
30
34
  renameSync(temporaryPath, path);
31
35
  }
32
- function ensureIgnored(cwd) {
33
- const path = resolve(cwd, ".gitignore");
36
+ function ensureAgentsPointer(cwd) {
37
+ const path = resolve(cwd, "AGENTS.md");
34
38
  const current = existsSync(path) ? readFileSync(path, "utf8") : "";
35
- const alreadyIgnored = current.split(/\r?\n/).some((line) => /^\/?\.agentflow\/?$/.test(line.trim()));
36
- if (alreadyIgnored) {
39
+ if (current.split(/\r?\n/).some((line) => line.trim() === AGENTS_POINTER)) {
37
40
  return;
38
41
  }
39
42
  const separator = current.length === 0 || current.endsWith(`
40
43
  `) ? "" : `
41
44
  `;
42
- writeAtomically(path, `${current}${separator}.agentflow/
45
+ writeAtomically(path, `${current}${separator}${AGENTS_POINTER}
46
+ `);
47
+ }
48
+ function readSetupConfig(cwd) {
49
+ const path = resolve(cwd, ".agentflow/config.json");
50
+ if (!existsSync(path)) {
51
+ return null;
52
+ }
53
+ const config = JSON.parse(readFileSync(path, "utf8"));
54
+ if (config.version !== 1 || !Array.isArray(config.components) || config.components.some((component) => !SETUP_COMPONENTS.includes(component))) {
55
+ throw new Error(".agentflow/config.json is not a valid AgentFlow config.");
56
+ }
57
+ return config;
58
+ }
59
+ function writeSetupConfig(cwd, components) {
60
+ const config = { version: 1, components };
61
+ writeAtomically(resolve(cwd, ".agentflow/config.json"), `${JSON.stringify(config, null, 2)}
43
62
  `);
44
63
  }
45
64
 
@@ -47,7 +66,7 @@ function ensureIgnored(cwd) {
47
66
  import { spawnSync } from "node:child_process";
48
67
  import { readFileSync as readFileSync2 } from "node:fs";
49
68
  import { createRequire } from "node:module";
50
- import { dirname, resolve as resolve2 } from "node:path";
69
+ import { dirname as dirname2, resolve as resolve2 } from "node:path";
51
70
  var AGENTFLOW_SKILLS = [
52
71
  "research",
53
72
  "grill",
@@ -65,7 +84,7 @@ var runSkills = (args) => {
65
84
  if (!relativeBin) {
66
85
  throw new Error("The installed skills package does not expose a skills bin.");
67
86
  }
68
- const result = spawnSync(process.execPath, [resolve2(dirname(packagePath), relativeBin), ...args], { stdio: "inherit" });
87
+ const result = spawnSync(process.execPath, [resolve2(dirname2(packagePath), relativeBin), ...args], { stdio: "inherit" });
69
88
  if (result.error) {
70
89
  throw result.error;
71
90
  }
@@ -73,7 +92,7 @@ var runSkills = (args) => {
73
92
  };
74
93
 
75
94
  // src/cli.ts
76
- var packageRoot = resolve3(dirname2(fileURLToPath(import.meta.url)), "..");
95
+ var packageRoot = resolve3(dirname3(fileURLToPath(import.meta.url)), "..");
77
96
  var packageJson = JSON.parse(readFileSync3(resolve3(packageRoot, "package.json"), "utf8"));
78
97
  function parseOptions(args, command) {
79
98
  const forwarded = [];
@@ -119,18 +138,36 @@ Usage:
119
138
  agentflow --version
120
139
 
121
140
  Commands:
122
- init Install all AgentFlow skills and write AGENTFLOW.md
123
- update Update installed AgentFlow skills and refresh AGENTFLOW.md
141
+ init Choose and install AgentFlow components
142
+ update Update AgentFlow, installing it first when needed
124
143
 
125
- AgentFlow prints the AGENTS.md snippet for you to add manually.
144
+ The skills CLI handles skill, agent, scope, and installation choices.
126
145
  `;
127
146
  }
128
- function runCli(args, {
147
+ async function promptSetup() {
148
+ const selected = await multiselect({
149
+ message: "What do you want to install?",
150
+ options: [
151
+ { value: "skills", label: "AgentFlow skills" },
152
+ { value: "workflow", label: "AGENTFLOW.md" },
153
+ { value: "agents", label: "AGENTS.md pointer" }
154
+ ],
155
+ initialValues: [...SETUP_COMPONENTS],
156
+ required: true
157
+ });
158
+ if (isCancel(selected)) {
159
+ cancel("AgentFlow setup cancelled.");
160
+ return null;
161
+ }
162
+ return selected;
163
+ }
164
+ async function runCli(args, {
129
165
  cwd = process.cwd(),
130
166
  stdout = process.stdout,
131
167
  stderr = process.stderr,
168
+ promptSetup: selectSetup = promptSetup,
132
169
  runSkills: executeSkills = runSkills,
133
- workflow = readFileSync3(resolve3(packageRoot, "README.md"), "utf8")
170
+ workflow = readFileSync3(resolve3(packageRoot, "AGENTFLOW.md"), "utf8")
134
171
  } = {}) {
135
172
  const [command, ...optionArgs] = args;
136
173
  if (!command || command === "--help" || command === "-h") {
@@ -151,27 +188,42 @@ ${help()}`);
151
188
  try {
152
189
  const forwarded = parseOptions(optionArgs, command);
153
190
  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.");
191
+ const config = readSetupConfig(cwd);
192
+ const legacyInstall = !config && isGeneratedWorkflow(workflowPath);
193
+ const installing = command === "init" || !config && !legacyInstall;
194
+ const assumeDefaults = forwarded.includes("--yes") || forwarded.includes("-y");
195
+ const components = installing ? assumeDefaults ? [...SETUP_COMPONENTS] : await selectSetup() : config?.components ?? ["skills", "workflow"];
196
+ if (!components) {
197
+ return 0;
156
198
  }
157
- if (command === "update" && !existsSync2(workflowPath)) {
158
- throw new Error("AgentFlow is not initialized here. Run `agentflow init`.");
199
+ if (components.includes("workflow") && existsSync2(workflowPath) && !isGeneratedWorkflow(workflowPath)) {
200
+ throw new Error("AGENTFLOW.md is not managed by AgentFlow CLI. Move it or merge it manually before continuing.");
159
201
  }
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}.
202
+ if (components.includes("skills")) {
203
+ const installOptions = forwarded.filter((option) => option !== "--project" && option !== "-p");
204
+ const skillsArgs = installing ? [
205
+ "add",
206
+ SKILLS_SOURCE,
207
+ ...assumeDefaults ? ["--skill", "*"] : [],
208
+ ...installOptions
209
+ ] : ["update", ...AGENTFLOW_SKILLS, ...forwarded];
210
+ const status = executeSkills(skillsArgs);
211
+ if (status !== 0) {
212
+ const operation = installing ? "install" : "update";
213
+ stderr.write(`Agent skills ${operation} failed with exit code ${status}.
164
214
  `);
165
- return status;
215
+ return status;
216
+ }
217
+ }
218
+ if (components.includes("workflow")) {
219
+ writeAtomically(workflowPath, generatedWorkflow(workflow));
166
220
  }
167
- writeAtomically(workflowPath, generatedWorkflow(workflow));
168
- ensureIgnored(cwd);
169
- if (command === "init") {
221
+ if (components.includes("agents")) {
222
+ ensureAgentsPointer(cwd);
223
+ }
224
+ writeSetupConfig(cwd, components);
225
+ if (installing) {
170
226
  stdout.write(`AgentFlow initialized.
171
-
172
- Add this line to AGENTS.md:
173
-
174
- ${AGENTS_SNIPPET}
175
227
  `);
176
228
  } else {
177
229
  stdout.write(`AgentFlow updated.
@@ -186,4 +238,4 @@ ${AGENTS_SNIPPET}
186
238
  }
187
239
 
188
240
  // src/bin.ts
189
- process.exitCode = runCli(process.argv.slice(2));
241
+ process.exitCode = await runCli(process.argv.slice(2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reforma/agentflow",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "An opinionated workflow for shipping production-ready changes with coding agents.",
5
5
  "author": "Reforma, Inc. <dev@reforma.ai>",
6
6
  "type": "module",
@@ -10,12 +10,13 @@
10
10
  "files": [
11
11
  "bin",
12
12
  "skills",
13
+ "AGENTFLOW.md",
13
14
  "README.md",
14
15
  "LICENSE"
15
16
  ],
16
17
  "scripts": {
17
18
  "prepare": "node -e \"if (process.env.CI === 'true' || process.env.SKIP_GIT_HOOKS === '1') process.exit(0); require('child_process').execSync('bunx simple-git-hooks', {stdio:'inherit'})\"",
18
- "build": "bun build src/bin.ts --target=node --outfile=bin/agentflow.js --external skills",
19
+ "build": "bun build src/bin.ts --target=node --outfile=bin/agentflow.js --external skills --external @clack/prompts",
19
20
  "typecheck": "tsc --noEmit",
20
21
  "changeset": "bunx @changesets/cli",
21
22
  "version-packages": "bunx @changesets/cli version",
@@ -48,6 +49,7 @@
48
49
  "access": "public"
49
50
  },
50
51
  "dependencies": {
52
+ "@clack/prompts": "1.7.0",
51
53
  "skills": "1.5.23"
52
54
  },
53
55
  "devDependencies": {