feature-flow-cli 0.14.0__tar.gz → 0.18.0__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.14.0/feature_flow_cli.egg-info → feature_flow_cli-0.18.0}/PKG-INFO +57 -2
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/README.md +56 -1
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/adapters/codex/feature-flow/SKILL.md +3 -3
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/__init__.py +1 -1
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/command.py +10 -2
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/conductor.py +8 -1
- feature_flow_cli-0.18.0/feature_flow/evaluate.py +378 -0
- feature_flow_cli-0.18.0/feature_flow/importer.py +222 -0
- feature_flow_cli-0.18.0/feature_flow/profiles.py +43 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/report.py +1 -1
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/state.py +1 -1
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/status.py +8 -2
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0/feature_flow_cli.egg-info}/PKG-INFO +57 -2
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow_cli.egg-info/SOURCES.txt +3 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/templates/commands.md +3 -1
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/skills/feature-flow/SKILL.md +6 -6
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/LICENSE +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/MANIFEST.in +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/adapters/codex/feature-flow/agents/openai.yaml +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/agents/feature-planner.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/agents/flow-architect.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/agents/flow-designer.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/agents/flow-po.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/agents/flow-scout.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/agents/flow-web.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/agents/plan-reviewer.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/agents/ticket-builder.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/agents/ticket-reviewer.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/__main__.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/checks.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/cli.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/codehash.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/floorguard.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/gate.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/git.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/install.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/lanes.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/proc.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/prompts.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/proof.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/released.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/scouts.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/suggest.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/tickets.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/uninstall.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow/view.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow_cli.egg-info/dependency_links.txt +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow_cli.egg-info/entry_points.txt +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/feature_flow_cli.egg-info/top_level.txt +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/brief.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/build.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/debug.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/plan-review.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/plan.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/replan.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/review-feature.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/review-quality.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/review-single.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/review.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/show.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/templates/learnings.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/templates/map.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/templates/spec.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/templates/ticket.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/guides/templates/ui-mockup.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/install.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/install.sh +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/pyproject.toml +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/scripts/floor-guard.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/scripts/flow-status.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/scripts/flow-trust.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/scripts/flow-view.html +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/scripts/flow-view.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/scripts/flow.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/scripts/gate.py +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/setup.cfg +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/skills/architect-review/SKILL.md +0 -0
- {feature_flow_cli-0.14.0 → feature_flow_cli-0.18.0}/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.
|
|
3
|
+
Version: 0.18.0
|
|
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,13 +45,22 @@ 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
|
+
As a Claude Code plugin, with nothing written into your repo (this repo is its own marketplace):
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
/plugin marketplace add hamilton-sky/feature-flow
|
|
52
|
+
/plugin install feature-flow@feature-flow
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The plugin carries the skills, the roles (named `feature-flow:ticket-builder` and so on), the conductor, the scripts and the guides, and the skill runs the conductor from the plugin's folder. A repo with its own install still uses that one. Claude Code cloud sessions (claude.ai/code) do not install plugins, so for them install into the repo and commit `.claude/`, `scripts/` and `.feature-flow/` as below. The plugin path is new and has not had a real run yet.
|
|
56
|
+
|
|
48
57
|
Without a clone, the same installer runs from [PyPI](https://pypi.org/project/feature-flow-cli/):
|
|
49
58
|
|
|
50
59
|
```bash
|
|
51
60
|
uvx feature-flow-cli install /path/to/your/repo --agent all # or: pipx run feature-flow-cli install ...
|
|
52
61
|
```
|
|
53
62
|
|
|
54
|
-
The package puts a `feature-flow` command on your PATH with `install`, `uninstall`, `status <feature>`, `view <feature>`, `reset <feature>` and `
|
|
63
|
+
The package puts a `feature-flow` command on your PATH with `install`, `uninstall`, `status <feature>`, `view <feature>`, `reset <feature>`, `report <feature>` and `eval <feature>`. It installs the same files, byte for byte, as `install.py` from a clone.
|
|
55
64
|
|
|
56
65
|
The installer lists what it wrote in `.feature-flow/installed.txt`, with each file's hash in `.feature-flow/installed.sha256`. Commit those files (`git add --pathspec-from-file=.feature-flow/installed.txt`); until you do, the flow does not count them as uncommitted changes. Installing a newer version over an older one updates every file nobody edited since it was installed, and keeps (and names) the ones you changed; `--force` replaces those too.
|
|
57
66
|
|
|
@@ -305,6 +314,49 @@ A builder, reviewer or planner that hits a question it should not decide alone a
|
|
|
305
314
|
- **Still over, a re-plan offer.** Once fast mode has finished a ticket and the run is still over (or at once, if fast mode was already on), the run stops once with the minutes spent, the tickets left, and an offer to re-plan. If one ticket took more than half the time over several attempts, the stop names it as stuck, since that is a stuck ticket, not a slow plan. `next` goes on as planned, and a new `budget` earns a new offer.
|
|
306
315
|
- **The re-plan needs your yes.** On your yes, between tickets, `replan-prompt` hands the `feature-planner` the open tickets with `guides/replan.md`. It folds them into fewer, larger tickets with the same bar: every Done when item and check moves into the ticket that takes over, folded tickets become `Status: parked (folded into NN)`, and resolved tickets stay as they are. The session shows you the change and commits it only on a second yes. The next `next` stops if the re-plan changed a resolved ticket. Nothing is ever cut without you.
|
|
307
316
|
|
|
317
|
+
## Recording a run (eval)
|
|
318
|
+
|
|
319
|
+
Every finished run can leave a record of what it cost and what it caught, so a trial on your own repo gives an answer, not an impression. On `DONE` the skill runs `python3 scripts/flow-status.py <feature> --eval` (or run `feature-flow eval <feature>` yourself) and asks you a few questions.
|
|
320
|
+
|
|
321
|
+
- **`plans/<feature>/eval.json`**: tickets, builds, reviews and rework; every catch by the check that made it (gate, floor guard, Done when, test first, footprint, reviewer, feature review); where the run needed you (stops, questions, handoffs, resets); active minutes; and tokens.
|
|
322
|
+
- **Tokens** come from Claude Code's own session transcripts (`~/.claude/projects/`, or `CLAUDE_CONFIG_DIR`), never from the agent: every message of the sessions and subagents that worked in this repo during the run, each counted once, by model and, without lanes, split per ticket by time. Codex sessions are not read yet.
|
|
323
|
+
- **`plans/<feature>/eval-verdict.md`**: a form for you. Was the plan right, how many fixes you made by hand, would you use it again, and for each catch whether it was real or a false alarm. Run `--eval` again and your answers land in `eval.json`. The false-alarm count is the only way to tell whether the checks are worth their cost.
|
|
324
|
+
- **Later:** `feature-flow eval <feature> --later`, a week or two on, lists the commits since the run whose subject says fix, revert, bug or regression on the paths each ticket's `Touches:` names: the defects the run let through.
|
|
325
|
+
|
|
326
|
+
Both files sit in the plan folder, so they are committed with it and survive a cloud session. Nothing is sent anywhere; attach `eval.json` to a field-run issue if you want to share it. Run logs from before 0.15.0 have no dates, so they give no tokens.
|
|
327
|
+
|
|
328
|
+
## Profiles
|
|
329
|
+
|
|
330
|
+
Most settings below never need touching. One word picks a sensible set:
|
|
331
|
+
|
|
332
|
+
| Profile | Sets | For |
|
|
333
|
+
|---|---|---|
|
|
334
|
+
| `careful` | `FLOW_GATE_FULL=1`, `FLOW_FAST=0`, `FLOW_SMALL_LINES=0`, `FLOW_TICKETS_PER_SESSION=3` | code where a wrong "done" is expensive: the full gate and an own review for every ticket |
|
|
335
|
+
| `normal` | nothing: the defaults | everyday features |
|
|
336
|
+
| `fast` | `FLOW_FAST=1`, `FLOW_SCOUTS=off`, `FLOW_TICKETS_PER_SESSION=6` | small, low-risk features: one feature review at the end |
|
|
337
|
+
|
|
338
|
+
Pick one with `/feature-flow <feature> careful` (or `fast`), `FLOW_PROFILE=<name>` on `start`, or a `Profile:` line in the plan's `commands.md`, approved with the plan. A profile fills only settings you did not set: `FLOW_PROFILE=fast FLOW_SCOUTS=on` keeps the scouts. The log records `PROFILE-<NAME>` at the start. A level 3 or `Sensitive:` ticket keeps its own reviews in every profile.
|
|
339
|
+
|
|
340
|
+
The long `python3 -I -c ...` line the skill uses to call the conductor stays as it is: it is how the skill checks `flow-trust.py` against the sha256 it pins before running it, and a shorter wrapper would be one more file nothing checks. The session copies it from the skill; you never type it.
|
|
341
|
+
|
|
342
|
+
## Starting from Spec Kit or OpenSpec
|
|
343
|
+
|
|
344
|
+
If you already plan with [GitHub Spec Kit](https://github.com/github/spec-kit) or [OpenSpec](https://github.com/Fission-AI/OpenSpec), feature-flow can build what they planned:
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
feature-flow import specs/001-photo-albums albums # a Spec Kit feature folder (or its tasks.md)
|
|
348
|
+
feature-flow import openspec/changes/add-auth auth # an OpenSpec change folder
|
|
349
|
+
python3 scripts/flow-status.py albums --import specs/001-photo-albums # the same, from a repo install
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Or ask the session to plan `albums` from `specs/001-photo-albums`. Either way it writes `plans/<feature>/`, never over an existing plan (`--dry-run` shows what it would write):
|
|
353
|
+
|
|
354
|
+
- **One ticket per phase or section**, its tasks as the Done when items, so thirty small tasks become five tickets with real reviews, not thirty.
|
|
355
|
+
- **Order:** each section waits for the one before. Spec Kit's user story phases (`[US1]`, `[US2]`) wait only for the phases before the first story, so with lanes they are built side by side, and the polish phase waits for every story.
|
|
356
|
+
- **Touches:** the paths the tasks name, when every task in the section names one.
|
|
357
|
+
- **spec.md** carries the source's `spec.md` and `plan.md` (Spec Kit) or `proposal.md` and `design.md` (OpenSpec).
|
|
358
|
+
- **What you add:** the commands (`commands.md` is the template), a `check` block per ticket and the levels. `flow-status.py <feature> --lint` says what is missing. The skill does this with you, then asks for a yes before it commits the plan.
|
|
359
|
+
|
|
308
360
|
## Long features and handoff
|
|
309
361
|
|
|
310
362
|
A session's context fills up, so the flow moves to a fresh session every few tickets. After `FLOW_TICKETS_PER_SESSION` tickets (default 4) pass review, `next` prints a `HANDOFF` line instead of the next ticket, for example `HANDOFF /feature-flow csv-export 3f9c0a1b2c3d4e5f.0a1b2c3d4e5f6a7b` (the last word is the runner's digest, which lets the new session check that the flow's files did not change in between). The session stops and tells you to open a new session and type that line. The new session picks up exactly where the last one stopped. Set `FLOW_TICKETS_PER_SESSION=0` to never hand off.
|
|
@@ -329,6 +381,8 @@ python3 scripts/flow-status.py <feature> --counts one line of counts
|
|
|
329
381
|
python3 scripts/flow-status.py <feature> --check cycles, missing blockers, missing Done when, unordered mentions
|
|
330
382
|
python3 scripts/flow-status.py <feature> --lint --check plus: a check block (or `Check: none <why>`) on every ticket, a Test command
|
|
331
383
|
feature-flow report <feature> what a run did, from its log: builds, reviews, failed checks, stops
|
|
384
|
+
feature-flow import <spec-kit or openspec folder> <feature> start plans/<feature>/ from their task list
|
|
385
|
+
feature-flow eval <feature> [--later] record the run in plans/<feature>/eval.json, with your verdict (also: flow-status.py <feature> --eval)
|
|
332
386
|
python3 scripts/flow-status.py <feature> --mermaid the ticket graph, coloured by status (add "plain" for none)
|
|
333
387
|
python3 scripts/flow-status.py <feature> --json every ticket with status, blockers and readiness
|
|
334
388
|
python3 scripts/flow-view.py <feature> [--watch] the animated graph page, opened in your browser
|
|
@@ -344,6 +398,7 @@ The commands in `commands.md` run through `bash -c` on Linux and macOS and throu
|
|
|
344
398
|
|
|
345
399
|
| Variable | Default | Meaning |
|
|
346
400
|
|---|---|---|
|
|
401
|
+
| `FLOW_PROFILE` | unset | `careful`, `normal` or `fast`: fills the settings in this table that are not set; a `Profile:` line in `commands.md` does the same |
|
|
347
402
|
| `FLOW_TICKETS_PER_SESSION` | `4` | tickets per session before `HANDOFF`; `0` never hands off |
|
|
348
403
|
| `FLOW_RELAY` | `0` | `1` hands off after every build and every review |
|
|
349
404
|
| `FLOW_LANES` | `1` | read by `start`: `2` to `4` builds tickets with disjoint `Touches:` side by side in worktrees (not with `FLOW_RELAY`) |
|
|
@@ -26,13 +26,22 @@ 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
|
+
As a Claude Code plugin, with nothing written into your repo (this repo is its own marketplace):
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
/plugin marketplace add hamilton-sky/feature-flow
|
|
33
|
+
/plugin install feature-flow@feature-flow
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The plugin carries the skills, the roles (named `feature-flow:ticket-builder` and so on), the conductor, the scripts and the guides, and the skill runs the conductor from the plugin's folder. A repo with its own install still uses that one. Claude Code cloud sessions (claude.ai/code) do not install plugins, so for them install into the repo and commit `.claude/`, `scripts/` and `.feature-flow/` as below. The plugin path is new and has not had a real run yet.
|
|
37
|
+
|
|
29
38
|
Without a clone, the same installer runs from [PyPI](https://pypi.org/project/feature-flow-cli/):
|
|
30
39
|
|
|
31
40
|
```bash
|
|
32
41
|
uvx feature-flow-cli install /path/to/your/repo --agent all # or: pipx run feature-flow-cli install ...
|
|
33
42
|
```
|
|
34
43
|
|
|
35
|
-
The package puts a `feature-flow` command on your PATH with `install`, `uninstall`, `status <feature>`, `view <feature>`, `reset <feature>` and `
|
|
44
|
+
The package puts a `feature-flow` command on your PATH with `install`, `uninstall`, `status <feature>`, `view <feature>`, `reset <feature>`, `report <feature>` and `eval <feature>`. It installs the same files, byte for byte, as `install.py` from a clone.
|
|
36
45
|
|
|
37
46
|
The installer lists what it wrote in `.feature-flow/installed.txt`, with each file's hash in `.feature-flow/installed.sha256`. Commit those files (`git add --pathspec-from-file=.feature-flow/installed.txt`); until you do, the flow does not count them as uncommitted changes. Installing a newer version over an older one updates every file nobody edited since it was installed, and keeps (and names) the ones you changed; `--force` replaces those too.
|
|
38
47
|
|
|
@@ -286,6 +295,49 @@ A builder, reviewer or planner that hits a question it should not decide alone a
|
|
|
286
295
|
- **Still over, a re-plan offer.** Once fast mode has finished a ticket and the run is still over (or at once, if fast mode was already on), the run stops once with the minutes spent, the tickets left, and an offer to re-plan. If one ticket took more than half the time over several attempts, the stop names it as stuck, since that is a stuck ticket, not a slow plan. `next` goes on as planned, and a new `budget` earns a new offer.
|
|
287
296
|
- **The re-plan needs your yes.** On your yes, between tickets, `replan-prompt` hands the `feature-planner` the open tickets with `guides/replan.md`. It folds them into fewer, larger tickets with the same bar: every Done when item and check moves into the ticket that takes over, folded tickets become `Status: parked (folded into NN)`, and resolved tickets stay as they are. The session shows you the change and commits it only on a second yes. The next `next` stops if the re-plan changed a resolved ticket. Nothing is ever cut without you.
|
|
288
297
|
|
|
298
|
+
## Recording a run (eval)
|
|
299
|
+
|
|
300
|
+
Every finished run can leave a record of what it cost and what it caught, so a trial on your own repo gives an answer, not an impression. On `DONE` the skill runs `python3 scripts/flow-status.py <feature> --eval` (or run `feature-flow eval <feature>` yourself) and asks you a few questions.
|
|
301
|
+
|
|
302
|
+
- **`plans/<feature>/eval.json`**: tickets, builds, reviews and rework; every catch by the check that made it (gate, floor guard, Done when, test first, footprint, reviewer, feature review); where the run needed you (stops, questions, handoffs, resets); active minutes; and tokens.
|
|
303
|
+
- **Tokens** come from Claude Code's own session transcripts (`~/.claude/projects/`, or `CLAUDE_CONFIG_DIR`), never from the agent: every message of the sessions and subagents that worked in this repo during the run, each counted once, by model and, without lanes, split per ticket by time. Codex sessions are not read yet.
|
|
304
|
+
- **`plans/<feature>/eval-verdict.md`**: a form for you. Was the plan right, how many fixes you made by hand, would you use it again, and for each catch whether it was real or a false alarm. Run `--eval` again and your answers land in `eval.json`. The false-alarm count is the only way to tell whether the checks are worth their cost.
|
|
305
|
+
- **Later:** `feature-flow eval <feature> --later`, a week or two on, lists the commits since the run whose subject says fix, revert, bug or regression on the paths each ticket's `Touches:` names: the defects the run let through.
|
|
306
|
+
|
|
307
|
+
Both files sit in the plan folder, so they are committed with it and survive a cloud session. Nothing is sent anywhere; attach `eval.json` to a field-run issue if you want to share it. Run logs from before 0.15.0 have no dates, so they give no tokens.
|
|
308
|
+
|
|
309
|
+
## Profiles
|
|
310
|
+
|
|
311
|
+
Most settings below never need touching. One word picks a sensible set:
|
|
312
|
+
|
|
313
|
+
| Profile | Sets | For |
|
|
314
|
+
|---|---|---|
|
|
315
|
+
| `careful` | `FLOW_GATE_FULL=1`, `FLOW_FAST=0`, `FLOW_SMALL_LINES=0`, `FLOW_TICKETS_PER_SESSION=3` | code where a wrong "done" is expensive: the full gate and an own review for every ticket |
|
|
316
|
+
| `normal` | nothing: the defaults | everyday features |
|
|
317
|
+
| `fast` | `FLOW_FAST=1`, `FLOW_SCOUTS=off`, `FLOW_TICKETS_PER_SESSION=6` | small, low-risk features: one feature review at the end |
|
|
318
|
+
|
|
319
|
+
Pick one with `/feature-flow <feature> careful` (or `fast`), `FLOW_PROFILE=<name>` on `start`, or a `Profile:` line in the plan's `commands.md`, approved with the plan. A profile fills only settings you did not set: `FLOW_PROFILE=fast FLOW_SCOUTS=on` keeps the scouts. The log records `PROFILE-<NAME>` at the start. A level 3 or `Sensitive:` ticket keeps its own reviews in every profile.
|
|
320
|
+
|
|
321
|
+
The long `python3 -I -c ...` line the skill uses to call the conductor stays as it is: it is how the skill checks `flow-trust.py` against the sha256 it pins before running it, and a shorter wrapper would be one more file nothing checks. The session copies it from the skill; you never type it.
|
|
322
|
+
|
|
323
|
+
## Starting from Spec Kit or OpenSpec
|
|
324
|
+
|
|
325
|
+
If you already plan with [GitHub Spec Kit](https://github.com/github/spec-kit) or [OpenSpec](https://github.com/Fission-AI/OpenSpec), feature-flow can build what they planned:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
feature-flow import specs/001-photo-albums albums # a Spec Kit feature folder (or its tasks.md)
|
|
329
|
+
feature-flow import openspec/changes/add-auth auth # an OpenSpec change folder
|
|
330
|
+
python3 scripts/flow-status.py albums --import specs/001-photo-albums # the same, from a repo install
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Or ask the session to plan `albums` from `specs/001-photo-albums`. Either way it writes `plans/<feature>/`, never over an existing plan (`--dry-run` shows what it would write):
|
|
334
|
+
|
|
335
|
+
- **One ticket per phase or section**, its tasks as the Done when items, so thirty small tasks become five tickets with real reviews, not thirty.
|
|
336
|
+
- **Order:** each section waits for the one before. Spec Kit's user story phases (`[US1]`, `[US2]`) wait only for the phases before the first story, so with lanes they are built side by side, and the polish phase waits for every story.
|
|
337
|
+
- **Touches:** the paths the tasks name, when every task in the section names one.
|
|
338
|
+
- **spec.md** carries the source's `spec.md` and `plan.md` (Spec Kit) or `proposal.md` and `design.md` (OpenSpec).
|
|
339
|
+
- **What you add:** the commands (`commands.md` is the template), a `check` block per ticket and the levels. `flow-status.py <feature> --lint` says what is missing. The skill does this with you, then asks for a yes before it commits the plan.
|
|
340
|
+
|
|
289
341
|
## Long features and handoff
|
|
290
342
|
|
|
291
343
|
A session's context fills up, so the flow moves to a fresh session every few tickets. After `FLOW_TICKETS_PER_SESSION` tickets (default 4) pass review, `next` prints a `HANDOFF` line instead of the next ticket, for example `HANDOFF /feature-flow csv-export 3f9c0a1b2c3d4e5f.0a1b2c3d4e5f6a7b` (the last word is the runner's digest, which lets the new session check that the flow's files did not change in between). The session stops and tells you to open a new session and type that line. The new session picks up exactly where the last one stopped. Set `FLOW_TICKETS_PER_SESSION=0` to never hand off.
|
|
@@ -310,6 +362,8 @@ python3 scripts/flow-status.py <feature> --counts one line of counts
|
|
|
310
362
|
python3 scripts/flow-status.py <feature> --check cycles, missing blockers, missing Done when, unordered mentions
|
|
311
363
|
python3 scripts/flow-status.py <feature> --lint --check plus: a check block (or `Check: none <why>`) on every ticket, a Test command
|
|
312
364
|
feature-flow report <feature> what a run did, from its log: builds, reviews, failed checks, stops
|
|
365
|
+
feature-flow import <spec-kit or openspec folder> <feature> start plans/<feature>/ from their task list
|
|
366
|
+
feature-flow eval <feature> [--later] record the run in plans/<feature>/eval.json, with your verdict (also: flow-status.py <feature> --eval)
|
|
313
367
|
python3 scripts/flow-status.py <feature> --mermaid the ticket graph, coloured by status (add "plain" for none)
|
|
314
368
|
python3 scripts/flow-status.py <feature> --json every ticket with status, blockers and readiness
|
|
315
369
|
python3 scripts/flow-view.py <feature> [--watch] the animated graph page, opened in your browser
|
|
@@ -325,6 +379,7 @@ The commands in `commands.md` run through `bash -c` on Linux and macOS and throu
|
|
|
325
379
|
|
|
326
380
|
| Variable | Default | Meaning |
|
|
327
381
|
|---|---|---|
|
|
382
|
+
| `FLOW_PROFILE` | unset | `careful`, `normal` or `fast`: fills the settings in this table that are not set; a `Profile:` line in `commands.md` does the same |
|
|
328
383
|
| `FLOW_TICKETS_PER_SESSION` | `4` | tickets per session before `HANDOFF`; `0` never hands off |
|
|
329
384
|
| `FLOW_RELAY` | `0` | `1` hands off after every build and every review |
|
|
330
385
|
| `FLOW_LANES` | `1` | read by `start`: `2` to `4` builds tickets with disjoint `Touches:` side by side in worktrees (not with `FLOW_RELAY`) |
|
|
@@ -7,7 +7,7 @@ description: "Use to plan a feature (from the conversation so far, with a fresh
|
|
|
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. `fast` means the fast path: run `start` with `FLOW_FAST=1`, and tickets below level 3 that pass their checks skip their own review until one review of the whole feature at the end (a level 3 or `Sensitive:` ticket keeps its reviews). When the user asks mid-run to go faster, run `fast on` (or `fast off`) through `flow-trust.py` between two other conductor commands; if it answers that fast is already on, offer the user the re-plan it names. A time budget (`FLOW_BUDGET=<minutes>` on `start`, `budget <minutes>|+<minutes>|off` mid-run) turns fast on by itself once spent, and later stops to offer a re-plan. A re-plan needs the user's own yes, even with `auto`: then run `replan-prompt` (between tickets), start the planner with it as in planning, with a new `task_name`, show the user its reply and `git diff --stat`, and on a second yes commit the plan folder as `plan(<feature>): re-plan the open tickets` (else `git checkout -- plans/<feature>`), then run `next`. 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.
|
|
10
|
+
The first word is the **feature**. `show` means draw the graph. `auto` means ask nothing and go. `careful` means run `start` with `FLOW_PROFILE=careful` (the full gate and an own review for every ticket, shorter sessions); a `Profile:` line in `commands.md` does the same for the plan. `fast` means the fast path: run `start` with `FLOW_FAST=1`, and tickets below level 3 that pass their checks skip their own review until one review of the whole feature at the end (a level 3 or `Sensitive:` ticket keeps its reviews). When the user asks mid-run to go faster, run `fast on` (or `fast off`) through `flow-trust.py` between two other conductor commands; if it answers that fast is already on, offer the user the re-plan it names. A time budget (`FLOW_BUDGET=<minutes>` on `start`, `budget <minutes>|+<minutes>|off` mid-run) turns fast on by itself once spent, and later stops to offer a re-plan. A re-plan needs the user's own yes, even with `auto`: then run `replan-prompt` (between tickets), start the planner with it as in planning, with a new `task_name`, show the user its reply and `git diff --stat`, and on a second yes commit the plan folder as `plan(<feature>): re-plan the open tickets` (else `git checkout -- plans/<feature>`), then run `next`. 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>`. If `scripts/flow.py` exists in the repo and the feature-flow package is beside it (`.feature-flow/feature_flow/`, or `feature_flow/` in a feature-flow checkout), use it (the repo's own install wins; an unrelated `scripts/flow.py` does not count); otherwise use `python3 "${FEATURE_FLOW_HOME:-$HOME/.feature-flow}/scripts/flow.py"` (`~/.feature-flow/scripts/flow.py`). Wherever this skill or a message names `scripts/flow.py`, `scripts/flow-status.py` or `scripts/flow-view.py`, it means the same folder, and the guides are then in that folder's sibling `guides/` (`~/.feature-flow/guides/`, else `.feature-flow/guides/` in the repo). Run the conductor with the working directory in the repo: state and plans stay there. 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
|
|
|
@@ -41,7 +41,7 @@ When `plans/<feature>/` does not exist, or you are planning from the conversatio
|
|
|
41
41
|
|
|
42
42
|
Then run `python3 scripts/flow-status.py <feature> --check` and list the files `plan-accept` wrote. The second yes also approves committing the plan, so commit the folder `plan-accept` printed (`OK <folder>`, `plans/<feature>` unless `FLOW_DIR` is set) and nothing else: `git add -- <folder> && git commit -m "docs(<feature>): plan" -- <folder>`. If the commit fails, report why and stop. Then go straight on to building it below, unless the user asked to stop after planning.
|
|
43
43
|
|
|
44
|
-
To add tickets to a plan that exists, edit it by hand following the ticket rules in `.feature-flow/guides/plan.md`.
|
|
44
|
+
To add tickets to a plan that exists, edit it by hand following the ticket rules in `.feature-flow/guides/plan.md`. When the user points at a Spec Kit (`specs/<NNN-name>/`) or OpenSpec (`openspec/changes/<id>/`) task list, start the plan from it instead of the planner: `python3 <S>/flow-status.py <feature> --import <that folder or its tasks.md>` writes `plans/<feature>/` with one ticket per phase or section. Then, with the user, fill in `commands.md` from the project's own config, add a `check` block under each ticket's Done when and set its level, run `flow-status.py <feature> --lint` until it is clean, show the graph, and on the user's yes commit the folder as `docs(<feature>): plan` and build it.
|
|
45
45
|
|
|
46
46
|
## Before building
|
|
47
47
|
|
|
@@ -65,7 +65,7 @@ Before the `prompt` of every `BUILD` and `REVIEW`, run `scouts` (with the lane,
|
|
|
65
65
|
|
|
66
66
|
- `BUILD <ticket> <NN> <sha>`: run `prompt`. Start the builder with `spawn_agent(task_name="ticket_builder_<NN>_<k>", fork_turns="none", message=...)`. The message is "Work only in <repo>." followed by the whole `prompt` output, which already starts with the builder role. Use a new `task_name` for every spawn: keep a count per ticket in this session, and `<k>` is the number of builders already started for this ticket, plus 1 (1 for the first, then up each time `next` sends the ticket back to a builder). Get its final reply with `wait_agent`, then run `next` with `--after-build <ticket> <sha>` from that `BUILD` line.
|
|
67
67
|
- `REVIEW <ticket> <NN> <sha>`: run `prompt`. Start a new reviewer the same way, with `fork_turns="none"` so it never sees the builder's context, and a unique `task_name` per spawn: `ticket_reviewer_<NN>_<pass>_<k>`, where `<pass>` is `spec`, `quality` or `single` by the pass the prompt names (`(spec pass)`, `(quality pass)` or `(single pass)`; `review` if it names none) and `<k>` is the number of reviews already started for this ticket in this session, plus 1 (1 for the first). Count every review, also one that ended in `RETRY` or a failed review followed by a rebuild, so a repeated pass never reuses a name. Get its final reply with `wait_agent`. A child cannot be made read-only, so the reviewer works from its instructions; the conductor catches any edit it makes. Save the whole reply with the shell: `cat > .feature-flow/state/flow-review-<feature>.txt <<'EOF'` ... `EOF`. Then run `verdict .feature-flow/state/flow-review-<feature>.txt` and `next`. If `verdict` prints `RETRY`, just run `next`. `REVIEW <plan folder> all <sha>` is the feature review of `fast`: it has no lane word even when lanes run, so run `prompt`, `verdict` and `next` with no lane, start a new reviewer with `task_name="ticket_reviewer_all_feature_<k>"` as for any review, and save its reply the same way. On a FAIL the conductor adds a fix ticket and reviews the feature again after it. `ASK <role> <ticket> <NN> <sha>` (role `architect`, `designer` or `po`) is a builder's or reviewer's question: handle it as a `REVIEW`, scouts first, but start a `flow_<role>_<NN>_<k>` child the same way with the `prompt` output in place of the reviewer, and save its reply into `.feature-flow/state/flow-ask-<feature>.txt` for `verdict`.
|
|
68
|
-
- `DONE <summary>`: report it and suggest `$feature-flow <feature> show`.
|
|
68
|
+
- `DONE <summary>`: report it and suggest `$feature-flow <feature> show`. Then record the run: `python3 <S>/flow-status.py <feature> --eval` writes `plans/<feature>/eval.json` and the form `eval-verdict.md`. Unless `auto`, ask the user the form's questions (is the plan right, hand fixes, would they use it again, and real or false for each catch it lists), write their answers after the colons, and run `--eval` again. Commit both files as `docs(<feature>): eval` and nothing else.
|
|
69
69
|
- `STOP <reason>`: report the reason, run `python3 scripts/flow-status.py <feature>`, show the table, and stop.
|
|
70
70
|
- `HANDOFF <line>`: stop here. Tell the user to open a new session and type exactly `<line>`, the whole line with its digest at the end. The new session resumes where this one stopped.
|
|
71
71
|
|
|
@@ -9,7 +9,7 @@ import sys
|
|
|
9
9
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
|
-
from feature_flow import __version__, cli, install, report, status, suggest, uninstall, view
|
|
12
|
+
from feature_flow import __version__, cli, install, evaluate, importer, report, status, suggest, uninstall, view
|
|
13
13
|
|
|
14
14
|
USAGE = """\
|
|
15
15
|
usage: feature-flow install [target-repo] [--agent claude|codex|all] [--user] [--private] [--force] [--dry-run]
|
|
@@ -18,16 +18,20 @@ usage: feature-flow install [target-repo] [--agent claude|codex|all] [--user] [-
|
|
|
18
18
|
feature-flow view <feature> [--watch] [--no-open] [--out FILE]
|
|
19
19
|
feature-flow reset <feature> [NN]
|
|
20
20
|
feature-flow report <feature>
|
|
21
|
+
feature-flow eval <feature> [--later] [--out DIR] [--claude-home DIR]
|
|
22
|
+
feature-flow import <spec-kit or openspec folder, or its tasks.md> <feature> [--dry-run]
|
|
21
23
|
feature-flow --version
|
|
22
24
|
install copies the skill, its roles, scripts and guides into a repo; --user installs them once for every repo, in ~/.claude, ~/.agents and ~/.feature-flow, and writes nothing into a repo. feature-flow install --help says more.
|
|
23
25
|
uninstall removes exactly what install wrote (files you edited are kept); --user removes the personal install in ~/.claude, ~/.agents and ~/.feature-flow.
|
|
24
26
|
status and view read plans/<feature>/ in the current directory, like scripts/flow-status.py and scripts/flow-view.py.
|
|
25
27
|
reset reopens a half-built ticket (or ticket NN), commits that, and clears the run, like scripts/flow.py <feature> reset.
|
|
26
28
|
report prints what a run did (builds, reviews, failed checks, stops) from its log in .feature-flow/state/. Read-only.
|
|
29
|
+
given a Spec Kit or OpenSpec task list, `import` starts plans/<feature>/ with one ticket per phase or section.
|
|
30
|
+
eval records a field run in plans/<feature>/eval.json: cost, tokens, what each check caught, where it needed you, and your verdict from eval-verdict.md.
|
|
27
31
|
"""
|
|
28
32
|
|
|
29
33
|
|
|
30
|
-
COMMANDS = ("install", "uninstall", "status", "view", "reset", "report", "--version", "--help")
|
|
34
|
+
COMMANDS = ("install", "uninstall", "status", "view", "reset", "report", "eval", "import", "--version", "--help")
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
def main(argv=None):
|
|
@@ -50,6 +54,10 @@ def main(argv=None):
|
|
|
50
54
|
return view.main(rest, here=os.path.join(install.source_root(), "scripts"))
|
|
51
55
|
if command == "report":
|
|
52
56
|
return report.main(rest)
|
|
57
|
+
if command == "eval":
|
|
58
|
+
return evaluate.main(rest)
|
|
59
|
+
if command == "import":
|
|
60
|
+
return importer.main(rest)
|
|
53
61
|
if command == "reset":
|
|
54
62
|
if not rest or len(rest) > 2:
|
|
55
63
|
sys.stderr.write(USAGE)
|
|
@@ -14,7 +14,7 @@ import shutil
|
|
|
14
14
|
import time
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
|
|
17
|
-
from feature_flow import checks, codehash, floorguard, gate, git, lanes, prompts, proof, scouts, state, suggest, tickets
|
|
17
|
+
from feature_flow import checks, codehash, floorguard, gate, git, lanes, profiles, prompts, proof, scouts, state, suggest, tickets
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
NOTE_SEP = "\t" # the state file keeps one line per key, so the review notes are tab separated
|
|
@@ -94,6 +94,11 @@ class Conductor:
|
|
|
94
94
|
self.root = Path(os.environ.get("FLOW_DIR") or "plans")
|
|
95
95
|
self.plan = self.root / feature
|
|
96
96
|
self.commands = self.plan / "commands.md"
|
|
97
|
+
try:
|
|
98
|
+
self.profile = profiles.pick(os.environ, tickets.commands_value(self.commands, "Profile"))
|
|
99
|
+
except profiles.UnknownProfile as err:
|
|
100
|
+
raise Stop(str(err))
|
|
101
|
+
profiles.apply(os.environ, self.profile)
|
|
97
102
|
self.max_retries = _env_int("FLOW_MAX_RETRIES", 2)
|
|
98
103
|
self.max_rounds = _env_int("FLOW_MAX_REVIEW_ROUNDS", 3)
|
|
99
104
|
self.gate_timeout = _env_int("FLOW_GATE_TIMEOUT", 30)
|
|
@@ -849,6 +854,8 @@ class Conductor:
|
|
|
849
854
|
self.st["seen"] = int(time.time()) # the time between sessions is not spent on the feature
|
|
850
855
|
self.save()
|
|
851
856
|
self.log("TAKEOVER" if takeover else "START")
|
|
857
|
+
if self.profile:
|
|
858
|
+
self.log("PROFILE-%s" % self.profile.upper())
|
|
852
859
|
return "OK %s" % token
|
|
853
860
|
|
|
854
861
|
def next(self, lane=None):
|