prpm 0.1.9 → 0.1.10

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.
@@ -356,10 +356,14 @@ async function handleInstall(packageSpec, options) {
356
356
  const destDir = (0, filesystem_1.getDestinationDir)(effectiveFormat, effectiveSubtype, pkg.name);
357
357
  // Multi-file package - create directory for package
358
358
  // For Claude skills, destDir already includes package name, so use it directly
359
+ // For Cursor rules converted from Claude skills, use flat structure
359
360
  const packageName = (0, filesystem_1.stripAuthorNamespace)(packageId);
361
+ const isCursorConversion = (effectiveFormat === 'cursor' && pkg.format === 'claude' && pkg.subtype === 'skill');
360
362
  const packageDir = (effectiveFormat === 'claude' && effectiveSubtype === 'skill')
361
363
  ? destDir
362
- : `${destDir}/${packageName}`;
364
+ : isCursorConversion
365
+ ? destDir // Cursor uses flat structure
366
+ : `${destDir}/${packageName}`;
363
367
  destPath = packageDir;
364
368
  console.log(` šŸ“ Multi-file package - creating directory: ${packageDir}`);
365
369
  // For Claude skills, verify SKILL.md exists
@@ -385,6 +389,8 @@ async function handleInstall(packageSpec, options) {
385
389
  }
386
390
  }
387
391
  }
392
+ // Track JSON files for @reference insertion in Cursor conversion
393
+ const jsonFiles = [];
388
394
  for (const file of extractedFiles) {
389
395
  // Strip the tarball's root directory prefix to preserve subdirectories
390
396
  // Example: ".claude/skills/agent-builder/docs/examples.md" → "docs/examples.md"
@@ -404,10 +410,58 @@ async function handleInstall(packageSpec, options) {
404
410
  // Fallback: just take the filename (last part)
405
411
  relativeFileName = pathParts[pathParts.length - 1];
406
412
  }
407
- const filePath = `${packageDir}/${relativeFileName}`;
408
- await (0, filesystem_1.saveFile)(filePath, file.content);
413
+ let fileContent = file.content;
414
+ let fileName = relativeFileName;
415
+ // Handle Cursor conversion from Claude skill
416
+ if (isCursorConversion) {
417
+ // Convert SKILL.md to .mdc
418
+ if (fileName === 'SKILL.md' || fileName.endsWith('/SKILL.md')) {
419
+ fileName = `${packageName}.mdc`;
420
+ // Add MDC header if missing
421
+ if (!(0, cursor_config_1.hasMDCHeader)(fileContent)) {
422
+ console.log(` āš ļø Adding MDC header to converted skill...`);
423
+ fileContent = (0, cursor_config_1.addMDCHeader)(fileContent, pkg.description);
424
+ }
425
+ // Apply cursor config if available
426
+ if (config.cursor) {
427
+ console.log(` āš™ļø Applying cursor config...`);
428
+ fileContent = (0, cursor_config_1.applyCursorConfig)(fileContent, config.cursor);
429
+ }
430
+ }
431
+ // Track JSON files for @reference
432
+ else if (fileName.endsWith('.json')) {
433
+ // Flatten structure - remove subdirectories
434
+ const jsonFileName = fileName.split('/').pop() || fileName;
435
+ fileName = jsonFileName;
436
+ jsonFiles.push(jsonFileName);
437
+ }
438
+ // For other files (docs, etc), flatten the structure
439
+ else {
440
+ fileName = fileName.split('/').pop() || fileName;
441
+ }
442
+ }
443
+ const filePath = `${packageDir}/${fileName}`;
444
+ await (0, filesystem_1.saveFile)(filePath, fileContent);
409
445
  fileCount++;
410
446
  }
447
+ // Add @references to .mdc file for JSON files
448
+ if (isCursorConversion && jsonFiles.length > 0) {
449
+ const mdcFile = `${packageDir}/${packageName}.mdc`;
450
+ const { readFile } = await Promise.resolve().then(() => __importStar(require('fs/promises')));
451
+ let mdcContent = await readFile(mdcFile, 'utf-8');
452
+ // Find the end of frontmatter (if exists)
453
+ const frontmatterMatch = mdcContent.match(/^---\n[\s\S]*?\n---\n/);
454
+ if (frontmatterMatch) {
455
+ const frontmatterEnd = frontmatterMatch[0].length;
456
+ const beforeFrontmatter = mdcContent.slice(0, frontmatterEnd);
457
+ const afterFrontmatter = mdcContent.slice(frontmatterEnd);
458
+ // Add @references right after frontmatter
459
+ const references = jsonFiles.map(f => `@${f}`).join('\n');
460
+ mdcContent = `${beforeFrontmatter}\n${references}\n${afterFrontmatter}`;
461
+ await (0, filesystem_1.saveFile)(mdcFile, mdcContent);
462
+ console.log(` āœ“ Added ${jsonFiles.length} @reference(s) to ${packageName}.mdc`);
463
+ }
464
+ }
411
465
  }
412
466
  // Update or create lock file
413
467
  const updatedLockfile = lockfile || (0, lockfile_1.createLockfile)();
@@ -254,6 +254,28 @@ function normalizeFilePaths(files) {
254
254
  }
255
255
  });
256
256
  }
