bmad-plus 0.13.0 → 0.14.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 +22 -0
- package/README.md +96 -489
- package/package.json +7 -2
- package/readme-international/README.de.md +9 -5
- package/readme-international/README.es.md +9 -5
- package/readme-international/README.fr.md +9 -5
- package/src/bmad-plus/agents/agent-orchestrator/SKILL.md +3 -3
- package/src/bmad-plus/packs/pack-dev-studio/README.md +133 -141
- package/src/bmad-plus/packs/pack-dev-studio/SKILL.md +49 -0
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/analyst-agent.md +35 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/document-project.md +59 -59
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/domain-research.md +55 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/market-research.md +58 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/prfaq.md +55 -132
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/product-brief.md +63 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/tech-writer-agent.md +54 -69
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/technical-research.md +54 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/architect-agent.md +32 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-architecture.md +67 -71
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-epics-stories.md +61 -90
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/generate-project-context.md +56 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/implementation-readiness.md +55 -88
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01-init.md +20 -153
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01b-continue.md +20 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-02-context.md +14 -220
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-03-starter.md +20 -329
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-04-decisions.md +15 -314
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-05-patterns.md +15 -355
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-06-structure.md +15 -375
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-07-validation.md +14 -357
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-08-complete.md +13 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/checkpoint-preview.md +52 -65
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-01-gather-context.md +14 -81
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-02-review.md +14 -31
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-03-triage.md +14 -45
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-04-present.md +13 -128
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +61 -87
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/correct-course.md +55 -298
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/create-story.md +52 -426
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-agent.md +48 -69
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story-checklist.md +22 -80
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +60 -482
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/investigate.md +50 -184
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/qa-e2e-tests.md +57 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/quick-dev.md +56 -108
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/retrospective.md +54 -1509
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-planning.md +54 -296
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-status.md +41 -283
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-prd.md +58 -18
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-ux-design.md +103 -72
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/edit-prd.md +55 -27
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/pm-agent.md +34 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/prd.md +46 -87
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/steps/step-01-init.md +10 -0
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/ux-designer-agent.md +30 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/validate-prd.md +57 -27
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/advanced-elicitation.md +47 -138
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/adversarial-review.md +48 -34
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/bmad-help.md +51 -68
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/brainstorming.md +46 -3
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/customize.md +68 -109
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/distillator.md +53 -174
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/edge-case-hunter.md +39 -53
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-prose.md +45 -83
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-structure.md +45 -176
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/index-docs.md +45 -63
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/party-mode.md +53 -124
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/shard-doc.md +44 -100
- package/src/bmad-plus/packs/pack-dev-studio/dev-studio-orchestrator.md +56 -116
- package/src/bmad-plus/packs/pack-dev-studio/shared/architecture-decision-template.md +49 -12
- package/src/bmad-plus/packs/pack-dev-studio/shared/bwml-spec.md +51 -328
- package/src/bmad-plus/packs/pack-dev-studio/shared/catalog.json +489 -0
- package/src/bmad-plus/packs/pack-dev-studio/shared/execution.md +69 -0
- package/src/bmad-plus/packs/pack-dev-studio/shared/module-help.csv +39 -32
- package/src/bmad-plus/packs/pack-dev-studio/upstream-sync.yaml +85 -14
- package/src/bmad-plus/packs/pack-memory/README.md +6 -0
- package/src/bmad-plus/packs/pack-memory/memory-orchestrator.md +12 -5
- package/src/bmad-plus/packs/pack-seo/SKILL.md +27 -1
- package/src/bmad-plus/packs/pack-seo/seo-chief.md +16 -1
- package/src/bmad-plus/packs/pack-seo/seo-judge.md +12 -0
- package/src/bmad-plus/packs/pack-seo/seo-scout.md +12 -0
- package/src/bmad-plus/skills/bmad-plus-autopilot/SKILL.md +3 -3
- package/src/bmad-plus/skills/bmad-plus-sync/SKILL.md +5 -3
- package/src/bmad-plus/skills/dev-studio/SKILL.md +19 -0
- package/tools/build/check-install-contract.js +165 -13
- package/tools/build/generate.js +213 -40
- package/tools/build/generated-adapters/.codex/AGENTS.md +1 -1
- package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +1 -1
- package/tools/build/generated-adapters/.opencode/AGENTS.md +1 -1
- package/tools/build/generated-adapters/AGENTS.md +1 -1
- package/tools/build/generated-adapters/CLAUDE.md +1 -1
- package/tools/build/generated-adapters/CONVENTIONS.md +1 -1
- package/tools/build/generated-adapters/GEMINI.md +1 -1
- package/tools/build/pack-delivery.js +78 -0
- package/tools/cli/bmad-plus-cli.js +1 -1
- package/tools/cli/commands/doctor.js +49 -189
- package/tools/cli/commands/install.js +1 -1
- package/tools/cli/commands/studio.js +68 -0
- package/tools/cli/lib/installation-health.js +360 -0
- package/tools/cli/lib/packs.js +118 -9
- package/tools/cli/lib/python-health.js +233 -0
- package/tools/cli/lib/python-provision.js +2 -2
- package/tools/cli/lib/studio.js +310 -0
- package/tools/release/publication-content.js +901 -0
package/tools/build/generate.js
CHANGED
|
@@ -35,15 +35,61 @@ const MODULE_TEMPLATE_PATH = path.join(__dirname, 'module.template.yaml');
|
|
|
35
35
|
const PACKAGE_JSON_PATH = path.join(REPO_ROOT, 'package.json');
|
|
36
36
|
|
|
37
37
|
const INSTALL_LAYOUTS = ['loose', 'packaged'];
|
|
38
|
-
const OWNED_COUNT_RE =
|
|
39
|
-
|
|
38
|
+
const OWNED_COUNT_RE =
|
|
39
|
+
/(?<![\w.-])\d+\+?[\s-]+(?:[A-Za-zÀ-ÿ-]+\s+){0,6}(?:agents?|skills?|workflows?|packs?|frameworks?|languages?|reference files)\b/gi;
|
|
40
|
+
const STANDARD_PREFIX_RE =
|
|
41
|
+
/\b(?:ISO(?:\/IEC)?|IEC|SOC|NIST(?: SP)?|PCI(?: DSS)?|WCAG|Section)\s*$/i;
|
|
40
42
|
|
|
41
43
|
function hasOwnedCount(value) {
|
|
42
|
-
return [...value.matchAll(OWNED_COUNT_RE)].some(
|
|
43
|
-
!STANDARD_PREFIX_RE.test(value.slice(0, match.index))
|
|
44
|
+
return [...value.matchAll(OWNED_COUNT_RE)].some(
|
|
45
|
+
(match) => !STANDARD_PREFIX_RE.test(value.slice(0, match.index))
|
|
44
46
|
);
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
function assertResourcePath(file, where) {
|
|
50
|
+
if (
|
|
51
|
+
typeof file !== 'string' ||
|
|
52
|
+
!file ||
|
|
53
|
+
/[\\:\0]/.test(file) ||
|
|
54
|
+
file.split('/').some((part) => !part || part === '.' || part === '..')
|
|
55
|
+
) {
|
|
56
|
+
throw new Error(`${where}: required resource must be a portable relative file path`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Installed entry points, derived from the same layout used by pack-copy.js. */
|
|
61
|
+
function buildRequiredResources(pack) {
|
|
62
|
+
const where = `registry.yaml: pack "${pack.pack_dir}"`;
|
|
63
|
+
for (const file of [
|
|
64
|
+
...pack.agents,
|
|
65
|
+
...(pack.skills || []),
|
|
66
|
+
...(pack.data || []),
|
|
67
|
+
...((pack.doctor || {}).pack_agents || []),
|
|
68
|
+
...((pack.doctor || {}).required_resources || []),
|
|
69
|
+
...(pack.python_entry ? [pack.python_entry] : []),
|
|
70
|
+
]) {
|
|
71
|
+
assertResourcePath(file, where);
|
|
72
|
+
}
|
|
73
|
+
const resources = [];
|
|
74
|
+
if (pack.install_layout === 'loose') {
|
|
75
|
+
resources.push(...pack.agents.map((id) => `.agents/skills/${id}/SKILL.md`));
|
|
76
|
+
} else {
|
|
77
|
+
resources.push(
|
|
78
|
+
...pack.doctor.pack_agents.map((file) => `.agents/skills/${pack.pack_dir}/${file}`)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
resources.push(...(pack.skills || []).map((id) => `.agents/skills/${id}/SKILL.md`));
|
|
82
|
+
resources.push(...(pack.data || []).map((file) => `.agents/data/${file}`));
|
|
83
|
+
resources.push(
|
|
84
|
+
...((pack.doctor || {}).required_resources || []).map(
|
|
85
|
+
(file) => `.agents/skills/${pack.pack_dir}/${file}`
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
if (pack.python_entry) resources.push(`.agents/skills/${pack.pack_dir}/${pack.python_entry}`);
|
|
89
|
+
for (const file of resources) assertResourcePath(file, where);
|
|
90
|
+
return [...new Set(resources)];
|
|
91
|
+
}
|
|
92
|
+
|
|
47
93
|
/**
|
|
48
94
|
* Validate the minimal contract the generator relies on.
|
|
49
95
|
* Throws with a precise message on the first violation.
|
|
@@ -74,11 +120,15 @@ function validateRegistry(registry) {
|
|
|
74
120
|
throw new Error(`${where}: missing cli.desc or cli.desc_template`);
|
|
75
121
|
}
|
|
76
122
|
for (const [field, value] of Object.entries({
|
|
77
|
-
'cli.desc': pack.cli.desc,
|
|
78
|
-
|
|
123
|
+
'cli.desc': pack.cli.desc,
|
|
124
|
+
'cli.desc_template': pack.cli.desc_template,
|
|
125
|
+
summary: pack.summary,
|
|
126
|
+
summary_template: pack.summary_template,
|
|
79
127
|
})) {
|
|
80
128
|
if (value !== undefined && (typeof value !== 'string' || hasOwnedCount(value))) {
|
|
81
|
-
throw new Error(
|
|
129
|
+
throw new Error(
|
|
130
|
+
`${where}: ${field} must use derived placeholders for owned count dimensions`
|
|
131
|
+
);
|
|
82
132
|
}
|
|
83
133
|
}
|
|
84
134
|
if (!Array.isArray(pack.agents)) throw new Error(`${where}: "agents" must be a list`);
|
|
@@ -94,22 +144,67 @@ function validateRegistry(registry) {
|
|
|
94
144
|
if (pack.install_layout === 'packaged' && !Array.isArray((pack.doctor || {}).pack_agents)) {
|
|
95
145
|
throw new Error(`${where}: packaged layout requires "doctor.pack_agents" list`);
|
|
96
146
|
}
|
|
147
|
+
if (
|
|
148
|
+
pack.doctor?.required_resources !== undefined &&
|
|
149
|
+
!Array.isArray(pack.doctor.required_resources)
|
|
150
|
+
) {
|
|
151
|
+
throw new Error(`${where}: doctor.required_resources must be a list`);
|
|
152
|
+
}
|
|
153
|
+
if (
|
|
154
|
+
!Array.isArray(pack.runtime) ||
|
|
155
|
+
pack.runtime.length === 0 ||
|
|
156
|
+
pack.runtime.some((runtime) => !Object.hasOwn(registry.runtimes || {}, runtime))
|
|
157
|
+
) {
|
|
158
|
+
throw new Error(`${where}: runtime must declare known runtimes`);
|
|
159
|
+
}
|
|
160
|
+
buildRequiredResources(pack);
|
|
97
161
|
if (pack.personas !== undefined) {
|
|
98
162
|
if (!Array.isArray(pack.personas)) throw new Error(`${where}: personas must be a list`);
|
|
99
163
|
const roster = new Set([...pack.agents, ...(pack.sub_agents || [])]);
|
|
100
164
|
const ids = new Set();
|
|
101
165
|
for (const persona of pack.personas) {
|
|
102
|
-
if (
|
|
103
|
-
|
|
166
|
+
if (
|
|
167
|
+
!persona ||
|
|
168
|
+
['id', 'name', 'role', 'description'].some(
|
|
169
|
+
(key) => typeof persona[key] !== 'string' || !persona[key].trim()
|
|
170
|
+
)
|
|
171
|
+
) {
|
|
104
172
|
throw new Error(`${where}: each persona needs id, name, role and description`);
|
|
105
173
|
}
|
|
106
174
|
if (!roster.has(persona.id) || ids.has(persona.id)) {
|
|
107
|
-
throw new Error(
|
|
175
|
+
throw new Error(
|
|
176
|
+
`${where}: persona ${persona.id} is absent from its roster or duplicated`
|
|
177
|
+
);
|
|
108
178
|
}
|
|
109
179
|
ids.add(persona.id);
|
|
110
180
|
}
|
|
111
181
|
}
|
|
112
182
|
}
|
|
183
|
+
const integration = registry.targets?.integration;
|
|
184
|
+
if (
|
|
185
|
+
integration?.execution !== 'host-managed' ||
|
|
186
|
+
integration?.lifecycle_events !== 'not-integrated'
|
|
187
|
+
) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
'registry.yaml: targets.integration must declare the delivered instruction integration'
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
for (const [runtime, config] of Object.entries(registry.runtimes || {})) {
|
|
193
|
+
if (
|
|
194
|
+
typeof config?.min_version !== 'string' ||
|
|
195
|
+
!/^\d+\.\d+(?:\.\d+)?$/.test(config.min_version)
|
|
196
|
+
) {
|
|
197
|
+
throw new Error(`registry.yaml: runtime "${runtime}" needs a numeric min_version`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const tools = new Set();
|
|
201
|
+
for (const adapter of registry.targets.adapters || []) {
|
|
202
|
+
if (!adapter || typeof adapter.tool !== 'string' || !adapter.tool || tools.has(adapter.tool)) {
|
|
203
|
+
throw new Error('registry.yaml: adapter tool names must be nonempty and unique');
|
|
204
|
+
}
|
|
205
|
+
tools.add(adapter.tool);
|
|
206
|
+
assertResourcePath(adapter.file, `registry.yaml: adapter "${adapter.tool}"`);
|
|
207
|
+
}
|
|
113
208
|
return registry;
|
|
114
209
|
}
|
|
115
210
|
|
|
@@ -171,10 +266,14 @@ function renderSummary(pack, derived, field = 'summary') {
|
|
|
171
266
|
const fallback = field === 'summary' ? pack.summary : pack.cli.desc;
|
|
172
267
|
if (!template) return fallback || '';
|
|
173
268
|
const values = {
|
|
174
|
-
agent_count: derived.agentCount,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
269
|
+
agent_count: derived.agentCount,
|
|
270
|
+
installer_agent_count: derived.installerAgentCount,
|
|
271
|
+
sub_agent_count: derived.subAgentCount,
|
|
272
|
+
workflow_count: derived.workflowCount,
|
|
273
|
+
framework_count: derived.frameworkCount,
|
|
274
|
+
category_count: derived.categoryCount,
|
|
275
|
+
skill_count: derived.skillCount,
|
|
276
|
+
reference_file_count: derived.referenceFiles,
|
|
178
277
|
};
|
|
179
278
|
return template.replace(/\{([a-z_]+)\}/g, (_match, key) => {
|
|
180
279
|
if (!(key in values)) throw new Error(`Unknown derived summary placeholder: ${key}`);
|
|
@@ -195,18 +294,33 @@ function buildDerived(registry, { sourceRoot = path.join(REPO_ROOT, 'src', 'bmad
|
|
|
195
294
|
const installerAgentCount = pack.agents.length;
|
|
196
295
|
const subAgentCount = (pack.sub_agents || []).length;
|
|
197
296
|
const facts = {
|
|
198
|
-
id,
|
|
199
|
-
|
|
297
|
+
id,
|
|
298
|
+
order: pack.order,
|
|
299
|
+
name: pack.cli.name,
|
|
300
|
+
displayName: pack.display_name || pack.cli.name,
|
|
301
|
+
required: pack.required === true,
|
|
302
|
+
installerAgentCount,
|
|
303
|
+
categoryAgentCount,
|
|
200
304
|
agentCount: categoryAgentCount || subAgentCount || installerAgentCount,
|
|
201
305
|
subAgentCount,
|
|
202
|
-
workflowCount:
|
|
306
|
+
workflowCount:
|
|
307
|
+
(pack.workflows || []).length +
|
|
308
|
+
categories.reduce((sum, c) => sum + (c.workflows || []).length, 0),
|
|
203
309
|
frameworkCount: (pack.compliance_tags || []).length,
|
|
204
310
|
categoryCount: categories.length,
|
|
205
311
|
skillCount: (pack.skills || []).length,
|
|
206
|
-
referenceFiles: countMarkdownFiles(
|
|
312
|
+
referenceFiles: countMarkdownFiles(
|
|
313
|
+
path.join(sourceRoot, pack.pack_src_dir || 'packs', pack.pack_dir, 'references')
|
|
314
|
+
),
|
|
207
315
|
categoryAgentCounts,
|
|
316
|
+
runtimes: [...pack.runtime],
|
|
317
|
+
requiredResources: buildRequiredResources(pack),
|
|
208
318
|
personas: (pack.personas || []).map(({ id, name, role, description, alias }) => ({
|
|
209
|
-
id,
|
|
319
|
+
id,
|
|
320
|
+
name,
|
|
321
|
+
role,
|
|
322
|
+
description,
|
|
323
|
+
...(alias ? { alias } : {}),
|
|
210
324
|
})),
|
|
211
325
|
};
|
|
212
326
|
facts.summary = renderSummary(pack, facts);
|
|
@@ -215,10 +329,13 @@ function buildDerived(registry, { sourceRoot = path.join(REPO_ROOT, 'src', 'bmad
|
|
|
215
329
|
packs[id] = facts;
|
|
216
330
|
installerAgents += installerAgentCount;
|
|
217
331
|
totalAgents += new Set([
|
|
218
|
-
...pack.agents,
|
|
332
|
+
...pack.agents,
|
|
333
|
+
...(pack.sub_agents || []),
|
|
334
|
+
...categories.flatMap((c) => c.agents || []),
|
|
219
335
|
]).size;
|
|
220
336
|
if ((pack.runtime || []).includes('python')) {
|
|
221
|
-
const packagePath =
|
|
337
|
+
const packagePath =
|
|
338
|
+
pack.python_package || `src/bmad-plus/${pack.pack_src_dir || 'packs'}/${pack.pack_dir}`;
|
|
222
339
|
pythonPacks[id] = {
|
|
223
340
|
requirements: [...packagePath.split(/[\\/]/), 'requirements.txt'],
|
|
224
341
|
verifyModules: [...(pack.python_verify_modules || [])],
|
|
@@ -228,12 +345,34 @@ function buildDerived(registry, { sourceRoot = path.join(REPO_ROOT, 'src', 'bmad
|
|
|
228
345
|
const { LANGUAGES } = require('../cli/i18n');
|
|
229
346
|
return {
|
|
230
347
|
product: {
|
|
231
|
-
code: registry.product.code,
|
|
232
|
-
|
|
348
|
+
code: registry.product.code,
|
|
349
|
+
displayName: registry.product.display_name,
|
|
350
|
+
version: registry.product.version,
|
|
351
|
+
derivedFrom: registry.product.derived_from,
|
|
233
352
|
},
|
|
234
353
|
packOrder: buildPackOrder(registry),
|
|
235
|
-
packCount: Object.keys(packs).length,
|
|
236
|
-
|
|
354
|
+
packCount: Object.keys(packs).length,
|
|
355
|
+
installerAgents,
|
|
356
|
+
totalAgents,
|
|
357
|
+
languages: Object.keys(LANGUAGES),
|
|
358
|
+
packs,
|
|
359
|
+
pythonPacks,
|
|
360
|
+
diagnostics: {
|
|
361
|
+
schemaVersion: 1,
|
|
362
|
+
runtimeMinimums: Object.fromEntries(
|
|
363
|
+
Object.entries(registry.runtimes).map(([id, runtime]) => [id, runtime.min_version])
|
|
364
|
+
),
|
|
365
|
+
integrations: Object.fromEntries(
|
|
366
|
+
registry.targets.adapters.map(({ tool, file }) => [
|
|
367
|
+
tool,
|
|
368
|
+
{
|
|
369
|
+
instructionFile: file,
|
|
370
|
+
execution: registry.targets.integration.execution,
|
|
371
|
+
lifecycleEvents: registry.targets.integration.lifecycle_events,
|
|
372
|
+
},
|
|
373
|
+
])
|
|
374
|
+
),
|
|
375
|
+
},
|
|
237
376
|
targets: {
|
|
238
377
|
spine: registry.targets.spine,
|
|
239
378
|
adapters: registry.targets.adapters.map(({ tool, file }) => ({ tool, file })),
|
|
@@ -251,11 +390,24 @@ function generateModuleSource(registry, templatePath = MODULE_TEMPLATE_PATH) {
|
|
|
251
390
|
for (const [id, pack] of sortedPackEntries(registry)) {
|
|
252
391
|
const facts = derived.packs[id];
|
|
253
392
|
const entry = {
|
|
254
|
-
name: facts.displayName,
|
|
255
|
-
|
|
393
|
+
name: facts.displayName,
|
|
394
|
+
icon: pack.icon_emoji,
|
|
395
|
+
description: facts.summary || facts.desc,
|
|
396
|
+
required: facts.required,
|
|
397
|
+
agents: [...pack.agents],
|
|
398
|
+
skills: [...(pack.skills || [])],
|
|
256
399
|
};
|
|
257
|
-
for (const key of [
|
|
258
|
-
'
|
|
400
|
+
for (const key of [
|
|
401
|
+
'data',
|
|
402
|
+
'external_package',
|
|
403
|
+
'orchestrator',
|
|
404
|
+
'categories',
|
|
405
|
+
'workflows',
|
|
406
|
+
'sub_agents',
|
|
407
|
+
'required_keys',
|
|
408
|
+
'optional_keys',
|
|
409
|
+
'cohabitation_warning',
|
|
410
|
+
]) {
|
|
259
411
|
if (key in pack) entry[key] = JSON.parse(JSON.stringify(pack[key]));
|
|
260
412
|
}
|
|
261
413
|
if (pack.install_layout === 'packaged') {
|
|
@@ -265,14 +417,19 @@ function generateModuleSource(registry, templatePath = MODULE_TEMPLATE_PATH) {
|
|
|
265
417
|
moduleConfig.packs[id] = entry;
|
|
266
418
|
}
|
|
267
419
|
moduleConfig.install_packs['multi-select'] = [
|
|
268
|
-
...derived.packOrder
|
|
269
|
-
|
|
270
|
-
|
|
420
|
+
...derived.packOrder
|
|
421
|
+
.filter((id) => !derived.packs[id].required)
|
|
422
|
+
.map((id) => ({
|
|
423
|
+
value: id,
|
|
424
|
+
label: `${registry.packs[id].icon_emoji} ${derived.packs[id].displayName} — ${derived.packs[id].summary || derived.packs[id].desc}`,
|
|
425
|
+
})),
|
|
271
426
|
{ value: 'all', label: 'Tout installer' },
|
|
272
427
|
{ value: 'none', label: 'Aucun — Core uniquement' },
|
|
273
428
|
];
|
|
274
|
-
return
|
|
275
|
-
yaml.
|
|
429
|
+
return (
|
|
430
|
+
'# AUTO-GENERATED from registry.yaml + tools/build/module.template.yaml — DO NOT EDIT.\n' +
|
|
431
|
+
yaml.dump(moduleConfig, { lineWidth: 110, noRefs: true })
|
|
432
|
+
);
|
|
276
433
|
}
|
|
277
434
|
|
|
278
435
|
/**
|
|
@@ -373,6 +530,16 @@ function check({
|
|
|
373
530
|
const mismatches = [];
|
|
374
531
|
|
|
375
532
|
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
533
|
+
const { checkPythonDelivery } = require('./pack-delivery');
|
|
534
|
+
mismatches.push(...checkPythonDelivery(registry, { packageRoot: REPO_ROOT, packageJson: pkg }));
|
|
535
|
+
try {
|
|
536
|
+
require('../cli/lib/studio').validatePack(
|
|
537
|
+
path.join(REPO_ROOT, 'src/bmad-plus/packs/pack-dev-studio'),
|
|
538
|
+
registry.packs['dev-studio']
|
|
539
|
+
);
|
|
540
|
+
} catch (error) {
|
|
541
|
+
mismatches.push('Dev Studio: ' + error.message);
|
|
542
|
+
}
|
|
376
543
|
if (!registry.product || registry.product.version !== pkg.version) {
|
|
377
544
|
const declared = registry.product ? registry.product.version : undefined;
|
|
378
545
|
mismatches.push(
|
|
@@ -385,7 +552,9 @@ function check({
|
|
|
385
552
|
describeDiff(key, generated[key], current[key], mismatches);
|
|
386
553
|
}
|
|
387
554
|
}
|
|
388
|
-
if (
|
|
555
|
+
if (
|
|
556
|
+
fs.readFileSync(modulePath, 'utf8').replace(/\r\n/g, '\n') !== generateModuleSource(registry)
|
|
557
|
+
) {
|
|
389
558
|
mismatches.push('module.yaml: generated pack metadata differs from registry.yaml');
|
|
390
559
|
}
|
|
391
560
|
|
|
@@ -427,7 +596,11 @@ function main(argv) {
|
|
|
427
596
|
const moduleOutIdx = args.indexOf('--module-out');
|
|
428
597
|
if (moduleOutIdx !== -1) {
|
|
429
598
|
if (!args[moduleOutIdx + 1]) throw new Error('--module-out requires a file path');
|
|
430
|
-
fs.writeFileSync(
|
|
599
|
+
fs.writeFileSync(
|
|
600
|
+
path.resolve(args[moduleOutIdx + 1]),
|
|
601
|
+
generateModuleSource(registry),
|
|
602
|
+
'utf8'
|
|
603
|
+
);
|
|
431
604
|
}
|
|
432
605
|
console.log(`Generated ${path.resolve(outPath)} from registry.yaml`);
|
|
433
606
|
return 0;
|
|
@@ -437,10 +610,6 @@ function main(argv) {
|
|
|
437
610
|
return 0;
|
|
438
611
|
}
|
|
439
612
|
|
|
440
|
-
if (require.main === module) {
|
|
441
|
-
process.exitCode = main(process.argv);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
613
|
module.exports = {
|
|
445
614
|
DEFAULT_REGISTRY_PATH,
|
|
446
615
|
DEFAULT_PACKS_MODULE_PATH,
|
|
@@ -459,3 +628,7 @@ module.exports = {
|
|
|
459
628
|
check,
|
|
460
629
|
main,
|
|
461
630
|
};
|
|
631
|
+
|
|
632
|
+
if (require.main === module) {
|
|
633
|
+
process.exitCode = main(process.argv);
|
|
634
|
+
}
|
|
@@ -113,7 +113,7 @@ After a successful update, reread the project spine and relevant installed skill
|
|
|
113
113
|
|
|
114
114
|
## Registry facts (computed from registry.yaml — never hand-typed)
|
|
115
115
|
|
|
116
|
-
- Product: BMAD+ v0.
|
|
116
|
+
- Product: BMAD+ v0.14.0 (derived from BMAD-METHOD v6.6.0)
|
|
117
117
|
- Models supported: claude, gpt, gemini, local (model-agnostic by contract)
|
|
118
118
|
- Packs (9): Core, OSINT, Maker, Shield, SEO, Memory, Dev Studio, Backup, Animated
|
|
119
119
|
- Installer agents (14 across all packs):
|
|
@@ -118,7 +118,7 @@ After a successful update, reread the project spine and relevant installed skill
|
|
|
118
118
|
|
|
119
119
|
## Registry facts (computed from registry.yaml — never hand-typed)
|
|
120
120
|
|
|
121
|
-
- Product: BMAD+ v0.
|
|
121
|
+
- Product: BMAD+ v0.14.0 (derived from BMAD-METHOD v6.6.0)
|
|
122
122
|
- Models supported: claude, gpt, gemini, local (model-agnostic by contract)
|
|
123
123
|
- Packs (9): Core, OSINT, Maker, Shield, SEO, Memory, Dev Studio, Backup, Animated
|
|
124
124
|
- Installer agents (14 across all packs):
|
|
@@ -113,7 +113,7 @@ After a successful update, reread the project spine and relevant installed skill
|
|
|
113
113
|
|
|
114
114
|
## Registry facts (computed from registry.yaml — never hand-typed)
|
|
115
115
|
|
|
116
|
-
- Product: BMAD+ v0.
|
|
116
|
+
- Product: BMAD+ v0.14.0 (derived from BMAD-METHOD v6.6.0)
|
|
117
117
|
- Models supported: claude, gpt, gemini, local (model-agnostic by contract)
|
|
118
118
|
- Packs (9): Core, OSINT, Maker, Shield, SEO, Memory, Dev Studio, Backup, Animated
|
|
119
119
|
- Installer agents (14 across all packs):
|
|
@@ -111,7 +111,7 @@ After a successful update, reread the project spine and relevant installed skill
|
|
|
111
111
|
|
|
112
112
|
## Registry facts (computed from registry.yaml — never hand-typed)
|
|
113
113
|
|
|
114
|
-
- Product: BMAD+ v0.
|
|
114
|
+
- Product: BMAD+ v0.14.0 (derived from BMAD-METHOD v6.6.0)
|
|
115
115
|
- Models supported: claude, gpt, gemini, local (model-agnostic by contract)
|
|
116
116
|
- Packs (9): Core, OSINT, Maker, Shield, SEO, Memory, Dev Studio, Backup, Animated
|
|
117
117
|
- Installer agents (14 across all packs):
|
|
@@ -114,7 +114,7 @@ After a successful update, reread the project spine and relevant installed skill
|
|
|
114
114
|
|
|
115
115
|
## Registry facts (computed from registry.yaml — never hand-typed)
|
|
116
116
|
|
|
117
|
-
- Product: BMAD+ v0.
|
|
117
|
+
- Product: BMAD+ v0.14.0 (derived from BMAD-METHOD v6.6.0)
|
|
118
118
|
- Models supported: claude, gpt, gemini, local (model-agnostic by contract)
|
|
119
119
|
- Packs (9): Core, OSINT, Maker, Shield, SEO, Memory, Dev Studio, Backup, Animated
|
|
120
120
|
- Installer agents (14 across all packs):
|
|
@@ -113,7 +113,7 @@ After a successful update, reread the project spine and relevant installed skill
|
|
|
113
113
|
|
|
114
114
|
## Registry facts (computed from registry.yaml — never hand-typed)
|
|
115
115
|
|
|
116
|
-
- Product: BMAD+ v0.
|
|
116
|
+
- Product: BMAD+ v0.14.0 (derived from BMAD-METHOD v6.6.0)
|
|
117
117
|
- Models supported: claude, gpt, gemini, local (model-agnostic by contract)
|
|
118
118
|
- Packs (9): Core, OSINT, Maker, Shield, SEO, Memory, Dev Studio, Backup, Animated
|
|
119
119
|
- Installer agents (14 across all packs):
|
|
@@ -118,7 +118,7 @@ After a successful update, reread the project spine and relevant installed skill
|
|
|
118
118
|
|
|
119
119
|
## Registry facts (computed from registry.yaml — never hand-typed)
|
|
120
120
|
|
|
121
|
-
- Product: BMAD+ v0.
|
|
121
|
+
- Product: BMAD+ v0.14.0 (derived from BMAD-METHOD v6.6.0)
|
|
122
122
|
- Models supported: claude, gpt, gemini, local (model-agnostic by contract)
|
|
123
123
|
- Packs (9): Core, OSINT, Maker, Shield, SEO, Memory, Dev Studio, Backup, Animated
|
|
124
124
|
- Installer agents (14 across all packs):
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Build-time Python delivery checks; the npm contract repeats them on the archive. */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
|
|
7
|
+
function portable(file) {
|
|
8
|
+
return (
|
|
9
|
+
typeof file === 'string' &&
|
|
10
|
+
Boolean(file) &&
|
|
11
|
+
!/[\\:\0*?![\]{}]/.test(file) &&
|
|
12
|
+
file.split('/').every((part) => part && part !== '.' && part !== '..')
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function checkPythonDelivery(registry, { packageRoot, packageJson }) {
|
|
17
|
+
const errors = [];
|
|
18
|
+
const roots = (packageJson.files || []).map((root) => root.replace(/\/$/, ''));
|
|
19
|
+
for (const [id, pack] of Object.entries(registry.packs)) {
|
|
20
|
+
if (!pack.runtime.includes('python')) continue;
|
|
21
|
+
if (!portable(pack.python_package)) {
|
|
22
|
+
errors.push(`${id}: python_package must name a portable package directory`);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const resources = [`${pack.python_package}/requirements.txt`];
|
|
26
|
+
if (pack.python_entry !== undefined) {
|
|
27
|
+
if (!portable(pack.python_entry)) {
|
|
28
|
+
errors.push(`${id}: python_entry must remain inside python_package`);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
resources.push(`${pack.python_package}/${pack.python_entry}`);
|
|
32
|
+
}
|
|
33
|
+
for (const file of resources) {
|
|
34
|
+
if (!roots.some((root) => portable(root) && (file === root || file.startsWith(`${root}/`)))) {
|
|
35
|
+
errors.push(`${id}: ${file} is outside package.json files roots`);
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const target = path.join(packageRoot, file);
|
|
39
|
+
if (!fs.lstatSync(target).isFile()) throw new Error('not a regular file');
|
|
40
|
+
const relative = path.relative(fs.realpathSync(packageRoot), fs.realpathSync(target));
|
|
41
|
+
if (relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
|
|
42
|
+
throw new Error('outside package');
|
|
43
|
+
} catch {
|
|
44
|
+
errors.push(`${id}: ${file} is missing or is not a regular file inside the package`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return errors;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function checkSeoMirror(packageRoot) {
|
|
52
|
+
const shipped = path.join(packageRoot, 'src/bmad-plus/packs/pack-seo');
|
|
53
|
+
const tested = path.join(packageRoot, 'oveanet-pack/seo-audit-360');
|
|
54
|
+
const files = (root) =>
|
|
55
|
+
fs
|
|
56
|
+
.readdirSync(path.join(root, 'scripts'))
|
|
57
|
+
.filter((file) => file.endsWith('.py'))
|
|
58
|
+
.sort();
|
|
59
|
+
const shippedFiles = files(shipped);
|
|
60
|
+
const testedFiles = files(tested);
|
|
61
|
+
const errors = [];
|
|
62
|
+
if (JSON.stringify(shippedFiles) !== JSON.stringify(testedFiles))
|
|
63
|
+
errors.push('SEO script inventory differs between shipped and tested sources');
|
|
64
|
+
for (const file of ['requirements.txt', ...shippedFiles.map((file) => `scripts/${file}`)]) {
|
|
65
|
+
try {
|
|
66
|
+
if (
|
|
67
|
+
!fs.readFileSync(path.join(shipped, file)).equals(fs.readFileSync(path.join(tested, file)))
|
|
68
|
+
) {
|
|
69
|
+
errors.push(`SEO shipped/tested sources differ: ${file}`);
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
errors.push(`SEO shipped/tested source is missing: ${file}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return errors;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { checkPythonDelivery, checkSeoMirror };
|
|
@@ -116,7 +116,7 @@ for (const option of autoconfig.options || []) {
|
|
|
116
116
|
autoconfigCmd.action(autoconfig.action);
|
|
117
117
|
|
|
118
118
|
// Version discovery and explicit per-project update policy.
|
|
119
|
-
for (const modulePath of ['./commands/update-check', './commands/update-policy']) {
|
|
119
|
+
for (const modulePath of ['./commands/update-check', './commands/update-policy', './commands/studio']) {
|
|
120
120
|
const command = require(modulePath);
|
|
121
121
|
const configured = program.command(command.command).description(command.description);
|
|
122
122
|
for (const option of command.options || []) configured.option(...option);
|