diff --git a/src/utility/agent-components/activation-rules.txt b/src/utility/agent-components/activation-rules.txt
new file mode 100644
index 000000000..a67ae4993
--- /dev/null
+++ b/src/utility/agent-components/activation-rules.txt
@@ -0,0 +1,6 @@
+
+ ALWAYS communicate in {communication_language} UNLESS contradicted by communication_style.
+ Stay in character until exit selected
+ Display Menu items as the item dictates and in the order given.
+ Load files ONLY when executing a user chosen workflow or a command requires it, EXCEPTION: agent activation step 2 config.yaml
+
\ No newline at end of file
diff --git a/src/utility/agent-components/activation-steps.txt b/src/utility/agent-components/activation-steps.txt
new file mode 100644
index 000000000..726be3e06
--- /dev/null
+++ b/src/utility/agent-components/activation-steps.txt
@@ -0,0 +1,14 @@
+ Load persona from this current agent file (already in context)
+ 🚨 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
+
+ Remember: user's name is {user_name}
+ {AGENT_SPECIFIC_STEPS}
+ Show greeting using {user_name} from config, communicate in {communication_language}, then display numbered list of ALL menu items from menu section
+ 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 Invoke the `bmad-help` skill with a question like "where should I start with an idea I have that does XYZ?"
+ STOP and WAIT for user input - do NOT execute menu items automatically - accept number or cmd trigger or fuzzy command match
+ On user input: Number → process menu item[n] | Text → case-insensitive substring match | Multiple matches → ask user to clarify | No match → show "Not recognized"
+ 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
diff --git a/src/utility/agent-components/agent-command-header.md b/src/utility/agent-components/agent-command-header.md
new file mode 100644
index 000000000..d4f9b5d6a
--- /dev/null
+++ b/src/utility/agent-components/agent-command-header.md
@@ -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.
diff --git a/src/utility/agent-components/agent.customize.template.yaml b/src/utility/agent-components/agent.customize.template.yaml
new file mode 100644
index 000000000..b8cc648b4
--- /dev/null
+++ b/src/utility/agent-components/agent.customize.template.yaml
@@ -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
diff --git a/src/utility/agent-components/handler-action.txt b/src/utility/agent-components/handler-action.txt
new file mode 100644
index 000000000..db31f4852
--- /dev/null
+++ b/src/utility/agent-components/handler-action.txt
@@ -0,0 +1,4 @@
+
+ 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
+
\ No newline at end of file
diff --git a/src/utility/agent-components/handler-data.txt b/src/utility/agent-components/handler-data.txt
new file mode 100644
index 000000000..14036fa58
--- /dev/null
+++ b/src/utility/agent-components/handler-data.txt
@@ -0,0 +1,5 @@
+
+ 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
+
diff --git a/src/utility/agent-components/handler-exec.txt b/src/utility/agent-components/handler-exec.txt
new file mode 100644
index 000000000..1c8459a64
--- /dev/null
+++ b/src/utility/agent-components/handler-exec.txt
@@ -0,0 +1,6 @@
+
+ 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.
+
\ No newline at end of file
diff --git a/src/utility/agent-components/handler-multi.txt b/src/utility/agent-components/handler-multi.txt
new file mode 100644
index 000000000..e05be2390
--- /dev/null
+++ b/src/utility/agent-components/handler-multi.txt
@@ -0,0 +1,13 @@
+
+ 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
+
\ No newline at end of file
diff --git a/src/utility/agent-components/handler-tmpl.txt b/src/utility/agent-components/handler-tmpl.txt
new file mode 100644
index 000000000..a190504b7
--- /dev/null
+++ b/src/utility/agent-components/handler-tmpl.txt
@@ -0,0 +1,5 @@
+
+ 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
+
\ No newline at end of file
diff --git a/src/utility/agent-components/menu-handlers.txt b/src/utility/agent-components/menu-handlers.txt
new file mode 100644
index 000000000..3dd1ae9c7
--- /dev/null
+++ b/src/utility/agent-components/menu-handlers.txt
@@ -0,0 +1,6 @@
+
+ {DYNAMIC_EXTRACT_LIST}
+
+ {DYNAMIC_HANDLERS}
+
+
diff --git a/tools/cli/installers/lib/core/dependency-resolver.js b/tools/cli/installers/lib/core/dependency-resolver.js
index 3fb282c5d..8b0971bf1 100644
--- a/tools/cli/installers/lib/core/dependency-resolver.js
+++ b/tools/cli/installers/lib/core/dependency-resolver.js
@@ -82,11 +82,11 @@ class DependencyResolver {
// Check if this is a source directory (has 'src' subdirectory)
const srcDir = path.join(bmadDir, 'src');
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') {
- moduleDir = path.join(srcDir, 'core');
+ moduleDir = path.join(srcDir, 'core-skills');
} 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\//, '');
// Try to resolve as if it's in src structure
- // bmad/core/tasks/foo.md -> src/core/tasks/foo.md
- // bmad/bmm/tasks/bar.md -> src/bmm/tasks/bar.md (bmm is directly under src/)
+ // bmad/core/tasks/foo.md -> src/core-skills/tasks/foo.md
+ // 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
if (bmadPath.startsWith('core/')) {
@@ -584,11 +584,11 @@ class DependencyResolver {
const relative = path.relative(bmadDir, filePath);
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[1] === 'core') {
+ if (parts[1] === 'core-skills') {
return 'core';
- } else if (parts[1] === 'bmm') {
+ } else if (parts[1] === 'bmm-skills') {
return 'bmm';
} else if (parts[1] === 'modules' && parts.length > 2) {
return parts[2];
@@ -631,11 +631,11 @@ class DependencyResolver {
let moduleBase;
// 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') {
- moduleBase = path.join(bmadDir, 'src', 'core');
+ moduleBase = path.join(bmadDir, 'src', 'core-skills');
} else if (module === 'bmm') {
- moduleBase = path.join(bmadDir, 'src', 'bmm');
+ moduleBase = path.join(bmadDir, 'src', 'bmm-skills');
}
} else {
moduleBase = module === 'core' ? path.join(bmadDir, 'core') : path.join(bmadDir, 'modules', module);
diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js
index 85864145f..5022ab954 100644
--- a/tools/cli/installers/lib/core/installer.js
+++ b/tools/cli/installers/lib/core/installer.js
@@ -1789,8 +1789,8 @@ class Installer {
.filter((entry) => entry.isDirectory() && entry.name !== '_config' && entry.name !== 'docs' && entry.name !== '_memory')
.map((entry) => entry.name);
- // Add core module to scan (it's installed at root level as _config, but we check src/core)
- const coreModulePath = getSourcePath('core');
+ // Add core module to scan (it's installed at root level as _config, but we check src/core-skills)
+ const coreModulePath = getSourcePath('core-skills');
const modulePaths = new Map();
// Map all module source paths
@@ -2709,7 +2709,7 @@ class Installer {
// Get source path
let sourcePath;
if (moduleId === 'core') {
- sourcePath = getSourcePath('core');
+ sourcePath = getSourcePath('core-skills');
} else {
// First check if it's in the custom cache
if (customModuleSources.has(moduleId)) {
diff --git a/tools/cli/installers/lib/core/manifest.js b/tools/cli/installers/lib/core/manifest.js
index cb5368843..0b5fc447b 100644
--- a/tools/cli/installers/lib/core/manifest.js
+++ b/tools/cli/installers/lib/core/manifest.js
@@ -764,10 +764,10 @@ class Manifest {
const configs = {};
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 =
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');
try {
diff --git a/tools/cli/installers/lib/ide/shared/workflow-command-generator.js b/tools/cli/installers/lib/ide/shared/workflow-command-generator.js
index ed8c3e508..996c8728d 100644
--- a/tools/cli/installers/lib/ide/shared/workflow-command-generator.js
+++ b/tools/cli/installers/lib/ide/shared/workflow-command-generator.js
@@ -146,13 +146,13 @@ When running any workflow:
transformWorkflowPath(workflowPath) {
let transformed = workflowPath;
- if (workflowPath.includes('/src/bmm/')) {
- const match = workflowPath.match(/\/src\/bmm\/(.+)/);
+ if (workflowPath.includes('/src/bmm-skills/')) {
+ const match = workflowPath.match(/\/src\/bmm-skills\/(.+)/);
if (match) {
transformed = `{project-root}/${this.bmadFolderName}/bmm/${match[1]}`;
}
- } else if (workflowPath.includes('/src/core/')) {
- const match = workflowPath.match(/\/src\/core\/(.+)/);
+ } else if (workflowPath.includes('/src/core-skills/')) {
+ const match = workflowPath.match(/\/src\/core-skills\/(.+)/);
if (match) {
transformed = `{project-root}/${this.bmadFolderName}/core/${match[1]}`;
}
diff --git a/tools/cli/installers/lib/modules/manager.js b/tools/cli/installers/lib/modules/manager.js
index 9bc027d85..625e38e98 100644
--- a/tools/cli/installers/lib/modules/manager.js
+++ b/tools/cli/installers/lib/modules/manager.js
@@ -195,10 +195,10 @@ class ModuleManager {
const modules = [];
const customModules = [];
- // Add built-in bmm module (directly under src/bmm)
- const bmmPath = getSourcePath('bmm');
+ // Add built-in bmm module (directly under src/bmm-skills)
+ const bmmPath = getSourcePath('bmm-skills');
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) {
modules.push(bmmInfo);
}
@@ -250,8 +250,8 @@ class ModuleManager {
return null;
}
- // Mark as custom if it's using custom.yaml OR if it's outside src/bmm or src/core
- const isCustomSource = sourceDescription !== 'src/bmm' && sourceDescription !== 'src/core' && sourceDescription !== 'src/modules';
+ // 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-skills' && sourceDescription !== 'src/core-skills' && sourceDescription !== 'src/modules';
const moduleInfo = {
id: defaultName,
path: modulePath,
@@ -300,9 +300,9 @@ class ModuleManager {
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') {
- const bmmPath = getSourcePath('bmm');
+ const bmmPath = getSourcePath('bmm-skills');
if (await fs.pathExists(bmmPath)) {
return bmmPath;
}
@@ -1141,10 +1141,10 @@ class ModuleManager {
const projectRoot = path.dirname(bmadDir);
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;
if (moduleName === 'core') {
- sourcePath = getSourcePath('core');
+ sourcePath = getSourcePath('core-skills');
} else {
sourcePath = await this.findModuleSource(moduleName, { silent: true });
if (!sourcePath) {
diff --git a/tools/cli/lib/project-root.js b/tools/cli/lib/project-root.js
index 4533c773c..5a1f97830 100644
--- a/tools/cli/lib/project-root.js
+++ b/tools/cli/lib/project-root.js
@@ -16,7 +16,7 @@ function findProjectRoot(startPath = __dirname) {
try {
const pkg = fs.readJsonSync(packagePath);
// 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;
}
} catch {
@@ -24,8 +24,8 @@ function findProjectRoot(startPath = __dirname) {
}
}
- // Also check for src/core as a marker
- if (fs.existsSync(path.join(currentPath, 'src', 'core', 'agents'))) {
+ // Also check for src/core-skills as a marker
+ if (fs.existsSync(path.join(currentPath, 'src', 'core-skills'))) {
return currentPath;
}
@@ -55,16 +55,16 @@ function getSourcePath(...segments) {
/**
* Get path to a module's directory
- * bmm is a built-in module directly under src/
- * core is also directly under src/
+ * bmm is a built-in module directly under src/bmm-skills
+ * core is also directly under src/core-skills
* All other modules are stored remote
*/
function getModulePath(moduleName, ...segments) {
if (moduleName === 'core') {
- return getSourcePath('core', ...segments);
+ return getSourcePath('core-skills', ...segments);
}
if (moduleName === 'bmm') {
- return getSourcePath('bmm', ...segments);
+ return getSourcePath('bmm-skills', ...segments);
}
return getSourcePath('modules', moduleName, ...segments);
}
diff --git a/tools/cli/lib/yaml-xml-builder.js b/tools/cli/lib/yaml-xml-builder.js
index f4f8e2f5a..f7c466e88 100644
--- a/tools/cli/lib/yaml-xml-builder.js
+++ b/tools/cli/lib/yaml-xml-builder.js
@@ -495,7 +495,7 @@ class YamlXmlBuilder {
// 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/src/bmm/agents/pm.yaml -> bmm
+ // or /path/to/src/bmm-skills/agents/pm.yaml -> bmm
let module = 'core'; // default to core
const pathParts = agentYamlPath.split(path.sep);
@@ -515,10 +515,12 @@ class YamlXmlBuilder {
module = potentialModule;
}
} 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];
- if (potentialModule === 'bmm' || potentialModule === 'core') {
- module = potentialModule;
+ if (potentialModule === 'bmm-skills') {
+ module = 'bmm';
+ } else if (potentialModule === 'core-skills') {
+ module = 'core';
}
}