prpm 0.0.1 ā 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/__tests__/e2e/test-helpers.js +150 -0
- package/dist/commands/collections.js +248 -51
- package/dist/commands/index.js +110 -51
- package/dist/commands/info.js +8 -7
- package/dist/commands/install.js +270 -45
- package/dist/commands/list.js +91 -14
- package/dist/commands/login.js +86 -24
- package/dist/commands/outdated.js +3 -2
- package/dist/commands/publish.js +98 -41
- package/dist/commands/remove.js +43 -10
- package/dist/commands/schema.js +37 -0
- package/dist/commands/search.js +263 -38
- package/dist/commands/trending.js +4 -3
- package/dist/commands/uninstall.js +77 -0
- package/dist/commands/update.js +3 -2
- package/dist/commands/upgrade.js +3 -2
- package/dist/commands/whoami.js +25 -1
- package/dist/core/claude-config.js +91 -0
- package/dist/core/cursor-config.js +130 -0
- package/dist/core/filesystem.js +30 -0
- package/dist/core/lockfile.js +57 -0
- package/dist/core/marketplace-converter.js +198 -0
- package/dist/core/schema-validator.js +74 -0
- package/dist/core/telemetry.js +5 -0
- package/dist/index.js +6 -7
- package/dist/types/registry.js +5 -0
- package/package.json +15 -7
- package/dist/commands/add.js +0 -107
- package/dist/commands/deps.js +0 -92
- package/dist/core/config.js +0 -91
package/dist/commands/search.js
CHANGED
|
@@ -2,6 +2,39 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Search command - Search for packages in the registry
|
|
4
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
|
+
})();
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
exports.handleSearch = handleSearch;
|
|
7
40
|
exports.createSearchCommand = createSearchCommand;
|
|
@@ -9,6 +42,7 @@ const commander_1 = require("commander");
|
|
|
9
42
|
const registry_client_1 = require("@prpm/registry-client");
|
|
10
43
|
const user_config_1 = require("../core/user-config");
|
|
11
44
|
const telemetry_1 = require("../core/telemetry");
|
|
45
|
+
const readline = __importStar(require("readline"));
|
|
12
46
|
/**
|
|
13
47
|
* Get icon for package type
|
|
14
48
|
*/
|
|
@@ -16,6 +50,9 @@ function getTypeIcon(type) {
|
|
|
16
50
|
const icons = {
|
|
17
51
|
skill: 'š',
|
|
18
52
|
agent: 'š¤',
|
|
53
|
+
command: 'ā”',
|
|
54
|
+
'slash-command': 'ā”',
|
|
55
|
+
'claude-slash-command': 'ā”',
|
|
19
56
|
rule: 'š',
|
|
20
57
|
plugin: 'š',
|
|
21
58
|
prompt: 'š¬',
|
|
@@ -33,6 +70,10 @@ function getTypeLabel(type) {
|
|
|
33
70
|
const labels = {
|
|
34
71
|
skill: 'Skill',
|
|
35
72
|
agent: 'Agent',
|
|
73
|
+
command: 'Slash Command',
|
|
74
|
+
'slash-command': 'Slash Command',
|
|
75
|
+
'claude-slash-command': 'Slash Command',
|
|
76
|
+
'claude-agent': 'Agent',
|
|
36
77
|
rule: 'Rule',
|
|
37
78
|
plugin: 'Plugin',
|
|
38
79
|
prompt: 'Prompt',
|
|
@@ -49,9 +90,14 @@ function getTypeLabel(type) {
|
|
|
49
90
|
function mapTypeToRegistry(cliType) {
|
|
50
91
|
const typeMap = {
|
|
51
92
|
rule: { type: 'cursor', tags: ['cursor-rule'] },
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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' },
|
|
55
101
|
plugin: { type: 'generic', tags: ['plugin'] },
|
|
56
102
|
prompt: { type: 'generic', tags: ['prompt'] },
|
|
57
103
|
workflow: { type: 'generic', tags: ['workflow'] },
|
|
@@ -60,32 +106,183 @@ function mapTypeToRegistry(cliType) {
|
|
|
60
106
|
};
|
|
61
107
|
return typeMap[cliType] || {};
|
|
62
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Build webapp URL for search results
|
|
111
|
+
*/
|
|
112
|
+
function buildWebappUrl(query, options, page = 1) {
|
|
113
|
+
const baseUrl = process.env.PRPM_WEBAPP_URL || 'https://prpm.dev';
|
|
114
|
+
const params = new URLSearchParams();
|
|
115
|
+
if (query)
|
|
116
|
+
params.append('q', query);
|
|
117
|
+
if (options.type)
|
|
118
|
+
params.append('type', options.type);
|
|
119
|
+
if (options.author)
|
|
120
|
+
params.append('author', options.author);
|
|
121
|
+
if (page > 1)
|
|
122
|
+
params.append('page', page.toString());
|
|
123
|
+
return `${baseUrl}/search?${params.toString()}`;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Display search results
|
|
127
|
+
*/
|
|
128
|
+
function displayResults(packages, total, page, limit) {
|
|
129
|
+
const startIdx = (page - 1) * limit + 1;
|
|
130
|
+
const endIdx = Math.min(page * limit, total);
|
|
131
|
+
console.log('\n' + 'ā'.repeat(80));
|
|
132
|
+
console.log(`š¦ Results ${startIdx}-${endIdx} of ${total}`.padEnd(80));
|
|
133
|
+
console.log('ā'.repeat(80) + '\n');
|
|
134
|
+
packages.forEach((pkg, idx) => {
|
|
135
|
+
const num = startIdx + idx;
|
|
136
|
+
const rating = pkg.rating_average ? `ā ${pkg.rating_average.toFixed(1)}` : '';
|
|
137
|
+
const downloads = pkg.total_downloads >= 1000
|
|
138
|
+
? `${(pkg.total_downloads / 1000).toFixed(1)}k`
|
|
139
|
+
: pkg.total_downloads;
|
|
140
|
+
const typeIcon = getTypeIcon(pkg.type);
|
|
141
|
+
const typeLabel = getTypeLabel(pkg.type);
|
|
142
|
+
// Add verified badge
|
|
143
|
+
let verifiedBadge = '';
|
|
144
|
+
if (pkg.featured || pkg.official || pkg.verified) {
|
|
145
|
+
verifiedBadge = ' | ā
Verified';
|
|
146
|
+
}
|
|
147
|
+
console.log(`\x1b[1m${num}. ${pkg.name}\x1b[0m ${rating}`);
|
|
148
|
+
console.log(` ${pkg.description || 'No description'}`);
|
|
149
|
+
console.log(` ${typeIcon} ${typeLabel} | š„ ${downloads} downloads | š·ļø ${pkg.tags.slice(0, 3).join(', ')}${verifiedBadge}`);
|
|
150
|
+
console.log();
|
|
151
|
+
});
|
|
152
|
+
console.log('ā'.repeat(80));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Prompt user for pagination action
|
|
156
|
+
*/
|
|
157
|
+
function promptUser() {
|
|
158
|
+
return new Promise((resolve) => {
|
|
159
|
+
const rl = readline.createInterface({
|
|
160
|
+
input: process.stdin,
|
|
161
|
+
output: process.stdout,
|
|
162
|
+
});
|
|
163
|
+
rl.question('', (answer) => {
|
|
164
|
+
rl.close();
|
|
165
|
+
resolve(answer.trim().toLowerCase());
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Interactive pagination handler
|
|
171
|
+
*/
|
|
172
|
+
async function handlePagination(query, options, client, searchOptions, initialResult, webappBaseUrl) {
|
|
173
|
+
let currentPage = 1;
|
|
174
|
+
let result = initialResult;
|
|
175
|
+
const totalPages = Math.ceil(result.total / options.limit);
|
|
176
|
+
while (true) {
|
|
177
|
+
// Display current page
|
|
178
|
+
displayResults(result.packages, result.total, currentPage, options.limit);
|
|
179
|
+
// Show navigation options
|
|
180
|
+
console.log('\nš” \x1b[1mOptions:\x1b[0m');
|
|
181
|
+
if (currentPage < totalPages) {
|
|
182
|
+
console.log(' \x1b[36mn\x1b[0m - Next page');
|
|
183
|
+
}
|
|
184
|
+
if (currentPage > 1) {
|
|
185
|
+
console.log(' \x1b[36mp\x1b[0m - Previous page');
|
|
186
|
+
}
|
|
187
|
+
console.log(' \x1b[36m1-' + result.packages.length + '\x1b[0m - Install package by number');
|
|
188
|
+
console.log(' \x1b[36mw\x1b[0m - View in web browser');
|
|
189
|
+
console.log(' \x1b[36mq\x1b[0m - Quit');
|
|
190
|
+
// Show webapp link
|
|
191
|
+
const webappUrl = buildWebappUrl(query, options, currentPage);
|
|
192
|
+
console.log(`\nš \x1b[2mView in browser: ${webappUrl}\x1b[0m`);
|
|
193
|
+
process.stdout.write('\nš ');
|
|
194
|
+
const input = await promptUser();
|
|
195
|
+
if (input === 'q' || input === 'quit' || input === 'exit') {
|
|
196
|
+
console.log('\n⨠Happy coding!\n');
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
if (input === 'n' || input === 'next') {
|
|
200
|
+
if (currentPage < totalPages) {
|
|
201
|
+
currentPage++;
|
|
202
|
+
const offset = (currentPage - 1) * options.limit;
|
|
203
|
+
result = await client.search(query || '', { ...searchOptions, offset });
|
|
204
|
+
console.clear();
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
console.log('\nā Already on last page');
|
|
208
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
209
|
+
console.clear();
|
|
210
|
+
}
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (input === 'p' || input === 'prev' || input === 'previous') {
|
|
214
|
+
if (currentPage > 1) {
|
|
215
|
+
currentPage--;
|
|
216
|
+
const offset = (currentPage - 1) * options.limit;
|
|
217
|
+
result = await client.search(query || '', { ...searchOptions, offset });
|
|
218
|
+
console.clear();
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
console.log('\nā Already on first page');
|
|
222
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
223
|
+
console.clear();
|
|
224
|
+
}
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (input === 'w' || input === 'web' || input === 'browser') {
|
|
228
|
+
const url = buildWebappUrl(query, options, currentPage);
|
|
229
|
+
console.log(`\nš Opening: ${url}`);
|
|
230
|
+
console.log(' (Copy and paste this URL into your browser)\n');
|
|
231
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
232
|
+
console.clear();
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
// Check if input is a number for installation
|
|
236
|
+
const num = parseInt(input, 10);
|
|
237
|
+
if (!isNaN(num) && num >= 1 && num <= result.packages.length) {
|
|
238
|
+
const pkg = result.packages[num - 1];
|
|
239
|
+
console.log(`\nš¦ To install: \x1b[36mprpm install ${pkg.name}\x1b[0m`);
|
|
240
|
+
console.log(` More info: \x1b[36mprpm info ${pkg.name}\x1b[0m\n`);
|
|
241
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
242
|
+
console.clear();
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
console.log('\nā Invalid option. Try again.');
|
|
246
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
247
|
+
console.clear();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
63
250
|
async function handleSearch(query, options) {
|
|
64
251
|
const startTime = Date.now();
|
|
65
252
|
let success = false;
|
|
66
253
|
let error;
|
|
67
254
|
let result = null;
|
|
255
|
+
let registryUrl = '';
|
|
68
256
|
try {
|
|
69
|
-
// Allow empty query when filtering by type
|
|
257
|
+
// Allow empty query when filtering by type or author
|
|
70
258
|
if (query) {
|
|
71
259
|
console.log(`š Searching for "${query}"...`);
|
|
72
260
|
}
|
|
73
261
|
else if (options.type) {
|
|
74
262
|
console.log(`š Listing ${options.type} packages...`);
|
|
75
263
|
}
|
|
264
|
+
else if (options.author) {
|
|
265
|
+
console.log(`š Listing packages by @${options.author}...`);
|
|
266
|
+
}
|
|
76
267
|
else {
|
|
77
|
-
console.log('ā Please provide a search query or use --type to filter
|
|
268
|
+
console.log('ā Please provide a search query or use --type/--author to filter');
|
|
78
269
|
console.log('\nš” Examples:');
|
|
79
270
|
console.log(' prpm search react');
|
|
80
271
|
console.log(' prpm search --type skill');
|
|
272
|
+
console.log(' prpm search --author prpm');
|
|
81
273
|
console.log(' prpm search react --type rule');
|
|
82
274
|
return;
|
|
83
275
|
}
|
|
84
276
|
const config = await (0, user_config_1.getConfig)();
|
|
277
|
+
registryUrl = config.registryUrl || 'https://registry.prpm.dev';
|
|
85
278
|
const client = (0, registry_client_1.getRegistryClient)(config);
|
|
86
279
|
// Map CLI type to registry schema
|
|
280
|
+
const limit = options.limit || 20;
|
|
281
|
+
const page = options.page || 1;
|
|
282
|
+
const offset = (page - 1) * limit;
|
|
87
283
|
const searchOptions = {
|
|
88
|
-
limit
|
|
284
|
+
limit,
|
|
285
|
+
offset,
|
|
89
286
|
};
|
|
90
287
|
if (options.type) {
|
|
91
288
|
const mapped = mapTypeToRegistry(options.type);
|
|
@@ -96,47 +293,62 @@ async function handleSearch(query, options) {
|
|
|
96
293
|
searchOptions.tags = mapped.tags;
|
|
97
294
|
}
|
|
98
295
|
}
|
|
296
|
+
if (options.author) {
|
|
297
|
+
searchOptions.author = options.author;
|
|
298
|
+
}
|
|
99
299
|
result = await client.search(query || '', searchOptions);
|
|
100
|
-
if (result.packages.length === 0) {
|
|
300
|
+
if (!result || result.packages.length === 0) {
|
|
101
301
|
console.log('\nā No packages found');
|
|
102
302
|
console.log(`\nTry:`);
|
|
103
303
|
console.log(` - Broadening your search terms`);
|
|
104
304
|
console.log(` - Checking spelling`);
|
|
105
305
|
console.log(` - Browsing trending: prpm trending`);
|
|
306
|
+
// Suggest webapp even if no results
|
|
307
|
+
const webappUrl = buildWebappUrl(query, options);
|
|
308
|
+
console.log(`\nš View in browser: ${webappUrl}`);
|
|
106
309
|
return;
|
|
107
310
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
311
|
+
// If interactive mode is disabled or only one page, show simple results
|
|
312
|
+
const totalPages = Math.ceil(result.total / limit);
|
|
313
|
+
const shouldPaginate = options.interactive !== false && totalPages > 1;
|
|
314
|
+
if (!shouldPaginate) {
|
|
315
|
+
displayResults(result.packages, result.total, page, limit);
|
|
316
|
+
console.log('\nš” \x1b[1mQuick Actions:\x1b[0m');
|
|
317
|
+
console.log(' Install: \x1b[36mprpm install <package-id>\x1b[0m');
|
|
318
|
+
console.log(' More info: \x1b[36mprpm info <package-id>\x1b[0m');
|
|
319
|
+
if (totalPages > 1) {
|
|
320
|
+
console.log(`\nš \x1b[1mMore Results:\x1b[0m`);
|
|
321
|
+
console.log(` Page ${page} of ${totalPages}`);
|
|
322
|
+
if (page < totalPages) {
|
|
323
|
+
console.log(` Next page: \x1b[36mprpm search "${query}" --page ${page + 1}\x1b[0m`);
|
|
324
|
+
}
|
|
325
|
+
console.log(` Interactive mode: \x1b[36mprpm search "${query}" --interactive\x1b[0m`);
|
|
326
|
+
}
|
|
327
|
+
// Always show webapp link
|
|
328
|
+
const webappUrl = buildWebappUrl(query, options, page);
|
|
329
|
+
console.log(`\nš \x1b[1mView in Browser:\x1b[0m`);
|
|
330
|
+
console.log(` ${webappUrl}`);
|
|
331
|
+
if (page < totalPages) {
|
|
332
|
+
const nextPageUrl = buildWebappUrl(query, options, page + 1);
|
|
333
|
+
console.log(` Next page: ${nextPageUrl}`);
|
|
334
|
+
}
|
|
126
335
|
console.log();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
console.log(`\n Showing ${result.packages.length} of ${result.total} results`);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
// Interactive pagination mode
|
|
339
|
+
await handlePagination(query, { ...options, limit }, client, searchOptions, result, registryUrl);
|
|
132
340
|
}
|
|
133
341
|
success = true;
|
|
134
342
|
}
|
|
135
343
|
catch (err) {
|
|
136
344
|
error = err instanceof Error ? err.message : String(err);
|
|
137
345
|
console.error(`\nā Search failed: ${error}`);
|
|
138
|
-
console.log(
|
|
139
|
-
|
|
346
|
+
console.log(` Registry: ${registryUrl}`);
|
|
347
|
+
// Provide helpful hint if using localhost
|
|
348
|
+
if (registryUrl.includes('localhost')) {
|
|
349
|
+
console.log(`\nš” Tip: You're using a local registry. Make sure it's running or update ~/.prpmrc`);
|
|
350
|
+
console.log(` To use the production registry, remove the registryUrl from ~/.prpmrc`);
|
|
351
|
+
}
|
|
140
352
|
process.exit(1);
|
|
141
353
|
}
|
|
142
354
|
finally {
|
|
@@ -149,31 +361,44 @@ async function handleSearch(query, options) {
|
|
|
149
361
|
query: query.substring(0, 100),
|
|
150
362
|
type: options.type,
|
|
151
363
|
resultCount: success && result ? result.packages.length : 0,
|
|
364
|
+
page: options.page,
|
|
365
|
+
interactive: options.interactive,
|
|
152
366
|
},
|
|
153
367
|
});
|
|
368
|
+
// Ensure telemetry is flushed before exit
|
|
369
|
+
await telemetry_1.telemetry.shutdown();
|
|
154
370
|
}
|
|
155
371
|
}
|
|
156
372
|
function createSearchCommand() {
|
|
157
373
|
const command = new commander_1.Command('search');
|
|
158
374
|
command
|
|
159
375
|
.description('Search for packages in the registry')
|
|
160
|
-
.argument('[query]', 'Search query (optional when using --type)')
|
|
161
|
-
.option('--type <type>', 'Filter by package type (skill, agent, rule, plugin, prompt, workflow, tool, template, mcp)')
|
|
162
|
-
.option('--
|
|
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)')
|
|
378
|
+
.option('--author <username>', 'Filter by author username')
|
|
379
|
+
.option('--limit <number>', 'Number of results per page', '20')
|
|
380
|
+
.option('--page <number>', 'Page number (default: 1)', '1')
|
|
381
|
+
.option('--interactive', 'Enable interactive pagination (default: true for multiple pages)', true)
|
|
382
|
+
.option('--no-interactive', 'Disable interactive pagination')
|
|
163
383
|
.action(async (query, options) => {
|
|
164
384
|
const type = options.type;
|
|
165
|
-
const
|
|
166
|
-
const
|
|
385
|
+
const author = options.author;
|
|
386
|
+
const limit = options.limit ? parseInt(options.limit, 10) : 20;
|
|
387
|
+
const page = options.page ? parseInt(options.page, 10) : 1;
|
|
388
|
+
const validTypes = ['skill', 'agent', 'command', 'slash-command', 'rule', 'plugin', 'prompt', 'workflow', 'tool', 'template', 'mcp'];
|
|
167
389
|
if (options.type && !validTypes.includes(type)) {
|
|
168
390
|
console.error(`ā Type must be one of: ${validTypes.join(', ')}`);
|
|
169
391
|
console.log(`\nš” Examples:`);
|
|
170
392
|
console.log(` prpm search postgres --type skill`);
|
|
171
393
|
console.log(` prpm search debugging --type agent`);
|
|
394
|
+
console.log(` prpm search refactor --type command`);
|
|
172
395
|
console.log(` prpm search react --type rule`);
|
|
396
|
+
console.log(` prpm search --type command # List all slash commands`);
|
|
173
397
|
console.log(` prpm search --type skill # List all skills`);
|
|
398
|
+
console.log(` prpm search --author prpm # List packages by @prpm`);
|
|
174
399
|
process.exit(1);
|
|
175
400
|
}
|
|
176
|
-
await handleSearch(query || '', { type, limit });
|
|
401
|
+
await handleSearch(query || '', { type, author, limit, page, interactive: options.interactive });
|
|
177
402
|
});
|
|
178
403
|
return command;
|
|
179
404
|
}
|
|
@@ -29,9 +29,9 @@ async function handleTrending(options) {
|
|
|
29
29
|
const downloads = pkg.total_downloads >= 1000
|
|
30
30
|
? `${(pkg.total_downloads / 1000).toFixed(1)}k`
|
|
31
31
|
: pkg.total_downloads;
|
|
32
|
-
console.log(`${index + 1}. [${verified}] ${pkg.
|
|
32
|
+
console.log(`${index + 1}. [${verified}] ${pkg.name} ${rating}`);
|
|
33
33
|
console.log(` ${pkg.description || 'No description'}`);
|
|
34
|
-
console.log(`
|
|
34
|
+
console.log(` š„ ${downloads} downloads`);
|
|
35
35
|
console.log();
|
|
36
36
|
});
|
|
37
37
|
console.log(`š” Install a package: prpm install <package-id>`);
|
|
@@ -54,6 +54,7 @@ async function handleTrending(options) {
|
|
|
54
54
|
limit: options.limit || 10,
|
|
55
55
|
},
|
|
56
56
|
});
|
|
57
|
+
await telemetry_1.telemetry.shutdown();
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
function createTrendingCommand() {
|
|
@@ -64,7 +65,7 @@ function createTrendingCommand() {
|
|
|
64
65
|
.option('--limit <number>', 'Number of packages to show', '10')
|
|
65
66
|
.action(async (options) => {
|
|
66
67
|
const type = options.type;
|
|
67
|
-
const limit = parseInt(options.limit, 10);
|
|
68
|
+
const limit = options.limit ? parseInt(options.limit, 10) : 10;
|
|
68
69
|
if (options.type && !['cursor', 'claude', 'continue', 'windsurf', 'generic'].includes(type)) {
|
|
69
70
|
console.error('ā Type must be one of: cursor, claude, continue, windsurf, generic');
|
|
70
71
|
process.exit(1);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Uninstall command implementation
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.handleUninstall = handleUninstall;
|
|
7
|
+
exports.createUninstallCommand = createUninstallCommand;
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const lockfile_1 = require("../core/lockfile");
|
|
10
|
+
const filesystem_1 = require("../core/filesystem");
|
|
11
|
+
const fs_1 = require("fs");
|
|
12
|
+
/**
|
|
13
|
+
* Handle the uninstall command
|
|
14
|
+
*/
|
|
15
|
+
async function handleUninstall(name) {
|
|
16
|
+
try {
|
|
17
|
+
console.log(`šļø Uninstalling package: ${name}`);
|
|
18
|
+
// Remove from lockfile and get package info
|
|
19
|
+
const pkg = await (0, lockfile_1.removePackage)(name);
|
|
20
|
+
if (!pkg) {
|
|
21
|
+
console.error(`ā Package "${name}" not found`);
|
|
22
|
+
process.exit(1);
|
|
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);
|
|
31
|
+
const fileExtension = pkg.format === 'cursor' ? 'mdc' : 'md';
|
|
32
|
+
// Strip author namespace to get just the package name
|
|
33
|
+
const packageName = (0, filesystem_1.stripAuthorNamespace)(name);
|
|
34
|
+
// Try single file first
|
|
35
|
+
const singleFilePath = `${destDir}/${packageName}.${fileExtension}`;
|
|
36
|
+
if (await (0, filesystem_1.fileExists)(singleFilePath)) {
|
|
37
|
+
// Single file package
|
|
38
|
+
await (0, filesystem_1.deleteFile)(singleFilePath);
|
|
39
|
+
console.log(` šļø Deleted file: ${singleFilePath}`);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Try multi-file package directory
|
|
43
|
+
const packageDir = `${destDir}/${packageName}`;
|
|
44
|
+
try {
|
|
45
|
+
const stats = await fs_1.promises.stat(packageDir);
|
|
46
|
+
if (stats.isDirectory()) {
|
|
47
|
+
await fs_1.promises.rm(packageDir, { recursive: true, force: true });
|
|
48
|
+
console.log(` šļø Deleted directory: ${packageDir}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const err = error;
|
|
53
|
+
if (err.code !== 'ENOENT') {
|
|
54
|
+
console.warn(` ā ļø Could not delete package files: ${err.message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
console.log(`ā
Successfully uninstalled ${name}`);
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(`ā Failed to uninstall package: ${error}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Create the uninstall command
|
|
68
|
+
*/
|
|
69
|
+
function createUninstallCommand() {
|
|
70
|
+
const command = new commander_1.Command('uninstall');
|
|
71
|
+
command
|
|
72
|
+
.description('Uninstall a prompt package')
|
|
73
|
+
.argument('<id>', 'Package ID to uninstall')
|
|
74
|
+
.alias('remove') // Keep 'remove' as an alias for backwards compatibility
|
|
75
|
+
.action(handleUninstall);
|
|
76
|
+
return command;
|
|
77
|
+
}
|
package/dist/commands/update.js
CHANGED
|
@@ -8,7 +8,7 @@ exports.createUpdateCommand = createUpdateCommand;
|
|
|
8
8
|
const commander_1 = require("commander");
|
|
9
9
|
const registry_client_1 = require("@prpm/registry-client");
|
|
10
10
|
const user_config_1 = require("../core/user-config");
|
|
11
|
-
const
|
|
11
|
+
const lockfile_1 = require("../core/lockfile");
|
|
12
12
|
const install_1 = require("./install");
|
|
13
13
|
const telemetry_1 = require("../core/telemetry");
|
|
14
14
|
/**
|
|
@@ -22,7 +22,7 @@ async function handleUpdate(packageName, options = {}) {
|
|
|
22
22
|
try {
|
|
23
23
|
const config = await (0, user_config_1.getConfig)();
|
|
24
24
|
const client = (0, registry_client_1.getRegistryClient)(config);
|
|
25
|
-
const installedPackages = await (0,
|
|
25
|
+
const installedPackages = await (0, lockfile_1.listPackages)();
|
|
26
26
|
if (installedPackages.length === 0) {
|
|
27
27
|
console.log('No packages installed.');
|
|
28
28
|
success = true;
|
|
@@ -92,6 +92,7 @@ async function handleUpdate(packageName, options = {}) {
|
|
|
92
92
|
updatedCount,
|
|
93
93
|
},
|
|
94
94
|
});
|
|
95
|
+
await telemetry_1.telemetry.shutdown();
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
/**
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -8,7 +8,7 @@ exports.createUpgradeCommand = createUpgradeCommand;
|
|
|
8
8
|
const commander_1 = require("commander");
|
|
9
9
|
const registry_client_1 = require("@prpm/registry-client");
|
|
10
10
|
const user_config_1 = require("../core/user-config");
|
|
11
|
-
const
|
|
11
|
+
const lockfile_1 = require("../core/lockfile");
|
|
12
12
|
const install_1 = require("./install");
|
|
13
13
|
const telemetry_1 = require("../core/telemetry");
|
|
14
14
|
/**
|
|
@@ -22,7 +22,7 @@ async function handleUpgrade(packageName, options = {}) {
|
|
|
22
22
|
try {
|
|
23
23
|
const config = await (0, user_config_1.getConfig)();
|
|
24
24
|
const client = (0, registry_client_1.getRegistryClient)(config);
|
|
25
|
-
const installedPackages = await (0,
|
|
25
|
+
const installedPackages = await (0, lockfile_1.listPackages)();
|
|
26
26
|
if (installedPackages.length === 0) {
|
|
27
27
|
console.log('No packages installed.');
|
|
28
28
|
success = true;
|
|
@@ -91,6 +91,7 @@ async function handleUpgrade(packageName, options = {}) {
|
|
|
91
91
|
upgradedCount,
|
|
92
92
|
},
|
|
93
93
|
});
|
|
94
|
+
await telemetry_1.telemetry.shutdown();
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
/**
|
package/dist/commands/whoami.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.handleWhoami = handleWhoami;
|
|
|
7
7
|
exports.createWhoamiCommand = createWhoamiCommand;
|
|
8
8
|
const commander_1 = require("commander");
|
|
9
9
|
const user_config_1 = require("../core/user-config");
|
|
10
|
+
const registry_client_1 = require("@prpm/registry-client");
|
|
10
11
|
const telemetry_1 = require("../core/telemetry");
|
|
11
12
|
/**
|
|
12
13
|
* Show current logged-in user
|
|
@@ -23,7 +24,29 @@ async function handleWhoami() {
|
|
|
23
24
|
success = true;
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
+
// Fetch user profile from registry
|
|
28
|
+
try {
|
|
29
|
+
const client = (0, registry_client_1.getRegistryClient)(config);
|
|
30
|
+
const userProfile = await client.getUserProfile(config.username);
|
|
31
|
+
console.log(`\nš¤ ${userProfile.username}${userProfile.verified_author ? ' ā' : ''}`);
|
|
32
|
+
if (userProfile.stats) {
|
|
33
|
+
console.log(`\nš Stats:`);
|
|
34
|
+
console.log(` š¦ Packages: ${userProfile.stats.total_packages}`);
|
|
35
|
+
console.log(` ā¬ļø Downloads: ${userProfile.stats.total_downloads.toLocaleString()}`);
|
|
36
|
+
}
|
|
37
|
+
// TODO: Add organizations when implemented in the database
|
|
38
|
+
if (userProfile.organizations && userProfile.organizations.length > 0) {
|
|
39
|
+
console.log(`\nš¢ Organizations:`);
|
|
40
|
+
userProfile.organizations.forEach((org) => {
|
|
41
|
+
console.log(` ⢠${org.name} (${org.role})`);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
console.log('');
|
|
45
|
+
}
|
|
46
|
+
catch (apiError) {
|
|
47
|
+
// Fallback to simple username display if API call fails
|
|
48
|
+
console.log(`${config.username}`);
|
|
49
|
+
}
|
|
27
50
|
success = true;
|
|
28
51
|
}
|
|
29
52
|
catch (err) {
|
|
@@ -39,6 +62,7 @@ async function handleWhoami() {
|
|
|
39
62
|
error,
|
|
40
63
|
duration: Date.now() - startTime,
|
|
41
64
|
});
|
|
65
|
+
await telemetry_1.telemetry.shutdown();
|
|
42
66
|
}
|
|
43
67
|
}
|
|
44
68
|
/**
|