prpm 0.1.8 ā 0.1.10
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/dist/commands/init.js +51 -16
- package/dist/commands/install.js +145 -8
- package/dist/commands/publish.js +39 -8
- package/dist/commands/search.js +3 -1
- package/dist/core/filesystem.js +12 -0
- package/dist/types.js +1 -0
- package/package.json +3 -3
package/dist/commands/init.js
CHANGED
|
@@ -63,12 +63,12 @@ const FORMAT_EXAMPLES = {
|
|
|
63
63
|
files: ['.windsurf/rules', 'README.md'],
|
|
64
64
|
},
|
|
65
65
|
copilot: {
|
|
66
|
-
description: 'GitHub Copilot
|
|
67
|
-
files: ['.github/copilot-instructions.md', 'README.md'],
|
|
66
|
+
description: 'GitHub Copilot (repository-wide, path-specific, and chat modes)',
|
|
67
|
+
files: ['.github/copilot-instructions.md', '.github/instructions/typescript.instructions.md', '.github/chatmodes/code-reviewer.chatmode.md', 'README.md'],
|
|
68
68
|
},
|
|
69
69
|
kiro: {
|
|
70
|
-
description: 'Kiro steering files',
|
|
71
|
-
files: ['.kiro/steering/example.md', 'README.md'],
|
|
70
|
+
description: 'Kiro steering files and hooks',
|
|
71
|
+
files: ['.kiro/steering/example.md', '.kiro/hooks/example-hook.kiro.hook', 'README.md'],
|
|
72
72
|
},
|
|
73
73
|
'agents.md': {
|
|
74
74
|
description: 'OpenAI agents.md project instructions',
|
|
@@ -145,31 +145,49 @@ Add your Windsurf AI coding rules here.
|
|
|
145
145
|
`,
|
|
146
146
|
},
|
|
147
147
|
copilot: {
|
|
148
|
-
'.github/copilot-instructions.md':
|
|
149
|
-
applyTo:
|
|
150
|
-
- "**/*.ts"
|
|
151
|
-
- "**/*.tsx"
|
|
152
|
-
---
|
|
148
|
+
'.github/copilot-instructions.md': `# GitHub Copilot Repository-Wide Instructions
|
|
153
149
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
Add your GitHub Copilot instructions here.
|
|
150
|
+
These instructions apply to all files in this repository.
|
|
157
151
|
|
|
158
152
|
## Code Standards
|
|
159
153
|
|
|
160
154
|
- Use TypeScript strict mode
|
|
161
155
|
- Follow ESLint rules
|
|
162
|
-
- Write JSDoc comments
|
|
156
|
+
- Write JSDoc comments for public APIs
|
|
163
157
|
|
|
164
158
|
## Patterns to Follow
|
|
165
159
|
|
|
166
160
|
- Use async/await for asynchronous operations
|
|
167
161
|
- Implement error handling with try/catch
|
|
168
162
|
- Export named functions instead of default exports
|
|
163
|
+
- Write unit tests for all new features
|
|
164
|
+
`,
|
|
165
|
+
'.github/instructions/typescript.instructions.md': `---
|
|
166
|
+
applyTo:
|
|
167
|
+
- "**/*.ts"
|
|
168
|
+
- "**/*.tsx"
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
# TypeScript-Specific Instructions
|
|
172
|
+
|
|
173
|
+
These instructions apply only to TypeScript files.
|
|
174
|
+
|
|
175
|
+
## Type Safety
|
|
176
|
+
|
|
177
|
+
- Use strict null checks
|
|
178
|
+
- Avoid 'any' type - use 'unknown' or specific types
|
|
179
|
+
- Define interfaces for all object shapes
|
|
180
|
+
- Use type guards for runtime type checking
|
|
181
|
+
|
|
182
|
+
## Patterns
|
|
183
|
+
|
|
184
|
+
- Prefer const over let
|
|
185
|
+
- Use template literals for string concatenation
|
|
186
|
+
- Destructure objects and arrays when appropriate
|
|
169
187
|
`,
|
|
170
|
-
'.github/chatmodes/
|
|
171
|
-
name:
|
|
172
|
-
description:
|
|
188
|
+
'.github/chatmodes/code-reviewer.chatmode.md': `---
|
|
189
|
+
name: Code Reviewer
|
|
190
|
+
description: Expert code reviewer focusing on best practices, security, and maintainability
|
|
173
191
|
---
|
|
174
192
|
|
|
175
193
|
# Example Chat Mode
|
|
@@ -221,6 +239,23 @@ Describe the context where this steering file applies.
|
|
|
221
239
|
|
|
222
240
|
Provide examples of correct patterns.
|
|
223
241
|
`,
|
|
242
|
+
'.kiro/hooks/example-hook.kiro.hook': `{
|
|
243
|
+
"enabled": false,
|
|
244
|
+
"name": "Example Hook",
|
|
245
|
+
"description": "Example Kiro hook - disabled by default. Replace with your actual hook logic.",
|
|
246
|
+
"version": "1",
|
|
247
|
+
"when": {
|
|
248
|
+
"type": "fileEdited",
|
|
249
|
+
"patterns": [
|
|
250
|
+
"**/*.ts",
|
|
251
|
+
"**/*.tsx"
|
|
252
|
+
]
|
|
253
|
+
},
|
|
254
|
+
"then": {
|
|
255
|
+
"type": "askAgent",
|
|
256
|
+
"prompt": "A file has been modified. Please review it for best practices:\\n1. Check for code quality issues\\n2. Verify error handling\\n3. Suggest improvements\\n4. Ensure tests are updated"
|
|
257
|
+
}
|
|
258
|
+
}`,
|
|
224
259
|
},
|
|
225
260
|
'agents.md': {
|
|
226
261
|
'agents.md': `# Project Coding Guidelines
|
package/dist/commands/install.js
CHANGED
|
@@ -37,6 +37,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
})();
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.handleInstall = handleInstall;
|
|
40
|
+
exports.installFromLockfile = installFromLockfile;
|
|
40
41
|
exports.createInstallCommand = createInstallCommand;
|
|
41
42
|
const commander_1 = require("commander");
|
|
42
43
|
const registry_client_1 = require("@pr-pm/registry-client");
|
|
@@ -61,6 +62,7 @@ function getPackageIcon(format, subtype) {
|
|
|
61
62
|
'collection': 'š¦',
|
|
62
63
|
'chatmode': 'š¬',
|
|
63
64
|
'tool': 'š§',
|
|
65
|
+
'hook': 'šŖ',
|
|
64
66
|
};
|
|
65
67
|
// Format-specific icons for rules/defaults
|
|
66
68
|
const formatIcons = {
|
|
@@ -100,6 +102,7 @@ function getPackageLabel(format, subtype) {
|
|
|
100
102
|
'collection': 'Collection',
|
|
101
103
|
'chatmode': 'Chat Mode',
|
|
102
104
|
'tool': 'Tool',
|
|
105
|
+
'hook': 'Hook',
|
|
103
106
|
};
|
|
104
107
|
const formatLabel = formatLabels[format];
|
|
105
108
|
const subtypeLabel = subtypeLabels[subtype];
|
|
@@ -159,8 +162,8 @@ async function handleInstall(packageSpec, options) {
|
|
|
159
162
|
// Normal mode - use specified version or locked version or latest
|
|
160
163
|
version = options.version || specVersion || lockedVersion || 'latest';
|
|
161
164
|
}
|
|
162
|
-
// Check if package is already installed
|
|
163
|
-
if (lockfile && lockfile.packages[packageId]) {
|
|
165
|
+
// Check if package is already installed (skip if --force option is set)
|
|
166
|
+
if (!options.force && lockfile && lockfile.packages[packageId]) {
|
|
164
167
|
const installedPkg = lockfile.packages[packageId];
|
|
165
168
|
const requestedVersion = options.version || specVersion;
|
|
166
169
|
// If no specific version requested, or same version requested
|
|
@@ -301,6 +304,7 @@ async function handleInstall(packageSpec, options) {
|
|
|
301
304
|
const packageName = (0, filesystem_1.stripAuthorNamespace)(packageId);
|
|
302
305
|
// For Claude skills, use SKILL.md filename in the package directory
|
|
303
306
|
// For agents.md, use package-name/AGENTS.md directory structure
|
|
307
|
+
// For Copilot, use official naming conventions
|
|
304
308
|
// For other formats, use package name as filename
|
|
305
309
|
if (effectiveFormat === 'claude' && effectiveSubtype === 'skill') {
|
|
306
310
|
destPath = `${destDir}/SKILL.md`;
|
|
@@ -308,6 +312,21 @@ async function handleInstall(packageSpec, options) {
|
|
|
308
312
|
else if (effectiveFormat === 'agents.md') {
|
|
309
313
|
destPath = `${destDir}/${packageName}/AGENTS.md`;
|
|
310
314
|
}
|
|
315
|
+
else if (effectiveFormat === 'copilot') {
|
|
316
|
+
// Official GitHub Copilot naming conventions
|
|
317
|
+
if (effectiveSubtype === 'chatmode') {
|
|
318
|
+
// Chat modes: .github/chatmodes/NAME.chatmode.md
|
|
319
|
+
destPath = `${destDir}/${packageName}.chatmode.md`;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
// Path-specific instructions: .github/instructions/NAME.instructions.md
|
|
323
|
+
destPath = `${destDir}/${packageName}.instructions.md`;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else if (effectiveFormat === 'kiro' && effectiveSubtype === 'hook') {
|
|
327
|
+
// Kiro hooks use .kiro.hook extension (JSON files)
|
|
328
|
+
destPath = `${destDir}/${packageName}.kiro.hook`;
|
|
329
|
+
}
|
|
311
330
|
else {
|
|
312
331
|
destPath = `${destDir}/${packageName}.${fileExtension}`;
|
|
313
332
|
}
|
|
@@ -337,10 +356,14 @@ async function handleInstall(packageSpec, options) {
|
|
|
337
356
|
const destDir = (0, filesystem_1.getDestinationDir)(effectiveFormat, effectiveSubtype, pkg.name);
|
|
338
357
|
// Multi-file package - create directory for package
|
|
339
358
|
// For Claude skills, destDir already includes package name, so use it directly
|
|
359
|
+
// For Cursor rules converted from Claude skills, use flat structure
|
|
340
360
|
const packageName = (0, filesystem_1.stripAuthorNamespace)(packageId);
|
|
361
|
+
const isCursorConversion = (effectiveFormat === 'cursor' && pkg.format === 'claude' && pkg.subtype === 'skill');
|
|
341
362
|
const packageDir = (effectiveFormat === 'claude' && effectiveSubtype === 'skill')
|
|
342
363
|
? destDir
|
|
343
|
-
:
|
|
364
|
+
: isCursorConversion
|
|
365
|
+
? destDir // Cursor uses flat structure
|
|
366
|
+
: `${destDir}/${packageName}`;
|
|
344
367
|
destPath = packageDir;
|
|
345
368
|
console.log(` š Multi-file package - creating directory: ${packageDir}`);
|
|
346
369
|
// For Claude skills, verify SKILL.md exists
|
|
@@ -366,6 +389,8 @@ async function handleInstall(packageSpec, options) {
|
|
|
366
389
|
}
|
|
367
390
|
}
|
|
368
391
|
}
|
|
392
|
+
// Track JSON files for @reference insertion in Cursor conversion
|
|
393
|
+
const jsonFiles = [];
|
|
369
394
|
for (const file of extractedFiles) {
|
|
370
395
|
// Strip the tarball's root directory prefix to preserve subdirectories
|
|
371
396
|
// Example: ".claude/skills/agent-builder/docs/examples.md" ā "docs/examples.md"
|
|
@@ -385,10 +410,58 @@ async function handleInstall(packageSpec, options) {
|
|
|
385
410
|
// Fallback: just take the filename (last part)
|
|
386
411
|
relativeFileName = pathParts[pathParts.length - 1];
|
|
387
412
|
}
|
|
388
|
-
|
|
389
|
-
|
|
413
|
+
let fileContent = file.content;
|
|
414
|
+
let fileName = relativeFileName;
|
|
415
|
+
// Handle Cursor conversion from Claude skill
|
|
416
|
+
if (isCursorConversion) {
|
|
417
|
+
// Convert SKILL.md to .mdc
|
|
418
|
+
if (fileName === 'SKILL.md' || fileName.endsWith('/SKILL.md')) {
|
|
419
|
+
fileName = `${packageName}.mdc`;
|
|
420
|
+
// Add MDC header if missing
|
|
421
|
+
if (!(0, cursor_config_1.hasMDCHeader)(fileContent)) {
|
|
422
|
+
console.log(` ā ļø Adding MDC header to converted skill...`);
|
|
423
|
+
fileContent = (0, cursor_config_1.addMDCHeader)(fileContent, pkg.description);
|
|
424
|
+
}
|
|
425
|
+
// Apply cursor config if available
|
|
426
|
+
if (config.cursor) {
|
|
427
|
+
console.log(` āļø Applying cursor config...`);
|
|
428
|
+
fileContent = (0, cursor_config_1.applyCursorConfig)(fileContent, config.cursor);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// Track JSON files for @reference
|
|
432
|
+
else if (fileName.endsWith('.json')) {
|
|
433
|
+
// Flatten structure - remove subdirectories
|
|
434
|
+
const jsonFileName = fileName.split('/').pop() || fileName;
|
|
435
|
+
fileName = jsonFileName;
|
|
436
|
+
jsonFiles.push(jsonFileName);
|
|
437
|
+
}
|
|
438
|
+
// For other files (docs, etc), flatten the structure
|
|
439
|
+
else {
|
|
440
|
+
fileName = fileName.split('/').pop() || fileName;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
const filePath = `${packageDir}/${fileName}`;
|
|
444
|
+
await (0, filesystem_1.saveFile)(filePath, fileContent);
|
|
390
445
|
fileCount++;
|
|
391
446
|
}
|
|
447
|
+
// Add @references to .mdc file for JSON files
|
|
448
|
+
if (isCursorConversion && jsonFiles.length > 0) {
|
|
449
|
+
const mdcFile = `${packageDir}/${packageName}.mdc`;
|
|
450
|
+
const { readFile } = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
451
|
+
let mdcContent = await readFile(mdcFile, 'utf-8');
|
|
452
|
+
// Find the end of frontmatter (if exists)
|
|
453
|
+
const frontmatterMatch = mdcContent.match(/^---\n[\s\S]*?\n---\n/);
|
|
454
|
+
if (frontmatterMatch) {
|
|
455
|
+
const frontmatterEnd = frontmatterMatch[0].length;
|
|
456
|
+
const beforeFrontmatter = mdcContent.slice(0, frontmatterEnd);
|
|
457
|
+
const afterFrontmatter = mdcContent.slice(frontmatterEnd);
|
|
458
|
+
// Add @references right after frontmatter
|
|
459
|
+
const references = jsonFiles.map(f => `@${f}`).join('\n');
|
|
460
|
+
mdcContent = `${beforeFrontmatter}\n${references}\n${afterFrontmatter}`;
|
|
461
|
+
await (0, filesystem_1.saveFile)(mdcFile, mdcContent);
|
|
462
|
+
console.log(` ā Added ${jsonFiles.length} @reference(s) to ${packageName}.mdc`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
392
465
|
}
|
|
393
466
|
// Update or create lock file
|
|
394
467
|
const updatedLockfile = lockfile || (0, lockfile_1.createLockfile)();
|
|
@@ -434,7 +507,7 @@ async function handleInstall(packageSpec, options) {
|
|
|
434
507
|
error,
|
|
435
508
|
duration: Date.now() - startTime,
|
|
436
509
|
data: {
|
|
437
|
-
packageId: packageSpec.split('@')[0],
|
|
510
|
+
packageId: packageSpec ? packageSpec.split('@')[0] : 'lockfile',
|
|
438
511
|
version: options.version || 'latest',
|
|
439
512
|
convertTo: options.as,
|
|
440
513
|
},
|
|
@@ -530,11 +603,65 @@ function detectProjectFormat() {
|
|
|
530
603
|
return 'windsurf';
|
|
531
604
|
return null;
|
|
532
605
|
}
|
|
606
|
+
/**
|
|
607
|
+
* Install all packages from prpm.lock
|
|
608
|
+
*/
|
|
609
|
+
async function installFromLockfile(options) {
|
|
610
|
+
try {
|
|
611
|
+
// Read lockfile
|
|
612
|
+
const lockfile = await (0, lockfile_1.readLockfile)();
|
|
613
|
+
if (!lockfile) {
|
|
614
|
+
console.error('ā No prpm.lock file found');
|
|
615
|
+
console.log('\nš” Run "prpm install <package>" first to create a lockfile, or initialize a new project with "prpm init"');
|
|
616
|
+
process.exit(1);
|
|
617
|
+
}
|
|
618
|
+
const packageIds = Object.keys(lockfile.packages);
|
|
619
|
+
if (packageIds.length === 0) {
|
|
620
|
+
console.log('ā
No packages to install (prpm.lock is empty)');
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
console.log(`š¦ Installing ${packageIds.length} package${packageIds.length === 1 ? '' : 's'} from prpm.lock...\n`);
|
|
624
|
+
let successCount = 0;
|
|
625
|
+
let failCount = 0;
|
|
626
|
+
// Install each package from lockfile
|
|
627
|
+
for (const packageId of packageIds) {
|
|
628
|
+
const lockEntry = lockfile.packages[packageId];
|
|
629
|
+
try {
|
|
630
|
+
// Extract package spec (strip version if present in packageId)
|
|
631
|
+
const packageSpec = packageId.includes('@') && !packageId.startsWith('@')
|
|
632
|
+
? packageId.substring(0, packageId.lastIndexOf('@'))
|
|
633
|
+
: packageId;
|
|
634
|
+
console.log(` Installing ${packageId}...`);
|
|
635
|
+
await handleInstall(packageSpec, {
|
|
636
|
+
version: lockEntry.version,
|
|
637
|
+
as: options.as || lockEntry.format,
|
|
638
|
+
subtype: options.subtype || lockEntry.subtype,
|
|
639
|
+
frozenLockfile: options.frozenLockfile,
|
|
640
|
+
force: true, // Force reinstall when installing from lockfile
|
|
641
|
+
});
|
|
642
|
+
successCount++;
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
failCount++;
|
|
646
|
+
console.error(` ā Failed to install ${packageId}: ${error}`);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
console.log(`\nā
Installed ${successCount}/${packageIds.length} packages`);
|
|
650
|
+
if (failCount > 0) {
|
|
651
|
+
console.error(`ā ${failCount} package${failCount === 1 ? '' : 's'} failed to install`);
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
catch (error) {
|
|
656
|
+
console.error(`ā Failed to install from lockfile: ${error}`);
|
|
657
|
+
process.exit(1);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
533
660
|
function createInstallCommand() {
|
|
534
661
|
const command = new commander_1.Command('install');
|
|
535
662
|
command
|
|
536
|
-
.description('Install a package from the registry')
|
|
537
|
-
.argument('
|
|
663
|
+
.description('Install a package from the registry, or install all packages from prpm.lock if no package specified')
|
|
664
|
+
.argument('[package]', 'Package to install (e.g., react-rules or react-rules@1.2.0). If omitted, installs all packages from prpm.lock')
|
|
538
665
|
.option('--version <version>', 'Specific version to install')
|
|
539
666
|
.option('--as <format>', 'Convert and install in specific format (cursor, claude, continue, windsurf, copilot, kiro, agents.md, canonical)')
|
|
540
667
|
.option('--format <format>', 'Alias for --as')
|
|
@@ -553,6 +680,16 @@ function createInstallCommand() {
|
|
|
553
680
|
console.log(' prpm install my-package # Install in native format');
|
|
554
681
|
process.exit(1);
|
|
555
682
|
}
|
|
683
|
+
// If no package specified, install from lockfile
|
|
684
|
+
if (!packageSpec) {
|
|
685
|
+
await installFromLockfile({
|
|
686
|
+
as: convertTo,
|
|
687
|
+
subtype: options.subtype,
|
|
688
|
+
frozenLockfile: options.frozenLockfile
|
|
689
|
+
});
|
|
690
|
+
process.exit(0);
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
556
693
|
await handleInstall(packageSpec, {
|
|
557
694
|
version: options.version,
|
|
558
695
|
as: convertTo,
|
package/dist/commands/publish.js
CHANGED
|
@@ -254,6 +254,28 @@ function normalizeFilePaths(files) {
|
|
|
254
254
|
}
|
|
255
255
|
});
|
|
256
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* Predict what the scoped package name will be after publishing
|
|
259
|
+
* This matches the server-side logic in packages.ts
|
|
260
|
+
*/
|
|
261
|
+
function predictScopedPackageName(manifestName, username, organization) {
|
|
262
|
+
const usernameLowercase = username.toLowerCase();
|
|
263
|
+
// If organization is specified, use @org-name/
|
|
264
|
+
if (organization) {
|
|
265
|
+
const orgNameLowercase = organization.toLowerCase();
|
|
266
|
+
const expectedPrefix = `@${orgNameLowercase}/`;
|
|
267
|
+
if (!manifestName.startsWith(expectedPrefix)) {
|
|
268
|
+
return `${expectedPrefix}${manifestName}`;
|
|
269
|
+
}
|
|
270
|
+
return manifestName;
|
|
271
|
+
}
|
|
272
|
+
// If package name doesn't already have a scope, add @username/
|
|
273
|
+
if (!manifestName.startsWith('@')) {
|
|
274
|
+
return `@${usernameLowercase}/${manifestName}`;
|
|
275
|
+
}
|
|
276
|
+
// Package already has a scope, return as-is
|
|
277
|
+
return manifestName;
|
|
278
|
+
}
|
|
257
279
|
/**
|
|
258
280
|
* Create tarball from current directory
|
|
259
281
|
*/
|
|
@@ -442,6 +464,7 @@ async function handlePublish(options) {
|
|
|
442
464
|
console.log('');
|
|
443
465
|
}
|
|
444
466
|
let selectedOrgId;
|
|
467
|
+
let selectedOrgName;
|
|
445
468
|
// Check if organization is specified in manifest
|
|
446
469
|
if (manifest.organization && userInfo) {
|
|
447
470
|
const orgFromManifest = userInfo.organizations?.find((org) => org.name === manifest.organization || org.id === manifest.organization);
|
|
@@ -454,9 +477,12 @@ async function handlePublish(options) {
|
|
|
454
477
|
`Your role: ${orgFromManifest.role}. Required: owner, admin, or maintainer`);
|
|
455
478
|
}
|
|
456
479
|
selectedOrgId = orgFromManifest.id;
|
|
480
|
+
selectedOrgName = orgFromManifest.name;
|
|
457
481
|
}
|
|
482
|
+
// Predict what the scoped package name will be
|
|
483
|
+
const scopedPackageName = predictScopedPackageName(manifest.name, userInfo?.username || config.username || 'unknown', selectedOrgName || manifest.organization);
|
|
458
484
|
console.log(` Source: ${source}`);
|
|
459
|
-
console.log(` Package: ${
|
|
485
|
+
console.log(` Package: ${scopedPackageName}@${manifest.version}`);
|
|
460
486
|
console.log(` Format: ${manifest.format} | Subtype: ${manifest.subtype}`);
|
|
461
487
|
console.log(` Description: ${manifest.description}`);
|
|
462
488
|
console.log(` Access: ${manifest.private ? 'private' : 'public'}`);
|
|
@@ -480,7 +506,7 @@ async function handlePublish(options) {
|
|
|
480
506
|
manifest.license_url = licenseInfo.url || undefined;
|
|
481
507
|
}
|
|
482
508
|
// Validate and warn about license (optional - will extract if present)
|
|
483
|
-
(0, license_extractor_1.validateLicenseInfo)(licenseInfo,
|
|
509
|
+
(0, license_extractor_1.validateLicenseInfo)(licenseInfo, scopedPackageName);
|
|
484
510
|
console.log('');
|
|
485
511
|
// Extract content snippet
|
|
486
512
|
console.log('š Extracting content snippet...');
|
|
@@ -488,7 +514,7 @@ async function handlePublish(options) {
|
|
|
488
514
|
if (snippet) {
|
|
489
515
|
manifest.snippet = snippet;
|
|
490
516
|
}
|
|
491
|
-
(0, snippet_extractor_1.validateSnippet)(snippet,
|
|
517
|
+
(0, snippet_extractor_1.validateSnippet)(snippet, scopedPackageName);
|
|
492
518
|
console.log('');
|
|
493
519
|
// Create tarball
|
|
494
520
|
console.log('š¦ Creating package tarball...');
|
|
@@ -509,7 +535,7 @@ async function handlePublish(options) {
|
|
|
509
535
|
if (options.dryRun) {
|
|
510
536
|
console.log('ā
Dry run successful! Package is ready to publish.');
|
|
511
537
|
publishedPackages.push({
|
|
512
|
-
name:
|
|
538
|
+
name: scopedPackageName,
|
|
513
539
|
version: manifest.version,
|
|
514
540
|
url: ''
|
|
515
541
|
});
|
|
@@ -537,7 +563,8 @@ async function handlePublish(options) {
|
|
|
537
563
|
// Default to registry URL for unknown environments
|
|
538
564
|
webappUrl = registryUrl;
|
|
539
565
|
}
|
|
540
|
-
|
|
566
|
+
// Use the name returned from the API (which includes auto-prefixed scope)
|
|
567
|
+
const packageUrl = `${webappUrl}/packages/${encodeURIComponent(result.name)}`;
|
|
541
568
|
console.log('');
|
|
542
569
|
console.log('ā
Package published successfully!');
|
|
543
570
|
console.log('');
|
|
@@ -545,16 +572,20 @@ async function handlePublish(options) {
|
|
|
545
572
|
console.log(` Install: prpm install ${result.name}`);
|
|
546
573
|
console.log('');
|
|
547
574
|
publishedPackages.push({
|
|
548
|
-
name:
|
|
575
|
+
name: result.name, // Use scoped name from server
|
|
549
576
|
version: result.version,
|
|
550
577
|
url: packageUrl
|
|
551
578
|
});
|
|
552
579
|
}
|
|
553
580
|
catch (err) {
|
|
554
581
|
const pkgError = err instanceof Error ? err.message : String(err);
|
|
555
|
-
|
|
582
|
+
// Try to use scoped name if we have user info, otherwise fall back to manifest name
|
|
583
|
+
const displayName = userInfo
|
|
584
|
+
? predictScopedPackageName(manifest.name, userInfo.username, manifest.organization)
|
|
585
|
+
: manifest.name;
|
|
586
|
+
console.error(`\nā Failed to publish ${displayName}: ${pkgError}\n`);
|
|
556
587
|
failedPackages.push({
|
|
557
|
-
name:
|
|
588
|
+
name: displayName,
|
|
558
589
|
error: pkgError
|
|
559
590
|
});
|
|
560
591
|
}
|
package/dist/commands/search.js
CHANGED
|
@@ -57,6 +57,7 @@ function getPackageIcon(format, subtype) {
|
|
|
57
57
|
'collection': 'š¦',
|
|
58
58
|
'chatmode': 'š¬',
|
|
59
59
|
'tool': 'š§',
|
|
60
|
+
'hook': 'šŖ',
|
|
60
61
|
};
|
|
61
62
|
// Format-specific icons for rules/defaults
|
|
62
63
|
const formatIcons = {
|
|
@@ -96,6 +97,7 @@ function getPackageLabel(format, subtype) {
|
|
|
96
97
|
'collection': 'Collection',
|
|
97
98
|
'chatmode': 'Chat Mode',
|
|
98
99
|
'tool': 'Tool',
|
|
100
|
+
'hook': 'Hook',
|
|
99
101
|
};
|
|
100
102
|
const formatLabel = formatLabels[format];
|
|
101
103
|
const subtypeLabel = subtypeLabels[subtype];
|
|
@@ -404,7 +406,7 @@ function createSearchCommand() {
|
|
|
404
406
|
.description('Search for packages in the registry')
|
|
405
407
|
.argument('[query]', 'Search query (optional when using --format/--subtype or --author)')
|
|
406
408
|
.option('--format <format>', 'Filter by package format (cursor, claude, continue, windsurf, copilot, kiro, agents.md, generic, mcp)')
|
|
407
|
-
.option('--subtype <subtype>', 'Filter by package subtype (rule, agent, skill, slash-command, prompt, workflow, tool, template, collection)')
|
|
409
|
+
.option('--subtype <subtype>', 'Filter by package subtype (rule, agent, skill, slash-command, prompt, workflow, tool, template, collection, chatmode, hook)')
|
|
408
410
|
.option('--author <username>', 'Filter by author username')
|
|
409
411
|
.option('--language <language>', 'Filter by programming language (javascript, typescript, python, etc.)')
|
|
410
412
|
.option('--framework <framework>', 'Filter by framework (react, nextjs, django, etc.)')
|
package/dist/core/filesystem.js
CHANGED
|
@@ -54,8 +54,20 @@ function getDestinationDir(format, subtype, name) {
|
|
|
54
54
|
case 'windsurf':
|
|
55
55
|
return '.windsurf/rules';
|
|
56
56
|
case 'copilot':
|
|
57
|
+
// Copilot has different locations based on subtype:
|
|
58
|
+
// - Repository-wide instructions: .github/copilot-instructions.md
|
|
59
|
+
// - Path-specific instructions: .github/instructions/*.instructions.md
|
|
60
|
+
// - Chat modes: .github/chatmodes/*.chatmode.md
|
|
61
|
+
if (subtype === 'chatmode')
|
|
62
|
+
return '.github/chatmodes';
|
|
63
|
+
// Default to path-specific instructions directory
|
|
57
64
|
return '.github/instructions';
|
|
58
65
|
case 'kiro':
|
|
66
|
+
// Kiro has different locations based on subtype:
|
|
67
|
+
// - Steering files: .kiro/steering/*.md
|
|
68
|
+
// - Hooks: .kiro/hooks/*.kiro.hook (JSON files)
|
|
69
|
+
if (subtype === 'hook')
|
|
70
|
+
return '.kiro/hooks';
|
|
59
71
|
return '.kiro/steering';
|
|
60
72
|
case 'agents.md':
|
|
61
73
|
return '.agents';
|
package/dist/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prpm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
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.3.
|
|
49
|
-
"@pr-pm/types": "^0.2.
|
|
48
|
+
"@pr-pm/registry-client": "^1.3.8",
|
|
49
|
+
"@pr-pm/types": "^0.2.9",
|
|
50
50
|
"ajv": "^8.17.1",
|
|
51
51
|
"ajv-formats": "^3.0.1",
|
|
52
52
|
"commander": "^11.1.0",
|