Introduction
bdd is one native binary for the whole spec-driven loop:
spec → Gherkin scenario → RED → implement → GREEN → REFACTOR
The requirements spec (requirements/requirements.json) is the source
of truth, and the discipline is enforced by tooling, not by
convention. The CLI validates the spec’s structure, quality-gates its
wording, turns approved requirements into tagged Gherkin scenarios,
runs the tests through your project’s own build tool, and tracks the
persistent RED/GREEN/REFACTOR phase between invocations. It also
embeds an MCP server (bdd mcp serve) so AI agents can drive the same
workflow through typed tools — with no filesystem or shell escape
hatches.
How to read this manual
- Using bdd covers the concepts that span commands: the global flags, the interactive shell, the workflow phases, and the staged changes model that protects your working tree.
- Command reference documents every command, subcommand, and flag, with realistic examples and the exact JSON reply shapes.
Use the search icon (or press S) to search the whole manual.
Conventions
- Commands are shown as you would type them in a shell. Inside the
interactive shell the leading
bddis optional. - Replies are JSON on stdout unless a command is inherently
interactive. Every reply carries a
nextStepfield that says what to do next — the same guidance an AI agent receives over MCP. - Names in parentheses in help text, like
(run_tests), are the matching MCP tool names — frozen contracts kept byte-identical to the workshop’s Javatdd-workflow-server.
Supported languages
| Language | Build tool | BDD framework | Runtime probed |
|---|---|---|---|
| Java | Maven | Cucumber-JVM | mvn |
| JavaScript | npm | Cucumber-JS | node |
| TypeScript | npm + ts-node | Cucumber-JS | node |
| .NET | dotnet | Reqnroll | dotnet |
| Rust | Cargo | cucumber-rs | cargo |
The CLI only ever executes when the language’s runtime is present;
it reports a structured runtime_missing refusal otherwise and never
installs anything.
LLM-backed generation uses local Ollama. The
model this CLI is developed and run against is
qwen3-coder-next:latest — see Getting started
and bdd model. Your mileage will vary with
other models, especially those not trained for development work.
Getting started
Install
Download an installer from the latest release (macOS Apple Silicon and Intel, Linux x86_64 and arm64, Windows x86_64), or build from source:
cd cli
cargo build --release
./target/release/bdd --help
The shell installer places the binary in $CARGO_HOME/bin (usually
~/.cargo/bin/bdd) and writes an install receipt to
~/.config/bdd-cli/bdd-cli-receipt.json.
Local LLM (Ollama)
LLM-backed generation uses a local Ollama
instance. The model this CLI is developed and run against is
qwen3-coder-next:latest:
ollama pull qwen3-coder-next:latest
bdd model use qwen3-coder-next:latest
Your mileage will vary with a different model. A stronger coding model may draft, generate, and implement better; a model trained for chat, general knowledge, or work other than development will typically produce weaker specs, step definitions, tests, and production code. Without a reachable model the CLI still runs — generation falls back to deterministic templates.
Your first session
Run bare bdd in a terminal. You get the help, the banner with the
version, the model status, and an interactive prompt:
$ bdd
╭──────────────────────────────────╮
│ ▼
│ > bdd v0.2.5 │
│ spec → RED → GREEN → REFACTOR │
▲ │
╰──────────────────────────────────╯
Model set for this session: qwen3-coder-next:latest (not saved - keep it with: bdd model use qwen3-coder-next:latest).
Interactive shell - type commands without the bdd prefix (e.g. spec list).
bdd>
Two ways to begin a project
Guided, from zero — one command runs the whole loop with exactly two human gates (approving the spec wording and approving generated tests):
mkdir calculator && cd calculator
bdd greenfield
Step by step — scaffold, then drive each phase yourself:
bdd init --language rust --name "String Calculator"
bdd spec draft # describe what to build in plain words; with a
# model resolved it proposes title, story, and
# criteria for you to edit (manual prompts otherwise)
bdd spec validate # structure gate
bdd spec refine REQ-001 # wording gate
bdd changes commit # apply the staged spec
bdd test # expect RED
# ...implement...
bdd test # expect GREEN
bdd refactor --note "extract parser"
bdd test # still GREEN
bdd status # confirm REQ-001 is ready to mark
bdd spec mark-implemented REQ-001 && bdd changes commit
Working against an existing project
Every command takes --root (see Global flags), so
you can point the CLI at any project:
bdd --root ~/code/my-kata inspect
bdd --root ~/code/my-kata spec validate
Global flags
These flags are accepted by bdd itself and by every command. Place
them anywhere on the command line.
--root <ROOT>
The project root — the directory where requirements/requirements.json
and .bdd-mcp.toml live, and the base for every relative path the CLI
reads or writes. Defaults to the current directory.
bdd --root ~/code/calculator spec list
bdd spec list --root ~/code/calculator # same thing
In the interactive shell, commands inherit the
shell’s --root unless a line supplies its own.
--model <MODEL>
Override the LLM model for this invocation only. This wins over the configured model and over discovery, and is never written to configuration:
bdd --model qwen3-coder-next:latest steps generate
bdd --model llama3:8b greenfield # another model; mileage will vary
Model resolution order (see bdd model for the full story):
--modelflag — this invocation only.modelin.bdd-mcp.toml— the persisted project choice.- Discovery — the first installed Ollama model, session-only.
-V, --version
Prints the version compiled into the binary (from Cargo.toml at
build time).
-h, --help
Every command and subcommand answers --help with its synopsis,
arguments, and flags.
Exit status
0— success (including replies whose JSON reports e.g. an invalid spec: the command succeeded).1— the command itself failed or refused: unknown requirement id, missing runtime (runtime_missing), unreachable Ollama for a model-required operation, unparseable arguments, and similar.
The interactive shell
Running bdd with no subcommand in a terminal opens a REPL. It prints
the help once, shows the banner and the session’s model status, and
then reads commands until you leave.
bdd> spec list
[
{ "id": "REQ-001", "title": "Empty string returns zero", "status": "implemented" }
]
bdd> test --feature features/calculator.feature
...
bdd> exit
Behavior
- No prefix needed. Type
spec list, notbdd spec list. A leadingbddis forgiven if you type it anyway. - Inherited flags. Commands inherit the shell’s
--rootand--modelunless the line supplies its own:
bdd --root ~/code/calculator --model qwen3-coder-next:latest
# every command in this shell now targets that root and model
- Quoting works. Lines are tokenized with shell rules, so
scenario add --step "Given a calculator"behaves as expected. Unbalanced quotes reportunreadable inputand the shell continues. - Errors don’t kill the shell. A failing command prints its error and returns to the prompt.
- Blank lines are ignored.
Leaving
exitorquit- Ctrl+C (interrupt)
- Ctrl+D (end of input)
On exit the shell prints a summary of how many commands ran.
Session history
Line history is kept across sessions in .bdd-history under the
project root. Use ↑/↓ to recall previous
commands and Ctrl+R for reverse search. If the
history cannot be saved, the shell says so and exits normally.
Model announcement at startup
The first prompt is preceded by one line describing the session’s
model. This CLI is developed and run against
qwen3-coder-next:latest; your mileage will vary with a different
model, especially one trained for work other than development. See
Getting started and
bdd model.
| Situation | Announcement |
|---|---|
Configured in .bdd-mcp.toml | Model set: qwen3-coder-next:latest (from configuration). |
| No config, models installed | Model set for this session: qwen3-coder-next:latest (not saved - keep it with: bdd model use qwen3-coder-next:latest). |
| Ollama up, no models | Ollama is running but has no models - generation will use deterministic templates. For optimal results pull a coding model, e.g.: ollama pull qwen3-coder-next:latest (mileage varies with models not trained for development) |
| Ollama unreachable | Ollama is not reachable - generation will use deterministic templates. Install it from https://ollama.com, start it, and pull a coding model, e.g.: ollama pull qwen3-coder-next:latest (mileage varies with models not trained for development) |
The greenfield nudge
When all three signs of a brand-new project line up —
- this is the first shell session in the root (no
.bdd-historyyet), - a model is ready, and
- there is no
requirements/requirements.json
— the shell offers to start the loop before the first prompt:
It appears you are in a greenfield - this project has no requirements/requirements.json yet.
Start with the greenfield command now? [y/N]
y runs bdd greenfield immediately;
anything else declines and the shell carries on:
No problem - type greenfield any time, or spec draft to begin with the spec.
When there is no terminal
If stdin is not a terminal (piped input, CI), bare bdd prints the
help and exits instead of opening the shell.
The workflow: spec → RED → GREEN → REFACTOR
The CLI enforces a two-altitude test discipline driven by a validated
spec. Understanding the phases makes every command’s nextStep field
self-explanatory.
The spec is the entry point
Nothing meaningful happens without requirements/requirements.json.
The iteration loop for the spec itself:
- Draft or edit a requirement —
bdd spec draftor your editor. bdd spec validateuntil the structure is valid.bdd spec refine <id>until there are no wording findings.- A human approves the wording. This is the first human gate.
The two altitudes
- BDD altitude — each requirement becomes a Gherkin scenario
tagged
@REQ-...in a feature file, with step definitions binding it to real code. - TDD altitude — unit tests
(
bdd unittest generate) pin down the fine-grained behavior beneath the scenario.
The phase machine
The persistent TDD phase lives in .bdd-tdd-state.json under the
project root and survives between invocations and across MCP sessions.
tests fail tests pass
(start) ──────────► RED ────────────► GREEN ──┐
▲ │ │ bdd refactor
│ tests fail ▼ ▼
└────────────── REFACTOR
(tests pass → GREEN)
bdd testruns the suite and moves the phase to RED (failures) or GREEN (all passing).bdd refactoris only allowed on GREEN — it moves to REFACTOR and records your note in the refactor log.bdd stateshows the phase, the last run’s counts, and the refactor log at any time.bdd statuszooms out from the phase to the spec: where every requirement stands on the road to implemented, and the single next step for the one that is furthest along.
One requirement at a time
The intended rhythm for each pending requirement:
bdd spec show REQ-002 # locations + workflow hint
bdd scenario add --feature features/calculator.feature \
--req REQ-002 --name "Two numbers are summed" \
--step 'Given the input "1,2"' \
--step 'When add is called' \
--step 'Then the result is 3'
bdd changes commit # apply the staged scenario
bdd steps missing # any undefined steps?
bdd steps generate && bdd changes commit
bdd test # RED: the scenario fails honestly
# ...implement the production code...
bdd test # GREEN
bdd refactor --note "tidy the parser" && bdd test
bdd status # confirm REQ-002 is ready to mark
bdd spec mark-implemented REQ-002 # flips the status, records the featureFile
bdd validate # checks the @REQ-002 scenario exists
bdd changes commit
bdd greenfield automates exactly this
rhythm, pausing only at the two human gates.
Staged changes
Every command that would write into your project — feature creation,
scenario mutations, step-definition and unit-test generation, spec
drafting, marking a requirement implemented — writes to a staging area
instead: .bdd-staged/ under the project root. Nothing touches your
working tree until you commit the transaction.
Why
- Review before apply. You (or an agent’s human supervisor) can inspect exactly what would change.
- Atomicity. A multi-file change (say, a feature file plus new step definitions) lands together or not at all.
- Safe agents. Over MCP, a model can propose file changes without ever holding write access to your tree.
The lifecycle
bdd feature create --path features/calculator.feature --name "String Calculator"
bdd changes show # review: one staged "create"
bdd changes commit # apply to the working tree, clear the stage
# or
bdd changes discard # drop everything staged, tree untouched
bdd changes show lists each staged entry with its action and path:
{
"changes": [
{ "action": "create", "path": "features/calculator.feature" }
],
"nextStep": "Review the staged changes, then 'bdd changes commit' to apply or 'bdd changes discard' to drop them."
}
What stages and what doesn’t
| Writes to the stage | Writes directly |
|---|---|
feature create | init (scaffolding a fresh project) |
scenario add / update / delete | model use (writes .bdd-mcp.toml) |
steps generate | test / refactor (phase state file) |
unittest generate | |
spec draft | |
spec mark-implemented |
Validation (bdd validate) checks staged Gherkin too, so you can gate
a transaction before committing it.
bdd init
Scaffold build files, a Cucumber runner, an empty requirements spec, and the CLI’s configuration in the project root. Existing files are never overwritten — they are reported as skipped.
Usage: bdd init [OPTIONS]
Flags
| Flag | Description |
|---|---|
--language <LANGUAGE> | Target language: java, javascript, typescript, dotnet, or rust. Prompted interactively when omitted. |
--name <NAME> | Project name used inside the generated build files. Defaults to the root directory’s name. |
--root <ROOT> | Project root to scaffold into. Defaults to .. |
--model <MODEL> | Accepted (global flag) but unused — init is fully deterministic. |
What gets created
Every language gets the two spec-driven anchors:
requirements/requirements.json— an empty, valid spec..bdd-mcp.toml— the CLI/MCP configuration.
Plus the language’s build and BDD harness:
| Language | Scaffolded files |
|---|---|
java | pom.xml (Maven + Cucumber-JVM), src/test/java/RunCucumberTest.java, features/.gitkeep |
javascript | package.json (Cucumber-JS), cucumber.js, features/step_definitions/.gitkeep |
typescript | package.json, tsconfig.json, cucumber.js (ts-node hooked in), features/step_definitions/.gitkeep |
dotnet | <Name>.Tests.csproj (Reqnroll), features/.gitkeep |
rust | Cargo.toml (cucumber-rs dev-dependency), src/lib.rs, a Cucumber test harness, features/.gitkeep |
Examples
Scaffold a Rust kata in a fresh directory:
mkdir calculator && cd calculator
bdd init --language rust --name "String Calculator"
{
"language": "rust",
"framework": "cucumber-rs",
"created": [
"requirements/requirements.json",
".bdd-mcp.toml",
"Cargo.toml",
"src/lib.rs",
"tests/cucumber.rs",
"features/.gitkeep"
],
"skipped": [],
"nextStep": "Draft your first requirement with 'bdd spec draft', then 'bdd spec validate'."
}
Re-running is safe — everything that already exists is skipped:
bdd init --language rust
{
"language": "rust",
"framework": "cucumber-rs",
"created": [],
"skipped": [
"requirements/requirements.json",
".bdd-mcp.toml",
"Cargo.toml",
"src/lib.rs",
"tests/cucumber.rs",
"features/.gitkeep"
],
"nextStep": "Draft your first requirement with 'bdd spec draft', then 'bdd spec validate'."
}
Omit --language and the CLI prompts with the supported list; an
unrecognized answer re-prompts.
Notes
initwrites directly to the working tree (there is nothing to protect in an empty project); everything afterinitgoes through staged changes.initdoes not install runtimes. Runbdd inspectto see whether the language’s runtime is present before expectingbdd testto execute.
See also
bdd greenfield— runsinitas its first move.bdd spec draft— the natural next step.
bdd greenfield
Run the full orchestrated loop from an empty directory to an implemented requirement, with exactly two human gates: approving the spec wording, and approving the generated tests before they run. Everything else — scaffolding, validation, scenario authoring, step generation, test execution, phase tracking — is automated.
Usage: bdd greenfield [OPTIONS]
Flags
| Flag | Description |
|---|---|
--root <ROOT> | Project root. Defaults to .. |
--model <MODEL> | LLM model for the generation steps, this run only. |
The orchestrated flow
1. inspect / init scaffold if the root is empty (asks for language)
2. describe you describe what to build in plain words; the
model splits it into requirement proposals
3. pick + wizard you pick a proposal; every field arrives
pre-filled - Enter accepts, typing replaces
4. spec validate structure gate; findings loop back to rewording
5. spec refine wording gate; each finding comes with a "try:" fix
── HUMAN GATE 1 ── approve the requirement's wording
6. scenario + steps Gherkin scenario tagged @REQ-...; step definitions
── HUMAN GATE 2 ── approve the generated tests
7. test → RED the scenario fails honestly
8. implement Enter lets the model attempt the implementation;
a number, e.g. 5, buys that many hands-off attempts
9. test → GREEN loop back to 8 while failing
10. refactor optional; only offered on GREEN
11. mark implemented the requirement's status flips in the spec
The description-driven wizard
With a resolved model, drafting starts from a plain-words description instead of a blank title prompt:
Describe what to build in plain words (one or several requirements). Enter drafts manually instead:
sum numbers from a comma separated string, empty input means zero
Splitting the description into requirements with qwen3-coder-next:latest - working ...
The description holds 2 requirement(s):
1. Comma separated numbers are summed
2. Empty string returns zero
Which requirement first? [1-2, Enter for 1]:
2
Left for later runs: Comma separated numbers are summed. Draft them the same way afterwards.
Walking through REQ-001. Each prompt shows the proposal - Enter accepts it, or type your own wording.
REQ-001 title [Empty string returns zero] (Enter keeps it):
REQ-001 story (As a ..., I want ..., so that ...) [As a user, I want empty input to be 0 so that no input is a safe default.] (Enter keeps it):
REQ-001 criterion 1 [Given an empty string "", when add is called, then the result is 0] (Enter keeps it, '-' drops it):
REQ-001 criterion 2 (leave blank to finish the criteria):
The model must deliver each proposal complete — title, story, and at least one Given/When/Then criterion — or the proposal is dropped. Nothing is accepted silently: every field passes through your hands, and the validate + refine gates still run on whatever you accept.
Drafting falls back to the classic manual prompts whenever the description is left blank, no model is resolved, the model is unreachable, or its reply holds no complete requirement.
At each gate you can approve, decline (the run stops cleanly), or pause to resume later — the phase state and staged changes survive between invocations.
The implementation attempt
On a RED bar with a resolved model, Enter asks the model to make the failing tests pass, then reruns the suite and prints the counts:
RED: 2 tests, 1 failures, 1 errors.
- Req001Test.empty_string_returns_zero: TODO: assert - ...
Press Enter to let the model attempt the implementation and rerun the tests, enter a number to attempt up to that many times without asking again, or type stop to pause here:
Generating an implementation attempt - working ...
Updated src/main/StringCalculator.java (llm).
Updated src/test/java/Req001Test.java (llm).
Running the tests - working ...
GREEN: 2 tests, 0 failures, 0 errors.
Every working ... line is live on a terminal: the trailing dots
animate in light yellow — . .. ... and over again — while the
model call or test run is in flight, then the line settles. Piped
output prints the single static line.
The model receives the requirement, the full failing test details — stack traces and build output included — the project’s source files, every prior attempt on this requirement (which files it wrote, which failures it was addressing, and what the run after it actually reported), and the session language’s best practices — package naming for Java, snake_case modules for Rust, and so on — so a second attempt never starts blind or repeats an approach that already failed. It must reply with complete files: production code plus real bodies for the TODO placeholders in the generated tests and step definitions. Only paths already in the project (or the production file named after the spec’s project) are accepted; anything else in the reply is dropped. The rerun is the real validator — if the bar stays RED, press Enter for another attempt (the fresh failure details plus the attempt history go back to the model) or implement by hand and press Enter.
A number buys a hands-off stretch: answering 5 lets the model
attempt, rerun, and attempt again up to five times without asking in
between — each round announced as Attempt 2 of 5. — stopping early
the moment the bar turns GREEN. When the budget runs out on RED, the
prompt returns. Anything unreadable at the prompt behaves like
Enter: one attempt.
An unusable reply is narrated
(The model's reply held no usable file update. Implement by hand instead.) and the loop simply hands control back to you. Dead ends
like this — model failures, missing runtimes, hand-offs back to manual
work — print in red so they stand out from the loop’s narration. Without a
model, Enter just reruns the tests.
Typing stop pauses the run; a paused project continues with the
standalone bdd implement command, which runs the
same attempt from the persisted failure details:
bdd implement REQ-001 && bdd changes commit && bdd test
Rewording loop details
When validation or refinement finds problems, each finding is printed with a concrete suggestion:
REQ-001: the outcome is not concrete
try: end with the exact expected value, e.g. '..., then the result is 3'
With a model, the findings become its brief: the draft and the findings go to the model, and the re-prompts carry its corrected proposal — Enter accepts each fix. If the review rejects a wording again, the next model call also recounts every earlier wording and the findings each one produced, so the model never circles back to a wording the review already rejected:
Asking qwen3-coder-next:latest to address finding 1 of 1 - working ...
The model reworded the draft. Each prompt shows its proposal - Enter accepts it, or type your own wording.
With several findings, each one is its own model call — call 2 is briefed with the draft call 1 fixed, so the corrections accumulate one finding at a time instead of all at once.
On a color console the bracketed suggestion — the text Enter
will use — renders green; the destructive '-' drops it hint on
criterion prompts and dead-end messages render red; the animated dots
on working ... lines render light yellow, and the
Generating an implementation attempt announcement renders dark
green. On a real
terminal every answer is edited on a > line with full line editing:
the arrow keys move the cursor anywhere in the typed text, Home/End
jump, and the up arrow recalls earlier answers from this session.
If the model call fails or its rewording is unusable, the re-prompt falls back to the requirement id and your prior answer; Enter keeps it:
REQ-001 title [Two numbers separated by a comma are summed] (Enter keeps it):
REQ-001 criterion 1 [Given "1,2", when add is called, then the result is 3] (Enter keeps it, '-' drops it):
REQ-001 criterion 3 (leave blank to finish the criteria):
Reply
The final JSON reply summarizes where the run ended:
{
"requirement": "REQ-001",
"feature": "features/string_calculator.feature",
"phase": "GREEN",
"completed": true,
"nextStep": "REQ-001 is implemented. Draft the next requirement with 'bdd spec draft'."
}
completed is false when a gate was declined, a runtime was
missing, or the run was paused; nextStep always says how to
continue.
Requirements for a full run
- The language’s runtime must be present (
mvn,node,dotnet, orcargo) — the orchestrator refuses to fake a test run. - An LLM is optional: with no reachable Ollama model the generation
steps fall back to deterministic templates you edit yourself. The
model this CLI is developed and run against is
qwen3-coder-next:latest(ollama pull qwen3-coder-next:latest). Your mileage will vary with other models, especially those not trained for development work.
See also
- The workflow — the same rhythm, step by step.
bdd model— pick which model powers generation.
bdd spec
Requirements spec tools. The spec at
requirements/requirements.json is the project’s source of truth;
these subcommands read it, gate it, and mutate it (through the
staging area).
Usage: bdd spec [OPTIONS] <COMMAND>
Commands: list, show, draft, validate, refine, mark-implemented
MCP tool equivalents: list_requirements, get_requirement,
validate_spec, refine_requirement, requirement_mark_implemented.
bdd spec list
List every requirement with its id, title, and status.
bdd spec list
[
{ "id": "REQ-001", "title": "Empty string returns zero", "status": "implemented" },
{ "id": "REQ-002", "title": "A single number is returned as-is", "status": "pending" },
{ "id": "REQ-003", "title": "Two numbers separated by a comma are summed", "status": "pending" }
]
Use it to pick the next pending requirement to work on.
bdd spec show
Show one requirement, enriched with file locations and a workflow hint.
Usage: bdd spec show [OPTIONS] <REQ_ID>
bdd spec show REQ-003
{
"id": "REQ-003",
"title": "Two numbers separated by a comma are summed",
"status": "pending",
"story": "As a user, I want comma-separated numbers to be summed so that I can add multiple values at once.",
"acceptanceCriteria": [
"Given \"1,2\", when add is called, then the result is 3",
"Given \"10,20\", when add is called, then the result is 30"
],
"featureLocation": "features/string_calculator.feature",
"stepDefinitions": "features/step_definitions/calculator_steps.js",
"testLocation": "test/calculator.test.js",
"productionLocation": "src/calculator.js",
"workflowHint": "Write the Gherkin scenario for this requirement in the feature file first (tag it @REQ-003), reuse or add step definitions, then run tests to see RED."
}
An unknown id fails with exit status 1:
Error: no requirement with id REQ-999
bdd spec draft
Interactively draft a requirement. Human input drives the spec — the CLI never invents requirements. The draft is validated and quality-gated in a loop until it is clean, then staged.
bdd spec draft
The prompts, first pass:
REQ-004 title:
REQ-004 story:
Acceptance criteria (Given/When/Then). A blank criterion ends the list:
REQ-004 criterion 1 (leave blank to finish the criteria):
REQ-004 criterion 2 (leave blank to finish the criteria):
- The id is allocated automatically (next free
REQ-nnn). - A blank criterion ends the list — enter at least one first.
- On a color console the bracketed suggestion — the text
Enter will use — renders green; the destructive
'-' drops ithint on criterion prompts, model failures, and other dead ends render red; the animated dots onworking ...lines render light yellow. - On a real terminal answers are edited on a
>line with full line editing — arrow keys move the cursor anywhere in the typed text, Home/End jump, and the up arrow recalls this session’s answers.
If validation or refinement finds problems, each finding prints with a concrete suggestion, and the rewording pass shows your prior answers:
REQ-004: acceptance criterion 1 must be phrased Given/When/Then
try: rephrase as: Given <starting state>, when <action>, then <exact result>
REQ-004 title [Newlines act as delimiters] (Enter keeps it):
REQ-004 story [As a user, I want newlines to work like commas so that input can be multi-line.] (Enter keeps it):
REQ-004 criterion 1 [it should handle newlines] (Enter keeps it, '-' drops it):
Given "1\n2", when add is called, then the result is 3
REQ-004 criterion 2 (leave blank to finish the criteria):
- Enter keeps the prior answer.
-drops a prior criterion.- New criteria can be appended after the priors.
With a resolved model, bdd spec draft runs the same
description-driven wizard as greenfield,
and findings are sent to the model first: the rewording prompts carry
its corrected proposal instead of your raw prior answers, so
Enter accepts each fix (for the happy-paths finding, that
includes the edge-case criterion it added):
Findings to address:
- criteria: only happy paths - add at least one edge case (empty, invalid, or error input)
try: add an edge case, e.g. Given an empty string "", when add is called, then the result is 0
Asking qwen3-coder-next:latest to address finding 1 of 1 - working ...
The model reworded the draft. Each prompt shows its proposal - Enter accepts it, or type your own wording.
REQ-004 title [Newlines act as delimiters] (Enter keeps it):
REQ-004 criterion 2 [Given an empty string "", when add is called, then the result is 0] (Enter keeps it, '-' drops it):
Each finding is its own model call: call 1 addresses finding 1 on your draft, call 2 addresses finding 2 on the draft call 1 produced, and so on — the fixes accumulate one finding at a time instead of asking for everything at once. An unusable reply skips that finding (it stays yours to fix at the prompts); a model error ends the chain and keeps whatever fixes already landed.
If the review rejects a wording again, the next model call also recounts every earlier wording of the draft and the findings each one produced, so the model never proposes a wording the review already rejected.
On a terminal the working ... lines animate — the trailing dots grow
. .. ... in light yellow and start over — while the model call
is in flight.
If the model is unreachable or its rewording is unusable, the prompts fall back to your prior answers exactly as without a model. Nothing is accepted silently either way — every field still passes through your hands, and validate + refine rerun on whatever you accept.
When the draft is clean it is staged:
{
"id": "REQ-004",
"title": "Newlines act as delimiters",
"staged": true,
"nextStep": "Review with 'bdd changes show', apply with 'bdd changes commit', then write the Gherkin scenario."
}
bdd spec validate
Validate the whole spec on disk: JSON shape, required fields, unique ids, status values, and Given/When/Then phrasing of every criterion.
bdd spec validate
Valid spec:
{
"valid": true,
"issues": [],
"nextStep": "Pick a pending requirement with 'bdd spec list' and write its scenario."
}
Invalid spec (the command still exits 0 — the report carries the verdict):
{
"valid": false,
"issues": [
"REQ-005: acceptance criterion 1 must be phrased Given/When/Then ('the calculator should handle newlines quickly')"
],
"nextStep": "Fix the issues in requirements/requirements.json, then validate again."
}
bdd spec refine
Review one requirement’s wording for quality: vague words, missing actor or benefit in the story, compound criteria, non-concrete outcomes, happy-path-only criteria.
Usage: bdd spec refine [OPTIONS] <REQ_ID>
bdd spec refine REQ-004
{
"id": "REQ-004",
"clean": false,
"findings": [
"acceptance criterion 1 is ambiguous: 'quickly' is not testable",
"the story is missing the why (no 'so that ...')"
],
"nextStep": "Reword the flagged parts, then refine again until there are no findings."
}
Refine until "clean": true, then have the developer approve the
wording — that approval is the first human gate of the workflow.
bdd spec mark-implemented
Flip a requirement’s status to implemented and record its
featureFile — the feature carrying the @REQ-... tag — in the same
staged edit, so the spec passes validation. The change is staged, not
applied directly.
Usage: bdd spec mark-implemented [OPTIONS] <REQ_ID>
bdd spec mark-implemented REQ-003
bdd validate
bdd changes commit
{
"id": "REQ-003",
"status": "implemented",
"staged": true,
"nextStep": "Review with changes show, run bdd validate (it checks the @REQ-003 scenario exists), then bdd changes commit."
}
The command is gated twice:
- GREEN only — it refuses unless the last recorded run passed. Do this only when the scenario and tests for the requirement are GREEN; it is the last move of the per-requirement rhythm.
- Tagged scenario only — it refuses when no committed feature
file carries a scenario tagged
@<REQ_ID>; add one withbdd scenario addand apply it withbdd changes commitfirst.
Re-running it on an already-implemented requirement is safe: on GREEN
it backfills a missing featureFile, which is exactly the repair for
a spec that fails validation with
implemented requirements must name their featureFile.
See also
- The workflow — where each subcommand fits.
bdd changes— reviewing and applying staged spec mutations.
bdd inspect
Detect the project’s languages, build system, BDD framework, and —
critically — whether each language’s runtime is actually installed.
The CLI only executes tests when the runtime is present, so inspect
tells you up front what will run and what will refuse.
Usage: bdd inspect [OPTIONS]
Flags
Only the global flags (--root, --model).
Detection rules
| Marker in the root | Language | Runtime probed |
|---|---|---|
pom.xml | Java (Maven + Cucumber-JVM) | mvn |
package.json + tsconfig.json | TypeScript (Cucumber-JS) | node |
package.json | JavaScript (Cucumber-JS) | node |
*.csproj | .NET (Reqnroll) | dotnet |
Cargo.toml | Rust (cucumber-rs) | cargo |
Examples
A Rust project with the toolchain installed:
bdd inspect
{
"languages": [
{
"language": "rust",
"bddFramework": "cucumber-rs",
"runtime": "cargo",
"runtimePresent": true,
"runtimeVersion": "cargo 1.97.0"
}
],
"nextStep": "The runtime is present. 'bdd test' will execute the suite."
}
A Java project without Maven on the PATH:
{
"languages": [
{
"language": "java",
"bddFramework": "cucumber-jvm",
"runtime": "mvn",
"runtimePresent": false,
"note": "Install Maven (and a JDK) to execute tests; the CLI reports, it never installs."
}
],
"nextStep": "Install the missing runtime before 'bdd test'; authoring commands still work."
}
An empty directory reports no languages and points you at
bdd init.
Notes
- Authoring commands (spec, feature, scenario, steps, unittest) work
without any runtime; only
bdd testand the test-running parts ofbdd greenfieldrequire one. - With multiple markers present (a polyglot root), every detected language is listed.
bdd feature
Feature discovery and creation. Feature files are the BDD altitude of
the workflow: each requirement gets a scenario in one, tagged with its
@REQ-... id.
Usage: bdd feature [OPTIONS] <COMMAND>
Commands: list, show, create
bdd feature list
List every feature file under the root with its feature name and scenario count.
bdd feature list
[
{
"path": "features/string_calculator.feature",
"name": "String Calculator",
"scenarios": 3
}
]
bdd feature show
Show one parsed feature file — its name, scenarios, tags, and steps — as structured JSON rather than raw text.
Usage: bdd feature show [OPTIONS] <PATH>
The path is relative to --root:
bdd feature show features/string_calculator.feature
{
"path": "features/string_calculator.feature",
"name": "String Calculator",
"scenarios": [
{
"name": "Empty string returns zero",
"tags": ["@REQ-001"],
"steps": [
"Given the input \"\"",
"When add is called",
"Then the result is 0"
]
}
]
}
A file that is not valid Gherkin fails with the parser’s diagnosis.
bdd feature create
Create a feature file. The file is staged, not written to the
working tree — review with bdd changes show and apply
with bdd changes commit.
Usage: bdd feature create [OPTIONS] --path <PATH> --name <NAME>
| Flag | Description |
|---|---|
--path <PATH> | Feature file path relative to --root (conventionally under features/). |
--name <NAME> | Feature name — the text after Feature:. |
bdd feature create --path features/string_calculator.feature --name "String Calculator"
bdd changes show
bdd changes commit
The staged file contains the Feature: header ready for scenarios:
Feature: String Calculator
Add scenarios with bdd scenario add — don’t edit the
staged file by hand.
See also
bdd scenario— populate features with tagged scenarios.bdd validate— parse-check all features, staged included.
bdd scenario
Scenario mutations. Every scenario is tied to a requirement by a
@REQ-... tag, keeping the feature files traceable back to the spec.
All three subcommands write to the staging area.
Usage: bdd scenario [OPTIONS] <COMMAND>
Commands: add, update, delete
bdd scenario add
Append a tagged scenario to an existing feature file.
Usage: bdd scenario add [OPTIONS] --feature <FEATURE> --req <REQ> --name <NAME>
| Flag | Description |
|---|---|
--feature <FEATURE> | Feature file path relative to --root. |
--req <REQ> | Requirement id the scenario implements; becomes the @REQ-... tag. |
--name <NAME> | Scenario name. |
--step <STEPS> | One full Gherkin step per flag, repeatable, in order. |
bdd scenario add \
--feature features/string_calculator.feature \
--req REQ-003 \
--name "Two numbers separated by a comma are summed" \
--step 'Given the input "1,2"' \
--step 'When add is called' \
--step 'Then the result is 3'
The staged result appended to the feature:
@REQ-003
Scenario: Two numbers separated by a comma are summed
Given the input "1,2"
When add is called
Then the result is 3
Each --step must start with a Gherkin keyword (Given, When,
Then, And, But); the mutation is validated as real Gherkin
before it stages.
bdd scenario update
Replace a scenario’s steps and/or its requirement tag. The scenario is found by feature path + scenario name; omitted parts are kept.
Usage: bdd scenario update [OPTIONS] --feature <FEATURE> --name <NAME>
| Flag | Description |
|---|---|
--feature <FEATURE> | Feature file path relative to --root. |
--name <NAME> | Name of the scenario to update. |
--req <REQ> | New requirement id for the tag; omit to keep the current tag. |
--step <STEPS> | New steps (repeatable, full replacement); omit to keep the current steps. |
Retag a scenario without touching its steps:
bdd scenario update \
--feature features/string_calculator.feature \
--name "Two numbers separated by a comma are summed" \
--req REQ-007
Rewrite the steps:
bdd scenario update \
--feature features/string_calculator.feature \
--name "Two numbers separated by a comma are summed" \
--step 'Given the input "10,20"' \
--step 'When add is called' \
--step 'Then the result is 30'
bdd scenario delete
Remove a scenario from a feature file.
Usage: bdd scenario delete [OPTIONS] --feature <FEATURE> --name <NAME>
bdd scenario delete \
--feature features/string_calculator.feature \
--name "Two numbers separated by a comma are summed"
Deleting a scenario that does not exist fails with exit status 1 and names the feature searched.
The full rhythm
bdd scenario add --feature features/calc.feature --req REQ-002 --name "..." --step '...'
bdd changes show # review the staged modify
bdd changes commit # apply
bdd steps missing # any steps without definitions?
bdd test # expect RED
See also
bdd steps— find and generate the step definitions behind these scenarios.bdd changes— review, apply, or discard the staged mutation.
bdd steps
Step-definition discovery and generation — the glue between Gherkin scenarios and real code.
Usage: bdd steps [OPTIONS] <COMMAND>
Commands: missing, generate
MCP tool equivalents: step_definitions_find, step_definition_create.
bdd steps missing
Report steps used by scenarios that have no matching definition (undefined), and steps matched by more than one definition (ambiguous). Detection is language-aware: Cucumber-JVM annotations for Java, Cucumber-JS functions for JavaScript/TypeScript, Reqnroll bindings for .NET, cucumber-rs attributes for Rust.
bdd steps missing
{
"language": "javascript",
"framework": "cucumber-js",
"missing": [
{ "step": "Given the input \"1,2\"", "keyword": "Given" },
{ "step": "Then the result is 3", "keyword": "Then" }
],
"nextStep": "Generate skeletons with 'bdd steps generate', then implement their bodies."
}
An empty missing array means every step in every scenario is bound.
bdd steps generate
Generate step definitions for the undefined steps and stage them. Already-defined steps are never regenerated — only the gap is filled.
bdd steps generate
{
"target": "features/step_definitions/string_calculator_steps.js",
"staged": true,
"source": "template",
"summary": "2 step definitions generated for undefined steps.",
"nextStep": "Review with 'bdd changes show', apply with 'bdd changes commit', implement the bodies, then 'bdd test'."
}
source: template or llm
"template"— the deterministic skeleton: correct annotations and signatures, bodies that fail honestly (throw /panic!/PendingStepException) so the first run is genuinely RED."llm"— a model polished the skeleton and the result passed validation. Requires a resolved model (seebdd model); when no model is reachable, generation silently falls back to the template. The LLM output must parse and compile-shape-check or the template is used instead — a model can never stage broken code.
The polish prompt pins the session language’s best practices — package
naming for Java, const/let and strict equality for JavaScript,
typed exports for TypeScript, folder-mirroring namespaces for .NET,
snake_case modules for Rust — so a polished file follows the
ecosystem’s conventions, not just the step expressions.
Generated skeleton (JavaScript flavor):
Given('the input {string}', function (input) {
throw new Error('Pending: implement this step');
});
See also
bdd scenario— where the steps come from.bdd changes— apply the staged definitions.bdd test— run and watch the honest RED.
bdd unittest
Unit-test generation — the TDD altitude beneath the Gherkin scenarios. Where a scenario proves the behavior end to end, the unit test pins down the fine-grained contract of the production code.
Usage: bdd unittest [OPTIONS] <COMMAND>
Commands: generate
MCP tool equivalent: unit_test_create.
bdd unittest generate
Generate a unit test from a requirement’s acceptance criteria and stage it. Each Given/When/Then criterion becomes one test case with the Given as setup, the When as the action, and the Then as the assertion.
Usage: bdd unittest generate [OPTIONS] <REQ_ID>
bdd unittest generate REQ-003
{
"target": "src/test/java/StringCalculatorTest.java",
"staged": true,
"source": "template",
"summary": "Unit test for REQ-003 with 2 cases from its acceptance criteria.",
"nextStep": "Review with 'bdd changes show', apply with 'bdd changes commit', then 'bdd test' to see RED."
}
The target file and framework follow the detected language:
| Language | Test framework | Typical target |
|---|---|---|
| Java | JUnit | src/test/java/<Name>Test.java |
| JavaScript | node test runner | test/<name>.test.js |
| TypeScript | node test runner + ts | test/<name>.test.ts |
| .NET | xUnit-style via the test project | <Name>Tests.cs |
| Rust | #[test] | tests/<name>_test.rs |
source works exactly as in
bdd steps generate: deterministic
template by default, "llm" only when a model’s polished version
passed validation, with the session language’s best practices pinned
in the prompt. Generated assertions fail honestly until the
production code exists — the point is a real RED.
An unknown requirement id fails with exit status 1.
Where it fits
bdd spec show REQ-003 # read the criteria
bdd unittest generate REQ-003 # stage the test
bdd changes commit
bdd test # RED at both altitudes
See also
bdd spec show— the criteria being turned into cases.bdd test— run the generated test.
bdd implement
Ask the resolved model to make the failing tests pass. The model receives the requirement, the last run’s full failure details — stack traces included — the project’s source files, the history of every prior attempt on this requirement, and the session language’s best practices (package naming for Java, snake_case modules for Rust, and so on), and must reply with complete files: the production code plus real bodies for the TODO placeholders in the generated tests and step definitions. Everything it writes lands in the staging area — you review before anything touches the working tree, and the next test run is the real validator.
Usage: bdd implement [OPTIONS] <REQ_ID>
Requires a resolved model (configured with
bdd model use, passed with --model, or
the session default when Ollama has installed models). Without one the
command is refused — implementing stays in your hands. The model this
CLI is developed and run against is qwen3-coder-next:latest; your
mileage will vary with a different model, especially one trained for
work other than development.
bdd test # a fresh RED bar records the failure details
bdd implement REQ-001 # the model attempts the implementation
The command narrates as it works — the preflight result, each asset it
found or missed, then a working ... line while the model call runs.
On a terminal the trailing dots animate in light yellow — growing .
.. ... and starting over — until the call returns; piped output
gets the single static line instead:
REQ-001: checking prerequisites - phase RED, 2 recorded failure(s), 0 prior attempt(s).
scenario tagged @REQ-001: features/string-calculator.feature - present
step definitions (every step defined): src/test/java/steps/GeneratedSteps.java - present
unit test: src/test/java/Req001Test.java - present
production code (the attempt creates it when missing): src/main/java/StringCalculator.java - missing
Sending the sources, the failures, and the attempt history to the model - working ...
staged: src/main/java/StringCalculator.java
{
"targets": [
"src/main/java/StringCalculator.java",
"src/test/java/Req001Test.java",
"src/test/java/steps/GeneratedSteps.java"
],
"staged": true,
"source": "llm",
"nextStep": "Apply with bdd changes commit, then bdd test - the run decides."
}
The follow-up offer
When files were staged and you are on a terminal, the command closes the loop itself:
Apply the staged files and run the tests now? [y/N]
Answering y runs changes commit and test in one go and prints
both reports, ending with the verdict in color — green
GREEN - next: refactor (optional), then spec mark-implemented REQ-001 && changes commit.
or red
Still RED - the fresh failures are recorded; run implement REQ-001 for another model attempt, or implement by hand and rerun test.
Pressing Enter (or piping the output, where no question is asked) declines and prints the next command in plain words instead:
Next: changes commit && test - then implement REQ-001 again if the bar stays RED.
In every one of these lines the command itself — changes commit && test, implement REQ-001, spec mark-implemented REQ-001 && changes commit — is printed in green, the CLI’s marker for text meant to be
copied and pasted.
The preflight
Before anything goes to the model, the command surveys the prerequisites of an implementation attempt:
- a scenario tagged
@REQ-XXXexists in a feature file, - every feature step has a definition,
- the requirement’s unit test exists,
- the requirement is still
pending, and - a RED test run is recorded, so its failures can brief the model.
When one is missing the attempt does not run. Each gap is printed in
red with the step to take instead — bdd scenario add,
bdd steps generate, bdd unittest generate REQ-XXX, or bdd test —
and the JSON reply is the readiness report:
{
"ready": false,
"assets": [
{ "role": "scenario tagged @REQ-001", "path": "features/*.feature", "present": false },
{ "role": "step definitions (every step defined)", "path": "src/test/java/steps/GeneratedSteps.java", "present": false },
{ "role": "unit test", "path": "src/test/java/Req001Test.java", "present": false },
{ "role": "production code (the attempt creates it when missing)", "path": "src/main/java/StringCalculator.java", "present": false }
],
"findings": [
"No RED test run is recorded - run bdd test first so its failures brief the model.",
"No scenario is tagged @REQ-001 - add one with bdd scenario add, then bdd changes commit."
],
"nextStep": "No RED test run is recorded - run bdd test first so its failures brief the model."
}
The production file is surveyed but never blocks — the attempt creates
it when it is missing. A missing prerequisite with a model resolved
also triggers one advice call: the requirement, the asset survey, the
findings, and the last failures go to the model, which answers in a
few sentences whether bdd implement can succeed right now and names
the exact next command. The advice is printed as
Model advice: ... under the findings.
What the model may write
The reply must be a strict JSON array of {path, content} file
updates. Only two kinds of path are accepted:
- files already in the project’s sources (the generated unit test and step definitions it needs to wire up), and
- the production file, named after the spec’s
projectfield by ecosystem convention:
| Language | Production target |
|---|---|
| Java | src/main/java/<Project>.java |
| JavaScript | src/<project>.js |
| TypeScript | src/<project>.ts |
| .NET | <Project>.cs |
| Rust | src/lib.rs |
Anything else in the reply is dropped. A reply with no usable update
fails with The model's reply held no usable file update. — nothing
is staged, and you implement by hand instead.
The implementation prompt is the largest call the CLI makes, so a
local model can need minutes to answer. The generation timeout
defaults to 300 seconds; if you see no reply within ...s, raise
timeout_seconds under [llm] in .bdd-mcp.toml (see
bdd model).
Where it fits
This is the standalone form of the greenfield implementation attempt — the same behavior Enter triggers on a RED bar inside the loop. Use it to continue a paused run:
bdd test # confirm RED, record the failures
bdd implement REQ-001 # stage the model's attempt
bdd changes show # review what it wrote
bdd changes commit
bdd test # GREEN? then bdd refactor / bdd spec mark-implemented
If the bar stays RED, run bdd implement again — the fresh failure
details from the latest run go back to the model — or take over by
hand.
Attempts are remembered
Every attempt is logged in .bdd-state.json (under attemptLog on a
timestamped state entry): the files it wrote, the failures it was
addressing, and — attached by the first test run after it — the
outcome: what that run actually reported, build output included. The
next attempt’s prompt recounts that whole chain — attempt 1 wrote
these files to fix these failures, and the run after it reported this;
what remains now is listed above — with an explicit instruction to
take a different, complete approach instead of repeating one that
already failed. An attempt no run ever followed is called out as never
verified. Failure details carry everything the runner captured:
assertion messages, stack traces, and up to the last 100 lines of a
build that failed before tests could run.
The prompt also carries the interpretation instructions from the state file and only the three latest dated state entries. Older history stays on disk for humans; it is not sent to the model.
The attempt log is scoped to the requirement and cleared the moment a test run goes GREEN — a closed loop leaves no history for the next requirement to inherit.
See also
bdd greenfield— the orchestrated loop with the same attempt built in.bdd changes— review and apply the staged files.bdd test— the run that decides.
bdd validate
Validate all Gherkin in the project — committed feature files and
staged ones — so a broken scenario never reaches a test run. This is
the cheap gate to run before bdd changes commit.
Usage: bdd validate [OPTIONS]
Flags
Only the global flags (--root, --model).
What is checked
- Every
.featurefile under the root parses as valid Gherkin. - Every file in the staging area (
.bdd-staged/) that is a feature file parses too — you cannot commit a transaction containing broken Gherkin without knowing. - Scenario requirement tags (
@REQ-...) refer to ids that exist in the spec.
Examples
Everything clean:
bdd validate
{
"valid": true,
"issues": [],
"nextStep": "Gherkin is clean. Run 'bdd test' or commit staged changes."
}
Problems found (the command exits 0; the report carries the verdict):
{
"valid": false,
"issues": [
"features/string_calculator.feature: (5:3) expected a step keyword",
"staged features/newlines.feature: scenario 'Newlines act as delimiters' is tagged @REQ-009 but the spec has no such requirement"
],
"nextStep": "Fix the listed files (staged ones via their originating command), then validate again."
}
Relation to bdd spec validate
| Command | Validates |
|---|---|
bdd spec validate | The requirements JSON: shape, ids, statuses, criterion phrasing. |
bdd validate | The Gherkin: feature files on disk and in the stage, plus tag/spec consistency. |
Run both before a commit-and-test cycle; both appear as nextStep
suggestions at the appropriate moments.
See also
bdd changes— the staged transaction this gate protects.bdd feature show— inspect a file that failed to parse.
bdd test
Run the project’s tests through its own build tool and update the persistent RED/GREEN/REFACTOR phase from the results. This is the heartbeat of the workflow.
Usage: bdd test [OPTIONS]
MCP tool equivalent: run_tests.
Flags
| Flag | Description |
|---|---|
--feature <FEATURE> | Run only one feature (path or name, passed to the runner’s filter). |
--scenario <SCENARIO> | Run only one scenario by name. |
--root <ROOT> | Project root. Defaults to .. |
--model <MODEL> | Accepted (global flag) but unused — running tests never involves an LLM. |
How the runner is chosen
The runner follows the detected language and shells out to the project’s own toolchain:
| Language | Command under the hood |
|---|---|
| Java | mvn test |
| JavaScript / TypeScript | npm test (Cucumber-JS) |
| .NET | dotnet test |
| Rust | cargo test |
If the runtime is missing, the command refuses instead of pretending:
Error: runtime_missing: mvn is not installed. Install Maven (and a JDK) to run tests; the CLI reports, it never installs.
Examples
A failing run — the phase moves to RED:
bdd test
{
"phase": "RED",
"tests": 3,
"failures": 1,
"errors": 0,
"skipped": 0,
"failureDetails": [
"Two numbers separated by a comma are summed: expected 3 but was 0"
],
"nextStep": "You are RED. Write just enough production code to make the failing test pass, then run tests again."
}
After implementing — GREEN:
{
"phase": "GREEN",
"tests": 3,
"failures": 0,
"errors": 0,
"skipped": 0,
"failureDetails": [],
"nextStep": "You are GREEN. Refactor with 'bdd refactor', or mark the requirement implemented and pick the next one."
}
Filtered runs:
bdd test --feature features/string_calculator.feature
bdd test --scenario "Two numbers separated by a comma are summed"
Filters are forwarded to the underlying runner (e.g. Cucumber’s name filter), so only the selected slice executes — useful while iterating on one scenario.
Phase semantics
- Any failure or error ⇒
RED. - All passing ⇒
GREEN(also ends a REFACTOR step successfully). - A failing run during REFACTOR drops you back to RED — the refactor broke behavior.
The phase is stored in .bdd-tdd-state.json and read back by
bdd state and enforced by bdd refactor.
See also
- The workflow — the phase machine in full.
bdd inspect— check the runtime before expecting a run.
bdd state
Show the current TDD phase, the last run’s counts, the refactor log, and at most the three latest dated state entries. Read-only — it never changes anything.
Usage: bdd state [OPTIONS]
MCP tool equivalent: get_tdd_state.
Flags
Only the global flags (--root, --model).
Examples
bdd state
{
"instructions": "This file is the TDD phase log. ... When briefing a model, include only the three most recent entries. ...",
"phase": "GREEN",
"lastRun": {
"tests": 3,
"failures": 0,
"errors": 0,
"skipped": 0
},
"refactorLog": [
"extract the delimiter parser from add()"
],
"entries": [
{
"timestamp": "2026-08-13T21:01:00Z",
"phase": "RED",
"lastRun": { "tests": 3, "failures": 1, "errors": 0, "skipped": 0 },
"refactorLog": [],
"attemptLog": []
},
{
"timestamp": "2026-08-13T21:02:00Z",
"phase": "GREEN",
"lastRun": { "tests": 3, "failures": 0, "errors": 0, "skipped": 0 },
"refactorLog": [
"extract the delimiter parser from add()"
],
"attemptLog": []
}
],
"nextStep": "You are GREEN. Refactor with 'bdd refactor', or mark the requirement implemented."
}
Before any test has ever run, the phase is the starting state and
lastRun is all zeros; nextStep points you at bdd test.
Where the state lives
.bdd-state.json under the project root. It is a chronological log of
timestamped entries — one per test run, refactor, or implementation
attempt — plus instructions that explain how to read the schema. The
file keeps the full history so a human can audit the loop; bdd state
and every model brief include only the three latest entries. Delete
the file to reset the phase machine (there is deliberately no reset
command — losing the log should be an explicit filesystem act).
The same file also carries attemptLog — the record of every model
implementation attempt that bdd implement and the
greenfield loop brief the next attempt with: the files it wrote
(targets), the failures it was briefed with (failures), and the
output of the first test run after it (outcome; empty means no run
ever verified it). A file written before this field existed loads with
an empty outcome — the attempt is treated as never verified. A GREEN
run clears it: a closed loop leaves no history for the next
requirement to inherit.
Reading the reply
instructions— how to interpret the log (the same text stored in the file).phase—RED,GREEN, orREFACTOR(see the workflow). The current phase; also the last entry’sphase.lastRun— counts only; the failure details live in thebdd testreply that produced them.refactorLog— every note passed tobdd refactor --note, in order. It is the audit trail of intentional design work.entries— at most the three latest dated snapshots (timestamp,phase,lastRun,refactorLog,attemptLog). Older entries stay on disk and are not sent to a model.
See also
bdd test— the command that moves the phase.bdd refactor— appends to the log shown here.
bdd status
Where the project stands on the road to every requirement being
implemented — and the one next step that moves it forward. bdd state
answers “what did the last test run say”; bdd status answers “where
am I in the whole loop and what do I do now”.
Usage: bdd status [OPTIONS]
bdd status
{
"phase": "RED",
"staged": [
{ "path": "src/main/java/BddTest.java", "action": "modify", "summary": "implementation attempt for REQ-001 (llm)" }
],
"requirements": [
{
"id": "REQ-001",
"title": "Text Input Calculator",
"status": "pending",
"findings": []
}
],
"nextStep": "1 staged file(s) await review - inspect with bdd changes show, apply with bdd changes commit, then run bdd test."
}
How the next step is chosen
The priority order mirrors the loop itself:
- Staged changes wait — nothing the CLI authors touches the
working tree until you apply it, so an unapplied implementation
attempt (or scenario, or spec edit) always comes first:
bdd changes show, thenbdd changes commit, thenbdd test. - A requirement is in flight — its scenario, step definitions,
and unit test all exist. On GREEN the loop closes with the chain
bdd spec mark-implemented <id>, thenbdd validate, thenbdd changes commit; on any other bar the step isbdd test, and on REDbdd implement <id>lets the model try. - The earliest asset gap — a pending requirement is missing its
tagged scenario (
bdd scenario add), step definitions (bdd steps generate), or unit test (bdd unittest generate <id>); the finding names the command. - Everything is implemented — draft the next requirement with
bdd spec draft.
Each pending requirement’s entry carries its own findings, so with
several requirements you see every gap, not just the first.
Model advice
When a model is resolved (see bdd model), the
deterministic report is followed by one advice call: the model is
briefed with the whole workflow process — the states, the commands,
the loop, and the invariants — plus the current phase, the last run’s
counts, the staging area, and every requirement’s position, and it
answers with the next command in plain words:
Model advice: The bar is GREEN and REQ-001 has every asset in place -
close the loop with bdd spec mark-implemented REQ-001, then bdd
validate, then bdd changes commit.
Without a model the report alone is the whole reply, and a model
failure never breaks bdd status.
Why a requirement stays pending
implemented is never set by a passing run alone. The status flips
only when you run bdd spec mark-implemented — and that
command is GREEN-gated: it refuses unless the last recorded run
passed, and it refuses without a scenario tagged @<id> (it records
the tagged feature as the requirement’s featureFile). The road is
always: staged changes applied → bdd test GREEN →
bdd spec mark-implemented <id> → bdd validate →
bdd changes commit (the status change is staged too, like every
mutation).
See also
bdd state— the raw TDD state: phase, last run, refactor log.bdd changes— review and apply what is staged.bdd implement— the model attempt, with its own preflight.
bdd refactor
Begin a refactor step. Only allowed on GREEN — the discipline’s core rule is that you never restructure code while tests are failing.
Usage: bdd refactor [OPTIONS]
MCP tool equivalent: start_refactor.
Flags
| Flag | Description |
|---|---|
--note <NOTE> | What you intend to refactor and why. Recorded in the refactor log. |
--root <ROOT> | Project root. Defaults to .. |
--model <MODEL> | Accepted (global flag) but unused. |
Examples
On GREEN:
bdd refactor --note "extract the delimiter parser from add()"
{
"phase": "REFACTOR",
"nextStep": "Refactor with the tests as your safety net, then 'bdd test'. Passing returns you to GREEN; a failure means the refactor broke behavior."
}
Attempting it on RED is refused with exit status 1:
Error: refactoring is only allowed on GREEN - you are RED. Make the tests pass first.
The refactor loop
bdd test # GREEN - safe to restructure
bdd refactor --note "collapse duplicate parsing"
# ...restructure, behavior unchanged...
bdd test # GREEN again: refactor complete
If that final bdd test fails, the phase drops to RED: the refactor
changed behavior, and the failing tests tell you exactly where.
Why the note matters
Each --note is appended to the refactorLog that
bdd state reports. Over a kata or a workshop, the log
becomes the narrative of deliberate design decisions — which is the
half of TDD that “make it pass” alone never captures.
See also
bdd state— the phase and the accumulated log.- The workflow — where REFACTOR sits in the machine.
bdd changes
Staged-transaction management. Every file mutation the CLI authors
lands in .bdd-staged/ first (see Staged changes);
these subcommands are how you review, apply, or drop the transaction.
Usage: bdd changes [OPTIONS] <COMMAND>
Commands: show, commit, discard
None of the subcommands take flags beyond the global flags.
bdd changes show
List everything currently staged: the path, whether applying would create or modify the file, and a one-line summary of the change.
bdd changes show
{
"changes": [
{
"path": "features/string_calculator.feature",
"action": "modify",
"summary": "append scenario 'Two numbers separated by a comma are summed' tagged @REQ-003"
},
{
"path": "features/step_definitions/string_calculator_steps.js",
"action": "create",
"summary": "2 step definitions generated for undefined steps"
}
],
"nextStep": "Apply with 'bdd changes commit' or drop with 'bdd changes discard'."
}
An empty stage:
{
"changes": [],
"nextStep": "Nothing is staged. Authoring commands (feature, scenario, steps, unittest, spec draft) stage their output here."
}
To see the full content of a staged file, read it directly under
.bdd-staged/ — the layout mirrors the project tree.
bdd changes commit
Apply every staged change to the working tree atomically and clear
the stage. Files marked create are written fresh; modify replaces
the working copy with the staged version.
bdd changes commit
Run bdd validate first when the transaction contains
Gherkin — broken staged Gherkin is reported there before it can land.
After applying, commit re-validates the working tree. Open issues
ride along in the reply as a warning — the commit still happened, but
an invalid spec never lands silently:
{
"changes": [
{ "path": "requirements/requirements.json", "action": "modify", "summary": "mark REQ-001 implemented" }
],
"issues": [
"REQ-001: implemented requirements must name their featureFile - rerun bdd spec mark-implemented REQ-001 on GREEN to backfill it"
],
"nextStep": "Staged changes applied, but the working tree does not validate - fix the issues above, then run bdd validate again."
}
A clean commit carries no issues field.
bdd changes discard
Drop the entire staged transaction. The working tree is untouched; the stage is emptied. There is no partial discard — the stage is one transaction by design (a scenario without its step definitions is not a state worth keeping).
bdd changes discard
A typical review session
bdd scenario add --feature features/calc.feature --req REQ-002 \
--name "A single number is returned" --step 'Given the input "5"' \
--step 'When add is called' --step 'Then the result is 5'
bdd steps generate
bdd changes show # one modify + one create
bdd validate # staged Gherkin parses, tags resolve
bdd changes commit # both land together
bdd test # honest RED
See also
- Staged changes — the model and its rationale.
bdd validate— the gate beforecommit.
bdd model
LLM model discovery and selection. The CLI talks to a local Ollama — no cloud calls, no tokens — and uses the model only to polish deterministic templates in the generation commands. Everything works without a model; generation just stays at template quality.
The model this CLI is developed and run against is
qwen3-coder-next:latest. Pull it with
ollama pull qwen3-coder-next:latest, then persist the choice with
bdd model use qwen3-coder-next:latest. Your mileage will vary with
other models: a stronger coding model may improve drafts and
implementations; a model trained for chat, general knowledge, or work
other than development will typically produce weaker specs, steps,
tests, and production code. The CLI does not require this specific
model — it uses whatever you configure, or the first model Ollama has
installed.
Usage: bdd model [OPTIONS] <COMMAND>
Commands: list, current, use
How a model is resolved
Highest priority first:
--modelflag — this invocation only, never persisted.- Configuration — the
modelkey in.bdd-mcp.tomlunder the project root, written bybdd model use. - Discovery — the first model installed in Ollama, as a session-only default. Nothing is written to disk.
If Ollama is unreachable or has no models, LLM-backed generation falls back to deterministic templates.
bdd model list
List the models installed in Ollama, marking the one that would currently be used.
bdd model list
Models available in Ollama:
* qwen3-coder-next:latest (configured)
qwen3:8b
llama3:8b
With no configuration, the marker moves to the discovered session default. If Ollama is down, the command fails with exit status 1 and says the provider is unreachable.
bdd model current
Show the resolved model and where it came from.
bdd model current
Configured model: qwen3-coder-next:latest
With nothing configured but models installed, the first one is the session default and the output tells you it is not saved:
Model set for this session: qwen3-coder-next:latest (not saved - keep it with: bdd model use qwen3-coder-next:latest).
The same announcement appears when the interactive shell starts.
bdd model use
Persist a model choice in the project’s configuration.
Usage: bdd model use [OPTIONS] <MODEL_NAME>
bdd model use qwen3-coder-next:latest
Configured model: qwen3-coder-next:latest
Written to /Users/you/code/calculator/.bdd-mcp.toml
The choice is validated against Ollama’s installed models — a name Ollama does not have is rejected rather than silently saved.
The [llm] configuration block
Everything model-related lives under [llm] in .bdd-mcp.toml:
[llm]
model = "qwen3-coder-next:latest" # persisted by bdd model use
endpoint = "http://localhost:11434" # the Ollama endpoint
timeout_seconds = 300 # generation timeout (default 300)
timeout_seconds bounds how long one generation call may take. Large
prompts — an implementation attempt carries the requirement, the
failure details, and every project source file — can keep a local
model generating for minutes; when the budget runs out the error names
it explicitly (no reply within 300s ... set timeout_seconds under [llm]). Raise it for big projects or slower models.
Which commands actually use the model
| Uses the model | Never touches it |
|---|---|
spec draft (description wizard, findings rewording) | test, state, refactor |
steps generate | spec (other subcommands) |
unittest generate | feature, scenario, changes |
implement | init, inspect, validate |
greenfield (drafting, generation, implementation) |
See also
- Global flags — the
--modeloverride. bdd steps generate— how LLM output is validated before it can stage.
bdd mcp
The embedded MCP server. This is the same workflow the CLI offers a human, exposed to AI agents as typed tools over the Model Context Protocol.
Usage: bdd mcp [OPTIONS] <COMMAND>
Commands: serve
bdd mcp serve
Serve the MCP tools over stdio. The process reads JSON-RPC on stdin and writes replies on stdout, so an MCP client (Cursor, Claude Desktop, any MCP-capable agent) launches it as a child process — you normally never run it by hand.
bdd mcp serve --root /path/to/project
Client configuration (Cursor’s mcp.json shown; others are
equivalent):
{
"mcpServers": {
"bdd-workflow": {
"command": "bdd",
"args": ["mcp", "serve", "--root", "/path/to/project"]
}
}
}
The tools served
The tool names and reply shapes are byte-compatible with the
workshop’s Java tdd-workflow-server, so existing clients work
unchanged:
| MCP tool | CLI equivalent |
|---|---|
list_requirements | bdd spec list |
get_requirement | bdd spec show |
validate_spec | bdd spec validate |
refine_requirement | bdd spec refine |
requirement_mark_implemented | bdd spec mark-implemented |
step_definitions_find | bdd steps missing |
step_definition_create | bdd steps generate |
unit_test_create | bdd unittest generate |
run_tests | bdd test |
get_tdd_state | bdd state |
start_refactor | bdd refactor |
Why serve tools instead of letting the agent edit files?
- No escape hatches. The agent gets exactly these tools — no shell, no arbitrary file writes. Mutations go through the staging area for human review.
- The discipline is in the server. An agent cannot skip RED,
refactor while failing, or invent requirements: the tools refuse,
with a
nextStepthat teaches the correct move. - State survives. The phase machine lives on disk, so a reconnecting agent (or a human taking over in the CLI) continues from the same place.
Flags
| Flag | Description |
|---|---|
--root <ROOT> | Project root the served tools operate on. Defaults to the process’s working directory. |
--model <MODEL> | Model override for the serving session’s generation tools. |
Notes
- The server logs nothing to stdout except protocol traffic (stdout is the wire). Diagnostics go to stderr.
- One server serves one project root. Point different projects at different server entries.
- Generation tools use the same local Ollama resolution as the rest
of the CLI. This CLI is developed and run against
qwen3-coder-next:latest; your mileage will vary with other models, especially those not trained for development work. Seebdd model.