ma-agents 1.3.0 → 1.5.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.
@@ -0,0 +1,392 @@
1
+ # Skills Structure
2
+
3
+ How skills are organized in this package and how the installer translates a unified generic structure into agent-specific formats for each supported coding assistant.
4
+
5
+ ---
6
+
7
+ ## Generic Skill Folder Structure
8
+
9
+ Every skill in this package follows a single unified structure. This is the **authoring format** — what you create when adding a new skill to `skills/`.
10
+
11
+ ```
12
+ skills/<skill-name>/
13
+ ├── skill.json # Metadata (required)
14
+ ├── SKILL.md # Main instructions (required)
15
+ ├── template.md # Output template for the agent to fill in (optional)
16
+ ├── examples/ # Example outputs showing expected format (optional)
17
+ │ └── sample.md
18
+ ├── scripts/ # Executable scripts the agent can run (optional)
19
+ │ └── validate.sh
20
+ ├── references/ # Static documentation and context (optional)
21
+ │ └── api-docs.md
22
+ ├── hooks/ # Git hooks or other hook scripts (optional)
23
+ │ └── pre-commit
24
+ └── assets/ # Templates, configs, and other resources (optional)
25
+ └── config.yaml
26
+ ```
27
+
28
+ ### Required Files
29
+
30
+ #### `skill.json` (Single Source of Truth)
31
+
32
+ All skill metadata lives here. The installer uses it for listing, and **automatically injects** YAML frontmatter into the installed `.md` file at install time. You never write frontmatter manually.
33
+
34
+ ```json
35
+ {
36
+ "name": "My Skill",
37
+ "description": "What this skill does in one sentence",
38
+ "version": "1.0.0",
39
+ "author": "Your Name",
40
+ "tags": ["category", "keywords"]
41
+ }
42
+ ```
43
+
44
+ | Field | Required | Description |
45
+ |---------------|----------|--------------------------------------------|
46
+ | `name` | Yes | Human-readable display name |
47
+ | `description` | Yes | Brief description shown in the installer |
48
+ | `version` | Yes | Semver version string |
49
+ | `author` | No | Author name or organization |
50
+ | `tags` | No | Array of keywords for categorization |
51
+
52
+ #### `SKILL.md`
53
+
54
+ The main instruction file. Write **pure Markdown instructions only** — no YAML frontmatter. The installer injects frontmatter from `skill.json` at install time so the target agent receives a properly formatted file.
55
+
56
+ ```markdown
57
+ # My Skill
58
+
59
+ ## Purpose
60
+ What problem this skill solves.
61
+
62
+ ## Instructions
63
+ Step-by-step instructions for the agent.
64
+
65
+ ## Rules
66
+ Constraints and requirements the agent must follow.
67
+
68
+ ## Output Format
69
+ What the agent should produce.
70
+ ```
71
+
72
+ > **Why no frontmatter in source files?** Many agents (Claude Code, Gemini, Copilot) expect YAML frontmatter with `name` and `description`. Rather than duplicating this across `skill.json` and every `.md` file, `skill.json` is the single source of truth. The installer reads it and injects the correct frontmatter during installation. If a source file already contains frontmatter, it is stripped and replaced.
73
+
74
+ ### Optional Files and Directories
75
+
76
+ | Path | Purpose |
77
+ |-----------------|----------------------------------------------------------|
78
+ | `template.md` | A fill-in-the-blank template the agent uses for output |
79
+ | `examples/` | Sample outputs demonstrating the expected result format |
80
+ | `scripts/` | Shell scripts, Python scripts, or other executables |
81
+ | `references/` | Background documentation the agent can consult |
82
+ | `assets/` | Config files, templates, images, or other static resources |
83
+ | `hooks/` | Git hooks or other hook scripts |
84
+
85
+ ### Naming Conventions
86
+
87
+ - Skill folder: lowercase with hyphens (`code-review`, `git-workflow-skill`)
88
+ - SKILL.md: keep under 500 lines; move detailed content to `references/`
89
+ - Scripts: use descriptive names (`validate.sh`, `init_skill.py`)
90
+ - One skill per folder
91
+
92
+ ---
93
+
94
+ ## How the Installer Translates Skills
95
+
96
+ When a user runs `npx ma-agents install`, they select skills and target agents. The installer maps the generic structure to each agent's native format.
97
+
98
+ ### Translation Flow
99
+
100
+ ```
101
+ Generic Structure (this repo) Installed Output
102
+ ───────────────────────────── ────────────────
103
+ skills/<skill-name>/
104
+ ├── skill.json ──────────► Injected as YAML frontmatter into SKILL.md
105
+ ├── SKILL.md ──────────► <agent-skills-dir>/<skill-name>/SKILL.md
106
+ ├── template.md ──────────► <agent-skills-dir>/<skill-name>/template.md
107
+ ├── examples/ ──────────► <agent-skills-dir>/<skill-name>/examples/
108
+ ├── scripts/ ──────────► <agent-skills-dir>/<skill-name>/scripts/
109
+ ├── references/ ──────────► <agent-skills-dir>/<skill-name>/references/
110
+ └── assets/ ──────────► <agent-skills-dir>/<skill-name>/assets/
111
+ ```
112
+
113
+ The installed `SKILL.md` always gets YAML frontmatter injected from `skill.json`:
114
+ ```yaml
115
+ ---
116
+ name: My Skill
117
+ description: What this skill does in one sentence
118
+ ---
119
+ # ... original Markdown content follows ...
120
+ ```
121
+
122
+ The installer resolves the instruction file in this priority order:
123
+
124
+ 1. **Agent-specific template** — `<agent-template>.md` (e.g., `claude-code.md`, `cline.md`)
125
+ 2. **Generic template** — `generic.md`
126
+ 3. **Fallback** — `SKILL.md`
127
+
128
+ This means you can optionally create agent-tailored versions of your instructions. If you only create `SKILL.md`, all agents get the same content.
129
+
130
+ ### Template Priority Example
131
+
132
+ ```
133
+ skills/code-review/
134
+ ├── skill.json
135
+ ├── SKILL.md ← Fallback for any agent without a specific template
136
+ ├── generic.md ← Used by Gemini, Copilot, Cursor, Kilocode
137
+ ├── claude-code.md ← Used only by Claude Code
138
+ └── cline.md ← Used only by Cline
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Agent-Specific Output Formats
144
+
145
+ Each agent has its own expected directory structure. The installer handles this translation automatically.
146
+
147
+ ### Claude Code
148
+
149
+ **Install path:** `.claude/skills/<skill-name>/`
150
+
151
+ ```
152
+ .claude/skills/
153
+ └── my-skill/
154
+ ├── SKILL.md ← Instructions with injected frontmatter
155
+ ├── template.md ← Output template (if exists)
156
+ ├── examples/ ← Sample outputs (if exists)
157
+ ├── scripts/ ← Executable scripts (if exists)
158
+ ├── references/ ← Static documentation (if exists)
159
+ └── assets/ ← Other resources (if exists)
160
+ ```
161
+
162
+ Claude Code loads skills from `.claude/skills/` as subdirectories containing a `SKILL.md` with YAML frontmatter. Supports `template.md`, `examples/`, and `scripts/` natively.
163
+
164
+ ### Google Gemini
165
+
166
+ **Install path:** `.gemini/skills/<skill-name>/`
167
+
168
+ ```
169
+ .gemini/skills/
170
+ └── my-skill/
171
+ ├── SKILL.md ← Instructions with injected frontmatter
172
+ ├── scripts/ ← Executable scripts (if exists)
173
+ ├── references/ ← Static documentation (if exists)
174
+ └── assets/ ← Templates and resources (if exists)
175
+ ```
176
+
177
+ Gemini CLI follows the same SKILL.md specification as Claude Code. Skills are placed in `.gemini/skills/` as subdirectories with `SKILL.md` inside.
178
+
179
+ ### GitHub Copilot
180
+
181
+ **Install path:** `.github/copilot/skills/<skill-name>/`
182
+
183
+ ```
184
+ .github/copilot/skills/
185
+ └── my-skill/
186
+ ├── SKILL.md ← Instructions with injected frontmatter
187
+ ├── scripts/ ← Executable scripts (if exists)
188
+ ├── references/ ← Static documentation (if exists)
189
+ └── examples/ ← Sample outputs (if exists)
190
+ ```
191
+
192
+ Copilot uses the same SKILL.md specification (YAML frontmatter + Markdown). Copilot also supports path-scoped instructions via `.github/instructions/*.instructions.md` files with `applyTo` glob patterns.
193
+
194
+ ### Cursor
195
+
196
+ **Install path:** `.cursor/skills/<skill-name>/`
197
+
198
+ ```
199
+ .cursor/skills/
200
+ └── my-skill/
201
+ ├── SKILL.md ← Instructions with injected frontmatter
202
+ ├── scripts/ ← Executable scripts (if exists)
203
+ └── references/ ← Static documentation (if exists)
204
+ ```
205
+
206
+ Cursor natively uses `.mdc` (Markdown Component) files in `.cursor/rules/` with frontmatter fields like `description`, `globs`, and `alwaysApply`. The installer places skills in `.cursor/skills/` using the standard folder structure. For deeper Cursor integration, users can manually convert to `.mdc` format in `.cursor/rules/`.
207
+
208
+ **Cursor native `.mdc` format (for reference):**
209
+ ```yaml
210
+ ---
211
+ description: What this rule does
212
+ globs: "src/**/*.ts"
213
+ alwaysApply: false
214
+ ---
215
+ # Rule body in Markdown
216
+ ```
217
+
218
+ ### Cline
219
+
220
+ **Install path:** `.cline/skills/<skill-name>/`
221
+
222
+ ```
223
+ .cline/skills/
224
+ └── my-skill/
225
+ ├── SKILL.md ← Instructions with injected frontmatter
226
+ ├── docs/ ← Additional documentation (if exists)
227
+ ├── templates/ ← Config/boilerplate files (if exists)
228
+ └── scripts/ ← Utility scripts (if exists)
229
+ ```
230
+
231
+ Cline loads skills from `.cline/skills/` using a progressive three-level system: metadata (name + description from frontmatter) loads at startup, full instructions load when triggered, and bundled files (`docs/`, `templates/`, `scripts/`) load on-demand. The `name` in frontmatter must match the directory name (kebab-case). Description should be under 1024 characters and specify trigger phrases.
232
+
233
+ **Resource directory mapping:** The installer maps `references/` → `docs/` and `assets/` → `templates/` for Cline, matching its native structure.
234
+
235
+ ### Kilocode
236
+
237
+ **Install path:** `.kilocode/skills/<skill-name>/`
238
+
239
+ ```
240
+ .kilocode/skills/
241
+ └── my-skill/
242
+ ├── SKILL.md ← Instructions with injected frontmatter
243
+ ├── scripts/ ← Executable scripts (if exists)
244
+ └── references/ ← Static documentation (if exists)
245
+ ```
246
+
247
+ Kilocode natively uses `.kilocode/rules/` for generic rules and `.kilocode/rules-<mode>/` for mode-specific rules. The installer places skills in `.kilocode/skills/` using the standard folder structure. Kilocode also reads `.clinerules` as a fallback.
248
+
249
+ ---
250
+
251
+ ## Creating a New Skill
252
+
253
+ ### Step 1: Create the Skill Folder
254
+
255
+ ```bash
256
+ mkdir -p skills/my-new-skill
257
+ ```
258
+
259
+ ### Step 2: Create `skill.json`
260
+
261
+ ```json
262
+ {
263
+ "name": "My New Skill",
264
+ "description": "Brief description of what this skill does",
265
+ "version": "1.0.0",
266
+ "author": "Your Name",
267
+ "tags": ["relevant", "tags"]
268
+ }
269
+ ```
270
+
271
+ ### Step 3: Create `SKILL.md`
272
+
273
+ Write pure Markdown instructions. Do **not** add YAML frontmatter — the installer injects it from `skill.json` automatically.
274
+
275
+ ```markdown
276
+ # My New Skill
277
+
278
+ ## Purpose
279
+ Explain what problem this skill solves.
280
+
281
+ ## When to Use
282
+ Describe the scenarios where this skill applies.
283
+
284
+ ## Instructions
285
+ 1. Step-by-step instructions for the agent
286
+ 2. Be specific and actionable
287
+ 3. Include examples where helpful
288
+
289
+ ## Rules
290
+ - Constraints the agent must follow
291
+ - Quality standards to enforce
292
+ - Things to avoid
293
+
294
+ ## Output Format
295
+ Describe the expected output format with examples.
296
+ ```
297
+
298
+ ### Step 4: Add Optional Resources
299
+
300
+ ```bash
301
+ # Add scripts the agent can execute
302
+ mkdir -p skills/my-new-skill/scripts
303
+ # scripts/validate.sh, scripts/setup.py, etc.
304
+
305
+ # Add reference documentation
306
+ mkdir -p skills/my-new-skill/references
307
+ # references/api-docs.md, references/standards.md, etc.
308
+
309
+ # Add templates
310
+ # template.md — output template for the agent
311
+
312
+ # Add examples
313
+ mkdir -p skills/my-new-skill/examples
314
+ # examples/sample-output.md, etc.
315
+
316
+ # Add other assets
317
+ mkdir -p skills/my-new-skill/assets
318
+ # assets/config.yaml, assets/boilerplate.json, etc.
319
+ ```
320
+
321
+ ### Step 5: (Optional) Create Agent-Specific Templates
322
+
323
+ If a skill needs different instructions for different agents:
324
+
325
+ ```bash
326
+ # Claude Code specific (uses 'claude-code' template)
327
+ skills/my-new-skill/claude-code.md
328
+
329
+ # Cline specific (uses 'cline' template)
330
+ skills/my-new-skill/cline.md
331
+
332
+ # All other agents use generic.md or SKILL.md
333
+ skills/my-new-skill/generic.md
334
+ ```
335
+
336
+ ### Step 6: Test
337
+
338
+ ```bash
339
+ # Verify the skill appears in the list
340
+ npx ma-agents list
341
+
342
+ # Test installation
343
+ npx ma-agents install my-new-skill claude-code
344
+ ```
345
+
346
+ ---
347
+
348
+ ## Template ID Mapping
349
+
350
+ Each agent has a `template` ID that determines which instruction file the installer looks for first.
351
+
352
+ | Agent | Template ID | Looks for (in order) |
353
+ |----------------|----------------|-----------------------------------------------|
354
+ | Claude Code | `claude-code` | `claude-code.md` → `generic.md` → `SKILL.md` |
355
+ | Google Gemini | `generic` | `generic.md` → `SKILL.md` |
356
+ | GitHub Copilot | `generic` | `generic.md` → `SKILL.md` |
357
+ | Cursor | `generic` | `generic.md` → `SKILL.md` |
358
+ | Cline | `cline` | `cline.md` → `generic.md` → `SKILL.md` |
359
+ | Kilocode | `generic` | `generic.md` → `SKILL.md` |
360
+
361
+ ---
362
+
363
+ ## Installation Paths Summary
364
+
365
+ Default installation is **project-level** (relative to where `npx ma-agents` is run).
366
+
367
+ | Agent | Project Path | Global Path |
368
+ |----------------|---------------------------------------|----------------------------------------------------------|
369
+ | Claude Code | `.claude/skills/` | `~/AppData/Roaming/Claude/skills/` (Win) |
370
+ | Google Gemini | `.gemini/skills/` | `~/AppData/Roaming/Gemini/skills/` (Win) |
371
+ | GitHub Copilot | `.github/copilot/skills/` | `~/AppData/Roaming/GitHub Copilot/skills/` (Win) |
372
+ | Cursor | `.cursor/skills/` | `~/AppData/Roaming/Cursor/User/skills/` (Win) |
373
+ | Cline | `.cline/skills/` | `~/AppData/.../saoudrizwan.claude-dev/skills/` (Win) |
374
+ | Kilocode | `.kilocode/skills/` | `~/AppData/Roaming/Kilocode/skills/` (Win) |
375
+
376
+ Use `--global` flag to install to user-level paths instead:
377
+ ```bash
378
+ npx ma-agents install my-skill claude-code --global
379
+ ```
380
+
381
+ ---
382
+
383
+ ## Checklist for New Skills
384
+
385
+ - [ ] `skill.json` exists with `name`, `description`, `version`
386
+ - [ ] `SKILL.md` exists with pure Markdown (no YAML frontmatter — injected at install time)
387
+ - [ ] Instructions are clear, specific, and actionable
388
+ - [ ] Scripts are executable and have shebangs (`#!/bin/bash`, `#!/usr/bin/env python3`)
389
+ - [ ] Skill appears in `npx ma-agents list`
390
+ - [ ] Test install works for at least one agent
391
+ - [ ] (Optional) Agent-specific templates created where needed
392
+ - [ ] (Optional) References moved out of SKILL.md to keep it under 500 lines
package/bin/cli.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const prompts = require('prompts');
4
4
  const chalk = require('chalk');
