installer updates part 1
This commit is contained in:
@@ -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`);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -90,6 +90,7 @@ class FileManager {
|
||||
agent: config.agent || null,
|
||||
ide_setup: config.ide || null,
|
||||
ides_setup: config.ides || [],
|
||||
expansion_packs: config.expansionPacks || [],
|
||||
files: [],
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
39
tools/md-assets/web-agent-startup-instructions.md
Normal file
39
tools/md-assets/web-agent-startup-instructions.md
Normal 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.
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user