prpm 0.2.0 → 1.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 (48) hide show
  1. package/dist/index.js +14257 -109
  2. package/package.json +11 -9
  3. package/dist/__tests__/e2e/test-helpers.js +0 -153
  4. package/dist/commands/buy-credits.js +0 -224
  5. package/dist/commands/catalog.js +0 -365
  6. package/dist/commands/collections.js +0 -655
  7. package/dist/commands/config.js +0 -161
  8. package/dist/commands/credits.js +0 -186
  9. package/dist/commands/index.js +0 -184
  10. package/dist/commands/info.js +0 -78
  11. package/dist/commands/init.js +0 -684
  12. package/dist/commands/install.js +0 -829
  13. package/dist/commands/list.js +0 -198
  14. package/dist/commands/login.js +0 -316
  15. package/dist/commands/outdated.js +0 -130
  16. package/dist/commands/playground.js +0 -637
  17. package/dist/commands/popular.js +0 -33
  18. package/dist/commands/publish.js +0 -803
  19. package/dist/commands/schema.js +0 -41
  20. package/dist/commands/search.js +0 -446
  21. package/dist/commands/starred.js +0 -147
  22. package/dist/commands/subscribe.js +0 -211
  23. package/dist/commands/telemetry.js +0 -104
  24. package/dist/commands/trending.js +0 -86
  25. package/dist/commands/uninstall.js +0 -120
  26. package/dist/commands/update.js +0 -121
  27. package/dist/commands/upgrade.js +0 -121
  28. package/dist/commands/whoami.js +0 -83
  29. package/dist/core/claude-config.js +0 -91
  30. package/dist/core/cursor-config.js +0 -130
  31. package/dist/core/downloader.js +0 -64
  32. package/dist/core/errors.js +0 -29
  33. package/dist/core/filesystem.js +0 -246
  34. package/dist/core/lockfile.js +0 -292
  35. package/dist/core/marketplace-converter.js +0 -224
  36. package/dist/core/prompts.js +0 -62
  37. package/dist/core/registry-client.js +0 -305
  38. package/dist/core/schema-validator.js +0 -74
  39. package/dist/core/telemetry.js +0 -253
  40. package/dist/core/user-config.js +0 -147
  41. package/dist/types/registry.js +0 -12
  42. package/dist/types.js +0 -9
  43. package/dist/utils/license-extractor.js +0 -122
  44. package/dist/utils/multi-package.js +0 -117
  45. package/dist/utils/parallel-publisher.js +0 -144
  46. package/dist/utils/script-executor.js +0 -72
  47. package/dist/utils/snippet-extractor.js +0 -77
  48. package/dist/utils/webapp-url.js +0 -44
