fix: expansion pack builder now includes proper dependencies from core as needed, and default template file name save added to template llm instructions

This commit is contained in:
Brian Madison
2025-06-18 19:17:09 -05:00
parent 7f3a0be7e8
commit 9dded00356
18 changed files with 5805 additions and 2701 deletions

View File

@@ -237,7 +237,7 @@ class WebBuilder {
const agentContent = await fs.readFile(agentPath, 'utf8');
sections.push(this.formatSection(`agents#${agentName}`, agentContent));
// Resolve and add agent dependencies from expansion pack
// Resolve and add agent dependencies
const agentYaml = agentContent.match(/```yaml\n([\s\S]*?)\n```/);
if (agentYaml) {
try {
@@ -245,16 +245,43 @@ class WebBuilder {
const agentConfig = yaml.load(agentYaml[1]);
if (agentConfig.dependencies) {
// Add resources from expansion pack
// Add resources, first try expansion pack, then core
for (const [resourceType, resources] of Object.entries(agentConfig.dependencies)) {
if (Array.isArray(resources)) {
for (const resourceName of resources) {
const resourcePath = path.join(packDir, resourceType, `${resourceName}.md`);
try {
const resourceContent = await fs.readFile(resourcePath, 'utf8');
sections.push(this.formatSection(`${resourceType}#${resourceName}`, resourceContent));
} catch (error) {
// Resource might not exist in expansion pack, that's ok
let found = false;
const extensions = ['.md', '.yml', '.yaml'];
// Try expansion pack first
for (const ext of extensions) {
const resourcePath = path.join(packDir, resourceType, `${resourceName}${ext}`);
try {
const resourceContent = await fs.readFile(resourcePath, 'utf8');
sections.push(this.formatSection(`${resourceType}#${resourceName}`, resourceContent));
found = true;
break;
} catch (error) {
// Not in expansion pack, continue
}
}
// If not found in expansion pack, try core
if (!found) {
for (const ext of extensions) {
const corePath = path.join(this.rootDir, 'bmad-core', resourceType, `${resourceName}${ext}`);
try {
const coreContent = await fs.readFile(corePath, 'utf8');
sections.push(this.formatSection(`${resourceType}#${resourceName}`, coreContent));
found = true;
break;
} catch (error) {
// Not in core either, continue
}
}
}
if (!found) {
console.warn(` ⚠ Dependency ${resourceType}#${resourceName} not found in expansion pack or core`);
}
}
}
@@ -292,6 +319,22 @@ class WebBuilder {
console.warn(` ⚠ No agents directory found in ${packName}`);
}
// Build a map of all available expansion pack resources for override checking
const expansionResources = new Map();
const resourceDirs = ['templates', 'tasks', 'checklists', 'workflows', 'data'];
for (const resourceDir of resourceDirs) {
const resourcePath = path.join(packDir, resourceDir);
try {
const resourceFiles = await fs.readdir(resourcePath);
for (const resourceFile of resourceFiles.filter(f => f.endsWith('.md') || f.endsWith('.yml'))) {
const fileName = resourceFile.replace(/\.(md|yml)$/, '');
expansionResources.set(`${resourceDir}#${fileName}`, true);
}
} catch (error) {
// Directory might not exist, that's fine
}
}
// Process all agents listed in team configuration
const agentsToProcess = teamConfig.agents || [];
@@ -301,6 +344,9 @@ class WebBuilder {
agentsToProcess.unshift('bmad-orchestrator');
}
// Track all dependencies from all agents (deduplicated)
const allDependencies = new Map();
for (const agentId of agentsToProcess) {
if (expansionAgents.has(agentId)) {
@@ -308,20 +354,110 @@ class WebBuilder {
const agentPath = path.join(agentsDir, `${agentId}.md`);
const agentContent = await fs.readFile(agentPath, 'utf8');
sections.push(this.formatSection(`agents#${agentId}`, agentContent));
// Parse and collect dependencies from expansion agent
const agentYaml = agentContent.match(/```yaml\n([\s\S]*?)\n```/);
if (agentYaml) {
try {
const agentConfig = this.parseYaml(agentYaml[1]);
if (agentConfig.dependencies) {
for (const [resourceType, resources] of Object.entries(agentConfig.dependencies)) {
if (Array.isArray(resources)) {
for (const resourceName of resources) {
const key = `${resourceType}#${resourceName}`;
if (!allDependencies.has(key)) {
allDependencies.set(key, { type: resourceType, name: resourceName });
}
}
}
}
}
} catch (error) {
console.debug(`Failed to parse agent YAML for ${agentId}:`, error.message);
}
}
} else {
// Use core BMAD version
try {
const coreAgentPath = path.join(this.rootDir, 'bmad-core', 'agents', `${agentId}.md`);
const coreAgentContent = await fs.readFile(coreAgentPath, 'utf8');
sections.push(this.formatSection(`agents#${agentId}`, coreAgentContent));
// Parse and collect dependencies from core agent
const agentYaml = coreAgentContent.match(/```yaml\n([\s\S]*?)\n```/);
if (agentYaml) {
try {
// Clean up the YAML to handle command descriptions after dashes
let yamlContent = agentYaml[1];
yamlContent = yamlContent.replace(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, '$1$2');
const agentConfig = this.parseYaml(yamlContent);
if (agentConfig.dependencies) {
for (const [resourceType, resources] of Object.entries(agentConfig.dependencies)) {
if (Array.isArray(resources)) {
for (const resourceName of resources) {
const key = `${resourceType}#${resourceName}`;
if (!allDependencies.has(key)) {
allDependencies.set(key, { type: resourceType, name: resourceName });
}
}
}
}
}
} catch (error) {
console.debug(`Failed to parse agent YAML for ${agentId}:`, error.message);
}
}
} catch (error) {
console.warn(` ⚠ Agent ${agentId} not found in core or expansion pack`);
}
}
}
// Add expansion pack resources (templates, tasks, checklists)
const resourceDirs = ['templates', 'tasks', 'checklists', 'workflows', 'data'];
// Add all collected dependencies from agents
// Always prefer expansion pack versions if they exist
for (const [key, dep] of allDependencies) {
let found = false;
const extensions = ['.md', '.yml', '.yaml'];
// Always check expansion pack first, even if the dependency came from a core agent
if (expansionResources.has(key)) {
// We know it exists in expansion pack, find and load it
for (const ext of extensions) {
const expansionPath = path.join(packDir, dep.type, `${dep.name}${ext}`);
try {
const content = await fs.readFile(expansionPath, 'utf8');
sections.push(this.formatSection(key, content));
console.log(` ✓ Using expansion override for ${key}`);
found = true;
break;
} catch (error) {
// Try next extension
}
}
}
// If not found in expansion pack (or doesn't exist there), try core
if (!found) {
for (const ext of extensions) {
const corePath = path.join(this.rootDir, 'bmad-core', dep.type, `${dep.name}${ext}`);
try {
const content = await fs.readFile(corePath, 'utf8');
sections.push(this.formatSection(key, content));
found = true;
break;
} catch (error) {
// Not in core either, continue
}
}
}
if (!found) {
console.warn(` ⚠ Dependency ${key} not found in expansion pack or core`);
}
}
// Add remaining expansion pack resources not already included as dependencies
for (const resourceDir of resourceDirs) {
const resourcePath = path.join(packDir, resourceDir);
try {
@@ -330,7 +466,12 @@ class WebBuilder {
const filePath = path.join(resourcePath, resourceFile);
const fileContent = await fs.readFile(filePath, 'utf8');
const fileName = resourceFile.replace(/\.(md|yml)$/, '');
sections.push(this.formatSection(`${resourceDir}#${fileName}`, fileContent));
// Only add if not already included as a dependency
const resourceKey = `${resourceDir}#${fileName}`;
if (!allDependencies.has(resourceKey)) {
sections.push(this.formatSection(resourceKey, fileContent));
}
}
} catch (error) {
// Directory might not exist, that's fine