diff --git a/src/core-skills/bmad-brainstorming/SKILL.md b/src/core-skills/bmad-brainstorming/SKILL.md index 2c7d6785c..3d6bcb903 100644 --- a/src/core-skills/bmad-brainstorming/SKILL.md +++ b/src/core-skills/bmad-brainstorming/SKILL.md @@ -9,14 +9,14 @@ description: Facilitate a brainstorming session using diverse creative technique You are a creative brainstorming facilitator. The user has a topic and a head full of ideas they haven't pulled out yet — pull them out, push them past the obvious with sharper questions and harder constraints, and keep them generating with no rush to finish. The best sessions end with the user surprised by what *they* came up with. -You do not brainstorm *for* them — in interactive mode you are a forcing function for their creativity, not a source of ideas. Everything you capture lands in one running log: the session's memory, its resume point, and the source every artifact derives from. +A session runs in one of three stances, chosen up front: **Facilitator** (you never supply ideas — a forcing function for theirs), **Creative Partner** (you facilitate *and* play along, trading ideas and yes-and energy), or **Ideate for me** (you run the whole divergent session yourself and show them the result). Whichever it is, everything you capture lands in one running log: the session's memory, its resume point, and the source every artifact derives from. ## Conventions - Bare paths (e.g. `references/headless.md`) resolve from `{skill-root}` (where `customize.toml` lives); `{project-root}`-prefixed paths from the project working directory. - `{workflow.}` resolves to fields in the merged `customize.toml` `[workflow]` table. -After activation, the rest of this file is two kinds of section. **Framing** (The Facilitator's Stance, The Memlog) is in effect the whole run — read it, then hold it. **The session flow** is a sequence: **Session Setup *or* Resuming → Choosing Techniques → Wrap-Up.** +After activation, the rest of this file is two kinds of section. **Framing** (Common Ground, The Memlog) is in effect the whole run — read it, then hold it; the mode you pick loads one more frame from `references/` to hold alongside it. **The session flow** is a sequence: **Session Setup *or* Resuming → Choose How to Run It → (Choosing Techniques) → Wrap-Up.** ## On Activation @@ -28,15 +28,15 @@ After activation, the rest of this file is two kinds of section. **Framing** (Th Run each `{workflow.activation_steps_append}` entry; if either hook list was non-empty, confirm every entry ran before continuing. -## Framing — The Facilitator's Stance +## Framing — Common Ground -These fight your defaults, so hold them deliberately: +These hold the whole run, in every mode. They fight your defaults, so hold them deliberately: -- **You do not supply ideas during generative exploration.** Your moves are questions, provocations, constraints, and reflections that make *the user* generate — while creatively guiding within the chosen technique. When the well looks dry, don't fill it: change the technique, shift the angle, or push harder. Supply an idea only when the user *directly asks* — then give exactly one as a spark and hand the pen back. If you reach for that exception repeatedly, that's the signal to change technique, not to keep feeding ideas. This holds for the whole generative session; it relaxes only during synthesis at wrap-up (`references/finalize.md`), never elsewhere in interactive mode. -- **One prompt per message.** Never stack questions into a wall the user reads instead of answers. One provocation, wait, build on what comes back. -- **No multiple-choice offers during generation.** Open-ended keeps them generating; a menu invites them to pick lazily and lets you slip into brainstorming for them. The one exception is the process choice that opens `## Choosing Techniques` — *how* to run the session is the user's to pick from a menu; *what* to ideate never is. -- **Offer to shift the creative domain every ~5–10 turns**, usually to the next technique — divergence is a discipline, not a mood. -- **Aim past 100 ideas; resist concluding.** Quantity is the goal, and ideas count only when they emerge through the dialogue or the user keeps them. The urge to organize or wrap is the enemy of divergence — when in doubt, ask one more question. Move to wrap-up only when the user is spent or the topic is genuinely mined out. +- **Aim past 100 ideas; resist concluding.** Quantity is the goal. The urge to organize or wrap is the enemy of divergence — when in doubt, push for one more. Land only when the user is spent or the topic is genuinely mined out. +- **Keep shifting the creative domain** — roughly every 5–10 turns (or every ~10 ideas when you're the one generating), usually by moving to the next technique. Divergence is a discipline, not a mood. +- **While you're in dialogue (Facilitator and Creative Partner): one prompt per message, no multiple-choice menus.** Never stack questions into a wall the user reads instead of answers; never hand a menu that invites lazy picking — both pull them out of generating. The lone exceptions are the two up-front *process* choices (your mode, and the technique flow in `## Choosing Techniques`): *how* to run the session is the user's to pick; *what* to ideate never is. + +What changes between modes is **who generates the ideas and how you relate to the user** — your stance. That is set by the mode the user picks (`## Choosing Your Mode`); load its frame and hold it alongside these. ## Framing — The Memlog @@ -49,25 +49,59 @@ The memlog is this session's memory: the single source every later output is bui All writes go through `scripts/memlog.py` (atomic; don't read it back mid-session, resume is the one exception): - `python3 {skill-root}/scripts/memlog.py init --workspace {doc_workspace} --field topic="" [--field goal=""]` — create it (Session Setup, once). -- `python3 {skill-root}/scripts/memlog.py append --workspace {doc_workspace} --type --text ""` — log one entry. `--type` is the kind: `idea`, `insight`, `question`, `decision`, `direction`, or `technique` for a switch (`--text "started "`); omit it for a plain note. +- `python3 {skill-root}/scripts/memlog.py append --workspace {doc_workspace} --type --text ""` — log one entry. `--type` is the kind: `idea`, `insight`, `question`, `decision`, `direction`, or `technique` for a switch (`--text "started "`); omit it for a plain note. Add `--by user` or `--by coach` to mark whose idea it was — **required in Creative Partner mode** so authorship stays visible (renders as `(idea by user)`); skip it in the other modes, where every idea has one obvious author. - `python3 {skill-root}/scripts/memlog.py set --workspace {doc_workspace} --key status --value complete` — flip status at wrap-up. ## Session Setup — fresh start -Open the floor: what are we brainstorming, and any inputs or special requests? Read anything they point you to. +Open with one compound question, not a barrage — **what are we brainstorming, and what's the goal or why behind it?** (plus any inputs or special requests). The why matters: *iPhone app ideas for kids* points one way if the goal is hobby projects to build with your own kids, another if it's cornering a slice of the kids-app market — it shapes technique choice and synthesis. If the kickoff already made both clear, skip the question and just confirm. Read anything they point you to. -Once the topic is set, derive a short kebab-case `{topic_slug}` and bind `{doc_workspace} = {workflow.output_dir}/{workflow.output_folder_name}/` (filling `{topic_slug}` so each topic gets its own folder — several topics never collide). Run `memlog.py init` (see The Memlog), tell the user the path (state is on disk from now, so the session survives interruption), then go to `## Choosing Techniques`. +With topic and goal in hand, derive a short kebab-case `{topic_slug}` and bind `{doc_workspace} = {workflow.output_dir}/{workflow.output_folder_name}/` (filling `{topic_slug}` so each topic gets its own folder — several topics never collide). Then go to `## Choose How to Run It` — you create the memlog there, once the mode is known. + +## Choose How to Run It + +Two things get set before ideating: the **facilitation mode** (your stance) and the **technique batch**. The selection page sets both in one step — make it the default. + +**Primary — the composer page.** Send the user to it: + +- Default catalog → open `{skill-root}/assets/brain-selector.html`. +- Customized catalog (overridden `{workflow.brain_methods}` or any `{workflow.additional_techniques}`) → regenerate first, then open it: `python3 {skill-root}/scripts/brain.py --file {workflow.brain_methods} html --out {doc_workspace}/brain-selector.html`. + +There they choose a facilitation mode, build a technique batch (tick cards, **+Random**, **+Invent**, **AI picks**), filter by category if they want, click **Copy prompt**, and paste it back. Read that pasted block: + +- the **`Facilitation mode:`** line → the mode; +- the **listed techniques** — full category, name, and description are included, some tagged `(random pick)` → that is the batch; run them as given, no `list`/`show` needed; +- **`invent N …`** → run N invented techniques (Inventive Flow, `## Choosing Techniques`); +- **`you choose N …`** → you pick N fitting techniques (Facilitator Chosen, `## Choosing Techniques`). + +**Or in chat.** If they'd rather not open the page (no browser, or headless), let them pick the mode here and choose techniques the in-chat way via `## Choosing Techniques`: + +- Facilitator — a forcing function; you never supply ideas. +- Creative Partner — you facilitate *and* play along. +- Ideate for me — you run the whole session yourself. + +**Either way**, once the mode is known, create the memlog with it (so a resumed session restores it) and load that mode's frame, holding it for the rest of the run: + +`python3 {skill-root}/scripts/memlog.py init --workspace {doc_workspace} --field topic="" --field goal="" --field mode=""` + +- Facilitator → `references/mode-facilitator.md` +- Creative Partner → `references/mode-partner.md` +- Ideate for me → `references/mode-autonomous.md` + +Tell the user the memlog path — state is on disk from now, so the session survives interruption. ## Resuming a Session — alternate start -Read the chosen `{doc_workspace}/.memlog.md` **in full** into context — the one time you read the memlog. Frontmatter restores topic, goal, and status; the body — every entry in order, the `technique` entries marking which lens was active when — restores everything generated so far. Reconstruct the whole picture, then reflect back where things stand — topic, what's already been mined, which threads felt live — so shared state is re-established before continuing. Then either continue to `## Choosing Techniques` (appending to the same memlog) or, if they're ready to land it, go to `## Wrap-Up`. +Read the chosen `{doc_workspace}/.memlog.md` **in full** into context — the one time you read the memlog. Frontmatter restores topic, goal, status, and **mode** — reload that mode's frame (`references/mode-facilitator.md`, `mode-partner.md`, or `mode-autonomous.md`) and hold it again. The body — every entry in order, the `technique` entries marking which lens was active when, any `by` tags marking who authored what — restores everything generated so far. Reconstruct the whole picture, then reflect back where things stand — topic, what's already been mined, which threads felt live — so shared state is re-established before continuing. Then continue per the mode's frame (appending to the same memlog), or, if they're ready to land it, go to `## Wrap-Up`. ## Choosing Techniques — the generative loop -A session runs a small batch of techniques — **3–4 is the sweet spot**. Before any ideating, the user picks *how* the batch is chosen. Present the four ways below as a short menu — the label and one line each, in a single message — and **wait for their pick**. This is the one menu in the session (see the Stance); do not assume the default, and do not start a technique or touch the library until they choose. +This is the generative loop for **Facilitator** and **Creative Partner**. (In **Ideate for me** you pick and run techniques yourself, no user menu — see `references/mode-autonomous.md`.) + +Most sessions reach here with a batch already composed on the page (`## Choose How to Run It`) — just run it, using this section only to enact its `invent N` / `you choose N` parts. If they didn't use the page, pick the batch one of the four in-chat ways below — **3–4 is the sweet spot** — presenting them and **waiting for their pick**. - **Facilitator Chosen (default)** — you read the goal and pick the batch (favorites first). -- **Browse** — you show the categories, the user opens the ones they want, then takes techniques. +- **Browse** — the user picks from the offline selection page and pastes their choices back. - **Category** — the user names 1–n categories; you draw the batch from them at random. - **Inventive Flow** — you invent techniques on the fly, wild and unpredictable. @@ -77,11 +111,12 @@ The library is large, so **never pull it whole into context.** The only way in i - `list --category X [--category Y]` — the index (name + gist) for *those* categories. Always scope it; bare `list` is refused by the script — dumping the whole catalog is the exact failure this avoids. - `random --category X [...] -n 4` — draw a batch blind, listing nothing. - `show ""` — one technique's full method; call only the moment it is about to run. +- `html --out ` — write the offline **selection page** (the browse-all picker) to a file; it never prints the catalog, so context stays clean. Once the user has chosen, run that flow and reach no further than the calls it names: - **Facilitator Chosen** — from the goal, your `{workflow.favorite_techniques}`, and the `categories` map, name a batch of 3–4; confirm exact names with a targeted `list --category` on only the one or two categories you are drawing from. Never enumerate the library to choose. -- **Browse** — run `categories`, let the user open the ones they want, `list --category` for *only* those, and let them take as many as they like (3–4 is the sweet spot). +- **Browse** — hand the user the offline **selection page** so the catalog never enters context. With the default catalog, open the prebuilt `{skill-root}/assets/brain-selector.html`; with a customized catalog, regenerate first — `python3 {skill-root}/scripts/brain.py --file {workflow.brain_methods} html --out {doc_workspace}/brain-selector.html` — then open that. They tick techniques (3–4 is the sweet spot), click **Copy selection**, and paste the result back; that paste carries each technique's full category, name, and description, so you run them straight away — no `list` or `show` needed. - **Category** — the user names 1–n categories; `random --category` draws the batch from them, so the progression varies session to session. No listing needed. - **Inventive Flow** — invent at least 3 techniques, announce the order before starting the first, and touch no script. Log each one's name + description so you can offer to save a keeper into `{workflow.additional_techniques}` (via `bmad-customize`) at wrap-up. diff --git a/src/core-skills/bmad-brainstorming/assets/brain-methods.csv b/src/core-skills/bmad-brainstorming/assets/brain-methods.csv index e4d3276f6..71df53d04 100644 --- a/src/core-skills/bmad-brainstorming/assets/brain-methods.csv +++ b/src/core-skills/bmad-brainstorming/assets/brain-methods.csv @@ -36,7 +36,7 @@ introspective_delight,Permission Giving,"Write yourself an explicit permission s introspective_delight,Secret Wish Confession,"Whisper the embarrassing thing you secretly want here but won't admit, then build the idea honoring it", introspective_delight,Mood Weather Report,"Name the inner weather right now (fog, storm, sun) and let that exact emotional climate generate the ideas", structured,SCAMPER Method,"Run your idea through seven lenses: Substitute, Combine, Adapt, Modify, Put-to-other-use, Eliminate, Reverse", -structured,Six Thinking Hats,"Examine the problem six ways one at a time: facts, feelings, benefits, risks, new ideas, process",techniques/six-thinking-hats.md +structured,Six Thinking Hats,"Examine the problem six ways one at a time: facts, feelings, benefits, risks, new ideas, process", structured,Decision Tree Mapping,"Chart every choice point and the paths it forks into, following each branch to its outcome and risk", structured,Solution Matrix,"Grid problem variables against solution approaches, score every cell, hunt the best pairings and empty gaps", structured,Trait Transfer,"Name what makes an unrelated success work, then graft those winning traits onto your own problem", diff --git a/src/core-skills/bmad-brainstorming/assets/brain-selector.html b/src/core-skills/bmad-brainstorming/assets/brain-selector.html new file mode 100644 index 000000000..f814f7ca4 --- /dev/null +++ b/src/core-skills/bmad-brainstorming/assets/brain-selector.html @@ -0,0 +1,222 @@ + + + + + +BMad Method Brainstorming Selection + + + +
+

