aided-dev 1.0.0

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.
@@ -0,0 +1,393 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import * as p from '@clack/prompts';
3
+ import chalk from 'chalk';
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import * as yaml from 'js-yaml';
7
+ import { getApiKey } from '../config.js';
8
+ import { getLoader, buildSystemPrompt } from '../bmad/index.js';
9
+ export class AgentCreator {
10
+ client;
11
+ model;
12
+ outputDir;
13
+ verbose;
14
+ conversationHistory = [];
15
+ constructor(options = {}) {
16
+ const apiKey = getApiKey();
17
+ if (!apiKey) {
18
+ throw new Error('API key not configured. Run: aidev config --api-key <key>');
19
+ }
20
+ this.client = new Anthropic({ apiKey });
21
+ this.model = options.model || 'claude-sonnet-4-20250514';
22
+ this.outputDir = options.outputDir || process.cwd();
23
+ this.verbose = options.verbose || false;
24
+ }
25
+ async buildAgentBuilderPrompt() {
26
+ const loader = getLoader();
27
+ if (loader.isAvailable()) {
28
+ const agentBuilderPath = path.join(loader.getPath(), 'src/modules/bmb/agents/agent-builder.agent.yaml');
29
+ if (fs.existsSync(agentBuilderPath)) {
30
+ try {
31
+ const content = fs.readFileSync(agentBuilderPath, 'utf-8');
32
+ const parsed = yaml.load(content);
33
+ if (parsed?.agent) {
34
+ return buildSystemPrompt(parsed.agent) + this.getAgentCreationInstructions();
35
+ }
36
+ }
37
+ catch (error) {
38
+ if (this.verbose) {
39
+ console.log(chalk.dim('Could not load agent-builder, using fallback'));
40
+ }
41
+ }
42
+ }
43
+ }
44
+ return this.getFallbackAgentBuilderPrompt();
45
+ }
46
+ getAgentCreationInstructions() {
47
+ return `
48
+
49
+ ## Your Task
50
+
51
+ You are helping the user create a new BMAD-compliant agent. Guide them through these steps:
52
+
53
+ ### Step 1: Brainstorming
54
+ Ask about:
55
+ - What problem should this agent solve?
56
+ - What domain expertise should it have?
57
+ - What's the target user?
58
+
59
+ ### Step 2: Metadata
60
+ Help define:
61
+ - **name**: A short, memorable name (like Mary, Winston, Bond)
62
+ - **title**: Professional title (like Business Analyst, Agent Building Expert)
63
+ - **icon**: A single emoji that represents the agent
64
+ - **id**: A unique identifier path
65
+
66
+ ### Step 3: Persona
67
+ Craft together:
68
+ - **role**: The agent's primary function and expertise areas
69
+ - **identity**: Background, experience, and specialization
70
+ - **communication_style**: How the agent speaks and interacts
71
+ - **principles**: Core rules and guidelines the agent follows
72
+
73
+ ### Step 4: Menu Commands (optional)
74
+ Define any commands the agent should support with:
75
+ - **trigger**: Short code and fuzzy match pattern
76
+ - **description**: What the command does
77
+
78
+ ### Step 5: Output
79
+ When complete, output the agent definition in YAML format between \`\`\`yaml and \`\`\` markers.
80
+
81
+ Start by introducing yourself and asking about the agent they want to create.`;
82
+ }
83
+ getFallbackAgentBuilderPrompt() {
84
+ return `You are Bond, the Agent Building Expert.
85
+
86
+ ## Role
87
+ Agent Architecture Specialist + BMAD Compliance Expert
88
+
89
+ ## Identity
90
+ Master agent architect with deep expertise in agent design patterns, persona development, and BMAD Core compliance. Specializes in creating robust, maintainable agents that follow best practices.
91
+
92
+ ## Communication Style
93
+ Precise and technical, like a senior software architect reviewing code. Focuses on structure, compliance, and long-term maintainability. Uses agent-specific terminology and framework references.
94
+
95
+ ## Principles
96
+ - Every agent must follow BMAD Core standards and best practices
97
+ - Personas drive agent behavior - make them specific and authentic
98
+ - Menu structure must be consistent across all agents
99
+ - Validate compliance before finalizing any agent
100
+ - Focus on practical implementation and real-world usage
101
+ ${this.getAgentCreationInstructions()}`;
102
+ }
103
+ async chat(userMessage, systemPrompt) {
104
+ this.conversationHistory.push({
105
+ role: 'user',
106
+ content: userMessage,
107
+ });
108
+ const response = await this.client.messages.create({
109
+ model: this.model,
110
+ max_tokens: 4096,
111
+ system: systemPrompt,
112
+ messages: this.conversationHistory,
113
+ });
114
+ const assistantMessage = response.content
115
+ .filter((block) => block.type === 'text')
116
+ .map((block) => block.text)
117
+ .join('\n');
118
+ this.conversationHistory.push({
119
+ role: 'assistant',
120
+ content: assistantMessage,
121
+ });
122
+ return assistantMessage;
123
+ }
124
+ extractYaml(response) {
125
+ const yamlMatch = response.match(/```yaml\n([\s\S]*?)\n```/);
126
+ if (yamlMatch) {
127
+ return yamlMatch[1];
128
+ }
129
+ return null;
130
+ }
131
+ parseAgentDefinition(yamlContent) {
132
+ try {
133
+ const parsed = yaml.load(yamlContent);
134
+ const agent = parsed.agent || parsed;
135
+ if (!agent.metadata?.name || !agent.metadata?.title || !agent.persona?.role) {
136
+ return null;
137
+ }
138
+ return agent;
139
+ }
140
+ catch {
141
+ return null;
142
+ }
143
+ }
144
+ async saveAgent(agent) {
145
+ const filename = `${agent.metadata.name.toLowerCase().replace(/\s+/g, '-')}.agent.yaml`;
146
+ const filepath = path.join(this.outputDir, filename);
147
+ const content = `# ${agent.metadata.title} Agent Definition
148
+ # Created with aidev agent-builder
149
+
150
+ agent:
151
+ metadata:
152
+ id: "${agent.metadata.id}"
153
+ name: ${agent.metadata.name}
154
+ title: ${agent.metadata.title}
155
+ icon: ${agent.metadata.icon}
156
+ hasSidecar: ${agent.metadata.hasSidecar || false}
157
+
158
+ persona:
159
+ role: ${agent.metadata.name}'s role as ${agent.persona.role}
160
+ identity: ${agent.persona.identity}
161
+ communication_style: "${agent.persona.communication_style}"
162
+ principles: |
163
+ ${agent.persona.principles.split('\n').map(line => ` ${line}`).join('\n')}
164
+ ${agent.menu && agent.menu.length > 0 ? `
165
+ menu:
166
+ ${agent.menu.map(item => ` - trigger: ${item.trigger}
167
+ ${item.exec ? `exec: "${item.exec}"` : item.workflow ? `workflow: "${item.workflow}"` : ''}
168
+ description: "${item.description}"`).join('\n')}` : ''}
169
+ `;
170
+ fs.writeFileSync(filepath, content, 'utf-8');
171
+ return filepath;
172
+ }
173
+ async create() {
174
+ console.log('');
175
+ p.intro(chalk.bgMagenta.white(' 🤖 Agent Builder (Bond) '));
176
+ const systemPrompt = await this.buildAgentBuilderPrompt();
177
+ const intro = await this.chat('I want to create a new BMAD agent. Please guide me through the process.', systemPrompt);
178
+ console.log('');
179
+ console.log(chalk.cyan('Bond:'));
180
+ console.log(intro);
181
+ console.log('');
182
+ let agentCreated = false;
183
+ let extractedAgent = null;
184
+ while (!agentCreated) {
185
+ const userInput = await p.text({
186
+ message: 'You:',
187
+ placeholder: 'Type your response (or "done" to finish, "cancel" to exit)',
188
+ });
189
+ if (p.isCancel(userInput)) {
190
+ p.cancel('Agent creation cancelled');
191
+ return;
192
+ }
193
+ const input = userInput.trim().toLowerCase();
194
+ if (input === 'cancel' || input === 'exit' || input === 'quit') {
195
+ p.cancel('Agent creation cancelled');
196
+ return;
197
+ }
198
+ const spinner = p.spinner();
199
+ spinner.start('Bond is thinking...');
200
+ const response = await this.chat(userInput, systemPrompt);
201
+ spinner.stop('');
202
+ console.log('');
203
+ console.log(chalk.cyan('Bond:'));
204
+ console.log(response);
205
+ console.log('');
206
+ const yamlContent = this.extractYaml(response);
207
+ if (yamlContent) {
208
+ extractedAgent = this.parseAgentDefinition(yamlContent);
209
+ if (extractedAgent) {
210
+ console.log('');
211
+ console.log(chalk.green('✓ Agent definition extracted!'));
212
+ console.log('');
213
+ const confirm = await p.confirm({
214
+ message: `Save agent "${extractedAgent.metadata.name}" to ${this.outputDir}?`,
215
+ });
216
+ if (p.isCancel(confirm) || !confirm) {
217
+ console.log(chalk.yellow('Agent not saved. You can continue refining or type "cancel" to exit.'));
218
+ }
219
+ else {
220
+ const filepath = await this.saveAgent(extractedAgent);
221
+ console.log('');
222
+ console.log(chalk.green(`✓ Agent saved to: ${filepath}`));
223
+ agentCreated = true;
224
+ }
225
+ }
226
+ }
227
+ if (input === 'done' || input === 'finish' || input === 'complete') {
228
+ if (!extractedAgent) {
229
+ const finalPrompt = await this.chat('Please generate the final YAML definition for the agent we discussed.', systemPrompt);
230
+ console.log('');
231
+ console.log(chalk.cyan('Bond:'));
232
+ console.log(finalPrompt);
233
+ console.log('');
234
+ const finalYaml = this.extractYaml(finalPrompt);
235
+ if (finalYaml) {
236
+ extractedAgent = this.parseAgentDefinition(finalYaml);
237
+ if (extractedAgent) {
238
+ const filepath = await this.saveAgent(extractedAgent);
239
+ console.log('');
240
+ console.log(chalk.green(`✓ Agent saved to: ${filepath}`));
241
+ agentCreated = true;
242
+ }
243
+ }
244
+ }
245
+ if (!agentCreated) {
246
+ console.log(chalk.yellow('Could not extract agent definition. Continue the conversation or type "cancel" to exit.'));
247
+ }
248
+ }
249
+ }
250
+ p.outro(chalk.green('Agent creation complete! 🎉'));
251
+ }
252
+ }
253
+ export async function quickCreateAgent(options = {}) {
254
+ console.log('');
255
+ p.intro(chalk.bgMagenta.white(' 🤖 Quick Agent Creator '));
256
+ const name = await p.text({
257
+ message: 'Agent name (short, memorable):',
258
+ placeholder: 'e.g., Max, Luna, Atlas',
259
+ validate: (value) => {
260
+ if (!value)
261
+ return 'Name is required';
262
+ if (value.length > 20)
263
+ return 'Name should be short (max 20 chars)';
264
+ return undefined;
265
+ },
266
+ });
267
+ if (p.isCancel(name)) {
268
+ p.cancel('Cancelled');
269
+ return;
270
+ }
271
+ const title = await p.text({
272
+ message: 'Professional title:',
273
+ placeholder: 'e.g., Code Review Expert, Documentation Specialist',
274
+ validate: (value) => {
275
+ if (!value)
276
+ return 'Title is required';
277
+ return undefined;
278
+ },
279
+ });
280
+ if (p.isCancel(title)) {
281
+ p.cancel('Cancelled');
282
+ return;
283
+ }
284
+ const icon = await p.text({
285
+ message: 'Icon (single emoji):',
286
+ placeholder: '🤖',
287
+ initialValue: '🤖',
288
+ validate: (value) => {
289
+ if (!value)
290
+ return 'Icon is required';
291
+ return undefined;
292
+ },
293
+ });
294
+ if (p.isCancel(icon)) {
295
+ p.cancel('Cancelled');
296
+ return;
297
+ }
298
+ const role = await p.text({
299
+ message: 'Role and expertise:',
300
+ placeholder: 'e.g., Senior Code Reviewer + Quality Assurance Expert',
301
+ validate: (value) => {
302
+ if (!value)
303
+ return 'Role is required';
304
+ return undefined;
305
+ },
306
+ });
307
+ if (p.isCancel(role)) {
308
+ p.cancel('Cancelled');
309
+ return;
310
+ }
311
+ const identity = await p.text({
312
+ message: 'Identity (background and specialization):',
313
+ placeholder: 'Describe experience and expertise...',
314
+ validate: (value) => {
315
+ if (!value)
316
+ return 'Identity is required';
317
+ return undefined;
318
+ },
319
+ });
320
+ if (p.isCancel(identity)) {
321
+ p.cancel('Cancelled');
322
+ return;
323
+ }
324
+ const style = await p.text({
325
+ message: 'Communication style:',
326
+ placeholder: 'e.g., Direct and technical, with a focus on actionable feedback',
327
+ validate: (value) => {
328
+ if (!value)
329
+ return 'Style is required';
330
+ return undefined;
331
+ },
332
+ });
333
+ if (p.isCancel(style)) {
334
+ p.cancel('Cancelled');
335
+ return;
336
+ }
337
+ const principles = await p.text({
338
+ message: 'Key principles (comma-separated):',
339
+ placeholder: 'e.g., Always explain why, Suggest improvements, Be constructive',
340
+ validate: (value) => {
341
+ if (!value)
342
+ return 'Principles are required';
343
+ return undefined;
344
+ },
345
+ });
346
+ if (p.isCancel(principles)) {
347
+ p.cancel('Cancelled');
348
+ return;
349
+ }
350
+ const agent = {
351
+ metadata: {
352
+ id: `custom/agents/${name.toLowerCase().replace(/\s+/g, '-')}.md`,
353
+ name: name,
354
+ title: title,
355
+ icon: icon,
356
+ hasSidecar: false,
357
+ },
358
+ persona: {
359
+ role: role,
360
+ identity: identity,
361
+ communication_style: style,
362
+ principles: principles
363
+ .split(',')
364
+ .map((p) => `- ${p.trim()}`)
365
+ .join('\n'),
366
+ },
367
+ };
368
+ const outputDir = options.outputDir || process.cwd();
369
+ const filename = `${agent.metadata.name.toLowerCase().replace(/\s+/g, '-')}.agent.yaml`;
370
+ const filepath = path.join(outputDir, filename);
371
+ const content = `# ${agent.metadata.title} Agent Definition
372
+ # Created with aidev quick-create
373
+
374
+ agent:
375
+ metadata:
376
+ id: "${agent.metadata.id}"
377
+ name: ${agent.metadata.name}
378
+ title: ${agent.metadata.title}
379
+ icon: ${agent.metadata.icon}
380
+ hasSidecar: false
381
+
382
+ persona:
383
+ role: ${agent.persona.role}
384
+ identity: ${agent.persona.identity}
385
+ communication_style: "${agent.persona.communication_style}"
386
+ principles: |
387
+ ${agent.persona.principles.split('\n').map(line => ` ${line}`).join('\n')}
388
+ `;
389
+ fs.writeFileSync(filepath, content, 'utf-8');
390
+ console.log('');
391
+ console.log(chalk.green(`✓ Agent saved to: ${filepath}`));
392
+ p.outro(chalk.green('Agent created! 🎉'));
393
+ }
@@ -0,0 +1 @@
1
+ export { AgentCreator, quickCreateAgent, type AgentCreatorOptions } from './creator.js';
@@ -0,0 +1 @@
1
+ export { AgentCreator, quickCreateAgent } from './creator.js';
@@ -0,0 +1 @@
1
+ export { BMADLoader, getLoader, findBMADPath, loadAgentYaml, buildSystemPrompt, toAgentConfig, type BMADAgent, type AgentConfig, type AgentMetadata, type AgentPersona, type AgentMenuItem, } from './loader.js';
@@ -0,0 +1 @@
1
+ export { BMADLoader, getLoader, findBMADPath, loadAgentYaml, buildSystemPrompt, toAgentConfig, } from './loader.js';
@@ -0,0 +1,68 @@
1
+ export interface AgentMetadata {
2
+ id: string;
3
+ name: string;
4
+ title: string;
5
+ icon: string;
6
+ module?: string;
7
+ hasSidecar?: boolean;
8
+ whenToUse?: string;
9
+ }
10
+ export interface AgentPersona {
11
+ role: string;
12
+ identity: string;
13
+ communication_style?: string;
14
+ style?: string;
15
+ focus?: string;
16
+ principles?: string;
17
+ core_principles?: string[];
18
+ }
19
+ export interface AgentMenuItem {
20
+ trigger: string;
21
+ workflow?: string;
22
+ exec?: string;
23
+ data?: string;
24
+ description: string;
25
+ }
26
+ export interface BMADAgent {
27
+ metadata?: AgentMetadata;
28
+ agent?: {
29
+ name: string;
30
+ id: string;
31
+ title: string;
32
+ icon: string;
33
+ whenToUse?: string;
34
+ };
35
+ persona: AgentPersona;
36
+ critical_actions?: string[];
37
+ commands?: string[];
38
+ menu?: AgentMenuItem[];
39
+ }
40
+ export interface AgentConfig {
41
+ name: string;
42
+ title: string;
43
+ icon: string;
44
+ systemPrompt: string;
45
+ persona: AgentPersona;
46
+ criticalActions: string[];
47
+ }
48
+ export declare function findBMADPath(): string | undefined;
49
+ export declare function loadAgentFile(filePath: string): Promise<BMADAgent | undefined>;
50
+ export declare function buildSystemPrompt(agent: BMADAgent): string;
51
+ export declare function toAgentConfig(agent: BMADAgent): AgentConfig;
52
+ export declare class BMADLoader {
53
+ private bmadPath;
54
+ private agentsPaths;
55
+ private agentCache;
56
+ private formatType;
57
+ constructor(bmadPath?: string);
58
+ isAvailable(): boolean;
59
+ getPath(): string | undefined;
60
+ getFormatType(): 'npm' | 'local' | 'unknown';
61
+ listAgents(): Promise<string[]>;
62
+ loadAgent(name: string): Promise<AgentConfig | undefined>;
63
+ loadAgents(names: string[]): Promise<Map<string, AgentConfig>>;
64
+ getLoadedAgents(): Map<string, AgentConfig>;
65
+ private getFallbackAgent;
66
+ }
67
+ export declare function getLoader(bmadPath?: string): BMADLoader;
68
+ export declare function loadAgentYaml(agentPath: string): Promise<BMADAgent | undefined>;