fix: update CLI installer paths after skill directory rename
The refactor in 0380656d renamed src/core -> src/core-skills and
src/bmm -> src/bmm-skills but did not update the CLI installer code.
This broke `npx bmad-method install` with ENOENT errors scanning
the old directory paths. Also restores the deleted
src/utility/agent-components/ directory whose fragment templates
are still required by activation-builder.js during agent compilation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
be555aad8b
commit
c0898ef003
|
|
@ -0,0 +1,6 @@
|
||||||
|
<rules>
|
||||||
|
<r>ALWAYS communicate in {communication_language} UNLESS contradicted by communication_style.</r>
|
||||||
|
<r> Stay in character until exit selected</r>
|
||||||
|
<r> Display Menu items as the item dictates and in the order given.</r>
|
||||||
|
<r> Load files ONLY when executing a user chosen workflow or a command requires it, EXCEPTION: agent activation step 2 config.yaml</r>
|
||||||
|
</rules>
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<step n="1">Load persona from this current agent file (already in context)</step>
|
||||||
|
<step n="2">🚨 IMMEDIATE ACTION REQUIRED - BEFORE ANY OUTPUT:
|
||||||
|
- Load and read {project-root}/_bmad/{{module}}/config.yaml NOW
|
||||||
|
- Store ALL fields as session variables: {user_name}, {communication_language}, {output_folder}
|
||||||
|
- VERIFY: If config not loaded, STOP and report error to user
|
||||||
|
- DO NOT PROCEED to step 3 until config is successfully loaded and variables stored
|
||||||
|
</step>
|
||||||
|
<step n="3">Remember: user's name is {user_name}</step>
|
||||||
|
{AGENT_SPECIFIC_STEPS}
|
||||||
|
<step n="{MENU_STEP}">Show greeting using {user_name} from config, communicate in {communication_language}, then display numbered list of ALL menu items from menu section</step>
|
||||||
|
<step n="{HELP_STEP}">Let {user_name} know they can invoke the `bmad-help` skill at any time to get advice on what to do next, and that they can combine it with what they need help with <example>Invoke the `bmad-help` skill with a question like "where should I start with an idea I have that does XYZ?"</example></step>
|
||||||
|
<step n="{HALT_STEP}">STOP and WAIT for user input - do NOT execute menu items automatically - accept number or cmd trigger or fuzzy command match</step>
|
||||||
|
<step n="{INPUT_STEP}">On user input: Number → process menu item[n] | Text → case-insensitive substring match | Multiple matches → ask user to clarify | No match → show "Not recognized"</step>
|
||||||
|
<step n="{EXECUTE_STEP}">When processing a menu item: Check menu-handlers section below - extract any attributes from the selected menu item (exec, tmpl, data, action, multi) and follow the corresponding handler instructions</step>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
You must fully embody this agent's persona and follow all activation instructions, steps and rules exactly as specified. NEVER break character until given an exit command.
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Agent Customization
|
||||||
|
# Customize any section below - all are optional
|
||||||
|
|
||||||
|
# Override agent name
|
||||||
|
agent:
|
||||||
|
metadata:
|
||||||
|
name: ""
|
||||||
|
|
||||||
|
# Replace entire persona (not merged)
|
||||||
|
persona:
|
||||||
|
role: ""
|
||||||
|
identity: ""
|
||||||
|
communication_style: ""
|
||||||
|
principles: []
|
||||||
|
|
||||||
|
# Add custom critical actions (appended after standard config loading)
|
||||||
|
critical_actions: []
|
||||||
|
|
||||||
|
# Add persistent memories for the agent
|
||||||
|
memories: []
|
||||||
|
# Example:
|
||||||
|
# memories:
|
||||||
|
# - "User prefers detailed technical explanations"
|
||||||
|
# - "Current project uses React and TypeScript"
|
||||||
|
|
||||||
|
# Add custom menu items (appended to base menu)
|
||||||
|
# Don't include * prefix or help/exit - auto-injected
|
||||||
|
menu: []
|
||||||
|
# Example:
|
||||||
|
# menu:
|
||||||
|
# - trigger: my-workflow
|
||||||
|
# workflow: "{project-root}/custom/my.yaml"
|
||||||
|
# description: My custom workflow
|
||||||
|
|
||||||
|
# Add custom prompts (for action="#id" handlers)
|
||||||
|
prompts: []
|
||||||
|
# Example:
|
||||||
|
# prompts:
|
||||||
|
# - id: my-prompt
|
||||||
|
# content: |
|
||||||
|
# Prompt instructions here
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<handler type="action">
|
||||||
|
When menu item has: action="#id" → Find prompt with id="id" in current agent XML, follow its content
|
||||||
|
When menu item has: action="text" → Follow the text directly as an inline instruction
|
||||||
|
</handler>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<handler type="data">
|
||||||
|
When menu item has: data="path/to/file.json|yaml|yml|csv|xml"
|
||||||
|
Load the file first, parse according to extension
|
||||||
|
Make available as {data} variable to subsequent handler operations
|
||||||
|
</handler>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<handler type="exec">
|
||||||
|
When menu item or handler has: exec="path/to/file.md":
|
||||||
|
1. Read fully and follow the file at that path
|
||||||
|
2. Process the complete file and follow all instructions within it
|
||||||
|
3. If there is data="some/path/data-foo.md" with the same item, pass that data path to the executed file as context.
|
||||||
|
</handler>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<handler type="multi">
|
||||||
|
When menu item has: type="multi" with nested handlers
|
||||||
|
1. Display the multi item text as a single menu option
|
||||||
|
2. Parse all nested handlers within the multi item
|
||||||
|
3. For each nested handler:
|
||||||
|
- Use the 'match' attribute for fuzzy matching user input (or Exact Match of character code in brackets [])
|
||||||
|
- Process based on handler attributes (exec, action)
|
||||||
|
4. When user input matches a handler's 'match' pattern:
|
||||||
|
- For exec="path/to/file.md": follow the `handler type="exec"` instructions
|
||||||
|
- For action="...": Perform the specified action directly
|
||||||
|
5. Support both exact matches and fuzzy matching based on the match attribute
|
||||||
|
6. If no handler matches, prompt user to choose from available options
|
||||||
|
</handler>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<handler type="tmpl">
|
||||||
|
1. When menu item has: tmpl="path/to/template.md"
|
||||||
|
2. Load template file, parse as markdown with {{mustache}} style variables
|
||||||
|
3. Make template content available as {template} to action/exec/workflow handlers
|
||||||
|
</handler>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<menu-handlers>
|
||||||
|
<extract>{DYNAMIC_EXTRACT_LIST}</extract>
|
||||||
|
<handlers>
|
||||||
|
{DYNAMIC_HANDLERS}
|
||||||
|
</handlers>
|
||||||
|
</menu-handlers>
|
||||||
|
|
@ -82,11 +82,11 @@ class DependencyResolver {
|
||||||
// Check if this is a source directory (has 'src' subdirectory)
|
// Check if this is a source directory (has 'src' subdirectory)
|
||||||
const srcDir = path.join(bmadDir, 'src');
|
const srcDir = path.join(bmadDir, 'src');
|
||||||
if (await fs.pathExists(srcDir)) {
|
if (await fs.pathExists(srcDir)) {
|
||||||
// Source directory structure: src/core or src/bmm
|
// Source directory structure: src/core-skills or src/bmm-skills
|
||||||
if (module === 'core') {
|
if (module === 'core') {
|
||||||
moduleDir = path.join(srcDir, 'core');
|
moduleDir = path.join(srcDir, 'core-skills');
|
||||||
} else if (module === 'bmm') {
|
} else if (module === 'bmm') {
|
||||||
moduleDir = path.join(srcDir, 'bmm');
|
moduleDir = path.join(srcDir, 'bmm-skills');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -401,8 +401,8 @@ class DependencyResolver {
|
||||||
const bmadPath = dep.dependency.replace(/^bmad\//, '');
|
const bmadPath = dep.dependency.replace(/^bmad\//, '');
|
||||||
|
|
||||||
// Try to resolve as if it's in src structure
|
// Try to resolve as if it's in src structure
|
||||||
// bmad/core/tasks/foo.md -> src/core/tasks/foo.md
|
// bmad/core/tasks/foo.md -> src/core-skills/tasks/foo.md
|
||||||
// bmad/bmm/tasks/bar.md -> src/bmm/tasks/bar.md (bmm is directly under src/)
|
// bmad/bmm/tasks/bar.md -> src/bmm-skills/tasks/bar.md (bmm is directly under src/)
|
||||||
// bmad/cis/agents/bar.md -> src/modules/cis/agents/bar.md
|
// bmad/cis/agents/bar.md -> src/modules/cis/agents/bar.md
|
||||||
|
|
||||||
if (bmadPath.startsWith('core/')) {
|
if (bmadPath.startsWith('core/')) {
|
||||||
|
|
@ -584,11 +584,11 @@ class DependencyResolver {
|
||||||
const relative = path.relative(bmadDir, filePath);
|
const relative = path.relative(bmadDir, filePath);
|
||||||
const parts = relative.split(path.sep);
|
const parts = relative.split(path.sep);
|
||||||
|
|
||||||
// Handle source directory structure (src/core, src/bmm, or src/modules/xxx)
|
// Handle source directory structure (src/core-skills, src/bmm-skills, or src/modules/xxx)
|
||||||
if (parts[0] === 'src') {
|
if (parts[0] === 'src') {
|
||||||
if (parts[1] === 'core') {
|
if (parts[1] === 'core-skills') {
|
||||||
return 'core';
|
return 'core';
|
||||||
} else if (parts[1] === 'bmm') {
|
} else if (parts[1] === 'bmm-skills') {
|
||||||
return 'bmm';
|
return 'bmm';
|
||||||
} else if (parts[1] === 'modules' && parts.length > 2) {
|
} else if (parts[1] === 'modules' && parts.length > 2) {
|
||||||
return parts[2];
|
return parts[2];
|
||||||
|
|
@ -631,11 +631,11 @@ class DependencyResolver {
|
||||||
let moduleBase;
|
let moduleBase;
|
||||||
|
|
||||||
// Check if file is in source directory structure
|
// Check if file is in source directory structure
|
||||||
if (file.includes('/src/core/') || file.includes('/src/bmm/')) {
|
if (file.includes('/src/core-skills/') || file.includes('/src/bmm-skills/')) {
|
||||||
if (module === 'core') {
|
if (module === 'core') {
|
||||||
moduleBase = path.join(bmadDir, 'src', 'core');
|
moduleBase = path.join(bmadDir, 'src', 'core-skills');
|
||||||
} else if (module === 'bmm') {
|
} else if (module === 'bmm') {
|
||||||
moduleBase = path.join(bmadDir, 'src', 'bmm');
|
moduleBase = path.join(bmadDir, 'src', 'bmm-skills');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
moduleBase = module === 'core' ? path.join(bmadDir, 'core') : path.join(bmadDir, 'modules', module);
|
moduleBase = module === 'core' ? path.join(bmadDir, 'core') : path.join(bmadDir, 'modules', module);
|
||||||
|
|
|
||||||
|
|
@ -1789,8 +1789,8 @@ class Installer {
|
||||||
.filter((entry) => entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs' && entry.name !== '_memory')
|
.filter((entry) => entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs' && entry.name !== '_memory')
|
||||||
.map((entry) => entry.name);
|
.map((entry) => entry.name);
|
||||||
|
|
||||||
// Add core module to scan (it's installed at root level as _config, but we check src/core)
|
// Add core module to scan (it's installed at root level as _config, but we check src/core-skills)
|
||||||
const coreModulePath = getSourcePath('core');
|
const coreModulePath = getSourcePath('core-skills');
|
||||||
const modulePaths = new Map();
|
const modulePaths = new Map();
|
||||||
|
|
||||||
// Map all module source paths
|
// Map all module source paths
|
||||||
|
|
@ -2709,7 +2709,7 @@ class Installer {
|
||||||
// Get source path
|
// Get source path
|
||||||
let sourcePath;
|
let sourcePath;
|
||||||
if (moduleId === 'core') {
|
if (moduleId === 'core') {
|
||||||
sourcePath = getSourcePath('core');
|
sourcePath = getSourcePath('core-skills');
|
||||||
} else {
|
} else {
|
||||||
// First check if it's in the custom cache
|
// First check if it's in the custom cache
|
||||||
if (customModuleSources.has(moduleId)) {
|
if (customModuleSources.has(moduleId)) {
|
||||||
|
|
|
||||||
|
|
@ -764,10 +764,10 @@ class Manifest {
|
||||||
const configs = {};
|
const configs = {};
|
||||||
|
|
||||||
for (const moduleName of modules) {
|
for (const moduleName of modules) {
|
||||||
// Handle core module differently - it's in src/core not src/modules/core
|
// Handle core module differently - it's in src/core-skills not src/modules/core
|
||||||
const configPath =
|
const configPath =
|
||||||
moduleName === 'core'
|
moduleName === 'core'
|
||||||
? path.join(process.cwd(), 'src', 'core', 'config.yaml')
|
? path.join(process.cwd(), 'src', 'core-skills', 'config.yaml')
|
||||||
: path.join(process.cwd(), 'src', 'modules', moduleName, 'config.yaml');
|
: path.join(process.cwd(), 'src', 'modules', moduleName, 'config.yaml');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -146,13 +146,13 @@ When running any workflow:
|
||||||
transformWorkflowPath(workflowPath) {
|
transformWorkflowPath(workflowPath) {
|
||||||
let transformed = workflowPath;
|
let transformed = workflowPath;
|
||||||
|
|
||||||
if (workflowPath.includes('/src/bmm/')) {
|
if (workflowPath.includes('/src/bmm-skills/')) {
|
||||||
const match = workflowPath.match(/\/src\/bmm\/(.+)/);
|
const match = workflowPath.match(/\/src\/bmm-skills\/(.+)/);
|
||||||
if (match) {
|
if (match) {
|
||||||
transformed = `{project-root}/${this.bmadFolderName}/bmm/${match[1]}`;
|
transformed = `{project-root}/${this.bmadFolderName}/bmm/${match[1]}`;
|
||||||
}
|
}
|
||||||
} else if (workflowPath.includes('/src/core/')) {
|
} else if (workflowPath.includes('/src/core-skills/')) {
|
||||||
const match = workflowPath.match(/\/src\/core\/(.+)/);
|
const match = workflowPath.match(/\/src\/core-skills\/(.+)/);
|
||||||
if (match) {
|
if (match) {
|
||||||
transformed = `{project-root}/${this.bmadFolderName}/core/${match[1]}`;
|
transformed = `{project-root}/${this.bmadFolderName}/core/${match[1]}`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -195,10 +195,10 @@ class ModuleManager {
|
||||||
const modules = [];
|
const modules = [];
|
||||||
const customModules = [];
|
const customModules = [];
|
||||||
|
|
||||||
// Add built-in bmm module (directly under src/bmm)
|
// Add built-in bmm module (directly under src/bmm-skills)
|
||||||
const bmmPath = getSourcePath('bmm');
|
const bmmPath = getSourcePath('bmm-skills');
|
||||||
if (await fs.pathExists(bmmPath)) {
|
if (await fs.pathExists(bmmPath)) {
|
||||||
const bmmInfo = await this.getModuleInfo(bmmPath, 'bmm', 'src/bmm');
|
const bmmInfo = await this.getModuleInfo(bmmPath, 'bmm', 'src/bmm-skills');
|
||||||
if (bmmInfo) {
|
if (bmmInfo) {
|
||||||
modules.push(bmmInfo);
|
modules.push(bmmInfo);
|
||||||
}
|
}
|
||||||
|
|
@ -250,8 +250,8 @@ class ModuleManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark as custom if it's using custom.yaml OR if it's outside src/bmm or src/core
|
// Mark as custom if it's using custom.yaml OR if it's outside src/bmm-skills or src/core-skills
|
||||||
const isCustomSource = sourceDescription !== 'src/bmm' && sourceDescription !== 'src/core' && sourceDescription !== 'src/modules';
|
const isCustomSource = sourceDescription !== 'src/bmm-skills' && sourceDescription !== 'src/core-skills' && sourceDescription !== 'src/modules';
|
||||||
const moduleInfo = {
|
const moduleInfo = {
|
||||||
id: defaultName,
|
id: defaultName,
|
||||||
path: modulePath,
|
path: modulePath,
|
||||||
|
|
@ -300,9 +300,9 @@ class ModuleManager {
|
||||||
return this.customModulePaths.get(moduleCode);
|
return this.customModulePaths.get(moduleCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for built-in bmm module (directly under src/bmm)
|
// Check for built-in bmm module (directly under src/bmm-skills)
|
||||||
if (moduleCode === 'bmm') {
|
if (moduleCode === 'bmm') {
|
||||||
const bmmPath = getSourcePath('bmm');
|
const bmmPath = getSourcePath('bmm-skills');
|
||||||
if (await fs.pathExists(bmmPath)) {
|
if (await fs.pathExists(bmmPath)) {
|
||||||
return bmmPath;
|
return bmmPath;
|
||||||
}
|
}
|
||||||
|
|
@ -1141,10 +1141,10 @@ class ModuleManager {
|
||||||
const projectRoot = path.dirname(bmadDir);
|
const projectRoot = path.dirname(bmadDir);
|
||||||
const emptyResult = { createdDirs: [], movedDirs: [], createdWdsFolders: [] };
|
const emptyResult = { createdDirs: [], movedDirs: [], createdWdsFolders: [] };
|
||||||
|
|
||||||
// Special handling for core module - it's in src/core not src/modules
|
// Special handling for core module - it's in src/core-skills not src/modules
|
||||||
let sourcePath;
|
let sourcePath;
|
||||||
if (moduleName === 'core') {
|
if (moduleName === 'core') {
|
||||||
sourcePath = getSourcePath('core');
|
sourcePath = getSourcePath('core-skills');
|
||||||
} else {
|
} else {
|
||||||
sourcePath = await this.findModuleSource(moduleName, { silent: true });
|
sourcePath = await this.findModuleSource(moduleName, { silent: true });
|
||||||
if (!sourcePath) {
|
if (!sourcePath) {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ function findProjectRoot(startPath = __dirname) {
|
||||||
try {
|
try {
|
||||||
const pkg = fs.readJsonSync(packagePath);
|
const pkg = fs.readJsonSync(packagePath);
|
||||||
// Check if this is the BMAD project
|
// Check if this is the BMAD project
|
||||||
if (pkg.name === 'bmad-method' || fs.existsSync(path.join(currentPath, 'src', 'core'))) {
|
if (pkg.name === 'bmad-method' || fs.existsSync(path.join(currentPath, 'src', 'core-skills'))) {
|
||||||
return currentPath;
|
return currentPath;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -24,8 +24,8 @@ function findProjectRoot(startPath = __dirname) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also check for src/core as a marker
|
// Also check for src/core-skills as a marker
|
||||||
if (fs.existsSync(path.join(currentPath, 'src', 'core', 'agents'))) {
|
if (fs.existsSync(path.join(currentPath, 'src', 'core-skills'))) {
|
||||||
return currentPath;
|
return currentPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,16 +55,16 @@ function getSourcePath(...segments) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get path to a module's directory
|
* Get path to a module's directory
|
||||||
* bmm is a built-in module directly under src/
|
* bmm is a built-in module directly under src/bmm-skills
|
||||||
* core is also directly under src/
|
* core is also directly under src/core-skills
|
||||||
* All other modules are stored remote
|
* All other modules are stored remote
|
||||||
*/
|
*/
|
||||||
function getModulePath(moduleName, ...segments) {
|
function getModulePath(moduleName, ...segments) {
|
||||||
if (moduleName === 'core') {
|
if (moduleName === 'core') {
|
||||||
return getSourcePath('core', ...segments);
|
return getSourcePath('core-skills', ...segments);
|
||||||
}
|
}
|
||||||
if (moduleName === 'bmm') {
|
if (moduleName === 'bmm') {
|
||||||
return getSourcePath('bmm', ...segments);
|
return getSourcePath('bmm-skills', ...segments);
|
||||||
}
|
}
|
||||||
return getSourcePath('modules', moduleName, ...segments);
|
return getSourcePath('modules', moduleName, ...segments);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -495,7 +495,7 @@ class YamlXmlBuilder {
|
||||||
|
|
||||||
// Extract module from path (e.g., /path/to/modules/bmm/agents/pm.yaml -> bmm)
|
// Extract module from path (e.g., /path/to/modules/bmm/agents/pm.yaml -> bmm)
|
||||||
// or /path/to/bmad/bmm/agents/pm.yaml -> bmm
|
// or /path/to/bmad/bmm/agents/pm.yaml -> bmm
|
||||||
// or /path/to/src/bmm/agents/pm.yaml -> bmm
|
// or /path/to/src/bmm-skills/agents/pm.yaml -> bmm
|
||||||
let module = 'core'; // default to core
|
let module = 'core'; // default to core
|
||||||
const pathParts = agentYamlPath.split(path.sep);
|
const pathParts = agentYamlPath.split(path.sep);
|
||||||
|
|
||||||
|
|
@ -515,10 +515,12 @@ class YamlXmlBuilder {
|
||||||
module = potentialModule;
|
module = potentialModule;
|
||||||
}
|
}
|
||||||
} else if (srcIndex !== -1 && pathParts[srcIndex + 1]) {
|
} else if (srcIndex !== -1 && pathParts[srcIndex + 1]) {
|
||||||
// Path contains /src/{module}/ (bmm and core are directly under src/)
|
// Path contains /src/{module-skills}/ (bmm-skills and core-skills are directly under src/)
|
||||||
const potentialModule = pathParts[srcIndex + 1];
|
const potentialModule = pathParts[srcIndex + 1];
|
||||||
if (potentialModule === 'bmm' || potentialModule === 'core') {
|
if (potentialModule === 'bmm-skills') {
|
||||||
module = potentialModule;
|
module = 'bmm';
|
||||||
|
} else if (potentialModule === 'core-skills') {
|
||||||
|
module = 'core';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue