installer updates part 1

This commit is contained in:
Brian Madison
2025-06-27 23:38:34 -05:00
parent 6cdecec61f
commit 1ea367619a
38 changed files with 569 additions and 2332 deletions

View File

@@ -9,8 +9,8 @@ class WebBuilder {
this.resolver = new DependencyResolver(this.rootDir);
this.templatePath = path.join(
this.rootDir,
"bmad-core",
"utils",
"tools",
"md-assets",
"web-agent-startup-instructions.md"
);
}
@@ -117,35 +117,39 @@ class WebBuilder {
const yamlContent = yamlMatch[1];
const yamlStartIndex = content.indexOf(yamlMatch[0]);
const yamlEndIndex = yamlStartIndex + yamlMatch[0].length;
// Parse YAML and remove root and IDE-FILE-RESOLUTION properties
try {
const yaml = require("js-yaml");
const parsed = yaml.load(yamlContent);
// Remove the properties if they exist at root level
delete parsed.root;
delete parsed['IDE-FILE-RESOLUTION'];
delete parsed['REQUEST-RESOLUTION'];
delete parsed["IDE-FILE-RESOLUTION"];
delete parsed["REQUEST-RESOLUTION"];
// Also remove from activation-instructions if they exist
if (parsed['activation-instructions'] && Array.isArray(parsed['activation-instructions'])) {
parsed['activation-instructions'] = parsed['activation-instructions'].filter(instruction => {
return !instruction.startsWith('IDE-FILE-RESOLUTION:') &&
!instruction.startsWith('REQUEST-RESOLUTION:');
});
if (parsed["activation-instructions"] && Array.isArray(parsed["activation-instructions"])) {
parsed["activation-instructions"] = parsed["activation-instructions"].filter(
(instruction) => {
return (
!instruction.startsWith("IDE-FILE-RESOLUTION:") &&
!instruction.startsWith("REQUEST-RESOLUTION:")
);
}
);
}
// Reconstruct the YAML
const cleanedYaml = yaml.dump(parsed, { lineWidth: -1 });
// Get the agent name from the YAML for the header
const agentName = parsed.agent?.id || 'agent';
const agentName = parsed.agent?.id || "agent";
// Build the new content with just the agent header and YAML
const newHeader = `# ${agentName}\n\nCRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n`;
const afterYaml = content.substring(yamlEndIndex);
return newHeader + "```yaml\n" + cleanedYaml.trim() + "\n```" + afterYaml;
} catch (error) {
console.warn("Failed to process agent YAML:", error.message);
@@ -156,12 +160,12 @@ class WebBuilder {
formatSection(path, content) {
const separator = "====================";
// Process agent content if this is an agent file
if (path.startsWith("agents#")) {
content = this.processAgentContent(content);
}
return [
`${separator} START: ${path} ${separator}`,
content.trim(),
@@ -341,6 +345,28 @@ class WebBuilder {
}
}
// If not found in core, try common folder
if (!found) {
for (const ext of extensions) {
const commonPath = path.join(
this.rootDir,
"common",
resourceType,
`${resourceName}${ext}`
);
try {
const commonContent = await fs.readFile(commonPath, "utf8");
sections.push(
this.formatSection(`${resourceType}#${resourceName}`, commonContent)
);
found = true;
break;
} catch (error) {
// Not in common either, continue
}
}
}
if (!found) {
console.warn(
` ⚠ Dependency ${resourceType}#${resourceName} not found in expansion pack or core`
@@ -516,6 +542,21 @@ class WebBuilder {
}
}
// If not found in core, try common folder
if (!found) {
for (const ext of extensions) {
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));
found = true;
break;
} catch (error) {
// Not in common either, continue
}
}
}
if (!found) {
console.warn(` ⚠ Dependency ${key} not found in expansion pack or core`);
}

View File

@@ -12,8 +12,8 @@ agent-dependencies:
core-files:
- bmad-core/utils/template-format.md
dev:
- bmad-core/templates/story-tmpl.md
- bmad-core/checklists/story-dod-checklist.md
- common/templates/story-tmpl.md
- common/checklists/story-dod-checklist.md
pm:
- bmad-core/templates/prd-tmpl.md
- bmad-core/templates/brownfield-prd-tmpl.md

View File

@@ -90,6 +90,7 @@ class FileManager {
agent: config.agent || null,
ide_setup: config.ide || null,
ides_setup: config.ides || [],
expansion_packs: config.expansionPacks || [],
files: [],
};

View File

@@ -225,6 +225,10 @@ class Installer {
const sourceDir = configLoader.getBmadCorePath();
const bmadCoreDestDir = path.join(installDir, ".bmad-core");
await fileManager.copyDirectory(sourceDir, bmadCoreDestDir);
// Copy common/ items to .bmad-core
spinner.text = "Copying common utilities...";
await this.copyCommonItems(installDir, ".bmad-core", spinner);
// Get list of all files for manifest
const glob = require("glob");
@@ -283,6 +287,11 @@ class Installer {
}
}
}
// Copy common/ items to .bmad-core
spinner.text = "Copying common utilities...";
const commonFiles = await this.copyCommonItems(installDir, ".bmad-core", spinner);
files.push(...commonFiles);
} else if (config.installType === "team") {
// Team installation
spinner.text = `Installing ${config.team} team...`;
@@ -313,6 +322,11 @@ class Installer {
}
}
}
// Copy common/ items to .bmad-core
spinner.text = "Copying common utilities...";
const commonFiles = await this.copyCommonItems(installDir, ".bmad-core", spinner);
files.push(...commonFiles);
} else if (config.installType === "expansion-only") {
// Expansion-only installation - create minimal .bmad-core structure
spinner.text = "Creating minimal .bmad-core structure for expansion packs...";
@@ -341,6 +355,10 @@ class Installer {
);
files.push(...copiedFiles.map(f => `.bmad-core/${f}`));
}
// Copy common/ items to .bmad-core
spinner.text = "Copying common utilities...";
await this.copyCommonItems(installDir, ".bmad-core", spinner);
}
// Install expansion packs if requested
@@ -607,8 +625,10 @@ class Installer {
console.log(chalk.green("✓ .bmad-core framework installed with all agents and workflows"));
if (config.expansionPacks && config.expansionPacks.length > 0) {
const packNames = config.expansionPacks.join(", ");
console.log(chalk.green(`✓ Expansion packs installed: ${packNames}`));
console.log(chalk.green(`✓ Expansion packs installed:`));
for (const packId of config.expansionPacks) {
console.log(chalk.green(` - ${packId} → .${packId}/`));
}
}
if (config.includeWebBundles && config.webBundlesDirectory) {
@@ -799,7 +819,11 @@ class Installer {
const expansionPackDir = path.dirname(pack.manifestPath);
// Define the folders to copy from expansion packs to .bmad-core
// Create dedicated dot folder for this expansion pack
const expansionDotFolder = path.join(installDir, `.${packId}`);
await fileManager.ensureDirectory(expansionDotFolder);
// Define the folders to copy from expansion packs
const foldersToSync = [
'agents',
'agent-teams',
@@ -824,21 +848,32 @@ class Installer {
nodir: true
});
// Copy each file to the destination
// Copy each file to the expansion pack's dot folder
for (const file of files) {
const sourcePath = path.join(sourceFolder, file);
const destPath = path.join(installDir, '.bmad-core', folder, file);
const destPath = path.join(expansionDotFolder, folder, file);
if (await fileManager.copyFile(sourcePath, destPath)) {
installedFiles.push(path.join('.bmad-core', folder, file));
installedFiles.push(path.join(`.${packId}`, folder, file));
}
}
}
}
// Web bundles are now available in the dist/ directory and don't need to be copied
// Copy manifest to the expansion pack's dot folder
const manifestDestPath = path.join(expansionDotFolder, 'manifest.yml');
if (await fileManager.copyFile(pack.manifestPath, manifestDestPath)) {
installedFiles.push(path.join(`.${packId}`, 'manifest.yml'));
}
console.log(chalk.green(`✓ Installed expansion pack: ${pack.name}`));
// Copy common/ items to expansion pack folder
spinner.text = `Copying common utilities to ${packId}...`;
await this.copyCommonItems(installDir, `.${packId}`, spinner);
// Check and resolve core dependencies
await this.resolveExpansionPackCoreDependencies(installDir, expansionDotFolder, packId, spinner);
console.log(chalk.green(`✓ Installed expansion pack: ${pack.name} to ${`.${packId}`}`));
} catch (error) {
console.error(chalk.red(`Failed to install expansion pack ${packId}: ${error.message}`));
}
@@ -847,6 +882,61 @@ class Installer {
return installedFiles;
}
async resolveExpansionPackCoreDependencies(installDir, expansionDotFolder, packId, spinner) {
const glob = require('glob');
const yaml = require('yaml');
const fs = require('fs').promises;
// Find all agent files in the expansion pack
const agentFiles = glob.sync('agents/*.md', {
cwd: expansionDotFolder
});
for (const agentFile of agentFiles) {
const agentPath = path.join(expansionDotFolder, agentFile);
const agentContent = await fs.readFile(agentPath, 'utf8');
// Extract YAML frontmatter to check dependencies
const yamlMatch = agentContent.match(/```yaml\n([\s\S]*?)```/);
if (yamlMatch) {
try {
const agentConfig = yaml.parse(yamlMatch[1]);
const dependencies = agentConfig.dependencies || {};
// Check for core dependencies (those that don't exist in the expansion pack)
for (const depType of ['tasks', 'templates', 'checklists', 'workflows', 'utils', 'data']) {
const deps = dependencies[depType] || [];
for (const dep of deps) {
const depFileName = dep.endsWith('.md') ? dep : `${dep}.md`;
const expansionDepPath = path.join(expansionDotFolder, depType, depFileName);
// Check if dependency exists in expansion pack
if (!(await fileManager.pathExists(expansionDepPath))) {
// Try to find it in core
const coreDepPath = path.join(configLoader.getBmadCorePath(), depType, depFileName);
if (await fileManager.pathExists(coreDepPath)) {
spinner.text = `Copying core dependency ${dep} for ${packId}...`;
// Copy from core to expansion pack dot folder
const destPath = path.join(expansionDotFolder, depType, depFileName);
await fileManager.copyFile(coreDepPath, destPath);
console.log(chalk.dim(` Added core dependency: ${depType}/${depFileName}`));
} else {
console.warn(chalk.yellow(` Warning: Dependency ${depType}/${dep} not found in core or expansion pack`));
}
}
}
}
} catch (error) {
console.warn(chalk.yellow(` Warning: Could not parse agent dependencies: ${error.message}`));
}
}
}
}
getWebBundleInfo(config) {
const webBundleType = config.webBundleType || 'all';
@@ -944,6 +1034,48 @@ class Installer {
}
}
async copyCommonItems(installDir, targetSubdir, spinner) {
const glob = require('glob');
const fs = require('fs').promises;
const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
const commonPath = path.join(sourceBase, 'common');
const targetPath = path.join(installDir, targetSubdir);
const copiedFiles = [];
// Check if common/ exists
if (!(await fileManager.pathExists(commonPath))) {
console.warn(chalk.yellow('Warning: common/ folder not found'));
return copiedFiles;
}
// Copy all items from common/ to target
const commonItems = glob.sync('**/*', {
cwd: commonPath,
nodir: true
});
for (const item of commonItems) {
const sourcePath = path.join(commonPath, item);
const destPath = path.join(targetPath, item);
// Read the file content
const content = await fs.readFile(sourcePath, 'utf8');
// Replace {root} with the target subdirectory
const updatedContent = content.replace(/\{root\}/g, targetSubdir);
// Ensure directory exists
await fileManager.ensureDirectory(path.dirname(destPath));
// Write the updated content
await fs.writeFile(destPath, updatedContent, 'utf8');
copiedFiles.push(path.join(targetSubdir, item));
}
console.log(chalk.dim(` Added ${commonItems.length} common utilities`));
return copiedFiles;
}
async findInstallation() {
// Look for .bmad-core in current directory or parent directories
let currentDir = process.cwd();

View File

@@ -6,6 +6,7 @@ class DependencyResolver {
constructor(rootDir) {
this.rootDir = rootDir;
this.bmadCore = path.join(rootDir, 'bmad-core');
this.common = path.join(rootDir, 'common');
this.cache = new Map();
}
@@ -123,6 +124,7 @@ class DependencyResolver {
let content = null;
let filePath = null;
// First try bmad-core
for (const ext of extensions) {
try {
filePath = path.join(this.bmadCore, type, `${id}${ext}`);
@@ -132,6 +134,19 @@ class DependencyResolver {
// Try next extension
}
}
// If not found in bmad-core, try common folder
if (!content) {
for (const ext of extensions) {
try {
filePath = path.join(this.common, type, `${id}${ext}`);
content = await fs.readFile(filePath, 'utf8');
break;
} catch (e) {
// Try next extension
}
}
}
if (!content) {
console.warn(`Resource not found: ${type}/${id}`);

View File

@@ -0,0 +1,39 @@
# 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: folder#filename ====================`
- `==================== END: folder#filename ====================`
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
**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: utils#template-format ====================`
- `tasks: create-story` → Look for `==================== START: tasks#create-story ====================`
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.
---