ma-agents 3.5.6 → 3.6.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 (53) hide show
  1. package/.ma-agents.json +10 -0
  2. package/AGENTS.md +97 -0
  3. package/MANIFEST.yaml +3 -0
  4. package/README.md +17 -0
  5. package/_bmad-output/implementation-artifacts/21-10-profile-reconfigure.md +30 -6
  6. package/_bmad-output/implementation-artifacts/21-11-profile-uninstall.md +2 -1
  7. package/_bmad-output/implementation-artifacts/21-2-universal-instruction-block-expansion.md +217 -62
  8. package/_bmad-output/implementation-artifacts/21-3-roomodes-template-bmad-modes.md +196 -73
  9. package/_bmad-output/implementation-artifacts/21-4-agents-md-template-opencode.md +242 -53
  10. package/_bmad-output/implementation-artifacts/21-5-clinerules-template-extension.md +180 -41
  11. package/_bmad-output/implementation-artifacts/21-6-onprem-layered-guardrails.md +250 -75
  12. package/_bmad-output/implementation-artifacts/21-7-bmad-persona-phase-prefix.md +221 -89
  13. package/_bmad-output/implementation-artifacts/21-8-vllm-reference-doc-readme.md +121 -63
  14. package/_bmad-output/implementation-artifacts/21-9-tests-validation.md +332 -61
  15. package/_bmad-output/implementation-artifacts/bug-bmad-recompile-fails-on-airgapped-network.md +112 -0
  16. package/_bmad-output/implementation-artifacts/sprint-status.yaml +3 -2
  17. package/bin/cli.js +59 -0
  18. package/docs/deployment/vllm-nemotron.md +130 -0
  19. package/lib/agents.js +17 -2
  20. package/lib/bmad-customize/bmm-analyst.customize.yaml +8 -0
  21. package/lib/bmad-customize/bmm-architect.customize.yaml +2 -0
  22. package/lib/bmad-customize/bmm-dev.customize.yaml +2 -0
  23. package/lib/bmad-customize/bmm-pm.customize.yaml +2 -0
  24. package/lib/bmad-customize/bmm-qa.customize.yaml +2 -0
  25. package/lib/bmad-customize/bmm-quick-flow-solo-dev.customize.yaml +8 -0
  26. package/lib/bmad-customize/bmm-sm.customize.yaml +2 -0
  27. package/lib/bmad-customize/bmm-tech-writer.customize.yaml +2 -0
  28. package/lib/bmad-customize/bmm-ux-designer.customize.yaml +2 -0
  29. package/lib/bmad.js +293 -1
  30. package/lib/installer.js +617 -43
  31. package/lib/merge/roomodes.js +125 -0
  32. package/lib/profile.js +25 -2
  33. package/lib/reconfigure.js +334 -0
  34. package/lib/templates/agents-md.template.md +67 -0
  35. package/lib/templates/clinerules.template.md +13 -0
  36. package/lib/templates/instruction-block-onprem.template.md +86 -0
  37. package/lib/templates/instruction-block-universal.template.md +29 -0
  38. package/lib/templates/roomodes.template.yaml +96 -0
  39. package/lib/uninstall.js +314 -0
  40. package/package.json +4 -3
  41. package/test/agents-md.test.js +398 -0
  42. package/test/bmad-extension.test.js +2 -2
  43. package/test/bmad-persona-phase-prefix.test.js +271 -0
  44. package/test/clinerules.test.js +339 -0
  45. package/test/instruction-block.test.js +388 -0
  46. package/test/integration-verification.test.js +2 -2
  47. package/test/migration-validation.test.js +2 -2
  48. package/test/offline-recompile.test.js +237 -0
  49. package/test/onprem-injection.test.js +425 -32
  50. package/test/onprem-layer.test.js +419 -0
  51. package/test/reconfigure.test.js +436 -0
  52. package/test/roomodes.test.js +343 -0
  53. package/test/uninstall.test.js +402 -0
