ma-agents 1.2.0 → 1.4.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/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,18 +17,24 @@ ${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
23
25
 
24
26
  ${chalk.bold('Install options:')}
27
+ ${chalk.cyan('--global')} Install to global/user-level paths (default: project-level)
25
28
  ${chalk.cyan('--path <dir>')} Custom installation directory
29
+ ${chalk.cyan('--force')} Skip upgrade prompts, always overwrite
26
30
 
27
31
  ${chalk.bold('Examples:')}
28
32
  npx ${NAME} install
29
33
  npx ${NAME} install code-review claude-code
30
- npx ${NAME} install test-generator claude-code cline cursor
31
- 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
32
38
  `);
33
39
  }
34
40
 
@@ -36,7 +42,7 @@ function showSkills() {
36
42
  const skills = listSkills();
37
43
  console.log(chalk.bold('\n Available Skills:\n'));
38
44
  skills.forEach(skill => {
39
- 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}`));
40
46
  console.log(chalk.gray(` ${''.padEnd(35)}${skill.description}\n`));
41
47
  });
42
48
  }
@@ -45,16 +51,76 @@ function showAgents() {
45
51
  const agents = listAgents();
46
52
  console.log(chalk.bold('\n Supported Agents:\n'));
47
53
  agents.forEach(agent => {
48
- 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}`));
49
55
  });
50
56
  console.log('');
51
57
  }
52
58
 
53
- 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) {
54
120
  const skills = listSkills();
55
121
  const agents = listAgents();
56
122
 
57
- // Step 1: Select skills (multi-select)
123
+ // Step 1: Select skills
58
124
  let selectedSkillIds;
59
125
  if (preselectedSkill) {
60
126
  selectedSkillIds = [preselectedSkill];
@@ -64,7 +130,7 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
64
130
  name: 'skills',
65
131
  message: 'Which skills do you want to install?',
66
132
  choices: skills.map(s => ({
67
- title: chalk.white(s.name) + chalk.gray(` - ${s.description}`),
133
+ title: chalk.white(s.name) + chalk.gray(` v${s.version} - ${s.description}`),
68
134
  value: s.id,
69
135
  selected: false
70
136
  })),
@@ -79,7 +145,7 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
79
145
  selectedSkillIds = chosen;
80
146
  }
81
147
 
82
- // Step 2: Select agents (multi-select)
148
+ // Step 2: Select agents
83
149
  let selectedAgentIds;
84
150
  if (preselectedAgents && preselectedAgents.length > 0) {
85
151
  selectedAgentIds = preselectedAgents;
@@ -89,7 +155,7 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
89
155
  name: 'agents',
90
156
  message: 'Which coding agents do you want to install to?',
91
157
  choices: agents.map(a => ({
92
- title: chalk.white(a.name) + chalk.gray(` - ${a.description}`),
158
+ title: chalk.white(a.name) + chalk.gray(` v${a.version} - ${a.description}`),
93
159
  value: a.id,
94
160
  selected: false
95
161
  })),
@@ -104,20 +170,24 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
104
170
  selectedAgentIds = chosen;
105
171
  }
106
172
 
107
- // Step 3: Custom path (optional)
173
+ // Step 3: Installation scope
108
174
  let installPath = customPath || '';
175
+ let installScope = 'project';
109
176
  if (!installPath) {
110
177
  const { pathChoice } = await prompts({
111
178
  type: 'select',
112
179
  name: 'pathChoice',
113
- message: 'Installation path:',
180
+ message: 'Installation scope:',
114
181
  choices: [
115
- { title: 'Default (agent-specific paths)', value: 'default' },
182
+ { title: 'Project level (current directory)', value: 'project' },
183
+ { title: 'Global (user-level settings)', value: 'global' },
116
184
  { title: 'Custom path...', value: 'custom' }
117
185
  ]
118
186
  });
119
187
 
120
- if (pathChoice === 'custom') {
188
+ if (pathChoice === 'global') {
189
+ installScope = 'global';
190
+ } else if (pathChoice === 'custom') {
121
191
  const { customDir } = await prompts({
122
192
  type: 'text',
123
193
  name: 'customDir',
@@ -133,7 +203,7 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
133
203
  console.log(chalk.bold(' Summary:'));
134
204
  console.log(chalk.cyan(' Skills: ') + selectedSkillIds.join(', '));
135
205
  console.log(chalk.cyan(' Agents: ') + selectedAgentIds.join(', '));
136
- console.log(chalk.cyan(' Path: ') + (installPath || 'default'));
206
+ console.log(chalk.cyan(' Path: ') + (installPath || (installScope === 'global' ? 'global (user-level)' : 'project (current directory)')));
137
207
  console.log('');
138
208
 
139
209
  const { confirmed } = await prompts({
@@ -148,10 +218,10 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
148
218
  process.exit(0);
149
219
  }
150
220
 
151
- // Step 5: Install each skill
221
+ // Step 5: Install each skill (with upgrade detection)
152
222
  for (const skillId of selectedSkillIds) {
153
223
  try {
154
- await installSkill(skillId, selectedAgentIds, installPath);
224
+ await installSkill(skillId, selectedAgentIds, installPath, installScope, { force: !!forceFlag });
155
225
  } catch (error) {
156
226
  console.error(chalk.red(`\n Failed to install ${skillId}:`), error.message);
157
227
  }
@@ -160,34 +230,29 @@ async function installWizard(preselectedSkill, preselectedAgents, customPath) {
160
230
  console.log(chalk.bold.green('\n Installation complete!\n'));
161
231
  }
162
232
 
163
- async function handleInstall(args) {
164
- const pathIdx = args.indexOf('--path');
165
- let customPath = '';
166
- let positional = [...args];
233
+ // --- Command handlers ---
167
234
 
168
- if (pathIdx !== -1) {
169
- customPath = args[pathIdx + 1] || '';
170
- positional.splice(pathIdx, 2);
171
- }
235
+ async function handleInstall(args) {
236
+ const { globalFlag, forceFlag, customPath, scope, positional } = parseFlags(args);
172
237
 
173
238
  const skillId = positional[0];
174
239
  const agentIds = positional.slice(1);
175
240
 
176
241
  // No args → launch wizard
177
242
  if (!skillId) {
178
- await installWizard(null, null, customPath);
243
+ await installWizard(null, null, customPath, forceFlag);
179
244
  return;
180
245
  }
181
246
 
182
247
  // Skill but no agents → wizard with skill preselected
183
248
  if (agentIds.length === 0) {
184
- await installWizard(skillId, null, customPath);
249
+ await installWizard(skillId, null, customPath, forceFlag);
185
250
  return;
186
251
  }
187
252
 
188
253
  // Full args → direct install
189
254
  try {
190
- await installSkill(skillId, agentIds, customPath);
255
+ await installSkill(skillId, agentIds, customPath, scope, { force: forceFlag });
191
256
  console.log(chalk.bold.green('\n Installation complete!\n'));
192
257
  } catch (error) {
193
258
  console.error(chalk.red('\n Installation failed:'), error.message);
@@ -195,6 +260,33 @@ async function handleInstall(args) {
195
260
  }
196
261
  }
197
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
+
198
290
  async function interactiveMode() {
199
291
  console.log(chalk.bold.cyan(`\n ${NAME} v${VERSION}\n`));
200
292
 
@@ -204,6 +296,7 @@ async function interactiveMode() {
204
296
  message: 'What would you like to do?',
205
297
  choices: [
206
298
  { title: 'Install skills', value: 'install' },
299
+ { title: 'Show installed skills', value: 'status' },
207
300
  { title: 'List available skills', value: 'list-skills' },
208
301
  { title: 'List supported agents', value: 'list-agents' },
209
302
  { title: 'Exit', value: 'exit' }
@@ -215,6 +308,11 @@ async function interactiveMode() {
215
308
  process.exit(0);
216
309
  }
217
310
 
311
+ if (action === 'status') {
312
+ showStatus([]);
313
+ process.exit(0);
314
+ }
315
+
218
316
  if (action === 'list-skills') {
219
317
  showSkills();
220
318
  process.exit(0);
@@ -230,6 +328,8 @@ async function interactiveMode() {
230
328
  }
231
329
  }
232
330
 
331
+ // --- Main ---
332
+
233
333
  async function main() {
234
334
  const args = process.argv.slice(2);
235
335
  const command = args[0];
@@ -238,6 +338,13 @@ async function main() {
238
338
  case 'install':
239
339
  await handleInstall(args.slice(1));
240
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;
241
348
  case 'list':
242
349
  case 'list-skills':
243
350
  showSkills();
package/index.js CHANGED
@@ -1,20 +1,22 @@
1
1
  /**
2
- * AI Agent Skills - Programmatic API
2
+ * ma-agents - Programmatic API
3
3
  *
4
4
  * This module exports the core functionality for use as a library.
5
- * For CLI usage, use: npx ai-agent-skills
5
+ * For CLI usage, use: npx ma-agents
6
6
  */
7
7
 
8
- const { installSkill, listSkills, listAgents } = require('./lib/installer');
8
+ const { installSkill, uninstallSkill, getStatus, listSkills, listAgents, readManifest, getInstalledSkillInfo, compareSemver } = require('./lib/installer');
9
9
  const { getAgent, getAllAgents } = require('./lib/agents');
10
10
 
11
11
  module.exports = {
12
- // Installer functions
13
12
  installSkill,
13
+ uninstallSkill,
14
+ getStatus,
14
15
  listSkills,
15
16
  listAgents,
16
-
17
- // Agent functions
17
+ readManifest,
18
+ getInstalledSkillInfo,
19
+ compareSemver,
18
20
  getAgent,
19
21
  getAllAgents
20
22
  };
package/lib/agents.js CHANGED
@@ -7,7 +7,8 @@ const path = require('path');
7
7
  * - id: unique identifier
8
8
  * - name: display name
9
9
  * - description: brief description
10
- * - getSkillsPath: function that returns the default skills directory
10
+ * - getProjectPath: function that returns the project-level skills directory (relative to cwd)
11
+ * - getGlobalPath: function that returns the global/user-level skills directory
11
12
  * - fileExtension: the file extension for skill files
12
13
  * - template: the template type to use for this agent
13
14
  */
@@ -16,8 +17,10 @@ const agents = [
16
17
  {
17
18
  id: 'claude-code',
18
19
  name: 'Claude Code',
20
+ version: '1.0.0',
19
21
  description: 'Anthropic Claude Code CLI',
20
- getSkillsPath: () => {
22
+ getProjectPath: () => path.join(process.cwd(), '.claude', 'skills'),
23
+ getGlobalPath: () => {
21
24
  const platform = os.platform();
22
25
  if (platform === 'win32') {
23
26
  return path.join(os.homedir(), 'AppData', 'Roaming', 'Claude', 'skills');
@@ -33,8 +36,10 @@ const agents = [
33
36
  {
34
37
  id: 'gemini',
35
38
  name: 'Google Gemini',
39
+ version: '1.0.0',
36
40
  description: 'Google Gemini Code Assist',
37
- getSkillsPath: () => {
41
+ getProjectPath: () => path.join(process.cwd(), '.gemini', 'skills'),
42
+ getGlobalPath: () => {
38
43
  const platform = os.platform();
39
44
  if (platform === 'win32') {
40
45
  return path.join(os.homedir(), 'AppData', 'Roaming', 'Gemini', 'skills');
@@ -50,8 +55,10 @@ const agents = [
50
55
  {
51
56
  id: 'copilot',
52
57
  name: 'GitHub Copilot',
58
+ version: '1.0.0',
53
59
  description: 'GitHub Copilot Agent Mode',
54
- getSkillsPath: () => {
60
+ getProjectPath: () => path.join(process.cwd(), '.github', 'copilot', 'skills'),
61
+ getGlobalPath: () => {
55
62
  const platform = os.platform();
56
63
  if (platform === 'win32') {
57
64
  return path.join(os.homedir(), 'AppData', 'Roaming', 'GitHub Copilot', 'skills');
@@ -67,8 +74,10 @@ const agents = [
67
74
  {
68
75
  id: 'kilocode',
69
76
  name: 'Kilocode',
77
+ version: '1.0.0',
70
78
  description: 'Kilocode AI Assistant',
71
- getSkillsPath: () => {
79
+ getProjectPath: () => path.join(process.cwd(), '.kilocode', 'skills'),
80
+ getGlobalPath: () => {
72
81
  const platform = os.platform();
73
82
  if (platform === 'win32') {
74
83
  return path.join(os.homedir(), 'AppData', 'Roaming', 'Kilocode', 'skills');
@@ -84,8 +93,10 @@ const agents = [
84
93
  {
85
94
  id: 'cline',
86
95
  name: 'Cline',
96
+ version: '1.0.0',
87
97
  description: 'Cline AI Assistant',
88
- getSkillsPath: () => {
98
+ getProjectPath: () => path.join(process.cwd(), '.cline', 'skills'),
99
+ getGlobalPath: () => {
89
100
  const platform = os.platform();
90
101
  if (platform === 'win32') {
91
102
  return path.join(os.homedir(), 'AppData', 'Roaming', 'Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'skills');
@@ -96,13 +107,20 @@ const agents = [
96
107
  }
97
108
  },
98
109
  fileExtension: '.md',
99
- template: 'cline'
110
+ template: 'cline',
111
+ // Cline uses different directory names than the generic structure
112
+ resourceMap: {
113
+ 'references': 'docs',
114
+ 'assets': 'templates'
115
+ }
100
116
  },
101
117
  {
102
118
  id: 'cursor',
103
119
  name: 'Cursor',
120
+ version: '1.0.0',
104
121
  description: 'Cursor AI Editor',
105
- getSkillsPath: () => {
122
+ getProjectPath: () => path.join(process.cwd(), '.cursor', 'skills'),
123
+ getGlobalPath: () => {
106
124
  const platform = os.platform();
107
125
  if (platform === 'win32') {
108
126
  return path.join(os.homedir(), 'AppData', 'Roaming', 'Cursor', 'User', 'skills');