Claude Cursor Skill

bmad-advanced-elicitation

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.

LLM Mart · 0 points · 11 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download skyf0xx-hedgehog-vendor-skills_BMAD_core-skills_bmad-advanced-elicitation-fae6653.zip · 15 KB
Part of skyf0xx/hedgehog — 21 skills

Install

skills CLI npx skills add https://github.com/skyf0xx/hedgehog/tree/master/vendor-skills/BMAD/core-skills/bmad-advanced-elicitation
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install skyf0xx-hedgehog@llmmart
Git git clone https://github.com/skyf0xx/hedgehog.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole skyf0xx/hedgehog collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

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.
  • {bmad-root} is the vendored vendor-skills/BMAD/ root.

On Activation

  1. Resolve customization: uv run {bmad-root}/scripts/resolve_customization.py --skill {skill-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 [a], where the user asked for all of it. Invoke as:

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 for [a].
  • 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

**Advanced Elicitation Options**
Choose a number (1-5), [r] to Reshuffle, [a] List All, or [x] to Proceed:

1. [Method Name]
2. [Method Name]
3. [Method Name]
4. [Method Name]
5. [Method Name]
r. Reshuffle the list with 5 new options
a. List all methods with descriptions
x. Proceed / No Further Actions

This menu is the interface other skills and their users rely on — keep its options and behavior stable. Handle the response:

  • 1–5 — run that method (several numbers: in sequence), then re-present the menu.
  • r — reshuffle as above and re-present.
  • a — show the full catalog (list --all) as a compact table; a pick by name or number runs like a numbered choice.
  • x — 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.
  • Anything else — treat as direction: apply it to the target and re-present the menu.

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 ask whether to apply them (y/n/other) and wait — never change the work without a yes; on no, drop the proposal entirely; any other reply is instruction to follow.

When a method casts personas (round tables, panels, debates), invent named viewpoints suited to the content.

Files (hedgehog)
  • assets
    • methods.csv 14.5 KB · in bundle
  • scripts
    • tests
      • test_pick_methods.py 7.9 KB
        # /// script
        # requires-python = ">=3.10"
        # 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"]))
        
    • pick_methods.py 9.7 KB
      #!/usr/bin/env python3
      # /// script
      # requires-python = ">=3.10"
      # ///
      """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())
      
  • customize.toml 2.6 KB
    # 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 = []
    
  • SKILL.md 4.4 KB
    ---
    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.
    - `{bmad-root}` is the vendored `vendor-skills/BMAD/` root.
    
    ## On Activation
    
    1. Resolve customization: `uv run {bmad-root}/scripts/resolve_customization.py --skill {skill-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 [a], where 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 for [a].
    - `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
    
    ```
    **Advanced Elicitation Options**
    Choose a number (1-5), [r] to Reshuffle, [a] List All, or [x] to Proceed:
    
    1. [Method Name]
    2. [Method Name]
    3. [Method Name]
    4. [Method Name]
    5. [Method Name]
    r. Reshuffle the list with 5 new options
    a. List all methods with descriptions
    x. Proceed / No Further Actions
    ```
    
    This menu is the interface other skills and their users rely on — keep its options and behavior stable. Handle the response:
    
    - **1–5** — run that method (several numbers: in sequence), then re-present the menu.
    - **r** — reshuffle as above and re-present.
    - **a** — show the full catalog (`list --all`) as a compact table; a pick by name or number runs like a numbered choice.
    - **x** — 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.
    - **Anything else** — treat as direction: apply it to the target and re-present the menu.
    
    ## 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 ask whether to apply them (y/n/other) and wait — never change the work without a yes; on no, drop the proposal entirely; any other reply is instruction to follow.
    
    When a method casts personas (round tables, panels, debates), invent named viewpoints suited to the content.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related