prpm 0.0.15 → 0.0.17

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.
@@ -327,20 +327,21 @@ async function handleInstall(packageSpec, options) {
327
327
  : `${destDir}/${packageName}`;
328
328
  destPath = packageDir;
329
329
  console.log(` 📁 Multi-file package - creating directory: ${packageDir}`);
330
- // For Claude skills, auto-fix filename to SKILL.md if needed
330
+ // For Claude skills, verify SKILL.md exists
331
331
  if (effectiveFormat === 'claude' && effectiveSubtype === 'skill') {
332
- const skillMdIndex = extractedFiles.findIndex(f => f.name === 'SKILL.md');
332
+ const skillMdIndex = extractedFiles.findIndex(f => f.name === 'SKILL.md' || f.name.endsWith('/SKILL.md'));
333
333
  if (skillMdIndex === -1) {
334
334
  // SKILL.md not found, look for common variations and auto-rename
335
- const skillFileIndex = extractedFiles.findIndex(f => f.name.toLowerCase() === 'skill.md' ||
336
- f.name === 'skill.md' ||
337
- f.name.endsWith('.md') && extractedFiles.length === 1 // Single .md file
335
+ const skillFileIndex = extractedFiles.findIndex(f => f.name.toLowerCase().endsWith('skill.md') ||
336
+ (f.name.endsWith('.md') && extractedFiles.length === 1) // Single .md file
338
337
  );
339
338
  if (skillFileIndex !== -1) {
340
339
  const oldName = extractedFiles[skillFileIndex].name;
341
- console.log(` ⚠️ Auto-fixing skill filename: ${oldName} → SKILL.md`);
340
+ const basePath = oldName.substring(0, oldName.lastIndexOf('/') + 1);
341
+ const newName = basePath + 'SKILL.md';
342
+ console.log(` ⚠️ Auto-fixing skill filename: ${oldName} → ${newName}`);
342
343
  console.log(` (Claude skills must be named SKILL.md per official documentation)`);
343
- extractedFiles[skillFileIndex].name = 'SKILL.md';
344
+ extractedFiles[skillFileIndex].name = newName;
344
345
  }
345
346
  else {
346
347
  throw new Error('Claude skills must contain a SKILL.md file. ' +
@@ -444,8 +445,10 @@ async function extractTarball(tarball, packageId) {
444
445
  });
445
446
  // Read all extracted files
446
447
  const extractedFiles = await fs.promises.readdir(tmpDir, { withFileTypes: true, recursive: true });
448
+ // Files to exclude from package content (metadata files)
449
+ const excludeFiles = ['package.tar', 'prpm.json', 'README.md', 'LICENSE', 'LICENSE.txt', 'LICENSE.md'];
447
450
  for (const entry of extractedFiles) {
448
- if (entry.isFile() && entry.name !== 'package.tar') {
451
+ if (entry.isFile() && !excludeFiles.includes(entry.name)) {
449
452
  const filePath = path.join(entry.path || tmpDir, entry.name);
450
453
  const content = await fs.promises.readFile(filePath, 'utf-8');
451
454
  const relativePath = path.relative(tmpDir, filePath);
@@ -350,8 +350,20 @@ async function handlePublish(options) {
350
350
  const { manifests, source } = await findAndLoadManifests();
351
351
  if (manifests.length > 1) {
352
352
  console.log(` Found ${manifests.length} plugins in ${source}`);
353
+ if (options.package) {
354
+ console.log(` Filtering to package: ${options.package}`);
355
+ }
353
356
  console.log(' Will publish each plugin separately\n');
354
357
  }
358
+ // Filter to specific package if requested
359
+ let filteredManifests = manifests;
360
+ if (options.package) {
361
+ filteredManifests = manifests.filter(m => m.name === options.package);
362
+ if (filteredManifests.length === 0) {
363
+ throw new Error(`Package "${options.package}" not found in manifest. Available packages: ${manifests.map(m => m.name).join(', ')}`);
364
+ }
365
+ console.log(` ✓ Found package "${options.package}"\n`);
366
+ }
355
367
  // Get user info to check for organizations (once for all packages)
356
368
  console.log('🔍 Checking authentication...');
357
369
  const client = (0, registry_client_1.getRegistryClient)(config);
@@ -363,11 +375,11 @@ async function handlePublish(options) {
363
375
  console.log(' Could not fetch user organizations, publishing as personal packages');
364
376
  }
365
377
  console.log('');
366
- // Check for duplicate package names
367
- if (manifests.length > 1) {
378
+ // Check for duplicate package names (only in filtered set)
379
+ if (filteredManifests.length > 1) {
368
380
  const nameMap = new Map();
369
381
  const duplicates = [];
370
- manifests.forEach((manifest, index) => {
382
+ filteredManifests.forEach((manifest, index) => {
371
383
  const existingIndex = nameMap.get(manifest.name);
372
384
  if (existingIndex !== undefined) {
373
385
  duplicates.push(` - "${manifest.name}" appears in positions ${existingIndex + 1} and ${index + 1}`);
@@ -389,14 +401,14 @@ async function handlePublish(options) {
389
401
  // Track published packages
390
402
  const publishedPackages = [];
391
403
  const failedPackages = [];
392
- // Publish each manifest
393
- for (let i = 0; i < manifests.length; i++) {
394
- const manifest = manifests[i];
404
+ // Publish each manifest (filtered set)
405
+ for (let i = 0; i < filteredManifests.length; i++) {
406
+ const manifest = filteredManifests[i];
395
407
  packageName = manifest.name;
396
408
  version = manifest.version;
397
- if (manifests.length > 1) {
409
+ if (filteredManifests.length > 1) {
398
410
  console.log(`\n${'='.repeat(60)}`);
399
- console.log(`📦 Publishing plugin ${i + 1} of ${manifests.length}`);
411
+ console.log(`📦 Publishing plugin ${i + 1} of ${filteredManifests.length}`);
400
412
  console.log(`${'='.repeat(60)}\n`);
401
413
  }
402
414
  try {
@@ -509,6 +521,10 @@ async function handlePublish(options) {
509
521
  }
510
522
  // Publish to registry
511
523
  console.log('🚀 Publishing to registry...');
524
+ if (selectedOrgId) {
525
+ console.log(` Publishing as organization: ${userInfo.organizations.find((org) => org.id === selectedOrgId)?.name}`);
526
+ console.log(` Organization ID: ${selectedOrgId}`);
527
+ }
512
528
  const result = await client.publish(manifest, tarball, selectedOrgId ? { orgId: selectedOrgId } : undefined);
513
529
  // Determine the webapp URL based on registry URL
514
530
  let webappUrl;
@@ -568,6 +584,14 @@ async function handlePublish(options) {
568
584
  console.log(` - ${pkg.name}: ${pkg.error}`);
569
585
  });
570
586
  console.log('');
587
+ // Provide hints for common permission errors
588
+ if (failedPackages.some(pkg => pkg.error.includes('Forbidden'))) {
589
+ console.log('💡 Forbidden errors usually mean:');
590
+ console.log(' - The package already exists and you don\'t have permission to update it');
591
+ console.log(' - The package belongs to an organization and you\'re not a member with publish rights');
592
+ console.log(' - Try: prpm whoami (to check your organization memberships)');
593
+ console.log('');
594
+ }
571
595
  }
572
596
  }
573
597
  success = publishedPackages.length > 0;
@@ -633,6 +657,7 @@ function createPublishCommand() {
633
657
  .option('--access <type>', 'Package access (public or private) - overrides manifest setting')
634
658
  .option('--tag <tag>', 'NPM-style tag (e.g., latest, beta)', 'latest')
635
659
  .option('--dry-run', 'Validate package without publishing')
660
+ .option('--package <name>', 'Publish only a specific package from multi-package manifest')
636
661
  .action(async (options) => {
637
662
  await handlePublish(options);
638
663
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prpm",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "Prompt Package Manager CLI - Install and manage prompt-based files",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -45,8 +45,8 @@
45
45
  "license": "MIT",
46
46
  "dependencies": {
47
47
  "@octokit/rest": "^22.0.0",
48
- "@pr-pm/registry-client": "^1.2.9",
49
- "@pr-pm/types": "^0.1.9",
48
+ "@pr-pm/registry-client": "^1.2.11",
49
+ "@pr-pm/types": "^0.1.11",
50
50
  "ajv": "^8.17.1",
51
51
  "ajv-formats": "^3.0.1",
52
52
  "commander": "^11.1.0",