bmad-plus 0.8.0 → 0.9.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.
- package/CHANGELOG.md +30 -1
- package/README.md +4 -2
- package/package.json +1 -1
- package/readme-international/README.de.md +10 -2
- package/readme-international/README.es.md +32 -9
- package/readme-international/README.fr.md +29 -6
- package/src/bmad-plus/packs/pack-seo/bmad-skill-manifest.yaml +13 -0
- package/src/bmad-plus/packs/pack-shield/SKILL.md +82 -0
- package/tools/bmad-plus-npx.js +3 -5
- package/tools/cli/commands/autoconfig.js +16 -6
- package/tools/cli/commands/doctor.js +28 -31
- package/tools/cli/commands/install.js +37 -228
- package/tools/cli/commands/scan.js +37 -35
- package/tools/cli/commands/update.js +13 -71
- package/tools/cli/i18n.js +92 -10
- package/tools/cli/lib/memory-init.js +114 -0
- package/tools/cli/lib/pack-copy.js +84 -0
- package/tools/cli/lib/packs.js +114 -0
- package/src/bmad-plus/agents/pack-animated/animated-website-agent.md +0 -325
- package/src/bmad-plus/agents/pack-animated/templates/animated-website-workflow.md +0 -55
- package/src/bmad-plus/agents/pack-backup/backup-agent.md +0 -71
- package/src/bmad-plus/agents/pack-backup/templates/backup-workflow.md +0 -51
- package/src/bmad-plus/agents/pack-seo/SKILL.md +0 -171
- package/src/bmad-plus/agents/pack-seo/checklist.md +0 -140
- package/src/bmad-plus/agents/pack-seo/pagespeed-playbook.md +0 -320
- package/src/bmad-plus/agents/pack-seo/ref/audit-schema.json +0 -187
- package/src/bmad-plus/agents/pack-seo/ref/cwv-thresholds.md +0 -87
- package/src/bmad-plus/agents/pack-seo/ref/eeat-criteria.md +0 -123
- package/src/bmad-plus/agents/pack-seo/ref/geo-signals.md +0 -167
- package/src/bmad-plus/agents/pack-seo/ref/hreflang-rules.md +0 -153
- package/src/bmad-plus/agents/pack-seo/ref/quality-gates.md +0 -133
- package/src/bmad-plus/agents/pack-seo/ref/schema-catalog.md +0 -91
- package/src/bmad-plus/agents/pack-seo/ref/schema-templates.json +0 -356
- package/src/bmad-plus/agents/pack-seo/seo-chief.md +0 -294
- package/src/bmad-plus/agents/pack-seo/seo-judge.md +0 -241
- package/src/bmad-plus/agents/pack-seo/seo-scout.md +0 -171
- package/src/bmad-plus/agents/pack-seo/templates/seo-audit-workflow.md +0 -241
|
@@ -13,94 +13,11 @@ const fsExtra = require('fs-extra');
|
|
|
13
13
|
const clack = require('@clack/prompts');
|
|
14
14
|
const pc = require('picocolors');
|
|
15
15
|
const { t, getLanguageOptions, getCommLanguageOptions } = require('../i18n');
|
|
16
|
+
const { PACKS } = require('../lib/packs');
|
|
17
|
+
const { copyPackFiles } = require('../lib/pack-copy');
|
|
18
|
+
const { initMemory } = require('../lib/memory-init');
|
|
16
19
|
|
|
17
|
-
// Pack definitions
|
|
18
|
-
const PACKS = {
|
|
19
|
-
core: {
|
|
20
|
-
name: 'Core Development',
|
|
21
|
-
icon: '⚙️',
|
|
22
|
-
description: '4 multi-role agents (Atlas, Forge, Sentinel, Nexus)',
|
|
23
|
-
required: true,
|
|
24
|
-
agents: ['agent-strategist', 'agent-architect-dev', 'agent-quality', 'agent-orchestrator'],
|
|
25
|
-
skills: ['bmad-plus-autopilot', 'bmad-plus-parallel', 'bmad-plus-sync'],
|
|
26
|
-
data: ['role-triggers.yaml'],
|
|
27
|
-
},
|
|
28
|
-
osint: {
|
|
29
|
-
name: 'OSINT Intelligence',
|
|
30
|
-
icon: '🔍',
|
|
31
|
-
description: 'Agent Shadow — investigation, scraping, psychoprofil',
|
|
32
|
-
required: false,
|
|
33
|
-
agents: ['agent-shadow'],
|
|
34
|
-
skills: [],
|
|
35
|
-
externalPackage: 'osint-agent-package',
|
|
36
|
-
},
|
|
37
|
-
maker: {
|
|
38
|
-
name: 'Agent Creator',
|
|
39
|
-
icon: '🧬',
|
|
40
|
-
description: 'Maker — design, build, and package new BMAD+ agents',
|
|
41
|
-
required: false,
|
|
42
|
-
agents: ['agent-maker'],
|
|
43
|
-
skills: [],
|
|
44
|
-
data: [],
|
|
45
|
-
},
|
|
46
|
-
shield: {
|
|
47
|
-
name: 'Pack Shield (GRC)',
|
|
48
|
-
icon: '🛡️',
|
|
49
|
-
description: '38 compliance agents — GDPR, ISO 27001, SOC 2, PCI DSS, EU AI Act...',
|
|
50
|
-
required: false,
|
|
51
|
-
agents: [],
|
|
52
|
-
skills: [],
|
|
53
|
-
packDir: 'pack-shield',
|
|
54
|
-
packSrcDir: 'packs',
|
|
55
|
-
},
|
|
56
|
-
'dev-studio': {
|
|
57
|
-
name: 'Dev Studio — Full SDLC',
|
|
58
|
-
icon: '🏗️',
|
|
59
|
-
description: 'Full SDLC pipeline: brainstorm → PRD → architecture → TDD → code review → deploy',
|
|
60
|
-
required: false,
|
|
61
|
-
agents: [],
|
|
62
|
-
skills: [],
|
|
63
|
-
packDir: 'pack-dev-studio',
|
|
64
|
-
packSrcDir: 'packs',
|
|
65
|
-
},
|
|
66
|
-
seo: {
|
|
67
|
-
name: 'SEO Audit 360',
|
|
68
|
-
icon: '🔍',
|
|
69
|
-
description: '3 agents (Scout, Chief, Judge) + 6-phase audit + PageSpeed loop',
|
|
70
|
-
required: false,
|
|
71
|
-
agents: [],
|
|
72
|
-
skills: [],
|
|
73
|
-
packDir: 'pack-seo',
|
|
74
|
-
},
|
|
75
|
-
backup: {
|
|
76
|
-
name: 'Universal Backup',
|
|
77
|
-
icon: '🗂️',
|
|
78
|
-
description: 'Timestamped ZIP backup with smart exclusions',
|
|
79
|
-
required: false,
|
|
80
|
-
agents: [],
|
|
81
|
-
skills: [],
|
|
82
|
-
packDir: 'pack-backup',
|
|
83
|
-
},
|
|
84
|
-
animated: {
|
|
85
|
-
name: 'Animated Website',
|
|
86
|
-
icon: '🎬',
|
|
87
|
-
description: 'Luxury scroll-driven website from video',
|
|
88
|
-
required: false,
|
|
89
|
-
agents: [],
|
|
90
|
-
skills: [],
|
|
91
|
-
packDir: 'pack-animated',
|
|
92
|
-
},
|
|
93
|
-
memory: {
|
|
94
|
-
name: 'Memory — Persistent Brain',
|
|
95
|
-
icon: '🧠',
|
|
96
|
-
description: 'Cross-session memory + project scanner + Karpathy guardrails. Agents learn.',
|
|
97
|
-
required: false,
|
|
98
|
-
agents: [],
|
|
99
|
-
skills: [],
|
|
100
|
-
packDir: 'pack-memory',
|
|
101
|
-
packSrcDir: 'packs',
|
|
102
|
-
},
|
|
103
|
-
};
|
|
20
|
+
// Pack definitions are imported from the shared module: require('../lib/packs').PACKS
|
|
104
21
|
|
|
105
22
|
// IDE configurations
|
|
106
23
|
const IDE_CONFIGS = {
|
|
@@ -153,7 +70,7 @@ module.exports = {
|
|
|
153
70
|
|
|
154
71
|
if (clack.isCancel(langChoice)) {
|
|
155
72
|
clack.cancel('Installation cancelled.');
|
|
156
|
-
|
|
73
|
+
throw new Error('Installation cancelled.');
|
|
157
74
|
}
|
|
158
75
|
lang = langChoice;
|
|
159
76
|
}
|
|
@@ -164,7 +81,7 @@ module.exports = {
|
|
|
164
81
|
if (!fs.existsSync(bmadSrc)) {
|
|
165
82
|
clack.log.error(`${i.source_not_found}: ${bmadSrc}`);
|
|
166
83
|
clack.outro(pc.red(i.failed));
|
|
167
|
-
|
|
84
|
+
throw new Error(`Source not found: ${bmadSrc}`);
|
|
168
85
|
}
|
|
169
86
|
|
|
170
87
|
clack.log.info(`${i.installing_to}: ${pc.cyan(projectDir)}`);
|
|
@@ -187,7 +104,7 @@ module.exports = {
|
|
|
187
104
|
.map(([key, pack]) => ({
|
|
188
105
|
value: key,
|
|
189
106
|
label: `${pack.icon} ${pack.name}`,
|
|
190
|
-
hint: pack.disabled ? i.soon : pack.description,
|
|
107
|
+
hint: pack.disabled ? i.soon : (pack.desc || pack.description || ''),
|
|
191
108
|
disabled: pack.disabled,
|
|
192
109
|
})),
|
|
193
110
|
required: false,
|
|
@@ -195,7 +112,7 @@ module.exports = {
|
|
|
195
112
|
|
|
196
113
|
if (clack.isCancel(packChoice)) {
|
|
197
114
|
clack.cancel(i.cancelled);
|
|
198
|
-
|
|
115
|
+
throw new Error(i.cancelled);
|
|
199
116
|
}
|
|
200
117
|
|
|
201
118
|
selectedPacks = [...new Set(['core', ...packChoice])];
|
|
@@ -278,10 +195,24 @@ module.exports = {
|
|
|
278
195
|
|
|
279
196
|
if (clack.isCancel(userConfig)) {
|
|
280
197
|
clack.cancel(i.cancelled);
|
|
281
|
-
|
|
198
|
+
throw new Error(i.cancelled);
|
|
282
199
|
}
|
|
283
200
|
|
|
284
|
-
|
|
201
|
+
// Validate user-provided name
|
|
202
|
+
const rawName = userConfig.userName;
|
|
203
|
+
const SHELL_META = /[;&|`$(){}[\]!#~<>*?\\\n\r]/;
|
|
204
|
+
if (!rawName || rawName.trim().length === 0) {
|
|
205
|
+
clack.log.warn('Name cannot be empty. Using default.');
|
|
206
|
+
userName = process.env.USER || process.env.USERNAME || 'Developer';
|
|
207
|
+
} else if (rawName.length > 100) {
|
|
208
|
+
clack.log.warn('Name too long (>100 chars). Truncating.');
|
|
209
|
+
userName = rawName.slice(0, 100);
|
|
210
|
+
} else if (SHELL_META.test(rawName)) {
|
|
211
|
+
clack.log.warn('Name contains shell metacharacters. Using sanitized version.');
|
|
212
|
+
userName = rawName.replace(SHELL_META, '').trim() || 'Developer';
|
|
213
|
+
} else {
|
|
214
|
+
userName = rawName;
|
|
215
|
+
}
|
|
285
216
|
commLang = userConfig.commLang;
|
|
286
217
|
}
|
|
287
218
|
|
|
@@ -302,148 +233,26 @@ module.exports = {
|
|
|
302
233
|
let copiedSkills = 0;
|
|
303
234
|
let copiedFiles = 0;
|
|
304
235
|
|
|
236
|
+
const projectRoot = path.join(bmadSrc, '..', '..');
|
|
237
|
+
|
|
305
238
|
for (const packId of selectedPacks) {
|
|
306
239
|
const pack = PACKS[packId];
|
|
307
240
|
if (!pack || pack.disabled) continue;
|
|
308
241
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
// Copy skills
|
|
320
|
-
for (const skill of pack.skills) {
|
|
321
|
-
const src = path.join(bmadSrc, 'skills', skill);
|
|
322
|
-
const dest = path.join(targetAgentsDir, skill);
|
|
323
|
-
if (fs.existsSync(src)) {
|
|
324
|
-
fsExtra.copySync(src, dest, { overwrite: true });
|
|
325
|
-
copiedSkills++;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Copy data files
|
|
330
|
-
for (const dataFile of (pack.data || [])) {
|
|
331
|
-
const src = path.join(bmadSrc, 'data', dataFile);
|
|
332
|
-
const dest = path.join(targetDataDir, dataFile);
|
|
333
|
-
if (fs.existsSync(src)) {
|
|
334
|
-
fsExtra.copySync(src, dest, { overwrite: true });
|
|
335
|
-
copiedFiles++;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Copy external package (OSINT)
|
|
340
|
-
if (pack.externalPackage) {
|
|
341
|
-
const extSrc = path.join(__dirname, '..', '..', '..', pack.externalPackage, 'skills');
|
|
342
|
-
const extDest = path.join(targetAgentsDir);
|
|
343
|
-
if (fs.existsSync(extSrc)) {
|
|
344
|
-
fsExtra.copySync(extSrc, extDest, { overwrite: true });
|
|
345
|
-
copiedSkills++;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Copy pack directory (SEO, Backup, Animated Website, Shield)
|
|
350
|
-
if (pack.packDir) {
|
|
351
|
-
const srcParent = pack.packSrcDir || 'agents';
|
|
352
|
-
const packSrc = path.join(bmadSrc, srcParent, pack.packDir);
|
|
353
|
-
const packDest = path.join(targetAgentsDir, pack.packDir);
|
|
354
|
-
if (fs.existsSync(packSrc)) {
|
|
355
|
-
fsExtra.copySync(packSrc, packDest, { overwrite: true });
|
|
356
|
-
copiedAgents++;
|
|
357
|
-
copiedFiles++;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
242
|
+
const result = copyPackFiles({
|
|
243
|
+
bmadSrc,
|
|
244
|
+
targetAgentsDir,
|
|
245
|
+
targetDataDir,
|
|
246
|
+
projectRoot,
|
|
247
|
+
pack,
|
|
248
|
+
});
|
|
249
|
+
copiedAgents += result.copiedAgents;
|
|
250
|
+
copiedSkills += result.copiedSkills;
|
|
251
|
+
copiedFiles += result.copiedFiles;
|
|
360
252
|
|
|
361
253
|
// Memory pack: initialize brain with existing brain detection
|
|
362
254
|
if (packId === 'memory' && pack.packDir) {
|
|
363
|
-
|
|
364
|
-
const sessionsDir = path.join(memoryDir, 'sessions');
|
|
365
|
-
const globalBrainDir = path.join(os.homedir(), '.bmad-plus', 'brain', 'projects');
|
|
366
|
-
const templateDir = path.join(bmadSrc, 'packs', 'pack-memory', 'templates');
|
|
367
|
-
|
|
368
|
-
// Create project memory (never overwrite existing)
|
|
369
|
-
fsExtra.ensureDirSync(sessionsDir);
|
|
370
|
-
const memoryFiles = ['decisions.md', 'lessons.md', 'patterns.md', 'context.md'];
|
|
371
|
-
for (const mf of memoryFiles) {
|
|
372
|
-
const dest = path.join(memoryDir, mf);
|
|
373
|
-
if (!fs.existsSync(dest)) {
|
|
374
|
-
const src = path.join(templateDir, mf);
|
|
375
|
-
if (fs.existsSync(src)) {
|
|
376
|
-
let content = fs.readFileSync(src, 'utf8');
|
|
377
|
-
content = content.replace(/\{\{date\}\}/g, new Date().toISOString().slice(0, 10));
|
|
378
|
-
content = content.replace(/\{\{project_name\}\}/g, path.basename(projectDir));
|
|
379
|
-
content = content.replace(/\{\{project_path\}\}/g, projectDir);
|
|
380
|
-
fs.writeFileSync(dest, content, 'utf8');
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Detect existing brain directories
|
|
386
|
-
const brainCandidates = [
|
|
387
|
-
path.join(os.homedir(), '.bmad-plus', 'brain'),
|
|
388
|
-
path.join(projectDir, '_brain'),
|
|
389
|
-
path.join(os.homedir(), '.claude', 'memory'),
|
|
390
|
-
];
|
|
391
|
-
const existingBrain = brainCandidates.find(p => fs.existsSync(p));
|
|
392
|
-
|
|
393
|
-
if (existingBrain) {
|
|
394
|
-
clack.log.info(`🧠 ${i.brain_detected || 'Existing brain detected'}: ${existingBrain}`);
|
|
395
|
-
// Write brain link pointer
|
|
396
|
-
fs.writeFileSync(
|
|
397
|
-
path.join(memoryDir, '.brain-link'),
|
|
398
|
-
JSON.stringify({ linked_brain: existingBrain, linked_at: new Date().toISOString() }, null, 2),
|
|
399
|
-
'utf8'
|
|
400
|
-
);
|
|
401
|
-
} else {
|
|
402
|
-
// Create fresh global brain
|
|
403
|
-
fsExtra.ensureDirSync(globalBrainDir);
|
|
404
|
-
const identitySrc = path.join(templateDir, 'identity.yaml');
|
|
405
|
-
const identityDest = path.join(os.homedir(), '.bmad-plus', 'brain', 'identity.yaml');
|
|
406
|
-
if (fs.existsSync(identitySrc) && !fs.existsSync(identityDest)) {
|
|
407
|
-
let content = fs.readFileSync(identitySrc, 'utf8');
|
|
408
|
-
content = content.replace(/\{\{user_name\}\}/g, userName);
|
|
409
|
-
content = content.replace(/\{\{language\}\}/g, commLang);
|
|
410
|
-
content = content.replace(/\{\{date\}\}/g, new Date().toISOString().slice(0, 10));
|
|
411
|
-
fs.writeFileSync(identityDest, content, 'utf8');
|
|
412
|
-
}
|
|
413
|
-
// Copy global memory templates
|
|
414
|
-
for (const gf of ['decisions.md', 'lessons.md', 'patterns.md']) {
|
|
415
|
-
const dest = path.join(os.homedir(), '.bmad-plus', 'brain', gf);
|
|
416
|
-
if (!fs.existsSync(dest)) {
|
|
417
|
-
const src = path.join(templateDir, gf);
|
|
418
|
-
if (fs.existsSync(src)) {
|
|
419
|
-
let content = fs.readFileSync(src, 'utf8');
|
|
420
|
-
content = content.replace(/\{\{date\}\}/g, new Date().toISOString().slice(0, 10));
|
|
421
|
-
content = content.replace(/\{\{project_name\}\}/g, 'Global Brain');
|
|
422
|
-
fs.writeFileSync(dest, content, 'utf8');
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
clack.log.info(`🧠 ${i.brain_created || 'Global brain created'}: ${path.join(os.homedir(), '.bmad-plus', 'brain')}`);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Index this project in global brain
|
|
430
|
-
const crypto = require('node:crypto');
|
|
431
|
-
const projHash = crypto.createHash('sha256').update(projectDir).digest('hex').slice(0, 8);
|
|
432
|
-
const projMeta = {
|
|
433
|
-
path: projectDir,
|
|
434
|
-
name: path.basename(projectDir),
|
|
435
|
-
hash: projHash,
|
|
436
|
-
status: 'active',
|
|
437
|
-
bmad_installed: true,
|
|
438
|
-
packs_installed: selectedPacks,
|
|
439
|
-
last_scanned: new Date().toISOString().slice(0, 10),
|
|
440
|
-
};
|
|
441
|
-
fsExtra.ensureDirSync(globalBrainDir);
|
|
442
|
-
fs.writeFileSync(
|
|
443
|
-
path.join(globalBrainDir, `${projHash}.yaml`),
|
|
444
|
-
Object.entries(projMeta).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join('\n'),
|
|
445
|
-
'utf8'
|
|
446
|
-
);
|
|
255
|
+
initMemory({ projectDir, bmadSrc, userName, commLang, selectedPacks });
|
|
447
256
|
}
|
|
448
257
|
}
|
|
449
258
|
|
|
@@ -143,6 +143,31 @@ function scanDirectory(rootDir, maxDepth = 4, currentDepth = 0, activeDays = 30,
|
|
|
143
143
|
return projects;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Index a single project in the global brain by writing its metadata YAML file.
|
|
148
|
+
* @param {object} project - Project metadata object
|
|
149
|
+
* @param {string} globalBrainDir - Path to the brain projects directory
|
|
150
|
+
* @returns {void}
|
|
151
|
+
*/
|
|
152
|
+
function indexProject(project, globalBrainDir) {
|
|
153
|
+
const hash = crypto.createHash('sha256').update(project.path).digest('hex').slice(0, 8);
|
|
154
|
+
const meta = {
|
|
155
|
+
path: project.path,
|
|
156
|
+
name: project.name,
|
|
157
|
+
hash,
|
|
158
|
+
stack: project.stack,
|
|
159
|
+
status: project.status,
|
|
160
|
+
bmad_installed: project.bmad,
|
|
161
|
+
has_git: project.hasGit,
|
|
162
|
+
last_scanned: new Date().toISOString().slice(0, 10),
|
|
163
|
+
};
|
|
164
|
+
fs.writeFileSync(
|
|
165
|
+
path.join(globalBrainDir, `${hash}.yaml`),
|
|
166
|
+
Object.entries(meta).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join('\n'),
|
|
167
|
+
'utf8'
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
146
171
|
module.exports = {
|
|
147
172
|
command: 'scan',
|
|
148
173
|
description: 'Scan directories to discover and index projects in the global brain',
|
|
@@ -155,9 +180,15 @@ module.exports = {
|
|
|
155
180
|
],
|
|
156
181
|
action: async (options) => {
|
|
157
182
|
const scanDir = path.resolve(options.directory || process.cwd());
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
const
|
|
183
|
+
|
|
184
|
+
const rawDepth = parseInt(options.depth, 10);
|
|
185
|
+
const maxDepth = (!isNaN(rawDepth) && rawDepth > 0) ? rawDepth : (() => { clack.log.warn(`Invalid --depth "${options.depth}", defaulting to 4`); return 4; })();
|
|
186
|
+
|
|
187
|
+
const rawActive = parseInt(options.activeDays, 10);
|
|
188
|
+
const activeDays = (!isNaN(rawActive) && rawActive > 0) ? rawActive : (() => { clack.log.warn(`Invalid --active-days "${options.activeDays}", defaulting to 30`); return 30; })();
|
|
189
|
+
|
|
190
|
+
const rawPaused = parseInt(options.pausedDays, 10);
|
|
191
|
+
const pausedDays = (!isNaN(rawPaused) && rawPaused > 0) ? rawPaused : (() => { clack.log.warn(`Invalid --paused-days "${options.pausedDays}", defaulting to 180`); return 180; })();
|
|
161
192
|
|
|
162
193
|
clack.intro(pc.bgMagenta(pc.white(' 🧠 BMAD+ Project Scanner ')));
|
|
163
194
|
|
|
@@ -220,22 +251,7 @@ module.exports = {
|
|
|
220
251
|
|
|
221
252
|
let indexed = 0;
|
|
222
253
|
for (const proj of projects) {
|
|
223
|
-
|
|
224
|
-
const meta = {
|
|
225
|
-
path: proj.path,
|
|
226
|
-
name: proj.name,
|
|
227
|
-
hash,
|
|
228
|
-
stack: proj.stack,
|
|
229
|
-
status: proj.status,
|
|
230
|
-
bmad_installed: proj.bmad,
|
|
231
|
-
has_git: proj.hasGit,
|
|
232
|
-
last_scanned: new Date().toISOString().slice(0, 10),
|
|
233
|
-
};
|
|
234
|
-
fs.writeFileSync(
|
|
235
|
-
path.join(globalBrainDir, `${hash}.yaml`),
|
|
236
|
-
Object.entries(meta).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join('\n'),
|
|
237
|
-
'utf8'
|
|
238
|
-
);
|
|
254
|
+
indexProject(proj, globalBrainDir);
|
|
239
255
|
indexed++;
|
|
240
256
|
}
|
|
241
257
|
clack.log.success(`✅ ${indexed} project(s) indexed in ${globalBrainDir}`);
|
|
@@ -281,22 +297,7 @@ module.exports = {
|
|
|
281
297
|
|
|
282
298
|
let indexed = 0;
|
|
283
299
|
for (const proj of toIndex) {
|
|
284
|
-
|
|
285
|
-
const meta = {
|
|
286
|
-
path: proj.path,
|
|
287
|
-
name: proj.name,
|
|
288
|
-
hash,
|
|
289
|
-
stack: proj.stack,
|
|
290
|
-
status: proj.status,
|
|
291
|
-
bmad_installed: proj.bmad,
|
|
292
|
-
has_git: proj.hasGit,
|
|
293
|
-
last_scanned: new Date().toISOString().slice(0, 10),
|
|
294
|
-
};
|
|
295
|
-
fs.writeFileSync(
|
|
296
|
-
path.join(globalBrainDir, `${hash}.yaml`),
|
|
297
|
-
Object.entries(meta).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join('\n'),
|
|
298
|
-
'utf8'
|
|
299
|
-
);
|
|
300
|
+
indexProject(proj, globalBrainDir);
|
|
300
301
|
indexed++;
|
|
301
302
|
}
|
|
302
303
|
|
|
@@ -356,5 +357,6 @@ module.exports = {
|
|
|
356
357
|
getProjectName,
|
|
357
358
|
hasBmadInstalled,
|
|
358
359
|
scanDirectory,
|
|
360
|
+
indexProject,
|
|
359
361
|
},
|
|
360
362
|
};
|
|
@@ -11,29 +11,10 @@ const fsExtra = require('fs-extra');
|
|
|
11
11
|
const clack = require('@clack/prompts');
|
|
12
12
|
const pc = require('picocolors');
|
|
13
13
|
const { t } = require('../i18n');
|
|
14
|
+
const { PACKS } = require('../lib/packs');
|
|
15
|
+
const { copyPackFiles } = require('../lib/pack-copy');
|
|
14
16
|
|
|
15
|
-
//
|
|
16
|
-
const PACKS = {
|
|
17
|
-
core: {
|
|
18
|
-
agents: ['agent-strategist', 'agent-architect-dev', 'agent-quality', 'agent-orchestrator'],
|
|
19
|
-
skills: ['bmad-plus-autopilot', 'bmad-plus-parallel', 'bmad-plus-sync'],
|
|
20
|
-
data: ['role-triggers.yaml'],
|
|
21
|
-
},
|
|
22
|
-
osint: {
|
|
23
|
-
agents: ['agent-shadow'],
|
|
24
|
-
skills: [],
|
|
25
|
-
externalPackage: 'osint-agent-package',
|
|
26
|
-
},
|
|
27
|
-
maker: {
|
|
28
|
-
agents: ['agent-maker'],
|
|
29
|
-
skills: [],
|
|
30
|
-
data: [],
|
|
31
|
-
},
|
|
32
|
-
seo: { agents: [], skills: [], packDir: 'pack-seo' },
|
|
33
|
-
backup: { agents: [], skills: [], packDir: 'pack-backup' },
|
|
34
|
-
animated: { agents: [], skills: [], packDir: 'pack-animated' },
|
|
35
|
-
shield: { agents: [], skills: [], packDir: 'pack-shield', packSrcDir: 'packs' },
|
|
36
|
-
};
|
|
17
|
+
// Pack definitions imported from shared module: require('../lib/packs').PACKS
|
|
37
18
|
|
|
38
19
|
module.exports = {
|
|
39
20
|
command: 'update',
|
|
@@ -92,59 +73,20 @@ module.exports = {
|
|
|
92
73
|
|
|
93
74
|
let updated = 0;
|
|
94
75
|
|
|
76
|
+
const projectRoot = path.join(bmadSrc, '..', '..');
|
|
77
|
+
|
|
95
78
|
for (const packId of selectedPacks) {
|
|
96
79
|
const pack = PACKS[packId];
|
|
97
80
|
if (!pack) continue;
|
|
98
81
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Update skills
|
|
110
|
-
for (const skill of (pack.skills || [])) {
|
|
111
|
-
const src = path.join(bmadSrc, 'skills', skill);
|
|
112
|
-
const dest = path.join(targetAgentsDir, skill);
|
|
113
|
-
if (fs.existsSync(src)) {
|
|
114
|
-
fsExtra.copySync(src, dest, { overwrite: true });
|
|
115
|
-
updated++;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Update data files
|
|
120
|
-
for (const dataFile of (pack.data || [])) {
|
|
121
|
-
const src = path.join(bmadSrc, 'data', dataFile);
|
|
122
|
-
const dest = path.join(targetDataDir, dataFile);
|
|
123
|
-
if (fs.existsSync(src)) {
|
|
124
|
-
fsExtra.copySync(src, dest, { overwrite: true });
|
|
125
|
-
updated++;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Update external package (OSINT)
|
|
130
|
-
if (pack.externalPackage) {
|
|
131
|
-
const extSrc = path.join(__dirname, '..', '..', '..', pack.externalPackage, 'skills');
|
|
132
|
-
if (fs.existsSync(extSrc)) {
|
|
133
|
-
fsExtra.copySync(extSrc, targetAgentsDir, { overwrite: true });
|
|
134
|
-
updated++;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Update pack directory (SEO, Backup, Animated, Shield)
|
|
139
|
-
if (pack.packDir) {
|
|
140
|
-
const srcParent = pack.packSrcDir || 'agents';
|
|
141
|
-
const packSrc = path.join(bmadSrc, srcParent, pack.packDir);
|
|
142
|
-
const packDest = path.join(targetAgentsDir, pack.packDir);
|
|
143
|
-
if (fs.existsSync(packSrc)) {
|
|
144
|
-
fsExtra.copySync(packSrc, packDest, { overwrite: true });
|
|
145
|
-
updated++;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
82
|
+
const result = copyPackFiles({
|
|
83
|
+
bmadSrc,
|
|
84
|
+
targetAgentsDir,
|
|
85
|
+
targetDataDir,
|
|
86
|
+
projectRoot,
|
|
87
|
+
pack,
|
|
88
|
+
});
|
|
89
|
+
updated += result.copiedAgents + result.copiedSkills + result.copiedFiles;
|
|
148
90
|
}
|
|
149
91
|
|
|
150
92
|
// Update module config (always)
|