prpm 1.2.1 → 2.0.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.
Files changed (67) hide show
  1. package/README.md +90 -862
  2. package/dist/index.js +23233 -65
  3. package/dist/schemas/agents-md.schema.json +24 -0
  4. package/dist/schemas/aider.schema.json +24 -0
  5. package/dist/schemas/canonical.schema.json +435 -0
  6. package/dist/schemas/claude-agent.schema.json +62 -0
  7. package/dist/schemas/claude-hook.schema.json +70 -0
  8. package/dist/schemas/claude-plugin.schema.json +122 -0
  9. package/dist/schemas/claude-skill.schema.json +51 -0
  10. package/dist/schemas/claude-slash-command.schema.json +77 -0
  11. package/dist/schemas/claude.schema.json +52 -0
  12. package/dist/schemas/continue.schema.json +98 -0
  13. package/dist/schemas/copilot.schema.json +76 -0
  14. package/dist/schemas/cursor-command.schema.json +27 -0
  15. package/dist/schemas/cursor-hooks.schema.json +59 -0
  16. package/dist/schemas/cursor.schema.json +74 -0
  17. package/dist/schemas/droid-hook.schema.json +103 -0
  18. package/dist/schemas/droid-skill.schema.json +46 -0
  19. package/dist/schemas/droid-slash-command.schema.json +53 -0
  20. package/dist/schemas/droid.schema.json +46 -0
  21. package/dist/schemas/format-registry.schema.json +99 -0
  22. package/dist/schemas/gemini-md.schema.json +24 -0
  23. package/dist/schemas/gemini.schema.json +30 -0
  24. package/dist/schemas/kiro-agent.schema.json +146 -0
  25. package/dist/schemas/kiro-hook.schema.json +120 -0
  26. package/dist/schemas/kiro-steering.schema.json +74 -0
  27. package/dist/schemas/mcp-server.schema.json +130 -0
  28. package/dist/schemas/opencode-slash-command.schema.json +60 -0
  29. package/dist/schemas/opencode.schema.json +111 -0
  30. package/dist/schemas/prpm-manifest.schema.json +733 -0
  31. package/dist/schemas/replit.schema.json +21 -0
  32. package/dist/schemas/ruler.schema.json +22 -0
  33. package/dist/schemas/trae.schema.json +24 -0
  34. package/dist/schemas/windsurf.schema.json +22 -0
  35. package/dist/schemas/zencoder.schema.json +51 -0
  36. package/package.json +20 -14
  37. package/schemas/prpm-manifest.schema.json +465 -39
  38. package/dist/__tests__/e2e/test-helpers.js +0 -150
  39. package/dist/commands/collections.js +0 -606
  40. package/dist/commands/index.js +0 -186
  41. package/dist/commands/info.js +0 -82
  42. package/dist/commands/install.js +0 -477
  43. package/dist/commands/list.js +0 -166
  44. package/dist/commands/login.js +0 -281
  45. package/dist/commands/outdated.js +0 -128
  46. package/dist/commands/popular.js +0 -27
  47. package/dist/commands/publish.js +0 -274
  48. package/dist/commands/schema.js +0 -37
  49. package/dist/commands/search.js +0 -404
  50. package/dist/commands/telemetry.js +0 -103
  51. package/dist/commands/trending.js +0 -76
  52. package/dist/commands/uninstall.js +0 -77
  53. package/dist/commands/update.js +0 -121
  54. package/dist/commands/upgrade.js +0 -121
  55. package/dist/commands/whoami.js +0 -75
  56. package/dist/core/claude-config.js +0 -91
  57. package/dist/core/cursor-config.js +0 -130
  58. package/dist/core/downloader.js +0 -64
  59. package/dist/core/filesystem.js +0 -124
  60. package/dist/core/lockfile.js +0 -239
  61. package/dist/core/marketplace-converter.js +0 -198
  62. package/dist/core/registry-client.js +0 -265
  63. package/dist/core/schema-validator.js +0 -74
  64. package/dist/core/telemetry.js +0 -175
  65. package/dist/core/user-config.js +0 -79
  66. package/dist/types/registry.js +0 -5
  67. package/dist/types.js +0 -5
