agent-skills-cli 1.0.8 ā 1.0.9
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/README.md +18 -17
- package/dist/cli/agents.d.ts +10 -0
- package/dist/cli/agents.d.ts.map +1 -0
- package/dist/cli/agents.js +270 -0
- package/dist/cli/agents.js.map +1 -0
- package/dist/cli/commands/audit.d.ts +11 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/audit.js +168 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/commands/blueprint.d.ts +11 -0
- package/dist/cli/commands/blueprint.d.ts.map +1 -0
- package/dist/cli/commands/blueprint.js +210 -0
- package/dist/cli/commands/blueprint.js.map +1 -0
- package/dist/cli/commands/bootstrap.d.ts +11 -0
- package/dist/cli/commands/bootstrap.d.ts.map +1 -0
- package/dist/cli/commands/bootstrap.js +267 -0
- package/dist/cli/commands/bootstrap.js.map +1 -0
- package/dist/cli/commands/capture.d.ts +11 -0
- package/dist/cli/commands/capture.d.ts.map +1 -0
- package/dist/cli/commands/capture.js +109 -0
- package/dist/cli/commands/capture.js.map +1 -0
- package/dist/cli/commands/ci.d.ts +11 -0
- package/dist/cli/commands/ci.d.ts.map +1 -0
- package/dist/cli/commands/ci.js +144 -0
- package/dist/cli/commands/ci.js.map +1 -0
- package/dist/cli/commands/collab.d.ts +11 -0
- package/dist/cli/commands/collab.d.ts.map +1 -0
- package/dist/cli/commands/collab.js +196 -0
- package/dist/cli/commands/collab.js.map +1 -0
- package/dist/cli/commands/convert.d.ts +11 -0
- package/dist/cli/commands/convert.d.ts.map +1 -0
- package/dist/cli/commands/convert.js +219 -0
- package/dist/cli/commands/convert.js.map +1 -0
- package/dist/cli/commands/craft.d.ts +18 -0
- package/dist/cli/commands/craft.d.ts.map +1 -0
- package/dist/cli/commands/craft.js +205 -0
- package/dist/cli/commands/craft.js.map +1 -0
- package/dist/cli/commands/export.d.ts +9 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +103 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/forge.d.ts +11 -0
- package/dist/cli/commands/forge.d.ts.map +1 -0
- package/dist/cli/commands/forge.js +152 -0
- package/dist/cli/commands/forge.js.map +1 -0
- package/dist/cli/commands/grid.d.ts +11 -0
- package/dist/cli/commands/grid.d.ts.map +1 -0
- package/dist/cli/commands/grid.js +217 -0
- package/dist/cli/commands/grid.js.map +1 -0
- package/dist/cli/commands/insight.d.ts +7 -0
- package/dist/cli/commands/insight.d.ts.map +1 -0
- package/dist/cli/commands/insight.js +71 -0
- package/dist/cli/commands/insight.js.map +1 -0
- package/dist/cli/commands/install.d.ts +6 -0
- package/dist/cli/commands/install.d.ts.map +1 -0
- package/dist/cli/commands/install.js +359 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/interactive.d.ts +7 -0
- package/dist/cli/commands/interactive.d.ts.map +1 -0
- package/dist/cli/commands/interactive.js +535 -0
- package/dist/cli/commands/interactive.js.map +1 -0
- package/dist/cli/commands/list.d.ts +6 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +77 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/lockspec.d.ts +11 -0
- package/dist/cli/commands/lockspec.d.ts.map +1 -0
- package/dist/cli/commands/lockspec.js +179 -0
- package/dist/cli/commands/lockspec.js.map +1 -0
- package/dist/cli/commands/marketplace.d.ts +7 -0
- package/dist/cli/commands/marketplace.d.ts.map +1 -0
- package/dist/cli/commands/marketplace.js +417 -0
- package/dist/cli/commands/marketplace.js.map +1 -0
- package/dist/cli/commands/method.d.ts +7 -0
- package/dist/cli/commands/method.d.ts.map +1 -0
- package/dist/cli/commands/method.js +140 -0
- package/dist/cli/commands/method.js.map +1 -0
- package/dist/cli/commands/mine.d.ts +11 -0
- package/dist/cli/commands/mine.d.ts.map +1 -0
- package/dist/cli/commands/mine.js +254 -0
- package/dist/cli/commands/mine.js.map +1 -0
- package/dist/cli/commands/recall.d.ts +11 -0
- package/dist/cli/commands/recall.d.ts.map +1 -0
- package/dist/cli/commands/recall.js +201 -0
- package/dist/cli/commands/recall.js.map +1 -0
- package/dist/cli/commands/rule.d.ts +11 -0
- package/dist/cli/commands/rule.d.ts.map +1 -0
- package/dist/cli/commands/rule.js +230 -0
- package/dist/cli/commands/rule.js.map +1 -0
- package/dist/cli/commands/search.d.ts +6 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +173 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/show.d.ts +6 -0
- package/dist/cli/commands/show.d.ts.map +1 -0
- package/dist/cli/commands/show.js +150 -0
- package/dist/cli/commands/show.js.map +1 -0
- package/dist/cli/commands/submit.d.ts +15 -0
- package/dist/cli/commands/submit.d.ts.map +1 -0
- package/dist/cli/commands/submit.js +151 -0
- package/dist/cli/commands/submit.js.map +1 -0
- package/dist/cli/commands/suggest.d.ts +11 -0
- package/dist/cli/commands/suggest.d.ts.map +1 -0
- package/dist/cli/commands/suggest.js +164 -0
- package/dist/cli/commands/suggest.js.map +1 -0
- package/dist/cli/commands/track.d.ts +11 -0
- package/dist/cli/commands/track.d.ts.map +1 -0
- package/dist/cli/commands/track.js +199 -0
- package/dist/cli/commands/track.js.map +1 -0
- package/dist/cli/commands/trigger.d.ts +11 -0
- package/dist/cli/commands/trigger.d.ts.map +1 -0
- package/dist/cli/commands/trigger.js +157 -0
- package/dist/cli/commands/trigger.js.map +1 -0
- package/dist/cli/commands/utils-commands.d.ts +9 -0
- package/dist/cli/commands/utils-commands.d.ts.map +1 -0
- package/dist/cli/commands/utils-commands.js +389 -0
- package/dist/cli/commands/utils-commands.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +6 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +40 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +84 -2743
- package/dist/cli/index.js.map +1 -1
- package/dist/core/audit.d.ts +24 -0
- package/dist/core/audit.d.ts.map +1 -0
- package/dist/core/audit.js +195 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/scanner-rules.d.ts +58 -0
- package/dist/core/scanner-rules.d.ts.map +1 -0
- package/dist/core/scanner-rules.js +335 -0
- package/dist/core/scanner-rules.js.map +1 -0
- package/dist/core/suggest.d.ts +51 -0
- package/dist/core/suggest.d.ts.map +1 -0
- package/dist/core/suggest.js +241 -0
- package/dist/core/suggest.js.map +1 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2,289 +2,55 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Agent Skills CLI
|
|
4
4
|
* Universal CLI for managing Agent Skills across Cursor, Claude Code, GitHub Copilot, OpenAI Codex
|
|
5
|
+
*
|
|
6
|
+
* This file is the thin orchestrator ā all command logic lives in ./commands/*.ts
|
|
7
|
+
* and shared config lives in ./agents.ts.
|
|
5
8
|
*/
|
|
6
9
|
import { Command } from 'commander';
|
|
7
10
|
import chalk from 'chalk';
|
|
8
11
|
import inquirer from 'inquirer';
|
|
9
12
|
import ora from 'ora';
|
|
10
13
|
import * as p from '@clack/prompts';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
import { listMarketplaceSkills, fetchSkillsForCLI, } from '../core/index.js';
|
|
15
|
+
import { setVersion } from '../core/telemetry.js';
|
|
16
|
+
import { AGENTS } from './agents.js';
|
|
17
|
+
// āāā Modular command imports āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
18
|
+
import { registerListCommand } from './commands/list.js';
|
|
19
|
+
import { registerValidateCommand } from './commands/validate.js';
|
|
20
|
+
import { registerShowCommand } from './commands/show.js';
|
|
21
|
+
import { registerMarketplaceCommands } from './commands/marketplace.js';
|
|
22
|
+
import { registerSearchInstallCommand } from './commands/search.js';
|
|
23
|
+
import { registerInstallCommand } from './commands/install.js';
|
|
24
|
+
import { registerExportCommand } from './commands/export.js';
|
|
25
|
+
import { registerDoctorCommand, registerCheckCommand, registerUpdateCommand, registerExecCommand } from './commands/utils-commands.js';
|
|
26
|
+
import { registerInteractiveCommands } from './commands/interactive.js';
|
|
27
|
+
// āāā Already-extracted command imports āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
15
28
|
import { registerRemoveCommand } from './commands/remove.js';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
globalDir: `${home}/.github/skills`,
|
|
38
|
-
},
|
|
39
|
-
'codex': {
|
|
40
|
-
name: 'codex',
|
|
41
|
-
displayName: 'Codex',
|
|
42
|
-
projectDir: '.codex/skills',
|
|
43
|
-
globalDir: `${home}/.codex/skills`,
|
|
44
|
-
},
|
|
45
|
-
'antigravity': {
|
|
46
|
-
name: 'antigravity',
|
|
47
|
-
displayName: 'Antigravity',
|
|
48
|
-
projectDir: '.agent/skills',
|
|
49
|
-
globalDir: `${home}/.gemini/antigravity/skills`,
|
|
50
|
-
},
|
|
51
|
-
// New agents from add-skill
|
|
52
|
-
'opencode': {
|
|
53
|
-
name: 'opencode',
|
|
54
|
-
displayName: 'OpenCode',
|
|
55
|
-
projectDir: '.opencode/skill',
|
|
56
|
-
globalDir: `${home}/.config/opencode/skill`,
|
|
57
|
-
},
|
|
58
|
-
'amp': {
|
|
59
|
-
name: 'amp',
|
|
60
|
-
displayName: 'Amp',
|
|
61
|
-
projectDir: '.agents/skills',
|
|
62
|
-
globalDir: `${home}/.config/agents/skills`,
|
|
63
|
-
},
|
|
64
|
-
'kilo': {
|
|
65
|
-
name: 'kilo',
|
|
66
|
-
displayName: 'Kilo Code',
|
|
67
|
-
projectDir: '.kilocode/skills',
|
|
68
|
-
globalDir: `${home}/.kilocode/skills`,
|
|
69
|
-
},
|
|
70
|
-
'roo': {
|
|
71
|
-
name: 'roo',
|
|
72
|
-
displayName: 'Roo Code',
|
|
73
|
-
projectDir: '.roo/skills',
|
|
74
|
-
globalDir: `${home}/.roo/skills`,
|
|
75
|
-
},
|
|
76
|
-
'goose': {
|
|
77
|
-
name: 'goose',
|
|
78
|
-
displayName: 'Goose',
|
|
79
|
-
projectDir: '.goose/skills',
|
|
80
|
-
globalDir: `${home}/.config/goose/skills`,
|
|
81
|
-
},
|
|
82
|
-
// New agents from vercel-labs/skills (19 additional)
|
|
83
|
-
'cline': {
|
|
84
|
-
name: 'cline',
|
|
85
|
-
displayName: 'Cline',
|
|
86
|
-
projectDir: '.cline/skills',
|
|
87
|
-
globalDir: `${home}/.cline/skills`,
|
|
88
|
-
},
|
|
89
|
-
'codebuddy': {
|
|
90
|
-
name: 'codebuddy',
|
|
91
|
-
displayName: 'CodeBuddy',
|
|
92
|
-
projectDir: '.codebuddy/skills',
|
|
93
|
-
globalDir: `${home}/.codebuddy/skills`,
|
|
94
|
-
},
|
|
95
|
-
'command-code': {
|
|
96
|
-
name: 'command-code',
|
|
97
|
-
displayName: 'Command Code',
|
|
98
|
-
projectDir: '.commandcode/skills',
|
|
99
|
-
globalDir: `${home}/.commandcode/skills`,
|
|
100
|
-
},
|
|
101
|
-
'continue': {
|
|
102
|
-
name: 'continue',
|
|
103
|
-
displayName: 'Continue',
|
|
104
|
-
projectDir: '.continue/skills',
|
|
105
|
-
globalDir: `${home}/.continue/skills`,
|
|
106
|
-
},
|
|
107
|
-
'crush': {
|
|
108
|
-
name: 'crush',
|
|
109
|
-
displayName: 'Crush',
|
|
110
|
-
projectDir: '.crush/skills',
|
|
111
|
-
globalDir: `${home}/.config/crush/skills`,
|
|
112
|
-
},
|
|
113
|
-
'clawdbot': {
|
|
114
|
-
name: 'clawdbot',
|
|
115
|
-
displayName: 'Clawdbot',
|
|
116
|
-
projectDir: 'skills',
|
|
117
|
-
globalDir: `${home}/.clawdbot/skills`,
|
|
118
|
-
},
|
|
119
|
-
'droid': {
|
|
120
|
-
name: 'droid',
|
|
121
|
-
displayName: 'Droid',
|
|
122
|
-
projectDir: '.factory/skills',
|
|
123
|
-
globalDir: `${home}/.factory/skills`,
|
|
124
|
-
},
|
|
125
|
-
'gemini-cli': {
|
|
126
|
-
name: 'gemini-cli',
|
|
127
|
-
displayName: 'Gemini CLI',
|
|
128
|
-
projectDir: '.gemini/skills',
|
|
129
|
-
globalDir: `${home}/.gemini/skills`,
|
|
130
|
-
},
|
|
131
|
-
'kiro-cli': {
|
|
132
|
-
name: 'kiro-cli',
|
|
133
|
-
displayName: 'Kiro CLI',
|
|
134
|
-
projectDir: '.kiro/skills',
|
|
135
|
-
globalDir: `${home}/.kiro/skills`,
|
|
136
|
-
},
|
|
137
|
-
'mcpjam': {
|
|
138
|
-
name: 'mcpjam',
|
|
139
|
-
displayName: 'MCPJam',
|
|
140
|
-
projectDir: '.mcpjam/skills',
|
|
141
|
-
globalDir: `${home}/.mcpjam/skills`,
|
|
142
|
-
},
|
|
143
|
-
'mux': {
|
|
144
|
-
name: 'mux',
|
|
145
|
-
displayName: 'Mux',
|
|
146
|
-
projectDir: '.mux/skills',
|
|
147
|
-
globalDir: `${home}/.mux/skills`,
|
|
148
|
-
},
|
|
149
|
-
'openhands': {
|
|
150
|
-
name: 'openhands',
|
|
151
|
-
displayName: 'OpenHands',
|
|
152
|
-
projectDir: '.openhands/skills',
|
|
153
|
-
globalDir: `${home}/.openhands/skills`,
|
|
154
|
-
},
|
|
155
|
-
'pi': {
|
|
156
|
-
name: 'pi',
|
|
157
|
-
displayName: 'Pi',
|
|
158
|
-
projectDir: '.pi/skills',
|
|
159
|
-
globalDir: `${home}/.pi/agent/skills`,
|
|
160
|
-
},
|
|
161
|
-
'qoder': {
|
|
162
|
-
name: 'qoder',
|
|
163
|
-
displayName: 'Qoder',
|
|
164
|
-
projectDir: '.qoder/skills',
|
|
165
|
-
globalDir: `${home}/.qoder/skills`,
|
|
166
|
-
},
|
|
167
|
-
'qwen-code': {
|
|
168
|
-
name: 'qwen-code',
|
|
169
|
-
displayName: 'Qwen Code',
|
|
170
|
-
projectDir: '.qwen/skills',
|
|
171
|
-
globalDir: `${home}/.qwen/skills`,
|
|
172
|
-
},
|
|
173
|
-
'trae': {
|
|
174
|
-
name: 'trae',
|
|
175
|
-
displayName: 'Trae',
|
|
176
|
-
projectDir: '.trae/skills',
|
|
177
|
-
globalDir: `${home}/.trae/skills`,
|
|
178
|
-
},
|
|
179
|
-
'windsurf': {
|
|
180
|
-
name: 'windsurf',
|
|
181
|
-
displayName: 'Windsurf',
|
|
182
|
-
projectDir: '.windsurf/skills',
|
|
183
|
-
globalDir: `${home}/.codeium/windsurf/skills`,
|
|
184
|
-
},
|
|
185
|
-
'zencoder': {
|
|
186
|
-
name: 'zencoder',
|
|
187
|
-
displayName: 'Zencoder',
|
|
188
|
-
projectDir: '.zencoder/skills',
|
|
189
|
-
globalDir: `${home}/.zencoder/skills`,
|
|
190
|
-
},
|
|
191
|
-
'neovate': {
|
|
192
|
-
name: 'neovate',
|
|
193
|
-
displayName: 'Neovate',
|
|
194
|
-
projectDir: '.neovate/skills',
|
|
195
|
-
globalDir: `${home}/.neovate/skills`,
|
|
196
|
-
},
|
|
197
|
-
// Additional agents from Vercel Skills (13 more = 42 total)
|
|
198
|
-
'ara': {
|
|
199
|
-
name: 'ara',
|
|
200
|
-
displayName: 'Ara',
|
|
201
|
-
projectDir: '.ara/skills',
|
|
202
|
-
globalDir: `${home}/.ara/skills`,
|
|
203
|
-
},
|
|
204
|
-
'aide': {
|
|
205
|
-
name: 'aide',
|
|
206
|
-
displayName: 'Aide',
|
|
207
|
-
projectDir: '.aide/skills',
|
|
208
|
-
globalDir: `${home}/.aide/skills`,
|
|
209
|
-
},
|
|
210
|
-
'alex': {
|
|
211
|
-
name: 'alex',
|
|
212
|
-
displayName: 'Alex',
|
|
213
|
-
projectDir: '.alex/skills',
|
|
214
|
-
globalDir: `${home}/.alex/skills`,
|
|
215
|
-
},
|
|
216
|
-
'bb': {
|
|
217
|
-
name: 'bb',
|
|
218
|
-
displayName: 'BB',
|
|
219
|
-
projectDir: '.bb/skills',
|
|
220
|
-
globalDir: `${home}/.bb/skills`,
|
|
221
|
-
},
|
|
222
|
-
'codestory': {
|
|
223
|
-
name: 'codestory',
|
|
224
|
-
displayName: 'CodeStory',
|
|
225
|
-
projectDir: '.codestory/skills',
|
|
226
|
-
globalDir: `${home}/.codestory/skills`,
|
|
227
|
-
},
|
|
228
|
-
'helix-ai': {
|
|
229
|
-
name: 'helix-ai',
|
|
230
|
-
displayName: 'Helix AI',
|
|
231
|
-
projectDir: '.helix/skills',
|
|
232
|
-
globalDir: `${home}/.helix/skills`,
|
|
233
|
-
},
|
|
234
|
-
'meekia': {
|
|
235
|
-
name: 'meekia',
|
|
236
|
-
displayName: 'Meekia',
|
|
237
|
-
projectDir: '.meekia/skills',
|
|
238
|
-
globalDir: `${home}/.meekia/skills`,
|
|
239
|
-
},
|
|
240
|
-
'pear-ai': {
|
|
241
|
-
name: 'pear-ai',
|
|
242
|
-
displayName: 'Pear AI',
|
|
243
|
-
projectDir: '.pearai/skills',
|
|
244
|
-
globalDir: `${home}/.pearai/skills`,
|
|
245
|
-
},
|
|
246
|
-
'adal': {
|
|
247
|
-
name: 'adal',
|
|
248
|
-
displayName: 'Adal',
|
|
249
|
-
projectDir: '.adal/skills',
|
|
250
|
-
globalDir: `${home}/.adal/skills`,
|
|
251
|
-
},
|
|
252
|
-
'pochi': {
|
|
253
|
-
name: 'pochi',
|
|
254
|
-
displayName: 'Pochi',
|
|
255
|
-
projectDir: '.pochi/skills',
|
|
256
|
-
globalDir: `${home}/.pochi/skills`,
|
|
257
|
-
},
|
|
258
|
-
'sourcegraph-cody': {
|
|
259
|
-
name: 'sourcegraph-cody',
|
|
260
|
-
displayName: 'Sourcegraph Cody',
|
|
261
|
-
projectDir: '.sourcegraph/skills',
|
|
262
|
-
globalDir: `${home}/.sourcegraph/skills`,
|
|
263
|
-
},
|
|
264
|
-
'void-ai': {
|
|
265
|
-
name: 'void-ai',
|
|
266
|
-
displayName: 'Void AI',
|
|
267
|
-
projectDir: '.void/skills',
|
|
268
|
-
globalDir: `${home}/.void/skills`,
|
|
269
|
-
},
|
|
270
|
-
'zed': {
|
|
271
|
-
name: 'zed',
|
|
272
|
-
displayName: 'Zed',
|
|
273
|
-
projectDir: '.zed/skills',
|
|
274
|
-
globalDir: `${home}/.zed/skills`,
|
|
275
|
-
},
|
|
276
|
-
};
|
|
277
|
-
// Helper to get install path
|
|
278
|
-
function getInstallPath(agent, global) {
|
|
279
|
-
const config = AGENTS[agent];
|
|
280
|
-
if (!config)
|
|
281
|
-
return `.${agent}/skills`;
|
|
282
|
-
return global ? config.globalDir : config.projectDir;
|
|
283
|
-
}
|
|
29
|
+
import { registerSuggestCommand } from './commands/suggest.js';
|
|
30
|
+
import { registerAuditCommand } from './commands/audit.js';
|
|
31
|
+
import { registerCraftCommand } from './commands/craft.js';
|
|
32
|
+
import { registerSubmitCommand } from './commands/submit.js';
|
|
33
|
+
import { registerBootstrapCommand } from './commands/bootstrap.js';
|
|
34
|
+
import { registerConvertCommand } from './commands/convert.js';
|
|
35
|
+
import { registerCollabCommand } from './commands/collab.js';
|
|
36
|
+
import { registerLockspecCommand } from './commands/lockspec.js';
|
|
37
|
+
import { registerForgeCommand } from './commands/forge.js';
|
|
38
|
+
import { registerMineCommand } from './commands/mine.js';
|
|
39
|
+
import { registerRecallCommand } from './commands/recall.js';
|
|
40
|
+
import { registerGridCommand } from './commands/grid.js';
|
|
41
|
+
import { registerCaptureCommand } from './commands/capture.js';
|
|
42
|
+
import { registerTriggerCommand } from './commands/trigger.js';
|
|
43
|
+
import { registerRuleCommand } from './commands/rule.js';
|
|
44
|
+
import { registerBlueprintCommand } from './commands/blueprint.js';
|
|
45
|
+
import { registerCiCommand } from './commands/ci.js';
|
|
46
|
+
import { registerTrackCommand } from './commands/track.js';
|
|
47
|
+
import { registerInsightCommand } from './commands/insight.js';
|
|
48
|
+
import { registerMethodCommand } from './commands/method.js';
|
|
49
|
+
// āāā Program setup āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
284
50
|
const program = new Command();
|
|
285
51
|
// Initialize telemetry with CLI version
|
|
286
52
|
setVersion('1.0.8');
|
|
287
|
-
// Main flow
|
|
53
|
+
// āāā Main interactive flow (`skills` with no subcommand) āāāāāāāāāāāāāāāāāāāā
|
|
288
54
|
async function showMainMenu() {
|
|
289
55
|
console.log('');
|
|
290
56
|
p.intro(chalk.bgCyan.black(' Agent Skills CLI '));
|
|
@@ -308,21 +74,18 @@ async function showMainMenu() {
|
|
|
308
74
|
p.log.warn('No agents selected. Exiting.');
|
|
309
75
|
return;
|
|
310
76
|
}
|
|
311
|
-
// Cast to string array for use throughout the function
|
|
312
77
|
const selectedAgents = agents;
|
|
313
|
-
// Step 2: Fetch skills from our database
|
|
78
|
+
// Step 2: Fetch skills from our database
|
|
314
79
|
const spinner = ora('Fetching skills from marketplace...').start();
|
|
315
80
|
let marketplaceSkills = [];
|
|
316
81
|
let total = 0;
|
|
317
82
|
try {
|
|
318
|
-
// Try our database first
|
|
319
83
|
const result = await fetchSkillsForCLI({ limit: 100, sortBy: 'stars' });
|
|
320
84
|
marketplaceSkills = result.skills;
|
|
321
85
|
total = result.total;
|
|
322
86
|
spinner.succeed(`Found ${total.toLocaleString()} skills (showing top 100 by stars)`);
|
|
323
87
|
}
|
|
324
88
|
catch (err) {
|
|
325
|
-
// Fallback to GitHub sources if our API is down
|
|
326
89
|
spinner.text = 'Falling back to GitHub sources...';
|
|
327
90
|
marketplaceSkills = await listMarketplaceSkills();
|
|
328
91
|
total = marketplaceSkills.length;
|
|
@@ -356,10 +119,8 @@ async function showMainMenu() {
|
|
|
356
119
|
console.log(chalk.yellow('\nNo skills selected. Exiting.\n'));
|
|
357
120
|
return;
|
|
358
121
|
}
|
|
359
|
-
// Step 4: Install skills directly to platform directories
|
|
122
|
+
// Step 4: Install skills directly to platform directories
|
|
360
123
|
console.log('');
|
|
361
|
-
// Use centralized AGENTS config for platform directories
|
|
362
|
-
// Import dependencies once
|
|
363
124
|
const { getSkillByScoped } = await import('../core/skillsdb.js');
|
|
364
125
|
const { mkdir, cp, rm } = await import('fs/promises');
|
|
365
126
|
const { join } = await import('path');
|
|
@@ -367,21 +128,17 @@ async function showMainMenu() {
|
|
|
367
128
|
const { exec } = await import('child_process');
|
|
368
129
|
const { promisify } = await import('util');
|
|
369
130
|
const execAsync = promisify(exec);
|
|
370
|
-
|
|
371
|
-
async function installSkillToplatforms(skill) {
|
|
131
|
+
async function installSkillToPlatforms(skill) {
|
|
372
132
|
try {
|
|
373
|
-
// Fetch skill from database using scopedName
|
|
374
133
|
const dbSkill = await getSkillByScoped(skill.scopedName || skill.name);
|
|
375
134
|
if (!dbSkill) {
|
|
376
135
|
return { success: false, name: skill.name, error: 'Skill not found' };
|
|
377
136
|
}
|
|
378
|
-
// Handle both camelCase (from API) and snake_case field names
|
|
379
137
|
const githubUrl = dbSkill.github_url || dbSkill.githubUrl;
|
|
380
138
|
const scopedName = dbSkill.scoped_name || dbSkill.scopedName || skill.scopedName;
|
|
381
139
|
if (!githubUrl) {
|
|
382
140
|
return { success: false, name: skill.name, error: 'No GitHub URL found' };
|
|
383
141
|
}
|
|
384
|
-
// Parse GitHub URL
|
|
385
142
|
const urlMatch = githubUrl.match(/github\.com\/([^\/]+)\/([^\/]+)/);
|
|
386
143
|
if (!urlMatch) {
|
|
387
144
|
return { success: false, name: skill.name, error: 'Invalid GitHub URL' };
|
|
@@ -389,12 +146,10 @@ async function showMainMenu() {
|
|
|
389
146
|
const [, owner, repo] = urlMatch;
|
|
390
147
|
const branch = dbSkill.branch || 'main';
|
|
391
148
|
const skillPath = (dbSkill.path || '').replace(/\/SKILL\.md$/i, '');
|
|
392
|
-
// Download to temp directory
|
|
393
149
|
const tempDir = join(tmpdir(), `skill-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
394
150
|
await mkdir(tempDir, { recursive: true });
|
|
395
151
|
try {
|
|
396
152
|
await execAsync(`git clone --depth 1 --branch ${branch} https://github.com/${owner}/${repo}.git .`, { cwd: tempDir });
|
|
397
|
-
// Install to each platform
|
|
398
153
|
for (const platform of selectedAgents) {
|
|
399
154
|
const agentConfig = AGENTS[platform];
|
|
400
155
|
if (!agentConfig)
|
|
@@ -402,14 +157,12 @@ async function showMainMenu() {
|
|
|
402
157
|
const targetDir = agentConfig.projectDir;
|
|
403
158
|
const skillDir = join(process.cwd(), targetDir, dbSkill.name);
|
|
404
159
|
await mkdir(skillDir, { recursive: true });
|
|
405
|
-
// Copy skill files
|
|
406
160
|
const sourceDir = skillPath ? join(tempDir, skillPath) : tempDir;
|
|
407
161
|
await cp(sourceDir, skillDir, { recursive: true });
|
|
408
162
|
}
|
|
409
163
|
return { success: true, name: dbSkill.name, scopedName };
|
|
410
164
|
}
|
|
411
165
|
finally {
|
|
412
|
-
// Cleanup temp directory
|
|
413
166
|
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
414
167
|
}
|
|
415
168
|
}
|
|
@@ -417,13 +170,10 @@ async function showMainMenu() {
|
|
|
417
170
|
return { success: false, name: skill.name, error: err.message || String(err) };
|
|
418
171
|
}
|
|
419
172
|
}
|
|
420
|
-
// Show what we're downloading
|
|
421
173
|
console.log(chalk.bold(`š¦ Installing ${selectedSkills.length} skill(s) in parallel...\n`));
|
|
422
174
|
const downloadSpinner = ora(`Downloading ${selectedSkills.length} skills...`).start();
|
|
423
|
-
|
|
424
|
-
const results = await Promise.all(selectedSkills.map((skill) => installSkillToplatforms(skill)));
|
|
175
|
+
const results = await Promise.all(selectedSkills.map((skill) => installSkillToPlatforms(skill)));
|
|
425
176
|
downloadSpinner.succeed(`Downloaded ${results.filter(r => r.success).length}/${selectedSkills.length} skills`);
|
|
426
|
-
// Show results
|
|
427
177
|
console.log('');
|
|
428
178
|
for (const result of results) {
|
|
429
179
|
if (result.success) {
|
|
@@ -442,2461 +192,52 @@ async function showMainMenu() {
|
|
|
442
192
|
}
|
|
443
193
|
console.log('');
|
|
444
194
|
}
|
|
445
|
-
|
|
446
|
-
// Step 1: Select target agent(s)
|
|
447
|
-
const { agents } = await inquirer.prompt([
|
|
448
|
-
{
|
|
449
|
-
type: 'checkbox',
|
|
450
|
-
name: 'agents',
|
|
451
|
-
message: 'Which AI agents will you use these skills with?',
|
|
452
|
-
choices: [
|
|
453
|
-
{ name: 'Cursor', value: 'cursor', checked: true },
|
|
454
|
-
{ name: 'Claude Code', value: 'claude', checked: true },
|
|
455
|
-
{ name: 'GitHub Copilot', value: 'copilot', checked: true },
|
|
456
|
-
{ name: 'OpenAI Codex', value: 'codex', checked: false }
|
|
457
|
-
]
|
|
458
|
-
}
|
|
459
|
-
]);
|
|
460
|
-
if (agents.length === 0) {
|
|
461
|
-
console.log(chalk.yellow('No agents selected.'));
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
// Step 2: Fetch and select skills
|
|
465
|
-
const spinner = ora('Fetching skills from marketplace...').start();
|
|
466
|
-
const skills = await listMarketplaceSkills();
|
|
467
|
-
spinner.stop();
|
|
468
|
-
if (skills.length === 0) {
|
|
469
|
-
console.log(chalk.yellow('No skills found.'));
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
const choices = skills.map(skill => ({
|
|
473
|
-
name: `${skill.name} - ${skill.description?.slice(0, 45) || 'No description'}...`,
|
|
474
|
-
value: skill.name,
|
|
475
|
-
short: skill.name
|
|
476
|
-
}));
|
|
477
|
-
const { selectedSkills } = await inquirer.prompt([
|
|
478
|
-
{
|
|
479
|
-
type: 'checkbox',
|
|
480
|
-
name: 'selectedSkills',
|
|
481
|
-
message: 'Select skills to install (Space to select):',
|
|
482
|
-
choices,
|
|
483
|
-
pageSize: 12
|
|
484
|
-
}
|
|
485
|
-
]);
|
|
486
|
-
if (selectedSkills.length === 0) {
|
|
487
|
-
console.log(chalk.yellow('No skills selected.'));
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
// Step 3: Install skills
|
|
491
|
-
console.log('');
|
|
492
|
-
for (const skillName of selectedSkills) {
|
|
493
|
-
const installSpinner = ora(`Installing ${skillName}...`).start();
|
|
494
|
-
try {
|
|
495
|
-
await installSkill(skillName);
|
|
496
|
-
installSpinner.succeed(`Installed: ${skillName}`);
|
|
497
|
-
}
|
|
498
|
-
catch (err) {
|
|
499
|
-
installSpinner.fail(`Failed: ${skillName}`);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
// Step 4: Export to selected agents
|
|
503
|
-
console.log('');
|
|
504
|
-
const exportSpinner = ora('Exporting to selected agents...').start();
|
|
505
|
-
const allSkills = await discoverSkills();
|
|
506
|
-
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
507
|
-
const { join } = await import('path');
|
|
508
|
-
const { existsSync } = await import('fs');
|
|
509
|
-
const fs = { mkdir, writeFile, appendFile, join, existsSync };
|
|
510
|
-
exportSpinner.stop();
|
|
511
|
-
for (const agent of agents) {
|
|
512
|
-
const agentSpinner = ora(`Exporting to ${agent}...`).start();
|
|
513
|
-
await exportToAgent(agent, allSkills, '.', fs);
|
|
514
|
-
agentSpinner.succeed(`Exported to ${agent}`);
|
|
515
|
-
}
|
|
516
|
-
console.log(chalk.bold.green('\n⨠Done! Skills installed and exported.\n'));
|
|
517
|
-
}
|
|
518
|
-
async function interactiveExport() {
|
|
519
|
-
const skills = await discoverSkills();
|
|
520
|
-
if (skills.length === 0) {
|
|
521
|
-
console.log(chalk.yellow('No skills found to export.'));
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
const { agents } = await inquirer.prompt([
|
|
525
|
-
{
|
|
526
|
-
type: 'checkbox',
|
|
527
|
-
name: 'agents',
|
|
528
|
-
message: 'Select target AI agents:',
|
|
529
|
-
choices: [
|
|
530
|
-
{ name: 'Cursor (.cursor/skills/)', value: 'cursor', checked: true },
|
|
531
|
-
{ name: 'Claude Code (.claude/skills/)', value: 'claude', checked: true },
|
|
532
|
-
{ name: 'GitHub Copilot (.github/skills/)', value: 'copilot', checked: true },
|
|
533
|
-
{ name: 'OpenAI Codex (.codex/skills/)', value: 'codex', checked: false },
|
|
534
|
-
{ name: 'Antigravity (.agent/workflows/)', value: 'antigravity', checked: true }
|
|
535
|
-
]
|
|
536
|
-
}
|
|
537
|
-
]);
|
|
538
|
-
if (agents.length === 0) {
|
|
539
|
-
console.log(chalk.yellow('No agents selected.'));
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
543
|
-
const { join } = await import('path');
|
|
544
|
-
const { existsSync } = await import('fs');
|
|
545
|
-
const fs = { mkdir, writeFile, appendFile, join, existsSync };
|
|
546
|
-
console.log('');
|
|
547
|
-
for (const agent of agents) {
|
|
548
|
-
const spinner = ora(`Exporting to ${agent}...`).start();
|
|
549
|
-
await exportToAgent(agent, skills, '.', fs);
|
|
550
|
-
spinner.succeed();
|
|
551
|
-
}
|
|
552
|
-
console.log(chalk.bold.green('\nā Export complete!\n'));
|
|
553
|
-
}
|
|
195
|
+
// āāā Root command āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
554
196
|
program
|
|
555
197
|
.name('skills')
|
|
556
198
|
.description('Agent Skills CLI - Manage skills for Cursor, Claude Code, GitHub Copilot, OpenAI Codex')
|
|
557
199
|
.version('1.0.0')
|
|
558
200
|
.action(showMainMenu);
|
|
559
|
-
//
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
console.log(chalk.gray(' - ~/.antigravity/skills/'));
|
|
580
|
-
console.log(chalk.gray(' - .antigravity/skills/'));
|
|
581
|
-
console.log(chalk.gray(' - ./skills/'));
|
|
582
|
-
}
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
// JSON output
|
|
586
|
-
if (options.json) {
|
|
587
|
-
console.log(JSON.stringify({
|
|
588
|
-
skills: skills.map(s => ({
|
|
589
|
-
name: s.name,
|
|
590
|
-
description: s.description,
|
|
591
|
-
path: s.path
|
|
592
|
-
})),
|
|
593
|
-
count: skills.length
|
|
594
|
-
}, null, 2));
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
// Quiet output (names only)
|
|
598
|
-
if (options.quiet) {
|
|
599
|
-
skills.forEach(s => console.log(s.name));
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
// Table output
|
|
603
|
-
if (options.table) {
|
|
604
|
-
const maxName = Math.max(...skills.map(s => s.name.length), 4);
|
|
605
|
-
const maxDesc = Math.min(Math.max(...skills.map(s => (s.description || '').length), 11), 50);
|
|
606
|
-
console.log('');
|
|
607
|
-
console.log(chalk.bold('Name'.padEnd(maxName + 2) + 'Description'));
|
|
608
|
-
console.log('ā'.repeat(maxName + 2 + maxDesc));
|
|
609
|
-
for (const skill of skills) {
|
|
610
|
-
const desc = (skill.description || '').slice(0, 50);
|
|
611
|
-
console.log(chalk.cyan(skill.name.padEnd(maxName + 2)) + chalk.gray(desc));
|
|
612
|
-
}
|
|
613
|
-
console.log('');
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
// Default output
|
|
617
|
-
console.log(chalk.bold(`\nFound ${skills.length} skill(s):\n`));
|
|
618
|
-
for (const skill of skills) {
|
|
619
|
-
console.log(chalk.cyan(` ${skill.name}`));
|
|
620
|
-
if (options.verbose) {
|
|
621
|
-
console.log(chalk.gray(` ${skill.description}`));
|
|
622
|
-
console.log(chalk.gray(` Path: ${skill.path}`));
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
console.log('');
|
|
626
|
-
}
|
|
627
|
-
catch (error) {
|
|
628
|
-
console.error(chalk.red('Error listing skills:'), error);
|
|
629
|
-
process.exit(1);
|
|
630
|
-
}
|
|
631
|
-
});
|
|
632
|
-
// Validate command
|
|
633
|
-
program
|
|
634
|
-
.command('validate <path>')
|
|
635
|
-
.description('Validate a skill against the Agent Skills specification')
|
|
636
|
-
.action(async (path) => {
|
|
637
|
-
try {
|
|
638
|
-
const skill = await loadSkill(path);
|
|
639
|
-
if (!skill) {
|
|
640
|
-
console.error(chalk.red(`Skill not found at: ${path}`));
|
|
641
|
-
process.exit(1);
|
|
642
|
-
}
|
|
643
|
-
console.log(chalk.bold(`\nValidating: ${skill.metadata.name}\n`));
|
|
644
|
-
// Validate metadata
|
|
645
|
-
const metadataResult = validateMetadata(skill.metadata);
|
|
646
|
-
console.log(chalk.underline('Metadata:'));
|
|
647
|
-
console.log(formatValidationResult(metadataResult));
|
|
648
|
-
// Validate body
|
|
649
|
-
const bodyResult = validateBody(skill.body);
|
|
650
|
-
console.log(chalk.underline('\nBody Content:'));
|
|
651
|
-
console.log(formatValidationResult(bodyResult));
|
|
652
|
-
// Overall result
|
|
653
|
-
const isValid = metadataResult.valid && bodyResult.valid;
|
|
654
|
-
console.log('\n' + 'ā'.repeat(40));
|
|
655
|
-
if (isValid) {
|
|
656
|
-
console.log(chalk.green.bold('ā Skill is valid'));
|
|
657
|
-
}
|
|
658
|
-
else {
|
|
659
|
-
console.log(chalk.red.bold('ā Skill has validation errors'));
|
|
660
|
-
process.exit(1);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
catch (error) {
|
|
664
|
-
console.error(chalk.red('Error validating skill:'), error);
|
|
665
|
-
process.exit(1);
|
|
666
|
-
}
|
|
667
|
-
});
|
|
668
|
-
// Show command
|
|
669
|
-
program
|
|
670
|
-
.command('show <name>')
|
|
671
|
-
.description('Show detailed information about a skill')
|
|
672
|
-
.action(async (name) => {
|
|
673
|
-
try {
|
|
674
|
-
const skills = await discoverSkills();
|
|
675
|
-
const skillRef = skills.find(s => s.name === name);
|
|
676
|
-
if (!skillRef) {
|
|
677
|
-
console.error(chalk.red(`Skill not found: ${name}`));
|
|
678
|
-
console.log(chalk.gray('Available skills:'), skills.map(s => s.name).join(', ') || 'none');
|
|
679
|
-
process.exit(1);
|
|
680
|
-
}
|
|
681
|
-
const skill = await loadSkill(skillRef.path);
|
|
682
|
-
if (!skill) {
|
|
683
|
-
console.error(chalk.red(`Could not load skill: ${name}`));
|
|
684
|
-
process.exit(1);
|
|
685
|
-
}
|
|
686
|
-
console.log(chalk.bold(`\n${skill.metadata.name}`));
|
|
687
|
-
console.log('ā'.repeat(40));
|
|
688
|
-
console.log(chalk.cyan('Description:'), skill.metadata.description);
|
|
689
|
-
console.log(chalk.cyan('Path:'), skill.path);
|
|
690
|
-
if (skill.metadata.license) {
|
|
691
|
-
console.log(chalk.cyan('License:'), skill.metadata.license);
|
|
692
|
-
}
|
|
693
|
-
if (skill.metadata.compatibility) {
|
|
694
|
-
console.log(chalk.cyan('Compatibility:'), skill.metadata.compatibility);
|
|
695
|
-
}
|
|
696
|
-
// List resources
|
|
697
|
-
const resources = await listSkillResources(skill.path);
|
|
698
|
-
if (resources.scripts.length > 0) {
|
|
699
|
-
console.log(chalk.cyan('\nScripts:'));
|
|
700
|
-
resources.scripts.forEach(s => console.log(chalk.gray(` - ${s}`)));
|
|
701
|
-
}
|
|
702
|
-
if (resources.references.length > 0) {
|
|
703
|
-
console.log(chalk.cyan('\nReferences:'));
|
|
704
|
-
resources.references.forEach(r => console.log(chalk.gray(` - ${r}`)));
|
|
705
|
-
}
|
|
706
|
-
if (resources.assets.length > 0) {
|
|
707
|
-
console.log(chalk.cyan('\nAssets:'));
|
|
708
|
-
resources.assets.forEach(a => console.log(chalk.gray(` - ${a}`)));
|
|
709
|
-
}
|
|
710
|
-
// Body preview
|
|
711
|
-
const bodyLines = skill.body.split('\n').slice(0, 10);
|
|
712
|
-
console.log(chalk.cyan('\nInstructions (preview):'));
|
|
713
|
-
console.log(chalk.gray(bodyLines.join('\n')));
|
|
714
|
-
if (skill.body.split('\n').length > 10) {
|
|
715
|
-
console.log(chalk.gray('...'));
|
|
716
|
-
}
|
|
717
|
-
console.log('');
|
|
718
|
-
}
|
|
719
|
-
catch (error) {
|
|
720
|
-
console.error(chalk.red('Error showing skill:'), error);
|
|
721
|
-
process.exit(1);
|
|
722
|
-
}
|
|
723
|
-
});
|
|
724
|
-
// Prompt command - generate system prompt XML
|
|
725
|
-
program
|
|
726
|
-
.command('prompt')
|
|
727
|
-
.description('Generate system prompt XML for discovered skills')
|
|
728
|
-
.option('-f, --full', 'Include full skill system instructions')
|
|
729
|
-
.action(async (options) => {
|
|
730
|
-
try {
|
|
731
|
-
const skills = await discoverSkills();
|
|
732
|
-
if (skills.length === 0) {
|
|
733
|
-
console.log(chalk.yellow('No skills found.'));
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
|
-
if (options.full) {
|
|
737
|
-
const context = generateFullSkillsContext(skills);
|
|
738
|
-
console.log(context);
|
|
739
|
-
}
|
|
740
|
-
else {
|
|
741
|
-
const { xml, skillCount, estimatedTokens } = generateSkillsPromptXML(skills);
|
|
742
|
-
console.log(xml);
|
|
743
|
-
console.log(chalk.gray(`\n# ${skillCount} skills, ~${estimatedTokens} tokens`));
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
catch (error) {
|
|
747
|
-
console.error(chalk.red('Error generating prompt:'), error);
|
|
748
|
-
process.exit(1);
|
|
749
|
-
}
|
|
750
|
-
});
|
|
751
|
-
// Init command - create a new skill
|
|
752
|
-
program
|
|
753
|
-
.command('init <name>')
|
|
754
|
-
.description('Create a new skill from template')
|
|
755
|
-
.option('-d, --directory <dir>', 'Directory to create skill in', './skills')
|
|
756
|
-
.action(async (name, options) => {
|
|
757
|
-
try {
|
|
758
|
-
const { mkdir, writeFile } = await import('fs/promises');
|
|
759
|
-
const { join } = await import('path');
|
|
760
|
-
const skillDir = join(options.directory, name);
|
|
761
|
-
// Create directories
|
|
762
|
-
await mkdir(join(skillDir, 'scripts'), { recursive: true });
|
|
763
|
-
await mkdir(join(skillDir, 'references'), { recursive: true });
|
|
764
|
-
await mkdir(join(skillDir, 'assets'), { recursive: true });
|
|
765
|
-
// Create SKILL.md
|
|
766
|
-
const skillMd = `---
|
|
767
|
-
name: ${name}
|
|
768
|
-
description: Brief description of what this skill does and when to use it.
|
|
769
|
-
license: MIT
|
|
770
|
-
metadata:
|
|
771
|
-
author: your-name
|
|
772
|
-
version: "1.0"
|
|
773
|
-
---
|
|
774
|
-
|
|
775
|
-
# ${name.split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}
|
|
776
|
-
|
|
777
|
-
## When to use this skill
|
|
778
|
-
|
|
779
|
-
Use this skill when the user needs to...
|
|
780
|
-
|
|
781
|
-
## Instructions
|
|
782
|
-
|
|
783
|
-
1. First step
|
|
784
|
-
2. Second step
|
|
785
|
-
3. Third step
|
|
786
|
-
|
|
787
|
-
## Examples
|
|
788
|
-
|
|
789
|
-
### Example 1
|
|
790
|
-
|
|
791
|
-
\`\`\`
|
|
792
|
-
Example input or command
|
|
793
|
-
\`\`\`
|
|
794
|
-
|
|
795
|
-
## Best practices
|
|
796
|
-
|
|
797
|
-
- Best practice 1
|
|
798
|
-
- Best practice 2
|
|
799
|
-
`;
|
|
800
|
-
await writeFile(join(skillDir, 'SKILL.md'), skillMd);
|
|
801
|
-
console.log(chalk.green(`ā Created skill: ${name}`));
|
|
802
|
-
console.log(chalk.gray(` Path: ${skillDir}`));
|
|
803
|
-
console.log(chalk.gray('\nNext steps:'));
|
|
804
|
-
console.log(chalk.gray(' 1. Edit SKILL.md with your instructions'));
|
|
805
|
-
console.log(chalk.gray(' 2. Add scripts to scripts/'));
|
|
806
|
-
console.log(chalk.gray(' 3. Run: skills validate ' + skillDir));
|
|
807
|
-
}
|
|
808
|
-
catch (error) {
|
|
809
|
-
console.error(chalk.red('Error creating skill:'), error);
|
|
810
|
-
process.exit(1);
|
|
811
|
-
}
|
|
812
|
-
});
|
|
813
|
-
// ============================================
|
|
814
|
-
// ASSETS COMMAND - On-demand asset fetching
|
|
815
|
-
// ============================================
|
|
816
|
-
program
|
|
817
|
-
.command('assets <skill-name>')
|
|
818
|
-
.description('List and fetch assets for a skill on-demand from GitHub')
|
|
819
|
-
.option('-l, --list', 'List available assets')
|
|
820
|
-
.option('-m, --manifest', 'Show asset manifest if available')
|
|
821
|
-
.option('-g, --get <path>', 'Fetch and display specific asset content')
|
|
822
|
-
.option('--json', 'Output in JSON format')
|
|
823
|
-
.action(async (skillName, options) => {
|
|
824
|
-
try {
|
|
825
|
-
const spinner = ora('Fetching skill info...').start();
|
|
826
|
-
// Fetch skill from database
|
|
827
|
-
const skill = await getSkillByScoped(skillName);
|
|
828
|
-
if (!skill) {
|
|
829
|
-
spinner.fail(`Skill not found: ${skillName}`);
|
|
830
|
-
process.exit(1);
|
|
831
|
-
}
|
|
832
|
-
if (!skill.raw_url) {
|
|
833
|
-
spinner.fail('Skill has no raw_url - cannot fetch assets');
|
|
834
|
-
process.exit(1);
|
|
835
|
-
}
|
|
836
|
-
const baseUrl = getSkillBaseUrl(skill.raw_url);
|
|
837
|
-
spinner.succeed(`Found skill: ${skill.scoped_name || skill.name}`);
|
|
838
|
-
if (options.manifest) {
|
|
839
|
-
// Show asset manifest
|
|
840
|
-
const manifestSpinner = ora('Fetching manifest...').start();
|
|
841
|
-
const manifest = await fetchAssetManifest(baseUrl);
|
|
842
|
-
if (!manifest) {
|
|
843
|
-
manifestSpinner.fail('No asset manifest found (index.jsonl)');
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
846
|
-
manifestSpinner.succeed(`Found ${manifest.length} components`);
|
|
847
|
-
if (options.json) {
|
|
848
|
-
console.log(JSON.stringify(manifest, null, 2));
|
|
849
|
-
}
|
|
850
|
-
else {
|
|
851
|
-
console.log('');
|
|
852
|
-
// Group by category
|
|
853
|
-
const byCategory = new Map();
|
|
854
|
-
for (const entry of manifest) {
|
|
855
|
-
const cat = entry.category || 'other';
|
|
856
|
-
if (!byCategory.has(cat))
|
|
857
|
-
byCategory.set(cat, []);
|
|
858
|
-
byCategory.get(cat).push(entry);
|
|
859
|
-
}
|
|
860
|
-
for (const [category, entries] of byCategory) {
|
|
861
|
-
console.log(chalk.bold.cyan(`\n${category}:`));
|
|
862
|
-
for (const entry of entries.slice(0, 5)) {
|
|
863
|
-
console.log(chalk.white(` ${entry.id}`));
|
|
864
|
-
if (entry.name)
|
|
865
|
-
console.log(chalk.gray(` ${entry.name}`));
|
|
866
|
-
}
|
|
867
|
-
if (entries.length > 5) {
|
|
868
|
-
console.log(chalk.gray(` ... and ${entries.length - 5} more`));
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
else if (options.get) {
|
|
874
|
-
// Fetch specific asset
|
|
875
|
-
const assetPath = options.get;
|
|
876
|
-
const assetUrl = getAssetUrl(baseUrl, assetPath);
|
|
877
|
-
const fetchSpinner = ora(`Fetching ${assetPath}...`).start();
|
|
878
|
-
const content = await fetchAsset(assetUrl);
|
|
879
|
-
if (!content) {
|
|
880
|
-
fetchSpinner.fail(`Asset not found: ${assetPath}`);
|
|
881
|
-
process.exit(1);
|
|
882
|
-
}
|
|
883
|
-
fetchSpinner.succeed(`Fetched ${content.length} chars`);
|
|
884
|
-
console.log('');
|
|
885
|
-
console.log(content);
|
|
886
|
-
}
|
|
887
|
-
else {
|
|
888
|
-
// Default: show info about assets
|
|
889
|
-
console.log(chalk.gray(`\nBase URL: ${baseUrl}`));
|
|
890
|
-
console.log('');
|
|
891
|
-
console.log(chalk.bold('Usage:'));
|
|
892
|
-
console.log(chalk.gray(` skills assets "${skillName}" --manifest`));
|
|
893
|
-
console.log(chalk.gray(' Show component manifest'));
|
|
894
|
-
console.log('');
|
|
895
|
-
console.log(chalk.gray(` skills assets "${skillName}" --get "assets/code/v3/html/buttons/primary.html"`));
|
|
896
|
-
console.log(chalk.gray(' Fetch specific asset content'));
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
catch (error) {
|
|
900
|
-
console.error(chalk.red('Error:'), error);
|
|
901
|
-
process.exit(1);
|
|
902
|
-
}
|
|
903
|
-
});
|
|
904
|
-
// ============================================
|
|
905
|
-
// MARKETPLACE COMMANDS
|
|
906
|
-
// ============================================
|
|
907
|
-
// Market list - list skills from SkillsMP (40k+ skills)
|
|
908
|
-
program
|
|
909
|
-
.command('market-list')
|
|
910
|
-
.alias('ml')
|
|
911
|
-
.description('List skills from SkillsMP marketplace (40k+ skills)')
|
|
912
|
-
.option('-l, --limit <number>', 'Number of skills to show', '50')
|
|
913
|
-
.option('-p, --page <number>', 'Page number', '1')
|
|
914
|
-
.option('--legacy', 'Use legacy GitHub sources instead of SkillsMP')
|
|
915
|
-
.action(async (options) => {
|
|
916
|
-
try {
|
|
917
|
-
if (options.legacy) {
|
|
918
|
-
// Legacy mode: fetch from configured GitHub sources
|
|
919
|
-
console.log(chalk.bold('\nFetching skills from GitHub sources...\n'));
|
|
920
|
-
const skills = await listMarketplaceSkills();
|
|
921
|
-
if (skills.length === 0) {
|
|
922
|
-
console.log(chalk.yellow('No skills found.'));
|
|
923
|
-
return;
|
|
924
|
-
}
|
|
925
|
-
const bySource = new Map();
|
|
926
|
-
for (const skill of skills) {
|
|
927
|
-
const sourceId = skill.source.id;
|
|
928
|
-
if (!bySource.has(sourceId)) {
|
|
929
|
-
bySource.set(sourceId, []);
|
|
930
|
-
}
|
|
931
|
-
bySource.get(sourceId).push(skill);
|
|
932
|
-
}
|
|
933
|
-
for (const [sourceId, sourceSkills] of bySource) {
|
|
934
|
-
const source = sourceSkills[0].source;
|
|
935
|
-
console.log(chalk.bold.cyan(`\nš¦ ${source.name}`));
|
|
936
|
-
console.log(chalk.gray(` ${source.owner}/${source.repo}`));
|
|
937
|
-
if (source.verified) {
|
|
938
|
-
console.log(chalk.green(' ā Verified'));
|
|
939
|
-
}
|
|
940
|
-
console.log('');
|
|
941
|
-
for (const skill of sourceSkills) {
|
|
942
|
-
console.log(chalk.white(` ${skill.name}`));
|
|
943
|
-
if (skill.description) {
|
|
944
|
-
const desc = skill.description.length > 60
|
|
945
|
-
? skill.description.slice(0, 60) + '...'
|
|
946
|
-
: skill.description;
|
|
947
|
-
console.log(chalk.gray(` ${desc}`));
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
console.log(chalk.gray(`\nTotal: ${skills.length} skills from ${bySource.size} sources`));
|
|
952
|
-
}
|
|
953
|
-
else {
|
|
954
|
-
// Database mode (primary): fetch from our API
|
|
955
|
-
console.log(chalk.bold('\nš Skills Marketplace\n'));
|
|
956
|
-
const limit = parseInt(options.limit) || 50;
|
|
957
|
-
const page = parseInt(options.page) || 1;
|
|
958
|
-
let result;
|
|
959
|
-
try {
|
|
960
|
-
result = await fetchSkillsForCLI({ limit, page, sortBy: 'stars' });
|
|
961
|
-
}
|
|
962
|
-
catch {
|
|
963
|
-
console.log(chalk.gray('Falling back to GitHub sources...'));
|
|
964
|
-
const skills = await listMarketplaceSkills();
|
|
965
|
-
result = { skills: skills.slice(0, limit), total: skills.length, hasNext: false };
|
|
966
|
-
}
|
|
967
|
-
console.log(chalk.gray(`Showing ${result.skills.length} of ${result.total.toLocaleString()} skills (page ${page})\n`));
|
|
968
|
-
for (const skill of result.skills) {
|
|
969
|
-
const stars = skill.stars ? chalk.yellow(`ā${skill.stars.toLocaleString()}`) : '';
|
|
970
|
-
console.log(chalk.white(` ${skill.name} ${stars}`));
|
|
971
|
-
if (skill.description) {
|
|
972
|
-
const desc = skill.description.length > 55
|
|
973
|
-
? skill.description.slice(0, 55) + '...'
|
|
974
|
-
: skill.description;
|
|
975
|
-
console.log(chalk.gray(` ${desc}`));
|
|
976
|
-
}
|
|
977
|
-
console.log(chalk.dim(` by ${skill.author || 'unknown'}`));
|
|
978
|
-
}
|
|
979
|
-
console.log(chalk.gray(`\nTotal: ${result.total.toLocaleString()} skills`));
|
|
980
|
-
if (result.hasNext) {
|
|
981
|
-
console.log(chalk.gray(`Next page: skills market-list --page ${page + 1}`));
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
console.log(chalk.gray('\nUse: skills (interactive) to install\n'));
|
|
985
|
-
}
|
|
986
|
-
catch (error) {
|
|
987
|
-
console.error(chalk.red('Error:'), error);
|
|
988
|
-
process.exit(1);
|
|
989
|
-
}
|
|
990
|
-
});
|
|
991
|
-
// Market search - search skills
|
|
992
|
-
program
|
|
993
|
-
.command('market-search <query>')
|
|
994
|
-
.alias('ms')
|
|
995
|
-
.description('Search skills in the marketplace')
|
|
996
|
-
.option('-l, --limit <number>', 'Number of results', '20')
|
|
997
|
-
.action(async (query, options) => {
|
|
998
|
-
try {
|
|
999
|
-
console.log(chalk.bold(`\nš Searching for "${query}"...\n`));
|
|
1000
|
-
const limit = parseInt(options.limit) || 20;
|
|
1001
|
-
let result = null;
|
|
1002
|
-
// Try database first, fallback to GitHub
|
|
1003
|
-
try {
|
|
1004
|
-
result = await fetchSkillsForCLI({ search: query, limit, sortBy: 'stars' });
|
|
1005
|
-
}
|
|
1006
|
-
catch {
|
|
1007
|
-
// Fallback to GitHub-based search
|
|
1008
|
-
console.log(chalk.gray('Falling back to GitHub sources...'));
|
|
1009
|
-
const skills = await searchSkills(query);
|
|
1010
|
-
result = { skills: skills.slice(0, limit), total: skills.length };
|
|
1011
|
-
}
|
|
1012
|
-
if (!result || result.skills.length === 0) {
|
|
1013
|
-
console.log(chalk.yellow(`No skills found matching "${query}"`));
|
|
1014
|
-
return;
|
|
1015
|
-
}
|
|
1016
|
-
console.log(chalk.gray(`Found ${result.total.toLocaleString()} skills (showing top ${result.skills.length}):\n`));
|
|
1017
|
-
for (const skill of result.skills) {
|
|
1018
|
-
const stars = skill.stars ? chalk.yellow(`ā${skill.stars.toLocaleString()}`) : '';
|
|
1019
|
-
console.log(chalk.cyan(` ${skill.name} ${stars}`));
|
|
1020
|
-
console.log(chalk.gray(` ${skill.description?.slice(0, 70)}${(skill.description?.length || 0) > 70 ? '...' : ''}`));
|
|
1021
|
-
console.log(chalk.dim(` by ${skill.author || 'unknown'}`));
|
|
1022
|
-
console.log('');
|
|
1023
|
-
}
|
|
1024
|
-
console.log(chalk.gray('Use: skills (interactive) to install\n'));
|
|
1025
|
-
}
|
|
1026
|
-
catch (error) {
|
|
1027
|
-
console.error(chalk.red('Error searching skills:'), error);
|
|
1028
|
-
process.exit(1);
|
|
1029
|
-
}
|
|
1030
|
-
});
|
|
1031
|
-
// ============================================
|
|
1032
|
-
// SEARCH COMMAND - Main user-facing search
|
|
1033
|
-
// ============================================
|
|
1034
|
-
program
|
|
1035
|
-
.command('search [query...]')
|
|
1036
|
-
.alias('s')
|
|
1037
|
-
.description('Search and install skills from marketplace (67K+ skills)')
|
|
1038
|
-
.option('-l, --limit <n>', 'Maximum results to show', '20')
|
|
1039
|
-
.option('-s, --sort <by>', 'Sort by: stars, recent, name', 'stars')
|
|
1040
|
-
.option('-i, --interactive', 'Launch interactive FZF-style search')
|
|
1041
|
-
.option('--json', 'Output as JSON for scripting (no interactive prompt)')
|
|
1042
|
-
.action(async (queryParts, options) => {
|
|
1043
|
-
try {
|
|
1044
|
-
// Interactive FZF mode
|
|
1045
|
-
if (options.interactive || (queryParts.length === 0 && !options.json)) {
|
|
1046
|
-
const selected = await fzfSearch(queryParts.join(' '));
|
|
1047
|
-
if (selected) {
|
|
1048
|
-
console.log(chalk.bold(`\nš¦ Selected: ${selected.name}\n`));
|
|
1049
|
-
console.log(chalk.gray(`Scoped name: ${selected.scopedName}`));
|
|
1050
|
-
console.log(chalk.gray(`Author: ${selected.author}`));
|
|
1051
|
-
console.log(chalk.gray(`Stars: ${selected.stars.toLocaleString()}`));
|
|
1052
|
-
if (selected.description) {
|
|
1053
|
-
console.log(chalk.gray(`Description: ${selected.description}`));
|
|
1054
|
-
}
|
|
1055
|
-
console.log('');
|
|
1056
|
-
// Ask if user wants to install
|
|
1057
|
-
const { install } = await inquirer.prompt([{
|
|
1058
|
-
type: 'confirm',
|
|
1059
|
-
name: 'install',
|
|
1060
|
-
message: `Install ${selected.name}?`,
|
|
1061
|
-
default: true
|
|
1062
|
-
}]);
|
|
1063
|
-
if (install) {
|
|
1064
|
-
const { execSync } = await import('child_process');
|
|
1065
|
-
execSync(`"${process.argv[0]}" "${process.argv[1]}" install "${selected.scopedName}"`, { stdio: 'inherit' });
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
else {
|
|
1069
|
-
console.log(chalk.yellow('\nSearch cancelled.\n'));
|
|
1070
|
-
}
|
|
1071
|
-
return;
|
|
1072
|
-
}
|
|
1073
|
-
const query = queryParts.join(' ');
|
|
1074
|
-
if (!query) {
|
|
1075
|
-
console.log(chalk.yellow('Please provide a search query or use -i for interactive mode.'));
|
|
1076
|
-
return;
|
|
1077
|
-
}
|
|
1078
|
-
const limit = parseInt(options.limit) || 20;
|
|
1079
|
-
if (!options.json) {
|
|
1080
|
-
console.log(chalk.bold(`\nš Searching for "${query}"...\n`));
|
|
1081
|
-
}
|
|
1082
|
-
// Fetch results from database
|
|
1083
|
-
let result;
|
|
1084
|
-
try {
|
|
1085
|
-
result = await fetchSkillsForCLI({
|
|
1086
|
-
search: query,
|
|
1087
|
-
limit,
|
|
1088
|
-
sortBy: options.sort
|
|
1089
|
-
});
|
|
1090
|
-
}
|
|
1091
|
-
catch {
|
|
1092
|
-
// Fallback to GitHub sources
|
|
1093
|
-
if (!options.json) {
|
|
1094
|
-
console.log(chalk.gray('Falling back to GitHub sources...'));
|
|
1095
|
-
}
|
|
1096
|
-
const skills = await searchSkills(query);
|
|
1097
|
-
result = { skills: skills.slice(0, limit), total: skills.length };
|
|
1098
|
-
}
|
|
1099
|
-
// Track search telemetry
|
|
1100
|
-
trackSearch(query, result.total);
|
|
1101
|
-
if (result.skills.length === 0) {
|
|
1102
|
-
if (options.json) {
|
|
1103
|
-
console.log(JSON.stringify({ skills: [], total: 0, query }));
|
|
1104
|
-
}
|
|
1105
|
-
else {
|
|
1106
|
-
console.log(chalk.yellow(`No skills found matching "${query}"`));
|
|
1107
|
-
}
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
// JSON output (non-interactive)
|
|
1111
|
-
if (options.json) {
|
|
1112
|
-
console.log(JSON.stringify({
|
|
1113
|
-
skills: result.skills.map(s => ({
|
|
1114
|
-
name: s.name,
|
|
1115
|
-
author: s.author,
|
|
1116
|
-
scopedName: s.scopedName || `${s.author}/${s.name}`,
|
|
1117
|
-
description: s.description,
|
|
1118
|
-
stars: s.stars || 0,
|
|
1119
|
-
githubUrl: s.githubUrl
|
|
1120
|
-
})),
|
|
1121
|
-
total: result.total,
|
|
1122
|
-
query
|
|
1123
|
-
}, null, 2));
|
|
1124
|
-
return;
|
|
1125
|
-
}
|
|
1126
|
-
// Display results summary
|
|
1127
|
-
console.log(chalk.gray(`Found ${result.total.toLocaleString()} skills. Select to install:\n`));
|
|
1128
|
-
// Interactive install - always show selection prompt
|
|
1129
|
-
const choices = result.skills.map((skill) => ({
|
|
1130
|
-
name: `${skill.name} ${skill.stars ? chalk.yellow(`ā${skill.stars.toLocaleString()}`) : ''} ${chalk.dim(`@${skill.author || 'unknown'}`)}`,
|
|
1131
|
-
value: {
|
|
1132
|
-
name: skill.name,
|
|
1133
|
-
scopedName: skill.scopedName || `${skill.author}/${skill.name}`,
|
|
1134
|
-
githubUrl: skill.githubUrl || ''
|
|
1135
|
-
},
|
|
1136
|
-
short: skill.name
|
|
1137
|
-
}));
|
|
1138
|
-
const { selectedSkills } = await inquirer.prompt([
|
|
1139
|
-
{
|
|
1140
|
-
type: 'checkbox',
|
|
1141
|
-
name: 'selectedSkills',
|
|
1142
|
-
message: 'Select skills (Space to select, Enter to confirm):',
|
|
1143
|
-
choices,
|
|
1144
|
-
pageSize: 15,
|
|
1145
|
-
loop: false
|
|
1146
|
-
}
|
|
1147
|
-
]);
|
|
1148
|
-
if (selectedSkills.length === 0) {
|
|
1149
|
-
console.log(chalk.yellow('\nNo skills selected.\n'));
|
|
1150
|
-
return;
|
|
1151
|
-
}
|
|
1152
|
-
// Select platforms
|
|
1153
|
-
const agentChoices = Object.entries(AGENTS).map(([key, config]) => ({
|
|
1154
|
-
name: config.displayName,
|
|
1155
|
-
value: key,
|
|
1156
|
-
checked: ['cursor', 'claude', 'copilot', 'antigravity'].includes(key)
|
|
1157
|
-
}));
|
|
1158
|
-
const { platforms } = await inquirer.prompt([
|
|
1159
|
-
{
|
|
1160
|
-
type: 'checkbox',
|
|
1161
|
-
name: 'platforms',
|
|
1162
|
-
message: 'Install to which platforms?',
|
|
1163
|
-
choices: agentChoices,
|
|
1164
|
-
pageSize: 10
|
|
1165
|
-
}
|
|
1166
|
-
]);
|
|
1167
|
-
if (platforms.length === 0) {
|
|
1168
|
-
console.log(chalk.yellow('\nNo platforms selected.\n'));
|
|
1169
|
-
return;
|
|
1170
|
-
}
|
|
1171
|
-
// Install skills
|
|
1172
|
-
const { mkdir, cp, rm } = await import('fs/promises');
|
|
1173
|
-
const { join } = await import('path');
|
|
1174
|
-
const { tmpdir } = await import('os');
|
|
1175
|
-
const { exec } = await import('child_process');
|
|
1176
|
-
const { promisify } = await import('util');
|
|
1177
|
-
const execAsync = promisify(exec);
|
|
1178
|
-
console.log(chalk.bold(`\nš¦ Installing ${selectedSkills.length} skill(s)...\n`));
|
|
1179
|
-
for (const skill of selectedSkills) {
|
|
1180
|
-
const installSpinner = ora(`Installing ${skill.name}...`).start();
|
|
1181
|
-
try {
|
|
1182
|
-
// Fetch skill details
|
|
1183
|
-
const { getSkillByScoped } = await import('../core/skillsdb.js');
|
|
1184
|
-
const dbSkill = await getSkillByScoped(skill.scopedName);
|
|
1185
|
-
if (!dbSkill) {
|
|
1186
|
-
installSpinner.fail(`${skill.name}: Not found`);
|
|
1187
|
-
continue;
|
|
1188
|
-
}
|
|
1189
|
-
const githubUrl = dbSkill.github_url || dbSkill.githubUrl;
|
|
1190
|
-
if (!githubUrl) {
|
|
1191
|
-
installSpinner.fail(`${skill.name}: No GitHub URL`);
|
|
1192
|
-
continue;
|
|
1193
|
-
}
|
|
1194
|
-
// Parse GitHub URL
|
|
1195
|
-
const urlMatch = githubUrl.match(/github\.com\/([^\/]+)\/([^\/]+)/);
|
|
1196
|
-
if (!urlMatch) {
|
|
1197
|
-
installSpinner.fail(`${skill.name}: Invalid GitHub URL`);
|
|
1198
|
-
continue;
|
|
1199
|
-
}
|
|
1200
|
-
const [, owner, repo] = urlMatch;
|
|
1201
|
-
const branch = dbSkill.branch || 'main';
|
|
1202
|
-
const skillPath = (dbSkill.path || '').replace(/\/SKILL\.md$/i, '');
|
|
1203
|
-
// Clone to temp
|
|
1204
|
-
const tempDir = join(tmpdir(), `skill-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
1205
|
-
await mkdir(tempDir, { recursive: true });
|
|
1206
|
-
try {
|
|
1207
|
-
await execAsync(`git clone --depth 1 --branch ${branch} https://github.com/${owner}/${repo}.git .`, { cwd: tempDir });
|
|
1208
|
-
// Copy to each platform
|
|
1209
|
-
for (const platform of platforms) {
|
|
1210
|
-
const agentConfig = AGENTS[platform];
|
|
1211
|
-
if (!agentConfig)
|
|
1212
|
-
continue;
|
|
1213
|
-
const targetDir = agentConfig.projectDir;
|
|
1214
|
-
const skillDir = join(process.cwd(), targetDir, dbSkill.name);
|
|
1215
|
-
await mkdir(skillDir, { recursive: true });
|
|
1216
|
-
const sourceDir = skillPath ? join(tempDir, skillPath) : tempDir;
|
|
1217
|
-
await cp(sourceDir, skillDir, { recursive: true });
|
|
1218
|
-
}
|
|
1219
|
-
installSpinner.succeed(`${skill.name} ā ${platforms.join(', ')}`);
|
|
1220
|
-
}
|
|
1221
|
-
finally {
|
|
1222
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
catch (err) {
|
|
1226
|
-
installSpinner.fail(`${skill.name}: ${err.message || err}`);
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
console.log(chalk.bold.green(`\n⨠Installation complete!\n`));
|
|
1230
|
-
}
|
|
1231
|
-
catch (error) {
|
|
1232
|
-
console.error(chalk.red('Error searching:'), error);
|
|
1233
|
-
process.exit(1);
|
|
1234
|
-
}
|
|
1235
|
-
});
|
|
1236
|
-
// Install - Install a skill by scoped name (e.g., @author/skill or author/skill)
|
|
1237
|
-
program
|
|
1238
|
-
.command('install <scoped-name> [platforms...]')
|
|
1239
|
-
.alias('i')
|
|
1240
|
-
.description('Install a skill by @author/name or just name')
|
|
1241
|
-
.option('-g, --global', 'Install skill globally (user-level) instead of project-level')
|
|
1242
|
-
.option('-l, --list', 'Show skill details without installing')
|
|
1243
|
-
.option('-p, --platform <platforms>', 'Target platforms (comma-separated): cursor,claude,copilot,codex,antigravity,opencode,amp,kilo,roo,goose')
|
|
1244
|
-
.option('-t, --target <platforms>', 'Target platforms (alias for --platform)')
|
|
1245
|
-
.option('--all', 'Install to all platforms')
|
|
1246
|
-
.action(async (scopedName, platformsArg, options) => {
|
|
1247
|
-
try {
|
|
1248
|
-
const { parseScopedName, getSkillByScoped, fetchFromDB } = await import('../core/skillsdb.js');
|
|
1249
|
-
const { mkdir, writeFile, cp } = await import('fs/promises');
|
|
1250
|
-
const { existsSync } = await import('fs');
|
|
1251
|
-
const { join } = await import('path');
|
|
1252
|
-
const { tmpdir, homedir } = await import('os');
|
|
1253
|
-
const { exec } = await import('child_process');
|
|
1254
|
-
const { promisify } = await import('util');
|
|
1255
|
-
const execAsync = promisify(exec);
|
|
1256
|
-
const { author, name } = parseScopedName(scopedName);
|
|
1257
|
-
console.log(chalk.bold(`\nš¦ Searching for "${scopedName}"...\n`));
|
|
1258
|
-
// Try our database first
|
|
1259
|
-
let skill;
|
|
1260
|
-
try {
|
|
1261
|
-
skill = await getSkillByScoped(scopedName);
|
|
1262
|
-
}
|
|
1263
|
-
catch {
|
|
1264
|
-
// Fallback to GitHub sources if our API is down
|
|
1265
|
-
console.log(chalk.gray('Falling back to GitHub sources...'));
|
|
1266
|
-
const skills = await searchSkills(name);
|
|
1267
|
-
skill = skills.find((s) => s.name.toLowerCase() === name.toLowerCase() &&
|
|
1268
|
-
(!author || s.author?.toLowerCase() === author.toLowerCase())) || skills[0];
|
|
1269
|
-
}
|
|
1270
|
-
if (!skill) {
|
|
1271
|
-
console.log(chalk.yellow(`No skill found matching "${scopedName}"`));
|
|
1272
|
-
console.log(chalk.gray('Try: skills market-search <query> to find skills\n'));
|
|
1273
|
-
return;
|
|
1274
|
-
}
|
|
1275
|
-
const githubUrl = skill.github_url || skill.githubUrl;
|
|
1276
|
-
if (!githubUrl) {
|
|
1277
|
-
console.log(chalk.red('Could not find GitHub URL for this skill'));
|
|
1278
|
-
return;
|
|
1279
|
-
}
|
|
1280
|
-
console.log(chalk.gray(`Found: ${skill.name} by ${skill.author}`));
|
|
1281
|
-
console.log(chalk.gray(`Stars: ${skill.stars?.toLocaleString() || 0}`));
|
|
1282
|
-
console.log(chalk.gray(`URL: ${githubUrl}`));
|
|
1283
|
-
if (skill.description) {
|
|
1284
|
-
console.log(chalk.gray(`Description: ${skill.description}`));
|
|
1285
|
-
}
|
|
1286
|
-
console.log('');
|
|
1287
|
-
// If --list flag, just show details and exit
|
|
1288
|
-
if (options.list) {
|
|
1289
|
-
console.log(chalk.cyan('Use without --list to install this skill.\n'));
|
|
1290
|
-
return;
|
|
1291
|
-
}
|
|
1292
|
-
// Determine target platforms (priority: --all > positional args > -t/-p > auto-detect)
|
|
1293
|
-
let platforms = [];
|
|
1294
|
-
if (options.all) {
|
|
1295
|
-
platforms = Object.keys(AGENTS);
|
|
1296
|
-
}
|
|
1297
|
-
else if (platformsArg && platformsArg.length > 0) {
|
|
1298
|
-
// Positional arguments like: skills install @author/skill claude cursor
|
|
1299
|
-
platforms = platformsArg.map((p) => p.trim().toLowerCase());
|
|
1300
|
-
}
|
|
1301
|
-
else if (options.target) {
|
|
1302
|
-
// -t or --target option
|
|
1303
|
-
platforms = options.target.split(',').map((p) => p.trim().toLowerCase());
|
|
1304
|
-
}
|
|
1305
|
-
else if (options.platform) {
|
|
1306
|
-
// -p or --platform option
|
|
1307
|
-
platforms = options.platform.split(',').map((p) => p.trim().toLowerCase());
|
|
1308
|
-
}
|
|
1309
|
-
else {
|
|
1310
|
-
// Auto-detect platforms in current directory
|
|
1311
|
-
const cwd = process.cwd();
|
|
1312
|
-
if (existsSync(join(cwd, '.cursor')))
|
|
1313
|
-
platforms.push('cursor');
|
|
1314
|
-
if (existsSync(join(cwd, '.claude')))
|
|
1315
|
-
platforms.push('claude');
|
|
1316
|
-
if (existsSync(join(cwd, '.github')))
|
|
1317
|
-
platforms.push('copilot');
|
|
1318
|
-
if (existsSync(join(cwd, '.codex')))
|
|
1319
|
-
platforms.push('codex');
|
|
1320
|
-
if (existsSync(join(cwd, '.agent')))
|
|
1321
|
-
platforms.push('antigravity');
|
|
1322
|
-
// If none detected, prompt user
|
|
1323
|
-
if (platforms.length === 0) {
|
|
1324
|
-
const { selectedPlatforms } = await inquirer.prompt([{
|
|
1325
|
-
type: 'checkbox',
|
|
1326
|
-
name: 'selectedPlatforms',
|
|
1327
|
-
message: 'Select target platforms:',
|
|
1328
|
-
choices: [
|
|
1329
|
-
{ name: 'Cursor', value: 'cursor', checked: true },
|
|
1330
|
-
{ name: 'Claude Code', value: 'claude', checked: true },
|
|
1331
|
-
{ name: 'GitHub Copilot', value: 'copilot' },
|
|
1332
|
-
{ name: 'OpenAI Codex', value: 'codex' },
|
|
1333
|
-
{ name: 'Antigravity', value: 'antigravity' }
|
|
1334
|
-
]
|
|
1335
|
-
}]);
|
|
1336
|
-
platforms = selectedPlatforms;
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
if (platforms.length === 0) {
|
|
1340
|
-
console.log(chalk.yellow('No platforms selected. Exiting.'));
|
|
1341
|
-
return;
|
|
1342
|
-
}
|
|
1343
|
-
console.log(chalk.gray(`Installing to: ${platforms.join(', ')}${options.global ? ' (global)' : ''}\n`));
|
|
1344
|
-
// Use centralized AGENTS config with global support
|
|
1345
|
-
const isGlobal = !!options.global;
|
|
1346
|
-
// Download skill to temp directory
|
|
1347
|
-
const tempDir = join(tmpdir(), `skill-${Date.now()}`);
|
|
1348
|
-
await mkdir(tempDir, { recursive: true });
|
|
1349
|
-
try {
|
|
1350
|
-
// Clone skill from GitHub
|
|
1351
|
-
const spinner = ora(`Downloading ${skill.name}...`).start();
|
|
1352
|
-
// Parse GitHub URL to get repo info
|
|
1353
|
-
const urlMatch = githubUrl.match(/github\.com\/([^\/]+)\/([^\/]+)/);
|
|
1354
|
-
if (!urlMatch) {
|
|
1355
|
-
spinner.fail('Invalid GitHub URL');
|
|
1356
|
-
return;
|
|
1357
|
-
}
|
|
1358
|
-
const [, owner, repo] = urlMatch;
|
|
1359
|
-
const branch = skill.branch || 'main';
|
|
1360
|
-
const skillPath = skill.path?.replace(/\/SKILL\.md$/i, '') || '';
|
|
1361
|
-
// Clone repo
|
|
1362
|
-
await execAsync(`git clone --depth 1 --branch ${branch} https://github.com/${owner}/${repo}.git .`, { cwd: tempDir });
|
|
1363
|
-
spinner.succeed(`Downloaded ${skill.name}`);
|
|
1364
|
-
// Install to each platform
|
|
1365
|
-
for (const platform of platforms) {
|
|
1366
|
-
const platformSpinner = ora(`Installing to ${platform}...`).start();
|
|
1367
|
-
const agentConfig = AGENTS[platform];
|
|
1368
|
-
if (!agentConfig) {
|
|
1369
|
-
platformSpinner.fail(`Unknown platform: ${platform}`);
|
|
1370
|
-
continue;
|
|
1371
|
-
}
|
|
1372
|
-
const targetDir = isGlobal ? agentConfig.globalDir : agentConfig.projectDir;
|
|
1373
|
-
const skillDir = isGlobal ? join(targetDir, skill.name) : join(process.cwd(), targetDir, skill.name);
|
|
1374
|
-
await mkdir(skillDir, { recursive: true });
|
|
1375
|
-
// Copy skill files
|
|
1376
|
-
const sourceDir = skillPath ? join(tempDir, skillPath) : tempDir;
|
|
1377
|
-
if (platform === 'antigravity') {
|
|
1378
|
-
// Antigravity uses .agent/skills/<skill-name>/
|
|
1379
|
-
// Copy all files including subdirectories (references, scripts, etc.)
|
|
1380
|
-
await cp(sourceDir, skillDir, { recursive: true });
|
|
1381
|
-
// Also create a flat .md file for quick access if SKILL.md exists
|
|
1382
|
-
const skillMdPath = join(sourceDir, 'SKILL.md');
|
|
1383
|
-
if (existsSync(skillMdPath)) {
|
|
1384
|
-
const { readFile } = await import('fs/promises');
|
|
1385
|
-
const content = await readFile(skillMdPath, 'utf-8');
|
|
1386
|
-
const flatMdDir = isGlobal ? targetDir : join(process.cwd(), targetDir);
|
|
1387
|
-
await writeFile(join(flatMdDir, `${skill.name}.md`), content);
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
else {
|
|
1391
|
-
// Other platforms use folder structure
|
|
1392
|
-
await cp(sourceDir, skillDir, { recursive: true });
|
|
1393
|
-
}
|
|
1394
|
-
platformSpinner.succeed(`Installed to ${targetDir}/${skill.name}`);
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
finally {
|
|
1398
|
-
// Cleanup temp directory
|
|
1399
|
-
const { rm } = await import('fs/promises');
|
|
1400
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1401
|
-
}
|
|
1402
|
-
// Track installation in lock file
|
|
1403
|
-
const canonicalPath = getCanonicalPath(skill.name, { global: isGlobal, cwd: process.cwd() });
|
|
1404
|
-
const lockEntry = createLockEntry({
|
|
1405
|
-
name: skill.name,
|
|
1406
|
-
scopedName: `@${skill.author}/${skill.name}`,
|
|
1407
|
-
source: githubUrl,
|
|
1408
|
-
sourceType: 'database',
|
|
1409
|
-
agents: platforms,
|
|
1410
|
-
canonicalPath,
|
|
1411
|
-
isGlobal,
|
|
1412
|
-
projectDir: isGlobal ? undefined : process.cwd()
|
|
1413
|
-
});
|
|
1414
|
-
await addSkillToLock(lockEntry);
|
|
1415
|
-
console.log(chalk.bold.green(`\n⨠Successfully installed: ${skill.name}`));
|
|
1416
|
-
console.log(chalk.gray(` Scoped name: @${skill.author}/${skill.name}`));
|
|
1417
|
-
console.log(chalk.gray(` Platforms: ${platforms.join(', ')}\n`));
|
|
1418
|
-
}
|
|
1419
|
-
catch (error) {
|
|
1420
|
-
console.error(chalk.red('Error installing skill:'), error.message || error);
|
|
1421
|
-
process.exit(1);
|
|
1422
|
-
}
|
|
1423
|
-
});
|
|
1424
|
-
// Add - Install skills directly from Git repository URLs
|
|
1425
|
-
program
|
|
1426
|
-
.command('add <source>')
|
|
1427
|
-
.description('Install skills from a Git repo (e.g., owner/repo, owner/repo@skill-name)')
|
|
1428
|
-
.option('-g, --global', 'Install skill globally (user-level) instead of project-level')
|
|
1429
|
-
.option('-l, --list', 'List available skills in the repository without installing')
|
|
1430
|
-
.option('-s, --skill <skills...>', 'Specify skill names to install')
|
|
1431
|
-
.option('-a, --agent <agents...>', 'Specify agents to install to')
|
|
1432
|
-
.option('-y, --yes', 'Skip confirmation prompts')
|
|
1433
|
-
.action(async (source, options) => {
|
|
1434
|
-
try {
|
|
1435
|
-
const { mkdir, cp, rm, readdir, readFile } = await import('fs/promises');
|
|
1436
|
-
const { existsSync, statSync } = await import('fs');
|
|
1437
|
-
const { join, basename, dirname } = await import('path');
|
|
1438
|
-
const { tmpdir } = await import('os');
|
|
1439
|
-
const { exec } = await import('child_process');
|
|
1440
|
-
const { promisify } = await import('util');
|
|
1441
|
-
const execAsync = promisify(exec);
|
|
1442
|
-
// Parse source URL with @skill-name support
|
|
1443
|
-
function parseSource(input) {
|
|
1444
|
-
// Check for @skill-name suffix: owner/repo@skill-name
|
|
1445
|
-
let targetSkill;
|
|
1446
|
-
const atMatch = input.match(/^(.+)@([a-zA-Z0-9_-]+)$/);
|
|
1447
|
-
if (atMatch && !input.includes('github.com') && !input.includes('gitlab.com')) {
|
|
1448
|
-
input = atMatch[1];
|
|
1449
|
-
targetSkill = atMatch[2];
|
|
1450
|
-
}
|
|
1451
|
-
// GitHub URL with path: github.com/owner/repo/tree/branch/path
|
|
1452
|
-
const githubTreeMatch = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/);
|
|
1453
|
-
if (githubTreeMatch) {
|
|
1454
|
-
const [, owner, repo, , subpath] = githubTreeMatch;
|
|
1455
|
-
return { type: 'github', url: `https://github.com/${owner}/${repo}.git`, subpath, targetSkill };
|
|
1456
|
-
}
|
|
1457
|
-
// GitHub URL: github.com/owner/repo
|
|
1458
|
-
const githubRepoMatch = input.match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
1459
|
-
if (githubRepoMatch) {
|
|
1460
|
-
const [, owner, repo] = githubRepoMatch;
|
|
1461
|
-
const cleanRepo = repo.replace(/\.git$/, '');
|
|
1462
|
-
return { type: 'github', url: `https://github.com/${owner}/${cleanRepo}.git`, targetSkill };
|
|
1463
|
-
}
|
|
1464
|
-
// GitLab URL: gitlab.com/owner/repo
|
|
1465
|
-
const gitlabMatch = input.match(/gitlab\.com\/([^/]+)\/([^/]+)/);
|
|
1466
|
-
if (gitlabMatch) {
|
|
1467
|
-
const [, owner, repo] = gitlabMatch;
|
|
1468
|
-
const cleanRepo = repo.replace(/\.git$/, '');
|
|
1469
|
-
return { type: 'gitlab', url: `https://gitlab.com/${owner}/${cleanRepo}.git`, targetSkill };
|
|
1470
|
-
}
|
|
1471
|
-
// GitHub shorthand: owner/repo or owner/repo/path
|
|
1472
|
-
const shorthandMatch = input.match(/^([^/]+)\/([^/]+)(?:\/(.+))?$/);
|
|
1473
|
-
if (shorthandMatch && !input.includes(':')) {
|
|
1474
|
-
const [, owner, repo, subpath] = shorthandMatch;
|
|
1475
|
-
return { type: 'github', url: `https://github.com/${owner}/${repo}.git`, subpath, targetSkill };
|
|
1476
|
-
}
|
|
1477
|
-
// Fallback: treat as direct git URL
|
|
1478
|
-
return { type: 'git', url: input, targetSkill };
|
|
1479
|
-
}
|
|
1480
|
-
// Discover skills in a directory
|
|
1481
|
-
async function discoverSkillsInDir(dir, subpath) {
|
|
1482
|
-
const skills = [];
|
|
1483
|
-
const searchPath = subpath ? join(dir, subpath) : dir;
|
|
1484
|
-
const searchDirs = [
|
|
1485
|
-
searchPath,
|
|
1486
|
-
join(searchPath, 'skills'),
|
|
1487
|
-
join(searchPath, '.claude/skills'),
|
|
1488
|
-
join(searchPath, '.cursor/skills'),
|
|
1489
|
-
join(searchPath, '.agent/skills'),
|
|
1490
|
-
join(searchPath, '.codex/skills'),
|
|
1491
|
-
join(searchPath, '.opencode/skill'),
|
|
1492
|
-
];
|
|
1493
|
-
for (const searchDir of searchDirs) {
|
|
1494
|
-
if (!existsSync(searchDir))
|
|
1495
|
-
continue;
|
|
1496
|
-
try {
|
|
1497
|
-
const entries = await readdir(searchDir, { withFileTypes: true });
|
|
1498
|
-
for (const entry of entries) {
|
|
1499
|
-
if (entry.isDirectory()) {
|
|
1500
|
-
const skillMdPath = join(searchDir, entry.name, 'SKILL.md');
|
|
1501
|
-
if (existsSync(skillMdPath)) {
|
|
1502
|
-
try {
|
|
1503
|
-
const content = await readFile(skillMdPath, 'utf-8');
|
|
1504
|
-
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
1505
|
-
const descMatch = content.match(/^description:\s*(.+)$/m);
|
|
1506
|
-
skills.push({
|
|
1507
|
-
name: nameMatch ? nameMatch[1].trim() : entry.name,
|
|
1508
|
-
description: descMatch ? descMatch[1].trim() : '',
|
|
1509
|
-
path: join(searchDir, entry.name),
|
|
1510
|
-
});
|
|
1511
|
-
}
|
|
1512
|
-
catch { }
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
catch { }
|
|
1518
|
-
}
|
|
1519
|
-
return skills;
|
|
1520
|
-
}
|
|
1521
|
-
console.log(chalk.bold('\nš¦ add-skill\n'));
|
|
1522
|
-
const parsed = parseSource(source);
|
|
1523
|
-
console.log(chalk.gray(`Source: ${parsed.url}${parsed.subpath ? ` (${parsed.subpath})` : ''}`));
|
|
1524
|
-
// Clone repository
|
|
1525
|
-
const tempDir = join(tmpdir(), `add-skill-${Date.now()}`);
|
|
1526
|
-
await mkdir(tempDir, { recursive: true });
|
|
1527
|
-
const spinner = ora('Cloning repository...').start();
|
|
1528
|
-
try {
|
|
1529
|
-
await execAsync(`git clone --depth 1 ${parsed.url} .`, { cwd: tempDir });
|
|
1530
|
-
spinner.succeed('Repository cloned');
|
|
1531
|
-
}
|
|
1532
|
-
catch (err) {
|
|
1533
|
-
spinner.fail('Failed to clone repository');
|
|
1534
|
-
console.error(chalk.red(err.message || err));
|
|
1535
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1538
|
-
// Discover skills
|
|
1539
|
-
const discoverSpinner = ora('Discovering skills...').start();
|
|
1540
|
-
const skills = await discoverSkillsInDir(tempDir, parsed.subpath);
|
|
1541
|
-
if (skills.length === 0) {
|
|
1542
|
-
discoverSpinner.fail('No skills found');
|
|
1543
|
-
console.log(chalk.yellow('\nNo valid skills found. Skills require a SKILL.md with name and description.'));
|
|
1544
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1545
|
-
return;
|
|
1546
|
-
}
|
|
1547
|
-
discoverSpinner.succeed(`Found ${skills.length} skill${skills.length > 1 ? 's' : ''}`);
|
|
1548
|
-
// If --list, just show skills and exit
|
|
1549
|
-
if (options.list) {
|
|
1550
|
-
console.log(chalk.bold('\nAvailable Skills:'));
|
|
1551
|
-
for (const skill of skills) {
|
|
1552
|
-
console.log(chalk.cyan(` ${skill.name}`));
|
|
1553
|
-
if (skill.description) {
|
|
1554
|
-
console.log(chalk.gray(` ${skill.description}`));
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
console.log(chalk.gray('\nUse --skill <name> to install specific skills\n'));
|
|
1558
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1559
|
-
return;
|
|
1560
|
-
}
|
|
1561
|
-
// Select skills to install
|
|
1562
|
-
let selectedSkills = skills;
|
|
1563
|
-
// If @skill-name was specified in the source URL, auto-select that skill
|
|
1564
|
-
if (parsed.targetSkill) {
|
|
1565
|
-
selectedSkills = skills.filter(s => s.name.toLowerCase() === parsed.targetSkill.toLowerCase());
|
|
1566
|
-
if (selectedSkills.length === 0) {
|
|
1567
|
-
console.log(chalk.yellow(`Skill "${parsed.targetSkill}" not found in repository.`));
|
|
1568
|
-
console.log(chalk.gray('Available skills:'));
|
|
1569
|
-
for (const skill of skills) {
|
|
1570
|
-
console.log(chalk.cyan(` ${skill.name}`));
|
|
1571
|
-
}
|
|
1572
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1573
|
-
return;
|
|
1574
|
-
}
|
|
1575
|
-
console.log(chalk.gray(`Auto-selected: ${parsed.targetSkill}`));
|
|
1576
|
-
}
|
|
1577
|
-
else if (options.skill && options.skill.length > 0) {
|
|
1578
|
-
selectedSkills = skills.filter(s => options.skill.some((name) => s.name.toLowerCase() === name.toLowerCase()));
|
|
1579
|
-
if (selectedSkills.length === 0) {
|
|
1580
|
-
console.log(chalk.yellow(`No matching skills found for: ${options.skill.join(', ')}`));
|
|
1581
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1582
|
-
return;
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
else if (!options.yes && skills.length > 1) {
|
|
1586
|
-
// Interactive selection
|
|
1587
|
-
const { selected } = await inquirer.prompt([{
|
|
1588
|
-
type: 'checkbox',
|
|
1589
|
-
name: 'selected',
|
|
1590
|
-
message: 'Select skills to install:',
|
|
1591
|
-
choices: skills.map(s => ({ name: `${s.name}${s.description ? ` - ${s.description.slice(0, 50)}` : ''}`, value: s, checked: true })),
|
|
1592
|
-
}]);
|
|
1593
|
-
selectedSkills = selected;
|
|
1594
|
-
}
|
|
1595
|
-
if (selectedSkills.length === 0) {
|
|
1596
|
-
console.log(chalk.yellow('No skills selected.'));
|
|
1597
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1598
|
-
return;
|
|
1599
|
-
}
|
|
1600
|
-
// Select agents
|
|
1601
|
-
let targetAgents = [];
|
|
1602
|
-
if (options.agent && options.agent.length > 0) {
|
|
1603
|
-
targetAgents = options.agent;
|
|
1604
|
-
}
|
|
1605
|
-
else if (options.yes) {
|
|
1606
|
-
targetAgents = Object.keys(AGENTS);
|
|
1607
|
-
}
|
|
1608
|
-
else {
|
|
1609
|
-
const { agents } = await inquirer.prompt([{
|
|
1610
|
-
type: 'checkbox',
|
|
1611
|
-
name: 'agents',
|
|
1612
|
-
message: 'Select agents to install to:',
|
|
1613
|
-
choices: Object.entries(AGENTS).map(([key, config]) => ({
|
|
1614
|
-
name: config.displayName,
|
|
1615
|
-
value: key,
|
|
1616
|
-
checked: ['cursor', 'claude', 'antigravity'].includes(key),
|
|
1617
|
-
})),
|
|
1618
|
-
}]);
|
|
1619
|
-
targetAgents = agents;
|
|
1620
|
-
}
|
|
1621
|
-
if (targetAgents.length === 0) {
|
|
1622
|
-
console.log(chalk.yellow('No agents selected.'));
|
|
1623
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1624
|
-
return;
|
|
1625
|
-
}
|
|
1626
|
-
const isGlobal = !!options.global;
|
|
1627
|
-
// Install skills
|
|
1628
|
-
console.log(chalk.bold('\nInstalling...\n'));
|
|
1629
|
-
for (const skill of selectedSkills) {
|
|
1630
|
-
for (const agent of targetAgents) {
|
|
1631
|
-
const agentConfig = AGENTS[agent];
|
|
1632
|
-
if (!agentConfig)
|
|
1633
|
-
continue;
|
|
1634
|
-
const targetDir = isGlobal ? agentConfig.globalDir : agentConfig.projectDir;
|
|
1635
|
-
const skillDir = isGlobal ? join(targetDir, skill.name) : join(process.cwd(), targetDir, skill.name);
|
|
1636
|
-
await mkdir(skillDir, { recursive: true });
|
|
1637
|
-
await cp(skill.path, skillDir, { recursive: true });
|
|
1638
|
-
console.log(chalk.green(`ā ${skill.name} ā ${agentConfig.displayName}`));
|
|
1639
|
-
console.log(chalk.gray(` ${isGlobal ? skillDir : targetDir + '/' + skill.name}`));
|
|
1640
|
-
}
|
|
1641
|
-
// Track in lock file
|
|
1642
|
-
const canonicalPath = getCanonicalPath(skill.name, { global: isGlobal, cwd: process.cwd() });
|
|
1643
|
-
const lockEntry = createLockEntry({
|
|
1644
|
-
name: skill.name,
|
|
1645
|
-
scopedName: skill.name, // From repo, no author info
|
|
1646
|
-
source: parsed.url,
|
|
1647
|
-
sourceType: parsed.type === 'github' ? 'github' : parsed.type === 'gitlab' ? 'gitlab' : 'local',
|
|
1648
|
-
agents: targetAgents,
|
|
1649
|
-
canonicalPath,
|
|
1650
|
-
isGlobal,
|
|
1651
|
-
projectDir: isGlobal ? undefined : process.cwd()
|
|
1652
|
-
});
|
|
1653
|
-
await addSkillToLock(lockEntry);
|
|
1654
|
-
}
|
|
1655
|
-
console.log(chalk.bold.green(`\n⨠Successfully installed ${selectedSkills.length} skill(s)\n`));
|
|
1656
|
-
// Cleanup
|
|
1657
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1658
|
-
}
|
|
1659
|
-
catch (error) {
|
|
1660
|
-
console.error(chalk.red('Error:'), error.message || error);
|
|
1661
|
-
process.exit(1);
|
|
1662
|
-
}
|
|
1663
|
-
});
|
|
1664
|
-
// Alias for backward compatibility
|
|
1665
|
-
program
|
|
1666
|
-
.command('market-install <name>')
|
|
1667
|
-
.alias('mi')
|
|
1668
|
-
.description('Install a skill (alias for: skills install)')
|
|
1669
|
-
.action(async (name) => {
|
|
1670
|
-
console.log(chalk.gray('Tip: Use `skills install <id-or-name>` directly\n'));
|
|
1671
|
-
const { execSync } = await import('child_process');
|
|
1672
|
-
try {
|
|
1673
|
-
execSync(`"${process.argv[0]}" "${process.argv[1]}" install "${name}"`, { stdio: 'inherit' });
|
|
1674
|
-
}
|
|
1675
|
-
catch { }
|
|
1676
|
-
});
|
|
1677
|
-
// Install from URL - install directly from GitHub or SkillsMP URL
|
|
1678
|
-
program
|
|
1679
|
-
.command('install-url <url>')
|
|
1680
|
-
.alias('iu')
|
|
1681
|
-
.description('Install a skill from GitHub URL or SkillsMP page URL')
|
|
1682
|
-
.action(async (url) => {
|
|
1683
|
-
try {
|
|
1684
|
-
let githubUrl = url;
|
|
1685
|
-
// Convert SkillsMP URL to GitHub URL
|
|
1686
|
-
// Format: https://skillsmp.com/skills/<id>
|
|
1687
|
-
if (url.includes('skillsmp.com/skills/')) {
|
|
1688
|
-
console.log(chalk.bold(`\nš¦ Fetching skill info from SkillsMP...`));
|
|
1689
|
-
// Extract skill ID from URL
|
|
1690
|
-
const skillId = url.split('/skills/').pop()?.replace(/\/$/, '');
|
|
1691
|
-
// Fetch skill details from API
|
|
1692
|
-
const response = await fetch(`https://skillsmp.com/api/skills/${skillId}`);
|
|
1693
|
-
if (!response.ok) {
|
|
1694
|
-
throw new Error('Could not find skill on SkillsMP');
|
|
1695
|
-
}
|
|
1696
|
-
const data = await response.json();
|
|
1697
|
-
githubUrl = data.skill.githubUrl;
|
|
1698
|
-
console.log(chalk.gray(`Found: ${data.skill.name} by ${data.skill.author}\n`));
|
|
1699
|
-
}
|
|
1700
|
-
// Validate GitHub URL
|
|
1701
|
-
if (!githubUrl.includes('github.com')) {
|
|
1702
|
-
console.log(chalk.red('Invalid URL. Please provide a GitHub URL or SkillsMP skill page URL.'));
|
|
1703
|
-
return;
|
|
1704
|
-
}
|
|
1705
|
-
console.log(chalk.gray(`Installing from: ${githubUrl}\n`));
|
|
1706
|
-
const homedir = (await import('os')).homedir();
|
|
1707
|
-
const skillsDir = `${homedir}/.antigravity/skills`;
|
|
1708
|
-
const installed = await installFromGitHubUrl(githubUrl, skillsDir);
|
|
1709
|
-
console.log(chalk.green(`ā Successfully installed: ${installed.name}`));
|
|
1710
|
-
console.log(chalk.gray(` Path: ${installed.path}`));
|
|
1711
|
-
console.log('');
|
|
1712
|
-
}
|
|
1713
|
-
catch (error) {
|
|
1714
|
-
console.error(chalk.red('Error installing skill:'), error.message || error);
|
|
1715
|
-
process.exit(1);
|
|
1716
|
-
}
|
|
1717
|
-
});
|
|
1718
|
-
// Market uninstall - remove an installed skill
|
|
1719
|
-
program
|
|
1720
|
-
.command('market-uninstall <name>')
|
|
1721
|
-
.alias('mu')
|
|
1722
|
-
.description('Uninstall a marketplace-installed skill')
|
|
1723
|
-
.action(async (name) => {
|
|
1724
|
-
try {
|
|
1725
|
-
await uninstallSkill(name);
|
|
1726
|
-
console.log(chalk.green(`ā Uninstalled: ${name}`));
|
|
1727
|
-
}
|
|
1728
|
-
catch (error) {
|
|
1729
|
-
console.error(chalk.red('Error uninstalling skill:'), error);
|
|
1730
|
-
process.exit(1);
|
|
1731
|
-
}
|
|
1732
|
-
});
|
|
1733
|
-
// Market installed - show installed marketplace skills
|
|
1734
|
-
program
|
|
1735
|
-
.command('market-installed')
|
|
1736
|
-
.alias('mind')
|
|
1737
|
-
.description('List skills installed from marketplaces')
|
|
1738
|
-
.action(async () => {
|
|
1739
|
-
try {
|
|
1740
|
-
const installed = await getInstalledSkills();
|
|
1741
|
-
if (installed.length === 0) {
|
|
1742
|
-
console.log(chalk.yellow('\nNo marketplace skills installed.'));
|
|
1743
|
-
console.log(chalk.gray('Use: skills market-install <name> to install\n'));
|
|
1744
|
-
return;
|
|
1745
|
-
}
|
|
1746
|
-
console.log(chalk.bold(`\nInstalled marketplace skills:\n`));
|
|
1747
|
-
for (const skill of installed) {
|
|
1748
|
-
console.log(chalk.cyan(` ${skill.name}`));
|
|
1749
|
-
console.log(chalk.gray(` Path: ${skill.localPath}`));
|
|
1750
|
-
if (skill.source) {
|
|
1751
|
-
console.log(chalk.gray(` Source: ${skill.source.name}`));
|
|
1752
|
-
}
|
|
1753
|
-
if (skill.version) {
|
|
1754
|
-
console.log(chalk.gray(` Version: ${skill.version}`));
|
|
1755
|
-
}
|
|
1756
|
-
console.log(chalk.gray(` Installed: ${skill.installedAt}`));
|
|
1757
|
-
console.log('');
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
catch (error) {
|
|
1761
|
-
console.error(chalk.red('Error listing installed skills:'), error);
|
|
1762
|
-
process.exit(1);
|
|
1763
|
-
}
|
|
1764
|
-
});
|
|
1765
|
-
// Market sources - list marketplace sources
|
|
1766
|
-
program
|
|
1767
|
-
.command('market-sources')
|
|
1768
|
-
.description('List registered marketplace sources')
|
|
1769
|
-
.action(async () => {
|
|
1770
|
-
try {
|
|
1771
|
-
// Show SkillsMP as primary
|
|
1772
|
-
console.log(chalk.bold('\nš Primary Marketplace:\n'));
|
|
1773
|
-
console.log(chalk.cyan(` SkillsMP`) + chalk.green(' ā'));
|
|
1774
|
-
console.log(chalk.gray(` URL: https://skillsmp.com`));
|
|
1775
|
-
console.log(chalk.gray(` Skills: 40,000+`));
|
|
1776
|
-
console.log(chalk.gray(` The largest Agent Skills marketplace`));
|
|
1777
|
-
console.log('');
|
|
1778
|
-
// Show legacy sources
|
|
1779
|
-
const sources = await listMarketplaces();
|
|
1780
|
-
if (sources.length > 0) {
|
|
1781
|
-
console.log(chalk.bold('Legacy GitHub Sources:\n'));
|
|
1782
|
-
for (const source of sources) {
|
|
1783
|
-
const verified = source.verified ? chalk.green(' ā') : '';
|
|
1784
|
-
console.log(chalk.cyan(` ${source.name}${verified}`));
|
|
1785
|
-
console.log(chalk.gray(` ID: ${source.id}`));
|
|
1786
|
-
console.log(chalk.gray(` Repo: ${source.owner}/${source.repo}`));
|
|
1787
|
-
if (source.description) {
|
|
1788
|
-
console.log(chalk.gray(` ${source.description}`));
|
|
1789
|
-
}
|
|
1790
|
-
console.log('');
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
catch (error) {
|
|
1795
|
-
console.error(chalk.red('Error listing sources:'), error);
|
|
1796
|
-
process.exit(1);
|
|
1797
|
-
}
|
|
1798
|
-
});
|
|
1799
|
-
// Market add-source - add a new marketplace
|
|
1800
|
-
program
|
|
1801
|
-
.command('market-add-source')
|
|
1802
|
-
.description('Add a custom marketplace source')
|
|
1803
|
-
.requiredOption('--id <id>', 'Unique identifier')
|
|
1804
|
-
.requiredOption('--name <name>', 'Display name')
|
|
1805
|
-
.requiredOption('--owner <owner>', 'GitHub owner')
|
|
1806
|
-
.requiredOption('--repo <repo>', 'GitHub repository')
|
|
1807
|
-
.option('--branch <branch>', 'Branch name', 'main')
|
|
1808
|
-
.option('--path <path>', 'Path to skills directory', 'skills')
|
|
1809
|
-
.action(async (options) => {
|
|
1810
|
-
try {
|
|
1811
|
-
await addMarketplace({
|
|
1812
|
-
id: options.id,
|
|
1813
|
-
name: options.name,
|
|
1814
|
-
owner: options.owner,
|
|
1815
|
-
repo: options.repo,
|
|
1816
|
-
branch: options.branch,
|
|
1817
|
-
skillsPath: options.path,
|
|
1818
|
-
verified: false
|
|
1819
|
-
});
|
|
1820
|
-
console.log(chalk.green(`ā Added marketplace: ${options.name}`));
|
|
1821
|
-
}
|
|
1822
|
-
catch (error) {
|
|
1823
|
-
console.error(chalk.red('Error adding marketplace:'), error);
|
|
1824
|
-
process.exit(1);
|
|
1825
|
-
}
|
|
1826
|
-
});
|
|
1827
|
-
// Market update-check - check for updates
|
|
1828
|
-
program
|
|
1829
|
-
.command('market-update-check')
|
|
1830
|
-
.alias('muc')
|
|
1831
|
-
.description('Check for updates to installed skills')
|
|
1832
|
-
.action(async () => {
|
|
1833
|
-
try {
|
|
1834
|
-
console.log(chalk.bold('\nChecking for updates...\n'));
|
|
1835
|
-
const updates = await checkUpdates();
|
|
1836
|
-
if (updates.length === 0) {
|
|
1837
|
-
console.log(chalk.yellow('No installed marketplace skills to check.'));
|
|
1838
|
-
return;
|
|
1839
|
-
}
|
|
1840
|
-
const hasUpdates = updates.filter(u => u.hasUpdate);
|
|
1841
|
-
if (hasUpdates.length === 0) {
|
|
1842
|
-
console.log(chalk.green('All skills are up to date! ā'));
|
|
1843
|
-
}
|
|
1844
|
-
else {
|
|
1845
|
-
console.log(chalk.yellow(`${hasUpdates.length} skill(s) have updates available:\n`));
|
|
1846
|
-
for (const update of hasUpdates) {
|
|
1847
|
-
console.log(chalk.cyan(` ${update.skill.name}`));
|
|
1848
|
-
console.log(chalk.gray(` Current: ${update.currentVersion || 'unknown'}`));
|
|
1849
|
-
console.log(chalk.green(` Latest: ${update.latestVersion}`));
|
|
1850
|
-
console.log('');
|
|
1851
|
-
}
|
|
1852
|
-
console.log(chalk.gray('To update, uninstall and reinstall the skill.'));
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
catch (error) {
|
|
1856
|
-
console.error(chalk.red('Error checking updates:'), error);
|
|
1857
|
-
process.exit(1);
|
|
1858
|
-
}
|
|
1859
|
-
});
|
|
1860
|
-
// ============================================
|
|
1861
|
-
// WORKFLOW SYNC COMMAND
|
|
1862
|
-
// ============================================
|
|
1863
|
-
// Sync - copy skills to .agent/workflows for Antigravity auto-discovery
|
|
1864
|
-
program
|
|
1865
|
-
.command('sync')
|
|
1866
|
-
.description('Sync skills to .agent/workflows/ for Antigravity auto-discovery')
|
|
1867
|
-
.option('-d, --directory <dir>', 'Target project directory', '.')
|
|
1868
|
-
.option('-a, --all', 'Sync all discovered skills')
|
|
1869
|
-
.option('-n, --name <name>', 'Sync a specific skill by name')
|
|
1870
|
-
.action(async (options) => {
|
|
1871
|
-
try {
|
|
1872
|
-
const { mkdir, writeFile, readFile, cp } = await import('fs/promises');
|
|
1873
|
-
const { join } = await import('path');
|
|
1874
|
-
const { existsSync } = await import('fs');
|
|
1875
|
-
const workflowsDir = join(options.directory, '.agent', 'workflows');
|
|
1876
|
-
await mkdir(workflowsDir, { recursive: true });
|
|
1877
|
-
const skills = await discoverSkills();
|
|
1878
|
-
if (skills.length === 0) {
|
|
1879
|
-
console.log(chalk.yellow('No skills found to sync.'));
|
|
1880
|
-
return;
|
|
1881
|
-
}
|
|
1882
|
-
// Filter skills if specific name provided
|
|
1883
|
-
const toSync = options.name
|
|
1884
|
-
? skills.filter(s => s.name === options.name)
|
|
1885
|
-
: options.all
|
|
1886
|
-
? skills
|
|
1887
|
-
: skills; // Default: sync all
|
|
1888
|
-
if (toSync.length === 0) {
|
|
1889
|
-
console.log(chalk.yellow(`Skill not found: ${options.name}`));
|
|
1890
|
-
return;
|
|
1891
|
-
}
|
|
1892
|
-
console.log(chalk.bold(`\nSyncing ${toSync.length} skill(s) to ${workflowsDir}...\n`));
|
|
1893
|
-
for (const skillRef of toSync) {
|
|
1894
|
-
try {
|
|
1895
|
-
const skill = await loadSkill(skillRef.path);
|
|
1896
|
-
if (!skill)
|
|
1897
|
-
continue;
|
|
1898
|
-
// Create workflow file from skill
|
|
1899
|
-
const workflowContent = `---
|
|
1900
|
-
description: ${skill.metadata.description.slice(0, 100)}
|
|
1901
|
-
---
|
|
1902
|
-
|
|
1903
|
-
${skill.body}
|
|
1904
|
-
`;
|
|
1905
|
-
const workflowPath = join(workflowsDir, `${skill.metadata.name}.md`);
|
|
1906
|
-
await writeFile(workflowPath, workflowContent);
|
|
1907
|
-
console.log(chalk.green(` ā ${skill.metadata.name}`));
|
|
1908
|
-
console.log(chalk.gray(` ā ${workflowPath}`));
|
|
1909
|
-
}
|
|
1910
|
-
catch (err) {
|
|
1911
|
-
console.log(chalk.red(` ā ${skillRef.name}: ${err}`));
|
|
1912
|
-
}
|
|
1913
|
-
}
|
|
1914
|
-
console.log(chalk.bold.green(`\nā Skills synced to .agent/workflows/`));
|
|
1915
|
-
console.log(chalk.gray(`\nNow you can use: "/${toSync.map(s => s.name).join('", "/')}"`));
|
|
1916
|
-
console.log(chalk.gray('Or just say: "Use the [skill-name] skill to..."'));
|
|
1917
|
-
}
|
|
1918
|
-
catch (error) {
|
|
1919
|
-
console.error(chalk.red('Error syncing skills:'), error);
|
|
1920
|
-
process.exit(1);
|
|
1921
|
-
}
|
|
1922
|
-
});
|
|
1923
|
-
// Export - convert skills to different AI agent formats
|
|
1924
|
-
program
|
|
1925
|
-
.command('export')
|
|
1926
|
-
.description('Export skills to different AI agent formats (Copilot, Cursor, Claude, Codex)')
|
|
1927
|
-
.option('-t, --target <agent>', 'Target agent: copilot, cursor, claude, codex, antigravity, all', 'all')
|
|
1928
|
-
.option('-d, --directory <dir>', 'Project directory', '.')
|
|
1929
|
-
.option('-n, --name <name>', 'Export specific skill only')
|
|
1930
|
-
.action(async (options) => {
|
|
1931
|
-
try {
|
|
1932
|
-
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
1933
|
-
const { join } = await import('path');
|
|
1934
|
-
const { existsSync } = await import('fs');
|
|
1935
|
-
const skills = await discoverSkills();
|
|
1936
|
-
const toExport = options.name
|
|
1937
|
-
? skills.filter(s => s.name === options.name)
|
|
1938
|
-
: skills;
|
|
1939
|
-
if (toExport.length === 0) {
|
|
1940
|
-
console.log(chalk.yellow('No skills found to export.'));
|
|
1941
|
-
return;
|
|
1942
|
-
}
|
|
1943
|
-
const targets = options.target === 'all'
|
|
1944
|
-
? ['copilot', 'cursor', 'claude', 'codex', 'antigravity']
|
|
1945
|
-
: [options.target];
|
|
1946
|
-
console.log(chalk.bold(`\nExporting ${toExport.length} skill(s) to: ${targets.join(', ')}\n`));
|
|
1947
|
-
for (const target of targets) {
|
|
1948
|
-
await exportToAgent(target, toExport, options.directory, { mkdir, writeFile, appendFile, join, existsSync });
|
|
1949
|
-
}
|
|
1950
|
-
console.log(chalk.bold.green('\nā Export complete!'));
|
|
1951
|
-
console.log(chalk.gray('\nGenerated files:'));
|
|
1952
|
-
if (targets.includes('copilot') || targets.includes('all')) {
|
|
1953
|
-
console.log(chalk.gray(' - .github/copilot-instructions.md'));
|
|
1954
|
-
}
|
|
1955
|
-
if (targets.includes('cursor') || targets.includes('all')) {
|
|
1956
|
-
console.log(chalk.gray(' - .cursor/rules/<skill>/RULE.md'));
|
|
1957
|
-
}
|
|
1958
|
-
if (targets.includes('claude') || targets.includes('all')) {
|
|
1959
|
-
console.log(chalk.gray(' - CLAUDE.md'));
|
|
1960
|
-
}
|
|
1961
|
-
if (targets.includes('codex') || targets.includes('all')) {
|
|
1962
|
-
console.log(chalk.gray(' - AGENTS.md'));
|
|
1963
|
-
}
|
|
1964
|
-
if (targets.includes('antigravity') || targets.includes('all')) {
|
|
1965
|
-
console.log(chalk.gray(' - .agent/workflows/<skill>.md'));
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
catch (error) {
|
|
1969
|
-
console.error(chalk.red('Error exporting skills:'), error);
|
|
1970
|
-
process.exit(1);
|
|
1971
|
-
}
|
|
1972
|
-
});
|
|
1973
|
-
async function exportToAgent(target, skillRefs, projectDir, fs) {
|
|
1974
|
-
const loadedSkills = [];
|
|
1975
|
-
for (const ref of skillRefs) {
|
|
1976
|
-
const skill = await loadSkill(ref.path);
|
|
1977
|
-
if (skill)
|
|
1978
|
-
loadedSkills.push(skill);
|
|
1979
|
-
}
|
|
1980
|
-
switch (target) {
|
|
1981
|
-
case 'copilot':
|
|
1982
|
-
await exportToCopilot(loadedSkills, projectDir, fs);
|
|
1983
|
-
break;
|
|
1984
|
-
case 'cursor':
|
|
1985
|
-
await exportToCursor(loadedSkills, projectDir, fs);
|
|
1986
|
-
break;
|
|
1987
|
-
case 'claude':
|
|
1988
|
-
await exportToClaude(loadedSkills, projectDir, fs);
|
|
1989
|
-
break;
|
|
1990
|
-
case 'codex':
|
|
1991
|
-
await exportToCodex(loadedSkills, projectDir, fs);
|
|
1992
|
-
break;
|
|
1993
|
-
case 'antigravity':
|
|
1994
|
-
await exportToAntigravity(loadedSkills, projectDir, fs);
|
|
1995
|
-
break;
|
|
1996
|
-
}
|
|
1997
|
-
}
|
|
1998
|
-
async function exportToCopilot(skills, projectDir, fs) {
|
|
1999
|
-
// GitHub Copilot now uses Agent Skills standard: .github/skills/<name>/SKILL.md
|
|
2000
|
-
// Also supports .claude/skills/ for compatibility
|
|
2001
|
-
const copilotDir = fs.join(projectDir, '.github', 'skills');
|
|
2002
|
-
await fs.mkdir(copilotDir, { recursive: true });
|
|
2003
|
-
for (const skill of skills) {
|
|
2004
|
-
const skillDir = fs.join(copilotDir, skill.metadata.name);
|
|
2005
|
-
await fs.mkdir(skillDir, { recursive: true });
|
|
2006
|
-
// Create SKILL.md in Agent Skills format
|
|
2007
|
-
const content = `---
|
|
2008
|
-
name: ${skill.metadata.name}
|
|
2009
|
-
description: ${skill.metadata.description}
|
|
2010
|
-
---
|
|
2011
|
-
|
|
2012
|
-
${skill.body}
|
|
2013
|
-
`;
|
|
2014
|
-
await fs.writeFile(fs.join(skillDir, 'SKILL.md'), content);
|
|
2015
|
-
}
|
|
2016
|
-
console.log(chalk.green(` ā GitHub Copilot: .github/skills/<skill>/SKILL.md`));
|
|
2017
|
-
}
|
|
2018
|
-
async function exportToCursor(skills, projectDir, fs) {
|
|
2019
|
-
// Cursor now uses Agent Skills standard: .cursor/skills/<name>/SKILL.md
|
|
2020
|
-
const cursorDir = fs.join(projectDir, '.cursor', 'skills');
|
|
2021
|
-
await fs.mkdir(cursorDir, { recursive: true });
|
|
2022
|
-
for (const skill of skills) {
|
|
2023
|
-
const skillDir = fs.join(cursorDir, skill.metadata.name);
|
|
2024
|
-
await fs.mkdir(skillDir, { recursive: true });
|
|
2025
|
-
// Create SKILL.md in Agent Skills format
|
|
2026
|
-
const content = `---
|
|
2027
|
-
name: ${skill.metadata.name}
|
|
2028
|
-
description: ${skill.metadata.description}
|
|
2029
|
-
---
|
|
2030
|
-
|
|
2031
|
-
${skill.body}
|
|
2032
|
-
`;
|
|
2033
|
-
await fs.writeFile(fs.join(skillDir, 'SKILL.md'), content);
|
|
2034
|
-
}
|
|
2035
|
-
console.log(chalk.green(` ā Cursor: .cursor/skills/<skill>/SKILL.md`));
|
|
2036
|
-
}
|
|
2037
|
-
async function exportToClaude(skills, projectDir, fs) {
|
|
2038
|
-
// Claude Code now uses Agent Skills standard: .claude/skills/<name>/SKILL.md
|
|
2039
|
-
const claudeDir = fs.join(projectDir, '.claude', 'skills');
|
|
2040
|
-
await fs.mkdir(claudeDir, { recursive: true });
|
|
2041
|
-
for (const skill of skills) {
|
|
2042
|
-
const skillDir = fs.join(claudeDir, skill.metadata.name);
|
|
2043
|
-
await fs.mkdir(skillDir, { recursive: true });
|
|
2044
|
-
// Create SKILL.md in Agent Skills format
|
|
2045
|
-
const content = `---
|
|
2046
|
-
name: ${skill.metadata.name}
|
|
2047
|
-
description: ${skill.metadata.description}
|
|
2048
|
-
---
|
|
2049
|
-
|
|
2050
|
-
${skill.body}
|
|
2051
|
-
`;
|
|
2052
|
-
await fs.writeFile(fs.join(skillDir, 'SKILL.md'), content);
|
|
2053
|
-
}
|
|
2054
|
-
console.log(chalk.green(` ā Claude Code: .claude/skills/<skill>/SKILL.md`));
|
|
2055
|
-
}
|
|
2056
|
-
async function exportToCodex(skills, projectDir, fs) {
|
|
2057
|
-
// OpenAI Codex uses Agent Skills standard: .codex/skills/<name>/SKILL.md
|
|
2058
|
-
const codexDir = fs.join(projectDir, '.codex', 'skills');
|
|
2059
|
-
await fs.mkdir(codexDir, { recursive: true });
|
|
2060
|
-
for (const skill of skills) {
|
|
2061
|
-
const skillDir = fs.join(codexDir, skill.metadata.name);
|
|
2062
|
-
await fs.mkdir(skillDir, { recursive: true });
|
|
2063
|
-
// Create SKILL.md in Agent Skills format
|
|
2064
|
-
const content = `---
|
|
2065
|
-
name: ${skill.metadata.name}
|
|
2066
|
-
description: ${skill.metadata.description}
|
|
2067
|
-
---
|
|
2068
|
-
|
|
2069
|
-
${skill.body}
|
|
2070
|
-
`;
|
|
2071
|
-
await fs.writeFile(fs.join(skillDir, 'SKILL.md'), content);
|
|
2072
|
-
}
|
|
2073
|
-
console.log(chalk.green(` ā OpenAI Codex: .codex/skills/<skill>/SKILL.md`));
|
|
2074
|
-
}
|
|
2075
|
-
async function exportToAntigravity(skills, projectDir, fs) {
|
|
2076
|
-
const workflowsDir = fs.join(projectDir, '.agent', 'workflows');
|
|
2077
|
-
await fs.mkdir(workflowsDir, { recursive: true });
|
|
2078
|
-
for (const skill of skills) {
|
|
2079
|
-
const content = `---
|
|
2080
|
-
description: ${skill.metadata.description.slice(0, 100)}
|
|
2081
|
-
---
|
|
2082
|
-
|
|
2083
|
-
${skill.body}
|
|
2084
|
-
`;
|
|
2085
|
-
await fs.writeFile(fs.join(workflowsDir, `${skill.metadata.name}.md`), content);
|
|
2086
|
-
}
|
|
2087
|
-
console.log(chalk.green(` ā Antigravity: .agent/workflows/<skill>.md`));
|
|
2088
|
-
}
|
|
2089
|
-
// ============================================
|
|
2090
|
-
// INTERACTIVE COMMANDS
|
|
2091
|
-
// ============================================
|
|
2092
|
-
// Interactive install wizard - select skills with arrow keys
|
|
2093
|
-
program
|
|
2094
|
-
.command('install-wizard')
|
|
2095
|
-
.alias('iw')
|
|
2096
|
-
.description('Interactive skill installation wizard (legacy)')
|
|
2097
|
-
.action(async () => {
|
|
2098
|
-
try {
|
|
2099
|
-
const spinner = ora('Fetching skills from marketplaces...').start();
|
|
2100
|
-
const skills = await listMarketplaceSkills();
|
|
2101
|
-
spinner.stop();
|
|
2102
|
-
if (skills.length === 0) {
|
|
2103
|
-
console.log(chalk.yellow('No skills found in marketplaces.'));
|
|
2104
|
-
return;
|
|
2105
|
-
}
|
|
2106
|
-
const choices = skills.map(skill => ({
|
|
2107
|
-
name: `${skill.name} - ${skill.description?.slice(0, 50) || 'No description'}...`,
|
|
2108
|
-
value: skill.name,
|
|
2109
|
-
short: skill.name
|
|
2110
|
-
}));
|
|
2111
|
-
const { selectedSkills } = await inquirer.prompt([
|
|
2112
|
-
{
|
|
2113
|
-
type: 'checkbox',
|
|
2114
|
-
name: 'selectedSkills',
|
|
2115
|
-
message: 'Select skills to install (Space to select, Enter to confirm):',
|
|
2116
|
-
choices,
|
|
2117
|
-
pageSize: 15
|
|
2118
|
-
}
|
|
2119
|
-
]);
|
|
2120
|
-
if (selectedSkills.length === 0) {
|
|
2121
|
-
console.log(chalk.yellow('No skills selected.'));
|
|
2122
|
-
return;
|
|
2123
|
-
}
|
|
2124
|
-
for (const skillName of selectedSkills) {
|
|
2125
|
-
const installSpinner = ora(`Installing ${skillName}...`).start();
|
|
2126
|
-
try {
|
|
2127
|
-
const result = await installSkill(skillName);
|
|
2128
|
-
installSpinner.succeed(`Installed: ${skillName}`);
|
|
2129
|
-
}
|
|
2130
|
-
catch (err) {
|
|
2131
|
-
installSpinner.fail(`Failed to install ${skillName}: ${err}`);
|
|
2132
|
-
}
|
|
2133
|
-
}
|
|
2134
|
-
console.log(chalk.bold.green('\nā Installation complete!'));
|
|
2135
|
-
console.log(chalk.gray('Run "skills export" to export to your AI agent.'));
|
|
2136
|
-
}
|
|
2137
|
-
catch (error) {
|
|
2138
|
-
console.error(chalk.red('Error:'), error);
|
|
2139
|
-
process.exit(1);
|
|
2140
|
-
}
|
|
2141
|
-
});
|
|
2142
|
-
// Interactive export - select target agents
|
|
2143
|
-
program
|
|
2144
|
-
.command('export-interactive')
|
|
2145
|
-
.alias('ei')
|
|
2146
|
-
.description('Interactive export with agent selection menu')
|
|
2147
|
-
.action(async () => {
|
|
2148
|
-
try {
|
|
2149
|
-
const skills = await discoverSkills();
|
|
2150
|
-
if (skills.length === 0) {
|
|
2151
|
-
console.log(chalk.yellow('No skills found to export.'));
|
|
2152
|
-
return;
|
|
2153
|
-
}
|
|
2154
|
-
const { agents } = await inquirer.prompt([
|
|
2155
|
-
{
|
|
2156
|
-
type: 'checkbox',
|
|
2157
|
-
name: 'agents',
|
|
2158
|
-
message: 'Select target AI agents:',
|
|
2159
|
-
choices: [
|
|
2160
|
-
{ name: 'GitHub Copilot (.github/skills/)', value: 'copilot', checked: true },
|
|
2161
|
-
{ name: 'Cursor (.cursor/skills/)', value: 'cursor', checked: true },
|
|
2162
|
-
{ name: 'Claude Code (.claude/skills/)', value: 'claude', checked: true },
|
|
2163
|
-
{ name: 'OpenAI Codex (.codex/skills/)', value: 'codex', checked: true },
|
|
2164
|
-
{ name: 'Antigravity (.agent/workflows/)', value: 'antigravity', checked: true }
|
|
2165
|
-
]
|
|
2166
|
-
}
|
|
2167
|
-
]);
|
|
2168
|
-
if (agents.length === 0) {
|
|
2169
|
-
console.log(chalk.yellow('No agents selected.'));
|
|
2170
|
-
return;
|
|
2171
|
-
}
|
|
2172
|
-
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
2173
|
-
const { join } = await import('path');
|
|
2174
|
-
const { existsSync } = await import('fs');
|
|
2175
|
-
console.log(chalk.bold(`\nExporting ${skills.length} skill(s) to: ${agents.join(', ')}\n`));
|
|
2176
|
-
for (const target of agents) {
|
|
2177
|
-
const spinner = ora(`Exporting to ${target}...`).start();
|
|
2178
|
-
await exportToAgent(target, skills, '.', { mkdir, writeFile, appendFile, join, existsSync });
|
|
2179
|
-
spinner.succeed();
|
|
2180
|
-
}
|
|
2181
|
-
console.log(chalk.bold.green('\nā Export complete!'));
|
|
2182
|
-
}
|
|
2183
|
-
catch (error) {
|
|
2184
|
-
console.error(chalk.red('Error:'), error);
|
|
2185
|
-
process.exit(1);
|
|
2186
|
-
}
|
|
2187
|
-
});
|
|
2188
|
-
// Quick setup wizard
|
|
2189
|
-
program
|
|
2190
|
-
.command('setup')
|
|
2191
|
-
.description('Interactive setup wizard - install skills and export to your agents')
|
|
2192
|
-
.action(async () => {
|
|
2193
|
-
console.log(chalk.bold.cyan('\nš Agent Skills Setup Wizard\n'));
|
|
2194
|
-
// Step 1: Choose what to do
|
|
2195
|
-
const { action } = await inquirer.prompt([
|
|
2196
|
-
{
|
|
2197
|
-
type: 'list',
|
|
2198
|
-
name: 'action',
|
|
2199
|
-
message: 'What would you like to do?',
|
|
2200
|
-
choices: [
|
|
2201
|
-
{ name: 'š¦ Install skills from marketplace', value: 'install' },
|
|
2202
|
-
{ name: 'š¤ Export installed skills to AI agents', value: 'export' },
|
|
2203
|
-
{ name: 'š Both - Install and export', value: 'both' }
|
|
2204
|
-
]
|
|
2205
|
-
}
|
|
2206
|
-
]);
|
|
2207
|
-
if (action === 'install' || action === 'both') {
|
|
2208
|
-
const spinner = ora('Fetching skills from marketplaces...').start();
|
|
2209
|
-
const skills = await listMarketplaceSkills();
|
|
2210
|
-
spinner.stop();
|
|
2211
|
-
if (skills.length > 0) {
|
|
2212
|
-
const choices = skills.slice(0, 20).map(skill => ({
|
|
2213
|
-
name: `${skill.name} - ${skill.description?.slice(0, 40) || ''}...`,
|
|
2214
|
-
value: skill.name
|
|
2215
|
-
}));
|
|
2216
|
-
const { selectedSkills } = await inquirer.prompt([
|
|
2217
|
-
{
|
|
2218
|
-
type: 'checkbox',
|
|
2219
|
-
name: 'selectedSkills',
|
|
2220
|
-
message: 'Select skills to install:',
|
|
2221
|
-
choices,
|
|
2222
|
-
pageSize: 10
|
|
2223
|
-
}
|
|
2224
|
-
]);
|
|
2225
|
-
for (const skillName of selectedSkills) {
|
|
2226
|
-
const installSpinner = ora(`Installing ${skillName}...`).start();
|
|
2227
|
-
try {
|
|
2228
|
-
await installSkill(skillName);
|
|
2229
|
-
installSpinner.succeed(`Installed: ${skillName}`);
|
|
2230
|
-
}
|
|
2231
|
-
catch (err) {
|
|
2232
|
-
installSpinner.fail(`Failed: ${skillName}`);
|
|
2233
|
-
}
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
if (action === 'export' || action === 'both') {
|
|
2238
|
-
const { agents } = await inquirer.prompt([
|
|
2239
|
-
{
|
|
2240
|
-
type: 'checkbox',
|
|
2241
|
-
name: 'agents',
|
|
2242
|
-
message: 'Which AI agents do you use?',
|
|
2243
|
-
choices: [
|
|
2244
|
-
{ name: 'Cursor', value: 'cursor', checked: true },
|
|
2245
|
-
{ name: 'Claude Code', value: 'claude', checked: true },
|
|
2246
|
-
{ name: 'GitHub Copilot', value: 'copilot', checked: true },
|
|
2247
|
-
{ name: 'OpenAI Codex', value: 'codex', checked: false }
|
|
2248
|
-
]
|
|
2249
|
-
}
|
|
2250
|
-
]);
|
|
2251
|
-
const skills = await discoverSkills();
|
|
2252
|
-
const { mkdir, writeFile, appendFile } = await import('fs/promises');
|
|
2253
|
-
const { join } = await import('path');
|
|
2254
|
-
const { existsSync } = await import('fs');
|
|
2255
|
-
for (const target of agents) {
|
|
2256
|
-
const spinner = ora(`Exporting to ${target}...`).start();
|
|
2257
|
-
await exportToAgent(target, skills, '.', { mkdir, writeFile, appendFile, join, existsSync });
|
|
2258
|
-
spinner.succeed();
|
|
2259
|
-
}
|
|
2260
|
-
}
|
|
2261
|
-
console.log(chalk.bold.green('\n⨠Setup complete!'));
|
|
2262
|
-
console.log(chalk.gray('Your skills are now ready to use in your AI agents.\n'));
|
|
2263
|
-
});
|
|
2264
|
-
// ============================================
|
|
2265
|
-
// PHASE 1: LEVERAGE EXISTING CODE
|
|
2266
|
-
// ============================================
|
|
2267
|
-
// Run - Execute a skill's script
|
|
2268
|
-
program
|
|
2269
|
-
.command('run <skill-name> <script>')
|
|
2270
|
-
.description('Execute a script from an installed skill')
|
|
2271
|
-
.option('-a, --args <args...>', 'Arguments to pass to the script')
|
|
2272
|
-
.option('--timeout <ms>', 'Timeout in milliseconds', '30000')
|
|
2273
|
-
.action(async (skillName, script, options) => {
|
|
2274
|
-
try {
|
|
2275
|
-
const { executeScript, listScripts } = await import('../core/executor.js');
|
|
2276
|
-
const { homedir } = await import('os');
|
|
2277
|
-
const { join } = await import('path');
|
|
2278
|
-
const { existsSync } = await import('fs');
|
|
2279
|
-
// Find the skill path
|
|
2280
|
-
const skillsDir = join(homedir(), '.antigravity', 'skills');
|
|
2281
|
-
const skillPath = join(skillsDir, skillName);
|
|
2282
|
-
if (!existsSync(skillPath)) {
|
|
2283
|
-
console.error(chalk.red(`Skill not found: ${skillName}`));
|
|
2284
|
-
console.log(chalk.gray(`Expected at: ${skillPath}`));
|
|
2285
|
-
console.log(chalk.gray('\nInstall with: skills install <skill-name>'));
|
|
2286
|
-
process.exit(1);
|
|
2287
|
-
}
|
|
2288
|
-
// List available scripts if asked
|
|
2289
|
-
const scripts = await listScripts(skillPath);
|
|
2290
|
-
if (scripts.length === 0) {
|
|
2291
|
-
console.log(chalk.yellow(`No scripts found in ${skillName}`));
|
|
2292
|
-
console.log(chalk.gray('Skills can have scripts in the scripts/ directory.'));
|
|
2293
|
-
return;
|
|
2294
|
-
}
|
|
2295
|
-
if (!scripts.includes(script)) {
|
|
2296
|
-
console.log(chalk.red(`Script not found: ${script}`));
|
|
2297
|
-
console.log(chalk.cyan('\nAvailable scripts:'));
|
|
2298
|
-
scripts.forEach(s => console.log(chalk.gray(` - ${s}`)));
|
|
2299
|
-
return;
|
|
2300
|
-
}
|
|
2301
|
-
const spinner = ora(`Running ${script}...`).start();
|
|
2302
|
-
const result = await executeScript(skillPath, script, options.args || [], { timeout: parseInt(options.timeout) });
|
|
2303
|
-
if (result.success) {
|
|
2304
|
-
spinner.succeed(`Completed in ${result.executionTime}ms`);
|
|
2305
|
-
if (result.stdout) {
|
|
2306
|
-
console.log(chalk.gray('\nOutput:'));
|
|
2307
|
-
console.log(result.stdout);
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
else {
|
|
2311
|
-
spinner.fail(`Failed (exit code: ${result.exitCode})`);
|
|
2312
|
-
if (result.stderr) {
|
|
2313
|
-
console.error(chalk.red(result.stderr));
|
|
2314
|
-
}
|
|
2315
|
-
process.exit(1);
|
|
2316
|
-
}
|
|
2317
|
-
}
|
|
2318
|
-
catch (error) {
|
|
2319
|
-
console.error(chalk.red('Error running script:'), error);
|
|
2320
|
-
process.exit(1);
|
|
2321
|
-
}
|
|
2322
|
-
});
|
|
2323
|
-
// Context - Generate LLM system prompt context
|
|
2324
|
-
program
|
|
2325
|
-
.command('context')
|
|
2326
|
-
.description('Generate system prompt context for AI agents')
|
|
2327
|
-
.option('-f, --format <format>', 'Output format: xml, json, markdown', 'xml')
|
|
2328
|
-
.option('-s, --skills <skills...>', 'Specific skills to include (default: all installed)')
|
|
2329
|
-
.option('-o, --output <file>', 'Write to file instead of stdout')
|
|
2330
|
-
.action(async (options) => {
|
|
2331
|
-
try {
|
|
2332
|
-
const { generateSkillsPromptXML, generateFullSkillsContext } = await import('../core/injector.js');
|
|
2333
|
-
const { discoverSkills } = await import('../core/loader.js');
|
|
2334
|
-
const allSkills = await discoverSkills();
|
|
2335
|
-
// Filter if specific skills requested
|
|
2336
|
-
let skills = allSkills;
|
|
2337
|
-
if (options.skills && options.skills.length > 0) {
|
|
2338
|
-
skills = allSkills.filter(s => options.skills.some((name) => s.name.toLowerCase().includes(name.toLowerCase())));
|
|
2339
|
-
}
|
|
2340
|
-
if (skills.length === 0) {
|
|
2341
|
-
console.error(chalk.yellow('No skills found.'));
|
|
2342
|
-
console.log(chalk.gray('Install skills with: skills install <name>'));
|
|
2343
|
-
return;
|
|
2344
|
-
}
|
|
2345
|
-
let output = '';
|
|
2346
|
-
if (options.format === 'xml') {
|
|
2347
|
-
const result = generateSkillsPromptXML(skills);
|
|
2348
|
-
output = result.xml;
|
|
2349
|
-
if (!options.output) {
|
|
2350
|
-
console.log(chalk.gray(`\n# ${result.skillCount} skills, ~${result.estimatedTokens} tokens\n`));
|
|
2351
|
-
}
|
|
2352
|
-
}
|
|
2353
|
-
else if (options.format === 'json') {
|
|
2354
|
-
output = JSON.stringify({
|
|
2355
|
-
skills: skills.map(s => ({
|
|
2356
|
-
name: s.name,
|
|
2357
|
-
description: s.description,
|
|
2358
|
-
path: s.path
|
|
2359
|
-
})),
|
|
2360
|
-
count: skills.length
|
|
2361
|
-
}, null, 2);
|
|
2362
|
-
}
|
|
2363
|
-
else if (options.format === 'markdown') {
|
|
2364
|
-
output = generateFullSkillsContext(skills);
|
|
2365
|
-
}
|
|
2366
|
-
if (options.output) {
|
|
2367
|
-
const { writeFile } = await import('fs/promises');
|
|
2368
|
-
await writeFile(options.output, output);
|
|
2369
|
-
console.log(chalk.green(`ā Written to ${options.output}`));
|
|
2370
|
-
}
|
|
2371
|
-
else {
|
|
2372
|
-
console.log(output);
|
|
2373
|
-
}
|
|
2374
|
-
}
|
|
2375
|
-
catch (error) {
|
|
2376
|
-
console.error(chalk.red('Error generating context:'), error);
|
|
2377
|
-
process.exit(1);
|
|
2378
|
-
}
|
|
2379
|
-
});
|
|
2380
|
-
// Preview - Open skill in browser
|
|
2381
|
-
program
|
|
2382
|
-
.command('preview <skill-name>')
|
|
2383
|
-
.description('Open skill detail page in browser')
|
|
2384
|
-
.option('--url-only', 'Just print the URL without opening')
|
|
2385
|
-
.action(async (skillName, options) => {
|
|
2386
|
-
try {
|
|
2387
|
-
// Parse scoped name
|
|
2388
|
-
const clean = skillName.replace(/^@/, '');
|
|
2389
|
-
const url = `https://skills.karanjot.dev/marketplace/${clean}`;
|
|
2390
|
-
if (options.urlOnly) {
|
|
2391
|
-
console.log(url);
|
|
2392
|
-
}
|
|
2393
|
-
else {
|
|
2394
|
-
const { exec } = await import('child_process');
|
|
2395
|
-
const { promisify } = await import('util');
|
|
2396
|
-
const execAsync = promisify(exec);
|
|
2397
|
-
// Cross-platform open
|
|
2398
|
-
const cmd = process.platform === 'darwin' ? 'open' :
|
|
2399
|
-
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
2400
|
-
await execAsync(`${cmd} "${url}"`);
|
|
2401
|
-
console.log(chalk.green(`ā Opened: ${url}`));
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
catch (error) {
|
|
2405
|
-
console.error(chalk.red('Error opening preview:'), error);
|
|
2406
|
-
process.exit(1);
|
|
2407
|
-
}
|
|
2408
|
-
});
|
|
2409
|
-
// Scripts - List scripts in an installed skill
|
|
2410
|
-
program
|
|
2411
|
-
.command('scripts <skill-name>')
|
|
2412
|
-
.description('List available scripts in an installed skill')
|
|
2413
|
-
.action(async (skillName) => {
|
|
2414
|
-
try {
|
|
2415
|
-
const { listScripts, isScriptSafe } = await import('../core/executor.js');
|
|
2416
|
-
const { homedir } = await import('os');
|
|
2417
|
-
const { join } = await import('path');
|
|
2418
|
-
const { existsSync } = await import('fs');
|
|
2419
|
-
const { readFile } = await import('fs/promises');
|
|
2420
|
-
const skillsDir = join(homedir(), '.antigravity', 'skills');
|
|
2421
|
-
const skillPath = join(skillsDir, skillName);
|
|
2422
|
-
if (!existsSync(skillPath)) {
|
|
2423
|
-
console.error(chalk.red(`Skill not found: ${skillName}`));
|
|
2424
|
-
return;
|
|
2425
|
-
}
|
|
2426
|
-
const scripts = await listScripts(skillPath);
|
|
2427
|
-
if (scripts.length === 0) {
|
|
2428
|
-
console.log(chalk.yellow('No scripts found in this skill.'));
|
|
2429
|
-
return;
|
|
2430
|
-
}
|
|
2431
|
-
console.log(chalk.bold(`\nš Scripts in ${skillName}:\n`));
|
|
2432
|
-
for (const script of scripts) {
|
|
2433
|
-
const scriptPath = join(skillPath, 'scripts', script);
|
|
2434
|
-
try {
|
|
2435
|
-
const content = await readFile(scriptPath, 'utf-8');
|
|
2436
|
-
const safety = isScriptSafe(content);
|
|
2437
|
-
const safetyIcon = safety.safe ? chalk.green('ā') : chalk.yellow('ā ');
|
|
2438
|
-
console.log(` ${safetyIcon} ${chalk.cyan(script)}`);
|
|
2439
|
-
if (!safety.safe) {
|
|
2440
|
-
safety.warnings.forEach(w => console.log(chalk.gray(` Warning: ${w}`)));
|
|
2441
|
-
}
|
|
2442
|
-
}
|
|
2443
|
-
catch {
|
|
2444
|
-
console.log(` ${chalk.gray('?')} ${script}`);
|
|
2445
|
-
}
|
|
2446
|
-
}
|
|
2447
|
-
console.log(chalk.gray(`\nRun with: skills run ${skillName} <script>\n`));
|
|
2448
|
-
}
|
|
2449
|
-
catch (error) {
|
|
2450
|
-
console.error(chalk.red('Error listing scripts:'), error);
|
|
2451
|
-
process.exit(1);
|
|
2452
|
-
}
|
|
2453
|
-
});
|
|
2454
|
-
// Shell completions
|
|
2455
|
-
program
|
|
2456
|
-
.command('completion <shell>')
|
|
2457
|
-
.description('Generate shell completion script (bash, zsh, fish)')
|
|
2458
|
-
.action((shell) => {
|
|
2459
|
-
const commands = [
|
|
2460
|
-
'list', 'validate', 'show', 'prompt', 'init', 'assets',
|
|
2461
|
-
'install', 'uninstall', 'search', 'run', 'context',
|
|
2462
|
-
'preview', 'scripts', 'market-list', 'market-search',
|
|
2463
|
-
'market-install', 'market-uninstall', 'market-installed',
|
|
2464
|
-
'market-sources', 'setup', 'completion'
|
|
2465
|
-
];
|
|
2466
|
-
if (shell === 'bash') {
|
|
2467
|
-
console.log(`# Bash completion for skills CLI
|
|
2468
|
-
# Add to ~/.bashrc: eval "$(skills completion bash)"
|
|
2469
|
-
|
|
2470
|
-
_skills_completions() {
|
|
2471
|
-
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
2472
|
-
local commands="${commands.join(' ')}"
|
|
2473
|
-
|
|
2474
|
-
if [ \${COMP_CWORD} -eq 1 ]; then
|
|
2475
|
-
COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
|
|
2476
|
-
fi
|
|
2477
|
-
}
|
|
2478
|
-
|
|
2479
|
-
complete -F _skills_completions skills`);
|
|
2480
|
-
}
|
|
2481
|
-
else if (shell === 'zsh') {
|
|
2482
|
-
console.log(`# Zsh completion for skills CLI
|
|
2483
|
-
# Add to ~/.zshrc: eval "$(skills completion zsh)"
|
|
2484
|
-
|
|
2485
|
-
_skills() {
|
|
2486
|
-
local commands=(
|
|
2487
|
-
${commands.map(c => `'${c}:${c} command'`).join('\n ')}
|
|
2488
|
-
)
|
|
2489
|
-
|
|
2490
|
-
_arguments '1: :->command' && return
|
|
2491
|
-
|
|
2492
|
-
case $state in
|
|
2493
|
-
command)
|
|
2494
|
-
_describe 'command' commands
|
|
2495
|
-
;;
|
|
2496
|
-
esac
|
|
2497
|
-
}
|
|
2498
|
-
|
|
2499
|
-
compdef _skills skills`);
|
|
2500
|
-
}
|
|
2501
|
-
else if (shell === 'fish') {
|
|
2502
|
-
console.log(`# Fish completion for skills CLI
|
|
2503
|
-
# Save to ~/.config/fish/completions/skills.fish
|
|
2504
|
-
|
|
2505
|
-
${commands.map(c => `complete -c skills -f -n "__fish_use_subcommand" -a "${c}" -d "${c} command"`).join('\n')}`);
|
|
2506
|
-
}
|
|
2507
|
-
else {
|
|
2508
|
-
console.error(chalk.red(`Unknown shell: ${shell}`));
|
|
2509
|
-
console.log(chalk.gray('Supported: bash, zsh, fish'));
|
|
2510
|
-
process.exit(1);
|
|
2511
|
-
}
|
|
2512
|
-
});
|
|
2513
|
-
// Info - Show installation status and paths
|
|
2514
|
-
program
|
|
2515
|
-
.command('info')
|
|
2516
|
-
.description('Show skills installation status and paths')
|
|
2517
|
-
.action(async () => {
|
|
2518
|
-
try {
|
|
2519
|
-
const { homedir } = await import('os');
|
|
2520
|
-
const { join } = await import('path');
|
|
2521
|
-
const { existsSync } = await import('fs');
|
|
2522
|
-
const { readdir } = await import('fs/promises');
|
|
2523
|
-
console.log(chalk.bold('\nš¦ Skills CLI Info\n'));
|
|
2524
|
-
// Installation paths
|
|
2525
|
-
const paths = [
|
|
2526
|
-
{ name: 'Global skills', path: join(homedir(), '.antigravity', 'skills') },
|
|
2527
|
-
{ name: 'Project skills', path: join(process.cwd(), '.antigravity', 'skills') },
|
|
2528
|
-
{ name: 'Legacy skills', path: join(process.cwd(), 'skills') },
|
|
2529
|
-
{ name: 'Config', path: join(homedir(), '.antigravity', 'marketplace.json') }
|
|
2530
|
-
];
|
|
2531
|
-
console.log(chalk.cyan('š Paths:'));
|
|
2532
|
-
for (const { name, path } of paths) {
|
|
2533
|
-
const exists = existsSync(path);
|
|
2534
|
-
const icon = exists ? chalk.green('ā') : chalk.gray('ā');
|
|
2535
|
-
console.log(` ${icon} ${name}: ${chalk.gray(path)}`);
|
|
2536
|
-
}
|
|
2537
|
-
// Count installed skills
|
|
2538
|
-
const skillsDir = join(homedir(), '.antigravity', 'skills');
|
|
2539
|
-
let skillCount = 0;
|
|
2540
|
-
if (existsSync(skillsDir)) {
|
|
2541
|
-
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
2542
|
-
skillCount = entries.filter(e => e.isDirectory()).length;
|
|
2543
|
-
}
|
|
2544
|
-
console.log(chalk.cyan('\nš Stats:'));
|
|
2545
|
-
console.log(` Installed skills: ${chalk.bold(skillCount.toString())}`);
|
|
2546
|
-
console.log(` Platform: ${chalk.gray(process.platform)}`);
|
|
2547
|
-
console.log(` Node: ${chalk.gray(process.version)}`);
|
|
2548
|
-
// Show agent directories
|
|
2549
|
-
console.log(chalk.cyan('\nš¤ Agent Directories:'));
|
|
2550
|
-
const agents = [
|
|
2551
|
-
{ name: 'Cursor', path: '.cursor/skills' },
|
|
2552
|
-
{ name: 'Claude', path: '.claude/skills' },
|
|
2553
|
-
{ name: 'Copilot', path: '.github/skills' },
|
|
2554
|
-
{ name: 'Codex', path: '.codex/skills' },
|
|
2555
|
-
{ name: 'Antigravity', path: '.agent/workflows' }
|
|
2556
|
-
];
|
|
2557
|
-
for (const { name, path } of agents) {
|
|
2558
|
-
const fullPath = join(process.cwd(), path);
|
|
2559
|
-
const exists = existsSync(fullPath);
|
|
2560
|
-
const icon = exists ? chalk.green('ā') : chalk.gray('ā');
|
|
2561
|
-
console.log(` ${icon} ${name}: ${chalk.gray(path)}`);
|
|
2562
|
-
}
|
|
2563
|
-
console.log('');
|
|
2564
|
-
}
|
|
2565
|
-
catch (error) {
|
|
2566
|
-
console.error(chalk.red('Error:'), error);
|
|
2567
|
-
process.exit(1);
|
|
2568
|
-
}
|
|
2569
|
-
});
|
|
2570
|
-
// (Old update command removed - see new lock file-based update command below)
|
|
2571
|
-
// Doctor - Diagnose and fix common issues
|
|
2572
|
-
program
|
|
2573
|
-
.command('doctor')
|
|
2574
|
-
.description('Diagnose and fix common skills installation issues')
|
|
2575
|
-
.option('--fix', 'Attempt to fix issues automatically')
|
|
2576
|
-
.action(async (options) => {
|
|
2577
|
-
try {
|
|
2578
|
-
const { homedir } = await import('os');
|
|
2579
|
-
const { join } = await import('path');
|
|
2580
|
-
const { existsSync } = await import('fs');
|
|
2581
|
-
const { mkdir, readdir } = await import('fs/promises');
|
|
2582
|
-
console.log(chalk.bold('\n𩺠Skills Doctor\n'));
|
|
2583
|
-
const checks = [];
|
|
2584
|
-
// Check 1: Skills directory exists
|
|
2585
|
-
const skillsDir = join(homedir(), '.antigravity', 'skills');
|
|
2586
|
-
if (existsSync(skillsDir)) {
|
|
2587
|
-
checks.push({ name: 'Skills directory', status: 'pass', message: skillsDir });
|
|
2588
|
-
}
|
|
2589
|
-
else {
|
|
2590
|
-
checks.push({
|
|
2591
|
-
name: 'Skills directory',
|
|
2592
|
-
status: 'warn',
|
|
2593
|
-
message: 'Not created yet',
|
|
2594
|
-
fix: async () => {
|
|
2595
|
-
await mkdir(skillsDir, { recursive: true });
|
|
2596
|
-
}
|
|
2597
|
-
});
|
|
2598
|
-
}
|
|
2599
|
-
// Check 2: Config directory
|
|
2600
|
-
const configDir = join(homedir(), '.antigravity');
|
|
2601
|
-
if (existsSync(configDir)) {
|
|
2602
|
-
checks.push({ name: 'Config directory', status: 'pass', message: configDir });
|
|
2603
|
-
}
|
|
2604
|
-
else {
|
|
2605
|
-
checks.push({
|
|
2606
|
-
name: 'Config directory',
|
|
2607
|
-
status: 'warn',
|
|
2608
|
-
message: 'Not created yet',
|
|
2609
|
-
fix: async () => {
|
|
2610
|
-
await mkdir(configDir, { recursive: true });
|
|
2611
|
-
}
|
|
2612
|
-
});
|
|
2613
|
-
}
|
|
2614
|
-
// Check 3: Node version
|
|
2615
|
-
const nodeVersion = process.version;
|
|
2616
|
-
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
2617
|
-
if (majorVersion >= 18) {
|
|
2618
|
-
checks.push({ name: 'Node.js version', status: 'pass', message: nodeVersion });
|
|
2619
|
-
}
|
|
2620
|
-
else {
|
|
2621
|
-
checks.push({ name: 'Node.js version', status: 'fail', message: `${nodeVersion} (requires >=18)` });
|
|
2622
|
-
}
|
|
2623
|
-
// Check 4: Git available
|
|
2624
|
-
try {
|
|
2625
|
-
const { execSync } = await import('child_process');
|
|
2626
|
-
execSync('git --version', { stdio: 'pipe' });
|
|
2627
|
-
checks.push({ name: 'Git installed', status: 'pass', message: 'Available' });
|
|
2628
|
-
}
|
|
2629
|
-
catch {
|
|
2630
|
-
checks.push({ name: 'Git installed', status: 'warn', message: 'Not found (optional)' });
|
|
2631
|
-
}
|
|
2632
|
-
// Check 5: Network connectivity
|
|
2633
|
-
try {
|
|
2634
|
-
const response = await fetch('https://api.github.com/rate_limit', {
|
|
2635
|
-
headers: { 'User-Agent': 'agent-skills-cli' }
|
|
2636
|
-
});
|
|
2637
|
-
if (response.ok) {
|
|
2638
|
-
checks.push({ name: 'GitHub API', status: 'pass', message: 'Connected' });
|
|
2639
|
-
}
|
|
2640
|
-
else {
|
|
2641
|
-
checks.push({ name: 'GitHub API', status: 'warn', message: `Status ${response.status}` });
|
|
2642
|
-
}
|
|
2643
|
-
}
|
|
2644
|
-
catch {
|
|
2645
|
-
checks.push({ name: 'GitHub API', status: 'fail', message: 'Cannot connect' });
|
|
2646
|
-
}
|
|
2647
|
-
// Check 6: Installed skills have valid SKILL.md
|
|
2648
|
-
if (existsSync(skillsDir)) {
|
|
2649
|
-
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
2650
|
-
const skillCount = entries.filter(e => e.isDirectory()).length;
|
|
2651
|
-
let validCount = 0;
|
|
2652
|
-
for (const entry of entries) {
|
|
2653
|
-
if (entry.isDirectory()) {
|
|
2654
|
-
const skillMd = join(skillsDir, entry.name, 'SKILL.md');
|
|
2655
|
-
if (existsSync(skillMd))
|
|
2656
|
-
validCount++;
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
if (skillCount === 0) {
|
|
2660
|
-
checks.push({ name: 'Installed skills', status: 'pass', message: 'None installed' });
|
|
2661
|
-
}
|
|
2662
|
-
else if (validCount === skillCount) {
|
|
2663
|
-
checks.push({ name: 'Installed skills', status: 'pass', message: `${validCount}/${skillCount} valid` });
|
|
2664
|
-
}
|
|
2665
|
-
else {
|
|
2666
|
-
checks.push({ name: 'Installed skills', status: 'warn', message: `${validCount}/${skillCount} valid` });
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
// Display results
|
|
2670
|
-
let hasIssues = false;
|
|
2671
|
-
for (const check of checks) {
|
|
2672
|
-
const icon = check.status === 'pass' ? chalk.green('ā') :
|
|
2673
|
-
check.status === 'warn' ? chalk.yellow('ā ') :
|
|
2674
|
-
chalk.red('ā');
|
|
2675
|
-
console.log(` ${icon} ${check.name}: ${chalk.gray(check.message)}`);
|
|
2676
|
-
if (check.status !== 'pass')
|
|
2677
|
-
hasIssues = true;
|
|
2678
|
-
}
|
|
2679
|
-
// Fix issues if requested
|
|
2680
|
-
if (options.fix && hasIssues) {
|
|
2681
|
-
console.log(chalk.cyan('\nš§ Attempting fixes...\n'));
|
|
2682
|
-
for (const check of checks) {
|
|
2683
|
-
if (check.fix && check.status !== 'pass') {
|
|
2684
|
-
try {
|
|
2685
|
-
await check.fix();
|
|
2686
|
-
console.log(chalk.green(` ā Fixed: ${check.name}`));
|
|
2687
|
-
}
|
|
2688
|
-
catch (err) {
|
|
2689
|
-
console.log(chalk.red(` ā Could not fix: ${check.name}`));
|
|
2690
|
-
}
|
|
2691
|
-
}
|
|
2692
|
-
}
|
|
2693
|
-
}
|
|
2694
|
-
if (!hasIssues) {
|
|
2695
|
-
console.log(chalk.green('\nā All checks passed!\n'));
|
|
2696
|
-
}
|
|
2697
|
-
else if (!options.fix) {
|
|
2698
|
-
console.log(chalk.gray('\nRun with --fix to attempt automatic fixes.\n'));
|
|
2699
|
-
}
|
|
2700
|
-
console.log('');
|
|
2701
|
-
}
|
|
2702
|
-
catch (error) {
|
|
2703
|
-
console.error(chalk.red('Error running doctor:'), error);
|
|
2704
|
-
process.exit(1);
|
|
2705
|
-
}
|
|
2706
|
-
});
|
|
2707
|
-
// ============================================
|
|
2708
|
-
// CHECK COMMAND - Check for skill updates
|
|
2709
|
-
// ============================================
|
|
2710
|
-
program
|
|
2711
|
-
.command('check')
|
|
2712
|
-
.description('Check installed skills and available updates')
|
|
2713
|
-
.option('-a, --agent <agent>', 'Check specific agent only')
|
|
2714
|
-
.option('-g, --global', 'Check globally installed skills only')
|
|
2715
|
-
.option('--json', 'Output as JSON')
|
|
2716
|
-
.action(async (options) => {
|
|
2717
|
-
try {
|
|
2718
|
-
const { listInstalledSkills, readLock } = await import('../core/index.js');
|
|
2719
|
-
const spinner = ora('Checking installed skills...').start();
|
|
2720
|
-
// Read from lock file
|
|
2721
|
-
const lock = await readLock();
|
|
2722
|
-
let skills = Object.values(lock.skills);
|
|
2723
|
-
// Filter by global/project
|
|
2724
|
-
if (options.global) {
|
|
2725
|
-
skills = skills.filter(s => s.isGlobal);
|
|
2726
|
-
}
|
|
2727
|
-
else if (!options.global && !options.agent) {
|
|
2728
|
-
// Show all (both global and project)
|
|
2729
|
-
}
|
|
2730
|
-
// Filter by agent
|
|
2731
|
-
if (options.agent) {
|
|
2732
|
-
skills = skills.filter(s => s.agents.includes(options.agent));
|
|
2733
|
-
}
|
|
2734
|
-
spinner.stop();
|
|
2735
|
-
if (options.json) {
|
|
2736
|
-
console.log(JSON.stringify({ skills, count: skills.length }, null, 2));
|
|
2737
|
-
return;
|
|
2738
|
-
}
|
|
2739
|
-
if (skills.length === 0) {
|
|
2740
|
-
console.log(chalk.yellow('\nš¦ No skills installed.'));
|
|
2741
|
-
console.log(chalk.gray('Use `skills search` or `skills install` to add skills.\n'));
|
|
2742
|
-
return;
|
|
2743
|
-
}
|
|
2744
|
-
console.log(chalk.bold(`\nš¦ Found ${skills.length} installed skill(s):\n`));
|
|
2745
|
-
for (const skill of skills) {
|
|
2746
|
-
const sourceLabel = skill.sourceType === 'database' ? 'š Database' :
|
|
2747
|
-
skill.sourceType === 'github' ? 'š GitHub' :
|
|
2748
|
-
skill.sourceType === 'gitlab' ? 'š¦ GitLab' : 'š Local';
|
|
2749
|
-
console.log(` ${chalk.cyan(skill.scopedName)} ${chalk.gray(`[${sourceLabel}]`)}`);
|
|
2750
|
-
console.log(chalk.gray(` Agents: ${skill.agents.join(', ')}`));
|
|
2751
|
-
console.log(chalk.gray(` Installed: ${new Date(skill.installedAt).toLocaleDateString()}`));
|
|
2752
|
-
if (skill.version) {
|
|
2753
|
-
console.log(chalk.gray(` Version: ${skill.version.slice(0, 7)}`));
|
|
2754
|
-
}
|
|
2755
|
-
console.log('');
|
|
2756
|
-
}
|
|
2757
|
-
console.log(chalk.gray('Tip: Run `skills update` to update all skills.'));
|
|
2758
|
-
console.log(chalk.gray(' Run `skills remove` to uninstall skills.\n'));
|
|
2759
|
-
}
|
|
2760
|
-
catch (error) {
|
|
2761
|
-
console.error(chalk.red('Error checking installed skills:'), error);
|
|
2762
|
-
process.exit(1);
|
|
2763
|
-
}
|
|
2764
|
-
});
|
|
2765
|
-
// Update - Re-download skills from their sources
|
|
2766
|
-
program
|
|
2767
|
-
.command('update [skill-names...]')
|
|
2768
|
-
.description('Update installed skills from their sources')
|
|
2769
|
-
.option('-a, --all', 'Update all installed skills')
|
|
2770
|
-
.option('-g, --global', 'Only update globally installed skills')
|
|
2771
|
-
.option('-y, --yes', 'Skip confirmation prompts')
|
|
2772
|
-
.action(async (skillNames, options) => {
|
|
2773
|
-
try {
|
|
2774
|
-
const { readLock, removeSkillFromLock, addSkillToLock, createLockEntry } = await import('../core/index.js');
|
|
2775
|
-
const { mkdir, cp, rm } = await import('fs/promises');
|
|
2776
|
-
const { existsSync } = await import('fs');
|
|
2777
|
-
const { join } = await import('path');
|
|
2778
|
-
const { tmpdir } = await import('os');
|
|
2779
|
-
const { exec } = await import('child_process');
|
|
2780
|
-
const { promisify } = await import('util');
|
|
2781
|
-
const execAsync = promisify(exec);
|
|
2782
|
-
const lock = await readLock();
|
|
2783
|
-
let skillsToUpdate = Object.values(lock.skills);
|
|
2784
|
-
// Filter by global
|
|
2785
|
-
if (options.global) {
|
|
2786
|
-
skillsToUpdate = skillsToUpdate.filter(s => s.isGlobal);
|
|
2787
|
-
}
|
|
2788
|
-
// Filter by specific names
|
|
2789
|
-
if (skillNames && skillNames.length > 0) {
|
|
2790
|
-
skillsToUpdate = skillsToUpdate.filter(s => skillNames.some((n) => s.name.toLowerCase() === n.toLowerCase() ||
|
|
2791
|
-
s.scopedName.toLowerCase() === n.toLowerCase()));
|
|
2792
|
-
}
|
|
2793
|
-
// If not --all and no specific skills, prompt for selection
|
|
2794
|
-
if (!options.all && skillNames.length === 0 && skillsToUpdate.length > 0) {
|
|
2795
|
-
const { selected } = await inquirer.prompt([{
|
|
2796
|
-
type: 'checkbox',
|
|
2797
|
-
name: 'selected',
|
|
2798
|
-
message: 'Select skills to update:',
|
|
2799
|
-
choices: skillsToUpdate.map(s => ({
|
|
2800
|
-
name: `${s.scopedName} (${s.sourceType})`,
|
|
2801
|
-
value: s,
|
|
2802
|
-
checked: true
|
|
2803
|
-
}))
|
|
2804
|
-
}]);
|
|
2805
|
-
skillsToUpdate = selected;
|
|
2806
|
-
}
|
|
2807
|
-
if (skillsToUpdate.length === 0) {
|
|
2808
|
-
console.log(chalk.yellow('\nš¦ No skills to update.'));
|
|
2809
|
-
console.log(chalk.gray('Use `skills install` to add skills first.\n'));
|
|
2810
|
-
return;
|
|
2811
|
-
}
|
|
2812
|
-
// Filter to only updateable skills (github/gitlab)
|
|
2813
|
-
const updateable = skillsToUpdate.filter(s => s.sourceType === 'github' || s.sourceType === 'gitlab' || s.sourceType === 'database');
|
|
2814
|
-
if (updateable.length === 0) {
|
|
2815
|
-
console.log(chalk.yellow('\nš¦ No remote skills to update.'));
|
|
2816
|
-
console.log(chalk.gray('Local skills cannot be updated automatically.\n'));
|
|
2817
|
-
return;
|
|
2818
|
-
}
|
|
2819
|
-
console.log(chalk.bold(`\nš¦ Updating ${updateable.length} skill(s)...\n`));
|
|
2820
|
-
let successCount = 0;
|
|
2821
|
-
let failCount = 0;
|
|
2822
|
-
for (const skill of updateable) {
|
|
2823
|
-
const spinner = ora(`Updating ${skill.scopedName}...`).start();
|
|
2824
|
-
try {
|
|
2825
|
-
// Clone to temp directory
|
|
2826
|
-
const tempDir = join(tmpdir(), `skill-update-${Date.now()}`);
|
|
2827
|
-
await mkdir(tempDir, { recursive: true });
|
|
2828
|
-
// Parse GitHub/GitLab URL
|
|
2829
|
-
const urlMatch = skill.source.match(/(github|gitlab)\.com\/([^/]+)\/([^/]+)/);
|
|
2830
|
-
if (!urlMatch) {
|
|
2831
|
-
spinner.fail(`${skill.scopedName}: Invalid source URL`);
|
|
2832
|
-
failCount++;
|
|
2833
|
-
continue;
|
|
2834
|
-
}
|
|
2835
|
-
// Clone the repo
|
|
2836
|
-
await execAsync(`git clone --depth 1 ${skill.source} .`, { cwd: tempDir });
|
|
2837
|
-
// Update each agent directory
|
|
2838
|
-
for (const agent of skill.agents) {
|
|
2839
|
-
const agentConfig = AGENTS[agent];
|
|
2840
|
-
if (!agentConfig)
|
|
2841
|
-
continue;
|
|
2842
|
-
const targetDir = skill.isGlobal ? agentConfig.globalDir : agentConfig.projectDir;
|
|
2843
|
-
const skillDir = skill.isGlobal
|
|
2844
|
-
? join(targetDir, skill.name)
|
|
2845
|
-
: join(skill.projectDir || process.cwd(), targetDir, skill.name);
|
|
2846
|
-
// Remove old version
|
|
2847
|
-
if (existsSync(skillDir)) {
|
|
2848
|
-
await rm(skillDir, { recursive: true, force: true });
|
|
2849
|
-
}
|
|
2850
|
-
// Copy new version
|
|
2851
|
-
await mkdir(skillDir, { recursive: true });
|
|
2852
|
-
await cp(tempDir, skillDir, { recursive: true });
|
|
2853
|
-
}
|
|
2854
|
-
// Get latest commit SHA for version tracking
|
|
2855
|
-
let version;
|
|
2856
|
-
try {
|
|
2857
|
-
const { stdout } = await execAsync('git rev-parse HEAD', { cwd: tempDir });
|
|
2858
|
-
version = stdout.trim();
|
|
2859
|
-
}
|
|
2860
|
-
catch { }
|
|
2861
|
-
// Update lock file
|
|
2862
|
-
await removeSkillFromLock(skill.scopedName);
|
|
2863
|
-
const updatedEntry = createLockEntry({
|
|
2864
|
-
name: skill.name,
|
|
2865
|
-
scopedName: skill.scopedName,
|
|
2866
|
-
source: skill.source,
|
|
2867
|
-
sourceType: skill.sourceType,
|
|
2868
|
-
version,
|
|
2869
|
-
agents: skill.agents,
|
|
2870
|
-
canonicalPath: skill.canonicalPath,
|
|
2871
|
-
isGlobal: skill.isGlobal,
|
|
2872
|
-
projectDir: skill.projectDir
|
|
2873
|
-
});
|
|
2874
|
-
await addSkillToLock(updatedEntry);
|
|
2875
|
-
// Cleanup
|
|
2876
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
2877
|
-
spinner.succeed(`${skill.scopedName}: Updated successfully`);
|
|
2878
|
-
successCount++;
|
|
2879
|
-
}
|
|
2880
|
-
catch (err) {
|
|
2881
|
-
spinner.fail(`${skill.scopedName}: ${err.message || err}`);
|
|
2882
|
-
failCount++;
|
|
2883
|
-
}
|
|
2884
|
-
}
|
|
2885
|
-
console.log('');
|
|
2886
|
-
if (successCount > 0) {
|
|
2887
|
-
console.log(chalk.bold.green(`⨠Updated ${successCount} skill(s)`));
|
|
2888
|
-
}
|
|
2889
|
-
if (failCount > 0) {
|
|
2890
|
-
console.log(chalk.yellow(`ā ${failCount} skill(s) failed to update`));
|
|
2891
|
-
}
|
|
2892
|
-
console.log('');
|
|
2893
|
-
}
|
|
2894
|
-
catch (error) {
|
|
2895
|
-
console.error(chalk.red('Error updating skills:'), error);
|
|
2896
|
-
process.exit(1);
|
|
2897
|
-
}
|
|
2898
|
-
});
|
|
2899
|
-
// Register the remove command
|
|
201
|
+
// āāā Register all command modules āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
202
|
+
// Group 1: Core commands
|
|
203
|
+
registerListCommand(program);
|
|
204
|
+
registerValidateCommand(program);
|
|
205
|
+
registerShowCommand(program); // show, prompt, init
|
|
206
|
+
// Group 2: Marketplace & assets
|
|
207
|
+
registerMarketplaceCommands(program); // assets, market-list/search/install/uninstall/installed/sources/add-source/update-check, install-url
|
|
208
|
+
// Group 3: Search & install
|
|
209
|
+
registerSearchInstallCommand(program); // search
|
|
210
|
+
registerInstallCommand(program); // install
|
|
211
|
+
// Group 4: Export
|
|
212
|
+
registerExportCommand(program);
|
|
213
|
+
// Group 5: Utilities
|
|
214
|
+
registerDoctorCommand(program);
|
|
215
|
+
registerCheckCommand(program);
|
|
216
|
+
registerUpdateCommand(program);
|
|
217
|
+
registerExecCommand(program);
|
|
218
|
+
// Group 6: Interactive wizards & misc
|
|
219
|
+
registerInteractiveCommands(program); // install-wizard, export-interactive, setup, run, context, preview, scripts, completion, info
|
|
220
|
+
// Group 7: Previously-extracted modular commands
|
|
2900
221
|
registerRemoveCommand(program, AGENTS);
|
|
222
|
+
registerSuggestCommand(program);
|
|
223
|
+
registerAuditCommand(program);
|
|
224
|
+
registerCraftCommand(program);
|
|
225
|
+
registerSubmitCommand(program);
|
|
226
|
+
registerBootstrapCommand(program);
|
|
227
|
+
registerConvertCommand(program);
|
|
228
|
+
registerCollabCommand(program);
|
|
229
|
+
registerLockspecCommand(program);
|
|
230
|
+
registerForgeCommand(program);
|
|
231
|
+
registerMineCommand(program);
|
|
232
|
+
registerRecallCommand(program);
|
|
233
|
+
registerGridCommand(program);
|
|
234
|
+
registerCaptureCommand(program);
|
|
235
|
+
registerTriggerCommand(program);
|
|
236
|
+
registerRuleCommand(program);
|
|
237
|
+
registerBlueprintCommand(program);
|
|
238
|
+
registerCiCommand(program);
|
|
239
|
+
registerTrackCommand(program);
|
|
240
|
+
registerInsightCommand(program);
|
|
241
|
+
registerMethodCommand(program);
|
|
2901
242
|
program.parse();
|
|
2902
243
|
//# sourceMappingURL=index.js.map
|