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.
Files changed (135) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +113 -487
  3. package/SECURITY.md +71 -0
  4. package/THIRD-PARTY-LICENSES.md +349 -0
  5. package/osint-agent-package/README.md +1 -1
  6. package/package.json +14 -3
  7. package/readme-international/README.de.md +18 -8
  8. package/readme-international/README.es.md +19 -9
  9. package/readme-international/README.fr.md +18 -8
  10. package/src/bmad-plus/agents/agent-architect-dev/SKILL.md +11 -13
  11. package/src/bmad-plus/agents/agent-orchestrator/SKILL.md +148 -9
  12. package/src/bmad-plus/agents/agent-quality/SKILL.md +41 -11
  13. package/src/bmad-plus/data/role-triggers.yaml +19 -0
  14. package/src/bmad-plus/module-help.csv +1 -0
  15. package/src/bmad-plus/module.yaml +1 -0
  16. package/src/bmad-plus/packs/pack-dev-studio/README.md +133 -141
  17. package/src/bmad-plus/packs/pack-dev-studio/SKILL.md +49 -0
  18. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/analyst-agent.md +35 -60
  19. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/document-project.md +59 -59
  20. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/domain-research.md +55 -93
  21. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/market-research.md +58 -93
  22. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/prfaq.md +55 -132
  23. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/product-brief.md +63 -78
  24. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/tech-writer-agent.md +54 -69
  25. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/technical-research.md +54 -93
  26. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/architect-agent.md +32 -60
  27. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-architecture.md +67 -71
  28. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-epics-stories.md +61 -90
  29. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/generate-project-context.md +56 -78
  30. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/implementation-readiness.md +55 -88
  31. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01-init.md +20 -153
  32. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01b-continue.md +20 -173
  33. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-02-context.md +14 -220
  34. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-03-starter.md +20 -329
  35. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-04-decisions.md +15 -314
  36. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-05-patterns.md +15 -355
  37. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-06-structure.md +15 -375
  38. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-07-validation.md +14 -357
  39. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-08-complete.md +13 -78
  40. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/checkpoint-preview.md +52 -65
  41. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-01-gather-context.md +14 -81
  42. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-02-review.md +14 -31
  43. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-03-triage.md +14 -45
  44. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-04-present.md +13 -128
  45. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +61 -87
  46. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/correct-course.md +55 -298
  47. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/create-story.md +54 -426
  48. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-agent.md +48 -69
  49. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story-checklist.md +24 -80
  50. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +64 -482
  51. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/investigate.md +50 -184
  52. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/qa-e2e-tests.md +57 -173
  53. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/quick-dev.md +56 -108
  54. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/retrospective.md +54 -1509
  55. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-planning.md +54 -296
  56. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-status.md +41 -283
  57. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-prd.md +58 -18
  58. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-ux-design.md +103 -72
  59. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/edit-prd.md +55 -27
  60. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/pm-agent.md +34 -60
  61. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/prd.md +46 -87
  62. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/steps/step-01-init.md +10 -0
  63. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/ux-designer-agent.md +30 -60
  64. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/validate-prd.md +57 -27
  65. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/advanced-elicitation.md +47 -138
  66. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/adversarial-review.md +48 -34
  67. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/bmad-help.md +51 -68
  68. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/brainstorming.md +46 -3
  69. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/customize.md +68 -109
  70. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/distillator.md +53 -174
  71. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/edge-case-hunter.md +39 -53
  72. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-prose.md +45 -83
  73. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-structure.md +45 -176
  74. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/index-docs.md +45 -63
  75. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/party-mode.md +53 -124
  76. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/shard-doc.md +44 -100
  77. package/src/bmad-plus/packs/pack-dev-studio/dev-studio-orchestrator.md +56 -116
  78. package/src/bmad-plus/packs/pack-dev-studio/shared/architecture-decision-template.md +49 -12
  79. package/src/bmad-plus/packs/pack-dev-studio/shared/bwml-spec.md +51 -328
  80. package/src/bmad-plus/packs/pack-dev-studio/shared/catalog.json +489 -0
  81. package/src/bmad-plus/packs/pack-dev-studio/shared/execution.md +69 -0
  82. package/src/bmad-plus/packs/pack-dev-studio/shared/module-help.csv +39 -32
  83. package/src/bmad-plus/packs/pack-dev-studio/upstream-sync.yaml +85 -14
  84. package/src/bmad-plus/packs/pack-memory/README.md +35 -4
  85. package/src/bmad-plus/packs/pack-memory/memory-orchestrator.md +33 -6
  86. package/src/bmad-plus/packs/pack-memory/shared/karpathy-guardrails.md +3 -3
  87. package/src/bmad-plus/packs/pack-memory/shared/memory-protocol.md +27 -3
  88. package/src/bmad-plus/packs/pack-memory/zecher-agent.md +18 -2
  89. package/src/bmad-plus/packs/pack-seo/SKILL.md +27 -1
  90. package/src/bmad-plus/packs/pack-seo/seo-chief.md +16 -1
  91. package/src/bmad-plus/packs/pack-seo/seo-judge.md +12 -0
  92. package/src/bmad-plus/packs/pack-seo/seo-scout.md +12 -0
  93. package/src/bmad-plus/skills/bmad-plus-autopilot/SKILL.md +49 -12
  94. package/src/bmad-plus/skills/bmad-plus-parallel/SKILL.md +17 -3
  95. package/src/bmad-plus/skills/bmad-plus-sync/SKILL.md +76 -65
  96. package/src/bmad-plus/skills/bmad-plus-uat/SKILL.md +144 -0
  97. package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-results.schema.json +60 -0
  98. package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-spec.schema.json +121 -0
  99. package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-triage.schema.json +60 -0
  100. package/src/bmad-plus/skills/bmad-plus-uat/template/page.html +552 -0
  101. package/src/bmad-plus/skills/bmad-plus-uat/template/strings.json +362 -0
  102. package/src/bmad-plus/skills/dev-studio/SKILL.md +19 -0
  103. package/tools/build/check-install-contract.js +367 -17
  104. package/tools/build/generate.js +229 -40
  105. package/tools/build/generated-adapters/.codex/AGENTS.md +1 -1
  106. package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +1 -1
  107. package/tools/build/generated-adapters/.opencode/AGENTS.md +1 -1
  108. package/tools/build/generated-adapters/AGENTS.md +1 -1
  109. package/tools/build/generated-adapters/CLAUDE.md +1 -1
  110. package/tools/build/generated-adapters/CONVENTIONS.md +1 -1
  111. package/tools/build/generated-adapters/GEMINI.md +1 -1
  112. package/tools/build/pack-delivery.js +78 -0
  113. package/tools/cli/bmad-plus-cli.js +15 -12
  114. package/tools/cli/commands/doctor.js +50 -189
  115. package/tools/cli/commands/install.js +22 -3
  116. package/tools/cli/commands/memory-journal-cmd.js +119 -19
  117. package/tools/cli/commands/nexus.js +111 -0
  118. package/tools/cli/commands/studio.js +68 -0
  119. package/tools/cli/commands/uat.js +389 -0
  120. package/tools/cli/lib/README-memory-journal.md +19 -8
  121. package/tools/cli/lib/installation-health.js +366 -0
  122. package/tools/cli/lib/memory-journal.js +0 -0
  123. package/tools/cli/lib/memory-outcomes.js +293 -0
  124. package/tools/cli/lib/memory-store.js +139 -0
  125. package/tools/cli/lib/nexus-process.js +377 -0
  126. package/tools/cli/lib/nexus.js +1532 -0
  127. package/tools/cli/lib/pack-copy.js +39 -11
  128. package/tools/cli/lib/packs.js +134 -11
  129. package/tools/cli/lib/python-health.js +233 -0
  130. package/tools/cli/lib/python-provision.js +2 -2
  131. package/tools/cli/lib/studio.js +310 -0
  132. package/tools/cli/lib/uat.js +869 -0
  133. package/tools/maintain/upstream-candidate.js +456 -0
  134. package/tools/release/publication-content.js +903 -0
  135. 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 { stat = fs.lstatSync(source); } catch (error) {
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()) walk(path.join(source, name), path.join(target, name));
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))) result.copiedAgents++;
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))) result.copiedSkills++;
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))) result.copiedFiles++;
44
+ if (walk(path.join(bmadSrc, 'data', name), path.join(targetDataDir, name)))
45
+ result.copiedFiles++;
37
46
  }
38
- if (pack.externalPackage && walk(path.join(projectRoot, pack.externalPackage, 'skills'), targetAgentsDir)) result.copiedSkills++;
39
- if (pack.packDir && walk(path.join(bmadSrc, pack.packSrcDir || 'agents', pack.packDir), path.join(targetAgentsDir, pack.packDir))) {
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({ source, target, projectDir, inventory, previousInventory = {}, onConflict = () => {} }) {
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()) throw new Error('Pack source is not a regular file: ' + source);
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) safeTarget(projectDir, path.relative(projectDir, target).split(path.sep).join('/'));
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
  }
@@ -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.13.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": 3,
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
- "memory": {
593
- "requirements": [
594
- "mcp-server",
595
- "requirements.txt"
667
+ }
668
+ },
669
+ "diagnostics": {
670
+ "schemaVersion": 1,
671
+ "processExecution": {
672
+ "adapters": [
673
+ "command",
674
+ "codex-exec"
596
675
  ],
597
- "verifyModules": [
598
- "chromadb"
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]` → seo, memory). One code path for every pack:
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" (seo, memory, …).
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