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
|
@@ -7,47 +7,74 @@ const fs = require('node:fs');
|
|
|
7
7
|
const crypto = require('node:crypto');
|
|
8
8
|
const { safeTarget } = require('../../build/generate-adapters');
|
|
9
9
|
|
|
10
|
-
const fileHash = content => crypto.createHash('sha256').update(content).digest('hex');
|
|
10
|
+
const fileHash = (content) => crypto.createHash('sha256').update(content).digest('hex');
|
|
11
11
|
|
|
12
12
|
function listPackFiles({ bmadSrc, targetAgentsDir, targetDataDir, projectRoot, pack }) {
|
|
13
13
|
const result = { copiedAgents: 0, copiedSkills: 0, copiedFiles: 0, files: [] };
|
|
14
14
|
if (!pack) return result;
|
|
15
15
|
function walk(source, target) {
|
|
16
|
+
// Local Python execution must not turn interpreter caches into owned/shipped
|
|
17
|
+
// framework files. Keep this aligned with the explicit npm files exclusions.
|
|
18
|
+
if (path.basename(source) === '__pycache__' || /\.py[co]$/i.test(source)) return false;
|
|
16
19
|
let stat;
|
|
17
|
-
try {
|
|
20
|
+
try {
|
|
21
|
+
stat = fs.lstatSync(source);
|
|
22
|
+
} catch (error) {
|
|
18
23
|
if (error.code === 'ENOENT') return false;
|
|
19
24
|
throw error;
|
|
20
25
|
}
|
|
21
26
|
if (stat.isSymbolicLink()) throw new Error('Pack source is a symbolic link: ' + source);
|
|
22
27
|
if (stat.isDirectory()) {
|
|
23
|
-
for (const name of fs.readdirSync(source).sort())
|
|
28
|
+
for (const name of fs.readdirSync(source).sort())
|
|
29
|
+
walk(path.join(source, name), path.join(target, name));
|
|
24
30
|
} else if (stat.isFile()) {
|
|
25
31
|
result.files.push({ source, target });
|
|
26
32
|
} else throw new Error('Pack source is not a regular file: ' + source);
|
|
27
33
|
return true;
|
|
28
34
|
}
|
|
29
35
|
for (const name of pack.agents || []) {
|
|
30
|
-
if (walk(path.join(bmadSrc, 'agents', name), path.join(targetAgentsDir, name)))
|
|
36
|
+
if (walk(path.join(bmadSrc, 'agents', name), path.join(targetAgentsDir, name)))
|
|
37
|
+
result.copiedAgents++;
|
|
31
38
|
}
|
|
32
39
|
for (const name of pack.skills || []) {
|
|
33
|
-
if (walk(path.join(bmadSrc, 'skills', name), path.join(targetAgentsDir, name)))
|
|
40
|
+
if (walk(path.join(bmadSrc, 'skills', name), path.join(targetAgentsDir, name)))
|
|
41
|
+
result.copiedSkills++;
|
|
34
42
|
}
|
|
35
43
|
for (const name of pack.data || []) {
|
|
36
|
-
if (walk(path.join(bmadSrc, 'data', name), path.join(targetDataDir, name)))
|
|
44
|
+
if (walk(path.join(bmadSrc, 'data', name), path.join(targetDataDir, name)))
|
|
45
|
+
result.copiedFiles++;
|
|
37
46
|
}
|
|
38
|
-
if (
|
|
39
|
-
|
|
47
|
+
if (
|
|
48
|
+
pack.externalPackage &&
|
|
49
|
+
walk(path.join(projectRoot, pack.externalPackage, 'skills'), targetAgentsDir)
|
|
50
|
+
)
|
|
51
|
+
result.copiedSkills++;
|
|
52
|
+
if (
|
|
53
|
+
pack.packDir &&
|
|
54
|
+
walk(
|
|
55
|
+
path.join(bmadSrc, pack.packSrcDir || 'agents', pack.packDir),
|
|
56
|
+
path.join(targetAgentsDir, pack.packDir)
|
|
57
|
+
)
|
|
58
|
+
) {
|
|
40
59
|
result.copiedAgents++;
|
|
41
60
|
result.copiedFiles++;
|
|
42
61
|
}
|
|
43
62
|
return result;
|
|
44
63
|
}
|
|
45
64
|
|
|
46
|
-
function copyManagedFile({
|
|
65
|
+
function copyManagedFile({
|
|
66
|
+
source,
|
|
67
|
+
target,
|
|
68
|
+
projectDir,
|
|
69
|
+
inventory,
|
|
70
|
+
previousInventory = {},
|
|
71
|
+
onConflict = () => {},
|
|
72
|
+
}) {
|
|
47
73
|
const file = path.relative(projectDir, target).split(path.sep).join('/');
|
|
48
74
|
safeTarget(projectDir, file);
|
|
49
75
|
const sourceStat = fs.lstatSync(source);
|
|
50
|
-
if (!sourceStat.isFile() || sourceStat.isSymbolicLink())
|
|
76
|
+
if (!sourceStat.isFile() || sourceStat.isSymbolicLink())
|
|
77
|
+
throw new Error('Pack source is not a regular file: ' + source);
|
|
51
78
|
const content = fs.readFileSync(source);
|
|
52
79
|
const hash = fileHash(content);
|
|
53
80
|
if (fs.existsSync(target)) {
|
|
@@ -72,7 +99,8 @@ function copyManagedFile({ source, target, projectDir, inventory, previousInvent
|
|
|
72
99
|
function copyPackFiles(options) {
|
|
73
100
|
const { files, ...counts } = listPackFiles(options);
|
|
74
101
|
const projectDir = options.projectDir || path.dirname(options.targetAgentsDir);
|
|
75
|
-
for (const { target } of files)
|
|
102
|
+
for (const { target } of files)
|
|
103
|
+
safeTarget(projectDir, path.relative(projectDir, target).split(path.sep).join('/'));
|
|
76
104
|
for (const entry of files) copyManagedFile({ ...options, ...entry, projectDir });
|
|
77
105
|
return counts;
|
|
78
106
|
}
|
package/tools/cli/lib/packs.js
CHANGED
|
@@ -19,7 +19,8 @@ const PACKS = {
|
|
|
19
19
|
"skills": [
|
|
20
20
|
"bmad-plus-autopilot",
|
|
21
21
|
"bmad-plus-parallel",
|
|
22
|
-
"bmad-plus-sync"
|
|
22
|
+
"bmad-plus-sync",
|
|
23
|
+
"bmad-plus-uat"
|
|
23
24
|
],
|
|
24
25
|
"data": [
|
|
25
26
|
"role-triggers.yaml"
|
|
@@ -210,7 +211,7 @@ const DERIVED = {
|
|
|
210
211
|
"product": {
|
|
211
212
|
"code": "bmad-plus",
|
|
212
213
|
"displayName": "BMAD+",
|
|
213
|
-
"version": "0.
|
|
214
|
+
"version": "0.16.0",
|
|
214
215
|
"derivedFrom": "BMAD-METHOD v6.6.0"
|
|
215
216
|
},
|
|
216
217
|
"packOrder": [
|
|
@@ -253,9 +254,23 @@ const DERIVED = {
|
|
|
253
254
|
"workflowCount": 0,
|
|
254
255
|
"frameworkCount": 0,
|
|
255
256
|
"categoryCount": 0,
|
|
256
|
-
"skillCount":
|
|
257
|
+
"skillCount": 4,
|
|
257
258
|
"referenceFiles": 0,
|
|
258
259
|
"categoryAgentCounts": [],
|
|
260
|
+
"runtimes": [
|
|
261
|
+
"node"
|
|
262
|
+
],
|
|
263
|
+
"requiredResources": [
|
|
264
|
+
".agents/skills/agent-strategist/SKILL.md",
|
|
265
|
+
".agents/skills/agent-architect-dev/SKILL.md",
|
|
266
|
+
".agents/skills/agent-quality/SKILL.md",
|
|
267
|
+
".agents/skills/agent-orchestrator/SKILL.md",
|
|
268
|
+
".agents/skills/bmad-plus-autopilot/SKILL.md",
|
|
269
|
+
".agents/skills/bmad-plus-parallel/SKILL.md",
|
|
270
|
+
".agents/skills/bmad-plus-sync/SKILL.md",
|
|
271
|
+
".agents/skills/bmad-plus-uat/SKILL.md",
|
|
272
|
+
".agents/data/role-triggers.yaml"
|
|
273
|
+
],
|
|
259
274
|
"personas": [
|
|
260
275
|
{
|
|
261
276
|
"id": "agent-strategist",
|
|
@@ -302,6 +317,12 @@ const DERIVED = {
|
|
|
302
317
|
"skillCount": 0,
|
|
303
318
|
"referenceFiles": 0,
|
|
304
319
|
"categoryAgentCounts": [],
|
|
320
|
+
"runtimes": [
|
|
321
|
+
"node"
|
|
322
|
+
],
|
|
323
|
+
"requiredResources": [
|
|
324
|
+
".agents/skills/agent-shadow/SKILL.md"
|
|
325
|
+
],
|
|
305
326
|
"personas": [
|
|
306
327
|
{
|
|
307
328
|
"id": "agent-shadow",
|
|
@@ -330,6 +351,12 @@ const DERIVED = {
|
|
|
330
351
|
"skillCount": 0,
|
|
331
352
|
"referenceFiles": 0,
|
|
332
353
|
"categoryAgentCounts": [],
|
|
354
|
+
"runtimes": [
|
|
355
|
+
"node"
|
|
356
|
+
],
|
|
357
|
+
"requiredResources": [
|
|
358
|
+
".agents/skills/agent-maker/SKILL.md"
|
|
359
|
+
],
|
|
333
360
|
"personas": [
|
|
334
361
|
{
|
|
335
362
|
"id": "agent-maker",
|
|
@@ -365,6 +392,13 @@ const DERIVED = {
|
|
|
365
392
|
3,
|
|
366
393
|
3
|
|
367
394
|
],
|
|
395
|
+
"runtimes": [
|
|
396
|
+
"node"
|
|
397
|
+
],
|
|
398
|
+
"requiredResources": [
|
|
399
|
+
".agents/skills/pack-shield/shield-orchestrator.md",
|
|
400
|
+
".agents/skills/pack-shield/SKILL.md"
|
|
401
|
+
],
|
|
368
402
|
"personas": [
|
|
369
403
|
{
|
|
370
404
|
"id": "shield-orchestrator",
|
|
@@ -393,6 +427,17 @@ const DERIVED = {
|
|
|
393
427
|
"skillCount": 0,
|
|
394
428
|
"referenceFiles": 0,
|
|
395
429
|
"categoryAgentCounts": [],
|
|
430
|
+
"runtimes": [
|
|
431
|
+
"node",
|
|
432
|
+
"python"
|
|
433
|
+
],
|
|
434
|
+
"requiredResources": [
|
|
435
|
+
".agents/skills/pack-seo/seo-scout.md",
|
|
436
|
+
".agents/skills/pack-seo/seo-chief.md",
|
|
437
|
+
".agents/skills/pack-seo/seo-judge.md",
|
|
438
|
+
".agents/skills/pack-seo/SKILL.md",
|
|
439
|
+
".agents/skills/pack-seo/scripts/seo_fetch.py"
|
|
440
|
+
],
|
|
396
441
|
"personas": [
|
|
397
442
|
{
|
|
398
443
|
"id": "seo-scout",
|
|
@@ -433,6 +478,13 @@ const DERIVED = {
|
|
|
433
478
|
"skillCount": 0,
|
|
434
479
|
"referenceFiles": 0,
|
|
435
480
|
"categoryAgentCounts": [],
|
|
481
|
+
"runtimes": [
|
|
482
|
+
"node"
|
|
483
|
+
],
|
|
484
|
+
"requiredResources": [
|
|
485
|
+
".agents/skills/pack-memory/zecher-agent.md",
|
|
486
|
+
".agents/skills/pack-memory/memory-orchestrator.md"
|
|
487
|
+
],
|
|
436
488
|
"personas": [
|
|
437
489
|
{
|
|
438
490
|
"id": "zecher",
|
|
@@ -468,6 +520,18 @@ const DERIVED = {
|
|
|
468
520
|
0,
|
|
469
521
|
0
|
|
470
522
|
],
|
|
523
|
+
"runtimes": [
|
|
524
|
+
"node"
|
|
525
|
+
],
|
|
526
|
+
"requiredResources": [
|
|
527
|
+
".agents/skills/pack-dev-studio/dev-studio-orchestrator.md",
|
|
528
|
+
".agents/skills/dev-studio/SKILL.md",
|
|
529
|
+
".agents/skills/pack-dev-studio/SKILL.md",
|
|
530
|
+
".agents/skills/pack-dev-studio/shared/catalog.json",
|
|
531
|
+
".agents/skills/pack-dev-studio/shared/execution.md",
|
|
532
|
+
".agents/skills/pack-dev-studio/categories/planning/create-ux-design.md",
|
|
533
|
+
".agents/skills/pack-dev-studio/categories/planning/steps/step-01-init.md"
|
|
534
|
+
],
|
|
471
535
|
"personas": [
|
|
472
536
|
{
|
|
473
537
|
"id": "analyst-agent",
|
|
@@ -532,6 +596,12 @@ const DERIVED = {
|
|
|
532
596
|
"skillCount": 0,
|
|
533
597
|
"referenceFiles": 0,
|
|
534
598
|
"categoryAgentCounts": [],
|
|
599
|
+
"runtimes": [
|
|
600
|
+
"node"
|
|
601
|
+
],
|
|
602
|
+
"requiredResources": [
|
|
603
|
+
".agents/skills/pack-backup/backup-agent.md"
|
|
604
|
+
],
|
|
535
605
|
"personas": [
|
|
536
606
|
{
|
|
537
607
|
"id": "backup-agent",
|
|
@@ -560,6 +630,12 @@ const DERIVED = {
|
|
|
560
630
|
"skillCount": 0,
|
|
561
631
|
"referenceFiles": 0,
|
|
562
632
|
"categoryAgentCounts": [],
|
|
633
|
+
"runtimes": [
|
|
634
|
+
"node"
|
|
635
|
+
],
|
|
636
|
+
"requiredResources": [
|
|
637
|
+
".agents/skills/pack-animated/animated-website-agent.md"
|
|
638
|
+
],
|
|
563
639
|
"personas": [
|
|
564
640
|
{
|
|
565
641
|
"id": "animated-website-agent",
|
|
@@ -588,15 +664,62 @@ const DERIVED = {
|
|
|
588
664
|
"defusedxml",
|
|
589
665
|
"lxml"
|
|
590
666
|
]
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
"diagnostics": {
|
|
670
|
+
"schemaVersion": 1,
|
|
671
|
+
"processExecution": {
|
|
672
|
+
"adapters": [
|
|
673
|
+
"command",
|
|
674
|
+
"codex-exec"
|
|
596
675
|
],
|
|
597
|
-
"
|
|
598
|
-
|
|
599
|
-
|
|
676
|
+
"supervisor": "foreground",
|
|
677
|
+
"launch": "explicit-plan",
|
|
678
|
+
"collect": "exact-attempt-receipt",
|
|
679
|
+
"cancellation": "original-owner-direct-child",
|
|
680
|
+
"verification": "independent-required",
|
|
681
|
+
"scheduling": "none"
|
|
682
|
+
},
|
|
683
|
+
"runtimeMinimums": {
|
|
684
|
+
"node": "20.0.0",
|
|
685
|
+
"python": "3.11"
|
|
686
|
+
},
|
|
687
|
+
"integrations": {
|
|
688
|
+
"claude-code": {
|
|
689
|
+
"instructionFile": "CLAUDE.md",
|
|
690
|
+
"execution": "host-managed",
|
|
691
|
+
"lifecycleEvents": "not-integrated"
|
|
692
|
+
},
|
|
693
|
+
"gemini-cli": {
|
|
694
|
+
"instructionFile": "GEMINI.md",
|
|
695
|
+
"execution": "host-managed",
|
|
696
|
+
"lifecycleEvents": "not-integrated"
|
|
697
|
+
},
|
|
698
|
+
"antigravity": {
|
|
699
|
+
"instructionFile": "GEMINI.md",
|
|
700
|
+
"execution": "host-managed",
|
|
701
|
+
"lifecycleEvents": "not-integrated"
|
|
702
|
+
},
|
|
703
|
+
"cursor": {
|
|
704
|
+
"instructionFile": ".cursor/rules/bmad-plus.mdc",
|
|
705
|
+
"execution": "host-managed",
|
|
706
|
+
"lifecycleEvents": "not-integrated"
|
|
707
|
+
},
|
|
708
|
+
"codex-cli": {
|
|
709
|
+
"instructionFile": ".codex/AGENTS.md",
|
|
710
|
+
"execution": "host-managed",
|
|
711
|
+
"lifecycleEvents": "not-integrated"
|
|
712
|
+
},
|
|
713
|
+
"opencode": {
|
|
714
|
+
"instructionFile": ".opencode/AGENTS.md",
|
|
715
|
+
"execution": "host-managed",
|
|
716
|
+
"lifecycleEvents": "not-integrated"
|
|
717
|
+
},
|
|
718
|
+
"aider": {
|
|
719
|
+
"instructionFile": "CONVENTIONS.md",
|
|
720
|
+
"execution": "host-managed",
|
|
721
|
+
"lifecycleEvents": "not-integrated"
|
|
722
|
+
}
|
|
600
723
|
}
|
|
601
724
|
},
|
|
602
725
|
"targets": {
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/** Opt-in verification of an existing pack environment. Never installs a runtime. */
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { MIN_PYTHON, venvPython, defaultRunner } = require('./python-provision');
|
|
5
|
+
|
|
6
|
+
const TIMEOUT_MS = 15_000;
|
|
7
|
+
const MAX_BUFFER = 64 * 1024;
|
|
8
|
+
const MODULE_NAME = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
9
|
+
|
|
10
|
+
// -I excludes project/PYTHONPATH imports; -B avoids bytecode writes. Importing
|
|
11
|
+
// installed dependencies still executes their code, so callers must opt in.
|
|
12
|
+
const PROBE = [
|
|
13
|
+
'import contextlib, importlib, json, os, sys',
|
|
14
|
+
'expected, minimum, names = sys.argv[1], json.loads(sys.argv[2]), json.loads(sys.argv[3])',
|
|
15
|
+
'normalize = lambda value: os.path.normcase(os.path.realpath(value))',
|
|
16
|
+
'prefix, base = normalize(sys.prefix), normalize(sys.base_prefix)',
|
|
17
|
+
'version = list(sys.version_info[:3])',
|
|
18
|
+
'checks = [{"name": name, "status": "not-verified"} for name in names]',
|
|
19
|
+
'if prefix == normalize(expected) and prefix != base and version >= minimum:',
|
|
20
|
+
' with open(os.devnull, "w", encoding="utf-8") as sink:',
|
|
21
|
+
' with contextlib.redirect_stdout(sink), contextlib.redirect_stderr(sink):',
|
|
22
|
+
' for check in checks:',
|
|
23
|
+
' try:',
|
|
24
|
+
' importlib.import_module(check["name"])',
|
|
25
|
+
' check["status"] = "verified"',
|
|
26
|
+
' except BaseException:',
|
|
27
|
+
' check["status"] = "failed"',
|
|
28
|
+
'print(json.dumps({"schemaVersion": 1, "prefix": prefix, "basePrefix": base, "version": version, "modules": checks}))',
|
|
29
|
+
].join('\n');
|
|
30
|
+
|
|
31
|
+
function minimumParts(minimum) {
|
|
32
|
+
if (typeof minimum !== 'string' || !/^\d+\.\d+(?:\.\d+)?$/.test(minimum)) return null;
|
|
33
|
+
const parts = minimum.split('.').map(Number);
|
|
34
|
+
if (parts.length === 2) parts.push(0);
|
|
35
|
+
return parts[0] > 0 && parts.every((part) => Number.isSafeInteger(part) && part >= 0)
|
|
36
|
+
? parts
|
|
37
|
+
: null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizedPath(value) {
|
|
41
|
+
const resolved = path.resolve(value);
|
|
42
|
+
return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function environmentPaths(projectDir, packId) {
|
|
46
|
+
const directory = path.join(projectDir, '.bmad', 'venv', packId);
|
|
47
|
+
const executable = venvPython(directory);
|
|
48
|
+
const parent = path.dirname(executable);
|
|
49
|
+
let cursor = path.parse(parent).root;
|
|
50
|
+
for (const component of parent.slice(cursor.length).split(path.sep)) {
|
|
51
|
+
cursor = path.join(cursor, component);
|
|
52
|
+
const stat = fs.lstatSync(cursor);
|
|
53
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) return null;
|
|
54
|
+
}
|
|
55
|
+
const config = fs.lstatSync(path.join(directory, 'pyvenv.cfg'));
|
|
56
|
+
if (config.isSymbolicLink() || !config.isFile()) return null;
|
|
57
|
+
const binary = fs.lstatSync(executable);
|
|
58
|
+
if (binary.isSymbolicLink()) {
|
|
59
|
+
// A Unix venv normally links bin/python to its base interpreter. Keep that
|
|
60
|
+
// executable path: resolving it before invocation would lose the venv.
|
|
61
|
+
if (process.platform === 'win32' || !fs.statSync(executable).isFile()) return null;
|
|
62
|
+
} else if (!binary.isFile()) return null;
|
|
63
|
+
return { directory, executable };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Verify the actual prefix, version and declared imports in .bmad/venv/<packId>.
|
|
68
|
+
* One bounded interpreter invocation; no PATH fallback, repair or provisioning.
|
|
69
|
+
*/
|
|
70
|
+
function probePythonRuntime({
|
|
71
|
+
projectDir,
|
|
72
|
+
packId,
|
|
73
|
+
minimum = `${MIN_PYTHON.major}.${MIN_PYTHON.minor}`,
|
|
74
|
+
verifyModules = [],
|
|
75
|
+
runner = defaultRunner,
|
|
76
|
+
} = {}) {
|
|
77
|
+
const started = Date.now();
|
|
78
|
+
const floor = minimumParts(minimum);
|
|
79
|
+
const validNames =
|
|
80
|
+
Array.isArray(verifyModules) &&
|
|
81
|
+
verifyModules.length <= 64 &&
|
|
82
|
+
[...verifyModules].every(
|
|
83
|
+
(name) => typeof name === 'string' && name.length <= 200 && MODULE_NAME.test(name)
|
|
84
|
+
);
|
|
85
|
+
const names = validNames ? [...new Set(verifyModules)] : [];
|
|
86
|
+
const finish = (
|
|
87
|
+
status,
|
|
88
|
+
message,
|
|
89
|
+
version = null,
|
|
90
|
+
modules = names.map((name) => ({ name, status: 'not-verified' }))
|
|
91
|
+
) => ({
|
|
92
|
+
status,
|
|
93
|
+
version,
|
|
94
|
+
imports: modules.some((entry) => entry.status === 'failed')
|
|
95
|
+
? 'failed'
|
|
96
|
+
: modules.length > 0 && modules.every((entry) => entry.status === 'verified')
|
|
97
|
+
? 'verified'
|
|
98
|
+
: 'not-verified',
|
|
99
|
+
modules,
|
|
100
|
+
message,
|
|
101
|
+
});
|
|
102
|
+
if (
|
|
103
|
+
typeof projectDir !== 'string' ||
|
|
104
|
+
!projectDir ||
|
|
105
|
+
projectDir.includes('\0') ||
|
|
106
|
+
typeof packId !== 'string' ||
|
|
107
|
+
!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(packId) ||
|
|
108
|
+
!floor ||
|
|
109
|
+
!validNames ||
|
|
110
|
+
typeof runner !== 'function'
|
|
111
|
+
) {
|
|
112
|
+
return finish('invalid', 'Python verification options are invalid.');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
projectDir = path.resolve(projectDir);
|
|
116
|
+
let environment;
|
|
117
|
+
try {
|
|
118
|
+
environment = environmentPaths(projectDir, packId);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return finish(
|
|
121
|
+
error.code === 'ENOENT' ? 'missing' : 'unavailable',
|
|
122
|
+
error.code === 'ENOENT'
|
|
123
|
+
? 'The pack Python environment is missing or incomplete.'
|
|
124
|
+
: 'The pack Python environment cannot be inspected.'
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
if (!environment)
|
|
128
|
+
return finish(
|
|
129
|
+
'invalid',
|
|
130
|
+
'The pack Python environment contains an unsafe path or invalid file.'
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const timeout = TIMEOUT_MS - (Date.now() - started);
|
|
134
|
+
if (timeout <= 0) return finish('unavailable', 'Python verification exceeded its time limit.');
|
|
135
|
+
let result;
|
|
136
|
+
try {
|
|
137
|
+
result = runner(
|
|
138
|
+
environment.executable,
|
|
139
|
+
[
|
|
140
|
+
'-I',
|
|
141
|
+
'-B',
|
|
142
|
+
'-c',
|
|
143
|
+
PROBE,
|
|
144
|
+
environment.directory,
|
|
145
|
+
JSON.stringify(floor),
|
|
146
|
+
JSON.stringify(names),
|
|
147
|
+
],
|
|
148
|
+
{
|
|
149
|
+
cwd: projectDir,
|
|
150
|
+
encoding: 'utf8',
|
|
151
|
+
shell: false,
|
|
152
|
+
windowsHide: true,
|
|
153
|
+
timeout,
|
|
154
|
+
killSignal: 'SIGKILL',
|
|
155
|
+
maxBuffer: MAX_BUFFER,
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
} catch {
|
|
159
|
+
return finish('unavailable', 'The pack Python interpreter could not complete verification.');
|
|
160
|
+
}
|
|
161
|
+
if (Date.now() - started >= TIMEOUT_MS || !result || result.error || result.status !== 0) {
|
|
162
|
+
return finish(
|
|
163
|
+
'unavailable',
|
|
164
|
+
'The pack Python interpreter could not complete verification within its limits.'
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
let evidence;
|
|
168
|
+
try {
|
|
169
|
+
if (typeof result.stdout !== 'string' || Buffer.byteLength(result.stdout, 'utf8') > MAX_BUFFER)
|
|
170
|
+
throw new Error();
|
|
171
|
+
evidence = JSON.parse(result.stdout);
|
|
172
|
+
} catch {
|
|
173
|
+
return finish('invalid', 'The Python interpreter returned invalid verification evidence.');
|
|
174
|
+
}
|
|
175
|
+
if (
|
|
176
|
+
!evidence ||
|
|
177
|
+
evidence.schemaVersion !== 1 ||
|
|
178
|
+
typeof evidence.prefix !== 'string' ||
|
|
179
|
+
!path.isAbsolute(evidence.prefix) ||
|
|
180
|
+
typeof evidence.basePrefix !== 'string' ||
|
|
181
|
+
!path.isAbsolute(evidence.basePrefix) ||
|
|
182
|
+
!Array.isArray(evidence.version) ||
|
|
183
|
+
evidence.version.length !== 3 ||
|
|
184
|
+
evidence.version.some((part) => !Number.isSafeInteger(part) || part < 0) ||
|
|
185
|
+
evidence.version[0] === 0 ||
|
|
186
|
+
!Array.isArray(evidence.modules) ||
|
|
187
|
+
evidence.modules.length !== names.length ||
|
|
188
|
+
evidence.modules.some(
|
|
189
|
+
(entry, index) =>
|
|
190
|
+
!entry ||
|
|
191
|
+
entry.name !== names[index] ||
|
|
192
|
+
!['verified', 'failed', 'not-verified'].includes(entry.status)
|
|
193
|
+
)
|
|
194
|
+
) {
|
|
195
|
+
return finish('invalid', 'The Python interpreter returned invalid verification evidence.');
|
|
196
|
+
}
|
|
197
|
+
if (
|
|
198
|
+
normalizedPath(evidence.prefix) !== normalizedPath(environment.directory) ||
|
|
199
|
+
normalizedPath(evidence.prefix) === normalizedPath(evidence.basePrefix)
|
|
200
|
+
) {
|
|
201
|
+
return finish(
|
|
202
|
+
'invalid',
|
|
203
|
+
'The interpreter is not running from the expected pack virtual environment.'
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
const version = evidence.version.join('.');
|
|
207
|
+
const difference = evidence.version.findIndex((part, index) => part !== floor[index]);
|
|
208
|
+
if (difference >= 0 && evidence.version[difference] < floor[difference]) {
|
|
209
|
+
return finish('unsupported', 'The pack Python version is below the required minimum.', version);
|
|
210
|
+
}
|
|
211
|
+
if (evidence.modules.some((entry) => entry.status === 'not-verified')) {
|
|
212
|
+
return finish('invalid', 'The interpreter did not verify every declared module.', version);
|
|
213
|
+
}
|
|
214
|
+
const modules = evidence.modules.map(({ name, status }) => ({ name, status }));
|
|
215
|
+
if (modules.some((entry) => entry.status === 'failed')) {
|
|
216
|
+
return finish(
|
|
217
|
+
'unavailable',
|
|
218
|
+
'One or more declared modules could not be imported in the pack environment.',
|
|
219
|
+
version,
|
|
220
|
+
modules
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return finish(
|
|
224
|
+
'verified',
|
|
225
|
+
modules.length
|
|
226
|
+
? 'The pack Python version and declared imports are verified.'
|
|
227
|
+
: 'The pack Python version is verified; no import checks were requested.',
|
|
228
|
+
version,
|
|
229
|
+
modules
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
module.exports = { probePythonRuntime };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* BMAD+ Unified Python Provisioning (Pillar 2 — kills split-brain / broken-on-arrival)
|
|
3
3
|
*
|
|
4
4
|
* Provisions the Python runtime a pack declares in the registry
|
|
5
|
-
* (`runtime: [node, python]`
|
|
5
|
+
* (`runtime: [node, python]`). One code path for every Python pack:
|
|
6
6
|
* 1. detect a Python >= 3.11 interpreter (or a uv that can fetch one)
|
|
7
7
|
* 2. choose a provisioner: uv > pipx > venv+pip
|
|
8
8
|
* 3. create an isolated env under a given dir (e.g. <project>/.bmad/venv)
|
|
@@ -386,7 +386,7 @@ function verifyEntry({ envDir, entryPath, entryArgs = ['--help'], runner = defau
|
|
|
386
386
|
/**
|
|
387
387
|
* Full pipeline: detect → choose provisioner → create env → install → verify.
|
|
388
388
|
* This is the single call site the installer uses for ANY pack whose registry
|
|
389
|
-
* runtime includes "python"
|
|
389
|
+
* runtime includes "python".
|
|
390
390
|
*
|
|
391
391
|
* Never throws for environmental problems — always returns
|
|
392
392
|
* { ok, tool, messages[] } so the installer can degrade gracefully and print
|