prpm 0.2.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/index.js +14257 -109
  2. package/package.json +11 -9
  3. package/dist/__tests__/e2e/test-helpers.js +0 -153
  4. package/dist/commands/buy-credits.js +0 -224
  5. package/dist/commands/catalog.js +0 -365
  6. package/dist/commands/collections.js +0 -655
  7. package/dist/commands/config.js +0 -161
  8. package/dist/commands/credits.js +0 -186
  9. package/dist/commands/index.js +0 -184
  10. package/dist/commands/info.js +0 -78
  11. package/dist/commands/init.js +0 -684
  12. package/dist/commands/install.js +0 -829
  13. package/dist/commands/list.js +0 -198
  14. package/dist/commands/login.js +0 -316
  15. package/dist/commands/outdated.js +0 -130
  16. package/dist/commands/playground.js +0 -637
  17. package/dist/commands/popular.js +0 -33
  18. package/dist/commands/publish.js +0 -803
  19. package/dist/commands/schema.js +0 -41
  20. package/dist/commands/search.js +0 -446
  21. package/dist/commands/starred.js +0 -147
  22. package/dist/commands/subscribe.js +0 -211
  23. package/dist/commands/telemetry.js +0 -104
  24. package/dist/commands/trending.js +0 -86
  25. package/dist/commands/uninstall.js +0 -120
  26. package/dist/commands/update.js +0 -121
  27. package/dist/commands/upgrade.js +0 -121
  28. package/dist/commands/whoami.js +0 -83
  29. package/dist/core/claude-config.js +0 -91
  30. package/dist/core/cursor-config.js +0 -130
  31. package/dist/core/downloader.js +0 -64
  32. package/dist/core/errors.js +0 -29
  33. package/dist/core/filesystem.js +0 -246
  34. package/dist/core/lockfile.js +0 -292
  35. package/dist/core/marketplace-converter.js +0 -224
  36. package/dist/core/prompts.js +0 -62
  37. package/dist/core/registry-client.js +0 -305
  38. package/dist/core/schema-validator.js +0 -74
  39. package/dist/core/telemetry.js +0 -253
  40. package/dist/core/user-config.js +0 -147
  41. package/dist/types/registry.js +0 -12
  42. package/dist/types.js +0 -9
  43. package/dist/utils/license-extractor.js +0 -122
  44. package/dist/utils/multi-package.js +0 -117
  45. package/dist/utils/parallel-publisher.js +0 -144
  46. package/dist/utils/script-executor.js +0 -72
  47. package/dist/utils/snippet-extractor.js +0 -77
  48. package/dist/utils/webapp-url.js +0 -44