5
5
  const path = require('path');
6
- const { installSkill, listSkills, listAgents } = require('../lib/installer');
6
+ const { installSkill, uninstallSkill, getStatus, listSkills, listAgents } = require('../lib/installer');
7
7
 
8
8
  const PKG = require('../package.json');
9
9
  const NAME = PKG.name;
@@ -17,6 +17,8 @@ ${chalk.bold('Usage:')}
17
17
  ${chalk.cyan(`npx ${NAME}`)} Interactive wizard
18
18
  ${chalk.cyan(`npx ${NAME} install`)} Interactive install wizard
19
19
  ${chalk.cyan(`npx ${NAME} install`)} <skill> <agents...> Install directly
20
+ ${chalk.cyan(`npx ${NAME} uninstall`)} <skill> <agents..> Uninstall a skill
21
+ ${chalk.cyan(`npx ${NAME} status`)} Show installed skills
20
22
  ${chalk.cyan(`npx ${NAME} list`)} List available skills
21
23
  ${chalk.cyan(`npx ${NAME} agents`)} List supported agents
22
24
  ${chalk.cyan(`npx ${NAME} help`)} Show this help
@@ -24,12 +26,15 @@ ${chalk.bold('Usage:')}
24
26
  ${chalk.bold('Install options:')}
