feat: install for ide now sets up rules also for expansion agents!
This commit is contained in:
41
test-ide-paths.js
Normal file
41
test-ide-paths.js
Normal file
@@ -0,0 +1,41 @@
|
||||
// Test script to verify IDE setup paths for expansion pack agents
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
// Simulate the findAgentPath logic
|
||||
function simulateFindAgentPath(agentId, installDir) {
|
||||
const possiblePaths = [
|
||||
path.join(installDir, ".bmad-core", "agents", `${agentId}.md`),
|
||||
path.join(installDir, "agents", `${agentId}.md`),
|
||||
// Expansion pack paths
|
||||
path.join(installDir, ".bmad-2d-phaser-game-dev", "agents", `${agentId}.md`),
|
||||
path.join(installDir, ".bmad-infrastructure-devops", "agents", `${agentId}.md`),
|
||||
path.join(installDir, ".bmad-creator-tools", "agents", `${agentId}.md`)
|
||||
];
|
||||
|
||||
// Simulate finding the agent in an expansion pack
|
||||
if (agentId === 'game-developer') {
|
||||
return path.join(installDir, ".bmad-2d-phaser-game-dev", "agents", `${agentId}.md`);
|
||||
}
|
||||
|
||||
// Default to core
|
||||
return path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
|
||||
}
|
||||
|
||||
// Test different scenarios
|
||||
const testDir = '/project';
|
||||
const agents = ['dev', 'game-developer', 'infra-devops-platform'];
|
||||
|
||||
console.log('Testing IDE path references:\n');
|
||||
|
||||
agents.forEach(agentId => {
|
||||
const agentPath = simulateFindAgentPath(agentId, testDir);
|
||||
const relativePath = path.relative(testDir, agentPath).replace(/\\/g, '/');
|
||||
|
||||
console.log(`Agent: ${agentId}`);
|
||||
console.log(` Full path: ${agentPath}`);
|
||||
console.log(` Relative path: ${relativePath}`);
|
||||
console.log(` Roo customInstructions: CRITICAL Read the full YML from ${relativePath} ...`);
|
||||
console.log(` Cursor MDC reference: [${relativePath}](mdc:${relativePath})`);
|
||||
console.log('');
|
||||
});
|
||||
58
tools/installer/config/ide-agent-config.yml
Normal file
58
tools/installer/config/ide-agent-config.yml
Normal file
@@ -0,0 +1,58 @@
|
||||
# IDE-specific agent configurations
|
||||
# This file defines agent-specific settings for different IDEs
|
||||
|
||||
# Roo Code file permissions
|
||||
# Each agent can have restricted file access based on regex patterns
|
||||
# If an agent is not listed here, it gets full edit access
|
||||
roo-permissions:
|
||||
# Core agents
|
||||
analyst:
|
||||
fileRegex: "\\.(md|txt)$"
|
||||
description: "Documentation and text files"
|
||||
pm:
|
||||
fileRegex: "\\.(md|txt)$"
|
||||
description: "Product documentation"
|
||||
architect:
|
||||
fileRegex: "\\.(md|txt|yml|yaml|json)$"
|
||||
description: "Architecture docs and configs"
|
||||
qa:
|
||||
fileRegex: "\\.(test|spec)\\.(js|ts|jsx|tsx)$|\\.md$"
|
||||
description: "Test files and documentation"
|
||||
ux-expert:
|
||||
fileRegex: "\\.(md|css|scss|html|jsx|tsx)$"
|
||||
description: "Design-related files"
|
||||
po:
|
||||
fileRegex: "\\.(md|txt)$"
|
||||
description: "Story and requirement docs"
|
||||
sm:
|
||||
fileRegex: "\\.(md|txt)$"
|
||||
description: "Process and planning docs"
|
||||
# Expansion pack agents
|
||||
game-designer:
|
||||
fileRegex: "\\.(md|txt|json|yaml|yml)$"
|
||||
description: "Game design documents and configs"
|
||||
game-sm:
|
||||
fileRegex: "\\.(md|txt)$"
|
||||
description: "Game project management docs"
|
||||
|
||||
# Cline agent ordering
|
||||
# Lower numbers appear first in the list
|
||||
# Agents not listed get order 99
|
||||
cline-order:
|
||||
# Core agents
|
||||
bmad-master: 1
|
||||
bmad-orchestrator: 2
|
||||
pm: 3
|
||||
analyst: 4
|
||||
architect: 5
|
||||
po: 6
|
||||
sm: 7
|
||||
dev: 8
|
||||
qa: 9
|
||||
ux-expert: 10
|
||||
# Expansion pack agents
|
||||
bmad-the-creator: 11
|
||||
game-designer: 12
|
||||
game-developer: 13
|
||||
game-sm: 14
|
||||
infra-devops-platform: 15
|
||||
@@ -1,4 +1,6 @@
|
||||
const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const yaml = require("js-yaml");
|
||||
const fileManager = require("./file-manager");
|
||||
const configLoader = require("./config-loader");
|
||||
|
||||
@@ -13,6 +15,27 @@ async function initializeModules() {
|
||||
}
|
||||
|
||||
class IdeSetup {
|
||||
constructor() {
|
||||
this.ideAgentConfig = null;
|
||||
}
|
||||
|
||||
async loadIdeAgentConfig() {
|
||||
if (this.ideAgentConfig) return this.ideAgentConfig;
|
||||
|
||||
try {
|
||||
const configPath = path.join(__dirname, '..', 'config', 'ide-agent-config.yml');
|
||||
const configContent = await fs.readFile(configPath, 'utf8');
|
||||
this.ideAgentConfig = yaml.load(configContent);
|
||||
return this.ideAgentConfig;
|
||||
} catch (error) {
|
||||
console.warn('Failed to load IDE agent configuration, using defaults');
|
||||
return {
|
||||
'roo-permissions': {},
|
||||
'cline-order': {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async setup(ide, installDir, selectedAgent = null) {
|
||||
await initializeModules();
|
||||
const ideConfig = await configLoader.getIdeConfiguration(ide);
|
||||
@@ -48,13 +71,10 @@ class IdeSetup {
|
||||
await fileManager.ensureDirectory(cursorRulesDir);
|
||||
|
||||
for (const agentId of agents) {
|
||||
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
|
||||
let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
|
||||
if (!(await fileManager.pathExists(agentPath))) {
|
||||
agentPath = path.join(installDir, "agents", `${agentId}.md`);
|
||||
}
|
||||
// Find the agent file
|
||||
const agentPath = await this.findAgentPath(agentId, installDir);
|
||||
|
||||
if (await fileManager.pathExists(agentPath)) {
|
||||
if (agentPath) {
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
const mdcPath = path.join(cursorRulesDir, `${agentId}.mdc`);
|
||||
|
||||
@@ -83,7 +103,8 @@ class IdeSetup {
|
||||
}
|
||||
mdcContent += "\n```\n\n";
|
||||
mdcContent += "## File Reference\n\n";
|
||||
mdcContent += `The complete agent definition is available in [.bmad-core/agents/${agentId}.md](mdc:.bmad-core/agents/${agentId}.md).\n\n`;
|
||||
const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
|
||||
mdcContent += `The complete agent definition is available in [${relativePath}](mdc:${relativePath}).\n\n`;
|
||||
mdcContent += "## Usage\n\n";
|
||||
mdcContent += `When the user types \`@${agentId}\`, activate this ${await this.getAgentTitle(
|
||||
agentId,
|
||||
@@ -107,14 +128,11 @@ class IdeSetup {
|
||||
await fileManager.ensureDirectory(commandsDir);
|
||||
|
||||
for (const agentId of agents) {
|
||||
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
|
||||
let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
|
||||
if (!(await fileManager.pathExists(agentPath))) {
|
||||
agentPath = path.join(installDir, "agents", `${agentId}.md`);
|
||||
}
|
||||
// Find the agent file
|
||||
const agentPath = await this.findAgentPath(agentId, installDir);
|
||||
const commandPath = path.join(commandsDir, `${agentId}.md`);
|
||||
|
||||
if (await fileManager.pathExists(agentPath)) {
|
||||
if (agentPath) {
|
||||
// Create command file with agent content
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
|
||||
@@ -140,13 +158,10 @@ class IdeSetup {
|
||||
await fileManager.ensureDirectory(windsurfRulesDir);
|
||||
|
||||
for (const agentId of agents) {
|
||||
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
|
||||
let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
|
||||
if (!(await fileManager.pathExists(agentPath))) {
|
||||
agentPath = path.join(installDir, "agents", `${agentId}.md`);
|
||||
}
|
||||
// Find the agent file
|
||||
const agentPath = await this.findAgentPath(agentId, installDir);
|
||||
|
||||
if (await fileManager.pathExists(agentPath)) {
|
||||
if (agentPath) {
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
const mdPath = path.join(windsurfRulesDir, `${agentId}.md`);
|
||||
|
||||
@@ -170,7 +185,8 @@ class IdeSetup {
|
||||
}
|
||||
mdContent += "\n```\n\n";
|
||||
mdContent += "## File Reference\n\n";
|
||||
mdContent += `The complete agent definition is available in [.bmad-core/agents/${agentId}.md](.bmad-core/agents/${agentId}.md).\n\n`;
|
||||
const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
|
||||
mdContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
|
||||
mdContent += "## Usage\n\n";
|
||||
mdContent += `When the user types \`@${agentId}\`, activate this ${await this.getAgentTitle(
|
||||
agentId,
|
||||
@@ -187,39 +203,86 @@ class IdeSetup {
|
||||
return true;
|
||||
}
|
||||
|
||||
async findAgentPath(agentId, installDir) {
|
||||
// Try to find the agent file in various locations
|
||||
const possiblePaths = [
|
||||
path.join(installDir, ".bmad-core", "agents", `${agentId}.md`),
|
||||
path.join(installDir, "agents", `${agentId}.md`)
|
||||
];
|
||||
|
||||
// Also check expansion pack directories
|
||||
const glob = require("glob");
|
||||
const expansionDirs = glob.sync(".*/agents", { cwd: installDir });
|
||||
for (const expDir of expansionDirs) {
|
||||
possiblePaths.push(path.join(installDir, expDir, `${agentId}.md`));
|
||||
}
|
||||
|
||||
for (const agentPath of possiblePaths) {
|
||||
if (await fileManager.pathExists(agentPath)) {
|
||||
return agentPath;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async getAllAgentIds(installDir) {
|
||||
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
|
||||
const glob = require("glob");
|
||||
const allAgentIds = [];
|
||||
|
||||
// Check core agents in .bmad-core or root
|
||||
let agentsDir = path.join(installDir, ".bmad-core", "agents");
|
||||
if (!(await fileManager.pathExists(agentsDir))) {
|
||||
agentsDir = path.join(installDir, "agents");
|
||||
}
|
||||
|
||||
const glob = require("glob");
|
||||
const agentFiles = glob.sync("*.md", { cwd: agentsDir });
|
||||
return agentFiles.map((file) => path.basename(file, ".md"));
|
||||
|
||||
if (await fileManager.pathExists(agentsDir)) {
|
||||
const agentFiles = glob.sync("*.md", { cwd: agentsDir });
|
||||
allAgentIds.push(...agentFiles.map((file) => path.basename(file, ".md")));
|
||||
}
|
||||
|
||||
// Also check for expansion pack agents in dot folders
|
||||
const expansionDirs = glob.sync(".*/agents", { cwd: installDir });
|
||||
for (const expDir of expansionDirs) {
|
||||
const fullExpDir = path.join(installDir, expDir);
|
||||
const expAgentFiles = glob.sync("*.md", { cwd: fullExpDir });
|
||||
allAgentIds.push(...expAgentFiles.map((file) => path.basename(file, ".md")));
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
return [...new Set(allAgentIds)];
|
||||
}
|
||||
|
||||
async getAgentTitle(agentId, installDir) {
|
||||
// Try to read the actual agent file to get the title
|
||||
let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
|
||||
if (!(await fileManager.pathExists(agentPath))) {
|
||||
agentPath = path.join(installDir, "agents", `${agentId}.md`);
|
||||
// Try to find the agent file in various locations
|
||||
const possiblePaths = [
|
||||
path.join(installDir, ".bmad-core", "agents", `${agentId}.md`),
|
||||
path.join(installDir, "agents", `${agentId}.md`)
|
||||
];
|
||||
|
||||
// Also check expansion pack directories
|
||||
const glob = require("glob");
|
||||
const expansionDirs = glob.sync(".*/agents", { cwd: installDir });
|
||||
for (const expDir of expansionDirs) {
|
||||
possiblePaths.push(path.join(installDir, expDir, `${agentId}.md`));
|
||||
}
|
||||
|
||||
if (await fileManager.pathExists(agentPath)) {
|
||||
try {
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
|
||||
|
||||
if (yamlMatch) {
|
||||
const yaml = yamlMatch[1];
|
||||
const titleMatch = yaml.match(/title:\s*(.+)/);
|
||||
if (titleMatch) {
|
||||
return titleMatch[1].trim();
|
||||
for (const agentPath of possiblePaths) {
|
||||
if (await fileManager.pathExists(agentPath)) {
|
||||
try {
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/);
|
||||
|
||||
if (yamlMatch) {
|
||||
const yaml = yamlMatch[1];
|
||||
const titleMatch = yaml.match(/title:\s*(.+)/);
|
||||
if (titleMatch) {
|
||||
return titleMatch[1].trim();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Failed to read agent title for ${agentId}: ${error.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Failed to read agent title for ${agentId}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,40 +313,9 @@ class IdeSetup {
|
||||
// Create new modes content
|
||||
let newModesContent = "";
|
||||
|
||||
// Define file permissions for each agent type
|
||||
const agentPermissions = {
|
||||
analyst: {
|
||||
fileRegex: "\\.(md|txt)$",
|
||||
description: "Documentation and text files",
|
||||
},
|
||||
pm: {
|
||||
fileRegex: "\\.(md|txt)$",
|
||||
description: "Product documentation",
|
||||
},
|
||||
architect: {
|
||||
fileRegex: "\\.(md|txt|yml|yaml|json)$",
|
||||
description: "Architecture docs and configs",
|
||||
},
|
||||
dev: null, // Full edit access
|
||||
qa: {
|
||||
fileRegex: "\\.(test|spec)\\.(js|ts|jsx|tsx)$|\\.md$",
|
||||
description: "Test files and documentation",
|
||||
},
|
||||
"ux-expert": {
|
||||
fileRegex: "\\.(md|css|scss|html|jsx|tsx)$",
|
||||
description: "Design-related files",
|
||||
},
|
||||
po: {
|
||||
fileRegex: "\\.(md|txt)$",
|
||||
description: "Story and requirement docs",
|
||||
},
|
||||
sm: {
|
||||
fileRegex: "\\.(md|txt)$",
|
||||
description: "Process and planning docs",
|
||||
},
|
||||
"bmad-orchestrator": null, // Full edit access
|
||||
"bmad-master": null, // Full edit access
|
||||
};
|
||||
// Load dynamic agent permissions from configuration
|
||||
const config = await this.loadIdeAgentConfig();
|
||||
const agentPermissions = config['roo-permissions'] || {};
|
||||
|
||||
for (const agentId of agents) {
|
||||
// Skip if already exists
|
||||
@@ -293,12 +325,9 @@ class IdeSetup {
|
||||
}
|
||||
|
||||
// Read agent file to extract all information
|
||||
let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
|
||||
if (!(await fileManager.pathExists(agentPath))) {
|
||||
agentPath = path.join(installDir, "agents", `${agentId}.md`);
|
||||
}
|
||||
const agentPath = await this.findAgentPath(agentId, installDir);
|
||||
|
||||
if (await fileManager.pathExists(agentPath)) {
|
||||
if (agentPath) {
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
|
||||
// Extract YAML content
|
||||
@@ -324,7 +353,9 @@ class IdeSetup {
|
||||
newModesContent += ` name: '${icon} ${title}'\n`;
|
||||
newModesContent += ` roleDefinition: ${roleDefinition}\n`;
|
||||
newModesContent += ` whenToUse: ${whenToUse}\n`;
|
||||
newModesContent += ` customInstructions: CRITICAL Read the full YML from .bmad-core/agents/${agentId}.md start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
|
||||
// Get relative path from installDir to agent file
|
||||
const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
|
||||
newModesContent += ` customInstructions: CRITICAL Read the full YML 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`;
|
||||
newModesContent += ` groups:\n`;
|
||||
newModesContent += ` - read\n`;
|
||||
|
||||
@@ -369,28 +400,15 @@ class IdeSetup {
|
||||
|
||||
await fileManager.ensureDirectory(clineRulesDir);
|
||||
|
||||
// Define agent order for numeric prefixes
|
||||
const agentOrder = {
|
||||
'bmad-master': 1,
|
||||
'bmad-orchestrator': 2,
|
||||
'pm': 3,
|
||||
'analyst': 4,
|
||||
'architect': 5,
|
||||
'po': 6,
|
||||
'sm': 7,
|
||||
'dev': 8,
|
||||
'qa': 9,
|
||||
'ux-expert': 10
|
||||
};
|
||||
// Load dynamic agent ordering from configuration
|
||||
const config = await this.loadIdeAgentConfig();
|
||||
const agentOrder = config['cline-order'] || {};
|
||||
|
||||
for (const agentId of agents) {
|
||||
// Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
|
||||
let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
|
||||
if (!(await fileManager.pathExists(agentPath))) {
|
||||
agentPath = path.join(installDir, "agents", `${agentId}.md`);
|
||||
}
|
||||
// Find the agent file
|
||||
const agentPath = await this.findAgentPath(agentId, installDir);
|
||||
|
||||
if (await fileManager.pathExists(agentPath)) {
|
||||
if (agentPath) {
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
|
||||
// Get numeric prefix for ordering
|
||||
@@ -418,7 +436,8 @@ class IdeSetup {
|
||||
mdContent += `- Always maintain consistency with project documentation in .bmad-core/\n`;
|
||||
mdContent += `- Follow the agent's specific guidelines and constraints\n`;
|
||||
mdContent += `- Update relevant project files when making changes\n`;
|
||||
mdContent += `- Reference the complete agent definition in [.bmad-core/agents/${agentId}.md](.bmad-core/agents/${agentId}.md)\n\n`;
|
||||
const relativePath = path.relative(installDir, agentPath).replace(/\\/g, '/');
|
||||
mdContent += `- Reference the complete agent definition in [${relativePath}](${relativePath})\n\n`;
|
||||
mdContent += "## Usage\n\n";
|
||||
mdContent += `Type \`@${agentId}\` to activate this ${await this.getAgentTitle(agentId, installDir)} persona.\n`;
|
||||
|
||||
@@ -444,12 +463,9 @@ class IdeSetup {
|
||||
|
||||
for (const agentId of agents) {
|
||||
// Find the source agent file
|
||||
let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
|
||||
if (!(await fileManager.pathExists(agentPath))) {
|
||||
agentPath = path.join(installDir, "agents", `${agentId}.md`);
|
||||
}
|
||||
const agentPath = await this.findAgentPath(agentId, installDir);
|
||||
|
||||
if (await fileManager.pathExists(agentPath)) {
|
||||
if (agentPath) {
|
||||
const agentContent = await fileManager.readFile(agentPath);
|
||||
const contextFilePath = path.join(agentsContextDir, `${agentId}.md`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user