@@ -1,246 +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.directoryExists = directoryExists;
15
- exports.autoDetectFormat = autoDetectFormat;
16
- exports.generateId = generateId;
17
- exports.stripAuthorNamespace = stripAuthorNamespace;
18
- exports.getInstalledFilePath = getInstalledFilePath;
19
- exports.getInstalledFilePaths = getInstalledFilePaths;
20
- const fs_1 = require("fs");
21
- const path_1 = __importDefault(require("path"));
22
- /**
23
- * Get the destination directory for a package based on format and subtype
24
- * @param format - Package format (cursor, claude, etc.)
25
- * @param subtype - Package subtype (skill, agent, rule, etc.)
26
- * @param name - Package name (optional, only needed for Claude skills which create subdirectories)
27
- */
28
- function getDestinationDir(format, subtype, name) {
29
- // Strip author namespace from package name to avoid nested directories
30
- const packageName = stripAuthorNamespace(name);
31
- switch (format) {
32
- case 'cursor':
33
- if (subtype === 'agent')
34
- return '.cursor/agents';
35
- if (subtype === 'slash-command')
36
- return '.cursor/commands';
37
- return '.cursor/rules';
38
- case 'claude':
39
- // Only create subdirectory for skills if name is provided
40
- if (subtype === 'skill' && packageName)
41
- return `.claude/skills/${packageName}`;
42
- if (subtype === 'skill')
43
- return '.claude/skills';
44
- if (subtype === 'slash-command')
45
- return '.claude/commands';
46
- if (subtype === 'agent')
47
- return '.claude/agents';
48
- // Hooks are configured in settings.json, return .claude directory
49
- if (subtype === 'hook')
50
- return '.claude';
51
- return '.claude/agents'; // Default for claude
52
- case 'continue':
53
- // Continue has separate directories for prompts (slash commands) and rules
54
- if (subtype === 'rule')
55
- return '.continue/rules';
56
- return '.continue/prompts';
57
- case 'windsurf':
58
- return '.windsurf/rules';
59
- case 'copilot':
60
- // Copilot has different locations based on subtype:
61
- // - Repository-wide instructions: .github/copilot-instructions.md
62
- // - Path-specific instructions: .github/instructions/*.instructions.md
63
- // - Chat modes: .github/chatmodes/*.chatmode.md
64
- if (subtype === 'chatmode')
65
- return '.github/chatmodes';
66
- // Default to path-specific instructions directory
67
- return '.github/instructions';
68
- case 'kiro':
69
- // Kiro has different locations based on subtype:
70
- // - Steering files: .kiro/steering/*.md
71
- // - Hooks: .kiro/hooks/*.kiro.hook (JSON files)
72
- if (subtype === 'hook')
73
- return '.kiro/hooks';
74
- return '.kiro/steering';
75
- case 'agents.md':
76
- return '.';
77
- case 'generic':
78
- return '.prompts';
79
- case 'mcp':
80
- return '.mcp/tools';
81
- default:
82
- throw new Error(`Unknown format: ${format}`);
83
- }
84
- }
85
- /**
86
- * Ensure directory exists, creating it if necessary
87
- */
88
- async function ensureDirectoryExists(dirPath) {
89
- try {
90
- await fs_1.promises.mkdir(dirPath, { recursive: true });
91
- }
92
- catch (error) {
93
- throw new Error(`Failed to create directory ${dirPath}: ${error}`);
94
- }
95
- }
96
- /**
97
- * Save content to a file
98
- */
99
- async function saveFile(filePath, content) {
100
- try {
101
- // Ensure parent directory exists
102
- const dir = path_1.default.dirname(filePath);
103
- await ensureDirectoryExists(dir);
104
- // Write file
105
- await fs_1.promises.writeFile(filePath, content, 'utf-8');
106
- }
107
- catch (error) {
108
- throw new Error(`Failed to save file ${filePath}: ${error}`);
109
- }
110
- }
111
- /**
112
- * Delete a file
113
- */
114
- async function deleteFile(filePath) {
115
- try {
116
- await fs_1.promises.unlink(filePath);
117
- }
118
- catch (error) {
119
- const err = error;
120
- if (err.code === 'ENOENT') {
121
- // File doesn't exist, that's fine
122
- return;
123
- }
124
- throw new Error(`Failed to delete file ${filePath}: ${error}`);
125
- }
126
- }
127
- /**
128
- * Check if a file exists
129
- */
130
- async function fileExists(filePath) {
131
- try {
132
- await fs_1.promises.access(filePath);
133
- return true;
134
- }
135
- catch {
136
- return false;
137
- }
138
- }
139
- /**
140
- * Check if a directory exists
141
- */
142
- async function directoryExists(dirPath) {
143
- try {
144
- const stats = await fs_1.promises.stat(dirPath);
145
- return stats.isDirectory();
146
- }
147
- catch {
148
- return false;
149
- }
150
- }
151
- /**
152
- * Auto-detect the format based on existing directories in the current project
153
- * Returns the format if a matching directory is found, or null if none found
154
- */
155
- async function autoDetectFormat() {
156
- // Agents.md installs live at project root
157
- if (await fileExists('AGENTS.md')) {
158
- return 'agents.md';
159
- }
160
- const formatDirs = [
161
- { format: 'cursor', dir: '.cursor' },
162
- { format: 'claude', dir: '.claude' },
163
- { format: 'continue', dir: '.continue' },
164
- { format: 'windsurf', dir: '.windsurf' },
165
- { format: 'copilot', dir: '.github/instructions' },
166
- { format: 'kiro', dir: '.kiro' },
167
- { format: 'agents.md', dir: '.agents' },
168
- ];
169
- for (const { format, dir } of formatDirs) {
170
- if (await directoryExists(dir)) {
171
- return format;
172
- }
173
- }
174
- return null;
175
- }
176
- /**
177
- * Generate a unique ID from filename
178
- */
179
- function generateId(filename) {
180
- // Remove extension and convert to kebab-case
181
- const name = filename.replace(/\.[^/.]+$/, '');
182
- return name
183
- .toLowerCase()
184
- .replace(/[^a-z0-9]+/g, '-')
185
- .replace(/^-+|-+$/g, '');
186
- }
187
- /**
188
- * Strip author namespace from package ID and return just the package name
189
- * @example
190
- * stripAuthorNamespace('@community/git-workflow-manager') // 'git-workflow-manager'
191
- * stripAuthorNamespace('community/git-workflow-manager') // 'git-workflow-manager'
192
- * stripAuthorNamespace('@wshobson/commands/agent-orchestration/improve-agent') // 'improve-agent'
193
- * stripAuthorNamespace('git-workflow-manager') // 'git-workflow-manager'
194
- */
195
- function stripAuthorNamespace(packageId) {
196
- // Handle undefined or empty string
197
- if (!packageId) {
198
- return '';
199
- }
200
- // Split by '/' and get the last segment (the actual package name)
201
- const parts = packageId.split('/');
202
- return parts[parts.length - 1];
203
- }
204
- /**
205
- * Get the expected installed file path for a package
206
- * This matches the logic used by the install command to determine where files are placed
207
- *
208
- * @param packageName - Full package name (e.g., '@prpm/typescript-rules')
209
- * @param format - Package format
210
- * @param subtype - Package subtype
211
- * @param fileName - Optional specific file name (defaults to main file)
212
- * @returns Path where the file will be installed relative to working directory
213
- */
214
- function getInstalledFilePath(packageName, format, subtype, fileName) {
215
- const destDir = getDestinationDir(format, subtype, packageName);
216
- const packageBaseName = stripAuthorNamespace(packageName);
217
- // If a specific file name is provided, use it
218
- if (fileName) {
219
- return path_1.default.join(destDir, fileName);
220
- }
221
- // Claude skills always use SKILL.md
222
- if (format === 'claude' && subtype === 'skill') {
223
- return path_1.default.join(destDir, 'SKILL.md');
224
- }
225
- // agents.md uses package-name/AGENTS.md structure
226
- if (format === 'agents.md') {
227
- return path_1.default.join(destDir, packageBaseName, 'AGENTS.md');
228
- }
229
- // Determine file extension
230
- const fileExtension = format === 'cursor' ? 'mdc' : 'md';
231
- // For other formats, use package name as filename
232
- return path_1.default.join(destDir, `${packageBaseName}.${fileExtension}`);
233
- }
234
- /**
235
- * Get all expected installed file paths for a multi-file package
236
- *
237
- * @param packageName - Full package name
238
- * @param format - Package format
239
- * @param subtype - Package subtype
240
- * @param fileNames - Array of file names in the package
241
- * @returns Array of paths where files will be installed
242
- */
243
- function getInstalledFilePaths(packageName, format, subtype, fileNames) {
244
- const destDir = getDestinationDir(format, subtype, packageName);
245
- return fileNames.map(fileName => path_1.default.join(destDir, fileName));
246
- }
@@ -1,292 +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
- exports.addCollectionToLockfile = addCollectionToLockfile;
22
- exports.getCollectionFromLockfile = getCollectionFromLockfile;
23
- exports.removeCollectionFromLockfile = removeCollectionFromLockfile;
24
- exports.listCollectionsFromLockfile = listCollectionsFromLockfile;
25
- const fs_1 = require("fs");
26
- const path_1 = require("path");
27
- const crypto_1 = require("crypto");
28
- const LOCKFILE_NAME = 'prpm.lock';
29
- const LOCKFILE_VERSION = 1;
30
- /**
31
- * Read lock file from current directory
32
- */
33
- async function readLockfile(cwd = process.cwd()) {
34
- try {
35
- const lockfilePath = (0, path_1.join)(cwd, LOCKFILE_NAME);
36
- const content = await fs_1.promises.readFile(lockfilePath, 'utf-8');
37
- return JSON.parse(content);
38
- }
39
- catch (error) {
40
- if (error.code === 'ENOENT') {
41
- return null; // Lock file doesn't exist
42
- }
43
- throw new Error(`Failed to read lock file: ${error}`);
44
- }
45
- }
46
- /**
47
- * Write lock file to current directory
48
- */
49
- async function writeLockfile(lockfile, cwd = process.cwd()) {
50
- try {
51
- const lockfilePath = (0, path_1.join)(cwd, LOCKFILE_NAME);
52
- const content = JSON.stringify(lockfile, null, 2);
53
- await fs_1.promises.writeFile(lockfilePath, content, 'utf-8');
54
- }
55
- catch (error) {
56
- throw new Error(`Failed to write lock file: ${error}`);
57
- }
58
- }
59
- /**
60
- * Create new lock file
61
- */
62
- function createLockfile() {
63
- return {
64
- version: '1.0.0',
65
- lockfileVersion: LOCKFILE_VERSION,
66
- packages: {},
67
- generated: new Date().toISOString(),
68
- };
69
- }
70
- /**
71
- * Add package to lock file
72
- */
73
- function addToLockfile(lockfile, packageId, packageInfo) {
74
- lockfile.packages[packageId] = {
75
- version: packageInfo.version,
76
- resolved: packageInfo.tarballUrl,
77
- integrity: '', // Will be set after download
78
- dependencies: packageInfo.dependencies,
79
- format: packageInfo.format,
80
- subtype: packageInfo.subtype,
81
- installedPath: packageInfo.installedPath,
82
- fromCollection: packageInfo.fromCollection,
83
- hookMetadata: packageInfo.hookMetadata,
84
- };
85
- lockfile.generated = new Date().toISOString();
86
- }
87
- /**
88
- * Update package integrity hash after download
89
- */
90
- function setPackageIntegrity(lockfile, packageId, tarballBuffer) {
91
- if (!lockfile.packages[packageId]) {
92
- throw new Error(`Package ${packageId} not found in lock file`);
93
- }
94
- const hash = (0, crypto_1.createHash)('sha256').update(tarballBuffer).digest('hex');
95
- lockfile.packages[packageId].integrity = `sha256-${hash}`;
96
- }
97
- /**
98
- * Verify package integrity
99
- */
100
- function verifyPackageIntegrity(lockfile, packageId, tarballBuffer) {
101
- const pkg = lockfile.packages[packageId];
102
- if (!pkg || !pkg.integrity) {
103
- return false;
104
- }
105
- const hash = (0, crypto_1.createHash)('sha256').update(tarballBuffer).digest('hex');
106
- const expectedHash = pkg.integrity.replace('sha256-', '');
107
- return hash === expectedHash;
108
- }
109
- /**
110
- * Get locked version for a package
111
- */
112
- function getLockedVersion(lockfile, packageId) {
113
- if (!lockfile || !lockfile.packages[packageId]) {
114
- return null;
115
- }
116
- return lockfile.packages[packageId].version;
117
- }
118
- /**
119
- * Check if lock file is out of sync with dependencies
120
- */
121
- function isLockfileOutOfSync(lockfile, requiredPackages) {
122
- if (!lockfile) {
123
- return true;
124
- }
125
- // Check if all required packages are in lock file
126
- for (const [pkgId, version] of Object.entries(requiredPackages)) {
127
- const locked = lockfile.packages[pkgId];
128
- if (!locked || locked.version !== version) {
129
- return true;
130
- }
131
- }
132
- return false;
133
- }
134
- /**
135
- * Merge lock files (for conflict resolution)
136
- */
137
- function mergeLockfiles(base, incoming) {
138
- const merged = createLockfile();
139
- // Merge packages from both lock files
140
- const allPackages = new Set([
141
- ...Object.keys(base.packages),
142
- ...Object.keys(incoming.packages),
143
- ]);
144
- for (const pkgId of allPackages) {
145
- const basePkg = base.packages[pkgId];
146
- const incomingPkg = incoming.packages[pkgId];
147
- if (!basePkg) {
148
- merged.packages[pkgId] = incomingPkg;
149
- }
150
- else if (!incomingPkg) {
151
- merged.packages[pkgId] = basePkg;
152
- }
153
- else {
154
- // Both exist - prefer newer version
155
- const baseVersion = basePkg.version;
156
- const incomingVersion = incomingPkg.version;
157
- merged.packages[pkgId] = compareVersions(baseVersion, incomingVersion) >= 0
158
- ? basePkg
159
- : incomingPkg;
160
- }
161
- }
162
- return merged;
163
- }
164
- /**
165
- * Simple semver comparison (returns 1 if a > b, -1 if a < b, 0 if equal)
166
- */
167
- function compareVersions(a, b) {
168
- const aParts = a.split('.').map(Number);
169
- const bParts = b.split('.').map(Number);
170
- for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
171
- const aVal = aParts[i] || 0;
172
- const bVal = bParts[i] || 0;
173
- if (aVal > bVal)
174
- return 1;
175
- if (aVal < bVal)
176
- return -1;
177
- }
178
- return 0;
179
- }
180
- /**
181
- * Prune unused packages from lock file
182
- */
183
- function pruneLockfile(lockfile, requiredPackages) {
184
- const pruned = { ...lockfile };
185
- pruned.packages = {};
186
- for (const pkgId of requiredPackages) {
187
- if (lockfile.packages[pkgId]) {
188
- pruned.packages[pkgId] = lockfile.packages[pkgId];
189
- }
190
- }
191
- pruned.generated = new Date().toISOString();
192
- return pruned;
193
- }
194
- /**
195
- * Add package to lock file (convenience wrapper)
196
- */
197
- async function addPackage(packageInfo) {
198
- const lockfile = (await readLockfile()) || createLockfile();
199
- addToLockfile(lockfile, packageInfo.id, {
200
- version: packageInfo.version,
201
- tarballUrl: packageInfo.tarballUrl,
202
- dependencies: packageInfo.dependencies,
203
- format: packageInfo.format,
204
- subtype: packageInfo.subtype,
205
- installedPath: packageInfo.installedPath,
206
- });
207
- await writeLockfile(lockfile);
208
- }
209
- /**
210
- * Remove package from lock file
211
- */
212
- async function removePackage(packageId) {
213
- const lockfile = await readLockfile();
214
- if (!lockfile || !lockfile.packages[packageId]) {
215
- return null;
216
- }
217
- const removed = lockfile.packages[packageId];
218
- delete lockfile.packages[packageId];
219
- lockfile.generated = new Date().toISOString();
220
- await writeLockfile(lockfile);
221
- return removed;
222
- }
223
- /**
224
- * List all packages in lock file
225
- */
226
- async function listPackages() {
227
- const lockfile = await readLockfile();
228
- if (!lockfile) {
229
- return [];
230
- }
231
- return Object.entries(lockfile.packages).map(([id, pkg]) => ({
232
- id,
233
- ...pkg,
234
- }));
235
- }
236
- /**
237
- * Get a specific package from lock file
238
- */
239
- async function getPackage(packageId) {
240
- const lockfile = await readLockfile();
241
- if (!lockfile || !lockfile.packages[packageId]) {
242
- return null;
243
- }
244
- return lockfile.packages[packageId];
245
- }
246
- /**
247
- * Add or update collection in lock file
248
- */
249
- function addCollectionToLockfile(lockfile, collectionKey, collectionInfo) {
250
- if (!lockfile.collections) {
251
- lockfile.collections = {};
252
- }
253
- lockfile.collections[collectionKey] = {
254
- scope: collectionInfo.scope,
255
- name_slug: collectionInfo.name_slug,
256
- version: collectionInfo.version,
257
- installedAt: new Date().toISOString(),
258
- packages: collectionInfo.packages,
259
- };
260
- lockfile.generated = new Date().toISOString();
261
- }
262
- /**
263
- * Get collection from lock file
264
- */
265
- function getCollectionFromLockfile(lockfile, collectionKey) {
266
- if (!lockfile || !lockfile.collections) {
267
- return null;
268
- }
269
- return lockfile.collections[collectionKey] || null;
270
- }
271
- /**
272
- * Remove collection from lock file
273
- */
274
- function removeCollectionFromLockfile(lockfile, collectionKey) {
275
- if (!lockfile.collections) {
276
- return;
277
- }
278
- delete lockfile.collections[collectionKey];
279
- lockfile.generated = new Date().toISOString();
280
- }
281
- /**
282
- * List all collections in lock file
283
- */
284
- function listCollectionsFromLockfile(lockfile) {
285
- if (!lockfile || !lockfile.collections) {
286
- return [];
287
- }
288
- return Object.entries(lockfile.collections).map(([key, collection]) => ({
289
- key,
290
- ...collection,
291
- }));
292
- }