@@ -0,0 +1,29 @@
1
+ # AI Agent Skills - Planning Instruction
2
+
3
+ You have access to a library of skills in your skills directory. Before starting any task:
4
+
5
+ 1. Read the skill manifest at {{MANIFEST_PATH}}
6
+ 2. Based on the task description, select which skills are relevant
7
+ 3. Read only the selected skill files
8
+ 4. Then proceed with the task
9
+
10
+ Always load skills marked with always_load: true.
11
+ Do not load skills that are not relevant to the current task.
12
+
13
+ ## Respond in TEXT vs. create FILES
14
+
15
+ Choose your response medium deliberately. Defaulting to file creation when the user asked a question is a common failure mode — especially for coding agents running in web UIs.
16
+
17
+ - **Create or modify FILES when the user's request contains file-action keywords:** `create`, `write`, `generate`, `build`, `implement` (and obvious synonyms such as `add`, `produce`, `refactor`, `fix`, `update <file>`). These signal a concrete artifact is expected.
18
+ - **Respond in TEXT when the request contains text-response keywords:** `what do you think`, `how should we`, `discuss`, `opinion` (and obvious synonyms such as `explain`, `why`, `should I`, `compare`, `recommend`). These signal that a conversation is expected, not a deliverable.
19
+ - **If unsure, respond in TEXT.** A text answer can always be followed by file creation on confirmation; an unwanted file cannot be cleanly undone.
20
+ - **Never create `response.md`, `output.md`, or any similarly named scratch file as a reply.** A reply belongs in the chat transcript, not on disk.
21
+ - **Confirm file paths before writing.** When you are about to create or modify a file whose path the user has not explicitly named, state the intended path in text and wait for confirmation, unless the path is unambiguous from the task context.
22
+
23
+ ## BMAD phase discipline
24
+
25
+ BMAD-METHOD organizes work into declared phases (analysis, planning, architecture, story-creation, implementation, review). Respect the currently declared phase.
26
+
27
+ - **Do not skip ahead to implementation during planning.** If the project is in a planning phase — or the user has asked for requirements, architecture, or a story — produce planning artifacts, not code.
28
+ - **Do not retroactively plan after you have already coded.** If implementation has already started, flag the gap instead of fabricating back-dated planning documents.
29
+ - The declared phase is established by the active skill, the story status, or an explicit statement from the user. When none of these is available, ask before assuming.
@@ -0,0 +1,96 @@
1
+ # Story 21.3 — .roomodes template for Roo Code BMAD mode gating.
2
+ #
3
+ # Schema source of truth: the roo-code entry in lib/agents.js (lines 140-163).
4
+ # Schema fields used per mode: slug, name, roleDefinition, whenToUse,
5
+ # customInstructions, groups (with ["edit", { fileRegex, description }] tuple form).
6
+ #
7
+ # The universal-block sentinel below is substituted by the installer (stamper)
8
+ # AFTER calling composeInstructionBlock({ profile, projectRoot }) from Story 21.2.
9
+ # The YAML merger (lib/merge/roomodes.js) treats this template as opaque, already-
10
+ # composed input — it never calls composeInstructionBlock itself (decision A).
11
+ #
12
+ # NFR47 (application-layer fileRegex enforcement): Roo Code enforces fileRegex
13
+ # at the IDE layer via FileRestrictionError — independent of the LLM's prompt
14
+ # compliance. The four ma-agents-owned slugs are: bmad-pm, bmad-architect,
15
+ # bmad-techlead, bmad-dev.
16
+ customModes:
17
+ - slug: bmad-pm
18
+ name: BMAD PM
19
+ roleDefinition: >-
20
+ Product manager driving discovery, requirements capture, and PRD
21
+ authoring. Operates strictly in BMAD's planning phase — produces
22
+ markdown artifacts only.
23
+ whenToUse: >-
24
+ Use when the user asks for requirements discovery, PRD creation,
25
+ scope framing, or any BMAD planning-phase deliverable.
26
+ groups:
27
+ - read
28
+ - - edit
29
+ - fileRegex: \.md$
30
+ description: Markdown planning artifacts only (PRDs, notes, epics)
31
+ customInstructions: |-
32
+ PM mode — discovery, requirements, and PRD authoring only.
33
+ Do not write code or configuration files; produce markdown only.
34
+
35
+ {{UNIVERSAL_BLOCK}}
36
+
37
+ - slug: bmad-architect
38
+ name: BMAD Architect
39
+ roleDefinition: >-
40
+ System architect responsible for architecture decisions, diagrams,
41
+ and structured architecture documents. Produces markdown, XML, and
42
+ drawio assets only — no source code.
43
+ whenToUse: >-
44
+ Use when the user asks to design, review architecture, produce
45
+ diagrams, or author the architecture document.
46
+ groups:
47
+ - read
48
+ - - edit
49
+ - fileRegex: \.(md|xml|drawio)$
50
+ description: Architecture artifacts (markdown, XML, drawio)
51
+ customInstructions: |-
52
+ Architect mode — architecture documents, diagrams, and design decisions only.
53
+ Do not write source code or package configuration; produce md/xml/drawio only.
54
+
55
+ {{UNIVERSAL_BLOCK}}
56
+
57
+ - slug: bmad-techlead
58
+ name: BMAD Tech Lead
59
+ roleDefinition: >-
60
+ Technical lead responsible for story preparation, task breakdown,
61
+ and structured configuration. Edits markdown stories and
62
+ configuration (json/yaml) — no source code.
63
+ whenToUse: >-
64
+ Use when the user asks to prepare stories, break down tasks, edit
65
+ sprint/status yaml, or tune project configuration.
66
+ groups:
67
+ - read
68
+ - - edit
69
+ - fileRegex: \.(md|json|yaml|yml)$
70
+ description: Stories, tasks, and structured configuration only
71
+ customInstructions: |-
72
+ Tech Lead mode — story preparation and configuration only.
73
+ Do not write source code; edit only markdown stories and json/yaml configuration.
74
+
75
+ {{UNIVERSAL_BLOCK}}
76
+
77
+ - slug: bmad-dev
78
+ name: BMAD Dev
79
+ roleDefinition: >-
80
+ Senior developer responsible for implementing an approved story.
81
+ Full editor and command access — enters ONLY after planning is
82
+ complete and the story is in status ready-for-dev.
83
+ whenToUse: >-
84
+ Use when a story is ready for implementation and the user asks to
85
+ code, run tests, or execute commands.
86
+ groups:
87
+ - read
88
+ - edit
89
+ - command
90
+ customInstructions: |-
91
+ Dev mode — implement approved stories only.
92
+ Do not start coding unless the story status is ready-for-dev. If the
93
+ project is still in a planning phase, switch to the appropriate BMAD
94
+ mode (PM, Architect, or Tech Lead) first.
95
+
96
+ {{UNIVERSAL_BLOCK}}
@@ -0,0 +1,314 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Story 21.11 — Profile Uninstall orchestrator.
5
+ *
6
+ * Public API:
7
+ * async uninstallProfileArtifacts(projectRoot, { yes = false } = {}) -> void
8
+ *
9
+ * Orchestrates:
10
+ * 1. Detect which of the 5 injection files contain MA-AGENTS-START/END markers.
11
+ * 2. Detect whether .roomodes contains any of the 4 owned slugs.
12
+ * 3. Idempotency check: if nothing is found, print "Nothing to do." and return.
13
+ * 4. Confirmation (unless --yes): list files, ask to continue.
14
+ * 5. For each injection file: backup → strip marker block → if empty, delete.
15
+ * 6. For .roomodes: backup → parse → remove owned slugs → write or delete.
16
+ * 7. Update .ma-agents.json: clearProfile(), append profileHistory entry.
17
+ *
18
+ * Notes:
19
+ * - buildBackupFilename is reused from lib/installer.js (Story 21.2).
20
+ * - MA_AGENTS_OWNED_SLUGS is imported from lib/merge/roomodes.js (Story 21.3).
21
+ * - clearProfile is the 4th export added to lib/profile.js in this story.
22
+ * - --yes is SUPPORTED (legitimate CI decommissioning use-case), unlike reconfigure
23
+ * which REJECTS --yes. Documented asymmetry.
24
+ */
25
+
26
+ const fs = require('fs');
27
+ const path = require('path');
28
+ const chalk = require('chalk');
29
+ const defaultPrompts = require('prompts');
30
+
31
+ const { clearProfile, getProfile } = require('./profile');
32
+ const { buildBackupFilename, EXTRA_TEMPLATE_DIR } = require('./installer');
33
+ const { MA_AGENTS_OWNED_SLUGS } = require('./merge/roomodes');
34
+
35
+ const MANIFEST_FILE = '.ma-agents.json';
36
+ const PROFILE_HISTORY_CAP = 20;
37
+
38
+ const MARKER_START = '<!-- MA-AGENTS-START -->';
39
+ const MARKER_END = '<!-- MA-AGENTS-END -->';
40
+
41
+ /**
42
+ * The 5 canonical injection files that can hold MA-AGENTS-START/END blocks.
43
+ * Relative to projectRoot.
44
+ */
45
+ const INJECTION_FILES = [
46
+ 'CLAUDE.md',
47
+ '.clinerules',
48
+ '.cline/clinerules.md',
49
+ '.roo/rules/00-ma-agents.md',
50
+ 'AGENTS.md'
51
+ ];
52
+
53
+ /**
54
+ * Strip the MA-AGENTS-START...MA-AGENTS-END block (INCLUDING marker lines)
55
+ * from a file's content string. Returns the content with the block removed.
56
+ * If the marker is not found, returns the original content unchanged.
57
+ */
58
+ function stripMarkerBlock(content) {
59
+ // Match from the start marker line through the end marker line, inclusive.
60
+ // The regex is non-greedy and dot-matches-newline via [\s\S].
61
+ return content.replace(/[ \t]*<!-- MA-AGENTS-START -->[ \t]*\r?\n[\s\S]*?<!-- MA-AGENTS-END -->[ \t]*\r?\n?/g, '');
62
+ }
63
+
64
+ /**
65
+ * Returns true if the string `content` is empty or whitespace-only.
66
+ */
67
+ function isBlankContent(content) {
68
+ return content.trim().length === 0;
69
+ }
70
+
71
+ /**
72
+ * Check for slug divergence between installed .roomodes and the shipped template.
73
+ * Returns an array of divergent owned slug names (may be empty).
74
+ * If either file is absent or malformed, returns [].
75
+ */
76
+ function detectDivergentSlugs(projectRoot) {
77
+ const roomodesPath = path.join(projectRoot, '.roomodes');
78
+ if (!fs.existsSync(roomodesPath)) return [];
79
+ const templatePath = path.join(EXTRA_TEMPLATE_DIR, 'roomodes.template.yaml');
80
+ if (!fs.existsSync(templatePath)) return [];
81
+
82
+ let yaml;
83
+ try { yaml = require('js-yaml'); } catch { return []; }
84
+
85
+ let existing, template;
86
+ try {
87
+ existing = yaml.load(fs.readFileSync(roomodesPath, 'utf-8')) || {};
88
+ template = yaml.load(fs.readFileSync(templatePath, 'utf-8')) || {};
89
+ } catch {
90
+ return [];
91
+ }
92
+
93
+ const existingModes = Array.isArray(existing.customModes) ? existing.customModes : [];
94
+ const templateModes = Array.isArray(template.customModes) ? template.customModes : [];
95
+ const templateBySlug = new Map();
96
+ for (const m of templateModes) {
97
+ if (m && typeof m === 'object' && typeof m.slug === 'string') {
98
+ templateBySlug.set(m.slug, m);
99
+ }
100
+ }
101
+
102
+ const divergent = [];
103
+ for (const entry of existingModes) {
104
+ if (!entry || typeof entry !== 'object') continue;
105
+ const slug = entry.slug;
106
+ if (typeof slug !== 'string') continue;
107
+ if (!MA_AGENTS_OWNED_SLUGS.includes(slug)) continue;
108
+ const tpl = templateBySlug.get(slug);
109
+ if (!tpl) {
110
+ // Owned slug present but not in template — treat as user-modified.
111
+ divergent.push(slug);
112
+ continue;
113
+ }
114
+ // Compare everything except customInstructions (which is per-profile composed).
115
+ const normalize = (mode) => {
116
+ const copy = { ...mode };
117
+ delete copy.customInstructions;
118
+ return yaml.dump(copy, { sortKeys: true });
119
+ };
120
+ if (normalize(entry) !== normalize(tpl)) {
121
+ divergent.push(slug);
122
+ }
123
+ }
124
+ return divergent;
125
+ }
126
+
127
+ /**
128
+ * Append a profileHistory entry, capped at PROFILE_HISTORY_CAP entries.
129
+ * Reads and writes .ma-agents.json directly (same pattern as reconfigure.js).
130
+ */
131
+ function appendUninstallHistory(projectRoot, entry) {
132
+ const manifestPath = path.join(projectRoot, MANIFEST_FILE);
133
+ if (!fs.existsSync(manifestPath)) return;
134
+ const raw = fs.readFileSync(manifestPath, 'utf-8');
135
+ const manifest = JSON.parse(raw);
136
+ const history = Array.isArray(manifest.profileHistory) ? manifest.profileHistory.slice() : [];
137
+ history.push(entry);
138
+ while (history.length > PROFILE_HISTORY_CAP) {
139
+ history.shift();
140
+ }
141
+ manifest.profileHistory = history;
142
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
143
+ }
144
+
145
+ /**
146
+ * Main uninstall orchestrator.
147
+ *
148
+ * @param {string} projectRoot
149
+ * @param {object} [opts]
150
+ * @param {boolean} [opts.yes=false] Skip confirmation prompt (CI decommissioning).
151
+ * NOTE: Unlike `reconfigure`, --yes IS supported
152
+ * here — this is intentional asymmetry since
153
+ * uninstall is a valid CI teardown operation.
154
+ * @param {object} [opts.promptsLib] Prompts implementation override (for tests).
155
+ * @param {Date} [opts.now] Date injection (for tests).
156
+ */
157
+ async function uninstallProfileArtifacts(projectRoot, { yes = false, promptsLib, now } = {}) {
158
+ const prompts = promptsLib || defaultPrompts;
159
+ const nowDate = now || new Date();
160
+
161
+ // --- Step 1: Detect targets ---
162
+
163
+ // Which injection files have ma-agents marker blocks?
164
+ const injectionTargets = [];
165
+ for (const rel of INJECTION_FILES) {
166
+ const absPath = path.join(projectRoot, rel);
167
+ if (!fs.existsSync(absPath)) continue;
168
+ const content = fs.readFileSync(absPath, 'utf-8');
169
+ if (content.includes(MARKER_START)) {
170
+ injectionTargets.push({ rel, absPath, content });
171
+ }
172
+ }
173
+
174
+ // Does .roomodes contain any owned slugs?
175
+ const roomodesPath = path.join(projectRoot, '.roomodes');
176
+ let roomodesHasOwnedSlugs = false;
177
+ if (fs.existsSync(roomodesPath)) {
178
+ const roomodesContent = fs.readFileSync(roomodesPath, 'utf-8');
179
+ roomodesHasOwnedSlugs = MA_AGENTS_OWNED_SLUGS.some(slug =>
180
+ // Match slug as a YAML key to avoid substring false-positives
181
+ roomodesContent.includes(`slug: ${slug}`) || roomodesContent.includes(`slug: '${slug}'`) || roomodesContent.includes(`slug: "${slug}"`)
182
+ );
183
+ }
184
+
185
+ // --- Step 2: Idempotency check ---
186
+ if (injectionTargets.length === 0 && !roomodesHasOwnedSlugs) {
187
+ console.log('No ma-agents-owned profile artifacts found. Nothing to do.');
188
+ return;
189
+ }
190
+
191
+ // --- Step 3: Confirmation ---
192
+ const filesToModify = [
193
+ ...injectionTargets.map(t => t.rel),
194
+ ...(roomodesHasOwnedSlugs ? ['.roomodes'] : [])
195
+ ];
196
+
197
+ const list = filesToModify.map(f => ` - ${f}`).join('\n');
198
+ console.log(`The following files will be modified:\n${list}`);
199
+
200
+ if (!yes) {
201
+ const resp = await prompts({
202
+ type: 'confirm',
203
+ name: 'proceed',
204
+ message: 'The above ma-agents-owned content will be removed. User content outside markers will be preserved. Continue? (y/N)',
205
+ initial: false
206
+ });
207
+ if (!resp || resp.proceed !== true) {
208
+ console.log(chalk.gray('Aborted — no files modified.'));
209
+ return;
210
+ }
211
+ }
212
+
213
+ // --- Step 4: Process injection files ---
214
+ for (const { rel, absPath, content } of injectionTargets) {
215
+ // Write backup first
216
+ const backupPath = buildBackupFilename(absPath);
217
+ fs.writeFileSync(backupPath, content, 'utf-8');
218
+
219
+ // Strip the marker block
220
+ const stripped = stripMarkerBlock(content);
221
+
222
+ if (isBlankContent(stripped)) {
223
+ // Resulting file is empty/whitespace-only — delete it
224
+ fs.unlinkSync(absPath);
225
+ console.log(chalk.gray(` Removed (now empty): ${rel}`));
226
+ } else {
227
+ fs.writeFileSync(absPath, stripped, 'utf-8');
228
+ console.log(chalk.gray(` Stripped marker block: ${rel}`));
229
+ }
230
+ }
231
+
232
+ // --- Step 5: Process .roomodes ---
233
+ if (roomodesHasOwnedSlugs) {
234
+ const roomodesContent = fs.readFileSync(roomodesPath, 'utf-8');
235
+
236
+ // Check for slug divergence — warn but proceed
237
+ const divergentSlugs = detectDivergentSlugs(projectRoot);
238
+ if (divergentSlugs.length > 0) {
239
+ const slugList = divergentSlugs.map(s => `"${s}"`).join(', ');
240
+ console.warn(chalk.yellow(
241
+ `WARNING: removing hand-edited ma-agents-owned slugs [${slugList}]. Previous bodies backed up.`
242
+ ));
243
+ }
244
+
245
+ // Write backup before modifying
246
+ const backupPath = buildBackupFilename(roomodesPath);
247
+ fs.writeFileSync(backupPath, roomodesContent, 'utf-8');
248
+
249
+ // Parse YAML, remove only owned slugs
250
+ const yaml = require('js-yaml');
251
+ let parsed;
252
+ try {
253
+ parsed = yaml.load(roomodesContent) || {};
254
+ } catch {
255
+ console.warn(chalk.yellow('WARNING: .roomodes could not be parsed as YAML — skipping slug removal.'));
256
+ parsed = null;
257
+ }
258
+
259
+ if (parsed !== null) {
260
+ const existingModes = Array.isArray(parsed.customModes) ? parsed.customModes : [];
261
+ const ownedSet = new Set(MA_AGENTS_OWNED_SLUGS);
262
+ const userModes = existingModes.filter(
263
+ m => m && typeof m === 'object' && !ownedSet.has(m.slug)
264
+ );
265
+
266
+ // Check if the file should be deleted (customModes empty and no other meaningful keys)
267
+ const otherKeys = Object.keys(parsed).filter(k => k !== 'customModes');
268
+ if (userModes.length === 0 && otherKeys.length === 0) {
269
+ fs.unlinkSync(roomodesPath);
270
+ console.log(chalk.gray(' Removed (now empty): .roomodes'));
271
+ } else {
272
+ parsed.customModes = userModes;
273
+ const DUMP_OPTIONS = {
274
+ indent: 2,
275
+ lineWidth: -1,
276
+ noRefs: true,
277
+ quotingType: '"',
278
+ sortKeys: false
279
+ };
280
+ fs.writeFileSync(roomodesPath, yaml.dump(parsed, DUMP_OPTIONS), 'utf-8');
281
+ console.log(chalk.gray(' Removed owned slugs from: .roomodes'));
282
+ }
283
+ }
284
+ }
285
+
286
+ // --- Step 6: Update .ma-agents.json ---
287
+ const manifestPath = path.join(projectRoot, MANIFEST_FILE);
288
+ if (fs.existsSync(manifestPath)) {
289
+ const previousProfile = getProfile(projectRoot);
290
+
291
+ // clearProfile deletes the 'profile' key (not set to null/empty)
292
+ clearProfile(projectRoot);
293
+
294
+ // Append history entry
295
+ appendUninstallHistory(projectRoot, {
296
+ date: nowDate.toISOString(),
297
+ from: previousProfile,
298
+ to: null,
299
+ source: 'uninstall'
300
+ });
301
+
302
+ console.log(chalk.green(' Updated .ma-agents.json: profile cleared, history appended.'));
303
+ }
304
+
305
+ console.log(chalk.bold.green('\n Uninstall complete. All ma-agents-owned artifacts removed.\n'));
306
+ }
307
+
308
+ module.exports = {
309
+ uninstallProfileArtifacts,
310
+ // Exposed for tests
311
+ stripMarkerBlock,
312
+ appendUninstallHistory,
313
+ PROFILE_HISTORY_CAP
314
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ma-agents",
3
- "version": "3.5.6",
3
+ "version": "3.6.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/bmad-version-bump.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/onprem-injection.test.js && node test/experimental-warning.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/bmad-version-bump.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/agents-md.test.js && node test/onprem-injection.test.js && node test/onprem-layer.test.js && node test/roomodes.test.js && node test/clinerules.test.js && node test/reconfigure.test.js && node test/experimental-warning.test.js && node test/bmad-persona-phase-prefix.test.js && node test/uninstall.test.js && node test/offline-recompile.test.js",
12
12
  "build:bmad-cache": "node scripts/build-bmad-cache.js"
13
13
  },
14
14
  "keywords": [
@@ -30,7 +30,8 @@
30
30
  "bmad-method": "6.2.2",
31
31
  "prompts": "^2.4.2",
32
32
  "chalk": "^4.1.2",
33
- "fs-extra": "^11.1.1"
33
+ "fs-extra": "^11.1.1",
34
+ "js-yaml": "^4.1.1"
34
35
  },
35
36
  "engines": {
36
37
  "node": ">=16.0.0"