@@ -1,124 +0,0 @@
1
- "use strict";
2
- /**
3
- * File system operations for managing prompt files
4
- */
5
- var __importDefault = (this && this.__importDefault) || function (mod) {
6
- return (mod && mod.__esModule) ? mod : { "default": mod };
7
- };
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.getDestinationDir = getDestinationDir;
10
- exports.ensureDirectoryExists = ensureDirectoryExists;
11
- exports.saveFile = saveFile;
12
- exports.deleteFile = deleteFile;
13
- exports.fileExists = fileExists;
14
- exports.generateId = generateId;
15
- exports.stripAuthorNamespace = stripAuthorNamespace;
16
- const fs_1 = require("fs");
17
- const path_1 = __importDefault(require("path"));
18
- /**
19
- * Get the destination directory for a package type
20
- */
21
- function getDestinationDir(type) {
22
- switch (type) {
23
- case 'cursor':
24
- return '.cursor/rules';
25
- case 'cursor-agent':
26
- return '.cursor/agents';
27
- case 'cursor-slash-command':
28
- return '.cursor/commands';
29
- case 'claude':
30
- return '.claude/agents';
31
- case 'claude-agent':
32
- return '.claude/agents';
33
- case 'claude-skill':
34
- return '.claude/skills';
35
- case 'claude-slash-command':
36
- return '.claude/commands';
37
- case 'continue':
38
- return '.continue/rules';
39
- case 'windsurf':
40
- return '.windsurf/rules';
41
- case 'generic':
42
- return '.prompts';
43
- default:
44
- throw new Error(`Unknown package type: ${type}`);
45
- }
46
- }
47
- /**
48
- * Ensure directory exists, creating it if necessary
49
- */
50
- async function ensureDirectoryExists(dirPath) {
51
- try {
52
- await fs_1.promises.mkdir(dirPath, { recursive: true });
53
- }
54
- catch (error) {
55
- throw new Error(`Failed to create directory ${dirPath}: ${error}`);
56
- }
57
- }
58
- /**
59
- * Save content to a file
60
- */
61
- async function saveFile(filePath, content) {
62
- try {
63
- // Ensure parent directory exists
64
- const dir = path_1.default.dirname(filePath);
65
- await ensureDirectoryExists(dir);
66
- // Write file
67
- await fs_1.promises.writeFile(filePath, content, 'utf-8');
68
- }
69
- catch (error) {
70
- throw new Error(`Failed to save file ${filePath}: ${error}`);
71
- }
72
- }
73
- /**
74
- * Delete a file
75
- */
76
- async function deleteFile(filePath) {
77
- try {
78
- await fs_1.promises.unlink(filePath);
79
- }
80
- catch (error) {
81
- const err = error;
82
- if (err.code === 'ENOENT') {
83
- // File doesn't exist, that's fine
84
- return;
85
- }
86
- throw new Error(`Failed to delete file ${filePath}: ${error}`);
87
- }
88
- }
89
- /**
90
- * Check if a file exists
91
- */
92
- async function fileExists(filePath) {
93
- try {
94
- await fs_1.promises.access(filePath);
95
- return true;
96
- }
97
- catch {
98
- return false;
99
- }
100
- }
101
- /**
102
- * Generate a unique ID from filename
103
- */
104
- function generateId(filename) {
105
- // Remove extension and convert to kebab-case
106
- const name = filename.replace(/\.[^/.]+$/, '');
107
- return name
108
- .toLowerCase()
109
- .replace(/[^a-z0-9]+/g, '-')
110
- .replace(/^-+|-+$/g, '');
111
- }
112
- /**
113
- * Strip author namespace from package ID and return just the package name
114
- * @example
115
- * stripAuthorNamespace('@community/git-workflow-manager') // 'git-workflow-manager'
116
- * stripAuthorNamespace('community/git-workflow-manager') // 'git-workflow-manager'
117
- * stripAuthorNamespace('@wshobson/commands/agent-orchestration/improve-agent') // 'improve-agent'
118
- * stripAuthorNamespace('git-workflow-manager') // 'git-workflow-manager'
119
- */
120
- function stripAuthorNamespace(packageId) {
121
- // Split by '/' and get the last segment (the actual package name)
122
- const parts = packageId.split('/');
123
- return parts[parts.length - 1];
124
- }
@@ -1,239 +0,0 @@
1
- "use strict";
2
- /**
3
- * Lock file management for reproducible installations
4
- * prpm.lock format similar to package-lock.json
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.readLockfile = readLockfile;
8
- exports.writeLockfile = writeLockfile;
9
- exports.createLockfile = createLockfile;
10
- exports.addToLockfile = addToLockfile;
11
- exports.setPackageIntegrity = setPackageIntegrity;
12
- exports.verifyPackageIntegrity = verifyPackageIntegrity;
13
- exports.getLockedVersion = getLockedVersion;
14
- exports.isLockfileOutOfSync = isLockfileOutOfSync;
15
- exports.mergeLockfiles = mergeLockfiles;
16
- exports.pruneLockfile = pruneLockfile;
17
- exports.addPackage = addPackage;
18
- exports.removePackage = removePackage;
19
- exports.listPackages = listPackages;
20
- exports.getPackage = getPackage;
21
- const fs_1 = require("fs");
22
- const path_1 = require("path");
23
- const crypto_1 = require("crypto");
24
- const LOCKFILE_NAME = 'prpm.lock';
25
- const LOCKFILE_VERSION = 1;
26
- /**
27
- * Read lock file from current directory
28
- */
29
- async function readLockfile(cwd = process.cwd()) {
30
- try {
31
- const lockfilePath = (0, path_1.join)(cwd, LOCKFILE_NAME);
32
- const content = await fs_1.promises.readFile(lockfilePath, 'utf-8');
33
- return JSON.parse(content);
34
- }
35
- catch (error) {
36
- if (error.code === 'ENOENT') {
37
- return null; // Lock file doesn't exist
38
- }
39
- throw new Error(`Failed to read lock file: ${error}`);
40
- }
41
- }
42
- /**
43
- * Write lock file to current directory
44
- */
45
- async function writeLockfile(lockfile, cwd = process.cwd()) {
46
- try {
47
- const lockfilePath = (0, path_1.join)(cwd, LOCKFILE_NAME);
48
- const content = JSON.stringify(lockfile, null, 2);
49
- await fs_1.promises.writeFile(lockfilePath, content, 'utf-8');
50
- }
51
- catch (error) {
52
- throw new Error(`Failed to write lock file: ${error}`);
53
- }
54
- }
55
- /**
56
- * Create new lock file
57
- */
58
- function createLockfile() {
59
- return {
60
- version: '1.0.0',
61
- lockfileVersion: LOCKFILE_VERSION,
62
- packages: {},
63
- generated: new Date().toISOString(),
64
- };
65
- }
66
- /**
67
- * Add package to lock file
68
- */
69
- function addToLockfile(lockfile, packageId, packageInfo) {
70
- lockfile.packages[packageId] = {
71
- version: packageInfo.version,
72
- resolved: packageInfo.tarballUrl,
73
- integrity: '', // Will be set after download
74
- dependencies: packageInfo.dependencies,
75
- type: packageInfo.type,
76
- format: packageInfo.format,
77
- installedPath: packageInfo.installedPath,
78
- };
79
- lockfile.generated = new Date().toISOString();
80
- }
81
- /**
82
- * Update package integrity hash after download
83
- */
84
- function setPackageIntegrity(lockfile, packageId, tarballBuffer) {
85
- if (!lockfile.packages[packageId]) {
86
- throw new Error(`Package ${packageId} not found in lock file`);
87
- }
88
- const hash = (0, crypto_1.createHash)('sha256').update(tarballBuffer).digest('hex');
89
- lockfile.packages[packageId].integrity = `sha256-${hash}`;
90
- }
91
- /**
92
- * Verify package integrity
93
- */
94
- function verifyPackageIntegrity(lockfile, packageId, tarballBuffer) {
95
- const pkg = lockfile.packages[packageId];
96
- if (!pkg || !pkg.integrity) {
97
- return false;
98
- }
99
- const hash = (0, crypto_1.createHash)('sha256').update(tarballBuffer).digest('hex');
100
- const expectedHash = pkg.integrity.replace('sha256-', '');
101
- return hash === expectedHash;
102
- }
103
- /**
104
- * Get locked version for a package
105
- */
106
- function getLockedVersion(lockfile, packageId) {
107
- if (!lockfile || !lockfile.packages[packageId]) {
108
- return null;
109
- }
110
- return lockfile.packages[packageId].version;
111
- }
112
- /**
113
- * Check if lock file is out of sync with dependencies
114
- */
115
- function isLockfileOutOfSync(lockfile, requiredPackages) {
116
- if (!lockfile) {
117
- return true;
118
- }
119
- // Check if all required packages are in lock file
120
- for (const [pkgId, version] of Object.entries(requiredPackages)) {
121
- const locked = lockfile.packages[pkgId];
122
- if (!locked || locked.version !== version) {
123
- return true;
124
- }
125
- }
126
- return false;
127
- }
128
- /**
129
- * Merge lock files (for conflict resolution)
130
- */
131
- function mergeLockfiles(base, incoming) {
132
- const merged = createLockfile();
133
- // Merge packages from both lock files
134
- const allPackages = new Set([
135
- ...Object.keys(base.packages),
136
- ...Object.keys(incoming.packages),
137
- ]);
138
- for (const pkgId of allPackages) {
139
- const basePkg = base.packages[pkgId];
140
- const incomingPkg = incoming.packages[pkgId];
141
- if (!basePkg) {
142
- merged.packages[pkgId] = incomingPkg;
143
- }
144
- else if (!incomingPkg) {
145
- merged.packages[pkgId] = basePkg;
146
- }
147
- else {
148
- // Both exist - prefer newer version
149
- const baseVersion = basePkg.version;
150
- const incomingVersion = incomingPkg.version;
151
- merged.packages[pkgId] = compareVersions(baseVersion, incomingVersion) >= 0
152
- ? basePkg
153
- : incomingPkg;
154
- }
155
- }
156
- return merged;
157
- }
158
- /**
159
- * Simple semver comparison (returns 1 if a > b, -1 if a < b, 0 if equal)
160
- */
161
- function compareVersions(a, b) {
162
- const aParts = a.split('.').map(Number);
163
- const bParts = b.split('.').map(Number);
164
- for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
165
- const aVal = aParts[i] || 0;
166
- const bVal = bParts[i] || 0;
167
- if (aVal > bVal)
168
- return 1;
169
- if (aVal < bVal)
170
- return -1;
171
- }
172
- return 0;
173
- }
174
- /**
175
- * Prune unused packages from lock file
176
- */
177
- function pruneLockfile(lockfile, requiredPackages) {
178
- const pruned = { ...lockfile };
179
- pruned.packages = {};
180
- for (const pkgId of requiredPackages) {
181
- if (lockfile.packages[pkgId]) {
182
- pruned.packages[pkgId] = lockfile.packages[pkgId];
183
- }
184
- }
185
- pruned.generated = new Date().toISOString();
186
- return pruned;
187
- }
188
- /**
189
- * Add package to lock file (convenience wrapper)
190
- */
191
- async function addPackage(packageInfo) {
192
- const lockfile = (await readLockfile()) || createLockfile();
193
- addToLockfile(lockfile, packageInfo.id, {
194
- version: packageInfo.version,
195
- tarballUrl: packageInfo.tarballUrl,
196
- dependencies: packageInfo.dependencies,
197
- type: packageInfo.type,
198
- format: packageInfo.format,
199
- installedPath: packageInfo.installedPath,
200
- });
201
- await writeLockfile(lockfile);
202
- }
203
- /**
204
- * Remove package from lock file
205
- */
206
- async function removePackage(packageId) {
207
- const lockfile = await readLockfile();
208
- if (!lockfile || !lockfile.packages[packageId]) {
209
- return null;
210
- }
211
- const removed = lockfile.packages[packageId];
212
- delete lockfile.packages[packageId];
213
- lockfile.generated = new Date().toISOString();
214
- await writeLockfile(lockfile);
215
- return removed;
216
- }
217
- /**
218
- * List all packages in lock file
219
- */
220
- async function listPackages() {
221
- const lockfile = await readLockfile();
222
- if (!lockfile) {
223
- return [];
224
- }
225
- return Object.entries(lockfile.packages).map(([id, pkg]) => ({
226
- id,
227
- ...pkg,
228
- }));
229
- }
230
- /**
231
- * Get a specific package from lock file
232
- */
233
- async function getPackage(packageId) {
234
- const lockfile = await readLockfile();
235
- if (!lockfile || !lockfile.packages[packageId]) {
236
- return null;
237
- }
238
- return lockfile.packages[packageId];
239
- }
@@ -1,198 +0,0 @@
1
- "use strict";
2
- /**
3
- * Converter for Claude marketplace.json format to PRPM manifest
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.marketplaceToManifest = marketplaceToManifest;
7
- exports.validateMarketplaceJson = validateMarketplaceJson;
8
- /**
9
- * Convert marketplace.json to PRPM manifest format
10
- *
11
- * Strategy:
12
- * - If multiple plugins exist, create a manifest for the first plugin (user can publish others separately)
13
- * - If the plugin has agents/skills/commands, prefer those over the root plugin info
14
- * - Map marketplace fields to PRPM manifest fields
15
- *
16
- * @param marketplace - The marketplace.json content
17
- * @param pluginIndex - Which plugin to convert (default: 0, for the first plugin)
18
- * @returns PRPM manifest
19
- */
20
- function marketplaceToManifest(marketplace, pluginIndex = 0) {
21
- if (!marketplace.plugins || marketplace.plugins.length === 0) {
22
- throw new Error('marketplace.json must contain at least one plugin');
23
- }
24
- if (pluginIndex >= marketplace.plugins.length) {
25
- throw new Error(`Plugin index ${pluginIndex} out of range. Found ${marketplace.plugins.length} plugins.`);
26
- }
27
- const plugin = marketplace.plugins[pluginIndex];
28
- // Determine package type based on what the plugin contains
29
- let type = 'claude';
30
- if (plugin.agents && plugin.agents.length > 0) {
31
- type = 'claude';
32
- }
33
- else if (plugin.skills && plugin.skills.length > 0) {
34
- type = 'claude';
35
- }
36
- else if (plugin.commands && plugin.commands.length > 0) {
37
- type = 'claude';
38
- }
39
- // Generate package name from plugin name
40
- // Format: @owner/plugin-name
41
- const packageName = generatePackageName(marketplace.owner, plugin.name);
42
- // Collect all files that should be included
43
- const files = collectFiles(plugin);
44
- // Determine the main file
45
- const main = determineMainFile(plugin);
46
- // Collect keywords from both marketplace and plugin
47
- const keywords = [
48
- ...(marketplace.keywords || []),
49
- ...(plugin.keywords || []),
50
- ].slice(0, 20); // Max 20 keywords
51
- // Extract tags from keywords (first 10)
52
- const tags = keywords.slice(0, 10);
53
- const manifest = {
54
- name: packageName,
55
- version: plugin.version || marketplace.version || '1.0.0',
56
- description: plugin.description || marketplace.description,
57
- type,
58
- author: plugin.author || marketplace.owner,
59
- files,
60
- tags,
61
- keywords,
62
- };
63
- // Add optional fields if available
64
- if (marketplace.githubUrl) {
65
- manifest.repository = marketplace.githubUrl;
66
- }
67
- if (marketplace.websiteUrl) {
68
- manifest.homepage = marketplace.websiteUrl;
69
- }
70
- if (plugin.category) {
71
- manifest.category = plugin.category;
72
- }
73
- if (main) {
74
- manifest.main = main;
75
- }
76
- return manifest;
77
- }
78
- /**
79
- * Generate PRPM-compatible package name from owner and plugin name
80
- */
81
- function generatePackageName(owner, pluginName) {
82
- // Sanitize owner and plugin name
83
- const sanitizedOwner = owner.toLowerCase().replace(/[^a-z0-9-]/g, '-');
84
- const sanitizedName = pluginName.toLowerCase().replace(/[^a-z0-9-]/g, '-');
85
- // Remove leading/trailing hyphens
86
- const cleanOwner = sanitizedOwner.replace(/^-+|-+$/g, '');
87
- const cleanName = sanitizedName.replace(/^-+|-+$/g, '');
88
- return `@${cleanOwner}/${cleanName}`;
89
- }
90
- /**
91
- * Collect all files referenced in the plugin
92
- */
93
- function collectFiles(plugin) {
94
- const files = new Set();
95
- // Add plugin source if it's a file path
96
- if (plugin.source && !plugin.source.startsWith('http')) {
97
- files.add(plugin.source);
98
- }
99
- // Add agent files
100
- if (plugin.agents) {
101
- for (const agent of plugin.agents) {
102
- if (agent.source && !agent.source.startsWith('http')) {
103
- files.add(agent.source);
104
- }
105
- }
106
- }
107
- // Add skill files
108
- if (plugin.skills) {
109
- for (const skill of plugin.skills) {
110
- if (skill.source && !skill.source.startsWith('http')) {
111
- files.add(skill.source);
112
- }
113
- }
114
- }
115
- // Add command files
116
- if (plugin.commands) {
117
- for (const command of plugin.commands) {
118
- if (command.source && !command.source.startsWith('http')) {
119
- files.add(command.source);
120
- }
121
- }
122
- }
123
- // Add standard files if they're not already included
124
- const standardFiles = ['README.md', 'LICENSE', '.claude/marketplace.json'];
125
- for (const file of standardFiles) {
126
- files.add(file);
127
- }
128
- return Array.from(files);
129
- }
130
- /**
131
- * Determine the main entry file for the package
132
- * Only set main if there's a single clear entry point
133
- */
134
- function determineMainFile(plugin) {
135
- const agentCount = plugin.agents?.length || 0;
136
- const skillCount = plugin.skills?.length || 0;
137
- const commandCount = plugin.commands?.length || 0;
138
- // Only set main if there's exactly one item total
139
- const totalCount = agentCount + skillCount + commandCount;
140
- if (totalCount !== 1) {
141
- // Multiple items or no items - no clear main file
142
- return undefined;
143
- }
144
- // Single agent
145
- if (agentCount === 1) {
146
- const source = plugin.agents[0].source;
147
- if (source && !source.startsWith('http')) {
148
- return source;
149
- }
150
- }
151
- // Single skill
152
- if (skillCount === 1) {
153
- const source = plugin.skills[0].source;
154
- if (source && !source.startsWith('http')) {
155
- return source;
156
- }
157
- }
158
- // Single command
159
- if (commandCount === 1) {
160
- const source = plugin.commands[0].source;
161
- if (source && !source.startsWith('http')) {
162
- return source;
163
- }
164
- }
165
- // Otherwise, use plugin source if available
166
- if (plugin.source && !plugin.source.startsWith('http')) {
167
- return plugin.source;
168
- }
169
- return undefined;
170
- }
171
- /**
172
- * Validate marketplace.json structure
173
- */
174
- function validateMarketplaceJson(data) {
175
- if (!data || typeof data !== 'object') {
176
- return false;
177
- }
178
- const marketplace = data;
179
- // Check required fields
180
- if (!marketplace.name || typeof marketplace.name !== 'string') {
181
- return false;
182
- }
183
- if (!marketplace.owner || typeof marketplace.owner !== 'string') {
184
- return false;
185
- }
186
- if (!marketplace.description || typeof marketplace.description !== 'string') {
187
- return false;
188
- }
189
- if (!Array.isArray(marketplace.plugins) || marketplace.plugins.length === 0) {
190
- return false;
191
- }
192
- // Validate first plugin has required fields
193
- const plugin = marketplace.plugins[0];
194
- if (!plugin.name || !plugin.description || !plugin.version) {
195
- return false;
196
- }
197
- return true;
198
- }