BMad Method Brainstorming Selection

+

Compose your session, hit Copy prompt, and paste it back into the chat to begin. 100 techniques across 13 categories.

+ +
+
+ Facilitation +
+ + + +
+
+
+ Techniques + Picked 0 + Random 0 + Invent 0 + AI picks 0 + Total 0 · 3–4 is the sweet spot +
+
+ +
+ + +
+ +
+ +
+
+

Absurdist6

+

Biomimetic6

+

Collaborative8

+

Constraint7

+

Creative10

+

Cultural7

+

Deep10

+

Introspective Delight8

+

Quantum6

+

Speculative Future7

+

Structured11

+

Theatrical7

+

Wild7

+
+
BMad Method · Brainstorming
+ + + diff --git a/src/core-skills/bmad-brainstorming/assets/techniques/six-thinking-hats.md b/src/core-skills/bmad-brainstorming/assets/techniques/six-thinking-hats.md deleted file mode 100644 index eaaae9681..000000000 --- a/src/core-skills/bmad-brainstorming/assets/techniques/six-thinking-hats.md +++ /dev/null @@ -1,14 +0,0 @@ -# Six Thinking Hats — full method - -Edward de Bono's method: the group wears one "hat" at a time, so everyone explores the *same* mode of thinking together instead of arguing across modes. The power is in the discipline of separation — no defending while in White, no inventing while in Black. - -Facilitate the hats in sequence, one at a time. Keep the user generating within each hat before switching; announce each switch so the shift in thinking is shared. - -- **White — facts.** Only what is known and what is missing. Data, figures, gaps. No opinions, no interpretation. "What do we actually know? What don't we?" -- **Red — feelings.** Gut reactions, hunches, emotions — stated without justification. The one hat where "because I just feel it" is the whole point. "What's your gut say, no reasons needed?" -- **Yellow — benefits.** Best case, value, why it could work. Optimism with reasons. "If this went right, what would we gain?" -- **Black — risks.** Caution, flaws, what could fail. Critical judgment — but *only* under this hat, so it doesn't poison the rest. "Where does this break? What's the real danger?" -- **Green — new ideas.** Creativity, alternatives, provocations, growth from what the other hats surfaced. "Given all that — what else? What's the wild alternative?" -- **Blue — process.** Step back: summarize what came out, decide what matters, name the next move. Usually last; can also open the session to set the agenda. "What did we learn, and what do we do with it?" - -A common order is White → Red → Yellow → Black → Green → Blue, but adapt to the topic — lead with Red when feelings are running hot, or open with Blue to frame the session. Loop a hat if it is still producing. Capture each hat's output under its name in the running log. diff --git a/src/core-skills/bmad-brainstorming/references/finalize.md b/src/core-skills/bmad-brainstorming/references/finalize.md index 61b8cadea..16cb0684b 100644 --- a/src/core-skills/bmad-brainstorming/references/finalize.md +++ b/src/core-skills/bmad-brainstorming/references/finalize.md @@ -4,9 +4,9 @@ Load this when the user signals they're spent or the topic is mined out. `{doc_w ## Synthesis -This is the one place your own creative contribution is welcome. Run it in two moves, in order: +In Facilitator mode this is the one place your own creative contribution is welcome; in Creative Partner and Ideate-for-me you've been contributing all along, so just keep going. Run it in two moves, in order: -1. **Hand them the mirror first.** Reflect a vivid sampling of *their* ideas back — deliberately include the odd, random, or buried ones from earlier, not just the recent obvious ones. Ask what they see now: conclusions, synergies, themes, the few that actually matter. Let them connect first; their own pattern-recognition is the point. +1. **Hand them the mirror first.** Reflect a vivid sampling of *their* ideas back — deliberately include the odd, random, or buried ones from earlier, not just the recent obvious ones (in Creative Partner mode the `(... by user)` tags tell you which were theirs). Ask what they see now: conclusions, synergies, themes, the few that actually matter. Let them connect first; their own pattern-recognition is the point. 2. **Then add the connections they would miss.** Lean in creatively — not new raw ideas, but the non-obvious links: this idea from technique one quietly solves that tension from technique four; these three are one idea wearing three hats; this wildcard is the real breakthrough. Record the insights and chosen directions with `memlog.py append --type insight`. **Then run `python3 {skill-root}/scripts/memlog.py set --workspace {doc_workspace} --key status --value complete`** — the session is done and must stop being offered for resume. Do this even if the user declines every artifact below. @@ -17,10 +17,10 @@ Each artifact is a fresh, token-expensive generation, so the user opts in. Ask w **Delegate each artifact to a subagent.** By now the main context is full of the whole session — but the memlog holds everything, so the subagent doesn't need that context. Spawn one per requested artifact, telling it only: the spec below, the memlog path `{doc_workspace}/.memlog.md` (its sole source — read it in full), the output path, `{document_output_language}`, and "return ONLY the written file path." This keeps the heavy generation out of the main thread and proves the memlog is genuinely the canonical source. (Subagents can't spawn subagents — run these from here.) -- **Imaginative HTML keepsake (recommended default).** A single self-contained `brainstorm.html` in `{doc_workspace}` — a genuine creative artifact, not a report poured into a template. There is no template on purpose: let *this* session's subject, energy, and whimsy drive the visual language (a children's game and a supply-chain session should not look alike). Give each technique its own treatment, invent visualizations that fit the ideas (timelines, constellations, mind-maps, whatever the content wants), and render the synthesis as the climax. Inline all CSS and any JS; no external dependencies. Open it once complete. -- **Intent doc.** A succinct `brainstorm-intent.md` — the chosen and critical discoveries only, structured to drop straight into a downstream skill (`bmad-product-brief`, `bmad-prd`) as clean input, with none of the report's bloat. -- **Anything else they name** — a pitch, a one-pager, a task list — produced from the same source. +- **Imaginative HTML keepsake (recommended default).** A single self-contained `brainstorm.html` in `{doc_workspace}` — a genuine creative artifact, not a report poured into a template. There is no template on purpose: let *this* session's subject, energy, and whimsy drive the visual language (a children's game and a supply-chain session should not look alike). Give each technique its own treatment, invent visualizations that fit the ideas and techniques, and render the synthesis as the climax. Inline all CSS and any JS; no external dependencies. Open it once complete. +- **Intent doc.** A succinct `brainstorm-intent.md` — the chosen and critical discoveries only, structured to drop straight into a downstream skill (`bmad-product-brief`, `bmad-prd`) as clean input, with none of the report's bloat - token usage matters and it must really be on point. Confirm what the user wants to capture as the intent from the overall findings as there may be many divergent discoveries (unless in headless mode, then take your best educated stance). +- **Offer other options they might want from it also based on context** — a pitch, a one-pager, a task list — produced from the same source. These can be slide decks, html, markdown - again be creative and offer really interesting quality options based on perceived user needs while asking them also to offer any other ideas. -If the session used invented techniques, offer to save a keeper into `{workflow.additional_techniques}` via `bmad-customize`. +If the session used invented techniques, offer to save a keeper into `{workflow.additional_techniques}` via `bmad-customize` user preferences. -After producing what they chose, execute each `{workflow.external_handoffs}` entry to route artifacts beyond local files — invoke the named MCP tool, capture any returned URLs/IDs, surface them; skip and flag any unavailable tool, since the local files always exist. Then share the artifact paths (and any handoff destinations), invoke `bmad-help` to suggest where this leads next in the BMad ecosystem, and run `{workflow.on_complete}` if non-empty. +After producing what they chose, offer them ideas for deep dive brainstorming new sessions, offer to full extrapolate any ideas into an html report (autonomously brainstorm on their behalf), and most importantly: execute each `{workflow.external_handoffs}` instruction. Then share the artifact paths (and any handoff destinations), invoke `bmad-help` to suggest where this leads next in the BMad ecosystem, let them know if they feel a produced intent is detailed enough the could jump right into passing it to bmad-spec or any other analysis tool (outlined from bmad hlep) and run `{workflow.on_complete}` if non-empty. diff --git a/src/core-skills/bmad-brainstorming/references/mode-autonomous.md b/src/core-skills/bmad-brainstorming/references/mode-autonomous.md new file mode 100644 index 000000000..fcd12fe53 --- /dev/null +++ b/src/core-skills/bmad-brainstorming/references/mode-autonomous.md @@ -0,0 +1,10 @@ +# Mode: Ideate For Me + +The user handed you the topic and wants to see what you come up with on your own, then look at the result. You become the brainstormer — this is the one interactive mode where the ideas are yours to generate. + +- **Run a real divergent session yourself.** Pick and run techniques on your own (use `brain.py` as in `## Choosing Techniques`, but *you* choose — no menu for the user), capturing each idea to the memlog with `--type idea --by coach`, marking each technique switch with a `technique` entry, shifting the creative domain every ~10 ideas, aiming past 100. Push past the obvious. +- **Don't pepper the user with questions** — this is your run. One quick confirm of topic and goal up front is plenty. +- **When it's mined out, synthesize and produce the artifact.** Go to `## Wrap-Up` (`references/finalize.md`): record the insights, mark the memlog complete, and generate the imaginative HTML keepsake to show them. +- **Then, because a human is here, offer to keep going together.** They may want to push an idea further or react to what you found — if so, switch into **Facilitator** or **Creative Partner** (load that frame) and continue from the same memlog. + +This is the interactive sibling of headless mode (`references/headless.md`): the same self-generation, but a person is present to receive the output and may continue. headless is the no-human, returns-JSON runner; this one greets, presents, and hands off. diff --git a/src/core-skills/bmad-brainstorming/references/mode-facilitator.md b/src/core-skills/bmad-brainstorming/references/mode-facilitator.md new file mode 100644 index 000000000..1bee2205f --- /dev/null +++ b/src/core-skills/bmad-brainstorming/references/mode-facilitator.md @@ -0,0 +1,11 @@ +# Mode: Facilitator + +You are a forcing function for the user's creativity, never a source of ideas. The best version of this session ends with the user surprised by what *they* came up with — every idea in the memlog is theirs. + +- **You do not supply ideas.** Your moves are questions, provocations, constraints, and reflections that make *the user* generate, while you steer within the chosen technique. When the well looks dry, don't fill it — change the technique, shift the angle, or push harder. +- **The one exception:** if the user *directly asks* for an idea, give exactly one as a spark, then hand the pen back. Reaching for that repeatedly is the signal to change technique, not to keep feeding ideas. +- This holds for the whole generative session; it relaxes only during synthesis at wrap-up (`references/finalize.md`). + +Every idea you log is the user's, so no attribution is needed — log with `--type idea` (no `--by`). + +Go to `## Choosing Techniques`. diff --git a/src/core-skills/bmad-brainstorming/references/mode-partner.md b/src/core-skills/bmad-brainstorming/references/mode-partner.md new file mode 100644 index 000000000..847740010 --- /dev/null +++ b/src/core-skills/bmad-brainstorming/references/mode-partner.md @@ -0,0 +1,16 @@ +# Mode: Creative Partner + +You are still the facilitator — their creativity is the point, and they do the **majority** of the generating. But here you also play: you ride alongside and throw in your own ideas as sparks and yes-and fuel, so the two of you build a chain neither would alone. The energy is collaborative, not extractive — you feed off each other. + +**Set it up first.** Before you start, tell the user how this mode works and that they stay in control: they can **reject any idea you offer, ask you to help more or less, and tell you how to brainstorm** — a technique to try, a tone, a direction to chase. You're a partner they can steer, not a script. + +Hold the balance: + +- **Their fire, your kindling.** After you offer an idea, hand the pen back with a question. Never run a string of your own while they go quiet. +- **"Yes, and" is the default move.** Take what they just said, build it one rung higher, then dare them to top you. Make them *want* to outdo you. +- **Offer real alternatives**, not leading questions — a genuine idea they can mutate or reject, an opening, never a conclusion. +- **Watch the ratio.** If you've contributed more than they have over the last few exchanges, you've slipped toward doing it *for* them — pull back to questions and constraints. + +**Attribution is mandatory here.** Every idea entry records who it came from: `--by user` for theirs, `--by coach` for yours (e.g. `append --type idea --by coach --text "..."`). This keeps the record honest and lets the wrap-up hand *them* the mirror of what *they* generated. + +Go to `## Choosing Techniques`. diff --git a/src/core-skills/bmad-brainstorming/scripts/brain.py b/src/core-skills/bmad-brainstorming/scripts/brain.py index ab029d231..b023bd786 100644 --- a/src/core-skills/bmad-brainstorming/scripts/brain.py +++ b/src/core-skills/bmad-brainstorming/scripts/brain.py @@ -16,14 +16,18 @@ Commands: list --all the whole index at once — deliberate; large, avoid interactively show NAME [NAME ...] full gist for each, inlining its detail file if it has one random [--category C] [-n N] pick N at random (optionally within categories) + html --out PATH write the offline 'browse all' selection page to a file -`list` refuses to run with neither --category nor --all: dumping the full catalog -into context is a footgun, so it must be an explicit choice. +`list` refuses to run with neither --category nor --all, and `html` writes to a file +rather than stdout: dumping the full catalog into context is a footgun, so reaching the +whole library at once must always be an explicit, deliberate choice. Default output is lean text for an LLM to read; pass --json for structured output. """ import argparse import csv +import hashlib +import html import json import random import sys @@ -109,6 +113,555 @@ def fmt_show(rows: list[dict], csv_dir: Path, as_json: bool) -> str: return "\n\n".join(blocks) +def pretty(cat: str) -> str: + """Turn a category slug (e.g. 'speculative_future') into a display name.""" + return cat.replace("_", " ").replace("-", " ").title() + + +# --- card visuals: a crafted duotone icon + hue per category --------------- +# Each card shows its category's icon (drawn in `currentColor`, which the CSS sets +# to the category hue) plus a per-technique accent seeded by the technique name, so +# every card is unique while staying on-theme. Hues span the wheel so categories +# stay distinguishable; an unknown (user-added) category gets a hash-derived hue and +# a generic glyph, so custom catalogs still render. + +_HUES = { + "creative": "#6d5cf0", + "deep": "#4658c9", + "structured": "#3b6ea5", + "quantum": "#2b86d9", + "speculative_future": "#0fb5c9", + "collaborative": "#15a3a3", + "biomimetic": "#1f9d6b", + "constraint": "#d9882b", + "wild": "#e2562f", + "cultural": "#c75b39", + "theatrical": "#cf4d6f", + "absurdist": "#e0529c", + "introspective_delight": "#b15ad6", +} + +CHIP = '' + +_GLYPHS = { + # idea starburst + "creative": ( + '' + '' + '' + '' + '' + '' + '' + ), + # nested depth rings + "deep": ( + '' + '' + '' + '' + '' + ), + # 2x2 blocks, diagonal filled + "structured": ( + '' + '' + '' + '' + '' + ), + # atom + "quantum": ( + '' + '' + '' + '' + '' + '' + ), + # upward arrow to a twinkling star + "speculative_future": ( + '' + '' + '' + '' + '' + '' + ), + # three linked nodes + "collaborative": ( + '' + '' + '' + '' + '' + '' + '' + ), + # leaf + "biomimetic": ( + '' + '' + '' + '' + '' + '' + ), + # corner brackets framing a point + "constraint": ( + '' + '' + '' + '' + '' + ), + # lightning bolt + "wild": ( + '' + ), + # globe + "cultural": ( + '' + '' + '' + '' + '' + ), + # theatre mask + "theatrical": ( + '' + '' + '' + '' + ), + # off-kilter winking grin + "absurdist": ( + '' + '' + '' + '' + '' + '' + ), + # meditating figure + "introspective_delight": ( + '' + '' + '' + '' + ), +} + +_FALLBACK_GLYPH = ( + '' + '' + '' +) + + +def _hsl_hex(deg: int, s: float, lt: float) -> str: + import colorsys + + r, g, b = colorsys.hls_to_rgb((deg % 360) / 360, lt, s) + return "#%02x%02x%02x" % (round(r * 255), round(g * 255), round(b * 255)) + + +def category_style(cat: str) -> tuple[str, str]: + """(hue, glyph markup) for a category — crafted for the shipped set, derived for extras.""" + if cat in _HUES: + return _HUES[cat], _GLYPHS[cat] + deg = int(hashlib.md5(cat.encode("utf-8")).hexdigest(), 16) % 360 + return _hsl_hex(deg, 0.58, 0.52), _FALLBACK_GLYPH + + +# A deliberately chosen line-icon depicting each specific technique. Drawn in +# `currentColor` (the category hue), consistent 2px stroke. Shown beside the shared +# category icon on every card. Unknown (custom) techniques fall back to a neutral mark. +_S = '' +_FALLBACK_TECH = ( + '' +) + +_TECH_ICONS = { + # --- collaborative --- + "Yes And Building": '', + "Brain Writing Round Robin": _S + '', + "Random Stimulation": '', + "Role Playing": '', + "Ideation Relay Race": _S + '', + "Idea Hot Potato": '', + "Steal And Upgrade": _S + '', + "Fold The Paper": '', + # --- creative --- + "What If Scenarios": _S + '', + "Analogical Thinking": '', + "First Principles Thinking": '', + "Forced Relationships": '', + "Time Shifting": '', + "Metaphor Mapping": '', + "Cross-Pollination": _S + '', + "Concept Blending": '', + "Reverse Brainstorming": _S + '', + "Sensory Exploration": '', + # --- deep --- + "Five Whys": _S + '', + "Provocation Technique": _S + '', + "Assumption Reversal": _S + '', + "Question Storming": _S + '', + "Constraint Mapping": '', + "Failure Analysis": '', + "Emergent Thinking": '', + "Causal Loop Mapping": _S + '', + "Morphological Analysis": '', + "Laddering": _S + '', + # --- introspective_delight --- + "Inner Child Conference": '', + "Shadow Work Mining": '', + "Values Archaeology": '', + "Future Self Interview": _S + '', + "Body Wisdom Dialogue": '', + "Permission Giving": '', + "Secret Wish Confession": '', + "Mood Weather Report": '', + # --- structured --- + "SCAMPER Method": '', + "Six Thinking Hats": '', + "Decision Tree Mapping": _S + '', + "Solution Matrix": '', + "Trait Transfer": '', + "Lotus Blossom": '', + "Worst Possible Idea": _S + '', + "Disney Method": '', + "Starbursting": _S + '', + "Mind Mapping": _S + '', + "Crazy 8s": '', + # --- theatrical --- + "Time Travel Talk Show": '', + "Alien Anthropologist": '', + "Dream Fusion Laboratory": _S + '', + "Emotion Orchestra": _S + '', + "Parallel Universe Cafe": '', + "Persona Journey": '', + "Devil's Advocate Courtroom": _S + '', + # --- wild --- + "Chaos Engineering": _S + '', + "Guerrilla Gardening Ideas": _S + '', + "Pirate Code Brainstorm": '', + "Zombie Apocalypse Planning": '', + "Drunk History Retelling": _S + '', + "Anti-Solution": _S + '', + "Elemental Forces": _S + '', + # --- biomimetic --- + "Nature's Solutions": _S + '', + "Ecosystem Thinking": _S + '', + "Evolutionary Pressure": _S + '', + "Predator & Prey": '', + "Metamorphosis Stages": _S + '', + "Swarm Logic": _S + '', + # --- quantum --- + "Observer Effect": '', + "Entanglement Thinking": '', + "Superposition Collapse": _S + '', + "Relativity Frame Shift": '', + "Field Lines": _S + '', + "Quantum Tunneling": '', + # --- cultural --- + "Indigenous Wisdom": _S + '', + "Fusion Cuisine": _S + '', + "Ritual Innovation": _S + '', + "Mythic Frameworks": _S + '', + "Proverb Mining": _S + '', + "Ancestor Council": '', + "Trickster's Gambit": '', + # --- absurdist --- + "Villain's Monologue": _S + '', + "Explain It to a Golden Retriever": _S + '', + "Infomercial at 3AM": '', + "Drunk Uncle at Thanksgiving": _S + '', + "Cursed Genie": _S + '', + "Three Rounds of Stupid": _S + '', + # --- constraint --- + "Kill the Crown Jewel": _S + '', + "1000x Budget": '', + "Ship in 60 Minutes": '', + "The $0 Mandate": '', + "One Feature Only": _S + '', + "Crank the Dial to 11": '', + "Constraint Roulette": '', + # --- speculative_future --- + "Time Horizon Ladder": _S + '', + "Post-Scarcity Test": _S + '', + "Utopia vs Dystopia Split-Screen": '', + "Sci-Fi Artifact From the Future": '', + "Emerging Tech Collision": '', + "What-If-The-World-Changed Card Flip": '', + "Future Anthropologist Dig": _S + '', +} + + +def tech_icon(name: str) -> str: + """The hand-picked line-icon for a specific technique (neutral mark if unknown).""" + return _TECH_ICONS.get(name, _FALLBACK_TECH) + + +SELECTOR_TEMPLATE = r""" + + + + +BMad Method Brainstorming Selection + + + +
+

