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