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.
- package/DEVELOPMENT.md +173 -0
- package/README.md +201 -309
- package/SKILLS_STRUCTURE.md +392 -0
- package/bin/cli.js +127 -28
- package/index.js +8 -6
- package/lib/agents.js +12 -1
- package/lib/installer.js +311 -44
- package/package.json +2 -2
- package/skills/README.md +25 -0
- package/skills/code-review/SKILL.md +39 -0
- package/skills/commit-message/SKILL.md +75 -0
- package/skills/create-hardened-docker-skill/SKILL.md +0 -5
- package/skills/git-workflow-skill/SKILL.md +0 -5
- package/skills/js-ts-security-skill/SKILL.md +0 -4
- package/skills/logging-best-practices/SKILL.md +46 -0
- package/skills/logging-best-practices/examples/cpp.md +36 -0
- package/skills/logging-best-practices/examples/csharp.md +49 -0
- package/skills/logging-best-practices/examples/javascript.md +77 -0
- package/skills/logging-best-practices/examples/python.md +57 -0
- package/skills/logging-best-practices/references/logging-standards.md +29 -0
- package/skills/logging-best-practices/skill.json +13 -0
- package/skills/skill-creator/SKILL.md +211 -0
- package/skills/skill-creator/claude-code.md +6 -8
- package/skills/skill-creator/generic.md +0 -5
- package/skills/test-accompanied-development/SKILL.md +39 -0
- package/skills/test-accompanied-development/skill.json +12 -0
- package/skills/test-generator/SKILL.md +61 -0
- package/skills/vercel-react-best-practices/SKILL.md +105 -0
- package/skills/verify-hardened-docker-skill/SKILL.md +0 -5
|
@@ -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
|
|
32
|
-
npx ${NAME}
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
175
|
-
|
|
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
|
|
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();
|