all modules custom or core use the same installer and have consistent behavior now.
This commit is contained in:
parent
baaa984a90
commit
6430173738
|
|
@ -1,47 +0,0 @@
|
||||||
# CBT Coach - Cognitive Distortions Reference
|
|
||||||
|
|
||||||
## The 10 Cognitive Distortions
|
|
||||||
|
|
||||||
1. **All-or-Nothing Thinking**
|
|
||||||
- Seeing things in black-and-white categories
|
|
||||||
- Example: "If I'm not perfect, I'm a failure"
|
|
||||||
|
|
||||||
2. **Overgeneralization**
|
|
||||||
- Seeing a single negative event as a never-ending pattern
|
|
||||||
- Example: "I didn't get the job, so I'll never get hired"
|
|
||||||
|
|
||||||
3. **Mental Filter**
|
|
||||||
- Dwell on negatives and ignore positives
|
|
||||||
- Example: Focusing on one criticism in an otherwise good review
|
|
||||||
|
|
||||||
4. **Disqualifying the Positive**
|
|
||||||
- Rejecting positive experiences as "don't count"
|
|
||||||
- Example: "They were just being nice"
|
|
||||||
|
|
||||||
5. **Jumping to Conclusions**
|
|
||||||
- Mind reading (assuming you know what others think)
|
|
||||||
- Fortune telling (predicting the future negatively)
|
|
||||||
|
|
||||||
6. **Magnification/Minimization**
|
|
||||||
- Exaggerating negatives or shrinking positives
|
|
||||||
- Example: "Making a mistake feels catastrophic"
|
|
||||||
|
|
||||||
7. **Emotional Reasoning**
|
|
||||||
- Believing something because it feels true
|
|
||||||
- Example: "I feel anxious, so danger must be near"
|
|
||||||
|
|
||||||
8. **"Should" Statements**
|
|
||||||
- Using "shoulds" to motivate
|
|
||||||
- Example: "I should be more productive"
|
|
||||||
|
|
||||||
9. **Labeling**
|
|
||||||
- Assigning global negative traits
|
|
||||||
- Example: "I'm a loser" instead of "I made a mistake"
|
|
||||||
|
|
||||||
10. **Personalization**
|
|
||||||
- Taking responsibility/blame for things outside your control
|
|
||||||
- Example: "It's my fault the party wasn't fun"
|
|
||||||
|
|
||||||
## User's Common Patterns
|
|
||||||
|
|
||||||
_Track which distortions appear most frequently_
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
# CBT Coach - Thought Records
|
|
||||||
|
|
||||||
## Thought Record History
|
|
||||||
|
|
||||||
_CBT thought records are documented here for pattern tracking and progress review_
|
|
||||||
|
|
||||||
## Common Patterns Identified
|
|
||||||
|
|
||||||
_Recurring cognitive distortions and thought patterns_
|
|
||||||
|
|
||||||
## Successful Reframes
|
|
||||||
|
|
||||||
_Examples of successful cognitive restructuring_
|
|
||||||
|
|
||||||
## Homework Assignments
|
|
||||||
|
|
||||||
_CBT exercises and behavioral experiments_
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
# Wellness Companion - Insights
|
|
||||||
|
|
||||||
## User Insights
|
|
||||||
|
|
||||||
_Important realizations and breakthrough moments are documented here with timestamps_
|
|
||||||
|
|
||||||
## Patterns Observed
|
|
||||||
|
|
||||||
_Recurring themes and patterns noticed over time_
|
|
||||||
|
|
||||||
## Progress Notes
|
|
||||||
|
|
||||||
_Milestones and positive changes in the wellness journey_
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# Wellness Companion - Instructions
|
|
||||||
|
|
||||||
## Safety Protocols
|
|
||||||
|
|
||||||
1. Always validate user feelings before offering guidance
|
|
||||||
2. Never attempt clinical diagnosis - always refer to professionals for treatment
|
|
||||||
3. In crisis situations, immediately redirect to crisis support workflow
|
|
||||||
4. Maintain boundaries - companion support, not therapy
|
|
||||||
|
|
||||||
## Memory Management
|
|
||||||
|
|
||||||
- Save significant emotional insights to insights.md
|
|
||||||
- Track recurring patterns in patterns.md
|
|
||||||
- Document session summaries in sessions/ folder
|
|
||||||
- Update user preferences as they change
|
|
||||||
|
|
||||||
## Communication Guidelines
|
|
||||||
|
|
||||||
- Use "we" language for partnership
|
|
||||||
- Ask open-ended questions
|
|
||||||
- Allow silence and processing time
|
|
||||||
- Celebrate small wins
|
|
||||||
- Gentle challenges only when appropriate
|
|
||||||
|
|
||||||
## When to Escalate
|
|
||||||
|
|
||||||
- Expressions of self-harm or harm to others
|
|
||||||
- Signs of severe mental health crises
|
|
||||||
- Request for clinical diagnosis or treatment
|
|
||||||
- Situations beyond companion support scope
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
# Wellness Companion - Memories
|
|
||||||
|
|
||||||
## User Preferences
|
|
||||||
|
|
||||||
_This file tracks user preferences and important context across sessions_
|
|
||||||
|
|
||||||
## Important Conversations
|
|
||||||
|
|
||||||
_Key moments and breakthroughs are documented here_
|
|
||||||
|
|
||||||
## Ongoing Goals
|
|
||||||
|
|
||||||
_User's wellness goals and progress_
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
# Wellness Companion - Patterns
|
|
||||||
|
|
||||||
## Emotional Patterns
|
|
||||||
|
|
||||||
_Track recurring emotional states and triggers_
|
|
||||||
|
|
||||||
## Behavioral Patterns
|
|
||||||
|
|
||||||
_Note habits and routines that affect wellness_
|
|
||||||
|
|
||||||
## Coping Patterns
|
|
||||||
|
|
||||||
_Identify effective coping strategies and challenges_
|
|
||||||
|
|
||||||
## Progress Patterns
|
|
||||||
|
|
||||||
_Document growth trends and areas needing attention_
|
|
||||||
|
|
@ -64,7 +64,7 @@ A custom module follows this structure:
|
||||||
my-module/
|
my-module/
|
||||||
├── _module-installer/
|
├── _module-installer/
|
||||||
│ ├── installer.js # optional, when it exists it will run with module installation
|
│ ├── installer.js # optional, when it exists it will run with module installation
|
||||||
│ └── install-config.yaml # Module installation configuration with custom question and answer capture
|
├── module.yaml # Module installation configuration with custom question and answer capture
|
||||||
├── docs/ # Module documentation
|
├── docs/ # Module documentation
|
||||||
├── agents/ # Module-specific agents
|
├── agents/ # Module-specific agents
|
||||||
├── workflows/ # Module-specific workflows
|
├── workflows/ # Module-specific workflows
|
||||||
|
|
@ -77,7 +77,7 @@ my-module/
|
||||||
|
|
||||||
#### Module Configuration
|
#### Module Configuration
|
||||||
|
|
||||||
The `_module-installer/install-config.yaml` file defines how your module is installed:
|
The `module.yaml` file defines how your module is installed:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# Module metadata
|
# Module metadata
|
||||||
|
|
@ -99,12 +99,6 @@ my_setting:
|
||||||
|
|
||||||
See `/example-custom-module` for a complete example:
|
See `/example-custom-module` for a complete example:
|
||||||
|
|
||||||
```bash
|
|
||||||
# The example is ready to use - just rename the _module-installer/install-config file:
|
|
||||||
mv example-custom-module/mwm/_module-installer/install-config.bak \
|
|
||||||
example-custom-module/mwm/_module-installer/install-config.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation Process
|
## Installation Process
|
||||||
|
|
||||||
### Step 1: Running the Installer
|
### Step 1: Running the Installer
|
||||||
|
|
@ -129,7 +123,7 @@ If you select "Enter a directory path", the installer will prompt for the locati
|
||||||
The installer will:
|
The installer will:
|
||||||
|
|
||||||
- Scan the directory and all subdirectories for the presence of a `custom.yaml` file (standalone content such as agents and workflows)
|
- Scan the directory and all subdirectories for the presence of a `custom.yaml` file (standalone content such as agents and workflows)
|
||||||
- Scan for `_module-installer/install-config.yaml` files (modules)
|
- Scan for `module.yaml` files (modules)
|
||||||
- Display an indication of how many installable folders it has found. Note that a project with stand along agents and workflows all under a single folder like the example will just list the count as 1 for that directory.
|
- Display an indication of how many installable folders it has found. Note that a project with stand along agents and workflows all under a single folder like the example will just list the count as 1 for that directory.
|
||||||
|
|
||||||
### Step 3: Selecting Content
|
### Step 3: Selecting Content
|
||||||
|
|
@ -230,7 +224,7 @@ Custom content can be distributed:
|
||||||
|
|
||||||
### No Custom Content Found
|
### No Custom Content Found
|
||||||
|
|
||||||
- Ensure your `custom.yaml` or `install-config.yaml` files are properly named
|
- Ensure your `custom.yaml` or `module.yaml` files are properly named
|
||||||
- Check file permissions
|
- Check file permissions
|
||||||
- Verify the directory path is correct
|
- Verify the directory path is correct
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ project-root/
|
||||||
### Key Exclusions
|
### Key Exclusions
|
||||||
|
|
||||||
- `_module-installer/` directories are never copied to destination
|
- `_module-installer/` directories are never copied to destination
|
||||||
|
- module.yaml
|
||||||
- `localskip="true"` agents are filtered out
|
- `localskip="true"` agents are filtered out
|
||||||
- Source `config.yaml` templates are replaced with generated configs
|
- Source `config.yaml` templates are replaced with generated configs
|
||||||
|
|
||||||
|
|
@ -92,8 +93,8 @@ Creative Innovation Studio for design workflows
|
||||||
```
|
```
|
||||||
src/modules/{module}/
|
src/modules/{module}/
|
||||||
├── _module-installer/ # Not copied to destination
|
├── _module-installer/ # Not copied to destination
|
||||||
│ ├── installer.js # Post-install logic
|
│ ├── installer.js # Post-install logic
|
||||||
│ └── install-config.yaml
|
├── module.yaml
|
||||||
├── agents/
|
├── agents/
|
||||||
├── tasks/
|
├── tasks/
|
||||||
├── templates/
|
├── templates/
|
||||||
|
|
@ -107,7 +108,7 @@ src/modules/{module}/
|
||||||
|
|
||||||
### Collection Process
|
### Collection Process
|
||||||
|
|
||||||
Modules define prompts in `install-config.yaml`:
|
Modules define prompts in `module.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
project_name:
|
project_name:
|
||||||
|
|
@ -218,12 +219,12 @@ Platform-specific content without source modification:
|
||||||
src/modules/mymod/
|
src/modules/mymod/
|
||||||
├── _module-installer/
|
├── _module-installer/
|
||||||
│ ├── installer.js
|
│ ├── installer.js
|
||||||
│ └── install-config.yaml
|
├── module.yaml
|
||||||
├── agents/
|
├── agents/
|
||||||
└── tasks/
|
└── tasks/
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Configuration** (`install-config.yaml`)
|
2. **Configuration** (`module.yaml`)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
code: mymod
|
code: mymod
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ CLI uses Commander.js, commands auto-loaded from `tools/cli/commands/`:
|
||||||
### Core Architecture Patterns
|
### Core Architecture Patterns
|
||||||
|
|
||||||
1. **IDE Handlers**: Each IDE extends BaseIdeSetup class
|
1. **IDE Handlers**: Each IDE extends BaseIdeSetup class
|
||||||
2. **Module Installers**: Modules can have `_module-installer/installer.js`
|
2. **Module Installers**: Modules can have `module.yaml` and `_module-installer/installer.js`
|
||||||
3. **Sub-modules**: IDE-specific customizations in `sub-modules/{ide-name}/`
|
3. **Sub-modules**: IDE-specific customizations in `sub-modules/{ide-name}/`
|
||||||
4. **Shared Utilities**: `tools/cli/installers/lib/ide/shared/` contains generators
|
4. **Shared Utilities**: `tools/cli/installers/lib/ide/shared/` contains generators
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ Contains:
|
||||||
|
|
||||||
- Add new IDE handler: Create file in /tools/cli/installers/lib/ide/, extend BaseIdeSetup
|
- Add new IDE handler: Create file in /tools/cli/installers/lib/ide/, extend BaseIdeSetup
|
||||||
- Fix installer bug: Check installer.js (94KB - main logic)
|
- Fix installer bug: Check installer.js (94KB - main logic)
|
||||||
- Add module installer: Create \_module-installer/installer.js in module
|
- Add module installer: Create \_module-installer/installer.js if custom installer logic needed
|
||||||
- Update shared generators: Modify files in /shared/ directory
|
- Update shared generators: Modify files in /shared/ directory
|
||||||
|
|
||||||
## Relationships
|
## Relationships
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ src/modules/{module-name}/
|
||||||
│ ├── injections.yaml
|
│ ├── injections.yaml
|
||||||
│ ├── config.yaml
|
│ ├── config.yaml
|
||||||
│ └── sub-agents/
|
│ └── sub-agents/
|
||||||
├── install-config.yaml # Module install configuration
|
├── module.yaml # Module install configuration
|
||||||
└── README.md # Module documentation
|
└── README.md # Module documentation
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -145,7 +145,7 @@ Defined in @/tools/cli/lib/platform-codes.js
|
||||||
- Create new module installer: Add \_module-installer/installer.js
|
- Create new module installer: Add \_module-installer/installer.js
|
||||||
- Add IDE sub-module: Create sub-modules/{ide-name}/ with config
|
- Add IDE sub-module: Create sub-modules/{ide-name}/ with config
|
||||||
- Add new IDE support: Create handler in installers/lib/ide/
|
- Add new IDE support: Create handler in installers/lib/ide/
|
||||||
- Customize module installation: Modify install-config.yaml
|
- Customize module installation: Modify module.yaml
|
||||||
|
|
||||||
## Relationships
|
## Relationships
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
code: bmad-custom
|
code: bmad-custom
|
||||||
name: "BMAD-Custom: Sample Stand Alone Custom Agents and Workflows"
|
name: "BMAD-Custom: Sample Stand Alone Custom Agents and Workflows"
|
||||||
default_selected: true
|
default_selected: true
|
||||||
|
type: custom
|
||||||
|
|
@ -3,9 +3,6 @@
|
||||||
This module is an example and is not at all recommended for any usage, this module was not vetted by any medical professionals and should
|
This module is an example and is not at all recommended for any usage, this module was not vetted by any medical professionals and should
|
||||||
be considered at best for entertainment purposes only.
|
be considered at best for entertainment purposes only.
|
||||||
|
|
||||||
IF you want to see how a custom module installation works, copy this whole folder to where you will be installing from with npx, and rename
|
|
||||||
"\_module-installer/install-config.bak" to "\_module-installer/install-config.yaml".
|
|
||||||
|
|
||||||
You should see the option in the module selector when installing.
|
You should see the option in the module selector when installing.
|
||||||
|
|
||||||
If you have received a module from someone else that is not in the official installation - you can install it similarly by running the
|
If you have received a module from someone else that is not in the official installation - you can install it similarly by running the
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
code: mwm
|
code: mwm
|
||||||
name: "MWM: Mental Wellness Module"
|
name: "MWM: Mental Wellness Module"
|
||||||
default_selected: false
|
default_selected: false
|
||||||
|
type: module
|
||||||
|
|
||||||
header: "MWM™: Custom Wellness Module"
|
header: "MWM™: Custom Wellness Module"
|
||||||
subheader: "Demo of Potential Non Coding Custom Module Use case"
|
subheader: "Demo of Potential Non Coding Custom Module Use case"
|
||||||
|
|
@ -6,7 +6,7 @@ const chalk = require('chalk');
|
||||||
*
|
*
|
||||||
* @param {Object} options - Installation options
|
* @param {Object} options - Installation options
|
||||||
* @param {string} options.projectRoot - The root directory of the target project
|
* @param {string} options.projectRoot - The root directory of the target project
|
||||||
* @param {Object} options.config - Module configuration from install-config.yaml
|
* @param {Object} options.config - Module configuration from module.yaml
|
||||||
* @param {Array<string>} options.installedIDEs - Array of IDE codes that were installed
|
* @param {Array<string>} options.installedIDEs - Array of IDE codes that were installed
|
||||||
* @param {Object} options.logger - Logger instance for output
|
* @param {Object} options.logger - Logger instance for output
|
||||||
* @returns {Promise<boolean>} - Success status
|
* @returns {Promise<boolean>} - Success status
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ const chalk = require('chalk');
|
||||||
*
|
*
|
||||||
* @param {Object} options - Installation options
|
* @param {Object} options - Installation options
|
||||||
* @param {string} options.projectRoot - The root directory of the target project
|
* @param {string} options.projectRoot - The root directory of the target project
|
||||||
* @param {Object} options.config - Module configuration from install-config.yaml
|
* @param {Object} options.config - Module configuration from module.yaml
|
||||||
* @param {Object} options.coreConfig - Core configuration containing user_name
|
* @param {Object} options.coreConfig - Core configuration containing user_name
|
||||||
* @param {Array<string>} options.installedIDEs - Array of IDE codes that were installed
|
* @param {Array<string>} options.installedIDEs - Array of IDE codes that were installed
|
||||||
* @param {Object} options.logger - Logger instance for output
|
* @param {Object} options.logger - Logger instance for output
|
||||||
|
|
|
||||||
|
|
@ -113,10 +113,10 @@ For a [module type] module, we'll create this structure:"
|
||||||
│ └── [template-files]
|
│ └── [template-files]
|
||||||
├── data/ # Module data files
|
├── data/ # Module data files
|
||||||
│ └── [data-files]
|
│ └── [data-files]
|
||||||
|
├── module.yaml # Required
|
||||||
├── _module-installer/ # Installation configuration
|
├── _module-installer/ # Installation configuration
|
||||||
│ ├── install-config.yaml # Required
|
│ ├── installer.js # Optional
|
||||||
│ ├── installer.js # Optional
|
│ └── assets/ # Optional install assets
|
||||||
│ └── assets/ # Optional install assets
|
|
||||||
└── README.md # Module documentation
|
└── README.md # Module documentation
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ Update module-plan.md with configuration section:
|
||||||
|
|
||||||
### Result Configuration Structure
|
### Result Configuration Structure
|
||||||
|
|
||||||
The install-config.yaml will generate:
|
The module.yaml will generate:
|
||||||
- Module configuration at: {bmad_folder}/{module_code}/config.yaml
|
- Module configuration at: {bmad_folder}/{module_code}/config.yaml
|
||||||
- User settings stored as: [describe structure]
|
- User settings stored as: [describe structure]
|
||||||
````
|
````
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ partyModeWorkflow: '{project-root}/{bmad_folder}/core/workflows/party-mode/workf
|
||||||
## EXECUTION PROTOCOLS:
|
## EXECUTION PROTOCOLS:
|
||||||
|
|
||||||
- 🎯 Use configuration plan from step 5
|
- 🎯 Use configuration plan from step 5
|
||||||
- 💾 Create install-config.yaml with all fields
|
- 💾 Create module.yaml with all fields
|
||||||
- 📖 Add "step-08-installer" to stepsCompleted array` before loading next step
|
- 📖 Add "step-08-installer" to stepsCompleted array` before loading next step
|
||||||
- 🚫 FORBIDDEN to load next step until user selects 'C'
|
- 🚫 FORBIDDEN to load next step until user selects 'C'
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ partyModeWorkflow: '{project-root}/{bmad_folder}/core/workflows/party-mode/workf
|
||||||
|
|
||||||
## STEP GOAL:
|
## STEP GOAL:
|
||||||
|
|
||||||
To create the module installer configuration (install-config.yaml) that defines how users will install and configure the module.
|
To create the module installer configuration (module.yaml) that defines how users will install and configure the module.
|
||||||
|
|
||||||
## INSTALLER SETUP PROCESS:
|
## INSTALLER SETUP PROCESS:
|
||||||
|
|
||||||
|
|
@ -74,11 +74,11 @@ From step 5, we planned these configuration fields:
|
||||||
Ensure \_module-installer directory exists
|
Ensure \_module-installer directory exists
|
||||||
Directory: {custom_module_location}/{module_name}/\_module-installer/
|
Directory: {custom_module_location}/{module_name}/\_module-installer/
|
||||||
|
|
||||||
### 3. Create install-config.yaml
|
### 3. Create module.yaml
|
||||||
|
|
||||||
"I'll create the install-config.yaml file based on your configuration plan. This is the core installer configuration file."
|
"I'll create the module.yaml file based on your configuration plan. This is the core installer configuration file."
|
||||||
|
|
||||||
Create file: {custom_module_location}/{module_name}/\_module-installer/install-config.yaml from template {installConfigTemplate}
|
Create file: {custom_module_location}/{module_name}/module.yaml from template {installConfigTemplate}
|
||||||
|
|
||||||
### 4. Handle Custom Installation Logic
|
### 4. Handle Custom Installation Logic
|
||||||
|
|
||||||
|
|
@ -117,7 +117,7 @@ Update module-plan.md with installer section:
|
||||||
|
|
||||||
### Install Configuration
|
### Install Configuration
|
||||||
|
|
||||||
- File: \_module-installer/install-config.yaml
|
- File: module.yaml
|
||||||
- Module code: {module_name}
|
- Module code: {module_name}
|
||||||
- Default selected: false
|
- Default selected: false
|
||||||
- Configuration fields: [count]
|
- Configuration fields: [count]
|
||||||
|
|
@ -166,7 +166,7 @@ Display: **Select an Option:** [A] Advanced Elicitation [P] Party Mode [C] Conti
|
||||||
|
|
||||||
### ✅ SUCCESS:
|
### ✅ SUCCESS:
|
||||||
|
|
||||||
- install-config.yaml created with all planned fields
|
- module.yaml created with all planned fields
|
||||||
- YAML syntax valid
|
- YAML syntax valid
|
||||||
- Custom installation logic prepared (if needed)
|
- Custom installation logic prepared (if needed)
|
||||||
- Installer follows BMAD standards
|
- Installer follows BMAD standards
|
||||||
|
|
@ -174,7 +174,7 @@ Display: **Select an Option:** [A] Advanced Elicitation [P] Party Mode [C] Conti
|
||||||
|
|
||||||
### ❌ SYSTEM FAILURE:
|
### ❌ SYSTEM FAILURE:
|
||||||
|
|
||||||
- Not creating install-config.yaml
|
- Not creating module.yaml
|
||||||
- Invalid YAML syntax
|
- Invalid YAML syntax
|
||||||
- Missing required fields
|
- Missing required fields
|
||||||
- Not using proper path templates
|
- Not using proper path templates
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,8 @@ bmad install {module_name}
|
||||||
├── tasks/ # Task files
|
├── tasks/ # Task files
|
||||||
├── templates/ # Shared templates
|
├── templates/ # Shared templates
|
||||||
├── data/ # Module data
|
├── data/ # Module data
|
||||||
├── _module-installer/ # Installation config
|
├── _module-installer/ # Installation optional js file with custom install routine
|
||||||
|
├── module.yaml # yaml config and install questions
|
||||||
└── README.md # This file
|
└── README.md # This file
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -207,9 +207,10 @@ workflow {workflow_name}
|
||||||
├── workflows/ # ✅ Structure created, plans written
|
├── workflows/ # ✅ Structure created, plans written
|
||||||
├── tasks/ # ✅ Created
|
├── tasks/ # ✅ Created
|
||||||
├── templates/ # ✅ Created
|
├── templates/ # ✅ Created
|
||||||
├── data/ # ✅ Created
|
├── data/ # ✅ Created
|
||||||
├── _module-installer/ # ✅ Configured
|
├── _module-installer/ # ✅ Configured
|
||||||
└── README.md # ✅ Complete
|
└── README.md # ✅ Complete
|
||||||
|
└── module.yaml # ✅ Complete
|
||||||
```
|
```
|
||||||
|
|
||||||
## Completion Criteria
|
## Completion Criteria
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,8 @@ Expected Structure:
|
||||||
├── templates/ [✅/❌]
|
├── templates/ [✅/❌]
|
||||||
├── data/ [✅/❌]
|
├── data/ [✅/❌]
|
||||||
├── _module-installer/ [✅/❌]
|
├── _module-installer/ [✅/❌]
|
||||||
│ ├── install-config.yaml [✅/❌]
|
│ └── installer.js [✅/N/A]
|
||||||
│ └── installer.js [✅/N/A]
|
├── module.yaml [✅/❌]
|
||||||
└── README.md [✅/❌]
|
└── README.md [✅/❌]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -87,7 +87,7 @@ Expected Structure:
|
||||||
"**2. Configuration Files Check**"
|
"**2. Configuration Files Check**"
|
||||||
|
|
||||||
**Install Configuration:**
|
**Install Configuration:**
|
||||||
Validate install-config.yaml
|
Validate module.yaml
|
||||||
|
|
||||||
- [ ] YAML syntax valid
|
- [ ] YAML syntax valid
|
||||||
- [ ] Module code matches folder name
|
- [ ] Module code matches folder name
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
/**
|
/**
|
||||||
* @param {Object} options - Installation options
|
* @param {Object} options - Installation options
|
||||||
* @param {string} options.projectRoot - Project root directory
|
* @param {string} options.projectRoot - Project root directory
|
||||||
* @param {Object} options.config - Module configuration from install-config.yaml
|
* @param {Object} options.config - Module configuration from module.yaml
|
||||||
* @param {Array} options.installedIDEs - List of IDE codes being configured
|
* @param {Array} options.installedIDEs - List of IDE codes being configured
|
||||||
* @param {Object} options.logger - Logger instance (log, warn, error methods)
|
* @param {Object} options.logger - Logger instance (log, warn, error methods)
|
||||||
* @returns {boolean} - true if successful, false to abort installation
|
* @returns {boolean} - true if successful, false to abort installation
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,15 @@ This document provides the validation criteria used in step-11-validate.md to en
|
||||||
- [ ] data/ - Module data
|
- [ ] data/ - Module data
|
||||||
- [ ] \_module-installer/ - Installation config
|
- [ ] \_module-installer/ - Installation config
|
||||||
- [ ] README.md - Module documentation
|
- [ ] README.md - Module documentation
|
||||||
|
- [ ] module.yaml - module config file
|
||||||
|
|
||||||
### Required Files in \_module-installer/
|
### Optional File in \_module-installer/
|
||||||
|
|
||||||
- [ ] install-config.yaml - Installation configuration
|
|
||||||
- [ ] installer.js - Custom logic (if needed)
|
- [ ] installer.js - Custom logic (if needed)
|
||||||
|
|
||||||
## Configuration Validation
|
## Configuration Validation
|
||||||
|
|
||||||
### install-config.yaml
|
### module.yaml
|
||||||
|
|
||||||
- [ ] Valid YAML syntax
|
- [ ] Valid YAML syntax
|
||||||
- [ ] Module code matches folder name
|
- [ ] Module code matches folder name
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ After getting the workflow name:
|
||||||
Based on the module selection, confirm the target location:
|
Based on the module selection, confirm the target location:
|
||||||
|
|
||||||
- For bmb module: `{custom_workflow_location}` (defaults to `{bmad_folder}/custom/src/workflows`)
|
- For bmb module: `{custom_workflow_location}` (defaults to `{bmad_folder}/custom/src/workflows`)
|
||||||
- For other modules: Check their install-config.yaml for custom workflow locations
|
- For other modules: Check their module.yaml for custom workflow locations
|
||||||
- Confirm the exact folder path where the workflow will be created
|
- Confirm the exact folder path where the workflow will be created
|
||||||
- Store the confirmed path as `{targetWorkflowPath}`
|
- Store the confirmed path as `{targetWorkflowPath}`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ Create the workflow folder structure in the target location:
|
||||||
```
|
```
|
||||||
|
|
||||||
For bmb module, this will be: `{bmad_folder}/custom/src/workflows/{workflow_name}/`
|
For bmb module, this will be: `{bmad_folder}/custom/src/workflows/{workflow_name}/`
|
||||||
For other modules, check their install-config.yaml for custom_workflow_location
|
For other modules, check their module.yaml for custom_workflow_location
|
||||||
|
|
||||||
### 3. Generate workflow.md
|
### 3. Generate workflow.md
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,8 +129,9 @@ bmgd/
|
||||||
│ (Uses BMM workflows via cross-module references)
|
│ (Uses BMM workflows via cross-module references)
|
||||||
├── templates/
|
├── templates/
|
||||||
├── data/
|
├── data/
|
||||||
|
├── module.yaml
|
||||||
└── _module-installer/
|
└── _module-installer/
|
||||||
└── install-config.yaml
|
└── installer.js (optional)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ const platformCodes = require(path.join(__dirname, '../../../../tools/cli/lib/pl
|
||||||
*
|
*
|
||||||
* @param {Object} options - Installation options
|
* @param {Object} options - Installation options
|
||||||
* @param {string} options.projectRoot - The root directory of the target project
|
* @param {string} options.projectRoot - The root directory of the target project
|
||||||
* @param {Object} options.config - Module configuration from install-config.yaml
|
* @param {Object} options.config - Module configuration from module.yaml
|
||||||
* @param {Array<string>} options.installedIDEs - Array of IDE codes that were installed
|
* @param {Array<string>} options.installedIDEs - Array of IDE codes that were installed
|
||||||
* @param {Object} options.logger - Logger instance for output
|
* @param {Object} options.logger - Logger instance for output
|
||||||
* @returns {Promise<boolean>} - Success status
|
* @returns {Promise<boolean>} - Success status
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ const chalk = require('chalk');
|
||||||
*
|
*
|
||||||
* @param {Object} options - Installation options
|
* @param {Object} options - Installation options
|
||||||
* @param {string} options.projectRoot - The root directory of the target project
|
* @param {string} options.projectRoot - The root directory of the target project
|
||||||
* @param {Object} options.config - Module configuration from install-config.yaml
|
* @param {Object} options.config - Module configuration from module.yaml
|
||||||
* @param {Object} options.logger - Logger instance for output
|
* @param {Object} options.logger - Logger instance for output
|
||||||
* @param {Object} options.platformInfo - Platform metadata from global config
|
* @param {Object} options.platformInfo - Platform metadata from global config
|
||||||
* @returns {Promise<boolean>} - Success status
|
* @returns {Promise<boolean>} - Success status
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ const chalk = require('chalk');
|
||||||
*
|
*
|
||||||
* @param {Object} options - Installation options
|
* @param {Object} options - Installation options
|
||||||
* @param {string} options.projectRoot - The root directory of the target project
|
* @param {string} options.projectRoot - The root directory of the target project
|
||||||
* @param {Object} options.config - Module configuration from install-config.yaml
|
* @param {Object} options.config - Module configuration from module.yaml
|
||||||
* @param {Object} options.logger - Logger instance for output
|
* @param {Object} options.logger - Logger instance for output
|
||||||
* @returns {Promise<boolean>} - Success status
|
* @returns {Promise<boolean>} - Success status
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ const chalk = require('chalk');
|
||||||
*
|
*
|
||||||
* @param {Object} options - Installation options
|
* @param {Object} options - Installation options
|
||||||
* @param {string} options.projectRoot - The root directory of the target project
|
* @param {string} options.projectRoot - The root directory of the target project
|
||||||
* @param {Object} options.config - Module configuration from install-config.yaml
|
* @param {Object} options.config - Module configuration from module.yaml
|
||||||
* @param {Array<string>} options.installedIDEs - Array of IDE codes that were installed
|
* @param {Array<string>} options.installedIDEs - Array of IDE codes that were installed
|
||||||
* @param {Object} options.logger - Logger instance for output
|
* @param {Object} options.logger - Logger instance for output
|
||||||
* @returns {Promise<boolean>} - Success status
|
* @returns {Promise<boolean>} - Success status
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ The installer is a multi-stage system that handles agent compilation, IDE integr
|
||||||
```
|
```
|
||||||
1. Collect User Input
|
1. Collect User Input
|
||||||
- Target directory, modules, IDEs
|
- Target directory, modules, IDEs
|
||||||
- Custom module configuration (via install-config.yaml)
|
- Custom module configuration (via module.yaml)
|
||||||
|
|
||||||
2. Pre-Installation
|
2. Pre-Installation
|
||||||
- Validate target, check conflicts, backup existing installations
|
- Validate target, check conflicts, backup existing installations
|
||||||
|
|
@ -183,12 +183,12 @@ The installer supports **15 IDE environments** through a base-derived architectu
|
||||||
|
|
||||||
### Custom Module Configuration
|
### Custom Module Configuration
|
||||||
|
|
||||||
Modules define interactive configuration menus via `install-config.yaml` files in their `_module-installer/` directories.
|
Modules define interactive configuration menus via `module.yaml` files in their `_module-installer/` directories.
|
||||||
|
|
||||||
**Config File Location**:
|
**Config File Location**:
|
||||||
|
|
||||||
- Core: `src/core/_module-installer/install-config.yaml`
|
- Core: `src/core/module.yaml`
|
||||||
- Modules: `src/modules/{module}/_module-installer/install-config.yaml`
|
- Modules: `src/modules/{module}/module.yaml`
|
||||||
|
|
||||||
**Configuration Types**:
|
**Configuration Types**:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,24 +183,28 @@ class ConfigCollector {
|
||||||
|
|
||||||
// Load module's install config schema
|
// Load module's install config schema
|
||||||
// First, try the standard src/modules location
|
// First, try the standard src/modules location
|
||||||
let installerConfigPath = path.join(getModulePath(moduleName), '_module-installer', 'install-config.yaml');
|
let installerConfigPath = path.join(getModulePath(moduleName), '_module-installer', 'module.yaml');
|
||||||
|
let moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
|
||||||
|
|
||||||
// If not found in src/modules, we need to find it by searching the project
|
// If not found in src/modules, we need to find it by searching the project
|
||||||
if (!(await fs.pathExists(installerConfigPath))) {
|
if (!(await fs.pathExists(installerConfigPath)) && !(await fs.pathExists(moduleConfigPath))) {
|
||||||
// Use the module manager to find the module source
|
// Use the module manager to find the module source
|
||||||
const { ModuleManager } = require('../modules/manager');
|
const { ModuleManager } = require('../modules/manager');
|
||||||
const moduleManager = new ModuleManager();
|
const moduleManager = new ModuleManager();
|
||||||
const moduleSourcePath = await moduleManager.findModuleSource(moduleName);
|
const moduleSourcePath = await moduleManager.findModuleSource(moduleName);
|
||||||
|
|
||||||
if (moduleSourcePath) {
|
if (moduleSourcePath) {
|
||||||
installerConfigPath = path.join(moduleSourcePath, '_module-installer', 'install-config.yaml');
|
installerConfigPath = path.join(moduleSourcePath, '_module-installer', 'module.yaml');
|
||||||
|
moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let configPath = null;
|
let configPath = null;
|
||||||
let isCustomModule = false;
|
let isCustomModule = false;
|
||||||
|
|
||||||
if (await fs.pathExists(installerConfigPath)) {
|
if (await fs.pathExists(moduleConfigPath)) {
|
||||||
|
configPath = moduleConfigPath;
|
||||||
|
} else if (await fs.pathExists(installerConfigPath)) {
|
||||||
configPath = installerConfigPath;
|
configPath = installerConfigPath;
|
||||||
} else {
|
} else {
|
||||||
// Check if this is a custom module with custom.yaml
|
// Check if this is a custom module with custom.yaml
|
||||||
|
|
@ -448,22 +452,26 @@ class ConfigCollector {
|
||||||
}
|
}
|
||||||
// Load module's config
|
// Load module's config
|
||||||
// First, try the standard src/modules location
|
// First, try the standard src/modules location
|
||||||
let installerConfigPath = path.join(getModulePath(moduleName), '_module-installer', 'install-config.yaml');
|
let installerConfigPath = path.join(getModulePath(moduleName), '_module-installer', 'module.yaml');
|
||||||
|
let moduleConfigPath = path.join(getModulePath(moduleName), 'module.yaml');
|
||||||
|
|
||||||
// If not found in src/modules, we need to find it by searching the project
|
// If not found in src/modules, we need to find it by searching the project
|
||||||
if (!(await fs.pathExists(installerConfigPath))) {
|
if (!(await fs.pathExists(installerConfigPath)) && !(await fs.pathExists(moduleConfigPath))) {
|
||||||
// Use the module manager to find the module source
|
// Use the module manager to find the module source
|
||||||
const { ModuleManager } = require('../modules/manager');
|
const { ModuleManager } = require('../modules/manager');
|
||||||
const moduleManager = new ModuleManager();
|
const moduleManager = new ModuleManager();
|
||||||
const moduleSourcePath = await moduleManager.findModuleSource(moduleName);
|
const moduleSourcePath = await moduleManager.findModuleSource(moduleName);
|
||||||
|
|
||||||
if (moduleSourcePath) {
|
if (moduleSourcePath) {
|
||||||
installerConfigPath = path.join(moduleSourcePath, '_module-installer', 'install-config.yaml');
|
installerConfigPath = path.join(moduleSourcePath, '_module-installer', 'module.yaml');
|
||||||
|
moduleConfigPath = path.join(moduleSourcePath, 'module.yaml');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let configPath = null;
|
let configPath = null;
|
||||||
if (await fs.pathExists(installerConfigPath)) {
|
if (await fs.pathExists(moduleConfigPath)) {
|
||||||
|
configPath = moduleConfigPath;
|
||||||
|
} else if (await fs.pathExists(installerConfigPath)) {
|
||||||
configPath = installerConfigPath;
|
configPath = installerConfigPath;
|
||||||
} else {
|
} else {
|
||||||
// No config for this module
|
// No config for this module
|
||||||
|
|
|
||||||
|
|
@ -753,10 +753,39 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||||
// Resolve dependencies for selected modules
|
// Resolve dependencies for selected modules
|
||||||
spinner.text = 'Resolving dependencies...';
|
spinner.text = 'Resolving dependencies...';
|
||||||
const projectRoot = getProjectRoot();
|
const projectRoot = getProjectRoot();
|
||||||
const modulesToInstall = config.installCore ? ['core', ...config.modules] : config.modules;
|
|
||||||
|
// Add custom content modules to the modules list for installation
|
||||||
|
let allModules = [...(config.modules || [])];
|
||||||
|
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
||||||
|
// Add custom modules to the installation list
|
||||||
|
for (const customFile of config.customContent.selectedFiles) {
|
||||||
|
const { CustomHandler } = require('../custom/handler');
|
||||||
|
const customHandler = new CustomHandler();
|
||||||
|
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
||||||
|
if (customInfo && customInfo.id) {
|
||||||
|
allModules.push(customInfo.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const modulesToInstall = config.installCore ? ['core', ...allModules] : allModules;
|
||||||
|
|
||||||
// For dependency resolution, we need to pass the project root
|
// For dependency resolution, we need to pass the project root
|
||||||
const resolution = await this.dependencyResolver.resolve(projectRoot, config.modules || [], { verbose: config.verbose });
|
// Create a temporary module manager that knows about custom content locations
|
||||||
|
const tempModuleManager = new ModuleManager({
|
||||||
|
scanProjectForModules: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make sure custom modules are discoverable
|
||||||
|
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
||||||
|
// The dependency resolver needs to know about these modules
|
||||||
|
// We'll handle custom modules separately in the installation loop
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolution = await this.dependencyResolver.resolve(projectRoot, allModules, {
|
||||||
|
verbose: config.verbose,
|
||||||
|
moduleManager: tempModuleManager,
|
||||||
|
});
|
||||||
|
|
||||||
if (config.verbose) {
|
if (config.verbose) {
|
||||||
spinner.succeed('Dependencies resolved');
|
spinner.succeed('Dependencies resolved');
|
||||||
|
|
@ -772,16 +801,90 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install modules with their dependencies
|
// Install modules with their dependencies
|
||||||
if (config.modules && config.modules.length > 0) {
|
if (allModules && allModules.length > 0) {
|
||||||
for (const moduleName of config.modules) {
|
const installedModuleNames = new Set();
|
||||||
|
|
||||||
|
for (const moduleName of allModules) {
|
||||||
|
// Skip if already installed
|
||||||
|
if (installedModuleNames.has(moduleName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
installedModuleNames.add(moduleName);
|
||||||
|
|
||||||
spinner.start(`Installing module: ${moduleName}...`);
|
spinner.start(`Installing module: ${moduleName}...`);
|
||||||
await this.installModuleWithDependencies(moduleName, bmadDir, resolution.byModule[moduleName]);
|
|
||||||
|
// Check if this is a custom module
|
||||||
|
let isCustomModule = false;
|
||||||
|
let customInfo = null;
|
||||||
|
if (config.customContent && config.customContent.selected && config.customContent.selectedFiles) {
|
||||||
|
const { CustomHandler } = require('../custom/handler');
|
||||||
|
const customHandler = new CustomHandler();
|
||||||
|
for (const customFile of config.customContent.selectedFiles) {
|
||||||
|
const info = await customHandler.getCustomInfo(customFile, projectDir);
|
||||||
|
if (info && info.id === moduleName) {
|
||||||
|
isCustomModule = true;
|
||||||
|
customInfo = info;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCustomModule && customInfo) {
|
||||||
|
// Install custom module using CustomHandler but as a proper module
|
||||||
|
const { CustomHandler } = require('../custom/handler');
|
||||||
|
const customHandler = new CustomHandler();
|
||||||
|
|
||||||
|
// Install to module directory instead of custom directory
|
||||||
|
const moduleTargetPath = path.join(bmadDir, moduleName);
|
||||||
|
await fs.ensureDir(moduleTargetPath);
|
||||||
|
|
||||||
|
const result = await customHandler.install(
|
||||||
|
customInfo.path,
|
||||||
|
path.join(bmadDir, 'temp-custom'),
|
||||||
|
{ ...config.coreConfig, ...customInfo.config, _bmadDir: bmadDir },
|
||||||
|
(filePath) => {
|
||||||
|
// Track installed files with correct path
|
||||||
|
const relativePath = path.relative(path.join(bmadDir, 'temp-custom'), filePath);
|
||||||
|
const finalPath = path.join(moduleTargetPath, relativePath);
|
||||||
|
this.installedFiles.push(finalPath);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Move from temp-custom to actual module directory
|
||||||
|
const tempCustomPath = path.join(bmadDir, 'temp-custom');
|
||||||
|
if (await fs.pathExists(tempCustomPath)) {
|
||||||
|
const customDir = path.join(tempCustomPath, 'custom');
|
||||||
|
if (await fs.pathExists(customDir)) {
|
||||||
|
// Move contents to module directory
|
||||||
|
const items = await fs.readdir(customDir);
|
||||||
|
for (const item of items) {
|
||||||
|
const srcPath = path.join(customDir, item);
|
||||||
|
const destPath = path.join(moduleTargetPath, item);
|
||||||
|
|
||||||
|
// If destination exists, remove it first (or we could merge)
|
||||||
|
if (await fs.pathExists(destPath)) {
|
||||||
|
await fs.remove(destPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.move(srcPath, destPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await fs.remove(tempCustomPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create module config
|
||||||
|
await this.generateModuleConfigs(bmadDir, { [moduleName]: { ...config.coreConfig, ...customInfo.config } });
|
||||||
|
} else {
|
||||||
|
// Regular module installation
|
||||||
|
await this.installModuleWithDependencies(moduleName, bmadDir, resolution.byModule[moduleName]);
|
||||||
|
}
|
||||||
|
|
||||||
spinner.succeed(`Module installed: ${moduleName}`);
|
spinner.succeed(`Module installed: ${moduleName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install partial modules (only dependencies)
|
// Install partial modules (only dependencies)
|
||||||
for (const [module, files] of Object.entries(resolution.byModule)) {
|
for (const [module, files] of Object.entries(resolution.byModule)) {
|
||||||
if (!config.modules.includes(module) && module !== 'core') {
|
if (!allModules.includes(module) && module !== 'core') {
|
||||||
const totalFiles =
|
const totalFiles =
|
||||||
files.agents.length +
|
files.agents.length +
|
||||||
files.tasks.length +
|
files.tasks.length +
|
||||||
|
|
@ -799,6 +902,11 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install custom content if provided AND selected
|
// Install custom content if provided AND selected
|
||||||
|
// Process custom content that wasn't installed as modules
|
||||||
|
// This is now handled in the module installation loop above
|
||||||
|
// This section is kept for backward compatibility with any custom content
|
||||||
|
// that doesn't have a module structure
|
||||||
|
const remainingCustomContent = [];
|
||||||
if (
|
if (
|
||||||
config.customContent &&
|
config.customContent &&
|
||||||
config.customContent.hasCustomContent &&
|
config.customContent.hasCustomContent &&
|
||||||
|
|
@ -806,12 +914,26 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||||
config.customContent.selected &&
|
config.customContent.selected &&
|
||||||
config.customContent.selectedFiles
|
config.customContent.selectedFiles
|
||||||
) {
|
) {
|
||||||
spinner.start('Installing custom content...');
|
// Filter out custom modules that were already installed
|
||||||
|
for (const customFile of config.customContent.selectedFiles) {
|
||||||
|
const { CustomHandler } = require('../custom/handler');
|
||||||
|
const customHandler = new CustomHandler();
|
||||||
|
const customInfo = await customHandler.getCustomInfo(customFile, projectDir);
|
||||||
|
|
||||||
|
// Skip if this was installed as a module
|
||||||
|
if (!customInfo || !customInfo.id || !allModules.includes(customInfo.id)) {
|
||||||
|
remainingCustomContent.push(customFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingCustomContent.length > 0) {
|
||||||
|
spinner.start('Installing remaining custom content...');
|
||||||
const { CustomHandler } = require('../custom/handler');
|
const { CustomHandler } = require('../custom/handler');
|
||||||
const customHandler = new CustomHandler();
|
const customHandler = new CustomHandler();
|
||||||
|
|
||||||
// Use the selected files instead of finding all files
|
// Use the remaining files
|
||||||
const customFiles = config.customContent.selectedFiles;
|
const customFiles = remainingCustomContent;
|
||||||
|
|
||||||
if (customFiles.length > 0) {
|
if (customFiles.length > 0) {
|
||||||
console.log(chalk.cyan(`\n Found ${customFiles.length} custom content file(s):`));
|
console.log(chalk.cyan(`\n Found ${customFiles.length} custom content file(s):`));
|
||||||
|
|
@ -867,10 +989,10 @@ If AgentVibes party mode is enabled, immediately trigger TTS with agent's voice:
|
||||||
spinner.start('Generating workflow and agent manifests...');
|
spinner.start('Generating workflow and agent manifests...');
|
||||||
const manifestGen = new ManifestGenerator();
|
const manifestGen = new ManifestGenerator();
|
||||||
|
|
||||||
// Include preserved modules (from quick update) in the manifest
|
// Include preserved modules (from quick update) and custom modules in the manifest
|
||||||
const allModulesToList = config._preserveModules ? [...(config.modules || []), ...config._preserveModules] : config.modules || [];
|
const allModulesToList = config._preserveModules ? [...allModules, ...config._preserveModules] : allModules || [];
|
||||||
|
|
||||||
const manifestStats = await manifestGen.generateManifests(bmadDir, config.modules || [], this.installedFiles, {
|
const manifestStats = await manifestGen.generateManifests(bmadDir, allModulesToList, this.installedFiles, {
|
||||||
ides: config.ides || [],
|
ides: config.ides || [],
|
||||||
preservedModules: config._preserveModules || [], // Scan these from installed bmad/ dir
|
preservedModules: config._preserveModules || [], // Scan these from installed bmad/ dir
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ const fs = require('fs-extra');
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const yaml = require('js-yaml');
|
const yaml = require('js-yaml');
|
||||||
const { FileOps } = require('../../../lib/file-ops');
|
const { FileOps } = require('../../../lib/file-ops');
|
||||||
|
const { XmlHandler } = require('../../../lib/xml-handler');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for custom content (custom.yaml)
|
* Handler for custom content (custom.yaml)
|
||||||
|
|
@ -11,6 +12,7 @@ const { FileOps } = require('../../../lib/file-ops');
|
||||||
class CustomHandler {
|
class CustomHandler {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.fileOps = new FileOps();
|
this.fileOps = new FileOps();
|
||||||
|
this.xmlHandler = new XmlHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -52,6 +54,12 @@ class CustomHandler {
|
||||||
} else if (entry.name === 'custom.yaml') {
|
} else if (entry.name === 'custom.yaml') {
|
||||||
// Found a custom.yaml file
|
// Found a custom.yaml file
|
||||||
customPaths.push(fullPath);
|
customPaths.push(fullPath);
|
||||||
|
} else if (
|
||||||
|
entry.name === 'module.yaml' && // Check if this is a custom module (either in _module-installer or in root directory)
|
||||||
|
// Skip if it's in src/modules (those are standard modules)
|
||||||
|
!fullPath.includes(path.join('src', 'modules'))
|
||||||
|
) {
|
||||||
|
customPaths.push(fullPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -66,40 +74,44 @@ class CustomHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get custom content info from a custom.yaml file
|
* Get custom content info from a custom.yaml or module.yaml file
|
||||||
* @param {string} customYamlPath - Path to custom.yaml file
|
* @param {string} configPath - Path to config file
|
||||||
* @param {string} projectRoot - Project root directory for calculating relative paths
|
* @param {string} projectRoot - Project root directory for calculating relative paths
|
||||||
* @returns {Object|null} Custom content info
|
* @returns {Object|null} Custom content info
|
||||||
*/
|
*/
|
||||||
async getCustomInfo(customYamlPath, projectRoot = null) {
|
async getCustomInfo(configPath, projectRoot = null) {
|
||||||
try {
|
try {
|
||||||
const configContent = await fs.readFile(customYamlPath, 'utf8');
|
const configContent = await fs.readFile(configPath, 'utf8');
|
||||||
|
|
||||||
// Try to parse YAML with error handling
|
// Try to parse YAML with error handling
|
||||||
let config;
|
let config;
|
||||||
try {
|
try {
|
||||||
config = yaml.load(configContent);
|
config = yaml.load(configContent);
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
console.warn(chalk.yellow(`Warning: YAML parse error in ${customYamlPath}:`, parseError.message));
|
console.warn(chalk.yellow(`Warning: YAML parse error in ${configPath}:`, parseError.message));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const customDir = path.dirname(customYamlPath);
|
// Check if this is an module.yaml (module) or custom.yaml (custom content)
|
||||||
|
const isInstallConfig = configPath.endsWith('module.yaml');
|
||||||
|
const configDir = path.dirname(configPath);
|
||||||
|
|
||||||
// Use provided projectRoot or fall back to process.cwd()
|
// Use provided projectRoot or fall back to process.cwd()
|
||||||
const basePath = projectRoot || process.cwd();
|
const basePath = projectRoot || process.cwd();
|
||||||
const relativePath = path.relative(basePath, customDir);
|
const relativePath = path.relative(basePath, configDir);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: config.code || path.basename(customDir),
|
id: config.code || 'unknown-code',
|
||||||
name: config.name || `Custom: ${path.basename(customDir)}`,
|
name: config.name,
|
||||||
description: config.description || 'Custom agents and workflows',
|
description: config.description || '',
|
||||||
path: customDir,
|
path: configDir,
|
||||||
relativePath: relativePath,
|
relativePath: relativePath,
|
||||||
defaultSelected: config.default_selected === true,
|
defaultSelected: config.default_selected === true,
|
||||||
config: config,
|
config: config,
|
||||||
|
isInstallConfig: isInstallConfig, // Track which type this is
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(chalk.yellow(`Warning: Failed to read ${customYamlPath}:`, error.message));
|
console.warn(chalk.yellow(`Warning: Failed to read ${configPath}:`, error.message));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,10 +143,10 @@ class CustomHandler {
|
||||||
await fs.ensureDir(bmadAgentsDir);
|
await fs.ensureDir(bmadAgentsDir);
|
||||||
await fs.ensureDir(bmadWorkflowsDir);
|
await fs.ensureDir(bmadWorkflowsDir);
|
||||||
|
|
||||||
// Process agents - copy entire agents directory structure
|
// Process agents - compile and copy agents
|
||||||
const agentsDir = path.join(customPath, 'agents');
|
const agentsDir = path.join(customPath, 'agents');
|
||||||
if (await fs.pathExists(agentsDir)) {
|
if (await fs.pathExists(agentsDir)) {
|
||||||
await this.copyDirectory(agentsDir, bmadAgentsDir, results, fileTrackingCallback, config);
|
await this.compileAndCopyAgents(agentsDir, bmadAgentsDir, bmadDir, config, fileTrackingCallback, results);
|
||||||
|
|
||||||
// Count agent files
|
// Count agent files
|
||||||
const agentFiles = await this.findFilesRecursively(agentsDir, ['.agent.yaml', '.md']);
|
const agentFiles = await this.findFilesRecursively(agentsDir, ['.agent.yaml', '.md']);
|
||||||
|
|
@ -271,6 +283,114 @@ class CustomHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile .agent.yaml files to .md format and handle sidecars
|
||||||
|
* @param {string} sourceAgentsPath - Source agents directory
|
||||||
|
* @param {string} targetAgentsPath - Target agents directory
|
||||||
|
* @param {string} bmadDir - BMAD installation directory
|
||||||
|
* @param {Object} config - Configuration for placeholder replacement
|
||||||
|
* @param {Function} fileTrackingCallback - Optional callback to track installed files
|
||||||
|
* @param {Object} results - Results object to update
|
||||||
|
*/
|
||||||
|
async compileAndCopyAgents(sourceAgentsPath, targetAgentsPath, bmadDir, config, fileTrackingCallback, results) {
|
||||||
|
// Get all .agent.yaml files recursively
|
||||||
|
const agentFiles = await this.findFilesRecursively(sourceAgentsPath, ['.agent.yaml']);
|
||||||
|
|
||||||
|
for (const agentFile of agentFiles) {
|
||||||
|
const relativePath = path.relative(sourceAgentsPath, agentFile);
|
||||||
|
const targetDir = path.join(targetAgentsPath, path.dirname(relativePath));
|
||||||
|
|
||||||
|
await fs.ensureDir(targetDir);
|
||||||
|
|
||||||
|
const agentName = path.basename(agentFile, '.agent.yaml');
|
||||||
|
const targetMdPath = path.join(targetDir, `${agentName}.md`);
|
||||||
|
// Use the actual bmadDir if available (for when installing to temp dir)
|
||||||
|
const actualBmadDir = config._bmadDir || bmadDir;
|
||||||
|
const customizePath = path.join(actualBmadDir, '_cfg', 'agents', `custom-${agentName}.customize.yaml`);
|
||||||
|
|
||||||
|
// Read and compile the YAML
|
||||||
|
try {
|
||||||
|
const yamlContent = await fs.readFile(agentFile, 'utf8');
|
||||||
|
const { compileAgent } = require('../../../lib/agent/compiler');
|
||||||
|
|
||||||
|
// Create customize template if it doesn't exist
|
||||||
|
if (!(await fs.pathExists(customizePath))) {
|
||||||
|
const { getSourcePath } = require('../../../lib/project-root');
|
||||||
|
const genericTemplatePath = getSourcePath('utility', 'templates', 'agent.customize.template.yaml');
|
||||||
|
if (await fs.pathExists(genericTemplatePath)) {
|
||||||
|
// Copy with placeholder replacement
|
||||||
|
let templateContent = await fs.readFile(genericTemplatePath, 'utf8');
|
||||||
|
templateContent = templateContent.replaceAll('{bmad_folder}', config.bmad_folder || 'bmad');
|
||||||
|
await fs.writeFile(customizePath, templateContent, 'utf8');
|
||||||
|
console.log(chalk.dim(` Created customize: custom-${agentName}.customize.yaml`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile the agent
|
||||||
|
const { xml } = compileAgent(yamlContent, {}, agentName, relativePath, { config });
|
||||||
|
|
||||||
|
// Replace placeholders in the compiled content
|
||||||
|
let processedXml = xml;
|
||||||
|
processedXml = processedXml.replaceAll('{bmad_folder}', config.bmad_folder || 'bmad');
|
||||||
|
processedXml = processedXml.replaceAll('{user_name}', config.user_name || 'User');
|
||||||
|
processedXml = processedXml.replaceAll('{communication_language}', config.communication_language || 'English');
|
||||||
|
processedXml = processedXml.replaceAll('{output_folder}', config.output_folder || 'docs');
|
||||||
|
|
||||||
|
// Write the compiled MD file
|
||||||
|
await fs.writeFile(targetMdPath, processedXml, 'utf8');
|
||||||
|
|
||||||
|
// Check if agent has sidecar
|
||||||
|
let hasSidecar = false;
|
||||||
|
try {
|
||||||
|
const yamlLib = require('yaml');
|
||||||
|
const agentYaml = yamlLib.parse(yamlContent);
|
||||||
|
hasSidecar = agentYaml?.agent?.metadata?.hasSidecar === true;
|
||||||
|
} catch {
|
||||||
|
// Continue without sidecar processing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy sidecar files if agent has hasSidecar flag
|
||||||
|
if (hasSidecar && config.agent_sidecar_folder) {
|
||||||
|
const { copyAgentSidecarFiles } = require('../../../lib/agent/installer');
|
||||||
|
|
||||||
|
// Resolve agent sidecar folder path
|
||||||
|
const projectDir = path.dirname(bmadDir);
|
||||||
|
const resolvedSidecarFolder = config.agent_sidecar_folder
|
||||||
|
.replaceAll('{project-root}', projectDir)
|
||||||
|
.replaceAll('{bmad_folder}', path.basename(bmadDir));
|
||||||
|
|
||||||
|
// Create sidecar directory for this agent
|
||||||
|
const agentSidecarDir = path.join(resolvedSidecarFolder, agentName);
|
||||||
|
await fs.ensureDir(agentSidecarDir);
|
||||||
|
|
||||||
|
// Copy sidecar files
|
||||||
|
const sidecarResult = copyAgentSidecarFiles(path.dirname(agentFile), agentSidecarDir, agentFile);
|
||||||
|
|
||||||
|
if (sidecarResult.copied.length > 0) {
|
||||||
|
console.log(chalk.dim(` Copied ${sidecarResult.copied.length} sidecar file(s) to: ${agentSidecarDir}`));
|
||||||
|
}
|
||||||
|
if (sidecarResult.preserved.length > 0) {
|
||||||
|
console.log(chalk.dim(` Preserved ${sidecarResult.preserved.length} existing sidecar file(s)`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track the file
|
||||||
|
if (fileTrackingCallback) {
|
||||||
|
fileTrackingCallback(targetMdPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
chalk.dim(
|
||||||
|
` Compiled agent: ${agentName} -> ${path.relative(targetAgentsPath, targetMdPath)}${hasSidecar ? ' (with sidecar)' : ''}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(chalk.yellow(` Failed to compile agent ${agentName}:`, error.message));
|
||||||
|
results.errors.push(`Failed to compile agent ${agentName}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { CustomHandler };
|
module.exports = { CustomHandler };
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ class ModuleManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all modules in the project by searching for install-config.yaml files
|
* Find all modules in the project by searching for module.yaml files
|
||||||
* @returns {Array} List of module paths
|
* @returns {Array} List of module paths
|
||||||
*/
|
*/
|
||||||
async findModulesInProject() {
|
async findModulesInProject() {
|
||||||
|
|
@ -144,12 +144,14 @@ class ModuleManager {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this directory contains a module (install-config.yaml OR custom.yaml)
|
// Check if this directory contains a module (module.yaml OR custom.yaml)
|
||||||
const installerConfigPath = path.join(fullPath, '_module-installer', 'install-config.yaml');
|
const moduleConfigPath = path.join(fullPath, 'module.yaml');
|
||||||
|
const installerConfigPath = path.join(fullPath, '_module-installer', 'module.yaml');
|
||||||
const customConfigPath = path.join(fullPath, '_module-installer', 'custom.yaml');
|
const customConfigPath = path.join(fullPath, '_module-installer', 'custom.yaml');
|
||||||
const rootCustomConfigPath = path.join(fullPath, 'custom.yaml');
|
const rootCustomConfigPath = path.join(fullPath, 'custom.yaml');
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
(await fs.pathExists(moduleConfigPath)) ||
|
||||||
(await fs.pathExists(installerConfigPath)) ||
|
(await fs.pathExists(installerConfigPath)) ||
|
||||||
(await fs.pathExists(customConfigPath)) ||
|
(await fs.pathExists(customConfigPath)) ||
|
||||||
(await fs.pathExists(rootCustomConfigPath))
|
(await fs.pathExists(rootCustomConfigPath))
|
||||||
|
|
@ -189,12 +191,17 @@ class ModuleManager {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
const modulePath = path.join(this.modulesSourcePath, entry.name);
|
const modulePath = path.join(this.modulesSourcePath, entry.name);
|
||||||
// Check for module structure (install-config.yaml OR custom.yaml)
|
// Check for module structure (module.yaml OR custom.yaml)
|
||||||
const installerConfigPath = path.join(modulePath, '_module-installer', 'install-config.yaml');
|
const moduleConfigPath = path.join(modulePath, 'module.yaml');
|
||||||
|
const installerConfigPath = path.join(modulePath, '_module-installer', 'module.yaml');
|
||||||
const customConfigPath = path.join(modulePath, '_module-installer', 'custom.yaml');
|
const customConfigPath = path.join(modulePath, '_module-installer', 'custom.yaml');
|
||||||
|
|
||||||
// Skip if this doesn't look like a module
|
// Skip if this doesn't look like a module
|
||||||
if (!(await fs.pathExists(installerConfigPath)) && !(await fs.pathExists(customConfigPath))) {
|
if (
|
||||||
|
!(await fs.pathExists(moduleConfigPath)) &&
|
||||||
|
!(await fs.pathExists(installerConfigPath)) &&
|
||||||
|
!(await fs.pathExists(customConfigPath))
|
||||||
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,13 +253,16 @@ class ModuleManager {
|
||||||
* @returns {Object|null} Module info or null if not a valid module
|
* @returns {Object|null} Module info or null if not a valid module
|
||||||
*/
|
*/
|
||||||
async getModuleInfo(modulePath, defaultName, sourceDescription) {
|
async getModuleInfo(modulePath, defaultName, sourceDescription) {
|
||||||
// Check for module structure (install-config.yaml OR custom.yaml)
|
// Check for module structure (module.yaml OR custom.yaml)
|
||||||
const installerConfigPath = path.join(modulePath, '_module-installer', 'install-config.yaml');
|
const moduleConfigPath = path.join(modulePath, 'module.yaml');
|
||||||
|
const installerConfigPath = path.join(modulePath, '_module-installer', 'module.yaml');
|
||||||
const customConfigPath = path.join(modulePath, '_module-installer', 'custom.yaml');
|
const customConfigPath = path.join(modulePath, '_module-installer', 'custom.yaml');
|
||||||
const rootCustomConfigPath = path.join(modulePath, 'custom.yaml');
|
const rootCustomConfigPath = path.join(modulePath, 'custom.yaml');
|
||||||
let configPath = null;
|
let configPath = null;
|
||||||
|
|
||||||
if (await fs.pathExists(installerConfigPath)) {
|
if (await fs.pathExists(moduleConfigPath)) {
|
||||||
|
configPath = moduleConfigPath;
|
||||||
|
} else if (await fs.pathExists(installerConfigPath)) {
|
||||||
configPath = installerConfigPath;
|
configPath = installerConfigPath;
|
||||||
} else if (await fs.pathExists(customConfigPath)) {
|
} else if (await fs.pathExists(customConfigPath)) {
|
||||||
configPath = customConfigPath;
|
configPath = customConfigPath;
|
||||||
|
|
@ -313,10 +323,11 @@ class ModuleManager {
|
||||||
// First, check src/modules
|
// First, check src/modules
|
||||||
const srcModulePath = path.join(this.modulesSourcePath, moduleName);
|
const srcModulePath = path.join(this.modulesSourcePath, moduleName);
|
||||||
if (await fs.pathExists(srcModulePath)) {
|
if (await fs.pathExists(srcModulePath)) {
|
||||||
// Check if this looks like a module (has install-config.yaml)
|
// Check if this looks like a module (has module.yaml)
|
||||||
const installerConfigPath = path.join(srcModulePath, '_module-installer', 'install-config.yaml');
|
const moduleConfigPath = path.join(srcModulePath, 'module.yaml');
|
||||||
|
const installerConfigPath = path.join(srcModulePath, '_module-installer', 'module.yaml');
|
||||||
|
|
||||||
if (await fs.pathExists(installerConfigPath)) {
|
if ((await fs.pathExists(moduleConfigPath)) || (await fs.pathExists(installerConfigPath))) {
|
||||||
return srcModulePath;
|
return srcModulePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,12 +349,15 @@ class ModuleManager {
|
||||||
// Also check by module ID (not just folder name)
|
// Also check by module ID (not just folder name)
|
||||||
// Need to read configs to match by ID
|
// Need to read configs to match by ID
|
||||||
for (const modulePath of allModulePaths) {
|
for (const modulePath of allModulePaths) {
|
||||||
const installerConfigPath = path.join(modulePath, '_module-installer', 'install-config.yaml');
|
const moduleConfigPath = path.join(modulePath, 'module.yaml');
|
||||||
|
const installerConfigPath = path.join(modulePath, '_module-installer', 'module.yaml');
|
||||||
const customConfigPath = path.join(modulePath, '_module-installer', 'custom.yaml');
|
const customConfigPath = path.join(modulePath, '_module-installer', 'custom.yaml');
|
||||||
const rootCustomConfigPath = path.join(modulePath, 'custom.yaml');
|
const rootCustomConfigPath = path.join(modulePath, 'custom.yaml');
|
||||||
|
|
||||||
let configPath = null;
|
let configPath = null;
|
||||||
if (await fs.pathExists(installerConfigPath)) {
|
if (await fs.pathExists(moduleConfigPath)) {
|
||||||
|
configPath = moduleConfigPath;
|
||||||
|
} else if (await fs.pathExists(installerConfigPath)) {
|
||||||
configPath = installerConfigPath;
|
configPath = installerConfigPath;
|
||||||
} else if (await fs.pathExists(customConfigPath)) {
|
} else if (await fs.pathExists(customConfigPath)) {
|
||||||
configPath = customConfigPath;
|
configPath = customConfigPath;
|
||||||
|
|
@ -584,7 +598,7 @@ class ModuleManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip _module-installer directory - it's only needed at install time
|
// Skip _module-installer directory - it's only needed at install time
|
||||||
if (file.startsWith('_module-installer/')) {
|
if (file.startsWith('_module-installer/') || file === 'module.yaml') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@ const CLIUtils = {
|
||||||
/**
|
/**
|
||||||
* Display module configuration header
|
* Display module configuration header
|
||||||
* @param {string} moduleName - Module name (fallback if no custom header)
|
* @param {string} moduleName - Module name (fallback if no custom header)
|
||||||
* @param {string} header - Custom header from install-config.yaml
|
* @param {string} header - Custom header from module.yaml
|
||||||
* @param {string} subheader - Custom subheader from install-config.yaml
|
* @param {string} subheader - Custom subheader from module.yaml
|
||||||
*/
|
*/
|
||||||
displayModuleConfigHeader(moduleName, header = null, subheader = null) {
|
displayModuleConfigHeader(moduleName, header = null, subheader = null) {
|
||||||
// Simple blue banner with custom header/subheader if provided
|
// Simple blue banner with custom header/subheader if provided
|
||||||
|
|
@ -100,8 +100,8 @@ const CLIUtils = {
|
||||||
/**
|
/**
|
||||||
* Display module with no custom configuration
|
* Display module with no custom configuration
|
||||||
* @param {string} moduleName - Module name (fallback if no custom header)
|
* @param {string} moduleName - Module name (fallback if no custom header)
|
||||||
* @param {string} header - Custom header from install-config.yaml
|
* @param {string} header - Custom header from module.yaml
|
||||||
* @param {string} subheader - Custom subheader from install-config.yaml
|
* @param {string} subheader - Custom subheader from module.yaml
|
||||||
*/
|
*/
|
||||||
displayModuleNoConfig(moduleName, header = null, subheader = null) {
|
displayModuleNoConfig(moduleName, header = null, subheader = null) {
|
||||||
// Show full banner with header/subheader, just like modules with config
|
// Show full banner with header/subheader, just like modules with config
|
||||||
|
|
|
||||||
|
|
@ -142,8 +142,19 @@ class UI {
|
||||||
if (selectedCustomContent.length > 0) {
|
if (selectedCustomContent.length > 0) {
|
||||||
customContentConfig.selected = true;
|
customContentConfig.selected = true;
|
||||||
customContentConfig.selectedFiles = selectedCustomContent.map((mod) => mod.replace('__CUSTOM_CONTENT__', ''));
|
customContentConfig.selectedFiles = selectedCustomContent.map((mod) => mod.replace('__CUSTOM_CONTENT__', ''));
|
||||||
// Filter out custom content markers since they're not real modules
|
// Convert custom content to module IDs for installation
|
||||||
selectedModules = selectedModules.filter((mod) => !mod.startsWith('__CUSTOM_CONTENT__'));
|
const customContentModuleIds = [];
|
||||||
|
const { CustomHandler } = require('../installers/lib/custom/handler');
|
||||||
|
const customHandler = new CustomHandler();
|
||||||
|
for (const customFile of customContentConfig.selectedFiles) {
|
||||||
|
// Get the module info to extract the ID
|
||||||
|
const customInfo = await customHandler.getCustomInfo(customFile);
|
||||||
|
if (customInfo) {
|
||||||
|
customContentModuleIds.push(customInfo.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Filter out custom content markers and add module IDs
|
||||||
|
selectedModules = [...selectedModules.filter((mod) => !mod.startsWith('__CUSTOM_CONTENT__')), ...customContentModuleIds];
|
||||||
} else if (customContentConfig.hasCustomContent) {
|
} else if (customContentConfig.hasCustomContent) {
|
||||||
// User provided custom content but didn't select any
|
// User provided custom content but didn't select any
|
||||||
customContentConfig.selected = false;
|
customContentConfig.selected = false;
|
||||||
|
|
@ -669,7 +680,7 @@ class UI {
|
||||||
*/
|
*/
|
||||||
async promptCustomContentLocation() {
|
async promptCustomContentLocation() {
|
||||||
try {
|
try {
|
||||||
CLIUtils.displaySection('Custom Content', 'Optional: Add custom agents and workflows');
|
CLIUtils.displaySection('Custom Content', 'Optional: Add custom agents, workflows, and modules');
|
||||||
|
|
||||||
const { hasCustomContent } = await inquirer.prompt([
|
const { hasCustomContent } = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
|
|
@ -703,7 +714,7 @@ class UI {
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'directory',
|
name: 'directory',
|
||||||
message: 'Enter the path to your custom content directory:',
|
message: 'Enter directory to search for custom content (will scan subfolders):',
|
||||||
default: process.cwd(), // Use actual current working directory
|
default: process.cwd(), // Use actual current working directory
|
||||||
validate: async (input) => {
|
validate: async (input) => {
|
||||||
if (!input || input.trim() === '') {
|
if (!input || input.trim() === '') {
|
||||||
|
|
@ -736,7 +747,7 @@ class UI {
|
||||||
const customFiles = await customHandler.findCustomContent(expandedPath);
|
const customFiles = await customHandler.findCustomContent(expandedPath);
|
||||||
|
|
||||||
if (customFiles.length === 0) {
|
if (customFiles.length === 0) {
|
||||||
console.log(chalk.yellow(`\nNo custom.yaml files found in ${expandedPath}`));
|
console.log(chalk.yellow(`\nNo custom content found in ${expandedPath}`));
|
||||||
|
|
||||||
const { tryAgain } = await inquirer.prompt([
|
const { tryAgain } = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
|
|
@ -755,7 +766,12 @@ class UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
customPath = expandedPath;
|
customPath = expandedPath;
|
||||||
console.log(chalk.green(`\n✓ Found ${customFiles.length} custom content file(s)`));
|
console.log(chalk.green(`\n✓ Found ${customFiles.length} custom content item(s):`));
|
||||||
|
for (const file of customFiles) {
|
||||||
|
const relativePath = path.relative(expandedPath, path.dirname(file));
|
||||||
|
const folderName = path.dirname(file).split(path.sep).pop();
|
||||||
|
console.log(chalk.dim(` • ${folderName} ${chalk.gray(`(${relativePath})`)}`));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { hasCustomContent: true, customPath };
|
return { hasCustomContent: true, customPath };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue