prpm 0.0.1 → 0.0.3
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.
- package/README.md +4 -4
- package/dist/__tests__/e2e/test-helpers.js +150 -0
- package/dist/commands/collections.js +248 -51
- package/dist/commands/index.js +110 -51
- package/dist/commands/info.js +8 -7
- package/dist/commands/install.js +270 -45
- package/dist/commands/list.js +91 -14
- package/dist/commands/login.js +86 -24
- package/dist/commands/outdated.js +3 -2
- package/dist/commands/publish.js +98 -41
- package/dist/commands/remove.js +43 -10
- package/dist/commands/schema.js +37 -0
- package/dist/commands/search.js +263 -38
- package/dist/commands/trending.js +4 -3
- package/dist/commands/uninstall.js +77 -0
- package/dist/commands/update.js +3 -2
- package/dist/commands/upgrade.js +3 -2
- package/dist/commands/whoami.js +25 -1
- package/dist/core/claude-config.js +91 -0
- package/dist/core/cursor-config.js +130 -0
- package/dist/core/filesystem.js +30 -0
- package/dist/core/lockfile.js +57 -0
- package/dist/core/marketplace-converter.js +198 -0
- package/dist/core/schema-validator.js +74 -0
- package/dist/core/telemetry.js +5 -0
- package/dist/index.js +6 -7
- package/dist/types/registry.js +5 -0
- package/package.json +15 -7
- package/dist/commands/add.js +0 -107
- package/dist/commands/deps.js +0 -92
- package/dist/core/config.js +0 -91
package/dist/commands/install.js
CHANGED
|
@@ -42,14 +42,28 @@ const commander_1 = require("commander");
|
|
|
42
42
|
const registry_client_1 = require("@prpm/registry-client");
|
|
43
43
|
const user_config_1 = require("../core/user-config");
|
|
44
44
|
const filesystem_1 = require("../core/filesystem");
|
|
45
|
-
const config_1 = require("../core/config");
|
|
46
45
|
const telemetry_1 = require("../core/telemetry");
|
|
46
|
+
const tar = __importStar(require("tar"));
|
|
47
47
|
const lockfile_1 = require("../core/lockfile");
|
|
48
|
+
const cursor_config_1 = require("../core/cursor-config");
|
|
49
|
+
const claude_config_1 = require("../core/claude-config");
|
|
48
50
|
/**
|
|
49
51
|
* Get icon for package type
|
|
50
52
|
*/
|
|
51
53
|
function getTypeIcon(type) {
|
|
52
54
|
const icons = {
|
|
55
|
+
'claude-skill': '🎓',
|
|
56
|
+
'claude-agent': '🤖',
|
|
57
|
+
'claude-slash-command': '⚡',
|
|
58
|
+
'claude': '🤖',
|
|
59
|
+
'cursor': '📋',
|
|
60
|
+
'cursor-agent': '🤖',
|
|
61
|
+
'cursor-slash-command': '⚡',
|
|
62
|
+
'windsurf': '🌊',
|
|
63
|
+
'continue': '➡️',
|
|
64
|
+
'mcp': '🔗',
|
|
65
|
+
'generic': '📦',
|
|
66
|
+
// Legacy mappings
|
|
53
67
|
skill: '🎓',
|
|
54
68
|
agent: '🤖',
|
|
55
69
|
rule: '📋',
|
|
@@ -58,7 +72,6 @@ function getTypeIcon(type) {
|
|
|
58
72
|
workflow: '⚡',
|
|
59
73
|
tool: '🔧',
|
|
60
74
|
template: '📄',
|
|
61
|
-
mcp: '🔗',
|
|
62
75
|
};
|
|
63
76
|
return icons[type] || '📦';
|
|
64
77
|
}
|
|
@@ -67,6 +80,18 @@ function getTypeIcon(type) {
|
|
|
67
80
|
*/
|
|
68
81
|
function getTypeLabel(type) {
|
|
69
82
|
const labels = {
|
|
83
|
+
'claude-skill': 'Claude Skill',
|
|
84
|
+
'claude-agent': 'Claude Agent',
|
|
85
|
+
'claude-slash-command': 'Claude Slash Command',
|
|
86
|
+
'claude': 'Claude Agent',
|
|
87
|
+
'cursor': 'Cursor Rule',
|
|
88
|
+
'cursor-agent': 'Cursor Agent',
|
|
89
|
+
'cursor-slash-command': 'Cursor Slash Command',
|
|
90
|
+
'windsurf': 'Windsurf Rule',
|
|
91
|
+
'continue': 'Continue Rule',
|
|
92
|
+
'mcp': 'MCP Server',
|
|
93
|
+
'generic': 'Package',
|
|
94
|
+
// Legacy mappings
|
|
70
95
|
skill: 'Skill',
|
|
71
96
|
agent: 'Agent',
|
|
72
97
|
rule: 'Rule',
|
|
@@ -75,7 +100,6 @@ function getTypeLabel(type) {
|
|
|
75
100
|
workflow: 'Workflow',
|
|
76
101
|
tool: 'Tool',
|
|
77
102
|
template: 'Template',
|
|
78
|
-
mcp: 'MCP Server',
|
|
79
103
|
};
|
|
80
104
|
return labels[type] || type;
|
|
81
105
|
}
|
|
@@ -84,8 +108,25 @@ async function handleInstall(packageSpec, options) {
|
|
|
84
108
|
let success = false;
|
|
85
109
|
let error;
|
|
86
110
|
try {
|
|
87
|
-
// Parse package spec (e.g., "react-rules" or "react-rules@1.2.0")
|
|
88
|
-
|
|
111
|
+
// Parse package spec (e.g., "react-rules" or "react-rules@1.2.0" or "@prpm/pkg@1.0.0")
|
|
112
|
+
// For scoped packages (@scope/name), the first @ is part of the package name
|
|
113
|
+
let packageId;
|
|
114
|
+
let specVersion;
|
|
115
|
+
if (packageSpec.startsWith('@')) {
|
|
116
|
+
// Scoped package: @scope/name or @scope/name@version
|
|
117
|
+
const match = packageSpec.match(/^(@[^/]+\/[^@]+)(?:@(.+))?$/);
|
|
118
|
+
if (!match) {
|
|
119
|
+
throw new Error('Invalid package spec format. Use: @scope/package or @scope/package@version');
|
|
120
|
+
}
|
|
121
|
+
packageId = match[1];
|
|
122
|
+
specVersion = match[2];
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// Unscoped package: name or name@version
|
|
126
|
+
const parts = packageSpec.split('@');
|
|
127
|
+
packageId = parts[0];
|
|
128
|
+
specVersion = parts[1];
|
|
129
|
+
}
|
|
89
130
|
// Read existing lock file
|
|
90
131
|
const lockfile = await (0, lockfile_1.readLockfile)();
|
|
91
132
|
const lockedVersion = (0, lockfile_1.getLockedVersion)(lockfile, packageId);
|
|
@@ -102,21 +143,72 @@ async function handleInstall(packageSpec, options) {
|
|
|
102
143
|
// Normal mode - use specified version or locked version or latest
|
|
103
144
|
version = options.version || specVersion || lockedVersion || 'latest';
|
|
104
145
|
}
|
|
146
|
+
// Check if package is already installed
|
|
147
|
+
if (lockfile && lockfile.packages[packageId]) {
|
|
148
|
+
const installedPkg = lockfile.packages[packageId];
|
|
149
|
+
const requestedVersion = options.version || specVersion;
|
|
150
|
+
// If no specific version requested, or same version requested
|
|
151
|
+
if (!requestedVersion || requestedVersion === 'latest' || requestedVersion === installedPkg.version) {
|
|
152
|
+
console.log(`\n✨ Package already installed!`);
|
|
153
|
+
console.log(` 📦 ${packageId}@${installedPkg.version}`);
|
|
154
|
+
console.log(` 🔄 Format: ${installedPkg.format || installedPkg.type || 'unknown'}`);
|
|
155
|
+
console.log(`\n💡 To reinstall or upgrade:`);
|
|
156
|
+
console.log(` prpm upgrade ${packageId} # Upgrade to latest version`);
|
|
157
|
+
console.log(` prpm uninstall ${packageId} # Uninstall first, then install`);
|
|
158
|
+
success = true;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
else if (requestedVersion !== installedPkg.version) {
|
|
162
|
+
// Different version requested - allow upgrade/downgrade
|
|
163
|
+
console.log(`📦 Upgrading ${packageId}: ${installedPkg.version} → ${requestedVersion}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
105
166
|
console.log(`📥 Installing ${packageId}@${version}...`);
|
|
106
167
|
const config = await (0, user_config_1.getConfig)();
|
|
107
168
|
const client = (0, registry_client_1.getRegistryClient)(config);
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
169
|
+
// Check if this is a collection first (by trying to fetch it)
|
|
170
|
+
// Collections can be: name, scope/name, or @scope/name
|
|
171
|
+
let isCollection = false;
|
|
172
|
+
try {
|
|
173
|
+
// Try to parse as collection
|
|
174
|
+
let scope;
|
|
175
|
+
let name_slug;
|
|
176
|
+
const matchWithScope = packageId.match(/^@?([^/]+)\/([^/@]+)$/);
|
|
177
|
+
if (matchWithScope) {
|
|
178
|
+
[, scope, name_slug] = matchWithScope;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// No scope, assume 'collection' scope
|
|
182
|
+
scope = 'collection';
|
|
183
|
+
name_slug = packageId;
|
|
184
|
+
}
|
|
185
|
+
// Try to fetch as collection
|
|
186
|
+
await client.getCollection(scope, name_slug, version === 'latest' ? undefined : version);
|
|
187
|
+
isCollection = true;
|
|
188
|
+
// If successful, delegate to collection install handler
|
|
189
|
+
const { handleCollectionInstall } = await Promise.resolve().then(() => __importStar(require('./collections.js')));
|
|
190
|
+
return await handleCollectionInstall(packageId, {
|
|
191
|
+
format: options.as,
|
|
192
|
+
skipOptional: false,
|
|
193
|
+
dryRun: false,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
// Not a collection, continue with package install
|
|
198
|
+
isCollection = false;
|
|
112
199
|
}
|
|
113
200
|
// Get package info
|
|
114
201
|
const pkg = await client.getPackage(packageId);
|
|
115
202
|
const typeIcon = getTypeIcon(pkg.type);
|
|
116
203
|
const typeLabel = getTypeLabel(pkg.type);
|
|
117
|
-
console.log(` ${pkg.
|
|
204
|
+
console.log(` ${pkg.name} ${pkg.official ? '🏅' : ''}`);
|
|
118
205
|
console.log(` ${pkg.description || 'No description'}`);
|
|
119
206
|
console.log(` ${typeIcon} Type: ${typeLabel}`);
|
|
207
|
+
// Determine format preference - use package type if no explicit conversion requested
|
|
208
|
+
const format = options.as || pkg.type;
|
|
209
|
+
if (options.as && format !== 'canonical') {
|
|
210
|
+
console.log(` 🔄 Converting to ${format} format...`);
|
|
211
|
+
}
|
|
120
212
|
// Determine version to install
|
|
121
213
|
let tarballUrl;
|
|
122
214
|
if (version === 'latest') {
|
|
@@ -136,37 +228,116 @@ async function handleInstall(packageSpec, options) {
|
|
|
136
228
|
const tarball = await client.downloadPackage(tarballUrl, { format });
|
|
137
229
|
// Extract tarball and save files
|
|
138
230
|
console.log(` 📂 Extracting...`);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
231
|
+
// Determine effective type based on format and original package type
|
|
232
|
+
let effectiveType;
|
|
233
|
+
if (format === 'cursor') {
|
|
234
|
+
// Map package types to cursor equivalents
|
|
235
|
+
if (pkg.type === 'claude-slash-command' || pkg.type === 'cursor-slash-command') {
|
|
236
|
+
effectiveType = 'cursor-slash-command';
|
|
237
|
+
}
|
|
238
|
+
else if (pkg.type === 'claude-agent' || pkg.type === 'cursor-agent') {
|
|
239
|
+
effectiveType = 'cursor-agent';
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
effectiveType = 'cursor';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else if (format === 'claude') {
|
|
246
|
+
// Map package types to claude equivalents
|
|
247
|
+
if (pkg.type === 'cursor-slash-command' || pkg.type === 'claude-slash-command') {
|
|
248
|
+
effectiveType = 'claude-slash-command';
|
|
249
|
+
}
|
|
250
|
+
else if (pkg.type === 'cursor-agent' || pkg.type === 'claude-agent') {
|
|
251
|
+
effectiveType = 'claude-agent';
|
|
252
|
+
}
|
|
253
|
+
else if (pkg.type === 'claude-skill') {
|
|
254
|
+
effectiveType = 'claude-skill';
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
effectiveType = 'claude-agent';
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (format === 'continue' || format === 'windsurf') {
|
|
261
|
+
effectiveType = format;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
effectiveType = (options.type || pkg.type);
|
|
265
|
+
}
|
|
266
|
+
const destDir = (0, filesystem_1.getDestinationDir)(effectiveType);
|
|
267
|
+
// Extract all files from tarball
|
|
268
|
+
const extractedFiles = await extractTarball(tarball, packageId);
|
|
269
|
+
// Track where files were saved for user feedback
|
|
270
|
+
let destPath;
|
|
271
|
+
let fileCount = 0;
|
|
272
|
+
// Check if this is a multi-file package
|
|
273
|
+
if (extractedFiles.length === 1) {
|
|
274
|
+
// Single file package
|
|
275
|
+
let mainFile = extractedFiles[0].content;
|
|
276
|
+
// Determine file extension based on effective type
|
|
277
|
+
// Cursor rules use .mdc, but slash commands and other files use .md
|
|
278
|
+
const fileExtension = (effectiveType === 'cursor' && format === 'cursor') ? 'mdc' : 'md';
|
|
279
|
+
const packageName = (0, filesystem_1.stripAuthorNamespace)(packageId);
|
|
280
|
+
destPath = `${destDir}/${packageName}.${fileExtension}`;
|
|
281
|
+
// Handle cursor format - add header if missing for .mdc files
|
|
282
|
+
if (format === 'cursor' && effectiveType === 'cursor') {
|
|
283
|
+
if (!(0, cursor_config_1.hasMDCHeader)(mainFile)) {
|
|
284
|
+
console.log(` ⚠️ Adding missing MDC header...`);
|
|
285
|
+
mainFile = (0, cursor_config_1.addMDCHeader)(mainFile, pkg.description);
|
|
286
|
+
}
|
|
287
|
+
// Apply cursor config if available
|
|
288
|
+
if (config.cursor) {
|
|
289
|
+
console.log(` ⚙️ Applying cursor config...`);
|
|
290
|
+
mainFile = (0, cursor_config_1.applyCursorConfig)(mainFile, config.cursor);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Apply Claude config if downloading in Claude format
|
|
294
|
+
if (format === 'claude' && (0, claude_config_1.hasClaudeHeader)(mainFile)) {
|
|
295
|
+
if (config.claude) {
|
|
296
|
+
console.log(` ⚙️ Applying Claude agent config...`);
|
|
297
|
+
mainFile = (0, claude_config_1.applyClaudeConfig)(mainFile, config.claude);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
await (0, filesystem_1.saveFile)(destPath, mainFile);
|
|
301
|
+
fileCount = 1;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
// Multi-file package - create directory for package
|
|
305
|
+
const packageName = (0, filesystem_1.stripAuthorNamespace)(packageId);
|
|
306
|
+
const packageDir = `${destDir}/${packageName}`;
|
|
307
|
+
destPath = packageDir;
|
|
308
|
+
console.log(` 📁 Multi-file package - creating directory: ${packageDir}`);
|
|
309
|
+
for (const file of extractedFiles) {
|
|
310
|
+
const filePath = `${packageDir}/${file.name}`;
|
|
311
|
+
await (0, filesystem_1.saveFile)(filePath, file.content);
|
|
312
|
+
fileCount++;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
146
315
|
// Update or create lock file
|
|
147
316
|
const updatedLockfile = lockfile || (0, lockfile_1.createLockfile)();
|
|
148
317
|
const actualVersion = version === 'latest' ? pkg.latest_version?.version : version;
|
|
149
318
|
(0, lockfile_1.addToLockfile)(updatedLockfile, packageId, {
|
|
150
319
|
version: actualVersion || version,
|
|
151
320
|
tarballUrl,
|
|
152
|
-
type,
|
|
321
|
+
type: pkg.type,
|
|
153
322
|
format,
|
|
323
|
+
installedPath: destPath,
|
|
154
324
|
});
|
|
155
325
|
(0, lockfile_1.setPackageIntegrity)(updatedLockfile, packageId, tarball);
|
|
156
326
|
await (0, lockfile_1.writeLockfile)(updatedLockfile);
|
|
157
|
-
// Update
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
};
|
|
165
|
-
|
|
327
|
+
// Update lockfile (already done above via addToLockfile + writeLockfile)
|
|
328
|
+
// No need to call addPackage again as it would be redundant
|
|
329
|
+
// Track download analytics
|
|
330
|
+
await client.trackDownload(packageId, {
|
|
331
|
+
version: actualVersion || version,
|
|
332
|
+
client: 'cli',
|
|
333
|
+
format,
|
|
334
|
+
});
|
|
335
|
+
// Display the incremented download count
|
|
336
|
+
const newDownloadCount = pkg.total_downloads + 1;
|
|
166
337
|
console.log(`\n✅ Successfully installed ${packageId}`);
|
|
167
338
|
console.log(` 📁 Saved to: ${destPath}`);
|
|
168
339
|
console.log(` 🔒 Lock file updated`);
|
|
169
|
-
console.log(`\n💡 This package has been downloaded ${
|
|
340
|
+
console.log(`\n💡 This package has been downloaded ${newDownloadCount.toLocaleString()} times`);
|
|
170
341
|
success = true;
|
|
171
342
|
}
|
|
172
343
|
catch (err) {
|
|
@@ -175,7 +346,6 @@ async function handleInstall(packageSpec, options) {
|
|
|
175
346
|
console.log(`\n💡 Tips:`);
|
|
176
347
|
console.log(` - Check package name: prpm search <query>`);
|
|
177
348
|
console.log(` - Get package info: prpm info <package>`);
|
|
178
|
-
console.log(` - Install from URL: prpm add <url> --as <type>`);
|
|
179
349
|
process.exit(1);
|
|
180
350
|
}
|
|
181
351
|
finally {
|
|
@@ -190,26 +360,77 @@ async function handleInstall(packageSpec, options) {
|
|
|
190
360
|
type: options.type,
|
|
191
361
|
},
|
|
192
362
|
});
|
|
363
|
+
await telemetry_1.telemetry.shutdown();
|
|
193
364
|
}
|
|
194
365
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
* TODO: Implement proper tar extraction with tar library
|
|
198
|
-
*/
|
|
199
|
-
async function extractMainFile(tarball, packageId) {
|
|
200
|
-
// Placeholder implementation
|
|
201
|
-
// In reality, we need to:
|
|
202
|
-
// 1. Extract tar.gz
|
|
203
|
-
// 2. Find main file (from manifest or naming convention)
|
|
204
|
-
// 3. Return file contents
|
|
205
|
-
// For now, assume tarball is just gzipped content
|
|
366
|
+
async function extractTarball(tarball, packageId) {
|
|
367
|
+
const files = [];
|
|
206
368
|
const zlib = await Promise.resolve().then(() => __importStar(require('zlib')));
|
|
369
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
370
|
+
const os = await Promise.resolve().then(() => __importStar(require('os')));
|
|
371
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
207
372
|
return new Promise((resolve, reject) => {
|
|
208
|
-
|
|
209
|
-
|
|
373
|
+
// Decompress gzip first
|
|
374
|
+
zlib.gunzip(tarball, async (err, result) => {
|
|
375
|
+
if (err) {
|
|
210
376
|
reject(err);
|
|
211
|
-
|
|
212
|
-
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
// Check if this is a tar archive by looking for tar header
|
|
380
|
+
const isTar = result.length > 257 && result.toString('utf-8', 257, 262) === 'ustar';
|
|
381
|
+
if (!isTar) {
|
|
382
|
+
// Not a tar archive, treat as single gzipped file
|
|
383
|
+
files.push({
|
|
384
|
+
name: `${packageId}.md`,
|
|
385
|
+
content: result.toString('utf-8')
|
|
386
|
+
});
|
|
387
|
+
resolve(files);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
// Create temp directory for extraction
|
|
391
|
+
const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'prpm-'));
|
|
392
|
+
try {
|
|
393
|
+
// Write tar data to temp file
|
|
394
|
+
const tarPath = path.join(tmpDir, 'package.tar');
|
|
395
|
+
await fs.promises.writeFile(tarPath, result);
|
|
396
|
+
// Extract using tar library
|
|
397
|
+
await tar.extract({
|
|
398
|
+
file: tarPath,
|
|
399
|
+
cwd: tmpDir,
|
|
400
|
+
});
|
|
401
|
+
// Read all extracted files
|
|
402
|
+
const extractedFiles = await fs.promises.readdir(tmpDir, { withFileTypes: true, recursive: true });
|
|
403
|
+
for (const entry of extractedFiles) {
|
|
404
|
+
if (entry.isFile() && entry.name !== 'package.tar') {
|
|
405
|
+
const filePath = path.join(entry.path || tmpDir, entry.name);
|
|
406
|
+
const content = await fs.promises.readFile(filePath, 'utf-8');
|
|
407
|
+
const relativePath = path.relative(tmpDir, filePath);
|
|
408
|
+
files.push({
|
|
409
|
+
name: relativePath,
|
|
410
|
+
content
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (files.length === 0) {
|
|
415
|
+
// No files found, fall back to single file
|
|
416
|
+
files.push({
|
|
417
|
+
name: `${packageId}.md`,
|
|
418
|
+
content: result.toString('utf-8')
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
// Cleanup
|
|
422
|
+
await fs.promises.rm(tmpDir, { recursive: true, force: true });
|
|
423
|
+
resolve(files);
|
|
424
|
+
}
|
|
425
|
+
catch (tarErr) {
|
|
426
|
+
// Cleanup and fall back to single file
|
|
427
|
+
await fs.promises.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
|
|
428
|
+
files.push({
|
|
429
|
+
name: `${packageId}.md`,
|
|
430
|
+
content: result.toString('utf-8')
|
|
431
|
+
});
|
|
432
|
+
resolve(files);
|
|
433
|
+
}
|
|
213
434
|
});
|
|
214
435
|
});
|
|
215
436
|
}
|
|
@@ -246,7 +467,11 @@ function createInstallCommand() {
|
|
|
246
467
|
console.error('❌ Format must be one of: cursor, claude, continue, windsurf, canonical');
|
|
247
468
|
process.exit(1);
|
|
248
469
|
}
|
|
249
|
-
await handleInstall(packageSpec,
|
|
470
|
+
await handleInstall(packageSpec, {
|
|
471
|
+
type: options.type,
|
|
472
|
+
as: options.as,
|
|
473
|
+
frozenLockfile: options.frozenLockfile
|
|
474
|
+
});
|
|
250
475
|
});
|
|
251
476
|
return command;
|
|
252
477
|
}
|
package/dist/commands/list.js
CHANGED
|
@@ -2,43 +2,119 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* List command implementation
|
|
4
4
|
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
5
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
9
|
exports.handleList = handleList;
|
|
7
10
|
exports.createListCommand = createListCommand;
|
|
8
11
|
const commander_1 = require("commander");
|
|
9
|
-
const
|
|
12
|
+
const lockfile_1 = require("../core/lockfile");
|
|
10
13
|
const telemetry_1 = require("../core/telemetry");
|
|
14
|
+
const fs_1 = require("fs");
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
/**
|
|
17
|
+
* Get destination directory based on package type
|
|
18
|
+
*/
|
|
19
|
+
function getDestinationDir(type) {
|
|
20
|
+
switch (type) {
|
|
21
|
+
case 'cursor':
|
|
22
|
+
return '.cursor/rules';
|
|
23
|
+
case 'claude':
|
|
24
|
+
return '.claude/agents';
|
|
25
|
+
case 'claude-agent':
|
|
26
|
+
return '.claude/agents';
|
|
27
|
+
case 'claude-skill':
|
|
28
|
+
return '.claude/skills';
|
|
29
|
+
case 'claude-slash-command':
|
|
30
|
+
return '.claude/commands';
|
|
31
|
+
case 'continue':
|
|
32
|
+
return '.continue/rules';
|
|
33
|
+
case 'windsurf':
|
|
34
|
+
return '.windsurf/rules';
|
|
35
|
+
case 'generic':
|
|
36
|
+
return '.prompts';
|
|
37
|
+
case 'mcp':
|
|
38
|
+
return '.mcp';
|
|
39
|
+
default:
|
|
40
|
+
return '.prompts';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Find the actual file location for a package
|
|
45
|
+
*/
|
|
46
|
+
async function findPackageLocation(id, type) {
|
|
47
|
+
if (!type)
|
|
48
|
+
return null;
|
|
49
|
+
const baseDir = getDestinationDir(type);
|
|
50
|
+
// Try direct file: <dir>/<id>.md
|
|
51
|
+
const directPath = path_1.default.join(baseDir, `${id}.md`);
|
|
52
|
+
try {
|
|
53
|
+
await fs_1.promises.access(directPath);
|
|
54
|
+
return directPath;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// File doesn't exist, try subdirectory
|
|
58
|
+
}
|
|
59
|
+
// Try subdirectory: <dir>/<id>/SKILL.md or <dir>/<id>/AGENT.md
|
|
60
|
+
if (type === 'claude-skill') {
|
|
61
|
+
const skillPath = path_1.default.join(baseDir, id, 'SKILL.md');
|
|
62
|
+
try {
|
|
63
|
+
await fs_1.promises.access(skillPath);
|
|
64
|
+
return skillPath;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Not found
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (type === 'claude') {
|
|
71
|
+
const agentPath = path_1.default.join(baseDir, id, 'AGENT.md');
|
|
72
|
+
try {
|
|
73
|
+
await fs_1.promises.access(agentPath);
|
|
74
|
+
return agentPath;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Not found
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
11
82
|
/**
|
|
12
83
|
* Display packages in a formatted table
|
|
13
84
|
*/
|
|
14
|
-
function displayPackages(packages) {
|
|
85
|
+
async function displayPackages(packages) {
|
|
15
86
|
if (packages.length === 0) {
|
|
16
87
|
console.log('📦 No packages installed');
|
|
17
88
|
return;
|
|
18
89
|
}
|
|
19
90
|
console.log('📦 Installed packages:');
|
|
20
91
|
console.log('');
|
|
92
|
+
// Find file locations
|
|
93
|
+
const packagesWithLocations = await Promise.all(packages.map(async (pkg) => ({
|
|
94
|
+
...pkg,
|
|
95
|
+
location: await findPackageLocation(pkg.id, pkg.type)
|
|
96
|
+
})));
|
|
21
97
|
// Calculate column widths
|
|
22
|
-
const idWidth = Math.max(8, ...
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
98
|
+
const idWidth = Math.max(8, ...packagesWithLocations.map(p => p.id.length));
|
|
99
|
+
const versionWidth = Math.max(7, ...packagesWithLocations.map(p => p.version.length));
|
|
100
|
+
const typeWidth = Math.max(6, ...packagesWithLocations.map(p => (p.type || '').length));
|
|
101
|
+
const locationWidth = Math.max(8, ...packagesWithLocations.map(p => (p.location || 'N/A').length));
|
|
26
102
|
// Header
|
|
27
103
|
const header = [
|
|
28
104
|
'ID'.padEnd(idWidth),
|
|
105
|
+
'VERSION'.padEnd(versionWidth),
|
|
29
106
|
'TYPE'.padEnd(typeWidth),
|
|
30
|
-
'
|
|
31
|
-
'DESTINATION'.padEnd(destWidth)
|
|
107
|
+
'LOCATION'.padEnd(locationWidth)
|
|
32
108
|
].join(' | ');
|
|
33
109
|
console.log(header);
|
|
34
110
|
console.log('-'.repeat(header.length));
|
|
35
111
|
// Rows
|
|
36
|
-
|
|
112
|
+
packagesWithLocations.forEach(pkg => {
|
|
37
113
|
const row = [
|
|
38
114
|
pkg.id.padEnd(idWidth),
|
|
39
|
-
pkg.
|
|
40
|
-
pkg.
|
|
41
|
-
pkg.
|
|
115
|
+
pkg.version.padEnd(versionWidth),
|
|
116
|
+
(pkg.type || '').padEnd(typeWidth),
|
|
117
|
+
(pkg.location || 'N/A').padEnd(locationWidth)
|
|
42
118
|
].join(' | ');
|
|
43
119
|
console.log(row);
|
|
44
120
|
});
|
|
@@ -54,9 +130,9 @@ async function handleList() {
|
|
|
54
130
|
let error;
|
|
55
131
|
let packageCount = 0;
|
|
56
132
|
try {
|
|
57
|
-
const packages = await (0,
|
|
133
|
+
const packages = await (0, lockfile_1.listPackages)();
|
|
58
134
|
packageCount = packages.length;
|
|
59
|
-
displayPackages(packages);
|
|
135
|
+
await displayPackages(packages);
|
|
60
136
|
success = true;
|
|
61
137
|
}
|
|
62
138
|
catch (err) {
|
|
@@ -75,6 +151,7 @@ async function handleList() {
|
|
|
75
151
|
packageCount,
|
|
76
152
|
},
|
|
77
153
|
});
|
|
154
|
+
await telemetry_1.telemetry.shutdown();
|
|
78
155
|
}
|
|
79
156
|
}
|
|
80
157
|
/**
|