prpm 0.0.6 ā 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.
- package/dist/commands/config.js +167 -0
- package/dist/commands/index.js +17 -17
- package/dist/commands/info.js +2 -1
- package/dist/commands/init.js +670 -0
- package/dist/commands/install.js +128 -99
- package/dist/commands/list.js +7 -4
- package/dist/commands/login.js +8 -2
- package/dist/commands/outdated.js +4 -1
- package/dist/commands/popular.js +10 -3
- package/dist/commands/publish.js +100 -11
- package/dist/commands/schema.js +4 -1
- package/dist/commands/search.js +114 -84
- package/dist/commands/telemetry.js +6 -0
- package/dist/commands/trending.js +17 -7
- package/dist/commands/uninstall.js +4 -7
- package/dist/commands/update.js +5 -4
- package/dist/commands/upgrade.js +5 -4
- package/dist/commands/whoami.js +10 -1
- package/dist/core/filesystem.js +21 -15
- package/dist/core/lockfile.js +2 -2
- package/dist/core/marketplace-converter.js +11 -6
- package/dist/core/registry-client.js +14 -8
- package/dist/index.js +19 -1
- package/package.json +7 -3
- package/schemas/prpm-manifest.schema.json +197 -38
- package/dist/commands/remove.js +0 -76
package/dist/commands/install.js
CHANGED
|
@@ -48,60 +48,69 @@ const lockfile_1 = require("../core/lockfile");
|
|
|
48
48
|
const cursor_config_1 = require("../core/cursor-config");
|
|
49
49
|
const claude_config_1 = require("../core/claude-config");
|
|
50
50
|
/**
|
|
51
|
-
* Get icon for package
|
|
51
|
+
* Get icon for package format and subtype
|
|
52
52
|
*/
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
'
|
|
57
|
-
'
|
|
53
|
+
function getPackageIcon(format, subtype) {
|
|
54
|
+
// Subtype icons take precedence
|
|
55
|
+
const subtypeIcons = {
|
|
56
|
+
'skill': 'š',
|
|
57
|
+
'agent': 'š¤',
|
|
58
|
+
'slash-command': 'ā”',
|
|
59
|
+
'rule': 'š',
|
|
60
|
+
'prompt': 'š¬',
|
|
61
|
+
'workflow': 'ā”',
|
|
62
|
+
'tool': 'š§',
|
|
63
|
+
'template': 'š',
|
|
64
|
+
'collection': 'š¦',
|
|
65
|
+
'chatmode': 'š¬',
|
|
66
|
+
};
|
|
67
|
+
// Format-specific icons for rules/defaults
|
|
68
|
+
const formatIcons = {
|
|
58
69
|
'claude': 'š¤',
|
|
59
70
|
'cursor': 'š',
|
|
60
|
-
'cursor-agent': 'š¤',
|
|
61
|
-
'cursor-slash-command': 'ā”',
|
|
62
71
|
'windsurf': 'š',
|
|
63
72
|
'continue': 'ā”ļø',
|
|
73
|
+
'copilot': 'āļø',
|
|
74
|
+
'kiro': 'šÆ',
|
|
64
75
|
'mcp': 'š',
|
|
76
|
+
'agents.md': 'š',
|
|
65
77
|
'generic': 'š¦',
|
|
66
|
-
// Legacy mappings
|
|
67
|
-
skill: 'š',
|
|
68
|
-
agent: 'š¤',
|
|
69
|
-
rule: 'š',
|
|
70
|
-
plugin: 'š',
|
|
71
|
-
prompt: 'š¬',
|
|
72
|
-
workflow: 'ā”',
|
|
73
|
-
tool: 'š§',
|
|
74
|
-
template: 'š',
|
|
75
78
|
};
|
|
76
|
-
return
|
|
79
|
+
return subtypeIcons[subtype] || formatIcons[format] || 'š¦';
|
|
77
80
|
}
|
|
78
81
|
/**
|
|
79
|
-
* Get human-readable label for package
|
|
82
|
+
* Get human-readable label for package format and subtype
|
|
80
83
|
*/
|
|
81
|
-
function
|
|
82
|
-
const
|
|
83
|
-
'claude
|
|
84
|
-
'
|
|
85
|
-
'
|
|
86
|
-
'
|
|
87
|
-
'
|
|
88
|
-
'
|
|
89
|
-
'
|
|
90
|
-
'
|
|
91
|
-
'
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
rule: 'Rule',
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
84
|
+
function getPackageLabel(format, subtype) {
|
|
85
|
+
const formatLabels = {
|
|
86
|
+
'claude': 'Claude',
|
|
87
|
+
'cursor': 'Cursor',
|
|
88
|
+
'windsurf': 'Windsurf',
|
|
89
|
+
'continue': 'Continue',
|
|
90
|
+
'copilot': 'GitHub Copilot',
|
|
91
|
+
'kiro': 'Kiro',
|
|
92
|
+
'mcp': 'MCP',
|
|
93
|
+
'agents.md': 'Agents.md',
|
|
94
|
+
'generic': '',
|
|
95
|
+
};
|
|
96
|
+
const subtypeLabels = {
|
|
97
|
+
'skill': 'Skill',
|
|
98
|
+
'agent': 'Agent',
|
|
99
|
+
'slash-command': 'Slash Command',
|
|
100
|
+
'rule': 'Rule',
|
|
101
|
+
'prompt': 'Prompt',
|
|
102
|
+
'workflow': 'Workflow',
|
|
103
|
+
'tool': 'Tool',
|
|
104
|
+
'template': 'Template',
|
|
105
|
+
'collection': 'Collection',
|
|
106
|
+
'chatmode': 'Chat Mode',
|
|
103
107
|
};
|
|
104
|
-
|
|
108
|
+
const formatLabel = formatLabels[format];
|
|
109
|
+
const subtypeLabel = subtypeLabels[subtype];
|
|
110
|
+
if (format === 'generic') {
|
|
111
|
+
return subtypeLabel;
|
|
112
|
+
}
|
|
113
|
+
return `${formatLabel} ${subtypeLabel}`;
|
|
105
114
|
}
|
|
106
115
|
async function handleInstall(packageSpec, options) {
|
|
107
116
|
const startTime = Date.now();
|
|
@@ -151,7 +160,7 @@ async function handleInstall(packageSpec, options) {
|
|
|
151
160
|
if (!requestedVersion || requestedVersion === 'latest' || requestedVersion === installedPkg.version) {
|
|
152
161
|
console.log(`\n⨠Package already installed!`);
|
|
153
162
|
console.log(` š¦ ${packageId}@${installedPkg.version}`);
|
|
154
|
-
console.log(` š Format: ${installedPkg.format || installedPkg.
|
|
163
|
+
console.log(` š Format: ${installedPkg.format || 'unknown'} | Subtype: ${installedPkg.subtype || 'unknown'}`);
|
|
155
164
|
console.log(`\nš” To reinstall or upgrade:`);
|
|
156
165
|
console.log(` prpm upgrade ${packageId} # Upgrade to latest version`);
|
|
157
166
|
console.log(` prpm uninstall ${packageId} # Uninstall first, then install`);
|
|
@@ -199,13 +208,29 @@ async function handleInstall(packageSpec, options) {
|
|
|
199
208
|
}
|
|
200
209
|
// Get package info
|
|
201
210
|
const pkg = await client.getPackage(packageId);
|
|
202
|
-
const typeIcon =
|
|
203
|
-
const typeLabel =
|
|
211
|
+
const typeIcon = getPackageIcon(pkg.format, pkg.subtype);
|
|
212
|
+
const typeLabel = getPackageLabel(pkg.format, pkg.subtype);
|
|
204
213
|
console.log(` ${pkg.name} ${pkg.official ? 'š
' : ''}`);
|
|
205
214
|
console.log(` ${pkg.description || 'No description'}`);
|
|
206
215
|
console.log(` ${typeIcon} Type: ${typeLabel}`);
|
|
207
|
-
// Determine format preference
|
|
208
|
-
|
|
216
|
+
// Determine format preference
|
|
217
|
+
let format = options.as || pkg.format;
|
|
218
|
+
// Special handling for Claude packages: default to CLAUDE.md if it doesn't exist
|
|
219
|
+
// BUT only for packages that are generic rules (not skills, agents, or commands)
|
|
220
|
+
if (!options.as && pkg.format === 'claude' && pkg.subtype === 'rule') {
|
|
221
|
+
const { fileExists } = await Promise.resolve().then(() => __importStar(require('../core/filesystem.js')));
|
|
222
|
+
const claudeMdExists = await fileExists('CLAUDE.md');
|
|
223
|
+
if (!claudeMdExists) {
|
|
224
|
+
// CLAUDE.md doesn't exist, install as CLAUDE.md (recommended format for Claude Code)
|
|
225
|
+
format = 'claude-md';
|
|
226
|
+
console.log(` š” Installing as CLAUDE.md (recommended for Claude Code)`);
|
|
227
|
+
console.log(` To install as skill instead, use: prpm install ${packageId} --as claude`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// CLAUDE.md already exists, install as skill to avoid overwriting
|
|
231
|
+
console.log(` ā¹ļø CLAUDE.md already exists, installing as skill in .claude/skills/`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
209
234
|
if (options.as && format !== 'canonical') {
|
|
210
235
|
console.log(` š Converting to ${format} format...`);
|
|
211
236
|
}
|
|
@@ -228,58 +253,36 @@ async function handleInstall(packageSpec, options) {
|
|
|
228
253
|
const tarball = await client.downloadPackage(tarballUrl, { format });
|
|
229
254
|
// Extract tarball and save files
|
|
230
255
|
console.log(` š Extracting...`);
|
|
231
|
-
// Determine effective
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// Map package types to cursor equivalents
|
|
235
|
-
if (pkg.type === 'claude-slash-command' || pkg.type === 'cursor-slash-command') {
|
|
236
|
-
effectiveType = 'cursor-slash-command';
|
|
237
|
-
}
|
|
238
|
-
else if (pkg.type === 'claude-agent' || pkg.type === 'cursor-agent') {
|
|
239
|
-
effectiveType = 'cursor-agent';
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
effectiveType = 'cursor';
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
else if (format === 'claude') {
|
|
246
|
-
// Map package types to claude equivalents
|
|
247
|
-
if (pkg.type === 'cursor-slash-command' || pkg.type === 'claude-slash-command') {
|
|
248
|
-
effectiveType = 'claude-slash-command';
|
|
249
|
-
}
|
|
250
|
-
else if (pkg.type === 'cursor-agent' || pkg.type === 'claude-agent') {
|
|
251
|
-
effectiveType = 'claude-agent';
|
|
252
|
-
}
|
|
253
|
-
else if (pkg.type === 'claude-skill') {
|
|
254
|
-
effectiveType = 'claude-skill';
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
effectiveType = 'claude-agent';
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
else if (format === 'continue' || format === 'windsurf') {
|
|
261
|
-
effectiveType = format;
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
effectiveType = (options.type || pkg.type);
|
|
265
|
-
}
|
|
266
|
-
const destDir = (0, filesystem_1.getDestinationDir)(effectiveType);
|
|
256
|
+
// Determine effective format and subtype (from conversion or package native format)
|
|
257
|
+
const effectiveFormat = format || pkg.format;
|
|
258
|
+
const effectiveSubtype = pkg.subtype;
|
|
267
259
|
// Extract all files from tarball
|
|
268
260
|
const extractedFiles = await extractTarball(tarball, packageId);
|
|
269
261
|
// Track where files were saved for user feedback
|
|
270
262
|
let destPath;
|
|
271
263
|
let fileCount = 0;
|
|
264
|
+
// Special handling for CLAUDE.md format (goes in project root)
|
|
265
|
+
if (format === 'claude-md') {
|
|
266
|
+
if (extractedFiles.length !== 1) {
|
|
267
|
+
throw new Error('CLAUDE.md format only supports single-file packages');
|
|
268
|
+
}
|
|
269
|
+
let mainFile = extractedFiles[0].content;
|
|
270
|
+
destPath = 'CLAUDE.md';
|
|
271
|
+
await (0, filesystem_1.saveFile)(destPath, mainFile);
|
|
272
|
+
fileCount = 1;
|
|
273
|
+
}
|
|
272
274
|
// Check if this is a multi-file package
|
|
273
|
-
if (extractedFiles.length === 1) {
|
|
275
|
+
else if (extractedFiles.length === 1) {
|
|
276
|
+
const destDir = (0, filesystem_1.getDestinationDir)(effectiveFormat, effectiveSubtype);
|
|
274
277
|
// Single file package
|
|
275
278
|
let mainFile = extractedFiles[0].content;
|
|
276
|
-
// Determine file extension based on effective
|
|
279
|
+
// Determine file extension based on effective format
|
|
277
280
|
// Cursor rules use .mdc, but slash commands and other files use .md
|
|
278
|
-
const fileExtension = (
|
|
281
|
+
const fileExtension = (effectiveFormat === 'cursor' && format === 'cursor') ? 'mdc' : 'md';
|
|
279
282
|
const packageName = (0, filesystem_1.stripAuthorNamespace)(packageId);
|
|
280
283
|
destPath = `${destDir}/${packageName}.${fileExtension}`;
|
|
281
284
|
// Handle cursor format - add header if missing for .mdc files
|
|
282
|
-
if (format === 'cursor' &&
|
|
285
|
+
if (format === 'cursor' && effectiveFormat === 'cursor') {
|
|
283
286
|
if (!(0, cursor_config_1.hasMDCHeader)(mainFile)) {
|
|
284
287
|
console.log(` ā ļø Adding missing MDC header...`);
|
|
285
288
|
mainFile = (0, cursor_config_1.addMDCHeader)(mainFile, pkg.description);
|
|
@@ -301,11 +304,34 @@ async function handleInstall(packageSpec, options) {
|
|
|
301
304
|
fileCount = 1;
|
|
302
305
|
}
|
|
303
306
|
else {
|
|
307
|
+
const destDir = (0, filesystem_1.getDestinationDir)(effectiveFormat, effectiveSubtype);
|
|
304
308
|
// Multi-file package - create directory for package
|
|
305
309
|
const packageName = (0, filesystem_1.stripAuthorNamespace)(packageId);
|
|
306
310
|
const packageDir = `${destDir}/${packageName}`;
|
|
307
311
|
destPath = packageDir;
|
|
308
312
|
console.log(` š Multi-file package - creating directory: ${packageDir}`);
|
|
313
|
+
// For Claude skills, auto-fix filename to SKILL.md if needed
|
|
314
|
+
if (effectiveFormat === 'claude' && effectiveSubtype === 'skill') {
|
|
315
|
+
const skillMdIndex = extractedFiles.findIndex(f => f.name === 'SKILL.md');
|
|
316
|
+
if (skillMdIndex === -1) {
|
|
317
|
+
// SKILL.md not found, look for common variations and auto-rename
|
|
318
|
+
const skillFileIndex = extractedFiles.findIndex(f => f.name.toLowerCase() === 'skill.md' ||
|
|
319
|
+
f.name === 'skill.md' ||
|
|
320
|
+
f.name.endsWith('.md') && extractedFiles.length === 1 // Single .md file
|
|
321
|
+
);
|
|
322
|
+
if (skillFileIndex !== -1) {
|
|
323
|
+
const oldName = extractedFiles[skillFileIndex].name;
|
|
324
|
+
console.log(` ā ļø Auto-fixing skill filename: ${oldName} ā SKILL.md`);
|
|
325
|
+
console.log(` (Claude skills must be named SKILL.md per official documentation)`);
|
|
326
|
+
extractedFiles[skillFileIndex].name = 'SKILL.md';
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
throw new Error('Claude skills must contain a SKILL.md file. ' +
|
|
330
|
+
'According to Claude documentation, skills must have a file named SKILL.md in their directory. ' +
|
|
331
|
+
'No suitable file found to rename. Please update the package to follow this requirement.');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
309
335
|
for (const file of extractedFiles) {
|
|
310
336
|
const filePath = `${packageDir}/${file.name}`;
|
|
311
337
|
await (0, filesystem_1.saveFile)(filePath, file.content);
|
|
@@ -318,8 +344,8 @@ async function handleInstall(packageSpec, options) {
|
|
|
318
344
|
(0, lockfile_1.addToLockfile)(updatedLockfile, packageId, {
|
|
319
345
|
version: actualVersion || version,
|
|
320
346
|
tarballUrl,
|
|
321
|
-
|
|
322
|
-
|
|
347
|
+
format: pkg.format, // Preserve original package format
|
|
348
|
+
subtype: pkg.subtype, // Preserve original package subtype
|
|
323
349
|
installedPath: destPath,
|
|
324
350
|
});
|
|
325
351
|
(0, lockfile_1.setPackageIntegrity)(updatedLockfile, packageId, tarball);
|
|
@@ -357,7 +383,7 @@ async function handleInstall(packageSpec, options) {
|
|
|
357
383
|
data: {
|
|
358
384
|
packageId: packageSpec.split('@')[0],
|
|
359
385
|
version: options.version || 'latest',
|
|
360
|
-
|
|
386
|
+
convertTo: options.as,
|
|
361
387
|
},
|
|
362
388
|
});
|
|
363
389
|
await telemetry_1.telemetry.shutdown();
|
|
@@ -455,23 +481,26 @@ function createInstallCommand() {
|
|
|
455
481
|
.description('Install a package from the registry')
|
|
456
482
|
.argument('<package>', 'Package to install (e.g., react-rules or react-rules@1.2.0)')
|
|
457
483
|
.option('--version <version>', 'Specific version to install')
|
|
458
|
-
.option('--
|
|
459
|
-
.option('--
|
|
484
|
+
.option('--as <format>', 'Convert and install in specific format (cursor, claude, continue, windsurf, canonical)')
|
|
485
|
+
.option('--format <format>', 'Alias for --as')
|
|
460
486
|
.option('--frozen-lockfile', 'Fail if lock file needs to be updated (for CI)')
|
|
461
487
|
.action(async (packageSpec, options) => {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
if (options.as && !['cursor', 'claude', 'continue', 'windsurf', 'canonical'].includes(options.as)) {
|
|
488
|
+
// Support both --as and --format (format is alias for as)
|
|
489
|
+
const convertTo = options.format || options.as;
|
|
490
|
+
if (convertTo && !['cursor', 'claude', 'continue', 'windsurf', 'canonical'].includes(convertTo)) {
|
|
467
491
|
console.error('ā Format must be one of: cursor, claude, continue, windsurf, canonical');
|
|
492
|
+
console.log('\nš” Examples:');
|
|
493
|
+
console.log(' prpm install my-package --as cursor # Convert to Cursor format');
|
|
494
|
+
console.log(' prpm install my-package --format claude # Convert to Claude format');
|
|
495
|
+
console.log(' prpm install my-package # Install in native format');
|
|
468
496
|
process.exit(1);
|
|
469
497
|
}
|
|
470
498
|
await handleInstall(packageSpec, {
|
|
471
|
-
|
|
472
|
-
as:
|
|
499
|
+
version: options.version,
|
|
500
|
+
as: convertTo,
|
|
473
501
|
frozenLockfile: options.frozenLockfile
|
|
474
502
|
});
|
|
503
|
+
process.exit(0);
|
|
475
504
|
});
|
|
476
505
|
return command;
|
|
477
506
|
}
|
package/dist/commands/list.js
CHANGED
|
@@ -92,12 +92,12 @@ async function displayPackages(packages) {
|
|
|
92
92
|
// Find file locations
|
|
93
93
|
const packagesWithLocations = await Promise.all(packages.map(async (pkg) => ({
|
|
94
94
|
...pkg,
|
|
95
|
-
location: await findPackageLocation(pkg.id, pkg.
|
|
95
|
+
location: await findPackageLocation(pkg.id, `${pkg.format}-${pkg.subtype}`)
|
|
96
96
|
})));
|
|
97
97
|
// Calculate column widths
|
|
98
98
|
const idWidth = Math.max(8, ...packagesWithLocations.map(p => p.id.length));
|
|
99
99
|
const versionWidth = Math.max(7, ...packagesWithLocations.map(p => p.version.length));
|
|
100
|
-
const typeWidth = Math.max(6, ...packagesWithLocations.map(p => (p.
|
|
100
|
+
const typeWidth = Math.max(6, ...packagesWithLocations.map(p => (`${p.format || ''}-${p.subtype || ''}`).length));
|
|
101
101
|
const locationWidth = Math.max(8, ...packagesWithLocations.map(p => (p.location || 'N/A').length));
|
|
102
102
|
// Header
|
|
103
103
|
const header = [
|
|
@@ -113,7 +113,7 @@ async function displayPackages(packages) {
|
|
|
113
113
|
const row = [
|
|
114
114
|
pkg.id.padEnd(idWidth),
|
|
115
115
|
pkg.version.padEnd(versionWidth),
|
|
116
|
-
(pkg.
|
|
116
|
+
(`${pkg.format}-${pkg.subtype}` || '').padEnd(typeWidth),
|
|
117
117
|
(pkg.location || 'N/A').padEnd(locationWidth)
|
|
118
118
|
].join(' | ');
|
|
119
119
|
console.log(row);
|
|
@@ -161,6 +161,9 @@ function createListCommand() {
|
|
|
161
161
|
const command = new commander_1.Command('list');
|
|
162
162
|
command
|
|
163
163
|
.description('List all installed prompt packages')
|
|
164
|
-
.action(
|
|
164
|
+
.action(async () => {
|
|
165
|
+
await handleList();
|
|
166
|
+
process.exit(0);
|
|
167
|
+
});
|
|
165
168
|
return command;
|
|
166
169
|
}
|
package/dist/commands/login.js
CHANGED
|
@@ -140,7 +140,10 @@ async function loginWithOAuth(registryUrl) {
|
|
|
140
140
|
}
|
|
141
141
|
// Create the CLI auth URL with session token, callback, and userId
|
|
142
142
|
const callbackUrl = 'http://localhost:8765/callback';
|
|
143
|
-
|
|
143
|
+
// Determine webapp URL - default to production
|
|
144
|
+
const webappUrl = registryUrl.includes('localhost')
|
|
145
|
+
? registryUrl.replace(':3000', ':5173') // Local: localhost:3000 ā localhost:5173
|
|
146
|
+
: 'https://prpm.dev'; // Production: always use prpm.dev
|
|
144
147
|
const authUrl = `${webappUrl}/cli-auth?sessionToken=${encodeURIComponent(connectSessionToken)}&cliCallback=${encodeURIComponent(callbackUrl)}&userId=${encodeURIComponent(userId)}`;
|
|
145
148
|
console.log(` Please open this link in your browser to authenticate:`);
|
|
146
149
|
console.log(` ${authUrl}\n`);
|
|
@@ -281,5 +284,8 @@ function createLoginCommand() {
|
|
|
281
284
|
return new commander_1.Command('login')
|
|
282
285
|
.description('Login to the PRMP registry')
|
|
283
286
|
.option('--token <token>', 'Login with a personal access token')
|
|
284
|
-
.action(
|
|
287
|
+
.action(async (options) => {
|
|
288
|
+
await handleLogin(options);
|
|
289
|
+
process.exit(0);
|
|
290
|
+
});
|
|
285
291
|
}
|
|
@@ -124,5 +124,8 @@ function getUpdateType(current, latest) {
|
|
|
124
124
|
function createOutdatedCommand() {
|
|
125
125
|
return new commander_1.Command('outdated')
|
|
126
126
|
.description('Check for package updates')
|
|
127
|
-
.action(
|
|
127
|
+
.action(async () => {
|
|
128
|
+
await handleOutdated();
|
|
129
|
+
process.exit(0);
|
|
130
|
+
});
|
|
128
131
|
}
|
package/dist/commands/popular.js
CHANGED
|
@@ -14,7 +14,10 @@ const trending_1 = require("./trending");
|
|
|
14
14
|
async function handlePopular(options) {
|
|
15
15
|
// Delegate to trending command
|
|
16
16
|
console.log('š Popular Packages (All Time)\n');
|
|
17
|
-
await (0, trending_1.handleTrending)({
|
|
17
|
+
await (0, trending_1.handleTrending)({
|
|
18
|
+
format: options.format,
|
|
19
|
+
subtype: options.subtype
|
|
20
|
+
});
|
|
18
21
|
}
|
|
19
22
|
/**
|
|
20
23
|
* Create the popular command
|
|
@@ -22,6 +25,10 @@ async function handlePopular(options) {
|
|
|
22
25
|
function createPopularCommand() {
|
|
23
26
|
return new commander_1.Command('popular')
|
|
24
27
|
.description('Show popular packages (all time)')
|
|
25
|
-
.option('
|
|
26
|
-
.
|
|
28
|
+
.option('--format <format>', 'Filter by format (cursor, claude, continue, windsurf, copilot, kiro, generic)')
|
|
29
|
+
.option('--subtype <subtype>', 'Filter by subtype (rule, agent, skill, slash-command, prompt, workflow, tool, template, collection)')
|
|
30
|
+
.action(async (options) => {
|
|
31
|
+
await handlePopular(options);
|
|
32
|
+
process.exit(0);
|
|
33
|
+
});
|
|
27
34
|
}
|
package/dist/commands/publish.js
CHANGED
|
@@ -65,7 +65,13 @@ async function findAndLoadManifest() {
|
|
|
65
65
|
return { manifest: validated, source: 'prpm.json' };
|
|
66
66
|
}
|
|
67
67
|
catch (error) {
|
|
68
|
-
//
|
|
68
|
+
// If it's a validation error, throw it immediately (don't try marketplace.json)
|
|
69
|
+
if (error instanceof Error && (error.message.includes('Manifest validation failed') ||
|
|
70
|
+
error.message.includes('Claude skill') ||
|
|
71
|
+
error.message.includes('SKILL.md'))) {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
// Otherwise, prpm.json not found or invalid JSON, try marketplace.json
|
|
69
75
|
}
|
|
70
76
|
// Try .claude/marketplace.json (Claude format)
|
|
71
77
|
const marketplaceJsonPath = (0, path_1.join)(process.cwd(), '.claude', 'marketplace.json');
|
|
@@ -103,13 +109,55 @@ function validateManifest(manifest) {
|
|
|
103
109
|
// Check if using enhanced format (file objects)
|
|
104
110
|
const hasEnhancedFormat = manifest.files.some(f => typeof f === 'object');
|
|
105
111
|
if (hasEnhancedFormat) {
|
|
106
|
-
// Check if files have multiple distinct
|
|
107
|
-
const
|
|
112
|
+
// Check if files have multiple distinct formats
|
|
113
|
+
const fileFormats = new Set(manifest.files
|
|
108
114
|
.filter(f => typeof f === 'object')
|
|
109
|
-
.map(f => f.
|
|
110
|
-
// Only suggest "collection" if there are multiple distinct
|
|
111
|
-
if (
|
|
112
|
-
console.warn('ā ļø Package contains multiple file
|
|
115
|
+
.map(f => f.format));
|
|
116
|
+
// Only suggest "collection" if there are multiple distinct formats
|
|
117
|
+
if (fileFormats.size > 1 && manifest.subtype !== 'collection') {
|
|
118
|
+
console.warn('ā ļø Package contains multiple file formats. Consider setting subtype to "collection" for clarity.');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Enforce SKILL.md filename for Claude skills
|
|
122
|
+
if (manifest.format === 'claude' && manifest.subtype === 'skill') {
|
|
123
|
+
const filePaths = normalizeFilePaths(manifest.files);
|
|
124
|
+
const hasSkillMd = filePaths.some(path => path.endsWith('/SKILL.md') || path === 'SKILL.md');
|
|
125
|
+
if (!hasSkillMd) {
|
|
126
|
+
throw new Error('Claude skills must contain a SKILL.md file.\n' +
|
|
127
|
+
'According to Claude documentation at https://docs.claude.com/en/docs/claude-code/skills,\n' +
|
|
128
|
+
'skills must have a file named SKILL.md in their directory.\n' +
|
|
129
|
+
'Please rename your skill file to SKILL.md (all caps) and update your prpm.json files array.');
|
|
130
|
+
}
|
|
131
|
+
// Validate skill name length (max 64 characters)
|
|
132
|
+
if (manifest.name.length > 64) {
|
|
133
|
+
throw new Error(`Claude skill name "${manifest.name}" exceeds 64 character limit (${manifest.name.length} characters).\n` +
|
|
134
|
+
'According to Claude documentation, skill names must be max 64 characters.\n' +
|
|
135
|
+
'Please shorten your package name.');
|
|
136
|
+
}
|
|
137
|
+
// Validate skill name format (lowercase, numbers, hyphens only)
|
|
138
|
+
if (!/^[a-z0-9-]+$/.test(manifest.name)) {
|
|
139
|
+
throw new Error(`Claude skill name "${manifest.name}" contains invalid characters.\n` +
|
|
140
|
+
'According to Claude documentation, skill names must use lowercase letters, numbers, and hyphens only.\n' +
|
|
141
|
+
'Please update your package name.');
|
|
142
|
+
}
|
|
143
|
+
// Validate description length (max 1024 characters)
|
|
144
|
+
if (manifest.description.length > 1024) {
|
|
145
|
+
throw new Error(`Claude skill description exceeds 1024 character limit (${manifest.description.length} characters).\n` +
|
|
146
|
+
'According to Claude documentation, skill descriptions must be max 1024 characters.\n' +
|
|
147
|
+
'Please shorten your description.');
|
|
148
|
+
}
|
|
149
|
+
// Warn if description is approaching the limit (80% = 819 chars)
|
|
150
|
+
if (manifest.description.length > 819) {
|
|
151
|
+
console.warn(`ā ļø Warning: Skill description is ${manifest.description.length}/1024 characters (${Math.round(manifest.description.length / 1024 * 100)}% of limit).\n` +
|
|
152
|
+
' Consider keeping it concise for better discoverability.');
|
|
153
|
+
}
|
|
154
|
+
// Warn if description is too short (less than 100 chars)
|
|
155
|
+
if (manifest.description.length < 100) {
|
|
156
|
+
console.warn(`ā ļø Warning: Skill description is only ${manifest.description.length} characters.\n` +
|
|
157
|
+
' Claude uses descriptions for skill discovery - consider adding more detail about:\n' +
|
|
158
|
+
' - What the skill does\n' +
|
|
159
|
+
' - When Claude should use it\n' +
|
|
160
|
+
' - What problems it solves');
|
|
113
161
|
}
|
|
114
162
|
}
|
|
115
163
|
return manifest;
|
|
@@ -212,14 +260,24 @@ async function handlePublish(options) {
|
|
|
212
260
|
version = manifest.version;
|
|
213
261
|
console.log(` Source: ${source}`);
|
|
214
262
|
console.log(` Package: ${manifest.name}@${manifest.version}`);
|
|
215
|
-
console.log(`
|
|
263
|
+
console.log(` Format: ${manifest.format} | Subtype: ${manifest.subtype || 'rule (default)'}`);
|
|
216
264
|
console.log(` Description: ${manifest.description}`);
|
|
217
265
|
console.log('');
|
|
218
266
|
// Create tarball
|
|
219
267
|
console.log('š¦ Creating package tarball...');
|
|
220
268
|
const tarball = await createTarball(manifest);
|
|
221
|
-
|
|
222
|
-
|
|
269
|
+
// Display size in KB or MB depending on size
|
|
270
|
+
const sizeInBytes = tarball.length;
|
|
271
|
+
const sizeInKB = sizeInBytes / 1024;
|
|
272
|
+
const sizeInMB = sizeInBytes / (1024 * 1024);
|
|
273
|
+
let sizeDisplay;
|
|
274
|
+
if (sizeInMB >= 1) {
|
|
275
|
+
sizeDisplay = `${sizeInMB.toFixed(2)}MB`;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
sizeDisplay = `${sizeInKB.toFixed(2)}KB`;
|
|
279
|
+
}
|
|
280
|
+
console.log(` Size: ${sizeDisplay}`);
|
|
223
281
|
console.log('');
|
|
224
282
|
if (options.dryRun) {
|
|
225
283
|
console.log('ā
Dry run successful! Package is ready to publish.');
|
|
@@ -243,6 +301,34 @@ async function handlePublish(options) {
|
|
|
243
301
|
catch (err) {
|
|
244
302
|
error = err instanceof Error ? err.message : String(err);
|
|
245
303
|
console.error(`\nā Failed to publish package: ${error}\n`);
|
|
304
|
+
// Provide helpful hints based on error type
|
|
305
|
+
if (error.includes('Manifest validation failed')) {
|
|
306
|
+
console.log('š” Common validation issues:');
|
|
307
|
+
console.log(' - Missing required fields (name, version, description, format)');
|
|
308
|
+
console.log(' - Invalid format or subtype values');
|
|
309
|
+
console.log(' - Description too short (min 10 chars) or too long (max 500 chars)');
|
|
310
|
+
console.log(' - Package name must be lowercase with hyphens only');
|
|
311
|
+
console.log('');
|
|
312
|
+
console.log('š” For Claude skills specifically:');
|
|
313
|
+
console.log(' - Add "subtype": "skill" to your prpm.json');
|
|
314
|
+
console.log(' - Ensure files include a SKILL.md file');
|
|
315
|
+
console.log(' - Package name must be max 64 characters');
|
|
316
|
+
console.log('');
|
|
317
|
+
console.log('š” View the schema: prpm schema');
|
|
318
|
+
console.log('');
|
|
319
|
+
}
|
|
320
|
+
else if (error.includes('SKILL.md')) {
|
|
321
|
+
console.log('š” Claude skills require:');
|
|
322
|
+
console.log(' - A file named SKILL.md (all caps) in your package');
|
|
323
|
+
console.log(' - "format": "claude" and "subtype": "skill" in prpm.json');
|
|
324
|
+
console.log('');
|
|
325
|
+
}
|
|
326
|
+
else if (error.includes('No manifest file found')) {
|
|
327
|
+
console.log('š” Create a manifest file:');
|
|
328
|
+
console.log(' - Run: prpm init');
|
|
329
|
+
console.log(' - Or create prpm.json manually');
|
|
330
|
+
console.log('');
|
|
331
|
+
}
|
|
246
332
|
process.exit(1);
|
|
247
333
|
}
|
|
248
334
|
finally {
|
|
@@ -270,5 +356,8 @@ function createPublishCommand() {
|
|
|
270
356
|
.option('--access <type>', 'Package access (public or private)', 'public')
|
|
271
357
|
.option('--tag <tag>', 'NPM-style tag (e.g., latest, beta)', 'latest')
|
|
272
358
|
.option('--dry-run', 'Validate package without publishing')
|
|
273
|
-
.action(
|
|
359
|
+
.action(async (options) => {
|
|
360
|
+
await handlePublish(options);
|
|
361
|
+
process.exit(0);
|
|
362
|
+
});
|
|
274
363
|
}
|
package/dist/commands/schema.js
CHANGED
|
@@ -32,6 +32,9 @@ function createSchemaCommand() {
|
|
|
32
32
|
const command = new commander_1.Command('schema');
|
|
33
33
|
command
|
|
34
34
|
.description('Display the PRPM manifest JSON schema')
|
|
35
|
-
.action(
|
|
35
|
+
.action(async () => {
|
|
36
|
+
await handleSchema();
|
|
37
|
+
process.exit(0);
|
|
38
|
+
});
|
|
36
39
|
return command;
|
|
37
40
|
}
|