mirror of
https://github.com/bmadcode/BMAD-METHOD.git
synced 2025-12-29 16:14:59 +00:00
feat: v6.0.0-alpha.0 - the future is now
This commit is contained in:
@@ -1,175 +0,0 @@
|
||||
const fs = require('node:fs').promises;
|
||||
const path = require('node:path');
|
||||
const yaml = require('js-yaml');
|
||||
const { extractYamlFromAgent } = require('./yaml-utils');
|
||||
|
||||
class DependencyResolver {
|
||||
constructor(rootDir) {
|
||||
this.rootDir = rootDir;
|
||||
this.bmadCore = path.join(rootDir, 'bmad-core');
|
||||
this.common = path.join(rootDir, 'common');
|
||||
this.cache = new Map();
|
||||
}
|
||||
|
||||
async resolveAgentDependencies(agentId) {
|
||||
const agentPath = path.join(this.bmadCore, 'agents', `${agentId}.md`);
|
||||
const agentContent = await fs.readFile(agentPath, 'utf8');
|
||||
|
||||
// Extract YAML from markdown content with command cleaning
|
||||
const yamlContent = extractYamlFromAgent(agentContent, true);
|
||||
if (!yamlContent) {
|
||||
throw new Error(`No YAML configuration found in agent ${agentId}`);
|
||||
}
|
||||
|
||||
const agentConfig = yaml.load(yamlContent);
|
||||
|
||||
const dependencies = {
|
||||
agent: {
|
||||
id: agentId,
|
||||
path: agentPath,
|
||||
content: agentContent,
|
||||
config: agentConfig,
|
||||
},
|
||||
resources: [],
|
||||
};
|
||||
|
||||
// Personas are now embedded in agent configs, no need to resolve separately
|
||||
|
||||
// Resolve other dependencies
|
||||
const depTypes = ['tasks', 'templates', 'checklists', 'data', 'utils'];
|
||||
for (const depType of depTypes) {
|
||||
const deps = agentConfig.dependencies?.[depType] || [];
|
||||
for (const depId of deps) {
|
||||
const resource = await this.loadResource(depType, depId);
|
||||
if (resource) dependencies.resources.push(resource);
|
||||
}
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
async resolveTeamDependencies(teamId) {
|
||||
const teamPath = path.join(this.bmadCore, 'agent-teams', `${teamId}.yaml`);
|
||||
const teamContent = await fs.readFile(teamPath, 'utf8');
|
||||
const teamConfig = yaml.load(teamContent);
|
||||
|
||||
const dependencies = {
|
||||
team: {
|
||||
id: teamId,
|
||||
path: teamPath,
|
||||
content: teamContent,
|
||||
config: teamConfig,
|
||||
},
|
||||
agents: [],
|
||||
resources: new Map(), // Use Map to deduplicate resources
|
||||
};
|
||||
|
||||
// Always add bmad-orchestrator agent first if it's a team
|
||||
const bmadAgent = await this.resolveAgentDependencies('bmad-orchestrator');
|
||||
dependencies.agents.push(bmadAgent.agent);
|
||||
for (const res of bmadAgent.resources) {
|
||||
dependencies.resources.set(res.path, res);
|
||||
}
|
||||
|
||||
// Resolve all agents in the team
|
||||
let agentsToResolve = teamConfig.agents || [];
|
||||
|
||||
// Handle wildcard "*" - include all agents except bmad-master
|
||||
if (agentsToResolve.includes('*')) {
|
||||
const allAgents = await this.listAgents();
|
||||
// Remove wildcard and add all agents except those already in the list and bmad-master
|
||||
agentsToResolve = agentsToResolve.filter((a) => a !== '*');
|
||||
for (const agent of allAgents) {
|
||||
if (!agentsToResolve.includes(agent) && agent !== 'bmad-master') {
|
||||
agentsToResolve.push(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const agentId of agentsToResolve) {
|
||||
if (agentId === 'bmad-orchestrator' || agentId === 'bmad-master') continue; // Already added or excluded
|
||||
const agentDeps = await this.resolveAgentDependencies(agentId);
|
||||
dependencies.agents.push(agentDeps.agent);
|
||||
|
||||
// Add resources with deduplication
|
||||
for (const res of agentDeps.resources) {
|
||||
dependencies.resources.set(res.path, res);
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve workflows
|
||||
for (const workflowId of teamConfig.workflows || []) {
|
||||
const resource = await this.loadResource('workflows', workflowId);
|
||||
if (resource) dependencies.resources.set(resource.path, resource);
|
||||
}
|
||||
|
||||
// Convert Map back to array
|
||||
dependencies.resources = [...dependencies.resources.values()];
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
async loadResource(type, id) {
|
||||
const cacheKey = `${type}#${id}`;
|
||||
if (this.cache.has(cacheKey)) {
|
||||
return this.cache.get(cacheKey);
|
||||
}
|
||||
|
||||
try {
|
||||
let content = null;
|
||||
let filePath = null;
|
||||
|
||||
// First try bmad-core
|
||||
try {
|
||||
filePath = path.join(this.bmadCore, type, id);
|
||||
content = await fs.readFile(filePath, 'utf8');
|
||||
} catch {
|
||||
// If not found in bmad-core, try common folder
|
||||
try {
|
||||
filePath = path.join(this.common, type, id);
|
||||
content = await fs.readFile(filePath, 'utf8');
|
||||
} catch {
|
||||
// File not found in either location
|
||||
}
|
||||
}
|
||||
|
||||
if (!content) {
|
||||
console.warn(`Resource not found: ${type}/${id}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const resource = {
|
||||
type,
|
||||
id,
|
||||
path: filePath,
|
||||
content,
|
||||
};
|
||||
|
||||
this.cache.set(cacheKey, resource);
|
||||
return resource;
|
||||
} catch (error) {
|
||||
console.error(`Error loading resource ${type}/${id}:`, error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async listAgents() {
|
||||
try {
|
||||
const files = await fs.readdir(path.join(this.bmadCore, 'agents'));
|
||||
return files.filter((f) => f.endsWith('.md')).map((f) => f.replace('.md', ''));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async listTeams() {
|
||||
try {
|
||||
const files = await fs.readdir(path.join(this.bmadCore, 'agent-teams'));
|
||||
return files.filter((f) => f.endsWith('.yaml')).map((f) => f.replace('.yaml', ''));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DependencyResolver;
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Utility functions for YAML extraction from agent files
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extract YAML content from agent markdown files
|
||||
* @param {string} agentContent - The full content of the agent file
|
||||
* @param {boolean} cleanCommands - Whether to clean command descriptions (default: false)
|
||||
* @returns {string|null} - The extracted YAML content or null if not found
|
||||
*/
|
||||
function extractYamlFromAgent(agentContent, cleanCommands = false) {
|
||||
// Remove carriage returns and match YAML block
|
||||
const yamlMatch = agentContent.replaceAll('\r', '').match(/```ya?ml\n([\s\S]*?)\n```/);
|
||||
if (!yamlMatch) return null;
|
||||
|
||||
let yamlContent = yamlMatch[1].trim();
|
||||
|
||||
// Clean up command descriptions if requested
|
||||
// Converts "- command - description" to just "- command"
|
||||
if (cleanCommands) {
|
||||
yamlContent = yamlContent.replaceAll(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, '$1$2');
|
||||
}
|
||||
|
||||
return yamlContent;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
extractYamlFromAgent,
|
||||
};
|
||||
Reference in New Issue
Block a user