Major Enhancements:

- Installation path is now fully configurable, allowing users to specify custom installation directories during setup
  - Default installation location changed to .bmad (hidden directory) for cleaner project root organization

    Web Bundle Improvements:

    - All web bundles (single agent and team) now include party mode support for multi-agent collaboration!
    - Advanced elicitation capabilities integrated into standalone agents
    - All bundles enhanced with party mode agent manifests
    - Added default-party.csv files to bmm, bmgd, and cis module teams
    - The default party file is what will be used with single agent bundles. teams can customize for different party configurations before web bundling through a setting in the team yaml file
    - New web bundle outputs for all agents (analyst, architect, dev, pm, sm, tea, tech-writer, ux-designer, game-*, creative-squad)

    Phase 4 Workflow Updates (In Progress):

    - Initiated shift to separate phase 4 implementation artifacts from documentation
        - Phase 4 implementation artifacts (stories, code review, sprint plan, context files) will move to dedicated location outside docs folder
        - Installer questions and configuration added for artifact path selection
        - Updated workflow.yaml files for code-review, sprint-planning, story-context, epic-tech-context, and retrospective workflows to support this, but still might require some udpates

    Additional Changes:

    - New agent and action command header models for standardization
    - Enhanced web-bundle-activation-steps fragment
    - Updated web-bundler.js to support new structure
    - VS Code settings updated for new .bmad directory
    - Party mode instructions and workflow enhanced for better orchestration

   IDE Installer Updates:

    - Show version number of installer in cli
    - improved Installer UX
    - Gemini TOML Improved to have clear loading instructions with @ commands
    - All tools agent launcher mds improved to use a central file template critical indication isntead of hardcoding in 2 different locations.
This commit is contained in:
Brian Madison
2025-11-09 17:39:05 -06:00
parent fd2521ec69
commit 7eb52520fa
433 changed files with 125975 additions and 689 deletions

View File

