bmad-plus 0.13.0 → 0.16.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 +74 -0
- package/README.md +113 -487
- package/SECURITY.md +71 -0
- package/THIRD-PARTY-LICENSES.md +349 -0
- package/osint-agent-package/README.md +1 -1
- package/package.json +14 -3
- package/readme-international/README.de.md +18 -8
- package/readme-international/README.es.md +19 -9
- package/readme-international/README.fr.md +18 -8
- package/src/bmad-plus/agents/agent-architect-dev/SKILL.md +11 -13
- package/src/bmad-plus/agents/agent-orchestrator/SKILL.md +148 -9
- package/src/bmad-plus/agents/agent-quality/SKILL.md +41 -11
- package/src/bmad-plus/data/role-triggers.yaml +19 -0
- package/src/bmad-plus/module-help.csv +1 -0
- package/src/bmad-plus/module.yaml +1 -0
- 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 +54 -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 +24 -80
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +64 -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 +35 -4
- package/src/bmad-plus/packs/pack-memory/memory-orchestrator.md +33 -6
- package/src/bmad-plus/packs/pack-memory/shared/karpathy-guardrails.md +3 -3
- package/src/bmad-plus/packs/pack-memory/shared/memory-protocol.md +27 -3
- package/src/bmad-plus/packs/pack-memory/zecher-agent.md +18 -2
- 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 +49 -12
- package/src/bmad-plus/skills/bmad-plus-parallel/SKILL.md +17 -3
- package/src/bmad-plus/skills/bmad-plus-sync/SKILL.md +76 -65
- package/src/bmad-plus/skills/bmad-plus-uat/SKILL.md +144 -0
- package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-results.schema.json +60 -0
- package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-spec.schema.json +121 -0
- package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-triage.schema.json +60 -0
- package/src/bmad-plus/skills/bmad-plus-uat/template/page.html +552 -0
- package/src/bmad-plus/skills/bmad-plus-uat/template/strings.json +362 -0
- package/src/bmad-plus/skills/dev-studio/SKILL.md +19 -0
- package/tools/build/check-install-contract.js +367 -17
- package/tools/build/generate.js +229 -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 +15 -12
- package/tools/cli/commands/doctor.js +50 -189
- package/tools/cli/commands/install.js +22 -3
- package/tools/cli/commands/memory-journal-cmd.js +119 -19
- package/tools/cli/commands/nexus.js +111 -0
- package/tools/cli/commands/studio.js +68 -0
- package/tools/cli/commands/uat.js +389 -0
- package/tools/cli/lib/README-memory-journal.md +19 -8
- package/tools/cli/lib/installation-health.js +366 -0
- package/tools/cli/lib/memory-journal.js +0 -0
- package/tools/cli/lib/memory-outcomes.js +293 -0
- package/tools/cli/lib/memory-store.js +139 -0
- package/tools/cli/lib/nexus-process.js +377 -0
- package/tools/cli/lib/nexus.js +1532 -0
- package/tools/cli/lib/pack-copy.js +39 -11
- package/tools/cli/lib/packs.js +134 -11
- 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/cli/lib/uat.js +869 -0
- package/tools/maintain/upstream-candidate.js +456 -0
- package/tools/release/publication-content.js +903 -0
- package/tools/release/supply-chain.js +282 -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,82 @@ 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
|
+
const processBackend = registry.targets?.optional_process_backend;
|
|
193
|
+
if (
|
|
194
|
+
processBackend &&
|
|
195
|
+
(!isDeepStrictEqual(processBackend.adapters, ['command', 'codex-exec']) ||
|
|
196
|
+
processBackend.supervisor !== 'foreground' ||
|
|
197
|
+
processBackend.launch !== 'explicit-plan' ||
|
|
198
|
+
processBackend.collect !== 'exact-attempt-receipt' ||
|
|
199
|
+
processBackend.cancellation !== 'original-owner-direct-child' ||
|
|
200
|
+
processBackend.verification !== 'independent-required' ||
|
|
201
|
+
processBackend.scheduling !== 'none')
|
|
202
|
+
) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
'registry.yaml: optional process backend differs from the delivered Nexus contract'
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
for (const [runtime, config] of Object.entries(registry.runtimes || {})) {
|
|
208
|
+
if (
|
|
209
|
+
typeof config?.min_version !== 'string' ||
|
|
210
|
+
!/^\d+\.\d+(?:\.\d+)?$/.test(config.min_version)
|
|
211
|
+
) {
|
|
212
|
+
throw new Error(`registry.yaml: runtime "${runtime}" needs a numeric min_version`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const tools = new Set();
|
|
216
|
+
for (const adapter of registry.targets.adapters || []) {
|
|
217
|
+
if (!adapter || typeof adapter.tool !== 'string' || !adapter.tool || tools.has(adapter.tool)) {
|
|
218
|
+
throw new Error('registry.yaml: adapter tool names must be nonempty and unique');
|
|
219
|
+
}
|
|
220
|
+
tools.add(adapter.tool);
|
|
221
|
+
assertResourcePath(adapter.file, `registry.yaml: adapter "${adapter.tool}"`);
|
|
222
|
+
}
|
|
113
223
|
return registry;
|
|
114
224
|
}
|
|
115
225
|
|
|
@@ -171,10 +281,14 @@ function renderSummary(pack, derived, field = 'summary') {
|
|
|
171
281
|
const fallback = field === 'summary' ? pack.summary : pack.cli.desc;
|
|
172
282
|
if (!template) return fallback || '';
|
|
173
283
|
const values = {
|
|
174
|
-
agent_count: derived.agentCount,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
284
|
+
agent_count: derived.agentCount,
|
|
285
|
+
installer_agent_count: derived.installerAgentCount,
|
|
286
|
+
sub_agent_count: derived.subAgentCount,
|
|
287
|
+
workflow_count: derived.workflowCount,
|
|
288
|
+
framework_count: derived.frameworkCount,
|
|
289
|
+
category_count: derived.categoryCount,
|
|
290
|
+
skill_count: derived.skillCount,
|
|
291
|
+
reference_file_count: derived.referenceFiles,
|
|
178
292
|
};
|
|
179
293
|
return template.replace(/\{([a-z_]+)\}/g, (_match, key) => {
|
|
180
294
|
if (!(key in values)) throw new Error(`Unknown derived summary placeholder: ${key}`);
|
|
@@ -195,18 +309,33 @@ function buildDerived(registry, { sourceRoot = path.join(REPO_ROOT, 'src', 'bmad
|
|
|
195
309
|
const installerAgentCount = pack.agents.length;
|
|
196
310
|
const subAgentCount = (pack.sub_agents || []).length;
|
|
197
311
|
const facts = {
|
|
198
|
-
id,
|
|
199
|
-
|
|
312
|
+
id,
|
|
313
|
+
order: pack.order,
|
|
314
|
+
name: pack.cli.name,
|
|
315
|
+
displayName: pack.display_name || pack.cli.name,
|
|
316
|
+
required: pack.required === true,
|
|
317
|
+
installerAgentCount,
|
|
318
|
+
categoryAgentCount,
|
|
200
319
|
agentCount: categoryAgentCount || subAgentCount || installerAgentCount,
|
|
201
320
|
subAgentCount,
|
|
202
|
-
workflowCount:
|
|
321
|
+
workflowCount:
|
|
322
|
+
(pack.workflows || []).length +
|
|
323
|
+
categories.reduce((sum, c) => sum + (c.workflows || []).length, 0),
|
|
203
324
|
frameworkCount: (pack.compliance_tags || []).length,
|
|
204
325
|
categoryCount: categories.length,
|
|
205
326
|
skillCount: (pack.skills || []).length,
|
|
206
|
-
referenceFiles: countMarkdownFiles(
|
|
327
|
+
referenceFiles: countMarkdownFiles(
|
|
328
|
+
path.join(sourceRoot, pack.pack_src_dir || 'packs', pack.pack_dir, 'references')
|
|
329
|
+
),
|
|
207
330
|
categoryAgentCounts,
|
|
331
|
+
runtimes: [...pack.runtime],
|
|
332
|
+
requiredResources: buildRequiredResources(pack),
|
|
208
333
|
personas: (pack.personas || []).map(({ id, name, role, description, alias }) => ({
|
|
209
|
-
id,
|
|
334
|
+
id,
|
|
335
|
+
name,
|
|
336
|
+
role,
|
|
337
|
+
description,
|
|
338
|
+
...(alias ? { alias } : {}),
|
|
210
339
|
})),
|
|
211
340
|
};
|
|
212
341
|
facts.summary = renderSummary(pack, facts);
|
|
@@ -215,10 +344,13 @@ function buildDerived(registry, { sourceRoot = path.join(REPO_ROOT, 'src', 'bmad
|
|
|
215
344
|
packs[id] = facts;
|
|
216
345
|
installerAgents += installerAgentCount;
|
|
217
346
|
totalAgents += new Set([
|
|
218
|
-
...pack.agents,
|
|
347
|
+
...pack.agents,
|
|
348
|
+
...(pack.sub_agents || []),
|
|
349
|
+
...categories.flatMap((c) => c.agents || []),
|
|
219
350
|
]).size;
|
|
220
351
|
if ((pack.runtime || []).includes('python')) {
|
|
221
|
-
const packagePath =
|
|
352
|
+
const packagePath =
|
|
353
|
+
pack.python_package || `src/bmad-plus/${pack.pack_src_dir || 'packs'}/${pack.pack_dir}`;
|
|
222
354
|
pythonPacks[id] = {
|
|
223
355
|
requirements: [...packagePath.split(/[\\/]/), 'requirements.txt'],
|
|
224
356
|
verifyModules: [...(pack.python_verify_modules || [])],
|
|
@@ -228,12 +360,35 @@ function buildDerived(registry, { sourceRoot = path.join(REPO_ROOT, 'src', 'bmad
|
|
|
228
360
|
const { LANGUAGES } = require('../cli/i18n');
|
|
229
361
|
return {
|
|
230
362
|
product: {
|
|
231
|
-
code: registry.product.code,
|
|
232
|
-
|
|
363
|
+
code: registry.product.code,
|
|
364
|
+
displayName: registry.product.display_name,
|
|
365
|
+
version: registry.product.version,
|
|
366
|
+
derivedFrom: registry.product.derived_from,
|
|
233
367
|
},
|
|
234
368
|
packOrder: buildPackOrder(registry),
|
|
235
|
-
packCount: Object.keys(packs).length,
|
|
236
|
-
|
|
369
|
+
packCount: Object.keys(packs).length,
|
|
370
|
+
installerAgents,
|
|
371
|
+
totalAgents,
|
|
372
|
+
languages: Object.keys(LANGUAGES),
|
|
373
|
+
packs,
|
|
374
|
+
pythonPacks,
|
|
375
|
+
diagnostics: {
|
|
376
|
+
schemaVersion: 1,
|
|
377
|
+
processExecution: registry.targets.optional_process_backend || null,
|
|
378
|
+
runtimeMinimums: Object.fromEntries(
|
|
379
|
+
Object.entries(registry.runtimes).map(([id, runtime]) => [id, runtime.min_version])
|
|
380
|
+
),
|
|
381
|
+
integrations: Object.fromEntries(
|
|
382
|
+
registry.targets.adapters.map(({ tool, file }) => [
|
|
383
|
+
tool,
|
|
384
|
+
{
|
|
385
|
+
instructionFile: file,
|
|
386
|
+
execution: registry.targets.integration.execution,
|
|
387
|
+
lifecycleEvents: registry.targets.integration.lifecycle_events,
|
|
388
|
+
},
|
|
389
|
+
])
|
|
390
|
+
),
|
|
391
|
+
},
|
|
237
392
|
targets: {
|
|
238
393
|
spine: registry.targets.spine,
|
|
239
394
|
adapters: registry.targets.adapters.map(({ tool, file }) => ({ tool, file })),
|
|
@@ -251,11 +406,24 @@ function generateModuleSource(registry, templatePath = MODULE_TEMPLATE_PATH) {
|
|
|
251
406
|
for (const [id, pack] of sortedPackEntries(registry)) {
|
|
252
407
|
const facts = derived.packs[id];
|
|
253
408
|
const entry = {
|
|
254
|
-
name: facts.displayName,
|
|
255
|
-
|
|
409
|
+
name: facts.displayName,
|
|
410
|
+
icon: pack.icon_emoji,
|
|
411
|
+
description: facts.summary || facts.desc,
|
|
412
|
+
required: facts.required,
|
|
413
|
+
agents: [...pack.agents],
|
|
414
|
+
skills: [...(pack.skills || [])],
|
|
256
415
|
};
|
|
257
|
-
for (const key of [
|
|
258
|
-
'
|
|
416
|
+
for (const key of [
|
|
417
|
+
'data',
|
|
418
|
+
'external_package',
|
|
419
|
+
'orchestrator',
|
|
420
|
+
'categories',
|
|
421
|
+
'workflows',
|
|
422
|
+
'sub_agents',
|
|
423
|
+
'required_keys',
|
|
424
|
+
'optional_keys',
|
|
425
|
+
'cohabitation_warning',
|
|
426
|
+
]) {
|
|
259
427
|
if (key in pack) entry[key] = JSON.parse(JSON.stringify(pack[key]));
|
|
260
428
|
}
|
|
261
429
|
if (pack.install_layout === 'packaged') {
|
|
@@ -265,14 +433,19 @@ function generateModuleSource(registry, templatePath = MODULE_TEMPLATE_PATH) {
|
|
|
265
433
|
moduleConfig.packs[id] = entry;
|
|
266
434
|
}
|
|
267
435
|
moduleConfig.install_packs['multi-select'] = [
|
|
268
|
-
...derived.packOrder
|
|
269
|
-
|
|
270
|
-
|
|
436
|
+
...derived.packOrder
|
|
437
|
+
.filter((id) => !derived.packs[id].required)
|
|
438
|
+
.map((id) => ({
|
|
439
|
+
value: id,
|
|
440
|
+
label: `${registry.packs[id].icon_emoji} ${derived.packs[id].displayName} — ${derived.packs[id].summary || derived.packs[id].desc}`,
|
|
441
|
+
})),
|
|
271
442
|
{ value: 'all', label: 'Tout installer' },
|
|
272
443
|
{ value: 'none', label: 'Aucun — Core uniquement' },
|
|
273
444
|
];
|
|
274
|
-
return
|
|
275
|
-
yaml.
|
|
445
|
+
return (
|
|
446
|
+
'# AUTO-GENERATED from registry.yaml + tools/build/module.template.yaml — DO NOT EDIT.\n' +
|
|
447
|
+
yaml.dump(moduleConfig, { lineWidth: 110, noRefs: true })
|
|
448
|
+
);
|
|
276
449
|
}
|
|
277
450
|
|
|
278
451
|
/**
|
|
@@ -373,6 +546,16 @@ function check({
|
|
|
373
546
|
const mismatches = [];
|
|
374
547
|
|
|
375
548
|
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
549
|
+
const { checkPythonDelivery } = require('./pack-delivery');
|
|
550
|
+
mismatches.push(...checkPythonDelivery(registry, { packageRoot: REPO_ROOT, packageJson: pkg }));
|
|
551
|
+
try {
|
|
552
|
+
require('../cli/lib/studio').validatePack(
|
|
553
|
+
path.join(REPO_ROOT, 'src/bmad-plus/packs/pack-dev-studio'),
|
|
554
|
+
registry.packs['dev-studio']
|
|
555
|
+
);
|
|
556
|
+
} catch (error) {
|
|
557
|
+
mismatches.push('Dev Studio: ' + error.message);
|
|
558
|
+
}
|
|
376
559
|
if (!registry.product || registry.product.version !== pkg.version) {
|
|
377
560
|
const declared = registry.product ? registry.product.version : undefined;
|
|
378
561
|
mismatches.push(
|
|
@@ -385,7 +568,9 @@ function check({
|
|
|
385
568
|
describeDiff(key, generated[key], current[key], mismatches);
|
|
386
569
|
}
|
|
387
570
|
}
|
|
388
|
-
if (
|
|
571
|
+
if (
|
|
572
|
+
fs.readFileSync(modulePath, 'utf8').replace(/\r\n/g, '\n') !== generateModuleSource(registry)
|
|
573
|
+
) {
|
|
389
574
|
mismatches.push('module.yaml: generated pack metadata differs from registry.yaml');
|
|
390
575
|
}
|
|
391
576
|
|
|
@@ -427,7 +612,11 @@ function main(argv) {
|
|
|
427
612
|
const moduleOutIdx = args.indexOf('--module-out');
|
|
428
613
|
if (moduleOutIdx !== -1) {
|
|
429
614
|
if (!args[moduleOutIdx + 1]) throw new Error('--module-out requires a file path');
|
|
430
|
-
fs.writeFileSync(
|
|
615
|
+
fs.writeFileSync(
|
|
616
|
+
path.resolve(args[moduleOutIdx + 1]),
|
|
617
|
+
generateModuleSource(registry),
|
|
618
|
+
'utf8'
|
|
619
|
+
);
|
|
431
620
|
}
|
|
432
621
|
console.log(`Generated ${path.resolve(outPath)} from registry.yaml`);
|
|
433
622
|
return 0;
|
|
@@ -437,10 +626,6 @@ function main(argv) {
|
|
|
437
626
|
return 0;
|
|
438
627
|
}
|
|
439
628
|
|
|
440
|
-
if (require.main === module) {
|
|
441
|
-
process.exitCode = main(process.argv);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
629
|
module.exports = {
|
|
445
630
|
DEFAULT_REGISTRY_PATH,
|
|
446
631
|
DEFAULT_PACKS_MODULE_PATH,
|
|
@@ -459,3 +644,7 @@ module.exports = {
|
|
|
459
644
|
check,
|
|
460
645
|
main,
|
|
461
646
|
};
|
|
647
|
+
|
|
648
|
+
if (require.main === module) {
|
|
649
|
+
process.exitCode = main(process.argv);
|
|
650
|
+
}
|
|
@@ -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.16.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.16.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.16.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.16.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.16.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.16.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.16.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 };
|
|
@@ -45,9 +45,7 @@ for (const option of install.options || []) {
|
|
|
45
45
|
installCmd.action(install.action);
|
|
46
46
|
|
|
47
47
|
// Uninstall command
|
|
48
|
-
const uninstallCmd = program
|
|
49
|
-
.command('uninstall')
|
|
50
|
-
.description('Remove BMAD+ from your project');
|
|
48
|
+
const uninstallCmd = program.command('uninstall').description('Remove BMAD+ from your project');
|
|
51
49
|
for (const option of uninstall.options || []) {
|
|
52
50
|
uninstallCmd.option(...option);
|
|
53
51
|
}
|
|
@@ -64,9 +62,7 @@ for (const option of update.options || []) {
|
|
|
64
62
|
updateCmd.action(update.action);
|
|
65
63
|
|
|
66
64
|
// Doctor command
|
|
67
|
-
const doctorCmd = program
|
|
68
|
-
.command('doctor')
|
|
69
|
-
.description('Check BMAD+ installation integrity');
|
|
65
|
+
const doctorCmd = program.command('doctor').description('Check BMAD+ installation integrity');
|
|
70
66
|
|
|
71
67
|
for (const option of doctor.options || []) {
|
|
72
68
|
doctorCmd.option(...option);
|
|
@@ -81,7 +77,9 @@ const scanCmd = program
|
|
|
81
77
|
for (const option of scan.options || []) {
|
|
82
78
|
scanCmd.option(...option);
|
|
83
79
|
}
|
|
84
|
-
scanCmd.action((scanPath, options) =>
|
|
80
|
+
scanCmd.action((scanPath, options) =>
|
|
81
|
+
scan.action({ ...options, directory: scanPath || options.directory })
|
|
82
|
+
);
|
|
85
83
|
|
|
86
84
|
// Memory command
|
|
87
85
|
const memoryCmd = program
|
|
@@ -95,9 +93,7 @@ memoryCmd.action(memory.action);
|
|
|
95
93
|
|
|
96
94
|
// Mem command (Karpathy memory loop — journal recall/write/reinforce)
|
|
97
95
|
const memJournal = require('./commands/memory-journal-cmd');
|
|
98
|
-
const memJournalCmd = program
|
|
99
|
-
.command(memJournal.command)
|
|
100
|
-
.description(memJournal.description);
|
|
96
|
+
const memJournalCmd = program.command(memJournal.command).description(memJournal.description);
|
|
101
97
|
|
|
102
98
|
for (const option of memJournal.options || []) {
|
|
103
99
|
memJournalCmd.option(...option);
|
|
@@ -116,15 +112,22 @@ for (const option of autoconfig.options || []) {
|
|
|
116
112
|
autoconfigCmd.action(autoconfig.action);
|
|
117
113
|
|
|
118
114
|
// Version discovery and explicit per-project update policy.
|
|
119
|
-
for (const modulePath of [
|
|
115
|
+
for (const modulePath of [
|
|
116
|
+
'./commands/update-check',
|
|
117
|
+
'./commands/update-policy',
|
|
118
|
+
'./commands/studio',
|
|
119
|
+
'./commands/nexus',
|
|
120
|
+
'./commands/uat',
|
|
121
|
+
]) {
|
|
120
122
|
const command = require(modulePath);
|
|
121
123
|
const configured = program.command(command.command).description(command.description);
|
|
124
|
+
if (command.aliases) configured.aliases(command.aliases);
|
|
122
125
|
for (const option of command.options || []) configured.option(...option);
|
|
123
126
|
configured.action(command.action);
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
// Await asynchronous registry checks and updates before completing the CLI.
|
|
127
|
-
program.parseAsync(process.argv).catch(error => {
|
|
130
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
128
131
|
console.error(`BMAD+: ${error.message}`);
|
|
129
132
|
process.exitCode = 1;
|
|
130
133
|
});
|