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.
- package/dist/index.js +14257 -109
- package/package.json +11 -9
- package/dist/__tests__/e2e/test-helpers.js +0 -153
- package/dist/commands/buy-credits.js +0 -224
- package/dist/commands/catalog.js +0 -365
- package/dist/commands/collections.js +0 -655
- package/dist/commands/config.js +0 -161
- package/dist/commands/credits.js +0 -186
- package/dist/commands/index.js +0 -184
- package/dist/commands/info.js +0 -78
- package/dist/commands/init.js +0 -684
- package/dist/commands/install.js +0 -829
- package/dist/commands/list.js +0 -198
- package/dist/commands/login.js +0 -316
- package/dist/commands/outdated.js +0 -130
- package/dist/commands/playground.js +0 -637
- package/dist/commands/popular.js +0 -33
- package/dist/commands/publish.js +0 -803
- package/dist/commands/schema.js +0 -41
- package/dist/commands/search.js +0 -446
- package/dist/commands/starred.js +0 -147
- package/dist/commands/subscribe.js +0 -211
- package/dist/commands/telemetry.js +0 -104
- package/dist/commands/trending.js +0 -86
- package/dist/commands/uninstall.js +0 -120
- package/dist/commands/update.js +0 -121
- package/dist/commands/upgrade.js +0 -121
- package/dist/commands/whoami.js +0 -83
- package/dist/core/claude-config.js +0 -91
- package/dist/core/cursor-config.js +0 -130
- package/dist/core/downloader.js +0 -64
- package/dist/core/errors.js +0 -29
- package/dist/core/filesystem.js +0 -246
- package/dist/core/lockfile.js +0 -292
- package/dist/core/marketplace-converter.js +0 -224
- package/dist/core/prompts.js +0 -62
- package/dist/core/registry-client.js +0 -305
- package/dist/core/schema-validator.js +0 -74
- package/dist/core/telemetry.js +0 -253
- package/dist/core/user-config.js +0 -147
- package/dist/types/registry.js +0 -12
- package/dist/types.js +0 -9
- package/dist/utils/license-extractor.js +0 -122
- package/dist/utils/multi-package.js +0 -117
- package/dist/utils/parallel-publisher.js +0 -144
- package/dist/utils/script-executor.js +0 -72
- package/dist/utils/snippet-extractor.js +0 -77
- package/dist/utils/webapp-url.js +0 -44
|
@@ -1,655 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Collections command - Manage package collections
|
|
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.handleCollectionsSearch = handleCollectionsSearch;
|
|
40
|
-
exports.handleCollectionsList = handleCollectionsList;
|
|
41
|
-
exports.handleCollectionInfo = handleCollectionInfo;
|
|
42
|
-
exports.handleCollectionPublish = handleCollectionPublish;
|
|
43
|
-
exports.handleCollectionInstall = handleCollectionInstall;
|
|
44
|
-
exports.createCollectionsCommand = createCollectionsCommand;
|
|
45
|
-
const commander_1 = require("commander");
|
|
46
|
-
const registry_client_1 = require("@pr-pm/registry-client");
|
|
47
|
-
const user_config_1 = require("../core/user-config");
|
|
48
|
-
const install_1 = require("./install");
|
|
49
|
-
const telemetry_1 = require("../core/telemetry");
|
|
50
|
-
const lockfile_1 = require("../core/lockfile");
|
|
51
|
-
const errors_1 = require("../core/errors");
|
|
52
|
-
/**
|
|
53
|
-
* Search collections by query
|
|
54
|
-
*/
|
|
55
|
-
async function handleCollectionsSearch(query, options) {
|
|
56
|
-
const startTime = Date.now();
|
|
57
|
-
try {
|
|
58
|
-
const config = await (0, user_config_1.getConfig)();
|
|
59
|
-
const client = (0, registry_client_1.getRegistryClient)(config);
|
|
60
|
-
console.log(`š Searching collections for "${query}"...\n`);
|
|
61
|
-
// Use server-side search with full-text index
|
|
62
|
-
const result = await client.getCollections({
|
|
63
|
-
query,
|
|
64
|
-
category: options.category,
|
|
65
|
-
tag: options.tag,
|
|
66
|
-
official: options.official,
|
|
67
|
-
limit: options.limit || 50,
|
|
68
|
-
});
|
|
69
|
-
if (result.collections.length === 0) {
|
|
70
|
-
console.log('No collections found matching your search.');
|
|
71
|
-
console.log('\nš” Try:');
|
|
72
|
-
console.log(' - Broadening your search terms');
|
|
73
|
-
console.log(' - Checking spelling');
|
|
74
|
-
console.log(' - Browsing all: prpm collections list');
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
console.log(`⨠Found ${result.collections.length} collection(s):\n`);
|
|
78
|
-
// Group by official vs community
|
|
79
|
-
const official = result.collections.filter(c => c.official);
|
|
80
|
-
const community = result.collections.filter(c => !c.official);
|
|
81
|
-
if (official.length > 0) {
|
|
82
|
-
console.log(`š¦ Official Collections (${official.length}):\n`);
|
|
83
|
-
official.forEach(c => {
|
|
84
|
-
const fullName = c.name_slug.padEnd(35);
|
|
85
|
-
const pkgCount = `(${c.package_count} packages)`.padEnd(15);
|
|
86
|
-
console.log(` ${c.icon || 'š¦'} ${fullName} ${pkgCount} ${c.name}`);
|
|
87
|
-
if (c.description) {
|
|
88
|
-
console.log(` ${c.description.substring(0, 70)}${c.description.length > 70 ? '...' : ''}`);
|
|
89
|
-
}
|
|
90
|
-
if (c.author) {
|
|
91
|
-
console.log(` š¤ by @${c.author}${c.verified ? ' ā' : ''}`);
|
|
92
|
-
}
|
|
93
|
-
console.log(` ā¬ļø ${c.downloads.toLocaleString()} installs Ā· ā ${c.stars.toLocaleString()} stars`);
|
|
94
|
-
console.log('');
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
if (community.length > 0) {
|
|
98
|
-
console.log(`\nš Community Collections (${community.length}):\n`);
|
|
99
|
-
community.forEach(c => {
|
|
100
|
-
const fullName = c.name_slug.padEnd(35);
|
|
101
|
-
const pkgCount = `(${c.package_count} packages)`.padEnd(15);
|
|
102
|
-
console.log(` ${c.icon || 'š¦'} ${fullName} ${pkgCount} ${c.name}`);
|
|
103
|
-
if (c.description) {
|
|
104
|
-
console.log(` ${c.description.substring(0, 70)}${c.description.length > 70 ? '...' : ''}`);
|
|
105
|
-
}
|
|
106
|
-
if (c.author) {
|
|
107
|
-
console.log(` š¤ by @${c.author}${c.verified ? ' ā' : ''}`);
|
|
108
|
-
}
|
|
109
|
-
console.log(` ā¬ļø ${c.downloads.toLocaleString()} installs Ā· ā ${c.stars.toLocaleString()} stars`);
|
|
110
|
-
console.log('');
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
// Show results count
|
|
114
|
-
console.log(`\nš Found: ${result.collections.length} matching collection${result.collections.length === 1 ? '' : 's'} (searched ${result.total} total)\n`);
|
|
115
|
-
console.log(`š” View details: prpm collection info <collection>`);
|
|
116
|
-
console.log(`š” Install: prpm install <collection>`);
|
|
117
|
-
await telemetry_1.telemetry.track({
|
|
118
|
-
command: 'collections:search',
|
|
119
|
-
success: true,
|
|
120
|
-
duration: Date.now() - startTime,
|
|
121
|
-
data: {
|
|
122
|
-
query: query.substring(0, 100),
|
|
123
|
-
count: result.collections.length,
|
|
124
|
-
total: result.total,
|
|
125
|
-
filters: options,
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
131
|
-
console.error(`\nā Failed to search collections: ${errorMessage}`);
|
|
132
|
-
await telemetry_1.telemetry.track({
|
|
133
|
-
command: 'collections:search',
|
|
134
|
-
success: false,
|
|
135
|
-
error: errorMessage,
|
|
136
|
-
duration: Date.now() - startTime,
|
|
137
|
-
});
|
|
138
|
-
throw new errors_1.CLIError(`\nā Failed to search collections: ${errorMessage}`, 1);
|
|
139
|
-
}
|
|
140
|
-
finally {
|
|
141
|
-
await telemetry_1.telemetry.shutdown();
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* List available collections
|
|
146
|
-
*/
|
|
147
|
-
async function handleCollectionsList(options) {
|
|
148
|
-
const startTime = Date.now();
|
|
149
|
-
try {
|
|
150
|
-
const config = await (0, user_config_1.getConfig)();
|
|
151
|
-
const client = (0, registry_client_1.getRegistryClient)(config);
|
|
152
|
-
console.log('š¦ Searching collections...\n');
|
|
153
|
-
const result = await client.getCollections({
|
|
154
|
-
category: options.category,
|
|
155
|
-
tag: options.tag,
|
|
156
|
-
official: options.official,
|
|
157
|
-
scope: options.scope,
|
|
158
|
-
limit: 500, // Increased limit to show more collections
|
|
159
|
-
});
|
|
160
|
-
if (result.collections.length === 0) {
|
|
161
|
-
console.log('No collections found matching your criteria.');
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
// Group by official vs community
|
|
165
|
-
const official = result.collections.filter(c => c.official);
|
|
166
|
-
const community = result.collections.filter(c => !c.official);
|
|
167
|
-
if (official.length > 0) {
|
|
168
|
-
console.log(`š¦ Official Collections (${official.length}):\n`);
|
|
169
|
-
official.forEach(c => {
|
|
170
|
-
const fullName = c.name_slug.padEnd(35);
|
|
171
|
-
const pkgCount = `(${c.package_count} packages)`.padEnd(15);
|
|
172
|
-
console.log(` ${c.icon || 'š¦'} ${fullName} ${pkgCount} ${c.name}`);
|
|
173
|
-
if (c.description) {
|
|
174
|
-
console.log(` ${c.description.substring(0, 70)}${c.description.length > 70 ? '...' : ''}`);
|
|
175
|
-
}
|
|
176
|
-
if (c.author) {
|
|
177
|
-
console.log(` š¤ by @${c.author}${c.verified ? ' ā' : ''}`);
|
|
178
|
-
}
|
|
179
|
-
console.log(` ā¬ļø ${c.downloads.toLocaleString()} installs Ā· ā ${c.stars.toLocaleString()} stars`);
|
|
180
|
-
console.log('');
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
if (community.length > 0) {
|
|
184
|
-
console.log(`\nš Community Collections (${community.length}):\n`);
|
|
185
|
-
community.forEach(c => {
|
|
186
|
-
const fullName = c.name_slug.padEnd(35);
|
|
187
|
-
const pkgCount = `(${c.package_count} packages)`.padEnd(15);
|
|
188
|
-
console.log(` ${c.icon || 'š¦'} ${fullName} ${pkgCount} ${c.name}`);
|
|
189
|
-
if (c.description) {
|
|
190
|
-
console.log(` ${c.description.substring(0, 70)}${c.description.length > 70 ? '...' : ''}`);
|
|
191
|
-
}
|
|
192
|
-
if (c.author) {
|
|
193
|
-
console.log(` š¤ by @${c.author}${c.verified ? ' ā' : ''}`);
|
|
194
|
-
}
|
|
195
|
-
console.log(` ā¬ļø ${c.downloads.toLocaleString()} installs Ā· ā ${c.stars.toLocaleString()} stars`);
|
|
196
|
-
console.log('');
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
// Show total from API (which includes all collections, not just the ones returned)
|
|
200
|
-
const showing = result.collections.length;
|
|
201
|
-
const total = result.total;
|
|
202
|
-
if (showing < total) {
|
|
203
|
-
console.log(`\nš Showing ${showing} of ${total} collection${total === 1 ? '' : 's'}\n`);
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
console.log(`\nš Total: ${total} collection${total === 1 ? '' : 's'}\n`);
|
|
207
|
-
}
|
|
208
|
-
console.log(`š” View details: prpm collection info <collection>`);
|
|
209
|
-
console.log(`š” Install: prpm install <collection>`);
|
|
210
|
-
await telemetry_1.telemetry.track({
|
|
211
|
-
command: 'collections:list',
|
|
212
|
-
success: true,
|
|
213
|
-
duration: Date.now() - startTime,
|
|
214
|
-
data: {
|
|
215
|
-
count: result.collections.length,
|
|
216
|
-
total: result.total,
|
|
217
|
-
filters: options,
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
catch (error) {
|
|
222
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
223
|
-
console.error(`\nā Failed to list collections: ${errorMessage}`);
|
|
224
|
-
await telemetry_1.telemetry.track({
|
|
225
|
-
command: 'collections:list',
|
|
226
|
-
success: false,
|
|
227
|
-
error: errorMessage,
|
|
228
|
-
duration: Date.now() - startTime,
|
|
229
|
-
});
|
|
230
|
-
throw new errors_1.CLIError(`\nā Failed to list collections: ${errorMessage}`, 1);
|
|
231
|
-
}
|
|
232
|
-
finally {
|
|
233
|
-
await telemetry_1.telemetry.shutdown();
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Show collection details
|
|
238
|
-
*/
|
|
239
|
-
async function handleCollectionInfo(collectionSpec) {
|
|
240
|
-
const startTime = Date.now();
|
|
241
|
-
try {
|
|
242
|
-
// Parse collection spec: @scope/name_slug, scope/name_slug, or just name_slug (defaults to 'collection' scope)
|
|
243
|
-
let scope;
|
|
244
|
-
let name_slug;
|
|
245
|
-
let version;
|
|
246
|
-
const matchWithScope = collectionSpec.match(/^@?([^/]+)\/([^/@]+)(?:@(.+))?$/);
|
|
247
|
-
if (matchWithScope) {
|
|
248
|
-
// Has explicit scope: @scope/name or scope/name
|
|
249
|
-
[, scope, name_slug, version] = matchWithScope;
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
// No scope, assume 'collection' scope: just name or name@version
|
|
253
|
-
const matchNoScope = collectionSpec.match(/^([^/@]+)(?:@(.+))?$/);
|
|
254
|
-
if (!matchNoScope) {
|
|
255
|
-
throw new Error('Invalid collection format. Use: name, @scope/name, or scope/name (optionally with @version)');
|
|
256
|
-
}
|
|
257
|
-
[, name_slug, version] = matchNoScope;
|
|
258
|
-
scope = 'collection'; // Default scope
|
|
259
|
-
}
|
|
260
|
-
const config = await (0, user_config_1.getConfig)();
|
|
261
|
-
const client = (0, registry_client_1.getRegistryClient)(config);
|
|
262
|
-
console.log(`š¦ Loading collection: ${scope === 'collection' ? name_slug : `@${scope}/${name_slug}`}...\n`);
|
|
263
|
-
const collection = await client.getCollection(scope, name_slug, version);
|
|
264
|
-
// Header
|
|
265
|
-
console.log(`${collection.icon || 'š¦'} ${collection.name}`);
|
|
266
|
-
console.log(`${'='.repeat(collection.name.length + 2)}`);
|
|
267
|
-
console.log('');
|
|
268
|
-
console.log(collection.description);
|
|
269
|
-
console.log('');
|
|
270
|
-
// Stats
|
|
271
|
-
console.log('š Stats:');
|
|
272
|
-
console.log(` Downloads: ${collection.downloads.toLocaleString()}`);
|
|
273
|
-
console.log(` Stars: ${collection.stars.toLocaleString()}`);
|
|
274
|
-
console.log(` Version: ${collection.version}`);
|
|
275
|
-
console.log(` Packages: ${collection.packages.length}`);
|
|
276
|
-
if (collection.author) {
|
|
277
|
-
console.log(` Author: ${collection.author}${collection.verified ? ' ā' : ''}`);
|
|
278
|
-
}
|
|
279
|
-
if (collection.category) {
|
|
280
|
-
console.log(` Category: ${collection.category}`);
|
|
281
|
-
}
|
|
282
|
-
if (collection.tags && collection.tags.length > 0) {
|
|
283
|
-
console.log(` Tags: ${collection.tags.join(', ')}`);
|
|
284
|
-
}
|
|
285
|
-
console.log('');
|
|
286
|
-
// Packages
|
|
287
|
-
console.log('š Included Packages:');
|
|
288
|
-
console.log('');
|
|
289
|
-
const requiredPkgs = collection.packages.filter(p => p.required);
|
|
290
|
-
const optionalPkgs = collection.packages.filter(p => !p.required);
|
|
291
|
-
if (requiredPkgs.length > 0) {
|
|
292
|
-
console.log(' Required:');
|
|
293
|
-
requiredPkgs.forEach((pkg, i) => {
|
|
294
|
-
console.log(` ${i + 1}. ā ${pkg?.package?.name}@${pkg.version || 'latest'}`);
|
|
295
|
-
if (pkg.package && pkg.package.description) {
|
|
296
|
-
console.log(` ${pkg.package.description}`);
|
|
297
|
-
}
|
|
298
|
-
if (pkg.reason) {
|
|
299
|
-
console.log(` š” ${pkg.reason}`);
|
|
300
|
-
}
|
|
301
|
-
console.log('');
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
if (optionalPkgs.length > 0) {
|
|
305
|
-
console.log(' Optional:');
|
|
306
|
-
optionalPkgs.forEach((pkg, i) => {
|
|
307
|
-
console.log(` ${i + 1}. ā ${pkg?.package?.name}@${pkg.version || 'latest'}`);
|
|
308
|
-
if (pkg.package && pkg.package.description) {
|
|
309
|
-
console.log(` ${pkg.package.description}`);
|
|
310
|
-
}
|
|
311
|
-
if (pkg.reason) {
|
|
312
|
-
console.log(` š” ${pkg.reason}`);
|
|
313
|
-
}
|
|
314
|
-
console.log('');
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
// Installation
|
|
318
|
-
console.log('š” Install:');
|
|
319
|
-
if (scope === 'collection') {
|
|
320
|
-
console.log(` prpm install ${name_slug}`);
|
|
321
|
-
if (optionalPkgs.length > 0) {
|
|
322
|
-
console.log(` prpm install ${name_slug} --skip-optional # Skip optional packages`);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
else {
|
|
326
|
-
console.log(` prpm install @${scope}/${name_slug}`);
|
|
327
|
-
if (optionalPkgs.length > 0) {
|
|
328
|
-
console.log(` prpm install @${scope}/${name_slug} --skip-optional # Skip optional packages`);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
console.log('');
|
|
332
|
-
await telemetry_1.telemetry.track({
|
|
333
|
-
command: 'collections:info',
|
|
334
|
-
success: true,
|
|
335
|
-
duration: Date.now() - startTime,
|
|
336
|
-
data: {
|
|
337
|
-
scope,
|
|
338
|
-
name_slug,
|
|
339
|
-
packageCount: collection.packages.length,
|
|
340
|
-
},
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
catch (error) {
|
|
344
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
345
|
-
console.error(`\nā Failed to get collection info: ${errorMessage}`);
|
|
346
|
-
await telemetry_1.telemetry.track({
|
|
347
|
-
command: 'collections:info',
|
|
348
|
-
success: false,
|
|
349
|
-
error: errorMessage,
|
|
350
|
-
duration: Date.now() - startTime,
|
|
351
|
-
});
|
|
352
|
-
throw new errors_1.CLIError(`\nā Failed to get collection info: ${errorMessage}`, 1);
|
|
353
|
-
}
|
|
354
|
-
finally {
|
|
355
|
-
await telemetry_1.telemetry.shutdown();
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Publish/create a collection
|
|
360
|
-
*/
|
|
361
|
-
async function handleCollectionPublish(manifestPath = './collection.json') {
|
|
362
|
-
const startTime = Date.now();
|
|
363
|
-
try {
|
|
364
|
-
const config = await (0, user_config_1.getConfig)();
|
|
365
|
-
const client = (0, registry_client_1.getRegistryClient)(config);
|
|
366
|
-
// Check authentication
|
|
367
|
-
if (!config.token) {
|
|
368
|
-
console.error('\nā Authentication required. Run `prpm login` first.\n');
|
|
369
|
-
throw new errors_1.CLIError('\nā Authentication required. Run `prpm login` first.', 1);
|
|
370
|
-
}
|
|
371
|
-
console.log('š¦ Publishing collection...\n');
|
|
372
|
-
// Read collection manifest
|
|
373
|
-
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
374
|
-
const manifestContent = await fs.readFile(manifestPath, 'utf-8');
|
|
375
|
-
const manifest = JSON.parse(manifestContent);
|
|
376
|
-
// Validate manifest
|
|
377
|
-
const required = ['id', 'name', 'description', 'packages'];
|
|
378
|
-
const missing = required.filter(field => !manifest[field]);
|
|
379
|
-
if (missing.length > 0) {
|
|
380
|
-
throw new Error(`Missing required fields: ${missing.join(', ')}`);
|
|
381
|
-
}
|
|
382
|
-
// Validate id format (must be lowercase alphanumeric with hyphens)
|
|
383
|
-
if (!/^[a-z0-9-]+$/.test(manifest.id)) {
|
|
384
|
-
throw new Error('Collection id must be lowercase alphanumeric with hyphens only');
|
|
385
|
-
}
|
|
386
|
-
// Validate name length
|
|
387
|
-
if (manifest.name.length < 3) {
|
|
388
|
-
throw new Error('Collection name must be at least 3 characters');
|
|
389
|
-
}
|
|
390
|
-
// Validate description length
|
|
391
|
-
if (manifest.description.length < 10) {
|
|
392
|
-
throw new Error('Collection description must be at least 10 characters');
|
|
393
|
-
}
|
|
394
|
-
// Validate packages array
|
|
395
|
-
if (!Array.isArray(manifest.packages) || manifest.packages.length === 0) {
|
|
396
|
-
throw new Error('Collection must include at least one package');
|
|
397
|
-
}
|
|
398
|
-
// Validate each package
|
|
399
|
-
manifest.packages.forEach((pkg, idx) => {
|
|
400
|
-
if (!pkg.packageId) {
|
|
401
|
-
throw new Error(`Package at index ${idx} is missing packageId`);
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
console.log(`š Validating collection manifest...`);
|
|
405
|
-
console.log(` Collection: ${manifest.name}`);
|
|
406
|
-
console.log(` ID: ${manifest.id}`);
|
|
407
|
-
console.log(` Packages: ${manifest.packages.length}`);
|
|
408
|
-
console.log('');
|
|
409
|
-
// Publish to registry
|
|
410
|
-
console.log('š Publishing to registry...\n');
|
|
411
|
-
const result = await client.createCollection({
|
|
412
|
-
id: manifest.id,
|
|
413
|
-
name: manifest.name,
|
|
414
|
-
description: manifest.description,
|
|
415
|
-
category: manifest.category,
|
|
416
|
-
tags: manifest.tags,
|
|
417
|
-
packages: manifest.packages.map((pkg) => ({
|
|
418
|
-
packageId: pkg.packageId,
|
|
419
|
-
version: pkg.version,
|
|
420
|
-
required: pkg.required !== false,
|
|
421
|
-
reason: pkg.reason,
|
|
422
|
-
})),
|
|
423
|
-
icon: manifest.icon,
|
|
424
|
-
});
|
|
425
|
-
console.log(`ā
Collection published successfully!`);
|
|
426
|
-
console.log(` Scope: ${result.scope}`);
|
|
427
|
-
console.log(` Name: ${result.name_slug}`);
|
|
428
|
-
console.log(` Version: ${result.version || '1.0.0'}`);
|
|
429
|
-
console.log('');
|
|
430
|
-
console.log(`š” View: prpm collection info @${result.scope}/${result.name_slug}`);
|
|
431
|
-
console.log(`š” Install: prpm install @${result.scope}/${result.name_slug}`);
|
|
432
|
-
console.log('');
|
|
433
|
-
await telemetry_1.telemetry.track({
|
|
434
|
-
command: 'collections:publish',
|
|
435
|
-
success: true,
|
|
436
|
-
duration: Date.now() - startTime,
|
|
437
|
-
data: {
|
|
438
|
-
id: manifest.id,
|
|
439
|
-
packageCount: manifest.packages.length,
|
|
440
|
-
},
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
catch (error) {
|
|
444
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
445
|
-
console.error(`\nā Failed to publish collection: ${errorMessage}\n`);
|
|
446
|
-
await telemetry_1.telemetry.track({
|
|
447
|
-
command: 'collections:publish',
|
|
448
|
-
success: false,
|
|
449
|
-
error: errorMessage,
|
|
450
|
-
duration: Date.now() - startTime,
|
|
451
|
-
});
|
|
452
|
-
throw new errors_1.CLIError(`\nā Failed to publish collection: ${errorMessage}`, 1);
|
|
453
|
-
}
|
|
454
|
-
finally {
|
|
455
|
-
await telemetry_1.telemetry.shutdown();
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Install a collection
|
|
460
|
-
*/
|
|
461
|
-
async function handleCollectionInstall(collectionSpec, options) {
|
|
462
|
-
const startTime = Date.now();
|
|
463
|
-
let packagesInstalled = 0;
|
|
464
|
-
let packagesFailed = 0;
|
|
465
|
-
try {
|
|
466
|
-
// Parse collection spec: @scope/name_slug, scope/name_slug, or just name_slug (defaults to 'collection' scope)
|
|
467
|
-
let scope;
|
|
468
|
-
let name_slug;
|
|
469
|
-
let version;
|
|
470
|
-
const matchWithScope = collectionSpec.match(/^@?([^/]+)\/([^/@]+)(?:@(.+))?$/);
|
|
471
|
-
if (matchWithScope) {
|
|
472
|
-
// Has explicit scope: @scope/name or scope/name
|
|
473
|
-
[, scope, name_slug, version] = matchWithScope;
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
// No scope, assume 'collection' scope: just name or name@version
|
|
477
|
-
const matchNoScope = collectionSpec.match(/^([^/@]+)(?:@(.+))?$/);
|
|
478
|
-
if (!matchNoScope) {
|
|
479
|
-
throw new Error('Invalid collection format. Use: name, @scope/name, or scope/name (optionally with @version)');
|
|
480
|
-
}
|
|
481
|
-
[, name_slug, version] = matchNoScope;
|
|
482
|
-
scope = 'collection'; // Default scope
|
|
483
|
-
}
|
|
484
|
-
const config = await (0, user_config_1.getConfig)();
|
|
485
|
-
const client = (0, registry_client_1.getRegistryClient)(config);
|
|
486
|
-
// Get collection installation plan
|
|
487
|
-
console.log(`š¦ Installing collection: ${scope === 'collection' ? name_slug : `@${scope}/${name_slug}`}...\n`);
|
|
488
|
-
const installResult = await client.installCollection({
|
|
489
|
-
scope,
|
|
490
|
-
id: name_slug,
|
|
491
|
-
version,
|
|
492
|
-
format: options.format,
|
|
493
|
-
skipOptional: options.skipOptional,
|
|
494
|
-
});
|
|
495
|
-
const collection = installResult.collection;
|
|
496
|
-
const packages = installResult.packagesToInstall;
|
|
497
|
-
console.log(`š¦ ${collection.name}`);
|
|
498
|
-
console.log(` ${packages.length} packages to install\n`);
|
|
499
|
-
if (options.dryRun) {
|
|
500
|
-
console.log('š Dry run - would install:\n');
|
|
501
|
-
packages.forEach((pkg, i) => {
|
|
502
|
-
const required = pkg.required ? 'ā' : 'ā';
|
|
503
|
-
console.log(` ${i + 1}/${packages.length} ${required} ${pkg.packageId}@${pkg.version} (${pkg.format})`);
|
|
504
|
-
});
|
|
505
|
-
console.log('');
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
// Install packages sequentially
|
|
509
|
-
const installedPackageIds = [];
|
|
510
|
-
let hasClaudeHooks = false;
|
|
511
|
-
for (let i = 0; i < packages.length; i++) {
|
|
512
|
-
const pkg = packages[i];
|
|
513
|
-
const progress = `${i + 1}/${packages.length}`;
|
|
514
|
-
try {
|
|
515
|
-
console.log(`\n ${progress} Installing ${pkg.packageId}@${pkg.version}...`);
|
|
516
|
-
// Only pass 'as' format if user explicitly requested it via --as flag
|
|
517
|
-
// Otherwise, handleInstall will use this priority order:
|
|
518
|
-
// 1. defaultFormat from .prpmrc config
|
|
519
|
-
// 2. Auto-detection based on existing directories
|
|
520
|
-
// 3. Package native format
|
|
521
|
-
const installOptions = {
|
|
522
|
-
fromCollection: {
|
|
523
|
-
scope,
|
|
524
|
-
name_slug,
|
|
525
|
-
version: collection.version || version || '1.0.0',
|
|
526
|
-
},
|
|
527
|
-
};
|
|
528
|
-
// Only set 'as' if user explicitly provided a format
|
|
529
|
-
if (options.format) {
|
|
530
|
-
installOptions.as = options.format;
|
|
531
|
-
}
|
|
532
|
-
// Track if this collection contains Claude hooks
|
|
533
|
-
if (pkg.format === 'claude' && pkg.subtype === 'hook') {
|
|
534
|
-
hasClaudeHooks = true;
|
|
535
|
-
}
|
|
536
|
-
await (0, install_1.handleInstall)(`${pkg.packageId}@${pkg.version}`, installOptions);
|
|
537
|
-
console.log(` ${progress} ā ${pkg.packageId}`);
|
|
538
|
-
installedPackageIds.push(pkg.packageId);
|
|
539
|
-
packagesInstalled++;
|
|
540
|
-
}
|
|
541
|
-
catch (error) {
|
|
542
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
543
|
-
console.error(` ${progress} ā ${pkg.packageId}: ${errorMessage}`);
|
|
544
|
-
packagesFailed++;
|
|
545
|
-
if (pkg.required) {
|
|
546
|
-
throw new Error(`Failed to install required package: ${pkg.packageId}`);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
// Update lockfile with collection info
|
|
551
|
-
const lockfile = (await (0, lockfile_1.readLockfile)()) || (0, lockfile_1.createLockfile)();
|
|
552
|
-
const collectionKey = `@${scope}/${name_slug}`;
|
|
553
|
-
(0, lockfile_1.addCollectionToLockfile)(lockfile, collectionKey, {
|
|
554
|
-
scope,
|
|
555
|
-
name_slug,
|
|
556
|
-
version: collection.version || version || '1.0.0',
|
|
557
|
-
packages: installedPackageIds,
|
|
558
|
-
});
|
|
559
|
-
await (0, lockfile_1.writeLockfile)(lockfile);
|
|
560
|
-
console.log(`\nā
Collection installed successfully!`);
|
|
561
|
-
console.log(` ${packagesInstalled}/${packages.length} packages installed`);
|
|
562
|
-
if (packagesFailed > 0) {
|
|
563
|
-
console.log(` ${packagesFailed} optional packages failed`);
|
|
564
|
-
}
|
|
565
|
-
console.log(` š Collection tracked in lock file`);
|
|
566
|
-
// Show Claude hooks warning if any were installed
|
|
567
|
-
if (hasClaudeHooks) {
|
|
568
|
-
console.log(`\nā ļø This collection includes Claude hooks that execute automatically.`);
|
|
569
|
-
console.log(` š Review hook configurations in .claude/settings.json`);
|
|
570
|
-
}
|
|
571
|
-
console.log('');
|
|
572
|
-
await telemetry_1.telemetry.track({
|
|
573
|
-
command: 'collections:install',
|
|
574
|
-
success: true,
|
|
575
|
-
duration: Date.now() - startTime,
|
|
576
|
-
data: {
|
|
577
|
-
scope,
|
|
578
|
-
name_slug,
|
|
579
|
-
packageCount: packages.length,
|
|
580
|
-
installed: packagesInstalled,
|
|
581
|
-
failed: packagesFailed,
|
|
582
|
-
format: options.format,
|
|
583
|
-
},
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
catch (error) {
|
|
587
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
588
|
-
console.error(`\nā Failed to install collection: ${errorMessage}`);
|
|
589
|
-
await telemetry_1.telemetry.track({
|
|
590
|
-
command: 'collections:install',
|
|
591
|
-
success: false,
|
|
592
|
-
error: errorMessage,
|
|
593
|
-
duration: Date.now() - startTime,
|
|
594
|
-
data: {
|
|
595
|
-
installed: packagesInstalled,
|
|
596
|
-
failed: packagesFailed,
|
|
597
|
-
},
|
|
598
|
-
});
|
|
599
|
-
throw new errors_1.CLIError(`\nā Failed to install collection: ${errorMessage}`, 1);
|
|
600
|
-
}
|
|
601
|
-
finally {
|
|
602
|
-
await telemetry_1.telemetry.shutdown();
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
/**
|
|
606
|
-
* Create collections command group
|
|
607
|
-
*/
|
|
608
|
-
function createCollectionsCommand() {
|
|
609
|
-
const command = new commander_1.Command('collections');
|
|
610
|
-
command
|
|
611
|
-
.description('Manage package collections')
|
|
612
|
-
.alias('collection')
|
|
613
|
-
.action(async (options) => {
|
|
614
|
-
await handleCollectionsList(options);
|
|
615
|
-
});
|
|
616
|
-
// Search subcommand
|
|
617
|
-
command
|
|
618
|
-
.command('search <query>')
|
|
619
|
-
.description('Search for collections')
|
|
620
|
-
.option('--category <category>', 'Filter by category')
|
|
621
|
-
.option('--tag <tag>', 'Filter by tag')
|
|
622
|
-
.option('--official', 'Show only official collections')
|
|
623
|
-
.option('--limit <number>', 'Number of results to show', '50')
|
|
624
|
-
.action(async (query, options) => {
|
|
625
|
-
await handleCollectionsSearch(query, {
|
|
626
|
-
category: options.category,
|
|
627
|
-
tag: options.tag,
|
|
628
|
-
official: options.official,
|
|
629
|
-
limit: options.limit ? parseInt(options.limit, 10) : 50,
|
|
630
|
-
});
|
|
631
|
-
});
|
|
632
|
-
// List subcommand
|
|
633
|
-
command
|
|
634
|
-
.command('list')
|
|
635
|
-
.description('List available collections')
|
|
636
|
-
.option('--category <category>', 'Filter by category')
|
|
637
|
-
.option('--tag <tag>', 'Filter by tag')
|
|
638
|
-
.option('--official', 'Show only official collections')
|
|
639
|
-
.option('--scope <scope>', 'Filter by scope')
|
|
640
|
-
.action(handleCollectionsList);
|
|
641
|
-
// Info subcommand
|
|
642
|
-
command
|
|
643
|
-
.command('info <collection>')
|
|
644
|
-
.description('Show collection details')
|
|
645
|
-
.action(handleCollectionInfo);
|
|
646
|
-
// Publish subcommand
|
|
647
|
-
command
|
|
648
|
-
.command('publish [manifest]')
|
|
649
|
-
.description('Publish a collection from collection.json')
|
|
650
|
-
.action(async (manifest) => {
|
|
651
|
-
await handleCollectionPublish(manifest);
|
|
652
|
-
});
|
|
653
|
-
// Install handled by main install command with @scope/id syntax
|
|
654
|
-
return command;
|
|
655
|
-
}
|