feature-flow-cli 0.1.0__py3-none-any.whl

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.
Files changed (44) hide show
  1. feature_flow/__init__.py +3 -0
  2. feature_flow/__main__.py +7 -0
  3. feature_flow/_bundle/adapters/codex/feature-flow/SKILL.md +62 -0
  4. feature_flow/_bundle/adapters/codex/feature-flow/agents/openai.yaml +6 -0
  5. feature_flow/_bundle/agents/ticket-builder.md +24 -0
  6. feature_flow/_bundle/agents/ticket-reviewer.md +16 -0
  7. feature_flow/_bundle/guides/build.md +139 -0
  8. feature_flow/_bundle/guides/plan.md +93 -0
  9. feature_flow/_bundle/guides/review.md +87 -0
  10. feature_flow/_bundle/guides/show.md +30 -0
  11. feature_flow/_bundle/guides/templates/commands.md +15 -0
  12. feature_flow/_bundle/guides/templates/learnings.md +7 -0
  13. feature_flow/_bundle/guides/templates/map.md +26 -0
  14. feature_flow/_bundle/guides/templates/spec.md +62 -0
  15. feature_flow/_bundle/guides/templates/ticket.md +29 -0
  16. feature_flow/_bundle/guides/templates/ui-mockup.md +43 -0
  17. feature_flow/_bundle/scripts/floor-guard.py +14 -0
  18. feature_flow/_bundle/scripts/flow-status.py +14 -0
  19. feature_flow/_bundle/scripts/flow-view.html +536 -0
  20. feature_flow/_bundle/scripts/flow-view.py +14 -0
  21. feature_flow/_bundle/scripts/flow.py +14 -0
  22. feature_flow/_bundle/scripts/gate.py +14 -0
  23. feature_flow/_bundle/skills/architect-review/SKILL.md +143 -0
  24. feature_flow/_bundle/skills/automation-design/SKILL.md +274 -0
  25. feature_flow/_bundle/skills/feature-flow/SKILL.md +62 -0
  26. feature_flow/checks.py +71 -0
  27. feature_flow/cli.py +47 -0
  28. feature_flow/command.py +39 -0
  29. feature_flow/conductor.py +299 -0
  30. feature_flow/floorguard.py +340 -0
  31. feature_flow/gate.py +100 -0
  32. feature_flow/git.py +47 -0
  33. feature_flow/install.py +365 -0
  34. feature_flow/prompts.py +52 -0
  35. feature_flow/state.py +75 -0
  36. feature_flow/status.py +231 -0
  37. feature_flow/tickets.py +168 -0
  38. feature_flow/view.py +312 -0
  39. feature_flow_cli-0.1.0.dist-info/METADATA +293 -0
  40. feature_flow_cli-0.1.0.dist-info/RECORD +44 -0
  41. feature_flow_cli-0.1.0.dist-info/WHEEL +5 -0
  42. feature_flow_cli-0.1.0.dist-info/entry_points.txt +3 -0
  43. feature_flow_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
  44. feature_flow_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: architect-review
