assets/methods.csv
num,category,method_name,description,output_pattern
1,advanced,Tree of Thoughts,Explore multiple reasoning paths simultaneously then evaluate and select the best - perfect for complex problems with multiple valid approaches,paths → evaluation → selection
2,advanced,Graph of Thoughts,Model reasoning as an interconnected network of ideas to reveal hidden relationships - ideal for systems thinking and discovering emergent patterns,nodes → connections → patterns
3,advanced,Thread of Thought,Maintain coherent reasoning across long contexts by weaving a continuous narrative thread - essential for RAG systems and maintaining consistency,context → thread → synthesis
4,advanced,Self-Consistency Validation,Generate multiple independent approaches then compare for consistency - crucial for high-stakes decisions where verification matters,approaches → comparison → consensus
5,advanced,Meta-Prompting Analysis,Step back to analyze the approach structure and methodology itself - valuable for optimizing prompts and improving problem-solving,current → analysis → optimization
6,advanced,Reasoning via Planning,Build a reasoning tree guided by world models and goal states - excellent for strategic planning and sequential decision-making,model → planning → strategy
7,advanced,Chain-of-Thought Scaffolding,Force explicit intermediate reasoning steps before any conclusion — prevents intuitive leaps that skip flawed logic,premise → step → step → conclusion
8,advanced,Few-Shot Exemplar Priming,Provide 2-3 worked examples of the desired reasoning pattern before the real task — aligns output format and depth through demonstration,examples → pattern recognition → application
9,collaboration,Stakeholder Round Table,Convene multiple personas to contribute diverse perspectives - essential for requirements gathering and finding balanced solutions across competing interests,perspectives → synthesis → alignment
10,collaboration,Expert Panel Review,Assemble domain experts for deep specialized analysis - ideal when technical depth and peer review quality are needed,expert views → consensus → recommendations
11,collaboration,Debate Club Showdown,Two personas argue opposing positions while a moderator scores points - great for exploring controversial decisions and finding middle ground,thesis → antithesis → synthesis
12,collaboration,User Persona Focus Group,Gather your product's user personas to react to proposals and share frustrations - essential for validating features and discovering unmet needs,reactions → concerns → priorities
13,collaboration,Time Traveler Council,Past-you and future-you advise present-you on decisions - powerful for gaining perspective on long-term consequences vs short-term pressures,past wisdom → present choice → future impact
14,collaboration,Cross-Functional War Room,Product manager + engineer + designer tackle a problem together - reveals trade-offs between feasibility desirability and viability,constraints → trade-offs → balanced solution
15,collaboration,Mentor and Apprentice,Senior expert teaches junior while junior asks naive questions - surfaces hidden assumptions through teaching,explanation → questions → deeper understanding
16,collaboration,Good Cop Bad Cop,Supportive persona and critical persona alternate - finds both strengths to build on and weaknesses to address,encouragement → criticism → balanced view
17,collaboration,Improv Yes-And,Multiple personas build on each other's ideas without blocking - generates unexpected creative directions through collaborative building,idea → build → build → surprising result
18,collaboration,Customer Support Theater,Angry customer and support rep roleplay to find pain points - reveals real user frustrations and service gaps,complaint → investigation → resolution → prevention
19,collaboration,Six Thinking Hats,Rotate through six modes (facts - feelings - caution - optimism - creativity - process) to ensure a group covers every angle without crosstalk,white → red → black → yellow → green → blue
20,collaboration,Delphi Method,Experts give independent estimates - see anonymized results - then revise — converges on calibrated group judgment while avoiding anchoring bias,independent estimates → reveal → revise → converge
21,competitive,Red Team vs Blue Team,Adversarial attack-defend analysis to find vulnerabilities - critical for security testing and building robust solutions,defense → attack → hardening
22,competitive,Shark Tank Pitch,Entrepreneur pitches to skeptical investors who poke holes - stress-tests business viability and forces clarity on value proposition,pitch → challenges → refinement
23,competitive,Code Review Gauntlet,Senior devs with different philosophies review the same code - surfaces style debates and finds consensus on best practices,reviews → debates → standards
24,core,First Principles Analysis,Strip away assumptions to rebuild from fundamental truths - breakthrough technique for innovation and solving impossible problems,assumptions → truths → new approach
25,core,5 Whys Deep Dive,Repeatedly ask why to drill down to root causes - simple but powerful for understanding failures,why chain → root cause → solution
26,core,Socratic Questioning,Use targeted questions to reveal hidden assumptions and guide discovery - excellent for teaching and self-discovery,questions → revelations → understanding
27,core,Critique and Refine,Systematic review to identify strengths and weaknesses then improve - standard quality check for drafts,strengths/weaknesses → improvements → refined
28,core,Explain Reasoning,Walk through step-by-step thinking to show how conclusions were reached - crucial for transparency,steps → logic → conclusion
29,core,Expand or Contract for Audience,Dynamically adjust detail level and technical depth for target audience - matches content to reader capabilities,audience → adjustments → refined content
30,core,Second-Order Thinking,Think beyond immediate consequences to anticipate cascading effects and long-term implications - essential for strategic decisions where first-order solutions create hidden downstream problems,action → consequences → second-order effects → informed choice
31,core,Inversion Analysis,Flip the problem by asking what would guarantee failure instead of how to succeed - reveals hidden obstacles and blind spots by approaching challenges from the opposite direction,goal → invert → failure paths → avoidance → solution
32,core,Problem Decomposition,Break a complex problem into independent sub-problems - solve each - then reassemble — essential when a task is too large or tangled to tackle whole,whole → parts → solutions → reassembly
33,core,Analogy Mapping,Find a well-understood parallel domain and transfer its structure to the current problem — unlocks insight by borrowing proven mental models,source domain → mapping → target insight
34,core,Steelmanning,Construct the strongest possible version of an opposing argument before responding — builds credibility and catches blind spots that strawmanning misses,opposing view → strongest form → honest rebuttal
35,creative,SCAMPER Method,Apply seven creativity lenses (Substitute/Combine/Adapt/Modify/Put/Eliminate/Reverse) - systematic ideation for product innovation,S→C→A→M→P→E→R
36,creative,Reverse Engineering,Work backwards from desired outcome to find implementation path - powerful for goal achievement and understanding endpoints,end state → steps backward → path forward
37,creative,What If Scenarios,Explore alternative realities to understand possibilities and implications - valuable for contingency planning and exploration,scenarios → implications → insights
38,creative,Random Input Stimulus,Inject unrelated concepts to spark unexpected connections - breaks creative blocks through forced lateral thinking,random word → associations → novel ideas
39,creative,Exquisite Corpse Brainstorm,Each persona adds to the idea seeing only the previous contribution - generates surprising combinations through constrained collaboration,contribution → handoff → contribution → surprise
40,creative,Genre Mashup,Combine two unrelated domains to find fresh approaches - innovation through unexpected cross-pollination,domain A + domain B → hybrid insights
41,creative,Constraint Injection,Deliberately add an artificial limitation (budget - time - technology) to force novel solutions — creativity thrives under pressure,add constraint → forced creativity → remove constraint → evaluate
42,creative,Morphological Analysis,List independent parameters of a problem - enumerate options for each - then systematically combine — ensures you don't miss non-obvious configurations,parameters → options grid → combinations → evaluation
43,creative,Subtraction,Improve by deliberately removing elements instead of adding them - counters the well-documented additive bias where people overlook subtractive changes that would simplify and strengthen the work,current state → what to remove → simplified result
44,framing,Abstraction Laddering,"Move up (""why?"") for strategic clarity or down (""how?"") for tactical detail — ensures you're solving at the right altitude",concrete ↔ abstract → right level
45,framing,Reframe the Question,Challenge whether the stated problem is the real problem — often the question itself is wrong and a better framing unlocks an easy answer,stated problem → reframe → true problem → solution
46,framing,Stakeholder Lens Rotation,Serially adopt each stakeholder's world-view to see the same situation differently — reveals whose needs are being overlooked,perspective A → B → C → gaps found
47,framing,Map Is Not the Territory,Treat any model or diagram as a lossy abstraction of reality - check where the representation diverges from the real system before trusting it,model → reality check → divergences found → corrected understanding
48,learning,Feynman Technique,Explain complex concepts simply as if teaching a child - the ultimate test of true understanding,complex → simple → gaps → mastery
49,learning,Active Recall Testing,Test understanding without references to verify true knowledge - essential for identifying gaps,test → gaps → reinforcement
50,learning,Deliberate Practice Loop,Identify a specific sub-skill - drill it with immediate feedback - adjust - repeat — targeted improvement beats general repetition,isolate → drill → feedback → adjust → repeat
51,philosophical,Occam's Razor Application,Find the simplest sufficient explanation by eliminating unnecessary complexity - essential for debugging,options → simplification → selection
52,philosophical,Trolley Problem Variations,Explore ethical trade-offs through moral dilemmas - valuable for understanding values and difficult decisions,dilemma → analysis → decision
53,research,Literature Review Personas,Optimist researcher + skeptic researcher + synthesizer review sources - balanced assessment of evidence quality,sources → critiques → synthesis
54,research,Thesis Defense Simulation,Student defends hypothesis against committee with different concerns - stress-tests research methodology and conclusions,thesis → challenges → defense → refinements
55,research,Comparative Analysis Matrix,Multiple analysts evaluate options against weighted criteria - structured decision-making with explicit scoring,options → criteria → scores → recommendation
56,research,Source Triangulation,Require at least three independent source types (quantitative - qualitative - expert) before accepting a claim — guards against single-source bias,claim → source A → source B → source C → confidence rating
57,retrospective,Hindsight Reflection,Imagine looking back from the future to gain perspective - powerful for project reviews,future view → insights → application
58,retrospective,Lessons Learned Extraction,Systematically identify key takeaways and actionable improvements - essential for continuous improvement,experience → lessons → actions
59,risk,Pre-mortem Analysis,Imagine future failure then work backwards to prevent it - powerful technique for risk mitigation before major launches,failure scenario → causes → prevention
60,risk,Failure Mode Analysis,Systematically explore how each component could fail - critical for reliability engineering and safety-critical systems,components → failures → prevention
61,risk,Challenge from Critical Perspective,Play devil's advocate to stress-test ideas and find weaknesses - essential for overcoming groupthink,assumptions → challenges → strengthening
62,risk,Identify Potential Risks,Brainstorm what could go wrong across all categories - fundamental for project planning and deployment preparation,categories → risks → mitigations
63,risk,Chaos Monkey Scenarios,Deliberately break things to test resilience and recovery - ensures systems handle failures gracefully,break → observe → harden
64,risk,Assumption Audit,Explicitly list every assumption underlying a plan - rate each by confidence and impact - then stress-test the weakest — prevents building on shaky foundations,list → rate → stress-test → shore up
65,risk,Cascading Failure Simulation,Trace how one component's failure propagates through dependencies — reveals hidden coupling and single points of failure,trigger failure → trace propagation → find amplifiers → decouple
66,technical,Architecture Decision Records,Multiple architect personas propose and debate architectural choices with explicit trade-offs - ensures decisions are well-reasoned and documented,options → trade-offs → decision → rationale
67,technical,Rubber Duck Debugging Evolved,Explain your code to progressively more technical ducks until you find the bug - forces clarity at multiple abstraction levels,simple → detailed → technical → aha
68,technical,Algorithm Olympics,Multiple approaches compete on the same problem with benchmarks - finds optimal solution through direct comparison,implementations → benchmarks → winner
69,technical,Security Audit Personas,Hacker + defender + auditor examine system from different threat models - comprehensive security review from multiple angles,vulnerabilities → defenses → compliance
70,technical,Performance Profiler Panel,Database expert + frontend specialist + DevOps engineer diagnose slowness - finds bottlenecks across the full stack,symptoms → analysis → optimizations
71,technical,Boundary & Edge Case Sweep,Systematically test extremes - zeros - nulls - maximums - and type mismatches — catches the failures that happy-path thinking always misses,inputs → boundaries → edge cases → failures found
customize.toml
# DO NOT EDIT -- overwritten on every update.
#
# Workflow customization surface for bmad-advanced-elicitation.
#
# Override files (not edited here):
# {project-root}/_bmad/custom/bmad-advanced-elicitation.toml (team)
# {project-root}/_bmad/custom/bmad-advanced-elicitation.user.toml (personal)
[workflow]
# --- Configurable below. Overrides merge per BMad structural rules: ---
# scalars: override wins • plain arrays: append
# arrays of tables keyed by `code`: matching key replaces, new keys append
# The elicitation method catalog served by scripts/pick_methods.py
# (columns: num,category,method_name,description,output_pattern). Swap the path
# in team/user TOML to ship a different catalog. Kept `{skill-root}`-anchored so
# it resolves regardless of the working directory (pick_methods.py is always
# invoked with `--file {workflow.methods_file}`).
methods_file = "{skill-root}/assets/methods.csv"
# Persistent preferences the refiner honors for every session — methods to
# favor or avoid, how pushback should land, house rules for applying changes.
# Literal sentences; append-merges, so team and personal preferences both apply.
#
# Examples (set in team/user override TOML):
# preferences = [
# "Lead with a risk-category method for anything touching production systems.",
# "Never offer roleplay or persona methods.",
# ]
preferences = []
# Extra methods — and whole new categories — merged into the catalog without
# editing the shipped CSV. Passed to pick_methods.py via --extra, so custom
# methods are first-class in every menu, reshuffle, and listing.
#
# Two keys, two jobs — keep them aligned:
# `code` is only the TOML merge key across override layers: a personal entry
# with the same code replaces the team one; new codes append.
# `method_name` is the catalog identity: an entry whose method_name matches a
# shipped method replaces it (retune its description or pattern; it keeps
# the shipped num), others append with new nums.
# To override another layer's entry, reuse its `code`. Two entries with
# different codes but the same method_name both survive the TOML merge, and
# only the later one reaches the catalog.
#
# Example (set in team/user override TOML):
# [[workflow.additional_methods]]
# code = "regulatory-inversion"
# category = "domain-specific"
# method_name = "Regulatory Inversion"
# description = "Start from the compliance constraint and ask what becomes possible only because of it - turns the rule into a generative frame"
# output_pattern = "constraint → possibilities → design"
additional_methods = []
module-manifest.toml
module = "toolbox"
version = "6.13.0-next"
update_source = "github:bmad-code-org/BMAD-METHOD/skills"
knowledge = "`references/help.md` in the `bmad` skill"
scripts/pick_methods.py
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.11"
# ///
"""Serve the elicitation method catalog without loading it all into context.
The catalog is a CSV (num, category, method_name, description, output_pattern).
`description` is a one-line gist — enough to run the method; `output_pattern` is
a flexible flow guide (e.g. "assumptions → truths → new approach").
Commands:
categories list category names + counts (the cheap entry point)
list --category C [...] the index (num/category/name/gist) for those categories
list --all the whole catalog at once — deliberate; large, avoid interactively
show NAME_OR_NUM [...] full row for each method, matched by name or num
random [-n N] [--category C ...] [--exclude NAME ...] [--spread]
draw N at random; --spread forces category diversity
(at most one per category until categories run out) —
the reshuffle draw; --exclude skips already-shown methods
`list` refuses to run with neither --category nor --all: dumping the full catalog
into context must always be an explicit, deliberate choice.
`--extra SPEC` merges additional methods (customize.toml's `additional_methods`)
into every command. SPEC is either a JSON array literal (starts with `[`) or a
path to a JSON file; each item is {code, category, method_name, description,
output_pattern}. An extra whose method_name matches a catalog row
(case-insensitive) REPLACES it and keeps that row's num — retune a shipped
method; others append and get the next free nums, so new methods and whole new
categories are first-class and number-addressable everywhere.
Default output is lean tab-separated text for an LLM to read; --json for structured.
"""
import argparse
import csv
import json
import random
import sys
from pathlib import Path
DEFAULT_FILE = Path(__file__).resolve().parent.parent / "assets" / "methods.csv"
FIELDS = ("num", "category", "method_name", "description", "output_pattern")
def load(file: Path) -> list[dict]:
# utf-8-sig: tolerate BOM-prefixed catalogs (Excel "CSV UTF-8", Notepad)
with open(file, newline="", encoding="utf-8-sig") as f:
rows = list(csv.DictReader(f))
for r in rows:
for k in FIELDS:
r.setdefault(k, "")
r[k] = (r.get(k) or "").strip()
return rows
def load_extra(spec: str) -> list[dict]:
"""Parse the --extra overlay: a JSON array literal or a path to a JSON file."""
text = spec if spec.lstrip().startswith("[") else Path(spec).read_text(encoding="utf-8-sig")
data = json.loads(text)
if not isinstance(data, list):
raise ValueError("--extra must be a JSON array of objects")
rows = []
for item in data:
if not isinstance(item, dict):
raise ValueError(f"each --extra entry must be a JSON object, got: {item!r}")
row = {k: str(item.get(k) or "").strip() for k in FIELDS}
row["code"] = str(item.get("code") or "").strip() # kept for traceability
rows.append(row)
return rows
def merge_extra(rows: list[dict], extras: list[dict]) -> list[dict]:
"""Extras replace a catalog row with the same method_name (case-insensitive),
otherwise append — so overrides can retune shipped methods or grow the catalog.
A replacement inherits the shipped row's num; appended extras get the next
free nums, so every merged method stays addressable by number."""
merged = list(rows)
index = {r["method_name"].lower(): i for i, r in enumerate(merged)}
for e in extras:
key = e["method_name"].lower()
if key in index:
e = dict(e)
e["num"] = e["num"] or merged[index[key]]["num"]
merged[index[key]] = e
else:
index[key] = len(merged)
merged.append(dict(e))
next_num = max((int(r["num"]) for r in merged if r["num"].isdigit()), default=0) + 1
for r in merged:
if not r["num"]:
r["num"] = str(next_num)
next_num += 1
return merged
def categories(rows: list[dict]) -> list[tuple[str, int]]:
counts: dict[str, int] = {}
for r in rows:
counts[r["category"]] = counts.get(r["category"], 0) + 1
return sorted(counts.items())
def filter_cats(rows: list[dict], cats: list[str] | None) -> list[dict]:
if not cats:
return rows
wanted = {c.lower() for c in cats}
return [r for r in rows if r["category"].lower() in wanted]
def find(rows: list[dict], names: list[str]) -> tuple[list[dict], list[str]]:
"""Match each query by method_name or by num, case-insensitively."""
by_key: dict[str, dict] = {}
for r in rows:
by_key[r["method_name"].lower()] = r
if r["num"]:
by_key.setdefault(r["num"], r)
found, missing = [], []
for n in names:
r = by_key.get(n.strip().lower())
(found if r else missing).append(r if r else n)
return found, missing
def exclude(rows: list[dict], names: list[str] | None) -> list[dict]:
if not names:
return rows
skip = {n.strip().lower() for n in names}
return [r for r in rows if r["method_name"].lower() not in skip]
def spread_sample(rows: list[dict], n: int, rng: random.Random | None = None) -> list[dict]:
"""Draw n methods with maximum category diversity: shuffle the categories,
take one random method per category round-robin, wrapping only when there
are fewer categories than picks."""
rng = rng or random
by_cat: dict[str, list[dict]] = {}
for r in rows:
by_cat.setdefault(r["category"], []).append(r)
buckets = list(by_cat.values())
rng.shuffle(buckets)
for b in buckets:
rng.shuffle(b)
out: list[dict] = []
while buckets and len(out) < n:
exhausted = []
for b in buckets:
if len(out) >= n:
break
out.append(b.pop())
if not b:
exhausted.append(b)
buckets = [b for b in buckets if b not in exhausted]
return out
def fmt_categories(cats: list[tuple[str, int]], as_json: bool) -> str:
if as_json:
return json.dumps([{"category": c, "count": n} for c, n in cats])
return "\n".join(f"{c}\t{n}" for c, n in cats)
def fmt_rows(rows: list[dict], as_json: bool) -> str:
if as_json:
return json.dumps([{k: r[k] for k in FIELDS} for r in rows])
return "\n".join(
f"{r['num']}\t{r['category']}\t{r['method_name']}\t{r['description']}\t{r['output_pattern']}" for r in rows
)
def main(argv: list[str] | None = None) -> int:
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8") # catalog rows contain →; don't die on locale code pages
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument("--file", type=Path, default=DEFAULT_FILE, help="method CSV (default: sibling assets/methods.csv)")
p.add_argument("--extra", help="additional methods: a JSON array literal or a path to a JSON file")
p.add_argument("--json", action="store_true", help="emit structured JSON instead of lean text")
sub = p.add_subparsers(dest="cmd", required=True)
sub.add_parser("categories", help="list category names + counts")
pl = sub.add_parser("list", help="the index for chosen categories (needs --category or --all)")
pl.add_argument("--category", action="append", help="filter to a category (repeatable)")
pl.add_argument("--all", action="store_true", help="dump the entire catalog (deliberate; large)")
ps = sub.add_parser("show", help="full row for each named method")
ps.add_argument("names", nargs="+", help="method names or nums")
pr = sub.add_parser("random", help="draw methods at random")
pr.add_argument("-n", type=int, default=1, help="how many (default 1)")
pr.add_argument("--category", action="append", help="restrict to a category (repeatable)")
pr.add_argument("--exclude", action="append", help="method name to skip (repeatable) — e.g. already shown")
pr.add_argument("--spread", action="store_true", help="force category diversity across the draw")
args = p.parse_args(argv)
if not args.file.is_file():
print(f"error: method file not found: {args.file}", file=sys.stderr)
return 2
rows = load(args.file)
if args.extra:
try:
rows = merge_extra(rows, load_extra(args.extra))
except (OSError, ValueError) as e:
print(f"error: could not read --extra: {e}", file=sys.stderr)
return 2
if args.cmd == "categories":
print(fmt_categories(categories(rows), args.json))
elif args.cmd == "list":
if not args.category and not args.all:
print(
"error: `list` needs --category (one or more) — or --all to dump the whole "
"catalog on purpose. Use `categories` for the cheap map, or `random` to draw blind.",
file=sys.stderr,
)
return 2
print(fmt_rows(filter_cats(rows, args.category), args.json))
elif args.cmd == "show":
found, missing = find(rows, args.names)
for m in missing:
print(f"# not found: {m}", file=sys.stderr)
if not found:
return 1
print(fmt_rows(found, args.json))
elif args.cmd == "random":
pool = exclude(filter_cats(rows, args.category), args.exclude)
if not pool:
print("# no methods match", file=sys.stderr)
return 1
n = max(0, min(args.n, len(pool))) # clamp: never crash on a negative or oversized -n
picks = spread_sample(pool, n) if args.spread else random.sample(pool, n)
print(fmt_rows(picks, args.json))
return 0
if __name__ == "__main__":
sys.exit(main())
scripts/tests/test_pick_methods.py
# /// script
# requires-python = ">=3.11"
# dependencies = ["pytest>=8.0"]
# ///
"""Tests for pick_methods.py.
Run: uv run scripts/tests/test_pick_methods.py
or: uv run --with pytest -m pytest scripts/tests/test_pick_methods.py
"""
import json
import random
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import pick_methods # noqa: E402
CSV = """num,category,method_name,description,output_pattern
1,risk,Pre-mortem Analysis,Imagine future failure then work backwards,failure → causes → prevention
2,risk,Assumption Audit,List and stress-test every assumption,list → rate → stress-test
3,core,First Principles Analysis,Rebuild from fundamental truths,assumptions → truths → new approach
4,core,Socratic Questioning,Targeted questions reveal hidden assumptions,questions → revelations → understanding
5,creative,SCAMPER Method,Seven creativity lenses,S→C→A→M→P→E→R
"""
EXTRA = [
{
"code": "reg-inversion",
"category": "domain",
"method_name": "Regulatory Inversion",
"description": "Start from the compliance constraint",
"output_pattern": "constraint → possibility",
},
{
"code": "premortem-lite",
"category": "risk",
"method_name": "Pre-mortem Analysis",
"description": "RETUNED pre-mortem",
"output_pattern": "failure → prevention",
},
]
@pytest.fixture
def lib(tmp_path):
csv_path = tmp_path / "methods.csv"
csv_path.write_text(CSV, encoding="utf-8")
return csv_path
def rows(lib):
return pick_methods.load(lib)
# --- load / merge -----------------------------------------------------------
def test_load_all_fields_present(lib):
r = rows(lib)
assert len(r) == 5
assert r[0]["method_name"] == "Pre-mortem Analysis"
assert all(set(pick_methods.FIELDS) <= set(row) for row in r)
def test_load_extra_json_literal_and_file(tmp_path, lib):
literal = pick_methods.load_extra(json.dumps(EXTRA))
f = tmp_path / "extra.json"
f.write_text(json.dumps(EXTRA), encoding="utf-8")
from_file = pick_methods.load_extra(str(f))
assert literal == from_file
assert literal[0]["method_name"] == "Regulatory Inversion"
assert literal[0]["num"] == "" # missing fields normalize to empty
assert literal[0]["code"] == "reg-inversion" # code survives loading
def test_merge_extra_replaces_by_name_and_appends(lib):
merged = pick_methods.merge_extra(rows(lib), pick_methods.load_extra(json.dumps(EXTRA)))
assert len(merged) == 6 # 5 shipped, 1 replaced in place, 1 appended
premortem = next(r for r in merged if r["method_name"] == "Pre-mortem Analysis")
assert premortem["description"] == "RETUNED pre-mortem"
assert premortem["num"] == "1" # replacement inherits the shipped num
appended = next(r for r in merged if r["method_name"] == "Regulatory Inversion")
assert appended["num"] == "6" # appended extras get the next free num
assert dict(pick_methods.categories(merged))["domain"] == 1 # new category is first-class
def test_extras_are_addressable_by_num(lib):
merged = pick_methods.merge_extra(rows(lib), pick_methods.load_extra(json.dumps(EXTRA)))
found, missing = pick_methods.find(merged, ["6", "1"])
assert [r["method_name"] for r in found] == ["Regulatory Inversion", "Pre-mortem Analysis"]
assert missing == []
# --- categories / filter / find / exclude -----------------------------------
def test_categories_counts_sorted(lib):
assert pick_methods.categories(rows(lib)) == [("core", 2), ("creative", 1), ("risk", 2)]
def test_filter_is_case_insensitive(lib):
got = pick_methods.filter_cats(rows(lib), ["RISK"])
assert {r["method_name"] for r in got} == {"Pre-mortem Analysis", "Assumption Audit"}
def test_filter_none_returns_all(lib):
assert len(pick_methods.filter_cats(rows(lib), None)) == 5
def test_find_by_name_num_and_missing(lib):
found, missing = pick_methods.find(rows(lib), ["scamper method", "3", "Nope"])
assert [r["method_name"] for r in found] == ["SCAMPER Method", "First Principles Analysis"]
assert missing == ["Nope"]
def test_exclude_skips_named(lib):
got = pick_methods.exclude(rows(lib), ["pre-mortem analysis", "SCAMPER Method"])
assert {r["method_name"] for r in got} == {
"Assumption Audit",
"First Principles Analysis",
"Socratic Questioning",
}
# --- spread sampling ---------------------------------------------------------
def test_spread_hits_distinct_categories(lib):
for seed in range(20):
picks = pick_methods.spread_sample(rows(lib), 3, random.Random(seed))
assert len({r["category"] for r in picks}) == 3
def test_spread_wraps_when_categories_run_out(lib):
picks = pick_methods.spread_sample(rows(lib), 5, random.Random(0))
assert len(picks) == 5
assert len({r["method_name"] for r in picks}) == 5 # no duplicates
def test_spread_clamps_to_pool(lib):
assert len(pick_methods.spread_sample(rows(lib), 99, random.Random(0))) == 5
# --- CLI ---------------------------------------------------------------------
def run(args, lib, capsys):
code = pick_methods.main(["--file", str(lib), *args])
captured = capsys.readouterr()
return code, captured.out, captured.err
def test_cli_categories(lib, capsys):
code, out, _ = run(["categories"], lib, capsys)
assert code == 0
assert "risk\t2" in out
def test_cli_list_requires_scope(lib, capsys):
code, _, err = run(["list"], lib, capsys)
assert code == 2
assert "--category" in err
def test_cli_list_category_and_all(lib, capsys):
code, out, _ = run(["list", "--category", "core"], lib, capsys)
assert code == 0 and len(out.strip().splitlines()) == 2
assert "Socratic Questioning" in out and "SCAMPER" not in out
code, out, _ = run(["list", "--all"], lib, capsys)
assert code == 0 and "SCAMPER" in out
def test_cli_show_found_and_missing(lib, capsys):
code, out, err = run(["show", "Assumption Audit", "Ghost"], lib, capsys)
assert code == 0
assert "stress-test" in out
assert "not found: Ghost" in err
code, _, _ = run(["show", "Ghost"], lib, capsys)
assert code == 1
def test_cli_random_spread_exclude(lib, capsys):
code, out, _ = run(["random", "-n", "3", "--spread", "--exclude", "SCAMPER Method"], lib, capsys)
assert code == 0
lines = [ln for ln in out.strip().splitlines() if ln]
assert len(lines) == 3
assert "SCAMPER" not in out
def test_cli_random_clamps_and_empty_pool(lib, capsys):
code, out, _ = run(["random", "-n", "99"], lib, capsys)
assert code == 0 and len(out.strip().splitlines()) == 5
code, _, err = run(["random", "--category", "nope"], lib, capsys)
assert code == 1 and "no methods match" in err
def test_cli_extra_inline_json(lib, capsys):
code, out, _ = run(["--extra", json.dumps(EXTRA), "list", "--category", "domain"], lib, capsys)
assert code == 0 and "Regulatory Inversion" in out
def test_cli_bad_extra_and_missing_file(tmp_path, lib, capsys):
code, _, err = run(["--extra", str(tmp_path / "gone.json"), "categories"], lib, capsys)
assert code == 2 and "--extra" in err
code = pick_methods.main(["--file", str(tmp_path / "gone.csv"), "categories"])
assert code == 2
def test_cli_json_output(lib, capsys):
code, out, _ = run(["--json", "show", "1"], lib, capsys)
assert code == 0
data = json.loads(out)
assert data[0]["method_name"] == "Pre-mortem Analysis"
# --- shipped catalog integration ----------------------------------------------
def test_shipped_catalog_loads_clean():
shipped = pick_methods.DEFAULT_FILE
assert shipped.is_file(), f"shipped catalog missing: {shipped}"
r = pick_methods.load(shipped)
assert len(r) >= 60
for row in r:
assert row["category"] and row["method_name"] and row["description"], row
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-q"]))
SKILL.md
---
name: bmad-advanced-elicitation
description: 'Push the LLM to reconsider, refine, and improve its recent output. Use when user asks for deeper critique or mentions a known deeper critique method, e.g. socratic, first principles, pre-mortem, red team'
---
# Advanced Elicitation
You are BMad's shared refinement checkpoint: other skills invoke you at natural pauses to pressure the piece of work they just produced, and users call you directly on anything recent. The target is the most recent output in the conversation — a section, plan, draft, or decision — unless the caller or user points at something else. You offer a short menu of elicitation methods, run the chosen ones against the target, and hand back the improved version so the invoking flow resumes exactly where it paused. Work in the surrounding session's communication language.
## Conventions
- Bare paths (e.g. `assets/methods.csv`) resolve from `{skill-root}` (where `customize.toml` lives); `{project-root}`-prefixed paths from the project working directory.
- `{workflow.<name>}` resolves to fields in the merged `customize.toml` `[workflow]` table.
## On Activation
1. Resolve customization: `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --project-root {project-root} --key workflow`. On failure, read `{skill-root}/customize.toml` directly and use defaults.
2. Hold every `{workflow.preferences}` entry for the whole session, fix the target, and serve the first menu.
## Serving the Catalog
`scripts/pick_methods.py` serves the method catalog (num, category, method_name, description, output_pattern) so it never enters context whole — the one exception is listing the full catalog, when the user asked for all of it. Invoke as:
```bash
uv run {skill-root}/scripts/pick_methods.py --file {workflow.methods_file} <command>
```
If `{workflow.additional_methods}` is non-empty, add `--extra '<its entries as a JSON array>'` (or a path to a JSON file holding them) on every call, so custom methods are first-class in menus, reshuffles, and listings.
- `categories` — category names + counts, the cheap map.
- `list --category <cat> [--category <cat>]` — the index for chosen categories; `--all` dumps the whole catalog, only when listing all.
- `show <name-or-num> [...]` — full rows by name or num.
- `random -n 5 --spread [--exclude <name>]...` — a category-diverse random draw.
**First menu:** run `categories`, pick the 2–4 categories that fit the target (risk before a launch, technical for code, collaboration when stakeholders compete, creative when the content is flat), `list` them, and hand-pick five methods that attack the target from different angles — honoring `{workflow.preferences}`. **Reshuffle:** `random -n 5 --spread`, excluding everything already offered.
## The Menu
HALT and give the user a choice:
- The five offered methods, listed by name. The user may pick one or several.
- **Reshuffle** — replace the list with five new options.
- **List all** — show the full catalog with descriptions.
- **Proceed** — no further elicitation.
This menu is the interface other skills and their users rely on — keep its options and behavior stable. When party mode is active in the session, add `_Party mode is active — agents will join in._` under the heading.
- If the user picks methods: run them (several: in sequence), then offer the menu again.
- If the user chooses **Reshuffle**: reshuffle as above and offer the menu again.
- If the user chooses **List all**: show the full catalog (`list --all`) as a compact table; a pick by name or number runs like a method choice.
- If the user chooses **Proceed**: done. The current enhanced version is final for this content: hand it back to the invoking skill as the replacement for what it had, and signal completion so it continues. If anything shown was never accepted, confirm what should carry over before returning.
- Any other reply is direction: apply it to the target and offer the menu again.
## Running a Method
Use the method's description as its intent and its output_pattern as a flexible flow guide; scale depth to the target — a paragraph gets a light pass, an architecture decision gets the full treatment. Each application works on the current enhanced version, so refinements compound. Show what the method revealed and the changes it proposes, then HALT and give the user a choice:
- **Apply** — accept the proposed changes.
- **Reject** — drop the proposal entirely.
- Or give different direction.
Never change the work unless the user accepts the proposal. If they reject it, drop the proposal entirely. Any other reply is instruction to follow.
When a method casts personas (round tables, panels, debates), reuse party members already in the session if party mode is active; otherwise resolve installed agents on demand via `uv run {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root} --key agents` (a three-layer merge of `_bmad/config.toml` and the two `_bmad/custom/` overrides; each entry keyed by agent code carries name, title, icon, description). If neither yields a fit, invent named viewpoints suited to the content.