agent-skills-cli 1.0.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/LICENSE +21 -0
- package/README.md +226 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +1181 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/executor.d.ts +22 -0
- package/dist/core/executor.d.ts.map +1 -0
- package/dist/core/executor.js +147 -0
- package/dist/core/executor.js.map +1 -0
- package/dist/core/index.d.ts +12 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +17 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/injector.d.ts +24 -0
- package/dist/core/injector.d.ts.map +1 -0
- package/dist/core/injector.js +99 -0
- package/dist/core/injector.js.map +1 -0
- package/dist/core/loader.d.ts +39 -0
- package/dist/core/loader.d.ts.map +1 -0
- package/dist/core/loader.js +161 -0
- package/dist/core/loader.js.map +1 -0
- package/dist/core/marketplace.d.ts +55 -0
- package/dist/core/marketplace.d.ts.map +1 -0
- package/dist/core/marketplace.js +399 -0
- package/dist/core/marketplace.js.map +1 -0
- package/dist/core/skillsmp.d.ts +38 -0
- package/dist/core/skillsmp.d.ts.map +1 -0
- package/dist/core/skillsmp.js +142 -0
- package/dist/core/skillsmp.js.map +1 -0
- package/dist/core/validator.d.ts +18 -0
- package/dist/core/validator.d.ts.map +1 -0
- package/dist/core/validator.js +177 -0
- package/dist/core/validator.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +108 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/marketplace.d.ts +81 -0
- package/dist/types/marketplace.d.ts.map +1 -0
- package/dist/types/marketplace.js +20 -0
- package/dist/types/marketplace.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,1181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent Skills CLI
|
|
4
|
+
* Universal CLI for managing Agent Skills across Cursor, Claude Code, GitHub Copilot, OpenAI Codex
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import inquirer from 'inquirer';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
import { discoverSkills, loadSkill, validateMetadata, validateBody, formatValidationResult, generateSkillsPromptXML, generateFullSkillsContext, listSkillResources, listMarketplaceSkills, installSkill, uninstallSkill, getInstalledSkills, listMarketplaces, addMarketplace, checkUpdates, fetchSkillsMP, installFromGitHubUrl } from '../core/index.js';
|
|
11
|
+
const program = new Command();
|
|
12
|
+
// Main flow when running `skills` - go straight to install
|
|
13
|
+
async function showMainMenu() {
|
|
14
|
+
console.log(chalk.bold.cyan('\nš Agent Skills CLI\n'));
|
|
15
|
+
// Step 1: Select target agents
|
|
16
|
+
const { agents } = await inquirer.prompt([
|
|
17
|
+
{
|
|
18
|
+
type: 'checkbox',
|
|
19
|
+
name: 'agents',
|
|
20
|
+
message: 'Select AI agents to install skills for:',
|
|
21
|
+
choices: [
|
|
22
|
+
{ name: 'Cursor', value: 'cursor', checked: true },
|
|
23
|
+
{ name: 'Claude Code', value: 'claude', checked: true },
|
|
24
|
+
{ name: 'GitHub Copilot', value: 'copilot', checked: true },
|
|
25
|
+
{ name: 'OpenAI Codex', value: 'codex', checked: false },
|
|
26
|
+
{ name: 'Antigravity', value: 'antigravity', checked: true }
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
]);
|
|
30
|
+
if (agents.length === 0) {
|
|
31
|
+
console.log(chalk.yellow('\nNo agents selected. Exiting.\n'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Step 2: Fetch skills from SkillsMP (40k+ skills, instant)
|
|
35
|
+
const spinner = ora('Fetching skills from SkillsMP...').start();
|
|
36
|
+
let marketplaceSkills = [];
|
|
37
|
+
let total = 0;
|
|
38
|
+
try {
|
|
39
|
+
const result = await fetchSkillsMP({ limit: 100, sortBy: 'stars' });
|
|
40
|
+
marketplaceSkills = result.skills;
|
|
41
|
+
total = result.total;
|
|
42
|
+
spinner.succeed(`Found ${total.toLocaleString()} skills (showing top 100 by stars)`);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
spinner.fail('SkillsMP unavailable, falling back to GitHub...');
|
|
46
|
+
marketplaceSkills = await listMarketplaceSkills();
|
|
47
|
+
}
|
|
48
|
+
if (marketplaceSkills.length === 0) {
|
|
49
|
+
console.log(chalk.yellow('No skills found.'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Step 3: Select skills to install
|
|
53
|
+
const choices = marketplaceSkills.map((skill) => ({
|
|
54
|
+
name: `${skill.name} ${skill.stars ? `(ā${skill.stars.toLocaleString()})` : ''} - ${skill.description?.slice(0, 40) || ''}...`,
|
|
55
|
+
value: { name: skill.name, githubUrl: skill.githubUrl || '' },
|
|
56
|
+
short: skill.name
|
|
57
|
+
}));
|
|
58
|
+
const { selectedSkills } = await inquirer.prompt([
|
|
59
|
+
{
|
|
60
|
+
type: 'checkbox',
|
|
61
|
+
name: 'selectedSkills',
|
|
62
|
+
message: 'Select skills to install (Space to select, Enter to confirm):',
|
|
63
|
+
choices,
|
|
64
|
+
pageSize: 20,
|
|
65
|
+
loop: false
|
|
66
|
+
}
|
|
67
|
+
]);
|
|
68
|
+
if (selectedSkills.length === 0) {
|
|
69
|
+
console.log(chalk.yellow('\nNo skills selected. Exiting.\n'));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Step 4: Install skills from GitHub URLs
|
|
73
|
+
console.log('');
|
|
74
|
+
const homedir = (await import('os')).homedir();
|
|
75
|
+
const skillsDir = `${homedir}/.antigravity/skills`;
|
|
76
|
+
for (const skill of selectedSkills) {
|
|
77
|
+
const installSpinner = ora(`Installing ${skill.name}...`).start();
|
|
78
|
+
try {
|
|
79
|
+
if (skill.githubUrl) {
|
|
80
|
+
// Install directly from GitHub URL (SkillsMP skills)
|
|
81
|
+
await installFromGitHubUrl(skill.githubUrl, skillsDir);
|
|
82
|
+
installSpinner.succeed(`Installed: ${skill.name}`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// Fallback to marketplace install
|
|
86
|
+
await installSkill(skill.name);
|
|
87
|
+
installSpinner.succeed(`Installed: ${skill.name}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
installSpinner.fail(`Failed: ${skill.name} - ${err.message || err}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Step 5: Export to selected agents
|
|
95
|
+
console.log('');
|
|
96
|
+
const allSkills = await discoverSkills();
|
|
97
|
+
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
98
|
+
const { join } = await import('path');
|
|
99
|
+
const { existsSync } = await import('fs');
|
|
100
|
+
const fs = { mkdir, writeFile, appendFile, join, existsSync };
|
|
101
|
+
for (const agent of agents) {
|
|
102
|
+
const exportSpinner = ora(`Exporting to ${agent}...`).start();
|
|
103
|
+
await exportToAgent(agent, allSkills, '.', fs);
|
|
104
|
+
exportSpinner.succeed(`Exported to ${agent}`);
|
|
105
|
+
}
|
|
106
|
+
console.log(chalk.bold.green('\n⨠Done! Skills installed and ready to use.\n'));
|
|
107
|
+
}
|
|
108
|
+
async function interactiveInstall() {
|
|
109
|
+
// Step 1: Select target agent(s)
|
|
110
|
+
const { agents } = await inquirer.prompt([
|
|
111
|
+
{
|
|
112
|
+
type: 'checkbox',
|
|
113
|
+
name: 'agents',
|
|
114
|
+
message: 'Which AI agents will you use these skills with?',
|
|
115
|
+
choices: [
|
|
116
|
+
{ name: 'Cursor', value: 'cursor', checked: true },
|
|
117
|
+
{ name: 'Claude Code', value: 'claude', checked: true },
|
|
118
|
+
{ name: 'GitHub Copilot', value: 'copilot', checked: true },
|
|
119
|
+
{ name: 'OpenAI Codex', value: 'codex', checked: false }
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
]);
|
|
123
|
+
if (agents.length === 0) {
|
|
124
|
+
console.log(chalk.yellow('No agents selected.'));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// Step 2: Fetch and select skills
|
|
128
|
+
const spinner = ora('Fetching skills from marketplace...').start();
|
|
129
|
+
const skills = await listMarketplaceSkills();
|
|
130
|
+
spinner.stop();
|
|
131
|
+
if (skills.length === 0) {
|
|
132
|
+
console.log(chalk.yellow('No skills found.'));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const choices = skills.map(skill => ({
|
|
136
|
+
name: `${skill.name} - ${skill.description?.slice(0, 45) || 'No description'}...`,
|
|
137
|
+
value: skill.name,
|
|
138
|
+
short: skill.name
|
|
139
|
+
}));
|
|
140
|
+
const { selectedSkills } = await inquirer.prompt([
|
|
141
|
+
{
|
|
142
|
+
type: 'checkbox',
|
|
143
|
+
name: 'selectedSkills',
|
|
144
|
+
message: 'Select skills to install (Space to select):',
|
|
145
|
+
choices,
|
|
146
|
+
pageSize: 12
|
|
147
|
+
}
|
|
148
|
+
]);
|
|
149
|
+
if (selectedSkills.length === 0) {
|
|
150
|
+
console.log(chalk.yellow('No skills selected.'));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Step 3: Install skills
|
|
154
|
+
console.log('');
|
|
155
|
+
for (const skillName of selectedSkills) {
|
|
156
|
+
const installSpinner = ora(`Installing ${skillName}...`).start();
|
|
157
|
+
try {
|
|
158
|
+
await installSkill(skillName);
|
|
159
|
+
installSpinner.succeed(`Installed: ${skillName}`);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
installSpinner.fail(`Failed: ${skillName}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Step 4: Export to selected agents
|
|
166
|
+
console.log('');
|
|
167
|
+
const exportSpinner = ora('Exporting to selected agents...').start();
|
|
168
|
+
const allSkills = await discoverSkills();
|
|
169
|
+
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
170
|
+
const { join } = await import('path');
|
|
171
|
+
const { existsSync } = await import('fs');
|
|
172
|
+
const fs = { mkdir, writeFile, appendFile, join, existsSync };
|
|
173
|
+
exportSpinner.stop();
|
|
174
|
+
for (const agent of agents) {
|
|
175
|
+
const agentSpinner = ora(`Exporting to ${agent}...`).start();
|
|
176
|
+
await exportToAgent(agent, allSkills, '.', fs);
|
|
177
|
+
agentSpinner.succeed(`Exported to ${agent}`);
|
|
178
|
+
}
|
|
179
|
+
console.log(chalk.bold.green('\n⨠Done! Skills installed and exported.\n'));
|
|
180
|
+
}
|
|
181
|
+
async function interactiveExport() {
|
|
182
|
+
const skills = await discoverSkills();
|
|
183
|
+
if (skills.length === 0) {
|
|
184
|
+
console.log(chalk.yellow('No skills found to export.'));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const { agents } = await inquirer.prompt([
|
|
188
|
+
{
|
|
189
|
+
type: 'checkbox',
|
|
190
|
+
name: 'agents',
|
|
191
|
+
message: 'Select target AI agents:',
|
|
192
|
+
choices: [
|
|
193
|
+
{ name: 'Cursor (.cursor/skills/)', value: 'cursor', checked: true },
|
|
194
|
+
{ name: 'Claude Code (.claude/skills/)', value: 'claude', checked: true },
|
|
195
|
+
{ name: 'GitHub Copilot (.github/skills/)', value: 'copilot', checked: true },
|
|
196
|
+
{ name: 'OpenAI Codex (.codex/skills/)', value: 'codex', checked: false }
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
]);
|
|
200
|
+
if (agents.length === 0) {
|
|
201
|
+
console.log(chalk.yellow('No agents selected.'));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
205
|
+
const { join } = await import('path');
|
|
206
|
+
const { existsSync } = await import('fs');
|
|
207
|
+
const fs = { mkdir, writeFile, appendFile, join, existsSync };
|
|
208
|
+
console.log('');
|
|
209
|
+
for (const agent of agents) {
|
|
210
|
+
const spinner = ora(`Exporting to ${agent}...`).start();
|
|
211
|
+
await exportToAgent(agent, skills, '.', fs);
|
|
212
|
+
spinner.succeed();
|
|
213
|
+
}
|
|
214
|
+
console.log(chalk.bold.green('\nā Export complete!\n'));
|
|
215
|
+
}
|
|
216
|
+
program
|
|
217
|
+
.name('skills')
|
|
218
|
+
.description('Agent Skills CLI - Manage skills for Cursor, Claude Code, GitHub Copilot, OpenAI Codex')
|
|
219
|
+
.version('1.0.0')
|
|
220
|
+
.action(showMainMenu);
|
|
221
|
+
// List command
|
|
222
|
+
program
|
|
223
|
+
.command('list')
|
|
224
|
+
.description('List all discovered skills')
|
|
225
|
+
.option('-p, --paths <paths...>', 'Custom search paths')
|
|
226
|
+
.option('-v, --verbose', 'Show detailed information')
|
|
227
|
+
.action(async (options) => {
|
|
228
|
+
try {
|
|
229
|
+
const config = options.paths ? { searchPaths: options.paths } : {};
|
|
230
|
+
const skills = await discoverSkills(config);
|
|
231
|
+
if (skills.length === 0) {
|
|
232
|
+
console.log(chalk.yellow('No skills found.'));
|
|
233
|
+
console.log(chalk.gray('Skills are searched in:'));
|
|
234
|
+
console.log(chalk.gray(' - ~/.antigravity/skills/'));
|
|
235
|
+
console.log(chalk.gray(' - .antigravity/skills/'));
|
|
236
|
+
console.log(chalk.gray(' - ./skills/'));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
console.log(chalk.bold(`\nFound ${skills.length} skill(s):\n`));
|
|
240
|
+
for (const skill of skills) {
|
|
241
|
+
console.log(chalk.cyan(` ${skill.name}`));
|
|
242
|
+
if (options.verbose) {
|
|
243
|
+
console.log(chalk.gray(` ${skill.description}`));
|
|
244
|
+
console.log(chalk.gray(` Path: ${skill.path}`));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
console.log('');
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
console.error(chalk.red('Error listing skills:'), error);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
// Validate command
|
|
255
|
+
program
|
|
256
|
+
.command('validate <path>')
|
|
257
|
+
.description('Validate a skill against the Agent Skills specification')
|
|
258
|
+
.action(async (path) => {
|
|
259
|
+
try {
|
|
260
|
+
const skill = await loadSkill(path);
|
|
261
|
+
if (!skill) {
|
|
262
|
+
console.error(chalk.red(`Skill not found at: ${path}`));
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
console.log(chalk.bold(`\nValidating: ${skill.metadata.name}\n`));
|
|
266
|
+
// Validate metadata
|
|
267
|
+
const metadataResult = validateMetadata(skill.metadata);
|
|
268
|
+
console.log(chalk.underline('Metadata:'));
|
|
269
|
+
console.log(formatValidationResult(metadataResult));
|
|
270
|
+
// Validate body
|
|
271
|
+
const bodyResult = validateBody(skill.body);
|
|
272
|
+
console.log(chalk.underline('\nBody Content:'));
|
|
273
|
+
console.log(formatValidationResult(bodyResult));
|
|
274
|
+
// Overall result
|
|
275
|
+
const isValid = metadataResult.valid && bodyResult.valid;
|
|
276
|
+
console.log('\n' + 'ā'.repeat(40));
|
|
277
|
+
if (isValid) {
|
|
278
|
+
console.log(chalk.green.bold('ā Skill is valid'));
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
console.log(chalk.red.bold('ā Skill has validation errors'));
|
|
282
|
+
process.exit(1);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
console.error(chalk.red('Error validating skill:'), error);
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
// Show command
|
|
291
|
+
program
|
|
292
|
+
.command('show <name>')
|
|
293
|
+
.description('Show detailed information about a skill')
|
|
294
|
+
.action(async (name) => {
|
|
295
|
+
try {
|
|
296
|
+
const skills = await discoverSkills();
|
|
297
|
+
const skillRef = skills.find(s => s.name === name);
|
|
298
|
+
if (!skillRef) {
|
|
299
|
+
console.error(chalk.red(`Skill not found: ${name}`));
|
|
300
|
+
console.log(chalk.gray('Available skills:'), skills.map(s => s.name).join(', ') || 'none');
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
const skill = await loadSkill(skillRef.path);
|
|
304
|
+
if (!skill) {
|
|
305
|
+
console.error(chalk.red(`Could not load skill: ${name}`));
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
console.log(chalk.bold(`\n${skill.metadata.name}`));
|
|
309
|
+
console.log('ā'.repeat(40));
|
|
310
|
+
console.log(chalk.cyan('Description:'), skill.metadata.description);
|
|
311
|
+
console.log(chalk.cyan('Path:'), skill.path);
|
|
312
|
+
if (skill.metadata.license) {
|
|
313
|
+
console.log(chalk.cyan('License:'), skill.metadata.license);
|
|
314
|
+
}
|
|
315
|
+
if (skill.metadata.compatibility) {
|
|
316
|
+
console.log(chalk.cyan('Compatibility:'), skill.metadata.compatibility);
|
|
317
|
+
}
|
|
318
|
+
// List resources
|
|
319
|
+
const resources = await listSkillResources(skill.path);
|
|
320
|
+
if (resources.scripts.length > 0) {
|
|
321
|
+
console.log(chalk.cyan('\nScripts:'));
|
|
322
|
+
resources.scripts.forEach(s => console.log(chalk.gray(` - ${s}`)));
|
|
323
|
+
}
|
|
324
|
+
if (resources.references.length > 0) {
|
|
325
|
+
console.log(chalk.cyan('\nReferences:'));
|
|
326
|
+
resources.references.forEach(r => console.log(chalk.gray(` - ${r}`)));
|
|
327
|
+
}
|
|
328
|
+
if (resources.assets.length > 0) {
|
|
329
|
+
console.log(chalk.cyan('\nAssets:'));
|
|
330
|
+
resources.assets.forEach(a => console.log(chalk.gray(` - ${a}`)));
|
|
331
|
+
}
|
|
332
|
+
// Body preview
|
|
333
|
+
const bodyLines = skill.body.split('\n').slice(0, 10);
|
|
334
|
+
console.log(chalk.cyan('\nInstructions (preview):'));
|
|
335
|
+
console.log(chalk.gray(bodyLines.join('\n')));
|
|
336
|
+
if (skill.body.split('\n').length > 10) {
|
|
337
|
+
console.log(chalk.gray('...'));
|
|
338
|
+
}
|
|
339
|
+
console.log('');
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
console.error(chalk.red('Error showing skill:'), error);
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
// Prompt command - generate system prompt XML
|
|
347
|
+
program
|
|
348
|
+
.command('prompt')
|
|
349
|
+
.description('Generate system prompt XML for discovered skills')
|
|
350
|
+
.option('-f, --full', 'Include full skill system instructions')
|
|
351
|
+
.action(async (options) => {
|
|
352
|
+
try {
|
|
353
|
+
const skills = await discoverSkills();
|
|
354
|
+
if (skills.length === 0) {
|
|
355
|
+
console.log(chalk.yellow('No skills found.'));
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (options.full) {
|
|
359
|
+
const context = generateFullSkillsContext(skills);
|
|
360
|
+
console.log(context);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
const { xml, skillCount, estimatedTokens } = generateSkillsPromptXML(skills);
|
|
364
|
+
console.log(xml);
|
|
365
|
+
console.log(chalk.gray(`\n# ${skillCount} skills, ~${estimatedTokens} tokens`));
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
console.error(chalk.red('Error generating prompt:'), error);
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
// Init command - create a new skill
|
|
374
|
+
program
|
|
375
|
+
.command('init <name>')
|
|
376
|
+
.description('Create a new skill from template')
|
|
377
|
+
.option('-d, --directory <dir>', 'Directory to create skill in', './skills')
|
|
378
|
+
.action(async (name, options) => {
|
|
379
|
+
try {
|
|
380
|
+
const { mkdir, writeFile } = await import('fs/promises');
|
|
381
|
+
const { join } = await import('path');
|
|
382
|
+
const skillDir = join(options.directory, name);
|
|
383
|
+
// Create directories
|
|
384
|
+
await mkdir(join(skillDir, 'scripts'), { recursive: true });
|
|
385
|
+
await mkdir(join(skillDir, 'references'), { recursive: true });
|
|
386
|
+
await mkdir(join(skillDir, 'assets'), { recursive: true });
|
|
387
|
+
// Create SKILL.md
|
|
388
|
+
const skillMd = `---
|
|
389
|
+
name: ${name}
|
|
390
|
+
description: Brief description of what this skill does and when to use it.
|
|
391
|
+
license: MIT
|
|
392
|
+
metadata:
|
|
393
|
+
author: your-name
|
|
394
|
+
version: "1.0"
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
# ${name.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}
|
|
398
|
+
|
|
399
|
+
## When to use this skill
|
|
400
|
+
|
|
401
|
+
Use this skill when the user needs to...
|
|
402
|
+
|
|
403
|
+
## Instructions
|
|
404
|
+
|
|
405
|
+
1. First step
|
|
406
|
+
2. Second step
|
|
407
|
+
3. Third step
|
|
408
|
+
|
|
409
|
+
## Examples
|
|
410
|
+
|
|
411
|
+
### Example 1
|
|
412
|
+
|
|
413
|
+
\`\`\`
|
|
414
|
+
Example input or command
|
|
415
|
+
\`\`\`
|
|
416
|
+
|
|
417
|
+
## Best practices
|
|
418
|
+
|
|
419
|
+
- Best practice 1
|
|
420
|
+
- Best practice 2
|
|
421
|
+
`;
|
|
422
|
+
await writeFile(join(skillDir, 'SKILL.md'), skillMd);
|
|
423
|
+
console.log(chalk.green(`ā Created skill: ${name}`));
|
|
424
|
+
console.log(chalk.gray(` Path: ${skillDir}`));
|
|
425
|
+
console.log(chalk.gray('\nNext steps:'));
|
|
426
|
+
console.log(chalk.gray(' 1. Edit SKILL.md with your instructions'));
|
|
427
|
+
console.log(chalk.gray(' 2. Add scripts to scripts/'));
|
|
428
|
+
console.log(chalk.gray(' 3. Run: skills validate ' + skillDir));
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
console.error(chalk.red('Error creating skill:'), error);
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
// ============================================
|
|
436
|
+
// MARKETPLACE COMMANDS
|
|
437
|
+
// ============================================
|
|
438
|
+
// Market list - list skills from SkillsMP (40k+ skills)
|
|
439
|
+
program
|
|
440
|
+
.command('market-list')
|
|
441
|
+
.alias('ml')
|
|
442
|
+
.description('List skills from SkillsMP marketplace (40k+ skills)')
|
|
443
|
+
.option('-l, --limit <number>', 'Number of skills to show', '50')
|
|
444
|
+
.option('-p, --page <number>', 'Page number', '1')
|
|
445
|
+
.option('--legacy', 'Use legacy GitHub sources instead of SkillsMP')
|
|
446
|
+
.action(async (options) => {
|
|
447
|
+
try {
|
|
448
|
+
if (options.legacy) {
|
|
449
|
+
// Legacy mode: fetch from configured GitHub sources
|
|
450
|
+
console.log(chalk.bold('\nFetching skills from GitHub sources...\n'));
|
|
451
|
+
const skills = await listMarketplaceSkills();
|
|
452
|
+
if (skills.length === 0) {
|
|
453
|
+
console.log(chalk.yellow('No skills found.'));
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const bySource = new Map();
|
|
457
|
+
for (const skill of skills) {
|
|
458
|
+
const sourceId = skill.source.id;
|
|
459
|
+
if (!bySource.has(sourceId)) {
|
|
460
|
+
bySource.set(sourceId, []);
|
|
461
|
+
}
|
|
462
|
+
bySource.get(sourceId).push(skill);
|
|
463
|
+
}
|
|
464
|
+
for (const [sourceId, sourceSkills] of bySource) {
|
|
465
|
+
const source = sourceSkills[0].source;
|
|
466
|
+
console.log(chalk.bold.cyan(`\nš¦ ${source.name}`));
|
|
467
|
+
console.log(chalk.gray(` ${source.owner}/${source.repo}`));
|
|
468
|
+
if (source.verified) {
|
|
469
|
+
console.log(chalk.green(' ā Verified'));
|
|
470
|
+
}
|
|
471
|
+
console.log('');
|
|
472
|
+
for (const skill of sourceSkills) {
|
|
473
|
+
console.log(chalk.white(` ${skill.name}`));
|
|
474
|
+
if (skill.description) {
|
|
475
|
+
const desc = skill.description.length > 60
|
|
476
|
+
? skill.description.slice(0, 60) + '...'
|
|
477
|
+
: skill.description;
|
|
478
|
+
console.log(chalk.gray(` ${desc}`));
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
console.log(chalk.gray(`\nTotal: ${skills.length} skills from ${bySource.size} sources`));
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
// SkillsMP mode: fetch from API
|
|
486
|
+
console.log(chalk.bold('\nš SkillsMP Marketplace\n'));
|
|
487
|
+
const limit = parseInt(options.limit) || 50;
|
|
488
|
+
const page = parseInt(options.page) || 1;
|
|
489
|
+
const result = await fetchSkillsMP({ limit, page, sortBy: 'stars' });
|
|
490
|
+
console.log(chalk.gray(`Showing ${result.skills.length} of ${result.total.toLocaleString()} skills (page ${page})\n`));
|
|
491
|
+
for (const skill of result.skills) {
|
|
492
|
+
const stars = skill.stars ? chalk.yellow(`ā${skill.stars.toLocaleString()}`) : '';
|
|
493
|
+
console.log(chalk.white(` ${skill.name} ${stars}`));
|
|
494
|
+
if (skill.description) {
|
|
495
|
+
const desc = skill.description.length > 55
|
|
496
|
+
? skill.description.slice(0, 55) + '...'
|
|
497
|
+
: skill.description;
|
|
498
|
+
console.log(chalk.gray(` ${desc}`));
|
|
499
|
+
}
|
|
500
|
+
console.log(chalk.dim(` by ${skill.author || 'unknown'}`));
|
|
501
|
+
}
|
|
502
|
+
console.log(chalk.gray(`\nTotal: ${result.total.toLocaleString()} skills`));
|
|
503
|
+
if (result.hasNext) {
|
|
504
|
+
console.log(chalk.gray(`Next page: skills market-list --page ${page + 1}`));
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
console.log(chalk.gray('\nUse: skills (interactive) to install\n'));
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
510
|
+
console.error(chalk.red('Error:'), error);
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
// Market search - search skills
|
|
515
|
+
program
|
|
516
|
+
.command('market-search <query>')
|
|
517
|
+
.alias('ms')
|
|
518
|
+
.description('Search skills on SkillsMP (40k+ skills)')
|
|
519
|
+
.option('-l, --limit <number>', 'Number of results', '20')
|
|
520
|
+
.action(async (query, options) => {
|
|
521
|
+
try {
|
|
522
|
+
console.log(chalk.bold(`\nš Searching SkillsMP for "${query}"...\n`));
|
|
523
|
+
const limit = parseInt(options.limit) || 20;
|
|
524
|
+
const result = await fetchSkillsMP({ search: query, limit, sortBy: 'stars' });
|
|
525
|
+
if (result.skills.length === 0) {
|
|
526
|
+
console.log(chalk.yellow(`No skills found matching "${query}"`));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
console.log(chalk.gray(`Found ${result.total.toLocaleString()} skills (showing top ${result.skills.length}):\n`));
|
|
530
|
+
for (const skill of result.skills) {
|
|
531
|
+
const stars = skill.stars ? chalk.yellow(`ā${skill.stars.toLocaleString()}`) : '';
|
|
532
|
+
console.log(chalk.cyan(` ${skill.name} ${stars}`));
|
|
533
|
+
console.log(chalk.gray(` ${skill.description?.slice(0, 70)}${(skill.description?.length || 0) > 70 ? '...' : ''}`));
|
|
534
|
+
console.log(chalk.dim(` by ${skill.author || 'unknown'}`));
|
|
535
|
+
console.log('');
|
|
536
|
+
}
|
|
537
|
+
console.log(chalk.gray('Use: skills (interactive) to install\n'));
|
|
538
|
+
}
|
|
539
|
+
catch (error) {
|
|
540
|
+
console.error(chalk.red('Error searching skills:'), error);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
// Install - Install a skill by name from SkillsMP
|
|
545
|
+
program
|
|
546
|
+
.command('install <name>')
|
|
547
|
+
.alias('i')
|
|
548
|
+
.description('Install a skill by name from SkillsMP')
|
|
549
|
+
.action(async (name) => {
|
|
550
|
+
try {
|
|
551
|
+
const homedir = (await import('os')).homedir();
|
|
552
|
+
const skillsDir = `${homedir}/.antigravity/skills`;
|
|
553
|
+
console.log(chalk.bold(`\nš¦ Searching for "${name}" on SkillsMP...\n`));
|
|
554
|
+
const result = await fetchSkillsMP({ search: name, limit: 20, sortBy: 'stars' });
|
|
555
|
+
// Find exact name match first
|
|
556
|
+
const exactMatch = result.skills.find(s => s.name.toLowerCase() === name.toLowerCase());
|
|
557
|
+
const skill = exactMatch || result.skills[0];
|
|
558
|
+
if (!skill) {
|
|
559
|
+
console.log(chalk.yellow(`No skill found matching "${name}"`));
|
|
560
|
+
console.log(chalk.gray('Try: skills market-search <query> to find skills\n'));
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const githubUrl = skill.githubUrl;
|
|
564
|
+
if (!githubUrl) {
|
|
565
|
+
console.log(chalk.red('Could not find GitHub URL for this skill'));
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
console.log(chalk.gray(`Found: ${skill.name} by ${skill.author}`));
|
|
569
|
+
console.log(chalk.gray(`Installing from: ${githubUrl}\n`));
|
|
570
|
+
const installed = await installFromGitHubUrl(githubUrl, skillsDir);
|
|
571
|
+
console.log(chalk.green(`ā Successfully installed: ${installed.name}`));
|
|
572
|
+
console.log(chalk.gray(` Path: ${installed.path}`));
|
|
573
|
+
console.log('');
|
|
574
|
+
}
|
|
575
|
+
catch (error) {
|
|
576
|
+
console.error(chalk.red('Error installing skill:'), error.message || error);
|
|
577
|
+
process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
// Alias for backward compatibility
|
|
581
|
+
program
|
|
582
|
+
.command('market-install <name>')
|
|
583
|
+
.alias('mi')
|
|
584
|
+
.description('Install a skill (alias for: skills install)')
|
|
585
|
+
.action(async (name) => {
|
|
586
|
+
console.log(chalk.gray('Tip: Use `skills install <id-or-name>` directly\n'));
|
|
587
|
+
const { execSync } = await import('child_process');
|
|
588
|
+
try {
|
|
589
|
+
execSync(`"${process.argv[0]}" "${process.argv[1]}" install "${name}"`, { stdio: 'inherit' });
|
|
590
|
+
}
|
|
591
|
+
catch { }
|
|
592
|
+
});
|
|
593
|
+
// Install from URL - install directly from GitHub or SkillsMP URL
|
|
594
|
+
program
|
|
595
|
+
.command('install-url <url>')
|
|
596
|
+
.alias('iu')
|
|
597
|
+
.description('Install a skill from GitHub URL or SkillsMP page URL')
|
|
598
|
+
.action(async (url) => {
|
|
599
|
+
try {
|
|
600
|
+
let githubUrl = url;
|
|
601
|
+
// Convert SkillsMP URL to GitHub URL
|
|
602
|
+
// Format: https://skillsmp.com/skills/<id>
|
|
603
|
+
if (url.includes('skillsmp.com/skills/')) {
|
|
604
|
+
console.log(chalk.bold(`\nš¦ Fetching skill info from SkillsMP...`));
|
|
605
|
+
// Extract skill ID from URL
|
|
606
|
+
const skillId = url.split('/skills/').pop()?.replace(/\/$/, '');
|
|
607
|
+
// Fetch skill details from API
|
|
608
|
+
const response = await fetch(`https://skillsmp.com/api/skills/${skillId}`);
|
|
609
|
+
if (!response.ok) {
|
|
610
|
+
throw new Error('Could not find skill on SkillsMP');
|
|
611
|
+
}
|
|
612
|
+
const data = await response.json();
|
|
613
|
+
githubUrl = data.skill.githubUrl;
|
|
614
|
+
console.log(chalk.gray(`Found: ${data.skill.name} by ${data.skill.author}\n`));
|
|
615
|
+
}
|
|
616
|
+
// Validate GitHub URL
|
|
617
|
+
if (!githubUrl.includes('github.com')) {
|
|
618
|
+
console.log(chalk.red('Invalid URL. Please provide a GitHub URL or SkillsMP skill page URL.'));
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
console.log(chalk.gray(`Installing from: ${githubUrl}\n`));
|
|
622
|
+
const homedir = (await import('os')).homedir();
|
|
623
|
+
const skillsDir = `${homedir}/.antigravity/skills`;
|
|
624
|
+
const installed = await installFromGitHubUrl(githubUrl, skillsDir);
|
|
625
|
+
console.log(chalk.green(`ā Successfully installed: ${installed.name}`));
|
|
626
|
+
console.log(chalk.gray(` Path: ${installed.path}`));
|
|
627
|
+
console.log('');
|
|
628
|
+
}
|
|
629
|
+
catch (error) {
|
|
630
|
+
console.error(chalk.red('Error installing skill:'), error.message || error);
|
|
631
|
+
process.exit(1);
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
// Market uninstall - remove an installed skill
|
|
635
|
+
program
|
|
636
|
+
.command('market-uninstall <name>')
|
|
637
|
+
.alias('mu')
|
|
638
|
+
.description('Uninstall a marketplace-installed skill')
|
|
639
|
+
.action(async (name) => {
|
|
640
|
+
try {
|
|
641
|
+
await uninstallSkill(name);
|
|
642
|
+
console.log(chalk.green(`ā Uninstalled: ${name}`));
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
console.error(chalk.red('Error uninstalling skill:'), error);
|
|
646
|
+
process.exit(1);
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
// Market installed - show installed marketplace skills
|
|
650
|
+
program
|
|
651
|
+
.command('market-installed')
|
|
652
|
+
.alias('mind')
|
|
653
|
+
.description('List skills installed from marketplaces')
|
|
654
|
+
.action(async () => {
|
|
655
|
+
try {
|
|
656
|
+
const installed = await getInstalledSkills();
|
|
657
|
+
if (installed.length === 0) {
|
|
658
|
+
console.log(chalk.yellow('\nNo marketplace skills installed.'));
|
|
659
|
+
console.log(chalk.gray('Use: skills market-install <name> to install\n'));
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
console.log(chalk.bold(`\nInstalled marketplace skills:\n`));
|
|
663
|
+
for (const skill of installed) {
|
|
664
|
+
console.log(chalk.cyan(` ${skill.name}`));
|
|
665
|
+
console.log(chalk.gray(` Path: ${skill.localPath}`));
|
|
666
|
+
if (skill.source) {
|
|
667
|
+
console.log(chalk.gray(` Source: ${skill.source.name}`));
|
|
668
|
+
}
|
|
669
|
+
if (skill.version) {
|
|
670
|
+
console.log(chalk.gray(` Version: ${skill.version}`));
|
|
671
|
+
}
|
|
672
|
+
console.log(chalk.gray(` Installed: ${skill.installedAt}`));
|
|
673
|
+
console.log('');
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
catch (error) {
|
|
677
|
+
console.error(chalk.red('Error listing installed skills:'), error);
|
|
678
|
+
process.exit(1);
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
// Market sources - list marketplace sources
|
|
682
|
+
program
|
|
683
|
+
.command('market-sources')
|
|
684
|
+
.description('List registered marketplace sources')
|
|
685
|
+
.action(async () => {
|
|
686
|
+
try {
|
|
687
|
+
// Show SkillsMP as primary
|
|
688
|
+
console.log(chalk.bold('\nš Primary Marketplace:\n'));
|
|
689
|
+
console.log(chalk.cyan(` SkillsMP`) + chalk.green(' ā'));
|
|
690
|
+
console.log(chalk.gray(` URL: https://skillsmp.com`));
|
|
691
|
+
console.log(chalk.gray(` Skills: 40,000+`));
|
|
692
|
+
console.log(chalk.gray(` The largest Agent Skills marketplace`));
|
|
693
|
+
console.log('');
|
|
694
|
+
// Show legacy sources
|
|
695
|
+
const sources = await listMarketplaces();
|
|
696
|
+
if (sources.length > 0) {
|
|
697
|
+
console.log(chalk.bold('Legacy GitHub Sources:\n'));
|
|
698
|
+
for (const source of sources) {
|
|
699
|
+
const verified = source.verified ? chalk.green(' ā') : '';
|
|
700
|
+
console.log(chalk.cyan(` ${source.name}${verified}`));
|
|
701
|
+
console.log(chalk.gray(` ID: ${source.id}`));
|
|
702
|
+
console.log(chalk.gray(` Repo: ${source.owner}/${source.repo}`));
|
|
703
|
+
if (source.description) {
|
|
704
|
+
console.log(chalk.gray(` ${source.description}`));
|
|
705
|
+
}
|
|
706
|
+
console.log('');
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
catch (error) {
|
|
711
|
+
console.error(chalk.red('Error listing sources:'), error);
|
|
712
|
+
process.exit(1);
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
// Market add-source - add a new marketplace
|
|
716
|
+
program
|
|
717
|
+
.command('market-add-source')
|
|
718
|
+
.description('Add a custom marketplace source')
|
|
719
|
+
.requiredOption('--id <id>', 'Unique identifier')
|
|
720
|
+
.requiredOption('--name <name>', 'Display name')
|
|
721
|
+
.requiredOption('--owner <owner>', 'GitHub owner')
|
|
722
|
+
.requiredOption('--repo <repo>', 'GitHub repository')
|
|
723
|
+
.option('--branch <branch>', 'Branch name', 'main')
|
|
724
|
+
.option('--path <path>', 'Path to skills directory', 'skills')
|
|
725
|
+
.action(async (options) => {
|
|
726
|
+
try {
|
|
727
|
+
await addMarketplace({
|
|
728
|
+
id: options.id,
|
|
729
|
+
name: options.name,
|
|
730
|
+
owner: options.owner,
|
|
731
|
+
repo: options.repo,
|
|
732
|
+
branch: options.branch,
|
|
733
|
+
skillsPath: options.path,
|
|
734
|
+
verified: false
|
|
735
|
+
});
|
|
736
|
+
console.log(chalk.green(`ā Added marketplace: ${options.name}`));
|
|
737
|
+
}
|
|
738
|
+
catch (error) {
|
|
739
|
+
console.error(chalk.red('Error adding marketplace:'), error);
|
|
740
|
+
process.exit(1);
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
// Market update-check - check for updates
|
|
744
|
+
program
|
|
745
|
+
.command('market-update-check')
|
|
746
|
+
.alias('muc')
|
|
747
|
+
.description('Check for updates to installed skills')
|
|
748
|
+
.action(async () => {
|
|
749
|
+
try {
|
|
750
|
+
console.log(chalk.bold('\nChecking for updates...\n'));
|
|
751
|
+
const updates = await checkUpdates();
|
|
752
|
+
if (updates.length === 0) {
|
|
753
|
+
console.log(chalk.yellow('No installed marketplace skills to check.'));
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
const hasUpdates = updates.filter(u => u.hasUpdate);
|
|
757
|
+
if (hasUpdates.length === 0) {
|
|
758
|
+
console.log(chalk.green('All skills are up to date! ā'));
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
console.log(chalk.yellow(`${hasUpdates.length} skill(s) have updates available:\n`));
|
|
762
|
+
for (const update of hasUpdates) {
|
|
763
|
+
console.log(chalk.cyan(` ${update.skill.name}`));
|
|
764
|
+
console.log(chalk.gray(` Current: ${update.currentVersion || 'unknown'}`));
|
|
765
|
+
console.log(chalk.green(` Latest: ${update.latestVersion}`));
|
|
766
|
+
console.log('');
|
|
767
|
+
}
|
|
768
|
+
console.log(chalk.gray('To update, uninstall and reinstall the skill.'));
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
catch (error) {
|
|
772
|
+
console.error(chalk.red('Error checking updates:'), error);
|
|
773
|
+
process.exit(1);
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
// ============================================
|
|
777
|
+
// WORKFLOW SYNC COMMAND
|
|
778
|
+
// ============================================
|
|
779
|
+
// Sync - copy skills to .agent/workflows for Antigravity auto-discovery
|
|
780
|
+
program
|
|
781
|
+
.command('sync')
|
|
782
|
+
.description('Sync skills to .agent/workflows/ for Antigravity auto-discovery')
|
|
783
|
+
.option('-d, --directory <dir>', 'Target project directory', '.')
|
|
784
|
+
.option('-a, --all', 'Sync all discovered skills')
|
|
785
|
+
.option('-n, --name <name>', 'Sync a specific skill by name')
|
|
786
|
+
.action(async (options) => {
|
|
787
|
+
try {
|
|
788
|
+
const { mkdir, writeFile, readFile, cp } = await import('fs/promises');
|
|
789
|
+
const { join } = await import('path');
|
|
790
|
+
const { existsSync } = await import('fs');
|
|
791
|
+
const workflowsDir = join(options.directory, '.agent', 'workflows');
|
|
792
|
+
await mkdir(workflowsDir, { recursive: true });
|
|
793
|
+
const skills = await discoverSkills();
|
|
794
|
+
if (skills.length === 0) {
|
|
795
|
+
console.log(chalk.yellow('No skills found to sync.'));
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
// Filter skills if specific name provided
|
|
799
|
+
const toSync = options.name
|
|
800
|
+
? skills.filter(s => s.name === options.name)
|
|
801
|
+
: options.all
|
|
802
|
+
? skills
|
|
803
|
+
: skills; // Default: sync all
|
|
804
|
+
if (toSync.length === 0) {
|
|
805
|
+
console.log(chalk.yellow(`Skill not found: ${options.name}`));
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
console.log(chalk.bold(`\nSyncing ${toSync.length} skill(s) to ${workflowsDir}...\n`));
|
|
809
|
+
for (const skillRef of toSync) {
|
|
810
|
+
try {
|
|
811
|
+
const skill = await loadSkill(skillRef.path);
|
|
812
|
+
if (!skill)
|
|
813
|
+
continue;
|
|
814
|
+
// Create workflow file from skill
|
|
815
|
+
const workflowContent = `---
|
|
816
|
+
description: ${skill.metadata.description.slice(0, 100)}
|
|
817
|
+
---
|
|
818
|
+
|
|
819
|
+
${skill.body}
|
|
820
|
+
`;
|
|
821
|
+
const workflowPath = join(workflowsDir, `${skill.metadata.name}.md`);
|
|
822
|
+
await writeFile(workflowPath, workflowContent);
|
|
823
|
+
console.log(chalk.green(` ā ${skill.metadata.name}`));
|
|
824
|
+
console.log(chalk.gray(` ā ${workflowPath}`));
|
|
825
|
+
}
|
|
826
|
+
catch (err) {
|
|
827
|
+
console.log(chalk.red(` ā ${skillRef.name}: ${err}`));
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
console.log(chalk.bold.green(`\nā Skills synced to .agent/workflows/`));
|
|
831
|
+
console.log(chalk.gray(`\nNow you can use: "/${toSync.map(s => s.name).join('", "/')}"`));
|
|
832
|
+
console.log(chalk.gray('Or just say: "Use the [skill-name] skill to..."'));
|
|
833
|
+
}
|
|
834
|
+
catch (error) {
|
|
835
|
+
console.error(chalk.red('Error syncing skills:'), error);
|
|
836
|
+
process.exit(1);
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
// Export - convert skills to different AI agent formats
|
|
840
|
+
program
|
|
841
|
+
.command('export')
|
|
842
|
+
.description('Export skills to different AI agent formats (Copilot, Cursor, Claude, Codex)')
|
|
843
|
+
.option('-t, --target <agent>', 'Target agent: copilot, cursor, claude, codex, antigravity, all', 'all')
|
|
844
|
+
.option('-d, --directory <dir>', 'Project directory', '.')
|
|
845
|
+
.option('-n, --name <name>', 'Export specific skill only')
|
|
846
|
+
.action(async (options) => {
|
|
847
|
+
try {
|
|
848
|
+
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
849
|
+
const { join } = await import('path');
|
|
850
|
+
const { existsSync } = await import('fs');
|
|
851
|
+
const skills = await discoverSkills();
|
|
852
|
+
const toExport = options.name
|
|
853
|
+
? skills.filter(s => s.name === options.name)
|
|
854
|
+
: skills;
|
|
855
|
+
if (toExport.length === 0) {
|
|
856
|
+
console.log(chalk.yellow('No skills found to export.'));
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
const targets = options.target === 'all'
|
|
860
|
+
? ['copilot', 'cursor', 'claude', 'codex', 'antigravity']
|
|
861
|
+
: [options.target];
|
|
862
|
+
console.log(chalk.bold(`\nExporting ${toExport.length} skill(s) to: ${targets.join(', ')}\n`));
|
|
863
|
+
for (const target of targets) {
|
|
864
|
+
await exportToAgent(target, toExport, options.directory, { mkdir, writeFile, appendFile, join, existsSync });
|
|
865
|
+
}
|
|
866
|
+
console.log(chalk.bold.green('\nā Export complete!'));
|
|
867
|
+
console.log(chalk.gray('\nGenerated files:'));
|
|
868
|
+
if (targets.includes('copilot') || targets.includes('all')) {
|
|
869
|
+
console.log(chalk.gray(' - .github/copilot-instructions.md'));
|
|
870
|
+
}
|
|
871
|
+
if (targets.includes('cursor') || targets.includes('all')) {
|
|
872
|
+
console.log(chalk.gray(' - .cursor/rules/<skill>/RULE.md'));
|
|
873
|
+
}
|
|
874
|
+
if (targets.includes('claude') || targets.includes('all')) {
|
|
875
|
+
console.log(chalk.gray(' - CLAUDE.md'));
|
|
876
|
+
}
|
|
877
|
+
if (targets.includes('codex') || targets.includes('all')) {
|
|
878
|
+
console.log(chalk.gray(' - AGENTS.md'));
|
|
879
|
+
}
|
|
880
|
+
if (targets.includes('antigravity') || targets.includes('all')) {
|
|
881
|
+
console.log(chalk.gray(' - .agent/workflows/<skill>.md'));
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
catch (error) {
|
|
885
|
+
console.error(chalk.red('Error exporting skills:'), error);
|
|
886
|
+
process.exit(1);
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
async function exportToAgent(target, skillRefs, projectDir, fs) {
|
|
890
|
+
const loadedSkills = [];
|
|
891
|
+
for (const ref of skillRefs) {
|
|
892
|
+
const skill = await loadSkill(ref.path);
|
|
893
|
+
if (skill)
|
|
894
|
+
loadedSkills.push(skill);
|
|
895
|
+
}
|
|
896
|
+
switch (target) {
|
|
897
|
+
case 'copilot':
|
|
898
|
+
await exportToCopilot(loadedSkills, projectDir, fs);
|
|
899
|
+
break;
|
|
900
|
+
case 'cursor':
|
|
901
|
+
await exportToCursor(loadedSkills, projectDir, fs);
|
|
902
|
+
break;
|
|
903
|
+
case 'claude':
|
|
904
|
+
await exportToClaude(loadedSkills, projectDir, fs);
|
|
905
|
+
break;
|
|
906
|
+
case 'codex':
|
|
907
|
+
await exportToCodex(loadedSkills, projectDir, fs);
|
|
908
|
+
break;
|
|
909
|
+
case 'antigravity':
|
|
910
|
+
await exportToAntigravity(loadedSkills, projectDir, fs);
|
|
911
|
+
break;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
async function exportToCopilot(skills, projectDir, fs) {
|
|
915
|
+
// GitHub Copilot now uses Agent Skills standard: .github/skills/<name>/SKILL.md
|
|
916
|
+
// Also supports .claude/skills/ for compatibility
|
|
917
|
+
const copilotDir = fs.join(projectDir, '.github', 'skills');
|
|
918
|
+
await fs.mkdir(copilotDir, { recursive: true });
|
|
919
|
+
for (const skill of skills) {
|
|
920
|
+
const skillDir = fs.join(copilotDir, skill.metadata.name);
|
|
921
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
922
|
+
// Create SKILL.md in Agent Skills format
|
|
923
|
+
const content = `---
|
|
924
|
+
name: ${skill.metadata.name}
|
|
925
|
+
description: ${skill.metadata.description}
|
|
926
|
+
---
|
|
927
|
+
|
|
928
|
+
${skill.body}
|
|
929
|
+
`;
|
|
930
|
+
await fs.writeFile(fs.join(skillDir, 'SKILL.md'), content);
|
|
931
|
+
}
|
|
932
|
+
console.log(chalk.green(` ā GitHub Copilot: .github/skills/<skill>/SKILL.md`));
|
|
933
|
+
}
|
|
934
|
+
async function exportToCursor(skills, projectDir, fs) {
|
|
935
|
+
// Cursor now uses Agent Skills standard: .cursor/skills/<name>/SKILL.md
|
|
936
|
+
const cursorDir = fs.join(projectDir, '.cursor', 'skills');
|
|
937
|
+
await fs.mkdir(cursorDir, { recursive: true });
|
|
938
|
+
for (const skill of skills) {
|
|
939
|
+
const skillDir = fs.join(cursorDir, skill.metadata.name);
|
|
940
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
941
|
+
// Create SKILL.md in Agent Skills format
|
|
942
|
+
const content = `---
|
|
943
|
+
name: ${skill.metadata.name}
|
|
944
|
+
description: ${skill.metadata.description}
|
|
945
|
+
---
|
|
946
|
+
|
|
947
|
+
${skill.body}
|
|
948
|
+
`;
|
|
949
|
+
await fs.writeFile(fs.join(skillDir, 'SKILL.md'), content);
|
|
950
|
+
}
|
|
951
|
+
console.log(chalk.green(` ā Cursor: .cursor/skills/<skill>/SKILL.md`));
|
|
952
|
+
}
|
|
953
|
+
async function exportToClaude(skills, projectDir, fs) {
|
|
954
|
+
// Claude Code now uses Agent Skills standard: .claude/skills/<name>/SKILL.md
|
|
955
|
+
const claudeDir = fs.join(projectDir, '.claude', 'skills');
|
|
956
|
+
await fs.mkdir(claudeDir, { recursive: true });
|
|
957
|
+
for (const skill of skills) {
|
|
958
|
+
const skillDir = fs.join(claudeDir, skill.metadata.name);
|
|
959
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
960
|
+
// Create SKILL.md in Agent Skills format
|
|
961
|
+
const content = `---
|
|
962
|
+
name: ${skill.metadata.name}
|
|
963
|
+
description: ${skill.metadata.description}
|
|
964
|
+
---
|
|
965
|
+
|
|
966
|
+
${skill.body}
|
|
967
|
+
`;
|
|
968
|
+
await fs.writeFile(fs.join(skillDir, 'SKILL.md'), content);
|
|
969
|
+
}
|
|
970
|
+
console.log(chalk.green(` ā Claude Code: .claude/skills/<skill>/SKILL.md`));
|
|
971
|
+
}
|
|
972
|
+
async function exportToCodex(skills, projectDir, fs) {
|
|
973
|
+
// OpenAI Codex uses Agent Skills standard: .codex/skills/<name>/SKILL.md
|
|
974
|
+
const codexDir = fs.join(projectDir, '.codex', 'skills');
|
|
975
|
+
await fs.mkdir(codexDir, { recursive: true });
|
|
976
|
+
for (const skill of skills) {
|
|
977
|
+
const skillDir = fs.join(codexDir, skill.metadata.name);
|
|
978
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
979
|
+
// Create SKILL.md in Agent Skills format
|
|
980
|
+
const content = `---
|
|
981
|
+
name: ${skill.metadata.name}
|
|
982
|
+
description: ${skill.metadata.description}
|
|
983
|
+
---
|
|
984
|
+
|
|
985
|
+
${skill.body}
|
|
986
|
+
`;
|
|
987
|
+
await fs.writeFile(fs.join(skillDir, 'SKILL.md'), content);
|
|
988
|
+
}
|
|
989
|
+
console.log(chalk.green(` ā OpenAI Codex: .codex/skills/<skill>/SKILL.md`));
|
|
990
|
+
}
|
|
991
|
+
async function exportToAntigravity(skills, projectDir, fs) {
|
|
992
|
+
const workflowsDir = fs.join(projectDir, '.agent', 'workflows');
|
|
993
|
+
await fs.mkdir(workflowsDir, { recursive: true });
|
|
994
|
+
for (const skill of skills) {
|
|
995
|
+
const content = `---
|
|
996
|
+
description: ${skill.metadata.description.slice(0, 100)}
|
|
997
|
+
---
|
|
998
|
+
|
|
999
|
+
${skill.body}
|
|
1000
|
+
`;
|
|
1001
|
+
await fs.writeFile(fs.join(workflowsDir, `${skill.metadata.name}.md`), content);
|
|
1002
|
+
}
|
|
1003
|
+
console.log(chalk.green(` ā Antigravity: .agent/workflows/<skill>.md`));
|
|
1004
|
+
}
|
|
1005
|
+
// ============================================
|
|
1006
|
+
// INTERACTIVE COMMANDS
|
|
1007
|
+
// ============================================
|
|
1008
|
+
// Interactive install wizard - select skills with arrow keys
|
|
1009
|
+
program
|
|
1010
|
+
.command('install-wizard')
|
|
1011
|
+
.alias('iw')
|
|
1012
|
+
.description('Interactive skill installation wizard (legacy)')
|
|
1013
|
+
.action(async () => {
|
|
1014
|
+
try {
|
|
1015
|
+
const spinner = ora('Fetching skills from marketplaces...').start();
|
|
1016
|
+
const skills = await listMarketplaceSkills();
|
|
1017
|
+
spinner.stop();
|
|
1018
|
+
if (skills.length === 0) {
|
|
1019
|
+
console.log(chalk.yellow('No skills found in marketplaces.'));
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const choices = skills.map(skill => ({
|
|
1023
|
+
name: `${skill.name} - ${skill.description?.slice(0, 50) || 'No description'}...`,
|
|
1024
|
+
value: skill.name,
|
|
1025
|
+
short: skill.name
|
|
1026
|
+
}));
|
|
1027
|
+
const { selectedSkills } = await inquirer.prompt([
|
|
1028
|
+
{
|
|
1029
|
+
type: 'checkbox',
|
|
1030
|
+
name: 'selectedSkills',
|
|
1031
|
+
message: 'Select skills to install (Space to select, Enter to confirm):',
|
|
1032
|
+
choices,
|
|
1033
|
+
pageSize: 15
|
|
1034
|
+
}
|
|
1035
|
+
]);
|
|
1036
|
+
if (selectedSkills.length === 0) {
|
|
1037
|
+
console.log(chalk.yellow('No skills selected.'));
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
for (const skillName of selectedSkills) {
|
|
1041
|
+
const installSpinner = ora(`Installing ${skillName}...`).start();
|
|
1042
|
+
try {
|
|
1043
|
+
const result = await installSkill(skillName);
|
|
1044
|
+
installSpinner.succeed(`Installed: ${skillName}`);
|
|
1045
|
+
}
|
|
1046
|
+
catch (err) {
|
|
1047
|
+
installSpinner.fail(`Failed to install ${skillName}: ${err}`);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
console.log(chalk.bold.green('\nā Installation complete!'));
|
|
1051
|
+
console.log(chalk.gray('Run "skills export" to export to your AI agent.'));
|
|
1052
|
+
}
|
|
1053
|
+
catch (error) {
|
|
1054
|
+
console.error(chalk.red('Error:'), error);
|
|
1055
|
+
process.exit(1);
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
// Interactive export - select target agents
|
|
1059
|
+
program
|
|
1060
|
+
.command('export-interactive')
|
|
1061
|
+
.alias('ei')
|
|
1062
|
+
.description('Interactive export with agent selection menu')
|
|
1063
|
+
.action(async () => {
|
|
1064
|
+
try {
|
|
1065
|
+
const skills = await discoverSkills();
|
|
1066
|
+
if (skills.length === 0) {
|
|
1067
|
+
console.log(chalk.yellow('No skills found to export.'));
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
const { agents } = await inquirer.prompt([
|
|
1071
|
+
{
|
|
1072
|
+
type: 'checkbox',
|
|
1073
|
+
name: 'agents',
|
|
1074
|
+
message: 'Select target AI agents:',
|
|
1075
|
+
choices: [
|
|
1076
|
+
{ name: 'GitHub Copilot (.github/skills/)', value: 'copilot', checked: true },
|
|
1077
|
+
{ name: 'Cursor (.cursor/skills/)', value: 'cursor', checked: true },
|
|
1078
|
+
{ name: 'Claude Code (.claude/skills/)', value: 'claude', checked: true },
|
|
1079
|
+
{ name: 'OpenAI Codex (.codex/skills/)', value: 'codex', checked: true },
|
|
1080
|
+
{ name: 'Antigravity (.agent/workflows/)', value: 'antigravity', checked: false }
|
|
1081
|
+
]
|
|
1082
|
+
}
|
|
1083
|
+
]);
|
|
1084
|
+
if (agents.length === 0) {
|
|
1085
|
+
console.log(chalk.yellow('No agents selected.'));
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
1089
|
+
const { join } = await import('path');
|
|
1090
|
+
const { existsSync } = await import('fs');
|
|
1091
|
+
console.log(chalk.bold(`\nExporting ${skills.length} skill(s) to: ${agents.join(', ')}\n`));
|
|
1092
|
+
for (const target of agents) {
|
|
1093
|
+
const spinner = ora(`Exporting to ${target}...`).start();
|
|
1094
|
+
await exportToAgent(target, skills, '.', { mkdir, writeFile, appendFile, join, existsSync });
|
|
1095
|
+
spinner.succeed();
|
|
1096
|
+
}
|
|
1097
|
+
console.log(chalk.bold.green('\nā Export complete!'));
|
|
1098
|
+
}
|
|
1099
|
+
catch (error) {
|
|
1100
|
+
console.error(chalk.red('Error:'), error);
|
|
1101
|
+
process.exit(1);
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
// Quick setup wizard
|
|
1105
|
+
program
|
|
1106
|
+
.command('setup')
|
|
1107
|
+
.description('Interactive setup wizard - install skills and export to your agents')
|
|
1108
|
+
.action(async () => {
|
|
1109
|
+
console.log(chalk.bold.cyan('\nš Agent Skills Setup Wizard\n'));
|
|
1110
|
+
// Step 1: Choose what to do
|
|
1111
|
+
const { action } = await inquirer.prompt([
|
|
1112
|
+
{
|
|
1113
|
+
type: 'list',
|
|
1114
|
+
name: 'action',
|
|
1115
|
+
message: 'What would you like to do?',
|
|
1116
|
+
choices: [
|
|
1117
|
+
{ name: 'š¦ Install skills from marketplace', value: 'install' },
|
|
1118
|
+
{ name: 'š¤ Export installed skills to AI agents', value: 'export' },
|
|
1119
|
+
{ name: 'š Both - Install and export', value: 'both' }
|
|
1120
|
+
]
|
|
1121
|
+
}
|
|
1122
|
+
]);
|
|
1123
|
+
if (action === 'install' || action === 'both') {
|
|
1124
|
+
const spinner = ora('Fetching skills from marketplaces...').start();
|
|
1125
|
+
const skills = await listMarketplaceSkills();
|
|
1126
|
+
spinner.stop();
|
|
1127
|
+
if (skills.length > 0) {
|
|
1128
|
+
const choices = skills.slice(0, 20).map(skill => ({
|
|
1129
|
+
name: `${skill.name} - ${skill.description?.slice(0, 40) || ''}...`,
|
|
1130
|
+
value: skill.name
|
|
1131
|
+
}));
|
|
1132
|
+
const { selectedSkills } = await inquirer.prompt([
|
|
1133
|
+
{
|
|
1134
|
+
type: 'checkbox',
|
|
1135
|
+
name: 'selectedSkills',
|
|
1136
|
+
message: 'Select skills to install:',
|
|
1137
|
+
choices,
|
|
1138
|
+
pageSize: 10
|
|
1139
|
+
}
|
|
1140
|
+
]);
|
|
1141
|
+
for (const skillName of selectedSkills) {
|
|
1142
|
+
const installSpinner = ora(`Installing ${skillName}...`).start();
|
|
1143
|
+
try {
|
|
1144
|
+
await installSkill(skillName);
|
|
1145
|
+
installSpinner.succeed(`Installed: ${skillName}`);
|
|
1146
|
+
}
|
|
1147
|
+
catch (err) {
|
|
1148
|
+
installSpinner.fail(`Failed: ${skillName}`);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
if (action === 'export' || action === 'both') {
|
|
1154
|
+
const { agents } = await inquirer.prompt([
|
|
1155
|
+
{
|
|
1156
|
+
type: 'checkbox',
|
|
1157
|
+
name: 'agents',
|
|
1158
|
+
message: 'Which AI agents do you use?',
|
|
1159
|
+
choices: [
|
|
1160
|
+
{ name: 'Cursor', value: 'cursor', checked: true },
|
|
1161
|
+
{ name: 'Claude Code', value: 'claude', checked: true },
|
|
1162
|
+
{ name: 'GitHub Copilot', value: 'copilot', checked: true },
|
|
1163
|
+
{ name: 'OpenAI Codex', value: 'codex', checked: false }
|
|
1164
|
+
]
|
|
1165
|
+
}
|
|
1166
|
+
]);
|
|
1167
|
+
const skills = await discoverSkills();
|
|
1168
|
+
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
1169
|
+
const { join } = await import('path');
|
|
1170
|
+
const { existsSync } = await import('fs');
|
|
1171
|
+
for (const target of agents) {
|
|
1172
|
+
const spinner = ora(`Exporting to ${target}...`).start();
|
|
1173
|
+
await exportToAgent(target, skills, '.', { mkdir, writeFile, appendFile, join, existsSync });
|
|
1174
|
+
spinner.succeed();
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
console.log(chalk.bold.green('\n⨠Setup complete!'));
|
|
1178
|
+
console.log(chalk.gray('Your skills are now ready to use in your AI agents.\n'));
|
|
1179
|
+
});
|
|
1180
|
+
program.parse();
|
|
1181
|
+
//# sourceMappingURL=index.js.map
|