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

@@ -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`));
}
}
}