ma-agents 2.20.3 → 2.22.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/.opencode/skills/.ma-agents.json +241 -0
- package/.opencode/skills/MANIFEST.yaml +254 -0
- package/.opencode/skills/ai-audit-trail/SKILL.md +23 -0
- package/.opencode/skills/auto-bug-detection/SKILL.md +169 -0
- package/.opencode/skills/cmake-best-practices/SKILL.md +64 -0
- package/.opencode/skills/cmake-best-practices/examples/cmake.md +59 -0
- package/.opencode/skills/code-documentation/SKILL.md +57 -0
- package/.opencode/skills/code-documentation/examples/cpp.md +29 -0
- package/.opencode/skills/code-documentation/examples/csharp.md +28 -0
- package/.opencode/skills/code-documentation/examples/javascript_typescript.md +28 -0
- package/.opencode/skills/code-documentation/examples/python.md +57 -0
- package/.opencode/skills/code-review/SKILL.md +43 -0
- package/.opencode/skills/commit-message/SKILL.md +79 -0
- package/.opencode/skills/cpp-best-practices/SKILL.md +234 -0
- package/.opencode/skills/cpp-best-practices/examples/modern-idioms.md +189 -0
- package/.opencode/skills/cpp-best-practices/examples/naming-and-organization.md +102 -0
- package/.opencode/skills/cpp-concurrency-safety/SKILL.md +60 -0
- package/.opencode/skills/cpp-concurrency-safety/examples/concurrency.md +73 -0
- package/.opencode/skills/cpp-const-correctness/SKILL.md +63 -0
- package/.opencode/skills/cpp-const-correctness/examples/const_correctness.md +54 -0
- package/.opencode/skills/cpp-memory-handling/SKILL.md +42 -0
- package/.opencode/skills/cpp-memory-handling/examples/modern-cpp.md +49 -0
- package/.opencode/skills/cpp-memory-handling/examples/smart-pointers.md +46 -0
- package/.opencode/skills/cpp-modern-composition/SKILL.md +64 -0
- package/.opencode/skills/cpp-modern-composition/examples/composition.md +51 -0
- package/.opencode/skills/cpp-robust-interfaces/SKILL.md +55 -0
- package/.opencode/skills/cpp-robust-interfaces/examples/interfaces.md +56 -0
- package/.opencode/skills/create-hardened-docker-skill/SKILL.md +637 -0
- package/.opencode/skills/create-hardened-docker-skill/scripts/create-all.sh +489 -0
- package/.opencode/skills/csharp-best-practices/SKILL.md +278 -0
- package/.opencode/skills/docker-hardening-verification/SKILL.md +28 -0
- package/.opencode/skills/docker-hardening-verification/scripts/verify-hardening.sh +39 -0
- package/.opencode/skills/docker-image-signing/SKILL.md +28 -0
- package/.opencode/skills/docker-image-signing/scripts/sign-image.sh +33 -0
- package/.opencode/skills/document-revision-history/SKILL.md +104 -0
- package/.opencode/skills/git-workflow-skill/SKILL.md +194 -0
- package/.opencode/skills/git-workflow-skill/hooks/commit-msg +61 -0
- package/.opencode/skills/git-workflow-skill/hooks/pre-commit +38 -0
- package/.opencode/skills/git-workflow-skill/hooks/prepare-commit-msg +56 -0
- package/.opencode/skills/git-workflow-skill/scripts/finish-feature.sh +192 -0
- package/.opencode/skills/git-workflow-skill/scripts/install-hooks.sh +55 -0
- package/.opencode/skills/git-workflow-skill/scripts/start-feature.sh +110 -0
- package/.opencode/skills/git-workflow-skill/scripts/validate-workflow.sh +229 -0
- package/.opencode/skills/js-ts-dependency-mgmt/SKILL.md +49 -0
- package/.opencode/skills/js-ts-dependency-mgmt/examples/dependency_mgmt.md +60 -0
- package/.opencode/skills/js-ts-security-skill/SKILL.md +64 -0
- package/.opencode/skills/js-ts-security-skill/scripts/verify-security.sh +136 -0
- package/.opencode/skills/logging-best-practices/SKILL.md +50 -0
- package/.opencode/skills/logging-best-practices/examples/cpp.md +36 -0
- package/.opencode/skills/logging-best-practices/examples/csharp.md +49 -0
- package/.opencode/skills/logging-best-practices/examples/javascript.md +77 -0
- package/.opencode/skills/logging-best-practices/examples/python.md +57 -0
- package/.opencode/skills/logging-best-practices/references/logging-standards.md +29 -0
- package/.opencode/skills/open-presentation/SKILL.md +35 -0
- package/.opencode/skills/opentelemetry-best-practices/SKILL.md +34 -0
- package/.opencode/skills/opentelemetry-best-practices/examples/go.md +32 -0
- package/.opencode/skills/opentelemetry-best-practices/examples/javascript.md +58 -0
- package/.opencode/skills/opentelemetry-best-practices/examples/python.md +37 -0
- package/.opencode/skills/opentelemetry-best-practices/references/otel-standards.md +37 -0
- package/.opencode/skills/python-best-practices/SKILL.md +385 -0
- package/.opencode/skills/python-dependency-mgmt/SKILL.md +42 -0
- package/.opencode/skills/python-dependency-mgmt/examples/dependency_mgmt.md +67 -0
- package/.opencode/skills/python-security-skill/SKILL.md +56 -0
- package/.opencode/skills/python-security-skill/examples/security.md +56 -0
- package/.opencode/skills/self-signed-cert/SKILL.md +42 -0
- package/.opencode/skills/self-signed-cert/scripts/generate-cert.ps1 +45 -0
- package/.opencode/skills/self-signed-cert/scripts/generate-cert.sh +43 -0
- package/.opencode/skills/skill-creator/SKILL.md +196 -0
- package/.opencode/skills/skill-creator/references/output-patterns.md +82 -0
- package/.opencode/skills/skill-creator/references/workflows.md +28 -0
- package/.opencode/skills/skill-creator/scripts/init_skill.py +208 -0
- package/.opencode/skills/skill-creator/scripts/package_skill.py +99 -0
- package/.opencode/skills/skill-creator/scripts/quick_validate.py +113 -0
- package/.opencode/skills/story-status-lookup/SKILL.md +78 -0
- package/.opencode/skills/test-accompanied-development/SKILL.md +50 -0
- package/.opencode/skills/test-generator/SKILL.md +65 -0
- package/.opencode/skills/vercel-react-best-practices/SKILL.md +109 -0
- package/.opencode/skills/verify-hardened-docker-skill/SKILL.md +442 -0
- package/.opencode/skills/verify-hardened-docker-skill/scripts/verify-docker-hardening.sh +439 -0
- package/AiAudit.md +5 -0
- package/QUICK_START.md +11 -5
- package/README.md +52 -1
- package/bin/cli.js +31 -4
- package/docs/BMAD_AI_Development_Training.pptx +0 -0
- package/docs/technical-notes/context-persistence-research.md +434 -0
- package/docs/technical-notes/enforcement-hooks-research.md +415 -0
- package/lib/agents.js +34 -0
- package/lib/bmad-extension/agents/bmm-architect.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-bmad-master.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-cyber.customize.yaml +30 -0
- package/lib/bmad-extension/agents/bmm-dev.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-devops.customize.yaml +30 -0
- package/lib/bmad-extension/agents/bmm-mil498.customize.yaml +42 -0
- package/lib/bmad-extension/agents/bmm-pm.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-qa.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-sm.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-sre.customize.yaml +30 -0
- package/lib/bmad-extension/agents/bmm-tech-writer.customize.yaml +5 -0
- package/lib/bmad-extension/agents/bmm-ux-designer.customize.yaml +5 -0
- package/lib/bmad-extension/module-help.csv +7 -0
- package/lib/bmad-extension/module.yaml +3 -0
- package/lib/bmad-extension/workflows/add-sprint/workflow.md +112 -0
- package/lib/bmad-extension/workflows/add-to-sprint/workflow.md +206 -0
- package/lib/bmad-extension/workflows/create-bug-story/workflow.md +186 -0
- package/lib/bmad-extension/workflows/modify-sprint/workflow.md +250 -0
- package/lib/bmad-extension/workflows/project-context-expansion/workflow.md +229 -0
- package/lib/bmad-extension/workflows/sprint-status-view/workflow.md +193 -0
- package/lib/bmad.js +168 -36
- package/lib/hooks/claude-code/verify-manifest.js +56 -0
- package/lib/installer.js +282 -1
- package/lib/methodology/BMAD_AI_Development_Training.pptx +0 -0
- package/lib/methodology/version.json +7 -0
- package/lib/skill-authoring.js +732 -0
- package/lib/templates/project-context.template.md +47 -0
- package/opencode.json +8 -0
- package/package.json +2 -2
- package/skills/auto-bug-detection/SKILL.md +165 -0
- package/skills/auto-bug-detection/skill.json +8 -0
- package/skills/code-review/SKILL.md +40 -0
- package/skills/cpp-best-practices/SKILL.md +230 -0
- package/skills/cpp-best-practices/examples/modern-idioms.md +189 -0
- package/skills/cpp-best-practices/examples/naming-and-organization.md +102 -0
- package/skills/cpp-best-practices/skill.json +25 -0
- package/skills/csharp-best-practices/SKILL.md +274 -0
- package/skills/csharp-best-practices/skill.json +23 -0
- package/skills/git-workflow-skill/skill.json +1 -1
- package/skills/open-presentation/SKILL.md +31 -0
- package/skills/open-presentation/skill.json +11 -0
- package/skills/python-best-practices/SKILL.md +381 -0
- package/skills/python-best-practices/skill.json +26 -0
- package/skills/story-status-lookup/SKILL.md +74 -0
- package/skills/story-status-lookup/skill.json +8 -0
- package/test/agent-injection-strategy.test.js +13 -7
- package/test/bmad-extension.test.js +237 -0
- package/test/bmad-output-policy.test.js +119 -0
- package/test/build-bmad-args.test.js +361 -0
- package/test/create-agent.test.js +232 -0
- package/test/enforcement-hooks.test.js +324 -0
- package/test/generate-project-context.test.js +337 -0
- package/test/integration-verification.test.js +402 -0
- package/test/opencode-agent.test.js +150 -0
- package/test/opencode-json-error.test.js +260 -0
- package/test/opencode-json-injection.test.js +256 -0
- package/test/opencode-json-merge.test.js +299 -0
- package/test/skill-authoring.test.js +272 -0
- package/test/skill-customize-agent.test.js +253 -0
- package/test/skill-mandatory.test.js +235 -0
- package/test/skill-validation.test.js +378 -0
- package/test/yes-flag.test.js +1 -1
package/lib/bmad.js
CHANGED
|
@@ -22,24 +22,78 @@ function isBmadInstalled(projectRoot = process.cwd()) {
|
|
|
22
22
|
return fs.existsSync(path.join(projectRoot, BMAD_DIR));
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Build a complete bmad-method CLI command from an install context object.
|
|
27
|
+
* Replaces ad-hoc string concatenation with a single authoritative builder.
|
|
28
|
+
* Architecture Decision P2-6.
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} ctx - Install context
|
|
31
|
+
* @param {string} ctx.projectRoot - Project root directory
|
|
32
|
+
* @param {string[]} ctx.modules - Module IDs (e.g. ['bmm', 'bmb'])
|
|
33
|
+
* @param {string[]} ctx.tools - Tool/IDE IDs (e.g. ['claude-code', 'cursor'])
|
|
34
|
+
* @param {string} ctx.action - 'install' (default) or 'update'
|
|
35
|
+
* @param {string} [ctx.userName] - User name for agents
|
|
36
|
+
* @param {string} [ctx.commLang] - Communication language
|
|
37
|
+
* @param {string} [ctx.docLang] - Document output language
|
|
38
|
+
* @param {string} [ctx.outputFolder] - Output folder relative to project root
|
|
39
|
+
* @returns {string} Complete command string ready for execSync
|
|
40
|
+
*/
|
|
41
|
+
function buildBmadArgs(ctx) {
|
|
42
|
+
if (!ctx || !ctx.projectRoot || !Array.isArray(ctx.modules)) {
|
|
43
|
+
throw new Error('buildBmadArgs requires ctx with projectRoot (string) and modules (array)');
|
|
44
|
+
}
|
|
45
|
+
const parts = ['install'];
|
|
46
|
+
|
|
47
|
+
parts.push('--directory', `"${ctx.projectRoot}"`);
|
|
48
|
+
parts.push('--modules', ctx.modules.join(','));
|
|
49
|
+
parts.push('--tools', ctx.tools && ctx.tools.length > 0 ? ctx.tools.join(',') : 'none');
|
|
50
|
+
parts.push('--yes');
|
|
51
|
+
|
|
52
|
+
// Optional params — only include if provided (let bmad-method default otherwise)
|
|
53
|
+
if (ctx.userName) {
|
|
54
|
+
parts.push('--user-name', `"${ctx.userName}"`);
|
|
55
|
+
}
|
|
56
|
+
if (ctx.commLang) {
|
|
57
|
+
parts.push('--communication-language', `"${ctx.commLang}"`);
|
|
58
|
+
}
|
|
59
|
+
if (ctx.docLang) {
|
|
60
|
+
parts.push('--document-output-language', `"${ctx.docLang}"`);
|
|
61
|
+
}
|
|
62
|
+
if (ctx.outputFolder) {
|
|
63
|
+
parts.push('--output-folder', `"${ctx.outputFolder}"`);
|
|
64
|
+
}
|
|
27
65
|
|
|
28
|
-
|
|
29
|
-
|
|
66
|
+
// Extension module — only if directory exists
|
|
67
|
+
const extensionPath = path.join(__dirname, 'bmad-extension');
|
|
68
|
+
if (fs.existsSync(extensionPath)) {
|
|
69
|
+
parts.push('--custom-content', `"${extensionPath}"`);
|
|
30
70
|
}
|
|
31
71
|
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
} else {
|
|
35
|
-
command += ' --tools none';
|
|
72
|
+
if (ctx.action === 'update') {
|
|
73
|
+
parts.push('--action', 'update');
|
|
36
74
|
}
|
|
37
75
|
|
|
76
|
+
return getBmadCommand(parts.join(' '));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function installBmad(modules = ['bmm', 'bmb'], tools = [], projectRoot = process.cwd(), force = false, { userName = '', commLang = '', docLang = '', outputFolder = '' } = {}) {
|
|
80
|
+
const command = buildBmadArgs({
|
|
81
|
+
projectRoot,
|
|
82
|
+
modules,
|
|
83
|
+
tools,
|
|
84
|
+
action: 'install',
|
|
85
|
+
userName,
|
|
86
|
+
commLang,
|
|
87
|
+
docLang,
|
|
88
|
+
outputFolder,
|
|
89
|
+
});
|
|
90
|
+
|
|
38
91
|
await prePopulateBmadCache(force);
|
|
39
92
|
|
|
40
93
|
console.log(chalk.gray(` Running: ${command}`));
|
|
41
94
|
try {
|
|
42
95
|
execSync(command, { stdio: 'inherit', cwd: projectRoot, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } });
|
|
96
|
+
await deployMethodology(projectRoot, force);
|
|
43
97
|
return true;
|
|
44
98
|
} catch (error) {
|
|
45
99
|
console.error(chalk.red(` BMAD installation failed: ${error.message}`));
|
|
@@ -47,24 +101,24 @@ async function installBmad(modules = ['bmm', 'bmb'], tools = [], projectRoot = p
|
|
|
47
101
|
}
|
|
48
102
|
}
|
|
49
103
|
|
|
50
|
-
async function updateBmad(modules = ['bmm', 'bmb'], tools = [], projectRoot = process.cwd(), force = false) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
104
|
+
async function updateBmad(modules = ['bmm', 'bmb'], tools = [], projectRoot = process.cwd(), force = false, { userName = '', commLang = '', docLang = '', outputFolder = '' } = {}) {
|
|
105
|
+
const command = buildBmadArgs({
|
|
106
|
+
projectRoot,
|
|
107
|
+
modules,
|
|
108
|
+
tools,
|
|
109
|
+
action: 'update',
|
|
110
|
+
userName,
|
|
111
|
+
commLang,
|
|
112
|
+
docLang,
|
|
113
|
+
outputFolder,
|
|
114
|
+
});
|
|
62
115
|
|
|
63
116
|
await prePopulateBmadCache(force);
|
|
64
117
|
|
|
65
118
|
console.log(chalk.gray(` Running: ${command}`));
|
|
66
119
|
try {
|
|
67
120
|
execSync(command, { stdio: 'inherit', cwd: projectRoot, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } });
|
|
121
|
+
await deployMethodology(projectRoot, force);
|
|
68
122
|
return true;
|
|
69
123
|
} catch (error) {
|
|
70
124
|
console.error(chalk.red(` BMAD update failed: ${error.message}`));
|
|
@@ -72,7 +126,7 @@ async function updateBmad(modules = ['bmm', 'bmb'], tools = [], projectRoot = pr
|
|
|
72
126
|
}
|
|
73
127
|
}
|
|
74
128
|
|
|
75
|
-
async function applyCustomizations(projectRoot = process.cwd(), modules = ['bmm', 'bmb'], tools = [], selectedAgentIds = [], force = false) {
|
|
129
|
+
async function applyCustomizations(projectRoot = process.cwd(), modules = ['bmm', 'bmb'], tools = [], selectedAgentIds = [], force = false, { userName = '', commLang = '', docLang = '', outputFolder = '' } = {}) {
|
|
76
130
|
const sourceDir = path.join(__dirname, 'bmad-customizations');
|
|
77
131
|
const workflowSourceDir = path.join(__dirname, 'bmad-workflows');
|
|
78
132
|
const configTargetDir = path.join(projectRoot, CONFIG_DIR);
|
|
@@ -122,18 +176,19 @@ async function applyCustomizations(projectRoot = process.cwd(), modules = ['bmm'
|
|
|
122
176
|
}
|
|
123
177
|
}
|
|
124
178
|
|
|
125
|
-
// Recompile agents to apply YAML customizations
|
|
179
|
+
// STAGE:RECOMPILE — Recompile agents to apply YAML customizations
|
|
126
180
|
// IMPORTANT: Steps 3-5 must run AFTER recompile because the recompile regenerates
|
|
127
181
|
// the _bmad/bmm/ tree and would wipe any files placed there beforehand.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
182
|
+
const command = buildBmadArgs({
|
|
183
|
+
projectRoot,
|
|
184
|
+
modules,
|
|
185
|
+
tools,
|
|
186
|
+
action: 'install',
|
|
187
|
+
userName,
|
|
188
|
+
commLang,
|
|
189
|
+
docLang,
|
|
190
|
+
outputFolder,
|
|
191
|
+
});
|
|
137
192
|
await prePopulateBmadCache(force);
|
|
138
193
|
|
|
139
194
|
console.log(chalk.gray(` Running: ${command}`));
|
|
@@ -143,21 +198,37 @@ async function applyCustomizations(projectRoot = process.cwd(), modules = ['bmm'
|
|
|
143
198
|
console.error(chalk.red(` BMAD recompile failed: ${error.message}`));
|
|
144
199
|
}
|
|
145
200
|
|
|
146
|
-
//
|
|
201
|
+
// STAGE:EXTENSION — Deploy BMAD extension module (AFTER recompile — extensions/ survives recompile)
|
|
202
|
+
// NOTE: Stage 1 deploys persona/menu customize.yaml to _bmad/_config/agents/ (consumed
|
|
203
|
+
// by BMAD's agent compilation). This stage deploys the extension module to
|
|
204
|
+
// _bmad/extensions/ma-agents-skills/ which adds critical_actions for skill loading.
|
|
205
|
+
// These are complementary paths: _config/agents/ drives agent recompilation (persona/menu),
|
|
206
|
+
// while extensions/ adds runtime behavior (critical_actions). BMAD does NOT merge them —
|
|
207
|
+
// they are consumed by different mechanisms. The 4 custom agents have persona/menu in both
|
|
208
|
+
// locations; this is intentional for backward compatibility during transition (Story 8.3).
|
|
209
|
+
const extensionSource = path.join(__dirname, 'bmad-extension');
|
|
210
|
+
const extensionTarget = path.join(projectRoot, BMAD_DIR, 'extensions', 'ma-agents-skills');
|
|
211
|
+
if (fs.existsSync(extensionSource)) {
|
|
212
|
+
await fs.ensureDir(extensionTarget);
|
|
213
|
+
await fs.copy(extensionSource, extensionTarget);
|
|
214
|
+
console.log(chalk.cyan(' + Deployed BMAD extension module: ma-agents-skills'));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// STAGE:WORKFLOWS — Apply workflows (AFTER recompile so they persist)
|
|
147
218
|
if (fs.existsSync(workflowSourceDir)) {
|
|
148
219
|
await fs.ensureDir(workflowTargetDir);
|
|
149
220
|
await fs.copy(workflowSourceDir, workflowTargetDir);
|
|
150
221
|
console.log(chalk.cyan(` + Applied BMAD workflows`));
|
|
151
222
|
}
|
|
152
223
|
|
|
153
|
-
//
|
|
224
|
+
// 5. Apply MIL-STD-498 templates (AFTER recompile so they persist)
|
|
154
225
|
if (fs.existsSync(templateSourceDir)) {
|
|
155
226
|
await fs.ensureDir(templateTargetDir);
|
|
156
227
|
await fs.copy(templateSourceDir, templateTargetDir);
|
|
157
228
|
console.log(chalk.cyan(` + Applied MIL-STD-498 templates`));
|
|
158
229
|
}
|
|
159
230
|
|
|
160
|
-
//
|
|
231
|
+
// 6. Copy agent templates to compiled location (AFTER recompile)
|
|
161
232
|
// BMAD recompile doesn't generate custom agents (mil498, cyber) in _bmad/bmm/agents/.
|
|
162
233
|
// Without this, updateAgentInstructions() creates the file with only the MA-AGENTS block.
|
|
163
234
|
if (fs.existsSync(sourceDir)) {
|
|
@@ -175,7 +246,7 @@ async function applyCustomizations(projectRoot = process.cwd(), modules = ['bmm'
|
|
|
175
246
|
}
|
|
176
247
|
}
|
|
177
248
|
|
|
178
|
-
//
|
|
249
|
+
// 7. Register custom workflows in BMAD CSV registries (after recompile)
|
|
179
250
|
if (selectedAgentIds.length === 0 || selectedAgentIds.includes('bmm-mil498')) {
|
|
180
251
|
await registerMilWorkflows(projectRoot);
|
|
181
252
|
}
|
|
@@ -260,6 +331,65 @@ async function registerMilWorkflows(projectRoot) {
|
|
|
260
331
|
}
|
|
261
332
|
}
|
|
262
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Deploy the methodology presentation to the target project's _bmad-output/methodology/ directory.
|
|
336
|
+
* Version-aware: skips if target is same or newer; overwrites only with --force.
|
|
337
|
+
* AC: #1, #2, #3, #5 (Story 6.1)
|
|
338
|
+
*/
|
|
339
|
+
async function deployMethodology(projectRoot = process.cwd(), force = false) {
|
|
340
|
+
const methodologySource = path.join(__dirname, 'methodology');
|
|
341
|
+
if (!(await fs.pathExists(methodologySource))) {
|
|
342
|
+
console.log(chalk.gray(' No methodology content found — skipping'));
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const methodologyTarget = path.join(projectRoot, '_bmad-output', 'methodology');
|
|
347
|
+
await fs.ensureDir(methodologyTarget);
|
|
348
|
+
|
|
349
|
+
// Version comparison
|
|
350
|
+
const sourceVersionFile = path.join(methodologySource, 'version.json');
|
|
351
|
+
const targetVersionFile = path.join(methodologyTarget, 'version.json');
|
|
352
|
+
|
|
353
|
+
let sourceVersion = '0.0.0';
|
|
354
|
+
let targetVersion = null;
|
|
355
|
+
|
|
356
|
+
if (await fs.pathExists(sourceVersionFile)) {
|
|
357
|
+
try {
|
|
358
|
+
const v = JSON.parse(await fs.readFile(sourceVersionFile, 'utf-8'));
|
|
359
|
+
sourceVersion = v.version || '0.0.0';
|
|
360
|
+
} catch { /* ignore */ }
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (await fs.pathExists(targetVersionFile)) {
|
|
364
|
+
try {
|
|
365
|
+
const v = JSON.parse(await fs.readFile(targetVersionFile, 'utf-8'));
|
|
366
|
+
targetVersion = v.version || null;
|
|
367
|
+
} catch { /* ignore */ }
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (targetVersion !== null && !force) {
|
|
371
|
+
// Simple semver comparison: skip if target is same or newer
|
|
372
|
+
const semverGte = (a, b) => {
|
|
373
|
+
const pa = a.split('.').map(Number);
|
|
374
|
+
const pb = b.split('.').map(Number);
|
|
375
|
+
for (let i = 0; i < 3; i++) {
|
|
376
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
|
377
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
|
378
|
+
}
|
|
379
|
+
return true;
|
|
380
|
+
};
|
|
381
|
+
if (semverGte(targetVersion, sourceVersion)) {
|
|
382
|
+
console.log(chalk.gray(` ✓ Methodology presentation is up to date (v${targetVersion})`));
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
await fs.copy(methodologySource, methodologyTarget, { overwrite: true });
|
|
388
|
+
console.log(chalk.green(' ✓ Methodology presentation installed to _bmad-output/methodology/'));
|
|
389
|
+
console.log(chalk.gray(' Hint: Share this with your team for BMAD-METHOD onboarding'));
|
|
390
|
+
console.log(chalk.gray(' Tip: Use /open-presentation to open the slides (install via npx ma-agents install open-presentation)'));
|
|
391
|
+
}
|
|
392
|
+
|
|
263
393
|
async function prePopulateBmadCache(force = false) {
|
|
264
394
|
const cacheSource = path.join(__dirname, 'bmad-cache');
|
|
265
395
|
if (!(await fs.pathExists(cacheSource))) {
|
|
@@ -342,8 +472,10 @@ function restoreGitDir(moduleDir) {
|
|
|
342
472
|
|
|
343
473
|
module.exports = {
|
|
344
474
|
isBmadInstalled,
|
|
475
|
+
buildBmadArgs,
|
|
345
476
|
installBmad,
|
|
346
477
|
updateBmad,
|
|
347
478
|
applyCustomizations,
|
|
348
|
-
prePopulateBmadCache
|
|
479
|
+
prePopulateBmadCache,
|
|
480
|
+
deployMethodology,
|
|
349
481
|
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Claude Code SessionStart Hook — MANIFEST.yaml Skill Enforcement
|
|
5
|
+
*
|
|
6
|
+
* Injects a context reminder at session startup to reinforce skill loading
|
|
7
|
+
* from the project's MANIFEST.yaml. Runs as a standalone Node.js script
|
|
8
|
+
* with no external dependencies.
|
|
9
|
+
*
|
|
10
|
+
* Hook event: SessionStart (matcher: "startup")
|
|
11
|
+
* Exit 0 + stdout text → injected into Claude's session context
|
|
12
|
+
*
|
|
13
|
+
* Installed by: ma-agents installer (lib/installer.js)
|
|
14
|
+
* Removed by: ma-agents uninstaller when all skills are removed
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
// Read hook input from stdin (JSON)
|
|
23
|
+
let input = '';
|
|
24
|
+
process.stdin.setEncoding('utf8');
|
|
25
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
26
|
+
process.stdin.on('end', () => {
|
|
27
|
+
try {
|
|
28
|
+
const data = JSON.parse(input);
|
|
29
|
+
const projectDir = data.cwd || process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
30
|
+
|
|
31
|
+
// Look for skills MANIFEST.yaml in the project
|
|
32
|
+
const manifestPath = path.join(projectDir, '.claude', 'skills', 'MANIFEST.yaml');
|
|
33
|
+
|
|
34
|
+
if (!fs.existsSync(manifestPath)) {
|
|
35
|
+
// No manifest — this project doesn't use ma-agents skills. Exit silently.
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// MANIFEST exists — inject context reminder into session
|
|
40
|
+
const reminder = [
|
|
41
|
+
'SKILL ENFORCEMENT ACTIVE: This project uses ma-agents skills.',
|
|
42
|
+
`Read the skill manifest at ${manifestPath} before starting any task.`,
|
|
43
|
+
'Load all skills marked always_load: true, then select relevant skills for the current task.',
|
|
44
|
+
].join(' ');
|
|
45
|
+
|
|
46
|
+
process.stdout.write(reminder);
|
|
47
|
+
process.exit(0);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
// On any error, allow session to proceed (don't block startup)
|
|
50
|
+
process.stderr.write(`verify-manifest hook warning: ${err.message}\n`);
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Handle stdin close without data (defensive)
|
|
56
|
+
process.stdin.on('error', () => { process.exit(0); });
|