Keeps a BDD step-definition library DRY across a Cucumber / Behave / Reqnroll project - inventories every step definition, detects duplicates (different patterns matching the same intent), recommends canonical consolidations, reorganizes steps by domain, publishes a step-library README the team greps for "is there already a step for X?" before authoring new ones, and builds a scenario coverage map that fingerprints new Gherkin scenarios against the live suite to classify each as duplicate, partial overlap, or genuine gap before any test is authored. Use when a BDD project's step count grows past ~50, on a quarterly step-library review, when a new engineer is about to write a duplicate step, or when fresh .feature files need a covered-already check.
73
92%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Deep reference for the bdd-step-library-curator SKILL. Consult when running the inventory against a specific BDD runner, wiring the programmatic overlap detector, or adding the pre-merge new-step CI gate.
Each BDD runner declares steps with its own annotation syntax; extract the raw patterns per runner and combine the output into a single list.
# Cucumber-JVM
grep -rE '@(Given|When|Then|And|But)\(' src/test/java/ | \
sed -E 's/.*@(Given|When|Then|And|But)\("([^"]*)".*/\2/'
# Behave
grep -rE '^@(given|when|then|step)\(' features/steps/ | \
sed -E 's/.*@(given|when|then|step)\("([^"]*)".*/\2/'
# Reqnroll / SpecFlow
grep -rE '\[(Given|When|Then|And|But)\(' Tests/Steps/ | \
sed -E 's/.*\[(Given|When|Then|And|But)\("([^"]*)".*/\2/'Each command emits one step pattern per line; feed the combined list into the overlap detector below.
Normalize each pattern (lowercase, strip articles, collapse every parameter to a single placeholder) and group patterns that collapse to the same normalized form. Any group of size greater than one is a candidate duplicate cluster.
# scripts/step-overlap.py
import re
def normalize(pattern):
"""Lower; strip articles; remove parameter type hints."""
p = pattern.lower()
p = re.sub(r'\b(a|an|the)\b', '', p)
p = re.sub(r'\{[^}]+\}', '{var}', p)
p = re.sub(r'\s+', ' ', p).strip()
return p
steps = [...] # from the extraction step
normalized = {}
for s in steps:
n = normalize(s['pattern'])
normalized.setdefault(n, []).append(s)
for n, group in normalized.items():
if len(group) > 1:
print(f"Likely duplicates ({len(group)}):")
for s in group:
print(f" {s['pattern']} - {s['file']}:{s['line']}")The detector is a heuristic - review each cluster before consolidating, since some collisions are legitimately distinct steps.
A CI check that warns (does not block) whenever a PR adds new step definitions, forcing the author to confirm the step is not already in the library.
# scripts/check-new-steps.sh
NEW_STEPS=$(git diff --diff-filter=A origin/main...HEAD -- '**/steps/*.py' '**/Steps/*.cs' '**/steps/*.java' \
| grep -E '^\+' | grep -E '@(Given|When|Then)' | wc -l)
if [ $NEW_STEPS -gt 0 ]; then
echo "::warning::This PR adds $NEW_STEPS new step definitions."
echo "Before merging, verify these aren't duplicates of existing steps."
echo "Run: bash scripts/step-overlap.py"
fiThe warning is intentionally non-blocking - it forces acknowledgment of a new step without stopping legitimately-new ones.