Merge branch 'main' into main
This commit is contained in:
commit
7371ce9271
|
|
@ -29,13 +29,15 @@ Thumbs.db
|
|||
# IDE and editor configs
|
||||
.windsurf/
|
||||
.trae/
|
||||
.bmad*/.cursor/
|
||||
.bmad*/
|
||||
.cursor/
|
||||
|
||||
# AI assistant files
|
||||
CLAUDE.md
|
||||
.ai/*
|
||||
.claude
|
||||
.gemini
|
||||
.iflow
|
||||
|
||||
# Project-specific
|
||||
.bmad-core
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -119,7 +119,7 @@ The BMAD-METHOD™ includes a powerful codebase flattener tool designed to prepa
|
|||
### Features
|
||||
|
||||
- **AI-Optimized Output**: Generates clean XML format specifically designed for AI model consumption
|
||||
- **Smart Filtering**: Automatically respects `.gitignore` patterns to exclude unnecessary files
|
||||
- **Smart Filtering**: Automatically respects `.gitignore` patterns to exclude unnecessary files, plus optional project-level `.bmad-flattenignore` for additional exclusions
|
||||
- **Binary File Detection**: Intelligently identifies and excludes binary files, focusing on source code
|
||||
- **Progress Tracking**: Real-time progress indicators and comprehensive completion statistics
|
||||
- **Flexible Output**: Customizable output file location and naming
|
||||
|
|
@ -170,6 +170,18 @@ The generated XML file contains your project's text-based source files in a stru
|
|||
- File discovery and ignoring
|
||||
- Uses `git ls-files` when inside a git repository for speed and correctness; otherwise falls back to a glob-based scan.
|
||||
- Applies your `.gitignore` plus a curated set of default ignore patterns (e.g., `node_modules`, build outputs, caches, logs, IDE folders, lockfiles, large media/binaries, `.env*`, and previously generated XML outputs).
|
||||
- Supports an optional `.bmad-flattenignore` file at the project root for additional ignore patterns (gitignore-style). If present, its rules are applied after `.gitignore` and the defaults.
|
||||
|
||||
##### `.bmad-flattenignore` example
|
||||
|
||||
Create a `.bmad-flattenignore` file in the root of your project to exclude files that must remain in git but should not be included in the flattened XML:
|
||||
|
||||
```text
|
||||
seeds/**
|
||||
scripts/private/**
|
||||
**/*.snap
|
||||
```
|
||||
|
||||
- Binary handling
|
||||
- Binary files are detected and excluded from the XML content. They are counted in the final summary but not embedded in the output.
|
||||
- XML format and safety
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ To comprehensively validate a story draft before implementation begins, ensuring
|
|||
|
||||
### 1. Template Completeness Validation
|
||||
|
||||
- Load `bmad-core/templates/story-tmpl.md` and extract all section headings from the template
|
||||
- Load `.bmad-core/templates/story-tmpl.yaml` and extract all section headings from the template
|
||||
- **Missing sections check**: Compare story sections against template sections to verify all required sections are present
|
||||
- **Placeholder validation**: Ensure no template placeholders remain unfilled (e.g., `{{EpicNum}}`, `{{role}}`, `_TBD_`)
|
||||
- **Agent section verification**: Confirm all sections from template exist for future agent use
|
||||
|
|
|
|||
|
|
@ -5,7 +5,13 @@
|
|||
> Gemini Web's 1M+ token context window or Gemini CLI (when it's working) can analyze your ENTIRE codebase, or critical sections of it, all at once (obviously within reason):
|
||||
>
|
||||
> - Upload via GitHub URL or use gemini cli in the project folder
|
||||
> - If working in the web: use `npx bmad-method flatten` to flatten your project into a single file, then upload that file to your web agent.
|
||||
> - If working in the web: use `npx bmad-method flatten` to flatten your project into a single file, then upload that file to your web agent. To exclude additional files that must remain in git but shouldn't be sent to the AI, add a `.bmad-flattenignore` file at the project root (gitignore-style), e.g.:
|
||||
>
|
||||
> ```text
|
||||
> seeds/**
|
||||
> scripts/private/**
|
||||
> **/*.snap
|
||||
> ```
|
||||
|
||||
## What is Brownfield Development?
|
||||
|
||||
|
|
|
|||
|
|
@ -154,9 +154,11 @@ async function parseGitignore(gitignorePath) {
|
|||
async function loadIgnore(rootDir, extraPatterns = []) {
|
||||
const ig = ignore();
|
||||
const gitignorePath = path.join(rootDir, '.gitignore');
|
||||
const flattenIgnorePath = path.join(rootDir, '.bmad-flattenignore');
|
||||
const patterns = [
|
||||
...(await readIgnoreFile(gitignorePath)),
|
||||
...DEFAULT_PATTERNS,
|
||||
...(await readIgnoreFile(flattenIgnorePath)),
|
||||
...extraPatterns,
|
||||
];
|
||||
// De-duplicate
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ program
|
|||
.option('-d, --directory <path>', 'Installation directory')
|
||||
.option(
|
||||
'-i, --ide <ide...>',
|
||||
'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, codex, codex-web, auggie-cli, other)',
|
||||
'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, codex, codex-web, auggie-cli, iflow-cli, other)',
|
||||
)
|
||||
.option(
|
||||
'-e, --expansion-packs <packs...>',
|
||||
|
|
@ -397,6 +397,7 @@ async function promptInstallation() {
|
|||
choices: [
|
||||
{ name: 'Cursor', value: 'cursor' },
|
||||
{ name: 'Claude Code', value: 'claude-code' },
|
||||
{ name: 'iFlow CLI', value: 'iflow-cli' },
|
||||
{ name: 'Windsurf', value: 'windsurf' },
|
||||
{ name: 'Trae', value: 'trae' }, // { name: 'Trae', value: 'trae'}
|
||||
{ name: 'Roo Code', value: 'roo' },
|
||||
|
|
|
|||
|
|
@ -28,6 +28,15 @@ ide-configurations:
|
|||
# To use BMad agents in Claude Code:
|
||||
# 1. Type /agent-name (e.g., "/dev", "/pm", "/architect")
|
||||
# 2. Claude will switch to that agent's persona
|
||||
iflow-cli:
|
||||
name: iFlow CLI
|
||||
rule-dir: .iflow/commands/BMad/
|
||||
format: multi-file
|
||||
command-suffix: .md
|
||||
instructions: |
|
||||
# To use BMad agents in iFlow CLI:
|
||||
# 1. Type /agent-name (e.g., "/dev", "/pm", "/architect")
|
||||
# 2. iFlow will switch to that agent's persona
|
||||
crush:
|
||||
name: Crush
|
||||
rule-dir: .crush/commands/BMad/
|
||||
|
|
@ -78,15 +87,15 @@ ide-configurations:
|
|||
# 4. Rules are stored in .clinerules/ directory in your project
|
||||
gemini:
|
||||
name: Gemini CLI
|
||||
rule-dir: .gemini/bmad-method/
|
||||
format: single-file
|
||||
command-suffix: .md
|
||||
rule-dir: .gemini/commands/BMad/
|
||||
format: multi-file
|
||||
command-suffix: .toml
|
||||
instructions: |
|
||||
# To use BMad agents with the Gemini CLI:
|
||||
# 1. The installer creates a .gemini/bmad-method/ directory in your project.
|
||||
# 2. It concatenates all agent files into a single GEMINI.md file.
|
||||
# 3. Simply mention the agent in your prompt (e.g., "As *dev, ...").
|
||||
# 4. The Gemini CLI will automatically have the context for that agent.
|
||||
# 1. The installer creates a `BMad` folder in `.gemini/commands`.
|
||||
# 2. This adds custom commands for each agent and task.
|
||||
# 3. Type /BMad:agents:<agent-name> (e.g., "/BMad:agents:dev", "/BMad:agents:pm") or /BMad:tasks:<task-name> (e.g., "/BMad:tasks:create-doc").
|
||||
# 4. The agent will adopt that persona for the conversation or preform the task.
|
||||
github-copilot:
|
||||
name: Github Copilot
|
||||
rule-dir: .github/chatmodes/
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ class IdeSetup extends BaseIdeSetup {
|
|||
case 'claude-code': {
|
||||
return this.setupClaudeCode(installDir, selectedAgent);
|
||||
}
|
||||
case 'iflow-cli': {
|
||||
return this.setupIFlowCli(installDir, selectedAgent);
|
||||
}
|
||||
case 'crush': {
|
||||
return this.setupCrush(installDir, selectedAgent);
|
||||
}
|
||||
|
|
@ -453,6 +456,134 @@ class IdeSetup extends BaseIdeSetup {
|
|||
console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
|
||||
}
|
||||
|
||||
async setupIFlowCli(installDir, selectedAgent) {
|
||||
// Setup bmad-core commands
|
||||
const coreSlashPrefix = await this.getCoreSlashPrefix(installDir);
|
||||
const coreAgents = selectedAgent ? [selectedAgent] : await this.getCoreAgentIds(installDir);
|
||||
const coreTasks = await this.getCoreTaskIds(installDir);
|
||||
await this.setupIFlowCliForPackage(
|
||||
installDir,
|
||||
'core',
|
||||
coreSlashPrefix,
|
||||
coreAgents,
|
||||
coreTasks,
|
||||
'.bmad-core',
|
||||
);
|
||||
|
||||
// Setup expansion pack commands
|
||||
const expansionPacks = await this.getInstalledExpansionPacks(installDir);
|
||||
for (const packInfo of expansionPacks) {
|
||||
const packSlashPrefix = await this.getExpansionPackSlashPrefix(packInfo.path);
|
||||
const packAgents = await this.getExpansionPackAgents(packInfo.path);
|
||||
const packTasks = await this.getExpansionPackTasks(packInfo.path);
|
||||
|
||||
if (packAgents.length > 0 || packTasks.length > 0) {
|
||||
// Use the actual directory name where the expansion pack is installed
|
||||
const rootPath = path.relative(installDir, packInfo.path);
|
||||
await this.setupIFlowCliForPackage(
|
||||
installDir,
|
||||
packInfo.name,
|
||||
packSlashPrefix,
|
||||
packAgents,
|
||||
packTasks,
|
||||
rootPath,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async setupIFlowCliForPackage(installDir, packageName, slashPrefix, agentIds, taskIds, rootPath) {
|
||||
const commandsBaseDir = path.join(installDir, '.iflow', 'commands', slashPrefix);
|
||||
const agentsDir = path.join(commandsBaseDir, 'agents');
|
||||
const tasksDir = path.join(commandsBaseDir, 'tasks');
|
||||
|
||||
// Ensure directories exist
|
||||
await fileManager.ensureDirectory(agentsDir);
|
||||
await fileManager.ensureDirectory(tasksDir);
|
||||
|
||||
// Setup agents
|
||||
for (const agentId of agentIds) {
|
||||
// Find the agent file - for expansion packs, prefer the expansion pack version
|
||||
let agentPath;
|
||||
if (packageName === 'core') {
|
||||
// For core, use the normal search
|
||||
agentPath = await this.findAgentPath(agentId, installDir);
|
||||
} else {
|
||||
// For expansion packs, first try to find the agent in the expansion pack directory
|
||||
const expansionPackPath = path.join(installDir, rootPath, 'agents', `${agentId}.md`);
|
||||
if (await fileManager.pathExists(expansionPackPath)) {
|
||||
agentPath = expansionPackPath;
|
||||
} else {
|
||||
// Fall back to core if not found in expansion pack
|
||||
agentPath = await this.findAgentPath(agentId, installDir);
|
||||
}
|
||||
}
|
||||
|
||||
const commandPath = path.join(agentsDir, `${agentId}.md`);
|
||||
|
||||
if (agentPath) {
|
||||
// Create command file with agent content
|
||||
let agentContent = await fileManager.readFile(agentPath);
|
||||
|
||||
// Replace {root} placeholder with the appropriate root path for this context
|
||||
agentContent = agentContent.replaceAll('{root}', rootPath);
|
||||
|
||||
// Add command header
|
||||
let commandContent = `# /${agentId} Command\n\n`;
|
||||
commandContent += `When this command is used, adopt the following agent persona:\n\n`;
|
||||
commandContent += agentContent;
|
||||
|
||||
await fileManager.writeFile(commandPath, commandContent);
|
||||
console.log(chalk.green(`✓ Created agent command: /${agentId}`));
|
||||
}
|
||||
}
|
||||
|
||||
// Setup tasks
|
||||
for (const taskId of taskIds) {
|
||||
// Find the task file - for expansion packs, prefer the expansion pack version
|
||||
let taskPath;
|
||||
if (packageName === 'core') {
|
||||
// For core, use the normal search
|
||||
taskPath = await this.findTaskPath(taskId, installDir);
|
||||
} else {
|
||||
// For expansion packs, first try to find the task in the expansion pack directory
|
||||
const expansionPackPath = path.join(installDir, rootPath, 'tasks', `${taskId}.md`);
|
||||
if (await fileManager.pathExists(expansionPackPath)) {
|
||||
taskPath = expansionPackPath;
|
||||
} else {
|
||||
// Fall back to core if not found in expansion pack
|
||||
taskPath = await this.findTaskPath(taskId, installDir);
|
||||
}
|
||||
}
|
||||
|
||||
const commandPath = path.join(tasksDir, `${taskId}.md`);
|
||||
|
||||
if (taskPath) {
|
||||
// Create command file with task content
|
||||
let taskContent = await fileManager.readFile(taskPath);
|
||||
|
||||
// Replace {root} placeholder with the appropriate root path for this context
|
||||
taskContent = taskContent.replaceAll('{root}', rootPath);
|
||||
|
||||
// Add command header
|
||||
let commandContent = `# /${taskId} Task\n\n`;
|
||||
commandContent += `When this command is used, execute the following task:\n\n`;
|
||||
commandContent += taskContent;
|
||||
|
||||
await fileManager.writeFile(commandPath, commandContent);
|
||||
console.log(chalk.green(`✓ Created task command: /${taskId}`));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
chalk.green(`\n✓ Created iFlow CLI commands for ${packageName} in ${commandsBaseDir}`),
|
||||
);
|
||||
console.log(chalk.dim(` - Agents in: ${agentsDir}`));
|
||||
console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
|
||||
}
|
||||
|
||||
async setupCrushForPackage(installDir, packageName, slashPrefix, agentIds, taskIds, rootPath) {
|
||||
const commandsBaseDir = path.join(installDir, '.crush', 'commands', slashPrefix);
|
||||
const agentsDir = path.join(commandsBaseDir, 'agents');
|
||||
|
|
@ -690,6 +821,7 @@ class IdeSetup extends BaseIdeSetup {
|
|||
|
||||
async getCoreTaskIds(installDir) {
|
||||
const allTaskIds = [];
|
||||
const glob = require('glob');
|
||||
|
||||
// Check core tasks in .bmad-core or root only
|
||||
let tasksDir = path.join(installDir, '.bmad-core', 'tasks');
|
||||
|
|
@ -698,7 +830,6 @@ class IdeSetup extends BaseIdeSetup {
|
|||
}
|
||||
|
||||
if (await fileManager.pathExists(tasksDir)) {
|
||||
const glob = require('glob');
|
||||
const taskFiles = glob.sync('*.md', { cwd: tasksDir });
|
||||
allTaskIds.push(...taskFiles.map((file) => path.basename(file, '.md')));
|
||||
}
|
||||
|
|
@ -706,6 +837,7 @@ class IdeSetup extends BaseIdeSetup {
|
|||
// Check common tasks
|
||||
const commonTasksDir = path.join(installDir, 'common', 'tasks');
|
||||
if (await fileManager.pathExists(commonTasksDir)) {
|
||||
const glob = require('glob');
|
||||
const commonTaskFiles = glob.sync('*.md', { cwd: commonTasksDir });
|
||||
allTaskIds.push(...commonTaskFiles.map((file) => path.basename(file, '.md')));
|
||||
}
|
||||
|
|
@ -1208,97 +1340,77 @@ class IdeSetup extends BaseIdeSetup {
|
|||
return true;
|
||||
}
|
||||
|
||||
async setupGeminiCli(installDir) {
|
||||
const geminiDir = path.join(installDir, '.gemini');
|
||||
const bmadMethodDir = path.join(geminiDir, 'bmad-method');
|
||||
await fileManager.ensureDirectory(bmadMethodDir);
|
||||
async setupGeminiCli(installDir, selectedAgent) {
|
||||
const ideConfig = await configLoader.getIdeConfiguration('gemini');
|
||||
const bmadCommandsDir = path.join(installDir, ideConfig['rule-dir']);
|
||||
|
||||
// Update logic for existing settings.json
|
||||
const settingsPath = path.join(geminiDir, 'settings.json');
|
||||
if (await fileManager.pathExists(settingsPath)) {
|
||||
try {
|
||||
const settingsContent = await fileManager.readFile(settingsPath);
|
||||
const settings = JSON.parse(settingsContent);
|
||||
let updated = false;
|
||||
|
||||
// Handle contextFileName property
|
||||
if (settings.contextFileName && Array.isArray(settings.contextFileName)) {
|
||||
const originalLength = settings.contextFileName.length;
|
||||
settings.contextFileName = settings.contextFileName.filter(
|
||||
(fileName) => !fileName.startsWith('agents/'),
|
||||
);
|
||||
if (settings.contextFileName.length !== originalLength) {
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
await fileManager.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
||||
console.log(
|
||||
chalk.green('✓ Updated .gemini/settings.json - removed agent file references'),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(chalk.yellow('Could not update .gemini/settings.json'), error);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove old agents directory
|
||||
const agentsDir = path.join(geminiDir, 'agents');
|
||||
if (await fileManager.pathExists(agentsDir)) {
|
||||
await fileManager.removeDirectory(agentsDir);
|
||||
console.log(chalk.green('✓ Removed old .gemini/agents directory'));
|
||||
}
|
||||
|
||||
// Get all available agents
|
||||
const agents = await this.getAllAgentIds(installDir);
|
||||
let concatenatedContent = '';
|
||||
const agentCommandsDir = path.join(bmadCommandsDir, 'agents');
|
||||
const taskCommandsDir = path.join(bmadCommandsDir, 'tasks');
|
||||
await fileManager.ensureDirectory(agentCommandsDir);
|
||||
await fileManager.ensureDirectory(taskCommandsDir);
|
||||
|
||||
// Process Agents
|
||||
const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
|
||||
for (const agentId of agents) {
|
||||
// Find the source agent file
|
||||
const agentPath = await this.findAgentPath(agentId, installDir);
|
||||
|
||||
if (agentPath) {
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
|
||||
// Create properly formatted agent rule content (similar to trae)
|
||||
let agentRuleContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`;
|
||||
agentRuleContent += `This rule is triggered when the user types \`*${agentId}\` and activates the ${await this.getAgentTitle(
|
||||
agentId,
|
||||
installDir,
|
||||
)} agent persona.\n\n`;
|
||||
agentRuleContent += '## Agent Activation\n\n';
|
||||
agentRuleContent +=
|
||||
'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:\n\n';
|
||||
agentRuleContent += '```yaml\n';
|
||||
// Extract just the YAML content from the agent file
|
||||
const yamlContent = extractYamlFromAgent(agentContent);
|
||||
if (yamlContent) {
|
||||
agentRuleContent += yamlContent;
|
||||
} else {
|
||||
// If no YAML found, include the whole content minus the header
|
||||
agentRuleContent += agentContent.replace(/^#.*$/m, '').trim();
|
||||
}
|
||||
agentRuleContent += '\n```\n\n';
|
||||
agentRuleContent += '## File Reference\n\n';
|
||||
const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/');
|
||||
agentRuleContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
|
||||
agentRuleContent += '## Usage\n\n';
|
||||
agentRuleContent += `When the user types \`*${agentId}\`, activate this ${await this.getAgentTitle(
|
||||
agentId,
|
||||
installDir,
|
||||
)} persona and follow all instructions defined in the YAML configuration above.\n`;
|
||||
|
||||
// Add to concatenated content with separator
|
||||
concatenatedContent += agentRuleContent + '\n\n---\n\n';
|
||||
console.log(chalk.green(`✓ Added context for @${agentId}`));
|
||||
}
|
||||
if (!agentPath) {
|
||||
console.log(chalk.yellow(`✗ Agent file not found for ${agentId}, skipping.`));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Write the concatenated content to GEMINI.md
|
||||
const geminiMdPath = path.join(bmadMethodDir, 'GEMINI.md');
|
||||
await fileManager.writeFile(geminiMdPath, concatenatedContent);
|
||||
console.log(chalk.green(`\n✓ Created GEMINI.md in ${bmadMethodDir}`));
|
||||
const agentTitle = await this.getAgentTitle(agentId, installDir);
|
||||
const commandPath = path.join(agentCommandsDir, `${agentId}.toml`);
|
||||
|
||||
// Get relative path from installDir to agent file for @{file} reference
|
||||
const relativeAgentPath = path.relative(installDir, agentPath).replaceAll('\\', '/');
|
||||
|
||||
const tomlContent = `description = "Activates the ${agentTitle} agent from the BMad Method."
|
||||
prompt = """
|
||||
CRITICAL: You are now the BMad '${agentTitle}' agent. Adopt its persona, follow its instructions, and use its capabilities. The full agent definition is below.
|
||||
|
||||
@{${relativeAgentPath}}
|
||||
"""`;
|
||||
|
||||
await fileManager.writeFile(commandPath, tomlContent);
|
||||
console.log(chalk.green(`✓ Created agent command: /bmad:agents:${agentId}`));
|
||||
}
|
||||
|
||||
// Process Tasks
|
||||
const tasks = await this.getAllTaskIds(installDir);
|
||||
for (const taskId of tasks) {
|
||||
const taskPath = await this.findTaskPath(taskId, installDir);
|
||||
if (!taskPath) {
|
||||
console.log(chalk.yellow(`✗ Task file not found for ${taskId}, skipping.`));
|
||||
continue;
|
||||
}
|
||||
|
||||
const taskTitle = taskId
|
||||
.split('-')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
const commandPath = path.join(taskCommandsDir, `${taskId}.toml`);
|
||||
|
||||
// Get relative path from installDir to task file for @{file} reference
|
||||
const relativeTaskPath = path.relative(installDir, taskPath).replaceAll('\\', '/');
|
||||
|
||||
const tomlContent = `description = "Executes the BMad Task: ${taskTitle}"
|
||||
prompt = """
|
||||
CRITICAL: You are to execute the BMad Task defined below.
|
||||
|
||||
@{${relativeTaskPath}}
|
||||
"""`;
|
||||
|
||||
await fileManager.writeFile(commandPath, tomlContent);
|
||||
console.log(chalk.green(`✓ Created task command: /bmad:tasks:${taskId}`));
|
||||
}
|
||||
|
||||
console.log(
|
||||
chalk.green(`
|
||||
✓ Created Gemini CLI extension in ${bmadCommandsDir}`),
|
||||
);
|
||||
console.log(
|
||||
chalk.dim('You can now use commands like /bmad:agents:dev or /bmad:tasks:create-doc.'),
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -492,6 +492,7 @@ class V3ToV4Upgrader {
|
|||
const ideMessages = {
|
||||
cursor: 'Rules created in .cursor/rules/bmad/',
|
||||
'claude-code': 'Commands created in .claude/commands/BMad/',
|
||||
'iflow-cli': 'Commands created in .iflow/commands/BMad/',
|
||||
windsurf: 'Rules created in .windsurf/workflows/',
|
||||
trae: 'Rules created in.trae/rules/',
|
||||
roo: 'Custom modes created in .roomodes',
|
||||
|
|
|
|||
Loading…
Reference in New Issue