ma-agents 3.6.0 → 3.6.2
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/README.md +36 -10
- package/lib/bmad.js +43 -6
- package/lib/installer.js +12 -0
- package/package.json +1 -1
- package/test/offline-recompile.test.js +30 -0
package/README.md
CHANGED
|
@@ -36,8 +36,9 @@ Skills are written in a **unified generic format** and stored in this package. W
|
|
|
36
36
|
3. Copies the skill and its resources into the target agent's directory
|
|
37
37
|
4. Renames resource directories to match the agent's native structure (e.g., `references/` becomes `docs/` for Cline)
|
|
38
38
|
5. **Generates `MANIFEST.yaml`**: A central discovery file for the agent to find all installed skills.
|
|
39
|
-
6. **Updates
|
|
40
|
-
7. **BMAD
|
|
39
|
+
6. **Updates Instruction Blocks**: Stamps a universal planning-instruction block into every agent's instruction file (`CLAUDE.md`, `.clinerules`, `.cline/clinerules.md`, `.roo/rules/00-ma-agents.md`, `AGENTS.md`) between `<!-- MA-AGENTS-START -->` / `<!-- MA-AGENTS-END -->` markers. User content outside the markers is preserved. On-prem profile additionally stamps guardrails for local LLMs.
|
|
40
|
+
7. **BMAD Roo Code Modes**: Generates `.roomodes` with 4 BMAD planning-mode custom modes (`bmad-pm`, `bmad-architect`, `bmad-techlead`, `bmad-dev`), each with `fileRegex` restrictions that enforce planning-phase file access at the application layer.
|
|
41
|
+
8. **BMAD-METHOD Integration**: Automatically detects and integrates with [BMAD-METHOD](https://docs.bmad-method.org/), applying project-specific customizations and orchestration.
|
|
41
42
|
|
|
42
43
|
```
|
|
43
44
|
Generic (this repo) Installed Output
|
|
@@ -111,11 +112,25 @@ When running AI coding agents against a locally-hosted LLM (e.g., Nemotron Super
|
|
|
111
112
|
? Profile (standard / on-prem):
|
|
112
113
|
```
|
|
113
114
|
|
|
114
|
-
Your answer is persisted in `.ma-agents.json` under the `profile` field and is read on every subsequent install, update, or reconfigure. The `standard` profile produces the same output as always; the `on-prem` profile additionally stamps on-prem-specific guardrails into every agent's instruction block — including `/no_think` directives for planning personas, `str_replace_editor` prohibition, and home-directory write restrictions.
|
|
115
|
+
Your answer is persisted in `.ma-agents.json` under the `profile` field and is read on every subsequent install, update, or reconfigure. The manifest also records the `toolVersion` (ma-agents version) and `bmadVersion` (bundled bmad-method version) on every install, making it easy to verify what ran last. The `standard` profile produces the same output as always; the `on-prem` profile additionally stamps on-prem-specific guardrails into every agent's instruction block — including `/no_think` directives for planning personas, `str_replace_editor` prohibition, and home-directory write restrictions.
|
|
115
116
|
|
|
116
117
|
To change your profile after initial setup: `npx ma-agents reconfigure`
|
|
117
118
|
To remove all on-prem profile artifacts: `npx ma-agents uninstall --profile-artifacts`
|
|
118
119
|
|
|
120
|
+
### What the on-prem profile stamps
|
|
121
|
+
|
|
122
|
+
| File | What is added |
|
|
123
|
+
|------|--------------|
|
|
124
|
+
| `CLAUDE.md` | Universal instruction block + on-prem guardrails (inside `MA-AGENTS-START/END` markers) |
|
|
125
|
+
| `.roo/rules/00-ma-agents.md` | Same block for Roo Code |
|
|
126
|
+
| `.clinerules` + `.cline/clinerules.md` | Same block for Cline |
|
|
127
|
+
| `AGENTS.md` | Same block for OpenCode |
|
|
128
|
+
| `opencode.json` | Additive entry in `instructions[]` array |
|
|
129
|
+
| `.roomodes` | 4 BMAD planning modes with `fileRegex` phase enforcement |
|
|
130
|
+
| `_bmad/_config/agents/bmm-*.customize.yaml` | Per-persona phase prefix: planning personas get `/no_think` prefix; dev personas get careful-coding prefix |
|
|
131
|
+
|
|
132
|
+
The standard profile stamps the universal block only (no `/no_think`, no `str_replace_editor` prohibition, no home-dir restriction).
|
|
133
|
+
|
|
119
134
|
For vLLM server configuration, quantization tradeoffs, per-phase sampling parameters, and the `str_replace_editor` hallucination mitigation, see [`docs/deployment/vllm-nemotron.md`](docs/deployment/vllm-nemotron.md).
|
|
120
135
|
|
|
121
136
|
---
|
|
@@ -263,6 +278,8 @@ npx ma-agents status # Show installed skills and paths
|
|
|
263
278
|
npx ma-agents list # List all available skills
|
|
264
279
|
npx ma-agents agents # List supported agents
|
|
265
280
|
npx ma-agents uninstall <skill> <agents> # Direct uninstall
|
|
281
|
+
npx ma-agents reconfigure # Re-run profile prompt and re-stamp all artifacts
|
|
282
|
+
npx ma-agents uninstall --profile-artifacts # Remove on-prem profile content from all agent files
|
|
266
283
|
```
|
|
267
284
|
|
|
268
285
|
### The Update Process
|
|
@@ -590,16 +607,25 @@ ma-agents/
|
|
|
590
607
|
├── bin/
|
|
591
608
|
│ └── cli.js # CLI entry point (wizard + commands)
|
|
592
609
|
├── lib/
|
|
593
|
-
│ ├── agents.js # Agent configurations and
|
|
594
|
-
│ ├── installer.js # Skill
|
|
595
|
-
│ ├── bmad.js # BMAD-METHOD integration
|
|
610
|
+
│ ├── agents.js # Agent configurations, paths, and extraInstructionTemplates
|
|
611
|
+
│ ├── installer.js # Skill installation, instruction-block stamping, marker management
|
|
612
|
+
│ ├── bmad.js # BMAD-METHOD integration, recompile, persona phase prefix
|
|
613
|
+
│ ├── profile.js # Profile persistence (getProfile / setProfile / resolveProfile / clearProfile)
|
|
614
|
+
│ ├── reconfigure.js # ma-agents reconfigure — re-stamp profile-dependent artifacts
|
|
615
|
+
│ ├── uninstall.js # ma-agents uninstall --profile-artifacts
|
|
616
|
+
│ ├── merge/
|
|
617
|
+
│ │ └── roomodes.js # YAML merger for .roomodes slug collision / user-slug preservation
|
|
618
|
+
│ ├── templates/
|
|
619
|
+
│ │ ├── instruction-block-universal.template.md # Universal planning-instruction block
|
|
620
|
+
│ │ ├── instruction-block-onprem.template.md # On-prem guardrails append (profile=on-prem)
|
|
621
|
+
│ │ ├── roomodes.template.yaml # 4 BMAD Roo Code modes with fileRegex
|
|
622
|
+
│ │ ├── agents-md.template.md # AGENTS.md for OpenCode
|
|
623
|
+
│ │ ├── clinerules.template.md # .clinerules expansion for Cline
|
|
624
|
+
│ │ └── project-context.template.md # project-context.md generation template
|
|
625
|
+
│ ├── bmad-customize/ # Per-persona customize.yaml with phase: + on_prem_phase_prefix:
|
|
596
626
|
│ ├── bmad-cache/ # Pre-bundled BMAD external modules (bmb, cis, gds, tea)
|
|
597
627
|
│ ├── bmad-customizations/ # BMAD persona templates (.md) and YAML configs
|
|
598
628
|
│ ├── bmad-extension/ # Extension module: sprint management, bug tracking, agent skills
|
|
599
|
-
│ │ ├── skills/ # SKILL.md packages (add-sprint, generate-backlog, etc.)
|
|
600
|
-
│ │ ├── workflows/ # Legacy workflow copies (redirects to SKILL.md)
|
|
601
|
-
│ │ ├── module.yaml # Extension module definition
|
|
602
|
-
│ │ └── module-help.csv # Skill registry with descriptions and output paths
|
|
603
629
|
│ ├── bmad-workflows/ # Specialized BMAD playbooks (SRE, Cyber, etc.)
|
|
604
630
|
│ └── mil498-templates/ # MIL-STD-498 DID library (SRS, SSS, SSDD, etc.)
|
|
605
631
|
├── scripts/
|
package/lib/bmad.js
CHANGED
|
@@ -32,18 +32,22 @@ const CONFIG_DIR = path.join(BMAD_DIR, '_config', 'agents');
|
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Run a shell command, relaying output to stdout/stderr.
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* Always uses pipe mode so that on failure, error.stdout and error.stderr
|
|
36
|
+
* are populated — enabling classifyRecompileFailure to detect EBUSY and
|
|
37
|
+
* other subprocess errors by inspecting their text.
|
|
38
|
+
* Output is manually relayed to the terminal on success; on failure the
|
|
39
|
+
* subprocess output is relayed before rethrowing so the user still sees it.
|
|
38
40
|
*/
|
|
39
41
|
function runCommand(command, options = {}) {
|
|
40
|
-
|
|
42
|
+
try {
|
|
41
43
|
const result = execSync(command, { ...options, stdio: 'pipe' });
|
|
42
44
|
if (result && result.length > 0) {
|
|
43
45
|
process.stdout.write(result);
|
|
44
46
|
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err.stdout && err.stdout.length > 0) process.stdout.write(err.stdout);
|
|
49
|
+
if (err.stderr && err.stderr.length > 0) process.stderr.write(err.stderr);
|
|
50
|
+
throw err;
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
|
|
@@ -116,6 +120,23 @@ function looksLikeOfflineFailure(error) {
|
|
|
116
120
|
return needles.some(n => haystack.includes(n));
|
|
117
121
|
}
|
|
118
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Heuristic: does the caught recompile error look like a Windows EBUSY lock?
|
|
125
|
+
*
|
|
126
|
+
* On Windows, rmdir calls during BMAD recompile can fail with EBUSY (resource
|
|
127
|
+
* busy or locked) because Windows Defender or Explorer holds a file lock on
|
|
128
|
+
* newly-created directories. This is a transient OS-level condition that
|
|
129
|
+
* does not indicate a real installation failure — treat it as warn-and-continue.
|
|
130
|
+
*
|
|
131
|
+
* @param {Error} error - The error thrown from runCommand() during recompile
|
|
132
|
+
* @returns {boolean}
|
|
133
|
+
*/
|
|
134
|
+
function looksLikeEbusyFailure(error) {
|
|
135
|
+
const msg = [error?.message, error?.stderr, error?.stdout]
|
|
136
|
+
.filter(Boolean).join(' ').toLowerCase();
|
|
137
|
+
return msg.includes('ebusy') || msg.includes('resource busy') || msg.includes('resource busy or locked');
|
|
138
|
+
}
|
|
139
|
+
|
|
119
140
|
/**
|
|
120
141
|
* Inspect the vendored BMAD cache (post pre-population) and report which
|
|
121
142
|
* modules are present. Used to classify recompile failures: when the cache is
|
|
@@ -192,6 +213,21 @@ function classifyRecompileFailure(error, opts = {}) {
|
|
|
192
213
|
const looksOffline = looksLikeOfflineFailure(error);
|
|
193
214
|
const inspector = opts.cacheInspector || inspectBmadCache;
|
|
194
215
|
|
|
216
|
+
// EBUSY: Windows file-lock transient error — warn and continue.
|
|
217
|
+
// Windows Defender / Explorer can briefly lock newly-created directories
|
|
218
|
+
// during BMAD recompile. This is not a real failure; downgrade to a warning
|
|
219
|
+
// so the install pipeline is not cancelled on a transient OS condition.
|
|
220
|
+
if (looksLikeEbusyFailure(error)) {
|
|
221
|
+
return {
|
|
222
|
+
action: 'warn',
|
|
223
|
+
reason: 'ebusy',
|
|
224
|
+
message:
|
|
225
|
+
'BMAD recompile encountered a Windows file-lock (EBUSY / resource busy or locked). ' +
|
|
226
|
+
'This is a transient condition (Windows Defender or Explorer briefly held a lock). ' +
|
|
227
|
+
'The install will proceed — re-run if BMAD personas appear incomplete.',
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
195
231
|
if (!declaredOffline && !looksOffline) {
|
|
196
232
|
return {
|
|
197
233
|
action: 'rethrow',
|
|
@@ -1493,6 +1529,7 @@ module.exports = {
|
|
|
1493
1529
|
// bug-bmad-recompile-fails-on-airgapped-network)
|
|
1494
1530
|
isOfflineModeDeclared,
|
|
1495
1531
|
looksLikeOfflineFailure,
|
|
1532
|
+
looksLikeEbusyFailure,
|
|
1496
1533
|
inspectBmadCache,
|
|
1497
1534
|
classifyRecompileFailure,
|
|
1498
1535
|
};
|
package/lib/installer.js
CHANGED
|
@@ -217,6 +217,15 @@ function getPackageVersion() {
|
|
|
217
217
|
return pkg.version;
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
function getBmadVersion() {
|
|
221
|
+
try {
|
|
222
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'bmad-method', 'package.json'), 'utf-8'));
|
|
223
|
+
return pkg.version;
|
|
224
|
+
} catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
220
229
|
// --- Manifest functions ---
|
|
221
230
|
|
|
222
231
|
function readManifest(installPath) {
|
|
@@ -233,6 +242,9 @@ function readManifest(installPath) {
|
|
|
233
242
|
|
|
234
243
|
function writeManifest(installPath, manifest) {
|
|
235
244
|
const manifestPath = path.join(installPath, MANIFEST_FILE);
|
|
245
|
+
// Stamp version fields on every write so they always reflect the last run.
|
|
246
|
+
manifest.toolVersion = getPackageVersion();
|
|
247
|
+
manifest.bmadVersion = getBmadVersion();
|
|
236
248
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
|
|
237
249
|
}
|
|
238
250
|
|
package/package.json
CHANGED
|
@@ -207,6 +207,36 @@ test('emits actionable error when offline and cache incomplete', () => {
|
|
|
207
207
|
}
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
+
test('classifyRecompileFailure() warns and continues on EBUSY (resource busy)', () => {
|
|
211
|
+
const prev = process.env.MA_AGENTS_OFFLINE;
|
|
212
|
+
delete process.env.MA_AGENTS_OFFLINE;
|
|
213
|
+
try {
|
|
214
|
+
const err = new Error('EBUSY: resource busy, rmdir \'C:\\\\project\\\\_bmad\\\\agents\'');
|
|
215
|
+
const result = bmad.classifyRecompileFailure(err, { cacheInspector: intactCache });
|
|
216
|
+
assert.strictEqual(result.action, 'warn');
|
|
217
|
+
assert.strictEqual(result.reason, 'ebusy');
|
|
218
|
+
assert.ok(result.message.includes('EBUSY') || result.message.includes('file-lock'),
|
|
219
|
+
'message should mention file-lock or EBUSY');
|
|
220
|
+
} finally {
|
|
221
|
+
if (prev !== undefined) process.env.MA_AGENTS_OFFLINE = prev;
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('classifyRecompileFailure() warns and continues on EBUSY (resource busy or locked)', () => {
|
|
226
|
+
const prev = process.env.MA_AGENTS_OFFLINE;
|
|
227
|
+
delete process.env.MA_AGENTS_OFFLINE;
|
|
228
|
+
try {
|
|
229
|
+
const err = new Error('EBUSY: resource busy or locked, rmdir \'C:\\\\project\\\\_bmad\'');
|
|
230
|
+
const result = bmad.classifyRecompileFailure(err, { cacheInspector: intactCache });
|
|
231
|
+
assert.strictEqual(result.action, 'warn');
|
|
232
|
+
assert.strictEqual(result.reason, 'ebusy');
|
|
233
|
+
assert.ok(result.message.includes('EBUSY') || result.message.includes('file-lock'),
|
|
234
|
+
'message should mention file-lock or EBUSY');
|
|
235
|
+
} finally {
|
|
236
|
+
if (prev !== undefined) process.env.MA_AGENTS_OFFLINE = prev;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
210
240
|
// ---- inspectBmadCache (lightweight integration — uses real manifest) ----
|
|
211
241
|
|
|
212
242
|
console.log('\ninspectBmadCache()');
|