25
27
  ${chalk.cyan('--global')} Install to global/user-level paths (default: project-level)
26
28
  ${chalk.cyan('--path <dir>')} Custom installation directory
29
+ ${chalk.cyan('--force')} Skip upgrade prompts, always overwrite
27
30
 
28
31
  ${chalk.bold('Examples:')}
29
32
  npx ${NAME} install
30
33
  npx ${NAME} install code-review claude-code
31
- npx ${NAME} install test-generator claude-code cline cursor
32
- npx ${NAME} install skill-creator claude-code --path ./my-skills
34
+ npx ${NAME} install code-review claude-code --force
35
+ npx ${NAME} uninstall code-review claude-code
36
+ npx ${NAME} status
37
+ npx ${NAME} status --global
33
38
  `);
34
39
  }
35
40
 
@@ -37,7 +42,7 @@ function showSkills() {
37
42
  const skills = listSkills();
38
43
  console.log(chalk.bold('\n Available Skills:\n'));
39
44
  skills.forEach(skill => {
40
- console.log(chalk.cyan(` ${skill.id.padEnd(35)}`) + chalk.white(skill.name));
45
+ console.log(chalk.cyan(` ${skill.id.padEnd(35)}`) + chalk.white(`${skill.name}`) + chalk.gray(` v${skill.version}`));
41
46
  console.log(chalk.gray(` ${''.padEnd(35)}${skill.description}\n`));
42
47
  });
43
48
  }
@@ -46,16 +51,76 @@ function showAgents() {
46
51
  const agents = listAgents();
47
52
  console.log(chalk.bold('\n Supported Agents:\n'));
48
53
  agents.forEach(agent => {
49
- console.log(chalk.cyan(` ${agent.id.padEnd(20)}`) + chalk.white(agent.name) + chalk.gray(` - ${agent.description}`));
54
+ console.log(chalk.cyan(` ${agent.id.padEnd(20)}`) + chalk.white(agent.name) + chalk.gray(` v${agent.version} - ${agent.description}`));
50
55
  });
51
56
  console.log('');
52
57
  }
53
58
 
54
- async function installWizard(preselectedSkill, preselectedAgents, customPath) {
59
+ function showStatus(args) {
60
+ const globalFlag = args.includes('--global');
61
+ const scope = globalFlag ? 'global' : 'project';
62
+ const positional = args.filter(a => a !== '--global');
63
+
64
+ const results = getStatus(positional, '', scope);
65
+
66
+ if (results.length === 0) {
67
+ console.log(chalk.gray(`\n No skills installed (${scope} scope)\n`));
68
+ return;
69
+ }
70
+
71
+ console.log(chalk.bold(`\n Installed Skills:\n`));
72
+
73
+ for (const entry of results) {
74
+ console.log(chalk.cyan(` ${entry.agent.name}`) + chalk.gray(` (${entry.scope}: ${entry.installPath})`));
75
+
76
+ const skillIds = Object.keys(entry.skills);
77
+ for (const skillId of skillIds) {
78
+ const info = entry.skills[skillId];
79
+ const installed = new Date(info.installedAt).toLocaleDateString();
80
+ const updated = info.updatedAt !== info.installedAt
81
+ ? chalk.gray(` updated ${new Date(info.updatedAt).toLocaleDateString()}`)
82
+ : '';
83
+ console.log(
84
+ chalk.white(` ${skillId.padEnd(35)}`) +
85
+ chalk.green(`v${info.version}`) +
86
+ chalk.gray(` installed ${installed}`) +
87
+ updated
88
+ );
89
+ }
90
+ console.log('');
91
+ }
92
+ }
93
+
94
+ // --- Parse common flags from args ---
95
+
96
+ function parseFlags(args) {
97
+ const globalFlag = args.includes('--global');
98
+ const forceFlag = args.includes('--force');
99
+ let customPath = '';
100
+ let positional = [...args].filter(a => a !== '--global' && a !== '--force');
101
+
102
+ const pathIdx = positional.indexOf('--path');
103
+ if (pathIdx !== -1) {
104
+ customPath = positional[pathIdx + 1] || '';
105
+ positional.splice(pathIdx, 2);
106
+ }
107
+
108
+ return {
109
+ globalFlag,
110
+ forceFlag,
111
+ customPath,
112
+ scope: globalFlag ? 'global' : 'project',
113
+ positional
114
+ };
115
+ }
116
+
117
+ // --- Install wizard ---
118
+
119
+ async function installWizard(preselectedSkill, preselectedAgents, customPath, forceFlag) {
55
120
  const skills = listSkills();
56
121
  const agents = listAgents();
57
122
 
58
- // Step 1: Select skills (multi-select)
123
+ // Step 1: Select skills
59
124
  let selectedSkillIds;
60
125
  if (preselectedSkill) {
61
126
  selectedSkillIds = [preselectedSkill];
@@ -65,7 +130,7 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
65
130
  name: 'skills',
66
131
  message: 'Which skills do you want to install?',
67
132
  choices: skills.map(s => ({
68
- title: chalk.white(s.name) + chalk.gray(` - ${s.description}`),
133
+ title: chalk.white(s.name) + chalk.gray(` v${s.version} - ${s.description}`),
69
134
  value: s.id,
70
135
  selected: false
71
136
  })),
@@ -80,7 +145,7 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
80
145
  selectedSkillIds = chosen;
81
146
  }
82
147
 
83
- // Step 2: Select agents (multi-select)
148
+ // Step 2: Select agents
84
149
  let selectedAgentIds;
85
150
  if (preselectedAgents && preselectedAgents.length > 0) {
86
151
  selectedAgentIds = preselectedAgents;
@@ -90,7 +155,7 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
90
155
  name: 'agents',
91
156
  message: 'Which coding agents do you want to install to?',
92
157
  choices: agents.map(a => ({
93
- title: chalk.white(a.name) + chalk.gray(` - ${a.description}`),
158
+ title: chalk.white(a.name) + chalk.gray(` v${a.version} - ${a.description}`),
94
159
  value: a.id,
95
160
  selected: false
96
161
  })),
@@ -105,7 +170,7 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
105
170
  selectedAgentIds = chosen;
106
171
  }
107
172
 
108
- // Step 3: Installation scope (project vs global)
173
+ // Step 3: Installation scope
109
174
  let installPath = customPath || '';
110
175
  let installScope = 'project';
111
176
  if (!installPath) {
@@ -153,10 +218,10 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
153
218
  process.exit(0);
154
219
  }
155
220
 
156
- // Step 5: Install each skill
221
+ // Step 5: Install each skill (with upgrade detection)
157
222
  for (const skillId of selectedSkillIds) {
158
223
  try {
159
- await installSkill(skillId, selectedAgentIds, installPath, installScope);
224
+ await installSkill(skillId, selectedAgentIds, installPath, installScope, { force: !!forceFlag });
160
225
  } catch (error) {
161
226
  console.error(chalk.red(`\n Failed to install ${skillId}:`), error.message);
162
227
  }
@@ -165,37 +230,29 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
165
230
  console.log(chalk.bold.green('\n Installation complete!\n'));
166
231
  }
167
232
 
168
- async function handleInstall(args) {
169
- const pathIdx = args.indexOf('--path');
170
- const globalFlag = args.includes('--global');
171
- let customPath = '';
172
- let positional = [...args].filter(a => a !== '--global');
233
+ // --- Command handlers ---
173
234
 
174
- if (pathIdx !== -1) {
175
- customPath = positional[positional.indexOf('--path') + 1] || '';
176
- const pIdx = positional.indexOf('--path');
177
- positional.splice(pIdx, 2);
178
- }
235
+ async function handleInstall(args) {
236
+ const { globalFlag, forceFlag, customPath, scope, positional } = parseFlags(args);
179
237
 
180
- const scope = globalFlag ? 'global' : 'project';
181
238
  const skillId = positional[0];
182
239
  const agentIds = positional.slice(1);
183
240
 
184
241
  // No args → launch wizard
185
242
  if (!skillId) {
186
- await installWizard(null, null, customPath);
243
+ await installWizard(null, null, customPath, forceFlag);
187
244
  return;
188
245
  }
189
246
 
190
247
  // Skill but no agents → wizard with skill preselected
191
248
  if (agentIds.length === 0) {
192
- await installWizard(skillId, null, customPath);
249
+ await installWizard(skillId, null, customPath, forceFlag);
193
250
  return;
194
251
  }
195
252
 
196
- // Full args → direct install (defaults to project-level)
253
+ // Full args → direct install
197
254
  try {
198
- await installSkill(skillId, agentIds, customPath, scope);
255
+ await installSkill(skillId, agentIds, customPath, scope, { force: forceFlag });
199
256
  console.log(chalk.bold.green('\n Installation complete!\n'));
200
257
  } catch (error) {
201
258
  console.error(chalk.red('\n Installation failed:'), error.message);
@@ -203,6 +260,33 @@ async function handleInstall(args) {
203
260
  }
204
261
  }
205
262
 
263
+ async function handleUninstall(args) {
264
+ const { globalFlag, customPath, scope, positional } = parseFlags(args);
265
+
266
+ const skillId = positional[0];
267
+ const agentIds = positional.slice(1);
268
+
269
+ if (!skillId) {
270
+ console.error(chalk.red('Usage: npx ma-agents uninstall <skill> <agents...>'));
271
+ process.exit(1);
272
+ }
273
+
274
+ if (agentIds.length === 0) {
275
+ console.error(chalk.red('Please specify at least one agent. Run "npx ma-agents agents" to see options.'));
276
+ process.exit(1);
277
+ }
278
+
279
+ try {
280
+ await uninstallSkill(skillId, agentIds, customPath, scope);
281
+ console.log(chalk.bold.green('\n Uninstall complete!\n'));
282
+ } catch (error) {
283
+ console.error(chalk.red('\n Uninstall failed:'), error.message);
284
+ process.exit(1);
285
+ }
286
+ }
287
+
288
+ // --- Interactive mode ---
289
+
206
290
  async function interactiveMode() {
207
291
  console.log(chalk.bold.cyan(`\n ${NAME} v${VERSION}\n`));
208
292
 
@@ -212,6 +296,7 @@ async function interactiveMode() {
212
296
  message: 'What would you like to do?',
213
297
  choices: [
214
298
  { title: 'Install skills', value: 'install' },
299
+ { title: 'Show installed skills', value: 'status' },
215
300
  { title: 'List available skills', value: 'list-skills' },
216
301
  { title: 'List supported agents', value: 'list-agents' },
217
302
  { title: 'Exit', value: 'exit' }
@@ -223,6 +308,11 @@ async function interactiveMode() {
223
308
  process.exit(0);
224
309
  }
225
310
 
311
+ if (action === 'status') {
312
+ showStatus([]);
313
+ process.exit(0);
314
+ }
315
+
226
316
  if (action === 'list-skills') {
227
317
  showSkills();
228
318
  process.exit(0);
@@ -238,6 +328,8 @@ async function interactiveMode() {
238
328
  }
239
329
  }
240
330
 
331
+ // --- Main ---
332
+
241
333
  async function main() {
242
334
  const args = process.argv.slice(2);
243
335
  const command = args[0];
@@ -246,6 +338,13 @@ async function main() {
246
338
  case 'install':
247
339
  await handleInstall(args.slice(1));
248
340
  break;
341
+ case 'uninstall':
342
+ case 'remove':
343
+ await handleUninstall(args.slice(1));
344
+ break;
345
+ case 'status':
346
+ showStatus(args.slice(1));
347
+ break;
249
348
  case 'list':
250
349
  case 'list-skills':
251
350
  showSkills();