Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 bdd is optional.
  • Replies are JSON on stdout unless a command is inherently interactive. Every reply carries a nextStep field 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 Java tdd-workflow-server.

Supported languages

LanguageBuild toolBDD frameworkRuntime probed
JavaMavenCucumber-JVMmvn
JavaScriptnpmCucumber-JSnode
TypeScriptnpm + ts-nodeCucumber-JSnode
.NETdotnetReqnrolldotnet
RustCargocucumber-rscargo

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):

  1. --model flag — this invocation only.
  2. model in .bdd-mcp.toml — the persisted project choice.
  3. 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, not bdd spec list. A leading bdd is forgiven if you type it anyway.
  • Inherited flags. Commands inherit the shell’s --root and --model unless 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 report unreadable input and 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

  • exit or quit
  • 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.

SituationAnnouncement
Configured in .bdd-mcp.tomlModel set: qwen3-coder-next:latest (from configuration).
No config, models installedModel set for this session: qwen3-coder-next:latest (not saved - keep it with: bdd model use qwen3-coder-next:latest).
Ollama up, no modelsOllama 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 unreachableOllama 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 —

  1. this is the first shell session in the root (no .bdd-history yet),
  2. a model is ready, and
  3. 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:

  1. Draft or edit a requirement — bdd spec draft or your editor.
  2. bdd spec validate until the structure is valid.
  3. bdd spec refine <id> until there are no wording findings.
  4. 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 test runs the suite and moves the phase to RED (failures) or GREEN (all passing).
  • bdd refactor is only allowed on GREEN — it moves to REFACTOR and records your note in the refactor log.
  • bdd state shows the phase, the last run’s counts, and the refactor log at any time.
  • bdd status zooms 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 stageWrites directly
feature createinit (scaffolding a fresh project)
scenario add / update / deletemodel use (writes .bdd-mcp.toml)
steps generatetest / 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

FlagDescription
--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:

LanguageScaffolded files
javapom.xml (Maven + Cucumber-JVM), src/test/java/RunCucumberTest.java, features/.gitkeep
javascriptpackage.json (Cucumber-JS), cucumber.js, features/step_definitions/.gitkeep
typescriptpackage.json, tsconfig.json, cucumber.js (ts-node hooked in), features/step_definitions/.gitkeep
dotnet<Name>.Tests.csproj (Reqnroll), features/.gitkeep
rustCargo.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

  • init writes directly to the working tree (there is nothing to protect in an empty project); everything after init goes through staged changes.
  • init does not install runtimes. Run bdd inspect to see whether the language’s runtime is present before expecting bdd test to execute.

See also

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

FlagDescription
--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, or cargo) — 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

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 it hint on criterion prompts, model failures, and other dead ends render red; the animated dots on working ... 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 with bdd scenario add and apply it with bdd changes commit first.

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

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 rootLanguageRuntime probed
pom.xmlJava (Maven + Cucumber-JVM)mvn
package.json + tsconfig.jsonTypeScript (Cucumber-JS)node
package.jsonJavaScript (Cucumber-JS)node
*.csproj.NET (Reqnroll)dotnet
Cargo.tomlRust (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 test and the test-running parts of bdd greenfield require 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>
FlagDescription
--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

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>
FlagDescription
--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>
FlagDescription
--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 (see bdd 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 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:

LanguageTest frameworkTypical target
JavaJUnitsrc/test/java/<Name>Test.java
JavaScriptnode test runnertest/<name>.test.js
TypeScriptnode test runner + tstest/<name>.test.ts
.NETxUnit-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 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-XXX exists 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 project field by ecosystem convention:
LanguageProduction target
Javasrc/main/java/<Project>.java
JavaScriptsrc/<project>.js
TypeScriptsrc/<project>.ts
.NET<Project>.cs
Rustsrc/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 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 .feature file 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

CommandValidates
bdd spec validateThe requirements JSON: shape, ids, statuses, criterion phrasing.
bdd validateThe 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 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

FlagDescription
--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:

LanguageCommand under the hood
Javamvn test
JavaScript / TypeScriptnpm test (Cucumber-JS)
.NETdotnet test
Rustcargo 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

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).
  • phaseRED, GREEN, or REFACTOR (see the workflow). The current phase; also the last entry’s phase.
  • lastRun — counts only; the failure details live in the bdd test reply that produced them.
  • refactorLog — every note passed to bdd 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 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:

  1. 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, then bdd changes commit, then bdd test.
  2. 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>, then bdd validate, then bdd changes commit; on any other bar the step is bdd test, and on RED bdd implement <id> lets the model try.
  3. 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.
  4. 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 validatebdd 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

FlagDescription
--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 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

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:

  1. --model flag — this invocation only, never persisted.
  2. Configuration — the model key in .bdd-mcp.toml under the project root, written by bdd model use.
  3. 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 modelNever touches it
spec draft (description wizard, findings rewording)test, state, refactor
steps generatespec (other subcommands)
unittest generatefeature, scenario, changes
implementinit, inspect, validate
greenfield (drafting, generation, implementation)

See also

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 toolCLI equivalent
list_requirementsbdd spec list
get_requirementbdd spec show
validate_specbdd spec validate
refine_requirementbdd spec refine
requirement_mark_implementedbdd spec mark-implemented
step_definitions_findbdd steps missing
step_definition_createbdd steps generate
unit_test_createbdd unittest generate
run_testsbdd test
get_tdd_statebdd state
start_refactorbdd 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 nextStep that 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

FlagDescription
--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. See bdd model.