mirror of
https://github.com/bmadcode/BMAD-METHOD.git
synced 2025-12-29 16:14:59 +00:00
web build optimization
This commit is contained in:
@@ -21,6 +21,76 @@ class WebBuilder {
|
||||
return yaml.load(content);
|
||||
}
|
||||
|
||||
convertToWebPath(filePath, bundleRoot = 'bmad-core') {
|
||||
// Convert absolute paths to web bundle paths with dot prefix
|
||||
// All resources get installed under the bundle root, so use that path
|
||||
const relativePath = path.relative(this.rootDir, filePath);
|
||||
const pathParts = relativePath.split(path.sep);
|
||||
|
||||
let resourcePath;
|
||||
if (pathParts[0] === 'expansion-packs') {
|
||||
// For expansion packs, remove 'expansion-packs/packname' and use the rest
|
||||
resourcePath = pathParts.slice(2).join('/');
|
||||
} else {
|
||||
// For bmad-core, common, etc., remove the first part
|
||||
resourcePath = pathParts.slice(1).join('/');
|
||||
}
|
||||
|
||||
return `.${bundleRoot}/${resourcePath}`;
|
||||
}
|
||||
|
||||
generateWebInstructions(bundleType, packName = null) {
|
||||
// Generate dynamic web instructions based on bundle type
|
||||
const rootExample = packName ? `.${packName}` : '.bmad-core';
|
||||
const examplePath = packName ? `.${packName}/folder/filename.md` : '.bmad-core/folder/filename.md';
|
||||
const personasExample = packName ? `.${packName}/personas/analyst.md` : '.bmad-core/personas/analyst.md';
|
||||
const tasksExample = packName ? `.${packName}/tasks/create-story.md` : '.bmad-core/tasks/create-story.md';
|
||||
const utilsExample = packName ? `.${packName}/utils/template-format.md` : '.bmad-core/utils/template-format.md';
|
||||
const tasksRef = packName ? `.${packName}/tasks/create-story.md` : '.bmad-core/tasks/create-story.md';
|
||||
|
||||
return `# Web Agent Bundle Instructions
|
||||
|
||||
You are now operating as a specialized AI agent from the BMad-Method framework. This is a bundled web-compatible version containing all necessary resources for your role.
|
||||
|
||||
## Important Instructions
|
||||
|
||||
1. **Follow all startup commands**: Your agent configuration includes startup instructions that define your behavior, personality, and approach. These MUST be followed exactly.
|
||||
|
||||
2. **Resource Navigation**: This bundle contains all resources you need. Resources are marked with tags like:
|
||||
|
||||
- \`==================== START: ${examplePath} ====================\`
|
||||
- \`==================== END: ${examplePath} ====================\`
|
||||
|
||||
When you need to reference a resource mentioned in your instructions:
|
||||
|
||||
- Look for the corresponding START/END tags
|
||||
- The format is always the full path with dot prefix (e.g., \`${personasExample}\`, \`${tasksExample}\`)
|
||||
- If a section is specified (e.g., \`{root}/tasks/create-story.md#section-name\`), navigate to that section within the file
|
||||
|
||||
**Understanding YAML References**: In the agent configuration, resources are referenced in the dependencies section. For example:
|
||||
|
||||
\`\`\`yaml
|
||||
dependencies:
|
||||
utils:
|
||||
- template-format
|
||||
tasks:
|
||||
- create-story
|
||||
\`\`\`
|
||||
|
||||
These references map directly to bundle sections:
|
||||
|
||||
- \`utils: template-format\` → Look for \`==================== START: ${utilsExample} ====================\`
|
||||
- \`tasks: create-story\` → Look for \`==================== START: ${tasksRef} ====================\`
|
||||
|
||||
3. **Execution Context**: You are operating in a web environment. All your capabilities and knowledge are contained within this bundle. Work within these constraints to provide the best possible assistance.
|
||||
|
||||
4. **Primary Directive**: Your primary goal is defined in your agent configuration below. Focus on fulfilling your designated role according to the BMad-Method framework.
|
||||
|
||||
---
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
async cleanOutputDirs() {
|
||||
for (const dir of this.outputDirs) {
|
||||
try {
|
||||
@@ -73,16 +143,18 @@ class WebBuilder {
|
||||
|
||||
async buildAgentBundle(agentId) {
|
||||
const dependencies = await this.resolver.resolveAgentDependencies(agentId);
|
||||
const template = await fs.readFile(this.templatePath, "utf8");
|
||||
const template = this.generateWebInstructions('agent');
|
||||
|
||||
const sections = [template];
|
||||
|
||||
// Add agent configuration
|
||||
sections.push(this.formatSection(dependencies.agent.path, dependencies.agent.content));
|
||||
const agentPath = this.convertToWebPath(dependencies.agent.path, 'bmad-core');
|
||||
sections.push(this.formatSection(agentPath, dependencies.agent.content, 'bmad-core'));
|
||||
|
||||
// Add all dependencies
|
||||
for (const resource of dependencies.resources) {
|
||||
sections.push(this.formatSection(resource.path, resource.content));
|
||||
const resourcePath = this.convertToWebPath(resource.path, 'bmad-core');
|
||||
sections.push(this.formatSection(resourcePath, resource.content, 'bmad-core'));
|
||||
}
|
||||
|
||||
return sections.join("\n");
|
||||
@@ -90,21 +162,24 @@ class WebBuilder {
|
||||
|
||||
async buildTeamBundle(teamId) {
|
||||
const dependencies = await this.resolver.resolveTeamDependencies(teamId);
|
||||
const template = await fs.readFile(this.templatePath, "utf8");
|
||||
const template = this.generateWebInstructions('team');
|
||||
|
||||
const sections = [template];
|
||||
|
||||
// Add team configuration
|
||||
sections.push(this.formatSection(dependencies.team.path, dependencies.team.content));
|
||||
const teamPath = this.convertToWebPath(dependencies.team.path, 'bmad-core');
|
||||
sections.push(this.formatSection(teamPath, dependencies.team.content, 'bmad-core'));
|
||||
|
||||
// Add all agents
|
||||
for (const agent of dependencies.agents) {
|
||||
sections.push(this.formatSection(agent.path, agent.content));
|
||||
const agentPath = this.convertToWebPath(agent.path, 'bmad-core');
|
||||
sections.push(this.formatSection(agentPath, agent.content, 'bmad-core'));
|
||||
}
|
||||
|
||||
// Add all deduplicated resources
|
||||
for (const resource of dependencies.resources) {
|
||||
sections.push(this.formatSection(resource.path, resource.content));
|
||||
const resourcePath = this.convertToWebPath(resource.path, 'bmad-core');
|
||||
sections.push(this.formatSection(resourcePath, resource.content, 'bmad-core'));
|
||||
}
|
||||
|
||||
return sections.join("\n");
|
||||
@@ -161,14 +236,17 @@ class WebBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
formatSection(path, content) {
|
||||
formatSection(path, content, bundleRoot = 'bmad-core') {
|
||||
const separator = "====================";
|
||||
|
||||
// Process agent content if this is an agent file
|
||||
if (path.startsWith("agents#")) {
|
||||
if (path.includes("/agents/")) {
|
||||
content = this.processAgentContent(content);
|
||||
}
|
||||
|
||||
// Replace {root} references with the actual bundle root
|
||||
content = this.replaceRootReferences(content, bundleRoot);
|
||||
|
||||
return [
|
||||
`${separator} START: ${path} ${separator}`,
|
||||
content.trim(),
|
||||
@@ -177,6 +255,11 @@ class WebBuilder {
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
replaceRootReferences(content, bundleRoot) {
|
||||
// Replace {root} with the appropriate bundle root path
|
||||
return content.replace(/\{root\}/g, `.${bundleRoot}`);
|
||||
}
|
||||
|
||||
async validate() {
|
||||
console.log("Validating agent configurations...");
|
||||
const agents = await this.resolver.listAgents();
|
||||
@@ -288,13 +371,14 @@ class WebBuilder {
|
||||
}
|
||||
|
||||
async buildExpansionAgentBundle(packName, packDir, agentName) {
|
||||
const template = await fs.readFile(this.templatePath, "utf8");
|
||||
const template = this.generateWebInstructions('expansion-agent', packName);
|
||||
const sections = [template];
|
||||
|
||||
// Add agent configuration
|
||||
const agentPath = path.join(packDir, "agents", `${agentName}.md`);
|
||||
const agentContent = await fs.readFile(agentPath, "utf8");
|
||||
sections.push(this.formatSection(`agents#${agentName}`, agentContent));
|
||||
const agentWebPath = this.convertToWebPath(agentPath, packName);
|
||||
sections.push(this.formatSection(agentWebPath, agentContent, packName));
|
||||
|
||||
// Resolve and add agent dependencies
|
||||
const yamlContent = yamlUtils.extractYamlFromAgent(agentContent);
|
||||
@@ -316,8 +400,9 @@ class WebBuilder {
|
||||
const resourcePath = path.join(packDir, resourceType, `${resourceName}${ext}`);
|
||||
try {
|
||||
const resourceContent = await fs.readFile(resourcePath, "utf8");
|
||||
const resourceWebPath = this.convertToWebPath(resourcePath, packName);
|
||||
sections.push(
|
||||
this.formatSection(`${resourceType}#${resourceName}`, resourceContent)
|
||||
this.formatSection(resourceWebPath, resourceContent, packName)
|
||||
);
|
||||
found = true;
|
||||
break;
|
||||
@@ -337,8 +422,9 @@ class WebBuilder {
|
||||
);
|
||||
try {
|
||||
const coreContent = await fs.readFile(corePath, "utf8");
|
||||
const coreWebPath = this.convertToWebPath(corePath, packName);
|
||||
sections.push(
|
||||
this.formatSection(`${resourceType}#${resourceName}`, coreContent)
|
||||
this.formatSection(coreWebPath, coreContent, packName)
|
||||
);
|
||||
found = true;
|
||||
break;
|
||||
@@ -359,8 +445,9 @@ class WebBuilder {
|
||||
);
|
||||
try {
|
||||
const commonContent = await fs.readFile(commonPath, "utf8");
|
||||
const commonWebPath = this.convertToWebPath(commonPath, packName);
|
||||
sections.push(
|
||||
this.formatSection(`${resourceType}#${resourceName}`, commonContent)
|
||||
this.formatSection(commonWebPath, commonContent, packName)
|
||||
);
|
||||
found = true;
|
||||
break;
|
||||
@@ -388,7 +475,7 @@ class WebBuilder {
|
||||
}
|
||||
|
||||
async buildExpansionTeamBundle(packName, packDir, teamConfigPath) {
|
||||
const template = await fs.readFile(this.templatePath, "utf8");
|
||||
const template = this.generateWebInstructions('expansion-team', packName);
|
||||
|
||||
const sections = [template];
|
||||
|
||||
@@ -396,7 +483,8 @@ class WebBuilder {
|
||||
const teamContent = await fs.readFile(teamConfigPath, "utf8");
|
||||
const teamFileName = path.basename(teamConfigPath, ".yaml");
|
||||
const teamConfig = this.parseYaml(teamContent);
|
||||
sections.push(this.formatSection(`agent-teams#${teamFileName}`, teamContent));
|
||||
const teamWebPath = this.convertToWebPath(teamConfigPath, packName);
|
||||
sections.push(this.formatSection(teamWebPath, teamContent, packName));
|
||||
|
||||
// Get list of expansion pack agents
|
||||
const expansionAgents = new Set();
|
||||
@@ -446,7 +534,8 @@ class WebBuilder {
|
||||
// Use expansion pack version (override)
|
||||
const agentPath = path.join(agentsDir, `${agentId}.md`);
|
||||
const agentContent = await fs.readFile(agentPath, "utf8");
|
||||
sections.push(this.formatSection(`agents#${agentId}`, agentContent));
|
||||
const expansionAgentWebPath = this.convertToWebPath(agentPath, packName);
|
||||
sections.push(this.formatSection(expansionAgentWebPath, agentContent, packName));
|
||||
|
||||
// Parse and collect dependencies from expansion agent
|
||||
const agentYaml = agentContent.match(/```yaml\n([\s\S]*?)\n```/);
|
||||
@@ -474,7 +563,8 @@ class WebBuilder {
|
||||
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));
|
||||
const coreAgentWebPath = this.convertToWebPath(coreAgentPath, packName);
|
||||
sections.push(this.formatSection(coreAgentWebPath, coreAgentContent, packName));
|
||||
|
||||
// Parse and collect dependencies from core agent
|
||||
const yamlContent = yamlUtils.extractYamlFromAgent(coreAgentContent, true);
|
||||
@@ -516,7 +606,8 @@ class WebBuilder {
|
||||
const expansionPath = path.join(packDir, dep.type, `${dep.name}${ext}`);
|
||||
try {
|
||||
const content = await fs.readFile(expansionPath, "utf8");
|
||||
sections.push(this.formatSection(key, content));
|
||||
const expansionWebPath = this.convertToWebPath(expansionPath, packName);
|
||||
sections.push(this.formatSection(expansionWebPath, content, packName));
|
||||
console.log(` ✓ Using expansion override for ${key}`);
|
||||
found = true;
|
||||
break;
|
||||
@@ -532,7 +623,8 @@ class WebBuilder {
|
||||
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));
|
||||
const coreWebPath = this.convertToWebPath(corePath, packName);
|
||||
sections.push(this.formatSection(coreWebPath, content, packName));
|
||||
found = true;
|
||||
break;
|
||||
} catch (error) {
|
||||
@@ -547,7 +639,8 @@ class WebBuilder {
|
||||
const commonPath = path.join(this.rootDir, "common", dep.type, `${dep.name}${ext}`);
|
||||
try {
|
||||
const content = await fs.readFile(commonPath, "utf8");
|
||||
sections.push(this.formatSection(key, content));
|
||||
const commonWebPath = this.convertToWebPath(commonPath, packName);
|
||||
sections.push(this.formatSection(commonWebPath, content, packName));
|
||||
found = true;
|
||||
break;
|
||||
} catch (error) {
|
||||
@@ -576,7 +669,9 @@ class WebBuilder {
|
||||
// Only add if not already included as a dependency
|
||||
const resourceKey = `${resourceDir}#${fileName}`;
|
||||
if (!allDependencies.has(resourceKey)) {
|
||||
sections.push(this.formatSection(resourceKey, fileContent));
|
||||
const fullResourcePath = path.join(resourcePath, resourceFile);
|
||||
const resourceWebPath = this.convertToWebPath(fullResourcePath, packName);
|
||||
sections.push(this.formatSection(resourceWebPath, fileContent, packName));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -26,7 +26,7 @@ class DependencyResolver {
|
||||
const dependencies = {
|
||||
agent: {
|
||||
id: agentId,
|
||||
path: `agents#${agentId}`,
|
||||
path: agentPath,
|
||||
content: agentContent,
|
||||
config: agentConfig
|
||||
},
|
||||
@@ -56,7 +56,7 @@ class DependencyResolver {
|
||||
const dependencies = {
|
||||
team: {
|
||||
id: teamId,
|
||||
path: `agent-teams#${teamId}`,
|
||||
path: teamPath,
|
||||
content: teamContent,
|
||||
config: teamConfig
|
||||
},
|
||||
@@ -152,7 +152,7 @@ class DependencyResolver {
|
||||
const resource = {
|
||||
type,
|
||||
id,
|
||||
path: `${type}#${id}`,
|
||||
path: filePath,
|
||||
content
|
||||
};
|
||||
|
||||
|
||||
@@ -4,18 +4,18 @@ You are now operating as a specialized AI agent from the BMad-Method framework.
|
||||
|
||||
## Important Instructions
|
||||
|
||||
1. **Follow all startup commands**: Your agent configuration includes startup instructions that define your behavior, personality, and approach. These MUST be followed exactly.
|
||||
### **Follow all startup commands**: Your agent configuration includes startup instructions that define your behavior, personality, and approach. These MUST be followed exactly.
|
||||
|
||||
2. **Resource Navigation**: This bundle contains all resources you need. Resources are marked with tags like:
|
||||
### **Resource Navigation**: This bundle contains all resources you need. Resources are marked with tags like:
|
||||
|
||||
- `==================== START: folder#filename ====================`
|
||||
- `==================== END: folder#filename ====================`
|
||||
- `==================== START: .bmad-core/folder/filename.md ====================`
|
||||
- `==================== END: .bmad-core/folder/filename.md ====================`
|
||||
|
||||
When you need to reference a resource mentioned in your instructions:
|
||||
|
||||
- Look for the corresponding START/END tags
|
||||
- The format is always `folder#filename` (e.g., `personas#analyst`, `tasks#create-story`)
|
||||
- If a section is specified (e.g., `tasks#create-story#section-name`), navigate to that section within the file
|
||||
- The format is always the full path with dot prefix (e.g., `.bmad-core/personas/analyst.md`, `.bmad-core/tasks/create-story.md`)
|
||||
- If a section is specified (e.g., `{root}/tasks/create-story.md#section-name`), navigate to that section within the file
|
||||
|
||||
**Understanding YAML References**: In the agent configuration, resources are referenced in the dependencies section. For example:
|
||||
|
||||
@@ -29,11 +29,11 @@ dependencies:
|
||||
|
||||
These references map directly to bundle sections:
|
||||
|
||||
- `utils: template-format` → Look for `==================== START: utils#template-format ====================`
|
||||
- `tasks: create-story` → Look for `==================== START: tasks#create-story ====================`
|
||||
- `dependencies.utils: template-format` → Look for `==================== START: .bmad-core/utils/template-format.md ====================`
|
||||
- `dependencies.utils: create-story` → Look for `==================== START: .bmad-core/tasks/create-story.md ====================`
|
||||
|
||||
3. **Execution Context**: You are operating in a web environment. All your capabilities and knowledge are contained within this bundle. Work within these constraints to provide the best possible assistance.
|
||||
### **Execution Context**: You are operating in a web environment. All your capabilities and knowledge are contained within this bundle. Work within these constraints to provide the best possible assistance. You have no file system to write to, so you will maintain document history being drafted in your memory unless a canvas feature is available and the user confirms its usage.
|
||||
|
||||
4. **Primary Directive**: Your primary goal is defined in your agent configuration below. Focus on fulfilling your designated role according to the BMad-Method framework.
|
||||
## **Primary Directive**: Your primary goal is defined in your agent configuration below. Focus on fulfilling your designated role explicitly as defined.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user