ma-agents 3.16.0 → 3.17.0-beta.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 (141) hide show
  1. package/README.md +85 -4
  2. package/bin/cli.js +323 -6
  3. package/lib/agents.js +22 -15
  4. package/lib/bmad-cache/cache-manifest.json +2 -2
  5. package/lib/bmad-extension/.claude-plugin/marketplace.json.template +2 -15
  6. package/lib/bmad-extension/skills/module-help.csv +0 -13
  7. package/lib/bmad-extension-plugin/.claude-plugin/marketplace.json +3 -16
  8. package/lib/bmad-extension-plugin/skills/module-help.csv +0 -13
  9. package/lib/bmad.js +116 -1
  10. package/lib/custom-marketplace.js +507 -0
  11. package/lib/installer.js +19 -7
  12. package/lib/knowledge-atlas/dist/atlas.cjs +14651 -14651
  13. package/lib/knowledge-atlas/src/render/vendor.ts +43 -43
  14. package/lib/knowledge-atlas/vendor/drawio-viewer.min.js +7563 -7563
  15. package/lib/knowledge-atlas/vendor/mermaid.min.js +2314 -2314
  16. package/lib/profile.js +201 -0
  17. package/lib/skill-authoring.js +3 -1
  18. package/package.json +3 -3
  19. package/scripts/build-bmad-cache.js +127 -43
  20. package/lib/bmad-extension/skills/ma-agent-ml/.gitkeep +0 -0
  21. package/lib/bmad-extension/skills/ma-agent-ml/SKILL.md +0 -59
  22. package/lib/bmad-extension/skills/ma-agent-ml/bmad-skill-manifest.yaml +0 -11
  23. package/lib/bmad-extension/skills/ml-advise/.gitkeep +0 -0
  24. package/lib/bmad-extension/skills/ml-advise/SKILL.md +0 -76
  25. package/lib/bmad-extension/skills/ml-advise/bmad-skill-manifest.yaml +0 -3
  26. package/lib/bmad-extension/skills/ml-advise/skill.json +0 -7
  27. package/lib/bmad-extension/skills/ml-analysis/.gitkeep +0 -0
  28. package/lib/bmad-extension/skills/ml-analysis/SKILL.md +0 -60
  29. package/lib/bmad-extension/skills/ml-analysis/bmad-skill-manifest.yaml +0 -3
  30. package/lib/bmad-extension/skills/ml-analysis/skill.json +0 -7
  31. package/lib/bmad-extension/skills/ml-architecture/.gitkeep +0 -0
  32. package/lib/bmad-extension/skills/ml-architecture/SKILL.md +0 -55
  33. package/lib/bmad-extension/skills/ml-architecture/bmad-skill-manifest.yaml +0 -3
  34. package/lib/bmad-extension/skills/ml-architecture/skill.json +0 -7
  35. package/lib/bmad-extension/skills/ml-detailed-design/.gitkeep +0 -0
  36. package/lib/bmad-extension/skills/ml-detailed-design/SKILL.md +0 -67
  37. package/lib/bmad-extension/skills/ml-detailed-design/bmad-skill-manifest.yaml +0 -3
  38. package/lib/bmad-extension/skills/ml-detailed-design/skill.json +0 -7
  39. package/lib/bmad-extension/skills/ml-eda/.gitkeep +0 -0
  40. package/lib/bmad-extension/skills/ml-eda/SKILL.md +0 -56
  41. package/lib/bmad-extension/skills/ml-eda/bmad-skill-manifest.yaml +0 -3
  42. package/lib/bmad-extension/skills/ml-eda/scripts/baseline_classifier.py +0 -522
  43. package/lib/bmad-extension/skills/ml-eda/scripts/class_weights_calculator.py +0 -295
  44. package/lib/bmad-extension/skills/ml-eda/scripts/clustering_explorer.py +0 -383
  45. package/lib/bmad-extension/skills/ml-eda/scripts/eda_analyzer.py +0 -654
  46. package/lib/bmad-extension/skills/ml-eda/skill.json +0 -7
  47. package/lib/bmad-extension/skills/ml-experiment/.gitkeep +0 -0
  48. package/lib/bmad-extension/skills/ml-experiment/SKILL.md +0 -74
  49. package/lib/bmad-extension/skills/ml-experiment/assets/advanced_trainer_configs.py +0 -430
  50. package/lib/bmad-extension/skills/ml-experiment/assets/quick_trainer_setup.py +0 -233
  51. package/lib/bmad-extension/skills/ml-experiment/assets/template_datamodule.py +0 -219
  52. package/lib/bmad-extension/skills/ml-experiment/assets/template_gnn_module.py +0 -341
  53. package/lib/bmad-extension/skills/ml-experiment/assets/template_lightning_module.py +0 -158
  54. package/lib/bmad-extension/skills/ml-experiment/bmad-skill-manifest.yaml +0 -3
  55. package/lib/bmad-extension/skills/ml-experiment/skill.json +0 -7
  56. package/lib/bmad-extension/skills/ml-hparam/.gitkeep +0 -0
  57. package/lib/bmad-extension/skills/ml-hparam/SKILL.md +0 -81
  58. package/lib/bmad-extension/skills/ml-hparam/bmad-skill-manifest.yaml +0 -3
  59. package/lib/bmad-extension/skills/ml-hparam/skill.json +0 -7
  60. package/lib/bmad-extension/skills/ml-ideation/.gitkeep +0 -0
  61. package/lib/bmad-extension/skills/ml-ideation/SKILL.md +0 -50
  62. package/lib/bmad-extension/skills/ml-ideation/bmad-skill-manifest.yaml +0 -3
  63. package/lib/bmad-extension/skills/ml-ideation/scripts/validate_ml_prd.py +0 -287
  64. package/lib/bmad-extension/skills/ml-ideation/skill.json +0 -7
  65. package/lib/bmad-extension/skills/ml-infra/.gitkeep +0 -0
  66. package/lib/bmad-extension/skills/ml-infra/SKILL.md +0 -58
  67. package/lib/bmad-extension/skills/ml-infra/bmad-skill-manifest.yaml +0 -3
  68. package/lib/bmad-extension/skills/ml-infra/skill.json +0 -7
  69. package/lib/bmad-extension/skills/ml-retrospective/.gitkeep +0 -0
  70. package/lib/bmad-extension/skills/ml-retrospective/SKILL.md +0 -63
  71. package/lib/bmad-extension/skills/ml-retrospective/bmad-skill-manifest.yaml +0 -3
  72. package/lib/bmad-extension/skills/ml-retrospective/skill.json +0 -7
  73. package/lib/bmad-extension/skills/ml-revision/.gitkeep +0 -0
  74. package/lib/bmad-extension/skills/ml-revision/SKILL.md +0 -82
  75. package/lib/bmad-extension/skills/ml-revision/bmad-skill-manifest.yaml +0 -3
  76. package/lib/bmad-extension/skills/ml-revision/skill.json +0 -7
  77. package/lib/bmad-extension/skills/ml-techspec/.gitkeep +0 -0
  78. package/lib/bmad-extension/skills/ml-techspec/SKILL.md +0 -80
  79. package/lib/bmad-extension/skills/ml-techspec/bmad-skill-manifest.yaml +0 -3
  80. package/lib/bmad-extension/skills/ml-techspec/skill.json +0 -7
  81. package/lib/bmad-extension-plugin/skills/ma-agent-ml/.gitkeep +0 -0
  82. package/lib/bmad-extension-plugin/skills/ma-agent-ml/SKILL.md +0 -59
  83. package/lib/bmad-extension-plugin/skills/ma-agent-ml/bmad-skill-manifest.yaml +0 -11
  84. package/lib/bmad-extension-plugin/skills/ml-advise/.gitkeep +0 -0
  85. package/lib/bmad-extension-plugin/skills/ml-advise/SKILL.md +0 -76
  86. package/lib/bmad-extension-plugin/skills/ml-advise/bmad-skill-manifest.yaml +0 -3
  87. package/lib/bmad-extension-plugin/skills/ml-advise/skill.json +0 -7
  88. package/lib/bmad-extension-plugin/skills/ml-analysis/.gitkeep +0 -0
  89. package/lib/bmad-extension-plugin/skills/ml-analysis/SKILL.md +0 -60
  90. package/lib/bmad-extension-plugin/skills/ml-analysis/bmad-skill-manifest.yaml +0 -3
  91. package/lib/bmad-extension-plugin/skills/ml-analysis/skill.json +0 -7
  92. package/lib/bmad-extension-plugin/skills/ml-architecture/.gitkeep +0 -0
  93. package/lib/bmad-extension-plugin/skills/ml-architecture/SKILL.md +0 -55
  94. package/lib/bmad-extension-plugin/skills/ml-architecture/bmad-skill-manifest.yaml +0 -3
  95. package/lib/bmad-extension-plugin/skills/ml-architecture/skill.json +0 -7
  96. package/lib/bmad-extension-plugin/skills/ml-detailed-design/.gitkeep +0 -0
  97. package/lib/bmad-extension-plugin/skills/ml-detailed-design/SKILL.md +0 -67
  98. package/lib/bmad-extension-plugin/skills/ml-detailed-design/bmad-skill-manifest.yaml +0 -3
  99. package/lib/bmad-extension-plugin/skills/ml-detailed-design/skill.json +0 -7
  100. package/lib/bmad-extension-plugin/skills/ml-eda/.gitkeep +0 -0
  101. package/lib/bmad-extension-plugin/skills/ml-eda/SKILL.md +0 -56
  102. package/lib/bmad-extension-plugin/skills/ml-eda/bmad-skill-manifest.yaml +0 -3
  103. package/lib/bmad-extension-plugin/skills/ml-eda/scripts/baseline_classifier.py +0 -522
  104. package/lib/bmad-extension-plugin/skills/ml-eda/scripts/class_weights_calculator.py +0 -295
  105. package/lib/bmad-extension-plugin/skills/ml-eda/scripts/clustering_explorer.py +0 -383
  106. package/lib/bmad-extension-plugin/skills/ml-eda/scripts/eda_analyzer.py +0 -654
  107. package/lib/bmad-extension-plugin/skills/ml-eda/skill.json +0 -7
  108. package/lib/bmad-extension-plugin/skills/ml-experiment/.gitkeep +0 -0
  109. package/lib/bmad-extension-plugin/skills/ml-experiment/SKILL.md +0 -74
  110. package/lib/bmad-extension-plugin/skills/ml-experiment/assets/advanced_trainer_configs.py +0 -430
  111. package/lib/bmad-extension-plugin/skills/ml-experiment/assets/quick_trainer_setup.py +0 -233
  112. package/lib/bmad-extension-plugin/skills/ml-experiment/assets/template_datamodule.py +0 -219
  113. package/lib/bmad-extension-plugin/skills/ml-experiment/assets/template_gnn_module.py +0 -341
  114. package/lib/bmad-extension-plugin/skills/ml-experiment/assets/template_lightning_module.py +0 -158
  115. package/lib/bmad-extension-plugin/skills/ml-experiment/bmad-skill-manifest.yaml +0 -3
  116. package/lib/bmad-extension-plugin/skills/ml-experiment/skill.json +0 -7
  117. package/lib/bmad-extension-plugin/skills/ml-hparam/.gitkeep +0 -0
  118. package/lib/bmad-extension-plugin/skills/ml-hparam/SKILL.md +0 -81
  119. package/lib/bmad-extension-plugin/skills/ml-hparam/bmad-skill-manifest.yaml +0 -3
  120. package/lib/bmad-extension-plugin/skills/ml-hparam/skill.json +0 -7
  121. package/lib/bmad-extension-plugin/skills/ml-ideation/.gitkeep +0 -0
  122. package/lib/bmad-extension-plugin/skills/ml-ideation/SKILL.md +0 -50
  123. package/lib/bmad-extension-plugin/skills/ml-ideation/bmad-skill-manifest.yaml +0 -3
  124. package/lib/bmad-extension-plugin/skills/ml-ideation/scripts/validate_ml_prd.py +0 -287
  125. package/lib/bmad-extension-plugin/skills/ml-ideation/skill.json +0 -7
  126. package/lib/bmad-extension-plugin/skills/ml-infra/.gitkeep +0 -0
  127. package/lib/bmad-extension-plugin/skills/ml-infra/SKILL.md +0 -58
  128. package/lib/bmad-extension-plugin/skills/ml-infra/bmad-skill-manifest.yaml +0 -3
  129. package/lib/bmad-extension-plugin/skills/ml-infra/skill.json +0 -7
  130. package/lib/bmad-extension-plugin/skills/ml-retrospective/.gitkeep +0 -0
  131. package/lib/bmad-extension-plugin/skills/ml-retrospective/SKILL.md +0 -63
  132. package/lib/bmad-extension-plugin/skills/ml-retrospective/bmad-skill-manifest.yaml +0 -3
  133. package/lib/bmad-extension-plugin/skills/ml-retrospective/skill.json +0 -7
  134. package/lib/bmad-extension-plugin/skills/ml-revision/.gitkeep +0 -0
  135. package/lib/bmad-extension-plugin/skills/ml-revision/SKILL.md +0 -82
  136. package/lib/bmad-extension-plugin/skills/ml-revision/bmad-skill-manifest.yaml +0 -3
  137. package/lib/bmad-extension-plugin/skills/ml-revision/skill.json +0 -7
  138. package/lib/bmad-extension-plugin/skills/ml-techspec/.gitkeep +0 -0
  139. package/lib/bmad-extension-plugin/skills/ml-techspec/SKILL.md +0 -80
  140. package/lib/bmad-extension-plugin/skills/ml-techspec/bmad-skill-manifest.yaml +0 -3
  141. package/lib/bmad-extension-plugin/skills/ml-techspec/skill.json +0 -7