@@ -1,41 +0,0 @@
1
- "use strict";
2
- /**
3
- * Schema command - Export and display JSON schema
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.handleSchema = handleSchema;
7
- exports.createSchemaCommand = createSchemaCommand;
8
- const commander_1 = require("commander");
9
- const schema_validator_1 = require("../core/schema-validator");
10
- const errors_1 = require("../core/errors");
11
- /**
12
- * Handle the schema command
13
- */
14
- async function handleSchema() {
15
- try {
16
- const schema = (0, schema_validator_1.getManifestSchema)();
17
- if (!schema) {
18
- throw new errors_1.CLIError('❌ Schema not available', 1);
19
- }
20
- // Output the schema as pretty-printed JSON
21
- console.log(JSON.stringify(schema, null, 2));
22
- }
23
- catch (error) {
24
- if (error instanceof errors_1.CLIError) {
25
- throw error;
26
- }
27
- throw new errors_1.CLIError(`❌ Failed to export schema: ${error}`, 1);
28
- }
29
- }
30
- /**
31
- * Create the schema command
32
- */
33
- function createSchemaCommand() {
34
- const command = new commander_1.Command('schema');
35
- command
36
- .description('Display the PRPM manifest JSON schema')
37
- .action(async () => {
38
- await handleSchema();
39
- });
40
- return command;
41
- }
@@ -1,446 +0,0 @@
1
- "use strict";
2
- /**
3
- * Search command - Search for packages in the registry
4
- */
5
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- var desc = Object.getOwnPropertyDescriptor(m, k);
8
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
- desc = { enumerable: true, get: function() { return m[k]; } };
10
- }
11
- Object.defineProperty(o, k2, desc);
12
- }) : (function(o, m, k, k2) {
13
- if (k2 === undefined) k2 = k;
14
- o[k2] = m[k];
15
- }));
16
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
- Object.defineProperty(o, "default", { enumerable: true, value: v });
18
- }) : function(o, v) {
19
- o["default"] = v;
20
- });
21
- var __importStar = (this && this.__importStar) || (function () {
22
- var ownKeys = function(o) {
23
- ownKeys = Object.getOwnPropertyNames || function (o) {
24
- var ar = [];
25
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
- return ar;
27
- };
28
- return ownKeys(o);
29
- };
30
- return function (mod) {
31
- if (mod && mod.__esModule) return mod;
32
- var result = {};
33
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
- __setModuleDefault(result, mod);
35
- return result;
36
- };
37
- })();
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.handleSearch = handleSearch;
40
- exports.createSearchCommand = createSearchCommand;
41
- const commander_1 = require("commander");
42
- const registry_client_1 = require("@pr-pm/registry-client");
43
- const user_config_1 = require("../core/user-config");
44
- const telemetry_1 = require("../core/telemetry");
45
- const readline = __importStar(require("readline"));
46
- const errors_1 = require("../core/errors");
47
- /**
48
- * Get icon for package format and subtype
49
- */
50
- function getPackageIcon(format, subtype) {
51
- // Subtype icons take precedence
52
- const subtypeIcons = {
53
- 'skill': '🎓',
54
- 'agent': '🤖',
55
- 'slash-command': '⚡',
56
- 'rule': '📋',
57
- 'prompt': '💬',
58
- 'collection': '📦',
59
- 'chatmode': '💬',
60
- 'tool': '🔧',
61
- 'hook': '🪝',
62
- };
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] || '📦';
76
- }
77
- /**
78
- * Get human-readable label for package format and subtype
79
- */
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',
95
- 'slash-command': 'Slash Command',
96
- 'rule': 'Rule',
97
- 'prompt': 'Prompt',
98
- 'collection': 'Collection',
99
- 'chatmode': 'Chat Mode',
100
- 'tool': 'Tool',
101
- 'hook': 'Hook',
102
- };
103
- const formatLabel = formatLabels[format];
104
- const subtypeLabel = subtypeLabels[subtype];
105
- if (format === 'generic') {
106
- return subtypeLabel;
107
- }
108
- return `${formatLabel} ${subtypeLabel}`;
109
- }
110
- /**
111
- * Map subtype filters for search
112
- */
113
- function buildSearchFilters(options) {
114
- const searchOptions = {};
115
- if (options.format) {
116
- searchOptions.format = options.format;
117
- }
118
- if (options.subtype) {
119
- searchOptions.subtype = options.subtype;
120
- }
121
- if (options.author) {
122
- searchOptions.author = options.author;
123
- }
124
- return searchOptions;
125
- }
126
- /**
127
- * Build webapp URL for search results
128
- */
129
- function buildWebappUrl(query, options, page = 1) {
130
- const baseUrl = process.env.PRPM_WEBAPP_URL || 'https://prpm.dev';
131
- const params = new URLSearchParams();
132
- if (query)
133
- params.append('q', query);
134
- if (options.format)
135
- params.append('format', options.format);
136
- if (options.subtype)
137
- params.append('subtype', options.subtype);
138
- if (options.author)
139
- params.append('author', options.author);
140
- if (options.language)
141
- params.append('language', options.language);
142
- if (options.framework)
143
- params.append('framework', options.framework);
144
- if (page > 1)
145
- params.append('page', page.toString());
146
- return `${baseUrl}/search?${params.toString()}`;
147
- }
148
- /**
149
- * Display search results
150
- */
151
- function displayResults(packages, total, page, limit) {
152
- const startIdx = (page - 1) * limit + 1;
153
- const endIdx = Math.min(page * limit, total);
154
- console.log('\n' + '─'.repeat(80));
155
- console.log(`📦 Results ${startIdx}-${endIdx} of ${total}`.padEnd(80));
156
- console.log('─'.repeat(80) + '\n');
157
- packages.forEach((pkg, idx) => {
158
- const num = startIdx + idx;
159
- const rating = pkg.rating_average ? `⭐ ${pkg.rating_average.toFixed(1)}` : '';
160
- const downloads = pkg.total_downloads >= 1000
161
- ? `${(pkg.total_downloads / 1000).toFixed(1)}k`
162
- : pkg.total_downloads;
163
- const typeIcon = getPackageIcon(pkg.format, pkg.subtype);
164
- const typeLabel = getPackageLabel(pkg.format, pkg.subtype);
165
- // Add verified badge
166
- let verifiedBadge = '';
167
- if (pkg.featured || pkg.official || pkg.verified) {
168
- verifiedBadge = ' | ✅ Verified';
169
- }
170
- console.log(`\x1b[1m${num}. ${pkg.name}\x1b[0m ${rating}`);
171
- console.log(` ${pkg.description || 'No description'}`);
172
- console.log(` ${typeIcon} ${typeLabel} | 📥 ${downloads} downloads | 🏷️ ${pkg.tags.slice(0, 3).join(', ')}${verifiedBadge}`);
173
- console.log();
174
- });
175
- console.log('─'.repeat(80));
176
- }
177
- /**
178
- * Prompt user for pagination action
179
- */
180
- function promptUser() {
181
- return new Promise((resolve) => {
182
- const rl = readline.createInterface({
183
- input: process.stdin,
184
- output: process.stdout,
185
- });
186
- rl.question('', (answer) => {
187
- rl.close();
188
- resolve(answer.trim().toLowerCase());
189
- });
190
- });
191
- }
192
- /**
193
- * Interactive pagination handler
194
- */
195
- async function handlePagination(query, options, client, searchOptions, initialResult, webappBaseUrl) {
196
- let currentPage = 1;
197
- let result = initialResult;
198
- const totalPages = Math.ceil(result.total / options.limit);
199
- while (true) {
200
- // Display current page
201
- displayResults(result.packages, result.total, currentPage, options.limit);
202
- // Show navigation options
203
- console.log('\n💡 \x1b[1mOptions:\x1b[0m');
204
- if (currentPage < totalPages) {
205
- console.log(' \x1b[36mn\x1b[0m - Next page');
206
- }
207
- if (currentPage > 1) {
208
- console.log(' \x1b[36mp\x1b[0m - Previous page');
209
- }
210
- console.log(' \x1b[36m1-' + result.packages.length + '\x1b[0m - Install package by number');
211
- console.log(' \x1b[36mw\x1b[0m - View in web browser');
212
- console.log(' \x1b[36mq\x1b[0m - Quit');
213
- // Show webapp link
214
- const webappUrl = buildWebappUrl(query, options, currentPage);
215
- console.log(`\n🌐 \x1b[2mView in browser: ${webappUrl}\x1b[0m`);
216
- process.stdout.write('\n👉 ');
217
- const input = await promptUser();
218
- if (input === 'q' || input === 'quit' || input === 'exit') {
219
- console.log('\n✨ Happy coding!\n');
220
- break;
221
- }
222
- if (input === 'n' || input === 'next') {
223
- if (currentPage < totalPages) {
224
- currentPage++;
225
- const offset = (currentPage - 1) * options.limit;
226
- result = await client.search(query || '', { ...searchOptions, offset });
227
- console.clear();
228
- }
229
- else {
230
- console.log('\n❌ Already on last page');
231
- await new Promise(resolve => setTimeout(resolve, 1000));
232
- console.clear();
233
- }
234
- continue;
235
- }
236
- if (input === 'p' || input === 'prev' || input === 'previous') {
237
- if (currentPage > 1) {
238
- currentPage--;
239
- const offset = (currentPage - 1) * options.limit;
240
- result = await client.search(query || '', { ...searchOptions, offset });
241
- console.clear();
242
- }
243
- else {
244
- console.log('\n❌ Already on first page');
245
- await new Promise(resolve => setTimeout(resolve, 1000));
246
- console.clear();
247
- }
248
- continue;
249
- }
250
- if (input === 'w' || input === 'web' || input === 'browser') {
251
- const url = buildWebappUrl(query, options, currentPage);
252
- console.log(`\n🌐 Opening: ${url}`);
253
- console.log(' (Copy and paste this URL into your browser)\n');
254
- await new Promise(resolve => setTimeout(resolve, 2000));
255
- console.clear();
256
- continue;
257
- }
258
- // Check if input is a number for installation
259
- const num = parseInt(input, 10);
260
- if (!isNaN(num) && num >= 1 && num <= result.packages.length) {
261
- const pkg = result.packages[num - 1];
262
- console.log(`\n📦 To install: \x1b[36mprpm install ${pkg.name}\x1b[0m`);
263
- console.log(` More info: \x1b[36mprpm info ${pkg.name}\x1b[0m\n`);
264
- await new Promise(resolve => setTimeout(resolve, 2000));
265
- console.clear();
266
- continue;
267
- }
268
- console.log('\n❌ Invalid option. Try again.');
269
- await new Promise(resolve => setTimeout(resolve, 1000));
270
- console.clear();
271
- }
272
- }
273
- async function handleSearch(query, options) {
274
- const startTime = Date.now();
275
- let success = false;
276
- let error;
277
- let result = null;
278
- let registryUrl = '';
279
- try {
280
- // Allow empty query when filtering by format/subtype or author
281
- if (query) {
282
- console.log(`🔍 Searching for "${query}"...`);
283
- }
284
- else if (options.format || options.subtype) {
285
- const filterType = options.subtype || options.format;
286
- console.log(`🔍 Listing ${filterType} packages...`);
287
- }
288
- else if (options.author) {
289
- console.log(`🔍 Listing packages by @${options.author}...`);
290
- }
291
- else {
292
- console.log('❌ Please provide a search query or use --format/--subtype/--author to filter');
293
- console.log('\n💡 Examples:');
294
- console.log(' prpm search react');
295
- console.log(' prpm search --subtype skill');
296
- console.log(' prpm search --format claude');
297
- console.log(' prpm search --author prpm');
298
- console.log(' prpm search react --subtype rule');
299
- return;
300
- }
301
- const config = await (0, user_config_1.getConfig)();
302
- registryUrl = config.registryUrl || 'https://registry.prpm.dev';
303
- const client = (0, registry_client_1.getRegistryClient)(config);
304
- // Build search options
305
- const limit = options.limit || 20;
306
- const page = options.page || 1;
307
- const offset = (page - 1) * limit;
308
- const searchOptions = {
309
- limit,
310
- offset,
311
- };
312
- // Add format/subtype filters
313
- if (options.format) {
314
- searchOptions.format = options.format;
315
- }
316
- if (options.subtype) {
317
- searchOptions.subtype = options.subtype;
318
- }
319
- if (options.author) {
320
- searchOptions.author = options.author;
321
- }
322
- if (options.language) {
323
- searchOptions.language = options.language;
324
- }
325
- if (options.framework) {
326
- searchOptions.framework = options.framework;
327
- }
328
- result = await client.search(query || '', searchOptions);
329
- if (!result || result.packages.length === 0) {
330
- console.log('\n❌ No packages found');
331
- console.log(`\nTry:`);
332
- console.log(` - Broadening your search terms`);
333
- console.log(` - Checking spelling`);
334
- console.log(` - Browsing trending: prpm trending`);
335
- // Suggest webapp even if no results
336
- const webappUrl = buildWebappUrl(query, options);
337
- console.log(`\n🌐 View in browser: ${webappUrl}`);
338
- return;
339
- }
340
- // If interactive mode is disabled or only one page, show simple results
341
- const totalPages = Math.ceil(result.total / limit);
342
- const shouldPaginate = options.interactive !== false && totalPages > 1;
343
- if (!shouldPaginate) {
344
- displayResults(result.packages, result.total, page, limit);
345
- console.log('\n💡 \x1b[1mQuick Actions:\x1b[0m');
346
- console.log(' Install: \x1b[36mprpm install <package-id>\x1b[0m');
347
- console.log(' More info: \x1b[36mprpm info <package-id>\x1b[0m');
348
- if (totalPages > 1) {
349
- console.log(`\n📄 \x1b[1mMore Results:\x1b[0m`);
350
- console.log(` Page ${page} of ${totalPages}`);
351
- if (page < totalPages) {
352
- console.log(` Next page: \x1b[36mprpm search "${query}" --page ${page + 1}\x1b[0m`);
353
- }
354
- console.log(` Interactive mode: \x1b[36mprpm search "${query}" --interactive\x1b[0m`);
355
- }
356
- // Always show webapp link
357
- const webappUrl = buildWebappUrl(query, options, page);
358
- console.log(`\n🌐 \x1b[1mView in Browser:\x1b[0m`);
359
- console.log(` ${webappUrl}`);
360
- if (page < totalPages) {
361
- const nextPageUrl = buildWebappUrl(query, options, page + 1);
362
- console.log(` Next page: ${nextPageUrl}`);
363
- }
364
- console.log();
365
- }
366
- else {
367
- // Interactive pagination mode
368
- await handlePagination(query, { ...options, limit }, client, searchOptions, result, registryUrl);
369
- }
370
- success = true;
371
- }
372
- catch (err) {
373
- error = err instanceof Error ? err.message : String(err);
374
- console.error(`\n❌ Search failed: ${error}`);
375
- console.log(` Registry: ${registryUrl}`);
376
- // Provide helpful hint if using localhost
377
- if (registryUrl.includes('localhost')) {
378
- console.log(`\n💡 Tip: You're using a local registry. Make sure it's running or update ~/.prpmrc`);
379
- console.log(` To use the production registry, remove the registryUrl from ~/.prpmrc`);
380
- }
381
- throw new errors_1.CLIError(`\n❌ Search failed: ${error}`, 1);
382
- }
383
- finally {
384
- await telemetry_1.telemetry.track({
385
- command: 'search',
386
- success,
387
- error,
388
- duration: Date.now() - startTime,
389
- data: {
390
- query: query.substring(0, 100),
391
- format: options.format,
392
- subtype: options.subtype,
393
- language: options.language,
394
- framework: options.framework,
395
- resultCount: success && result ? result.packages.length : 0,
396
- page: options.page,
397
- interactive: options.interactive,
398
- },
399
- });
400
- // Ensure telemetry is flushed before exit
401
- await telemetry_1.telemetry.shutdown();
402
- }
403
- }
404
- function createSearchCommand() {
405
- const command = new commander_1.Command('search');
406
- command
407
- .description('Search for packages in the registry')
408
- .argument('[query]', 'Search query (optional when using --format/--subtype or --author)')
409
- .option('--format <format>', 'Filter by package format (cursor, claude, continue, windsurf, copilot, kiro, agents.md, generic, mcp)')
410
- .option('--subtype <subtype>', 'Filter by package subtype (rule, agent, skill, slash-command, prompt, workflow, tool, template, collection, chatmode, hook)')
411
- .option('--author <username>', 'Filter by author username')
412
- .option('--language <language>', 'Filter by programming language (javascript, typescript, python, etc.)')
413
- .option('--framework <framework>', 'Filter by framework (react, nextjs, django, etc.)')
414
- .option('--limit <number>', 'Number of results per page', '20')
415
- .option('--page <number>', 'Page number (default: 1)', '1')
416
- .option('--interactive', 'Enable interactive pagination (default: true for multiple pages)', true)
417
- .option('--no-interactive', 'Disable interactive pagination')
418
- .action(async (query, options) => {
419
- const format = options.format;
420
- const subtype = options.subtype;
421
- const author = options.author;
422
- const limit = options.limit ? parseInt(options.limit, 10) : 20;
423
- const page = options.page ? parseInt(options.page, 10) : 1;
424
- const validFormats = ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'agents.md', 'generic', 'mcp'];
425
- const validSubtypes = ['rule', 'agent', 'skill', 'slash-command', 'prompt', 'collection', 'chatmode', 'tool', 'hook'];
426
- if (options.format && !validFormats.includes(format)) {
427
- console.error(`❌ Format must be one of: ${validFormats.join(', ')}`);
428
- throw new errors_1.CLIError(`❌ Format must be one of: ${validFormats.join(', ')}`, 1);
429
- }
430
- if (options.subtype && !validSubtypes.includes(subtype)) {
431
- console.error(`❌ Subtype must be one of: ${validSubtypes.join(', ')}`);
432
- console.log(`\n💡 Examples:`);
433
- console.log(` prpm search postgres --subtype skill`);
434
- console.log(` prpm search debugging --subtype agent`);
435
- console.log(` prpm search refactor --subtype slash-command`);
436
- console.log(` prpm search react --subtype rule`);
437
- console.log(` prpm search --subtype slash-command # List all slash commands`);
438
- console.log(` prpm search --subtype skill # List all skills`);
439
- console.log(` prpm search --format claude # List all Claude packages`);
440
- console.log(` prpm search --author prpm # List packages by @prpm`);
441
- throw new errors_1.CLIError(`❌ Subtype must be one of: ${validSubtypes.join(', ')}`, 1);
442
- }
443
- await handleSearch(query || '', { format, subtype, author, language: options.language, framework: options.framework, limit, page, interactive: options.interactive });
444
- });
445
- return command;
446
- }
@@ -1,147 +0,0 @@
1
- "use strict";
2
- /**
3
- * Starred command implementation - List user's starred packages and collections
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.handleStarred = handleStarred;
7
- exports.createStarredCommand = createStarredCommand;
8
- const commander_1 = require("commander");
9
- const registry_client_1 = require("@pr-pm/registry-client");
10
- const user_config_1 = require("../core/user-config");
11
- const telemetry_1 = require("../core/telemetry");
12
- const errors_1 = require("../core/errors");
13
- async function handleStarred(options) {
14
- let success = false;
15
- try {
16
- const config = await (0, user_config_1.getConfig)();
17
- const token = config.token;
18
- if (!token) {
19
- throw new errors_1.CLIError('You must be logged in to view starred items. Run `prpm login` first.');
20
- }
21
- const registryUrl = config.registryUrl || process.env.PRPM_REGISTRY_URL || 'https://registry.prpm.dev';
22
- const client = (0, registry_client_1.getRegistryClient)({ registryUrl, token });
23
- // Determine what to show (both by default)
24
- const showPackages = options.packages || (!options.packages && !options.collections);
25
- const showCollections = options.collections || (!options.packages && !options.collections);
26
- const limit = options.limit || 100;
27
- // Fetch starred packages
28
- let packages = [];
29
- if (showPackages) {
30
- try {
31
- const response = await fetch(`${registryUrl}/api/v1/packages/starred?limit=${limit}`, {
32
- headers: {
33
- Authorization: `Bearer ${token}`,
34
- },
35
- });
36
- if (!response.ok) {
37
- throw new Error(`Failed to fetch starred packages: ${response.statusText}`);
38
- }
39
- const data = await response.json();
40
- packages = data.packages || [];
41
- // Filter by format if specified
42
- if (options.format) {
43
- packages = packages.filter((pkg) => pkg.format === options.format);
44
- }
45
- }
46
- catch (error) {
47
- console.error('Failed to fetch starred packages:', error);
48
- }
49
- }
50
- // Fetch starred collections
51
- let collections = [];
52
- if (showCollections) {
53
- try {
54
- const response = await fetch(`${registryUrl}/api/v1/collections/starred?limit=${limit}`, {
55
- headers: {
56
- Authorization: `Bearer ${token}`,
57
- },
58
- });
59
- if (!response.ok) {
60
- throw new Error(`Failed to fetch starred collections: ${response.statusText}`);
61
- }
62
- const data = await response.json();
63
- collections = data.collections || [];
64
- }
65
- catch (error) {
66
- console.error('Failed to fetch starred collections:', error);
67
- }
68
- }
69
- // Display results
70
- if (packages.length === 0 && collections.length === 0) {
71
- console.log('\nNo starred items found.');
72
- if (options.format) {
73
- console.log(`Try removing the --format filter to see all starred packages.`);
74
- }
75
- return;
76
- }
77
- console.log('');
78
- // Display packages
79
- if (packages.length > 0) {
80
- console.log(`📦 Starred Packages (${packages.length}):`);
81
- console.log('');
82
- for (const pkg of packages) {
83
- const formatBadge = `[${pkg.format || 'generic'}]`.padEnd(12);
84
- const stars = `⭐ ${pkg.stars || 0}`.padEnd(8);
85
- const downloads = `⬇️ ${(pkg.total_downloads || 0).toLocaleString()}`;
86
- console.log(` ${formatBadge} ${pkg.name}`);
87
- console.log(` ${stars} ${downloads}`);
88
- if (pkg.description) {
89
- const desc = pkg.description.length > 80
90
- ? pkg.description.substring(0, 77) + '...'
91
- : pkg.description;
92
- console.log(` ${desc}`);
93
- }
94
- console.log('');
95
- }
96
- }
97
- // Display collections
98
- if (collections.length > 0) {
99
- console.log(`📚 Starred Collections (${collections.length}):`);
100
- console.log('');
101
- for (const collection of collections) {
102
- const stars = `⭐ ${collection.stars || 0}`.padEnd(8);
103
- const packages = `📦 ${collection.package_count || 0} packages`;
104
- console.log(` ${collection.scope}/${collection.name_slug}`);
105
- console.log(` ${stars} ${packages}`);
106
- if (collection.description) {
107
- const desc = collection.description.length > 80
108
- ? collection.description.substring(0, 77) + '...'
109
- : collection.description;
110
- console.log(` ${desc}`);
111
- }
112
- console.log('');
113
- }
114
- }
115
- console.log(`\nTotal: ${packages.length + collections.length} starred items`);
116
- console.log('');
117
- success = true;
118
- }
119
- catch (error) {
120
- if (error instanceof errors_1.CLIError) {
121
- throw error;
122
- }
123
- throw new errors_1.CLIError(`Failed to fetch starred items: ${error instanceof Error ? error.message : String(error)}`);
124
- }
125
- finally {
126
- await telemetry_1.telemetry.track({
127
- command: 'starred',
128
- success,
129
- data: {
130
- showPackages: options.packages,
131
- showCollections: options.collections,
132
- format: options.format,
133
- },
134
- });
135
- }
136
- }
137
- function createStarredCommand() {
138
- const command = new commander_1.Command('starred');
139
- command
140
- .description('List your starred packages and collections')
141
- .option('--packages', 'Show only starred packages')
142
- .option('--collections', 'Show only starred collections')
143
- .option('--format <format>', 'Filter packages by format (cursor, claude, continue, windsurf, etc.)')
144
- .option('--limit <number>', 'Maximum number of items to fetch (default: 100)', (val) => parseInt(val, 10))
145
- .action(handleStarred);
146
- return command;
147
- }