Compare commits

...

2 Commits

Author SHA1 Message Date
Murat K Ozcan ae7b3a7930
docs: post install notes (#1653) 2026-02-14 10:59:07 -06:00
Murat K Ozcan 3103c3d4ce
feat: tea automation prereq prompts (#1649)
* feat: tea automation prereq prompts

* fix: addressed PR comments

* docs: added docs on how to set the post install output in the module

* addressed PR request from Brian
2026-02-14 10:44:39 -06:00
2 changed files with 110 additions and 0 deletions

View File

@ -5,3 +5,56 @@
For external official modules to be discoverable during install, ensure an entry for the external repo is added to external-official-modules.yaml. For external official modules to be discoverable during install, ensure an entry for the external repo is added to external-official-modules.yaml.
For community modules - this will be handled in a different way. This file is only for registration of modules under the bmad-code-org. For community modules - this will be handled in a different way. This file is only for registration of modules under the bmad-code-org.
## Post-Install Notes
Modules can display setup guidance to users after configuration is collected during `npx bmad-method install`. Notes are defined in the module's own `module.yaml` — no changes to the installer are needed.
### Simple Format
Always displayed after the module is configured:
```yaml
post-install-notes: |
Remember to set the API_KEY environment variable.
See: https://example.com/setup
```
### Conditional Format
Display different messages based on a config question's answer:
```yaml
post-install-notes:
config_key_name:
value1: |
Instructions for value1...
value2: |
Instructions for value2...
```
Values without an entry (e.g., `none`) display nothing. Multiple config keys can each have their own conditional notes.
### Example: TEA Module
The TEA module uses the conditional format keyed on `tea_browser_automation`:
```yaml
post-install-notes:
tea_browser_automation:
cli: |
Playwright CLI Setup:
npm install -g @playwright/cli@latest
playwright-cli install --skills
mcp: |
Playwright MCP Setup (two servers):
1. playwright — npx @playwright/mcp@latest
2. playwright-test — npx playwright run-test-mcp-server
auto: |
Playwright CLI Setup:
...
Playwright MCP Setup (two servers):
...
```
When a user selects `auto`, they see both CLI and MCP instructions. When they select `none`, nothing is shown.

View File

@ -550,6 +550,8 @@ class ConfigCollector {
} }
} }
await this.displayModulePostConfigNotes(moduleName, moduleConfig);
return newKeys.length > 0 || newStaticKeys.length > 0; // Return true if we had any new fields (interactive or static) return newKeys.length > 0 || newStaticKeys.length > 0; // Return true if we had any new fields (interactive or static)
} }
@ -923,6 +925,8 @@ class ConfigCollector {
} }
} }
} }
await this.displayModulePostConfigNotes(moduleName, moduleConfig);
} }
/** /**
@ -1195,6 +1199,59 @@ class ConfigCollector {
return question; return question;
} }
/**
* Display post-configuration notes for a module
* Shows prerequisite guidance based on collected config values
* Reads notes from the module's `post-install-notes` section in module.yaml
* Supports two formats:
* - Simple string: always displayed
* - Object keyed by config field name, with value-specific messages
* @param {string} moduleName - Module name
* @param {Object} moduleConfig - Parsed module.yaml content
*/
async displayModulePostConfigNotes(moduleName, moduleConfig) {
if (this._silentConfig) return;
if (!moduleConfig || !moduleConfig['post-install-notes']) return;
const notes = moduleConfig['post-install-notes'];
const color = await prompts.getColor();
// Format 1: Simple string - always display
if (typeof notes === 'string') {
await prompts.log.message('');
for (const line of notes.trim().split('\n')) {
await prompts.log.message(color.dim(line));
}
return;
}
// Format 2: Conditional on config values
if (typeof notes === 'object') {
const config = this.collectedConfig[moduleName];
if (!config) return;
let hasOutput = false;
for (const [configKey, valueMessages] of Object.entries(notes)) {
const selectedValue = config[configKey];
if (!selectedValue || !valueMessages[selectedValue]) continue;
if (hasOutput) await prompts.log.message('');
hasOutput = true;
const message = valueMessages[selectedValue];
await prompts.log.message('');
for (const line of message.trim().split('\n')) {
const trimmedLine = line.trim();
if (trimmedLine.endsWith(':') && !trimmedLine.startsWith(' ')) {
await prompts.log.info(color.bold(trimmedLine));
} else {
await prompts.log.message(color.dim(' ' + trimmedLine));
}
}
}
}
}
/** /**
* Deep merge two objects * Deep merge two objects
* @param {Object} target - Target object * @param {Object} target - Target object