3
+ description: Enterprise architecture review — evaluate a module, feature, or PR diff against production-grade standards. Covers resilience, observability, data contracts, scalability, and security. Returns a prioritized findings report with severity ratings.
4
+ argument-hint: "[file-path | feature-name | 'staged' | 'last-commit']"
5
+ ---
6
+
7
+ Perform an enterprise-grade architecture review of `$ARGUMENTS`.
8
+
9
+ ## Step 0: Gather the target
10
+
11
+ - If `$ARGUMENTS` is `staged` → run `git diff --staged`
12
+ - If `$ARGUMENTS` is `last-commit` → run `git diff HEAD~1 HEAD`
13
+ - If `$ARGUMENTS` is a file path → read the file
14
+ - If `$ARGUMENTS` is a feature name → search `plans/$ARGUMENTS/` (or the project's ticket folder) and the relevant source directories
15
+ - If `$ARGUMENTS` is empty → review `git diff --staged`
16
+
17
+ Read all relevant files before forming any judgements. For backend code, also read the module file and any dependent service interfaces.
18
+
19
+ ---
20
+
21
+ ## Step 1: Structural Architecture Review
22
+
23
+ ### Module Boundaries
24
+ - [ ] **Single Responsibility**: Does each class/module own exactly one domain concern?
25
+ - [ ] **Interface Segregation**: Are interfaces narrow (consumers depend only on what they use)?
26
+ - [ ] **Dependency Direction**: Do dependencies flow inward (domain ← application ← infrastructure)?
27
+ - [ ] **Circular Dependencies**: Any imports that cycle back to the same module?
28
+ - [ ] **Module Encapsulation**: Is the public surface (exports, registered providers) deliberate, with internals kept private?
29
+
30
+ ### Data Contracts
31
+ - [ ] **Typed boundaries**: All external inputs (WS messages, HTTP bodies, API responses) validated with typed guards?
32
+ - [ ] **Discriminated unions**: Event/message types use union types, not string comparisons?
33
+ - [ ] **No `any`**: Every `any` is a contract hole — flag each one with its severity
34
+ - [ ] **Shared types**: Cross-boundary types live in one shared definition, not duplicated per side?
35
+ - [ ] **Immutability**: Config objects and props marked `readonly`?
36
+
37
+ ---
38
+
39
+ ## Step 2: Resilience & Fault Tolerance Review
40
+
41
+ For each async operation or external call found in the code:
42
+
43
+ - [ ] **Timeout**: Is there a timeout guard? Unbounded awaits are production incidents.
44
+ - [ ] **Retry**: Is retry implemented with exponential backoff + jitter for transient failures?
45
+ - [ ] **Circuit Breaker**: Is there a circuit breaker to stop cascading failure under sustained errors?
46
+ - [ ] **Idempotency**: Can this operation safely run twice without side effects?
47
+ - [ ] **Dead Letter / Fallback**: What happens when the operation permanently fails?
48
+ - [ ] **Graceful Degradation**: Does the system continue operating (degraded) when this service is unavailable?
49
+ - [ ] **Bulkhead Isolation**: Can failure in this path impact unrelated request paths?
50
+
51
+ ---
52
+
53
+ ## Step 3: Observability Review
54
+
55
+ - [ ] **Structured logging**: All log calls use the project's structured logger with metadata, no bare `console.log` / `print`
56
+ - [ ] **Correlation IDs**: Are request/session IDs propagated through async boundaries?
57
+ - [ ] **Error context**: Caught errors logged with `{ error, stack, context }` — not just `err.message`
58
+ - [ ] **Operation timing**: Are slow paths (AI calls, DB queries, external APIs) measured?
59
+ - [ ] **Failure visibility**: Are error conditions logged at `error` level, warnings at `warn`?
60
+ - [ ] **OpenTelemetry**: Are spans created for significant operations? Is trace context propagated?
61
+
62
+ ---
63
+
64
+ ## Step 4: Performance & Scalability Review
65
+
66
+ - [ ] **Blocking operations**: Any sync-heavy work on the event loop that should be offloaded?
67
+ - [ ] **N+1 queries**: Any DB or cache access inside a loop?
68
+ - [ ] **Caching opportunity**: Is expensive, repeated computation cached (Redis or in-memory with TTL)?
69
+ - [ ] **Queue offload**: Should long-running work be pushed to a background job queue instead of awaited inline?
70
+ - [ ] **Memory leaks**: Event listeners, intervals, and subscriptions cleaned up properly?
71
+ - [ ] **Pagination**: Are unbounded list queries guarded with limits?
72
+
73
+ ---
74
+
75
+ ## Step 5: Security Review
76
+
77
+ - [ ] **Input validation**: All external inputs sanitized before use?
78
+ - [ ] **Injection risk**: Any user data concatenated into queries, shell commands, or log messages?
79
+ - [ ] **Auth guards**: Are protected routes/WS handlers gated with auth checks?
80
+ - [ ] **Secret exposure**: No secrets, API keys, or PII in logs or error responses?
81
+ - [ ] **Human-in-the-loop**: Destructive or irreversible operations require explicit user approval?
82
+ - [ ] **Rate limiting**: Are high-frequency endpoints or WS message types rate-limited?
83
+
84
+ ---
85
+
86
+ ## Step 6: Testability Review
87
+
88
+ - [ ] **Dependency injection**: Are dependencies injected (not instantiated inline) so they can be mocked?
89
+ - [ ] **Pure functions**: Are complex transformations extracted as pure, easily-testable functions?
90
+ - [ ] **Test surface**: Is there a test in the project's test directory covering the primary paths?
91
+ - [ ] **Edge cases in tests**: Are error paths (timeout, null input, unavailable service) tested?
92
+
93
+ ---
94
+
95
+ ## Step 7: Project Convention Violations
96
+
97
+ Read `CLAUDE.md`, `AGENTS.md`, and any contributing or style docs in the repo. For each explicit rule or "forbidden" item they list, check whether the target violates it, and add each violation to the findings table with the rule it breaks. If the repo documents no conventions, mark this category ✅ PASS.
98
+
99
+ ---
100
+
101
+ ## Step 8: Produce the Report
102
+
103
+ Output a prioritized findings report:
104
+
105
+ ```
106
+ ## Architecture Review: [target]
107
+
108
+ ### Summary
109
+ [1-3 sentence overall assessment: is this production-ready, needs hardening, or has blockers?]
110
+
111
+ ### Severity Legend
112
+ 🔴 BLOCKER — Must fix before merge. Production incident risk or contract breakage.
113
+ 🟠 HIGH — Fix in this PR or immediately after. Degrades reliability or observability.
114
+ 🟡 MEDIUM — Fix in next iteration. Code smell or missing best practice.
115
+ 🟢 LOW — Nice to have. Minor improvement.
116
+ ✅ PASS — No issues found in this category.
117
+
118
+ ---
119
+
120
+ ### Findings
121
+
122
+ | # | Severity | Category | Location | Issue | Recommendation |
123
+ |---|----------|----------|----------|-------|----------------|
124
+ | 1 | 🔴 | Resilience | `ServiceName.method()` | No timeout on upstream API call | Wrap with `Promise.race([call, timeout(10_000)])` |
125
+ | 2 | 🟠 | Observability | `handler.ts:42` | Error caught but not logged with context | `logger.error('Failed to process', { error: e.message, sessionId })` |
126
+ ...
127
+
128
+ ---
129
+
130
+ ### Categories with No Issues
131
+ ✅ Data Contracts — all types narrow and properly guarded
132
+ ✅ Security — inputs validated, auth guards present
133
+ ...
134
+
135
+ ---
136
+
137
+ ### Recommended Next Actions
138
+ 1. [Highest priority fix — one sentence]
139
+ 2. [Second priority fix]
140
+ 3. [Optional improvement]
141
+ ```
142
+
143
+ If there are zero findings in a category, mark it ✅ PASS — do not pad the report with empty sections.
@@ -0,0 +1,274 @@
1
+ ---
2
+ name: automation-design
3
+ description: Design an enterprise-grade automation pipeline or workflow system. Takes a plain-language description of what needs to be automated and produces a full technical blueprint: pipeline stages, fault-tolerance model, state machine, data contracts, observability plan, and a phased implementation plan that the feature-flow skill can turn into tickets.
4
+ argument-hint: "[plain-language description of the automation, e.g., 'auto-retry failed jobs with backoff', 'nightly batch import pipeline', 'resume interrupted uploads']"
5
+ ---
6
+
7
+ Design an enterprise automation system for: **$ARGUMENTS**
8
+
9
+ You are a world-class automation architect. You design systems that are **reliable at scale**, **observable in production**, and **maintainable by a team**. Every automation you design has a clear failure model, not just a happy path.
10
+
11
+ ---
12
+
13
+ ## Step 1: Clarify the Automation Scope
14
+
15
+ Before designing, establish:
16
+
17
+ 1. **Trigger**: What starts the automation? (user action, schedule, event, webhook, threshold breach)
18
+ 2. **Input**: What data does it receive? What is the expected shape and validation requirements?
19
+ 3. **Output / Side Effect**: What does it produce or change? (DB write, API call, UI update, message sent)
20
+ 4. **Frequency & Volume**: How often? How many concurrent instances? What's the peak load?
21
+ 5. **Latency Requirement**: Is this real-time (<100ms), near-real-time (<2s), or batch (minutes)?
22
+ 6. **Failure Tolerance**: What's acceptable? (retry silently, alert user, block, compensate?)
23
+ 7. **Statefulness**: Does it need to resume after interruption, or restart from scratch?
24
+
25
+ If the user already provided enough context, skip the interview and proceed to Step 2.
26
+
27
+ ---
28
+
29
+ ## Step 2: Research Existing Patterns
30
+
31
+ Before proposing anything new, read the codebase:
32
+
33
+ 1. **Existing pipeline code** — workers, job processors, schedulers, event handlers
34
+ 2. **Existing workflow patterns** — similar automations already in the repo, and existing `plans/` folders
35
+ 3. **Shared types** — existing message/event/contract types the automation should reuse
36
+ 4. **Job queue usage** — how existing jobs are defined, retried, and monitored
37
+ 5. **Trigger and message flow** — existing gateways, controllers, or event buses the automation could plug into
38
+
39
+ Identify: what can be reused, what needs extending, what must be built from scratch.
40
+
41
+ ---
42
+
43
+ ## Step 3: Design the Automation Blueprint
44
+
45
+ ### 3a. Pipeline Stage Diagram
46
+
47
+ Model the automation as a sequence of named, isolated stages:
48
+
49
+ ```
50
+ [Trigger / Input]
51
+ │
52
+ ▼
53
+ ┌─────────────────────────────────────────────┐
54
+ │ Stage 1: INPUT VALIDATION │
55
+ │ • Validate schema (throw on invalid) │
56
+ │ • Normalize / enrich input │
57
+ │ • Emit: validation.passed / failed metric │
58
+ └─────────────────────────────────────────────┘
59
+ │ valid input
60
+ ▼
61
+ ┌─────────────────────────────────────────────┐
62
+ │ Stage 2: [DOMAIN PROCESSING STAGE NAME] │
63
+ │ • [Core business logic] │
64
+ │ • Side effect: [DB write / API call / etc.] │
65
+ │ • Retry: [yes/no — strategy] │
66
+ └─────────────────────────────────────────────┘
67
+ │
68
+ ├─── success ──► [Stage 3 or Output]
69
+ │
70
+ └─── failure ──► [Error Handler / DLQ]
71
+ ```
72
+
73
+ Rules for pipeline design:
74
+ - Each stage is a **single responsibility** — one input, one output, one error path
75
+ - Stages communicate via **typed events or return values**, never shared mutable state
76
+ - Each stage logs entry, exit, and duration
77
+ - Each stage that calls an external system has a **timeout + fallback**
78
+
79
+ ### 3b. State Machine (if stateful)
80
+
81
+ If the automation has multiple states (e.g., a multi-step workflow), define the state machine:
82
+
83
+ ```
84
+ States: PENDING → RUNNING → [COMPLETED | FAILED | RETRYING | CANCELLED]
85
+
86
+ Transitions:
87
+ PENDING + trigger → RUNNING (persist state, start processing)
88
+ RUNNING + success → COMPLETED (persist result, emit event)
89
+ RUNNING + retryable → RETRYING (increment attempt, schedule next)
90
+ RUNNING + fatal-error → FAILED (persist error, alert, DLQ)
91
+ RETRYING + max-attempts → FAILED
92
+ * + cancel → CANCELLED (compensate if needed)
93
+ ```
94
+
95
+ ### 3c. Fault Tolerance Model
96
+
97
+ For each external call or async operation in the pipeline:
98
+
99
+ | Operation | Timeout | Retry Strategy | Max Attempts | Circuit Breaker | Fallback |
100
+ |-----------|---------|----------------|--------------|-----------------|---------|
101
+ | [op name] | [ms] | [exp backoff + jitter] | [N] | [yes/no] | [what happens] |
102
+
103
+ **Retry formula**: `delay = min(baseDelay * 2^attempt + jitter(0..500ms), maxDelay)`
104
+
105
+ ### 3d. Data Contracts
106
+
107
+ Define all types at the pipeline boundary:
108
+
109
+ ```typescript
110
+ // Input contract
111
+ interface AutomationInput {
112
+ readonly id: string; // correlation ID for tracing
113
+ readonly triggeredBy: string; // user ID or system
114
+ readonly payload: [specific type];
115
+ readonly metadata: {
116
+ readonly sessionId: string;
117
+ readonly timestamp: number;
118
+ };
119
+ }
120
+
121
+ // Output / result contract
122
+ type AutomationResult =
123
+ | { readonly status: 'completed'; readonly output: [type]; readonly duration: number }
124
+ | { readonly status: 'failed'; readonly error: string; readonly attempt: number }
125
+ | { readonly status: 'partial'; readonly completed: [type][]; readonly failed: [type][] };
126
+
127
+ // Event emitted to consumers
128
+ interface AutomationEvent {
129
+ readonly type: 'automation.completed' | 'automation.failed' | 'automation.step';
130
+ readonly automationId: string;
131
+ readonly payload: AutomationResult;
132
+ }
133
+ ```
134
+
135
+ ### 3e. Observability Plan
136
+
137
+ Every automation must be observable. Define:
138
+
139
+ ```
140
+ Logs (structured):
141
+ INFO — stage start/end with duration
142
+ WARN — retryable failure (attempt N of M)
143
+ ERROR — fatal failure with full context { error, stack, input.id, stage }
144
+
145
+ Metrics (OpenTelemetry or the project's metrics library):
146
+ automation.[name].duration — histogram
147
+ automation.[name].success — counter
148
+ automation.[name].failure — counter (tagged by stage + error type)
149
+ automation.[name].retry — counter
150
+ automation.[name].queue.depth — gauge (if queue-based)
151
+
152
+ Traces:
153
+ Root span: automation.[name]
154
+ Child spans: one per stage
155
+ Attributes: correlation ID, input size, output size, attempt count
156
+ ```
157
+
158
+ ### 3f. Job Queue Configuration (if async/background)
159
+
160
+ Express these settings in whatever queue library the project already uses:
161
+
162
+ ```typescript
163
+ // Job definition
164
+ interface [AutomationName]Job {
165
+ data: AutomationInput;
166
+ opts: {
167
+ attempts: 3,
168
+ backoff: { type: 'exponential', delay: 1000 },
169
+ removeOnComplete: 100, // keep last 100 completed
170
+ removeOnFail: 500, // keep last 500 failed for debugging
171
+ timeout: 30_000, // kill job after 30s
172
+ };
173
+ }
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Step 4: Integration Points
179
+
180
+ Map how this automation fits into the project's existing architecture:
181
+
182
+ ```
183
+ [Where it plugs in]
184
+ ├── Message trigger → existing gateway/handler → new message type: [name]
185
+ ├── OR HTTP trigger → new endpoint on an existing service
186
+ ├── OR Scheduled → queue or scheduler cron job
187
+ └── OR Event-driven → subscribes to existing [EventName] event
188
+
189
+ [Output goes to]
190
+ ├── Stream back to the client (real-time progress)
191
+ ├── OR DB persistence (entity/table: [name])
192
+ ├── OR Cache (TTL: [value])
193
+ └── OR triggers downstream automation: [name]
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Step 5: Implementation Plan
199
+
200
+ Produce a phased plan. Each phase should be small enough to become one ticket:
201
+
202
+ ### Phase 1: Types & Contracts
203
+ - Define all input/output/event types where the project keeps shared types
204
+ - No implementation — types only
205
+ - Verify: `[the project's build or typecheck command]`
206
+
207
+ ### Phase 2: Core Pipeline Service
208
+ - Create the service in the project's usual location for the domain, named after the automation
209
+ - Implement pipeline stages as private methods
210
+ - Inject dependencies (optional services gracefully degrade)
211
+ - Include full fault tolerance (retry, timeout, circuit breaker)
212
+ - Verify: `[the project's build command]`
213
+
214
+ ### Phase 3: Queue / Trigger Integration
215
+ - Register the queue processor if async
216
+ - Wire into the chosen trigger: gateway, endpoint, scheduler or event subscription
217
+ - Add routing for the new message type or route
218
+ - Verify: `[the project's build command]`
219
+
220
+ ### Phase 4: Tests
221
+ - Unit tests for each pipeline stage in isolation
222
+ - Integration test for the happy path
223
+ - Error path tests (timeout, retry exhaustion, invalid input)
224
+ - Verify: `[the project's test command]`
225
+
226
+ ### Phase 5: Client / UI Integration (only if the automation has a user-facing surface)
227
+ - Add client state for tracking automation status
228
+ - Wire event listeners for progress streaming
229
+ - UI: progress indicator + error state
230
+ - Verify: `[the client build and test command]`
231
+
232
+ ---
233
+
234
+ ## Step 6: Create Strategy Folder
235
+
236
+ Ask the user: "Should I turn this design into a plan at `plans/[automation-name]/` (a spec, a map and a graph of tickets)?"
237
+
238
+ If yes → plan it with the feature-flow skill (`/feature-flow [automation-name]` in Claude Code, `$feature-flow [automation-name]` in Codex), using this blueprint as the specification.
239
+
240
+ ---
241
+
242
+ ## Step 7: Output the Blueprint
243
+
244
+ ```
245
+ ## Automation Blueprint: [Name]
246
+
247
+ ### What it does
248
+ [2-3 sentences: trigger → processing → output]
249
+
250
+ ### Architecture Pattern
251
+ [Pipeline / State Machine / Event-Driven / Scheduled Batch]
252
+
253
+ ### Pipeline Stages
254
+ [diagram from Step 3a]
255
+
256
+ ### Fault Tolerance Summary
257
+ [table from Step 3c]
258
+
259
+ ### Key Data Types
260
+ [contracts from Step 3d]
261
+
262
+ ### Integration Point
263
+ [where it plugs in to the existing architecture]
264
+
265
+ ### Implementation Phases
266
+ [phases from Step 5 — with file paths and verify commands]
267
+
268
+ ### Risks & Mitigations
269
+ - [Risk 1]: [Mitigation]
270
+ - [Risk 2]: [Mitigation]
271
+
272
+ ### Open Questions
273
+ - [Any decisions that need user input before implementation]
274
+ ```
@@ -0,0 +1,62 @@
1
+ ---
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.
4
+ argument-hint: "[feature] [show] [auto]"
5
+ disable-model-invocation: true
6
+ ---
7
+
8
+ Plan or build the feature in `$ARGUMENTS`.
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.
11
+
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
+
14
+ ## Show
15
+
16
+ With `show`, follow `guides/show.md` for the feature and stop.
17
+
18
+ ## Plan
19
+
20
+ If `plans/<feature>/` does not exist, run `FLOW_INVOKE=/feature-flow python3 scripts/flow.py <feature> start` and check that it prints `PLAN`. Then:
21
+
22
+ 1. Follow `guides/plan.md` with the user.
23
+ 2. Run `python3 scripts/flow-status.py <feature> --check` and fix every problem.
24
+ 3. Stop. List the files you created 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.
25
+
26
+ ## Before building
27
+
28
+ With a plan present, check these before `start`, and stop at the first that fails:
29
+
30
+ - `git status --porcelain` is empty.
31
+ - `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 `bash install.sh` from feature-flow.
33
+
34
+ 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
+
36
+ If `start` prints `STOP` naming another owner, another session may still be working this feature. Ask the user whether that session is closed. Only on a clear yes, run `start` once more with `FLOW_TAKEOVER=1`. In `auto` mode, never take over: report and stop.
37
+
38
+ Say what will happen: for each ticket, a builder subagent and then a reviewer subagent. After every few tickets (`FLOW_TICKETS_PER_SESSION`, default 4) you hand off to a new session. Ask for a yes, unless `auto` was given.
39
+
40
+ ## The loop
41
+
42
+ Run `next`, act on its one line, and repeat:
43
+
44
+ - `BUILD <ticket> <NN> <sha>`: run `prompt`. Spawn a `ticket-builder` subagent with that output as its whole prompt, and ask for a short summary back. Wait for its final reply: an Agent call can return before the subagent finishes, and the reply then arrives as a notification. Then run `next`.
45
+ - `REVIEW <ticket> <NN> <sha>`: run `prompt` and spawn a `ticket-reviewer` subagent with it. Wait for its final reply. Save the whole reply with Bash, not the Write tool, into the conductor's git-ignored state folder: `cat > .feature-flow/state/flow-review-<feature>.txt <<'EOF'` ... `EOF`. Never under `.git`: an unattended session is refused there as a sensitive file. Then, as separate commands, run `verdict .feature-flow/state/flow-review-<feature>.txt` and `next`. If `verdict` prints `RETRY`, just run `next`.
46
+ - `DONE <summary>`: report it and suggest `/feature-flow <feature> show`.
47
+ - `STOP <reason>`: report the reason, run `python3 scripts/flow-status.py <feature>`, show the table, and stop.
48
+ - `HANDOFF <line>`: stop here. Tell the user to open a new session and type exactly `<line>`. The new session resumes where this one stopped.
49
+
50
+ A conductor command looks like this:
51
+
52
+ FLOW_SESSION=<token> FLOW_INVOKE=/feature-flow python3 scripts/flow.py <feature> next
53
+
54
+ ## Rules while building
55
+
56
+ These apply from `start` on, once a plan exists. Planning (above) writes the plan files itself.
57
+
58
+ - Never run the gate, the floor guard or a review yourself. The conductor runs the checks, and the reviewer subagent reviews.
59
+ - Never edit a ticket, the plan or the code, and never commit. The builder does that.
60
+ - Never skip a step, reorder steps, or decide the next step yourself. Only `next` decides.
61
+ - Never read a ticket's `## Answer` into the reviewer's prompt. `prompt` already holds everything it needs.
62
+ - Tell the user one short line per phase (`01 built`, `01 review: PASS`), not the subagents' reports.
feature_flow/checks.py ADDED
@@ -0,0 +1,71 @@
1
+ """The checks the conductor runs: the bash scripts it still calls, and the ported ones in process.
2
+
3
+ Each call lives in one function so plans/interactive-flow-python can swap it for an
4
+ in-process call without touching the conductor.
5
+ """
6
+
7
+ import subprocess
8
+ from pathlib import Path
9
+
10
+ from feature_flow import status
11
+ from feature_flow import gate as gate_module
12
+ from feature_flow import floorguard
13
+
14
+
15
+ class Result:
16
+ def __init__(self, code, out):
17
+ self.code = code
18
+ self.out = out
19
+
20
+ @property
21
+ def ok(self):
22
+ return self.code == 0
23
+
24
+
25
+ class _Collect:
26
+ """stdout and stderr of an in-process port, folded together in the order they were written."""
27
+
28
+ def __init__(self):
29
+ self.parts = []
30
+
31
+ def write(self, text):
32
+ self.parts.append(text)
33
+
34
+ def text(self):
35
+ return "".join(self.parts)
36
+
37
+
38
+ def _bash(script, *args):
39
+ result = subprocess.run(["bash", str(script)] + [str(a) for a in args],
40
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
41
+ return Result(result.returncode, result.stdout)
42
+
43
+
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."""
46
+ out = _Collect()
47
+ code = status.run([feature, mode], out, out)
48
+ return Result(code, out.text())
49
+
50
+
51
+ def gate(scripts, feature):
52
+ """The gate, run in process. stderr is folded into the output as the subprocess call did."""
53
+ chunks = []
54
+ code = gate_module.run(str(feature), chunks.append, chunks.append)
55
+ out = b"".join(chunks).decode("utf-8", "replace").replace("\r\n", "\n").replace("\r", "\n")
56
+ return Result(code, out)
57
+
58
+
59
+ def floor_guard(scripts, feature, num, base):
60
+ """feature_flow.floorguard in-process; stdout and stderr are folded together in order."""
61
+ chunks = []
62
+ code = floorguard.run([str(feature), str(num), str(base)], chunks.append, chunks.append)
63
+ out = "".join(chunks).encode("utf-8", "surrogateescape").decode("utf-8", "replace")
64
+ return Result(code, out)
65
+
66
+
67
+ def smoke(command):
68
+ args, use_shell = gate_module.shell(command)
69
+ result = subprocess.run(args, shell=use_shell, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
70
+ universal_newlines=True)
71
+ return Result(result.returncode, result.stdout)
feature_flow/cli.py ADDED
@@ -0,0 +1,47 @@
1
+ """python3 scripts/flow.py <feature> <command>: the conductor's command line."""
2
+
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ from feature_flow.conductor import Conductor, NoPhase, Stop
7
+
8
+ USAGE = "usage: python3 scripts/flow.py <feature> start | next | prompt | verdict <file>"
9
+ COMMANDS = ("start", "next", "prompt", "verdict")
10
+
11
+
12
+ def main(argv=None, scripts=None):
13
+ args = list(sys.argv[1:] if argv is None else argv)
14
+ if len(args) < 2 or args[1] not in COMMANDS or not args[0] or args[0].startswith("-"):
15
+ print(USAGE, file=sys.stderr)
16
+ return 2
17
+ feature, command = args[0], args[1]
18
+ if (command == "verdict") != (len(args) == 3) or len(args) > 3:
19
+ print(USAGE, file=sys.stderr)
20
+ return 2
21
+ scripts = Path(scripts) if scripts else Path.cwd() / "scripts"
22
+ conductor = None
23
+ try:
24
+ conductor = Conductor(feature, scripts)
25
+ if command == "verdict":
26
+ line = conductor.verdict(args[2])
27
+ elif command == "prompt":
28
+ line = conductor.prompt()
29
+ elif command == "start":
30
+ line = conductor.start()
31
+ else:
32
+ line = conductor.next()
33
+ except NoPhase as err:
34
+ print(err, file=sys.stderr)
35
+ return 2
36
+ except Stop as stop:
37
+ line = "STOP %s" % stop
38
+ if conductor is not None:
39
+ conductor.log("STOP")
40
+ print(line)
41
+ return 1
42
+ print(line, end="" if line.endswith("\n") else "\n")
43
+ return 0
44
+
45
+
46
+ if __name__ == "__main__":
47
+ sys.exit(main())
@@ -0,0 +1,39 @@
1
+ """feature-flow: the command an installed package puts on PATH (`uvx feature-flow-cli install .`).
2
+
3
+ It installs the flow into a repo and reads a plan. The flow itself runs from the repo, as
4
+ python3 scripts/flow.py, with the copy the installer put there.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+
10
+ from feature_flow import __version__, install, status, view
11
+
12
+ USAGE = """\
13
+ usage: feature-flow install [target-repo] [--agent claude|codex|all] [--user] [--force] [--dry-run]
14
+ feature-flow status <feature> [--next | --counts | --check | --mermaid [plain] | --json]
15
+ feature-flow view <feature> [--watch] [--no-open] [--out FILE]
16
+ feature-flow --version
17
+ install copies the skill, its roles, scripts and guides into a repo; feature-flow install --help says more.
18
+ status and view read plans/<feature>/ in the current directory, like scripts/flow-status.py and scripts/flow-view.py.
19
+ """
20
+
21
+
22
+ def main(argv=None):
23
+ args = list(sys.argv[1:] if argv is None else argv)
24
+ if not args or args[0] in ("-h", "--help", "help"):
25
+ out = sys.stdout if args else sys.stderr
26
+ out.write(USAGE)
27
+ return 0 if args else 2
28
+ command, rest = args[0], args[1:]
29
+ if command in ("-V", "--version"):
30
+ print("feature-flow " + __version__)
31
+ return 0
32
+ if command == "install":
33
+ return install.main(install.source_root(), rest)
34
+ if command == "status":
35
+ return status.main(rest)
36
+ if command == "view":
37
+ return view.main(rest, here=os.path.join(install.source_root(), "scripts"))
38
+ sys.stderr.write("unknown command: %s\n%s" % (command, USAGE))
39
+ return 2