prpm 0.0.7 → 0.0.8

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.
@@ -44,67 +44,85 @@ const user_config_1 = require("../core/user-config");
44
44
  const telemetry_1 = require("../core/telemetry");
45
45
  const readline = __importStar(require("readline"));
46
46
  /**
47
- * Get icon for package type
47
+ * Get icon for package format and subtype
48
48
  */
49
- function getTypeIcon(type) {
50
- const icons = {
51
- skill: '🎓',
52
- agent: '🤖',
53
- command: '',
49
+ function getPackageIcon(format, subtype) {
50
+ // Subtype icons take precedence
51
+ const subtypeIcons = {
52
+ 'skill': '🎓',
53
+ 'agent': '🤖',
54
54
  'slash-command': '⚡',
55
- 'claude-slash-command': '',
56
- rule: '📋',
57
- plugin: '🔌',
58
- prompt: '💬',
59
- workflow: '',
60
- tool: '🔧',
61
- template: '📄',
62
- mcp: '🔗',
55
+ 'rule': '📋',
56
+ 'prompt': '💬',
57
+ 'workflow': '',
58
+ 'tool': '🔧',
59
+ 'template': '📄',
60
+ 'collection': '📦',
61
+ 'chatmode': '💬',
63
62
  };
64
- return icons[type] || '📦';
63
+ // Format-specific icons for rules/defaults
64
+ const formatIcons = {
65
+ 'claude': '🤖',
66
+ 'cursor': '📋',
67
+ 'windsurf': '🌊',
68
+ 'continue': '➡️',
69
+ 'copilot': '✈️',
70
+ 'kiro': '🎯',
71
+ 'mcp': '🔗',
72
+ 'agents.md': '📝',
73
+ 'generic': '📦',
74
+ };
75
+ return subtypeIcons[subtype] || formatIcons[format] || '📦';
65
76
  }
66
77
  /**
67
- * Get human-readable label for package type
78
+ * Get human-readable label for package format and subtype
68
79
  */
69
- function getTypeLabel(type) {
70
- const labels = {
71
- skill: 'Skill',
72
- agent: 'Agent',
73
- command: 'Slash Command',
80
+ function getPackageLabel(format, subtype) {
81
+ const formatLabels = {
82
+ 'claude': 'Claude',
83
+ 'cursor': 'Cursor',
84
+ 'windsurf': 'Windsurf',
85
+ 'continue': 'Continue',
86
+ 'copilot': 'GitHub Copilot',
87
+ 'kiro': 'Kiro',
88
+ 'mcp': 'MCP',
89
+ 'agents.md': 'Agents.md',
90
+ 'generic': '',
91
+ };
92
+ const subtypeLabels = {
93
+ 'skill': 'Skill',
94
+ 'agent': 'Agent',
74
95
  'slash-command': 'Slash Command',
75
- 'claude-slash-command': 'Slash Command',
76
- 'claude-agent': 'Agent',
77
- rule: 'Rule',
78
- plugin: 'Plugin',
79
- prompt: 'Prompt',
80
- workflow: 'Workflow',
81
- tool: 'Tool',
82
- template: 'Template',
83
- mcp: 'MCP Server',
96
+ 'rule': 'Rule',
97
+ 'prompt': 'Prompt',
98
+ 'workflow': 'Workflow',
99
+ 'tool': 'Tool',
100
+ 'template': 'Template',
101
+ 'collection': 'Collection',
102
+ 'chatmode': 'Chat Mode',
84
103
  };
85
- return labels[type] || type;
104
+ const formatLabel = formatLabels[format];
105
+ const subtypeLabel = subtypeLabels[subtype];
106
+ if (format === 'generic') {
107
+ return subtypeLabel;
108
+ }
109
+ return `${formatLabel} ${subtypeLabel}`;
86
110
  }
87
111
  /**
88
- * Map user-friendly CLI types to registry schema
112
+ * Map subtype filters for search
89
113
  */
90
- function mapTypeToRegistry(cliType) {
91
- const typeMap = {
92
- rule: { type: 'cursor', tags: ['cursor-rule'] },
93
- // Skills are packages with type=claude-skill
94
- skill: { type: 'claude-skill' },
95
- // Agents are packages with type=claude-agent or claude (not claude-skill)
96
- agent: { type: 'claude-agent' },
97
- // Slash commands are packages with type=claude-slash-command
98
- command: { type: 'claude-slash-command' },
99
- 'slash-command': { type: 'claude-slash-command' },
100
- mcp: { type: 'mcp' },
101
- plugin: { type: 'generic', tags: ['plugin'] },
102
- prompt: { type: 'generic', tags: ['prompt'] },
103
- workflow: { type: 'generic', tags: ['workflow'] },
104
- tool: { type: 'generic', tags: ['tool'] },
105
- template: { type: 'generic', tags: ['template'] },
106
- };
107
- return typeMap[cliType] || {};
114
+ function buildSearchFilters(options) {
115
+ const searchOptions = {};
116
+ if (options.format) {
117
+ searchOptions.format = options.format;
118
+ }
119
+ if (options.subtype) {
120
+ searchOptions.subtype = options.subtype;
121
+ }
122
+ if (options.author) {
123
+ searchOptions.author = options.author;
124
+ }
125
+ return searchOptions;
108
126
  }
109
127
  /**
110
128
  * Build webapp URL for search results
@@ -114,8 +132,10 @@ function buildWebappUrl(query, options, page = 1) {
114
132
  const params = new URLSearchParams();
115
133
  if (query)
116
134
  params.append('q', query);
117
- if (options.type)
118
- params.append('type', options.type);
135
+ if (options.format)
136
+ params.append('format', options.format);
137
+ if (options.subtype)
138
+ params.append('subtype', options.subtype);
119
139
  if (options.author)
120
140
  params.append('author', options.author);
121
141
  if (page > 1)
@@ -137,8 +157,8 @@ function displayResults(packages, total, page, limit) {
137
157
  const downloads = pkg.total_downloads >= 1000
138
158
  ? `${(pkg.total_downloads / 1000).toFixed(1)}k`
139
159
  : pkg.total_downloads;
140
- const typeIcon = getTypeIcon(pkg.type);
141
- const typeLabel = getTypeLabel(pkg.type);
160
+ const typeIcon = getPackageIcon(pkg.format, pkg.subtype);
161
+ const typeLabel = getPackageLabel(pkg.format, pkg.subtype);
142
162
  // Add verified badge
143
163
  let verifiedBadge = '';
144
164
  if (pkg.featured || pkg.official || pkg.verified) {
@@ -254,29 +274,31 @@ async function handleSearch(query, options) {
254
274
  let result = null;
255
275
  let registryUrl = '';
256
276
  try {
257
- // Allow empty query when filtering by type or author
277
+ // Allow empty query when filtering by format/subtype or author
258
278
  if (query) {
259
279
  console.log(`🔍 Searching for "${query}"...`);
260
280
  }
261
- else if (options.type) {
262
- console.log(`🔍 Listing ${options.type} packages...`);
281
+ else if (options.format || options.subtype) {
282
+ const filterType = options.subtype || options.format;
283
+ console.log(`🔍 Listing ${filterType} packages...`);
263
284
  }
264
285
  else if (options.author) {
265
286
  console.log(`🔍 Listing packages by @${options.author}...`);
266
287
  }
267
288
  else {
268
- console.log('❌ Please provide a search query or use --type/--author to filter');
289
+ console.log('❌ Please provide a search query or use --format/--subtype/--author to filter');
269
290
  console.log('\n💡 Examples:');
270
291
  console.log(' prpm search react');
271
- console.log(' prpm search --type skill');
292
+ console.log(' prpm search --subtype skill');
293
+ console.log(' prpm search --format claude');
272
294
  console.log(' prpm search --author prpm');
273
- console.log(' prpm search react --type rule');
295
+ console.log(' prpm search react --subtype rule');
274
296
  return;
275
297
  }
276
298
  const config = await (0, user_config_1.getConfig)();
277
299
  registryUrl = config.registryUrl || 'https://registry.prpm.dev';
278
300
  const client = (0, registry_client_1.getRegistryClient)(config);
279
- // Map CLI type to registry schema
301
+ // Build search options
280
302
  const limit = options.limit || 20;
281
303
  const page = options.page || 1;
282
304
  const offset = (page - 1) * limit;
@@ -284,14 +306,12 @@ async function handleSearch(query, options) {
284
306
  limit,
285
307
  offset,
286
308
  };
287
- if (options.type) {
288
- const mapped = mapTypeToRegistry(options.type);
289
- if (mapped.type) {
290
- searchOptions.type = mapped.type;
291
- }
292
- if (mapped.tags) {
293
- searchOptions.tags = mapped.tags;
294
- }
309
+ // Add format/subtype filters
310
+ if (options.format) {
311
+ searchOptions.format = options.format;
312
+ }
313
+ if (options.subtype) {
314
+ searchOptions.subtype = options.subtype;
295
315
  }
296
316
  if (options.author) {
297
317
  searchOptions.author = options.author;
@@ -359,7 +379,8 @@ async function handleSearch(query, options) {
359
379
  duration: Date.now() - startTime,
360
380
  data: {
361
381
  query: query.substring(0, 100),
362
- type: options.type,
382
+ format: options.format,
383
+ subtype: options.subtype,
363
384
  resultCount: success && result ? result.packages.length : 0,
364
385
  page: options.page,
365
386
  interactive: options.interactive,
@@ -373,32 +394,41 @@ function createSearchCommand() {
373
394
  const command = new commander_1.Command('search');
374
395
  command
375
396
  .description('Search for packages in the registry')
376
- .argument('[query]', 'Search query (optional when using --type or --author)')
377
- .option('--type <type>', 'Filter by package type (skill, agent, command, slash-command, rule, plugin, prompt, workflow, tool, template, mcp)')
397
+ .argument('[query]', 'Search query (optional when using --format/--subtype or --author)')
398
+ .option('--format <format>', 'Filter by package format (cursor, claude, continue, windsurf, copilot, kiro, generic, mcp)')
399
+ .option('--subtype <subtype>', 'Filter by package subtype (rule, agent, skill, slash-command, prompt, workflow, tool, template, collection)')
378
400
  .option('--author <username>', 'Filter by author username')
379
401
  .option('--limit <number>', 'Number of results per page', '20')
380
402
  .option('--page <number>', 'Page number (default: 1)', '1')
381
403
  .option('--interactive', 'Enable interactive pagination (default: true for multiple pages)', true)
382
404
  .option('--no-interactive', 'Disable interactive pagination')
383
405
  .action(async (query, options) => {
384
- const type = options.type;
406
+ const format = options.format;
407
+ const subtype = options.subtype;
385
408
  const author = options.author;
386
409
  const limit = options.limit ? parseInt(options.limit, 10) : 20;
387
410
  const page = options.page ? parseInt(options.page, 10) : 1;
388
- const validTypes = ['skill', 'agent', 'command', 'slash-command', 'rule', 'plugin', 'prompt', 'workflow', 'tool', 'template', 'mcp'];
389
- if (options.type && !validTypes.includes(type)) {
390
- console.error(`❌ Type must be one of: ${validTypes.join(', ')}`);
411
+ const validFormats = ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'generic', 'mcp'];
412
+ const validSubtypes = ['rule', 'agent', 'skill', 'slash-command', 'prompt', 'workflow', 'tool', 'template', 'collection'];
413
+ if (options.format && !validFormats.includes(format)) {
414
+ console.error(`❌ Format must be one of: ${validFormats.join(', ')}`);
415
+ process.exit(1);
416
+ }
417
+ if (options.subtype && !validSubtypes.includes(subtype)) {
418
+ console.error(`❌ Subtype must be one of: ${validSubtypes.join(', ')}`);
391
419
  console.log(`\n💡 Examples:`);
392
- console.log(` prpm search postgres --type skill`);
393
- console.log(` prpm search debugging --type agent`);
394
- console.log(` prpm search refactor --type command`);
395
- console.log(` prpm search react --type rule`);
396
- console.log(` prpm search --type command # List all slash commands`);
397
- console.log(` prpm search --type skill # List all skills`);
398
- console.log(` prpm search --author prpm # List packages by @pr-pm`);
420
+ console.log(` prpm search postgres --subtype skill`);
421
+ console.log(` prpm search debugging --subtype agent`);
422
+ console.log(` prpm search refactor --subtype slash-command`);
423
+ console.log(` prpm search react --subtype rule`);
424
+ console.log(` prpm search --subtype slash-command # List all slash commands`);
425
+ console.log(` prpm search --subtype skill # List all skills`);
426
+ console.log(` prpm search --format claude # List all Claude packages`);
427
+ console.log(` prpm search --author prpm # List packages by @prpm`);
399
428
  process.exit(1);
400
429
  }
401
- await handleSearch(query || '', { type, author, limit, page, interactive: options.interactive });
430
+ await handleSearch(query || '', { format, subtype, author, limit, page, interactive: options.interactive });
431
+ process.exit(0);
402
432
  });
403
433
  return command;
404
434
  }
@@ -33,6 +33,7 @@ function createStatusCommand() {
33
33
  else {
34
34
  console.log('\n💡 Telemetry is disabled. Run "prpm telemetry enable" to help improve the tool.');
35
35
  }
36
+ process.exit(0);
36
37
  });
37
38
  }
38
39
  function createEnableCommand() {
@@ -42,6 +43,7 @@ function createEnableCommand() {
42
43
  await telemetry_1.telemetry.enable();
43
44
  console.log('✅ Telemetry enabled');
44
45
  console.log('📊 Anonymous usage data will be collected to help improve the tool.');
46
+ process.exit(0);
45
47
  });
46
48
  }
47
49
  function createDisableCommand() {
@@ -51,6 +53,7 @@ function createDisableCommand() {
51
53
  await telemetry_1.telemetry.disable();
52
54
  console.log('❌ Telemetry disabled');
53
55
  console.log('📊 No usage data will be collected.');
56
+ process.exit(0);
54
57
  });
55
58
  }
56
59
  function createStatsCommand() {
@@ -63,6 +66,7 @@ function createStatsCommand() {
63
66
  if (stats.lastEvent) {
64
67
  console.log(` Last event: ${stats.lastEvent}`);
65
68
  }
69
+ process.exit(0);
66
70
  });
67
71
  }
68
72
  function createTestCommand() {
@@ -98,6 +102,8 @@ function createTestCommand() {
98
102
  }
99
103
  catch (error) {
100
104
  console.error('❌ Failed to send test event:', error);
105
+ process.exit(1);
101
106
  }
107
+ process.exit(0);
102
108
  });
103
109
  }
@@ -17,7 +17,7 @@ async function handleTrending(options) {
17
17
  console.log(`🔥 Fetching trending packages...`);
18
18
  const config = await (0, user_config_1.getConfig)();
19
19
  const client = (0, registry_client_1.getRegistryClient)(config);
20
- const packages = await client.getTrending(options.type, options.limit || 10);
20
+ const packages = await client.getTrending(options.format, options.subtype, options.limit || 10);
21
21
  if (packages.length === 0) {
22
22
  console.log('\n❌ No trending packages found');
23
23
  return;
@@ -50,7 +50,8 @@ async function handleTrending(options) {
50
50
  error,
51
51
  duration: Date.now() - startTime,
52
52
  data: {
53
- type: options.type,
53
+ format: options.format,
54
+ subtype: options.subtype,
54
55
  limit: options.limit || 10,
55
56
  },
56
57
  });
@@ -61,16 +62,25 @@ function createTrendingCommand() {
61
62
  const command = new commander_1.Command('trending');
62
63
  command
63
64
  .description('Show trending packages')
64
- .option('--type <type>', 'Filter by package type (cursor, claude, continue)')
65
+ .option('--format <format>', 'Filter by format (cursor, claude, continue, windsurf, copilot, kiro, generic)')
66
+ .option('--subtype <subtype>', 'Filter by subtype (rule, agent, skill, slash-command, prompt, workflow, tool, template, collection)')
65
67
  .option('--limit <number>', 'Number of packages to show', '10')
66
68
  .action(async (options) => {
67
- const type = options.type;
69
+ const format = options.format;
70
+ const subtype = options.subtype;
68
71
  const limit = options.limit ? parseInt(options.limit, 10) : 10;
69
- if (options.type && !['cursor', 'claude', 'continue', 'windsurf', 'generic'].includes(type)) {
70
- console.error('❌ Type must be one of: cursor, claude, continue, windsurf, generic');
72
+ const validFormats = ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'generic', 'mcp'];
73
+ const validSubtypes = ['rule', 'agent', 'skill', 'slash-command', 'prompt', 'workflow', 'tool', 'template', 'collection'];
74
+ if (options.format && !validFormats.includes(format)) {
75
+ console.error(`❌ Format must be one of: ${validFormats.join(', ')}`);
71
76
  process.exit(1);
72
77
  }
73
- await handleTrending({ type, limit });
78
+ if (options.subtype && !validSubtypes.includes(subtype)) {
79
+ console.error(`❌ Subtype must be one of: ${validSubtypes.join(', ')}`);
80
+ process.exit(1);
81
+ }
82
+ await handleTrending({ format, subtype, limit });
83
+ process.exit(0);
74
84
  });
75
85
  return command;
76
86
  }
@@ -21,13 +21,10 @@ async function handleUninstall(name) {
21
21
  console.error(`❌ Package "${name}" not found`);
22
22
  process.exit(1);
23
23
  }
24
- // Determine file path based on package type and format
25
- const effectiveType = (pkg.format === 'claude' ? 'claude-skill' :
26
- pkg.format === 'cursor' ? 'cursor' :
27
- pkg.format === 'continue' ? 'continue' :
28
- pkg.format === 'windsurf' ? 'windsurf' :
29
- pkg.type);
30
- const destDir = (0, filesystem_1.getDestinationDir)(effectiveType);
24
+ // Get destination directory using format and subtype
25
+ const format = pkg.format || 'generic';
26
+ const subtype = pkg.subtype || 'rule';
27
+ const destDir = (0, filesystem_1.getDestinationDir)(format, subtype);
31
28
  const fileExtension = pkg.format === 'cursor' ? 'mdc' : 'md';
32
29
  // Strip author namespace to get just the package name
33
30
  const packageName = (0, filesystem_1.stripAuthorNamespace)(name);
@@ -59,9 +59,7 @@ async function handleUpdate(packageName, options = {}) {
59
59
  }
60
60
  console.log(`\n📦 Updating ${pkg.id}: ${currentVersion} → ${latestVersion}`);
61
61
  // Install new version
62
- await (0, install_1.handleInstall)(`${pkg.id}@${latestVersion}`, {
63
- type: pkg.type,
64
- });
62
+ await (0, install_1.handleInstall)(`${pkg.id}@${latestVersion}`, {});
65
63
  updatedCount++;
66
64
  }
67
65
  catch (err) {
@@ -117,5 +115,8 @@ function createUpdateCommand() {
117
115
  .description('Update packages to latest compatible versions (minor/patch only)')
118
116
  .argument('[package]', 'Specific package to update (optional)')
119
117
  .option('--all', 'Update all packages')
120
- .action(handleUpdate);
118
+ .action(async (packageName, options) => {
119
+ await handleUpdate(packageName, options);
120
+ process.exit(0);
121
+ });
121
122
  }
@@ -58,9 +58,7 @@ async function handleUpgrade(packageName, options = {}) {
58
58
  console.log(` ⚠️ This is a major version upgrade and may contain breaking changes`);
59
59
  }
60
60
  // Install new version
61
- await (0, install_1.handleInstall)(`${pkg.id}@${latestVersion}`, {
62
- type: pkg.type,
63
- });
61
+ await (0, install_1.handleInstall)(`${pkg.id}@${latestVersion}`, {});
64
62
  upgradedCount++;
65
63
  }
66
64
  catch (err) {
@@ -117,5 +115,8 @@ function createUpgradeCommand() {
117
115
  .argument('[package]', 'Specific package to upgrade (optional)')
118
116
  .option('--all', 'Upgrade all packages')
119
117
  .option('--force', 'Skip warning for major version upgrades')
120
- .action(handleUpgrade);
118
+ .action(async (packageName, options) => {
119
+ await handleUpgrade(packageName, options);
120
+ process.exit(0);
121
+ });
121
122
  }
@@ -45,7 +45,13 @@ async function handleWhoami() {
45
45
  }
46
46
  catch (apiError) {
47
47
  // Fallback to simple username display if API call fails
48
+ // This can happen if the user's token is stale or the registry is unavailable
48
49
  console.log(`${config.username}`);
50
+ // Show hint if it looks like an auth issue
51
+ const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
52
+ if (errorMessage.includes('User not found') || errorMessage.includes('Unauthorized')) {
53
+ console.log('💡 Tip: Your token may be outdated. Run "prpm login" to refresh.\n');
54
+ }
49
55
  }
50
56
  success = true;
51
57
  }
@@ -71,5 +77,8 @@ async function handleWhoami() {
71
77
  function createWhoamiCommand() {
72
78
  return new commander_1.Command('whoami')
73
79
  .description('Show current logged-in user')
74
- .action(handleWhoami);
80
+ .action(async () => {
81
+ await handleWhoami();
82
+ process.exit(0);
83
+ });
75
84
  }
@@ -16,32 +16,38 @@ exports.stripAuthorNamespace = stripAuthorNamespace;
16
16
  const fs_1 = require("fs");
17
17
  const path_1 = __importDefault(require("path"));
18
18
  /**
19
- * Get the destination directory for a package type
19
+ * Get the destination directory for a package based on format and subtype
20
20
  */
21
- function getDestinationDir(type) {
22
- switch (type) {
21
+ function getDestinationDir(format, subtype) {
22
+ switch (format) {
23
23
  case 'cursor':
24
+ if (subtype === 'agent')
25
+ return '.cursor/agents';
26
+ if (subtype === 'slash-command')
27
+ return '.cursor/commands';
24
28
  return '.cursor/rules';
25
- case 'cursor-agent':
26
- return '.cursor/agents';
27
- case 'cursor-slash-command':
28
- return '.cursor/commands';
29
29
  case 'claude':
30
- return '.claude/agents';
31
- case 'claude-agent':
32
- return '.claude/agents';
33
- case 'claude-skill':
34
- return '.claude/skills';
35
- case 'claude-slash-command':
36
- return '.claude/commands';
30
+ if (subtype === 'skill')
31
+ return '.claude/skills';
32
+ if (subtype === 'slash-command')
33
+ return '.claude/commands';
34
+ if (subtype === 'agent')
35
+ return '.claude/agents';
36
+ return '.claude/agents'; // Default for claude
37
37
  case 'continue':
38
38
  return '.continue/rules';
39
39
  case 'windsurf':
40
40
  return '.windsurf/rules';
41
+ case 'copilot':
42
+ return '.github/instructions';
43
+ case 'kiro':
44
+ return '.kiro/steering';
41
45
  case 'generic':
42
46
  return '.prompts';
47
+ case 'mcp':
48
+ return '.mcp/tools';
43
49
  default:
44
- throw new Error(`Unknown package type: ${type}`);
50
+ throw new Error(`Unknown format: ${format}`);
45
51
  }
46
52
  }
47
53
  /**
@@ -72,8 +72,8 @@ function addToLockfile(lockfile, packageId, packageInfo) {
72
72
  resolved: packageInfo.tarballUrl,
73
73
  integrity: '', // Will be set after download
74
74
  dependencies: packageInfo.dependencies,
75
- type: packageInfo.type,
76
75
  format: packageInfo.format,
76
+ subtype: packageInfo.subtype,
77
77
  installedPath: packageInfo.installedPath,
78
78
  };
79
79
  lockfile.generated = new Date().toISOString();
@@ -194,8 +194,8 @@ async function addPackage(packageInfo) {
194
194
  version: packageInfo.version,
195
195
  tarballUrl: packageInfo.tarballUrl,
196
196
  dependencies: packageInfo.dependencies,
197
- type: packageInfo.type,
198
197
  format: packageInfo.format,
198
+ subtype: packageInfo.subtype,
199
199
  installedPath: packageInfo.installedPath,
200
200
  });
201
201
  await writeLockfile(lockfile);
@@ -25,16 +25,20 @@ function marketplaceToManifest(marketplace, pluginIndex = 0) {
25
25
  throw new Error(`Plugin index ${pluginIndex} out of range. Found ${marketplace.plugins.length} plugins.`);
26
26
  }
27
27
  const plugin = marketplace.plugins[pluginIndex];
28
- // Determine package type based on what the plugin contains
29
- let type = 'claude';
28
+ // Determine package format and subtype based on what the plugin contains
29
+ let format = 'claude';
30
+ let subtype = 'rule';
30
31
  if (plugin.agents && plugin.agents.length > 0) {
31
- type = 'claude';
32
+ format = 'claude';
33
+ subtype = 'agent';
32
34
  }
33
35
  else if (plugin.skills && plugin.skills.length > 0) {
34
- type = 'claude';
36
+ format = 'claude';
37
+ subtype = 'skill';
35
38
  }
36
39
  else if (plugin.commands && plugin.commands.length > 0) {
37
- type = 'claude';
40
+ format = 'claude';
41
+ subtype = 'slash-command';
38
42
  }
39
43
  // Generate package name from plugin name
40
44
  // Format: @owner/plugin-name
@@ -54,7 +58,8 @@ function marketplaceToManifest(marketplace, pluginIndex = 0) {
54
58
  name: packageName,
55
59
  version: plugin.version || marketplace.version || '1.0.0',
56
60
  description: plugin.description || marketplace.description,
57
- type,
61
+ format,
62
+ subtype,
58
63
  author: plugin.author || marketplace.owner,
59
64
  files,
60
65
  tags,
@@ -16,8 +16,10 @@ class RegistryClient {
16
16
  */
17
17
  async search(query, options) {
18
18
  const params = new URLSearchParams({ q: query });
19
- if (options?.type)
20
- params.append('type', options.type);
19
+ if (options?.format)
20
+ params.append('format', options.format);
21
+ if (options?.subtype)
22
+ params.append('subtype', options.subtype);
21
23
  if (options?.tags)
22
24
  options.tags.forEach(tag => params.append('tags', tag));
23
25
  if (options?.limit)
@@ -87,10 +89,12 @@ class RegistryClient {
87
89
  /**
88
90
  * Get trending packages
89
91
  */
90
- async getTrending(type, limit = 20) {
92
+ async getTrending(format, subtype, limit = 20) {
91
93
  const params = new URLSearchParams({ limit: limit.toString() });
92
- if (type)
93
- params.append('type', type);
94
+ if (format)
95
+ params.append('format', format);
96
+ if (subtype)
97
+ params.append('subtype', subtype);
94
98
  const response = await this.fetch(`/api/v1/search/trending?${params}`);
95
99
  const data = await response.json();
96
100
  return data.packages;
@@ -98,10 +102,12 @@ class RegistryClient {
98
102
  /**
99
103
  * Get featured packages
100
104
  */
101
- async getFeatured(type, limit = 20) {
105
+ async getFeatured(format, subtype, limit = 20) {
102
106
  const params = new URLSearchParams({ limit: limit.toString() });
103
- if (type)
104
- params.append('type', type);
107
+ if (format)
108
+ params.append('format', format);
109
+ if (subtype)
110
+ params.append('subtype', subtype);
105
111
  const response = await this.fetch(`/api/v1/search/featured?${params}`);
106
112
  const data = await response.json();
107
113
  return data.packages;