@@ -13,6 +13,41 @@ class ConfigCollector {
this.currentProjectDir = null;
}
/**
* Find the bmad installation directory in a project
* V6+ installations can use ANY folder name but ALWAYS have _cfg/manifest.yaml
* @param {string} projectDir - Project directory
* @returns {Promise<string>} Path to bmad directory
*/
async findBmadDir(projectDir) {
// Check if project directory exists
if (!(await fs.pathExists(projectDir))) {
// Project doesn't exist yet, return default
return path.join(projectDir, 'bmad');
}
// V6+ strategy: Look for ANY directory with _cfg/manifest.yaml
// This is the definitive marker of a V6+ installation
try {
const entries = await fs.readdir(projectDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
const manifestPath = path.join(projectDir, entry.name, '_cfg', 'manifest.yaml');
if (await fs.pathExists(manifestPath)) {
// Found a V6+ installation
return path.join(projectDir, entry.name);
}
}
}
} catch {
// Ignore errors, fall through to default
}
// No V6+ installation found, return default
// This will be used for new installations
return path.join(projectDir, 'bmad');
}
/**
* Load existing config if it exists from module config files
* @param {string} projectDir - Target project directory
@@ -25,7 +60,8 @@ class ConfigCollector {
return false;
}
const bmadDir = path.join(projectDir, 'bmad');
// Find the actual bmad directory (handles custom folder names)
const bmadDir = await this.findBmadDir(projectDir);
// Check if bmad directory exists
if (!(await fs.pathExists(bmadDir))) {
@@ -165,17 +201,13 @@ class ConfigCollector {
this.allAnswers[`${moduleName}_${key}`] = value;
}
}
// Show "no config" message for modules with no new questions
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
return false; // No new fields
}
// If we have new fields, show prompt section and collect only new fields
// If we have new fields, build questions first
if (newKeys.length > 0) {
console.log(chalk.yellow(`\n📋 New configuration options available for ${moduleName}`));
if (moduleConfig.prompt) {
const prompts = Array.isArray(moduleConfig.prompt) ? moduleConfig.prompt : [moduleConfig.prompt];
CLIUtils.displayPromptSection(prompts);
}
const questions = [];
for (const key of newKeys) {
const item = moduleConfig[key];
@@ -186,6 +218,8 @@ class ConfigCollector {
}
if (questions.length > 0) {
// Only show header if we actually have questions
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
console.log(); // Line break before questions
const answers = await inquirer.prompt(questions);
@@ -212,6 +246,9 @@ class ConfigCollector {
}
this.collectedConfig[moduleName][originalKey] = result;
}
} else {
// New keys exist but no questions generated - show no config message
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
}
}
@@ -331,12 +368,6 @@ class ConfigCollector {
return;
}
// Display module prompts using better formatting
if (moduleConfig.prompt) {
const prompts = Array.isArray(moduleConfig.prompt) ? moduleConfig.prompt : [moduleConfig.prompt];
CLIUtils.displayPromptSection(prompts);
}
// Process each config item
const questions = [];
const configKeys = Object.keys(moduleConfig).filter((key) => key !== 'prompt');
@@ -355,7 +386,9 @@ class ConfigCollector {
}
}
// Display appropriate header based on whether there are questions
if (questions.length > 0) {
CLIUtils.displayModuleConfigHeader(moduleName, moduleConfig.header, moduleConfig.subheader);
console.log(); // Line break before questions
const answers = await inquirer.prompt(questions);
@@ -456,10 +489,10 @@ class ConfigCollector {
this.collectedConfig[moduleName][originalKey] = result;
}
// Display module completion message after collecting all answers (unless skipped)
if (!skipCompletion) {
CLIUtils.displayModuleComplete(moduleName);
}
// No longer display completion boxes - keep output clean
} else {
// No questions for this module - show completion message
CLIUtils.displayModuleNoConfig(moduleName, moduleConfig.header, moduleConfig.subheader);
}
}

View File

@@ -39,6 +39,7 @@ class ManifestGenerator {
this.updatedModules = [...new Set(['core', ...selectedModules])]; // Only these get rescanned
this.preservedModules = preservedModules; // These stay as-is in CSVs
this.bmadDir = bmadDir;
this.bmadFolderName = path.basename(bmadDir); // Get the actual folder name (e.g., '.bmad' or 'bmad')
this.allInstalledFiles = installedFiles;
if (!Object.prototype.hasOwnProperty.call(options, 'ides')) {
@@ -140,8 +141,8 @@ class ManifestGenerator {
// Build relative path for installation
const installPath =
moduleName === 'core'
? `bmad/core/workflows/${relativePath}/workflow.yaml`
: `bmad/${moduleName}/workflows/${relativePath}/workflow.yaml`;
? `${this.bmadFolderName}/core/workflows/${relativePath}/workflow.yaml`
: `${this.bmadFolderName}/${moduleName}/workflows/${relativePath}/workflow.yaml`;
// Check for standalone property (default: false)
const standalone = workflow.standalone === true;
@@ -241,7 +242,8 @@ class ManifestGenerator {
const principlesMatch = content.match(/<principles>([\s\S]*?)<\/principles>/);
// Build relative path for installation
const installPath = moduleName === 'core' ? `bmad/core/agents/${file}` : `bmad/${moduleName}/agents/${file}`;
const installPath =
moduleName === 'core' ? `${this.bmadFolderName}/core/agents/${file}` : `${this.bmadFolderName}/${moduleName}/agents/${file}`;
const agentName = file.replace('.md', '');
@@ -324,7 +326,8 @@ class ManifestGenerator {
const standalone = !!standaloneMatch;
// Build relative path for installation
const installPath = moduleName === 'core' ? `bmad/core/tasks/${file}` : `bmad/${moduleName}/tasks/${file}`;
const installPath =
moduleName === 'core' ? `${this.bmadFolderName}/core/tasks/${file}` : `${this.bmadFolderName}/${moduleName}/tasks/${file}`;
const taskName = file.replace(/\.(xml|md)$/, '');
tasks.push({
@@ -393,7 +396,8 @@ class ManifestGenerator {
const standalone = !!standaloneMatch;
// Build relative path for installation
const installPath = moduleName === 'core' ? `bmad/core/tools/${file}` : `bmad/${moduleName}/tools/${file}`;
const installPath =
moduleName === 'core' ? `${this.bmadFolderName}/core/tools/${file}` : `${this.bmadFolderName}/${moduleName}/tools/${file}`;
const toolName = file.replace(/\.(xml|md)$/, '');
tools.push({
@@ -659,7 +663,7 @@ class ManifestGenerator {
} else {
// Fallback: use the collected workflows/agents/tasks
for (const file of this.files) {
const filePath = path.join(this.bmadDir, file.path.replace('bmad/', ''));
const filePath = path.join(this.bmadDir, file.path.replace(this.bmadFolderName + '/', ''));
const hash = await this.calculateFileHash(filePath);
allFiles.push({
...file,

View File

@@ -29,6 +29,22 @@ class BaseIdeSetup {
this.bmadFolderName = bmadFolderName;
}
/**
* Get the agent command activation header from the central template
* @returns {string} The activation header text (without XML tags)
*/
async getAgentCommandHeader() {
const headerPath = path.join(getSourcePath(), 'src', 'utility', 'models', 'agent-command-header.md');
try {
const content = await fs.readFile(headerPath, 'utf8');
// Strip the <critical> tags to get plain text
return content.replaceAll(/<critical>|<\/critical>/g, '').trim();
} catch {
// Fallback if file doesn't exist
return "You must fully embody this agent's persona and follow all activation instructions, steps and rules exactly as specified. NEVER break character until given an exit command.";
}
}
/**
* Main setup method - must be implemented by subclasses
* @param {string} projectDir - Project directory

View File

@@ -1,70 +1,17 @@
const path = require('node:path');
const os = require('node:os');
const { BaseIdeSetup } = require('./_base-ide');
const chalk = require('chalk');
const inquirer = require('inquirer');
/**
* Auggie CLI setup handler
* Allows flexible installation of agents to multiple locations
* Installs to project directory (.augment/commands)
*/
class AuggieSetup extends BaseIdeSetup {
constructor() {
super('auggie', 'Auggie CLI');
this.defaultLocations = [
{ name: 'Project Directory (.augment/commands)', value: '.augment/commands', checked: true },
{ name: 'User Home (~/.augment/commands)', value: path.join(os.homedir(), '.augment', 'commands') },
{ name: 'Custom Location', value: 'custom' },
];
this.detectionPaths = ['.augment'];
}
/**
* Collect configuration choices before installation
* @param {Object} options - Configuration options
* @returns {Object} Collected configuration
*/
async collectConfiguration(options = {}) {
const response = await inquirer.prompt([
{
type: 'checkbox',
name: 'locations',
message: 'Select Auggie CLI installation locations:',
choices: this.defaultLocations,
validate: (answers) => {
if (answers.length === 0) {
return 'Please select at least one location';
}
return true;
},
},
]);
const locations = [];
for (const loc of response.locations) {
if (loc === 'custom') {
const custom = await inquirer.prompt([
{
type: 'input',
name: 'path',
message: 'Enter custom path for Auggie commands:',
validate: (input) => {
if (!input.trim()) {
return 'Path cannot be empty';
}
return true;
},
},
]);
locations.push(custom.path);
} else {
locations.push(loc);
}
}
return { auggieLocations: locations };
}
/**
* Setup Auggie CLI configuration
* @param {string} projectDir - Project directory
@@ -74,14 +21,8 @@ class AuggieSetup extends BaseIdeSetup {
async setup(projectDir, bmadDir, options = {}) {
console.log(chalk.cyan(`Setting up ${this.name}...`));
// Use pre-collected configuration if available
const config = options.preCollectedConfig || {};
const locations = await this.getInstallLocations(projectDir, { ...options, auggieLocations: config.auggieLocations });
if (locations.length === 0) {
console.log(chalk.yellow('No locations selected. Skipping Auggie CLI setup.'));
return { success: false, reason: 'no-locations' };
}
// Always use project directory
const location = path.join(projectDir, '.augment', 'commands');
// Clean up old BMAD installation first
await this.cleanup(projectDir);
@@ -92,151 +33,93 @@ class AuggieSetup extends BaseIdeSetup {
const tools = await this.getTools(bmadDir, true);
const workflows = await this.getWorkflows(bmadDir, true);
let totalInstalled = 0;
const bmadCommandsDir = path.join(location, 'bmad');
const agentsDir = path.join(bmadCommandsDir, 'agents');
const tasksDir = path.join(bmadCommandsDir, 'tasks');
const toolsDir = path.join(bmadCommandsDir, 'tools');
const workflowsDir = path.join(bmadCommandsDir, 'workflows');
// Install to each selected location
for (const location of locations) {
console.log(chalk.dim(`\n Installing to: ${location}`));
await this.ensureDir(agentsDir);
await this.ensureDir(tasksDir);
await this.ensureDir(toolsDir);
await this.ensureDir(workflowsDir);
const bmadCommandsDir = path.join(location, 'bmad');
const agentsDir = path.join(bmadCommandsDir, 'agents');
const tasksDir = path.join(bmadCommandsDir, 'tasks');
const toolsDir = path.join(bmadCommandsDir, 'tools');
const workflowsDir = path.join(bmadCommandsDir, 'workflows');
// Install agents
for (const agent of agents) {
const content = await this.readFile(agent.path);
const commandContent = await this.createAgentCommand(agent, content);
await this.ensureDir(agentsDir);
await this.ensureDir(tasksDir);
await this.ensureDir(toolsDir);
await this.ensureDir(workflowsDir);
// Install agents
for (const agent of agents) {
const content = await this.readFile(agent.path);
const commandContent = this.createAgentCommand(agent, content);
const targetPath = path.join(agentsDir, `${agent.module}-${agent.name}.md`);
await this.writeFile(targetPath, commandContent);
totalInstalled++;
}
// Install tasks
for (const task of tasks) {
const content = await this.readFile(task.path);
const commandContent = this.createTaskCommand(task, content);
const targetPath = path.join(tasksDir, `${task.module}-${task.name}.md`);
await this.writeFile(targetPath, commandContent);
totalInstalled++;
}
// Install tools
for (const tool of tools) {
const content = await this.readFile(tool.path);
const commandContent = this.createToolCommand(tool, content);
const targetPath = path.join(toolsDir, `${tool.module}-${tool.name}.md`);
await this.writeFile(targetPath, commandContent);
totalInstalled++;
}
// Install workflows
for (const workflow of workflows) {
const content = await this.readFile(workflow.path);
const commandContent = this.createWorkflowCommand(workflow, content);
const targetPath = path.join(workflowsDir, `${workflow.module}-${workflow.name}.md`);
await this.writeFile(targetPath, commandContent);
totalInstalled++;
}
console.log(
chalk.green(` ✓ Installed ${agents.length} agents, ${tasks.length} tasks, ${tools.length} tools, ${workflows.length} workflows`),
);
const targetPath = path.join(agentsDir, `${agent.module}-${agent.name}.md`);
await this.writeFile(targetPath, commandContent);
}
console.log(chalk.green(`\n${this.name} configured:`));
console.log(chalk.dim(` - ${totalInstalled} total commands installed`));
console.log(chalk.dim(` - ${locations.length} location(s) configured`));
// Install tasks
for (const task of tasks) {
const content = await this.readFile(task.path);
const commandContent = this.createTaskCommand(task, content);
const targetPath = path.join(tasksDir, `${task.module}-${task.name}.md`);
await this.writeFile(targetPath, commandContent);
}
// Install tools
for (const tool of tools) {
const content = await this.readFile(tool.path);
const commandContent = this.createToolCommand(tool, content);
const targetPath = path.join(toolsDir, `${tool.module}-${tool.name}.md`);
await this.writeFile(targetPath, commandContent);
}
// Install workflows
for (const workflow of workflows) {
const content = await this.readFile(workflow.path);
const commandContent = this.createWorkflowCommand(workflow, content);
const targetPath = path.join(workflowsDir, `${workflow.module}-${workflow.name}.md`);
await this.writeFile(targetPath, commandContent);
}
const totalInstalled = agents.length + tasks.length + tools.length + workflows.length;
console.log(chalk.green(`${this.name} configured:`));
console.log(chalk.dim(` - ${agents.length} agents installed`));
console.log(chalk.dim(` - ${tasks.length} tasks installed`));
console.log(chalk.dim(` - ${tools.length} tools installed`));
console.log(chalk.dim(` - ${workflows.length} workflows installed`));
console.log(chalk.dim(` - Location: ${path.relative(projectDir, location)}`));
console.log(chalk.yellow(`\n 💡 Tip: Add 'model: gpt-4o' to command frontmatter to specify AI model`));
return {
success: true,
commands: totalInstalled,
locations: locations.length,
agents: agents.length,
tasks: tasks.length,
tools: tools.length,
workflows: workflows.length,
};
}
/**
* Get installation locations from user
*/
async getInstallLocations(projectDir, options) {
if (options.auggieLocations) {
// Process the pre-collected locations to resolve relative paths
const processedLocations = [];
for (const loc of options.auggieLocations) {
if (loc === '.augment/commands') {
// Relative to project directory
processedLocations.push(path.join(projectDir, loc));
} else {
processedLocations.push(loc);
}
}
return processedLocations;
}
const response = await inquirer.prompt([
{
type: 'checkbox',
name: 'locations',
message: 'Select Auggie CLI installation locations:',
choices: this.defaultLocations,
validate: (answers) => {
if (answers.length === 0) {
return 'Please select at least one location';
}
return true;
},
},
]);
const locations = [];
for (const loc of response.locations) {
if (loc === 'custom') {
const custom = await inquirer.prompt([
{
type: 'input',
name: 'path',
message: 'Enter custom path for Auggie commands:',
validate: (input) => {
if (!input.trim()) {
return 'Path cannot be empty';
}
return true;
},
},
]);
locations.push(custom.path);
} else if (loc.startsWith('.augment')) {
// Relative to project directory
locations.push(path.join(projectDir, loc));
} else {
locations.push(loc);
}
}
return locations;
}
/**
* Create agent command content
*/
createAgentCommand(agent, content) {
async createAgentCommand(agent, content) {
const titleMatch = content.match(/title="([^"]+)"/);
const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name);
return `# ${title} Agent
// Extract description from agent if available
const whenToUseMatch = content.match(/whenToUse="([^"]+)"/);
const description = whenToUseMatch ? whenToUseMatch[1] : `Activate the ${title} agent`;
## Activation
Type \`@${agent.name}\` to activate this agent.
// Get the activation header from central template
const activationHeader = await this.getAgentCommandHeader();
return `---
description: "${description}"
---
# ${title} Agent
${activationHeader}
${content}
@@ -252,10 +135,11 @@ BMAD ${agent.module.toUpperCase()} module
const nameMatch = content.match(/name="([^"]+)"/);
const taskName = nameMatch ? nameMatch[1] : this.formatTitle(task.name);
return `# ${taskName} Task
return `---
description: "Execute the ${taskName} task"
---
## Activation
Type \`@task-${task.name}\` to execute this task.
# ${taskName} Task
${content}
@@ -271,10 +155,11 @@ BMAD ${task.module.toUpperCase()} module
const nameMatch = content.match(/name="([^"]+)"/);
const toolName = nameMatch ? nameMatch[1] : this.formatTitle(tool.name);
return `# ${toolName} Tool
return `---
description: "Use the ${toolName} tool"
---
## Activation
Type \`@tool-${tool.name}\` to execute this tool.
# ${toolName} Tool
${content}
@@ -287,13 +172,13 @@ BMAD ${tool.module.toUpperCase()} module
* Create workflow command content
*/
createWorkflowCommand(workflow, content) {
return `# ${workflow.name} Workflow
const description = workflow.description || `Execute the ${workflow.name} workflow`;
## Description
${workflow.description || 'No description provided'}
return `---
description: "${description}"
---
## Activation
Type \`@workflow-${workflow.name}\` to execute this workflow.
# ${workflow.name} Workflow
${content}
@@ -308,16 +193,13 @@ BMAD ${workflow.module.toUpperCase()} module
async cleanup(projectDir) {
const fs = require('fs-extra');
// Check common locations - bmad folder structure
const locations = [path.join(os.homedir(), '.augment', 'commands'), path.join(projectDir, '.augment', 'commands')];
// Only clean up project directory
const location = path.join(projectDir, '.augment', 'commands');
const bmadDir = path.join(location, 'bmad');
for (const location of locations) {
const bmadDir = path.join(location, 'bmad');
if (await fs.pathExists(bmadDir)) {
await fs.remove(bmadDir);
console.log(chalk.dim(` Removed old BMAD commands from ${location}`));
}
if (await fs.pathExists(bmadDir)) {
await fs.remove(bmadDir);
console.log(chalk.dim(` Removed old BMAD commands`));
}
}
}

View File

@@ -2,8 +2,8 @@ const path = require('node:path');
const { BaseIdeSetup } = require('./_base-ide');
const chalk = require('chalk');
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
const { WorkflowCommandGenerator } = require('./workflow-command-generator');
const { TaskToolCommandGenerator } = require('./task-tool-command-generator');
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator');
const {
loadModuleInjectionConfig,
shouldApplyInjection,
@@ -161,7 +161,7 @@ class ClaudeCodeSetup extends BaseIdeSetup {
// await this.createClaudeConfig(projectDir, modules);
// Generate workflow commands from manifest (if it exists)
const workflowGen = new WorkflowCommandGenerator();
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts: workflowArtifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir);
// Write only workflow-command artifacts, skip workflow-launcher READMEs

View File

@@ -2,7 +2,7 @@ const path = require('node:path');
const fs = require('fs-extra');
const chalk = require('chalk');
const { BaseIdeSetup } = require('./_base-ide');
const { WorkflowCommandGenerator } = require('./workflow-command-generator');
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
const { getAgentsFromBmad, getTasksFromBmad } = require('./shared/bmad-artifacts');
/**
@@ -11,7 +11,7 @@ const { getAgentsFromBmad, getTasksFromBmad } = require('./shared/bmad-artifacts
*/
class ClineSetup extends BaseIdeSetup {
constructor() {
super('cline', 'Cline', true); // preferred IDE
super('cline', 'Cline', false);
this.configDir = '.clinerules';
this.workflowsDir = 'workflows';
}
@@ -131,7 +131,7 @@ class ClineSetup extends BaseIdeSetup {
}
// Get workflows
const workflowGenerator = new WorkflowCommandGenerator();
const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir);
artifacts.push(...workflowArtifacts);

View File

@@ -2,41 +2,18 @@ const path = require('node:path');
const fs = require('fs-extra');
const os = require('node:os');
const chalk = require('chalk');
const inquirer = require('inquirer');
const { BaseIdeSetup } = require('./_base-ide');
const { WorkflowCommandGenerator } = require('./workflow-command-generator');
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
const { getAgentsFromBmad, getTasksFromBmad } = require('./shared/bmad-artifacts');
/**
* Codex setup handler (supports both CLI and Web)
* Codex setup handler (CLI mode)
*/
class CodexSetup extends BaseIdeSetup {
constructor() {
super('codex', 'Codex', true); // preferred IDE
}
/**
* Collect configuration choices before installation
* @param {Object} options - Configuration options
* @returns {Object} Collected configuration
*/
async collectConfiguration(options = {}) {
const response = await inquirer.prompt([
{
type: 'list',
name: 'mode',
message: 'Select Codex deployment mode:',
choices: [
{ name: 'CLI (Command-line interface)', value: 'cli' },
{ name: 'Web (Browser-based interface)', value: 'web' },
],
default: 'cli',
},
]);
return { codexMode: response.mode };
}
/**
* Setup Codex configuration
* @param {string} projectDir - Project directory
@@ -46,8 +23,8 @@ class CodexSetup extends BaseIdeSetup {
async setup(projectDir, bmadDir, options = {}) {
console.log(chalk.cyan(`Setting up ${this.name}...`));
const config = options.preCollectedConfig || {};
const mode = config.codexMode || options.codexMode || 'cli';
// Always use CLI mode
const mode = 'cli';
const { artifacts, counts } = await this.collectClaudeArtifacts(projectDir, bmadDir, options);
@@ -57,16 +34,13 @@ class CodexSetup extends BaseIdeSetup {
const written = await this.flattenAndWriteArtifacts(artifacts, destDir);
console.log(chalk.green(`${this.name} configured:`));
console.log(chalk.dim(` - Mode: ${mode === 'web' ? 'Web' : 'CLI'}`));
console.log(chalk.dim(` - Mode: CLI`));
console.log(chalk.dim(` - ${counts.agents} agents exported`));
console.log(chalk.dim(` - ${counts.tasks} tasks exported`));
console.log(chalk.dim(` - ${counts.workflows} workflow commands exported`));
if (counts.workflowLaunchers > 0) {
console.log(chalk.dim(` - ${counts.workflowLaunchers} workflow launchers exported`));
}
if (counts.subagents > 0) {
console.log(chalk.dim(` - ${counts.subagents} subagents exported`));
}
console.log(chalk.dim(` - ${written} Codex prompt files written`));
console.log(chalk.dim(` - Destination: ${destDir}`));
@@ -158,7 +132,7 @@ class CodexSetup extends BaseIdeSetup {
});
}
const workflowGenerator = new WorkflowCommandGenerator();
const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir);
artifacts.push(...workflowArtifacts);

View File

@@ -84,7 +84,7 @@ class CrushSetup extends BaseIdeSetup {
const moduleAgents = agents.filter((a) => a.module === module);
for (const agent of moduleAgents) {
const content = await this.readFile(agent.path);
const commandContent = this.createAgentCommand(agent, content, projectDir);
const commandContent = await this.createAgentCommand(agent, content, projectDir);
const targetPath = path.join(moduleAgentsDir, `${agent.name}.md`);
await this.writeFile(targetPath, commandContent);
agentCount++;
@@ -132,7 +132,7 @@ class CrushSetup extends BaseIdeSetup {
/**
* Create agent command content
*/
createAgentCommand(agent, content, projectDir) {
async createAgentCommand(agent, content, projectDir) {
// Extract metadata
const titleMatch = content.match(/title="([^"]+)"/);
const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name);
@@ -140,12 +140,15 @@ class CrushSetup extends BaseIdeSetup {
const iconMatch = content.match(/icon="([^"]+)"/);
const icon = iconMatch ? iconMatch[1] : '🤖';
// Get the activation header from central template
const activationHeader = await this.getAgentCommandHeader();
// Get relative path
const relativePath = path.relative(projectDir, agent.path).replaceAll('\\', '/');
let commandContent = `# /${agent.name} Command
When this command is used, adopt the following agent persona:
${activationHeader}
## ${icon} ${title} Agent
@@ -159,9 +162,6 @@ This command activates the ${title} agent from the BMAD ${agent.module.toUpperCa
Complete agent definition: [${relativePath}](${relativePath})
## Module
Part of the BMAD ${agent.module.toUpperCase()} module.
`;
return commandContent;

View File

@@ -256,6 +256,13 @@ specific agent expertise, task workflows, tools, or guided workflows.
// First apply base processing (includes activation injection for agents)
let processed = super.processContent(content, metadata);
// Strip any existing frontmatter from the processed content
// This prevents duplicate frontmatter blocks
const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
if (frontmatterRegex.test(processed)) {
processed = processed.replace(frontmatterRegex, '');
}
// Determine the type and description based on content
const isAgent = content.includes('<agent');
const isTask = content.includes('<task');

View File

@@ -1,4 +1,6 @@
const path = require('node:path');
const fs = require('fs-extra');
const yaml = require('js-yaml');
const { BaseIdeSetup } = require('./_base-ide');
const chalk = require('chalk');
@@ -8,9 +10,39 @@ const chalk = require('chalk');
*/
class GeminiSetup extends BaseIdeSetup {
constructor() {
super('gemini', 'Gemini CLI', true); // preferred IDE
super('gemini', 'Gemini CLI', false);
this.configDir = '.gemini';
this.commandsDir = 'commands';
this.agentTemplatePath = path.join(__dirname, 'templates', 'gemini-agent-command.toml');
this.taskTemplatePath = path.join(__dirname, 'templates', 'gemini-task-command.toml');
}
/**
* Load config values from bmad installation
* @param {string} bmadDir - BMAD installation directory
* @returns {Object} Config values
*/
async loadConfigValues(bmadDir) {
const configValues = {
user_name: 'User', // Default fallback
};
// Try to load core config.yaml
const coreConfigPath = path.join(bmadDir, 'core', 'config.yaml');
if (await fs.pathExists(coreConfigPath)) {
try {
const configContent = await fs.readFile(coreConfigPath, 'utf8');
const config = yaml.load(configContent);
if (config.user_name) {
configValues.user_name = config.user_name;
}
} catch (error) {
console.warn(chalk.yellow(` Warning: Could not load config values: ${error.message}`));
}
}
return configValues;
}
/**
@@ -39,7 +71,7 @@ class GeminiSetup extends BaseIdeSetup {
let agentCount = 0;
for (const agent of agents) {
const content = await this.readFile(agent.path);
const tomlContent = this.createAgentToml(agent, content, bmadDir);
const tomlContent = await this.createAgentToml(agent, content);
// Flat structure: bmad-agent-{module}-{name}.toml
const tomlPath = path.join(commandsDir, `bmad-agent-${agent.module}-${agent.name}.toml`);
@@ -53,7 +85,7 @@ class GeminiSetup extends BaseIdeSetup {
let taskCount = 0;
for (const task of tasks) {
const content = await this.readFile(task.path);
const tomlContent = this.createTaskToml(task, content, bmadDir);
const tomlContent = await this.createTaskToml(task, content);
// Flat structure: bmad-task-{module}-{name}.toml
const tomlPath = path.join(commandsDir, `bmad-task-${task.module}-${task.name}.toml`);
@@ -78,51 +110,44 @@ class GeminiSetup extends BaseIdeSetup {
}
/**
* Create agent TOML content
* Create agent TOML content using template
*/
createAgentToml(agent, content, bmadDir) {
async createAgentToml(agent, content) {
// Extract metadata
const titleMatch = content.match(/title="([^"]+)"/);
const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name);
// Get relative path from project root to agent file
const relativePath = path.relative(process.cwd(), agent.path).replaceAll('\\', '/');
// Load template
const template = await fs.readFile(this.agentTemplatePath, 'utf8');
// Create TOML content
const tomlContent = `description = "Activates the ${title} agent from the BMad Method."
prompt = """
CRITICAL: You are now the BMad '${title}' agent. Adopt its persona and capabilities as defined in the following configuration.
Read and internalize the full agent definition, following all instructions and maintaining this persona until explicitly told to switch or exit.
@${relativePath}
"""
`;
// Replace template variables
// Note: {user_name} and other {config_values} are left as-is for runtime substitution by Gemini
const tomlContent = template
.replaceAll('{{title}}', title)
.replaceAll('{{bmad_folder}}', this.bmadFolderName)
.replaceAll('{{module}}', agent.module)
.replaceAll('{{name}}', agent.name);
return tomlContent;
}
/**
* Create task TOML content
* Create task TOML content using template
*/
createTaskToml(task, content, bmadDir) {
async createTaskToml(task, content) {
// Extract task name from XML if available
const nameMatch = content.match(/<name>([^<]+)<\/name>/);
const taskName = nameMatch ? nameMatch[1] : this.formatTitle(task.name);
// Get relative path from project root to task file
const relativePath = path.relative(process.cwd(), task.path).replaceAll('\\', '/');
// Load template
const template = await fs.readFile(this.taskTemplatePath, 'utf8');
// Create TOML content
const tomlContent = `description = "Executes the ${taskName} task from the BMad Method."
prompt = """
Execute the following BMad Method task workflow:
@${relativePath}
Follow all instructions and complete the task as defined.
"""
`;
// Replace template variables
const tomlContent = template
.replaceAll('{{taskName}}', taskName)
.replaceAll('{{bmad_folder}}', this.bmadFolderName)
.replaceAll('{{module}}', task.module)
.replaceAll('{{filename}}', task.filename);
return tomlContent;
}

View File

@@ -111,7 +111,7 @@ class GitHubCopilotSetup extends BaseIdeSetup {
let modeCount = 0;
for (const agent of agents) {
const content = await this.readFile(agent.path);
const chatmodeContent = this.createChatmodeContent(agent, content);
const chatmodeContent = await this.createChatmodeContent(agent, content);
// Use bmad- prefix: bmad-agent-{module}-{name}.chatmode.md
const targetPath = path.join(chatmodesDir, `bmad-agent-${agent.module}-${agent.name}.chatmode.md`);
@@ -206,7 +206,7 @@ class GitHubCopilotSetup extends BaseIdeSetup {
/**
* Create chat mode content
*/
createChatmodeContent(agent, content) {
async createChatmodeContent(agent, content) {
// Extract metadata
const titleMatch = content.match(/title="([^"]+)"/);
const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name);
@@ -214,6 +214,16 @@ class GitHubCopilotSetup extends BaseIdeSetup {
const whenToUseMatch = content.match(/whenToUse="([^"]+)"/);
const description = whenToUseMatch ? whenToUseMatch[1] : `Activates the ${title} agent persona.`;
// Get the activation header from central template
const activationHeader = await this.getAgentCommandHeader();
// Strip any existing frontmatter from the content
const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
let cleanContent = content;
if (frontmatterRegex.test(content)) {
cleanContent = content.replace(frontmatterRegex, '');
}
// Available GitHub Copilot tools (November 2025 - Official VS Code Documentation)
// Reference: https://code.visualstudio.com/docs/copilot/reference/copilot-vscode-features#_chat-tools
const tools = [
@@ -248,11 +258,10 @@ tools: ${JSON.stringify(tools)}
# ${title} Agent
${content}
${activationHeader}
## Module
${cleanContent}
Part of the BMAD ${agent.module.toUpperCase()} module.
`;
return chatmodeContent;

View File

@@ -39,7 +39,7 @@ class IFlowSetup extends BaseIdeSetup {
let agentCount = 0;
for (const agent of agents) {
const content = await this.readFile(agent.path);
const commandContent = this.createAgentCommand(agent, content);
const commandContent = await this.createAgentCommand(agent, content);
const targetPath = path.join(agentsDir, `${agent.module}-${agent.name}.md`);
await this.writeFile(targetPath, commandContent);
@@ -72,14 +72,17 @@ class IFlowSetup extends BaseIdeSetup {
/**
* Create agent command content
*/
createAgentCommand(agent, content) {
async createAgentCommand(agent, content) {
// Extract metadata
const titleMatch = content.match(/title="([^"]+)"/);
const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name);
// Get the activation header from central template
const activationHeader = await this.getAgentCommandHeader();
let commandContent = `# /${agent.name} Command
When this command is used, adopt the following agent persona:
${activationHeader}
## ${title} Agent
@@ -89,9 +92,6 @@ ${content}
This command activates the ${title} agent from the BMAD ${agent.module.toUpperCase()} module.
## Module
Part of the BMAD ${agent.module.toUpperCase()} module.
`;
return commandContent;

View File

@@ -55,7 +55,7 @@ class KiloSetup extends BaseIdeSetup {
}
const content = await this.readFile(agent.path);
const modeEntry = this.createModeEntry(agent, content, projectDir);
const modeEntry = await this.createModeEntry(agent, content, projectDir);
newModesContent += modeEntry;
addedCount++;
@@ -90,7 +90,7 @@ class KiloSetup extends BaseIdeSetup {
/**
* Create a mode entry for an agent
*/
createModeEntry(agent, content, projectDir) {
async createModeEntry(agent, content, projectDir) {
// Extract metadata
const titleMatch = content.match(/title="([^"]+)"/);
const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name);
@@ -101,6 +101,9 @@ class KiloSetup extends BaseIdeSetup {
const whenToUseMatch = content.match(/whenToUse="([^"]+)"/);
const whenToUse = whenToUseMatch ? whenToUseMatch[1] : `Use for ${title} tasks`;
// Get the activation header from central template
const activationHeader = await this.getAgentCommandHeader();
const roleDefinitionMatch = content.match(/roleDefinition="([^"]+)"/);
const roleDefinition = roleDefinitionMatch
? roleDefinitionMatch[1]
@@ -115,7 +118,7 @@ class KiloSetup extends BaseIdeSetup {
modeEntry += ` name: '${icon} ${title}'\n`;
modeEntry += ` roleDefinition: ${roleDefinition}\n`;
modeEntry += ` whenToUse: ${whenToUse}\n`;
modeEntry += ` customInstructions: CRITICAL Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
modeEntry += ` customInstructions: ${activationHeader} Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
modeEntry += ` groups:\n`;
modeEntry += ` - read\n`;
modeEntry += ` - edit\n`;

View File

@@ -4,8 +4,8 @@ const os = require('node:os');
const chalk = require('chalk');
const yaml = require('js-yaml');
const { BaseIdeSetup } = require('./_base-ide');
const { WorkflowCommandGenerator } = require('./workflow-command-generator');
const { TaskToolCommandGenerator } = require('./task-tool-command-generator');
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator');
const { getAgentsFromBmad } = require('./shared/bmad-artifacts');
@@ -44,7 +44,7 @@ class OpenCodeSetup extends BaseIdeSetup {
name: agent.name,
});
const agentContent = this.createAgentContent(processed, agent);
const agentContent = await this.createAgentContent(processed, agent);
// Flat structure in agent folder: bmad-agent-{module}-{name}.md
const targetPath = path.join(agentsBaseDir, `bmad-agent-${agent.module}-${agent.name}.md`);
await this.writeFile(targetPath, agentContent);
@@ -52,7 +52,7 @@ class OpenCodeSetup extends BaseIdeSetup {
}
// Install workflow commands with flat naming: bmad-workflow-{module}-{name}.md
const workflowGenerator = new WorkflowCommandGenerator();
const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir);
let workflowCommandCount = 0;
@@ -127,7 +127,7 @@ class OpenCodeSetup extends BaseIdeSetup {
return this.processContent(content, metadata);
}
createAgentContent(content, metadata) {
async createAgentContent(content, metadata) {
const { frontmatter = {}, body } = this.parseFrontmatter(content);
frontmatter.description =
@@ -140,7 +140,10 @@ class OpenCodeSetup extends BaseIdeSetup {
const frontmatterString = this.stringifyFrontmatter(frontmatter);
return `${frontmatterString}\n${body}`;
// Get the activation header from central template
const activationHeader = await this.getAgentCommandHeader();
return `${frontmatterString}\n\n${activationHeader}\n\n${body}`;
}
parseFrontmatter(content) {

View File

@@ -1,7 +1,6 @@
const path = require('node:path');
const { BaseIdeSetup } = require('./_base-ide');
const chalk = require('chalk');
const inquirer = require('inquirer');
/**
* Roo IDE setup handler
@@ -35,31 +34,6 @@ class RooSetup extends BaseIdeSetup {
};
}
/**
* Collect configuration choices before installation
* @param {Object} options - Configuration options
* @returns {Object} Collected configuration
*/
async collectConfiguration(options = {}) {
const response = await inquirer.prompt([
{
type: 'list',
name: 'permissions',
message: 'Select default file edit permissions for BMAD agents:',
choices: [
{ name: 'Development files only (js, ts, py, etc.)', value: 'dev' },
{ name: 'Configuration files only (json, yaml, xml, etc.)', value: 'config' },
{ name: 'Documentation files only (md, txt, doc, etc.)', value: 'docs' },
{ name: 'All files (unrestricted access)', value: 'all' },
{ name: 'Custom per agent (will be configured individually)', value: 'custom' },
],
default: 'dev',
},
]);
return { permissions: response.permissions };
}
/**
* Setup Roo IDE configuration
* @param {string} projectDir - Project directory
@@ -87,9 +61,8 @@ class RooSetup extends BaseIdeSetup {
// Get agents
const agents = await this.getAgents(bmadDir);
// Use pre-collected configuration if available
const config = options.preCollectedConfig || {};
let permissionChoice = config.permissions || options.permissions || 'dev';
// Always use 'all' permissions - users can customize in .roomodes file
const permissionChoice = 'all';
// Create modes content
let newModesContent = '';
@@ -107,7 +80,7 @@ class RooSetup extends BaseIdeSetup {
}
const content = await this.readFile(agent.path);
const modeEntry = this.createModeEntry(agent, content, permissionChoice, projectDir);
const modeEntry = await this.createModeEntry(agent, content, permissionChoice, projectDir);
newModesContent += modeEntry;
addedCount++;
@@ -133,8 +106,9 @@ class RooSetup extends BaseIdeSetup {
console.log(chalk.dim(` - ${skippedCount} modes skipped (already exist)`));
}
console.log(chalk.dim(` - Configuration file: ${this.configFile}`));
console.log(chalk.dim(` - Permission level: ${permissionChoice}`));
console.log(chalk.dim('\n Modes will be available when you open this project in Roo Code'));
console.log(chalk.dim(` - Permission level: all (unrestricted)`));
console.log(chalk.yellow(`\n 💡 Tip: Edit ${this.configFile} to customize file permissions per agent`));
console.log(chalk.dim(` Modes will be available when you open this project in Roo Code`));
return {
success: true,
@@ -143,33 +117,10 @@ class RooSetup extends BaseIdeSetup {
};
}
/**
* Ask user about permission configuration
*/
async askPermissions() {
const response = await inquirer.prompt([
{
type: 'list',
name: 'permissions',
message: 'Select default file edit permissions for BMAD agents:',
choices: [
{ name: 'Development files only (js, ts, py, etc.)', value: 'dev' },
{ name: 'Configuration files only (json, yaml, xml, etc.)', value: 'config' },
{ name: 'Documentation files only (md, txt, doc, etc.)', value: 'docs' },
{ name: 'All files (unrestricted access)', value: 'all' },
{ name: 'Custom per agent (will be configured individually)', value: 'custom' },
],
default: 'dev',
},
]);
return response.permissions;
}
/**
* Create a mode entry for an agent
*/
createModeEntry(agent, content, permissionChoice, projectDir) {
async createModeEntry(agent, content, permissionChoice, projectDir) {
// Extract metadata from agent content
const titleMatch = content.match(/title="([^"]+)"/);
const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name);
@@ -180,6 +131,9 @@ class RooSetup extends BaseIdeSetup {
const whenToUseMatch = content.match(/whenToUse="([^"]+)"/);
const whenToUse = whenToUseMatch ? whenToUseMatch[1] : `Use for ${title} tasks`;
// Get the activation header from central template
const activationHeader = await this.getAgentCommandHeader();
const roleDefinitionMatch = content.match(/roleDefinition="([^"]+)"/);
const roleDefinition = roleDefinitionMatch
? roleDefinitionMatch[1]
@@ -202,7 +156,7 @@ class RooSetup extends BaseIdeSetup {
modeEntry += ` roleDefinition: ${roleDefinition}\n`;
modeEntry += ` whenToUse: ${whenToUse}\n`;
modeEntry += ` customInstructions: CRITICAL Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
modeEntry += ` customInstructions: ${activationHeader} Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
modeEntry += ` groups:\n`;
modeEntry += ` - read\n`;

View File

@@ -4,7 +4,7 @@ const csv = require('csv-parse/sync');
const chalk = require('chalk');
/**
* Generates Claude Code command files for standalone tasks and tools
* Generates command files for standalone tasks and tools
*/
class TaskToolCommandGenerator {
/**

View File

@@ -4,11 +4,12 @@ const csv = require('csv-parse/sync');
const chalk = require('chalk');
/**
* Generates Claude Code command files for each workflow in the manifest
* Generates command files for each workflow in the manifest
*/
class WorkflowCommandGenerator {
constructor() {
this.templatePath = path.join(__dirname, 'workflow-command-template.md');
constructor(bmadFolderName = 'bmad') {
this.templatePath = path.join(__dirname, '../templates/workflow-command-template.md');
this.bmadFolderName = bmadFolderName;
}
/**
@@ -103,19 +104,19 @@ class WorkflowCommandGenerator {
// Convert source path to installed path
// From: /Users/.../src/modules/bmm/workflows/.../workflow.yaml
// To: {project-root}/bmad/bmm/workflows/.../workflow.yaml
// To: {project-root}/{bmad_folder}/bmm/workflows/.../workflow.yaml
let workflowPath = workflow.path;
// Extract the relative path from source
if (workflowPath.includes('/src/modules/')) {
const match = workflowPath.match(/\/src\/modules\/(.+)/);
if (match) {
workflowPath = `{project-root}/bmad/${match[1]}`;
workflowPath = `${this.bmadFolderName}/${match[1]}`;
}
} else if (workflowPath.includes('/src/core/')) {
const match = workflowPath.match(/\/src\/core\/(.+)/);
if (match) {
workflowPath = `{project-root}/bmad/core/${match[1]}`;
workflowPath = `${this.bmadFolderName}/core/${match[1]}`;
}
}
@@ -125,6 +126,7 @@ class WorkflowCommandGenerator {
.replaceAll('{{module}}', workflow.module)
.replaceAll('{{description}}', workflow.description)
.replaceAll('{{workflow_path}}', workflowPath)
.replaceAll('{bmad_folder}', this.bmadFolderName)
.replaceAll('{{interactive}}', workflow.interactive)
.replaceAll('{{author}}', workflow.author || 'BMAD');
}
@@ -186,7 +188,7 @@ class WorkflowCommandGenerator {
## Execution
When running any workflow:
1. LOAD {project-root}/bmad/core/tasks/workflow.xml
1. LOAD {project-root}/${this.bmadFolderName}/core/tasks/workflow.xml
2. Pass the workflow path as 'workflow-config' parameter
3. Follow workflow.xml instructions EXACTLY
4. Save outputs after EACH section
@@ -205,12 +207,12 @@ When running any workflow:
if (workflowPath.includes('/src/modules/')) {
const match = workflowPath.match(/\/src\/modules\/(.+)/);
if (match) {
transformed = `{project-root}/bmad/${match[1]}`;
transformed = `{project-root}/${this.bmadFolderName}/${match[1]}`;
}
} else if (workflowPath.includes('/src/core/')) {
const match = workflowPath.match(/\/src\/core\/(.+)/);
if (match) {
transformed = `{project-root}/bmad/core/${match[1]}`;
transformed = `{project-root}/${this.bmadFolderName}/core/${match[1]}`;
}
}

View File

@@ -0,0 +1,14 @@
description = "Activates the {{title}} agent from the BMad Method."
prompt = """
CRITICAL: You are now the BMad '{{title}}' agent.
PRE-FLIGHT CHECKLIST:
1. [ ] IMMEDIATE ACTION: Load and parse @{{bmad_folder}}/{{module}}/config.yaml - store ALL config values in memory for use throughout the session.
2. [ ] IMMEDIATE ACTION: Read and internalize the full agent definition at @{{bmad_folder}}/{{module}}/agents/{{name}}.md.
3. [ ] CONFIRM: The user's name from config is {user_name}.
Only after all checks are complete, greet the user by name and display the menu.
Acknowledge this checklist is complete in your first response.
AGENT DEFINITION: @{{bmad_folder}}/{{module}}/agents/{{name}}.md
"""

View File

@@ -0,0 +1,12 @@
description = "Executes the {{taskName}} task from the BMad Method."
prompt = """
Execute the following BMad Method task workflow:
PRE-FLIGHT CHECKLIST:
1. [ ] IMMEDIATE ACTION: Load and parse @{{bmad_folder}}/{{module}}/config.yaml.
2. [ ] IMMEDIATE ACTION: Read and load the task definition at @{{bmad_folder}}/{{module}}/tasks/{{filename}}.
Follow all instructions and complete the task as defined.
TASK DEFINITION: @{{bmad_folder}}/{{module}}/tasks/{{filename}}
"""

View File

@@ -2,14 +2,12 @@
description: '{{description}}'
---
# {{name}}
IT IS CRITICAL THAT YOU FOLLOW THESE STEPS - while staying in character as the current agent persona you may have loaded:
<steps CRITICAL="TRUE">
1. Always LOAD the FULL {project-root}/bmad/core/tasks/workflow.xml
1. Always LOAD the FULL {project-root}/{bmad_folder}/core/tasks/workflow.xml
2. READ its entire contents - this is the CORE OS for EXECUTING the specific workflow-config {{workflow_path}}
3. Pass the yaml path {{workflow_path}} as 'workflow-config' parameter to the workflow.xml instructions
4. Follow workflow.xml instructions EXACTLY as written
4. Follow workflow.xml instructions EXACTLY as written to process and follow the specific workflow config and its instructions
5. Save outputs after EACH section when generating any documents from templates
</steps>

View File

@@ -40,7 +40,7 @@ class TraeSetup extends BaseIdeSetup {
let agentCount = 0;
for (const agent of agents) {
const content = await this.readFile(agent.path);
const processedContent = this.createAgentRule(agent, content, bmadDir, projectDir);
const processedContent = await this.createAgentRule(agent, content, bmadDir, projectDir);
// Use bmad- prefix: bmad-agent-{module}-{name}.md
const targetPath = path.join(rulesDir, `bmad-agent-${agent.module}-${agent.name}.md`);
@@ -108,7 +108,7 @@ class TraeSetup extends BaseIdeSetup {
/**
* Create rule content for an agent
*/
createAgentRule(agent, content, bmadDir, projectDir) {
async createAgentRule(agent, content, bmadDir, projectDir) {
// Extract metadata from agent content
const titleMatch = content.match(/title="([^"]+)"/);
const title = titleMatch ? titleMatch[1] : this.formatTitle(agent.name);
@@ -116,6 +116,9 @@ class TraeSetup extends BaseIdeSetup {
const iconMatch = content.match(/icon="([^"]+)"/);
const icon = iconMatch ? iconMatch[1] : '🤖';
// Get the activation header from central template
const activationHeader = await this.getAgentCommandHeader();
// Extract YAML content if available
const yamlMatch = content.match(/```ya?ml\r?\n([\s\S]*?)```/);
const yamlContent = yamlMatch ? yamlMatch[1] : content;
@@ -129,7 +132,9 @@ This rule is triggered when the user types \`@${agent.name}\` and activates the
## Agent Activation
CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
${activationHeader}
Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:
\`\`\`yaml
${yamlContent}
@@ -143,9 +148,6 @@ The complete agent definition is available in [${relativePath}](${relativePath})
When the user types \`@${agent.name}\`, activate this ${title} persona and follow all instructions defined in the YAML configuration above.
## Module
Part of the BMAD ${agent.module.toUpperCase()} module.
`;
return ruleContent;