package/lib/profile.js CHANGED
@@ -19,6 +19,7 @@
19
19
 
20
20
  const fs = require('fs');
21
21
  const path = require('path');
22
+ const chalk = require('chalk');
22
23
 
23
24
  const MANIFEST_FILE = '.ma-agents.json';
24
25
  const VALID_PROFILES = ['on-prem', 'standard'];
@@ -211,6 +212,201 @@ function setBmadModules(projectRoot, modules) {
211
212
  fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
212
213
  }
213
214
 
215
+ /**
216
+ * Story 30.4 (FR245/FR246, NFR49) — generic one-time notice tracking.
217
+ *
218
+ * Notices are keyed by an arbitrary string id and persisted under a `notices`
219
+ * object in the project-root `.ma-agents.json` (`{ notices: { <id>: true } }`).
220
+ * This mirrors the `profile`/`bmadModules` field-ownership pattern above:
221
+ * read/write is scoped to this module, bootstrap-on-absence uses the same
222
+ * profile-only `bootstrapRootManifest()` shape, and no other field is
223
+ * disturbed. A notice is meant to be shown to the user exactly once, ever,
224
+ * regardless of how many times install/update subsequently runs.
225
+ */
226
+
227
+ /**
228
+ * Returns true if the given notice id has already been recorded as shown.
229
+ * Missing/malformed manifest, or a manifest with no matching entry, both
230
+ * return false (never shown) — mirrors getProfile's non-throwing contract.
231
+ */
232
+ function hasShownNotice(projectRoot, noticeId) {
233
+ const manifestPath = path.join(projectRoot, MANIFEST_FILE);
234
+ if (!fs.existsSync(manifestPath)) return false;
235
+ let manifest;
236
+ try {
237
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
238
+ } catch {
239
+ return false;
240
+ }
241
+ if (!manifest || typeof manifest !== 'object') return false;
242
+ return !!(manifest.notices && manifest.notices[noticeId] === true);
243
+ }
244
+
245
+ /**
246
+ * Persists that the given notice id has been shown, preserving all other
247
+ * `.ma-agents.json` fields (profile, bmadModules, profileHistory, ...).
248
+ * Bootstraps a profile-only root manifest if none exists yet (mirrors
249
+ * setProfile/setBmadModules).
250
+ */
251
+ function markNoticeShown(projectRoot, noticeId) {
252
+ const manifestPath = path.join(projectRoot, MANIFEST_FILE);
253
+ let manifest;
254
+ if (fs.existsSync(manifestPath)) {
255
+ try {
256
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
257
+ } catch {
258
+ manifest = null;
259
+ }
260
+ }
261
+ if (!manifest || typeof manifest !== 'object') {
262
+ manifest = bootstrapRootManifest();
263
+ }
264
+ // Guard: `typeof null === 'object'` is already caught by `!manifest.notices`,
265
+ // but `typeof [] === 'object'` is NOT — a foreign tool that wrote
266
+ // `notices: []` would otherwise keep an array here, and a named property set
267
+ // on an array is dropped by JSON.stringify, silently defeating the
268
+ // show-once guarantee. Reject any non-plain-object shape.
269
+ if (!manifest.notices || typeof manifest.notices !== 'object' || Array.isArray(manifest.notices)) {
270
+ manifest.notices = {};
271
+ }
272
+ manifest.notices[noticeId] = true;
273
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
274
+ }
275
+
276
+ /**
277
+ * Story 30.7 (FR242) — persisted custom-marketplace source + selection.
278
+ *
279
+ * Stored under the `customMarketplace` key (a single object, NOT nested under
280
+ * `bmadModules`/`profile`) so a future multi-source design can introduce a
281
+ * sibling array field (e.g. `customMarketplaceSources`) without a breaking
282
+ * migration of this one. v1 supports exactly ONE marketplace source, per
283
+ * FR242 / architecture Decision P5-8.
284
+ *
285
+ * Shape:
286
+ * {
287
+ * source: string, // original user input (local path or Git URL)
288
+ * sourceUrl: string|null, // resolved clone URL (Git sources) or null (local)
289
+ * mode: 'discovery'|'direct',
290
+ * selectedCodes: string[],
291
+ * sha: string|null, // resolved commit SHA pin (Git-URL sources only)
292
+ * updatedAt: string, // ISO timestamp of the last persist
293
+ * }
294
+ */
295
+
296
+ /**
297
+ * Reads the persisted custom-marketplace selection from `.ma-agents.json`.
298
+ * Returns undefined if the file is absent/malformed or the `customMarketplace`
299
+ * field is missing/malformed — mirrors getBmadModules' non-throwing contract.
300
+ */
301
+ function getCustomMarketplaceSelection(projectRoot) {
302
+ const manifestPath = path.join(projectRoot, MANIFEST_FILE);
303
+ if (!fs.existsSync(manifestPath)) return undefined;
304
+ let manifest;
305
+ try {
306
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
307
+ } catch {
308
+ return undefined;
309
+ }
310
+ if (!manifest || typeof manifest !== 'object') return undefined;
311
+ const entry = manifest.customMarketplace;
312
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return undefined;
313
+ if (typeof entry.source !== 'string' || !entry.source) return undefined;
314
+ if (!Array.isArray(entry.selectedCodes)) return undefined;
315
+ return entry;
316
+ }
317
+
318
+ /**
319
+ * Persists the custom-marketplace source + selection into `.ma-agents.json`,
320
+ * preserving all other fields (profile, bmadModules, notices, ...). Creates
321
+ * the file via the standard profile-only bootstrap path if absent (mirrors
322
+ * setProfile/setBmadModules). Stamps `updatedAt` with the current time.
323
+ *
324
+ * @param {string} projectRoot
325
+ * @param {Object} entry
326
+ * @param {string} entry.source - Original source string (local path or Git URL).
327
+ * @param {string|null} [entry.sourceUrl] - Resolved clone URL (Git sources) or null.
328
+ * @param {'discovery'|'direct'} entry.mode
329
+ * @param {string[]} entry.selectedCodes
330
+ * @param {string|null} [entry.sha] - Commit SHA of the LAST-INSTALLED marketplace
331
+ * state (Git-URL sources only; null for local paths). This is INFORMATIONAL —
332
+ * it records what was installed, not a lock. Per FR242/NFR72, `update`
333
+ * deliberately re-enumerates the source ref and floats to remote HEAD to offer
334
+ * newly-published extensions; it does NOT check out this SHA. reconcile surfaces
335
+ * this last-installed SHA and the float-to-HEAD behavior (see
336
+ * reconcileCustomMarketplaceOnUpdate) but never pins/reverts to it.
337
+ */
338
+ function setCustomMarketplaceSelection(projectRoot, entry) {
339
+ if (!entry || typeof entry !== 'object') {
340
+ throw new Error('setCustomMarketplaceSelection expects an entry object');
341
+ }
342
+ if (typeof entry.source !== 'string' || !entry.source) {
343
+ throw new Error('setCustomMarketplaceSelection requires a non-empty "source" string');
344
+ }
345
+ if (entry.mode !== 'discovery' && entry.mode !== 'direct') {
346
+ throw new Error(`setCustomMarketplaceSelection requires mode 'discovery' or 'direct' (got ${JSON.stringify(entry.mode)})`);
347
+ }
348
+ if (!Array.isArray(entry.selectedCodes)) {
349
+ throw new Error('setCustomMarketplaceSelection requires "selectedCodes" to be an array');
350
+ }
351
+
352
+ const manifestPath = path.join(projectRoot, MANIFEST_FILE);
353
+ let manifest;
354
+ if (fs.existsSync(manifestPath)) {
355
+ try {
356
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
357
+ } catch {
358
+ manifest = null;
359
+ }
360
+ }
361
+ if (!manifest || typeof manifest !== 'object') {
362
+ manifest = bootstrapRootManifest();
363
+ }
364
+
365
+ manifest.customMarketplace = {
366
+ source: entry.source,
367
+ sourceUrl: entry.sourceUrl ?? null,
368
+ mode: entry.mode,
369
+ selectedCodes: [...new Set(entry.selectedCodes.filter(c => typeof c === 'string' && c.length > 0))],
370
+ sha: entry.sha ?? null,
371
+ updatedAt: new Date().toISOString(),
372
+ };
373
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
374
+ }
375
+
376
+ const DEMERZEL_DEPRECATION_NOTICE_ID = 'demerzel-deprecation';
377
+
378
+ /**
379
+ * FR245/FR246 — idempotent one-time deprecation notice for the Demerzel
380
+ * (ML Scientist) agent. Prints an informational message the first time it is
381
+ * called for a given project root, then persists a `shown` flag so every
382
+ * subsequent call (any future install/update run) is a silent no-op.
383
+ *
384
+ * Deliberately does NOT touch any already-installed `ma-agent-ml`/`ml-*`
385
+ * content — that is the job of `migrateRetiredSkills()` for genuinely
386
+ * retired/renamed skills, and `ma-agent-ml`/`ml-*` are intentionally excluded
387
+ * from that sweep (NFR49 — existing installs are non-destructively preserved).
388
+ * Names no replacement target (FR246 — no in-tool migration).
389
+ *
390
+ * @param {string} projectRoot
391
+ * @returns {boolean} true if the notice was printed on this call, false if
392
+ * it had already been shown previously (no-op).
393
+ */
394
+ function showDemerzelDeprecationNoticeOnce(projectRoot) {
395
+ if (hasShownNotice(projectRoot, DEMERZEL_DEPRECATION_NOTICE_ID)) {
396
+ return false;
397
+ }
398
+ console.log(chalk.yellow(
399
+ '\n Notice: the Demerzel (ML Scientist) agent - ma-agent-ml plus the 12 ml-*\n' +
400
+ ' workflow skills - is deprecated and is no longer installed by default for\n' +
401
+ ' new projects (FR244-FR247). Already-installed content on this or any other\n' +
402
+ ' project is left untouched; no action is required. Teams that still need ML\n' +
403
+ ' workflows can install a replacement from a custom BMAD marketplace source\n' +
404
+ ' (see --bmad-custom-source). This message is shown once.\n'
405
+ ));
406
+ markNoticeShown(projectRoot, DEMERZEL_DEPRECATION_NOTICE_ID);
407
+ return true;
408
+ }
409
+
214
410
  module.exports = {
215
411
  getProfile,
216
412
  setProfile,
@@ -218,4 +414,9 @@ module.exports = {
218
414
  clearProfile,
219
415
  getBmadModules,
220
416
  setBmadModules,
417
+ hasShownNotice,
418
+ markNoticeShown,
419
+ showDemerzelDeprecationNoticeOnce,
420
+ getCustomMarketplaceSelection,
421
+ setCustomMarketplaceSelection,
221
422
  };
@@ -405,7 +405,9 @@ async function handleSetMandatory(args) {
405
405
 
406
406
  // ─── Story 3.4: BMAD Persona Customization Tooling ───────────────────────────
407
407
 
408
- const CUSTOM_AGENTS = ['bmm-sre', 'bmm-devops', 'bmm-cyber', 'bmm-qa', 'bmm-demerzel'];
408
+ // Story 30.4 / FR244-FR247: 'bmm-demerzel' removed — Demerzel is deprecated for
409
+ // new installs (see lib/agents.js for the full rationale).
410
+ const CUSTOM_AGENTS = ['bmm-sre', 'bmm-devops', 'bmm-cyber', 'bmm-qa'];
409
411
  const BUILTIN_AGENTS = ['bmm-pm', 'bmm-architect', 'bmm-dev', 'bmm-sm', 'bmm-tech-writer', 'bmm-ux-designer'];
410
412
 
411
413
  const MANDATORY_CRITICAL_ACTIONS = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ma-agents",
3
- "version": "3.16.0",
3
+ "version": "3.17.0-beta.0",
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": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "start": "node bin/cli.js",
11
- "test": "node test/yes-flag.test.js && node test/skill-authoring.test.js && node test/skill-validation.test.js && node test/skill-mandatory.test.js && node test/skill-customize-agent.test.js && node test/create-agent.test.js && node test/generate-project-context.test.js && node test/build-bmad-args.test.js && node test/build-bmad-cache-reader.test.js && node test/bmad-version-bump.test.js && node test/uv-transition-resilience.test.js && node test/passthrough-reconciliation-6-9-0.test.js && node test/extension-module-restructure.test.js && node test/convert-agents-to-skills.test.js && node test/migration.test.js && node test/migration-validation.test.js && node test/story-15-5-workflow-skills.test.js && node test/repo-layout.test.js && node test/config-storage.test.js && node test/cross-repo-validation.test.js && node test/config-lost-on-update.test.js && node test/portable-paths.test.js && node test/cicd-remote-mode.test.js && node test/config-layout.test.js && node test/roo-code-agent.test.js && node test/roo-code-injection.test.js && node test/profile.test.js && node test/instruction-block.test.js && node test/instruction-injection.test.js && node test/agents-md.test.js && node test/instruction-block-git.test.js && node test/onprem-injection.test.js && node test/onprem-layer.test.js && node test/copilot-instructions-path.test.js && node test/roomodes.test.js && node test/clinerules.test.js && node test/cline-workflow-reconciliation.test.js && node test/reconfigure.test.js && node test/experimental-warning.test.js && node test/bmad-persona-phase-prefix.test.js && node test/f1a-on-prem-prefix-post-install.test.js && node test/retired-skills-migration.test.js && node test/rename-migration.test.js && node test/uninstall.test.js && node test/offline-recompile.test.js && node test/plugin-manifests.test.js && node test/build-plugin.test.js && node test/config-path-migration.test.js && node test/gitignore-plugin-stage.test.js && node test/customizations-translation.test.js && node test/routing-parity.test.js && node test/bmad-extension.test.js && node test/integration-verification.test.js && node test/agent-scope.test.js && node test/obsolete-tool-dirs.test.js && node test/bmad-empty-tools-guard.test.js && node test/v68-pluginresolver-enumeration-passthrough.test.js && node test/agents.test.js && node test/installer.test.js && node test/bmad.test.js && node test/cli.test.js && node test/knowledge-atlas-smoke.test.js && node test/knowledge-atlas-manifest.test.js && node test/knowledge-atlas-render.test.js && node test/knowledge-atlas-graph.test.js && node test/knowledge-atlas-packaging.test.js && node test/knowledge-atlas-offline-safety.test.js && node test/knowledge-atlas-reconcile.test.js && node test/knowledge-atlas-determinism.test.js && node test/knowledge-atlas-degradation.test.js && node test/knowledge-atlas-realproject.test.js && node test/knowledge-atlas-link-integrity.test.js && node test/knowledge-atlas-config-scan.test.js && node test/knowledge-atlas-references.test.js",
11
+ "test": "node test/yes-flag.test.js && node test/skill-authoring.test.js && node test/skill-validation.test.js && node test/skill-mandatory.test.js && node test/skill-customize-agent.test.js && node test/create-agent.test.js && node test/generate-project-context.test.js && node test/build-bmad-args.test.js && node test/build-bmad-cache-reader.test.js && node test/bundle-filter-set-equality.test.js && node test/bmad-version-bump.test.js && node test/staging-6-10-0-regression.test.js && node test/uv-transition-resilience.test.js && node test/passthrough-reconciliation-6-9-0.test.js && node test/extension-module-restructure.test.js && node test/convert-agents-to-skills.test.js && node test/migration.test.js && node test/migration-validation.test.js && node test/story-15-5-workflow-skills.test.js && node test/repo-layout.test.js && node test/config-storage.test.js && node test/cross-repo-validation.test.js && node test/config-lost-on-update.test.js && node test/portable-paths.test.js && node test/cicd-remote-mode.test.js && node test/config-layout.test.js && node test/roo-code-agent.test.js && node test/roo-code-injection.test.js && node test/profile.test.js && node test/instruction-block.test.js && node test/instruction-injection.test.js && node test/agents-md.test.js && node test/instruction-block-git.test.js && node test/onprem-injection.test.js && node test/onprem-layer.test.js && node test/copilot-instructions-path.test.js && node test/roomodes.test.js && node test/clinerules.test.js && node test/cline-workflow-reconciliation.test.js && node test/reconfigure.test.js && node test/experimental-warning.test.js && node test/bmad-persona-phase-prefix.test.js && node test/f1a-on-prem-prefix-post-install.test.js && node test/retired-skills-migration.test.js && node test/rename-migration.test.js && node test/uninstall.test.js && node test/offline-recompile.test.js && node test/plugin-manifests.test.js && node test/demerzel-ml-removal-drift.test.js && node test/demerzel-deprecation-notice.test.js && node test/build-plugin.test.js && node test/config-path-migration.test.js && node test/gitignore-plugin-stage.test.js && node test/customizations-translation.test.js && node test/routing-parity.test.js && node test/bmad-extension.test.js && node test/integration-verification.test.js && node test/agent-scope.test.js && node test/obsolete-tool-dirs.test.js && node test/bmad-empty-tools-guard.test.js && node test/v68-pluginresolver-enumeration-passthrough.test.js && node test/agents.test.js && node test/installer.test.js && node test/bmad.test.js && node test/cli.test.js && node test/knowledge-atlas-smoke.test.js && node test/knowledge-atlas-manifest.test.js && node test/knowledge-atlas-render.test.js && node test/knowledge-atlas-graph.test.js && node test/knowledge-atlas-packaging.test.js && node test/knowledge-atlas-offline-safety.test.js && node test/knowledge-atlas-reconcile.test.js && node test/knowledge-atlas-determinism.test.js && node test/knowledge-atlas-degradation.test.js && node test/knowledge-atlas-realproject.test.js && node test/knowledge-atlas-link-integrity.test.js && node test/knowledge-atlas-config-scan.test.js && node test/knowledge-atlas-references.test.js && node test/agent-injection-strategy.test.js && node test/bmad-module-selection.test.js && node test/custom-marketplace-surface-drift.test.js && node test/custom-marketplace-wizard.test.js && node test/custom-marketplace-live-clone.test.js && node test/custom-marketplace-stage-install.test.js && node test/custom-marketplace-persist-reconcile.test.js && node test/demerzel-nondestructive-invariant.test.js && node test/fr243-custom-source-allowlist-nonleak.test.js",
12
12
  "build:bmad-cache": "node scripts/build-bmad-cache.js",
13
13
  "build:plugin": "node scripts/build-plugin.js",
14
14
  "build:atlas": "node scripts/build-atlas.js",
@@ -32,7 +32,7 @@
32
32
  "author": "",
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
- "bmad-method": "6.9.0",
35
+ "bmad-method": "6.10.0",
36
36
  "chalk": "^4.1.2",
37
37
  "fs-extra": "^11.1.1",
38
38
  "js-yaml": "^4.1.1",
@@ -47,18 +47,44 @@ const REGISTRY_CANDIDATES = [
47
47
  { parts: ['tools', 'cli', 'external-official-modules.yaml'], schema: 'legacy' },
48
48
  ];
49
49
 
50
- // The set of modules ma-agents bundles for offline installs. At v6.8.0 these
51
- // are exactly the `type: bmad-org` registry entries. The fail-loud guard
52
- // (NFR56) treats a resolved set SMALLER than this as a thin-cache failure —
53
- // the registry path has moved twice, so silent degradation is a recurring
54
- // risk that must abort the build rather than ship an incomplete cache.
50
+ // The set of modules ma-agents bundles for offline installs the CLOSED
51
+ // ALLOWLIST. `normalizeModules()` includes ONLY these codes; every other
52
+ // registry entry is dropped, regardless of its `type`. This is the load-bearing
53
+ // safety posture (P5-8 / FR235): at v6.10.0 the registry gained `bmad-loop`
54
+ // (`type: bmad-org`, `marketplace-plugin: true`, air-gap-hostile) and marked
55
+ // `bmad-automator` `deprecated: true`. An open-by-default "keep every
56
+ // `type: bmad-org`" filter would silently force-bundle `bmad-loop` (and any
57
+ // FUTURE plain `bmad-org` module) onto air-gapped machines. Closing to this
58
+ // allowlist means new/renamed/re-flagged upstream modules FAIL CLOSED (excluded)
59
+ // rather than fail open.
55
60
  const KNOWN_BUNDLED_MODULES = ['tea', 'bmb', 'cis', 'gds', 'wds'];
56
61
 
57
- // Registry `type` values that mark a module for bundling. `bmad-org` modules
58
- // are first-party expansions ma-agents clones; `experimental` (e.g. automator)
59
- // is deliberately excluded from the offline bundle.
62
+ // The registry `type` first-party expansions carry. Retained for diagnostics
63
+ // and for the shape-flip assertion below (an allowlisted module is expected to
64
+ // stay a plain `bmad-org` module). It is NO LONGER the include criterion — the
65
+ // allowlist above is. `experimental` (automator) and `marketplace-plugin`
66
+ // modules (bmad-loop) are excluded simply by not being on the allowlist.
60
67
  const BUNDLED_MODULE_TYPE = 'bmad-org';
61
68
 
69
+ // Registry traits that DISQUALIFY a module from the offline bundle. If a module
70
+ // that IS on the allowlist ever starts carrying one of these (an upstream
71
+ // shape/status flip on a module we bundle), the build HALTS rather than silently
72
+ // shipping it — see the assertion in normalizeModules(). `bmad-loop` carries
73
+ // `marketplace-plugin: true`; deprecated modules carry `deprecated: true`.
74
+ const DISQUALIFYING_FLAGS = ['marketplace-plugin', 'deprecated'];
75
+
76
+ /**
77
+ * True when a parsed registry flag reads as boolean-true. The line parser
78
+ * stores YAML scalars as strings (`'true'`), but unit callers may pass a real
79
+ * boolean, so accept both. Anything else (absent, `'false'`, other) is false.
80
+ *
81
+ * @param {*} value Raw flag value off a parsed module entry.
82
+ * @returns {boolean}
83
+ */
84
+ function isFlagTrue(value) {
85
+ return value === true || String(value).trim().toLowerCase() === 'true';
86
+ }
87
+
62
88
  /**
63
89
  * Locate the bmad-method package on disk. Abort cleanly if missing so the
64
90
  * caller gets an actionable "run npm install" message instead of a MODULE_NOT_FOUND
@@ -179,20 +205,29 @@ function parseModulesYaml(content) {
179
205
  * Normalize parsed module entries into a uniform cache-ready shape:
180
206
  * { code, url, name, branch }
181
207
  *
208
+ * CLOSED-BY-DEFAULT (FR235). The filter is an ALLOWLIST: an entry survives ONLY
209
+ * if its `code` is in KNOWN_BUNDLED_MODULES (tea, bmb, cis, gds, wds). Every
210
+ * other registry entry is dropped no matter its `type` — so a NEW upstream
211
+ * module (even a plain `type: bmad-org` one carrying neither disqualifying
212
+ * flag), `bmad-loop` (`marketplace-plugin: true`), the deprecated
213
+ * `bmad-automator`, `experimental` modules, and the built-in `bmm`/`core` are
214
+ * ALL excluded by default. This replaces the prior open-by-default posture
215
+ * ("keep every `type: bmad-org`, subtract known-bad"), which would silently
216
+ * bundle any future first-party module the moment it appeared upstream.
217
+ *
218
+ * SHAPE-FLIP HALT. For each entry that IS on the allowlist, assert it carries
219
+ * NONE of DISQUALIFYING_FLAGS (`marketplace-plugin: true` / `deprecated: true`).
220
+ * If a bundled module ever flips shape/status upstream, THROW — a bundled
221
+ * module turning into a marketplace-plugin or getting deprecated must be
222
+ * re-reviewed by a maintainer, never shipped silently.
223
+ *
182
224
  * - Requires both `code` and `url`.
183
- * - Keeps ONLY `type: bmad-org` modules (tea, bmb, cis, gds, wds) and drops
184
- * everything else. This replaces the pre-6.8.0 `built_in` exclusion: the
185
- * relocated registry no longer carries a `built_in` flag and instead tags
186
- * each module with a `type`. `type: experimental` (e.g. `automator`) is
187
- * excluded from the offline bundle; `bmm`/`core` are first-party modules
188
- * that ship inside the npm package and are absent from the registry, so they
189
- * are dropped here too (no `bmad-org` type → filtered out). There is NO
190
- * `wds`-retired/skip path — `wds` is an active `bmad-org` clone at 6.8.0.
191
- * - Defaults `branch` to `main` when absent (the 6.8.0 schema omits it).
225
+ * - Defaults `branch` to `main` when absent (the 6.8.0+ schema omits it).
192
226
  * - Passes the URL through verbatim (git accepts both `repo` and `repo.git`).
193
227
  *
194
228
  * @param {Array<Object>} raw Entries from parseModulesYaml().
195
229
  * @returns {Array<{code: string, url: string, name: string, branch: string}>}
230
+ * @throws {Error} if an allowlisted entry carries a disqualifying flag.
196
231
  */
197
232
  // Module `code` values are used as directory names under lib/bmad-cache.
198
233
  // Reject anything that could escape the cache dir or inject shell characters;
@@ -200,25 +235,65 @@ function parseModulesYaml(content) {
200
235
  const MODULE_CODE_PATTERN = /^[a-zA-Z0-9_-]+$/;
201
236
 
202
237
  function normalizeModules(raw) {
238
+ const allowlist = new Set(KNOWN_BUNDLED_MODULES);
203
239
  return raw
204
240
  .filter(m => m.code && m.url)
205
- .filter(m => {
206
- // Bundle only `type: bmad-org`. Compare case-insensitively and trim so
207
- // minor upstream whitespace/casing drift cannot silently drop a module.
208
- const type = String(m.type || '').trim().toLowerCase();
209
- return type === BUNDLED_MODULE_TYPE;
210
- })
241
+ // CLOSED allowlist: include ONLY the known bundled codes. Anything not on
242
+ // the allowlist fails closed (excluded) the safety-critical inversion.
243
+ .filter(m => allowlist.has(m.code))
211
244
  .filter(m => {
212
245
  if (MODULE_CODE_PATTERN.test(m.code)) return true;
213
246
  console.warn(` ⚠ Skipping module with invalid code "${m.code}" (must match [a-zA-Z0-9_-]+)`);
214
247
  return false;
215
248
  })
216
- .map(m => ({
217
- code: m.code,
218
- url: m.url,
219
- name: m.name || m.key,
220
- branch: m.branch || 'main',
221
- }));
249
+ .map(m => {
250
+ // Shape-flip halt: a module we DO bundle must not have turned into a
251
+ // marketplace-plugin or been deprecated upstream. Fail loud, don't ship.
252
+ const tripped = DISQUALIFYING_FLAGS.filter(flag => isFlagTrue(m[flag]));
253
+ if (tripped.length > 0) {
254
+ throw new Error(
255
+ `Allowlisted bundled module "${m.code}" now carries disqualifying flag(s): ` +
256
+ `${tripped.map(f => `${f}: true`).join(', ')}. A bundled module changing shape/status ` +
257
+ `upstream must be re-reviewed before shipping (FR235) — refusing to build. ` +
258
+ `Remove it from KNOWN_BUNDLED_MODULES in scripts/build-bmad-cache.js, or coordinate ` +
259
+ `the upstream change, then rebuild.`
260
+ );
261
+ }
262
+ return {
263
+ code: m.code,
264
+ url: m.url,
265
+ name: m.name || m.key,
266
+ branch: m.branch || 'main',
267
+ };
268
+ });
269
+ }
270
+
271
+ /**
272
+ * Compare a RESOLVED bundle set (the codes normalizeModules() kept) against the
273
+ * closed allowlist and report both directions of drift (NFR73 two-sided
274
+ * set-EQUALITY). The resolved set must EQUAL the allowlist exactly.
275
+ *
276
+ * - `missing`: allowlisted codes the registry did NOT resolve — the thin-cache
277
+ * case (a bundled module vanished / the registry relocated / a downgrade).
278
+ * - `extra`: resolved codes NOT on the allowlist — a silent upstream addition
279
+ * (e.g. `bmad-loop`) that must never be force-bundled. Under the closed
280
+ * allowlist filter this is normally empty; the check is defense-in-depth so a
281
+ * future loosening of the filter can never ship an over-broad cache silently.
282
+ *
283
+ * Computed on the resolved/filtered set — NOT the final manifest keys, which
284
+ * the incremental-retention path deliberately augments with retired-upstream
285
+ * modules that are not on the allowlist (see AC #7 retention in main()).
286
+ *
287
+ * @param {Iterable<string>|Set<string>} resolvedCodes Codes kept by normalize.
288
+ * @param {Array<string>} [allowlist=KNOWN_BUNDLED_MODULES]
289
+ * @returns {{ missing: string[], extra: string[] }} Sorted; both empty = equal.
290
+ */
291
+ function diffBundleSet(resolvedCodes, allowlist = KNOWN_BUNDLED_MODULES) {
292
+ const allow = new Set(allowlist);
293
+ const resolved = resolvedCodes instanceof Set ? resolvedCodes : new Set(resolvedCodes);
294
+ const missing = [...allow].filter(code => !resolved.has(code)).sort();
295
+ const extra = [...resolved].filter(code => !allow.has(code)).sort();
296
+ return { missing, extra };
222
297
  }
223
298
 
224
299
  function loadModuleDefinitions() {
@@ -371,12 +446,15 @@ function main() {
371
446
 
372
447
  const { modules, schema } = loadModuleDefinitions();
373
448
 
374
- // Thin-cache fail-loud guard (NFR56). The registry path has moved twice;
375
- // the real risk is no longer "zero modules" but the path moving again and
376
- // the reader silently resolving FEWER than the known bundled set. Treat a
377
- // deficient set as a hard error and abort BEFORE writing any manifest — a
378
- // partial/empty cache-manifest.json would otherwise ship a broken offline
379
- // bundle that "looks" successful.
449
+ // Two-sided set-EQUALITY fail-loud guard (NFR73, strengthening NFR56). The
450
+ // resolved bundle set must EQUAL the known allowlist abort on a set that is
451
+ // either SMALLER (thin cache: a bundled module vanished / the registry moved /
452
+ // a downgrade below 6.8.0) OR LARGER (a silent upstream addition such as
453
+ // bmad-loop that must never be force-bundled). Abort BEFORE writing any
454
+ // manifest a partial or over-broad cache-manifest.json would otherwise ship
455
+ // a broken/air-gap-hostile offline bundle that "looks" successful. Computed on
456
+ // the RESOLVED set (not the final manifest keys — the retention path below
457
+ // augments the manifest with non-allowlist retired modules by design).
380
458
  const resolvedCodes = new Set(modules.map(m => m.code));
381
459
 
382
460
  // Duplicate-code guard. Two registry entries sharing a `code` would clone
@@ -395,15 +473,18 @@ function main() {
395
473
  process.exit(1);
396
474
  }
397
475
 
398
- const missing = KNOWN_BUNDLED_MODULES.filter(code => !resolvedCodes.has(code));
399
- if (missing.length > 0) {
400
- console.error('Error: registry resolved a thin module set — refusing to build a partial cache.');
401
- console.error(` Expected at least: ${KNOWN_BUNDLED_MODULES.join(', ')}`);
402
- console.error(` Resolved (type: ${BUNDLED_MODULE_TYPE}): ${[...resolvedCodes].join(', ') || '(none)'}`);
403
- console.error(` Missing: ${missing.join(', ')}`);
476
+ const { missing, extra } = diffBundleSet(resolvedCodes);
477
+ if (missing.length > 0 || extra.length > 0) {
478
+ console.error('Error: resolved bundle set does not EQUAL the known allowlist — refusing to build a');
479
+ console.error(' partial or over-broad cache (thin module set / silent upstream addition).');
480
+ console.error(` Allowlist (KNOWN_BUNDLED_MODULES): ${KNOWN_BUNDLED_MODULES.join(', ')}`);
481
+ console.error(` Resolved: ${[...resolvedCodes].sort().join(', ') || '(none)'}`);
482
+ console.error(` Missing: ${missing.join(', ') || '(none)'} (thin cache — a bundled module vanished/relocated)`);
483
+ console.error(` Extra: ${extra.join(', ') || '(none)'} (silent upstream addition — must not be force-bundled)`);
404
484
  console.error(' Likely cause: bmad-method was downgraded below 6.8.0, the registry layout moved');
405
- console.error(' again, or a bundled module was renamed/retired upstream. Update REGISTRY_CANDIDATES,');
406
- console.error(' the type filter, or KNOWN_BUNDLED_MODULES in scripts/build-bmad-cache.js to match.');
485
+ console.error(' again, or a bundled module was added/renamed/retired upstream. Update');
486
+ console.error(' REGISTRY_CANDIDATES or KNOWN_BUNDLED_MODULES in scripts/build-bmad-cache.js to match');
487
+ console.error(' and, for an EXTRA module, confirm it is safe to bundle offline before allowlisting it.');
407
488
  process.exit(1);
408
489
  }
409
490
  console.log(`Found ${modules.length} modules: ${modules.map(m => m.code).join(', ')}`);
@@ -587,9 +668,12 @@ if (require.main === module) {
587
668
  module.exports = {
588
669
  parseModulesYaml,
589
670
  normalizeModules,
671
+ diffBundleSet,
672
+ isFlagTrue,
590
673
  locateRegistry,
591
674
  KNOWN_BUNDLED_MODULES,
592
675
  BUNDLED_MODULE_TYPE,
676
+ DISQUALIFYING_FLAGS,
593
677
  REGISTRY_CANDIDATES,
594
678
  };
595
679
  }
File without changes
@@ -1,59 +0,0 @@
1
- ---
2
- name: ma-agent-ml
3
- description: Demerzel (ML Scientist) — Machine Learning Scientist specializing in falsifiable hypothesis validation and failure-cost analysis
4
- ---
5
-
6
- # Agent: Demerzel — ML Scientist
7
-
8
- You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.
9
-
10
- ## Persona
11
- - **Role:** Machine Learning Scientist specializing in falsifiable hypothesis validation and failure-cost analysis.
12
- - **Identity:** Named after Eto Demerzel from Isaac Asimov's Foundation series. Senior AI/ML Architect and Data Scientist committed to the scientific method. She refuses to skip to modelling before high-quality EDA and a locked TechSpec are in place.
13
- - **Communication Style:** Professional, precise, and hypothesis-driven. Uses statistical terminology correctly. Prioritizes failure modes and their domain costs.
14
- - **Principles:**
15
- - Scientific Rigor: No modelling without EDA. No modelling without a locked TechSpec.
16
- - Dependency Integrity: Always use uv for project reproducibility.
17
- - Failure Focus: Capture every failure mode and its domain cost.
18
-
19
- ## Activation Sequence
20
- 1. Load persona from this agent skill (already in context)
21
- 2. Load and read {project-root}/_bmad/bmm/config.yaml — store ALL fields as session variables: {user_name}, {communication_language}, {output_folder}. If config not loaded, STOP and report error to user.
22
- 3. Remember: user's name is {user_name}
23
- 4. Show greeting as Demerzel using {user_name} from config, communicate in {communication_language}, then display the ML Lifecycle stage menu.
24
- 5. Let {user_name} know they can type `/ml-advise` at any time to consult previous findings.
25
- 6. STOP and WAIT for user input — do NOT execute menu items automatically — accept number or cmd trigger or fuzzy command match
26
- 7. On user input: Number -> process menu item[n] | Text -> case-insensitive substring match | Multiple matches -> ask user to clarify | No match -> show "Not recognized"
27
- 8. When processing a menu item: load the referenced skill and follow its instructions
28
-
29
- ### Rules
30
- - ALWAYS communicate in {communication_language} UNLESS contradicted by communication_style.
31
- - Stay in character until exit selected.
32
- - After each stage approval, commit all new/modified files under `_bmad-output/` with: `git add _bmad-output/ && git commit -m "feat(ml): stage [N] complete - [stage-name]"`
33
- - Never commit before explicit user approval — the commit is the confirmation receipt.
34
- - Display menu items as the item dictates and in the order given.
35
-
36
- ## Menu
37
- | # | Cmd | Action | Trigger | Skill |
38
- |---|-----|--------|---------|-------|
39
- | 1 | MH | Redisplay Menu Help | "menu", "help" | _(built-in)_ |
40
- | 2 | CH | Chat with Demerzel about ML | "chat", "ml" | _(built-in)_ |
41
- | 3 | MLI | ML Ideation & PRD | "ideation", "prd" | ml-ideation |
42
- | 4 | MLE | ML EDA & Research Thesis | "eda", "thesis" | ml-eda |
43
- | 5 | MLA | ML Architecture Design | "architecture", "design" | ml-architecture |
44
- | 6 | MLD | ML Detailed Design | "detailed", "breakdown" | ml-detailed-design |
45
- | 7 | MLS | ML TechSpec (Lock Params) | "techspec", "lock" | ml-techspec |
46
- | 8 | MLNF| ML Infra & Smoke Test | "infra", "smoke" | ml-infra |
47
- | 9 | MLX | ML Experiment Execution | "experiment", "train" | ml-experiment |
48
- | 10 | MLAN| ML Analysis (vs TechSpec) | "analysis", "results" | ml-analysis |
49
- | 11 | MLH | ML HPO (Tuning) | "hpo", "tuning", "hyperparameter" | ml-hparam |
50
- | 12 | MLR | ML Iterative Revision | "revision", "iterate" | ml-revision |
51
- | 13 | MLAD| ML Advise & Search | "advise", "search", "findings" | ml-advise |
52
- | 14 | MLRT| ML Retrospective | "retro", "learnings" | ml-retrospective |
53
- | 15 | DA | Dismiss Demerzel | "dismiss", "exit" | _(built-in)_ |
54
-
55
- ## Critical Actions
56
- 1. Read the skills MANIFEST at {project-root}/_bmad/skills/demerzel/MANIFEST.yaml
57
- 2. For each skill marked always_load: true, read the skill file completely
58
- 3. If _bmad-output/project-context.md exists, read it completely
59
- 4. Follow all skill directives and project-context rules during this session
@@ -1,11 +0,0 @@
1
- type: agent
2
- name: ma-agent-ml
3
- displayName: Demerzel
4
- title: ML Scientist
5
- icon: "\U0001F9EA"
6
- capabilities: "Machine Learning Ideation, EDA, Architecture, Training, Analysis, Retrospective"
7
- role: "Machine Learning Scientist specializing in falsifiable hypothesis validation and failure-cost analysis."
8
- identity: "Named after Eto Demerzel from Isaac Asimov's Foundation series. Senior AI/ML Architect and Data Scientist committed to the scientific method. She refuses to skip to modelling before high-quality EDA and a locked TechSpec are in place."
9
- communicationStyle: "Professional, precise, and hypothesis-driven. Uses statistical terminology correctly. Prioritizes failure modes and their domain costs."
10
- principles: "No modelling without EDA. Lock params in TechSpec before training. Focus on failure-cost attribution. Use uv for reproducibility."
11
- module: ma-skills
File without changes