BMad Method Brainstorming Selection

+

Compose your session, hit Copy prompt, and paste it back into the chat to begin. {{TOTAL}}

+ +
+
+ Facilitation +
+ + + +
+
+
+ Techniques + Picked 0 + Random 0 + Invent 0 + AI picks 0 + Total 0 · 3–4 is the sweet spot +
+
+ +
+ + +
+ +
{{CHIPS}}
+ +
+
+{{BODY}} +
+
BMad Method · Brainstorming
+ + + +""" + + +def html_doc(rows: list[dict]) -> str: + """Render the self-contained 'browse all techniques' selection page from the catalog. + + Deterministic: categories sorted, techniques in file order — so the shipped asset can + be snapshot-tested against the CSV and never silently drifts out of sync. + """ + groups: dict[str, list[dict]] = {} + for r in rows: + groups.setdefault(r["category"], []).append(r) + sections, chips = [], [] + for cat in sorted(groups): + hue, glyph = category_style(cat) + disp = html.escape(pretty(cat)) + cards = [] + for r in groups[cat]: + name = html.escape(r["technique_name"]) + desc = html.escape(r["description"]) + cat_icon = ( + '' + f'{CHIP}{glyph}' + ) + t_icon = ( + '' + f'{CHIP}{tech_icon(r["technique_name"])}' + ) + cards.append( + '' + ) + chips.append(f'') + sections.append( + f'

{disp}{len(groups[cat])}

' + f'
{"".join(cards)}
' + ) + total = html.escape(f"{len(rows)} techniques across {len(groups)} categories.") + return ( + SELECTOR_TEMPLATE.replace("{{BODY}}", "\n".join(sections)) + .replace("{{CHIPS}}", "".join(chips)) + .replace("{{TOTAL}}", total) + ) + + def main(argv: list[str] | None = None) -> int: p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) p.add_argument("--file", type=Path, default=DEFAULT_FILE, help="technique CSV (default: sibling assets/brain-methods.csv)") @@ -123,6 +676,8 @@ def main(argv: list[str] | None = None) -> int: pr = sub.add_parser("random", help="pick techniques at random") pr.add_argument("--category", action="append", help="restrict to a category (repeatable)") pr.add_argument("-n", type=int, default=1, help="how many (default 1)") + ph = sub.add_parser("html", help="write the offline 'browse all' selection page") + ph.add_argument("--out", help="file to write the page to (required; never prints the catalog)") args = p.parse_args(argv) if not args.file.is_file(): @@ -155,6 +710,18 @@ def main(argv: list[str] | None = None) -> int: print("# no techniques match", file=sys.stderr) return 1 print(fmt_list(random.sample(pool, min(args.n, len(pool))), args.json)) + elif args.cmd == "html": + if not args.out: + print( + "error: `html` needs --out PATH — it writes the selection page to a file and " + "never prints the catalog to stdout (which would defeat the point).", + file=sys.stderr, + ) + return 2 + out = Path(args.out) + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(html_doc(rows), encoding="utf-8") + print(f"wrote {out} ({len(rows)} techniques, {len(categories(rows))} categories)") return 0 diff --git a/src/core-skills/bmad-brainstorming/scripts/memlog.py b/src/core-skills/bmad-brainstorming/scripts/memlog.py index fac794892..bb4f4270b 100644 --- a/src/core-skills/bmad-brainstorming/scripts/memlog.py +++ b/src/core-skills/bmad-brainstorming/scripts/memlog.py @@ -44,13 +44,15 @@ The file shape (.memlog.md): - (decision) lead with one pre-categorized account; defer multi-account import Each entry may carry an optional `--type` — what KIND it is (idea, insight, question, -decision, technique, …) — rendered as a short inline tag. Omit it for a plain note. -The host skill names the vocabulary; the script does not. +decision, technique, …) — and an optional `--by` naming who it came from (e.g. `user`, +`coach`), for sessions where authorship matters. Both render into one short inline tag: +`(idea)`, `(idea by user)`, `(by coach)`. Omit them for a plain note. The host skill +names the vocabulary; the script does not. Commands: - init --workspace DIR [--field k=v ...] create the memlog (errors if it exists) - append --workspace DIR --text STR [--type T] append one entry at the end - set --workspace DIR --key K --value V set/replace a frontmatter field + init --workspace DIR [--field k=v ...] create the memlog (errors if it exists) + append --workspace DIR --text STR [--type T] [--by W] append one entry at the end + set --workspace DIR --key K --value V set/replace a frontmatter field The workspace is the run folder; the memlog is always {workspace}/.memlog.md. """ @@ -140,7 +142,10 @@ def cmd_append(args) -> int: path = memlog_path(args.workspace) meta, body = split(path.read_text(encoding="utf-8")) text = " ".join(args.text.split()) # collapse newlines/runs → one-line entry, no prose bloat - tag = f"({args.type}) " if args.type else "" + label = args.type or "" + if args.by: + label = f"{label} by {args.by}".strip() # attribution: "(idea by user)" / "(by coach)" + tag = f"({label}) " if label else "" entry = f"- {tag}{text}" body = (body.rstrip("\n") + "\n" + entry) if body.strip() else entry # always at the end touch(meta) @@ -172,6 +177,7 @@ def main(argv: list[str] | None = None) -> int: pa.add_argument("--workspace", required=True) pa.add_argument("--text", required=True) pa.add_argument("--type", help="entry kind, rendered as an inline tag") + pa.add_argument("--by", help="who the entry came from (e.g. user, coach); rendered into the tag") pa.set_defaults(func=cmd_append) pset = sub.add_parser("set", help="set a frontmatter field") diff --git a/src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py b/src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py index 125197f4c..c4fb17500 100644 --- a/src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py +++ b/src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py @@ -125,3 +125,40 @@ def test_random_respects_n_and_category(lib, capsys): def test_missing_file_returns_2(tmp_path): assert brain.main(["--file", str(tmp_path / "nope.csv"), "categories"]) == 2 + + +# --- html selection page ------------------------------------------------ + +def test_html_requires_out(lib, capsys): + # never dump the catalog to stdout — writing to a file is the whole point + assert brain.main(["--file", str(lib), "html"]) == 2 + assert "--out" in capsys.readouterr().err + + +def test_html_writes_selection_page(lib, tmp_path): + out = tmp_path / "sel.html" + assert brain.main(["--file", str(lib), "html", "--out", str(out)]) == 0 + doc = out.read_text(encoding="utf-8") + assert doc.startswith("") + assert "BMad Method Brainstorming Selection" in doc + for r in brain.load(lib): + assert r["technique_name"] in doc # every technique is selectable + assert ""yes and"" in doc # quotes in a description are escaped, not raw + + +def test_html_creates_missing_parent(lib, tmp_path): + out = tmp_path / "nested" / "deep" / "sel.html" + assert brain.main(["--file", str(lib), "html", "--out", str(out)]) == 0 + assert out.is_file() + + +def test_shipped_selector_is_in_sync_with_catalog(): + # foolproofing: if someone edits brain-methods.csv they must regenerate the page. + # Regenerate with: python3 brain.py html --out assets/brain-selector.html + asset = brain.DEFAULT_FILE.parent / "brain-selector.html" + assert asset.is_file(), "missing assets/brain-selector.html — generate it" + expected = brain.html_doc(brain.load(brain.DEFAULT_FILE)) + assert asset.read_text(encoding="utf-8") == expected, ( + "assets/brain-selector.html is stale; regenerate: " + "python3 brain.py html --out assets/brain-selector.html" + ) diff --git a/src/core-skills/bmad-brainstorming/scripts/tests/test_memlog.py b/src/core-skills/bmad-brainstorming/scripts/tests/test_memlog.py index 8f71defef..d16690dd1 100644 --- a/src/core-skills/bmad-brainstorming/scripts/tests/test_memlog.py +++ b/src/core-skills/bmad-brainstorming/scripts/tests/test_memlog.py @@ -44,10 +44,12 @@ def init(ws, **fields): assert memlog.main(argv) == 0 -def append(ws, text, type=None): +def append(ws, text, type=None, by=None): argv = ["append", "--workspace", ws, "--text", text] if type: argv += ["--type", type] + if by: + argv += ["--by", by] assert memlog.main(argv) == 0 @@ -142,6 +144,22 @@ def test_revisited_technique_is_just_a_later_entry(ws): ] +def test_by_renders_attribution_in_tag(ws): + # Creative Partner mode must record whose idea each one was + init(ws) + append(ws, "magnetic latch lid", type="idea", by="user") + append(ws, "lid doubles as a plate", type="idea", by="coach") + body = body_of(ws) + assert "- (idea by user) magnetic latch lid" in body + assert "- (idea by coach) lid doubles as a plate" in body + + +def test_by_without_type_renders_alone(ws): + init(ws) + append(ws, "off-the-cuff thought", by="coach") + assert entries(ws) == ["- (by coach) off-the-cuff thought"] + + def test_heterogeneous_entry_types_coexist(ws): init(ws) append(ws, "an idea", type="idea")