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 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 Instructions**: Injects planning hints into agent files (e.g., `CLAUDE.md`, `.clinerules`) to ensure the agent uses the skills.
40
- 7. **BMAD-METHOD Integration**: Automatically detects and integrates with [BMAD-METHOD](https://docs.bmad-method.org/), applying project-specific customizations and orchestration.
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 paths
594
- │ ├── installer.js # Skill discovery and installation logic
595
- │ ├── bmad.js # BMAD-METHOD integration and injection logic
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
- * When MA_AGENTS_LOG_ACTIVE is set, uses pipe mode so the
36
- * tee hooks in cli.js capture subprocess output in the log file.
37
- * Otherwise falls back to inherit for direct pass-through.
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
- if (process.env.MA_AGENTS_LOG_ACTIVE) {
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
- } else {
46
- execSync(command, { ...options, stdio: 'inherit' });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ma-agents",
3
- "version": "3.6.0",
3
+ "version": "3.6.2",
4
4
  "description": "NPX tool to install skills for AI coding agents (Claude Code, Gemini, Copilot, Kilocode, Cline, Cursor, Roo Code)",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -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()');