prpm 0.0.21 → 0.1.1

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.
@@ -32,6 +32,8 @@ function getDestinationDir(type) {
32
32
  return '.continue/rules';
33
33
  case 'windsurf':
34
34
  return '.windsurf/rules';
35
+ case 'agents.md':
36
+ return '.agents';
35
37
  case 'generic':
36
38
  return '.prompts';
37
39
  case 'mcp':
@@ -25,7 +25,7 @@ async function handlePopular(options) {
25
25
  function createPopularCommand() {
26
26
  return new commander_1.Command('popular')
27
27
  .description('Show popular packages (all time)')
28
- .option('--format <format>', 'Filter by format (cursor, claude, continue, windsurf, copilot, kiro, generic)')
28
+ .option('--format <format>', 'Filter by format (cursor, claude, continue, windsurf, copilot, kiro, agents.md, generic)')
29
29
  .option('--subtype <subtype>', 'Filter by subtype (rule, agent, skill, slash-command, prompt, workflow, tool, template, collection)')
30
30
  .action(async (options) => {
31
31
  await handlePopular(options);
@@ -54,7 +54,7 @@ const snippet_extractor_1 = require("../utils/snippet-extractor");
54
54
  /**
55
55
  * Try to find and load manifest files
56
56
  * Checks for:
57
- * 1. prpm.json (native format) - returns single manifest or array of packages
57
+ * 1. prpm.json (native format) - returns single manifest or array of packages and collections
58
58
  * 2. .claude/marketplace.json (Claude format) - returns all plugins as separate manifests
59
59
  * 3. .claude-plugin/marketplace.json (Claude format - alternative location) - returns all plugins
60
60
  */
@@ -66,6 +66,12 @@ async function findAndLoadManifests() {
66
66
  try {
67
67
  const content = await (0, promises_1.readFile)(prpmJsonPath, 'utf-8');
68
68
  const manifest = JSON.parse(content);
69
+ // Extract collections if present
70
+ const collections = [];
71
+ if ('collections' in manifest && Array.isArray(manifest.collections)) {
72
+ const rawCollections = manifest.collections;
73
+ collections.push(...rawCollections);
74
+ }
69
75
  // Check if this is a multi-package manifest
70
76
  if ('packages' in manifest && Array.isArray(manifest.packages)) {
71
77
  const multiManifest = manifest;
@@ -94,11 +100,15 @@ async function findAndLoadManifests() {
94
100
  };
95
101
  return validateManifest(packageWithDefaults);
96
102
  });
97
- return { manifests: validatedManifests, source: 'prpm.json (multi-package)' };
103
+ return { manifests: validatedManifests, collections, source: 'prpm.json (multi-package)' };
104
+ }
105
+ // Collections-only manifest (no packages)
106
+ if (collections.length > 0) {
107
+ return { manifests: [], collections, source: 'prpm.json (collections-only)' };
98
108
  }
99
109
  // Single package manifest
100
110
  const validated = validateManifest(manifest);
101
- return { manifests: [validated], source: 'prpm.json' };
111
+ return { manifests: [validated], collections, source: 'prpm.json' };
102
112
  }
103
113
  catch (error) {
104
114
  // Store error for later
@@ -127,7 +137,7 @@ async function findAndLoadManifests() {
127
137
  const validated = validateManifest(manifest);
128
138
  manifests.push(validated);
129
139
  }
130
- return { manifests, source: '.claude/marketplace.json' };
140
+ return { manifests, collections: [], source: '.claude/marketplace.json' };
131
141
  }
132
142
  catch (error) {
133
143
  // marketplace.json not found or invalid at .claude path, try .claude-plugin
@@ -147,7 +157,7 @@ async function findAndLoadManifests() {
147
157
  const validated = validateManifest(manifest);
148
158
  manifests.push(validated);
149
159
  }
150
- return { manifests, source: '.claude-plugin/marketplace.json' };
160
+ return { manifests, collections: [], source: '.claude-plugin/marketplace.json' };
151
161
  }
152
162
  catch (error) {
153
163
  // marketplace.json not found or invalid
@@ -322,13 +332,21 @@ async function handlePublish(options) {
322
332
  console.log('📦 Publishing package...\n');
323
333
  // Read and validate manifests
324
334
  console.log('🔍 Validating package manifest(s)...');
325
- const { manifests, source } = await findAndLoadManifests();
326
- if (manifests.length > 1) {
327
- console.log(` Found ${manifests.length} plugins in ${source}`);
328
- if (options.package) {
329
- console.log(` Filtering to package: ${options.package}`);
335
+ const { manifests, collections, source } = await findAndLoadManifests();
336
+ if (manifests.length > 1 || collections.length > 0) {
337
+ if (manifests.length > 0) {
338
+ console.log(` Found ${manifests.length} package(s) in ${source}`);
339
+ if (options.package) {
340
+ console.log(` Filtering to package: ${options.package}`);
341
+ }
330
342
  }
331
- console.log(' Will publish each plugin separately\n');
343
+ if (collections.length > 0) {
344
+ console.log(` Found ${collections.length} collection(s) in ${source}`);
345
+ if (options.collection) {
346
+ console.log(` Filtering to collection: ${options.collection}`);
347
+ }
348
+ }
349
+ console.log(' Will publish each separately\n');
332
350
  }
333
351
  // Filter to specific package if requested
334
352
  let filteredManifests = manifests;
@@ -373,9 +391,11 @@ async function handlePublish(options) {
373
391
  throw new Error('Cannot publish packages with duplicate names');
374
392
  }
375
393
  }
376
- // Track published packages
394
+ // Track published packages and collections
377
395
  const publishedPackages = [];
378
396
  const failedPackages = [];
397
+ const publishedCollections = [];
398
+ const failedCollections = [];
379
399
  // Publish each manifest (filtered set)
380
400
  for (let i = 0; i < filteredManifests.length; i++) {
381
401
  const manifest = filteredManifests[i];
@@ -538,6 +558,94 @@ async function handlePublish(options) {
538
558
  });
539
559
  }
540
560
  }
561
+ // Publish collections if present
562
+ if (collections.length > 0) {
563
+ // Filter to specific collection if requested
564
+ let filteredCollections = collections;
565
+ if (options.collection) {
566
+ filteredCollections = collections.filter(c => c.id === options.collection);
567
+ if (filteredCollections.length === 0) {
568
+ throw new Error(`Collection "${options.collection}" not found in manifest. Available collections: ${collections.map(c => c.id).join(', ')}`);
569
+ }
570
+ console.log(` ✓ Found collection "${options.collection}"\n`);
571
+ }
572
+ for (const collection of filteredCollections) {
573
+ if (filteredCollections.length > 1) {
574
+ console.log(`\n${'='.repeat(60)}`);
575
+ console.log(`📚 Publishing collection`);
576
+ console.log(`${'='.repeat(60)}\n`);
577
+ }
578
+ try {
579
+ console.log(`📚 Publishing collection "${collection.name}"...`);
580
+ console.log(` ID: ${collection.id}`);
581
+ console.log(` Packages: ${collection.packages.length}`);
582
+ console.log('');
583
+ if (options.dryRun) {
584
+ console.log('✅ Dry run successful! Collection is ready to publish.');
585
+ publishedCollections.push({
586
+ id: collection.id,
587
+ name: collection.name,
588
+ version: collection.version || '1.0.0'
589
+ });
590
+ continue;
591
+ }
592
+ // Import and call the collection publish logic
593
+ const { handleCollectionPublish } = await Promise.resolve().then(() => __importStar(require('./collections.js')));
594
+ // Create a temporary manifest object for the collection
595
+ const collectionData = {
596
+ id: collection.id,
597
+ name: collection.name,
598
+ description: collection.description,
599
+ version: collection.version,
600
+ category: collection.category,
601
+ tags: collection.tags,
602
+ icon: collection.icon,
603
+ packages: collection.packages.map(pkg => ({
604
+ packageId: pkg.packageId,
605
+ version: pkg.version,
606
+ required: pkg.required !== false,
607
+ reason: pkg.reason,
608
+ })),
609
+ };
610
+ const result = await client.createCollection(collectionData);
611
+ console.log(`✅ Collection published successfully!`);
612
+ console.log(` Scope: ${result.scope}`);
613
+ console.log(` Name: ${result.name_slug}`);
614
+ console.log(` Version: ${result.version || '1.0.0'}`);
615
+ console.log('');
616
+ console.log(`💡 Install: prpm install collections/${result.name_slug}`);
617
+ console.log('');
618
+ publishedCollections.push({
619
+ id: collection.id,
620
+ name: collection.name,
621
+ version: result.version || '1.0.0'
622
+ });
623
+ }
624
+ catch (err) {
625
+ const collError = err instanceof Error ? err.message : String(err);
626
+ console.error(`\n❌ Failed to publish collection ${collection.id}: ${collError}\n`);
627
+ failedCollections.push({
628
+ id: collection.id,
629
+ error: collError
630
+ });
631
+ }
632
+ }
633
+ // Add collection results to summary
634
+ if (publishedCollections.length > 0) {
635
+ console.log(`✅ Successfully published ${publishedCollections.length} collection(s):`);
636
+ publishedCollections.forEach(coll => {
637
+ console.log(` - ${coll.name} (${coll.id}) v${coll.version}`);
638
+ });
639
+ console.log('');
640
+ }
641
+ if (failedCollections.length > 0) {
642
+ console.log(`❌ Failed to publish ${failedCollections.length} collection(s):`);
643
+ failedCollections.forEach(coll => {
644
+ console.log(` - ${coll.id}: ${coll.error}`);
645
+ });
646
+ console.log('');
647
+ }
648
+ }
541
649
  // Print summary if multiple packages
542
650
  if (manifests.length > 1) {
543
651
  console.log(`\n${'='.repeat(60)}`);
@@ -569,8 +677,9 @@ async function handlePublish(options) {
569
677
  }
570
678
  }
571
679
  }
572
- success = publishedPackages.length > 0;
573
- if (failedPackages.length > 0 && publishedPackages.length === 0) {
680
+ // Success if we published any packages OR collections
681
+ success = publishedPackages.length > 0 || publishedCollections.length > 0;
682
+ if (failedPackages.length > 0 && publishedPackages.length === 0 && publishedCollections.length === 0) {
574
683
  process.exit(1);
575
684
  }
576
685
  }
@@ -628,11 +737,12 @@ async function handlePublish(options) {
628
737
  */
629
738
  function createPublishCommand() {
630
739
  return new commander_1.Command('publish')
631
- .description('Publish a package to the registry')
740
+ .description('Publish packages and collections to the registry')
632
741
  .option('--access <type>', 'Package access (public or private) - overrides manifest setting')
633
742
  .option('--tag <tag>', 'NPM-style tag (e.g., latest, beta)', 'latest')
634
743
  .option('--dry-run', 'Validate package without publishing')
635
744
  .option('--package <name>', 'Publish only a specific package from multi-package manifest')
745
+ .option('--collection <id>', 'Publish only a specific collection from manifest')
636
746
  .action(async (options) => {
637
747
  await handlePublish(options);
638
748
  process.exit(0);
@@ -391,7 +391,7 @@ function createSearchCommand() {
391
391
  command
392
392
  .description('Search for packages in the registry')
393
393
  .argument('[query]', 'Search query (optional when using --format/--subtype or --author)')
394
- .option('--format <format>', 'Filter by package format (cursor, claude, continue, windsurf, copilot, kiro, generic, mcp)')
394
+ .option('--format <format>', 'Filter by package format (cursor, claude, continue, windsurf, copilot, kiro, agents.md, generic, mcp)')
395
395
  .option('--subtype <subtype>', 'Filter by package subtype (rule, agent, skill, slash-command, prompt, workflow, tool, template, collection)')
396
396
  .option('--author <username>', 'Filter by author username')
397
397
  .option('--limit <number>', 'Number of results per page', '20')
@@ -404,7 +404,7 @@ function createSearchCommand() {
404
404
  const author = options.author;
405
405
  const limit = options.limit ? parseInt(options.limit, 10) : 20;
406
406
  const page = options.page ? parseInt(options.page, 10) : 1;
407
- const validFormats = ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'generic', 'mcp'];
407
+ const validFormats = ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'agents.md', 'generic', 'mcp'];
408
408
  const validSubtypes = ['rule', 'agent', 'skill', 'slash-command', 'prompt', 'collection', 'chatmode'];
409
409
  if (options.format && !validFormats.includes(format)) {
410
410
  console.error(`❌ Format must be one of: ${validFormats.join(', ')}`);
@@ -62,14 +62,14 @@ function createTrendingCommand() {
62
62
  const command = new commander_1.Command('trending');
63
63
  command
64
64
  .description('Show trending packages')
65
- .option('--format <format>', 'Filter by format (cursor, claude, continue, windsurf, copilot, kiro, generic)')
65
+ .option('--format <format>', 'Filter by format (cursor, claude, continue, windsurf, copilot, kiro, agents.md, generic)')
66
66
  .option('--subtype <subtype>', 'Filter by subtype (rule, agent, skill, slash-command, prompt, workflow, tool, template, collection)')
67
67
  .option('--limit <number>', 'Number of packages to show', '10')
68
68
  .action(async (options) => {
69
69
  const format = options.format;
70
70
  const subtype = options.subtype;
71
71
  const limit = options.limit ? parseInt(options.limit, 10) : 10;
72
- const validFormats = ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'generic', 'mcp'];
72
+ const validFormats = ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'agents.md', 'generic', 'mcp'];
73
73
  const validSubtypes = ['rule', 'agent', 'skill', 'slash-command', 'prompt', 'workflow', 'tool', 'template', 'collection'];
74
74
  if (options.format && !validFormats.includes(format)) {
75
75
  console.error(`❌ Format must be one of: ${validFormats.join(', ')}`);
@@ -52,6 +52,8 @@ function getDestinationDir(format, subtype, name) {
52
52
  return '.github/instructions';
53
53
  case 'kiro':
54
54
  return '.kiro/steering';
55
+ case 'agents.md':
56
+ return '.agents';
55
57
  case 'generic':
56
58
  return '.prompts';
57
59
  case 'mcp':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prpm",
3
- "version": "0.0.21",
3
+ "version": "0.1.1",
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.15",
49
- "@pr-pm/types": "^0.1.15",
48
+ "@pr-pm/registry-client": "^1.3.0",
49
+ "@pr-pm/types": "^0.2.0",
50
50
  "ajv": "^8.17.1",
51
51
  "ajv-formats": "^3.0.1",
52
52
  "commander": "^11.1.0",
@@ -367,6 +367,100 @@
367
367
  "$ref": "#"
368
368
  },
369
369
  "minItems": 1
370
+ },
371
+ "collections": {
372
+ "type": "array",
373
+ "description": "Array of collections to publish. Collections bundle multiple packages together for easier installation.",
374
+ "items": {
375
+ "type": "object",
376
+ "required": ["id", "name", "description", "packages"],
377
+ "properties": {
378
+ "id": {
379
+ "type": "string",
380
+ "description": "Unique collection identifier (kebab-case)",
381
+ "pattern": "^[a-z0-9-]+$",
382
+ "minLength": 3,
383
+ "maxLength": 100,
384
+ "examples": ["nextjs-complete", "fullstack-setup", "react-essentials"]
385
+ },
386
+ "name": {
387
+ "type": "string",
388
+ "description": "Display name of the collection",
389
+ "minLength": 3,
390
+ "maxLength": 100,
391
+ "examples": ["Next.js Complete", "Full Stack Setup"]
392
+ },
393
+ "description": {
394
+ "type": "string",
395
+ "description": "What this collection provides",
396
+ "minLength": 10,
397
+ "maxLength": 500
398
+ },
399
+ "version": {
400
+ "type": "string",
401
+ "description": "Semantic version of the collection",
402
+ "pattern": "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$",
403
+ "examples": ["1.0.0", "2.1.0"]
404
+ },
405
+ "category": {
406
+ "type": "string",
407
+ "description": "Collection category",
408
+ "enum": ["development", "testing", "deployment", "data-science", "devops", "design", "documentation", "security", "performance", "general"],
409
+ "examples": ["development", "testing"]
410
+ },
411
+ "tags": {
412
+ "type": "array",
413
+ "description": "Tags for discoverability (kebab-case)",
414
+ "items": {
415
+ "type": "string",
416
+ "pattern": "^[a-z0-9-]+$"
417
+ },
418
+ "minItems": 1,
419
+ "maxItems": 10,
420
+ "examples": [["react", "typescript", "nextjs"], ["python", "data-science", "ml"]]
421
+ },
422
+ "icon": {
423
+ "type": "string",
424
+ "description": "Emoji or icon for the collection",
425
+ "maxLength": 10,
426
+ "examples": ["⚛️", "🚀", "📦"]
427
+ },
428
+ "packages": {
429
+ "type": "array",
430
+ "description": "Array of packages included in this collection",
431
+ "items": {
432
+ "type": "object",
433
+ "required": ["packageId"],
434
+ "properties": {
435
+ "packageId": {
436
+ "type": "string",
437
+ "description": "Package identifier to include",
438
+ "minLength": 1,
439
+ "examples": ["typescript-strict", "react-best-practices"]
440
+ },
441
+ "version": {
442
+ "type": "string",
443
+ "description": "Version range (semver) or 'latest'",
444
+ "examples": ["^1.0.0", "~2.1.0", "1.0.0", "latest"]
445
+ },
446
+ "required": {
447
+ "type": "boolean",
448
+ "description": "Whether this package is required (true) or optional (false)",
449
+ "default": true
450
+ },
451
+ "reason": {
452
+ "type": "string",
453
+ "description": "Explanation of why this package is included",
454
+ "maxLength": 200,
455
+ "examples": ["Enforces strict TypeScript type safety", "React component best practices"]
456
+ }
457
+ }
458
+ },
459
+ "minItems": 1
460
+ }
461
+ }
462
+ },
463
+ "minItems": 1
370
464
  }
371
465
  },
372
466
  "additionalProperties": false,
@@ -549,6 +643,58 @@
549
643
  ]
550
644
  }
551
645
  ]
646
+ },
647
+ {
648
+ "name": "@username/multi-package-with-collection",
649
+ "version": "1.0.0",
650
+ "description": "Repository with both packages and a collection",
651
+ "author": "Your Name",
652
+ "license": "MIT",
653
+ "packages": [
654
+ {
655
+ "name": "typescript-rules",
656
+ "version": "1.0.0",
657
+ "description": "TypeScript coding standards",
658
+ "format": "cursor",
659
+ "subtype": "rule",
660
+ "tags": ["typescript", "cursor"],
661
+ "files": [".cursor/rules/typescript.mdc"]
662
+ },
663
+ {
664
+ "name": "react-patterns",
665
+ "version": "1.0.0",
666
+ "description": "React best practices",
667
+ "format": "claude",
668
+ "subtype": "skill",
669
+ "tags": ["react", "best-practices"],
670
+ "files": [".claude/skills/react-patterns/SKILL.md"]
671
+ }
672
+ ],
673
+ "collections": [
674
+ {
675
+ "id": "fullstack-setup",
676
+ "name": "Full Stack Setup",
677
+ "description": "Complete full-stack development setup with TypeScript and React",
678
+ "version": "1.0.0",
679
+ "category": "development",
680
+ "tags": ["typescript", "react", "fullstack"],
681
+ "icon": "🚀",
682
+ "packages": [
683
+ {
684
+ "packageId": "typescript-rules",
685
+ "version": "^1.0.0",
686
+ "required": true,
687
+ "reason": "TypeScript coding standards for the project"
688
+ },
689
+ {
690
+ "packageId": "react-patterns",
691
+ "version": "^1.0.0",
692
+ "required": true,
693
+ "reason": "React component best practices"
694
+ }
695
+ ]
696
+ }
697
+ ]
552
698
  }
553
699
  ]
554
700
  }