feature-flow-cli 0.1.0__tar.gz → 0.1.1__tar.gz
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.
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/MANIFEST.in +1 -0
- {feature_flow_cli-0.1.0/feature_flow_cli.egg-info → feature_flow_cli-0.1.1}/PKG-INFO +19 -6
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/README.md +18 -5
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/adapters/codex/feature-flow/SKILL.md +12 -8
- feature_flow_cli-0.1.1/agents/feature-planner.md +33 -0
- feature_flow_cli-0.1.1/agents/plan-reviewer.md +16 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/__init__.py +1 -1
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/checks.py +15 -3
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/cli.py +19 -4
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/conductor.py +71 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/prompts.py +28 -2
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1/feature_flow_cli.egg-info}/PKG-INFO +19 -6
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow_cli.egg-info/SOURCES.txt +4 -1
- feature_flow_cli-0.1.1/guides/brief.md +62 -0
- feature_flow_cli-0.1.1/guides/plan-review.md +29 -0
- feature_flow_cli-0.1.1/guides/plan.md +94 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/guides/show.md +1 -1
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/skills/feature-flow/SKILL.md +12 -8
- feature_flow_cli-0.1.0/guides/plan.md +0 -93
- feature_flow_cli-0.1.0/skills/.DS_Store +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/LICENSE +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/adapters/codex/feature-flow/agents/openai.yaml +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/agents/ticket-builder.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/agents/ticket-reviewer.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/__main__.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/command.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/floorguard.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/gate.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/git.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/install.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/state.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/status.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/tickets.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow/view.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow_cli.egg-info/dependency_links.txt +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow_cli.egg-info/entry_points.txt +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow_cli.egg-info/top_level.txt +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/guides/build.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/guides/review.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/guides/templates/commands.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/guides/templates/learnings.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/guides/templates/map.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/guides/templates/spec.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/guides/templates/ticket.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/guides/templates/ui-mockup.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/install.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/install.sh +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/pyproject.toml +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/scripts/floor-guard.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/scripts/flow-status.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/scripts/flow-view.html +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/scripts/flow-view.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/scripts/flow.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/scripts/gate.py +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/setup.cfg +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/skills/architect-review/SKILL.md +0 -0
- {feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/skills/automation-design/SKILL.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: feature-flow-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A ticket-graph workflow for coding agents: installs the feature-flow skill into a repo
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/hamilton-sky/feature-flow
|
|
@@ -45,7 +45,7 @@ python3 install.py /path/to/your/repo --agent all # both, side by side
|
|
|
45
45
|
# `bash install.sh ...` still works on Linux and macOS: it only runs install.py
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
Without a clone,
|
|
48
|
+
Without a clone, the same installer runs from [PyPI](https://pypi.org/project/feature-flow-cli/):
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
uvx feature-flow-cli install /path/to/your/repo --agent all # or: pipx run feature-flow-cli install ...
|
|
@@ -58,12 +58,15 @@ Then, in your repo, in the agent:
|
|
|
58
58
|
| | Claude Code | Codex |
|
|
59
59
|
|---|---|---|
|
|
60
60
|
| Plan a feature (no plan yet) | `/feature-flow csv-export` | `$feature-flow csv-export` |
|
|
61
|
+
| Plan what you just talked through | `/feature-flow` | `$feature-flow` |
|
|
61
62
|
| Build its tickets (a plan exists) | `/feature-flow csv-export` | `$feature-flow csv-export` |
|
|
62
63
|
| The same, asking nothing | `/feature-flow csv-export auto` | `$feature-flow csv-export auto` |
|
|
63
64
|
| Watch the graph, animated | `/feature-flow csv-export show` | `$feature-flow csv-export show` |
|
|
64
65
|
|
|
65
66
|
The same command plans when `plans/csv-export/` does not exist yet and builds when it does. Commit the plan before you build it.
|
|
66
67
|
|
|
68
|
+
**Planning asks you twice.** First the session shows a short feature brief, written from your conversation (what, why, scope, the bar, and anything it had to assume), and waits for yes, edit or cancel. Then a fresh `feature-planner` subagent, given only that brief, reads the code, looks up outside docs where the code cannot answer, and writes a draft plan into the git-ignored `.feature-flow/state/draft/`. A fresh `plan-reviewer` checks the draft against the brief. The session shows you the goal, the commands, the ticket graph, what was dropped and the review result, and waits for a second yes. Only then does `flow.py plan-accept` copy the draft into `plans/<feature>/`.
|
|
69
|
+
|
|
67
70
|
Try the graph first, with no setup and no agent: `bash examples/demo.sh --open`.
|
|
68
71
|
|
|
69
72
|
### What the installer does
|
|
@@ -112,7 +115,7 @@ The Claude Code skill is `skills/feature-flow/`. The Codex skill is written by h
|
|
|
112
115
|
|
|
113
116
|
| Skill | What it does |
|
|
114
117
|
|---|---|
|
|
115
|
-
| `feature-flow` | **Plan:**
|
|
118
|
+
| `feature-flow` | **Plan:** turns the conversation (or your answers) into a feature brief and waits for a yes, has a planner and a plan reviewer subagent draft and check `plans/<feature>/` (a spec, a map, commands, learnings and tickets), shows you the graph, and writes it after a second yes. **Build:** runs the tickets one by one, a builder and a reviewer subagent each, with `flow.py` deciding every step. **Show:** the animated ticket graph and a summary of what is ready and what blocks the finish. |
|
|
116
119
|
| `architect-review` | Architecture review of a file, diff or feature, with severity rated findings. Reads `CLAUDE.md` or `AGENTS.md` for the project's own rules. |
|
|
117
120
|
| `automation-design` | Blueprint for an automation pipeline. Hands off to `feature-flow` for the plan. |
|
|
118
121
|
|
|
@@ -120,10 +123,12 @@ The Claude Code skill is `skills/feature-flow/`. The Codex skill is written by h
|
|
|
120
123
|
|---|---|---|
|
|
121
124
|
| `ticket-builder` | Read, Glob, Grep, Edit, Write, Bash | Builds one ticket by the build guide in its prompt. |
|
|
122
125
|
| `ticket-reviewer` | Read, Glob, Grep, Bash (no Edit, no Write) | Reviews one ticket by the review guide in its prompt and ends with `REVIEW: PASS` or `REVIEW: FAIL`. |
|
|
126
|
+
| `feature-planner` | Read, Glob, Grep, Bash, Write, Edit, WebSearch, WebFetch | Plans one feature from the approved brief by the plan guide, writes only the draft, cites outside sources, and ends with `PLAN: READY` or `PLAN: QUESTIONS`. |
|
|
127
|
+
| `plan-reviewer` | Read, Glob, Grep, Bash (no Edit, no Write) | Checks the draft against the brief by the plan review guide and ends with `PLAN-REVIEW: PASS` or `PLAN-REVIEW: FAIL`. |
|
|
123
128
|
|
|
124
|
-
In Claude Code the
|
|
129
|
+
In Claude Code the reviewers' tool lists are enforced, so they cannot edit. A Codex subagent cannot be limited that way, so there the reviewers work from their instructions: the conductor stops the run if a ticket reviewer changed anything, and a planner's draft reaches `plans/` only through `plan-accept`. On Codex the planner can research the web only if web search is on; otherwise it plans from the code and says so. The Codex planning path has not been run by hand.
|
|
125
130
|
|
|
126
|
-
The guides in `guides/` (installed to `.feature-flow/guides/`) hold the protocol: how to build, review, plan and show. The skill holds only what differs per agent.
|
|
131
|
+
The guides in `guides/` (installed to `.feature-flow/guides/`) hold the protocol: how to build, review, write the brief, plan, review a plan and show. The skill holds only what differs per agent.
|
|
127
132
|
|
|
128
133
|
## Plans and tickets
|
|
129
134
|
|
|
@@ -282,7 +287,15 @@ The installer never deletes the old skill folders, but it names any it finds. De
|
|
|
282
287
|
|
|
283
288
|
## Releasing to PyPI
|
|
284
289
|
|
|
285
|
-
The package is `feature-flow-cli` (the shorter `feature-flow` is likely refused by PyPI as too close to the existing `featureflow`). Its version is `__version__` in `feature_flow/__init__.py`.
|
|
290
|
+
The package is [`feature-flow-cli`](https://pypi.org/project/feature-flow-cli/) (the shorter `feature-flow` is likely refused by PyPI as too close to the existing `featureflow`). Its version is `__version__` in `feature_flow/__init__.py`.
|
|
291
|
+
|
|
292
|
+
To release, bump `__version__`, merge it to `main`, then tag that commit and push the tag:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
git tag v0.1.1 && git push origin v0.1.1
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
`.github/workflows/release.yml` checks that the tag matches `__version__`, builds the wheel and the sdist, runs `tests/package_smoke.py` on them and publishes them to PyPI. PyPI trusts the workflow as a trusted publisher (owner `hamilton-sky`, repository `feature-flow`, workflow `release.yml`, environment `pypi`), so no token is stored. PyPI never accepts the same version twice.
|
|
286
299
|
|
|
287
300
|
## Caution
|
|
288
301
|
|
|
@@ -26,7 +26,7 @@ python3 install.py /path/to/your/repo --agent all # both, side by side
|
|
|
26
26
|
# `bash install.sh ...` still works on Linux and macOS: it only runs install.py
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
Without a clone,
|
|
29
|
+
Without a clone, the same installer runs from [PyPI](https://pypi.org/project/feature-flow-cli/):
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
uvx feature-flow-cli install /path/to/your/repo --agent all # or: pipx run feature-flow-cli install ...
|
|
@@ -39,12 +39,15 @@ Then, in your repo, in the agent:
|
|
|
39
39
|
| | Claude Code | Codex |
|
|
40
40
|
|---|---|---|
|
|
41
41
|
| Plan a feature (no plan yet) | `/feature-flow csv-export` | `$feature-flow csv-export` |
|
|
42
|
+
| Plan what you just talked through | `/feature-flow` | `$feature-flow` |
|
|
42
43
|
| Build its tickets (a plan exists) | `/feature-flow csv-export` | `$feature-flow csv-export` |
|
|
43
44
|
| The same, asking nothing | `/feature-flow csv-export auto` | `$feature-flow csv-export auto` |
|
|
44
45
|
| Watch the graph, animated | `/feature-flow csv-export show` | `$feature-flow csv-export show` |
|
|
45
46
|
|
|
46
47
|
The same command plans when `plans/csv-export/` does not exist yet and builds when it does. Commit the plan before you build it.
|
|
47
48
|
|
|
49
|
+
**Planning asks you twice.** First the session shows a short feature brief, written from your conversation (what, why, scope, the bar, and anything it had to assume), and waits for yes, edit or cancel. Then a fresh `feature-planner` subagent, given only that brief, reads the code, looks up outside docs where the code cannot answer, and writes a draft plan into the git-ignored `.feature-flow/state/draft/`. A fresh `plan-reviewer` checks the draft against the brief. The session shows you the goal, the commands, the ticket graph, what was dropped and the review result, and waits for a second yes. Only then does `flow.py plan-accept` copy the draft into `plans/<feature>/`.
|
|
50
|
+
|
|
48
51
|
Try the graph first, with no setup and no agent: `bash examples/demo.sh --open`.
|
|
49
52
|
|
|
50
53
|
### What the installer does
|
|
@@ -93,7 +96,7 @@ The Claude Code skill is `skills/feature-flow/`. The Codex skill is written by h
|
|
|
93
96
|
|
|
94
97
|
| Skill | What it does |
|
|
95
98
|
|---|---|
|
|
96
|
-
| `feature-flow` | **Plan:**
|
|
99
|
+
| `feature-flow` | **Plan:** turns the conversation (or your answers) into a feature brief and waits for a yes, has a planner and a plan reviewer subagent draft and check `plans/<feature>/` (a spec, a map, commands, learnings and tickets), shows you the graph, and writes it after a second yes. **Build:** runs the tickets one by one, a builder and a reviewer subagent each, with `flow.py` deciding every step. **Show:** the animated ticket graph and a summary of what is ready and what blocks the finish. |
|
|
97
100
|
| `architect-review` | Architecture review of a file, diff or feature, with severity rated findings. Reads `CLAUDE.md` or `AGENTS.md` for the project's own rules. |
|
|
98
101
|
| `automation-design` | Blueprint for an automation pipeline. Hands off to `feature-flow` for the plan. |
|
|
99
102
|
|
|
@@ -101,10 +104,12 @@ The Claude Code skill is `skills/feature-flow/`. The Codex skill is written by h
|
|
|
101
104
|
|---|---|---|
|
|
102
105
|
| `ticket-builder` | Read, Glob, Grep, Edit, Write, Bash | Builds one ticket by the build guide in its prompt. |
|
|
103
106
|
| `ticket-reviewer` | Read, Glob, Grep, Bash (no Edit, no Write) | Reviews one ticket by the review guide in its prompt and ends with `REVIEW: PASS` or `REVIEW: FAIL`. |
|
|
107
|
+
| `feature-planner` | Read, Glob, Grep, Bash, Write, Edit, WebSearch, WebFetch | Plans one feature from the approved brief by the plan guide, writes only the draft, cites outside sources, and ends with `PLAN: READY` or `PLAN: QUESTIONS`. |
|
|
108
|
+
| `plan-reviewer` | Read, Glob, Grep, Bash (no Edit, no Write) | Checks the draft against the brief by the plan review guide and ends with `PLAN-REVIEW: PASS` or `PLAN-REVIEW: FAIL`. |
|
|
104
109
|
|
|
105
|
-
In Claude Code the
|
|
110
|
+
In Claude Code the reviewers' tool lists are enforced, so they cannot edit. A Codex subagent cannot be limited that way, so there the reviewers work from their instructions: the conductor stops the run if a ticket reviewer changed anything, and a planner's draft reaches `plans/` only through `plan-accept`. On Codex the planner can research the web only if web search is on; otherwise it plans from the code and says so. The Codex planning path has not been run by hand.
|
|
106
111
|
|
|
107
|
-
The guides in `guides/` (installed to `.feature-flow/guides/`) hold the protocol: how to build, review, plan and show. The skill holds only what differs per agent.
|
|
112
|
+
The guides in `guides/` (installed to `.feature-flow/guides/`) hold the protocol: how to build, review, write the brief, plan, review a plan and show. The skill holds only what differs per agent.
|
|
108
113
|
|
|
109
114
|
## Plans and tickets
|
|
110
115
|
|
|
@@ -263,7 +268,15 @@ The installer never deletes the old skill folders, but it names any it finds. De
|
|
|
263
268
|
|
|
264
269
|
## Releasing to PyPI
|
|
265
270
|
|
|
266
|
-
The package is `feature-flow-cli` (the shorter `feature-flow` is likely refused by PyPI as too close to the existing `featureflow`). Its version is `__version__` in `feature_flow/__init__.py`.
|
|
271
|
+
The package is [`feature-flow-cli`](https://pypi.org/project/feature-flow-cli/) (the shorter `feature-flow` is likely refused by PyPI as too close to the existing `featureflow`). Its version is `__version__` in `feature_flow/__init__.py`.
|
|
272
|
+
|
|
273
|
+
To release, bump `__version__`, merge it to `main`, then tag that commit and push the tag:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
git tag v0.1.1 && git push origin v0.1.1
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
`.github/workflows/release.yml` checks that the tag matches `__version__`, builds the wheel and the sdist, runs `tests/package_smoke.py` on them and publishes them to PyPI. PyPI trusts the workflow as a trusted publisher (owner `hamilton-sky`, repository `feature-flow`, workflow `release.yml`, environment `pypi`), so no token is stored. PyPI never accepts the same version twice.
|
|
267
280
|
|
|
268
281
|
## Caution
|
|
269
282
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: feature-flow
|
|
3
|
-
description: "Use to plan a feature or build its tickets, one fresh builder and one fresh reviewer subagent per ticket, with scripts/flow.py deciding every step. Also draws the ticket graph with show."
|
|
3
|
+
description: "Use to plan a feature (from the conversation so far, with a fresh planner and plan reviewer subagent) or build its tickets, one fresh builder and one fresh reviewer subagent per ticket, with scripts/flow.py deciding every step. Also draws the ticket graph with show."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
`<arguments>` below stands for the text the user typed after `$feature-flow`.
|
|
7
7
|
|
|
8
8
|
Plan or build the feature in `<arguments>`.
|
|
9
9
|
|
|
10
|
-
The first word is the **feature**. `show` means draw the graph. `auto` means ask nothing and go. If no feature was given, list `plans/*/` and ask which one.
|
|
10
|
+
The first word is the **feature**. `show` means draw the graph. `auto` means ask nothing and go. If no feature was given and the conversation so far describes work to build, plan it (below) and propose the feature's name in the brief. If no feature was given otherwise, list `plans/*/` and ask which one.
|
|
11
11
|
|
|
12
12
|
The conductor is `python3 scripts/flow.py <feature> <command>`. It decides the order, runs the gate and the floor guard, and keeps its state in `.feature-flow/state/`, a folder git ignores. You ask it, and you do what it says. The guides it uses are in `.feature-flow/guides/` (in a feature-flow checkout, `guides/`). Read the project's `AGENTS.md` for its conventions.
|
|
13
13
|
|
|
@@ -17,11 +17,15 @@ With `show`, follow `.feature-flow/guides/show.md` for the feature and stop.
|
|
|
17
17
|
|
|
18
18
|
## Plan
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
When `plans/<feature>/` does not exist, or you are planning from the conversation, follow `.feature-flow/guides/brief.md` with the user. You write the brief and ask for both yeses (unless `auto`); a child agent plans, another reviews, and you never draft the plan yourself. Once the brief names the feature, run `FLOW_INVOKE='$feature-flow' python3 scripts/flow.py <feature> start` and check that it prints `PLAN`, and check that `.agents/flow-roles/feature-planner.md` and `.agents/flow-roles/plan-reviewer.md` exist (see below).
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
- `plan-prompt <brief>`: start the planner with `spawn_agent(task_name="feature_planner", fork_turns="none", message=...)`. The message is "Work only in <repo>." followed by the whole output. Get its final reply with `wait_agent`. A child cannot be limited to the draft folder, so it works from its instructions, and `plan-accept` is the only way its draft reaches `plans/`. If web search is off in this Codex, the planner plans from the codebase and says so.
|
|
23
|
+
- `plan-review-prompt`: start a new reviewer the same way, with `task_name="plan_reviewer"` and `fork_turns="none"`, never with the planner's reply. Get its final reply with `wait_agent` and save it with the shell into `.feature-flow/state/plan-review-<feature>.txt`.
|
|
24
|
+
- `plan-accept`: writes `plans/<feature>/` from the draft, only after the second yes.
|
|
25
|
+
|
|
26
|
+
Then run `python3 scripts/flow-status.py <feature> --check`, list the files `plan-accept` wrote, and suggest the commit command (`git add plans/<feature> && git commit -m "docs(<feature>): plan"`). Say to commit the plan and run `$feature-flow <feature>` again. While the plan is uncommitted, do not say the feature is ready to build.
|
|
27
|
+
|
|
28
|
+
To add tickets to a plan that exists, edit it by hand following the ticket rules in `.feature-flow/guides/plan.md`.
|
|
25
29
|
|
|
26
30
|
## Before building
|
|
27
31
|
|
|
@@ -29,7 +33,7 @@ With a plan present, check these before `start`, and stop at the first that fail
|
|
|
29
33
|
|
|
30
34
|
- `git status --porcelain` is empty.
|
|
31
35
|
- `python3 scripts/flow-status.py <feature> --check` prints `OK`.
|
|
32
|
-
- `.agents/flow-roles/ticket-builder.md` and `.agents/flow-roles/ticket-reviewer.md` exist. If not, say to run `
|
|
36
|
+
- `.agents/flow-roles/ticket-builder.md` and `.agents/flow-roles/ticket-reviewer.md` exist. If not, say to run `uvx feature-flow-cli install . --agent codex` in this repo (or `python3 install.py <repo> --agent codex` from a feature-flow clone).
|
|
33
37
|
|
|
34
38
|
Then run `FLOW_INVOKE='$feature-flow' python3 scripts/flow.py <feature> start`. It prints `OK <token>`. Keep the token and put `FLOW_SESSION=<token>` in front of **every** later conductor command, with `FLOW_INVOKE='$feature-flow'`. The conductor keeps its state in `.feature-flow/state/`, a git-ignored folder in the repo, so it never needs to write `.git`. If it prints `STOP cannot write the flow state`, tell the user this session must be allowed to write that folder.
|
|
35
39
|
|
|
@@ -53,7 +57,7 @@ A conductor command looks like this:
|
|
|
53
57
|
|
|
54
58
|
## Rules while building
|
|
55
59
|
|
|
56
|
-
These apply from `start` on, once a plan exists.
|
|
60
|
+
These apply from `start` on, once a plan exists.
|
|
57
61
|
|
|
58
62
|
- Never run the gate, the floor guard or a review yourself. The conductor runs the checks, and the reviewer subagent reviews.
|
|
59
63
|
- Never edit a ticket, the plan or the code, and never commit. The builder does that.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feature-planner
|
|
3
|
+
description: Plans one feature from an approved feature brief by following the plan guide in its prompt. Writes a draft plan into the flow's state folder and nothing else. Use it from /feature-flow, in a fresh session that never saw the conversation.
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Glob
|
|
7
|
+
- Grep
|
|
8
|
+
- Bash
|
|
9
|
+
- Write
|
|
10
|
+
- Edit
|
|
11
|
+
- WebSearch
|
|
12
|
+
- WebFetch
|
|
13
|
+
model: inherit
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
You are the planner. You turn one feature brief into a draft plan, then stop.
|
|
17
|
+
|
|
18
|
+
Follow the plan guide in your prompt exactly. It tells you what to read, how to research, how to cut the graph into tickets, where to write, and how to reply.
|
|
19
|
+
|
|
20
|
+
How you think, in this order:
|
|
21
|
+
|
|
22
|
+
- The brief is the whole ask. You never saw the conversation behind it. Plan what the brief says; a gap goes under Open questions or into a `settle` ticket, never into a silent guess.
|
|
23
|
+
- Read before you plan: the project's conventions, similar features, the files that will change, and the real build, test and lint commands from the project's own config. Never guess a command.
|
|
24
|
+
- Look outside the repo only where the code cannot answer: a library's documentation for the version the lockfile pins, an outside API the brief names. Every outside fact gets its source in `map.md`. If you cannot reach the web, say so in your reply and plan from the codebase.
|
|
25
|
+
- At every real fork, write two options in a sentence each, pick one and say why. If the choice belongs to the user, make it a `settle` ticket instead of picking.
|
|
26
|
+
- Be lazy: drop every ticket the bar does not need, and list what you dropped.
|
|
27
|
+
- Prove the draft is well formed with the check the guide names, and fix every problem.
|
|
28
|
+
|
|
29
|
+
Rules that never bend:
|
|
30
|
+
|
|
31
|
+
- Write and edit only inside the draft folder your prompt names. Never change a file outside it, never write under `plans/`, never commit.
|
|
32
|
+
- You cannot talk to the user. Anything only the user can decide comes back in your reply as an open question.
|
|
33
|
+
- End your reply with exactly `PLAN: READY` or `PLAN: QUESTIONS`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan-reviewer
|
|
3
|
+
description: Independently checks a draft plan against the approved feature brief by following the plan review guide in its prompt. Read only. Use it in a fresh session so it never saw the planner's reasoning.
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Glob
|
|
7
|
+
- Grep
|
|
8
|
+
- Bash
|
|
9
|
+
model: inherit
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are the plan reviewer, not the planner. You did not write this plan and you do not trust the planner's account of it.
|
|
13
|
+
|
|
14
|
+
Follow the plan review guide in your prompt exactly. Read the brief and the draft, run the commands the draft relies on, and end with the single line `PLAN-REVIEW: PASS` or `PLAN-REVIEW: FAIL`.
|
|
15
|
+
|
|
16
|
+
You cannot edit, write, stage or commit anything, and you must not try to get around that with shell commands. You may run read only commands, the plan check, and the commands the draft's `commands.md` lists.
|
|
@@ -4,6 +4,7 @@ Each call lives in one function so plans/interactive-flow-python can swap it for
|
|
|
4
4
|
in-process call without touching the conductor.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import os
|
|
7
8
|
import subprocess
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
|
|
@@ -41,10 +42,21 @@ def _bash(script, *args):
|
|
|
41
42
|
return Result(result.returncode, result.stdout)
|
|
42
43
|
|
|
43
44
|
|
|
44
|
-
def flow_status(scripts, feature, mode):
|
|
45
|
-
"""The ticket graph reader with --next, --counts or --check, in process. stderr is folded into the output.
|
|
45
|
+
def flow_status(scripts, feature, mode, root=None):
|
|
46
|
+
"""The ticket graph reader with --next, --counts or --check, in process. stderr is folded into the output.
|
|
47
|
+
root, when given, stands in for FLOW_DIR for this one call (the planner's draft folder)."""
|
|
46
48
|
out = _Collect()
|
|
47
|
-
|
|
49
|
+
saved = os.environ.get("FLOW_DIR")
|
|
50
|
+
if root is not None:
|
|
51
|
+
os.environ["FLOW_DIR"] = str(root)
|
|
52
|
+
try:
|
|
53
|
+
code = status.run([feature, mode], out, out)
|
|
54
|
+
finally:
|
|
55
|
+
if root is not None:
|
|
56
|
+
if saved is None:
|
|
57
|
+
del os.environ["FLOW_DIR"]
|
|
58
|
+
else:
|
|
59
|
+
os.environ["FLOW_DIR"] = saved
|
|
48
60
|
return Result(code, out.text())
|
|
49
61
|
|
|
50
62
|
|
|
@@ -5,8 +5,10 @@ from pathlib import Path
|
|
|
5
5
|
|
|
6
6
|
from feature_flow.conductor import Conductor, NoPhase, Stop
|
|
7
7
|
|
|
8
|
-
USAGE = "usage: python3 scripts/flow.py <feature> start | next | prompt | verdict <file
|
|
9
|
-
|
|
8
|
+
USAGE = ("usage: python3 scripts/flow.py <feature> start | next | prompt | verdict <file>\n"
|
|
9
|
+
" python3 scripts/flow.py <feature> plan-prompt <brief> [findings] | plan-review-prompt | plan-accept")
|
|
10
|
+
COMMANDS = ("start", "next", "prompt", "verdict", "plan-prompt", "plan-review-prompt", "plan-accept")
|
|
11
|
+
ARGS = {"verdict": (3,), "plan-prompt": (3, 4)}
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
def main(argv=None, scripts=None):
|
|
@@ -15,7 +17,10 @@ def main(argv=None, scripts=None):
|
|
|
15
17
|
print(USAGE, file=sys.stderr)
|
|
16
18
|
return 2
|
|
17
19
|
feature, command = args[0], args[1]
|
|
18
|
-
if
|
|
20
|
+
if feature in (".", "..") or any(c in feature for c in "/\\:"):
|
|
21
|
+
print("the feature must be a plain folder name, not %s" % feature, file=sys.stderr)
|
|
22
|
+
return 2
|
|
23
|
+
if len(args) not in ARGS.get(command, (2,)):
|
|
19
24
|
print(USAGE, file=sys.stderr)
|
|
20
25
|
return 2
|
|
21
26
|
scripts = Path(scripts) if scripts else Path.cwd() / "scripts"
|
|
@@ -24,6 +29,12 @@ def main(argv=None, scripts=None):
|
|
|
24
29
|
conductor = Conductor(feature, scripts)
|
|
25
30
|
if command == "verdict":
|
|
26
31
|
line = conductor.verdict(args[2])
|
|
32
|
+
elif command == "plan-prompt":
|
|
33
|
+
line = conductor.plan_prompt(args[2], args[3] if len(args) == 4 else None)
|
|
34
|
+
elif command == "plan-review-prompt":
|
|
35
|
+
line = conductor.plan_review_prompt()
|
|
36
|
+
elif command == "plan-accept":
|
|
37
|
+
line = conductor.plan_accept()
|
|
27
38
|
elif command == "prompt":
|
|
28
39
|
line = conductor.prompt()
|
|
29
40
|
elif command == "start":
|
|
@@ -39,7 +50,11 @@ def main(argv=None, scripts=None):
|
|
|
39
50
|
conductor.log("STOP")
|
|
40
51
|
print(line)
|
|
41
52
|
return 1
|
|
42
|
-
|
|
53
|
+
out = sys.stdout
|
|
54
|
+
if command in ("plan-prompt", "plan-review-prompt") and hasattr(out, "reconfigure"):
|
|
55
|
+
# the plan guides hold non-ASCII text, which a Windows console code page cannot print
|
|
56
|
+
out.reconfigure(encoding="utf-8")
|
|
57
|
+
print(line, end="" if line.endswith("\n") else "\n", file=out)
|
|
43
58
|
return 0
|
|
44
59
|
|
|
45
60
|
|
|
@@ -7,6 +7,7 @@ caller, and prints exactly one line per command.
|
|
|
7
7
|
import os
|
|
8
8
|
import re
|
|
9
9
|
import secrets
|
|
10
|
+
import shutil
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
12
13
|
from feature_flow import checks, git, prompts, state, tickets
|
|
@@ -53,6 +54,9 @@ class Conductor:
|
|
|
53
54
|
except OSError as err:
|
|
54
55
|
raise Stop("cannot write the flow state in %s: %s. this session must be allowed to write there"
|
|
55
56
|
% (top / state.STATE_DIR, err.strerror or err))
|
|
57
|
+
self.draft_root = state.STATE_DIR / "draft"
|
|
58
|
+
self.draft = self.draft_root / feature
|
|
59
|
+
self.brief_file = state.STATE_DIR / ("brief-%s.md" % feature)
|
|
56
60
|
self.state_file = state.state_path(folder, feature)
|
|
57
61
|
self.log_file = state.log_path(folder, feature)
|
|
58
62
|
self.findings_file = state.file_path(folder, feature, "findings")
|
|
@@ -297,3 +301,70 @@ class Conductor:
|
|
|
297
301
|
self.save()
|
|
298
302
|
self.log("VERDICT-%s" % (found or "none").upper())
|
|
299
303
|
return "OK" if found else "RETRY no review verdict"
|
|
304
|
+
|
|
305
|
+
# ---- planning: stateless, the brief and the draft are all there is --
|
|
306
|
+
|
|
307
|
+
def no_plan_yet(self):
|
|
308
|
+
if self.plan.is_dir():
|
|
309
|
+
raise Stop("%s already exists. pick another name: a plan is never overwritten" % self.plan.as_posix())
|
|
310
|
+
|
|
311
|
+
def draft_text(self):
|
|
312
|
+
return self.draft.as_posix() + "/"
|
|
313
|
+
|
|
314
|
+
def read_text(self, path, what):
|
|
315
|
+
try:
|
|
316
|
+
return Path(path).read_text(encoding="utf-8", errors="replace")
|
|
317
|
+
except OSError as err:
|
|
318
|
+
raise Stop("cannot read the %s %s: %s" % (what, path, err.strerror))
|
|
319
|
+
|
|
320
|
+
def plan_prompt(self, brief, findings=None):
|
|
321
|
+
"""The feature-planner's prompt. Saves the brief; a first round (no findings) starts an empty draft."""
|
|
322
|
+
self.no_plan_yet()
|
|
323
|
+
text = self.read_text(brief, "brief")
|
|
324
|
+
notes = self.read_text(findings, "review findings") if findings else ""
|
|
325
|
+
if not text.strip():
|
|
326
|
+
raise Stop("the brief %s is empty" % brief)
|
|
327
|
+
if Path(brief).resolve() != self.brief_file.resolve():
|
|
328
|
+
self.brief_file.write_text(text, encoding="utf-8")
|
|
329
|
+
if not findings and self.draft.is_dir():
|
|
330
|
+
if self.draft.resolve().parent != self.draft_root.resolve():
|
|
331
|
+
raise Stop("the draft folder %s is outside %s" % (self.draft_text(), self.draft_root.as_posix()))
|
|
332
|
+
shutil.rmtree(str(self.draft))
|
|
333
|
+
(self.draft / (os.environ.get("FLOW_TICKETS") or "tasks")).mkdir(parents=True, exist_ok=True)
|
|
334
|
+
self.log("PLAN-PROMPT")
|
|
335
|
+
try:
|
|
336
|
+
return prompts.plan("plan", self.scripts, self.feature, self.draft_text(), text, notes)
|
|
337
|
+
except FileNotFoundError as err:
|
|
338
|
+
raise Stop(str(err))
|
|
339
|
+
|
|
340
|
+
def plan_review_prompt(self):
|
|
341
|
+
"""The plan-reviewer's prompt: the saved brief and the draft, nothing from the planner."""
|
|
342
|
+
self.no_plan_yet()
|
|
343
|
+
if not self.brief_file.is_file():
|
|
344
|
+
raise Stop("no brief for %s. run plan-prompt first" % self.feature)
|
|
345
|
+
if not self.draft.is_dir():
|
|
346
|
+
raise Stop("no draft plan in %s. run the planner first" % self.draft_text())
|
|
347
|
+
self.log("PLAN-REVIEW-PROMPT")
|
|
348
|
+
try:
|
|
349
|
+
return prompts.plan("plan-review", self.scripts, self.feature, self.draft_text(),
|
|
350
|
+
self.read_text(self.brief_file, "brief"))
|
|
351
|
+
except FileNotFoundError as err:
|
|
352
|
+
raise Stop(str(err))
|
|
353
|
+
|
|
354
|
+
def plan_accept(self):
|
|
355
|
+
"""Copy a draft that passes the check into the plan folder. Never overwrites, never commits."""
|
|
356
|
+
self.no_plan_yet()
|
|
357
|
+
if not self.draft.is_dir():
|
|
358
|
+
raise Stop("no draft plan in %s. run the planner first" % self.draft_text())
|
|
359
|
+
check = checks.flow_status(self.scripts, self.feature, "--check", root=self.draft_root)
|
|
360
|
+
if not check.ok:
|
|
361
|
+
raise Stop("the draft fails the plan check, fix it first: %s"
|
|
362
|
+
% " ".join(check.out.split())[:400])
|
|
363
|
+
self.plan.parent.mkdir(parents=True, exist_ok=True)
|
|
364
|
+
shutil.copytree(str(self.draft), str(self.plan))
|
|
365
|
+
check = checks.flow_status(self.scripts, self.feature, "--check")
|
|
366
|
+
if not check.ok:
|
|
367
|
+
raise Stop("the accepted plan in %s fails the plan check: %s"
|
|
368
|
+
% (self.plan.as_posix(), " ".join(check.out.split())[:400]))
|
|
369
|
+
self.log("PLAN-ACCEPT")
|
|
370
|
+
return "OK %s" % self.plan.as_posix()
|
|
@@ -5,8 +5,9 @@ The text is runtime neutral: any agent that can read a prompt can follow it.
|
|
|
5
5
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
ROLES = {"build": "ticket-builder.md", "review": "ticket-reviewer.md"
|
|
9
|
-
|
|
8
|
+
ROLES = {"build": "ticket-builder.md", "review": "ticket-reviewer.md",
|
|
9
|
+
"plan": "feature-planner.md", "plan-review": "plan-reviewer.md"}
|
|
10
|
+
GUIDES = {"build": "build.md", "review": "review.md", "plan": "plan.md", "plan-review": "plan-review.md"}
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def find_file(scripts, folder, name):
|
|
@@ -50,3 +51,28 @@ def build(phase, scripts, feature, ticket, num, sha):
|
|
|
50
51
|
"with exactly `REVIEW: PASS` or `REVIEW: FAIL`." % (num, feature, feature, num, sha, ticket, sha, fresh))
|
|
51
52
|
intro = "The review guide follows. Follow it exactly."
|
|
52
53
|
return "\n\n".join([role, "---", intro, guide, "---", task]) + "\n"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def plan(phase, scripts, feature, draft, brief, findings=""):
|
|
57
|
+
"""The prompt for the feature-planner (phase "plan") or the plan-reviewer ("plan-review").
|
|
58
|
+
|
|
59
|
+
Both get the approved brief and the draft folder, never the conversation or each other's reasoning.
|
|
60
|
+
"""
|
|
61
|
+
role = _body(find_file(scripts, "agents", ROLES[phase]))
|
|
62
|
+
guide = _body(find_file(scripts, "guides", GUIDES[phase]))
|
|
63
|
+
guide = guide.replace("<feature>", feature).replace("<draft>", draft)
|
|
64
|
+
if phase == "plan":
|
|
65
|
+
task = ("Your task: plan the feature `%s` from the approved brief below. Write the draft only inside "
|
|
66
|
+
"`%s`, laid out as the plan guide says. Do not write anywhere else and do not commit. Your final "
|
|
67
|
+
"reply must end with exactly `PLAN: READY` or `PLAN: QUESTIONS`." % (feature, draft))
|
|
68
|
+
intro = "The plan guide follows. Follow it exactly."
|
|
69
|
+
else:
|
|
70
|
+
task = ("Your task: review the draft plan for the feature `%s` in `%s` against the approved brief below. "
|
|
71
|
+
"Do not edit any file or create a commit. Your final reply must end with exactly "
|
|
72
|
+
"`PLAN-REVIEW: PASS` or `PLAN-REVIEW: FAIL`." % (feature, draft))
|
|
73
|
+
intro = "The plan review guide follows. Follow it exactly."
|
|
74
|
+
parts = [role, "---", intro, guide, "---", task, "## The brief", brief.strip("\n")]
|
|
75
|
+
if findings.strip():
|
|
76
|
+
parts += ["## Review findings from the last round", "The draft is already in `%s`. Fix each finding in it, "
|
|
77
|
+
"or say in your reply why it stays." % draft, findings.strip("\n")]
|
|
78
|
+
return "\n\n".join(parts) + "\n"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: feature-flow-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A ticket-graph workflow for coding agents: installs the feature-flow skill into a repo
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/hamilton-sky/feature-flow
|
|
@@ -45,7 +45,7 @@ python3 install.py /path/to/your/repo --agent all # both, side by side
|
|
|
45
45
|
# `bash install.sh ...` still works on Linux and macOS: it only runs install.py
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
Without a clone,
|
|
48
|
+
Without a clone, the same installer runs from [PyPI](https://pypi.org/project/feature-flow-cli/):
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
uvx feature-flow-cli install /path/to/your/repo --agent all # or: pipx run feature-flow-cli install ...
|
|
@@ -58,12 +58,15 @@ Then, in your repo, in the agent:
|
|
|
58
58
|
| | Claude Code | Codex |
|
|
59
59
|
|---|---|---|
|
|
60
60
|
| Plan a feature (no plan yet) | `/feature-flow csv-export` | `$feature-flow csv-export` |
|
|
61
|
+
| Plan what you just talked through | `/feature-flow` | `$feature-flow` |
|
|
61
62
|
| Build its tickets (a plan exists) | `/feature-flow csv-export` | `$feature-flow csv-export` |
|
|
62
63
|
| The same, asking nothing | `/feature-flow csv-export auto` | `$feature-flow csv-export auto` |
|
|
63
64
|
| Watch the graph, animated | `/feature-flow csv-export show` | `$feature-flow csv-export show` |
|
|
64
65
|
|
|
65
66
|
The same command plans when `plans/csv-export/` does not exist yet and builds when it does. Commit the plan before you build it.
|
|
66
67
|
|
|
68
|
+
**Planning asks you twice.** First the session shows a short feature brief, written from your conversation (what, why, scope, the bar, and anything it had to assume), and waits for yes, edit or cancel. Then a fresh `feature-planner` subagent, given only that brief, reads the code, looks up outside docs where the code cannot answer, and writes a draft plan into the git-ignored `.feature-flow/state/draft/`. A fresh `plan-reviewer` checks the draft against the brief. The session shows you the goal, the commands, the ticket graph, what was dropped and the review result, and waits for a second yes. Only then does `flow.py plan-accept` copy the draft into `plans/<feature>/`.
|
|
69
|
+
|
|
67
70
|
Try the graph first, with no setup and no agent: `bash examples/demo.sh --open`.
|
|
68
71
|
|
|
69
72
|
### What the installer does
|
|
@@ -112,7 +115,7 @@ The Claude Code skill is `skills/feature-flow/`. The Codex skill is written by h
|
|
|
112
115
|
|
|
113
116
|
| Skill | What it does |
|
|
114
117
|
|---|---|
|
|
115
|
-
| `feature-flow` | **Plan:**
|
|
118
|
+
| `feature-flow` | **Plan:** turns the conversation (or your answers) into a feature brief and waits for a yes, has a planner and a plan reviewer subagent draft and check `plans/<feature>/` (a spec, a map, commands, learnings and tickets), shows you the graph, and writes it after a second yes. **Build:** runs the tickets one by one, a builder and a reviewer subagent each, with `flow.py` deciding every step. **Show:** the animated ticket graph and a summary of what is ready and what blocks the finish. |
|
|
116
119
|
| `architect-review` | Architecture review of a file, diff or feature, with severity rated findings. Reads `CLAUDE.md` or `AGENTS.md` for the project's own rules. |
|
|
117
120
|
| `automation-design` | Blueprint for an automation pipeline. Hands off to `feature-flow` for the plan. |
|
|
118
121
|
|
|
@@ -120,10 +123,12 @@ The Claude Code skill is `skills/feature-flow/`. The Codex skill is written by h
|
|
|
120
123
|
|---|---|---|
|
|
121
124
|
| `ticket-builder` | Read, Glob, Grep, Edit, Write, Bash | Builds one ticket by the build guide in its prompt. |
|
|
122
125
|
| `ticket-reviewer` | Read, Glob, Grep, Bash (no Edit, no Write) | Reviews one ticket by the review guide in its prompt and ends with `REVIEW: PASS` or `REVIEW: FAIL`. |
|
|
126
|
+
| `feature-planner` | Read, Glob, Grep, Bash, Write, Edit, WebSearch, WebFetch | Plans one feature from the approved brief by the plan guide, writes only the draft, cites outside sources, and ends with `PLAN: READY` or `PLAN: QUESTIONS`. |
|
|
127
|
+
| `plan-reviewer` | Read, Glob, Grep, Bash (no Edit, no Write) | Checks the draft against the brief by the plan review guide and ends with `PLAN-REVIEW: PASS` or `PLAN-REVIEW: FAIL`. |
|
|
123
128
|
|
|
124
|
-
In Claude Code the
|
|
129
|
+
In Claude Code the reviewers' tool lists are enforced, so they cannot edit. A Codex subagent cannot be limited that way, so there the reviewers work from their instructions: the conductor stops the run if a ticket reviewer changed anything, and a planner's draft reaches `plans/` only through `plan-accept`. On Codex the planner can research the web only if web search is on; otherwise it plans from the code and says so. The Codex planning path has not been run by hand.
|
|
125
130
|
|
|
126
|
-
The guides in `guides/` (installed to `.feature-flow/guides/`) hold the protocol: how to build, review, plan and show. The skill holds only what differs per agent.
|
|
131
|
+
The guides in `guides/` (installed to `.feature-flow/guides/`) hold the protocol: how to build, review, write the brief, plan, review a plan and show. The skill holds only what differs per agent.
|
|
127
132
|
|
|
128
133
|
## Plans and tickets
|
|
129
134
|
|
|
@@ -282,7 +287,15 @@ The installer never deletes the old skill folders, but it names any it finds. De
|
|
|
282
287
|
|
|
283
288
|
## Releasing to PyPI
|
|
284
289
|
|
|
285
|
-
The package is `feature-flow-cli` (the shorter `feature-flow` is likely refused by PyPI as too close to the existing `featureflow`). Its version is `__version__` in `feature_flow/__init__.py`.
|
|
290
|
+
The package is [`feature-flow-cli`](https://pypi.org/project/feature-flow-cli/) (the shorter `feature-flow` is likely refused by PyPI as too close to the existing `featureflow`). Its version is `__version__` in `feature_flow/__init__.py`.
|
|
291
|
+
|
|
292
|
+
To release, bump `__version__`, merge it to `main`, then tag that commit and push the tag:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
git tag v0.1.1 && git push origin v0.1.1
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
`.github/workflows/release.yml` checks that the tag matches `__version__`, builds the wheel and the sdist, runs `tests/package_smoke.py` on them and publishes them to PyPI. PyPI trusts the workflow as a trusted publisher (owner `hamilton-sky`, repository `feature-flow`, workflow `release.yml`, environment `pypi`), so no token is stored. PyPI never accepts the same version twice.
|
|
286
299
|
|
|
287
300
|
## Caution
|
|
288
301
|
|
|
@@ -6,6 +6,8 @@ install.sh
|
|
|
6
6
|
pyproject.toml
|
|
7
7
|
adapters/codex/feature-flow/SKILL.md
|
|
8
8
|
adapters/codex/feature-flow/agents/openai.yaml
|
|
9
|
+
agents/feature-planner.md
|
|
10
|
+
agents/plan-reviewer.md
|
|
9
11
|
agents/ticket-builder.md
|
|
10
12
|
agents/ticket-reviewer.md
|
|
11
13
|
feature_flow/__init__.py
|
|
@@ -28,7 +30,9 @@ feature_flow_cli.egg-info/SOURCES.txt
|
|
|
28
30
|
feature_flow_cli.egg-info/dependency_links.txt
|
|
29
31
|
feature_flow_cli.egg-info/entry_points.txt
|
|
30
32
|
feature_flow_cli.egg-info/top_level.txt
|
|
33
|
+
guides/brief.md
|
|
31
34
|
guides/build.md
|
|
35
|
+
guides/plan-review.md
|
|
32
36
|
guides/plan.md
|
|
33
37
|
guides/review.md
|
|
34
38
|
guides/show.md
|
|
@@ -44,7 +48,6 @@ scripts/flow-view.html
|
|
|
44
48
|
scripts/flow-view.py
|
|
45
49
|
scripts/flow.py
|
|
46
50
|
scripts/gate.py
|
|
47
|
-
skills/.DS_Store
|
|
48
51
|
skills/architect-review/SKILL.md
|
|
49
52
|
skills/automation-design/SKILL.md
|
|
50
53
|
skills/feature-flow/SKILL.md
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Plan a feature with the user
|
|
2
|
+
|
|
3
|
+
You are the user's session. You turn what the user wants into an approved **feature brief**, have a fresh planner draft the plan and a fresh plan reviewer check it, and write the plan into the repo only after the user says yes twice. You never draft the plan yourself, and only you talk to the user.
|
|
4
|
+
|
|
5
|
+
The conductor commands below are `python3 scripts/flow.py <feature> <command>`. The state folder is `.feature-flow/state/`, which git ignores.
|
|
6
|
+
|
|
7
|
+
## Step 1: Write the brief
|
|
8
|
+
|
|
9
|
+
If the conversation before this already describes the work, write the brief from it. If it does not, ask only what the code cannot tell you (what it does and why, what is in and out of scope, dependencies on other work, and the bar), then write the brief from the answers.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Feature: <folder name, lowercase-with-dashes>
|
|
13
|
+
What: <one or two sentences>
|
|
14
|
+
Why: <the problem, in the user's words where you can>
|
|
15
|
+
In scope: <what this covers>
|
|
16
|
+
Out: <what it does not cover, and anything the conversation set aside>
|
|
17
|
+
The bar: <one command and what it should print, or the observable outcome that proves the whole feature>
|
|
18
|
+
Unsure: <each thing you assumed, marked as an assumption>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Every line comes from the conversation or is marked as an assumption under Unsure. Never fill a gap with a guess. If there is no bar, ask for one: the brief is not ready without it. If `plans/<feature>/` already exists, say so and ask for another name; adding tickets to an existing plan is a hand edit that follows the ticket rules in the plan guide, not this flow.
|
|
22
|
+
|
|
23
|
+
## Step 2: Approval 1
|
|
24
|
+
|
|
25
|
+
Show the brief and ask: plan this? yes, edit, or cancel. On an edit, change the brief and show it again. On cancel, stop. Skip the question only in auto mode.
|
|
26
|
+
|
|
27
|
+
On yes, save the brief with the shell, not a file tool:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
cat > .feature-flow/state/brief-<feature>.md <<'BRIEF'
|
|
31
|
+
<the brief>
|
|
32
|
+
BRIEF
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Step 3: The planner
|
|
36
|
+
|
|
37
|
+
Run `plan-prompt .feature-flow/state/brief-<feature>.md`. Start a fresh `feature-planner` subagent with the whole output as its prompt, and wait for its final reply. It writes the draft into `.feature-flow/state/draft/<feature>/` and ends with `PLAN: READY` or `PLAN: QUESTIONS`.
|
|
38
|
+
|
|
39
|
+
On `PLAN: QUESTIONS`, ask the user the questions, add the answers to the brief under a `Decided:` line, save it again, and start a new planner with a new `plan-prompt`. After two rounds of questions, show the user what is still open and ask whether to go on.
|
|
40
|
+
|
|
41
|
+
## Step 4: The plan reviewer
|
|
42
|
+
|
|
43
|
+
Run `plan-review-prompt`. Start a fresh `plan-reviewer` subagent with the whole output, never with the planner's reply, and wait for its final reply. Save the whole reply with the shell into `.feature-flow/state/plan-review-<feature>.txt`.
|
|
44
|
+
|
|
45
|
+
If it ends `PLAN-REVIEW: FAIL`, run the planner once more with the findings, `plan-prompt .feature-flow/state/brief-<feature>.md .feature-flow/state/plan-review-<feature>.txt`, then review again. A second FAIL is not retried: show its findings to the user at approval 2.
|
|
46
|
+
|
|
47
|
+
## Step 5: Approval 2
|
|
48
|
+
|
|
49
|
+
Show, from the planner's reply and the draft:
|
|
50
|
+
|
|
51
|
+
- the goal and the bar
|
|
52
|
+
- the build, test and smoke commands it found (they are frozen once approved)
|
|
53
|
+
- the tickets as a compact graph, for example `02 Build the service after 01`
|
|
54
|
+
- what it dropped, so the user can overrule it
|
|
55
|
+
- what it assumed, its open questions, and any outside sources it relied on
|
|
56
|
+
- the plan review result, and its findings if it failed
|
|
57
|
+
|
|
58
|
+
Ask: write this plan? yes, edit, or cancel. On an edit, add it to the brief under `Decided:`, save, and go back to Step 3. On cancel, stop: the draft stays in the state folder and nothing in the repo changed. Skip the question only in auto mode.
|
|
59
|
+
|
|
60
|
+
## Step 6: Accept
|
|
61
|
+
|
|
62
|
+
Run `plan-accept`. It copies the draft into `plans/<feature>/` (or the project's `FLOW_DIR`) and checks it, then prints `OK <folder>`, or `STOP <reason>`: report the reason and stop. It never overwrites a plan and never commits.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Review a draft plan
|
|
2
|
+
|
|
3
|
+
You are the **plan reviewer**, not the planner. You did not write this plan, you never saw the planner's reasoning, and you do not trust its account of it. You are given the approved feature brief and the draft folder (`.feature-flow/state/draft/<feature>/`).
|
|
4
|
+
|
|
5
|
+
You cannot edit or write anything. You may read, search, run the plan check, and run the commands in the draft's `commands.md`.
|
|
6
|
+
|
|
7
|
+
## What to check
|
|
8
|
+
|
|
9
|
+
1. **The brief is covered.** Every part of the brief's What and In scope is planned by some ticket. Name each part that is not.
|
|
10
|
+
2. **Nothing outside the brief.** No ticket builds what the brief puts under Out, or what the brief never asked for and the bar does not need.
|
|
11
|
+
3. **The bar holds.** The spec's bar matches the brief's bar, and the last ticket is the acceptance ticket that runs it.
|
|
12
|
+
4. **The commands are real.** Each command in `commands.md` exists in the project's own config, and Smoke runs and exits 0 now. Run it.
|
|
13
|
+
5. **Done when is checkable by a stranger.** Each bullet names a command and the result it should print, or an outcome someone who has not read the plan can observe.
|
|
14
|
+
6. **Forks belong to the right person.** A choice that changes what the user gets is a `settle` ticket or an open question, not a decision the planner made quietly.
|
|
15
|
+
7. **The graph is sound.** Run `FLOW_DIR=.feature-flow/state/draft python3 scripts/flow-status.py <feature> --check` and report any problem or warning. Tickets are small (about 5 Done when bullets at most), and `Blocked by` lists only real dependencies.
|
|
16
|
+
8. **Outside facts have sources.** Each fact about a library or outside API that a ticket relies on has a source line in `map.md`.
|
|
17
|
+
|
|
18
|
+
Do not check style, wording or ticket order beyond what the list says.
|
|
19
|
+
|
|
20
|
+
## Your reply
|
|
21
|
+
|
|
22
|
+
For each failed check, one finding: the check's number, the file, and what is wrong in a sentence. Then a last line that is exactly one of:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
PLAN-REVIEW: PASS
|
|
26
|
+
PLAN-REVIEW: FAIL
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
PASS only when every check above holds. Nothing after that line.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Plan a feature
|
|
2
|
+
|
|
3
|
+
You are the **planner**. You are given a **feature brief** the user approved, and you turn it into one spec, one map and a graph of small tickets, written as a **draft**. You never saw the conversation behind the brief, and you cannot ask the user anything: what only the user can decide comes back in your reply.
|
|
4
|
+
|
|
5
|
+
Below, `<feature>` is the feature's folder name from the brief, and `<draft>` is the draft folder your prompt names (`.feature-flow/state/draft/<feature>/`).
|
|
6
|
+
|
|
7
|
+
## Where the files go
|
|
8
|
+
|
|
9
|
+
Write everything inside `<draft>`, laid out as the plan will be:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
<draft>
|
|
13
|
+
├── spec.md what and why
|
|
14
|
+
├── map.md destination, decisions so far, open questions
|
|
15
|
+
├── commands.md the real commands, frozen once approved
|
|
16
|
+
├── learnings.md empty, workers append to it
|
|
17
|
+
└── tasks/
|
|
18
|
+
├── 01-slug.md one ticket per file
|
|
19
|
+
└── 02-slug.md
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Write and edit only inside `<draft>` (on a findings round, fix the draft there), never change a file outside it, never commit. The conductor copies the draft into the repo after the user approves it. If the repo documents another place for plans (CLAUDE.md, AGENTS.md, `docs/`), still write the draft here, and name that place and the two variables the scripts need (`FLOW_DIR`, `FLOW_TICKETS`) in your reply.
|
|
23
|
+
|
|
24
|
+
## Step 1: Read the brief
|
|
25
|
+
|
|
26
|
+
The brief is the whole ask. Plan what it says: its What, Why, scope and bar. Each line under Unsure is an assumption you may keep or replace with what the code shows; say which in your reply. Lines under `Decided:` are the user's answers and win over everything above them. A gap the code cannot close goes under Open questions in `map.md`, or becomes a `settle` ticket when later tickets depend on it. If the brief has no bar, stop and reply `PLAN: QUESTIONS`.
|
|
27
|
+
|
|
28
|
+
If your prompt carries review findings from an earlier round, the draft is already there: fix each finding in it, or say in your reply why it stays.
|
|
29
|
+
|
|
30
|
+
## Step 2: Research
|
|
31
|
+
|
|
32
|
+
**The codebase first.** Find similar features and how they were built, the files that will change, the existing types and APIs the feature touches, and the test conventions. Find the **real** build, test and lint commands: read them from `package.json`, `Makefile`, `Taskfile.yml`, CI config or the project's agent instructions file (CLAUDE.md or AGENTS.md). Do not guess. Pick a **smoke command**: the quickest one that proves the base is healthy (usually build plus the fast tests). The flow runs it before every ticket, and runs Build, Test and Lint after every ticket, so all of them must run without asking anything and exit non zero on failure.
|
|
33
|
+
|
|
34
|
+
**Outside only where the code cannot answer.** For a library or API the feature uses, read the version the lockfile pins, then that version's official documentation. When the brief names a hard problem, look for how others solved it. Never research what the codebase already decides. Write every outside fact you rely on into `map.md` under Decisions so far with its source (`docs: <url>`). If you cannot reach the web, say so in your reply and plan from the codebase.
|
|
35
|
+
|
|
36
|
+
## Step 3: Draft the graph
|
|
37
|
+
|
|
38
|
+
At every real fork (two reasonable ways to build something), write both in a sentence, pick one and give the reason in `spec.md` under Decisions, with the other named as rejected. If the choice changes what the user gets, do not pick: make it a `settle` ticket and list it under Open questions.
|
|
39
|
+
|
|
40
|
+
Then a lazy pass over your draft. For each ticket ask, in order: does the bar still hold without it? does the codebase already do it? does the standard library, the platform or an installed dependency already do it? can it be one line, or merged into another ticket? Drop or shrink what fails, and list what you dropped so the user can overrule you. Never drop: validation at trust boundaries, handling that prevents data loss, security, accessibility, or anything the brief asks for.
|
|
41
|
+
|
|
42
|
+
## Step 4: Write the files
|
|
43
|
+
|
|
44
|
+
- `spec.md` from [templates/spec.md](templates/spec.md). One document. Remove the sections that do not apply. Its Problem and Goal carry the brief's What, Why and bar, so the plan never depends on the conversation.
|
|
45
|
+
- `map.md` from [templates/map.md](templates/map.md).
|
|
46
|
+
- `commands.md` from [templates/commands.md](templates/commands.md), filled with the real commands from Step 2.
|
|
47
|
+
- `learnings.md` from [templates/learnings.md](templates/learnings.md), left empty. Workers append to it; it is separate from the map because it grows and the map should stay short.
|
|
48
|
+
- One `tasks/NN-slug.md` per ticket from [templates/ticket.md](templates/ticket.md).
|
|
49
|
+
- Screens? Add mockups to the spec using [templates/ui-mockup.md](templates/ui-mockup.md).
|
|
50
|
+
|
|
51
|
+
### Ticket rules
|
|
52
|
+
|
|
53
|
+
1. **One ticket is one change that leaves the build green**, small enough for one session. If Done when needs more than about 5 bullets, or Not in this ticket spans two areas, split it.
|
|
54
|
+
2. **`Blocked by` only for a real dependency**: the ticket cannot start without another's output. Independent tickets stay unblocked. Do not chain tickets just because of their numbers.
|
|
55
|
+
3. **Types**:
|
|
56
|
+
- `task`: normal work.
|
|
57
|
+
- `settle`: a decision that later tickets depend on. The Answer is the decision. Use it when two tickets would otherwise decide the same thing differently.
|
|
58
|
+
- `convert`: migrating data, code or a format. It must be a script that gives the same result when run twice, list every transform, leave anything that does not match unchanged and print it, state expected counts in Done when ("reports N converted, M left alone"), include a test that no old shape survives, and change the writer or generator so new output is already the new shape.
|
|
59
|
+
4. **Done when must be checkable by a stranger**: the command and the result it prints. Never hard code the count of existing tests; "all existing tests still pass" is enough. A measured invariant is fine ("24 of 24 files round trip").
|
|
60
|
+
5. **Not in this ticket** names the nearest thing a reader would assume is included, and where it lives instead.
|
|
61
|
+
6. **Survive code drift**: no line numbers. Name functions and classes, and give a search hint for insertion points ("after the call to `parseConfig()`").
|
|
62
|
+
7. **Tests ride with the code they cover**: every ticket adds its own. Frontend tickets come after the API shape is stable.
|
|
63
|
+
8. **Set `Test first:`** on every ticket. `yes` for anything that adds or changes logic (the worker writes the failing test before the code), `no` for config, docs, renames and migrations that a command already proves.
|
|
64
|
+
9. **The last ticket is the acceptance ticket**: it runs the map's bar end to end and is blocked by every ticket that leaves the bar unproven.
|
|
65
|
+
10. **Number from 01 and never renumber.** Tickets added later get the next number.
|
|
66
|
+
11. **More than about 12 tickets?** Plan in rounds: `plans/<feature>/` first, then `plans/<feature>-round-two/` with its own map that links back.
|
|
67
|
+
12. **`Floor: allow <categories>` only when the brief says so.** The floor guard fails a ticket whose diff skips or deletes tests, silences checks, adds empty catches, lowers thresholds, or edits lint, test or CI config. It also fails a worker that edits anything in the plan except its own Status and Answer, appended lines in `map.md` and `learnings.md`, and it fails any change to `commands.md`. If a ticket legitimately has to (for example "migrate the test runner", which changes `commands.md`), add a line such as `Floor: allow config, test-delete, commands-edit` to that ticket, and say so in your reply. Categories: `skip`, `suppress`, `empty-catch`, `test-delete`, `threshold`, `config`, `ticket-edit`, `commands-edit`. The line is read from the ticket as it was before the work started, so a worker cannot add it for itself.
|
|
68
|
+
13. **Name a ticket in prose only if it is ordered against this one.** If a ticket's text says "uses the output of ticket 02", list 02 under `Blocked by`. `--check` warns about mentions that are not ordered.
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## Step 5: Check
|
|
72
|
+
|
|
73
|
+
Run `FLOW_DIR=.feature-flow/state/draft python3 scripts/flow-status.py <feature> --check`. It fails on a missing blocker, a cycle, a missing Done when, a duplicate number, a bad `Test first` value, or no ready ticket, and it warns about tickets that name another ticket in their text without being ordered against it. If the script is not installed, check those by hand. Fix every problem and every warning before you reply.
|
|
74
|
+
|
|
75
|
+
## Step 6: Reply
|
|
76
|
+
|
|
77
|
+
Reply in this shape, with nothing after the last line:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
GOAL: <one sentence>
|
|
81
|
+
BAR: <the command and what it should print>
|
|
82
|
+
COMMANDS: Build <cmd> | Test <cmd> | Lint <cmd> | Smoke <cmd>
|
|
83
|
+
GRAPH:
|
|
84
|
+
01 Define the types
|
|
85
|
+
02 Build the service after 01
|
|
86
|
+
03 Acceptance after 02
|
|
87
|
+
DROPPED: <each dropped ticket and why, or none>
|
|
88
|
+
ASSUMED: <each Unsure line: kept, or replaced by what the code showed>
|
|
89
|
+
SOURCES: <outside sources relied on, or none; say if you had no web access>
|
|
90
|
+
OPEN QUESTIONS: <what only the user can decide, or none>
|
|
91
|
+
PLAN: READY
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
End with `PLAN: QUESTIONS` instead when the draft cannot be finished without an answer from the user. Then list the questions under OPEN QUESTIONS and leave the draft as far as you got.
|
|
@@ -8,7 +8,7 @@ If no feature was given, list `plans/*/` and ask which one.
|
|
|
8
8
|
|
|
9
9
|
Run `python3 scripts/flow-view.py <feature>`. Add `--watch` when the user said `watch`. It writes one HTML file, normally `.git/flow-<feature>.html`, and opens it in the default browser.
|
|
10
10
|
|
|
11
|
-
If the script is not installed, say so and point to `install.
|
|
11
|
+
If the script is not installed, say so and point to `uvx feature-flow-cli install .` (or `python3 install.py <repo>` from a feature-flow clone). If no browser could be opened (a remote machine, for example), print the path and tell the user to open it. Set `FLOW_NO_OPEN=1` to never open one.
|
|
12
12
|
|
|
13
13
|
## Step 2: Summarise it in words
|
|
14
14
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: feature-flow
|
|
3
|
-
description: Use to plan a feature or build its tickets, one fresh builder and one fresh reviewer subagent per ticket, with scripts/flow.py deciding every step. Also draws the ticket graph with show.
|
|
3
|
+
description: Use to plan a feature (from the conversation so far, with a fresh planner and plan reviewer subagent) or build its tickets, one fresh builder and one fresh reviewer subagent per ticket, with scripts/flow.py deciding every step. Also draws the ticket graph with show.
|
|
4
4
|
argument-hint: "[feature] [show] [auto]"
|
|
5
5
|
disable-model-invocation: true
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Plan or build the feature in `$ARGUMENTS`.
|
|
9
9
|
|
|
10
|
-
The first word is the **feature**. `show` means draw the graph. `auto` means ask nothing and go. If no feature was given, list `plans/*/` and ask which one.
|
|
10
|
+
The first word is the **feature**. `show` means draw the graph. `auto` means ask nothing and go. If no feature was given and the conversation so far describes work to build, plan it (below) and propose the feature's name in the brief. If no feature was given otherwise, list `plans/*/` and ask which one.
|
|
11
11
|
|
|
12
12
|
The conductor is `python3 scripts/flow.py <feature> <command>`. It decides the order, runs the gate and the floor guard, and keeps its state in `.feature-flow/state/`, a folder git ignores. You ask it, and you do what it says. The guides it uses are in `guides/` or `.feature-flow/guides/`.
|
|
13
13
|
|
|
@@ -17,11 +17,15 @@ With `show`, follow `guides/show.md` for the feature and stop.
|
|
|
17
17
|
|
|
18
18
|
## Plan
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
When `plans/<feature>/` does not exist, or you are planning from the conversation, follow `guides/brief.md` with the user. You write the brief and ask for both yeses (unless `auto`); a subagent plans, another reviews, and you never draft the plan yourself. Once the brief names the feature, run `FLOW_INVOKE=/feature-flow python3 scripts/flow.py <feature> start` and check that it prints `PLAN`, and check that `feature-planner.md` and `plan-reviewer.md` are installed where the builder's role is (see below).
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
- `plan-prompt <brief>`: spawn a `feature-planner` subagent with the whole output as its prompt. Wait for its final reply.
|
|
23
|
+
- `plan-review-prompt`: spawn a `plan-reviewer` subagent with the whole output, never with the planner's reply. Wait for its final reply and save it with Bash into `.feature-flow/state/plan-review-<feature>.txt`.
|
|
24
|
+
- `plan-accept`: writes `plans/<feature>/` from the draft, only after the second yes.
|
|
25
|
+
|
|
26
|
+
Then run `python3 scripts/flow-status.py <feature> --check`, list the files `plan-accept` wrote, and suggest the commit command (`git add plans/<feature> && git commit -m "docs(<feature>): plan"`). Say to commit the plan and run `/feature-flow <feature>` again. While the plan is uncommitted, do not say the feature is ready to build.
|
|
27
|
+
|
|
28
|
+
To add tickets to a plan that exists, edit it by hand following the ticket rules in `guides/plan.md`.
|
|
25
29
|
|
|
26
30
|
## Before building
|
|
27
31
|
|
|
@@ -29,7 +33,7 @@ With a plan present, check these before `start`, and stop at the first that fail
|
|
|
29
33
|
|
|
30
34
|
- `git status --porcelain` is empty.
|
|
31
35
|
- `python3 scripts/flow-status.py <feature> --check` prints `OK`.
|
|
32
|
-
- `ticket-builder.md` and `ticket-reviewer.md` are in `.claude/agents/`, `~/.claude/agents/`, or `$CLAUDE_HOME/agents/` when `CLAUDE_HOME` is set. If not, say to run `
|
|
36
|
+
- `ticket-builder.md` and `ticket-reviewer.md` are in `.claude/agents/`, `~/.claude/agents/`, or `$CLAUDE_HOME/agents/` when `CLAUDE_HOME` is set. If not, say to run `uvx feature-flow-cli install .` in this repo (or `python3 install.py <repo>` from a feature-flow clone).
|
|
33
37
|
|
|
34
38
|
Then run `FLOW_INVOKE=/feature-flow python3 scripts/flow.py <feature> start`. It prints `OK <token>`. Keep the token and put `FLOW_SESSION=<token>` in front of **every** later conductor command, with `FLOW_INVOKE=/feature-flow`.
|
|
35
39
|
|
|
@@ -53,7 +57,7 @@ A conductor command looks like this:
|
|
|
53
57
|
|
|
54
58
|
## Rules while building
|
|
55
59
|
|
|
56
|
-
These apply from `start` on, once a plan exists.
|
|
60
|
+
These apply from `start` on, once a plan exists.
|
|
57
61
|
|
|
58
62
|
- Never run the gate, the floor guard or a review yourself. The conductor runs the checks, and the reviewer subagent reviews.
|
|
59
63
|
- Never edit a ticket, the plan or the code, and never commit. The builder does that.
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# Plan a feature
|
|
2
|
-
|
|
3
|
-
Plan the **feature** you were given (called `<feature>` below) as one spec, one map and a graph of small tickets.
|
|
4
|
-
|
|
5
|
-
## Where the files go
|
|
6
|
-
|
|
7
|
-
First look for a documented convention (CLAUDE.md, AGENTS.md, `docs/`). If the repo already says where tickets live, use that place, and tell the user the two environment variables the scripts need (`FLOW_DIR`, `FLOW_TICKETS`).
|
|
8
|
-
|
|
9
|
-
Otherwise create a `plans/` folder at the repo root if there is none, then one folder named after the feature inside it:
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
plans/
|
|
13
|
-
└── <feature>/
|
|
14
|
-
├── spec.md what and why
|
|
15
|
-
├── map.md destination, decisions so far, open questions
|
|
16
|
-
└── tasks/
|
|
17
|
-
├── 01-slug.md one ticket per file
|
|
18
|
-
└── 02-slug.md
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
If the folder already exists, stop and ask: add tickets after the highest number, or pick another name. Never overwrite.
|
|
22
|
-
|
|
23
|
-
## Step 1: Understand the feature
|
|
24
|
-
|
|
25
|
-
If the user already described it well, skip to Step 2. Otherwise ask only what the code cannot tell you:
|
|
26
|
-
|
|
27
|
-
- **What** it does and what problem it solves
|
|
28
|
-
- **Scope**: backend, frontend or both; what is explicitly out
|
|
29
|
-
- **Dependencies** on other work
|
|
30
|
-
- **The bar**: how will we know the whole feature works? One command and the output it should print.
|
|
31
|
-
|
|
32
|
-
## Step 2: Research the codebase
|
|
33
|
-
|
|
34
|
-
Find similar features and how they were built, the files that will change, the existing types and APIs the feature touches, and the test conventions. Also find the **real** build, test and lint commands. Tickets will quote them and `commands.md` will record them, so read them from `package.json`, `Makefile`, `Taskfile.yml`, CI config or the project's agent instructions file (CLAUDE.md or AGENTS.md). Do not guess. Also pick a **smoke command**: the quickest one that proves the base is healthy (usually build plus the fast tests). The flow runs it before every ticket, and runs Build, Test and Lint after every ticket, so all of them must run without asking anything and exit non zero on failure.
|
|
35
|
-
|
|
36
|
-
## Step 3: Draft the graph and get a yes
|
|
37
|
-
|
|
38
|
-
First, a lazy pass over your draft. For each ticket ask, in order: does the bar still hold without it? does the codebase already do it? does the standard library, the platform or an installed dependency already do it? can it be one line, or merged into another ticket? Drop or shrink what fails, and list what you dropped so the user can overrule you. Never drop: validation at trust boundaries, handling that prevents data loss, security, accessibility, or anything the user asked for.
|
|
39
|
-
|
|
40
|
-
Then, before writing any file, show the user the goal, the bar, the build, test and smoke commands you found, the tickets as a compact graph, and the dropped list:
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
01 Define the types
|
|
44
|
-
02 Build the service after 01
|
|
45
|
-
03 Wire it into the API after 02
|
|
46
|
-
04 Acceptance after 03
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Ask them to confirm, edit or cancel. Write nothing until they say yes. Skip this only if they already approved a ticket list.
|
|
50
|
-
|
|
51
|
-
## Step 4: Write the files
|
|
52
|
-
|
|
53
|
-
- `spec.md` from [templates/spec.md](templates/spec.md). One document. Remove the sections that do not apply.
|
|
54
|
-
- `map.md` from [templates/map.md](templates/map.md).
|
|
55
|
-
- `commands.md` from [templates/commands.md](templates/commands.md), filled with the real commands from Step 2. Show it to the user with the graph: it is frozen once approved.
|
|
56
|
-
- `learnings.md` from [templates/learnings.md](templates/learnings.md), left empty. Workers append to it; it is separate from the map because it grows and the map should stay short.
|
|
57
|
-
- One `tasks/NN-slug.md` per ticket from [templates/ticket.md](templates/ticket.md).
|
|
58
|
-
- Screens? Add mockups to the spec using [templates/ui-mockup.md](templates/ui-mockup.md).
|
|
59
|
-
|
|
60
|
-
### Ticket rules
|
|
61
|
-
|
|
62
|
-
1. **One ticket is one change that leaves the build green**, small enough for one session. If Done when needs more than about 5 bullets, or Not in this ticket spans two areas, split it.
|
|
63
|
-
2. **`Blocked by` only for a real dependency**: the ticket cannot start without another's output. Independent tickets stay unblocked. Do not chain tickets just because of their numbers.
|
|
64
|
-
3. **Types**:
|
|
65
|
-
- `task`: normal work.
|
|
66
|
-
- `settle`: a decision that later tickets depend on. The Answer is the decision. Use it when two tickets would otherwise decide the same thing differently.
|
|
67
|
-
- `convert`: migrating data, code or a format. It must be a script that gives the same result when run twice, list every transform, leave anything that does not match unchanged and print it, state expected counts in Done when ("reports N converted, M left alone"), include a test that no old shape survives, and change the writer or generator so new output is already the new shape.
|
|
68
|
-
4. **Done when must be checkable by a stranger**: the command and the result it prints. Never hard code the count of existing tests; "all existing tests still pass" is enough. A measured invariant is fine ("24 of 24 files round trip").
|
|
69
|
-
5. **Not in this ticket** names the nearest thing a reader would assume is included, and where it lives instead.
|
|
70
|
-
6. **Survive code drift**: no line numbers. Name functions and classes, and give a search hint for insertion points ("after the call to `parseConfig()`").
|
|
71
|
-
7. **Tests ride with the code they cover**: every ticket adds its own. Frontend tickets come after the API shape is stable.
|
|
72
|
-
8. **Set `Test first:`** on every ticket. `yes` for anything that adds or changes logic (the worker writes the failing test before the code), `no` for config, docs, renames and migrations that a command already proves.
|
|
73
|
-
9. **The last ticket is the acceptance ticket**: it runs the map's bar end to end and is blocked by every ticket that leaves the bar unproven.
|
|
74
|
-
10. **Number from 01 and never renumber.** Tickets added later get the next number.
|
|
75
|
-
11. **More than about 12 tickets?** Plan in rounds: `plans/<feature>/` first, then `plans/<feature>-round-two/` with its own map that links back.
|
|
76
|
-
12. **`Floor: allow <categories>` only when the user says so.** The floor guard fails a ticket whose diff skips or deletes tests, silences checks, adds empty catches, lowers thresholds, or edits lint, test or CI config. It also fails a worker that edits anything in the plan except its own Status and Answer, appended lines in `map.md` and `learnings.md`, and it fails any change to `commands.md`. If a ticket legitimately has to (for example "migrate the test runner", which changes `commands.md`), add a line such as `Floor: allow config, test-delete, commands-edit` to that ticket, and tell the user you did. Categories: `skip`, `suppress`, `empty-catch`, `test-delete`, `threshold`, `config`, `ticket-edit`, `commands-edit`. The line is read from the ticket as it was before the work started, so a worker cannot add it for itself.
|
|
77
|
-
13. **Name a ticket in prose only if it is ordered against this one.** If a ticket's text says "uses the output of ticket 02", list 02 under `Blocked by`. `--check` warns about mentions that are not ordered.
|
|
78
|
-
|
|
79
|
-
## Step 5: Check
|
|
80
|
-
|
|
81
|
-
Run `python3 scripts/flow-status.py <feature> --check`. It fails on a missing blocker, a cycle, a missing Done when, a duplicate number, a bad `Test first` value, or no ready ticket, and it warns about tickets that name another ticket in their text without being ordered against it. If the script is not installed, check those by hand. Fix every problem and every warning before reporting.
|
|
82
|
-
|
|
83
|
-
## Step 6: Report
|
|
84
|
-
|
|
85
|
-
```
|
|
86
|
-
Planned: plans/<feature>/
|
|
87
|
-
Tickets: N (M ready now)
|
|
88
|
-
The bar: <the command and expected output>
|
|
89
|
-
|
|
90
|
-
Graph: python3 scripts/flow-status.py <feature> --mermaid
|
|
91
|
-
|
|
92
|
-
Next: commit the plan, then start the feature-flow skill for <feature>
|
|
93
|
-
```
|
|
Binary file
|
|
File without changes
|
{feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/adapters/codex/feature-flow/agents/openai.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{feature_flow_cli-0.1.0 → feature_flow_cli-0.1.1}/feature_flow_cli.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|