257
+ /**
258
+ * Predict what the scoped package name will be after publishing
259
+ * This matches the server-side logic in packages.ts
260
+ */
261
+ function predictScopedPackageName(manifestName, username, organization) {
262
+ const usernameLowercase = username.toLowerCase();
263
+ // If organization is specified, use @org-name/
264
+ if (organization) {
265
+ const orgNameLowercase = organization.toLowerCase();
266
+ const expectedPrefix = `@${orgNameLowercase}/`;
267
+ if (!manifestName.startsWith(expectedPrefix)) {
268
+ return `${expectedPrefix}${manifestName}`;
269
+ }
270
+ return manifestName;
271
+ }
272
+ // If package name doesn't already have a scope, add @username/
273
+ if (!manifestName.startsWith('@')) {
274
+ return `@${usernameLowercase}/${manifestName}`;
275
+ }
276
+ // Package already has a scope, return as-is
277
+ return manifestName;
278
+ }
257
279
  /**
258
280
  * Create tarball from current directory
259
281
  */
@@ -442,6 +464,7 @@ async function handlePublish(options) {
442
464
  console.log('');
443
465
  }
444
466
  let selectedOrgId;
467
+ let selectedOrgName;
445
468
  // Check if organization is specified in manifest
446
469
  if (manifest.organization && userInfo) {
447
470
  const orgFromManifest = userInfo.organizations?.find((org) => org.name === manifest.organization || org.id === manifest.organization);
@@ -454,9 +477,12 @@ async function handlePublish(options) {
454
477
  `Your role: ${orgFromManifest.role}. Required: owner, admin, or maintainer`);
455
478
  }
456
479
  selectedOrgId = orgFromManifest.id;
480
+ selectedOrgName = orgFromManifest.name;
457
481
  }
482
+ // Predict what the scoped package name will be
483
+ const scopedPackageName = predictScopedPackageName(manifest.name, userInfo?.username || config.username || 'unknown', selectedOrgName || manifest.organization);
458
484
  console.log(` Source: ${source}`);
459
- console.log(` Package: ${manifest.name}@${manifest.version}`);
485
+ console.log(` Package: ${scopedPackageName}@${manifest.version}`);
460
486
  console.log(` Format: ${manifest.format} | Subtype: ${manifest.subtype}`);
461
487
  console.log(` Description: ${manifest.description}`);
462
488
  console.log(` Access: ${manifest.private ? 'private' : 'public'}`);
@@ -480,7 +506,7 @@ async function handlePublish(options) {
480
506
  manifest.license_url = licenseInfo.url || undefined;
481
507
  }
482
508
  // Validate and warn about license (optional - will extract if present)
483
- (0, license_extractor_1.validateLicenseInfo)(licenseInfo, manifest.name);
509
+ (0, license_extractor_1.validateLicenseInfo)(licenseInfo, scopedPackageName);
484
510
  console.log('');
485
511
  // Extract content snippet
486
512
  console.log('šŸ“ Extracting content snippet...');
@@ -488,7 +514,7 @@ async function handlePublish(options) {
488
514
  if (snippet) {
489
515
  manifest.snippet = snippet;
490
516
  }
491
- (0, snippet_extractor_1.validateSnippet)(snippet, manifest.name);
517
+ (0, snippet_extractor_1.validateSnippet)(snippet, scopedPackageName);
492
518
  console.log('');
493
519
  // Create tarball
494
520
  console.log('šŸ“¦ Creating package tarball...');
@@ -509,7 +535,7 @@ async function handlePublish(options) {
509
535
  if (options.dryRun) {
510
536
  console.log('āœ… Dry run successful! Package is ready to publish.');
511
537
  publishedPackages.push({
512
- name: manifest.name,
538
+ name: scopedPackageName,
513
539
  version: manifest.version,
514
540
  url: ''
515
541
  });
@@ -537,7 +563,8 @@ async function handlePublish(options) {
537
563
  // Default to registry URL for unknown environments
538
564
  webappUrl = registryUrl;
539
565
  }
540
- const packageUrl = `${webappUrl}/packages/${encodeURIComponent(manifest.name)}`;
566
+ // Use the name returned from the API (which includes auto-prefixed scope)
567
+ const packageUrl = `${webappUrl}/packages/${encodeURIComponent(result.name)}`;
541
568
  console.log('');
542
569
  console.log('āœ… Package published successfully!');
543
570
  console.log('');
@@ -545,16 +572,20 @@ async function handlePublish(options) {
545
572
  console.log(` Install: prpm install ${result.name}`);
546
573
  console.log('');
547
574
  publishedPackages.push({
548
- name: manifest.name,
575
+ name: result.name, // Use scoped name from server
549
576
  version: result.version,
550
577
  url: packageUrl
551
578
  });
552
579
  }
553
580
  catch (err) {
554
581
  const pkgError = err instanceof Error ? err.message : String(err);
555
- console.error(`\nāŒ Failed to publish ${manifest.name}: ${pkgError}\n`);
582
+ // Try to use scoped name if we have user info, otherwise fall back to manifest name
583
+ const displayName = userInfo
584
+ ? predictScopedPackageName(manifest.name, userInfo.username, manifest.organization)
585
+ : manifest.name;
586
+ console.error(`\nāŒ Failed to publish ${displayName}: ${pkgError}\n`);
556
587
  failedPackages.push({
557
- name: manifest.name,
588
+ name: displayName,
558
589
  error: pkgError
559
590
  });
560
591
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prpm",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
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.3.7",
49
- "@pr-pm/types": "^0.2.8",
48
+ "@pr-pm/registry-client": "^1.3.8",
49
+ "@pr-pm/types": "^0.2.9",
50
50
  "ajv": "^8.17.1",
51
51
  "ajv-formats": "^3.0.1",
52
52
  "commander": "^11.1.0",