prpm 0.0.21 → 0.1.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.
@@ -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,11 @@ 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)' };
98
104
  }
99
105
  // Single package manifest
100
106
  const validated = validateManifest(manifest);
101
- return { manifests: [validated], source: 'prpm.json' };
107
+ return { manifests: [validated], collections, source: 'prpm.json' };
102
108
  }
103
109
  catch (error) {
104
110
  // Store error for later
@@ -127,7 +133,7 @@ async function findAndLoadManifests() {
127
133
  const validated = validateManifest(manifest);
128
134
  manifests.push(validated);
129
135
  }
130
- return { manifests, source: '.claude/marketplace.json' };
136
+ return { manifests, collections: [], source: '.claude/marketplace.json' };
131
137
  }
132
138
  catch (error) {
133
139
  // marketplace.json not found or invalid at .claude path, try .claude-plugin
@@ -147,7 +153,7 @@ async function findAndLoadManifests() {
147
153
  const validated = validateManifest(manifest);
148
154
  manifests.push(validated);
149
155
  }
150
- return { manifests, source: '.claude-plugin/marketplace.json' };
156
+ return { manifests, collections: [], source: '.claude-plugin/marketplace.json' };
151
157
  }
152
158
  catch (error) {
153
159
  // marketplace.json not found or invalid
@@ -322,13 +328,21 @@ async function handlePublish(options) {
322
328
  console.log('📦 Publishing package...\n');
323
329
  // Read and validate manifests
324
330
  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}`);
331
+ const { manifests, collections, source } = await findAndLoadManifests();
332
+ if (manifests.length > 1 || collections.length > 0) {
333
+ if (manifests.length > 0) {
334
+ console.log(` Found ${manifests.length} package(s) in ${source}`);
335
+ if (options.package) {
336
+ console.log(` Filtering to package: ${options.package}`);
337
+ }
330
338
  }
331
- console.log(' Will publish each plugin separately\n');
339
+ if (collections.length > 0) {
340
+ console.log(` Found ${collections.length} collection(s) in ${source}`);
341
+ if (options.collection) {
342
+ console.log(` Filtering to collection: ${options.collection}`);
343
+ }
344
+ }
345
+ console.log(' Will publish each separately\n');
332
346
  }
333
347
  // Filter to specific package if requested
334
348
  let filteredManifests = manifests;
@@ -538,6 +552,97 @@ async function handlePublish(options) {
538
552
  });
539
553
  }
540
554
  }
555
+ // Publish collections if present
556
+ if (collections.length > 0) {
557
+ // Filter to specific collection if requested
558
+ let filteredCollections = collections;
559
+ if (options.collection) {
560
+ filteredCollections = collections.filter(c => c.id === options.collection);
561
+ if (filteredCollections.length === 0) {
562
+ throw new Error(`Collection "${options.collection}" not found in manifest. Available collections: ${collections.map(c => c.id).join(', ')}`);
563
+ }
564
+ console.log(` ✓ Found collection "${options.collection}"\n`);
565
+ }
566
+ // Track published collections
567
+ const publishedCollections = [];
568
+ const failedCollections = [];
569
+ for (const collection of filteredCollections) {
570
+ if (filteredCollections.length > 1) {
571
+ console.log(`\n${'='.repeat(60)}`);
572
+ console.log(`📚 Publishing collection`);
573
+ console.log(`${'='.repeat(60)}\n`);
574
+ }
575
+ try {
576
+ console.log(`📚 Publishing collection "${collection.name}"...`);
577
+ console.log(` ID: ${collection.id}`);
578
+ console.log(` Packages: ${collection.packages.length}`);
579
+ console.log('');
580
+ if (options.dryRun) {
581
+ console.log('✅ Dry run successful! Collection is ready to publish.');
582
+ publishedCollections.push({
583
+ id: collection.id,
584
+ name: collection.name,
585
+ version: collection.version || '1.0.0'
586
+ });
587
+ continue;
588
+ }
589
+ // Import and call the collection publish logic
590
+ const { handleCollectionPublish } = await Promise.resolve().then(() => __importStar(require('./collections.js')));
591
+ // Create a temporary manifest object for the collection
592
+ const collectionData = {
593
+ id: collection.id,
594
+ name: collection.name,
595
+ description: collection.description,
596
+ version: collection.version,
597
+ category: collection.category,
598
+ tags: collection.tags,
599
+ icon: collection.icon,
600
+ packages: collection.packages.map(pkg => ({
601
+ packageId: pkg.packageId,
602
+ version: pkg.version,
603
+ required: pkg.required !== false,
604
+ reason: pkg.reason,
605
+ })),
606
+ };
607
+ const result = await client.createCollection(collectionData);
608
+ console.log(`✅ Collection published successfully!`);
609
+ console.log(` Scope: ${result.scope}`);
610
+ console.log(` Name: ${result.name_slug}`);
611
+ console.log(` Version: ${result.version || '1.0.0'}`);
612
+ console.log('');
613
+ console.log(`💡 Install: prpm install collections/${result.name_slug}`);
614
+ console.log('');
615
+ publishedCollections.push({
616
+ id: collection.id,
617
+ name: collection.name,
618
+ version: result.version || '1.0.0'
619
+ });
620
+ }
621
+ catch (err) {
622
+ const collError = err instanceof Error ? err.message : String(err);
623
+ console.error(`\n❌ Failed to publish collection ${collection.id}: ${collError}\n`);
624
+ failedCollections.push({
625
+ id: collection.id,
626
+ error: collError
627
+ });
628
+ }
629
+ }
630
+ // Add collection results to summary
631
+ if (publishedCollections.length > 0) {
632
+ console.log(`✅ Successfully published ${publishedCollections.length} collection(s):`);
633
+ publishedCollections.forEach(coll => {
634
+ console.log(` - ${coll.name} (${coll.id}) v${coll.version}`);
635
+ });
636
+ console.log('');
637
+ }
638
+ if (failedCollections.length > 0) {
639
+ console.log(`❌ Failed to publish ${failedCollections.length} collection(s):`);
640
+ failedCollections.forEach(coll => {
641
+ console.log(` - ${coll.id}: ${coll.error}`);
642
+ });
643
+ console.log('');
644
+ }
645
+ }
541
646
  // Print summary if multiple packages
542
647
  if (manifests.length > 1) {
543
648
  console.log(`\n${'='.repeat(60)}`);
@@ -628,11 +733,12 @@ async function handlePublish(options) {
628
733
  */
629
734
  function createPublishCommand() {
630
735
  return new commander_1.Command('publish')
631
- .description('Publish a package to the registry')
736
+ .description('Publish packages and collections to the registry')
632
737
  .option('--access <type>', 'Package access (public or private) - overrides manifest setting')
633
738
  .option('--tag <tag>', 'NPM-style tag (e.g., latest, beta)', 'latest')
634
739
  .option('--dry-run', 'Validate package without publishing')
635
740
  .option('--package <name>', 'Publish only a specific package from multi-package manifest')
741
+ .option('--collection <id>', 'Publish only a specific collection from manifest')
636
742
  .action(async (options) => {
637
743
  await handlePublish(options);
638
744
  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.0",
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.2.16",
49
+ "@pr-pm/types": "^0.1.16",
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
  }