bmad-method 6.0.0-alpha.10 → 6.0.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +215 -1109
- package/README.md +129 -359
- package/docs/custom-agent-installation.md +169 -0
- package/{v6-open-items.md → docs/v6-open-items.md} +1 -1
- package/package.json +2 -1
- package/src/core/resources/excalidraw/README.md +160 -0
- package/src/core/resources/excalidraw/library-loader.md +50 -0
- package/src/modules/bmb/docs/agent-compilation.md +340 -0
- package/src/modules/bmb/docs/agent-menu-patterns.md +524 -0
- package/src/modules/bmb/docs/expert-agent-architecture.md +364 -0
- package/src/modules/bmb/docs/index.md +55 -0
- package/src/modules/bmb/docs/module-agent-architecture.md +367 -0
- package/src/modules/bmb/docs/simple-agent-architecture.md +288 -0
- package/src/modules/bmb/docs/understanding-agent-types.md +184 -0
- package/src/modules/bmb/reference/agents/expert-examples/journal-keeper/README.md +242 -0
- package/src/modules/bmb/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/breakthroughs.md +24 -0
- package/src/modules/bmb/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/instructions.md +108 -0
- package/src/modules/bmb/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/memories.md +46 -0
- package/src/modules/bmb/reference/agents/expert-examples/journal-keeper/journal-keeper-sidecar/mood-patterns.md +39 -0
- package/src/modules/bmb/reference/agents/expert-examples/journal-keeper/journal-keeper.agent.yaml +152 -0
- package/src/modules/bmb/reference/agents/module-examples/README.md +50 -0
- package/src/modules/bmb/reference/agents/module-examples/security-engineer.agent.yaml +53 -0
- package/src/modules/bmb/reference/agents/module-examples/trend-analyst.agent.yaml +57 -0
- package/src/modules/bmb/reference/agents/simple-examples/README.md +223 -0
- package/src/modules/bmb/reference/agents/simple-examples/commit-poet.agent.yaml +126 -0
- package/src/modules/bmb/reference/readme.md +3 -0
- package/src/modules/bmb/workflows/create-agent/agent-validation-checklist.md +174 -0
- package/src/modules/bmb/workflows/create-agent/brainstorm-context.md +99 -120
- package/src/modules/bmb/workflows/create-agent/communication-presets.csv +61 -0
- package/src/modules/bmb/workflows/create-agent/instructions.md +126 -65
- package/src/modules/bmb/workflows/create-agent/workflow.yaml +19 -12
- package/src/modules/bmb/workflows/edit-agent/README.md +174 -47
- package/src/modules/bmb/workflows/edit-agent/instructions.md +397 -33
- package/src/modules/bmb/workflows/edit-agent/workflow.yaml +24 -8
- package/src/modules/bmgd/workflows/4-production/story-context/workflow.yaml +1 -1
- package/src/modules/bmm/agents/analyst.agent.yaml +2 -2
- package/src/modules/bmm/agents/architect.agent.yaml +10 -2
- package/src/modules/bmm/agents/dev.agent.yaml +2 -2
- package/src/modules/bmm/agents/pm.agent.yaml +7 -3
- package/src/modules/bmm/agents/sm.agent.yaml +2 -2
- package/src/modules/bmm/agents/tea.agent.yaml +2 -2
- package/src/modules/bmm/agents/tech-writer.agent.yaml +15 -3
- package/src/modules/bmm/agents/ux-designer.agent.yaml +6 -2
- package/src/modules/bmm/docs/README.md +4 -0
- package/src/modules/bmm/docs/images/workflow-method-greenfield.excalidraw +5919 -0
- package/src/modules/bmm/docs/images/workflow-method-greenfield.svg +2 -0
- package/src/modules/bmm/docs/quick-start.md +6 -0
- package/src/modules/bmm/docs/scale-adaptive-system.md +6 -0
- package/src/modules/bmm/docs/workflows-implementation.md +10 -0
- package/src/modules/bmm/workflows/2-plan-workflows/prd/workflow.yaml +4 -4
- package/src/modules/bmm/workflows/{2-plan-workflows → 3-solutioning}/create-epics-and-stories/workflow.yaml +5 -5
- package/src/modules/bmm/workflows/4-implementation/story-context/workflow.yaml +1 -1
- package/src/modules/bmm/workflows/{frame-expert → diagrams}/create-dataflow/instructions.md +7 -8
- package/src/modules/bmm/workflows/diagrams/create-dataflow/workflow.yaml +27 -0
- package/src/modules/bmm/workflows/{frame-expert → diagrams}/create-diagram/instructions.md +9 -10
- package/src/modules/bmm/workflows/diagrams/create-diagram/workflow.yaml +27 -0
- package/src/modules/bmm/workflows/{frame-expert → diagrams}/create-flowchart/instructions.md +4 -5
- package/src/modules/bmm/workflows/diagrams/create-flowchart/workflow.yaml +27 -0
- package/src/modules/bmm/workflows/{frame-expert → diagrams}/create-wireframe/instructions.md +3 -3
- package/src/modules/bmm/workflows/diagrams/create-wireframe/workflow.yaml +27 -0
- package/src/modules/bmm/workflows/workflow-status/paths/enterprise-brownfield.yaml +18 -30
- package/src/modules/bmm/workflows/workflow-status/paths/enterprise-greenfield.yaml +2 -14
- package/src/modules/bmm/workflows/workflow-status/paths/method-brownfield.yaml +2 -14
- package/src/modules/bmm/workflows/workflow-status/paths/method-greenfield.yaml +2 -14
- package/src/modules/cis/agents/presentation-master.agent.yaml +60 -0
- package/tools/cli/commands/agent-install.js +409 -0
- package/tools/cli/installers/lib/core/installer.js +119 -0
- package/tools/cli/installers/lib/ide/_base-ide.js +25 -0
- package/tools/cli/installers/lib/ide/antigravity.js +463 -0
- package/tools/cli/installers/lib/ide/claude-code.js +43 -0
- package/tools/cli/installers/lib/ide/codex.js +217 -32
- package/tools/cli/installers/lib/ide/cursor.js +48 -0
- package/tools/cli/installers/lib/ide/github-copilot.js +74 -0
- package/tools/cli/installers/lib/ide/manager.js +35 -0
- package/tools/cli/installers/lib/ide/opencode.js +45 -0
- package/tools/cli/installers/lib/ide/windsurf.js +47 -0
- package/tools/cli/lib/agent/compiler.js +390 -0
- package/tools/cli/lib/agent/installer.js +725 -0
- package/tools/cli/lib/agent/template-engine.js +152 -0
- package/docs/installers-bundlers/web-bundler-usage.md +0 -54
- package/src/modules/bmb/workflows/create-agent/README.md +0 -203
- package/src/modules/bmb/workflows/create-agent/agent-architecture.md +0 -415
- package/src/modules/bmb/workflows/create-agent/agent-command-patterns.md +0 -759
- package/src/modules/bmb/workflows/create-agent/agent-types.md +0 -292
- package/src/modules/bmb/workflows/create-agent/checklist.md +0 -62
- package/src/modules/bmb/workflows/create-agent/communication-styles.md +0 -202
- package/src/modules/bmb/workflows/edit-agent/checklist.md +0 -112
- package/src/modules/bmb/workflows/redoc/README.md +0 -87
- package/src/modules/bmb/workflows/redoc/checklist.md +0 -99
- package/src/modules/bmb/workflows/redoc/instructions.md +0 -265
- package/src/modules/bmb/workflows/redoc/workflow.yaml +0 -34
- package/src/modules/bmm/agents/frame-expert.agent.yaml +0 -42
- package/src/modules/bmm/workflows/frame-expert/create-dataflow/workflow.yaml +0 -24
- package/src/modules/bmm/workflows/frame-expert/create-diagram/workflow.yaml +0 -25
- package/src/modules/bmm/workflows/frame-expert/create-flowchart/workflow.yaml +0 -28
- package/src/modules/bmm/workflows/frame-expert/create-wireframe/workflow.yaml +0 -24
- package/src/modules/bmm/workflows/workflow-status/paths/game-design.yaml +0 -52
- /package/src/{modules/bmm/workflows/frame-expert/_shared → core/resources/excalidraw}/excalidraw-helpers.md +0 -0
- /package/src/{modules/bmm/workflows/frame-expert/_shared → core/resources/excalidraw}/validate-json-instructions.md +0 -0
- /package/src/modules/bmm/workflows/{2-plan-workflows → 3-solutioning}/create-epics-and-stories/epics-template.md +0 -0
- /package/src/modules/bmm/workflows/{2-plan-workflows → 3-solutioning}/create-epics-and-stories/instructions.md +0 -0
- /package/src/modules/bmm/workflows/{frame-expert → diagrams}/_shared/excalidraw-library.json +0 -0
- /package/src/modules/bmm/workflows/{frame-expert → diagrams}/_shared/excalidraw-templates.yaml +0 -0
- /package/src/modules/bmm/workflows/{frame-expert → diagrams}/create-dataflow/checklist.md +0 -0
- /package/src/modules/bmm/workflows/{frame-expert → diagrams}/create-diagram/checklist.md +0 -0
- /package/src/modules/bmm/workflows/{frame-expert → diagrams}/create-flowchart/checklist.md +0 -0
- /package/src/modules/bmm/workflows/{frame-expert → diagrams}/create-wireframe/checklist.md +0 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const readline = require('node:readline');
|
|
5
|
+
const {
|
|
6
|
+
findBmadConfig,
|
|
7
|
+
resolvePath,
|
|
8
|
+
discoverAgents,
|
|
9
|
+
loadAgentConfig,
|
|
10
|
+
promptInstallQuestions,
|
|
11
|
+
detectBmadProject,
|
|
12
|
+
addToManifest,
|
|
13
|
+
extractManifestData,
|
|
14
|
+
checkManifestForPath,
|
|
15
|
+
updateManifestEntry,
|
|
16
|
+
saveAgentSource,
|
|
17
|
+
createIdeSlashCommands,
|
|
18
|
+
updateManifestYaml,
|
|
19
|
+
} = require('../lib/agent/installer');
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
command: 'agent-install',
|
|
23
|
+
description: 'Install and compile BMAD agents with personalization',
|
|
24
|
+
options: [
|
|
25
|
+
['-p, --path <path>', 'Path to specific agent YAML file or folder'],
|
|
26
|
+
['-d, --defaults', 'Use default values without prompting'],
|
|
27
|
+
['-t, --target <path>', 'Target installation directory (default: .bmad/agents)'],
|
|
28
|
+
],
|
|
29
|
+
action: async (options) => {
|
|
30
|
+
try {
|
|
31
|
+
console.log(chalk.cyan('\n🔧 BMAD Agent Installer\n'));
|
|
32
|
+
|
|
33
|
+
// Find BMAD config
|
|
34
|
+
const config = findBmadConfig();
|
|
35
|
+
if (!config) {
|
|
36
|
+
console.log(chalk.yellow('No BMAD installation found in current directory.'));
|
|
37
|
+
console.log(chalk.dim('Looking for .bmad/bmb/config.yaml...'));
|
|
38
|
+
console.log(chalk.red('\nPlease run this command from a project with BMAD installed.'));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log(chalk.dim(`Found BMAD at: ${config.bmadFolder}`));
|
|
43
|
+
|
|
44
|
+
let selectedAgent = null;
|
|
45
|
+
|
|
46
|
+
// If path provided, use it directly
|
|
47
|
+
if (options.path) {
|
|
48
|
+
const providedPath = path.resolve(options.path);
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(providedPath)) {
|
|
51
|
+
console.log(chalk.red(`Path not found: ${providedPath}`));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const stat = fs.statSync(providedPath);
|
|
56
|
+
if (stat.isFile() && providedPath.endsWith('.agent.yaml')) {
|
|
57
|
+
selectedAgent = {
|
|
58
|
+
type: 'simple',
|
|
59
|
+
name: path.basename(providedPath, '.agent.yaml'),
|
|
60
|
+
path: providedPath,
|
|
61
|
+
yamlFile: providedPath,
|
|
62
|
+
};
|
|
63
|
+
} else if (stat.isDirectory()) {
|
|
64
|
+
const yamlFiles = fs.readdirSync(providedPath).filter((f) => f.endsWith('.agent.yaml'));
|
|
65
|
+
if (yamlFiles.length === 1) {
|
|
66
|
+
selectedAgent = {
|
|
67
|
+
type: 'expert',
|
|
68
|
+
name: path.basename(providedPath),
|
|
69
|
+
path: providedPath,
|
|
70
|
+
yamlFile: path.join(providedPath, yamlFiles[0]),
|
|
71
|
+
hasSidecar: true,
|
|
72
|
+
};
|
|
73
|
+
} else {
|
|
74
|
+
console.log(chalk.red('Directory must contain exactly one .agent.yaml file'));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
console.log(chalk.red('Path must be an .agent.yaml file or a folder containing one'));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
// Discover agents from custom location
|
|
83
|
+
const customAgentLocation = config.custom_agent_location
|
|
84
|
+
? resolvePath(config.custom_agent_location, config)
|
|
85
|
+
: path.join(config.bmadFolder, 'custom', 'agents');
|
|
86
|
+
|
|
87
|
+
console.log(chalk.dim(`Searching for agents in: ${customAgentLocation}\n`));
|
|
88
|
+
|
|
89
|
+
const agents = discoverAgents(customAgentLocation);
|
|
90
|
+
|
|
91
|
+
if (agents.length === 0) {
|
|
92
|
+
console.log(chalk.yellow('No agents found in custom agent location.'));
|
|
93
|
+
console.log(chalk.dim(`Expected location: ${customAgentLocation}`));
|
|
94
|
+
console.log(chalk.dim('\nCreate agents using the BMad Builder workflow or place .agent.yaml files there.'));
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// List available agents
|
|
99
|
+
console.log(chalk.cyan('Available Agents:\n'));
|
|
100
|
+
for (const [idx, agent] of agents.entries()) {
|
|
101
|
+
const typeIcon = agent.type === 'expert' ? '📚' : '📄';
|
|
102
|
+
console.log(` ${idx + 1}. ${typeIcon} ${chalk.bold(agent.name)} ${chalk.dim(`(${agent.type})`)}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Prompt for selection
|
|
106
|
+
const rl = readline.createInterface({
|
|
107
|
+
input: process.stdin,
|
|
108
|
+
output: process.stdout,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const selection = await new Promise((resolve) => {
|
|
112
|
+
rl.question('\nSelect agent to install (number): ', resolve);
|
|
113
|
+
});
|
|
114
|
+
rl.close();
|
|
115
|
+
|
|
116
|
+
const selectedIdx = parseInt(selection, 10) - 1;
|
|
117
|
+
if (isNaN(selectedIdx) || selectedIdx < 0 || selectedIdx >= agents.length) {
|
|
118
|
+
console.log(chalk.red('Invalid selection'));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
selectedAgent = agents[selectedIdx];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log(chalk.cyan(`\nSelected: ${chalk.bold(selectedAgent.name)}`));
|
|
126
|
+
|
|
127
|
+
// Load agent configuration
|
|
128
|
+
const agentConfig = loadAgentConfig(selectedAgent.yamlFile);
|
|
129
|
+
|
|
130
|
+
if (agentConfig.metadata.name) {
|
|
131
|
+
console.log(chalk.dim(`Agent Name: ${agentConfig.metadata.name}`));
|
|
132
|
+
}
|
|
133
|
+
if (agentConfig.metadata.title) {
|
|
134
|
+
console.log(chalk.dim(`Title: ${agentConfig.metadata.title}`));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Get the agent type (source name)
|
|
138
|
+
const agentType = selectedAgent.name; // e.g., "commit-poet"
|
|
139
|
+
|
|
140
|
+
// Confirm/customize agent persona name
|
|
141
|
+
const rl1 = readline.createInterface({
|
|
142
|
+
input: process.stdin,
|
|
143
|
+
output: process.stdout,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const defaultPersonaName = agentConfig.metadata.name || agentType;
|
|
147
|
+
console.log(chalk.cyan('\n📛 Agent Persona Name\n'));
|
|
148
|
+
console.log(chalk.dim(` Agent type: ${agentType}`));
|
|
149
|
+
console.log(chalk.dim(` Default persona: ${defaultPersonaName}`));
|
|
150
|
+
console.log(chalk.dim(' Leave blank to use default, or provide a custom name.'));
|
|
151
|
+
console.log(chalk.dim(' Examples:'));
|
|
152
|
+
console.log(chalk.dim(` - (blank) → "${defaultPersonaName}" as ${agentType}.md`));
|
|
153
|
+
console.log(chalk.dim(` - "Fred" → "Fred" as fred-${agentType}.md`));
|
|
154
|
+
console.log(chalk.dim(` - "Captain Code" → "Captain Code" as captain-code-${agentType}.md`));
|
|
155
|
+
|
|
156
|
+
const customPersonaName = await new Promise((resolve) => {
|
|
157
|
+
rl1.question(`\n Custom name (or Enter for default): `, resolve);
|
|
158
|
+
});
|
|
159
|
+
rl1.close();
|
|
160
|
+
|
|
161
|
+
// Determine final agent file name based on persona name
|
|
162
|
+
let finalAgentName;
|
|
163
|
+
let personaName;
|
|
164
|
+
if (customPersonaName.trim()) {
|
|
165
|
+
personaName = customPersonaName.trim();
|
|
166
|
+
const namePrefix = personaName.toLowerCase().replaceAll(/\s+/g, '-');
|
|
167
|
+
finalAgentName = `${namePrefix}-${agentType}`;
|
|
168
|
+
} else {
|
|
169
|
+
personaName = defaultPersonaName;
|
|
170
|
+
finalAgentName = agentType;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.log(chalk.dim(` Persona: ${personaName}`));
|
|
174
|
+
console.log(chalk.dim(` File: ${finalAgentName}.md`));
|
|
175
|
+
|
|
176
|
+
// Get answers (prompt or use defaults)
|
|
177
|
+
let presetAnswers = {};
|
|
178
|
+
|
|
179
|
+
// If custom persona name provided, inject it as custom_name for template processing
|
|
180
|
+
if (customPersonaName.trim()) {
|
|
181
|
+
presetAnswers.custom_name = personaName;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let answers;
|
|
185
|
+
if (agentConfig.installConfig && !options.defaults) {
|
|
186
|
+
answers = await promptInstallQuestions(agentConfig.installConfig, agentConfig.defaults, presetAnswers);
|
|
187
|
+
} else if (agentConfig.installConfig && options.defaults) {
|
|
188
|
+
console.log(chalk.dim('\nUsing default configuration values.'));
|
|
189
|
+
answers = { ...agentConfig.defaults, ...presetAnswers };
|
|
190
|
+
} else {
|
|
191
|
+
answers = { ...agentConfig.defaults, ...presetAnswers };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Determine target directory
|
|
195
|
+
let targetDir = options.target ? path.resolve(options.target) : null;
|
|
196
|
+
|
|
197
|
+
// If no target specified, prompt for it
|
|
198
|
+
if (targetDir) {
|
|
199
|
+
// If target provided via --target, check if it's a project root and adjust
|
|
200
|
+
const otherProject = detectBmadProject(targetDir);
|
|
201
|
+
if (otherProject && !targetDir.includes('agents')) {
|
|
202
|
+
// User specified project root, redirect to custom agents folder
|
|
203
|
+
targetDir = path.join(otherProject.bmadFolder, 'custom', 'agents');
|
|
204
|
+
console.log(chalk.dim(` Auto-selecting custom agents folder: ${targetDir}`));
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
const rl = readline.createInterface({
|
|
208
|
+
input: process.stdin,
|
|
209
|
+
output: process.stdout,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
console.log(chalk.cyan('\n📂 Installation Target\n'));
|
|
213
|
+
|
|
214
|
+
// Option 1: Current project's custom agents folder
|
|
215
|
+
const currentCustom = path.join(config.bmadFolder, 'custom', 'agents');
|
|
216
|
+
console.log(` 1. Current project: ${chalk.dim(currentCustom)}`);
|
|
217
|
+
|
|
218
|
+
// Option 2: Specify another project path
|
|
219
|
+
console.log(` 2. Another project (enter path)`);
|
|
220
|
+
|
|
221
|
+
const choice = await new Promise((resolve) => {
|
|
222
|
+
rl.question('\n Select option (1 or path): ', resolve);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (choice.trim() === '1' || choice.trim() === '') {
|
|
226
|
+
targetDir = currentCustom;
|
|
227
|
+
} else if (choice.trim() === '2') {
|
|
228
|
+
const projectPath = await new Promise((resolve) => {
|
|
229
|
+
rl.question(' Project path: ', resolve);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Detect if it's a BMAD project and use its custom folder
|
|
233
|
+
const otherProject = detectBmadProject(path.resolve(projectPath));
|
|
234
|
+
if (otherProject) {
|
|
235
|
+
targetDir = path.join(otherProject.bmadFolder, 'custom', 'agents');
|
|
236
|
+
console.log(chalk.dim(` Found BMAD project, using: ${targetDir}`));
|
|
237
|
+
} else {
|
|
238
|
+
targetDir = path.resolve(projectPath);
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
// User entered a path directly
|
|
242
|
+
const otherProject = detectBmadProject(path.resolve(choice));
|
|
243
|
+
if (otherProject) {
|
|
244
|
+
targetDir = path.join(otherProject.bmadFolder, 'custom', 'agents');
|
|
245
|
+
console.log(chalk.dim(` Found BMAD project, using: ${targetDir}`));
|
|
246
|
+
} else {
|
|
247
|
+
targetDir = path.resolve(choice);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
rl.close();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!fs.existsSync(targetDir)) {
|
|
255
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
console.log(chalk.dim(`\nInstalling to: ${targetDir}`));
|
|
259
|
+
|
|
260
|
+
// Detect if target is within a BMAD project
|
|
261
|
+
const targetProject = detectBmadProject(targetDir);
|
|
262
|
+
if (targetProject) {
|
|
263
|
+
console.log(chalk.cyan(` Detected BMAD project at: ${targetProject.projectRoot}`));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Check for duplicate in manifest by path (not by type)
|
|
267
|
+
let shouldUpdateExisting = false;
|
|
268
|
+
let existingEntry = null;
|
|
269
|
+
|
|
270
|
+
if (targetProject) {
|
|
271
|
+
// Check if this exact installed name already exists
|
|
272
|
+
const expectedPath = `.bmad/custom/agents/${finalAgentName}/${finalAgentName}.md`;
|
|
273
|
+
existingEntry = checkManifestForPath(targetProject.manifestFile, expectedPath);
|
|
274
|
+
|
|
275
|
+
if (existingEntry) {
|
|
276
|
+
const rl2 = readline.createInterface({
|
|
277
|
+
input: process.stdin,
|
|
278
|
+
output: process.stdout,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
console.log(chalk.yellow(`\n⚠️ Agent "${finalAgentName}" already installed`));
|
|
282
|
+
console.log(chalk.dim(` Type: ${agentType}`));
|
|
283
|
+
console.log(chalk.dim(` Path: ${existingEntry.path}`));
|
|
284
|
+
|
|
285
|
+
const overwrite = await new Promise((resolve) => {
|
|
286
|
+
rl2.question(' Overwrite existing installation? [Y/n]: ', resolve);
|
|
287
|
+
});
|
|
288
|
+
rl2.close();
|
|
289
|
+
|
|
290
|
+
if (overwrite.toLowerCase() === 'n') {
|
|
291
|
+
console.log(chalk.yellow('Installation cancelled.'));
|
|
292
|
+
process.exit(0);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
shouldUpdateExisting = true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Install the agent with custom name
|
|
300
|
+
// Override the folder name with finalAgentName
|
|
301
|
+
const agentTargetDir = path.join(targetDir, finalAgentName);
|
|
302
|
+
|
|
303
|
+
if (!fs.existsSync(agentTargetDir)) {
|
|
304
|
+
fs.mkdirSync(agentTargetDir, { recursive: true });
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Compile and install
|
|
308
|
+
const { compileAgent } = require('../lib/agent/compiler');
|
|
309
|
+
|
|
310
|
+
// Calculate target path for agent ID
|
|
311
|
+
const projectRoot = targetProject ? targetProject.projectRoot : config.projectRoot;
|
|
312
|
+
const compiledFileName = `${finalAgentName}.md`;
|
|
313
|
+
const compiledPath = path.join(agentTargetDir, compiledFileName);
|
|
314
|
+
const relativePath = path.relative(projectRoot, compiledPath);
|
|
315
|
+
|
|
316
|
+
// Compile with proper name and path
|
|
317
|
+
const { xml, metadata, processedYaml } = compileAgent(
|
|
318
|
+
fs.readFileSync(selectedAgent.yamlFile, 'utf8'),
|
|
319
|
+
answers,
|
|
320
|
+
finalAgentName,
|
|
321
|
+
relativePath,
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// Write compiled XML (.md) with custom name
|
|
325
|
+
fs.writeFileSync(compiledPath, xml, 'utf8');
|
|
326
|
+
|
|
327
|
+
const result = {
|
|
328
|
+
success: true,
|
|
329
|
+
agentName: finalAgentName,
|
|
330
|
+
targetDir: agentTargetDir,
|
|
331
|
+
compiledFile: compiledPath,
|
|
332
|
+
sidecarCopied: false,
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// Copy sidecar files for expert agents
|
|
336
|
+
if (selectedAgent.hasSidecar && selectedAgent.type === 'expert') {
|
|
337
|
+
const { copySidecarFiles } = require('../lib/agent/installer');
|
|
338
|
+
const sidecarFiles = copySidecarFiles(selectedAgent.path, agentTargetDir, selectedAgent.yamlFile);
|
|
339
|
+
result.sidecarCopied = true;
|
|
340
|
+
result.sidecarFiles = sidecarFiles;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
console.log(chalk.green('\n✨ Agent installed successfully!'));
|
|
344
|
+
console.log(chalk.cyan(` Name: ${result.agentName}`));
|
|
345
|
+
console.log(chalk.cyan(` Location: ${result.targetDir}`));
|
|
346
|
+
console.log(chalk.cyan(` Compiled: ${path.basename(result.compiledFile)}`));
|
|
347
|
+
|
|
348
|
+
if (result.sidecarCopied) {
|
|
349
|
+
console.log(chalk.cyan(` Sidecar files: ${result.sidecarFiles.length} files copied`));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Save source YAML to _cfg/custom/agents/ and register in manifest
|
|
353
|
+
if (targetProject) {
|
|
354
|
+
// Save source for reinstallation with embedded answers
|
|
355
|
+
console.log(chalk.dim(`\nSaving source to: ${targetProject.cfgFolder}/custom/agents/`));
|
|
356
|
+
saveAgentSource(selectedAgent, targetProject.cfgFolder, finalAgentName, answers);
|
|
357
|
+
console.log(chalk.green(` ✓ Source saved for reinstallation`));
|
|
358
|
+
|
|
359
|
+
// Register/update in manifest
|
|
360
|
+
console.log(chalk.dim(`Registering in manifest: ${targetProject.manifestFile}`));
|
|
361
|
+
|
|
362
|
+
const manifestData = extractManifestData(xml, { ...metadata, name: finalAgentName }, relativePath, 'custom');
|
|
363
|
+
// Use finalAgentName as the manifest name field (unique identifier)
|
|
364
|
+
manifestData.name = finalAgentName;
|
|
365
|
+
// Use compiled metadata.name (persona name after template processing), not source agentConfig
|
|
366
|
+
manifestData.displayName = metadata.name || agentType;
|
|
367
|
+
// Store the actual installed path/name
|
|
368
|
+
manifestData.path = relativePath;
|
|
369
|
+
|
|
370
|
+
if (shouldUpdateExisting && existingEntry) {
|
|
371
|
+
updateManifestEntry(targetProject.manifestFile, manifestData, existingEntry._lineNumber);
|
|
372
|
+
console.log(chalk.green(` ✓ Updated existing entry in agent-manifest.csv`));
|
|
373
|
+
} else {
|
|
374
|
+
addToManifest(targetProject.manifestFile, manifestData);
|
|
375
|
+
console.log(chalk.green(` ✓ Added to agent-manifest.csv`));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Create IDE slash commands
|
|
379
|
+
const ideResults = await createIdeSlashCommands(targetProject.projectRoot, finalAgentName, relativePath, metadata);
|
|
380
|
+
if (Object.keys(ideResults).length > 0) {
|
|
381
|
+
console.log(chalk.green(` ✓ Created IDE commands:`));
|
|
382
|
+
for (const [ideName, result] of Object.entries(ideResults)) {
|
|
383
|
+
console.log(chalk.dim(` ${ideName}: ${result.command}`));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Update manifest.yaml with custom_agents tracking
|
|
388
|
+
const manifestYamlPath = path.join(targetProject.cfgFolder, 'manifest.yaml');
|
|
389
|
+
if (updateManifestYaml(manifestYamlPath, finalAgentName, agentType)) {
|
|
390
|
+
console.log(chalk.green(` ✓ Updated manifest.yaml custom_agents`));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
console.log(chalk.dim(`\nAgent ID: ${relativePath}`));
|
|
395
|
+
|
|
396
|
+
if (targetProject) {
|
|
397
|
+
console.log(chalk.yellow('\nAgent is now registered and available in the target project!'));
|
|
398
|
+
} else {
|
|
399
|
+
console.log(chalk.yellow('\nTo use this agent, reference it in your manifest or load it directly.'));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
process.exit(0);
|
|
403
|
+
} catch (error) {
|
|
404
|
+
console.error(chalk.red('Agent installation failed:'), error.message);
|
|
405
|
+
console.error(chalk.dim(error.stack));
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
};
|
|
@@ -840,6 +840,15 @@ class Installer {
|
|
|
840
840
|
console.log(chalk.dim('Review the .bak files to see your changes and merge if needed.\n'));
|
|
841
841
|
}
|
|
842
842
|
|
|
843
|
+
// Reinstall custom agents from _cfg/custom/agents/ sources
|
|
844
|
+
const customAgentResults = await this.reinstallCustomAgents(projectDir, bmadDir);
|
|
845
|
+
if (customAgentResults.count > 0) {
|
|
846
|
+
console.log(chalk.green(`\n✓ Reinstalled ${customAgentResults.count} custom agent${customAgentResults.count > 1 ? 's' : ''}`));
|
|
847
|
+
for (const agent of customAgentResults.agents) {
|
|
848
|
+
console.log(chalk.dim(` - ${agent}`));
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
843
852
|
// Display completion message
|
|
844
853
|
const { UI } = require('../../../lib/ui');
|
|
845
854
|
const ui = new UI();
|
|
@@ -2245,6 +2254,116 @@ class Installer {
|
|
|
2245
2254
|
return nodes;
|
|
2246
2255
|
}
|
|
2247
2256
|
|
|
2257
|
+
/**
|
|
2258
|
+
* Reinstall custom agents from _cfg/custom/agents/ sources
|
|
2259
|
+
* This preserves custom agents across quick updates/reinstalls
|
|
2260
|
+
* @param {string} projectDir - Project directory
|
|
2261
|
+
* @param {string} bmadDir - BMAD installation directory
|
|
2262
|
+
* @returns {Object} Result with count and agent names
|
|
2263
|
+
*/
|
|
2264
|
+
async reinstallCustomAgents(projectDir, bmadDir) {
|
|
2265
|
+
const customAgentsCfgDir = path.join(bmadDir, '_cfg', 'custom', 'agents');
|
|
2266
|
+
const results = { count: 0, agents: [] };
|
|
2267
|
+
|
|
2268
|
+
if (!(await fs.pathExists(customAgentsCfgDir))) {
|
|
2269
|
+
return results;
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
try {
|
|
2273
|
+
const {
|
|
2274
|
+
discoverAgents,
|
|
2275
|
+
loadAgentConfig,
|
|
2276
|
+
extractManifestData,
|
|
2277
|
+
addToManifest,
|
|
2278
|
+
createIdeSlashCommands,
|
|
2279
|
+
updateManifestYaml,
|
|
2280
|
+
} = require('../../../lib/agent/installer');
|
|
2281
|
+
const { compileAgent } = require('../../../lib/agent/compiler');
|
|
2282
|
+
|
|
2283
|
+
// Discover custom agents in _cfg/custom/agents/
|
|
2284
|
+
const agents = discoverAgents(customAgentsCfgDir);
|
|
2285
|
+
|
|
2286
|
+
if (agents.length === 0) {
|
|
2287
|
+
return results;
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
const customAgentsDir = path.join(bmadDir, 'custom', 'agents');
|
|
2291
|
+
await fs.ensureDir(customAgentsDir);
|
|
2292
|
+
|
|
2293
|
+
const manifestFile = path.join(bmadDir, '_cfg', 'agent-manifest.csv');
|
|
2294
|
+
const manifestYamlFile = path.join(bmadDir, '_cfg', 'manifest.yaml');
|
|
2295
|
+
|
|
2296
|
+
for (const agent of agents) {
|
|
2297
|
+
try {
|
|
2298
|
+
const agentConfig = loadAgentConfig(agent.yamlFile);
|
|
2299
|
+
const finalAgentName = agent.name; // Already named correctly from save
|
|
2300
|
+
|
|
2301
|
+
// Determine agent type from the name (e.g., "fred-commit-poet" → "commit-poet")
|
|
2302
|
+
let agentType = finalAgentName;
|
|
2303
|
+
const parts = finalAgentName.split('-');
|
|
2304
|
+
if (parts.length >= 2) {
|
|
2305
|
+
// Try to extract type (last part or last two parts)
|
|
2306
|
+
// For "fred-commit-poet", we want "commit-poet"
|
|
2307
|
+
// This is heuristic - could be improved with metadata storage
|
|
2308
|
+
agentType = parts.slice(-2).join('-'); // Take last 2 parts as type
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
// Create target directory
|
|
2312
|
+
const agentTargetDir = path.join(customAgentsDir, finalAgentName);
|
|
2313
|
+
await fs.ensureDir(agentTargetDir);
|
|
2314
|
+
|
|
2315
|
+
// Calculate paths
|
|
2316
|
+
const compiledFileName = `${finalAgentName}.md`;
|
|
2317
|
+
const compiledPath = path.join(agentTargetDir, compiledFileName);
|
|
2318
|
+
const relativePath = path.relative(projectDir, compiledPath);
|
|
2319
|
+
|
|
2320
|
+
// Compile with embedded defaults (answers are already in defaults section)
|
|
2321
|
+
const { xml, metadata } = compileAgent(
|
|
2322
|
+
await fs.readFile(agent.yamlFile, 'utf8'),
|
|
2323
|
+
agentConfig.defaults || {},
|
|
2324
|
+
finalAgentName,
|
|
2325
|
+
relativePath,
|
|
2326
|
+
);
|
|
2327
|
+
|
|
2328
|
+
// Write compiled agent
|
|
2329
|
+
await fs.writeFile(compiledPath, xml, 'utf8');
|
|
2330
|
+
|
|
2331
|
+
// Copy sidecar files if expert agent
|
|
2332
|
+
if (agent.hasSidecar && agent.type === 'expert') {
|
|
2333
|
+
const { copySidecarFiles } = require('../../../lib/agent/installer');
|
|
2334
|
+
copySidecarFiles(agent.path, agentTargetDir, agent.yamlFile);
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
// Update manifest CSV
|
|
2338
|
+
if (await fs.pathExists(manifestFile)) {
|
|
2339
|
+
const manifestData = extractManifestData(xml, { ...metadata, name: finalAgentName }, relativePath, 'custom');
|
|
2340
|
+
manifestData.name = finalAgentName;
|
|
2341
|
+
manifestData.displayName = metadata.name || finalAgentName;
|
|
2342
|
+
manifestData.path = relativePath;
|
|
2343
|
+
addToManifest(manifestFile, manifestData);
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
// Create IDE slash commands (async function)
|
|
2347
|
+
await createIdeSlashCommands(projectDir, finalAgentName, relativePath, metadata);
|
|
2348
|
+
|
|
2349
|
+
// Update manifest.yaml
|
|
2350
|
+
if (await fs.pathExists(manifestYamlFile)) {
|
|
2351
|
+
updateManifestYaml(manifestYamlFile, finalAgentName, agentType);
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
results.count++;
|
|
2355
|
+
results.agents.push(finalAgentName);
|
|
2356
|
+
} catch (agentError) {
|
|
2357
|
+
console.log(chalk.yellow(` ⚠️ Failed to reinstall ${agent.name}: ${agentError.message}`));
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
} catch (error) {
|
|
2361
|
+
console.log(chalk.yellow(` ⚠️ Error reinstalling custom agents: ${error.message}`));
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
return results;
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2248
2367
|
/**
|
|
2249
2368
|
* Copy IDE-specific documentation to BMAD docs
|
|
2250
2369
|
* @param {Array} ides - List of selected IDEs
|
|
@@ -73,6 +73,19 @@ class BaseIdeSetup {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Install a custom agent launcher - subclasses should override
|
|
78
|
+
* @param {string} projectDir - Project directory
|
|
79
|
+
* @param {string} agentName - Agent name (e.g., "fred-commit-poet")
|
|
80
|
+
* @param {string} agentPath - Path to compiled agent (relative to project root)
|
|
81
|
+
* @param {Object} metadata - Agent metadata
|
|
82
|
+
* @returns {Object|null} Info about created command, or null if not supported
|
|
83
|
+
*/
|
|
84
|
+
async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) {
|
|
85
|
+
// Default implementation - subclasses can override
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
76
89
|
/**
|
|
77
90
|
* Detect whether this IDE already has configuration in the project
|
|
78
91
|
* Subclasses can override for custom logic
|
|
@@ -601,6 +614,18 @@ class BaseIdeSetup {
|
|
|
601
614
|
.join(' ');
|
|
602
615
|
}
|
|
603
616
|
|
|
617
|
+
/**
|
|
618
|
+
* Flatten a relative path to a single filename for flat slash command naming
|
|
619
|
+
* Example: 'module/agents/name.md' -> 'bmad-module-agents-name.md'
|
|
620
|
+
* Used by IDEs that ignore directory structure for slash commands (e.g., Antigravity, Codex)
|
|
621
|
+
* @param {string} relativePath - Relative path to flatten
|
|
622
|
+
* @returns {string} Flattened filename with 'bmad-' prefix
|
|
623
|
+
*/
|
|
624
|
+
flattenFilename(relativePath) {
|
|
625
|
+
const sanitized = relativePath.replaceAll(/[/\\]/g, '-');
|
|
626
|
+
return `bmad-${sanitized}`;
|
|
627
|
+
}
|
|
628
|
+
|
|
604
629
|
/**
|
|
605
630
|
* Create agent configuration file
|
|
606
631
|
* @param {string} bmadDir - BMAD installation directory
|