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
|
@@ -1,205 +1,66 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BMAD+ Doctor Command
|
|
3
|
-
* Checks installation integrity and reports issues
|
|
4
|
-
*
|
|
5
|
-
* Author: Laurent Rochetta
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const path = require('node:path');
|
|
9
|
-
const fs = require('node:fs');
|
|
1
|
+
/** Human and JSON output share one report; runtime execution is explicitly opt-in. */
|
|
10
2
|
const clack = require('@clack/prompts');
|
|
11
3
|
const pc = require('picocolors');
|
|
12
|
-
const {
|
|
13
|
-
const { EXPECTED_AGENTS } = require('../lib/packs');
|
|
14
|
-
const { readInstallManifest } = require('../lib/install-manifest');
|
|
4
|
+
const { collectInstallationHealth } = require('../lib/installation-health');
|
|
15
5
|
|
|
16
6
|
module.exports = {
|
|
17
7
|
command: 'doctor',
|
|
18
|
-
description: 'Check BMAD+ installation integrity',
|
|
8
|
+
description: 'Check BMAD+ installation integrity and declared capabilities',
|
|
19
9
|
options: [
|
|
20
10
|
['-d, --directory <path>', 'Project directory (default: current directory)'],
|
|
21
11
|
['-l, --lang <code>', 'Language code: en, fr, es, de, pt-br, ru, zh, he, ja, it'],
|
|
12
|
+
['--json', 'Print the diagnostic report as JSON (read-only unless a probe is requested)'],
|
|
13
|
+
['--verify-python', 'Execute installed Python interpreters to verify versions and imports'],
|
|
22
14
|
],
|
|
23
|
-
action: async (options) => {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
let warnings = 0;
|
|
32
|
-
let errors = 0;
|
|
33
|
-
|
|
34
|
-
// ── Check 1: Installation manifest ──
|
|
35
|
-
checks++;
|
|
36
|
-
const manifestPath = path.join(projectDir, '_bmad', '.bmad-plus-install.json');
|
|
37
|
-
if (!fs.existsSync(manifestPath)) {
|
|
38
|
-
clack.log.error('❌ BMAD+ is not installed in this directory');
|
|
39
|
-
clack.outro(pc.red('Run `npx bmad-plus install` first.'));
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
let manifest;
|
|
44
|
-
try {
|
|
45
|
-
manifest = readInstallManifest(manifestPath);
|
|
46
|
-
} catch (err) {
|
|
47
|
-
const i = t(options.lang || 'en');
|
|
48
|
-
clack.log.error(i.manifest_invalid(err.message));
|
|
49
|
-
clack.outro(pc.red(i.manifest_repair));
|
|
50
|
-
process.exitCode = 1;
|
|
15
|
+
action: async (options = {}) => {
|
|
16
|
+
const report = collectInstallationHealth({
|
|
17
|
+
projectDir: options.directory,
|
|
18
|
+
verifyPython: Boolean(options.verifyPython),
|
|
19
|
+
});
|
|
20
|
+
process.exitCode = report.summary.errors ? 1 : 0;
|
|
21
|
+
if (options.json) {
|
|
22
|
+
console.log(JSON.stringify(report, null, 2));
|
|
51
23
|
return;
|
|
52
24
|
}
|
|
53
|
-
clack.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
25
|
+
clack.intro(pc.bgBlue(pc.white(' BMAD+ Doctor v' + report.versions.cli + ' ')));
|
|
26
|
+
for (const entry of report.checks.filter(
|
|
27
|
+
(item) => item.status !== 'pass' || item.code === 'manifest.valid'
|
|
28
|
+
)) {
|
|
29
|
+
const log =
|
|
30
|
+
entry.status === 'error'
|
|
31
|
+
? clack.log.error
|
|
32
|
+
: entry.status === 'warning'
|
|
33
|
+
? clack.log.warn
|
|
34
|
+
: clack.log.success;
|
|
35
|
+
log(
|
|
36
|
+
entry.message +
|
|
37
|
+
(entry.path && !entry.message.includes(entry.path) ? ' (' + entry.path + ')' : '')
|
|
38
|
+
);
|
|
65
39
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
for (const pack of (manifest.packs || ['core'])) {
|
|
79
|
-
const entry = EXPECTED_AGENTS[pack];
|
|
80
|
-
if (!entry) continue;
|
|
81
|
-
|
|
82
|
-
// Check individual agent directories
|
|
83
|
-
for (const agent of (entry.agents || [])) {
|
|
84
|
-
checks++;
|
|
85
|
-
const agentPath = path.join(agentsDir, agent);
|
|
86
|
-
if (fs.existsSync(agentPath)) {
|
|
87
|
-
passed++;
|
|
88
|
-
} else {
|
|
89
|
-
clack.log.warn(`⚠️ Missing agent: ${agent} (pack: ${pack})`);
|
|
90
|
-
warnings++;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Check pack directory if applicable
|
|
95
|
-
if (entry.packDir) {
|
|
96
|
-
checks++;
|
|
97
|
-
const packPath = path.join(agentsDir, entry.packDir);
|
|
98
|
-
if (fs.existsSync(packPath)) {
|
|
99
|
-
passed++;
|
|
100
|
-
|
|
101
|
-
// These packs bundle their agents as files inside packDir — verify
|
|
102
|
-
// them there (not as loose directories, which would falsely warn).
|
|
103
|
-
for (const agentFile of (entry.packAgents || [])) {
|
|
104
|
-
checks++;
|
|
105
|
-
if (fs.existsSync(path.join(packPath, agentFile))) {
|
|
106
|
-
passed++;
|
|
107
|
-
} else {
|
|
108
|
-
clack.log.warn(`⚠️ Missing agent: ${agentFile} (pack: ${pack})`);
|
|
109
|
-
warnings++;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
clack.log.warn(`⚠️ Missing pack directory: ${entry.packDir} (pack: ${pack})`);
|
|
114
|
-
warnings++;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
clack.log.error('❌ No .agents/skills/ directory found');
|
|
120
|
-
errors++;
|
|
40
|
+
if (report.versions.installed) {
|
|
41
|
+
clack.note(
|
|
42
|
+
[
|
|
43
|
+
report.summary.passed + '/' + report.checks.length + ' checks passed',
|
|
44
|
+
report.summary.errors + ' error(s), ' + report.summary.warnings + ' warning(s)',
|
|
45
|
+
'Installed: v' + report.versions.installed + ' | Executing CLI: v' + report.versions.cli,
|
|
46
|
+
'Ownership inventory: ' + report.inventory.status,
|
|
47
|
+
'Integrations use host-managed execution; host capabilities were not probed.',
|
|
48
|
+
'Optional Nexus process execution requires an explicit plan; executable availability was not probed.',
|
|
49
|
+
].join('\n'),
|
|
50
|
+
'BMAD+ Health Report'
|
|
51
|
+
);
|
|
121
52
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// ── Check 5: Module config ──
|
|
135
|
-
checks++;
|
|
136
|
-
const moduleYaml = path.join(projectDir, '_bmad', 'module.yaml');
|
|
137
|
-
if (fs.existsSync(moduleYaml)) {
|
|
138
|
-
clack.log.success('✅ module.yaml present');
|
|
139
|
-
passed++;
|
|
140
|
-
} else {
|
|
141
|
-
clack.log.warn('⚠️ module.yaml missing');
|
|
142
|
-
warnings++;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// ── Check 6: IDE configs ──
|
|
146
|
-
checks++;
|
|
147
|
-
const ideFiles = ['CLAUDE.md', 'GEMINI.md', 'AGENTS.md'].filter(f =>
|
|
148
|
-
fs.existsSync(path.join(projectDir, f))
|
|
53
|
+
clack.log.info(
|
|
54
|
+
'Published version is not checked here. Use `bmad-plus update-check --json` to check npm.'
|
|
55
|
+
);
|
|
56
|
+
clack.outro(
|
|
57
|
+
report.status === 'ok'
|
|
58
|
+
? pc.green('Installation checks passed.')
|
|
59
|
+
: pc.yellow(
|
|
60
|
+
options.verifyPython
|
|
61
|
+
? 'Review the reported evidence. Python import probes ran; no provisioning was requested.'
|
|
62
|
+
: 'Review the reported evidence before changing files. Doctor did not modify the project.'
|
|
63
|
+
)
|
|
149
64
|
);
|
|
150
|
-
if (ideFiles.length > 0) {
|
|
151
|
-
clack.log.success(`✅ ${ideFiles.length} IDE config(s): ${ideFiles.join(', ')}`);
|
|
152
|
-
passed++;
|
|
153
|
-
} else {
|
|
154
|
-
clack.log.warn('⚠️ No IDE config files found');
|
|
155
|
-
warnings++;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ── Check 7: Output directories ──
|
|
159
|
-
checks++;
|
|
160
|
-
const outputDir = path.join(projectDir, '_bmad-output');
|
|
161
|
-
if (fs.existsSync(outputDir)) {
|
|
162
|
-
clack.log.success('✅ _bmad-output/ directory exists');
|
|
163
|
-
passed++;
|
|
164
|
-
} else {
|
|
165
|
-
clack.log.warn('⚠️ _bmad-output/ not found');
|
|
166
|
-
warnings++;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// ── Check 8: Skills integrity (SKILL.md exists in pack dirs) ──
|
|
170
|
-
if (manifest.packs && manifest.packs.includes('seo')) {
|
|
171
|
-
checks++;
|
|
172
|
-
const seoSkill = path.join(agentsDir, 'pack-seo', 'SKILL.md');
|
|
173
|
-
if (fs.existsSync(seoSkill)) {
|
|
174
|
-
clack.log.success('✅ SEO Engine SKILL.md present');
|
|
175
|
-
passed++;
|
|
176
|
-
} else {
|
|
177
|
-
clack.log.error('❌ SEO Engine SKILL.md missing — /seo audit will not work');
|
|
178
|
-
errors++;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ── Summary ──
|
|
183
|
-
const summaryColor = errors > 0 ? pc.red : warnings > 0 ? pc.yellow : pc.green;
|
|
184
|
-
const summaryIcon = errors > 0 ? '❌' : warnings > 0 ? '⚠️' : '✅';
|
|
185
|
-
|
|
186
|
-
clack.note([
|
|
187
|
-
`${summaryIcon} ${passed}/${checks} checks passed`,
|
|
188
|
-
errors > 0 ? `❌ ${errors} error(s)` : null,
|
|
189
|
-
warnings > 0 ? `⚠️ ${warnings} warning(s)` : null,
|
|
190
|
-
'',
|
|
191
|
-
`📦 Version: v${manifest.version}`,
|
|
192
|
-
`📅 Installed: ${manifest.installed.split('T')[0]}`,
|
|
193
|
-
`🗣️ Language: ${manifest.language || 'N/A'}`,
|
|
194
|
-
`📦 Packs: ${(manifest.packs || ['core']).join(', ')}`,
|
|
195
|
-
].filter(Boolean).join('\n'), '🩺 BMAD+ Health Report');
|
|
196
|
-
|
|
197
|
-
clack.outro(summaryColor(
|
|
198
|
-
errors > 0
|
|
199
|
-
? 'Issues found — run `npx bmad-plus install` to fix'
|
|
200
|
-
: warnings > 0
|
|
201
|
-
? 'Minor issues found — consider running `npx bmad-plus update`'
|
|
202
|
-
: 'Everything looks great! 🎉'
|
|
203
|
-
));
|
|
204
65
|
},
|
|
205
66
|
};
|
|
@@ -35,7 +35,7 @@ module.exports = {
|
|
|
35
35
|
['-l, --lang <code>', 'Language code: en, fr, es, de, pt-br, ru, zh, he, ja, it (overrides auto-detection)'],
|
|
36
36
|
['--tools <tools>', 'Comma-separated IDE IDs, all, or none (default: auto-detect)'],
|
|
37
37
|
['--mode <mode>', 'Execution mode: manual, autopilot, hybrid (default: manual)'],
|
|
38
|
-
['--provision-python', 'Provision
|
|
38
|
+
['--provision-python', 'Provision isolated environments for selected Python packs'],
|
|
39
39
|
],
|
|
40
40
|
action: async (options) => {
|
|
41
41
|
// A closed/piped stdin cannot answer prompts. Require explicit defaults
|
|
@@ -180,6 +180,7 @@ module.exports = {
|
|
|
180
180
|
let userName = process.env.USER || process.env.USERNAME || 'Developer';
|
|
181
181
|
let commLang = 'French';
|
|
182
182
|
let execMode = options.mode || 'manual';
|
|
183
|
+
let uatEnvironment = '';
|
|
183
184
|
|
|
184
185
|
if (!options.yes) {
|
|
185
186
|
const userConfig = await clack.group({
|
|
@@ -192,6 +193,13 @@ module.exports = {
|
|
|
192
193
|
message: i.comm_language,
|
|
193
194
|
options: getCommLanguageOptions(),
|
|
194
195
|
}),
|
|
196
|
+
uatEnvironment: () => clack.text({
|
|
197
|
+
message:
|
|
198
|
+
i.uat_environment ||
|
|
199
|
+
'Test environment for human acceptance recipes (URL, blank if none)',
|
|
200
|
+
placeholder: 'https://demo.example.com',
|
|
201
|
+
defaultValue: '',
|
|
202
|
+
}),
|
|
195
203
|
execMode: () => clack.select({
|
|
196
204
|
message: i.exec_mode,
|
|
197
205
|
options: [
|
|
@@ -215,6 +223,7 @@ module.exports = {
|
|
|
215
223
|
userName = validatedName;
|
|
216
224
|
commLang = userConfig.commLang;
|
|
217
225
|
execMode = options.mode || userConfig.execMode;
|
|
226
|
+
uatEnvironment = String(userConfig.uatEnvironment || '').trim();
|
|
218
227
|
}
|
|
219
228
|
|
|
220
229
|
const releaseLock = acquireProjectLock(projectDir);
|
|
@@ -366,7 +375,7 @@ module.exports = {
|
|
|
366
375
|
}
|
|
367
376
|
|
|
368
377
|
// ── Step 6: Create config.yaml ──
|
|
369
|
-
const configYaml = generateConfigYaml(userName, commLang, projectDir, execMode);
|
|
378
|
+
const configYaml = generateConfigYaml(userName, commLang, projectDir, execMode, uatEnvironment);
|
|
370
379
|
const configPath = path.join(targetBmadDir, 'config.yaml');
|
|
371
380
|
fs.writeFileSync(configPath, configYaml, 'utf8');
|
|
372
381
|
|
|
@@ -522,7 +531,7 @@ module.exports = {
|
|
|
522
531
|
_internal: { writeIDEConfigs, generateConfigYaml },
|
|
523
532
|
};
|
|
524
533
|
|
|
525
|
-
function generateConfigYaml(userName, language, projectDir, execMode = 'manual') {
|
|
534
|
+
function generateConfigYaml(userName, language, projectDir, execMode = 'manual', uatEnvironment = '') {
|
|
526
535
|
if (!['manual', 'autopilot', 'hybrid'].includes(execMode)) throw new Error('Unknown execution mode: ' + execMode);
|
|
527
536
|
return '# BMAD+ Project Configuration\n# Generated by bmad-plus install\n\n' + yaml.dump({
|
|
528
537
|
user_name: userName,
|
|
@@ -539,5 +548,15 @@ function generateConfigYaml(userName, language, projectDir, execMode = 'manual')
|
|
|
539
548
|
story: execMode === 'autopilot' ? 'notify_only' : 'require_approval',
|
|
540
549
|
delivery: 'require_approval',
|
|
541
550
|
},
|
|
551
|
+
// Human acceptance recipes. advisory: every delivery produces and offers its page and
|
|
552
|
+
// nothing is ever blocked. gate: the delivery waits for a passing "bmad-plus uat gate".
|
|
553
|
+
// off: no recipe, and the delivery report says so.
|
|
554
|
+
uat: {
|
|
555
|
+
mode: 'advisory',
|
|
556
|
+
granularity: 'delivery',
|
|
557
|
+
environment: { name: uatEnvironment ? 'TEST' : '', url: uatEnvironment || '' },
|
|
558
|
+
page_budget: { max_steps: 15, max_minutes: 30 },
|
|
559
|
+
dir: '_bmad-output/uat',
|
|
560
|
+
},
|
|
542
561
|
}, { lineWidth: -1, forceQuotes: true, quotingType: '"' });
|
|
543
562
|
}
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
const path = require('node:path');
|
|
33
33
|
const fs = require('node:fs');
|
|
34
34
|
const mj = require('../lib/memory-journal');
|
|
35
|
+
const store = require('../lib/memory-store');
|
|
36
|
+
const outcomes = require('../lib/memory-outcomes');
|
|
35
37
|
|
|
36
38
|
// ── Pattern score store ──────────────────────────────────────────────────────
|
|
37
39
|
// Lives next to the journal in the north-star scope (.bmad/memory/). Keyed by
|
|
@@ -63,9 +65,7 @@ function readScores(baseDir) {
|
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
function writeScores(baseDir, scores) {
|
|
66
|
-
|
|
67
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
68
|
-
fs.writeFileSync(file, JSON.stringify(scores, null, 2) + '\n', 'utf8');
|
|
68
|
+
store.writeJson(baseDir, SCORES_RELPATH, scores);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// ── Flag parsing helpers ─────────────────────────────────────────────────────
|
|
@@ -104,12 +104,30 @@ function truncate(text, max = 120) {
|
|
|
104
104
|
* @returns {object[]} ranked entries (same shape as memory-journal.recall)
|
|
105
105
|
*/
|
|
106
106
|
function runRecall(query, opts = {}) {
|
|
107
|
-
const {
|
|
107
|
+
const {
|
|
108
|
+
baseDir = process.cwd(),
|
|
109
|
+
scope = 'project',
|
|
110
|
+
portfolioDir = null,
|
|
111
|
+
limit = 8,
|
|
112
|
+
now = null,
|
|
113
|
+
json = false,
|
|
114
|
+
log = console.log,
|
|
115
|
+
ranking = 'lexical',
|
|
116
|
+
contextScope,
|
|
117
|
+
} = opts;
|
|
108
118
|
|
|
109
|
-
const results = mj.recall(query, {
|
|
119
|
+
const results = mj.recall(query, {
|
|
120
|
+
baseDir,
|
|
121
|
+
scope,
|
|
122
|
+
portfolioDir,
|
|
123
|
+
limit,
|
|
124
|
+
now,
|
|
125
|
+
ranking,
|
|
126
|
+
contextScope,
|
|
127
|
+
});
|
|
110
128
|
|
|
111
129
|
if (json) {
|
|
112
|
-
log(JSON.stringify({ query, scope, count: results.length, results }, null, 2));
|
|
130
|
+
log(JSON.stringify({ query, scope, ranking, count: results.length, results }, null, 2));
|
|
113
131
|
return results;
|
|
114
132
|
}
|
|
115
133
|
|
|
@@ -119,7 +137,8 @@ function runRecall(query, opts = {}) {
|
|
|
119
137
|
}
|
|
120
138
|
log(`mem recall: ${results.length} match(es) for "${query}" (scope: ${scope})`);
|
|
121
139
|
results.forEach((r, i) => {
|
|
122
|
-
const origin =
|
|
140
|
+
const origin =
|
|
141
|
+
r.kind === 'event' ? `event ${r.source}#${r.ref}` : `note ${r.source} § ${r.ref}`;
|
|
123
142
|
log(` ${i + 1}. [${r.score.toFixed(3)}] ${origin}`);
|
|
124
143
|
log(` ${truncate(r.text)}`);
|
|
125
144
|
});
|
|
@@ -134,7 +153,18 @@ function runRecall(query, opts = {}) {
|
|
|
134
153
|
* @returns {object} the persisted event
|
|
135
154
|
*/
|
|
136
155
|
function runWrite(opts = {}) {
|
|
137
|
-
const {
|
|
156
|
+
const {
|
|
157
|
+
baseDir = process.cwd(),
|
|
158
|
+
agent,
|
|
159
|
+
cli = null,
|
|
160
|
+
model = null,
|
|
161
|
+
task = '',
|
|
162
|
+
outcome = undefined,
|
|
163
|
+
signals = {},
|
|
164
|
+
now,
|
|
165
|
+
json = false,
|
|
166
|
+
log = console.log,
|
|
167
|
+
} = opts;
|
|
138
168
|
|
|
139
169
|
const event = mj.appendEvent(baseDir, {
|
|
140
170
|
ts: now,
|
|
@@ -150,7 +180,9 @@ function runWrite(opts = {}) {
|
|
|
150
180
|
log(JSON.stringify({ written: event }, null, 2));
|
|
151
181
|
} else {
|
|
152
182
|
log(`mem write: event ${event.id} appended (${event.ts})`);
|
|
153
|
-
log(
|
|
183
|
+
log(
|
|
184
|
+
` agent=${event.agent} cli=${event.cli || '-'} model=${event.model || '-'} outcome=${event.outcome || '-'}`
|
|
185
|
+
);
|
|
154
186
|
if (event.task) log(` task: ${truncate(event.task)}`);
|
|
155
187
|
}
|
|
156
188
|
return event;
|
|
@@ -168,10 +200,24 @@ function runWrite(opts = {}) {
|
|
|
168
200
|
* @returns {{patternId:string, reward:number, previous:object, next:object, promotion:object|null}}
|
|
169
201
|
*/
|
|
170
202
|
function runReinforce(opts = {}) {
|
|
171
|
-
|
|
203
|
+
return store.withMemoryLock(opts.baseDir || process.cwd(), () => reinforceUnderLock(opts));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function reinforceUnderLock(opts = {}) {
|
|
207
|
+
const {
|
|
208
|
+
baseDir = process.cwd(),
|
|
209
|
+
patternId,
|
|
210
|
+
signals = {},
|
|
211
|
+
evidence = [],
|
|
212
|
+
now,
|
|
213
|
+
json = false,
|
|
214
|
+
log = console.log,
|
|
215
|
+
} = opts;
|
|
172
216
|
|
|
173
217
|
if (typeof patternId !== 'string' || patternId.trim().length === 0) {
|
|
174
|
-
throw new TypeError(
|
|
218
|
+
throw new TypeError(
|
|
219
|
+
'mem reinforce: --pattern <id> is required (the pattern heading in patterns.md)'
|
|
220
|
+
);
|
|
175
221
|
}
|
|
176
222
|
|
|
177
223
|
const reward = mj.computeReward(signals);
|
|
@@ -198,7 +244,15 @@ function runReinforce(opts = {}) {
|
|
|
198
244
|
);
|
|
199
245
|
}
|
|
200
246
|
|
|
201
|
-
const result = {
|
|
247
|
+
const result = {
|
|
248
|
+
patternId,
|
|
249
|
+
reward,
|
|
250
|
+
previous,
|
|
251
|
+
next,
|
|
252
|
+
promotion,
|
|
253
|
+
evidenceStatus: 'self-reported',
|
|
254
|
+
rankingEligible: false,
|
|
255
|
+
};
|
|
202
256
|
|
|
203
257
|
if (json) {
|
|
204
258
|
log(JSON.stringify(result, null, 2));
|
|
@@ -209,12 +263,16 @@ function runReinforce(opts = {}) {
|
|
|
209
263
|
log(`mem reinforce: pattern "${patternId}"`);
|
|
210
264
|
log(` reward ${reward.toFixed(3)} (from ${JSON.stringify(signals)})`);
|
|
211
265
|
log(` elo ${prevElo.toFixed(1)} -> ${next.elo.toFixed(1)}`);
|
|
212
|
-
log(
|
|
266
|
+
log(
|
|
267
|
+
` mean ${next.mean.toFixed(3)} (alpha=${next.alpha.toFixed(2)}, beta=${next.beta.toFixed(2)}, updates=${next.updates})`
|
|
268
|
+
);
|
|
213
269
|
if (promotion) {
|
|
214
270
|
log(` promotion PROPOSED (${promotion.id}): ${promotion.fromStatus} -> ${promotion.toStatus}`);
|
|
215
271
|
log(` awaiting human/Shield approval — never auto-applied (governance guard)`);
|
|
216
272
|
} else {
|
|
217
|
-
log(
|
|
273
|
+
log(
|
|
274
|
+
` promotion: none proposed (needs mean >= ${PROMOTION_MEAN_THRESHOLD} and >= ${PROMOTION_MIN_UPDATES} updates)`
|
|
275
|
+
);
|
|
218
276
|
}
|
|
219
277
|
return result;
|
|
220
278
|
}
|
|
@@ -223,14 +281,20 @@ function runReinforce(opts = {}) {
|
|
|
223
281
|
|
|
224
282
|
module.exports = {
|
|
225
283
|
command: 'mem <action> [query...]',
|
|
226
|
-
description: '
|
|
284
|
+
description: 'Project memory — recall | write | reinforce | observe | outcomes',
|
|
227
285
|
options: [
|
|
228
286
|
['-d, --directory <path>', 'Project directory (default: current directory)'],
|
|
229
287
|
['--scope <scope>', 'Recall scope: project | portfolio', 'project'],
|
|
230
288
|
['--portfolio <path>', 'Portfolio brain directory (used with --scope portfolio)'],
|
|
231
289
|
['--limit <n>', 'Max recall results', '8'],
|
|
290
|
+
['--ranking <mode>', 'Recall ranking: lexical | evidence (project only)', 'lexical'],
|
|
291
|
+
['--context-scope <paths>', 'Comma-separated project paths for evidence recall'],
|
|
292
|
+
['--input <file>', 'Project-relative JSON outcome observation input (observe)'],
|
|
232
293
|
['--agent <name>', 'Agent that produced the event (write)'],
|
|
233
|
-
[
|
|
294
|
+
[
|
|
295
|
+
'--cli <name>',
|
|
296
|
+
'Driving CLI: claude-code, gemini-cli, codex-cli, cursor, opencode, aider, antigravity',
|
|
297
|
+
],
|
|
234
298
|
['--model <id>', 'Model id used (model-agnostic: claude/gpt/gemini/local)'],
|
|
235
299
|
['--task <text>', 'What was attempted (write)'],
|
|
236
300
|
['--outcome <outcome>', 'success | failure | partial | abandoned (write)'],
|
|
@@ -245,6 +309,8 @@ module.exports = {
|
|
|
245
309
|
recall: 'Ranked retrieval over the journal + memory files',
|
|
246
310
|
write: 'Append a structured event to .bmad/memory/journal.ndjson',
|
|
247
311
|
reinforce: 'Apply a reward to a pattern score (promotions PROPOSED only)',
|
|
312
|
+
observe: 'Record accepted current Nexus evidence for an exact memory section',
|
|
313
|
+
outcomes: 'Inspect stored outcomes and their current eligibility',
|
|
248
314
|
},
|
|
249
315
|
|
|
250
316
|
action: async (action, query, options = {}) => {
|
|
@@ -264,6 +330,12 @@ module.exports = {
|
|
|
264
330
|
limit: Number(options.limit || 8),
|
|
265
331
|
now,
|
|
266
332
|
json,
|
|
333
|
+
ranking: options.ranking || 'lexical',
|
|
334
|
+
contextScope: options.contextScope
|
|
335
|
+
? String(options.contextScope)
|
|
336
|
+
.split(',')
|
|
337
|
+
.map((s) => s.trim())
|
|
338
|
+
: undefined,
|
|
267
339
|
});
|
|
268
340
|
} else if (action === 'write') {
|
|
269
341
|
runWrite({
|
|
@@ -282,16 +354,44 @@ module.exports = {
|
|
|
282
354
|
baseDir,
|
|
283
355
|
patternId: options.pattern,
|
|
284
356
|
signals: parseSignals(options),
|
|
285
|
-
evidence: options.evidence
|
|
357
|
+
evidence: options.evidence
|
|
358
|
+
? String(options.evidence)
|
|
359
|
+
.split(',')
|
|
360
|
+
.map((s) => s.trim())
|
|
361
|
+
.filter(Boolean)
|
|
362
|
+
: [],
|
|
286
363
|
now,
|
|
287
364
|
json,
|
|
288
365
|
});
|
|
366
|
+
} else if (action === 'observe') {
|
|
367
|
+
const input = JSON.parse(store.readText(baseDir, options.input));
|
|
368
|
+
const written = outcomes.observeOutcome(baseDir, input, { now });
|
|
369
|
+
console.log(
|
|
370
|
+
json
|
|
371
|
+
? JSON.stringify({ written }, null, 2)
|
|
372
|
+
: `mem observe: ${written.id} recorded; lesson causality remains unmeasured.`
|
|
373
|
+
);
|
|
374
|
+
} else if (action === 'outcomes') {
|
|
375
|
+
const results = outcomes.inspectOutcomes(baseDir);
|
|
376
|
+
console.log(
|
|
377
|
+
json
|
|
378
|
+
? JSON.stringify({ count: results.length, results }, null, 2)
|
|
379
|
+
: results
|
|
380
|
+
.map(
|
|
381
|
+
(record) =>
|
|
382
|
+
`${record.id}: ${record.eligible ? 'accepted-current' : record.reason}`
|
|
383
|
+
)
|
|
384
|
+
.join('\n') || 'mem outcomes: no observations.'
|
|
385
|
+
);
|
|
289
386
|
} else {
|
|
290
|
-
console.error(
|
|
387
|
+
console.error(
|
|
388
|
+
`mem: unknown action '${action}' — expected recall | write | reinforce | observe | outcomes`
|
|
389
|
+
);
|
|
291
390
|
process.exitCode = 1;
|
|
292
391
|
}
|
|
293
392
|
} catch (err) {
|
|
294
|
-
console.
|
|
393
|
+
if (json) console.log(JSON.stringify({ error: err.message }));
|
|
394
|
+
else console.error(`mem ${action}: ${err.message}`);
|
|
295
395
|
process.exitCode = 1;
|
|
296
396
|
}
|
|
297
397
|
},
|