prpm 0.1.15 ā 0.1.16
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/collections.js +10 -0
- package/dist/commands/install.js +80 -2
- package/dist/commands/publish.js +21 -0
- package/dist/commands/search.js +1 -1
- package/dist/commands/uninstall.js +40 -1
- package/dist/core/filesystem.js +3 -0
- package/dist/core/lockfile.js +1 -0
- package/dist/utils/script-executor.js +72 -0
- package/package.json +3 -3
- package/schemas/prpm-manifest.schema.json +23 -0
|
@@ -507,6 +507,7 @@ async function handleCollectionInstall(collectionSpec, options) {
|
|
|
507
507
|
}
|
|
508
508
|
// Install packages sequentially
|
|
509
509
|
const installedPackageIds = [];
|
|
510
|
+
let hasClaudeHooks = false;
|
|
510
511
|
for (let i = 0; i < packages.length; i++) {
|
|
511
512
|
const pkg = packages[i];
|
|
512
513
|
const progress = `${i + 1}/${packages.length}`;
|
|
@@ -528,6 +529,10 @@ async function handleCollectionInstall(collectionSpec, options) {
|
|
|
528
529
|
if (options.format) {
|
|
529
530
|
installOptions.as = options.format;
|
|
530
531
|
}
|
|
532
|
+
// Track if this collection contains Claude hooks
|
|
533
|
+
if (pkg.format === 'claude' && pkg.subtype === 'hook') {
|
|
534
|
+
hasClaudeHooks = true;
|
|
535
|
+
}
|
|
531
536
|
await (0, install_1.handleInstall)(`${pkg.packageId}@${pkg.version}`, installOptions);
|
|
532
537
|
console.log(` ${progress} ā ${pkg.packageId}`);
|
|
533
538
|
installedPackageIds.push(pkg.packageId);
|
|
@@ -558,6 +563,11 @@ async function handleCollectionInstall(collectionSpec, options) {
|
|
|
558
563
|
console.log(` ${packagesFailed} optional packages failed`);
|
|
559
564
|
}
|
|
560
565
|
console.log(` š Collection tracked in lock file`);
|
|
566
|
+
// Show Claude hooks warning if any were installed
|
|
567
|
+
if (hasClaudeHooks) {
|
|
568
|
+
console.log(`\nā ļø This collection includes Claude hooks that execute automatically.`);
|
|
569
|
+
console.log(` š Review hook configurations in .claude/settings.json`);
|
|
570
|
+
}
|
|
561
571
|
console.log('');
|
|
562
572
|
await telemetry_1.telemetry.track({
|
|
563
573
|
command: 'collections:install',
|
package/dist/commands/install.js
CHANGED
|
@@ -224,6 +224,20 @@ async function handleInstall(packageSpec, options) {
|
|
|
224
224
|
console.log(` ${pkg.name} ${pkg.official ? 'š
' : ''}`);
|
|
225
225
|
console.log(` ${pkg.description || 'No description'}`);
|
|
226
226
|
console.log(` ${typeIcon} Type: ${typeLabel}`);
|
|
227
|
+
// Check if this is a Claude hook and show informational message
|
|
228
|
+
if (pkg.format === 'claude' && pkg.subtype === 'hook') {
|
|
229
|
+
// Only show detailed warning if not part of a collection (to avoid spam)
|
|
230
|
+
if (!options.fromCollection) {
|
|
231
|
+
console.log(`\nš Installing Claude Hook`);
|
|
232
|
+
console.log(` ā ļø Note: Hooks execute shell commands automatically.`);
|
|
233
|
+
console.log(` š Review the hook configuration in .claude/settings.json after installation.`);
|
|
234
|
+
console.log();
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
// Brief message for collection installs
|
|
238
|
+
console.log(` šŖ Hook (merges into .claude/settings.json)`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
227
241
|
// Determine format preference with priority order:
|
|
228
242
|
// 1. CLI --as flag (highest priority)
|
|
229
243
|
// 2. defaultFormat from .prpmrc config
|
|
@@ -252,7 +266,7 @@ async function handleInstall(packageSpec, options) {
|
|
|
252
266
|
// Special handling for Claude packages: default to CLAUDE.md if it doesn't exist
|
|
253
267
|
// BUT only for packages that are generic rules (not skills, agents, or commands)
|
|
254
268
|
if (!options.as && pkg.format === 'claude' && pkg.subtype === 'rule') {
|
|
255
|
-
const { fileExists } = await Promise.resolve().then(() => __importStar(require('../core/filesystem
|
|
269
|
+
const { fileExists } = await Promise.resolve().then(() => __importStar(require('../core/filesystem')));
|
|
256
270
|
const claudeMdExists = await fileExists('CLAUDE.md');
|
|
257
271
|
if (!claudeMdExists) {
|
|
258
272
|
// CLAUDE.md doesn't exist, install as CLAUDE.md (recommended format for Claude Code)
|
|
@@ -270,16 +284,19 @@ async function handleInstall(packageSpec, options) {
|
|
|
270
284
|
}
|
|
271
285
|
// Determine version to install
|
|
272
286
|
let tarballUrl;
|
|
287
|
+
let actualVersion;
|
|
273
288
|
if (version === 'latest') {
|
|
274
289
|
if (!pkg.latest_version) {
|
|
275
290
|
throw new Error('No versions available for this package');
|
|
276
291
|
}
|
|
277
292
|
tarballUrl = pkg.latest_version.tarball_url;
|
|
293
|
+
actualVersion = pkg.latest_version.version;
|
|
278
294
|
console.log(` š¦ Installing version ${pkg.latest_version.version}`);
|
|
279
295
|
}
|
|
280
296
|
else {
|
|
281
297
|
const versionInfo = await client.getPackageVersion(packageId, version);
|
|
282
298
|
tarballUrl = versionInfo.tarball_url;
|
|
299
|
+
actualVersion = version;
|
|
283
300
|
console.log(` š¦ Installing version ${version}`);
|
|
284
301
|
}
|
|
285
302
|
// Download package in requested format
|
|
@@ -295,6 +312,7 @@ async function handleInstall(packageSpec, options) {
|
|
|
295
312
|
// Track where files were saved for user feedback
|
|
296
313
|
let destPath;
|
|
297
314
|
let fileCount = 0;
|
|
315
|
+
let hookMetadata = undefined;
|
|
298
316
|
// Special handling for CLAUDE.md format (goes in project root)
|
|
299
317
|
if (format === 'claude-md') {
|
|
300
318
|
if (extractedFiles.length !== 1) {
|
|
@@ -321,6 +339,10 @@ async function handleInstall(packageSpec, options) {
|
|
|
321
339
|
if (effectiveFormat === 'claude' && effectiveSubtype === 'skill') {
|
|
322
340
|
destPath = `${destDir}/SKILL.md`;
|
|
323
341
|
}
|
|
342
|
+
else if (effectiveFormat === 'claude' && effectiveSubtype === 'hook') {
|
|
343
|
+
// Claude hooks are merged into settings.json
|
|
344
|
+
destPath = `${destDir}/settings.json`;
|
|
345
|
+
}
|
|
324
346
|
else if (effectiveFormat === 'agents.md') {
|
|
325
347
|
destPath = `${destDir}/${packageName}/AGENTS.md`;
|
|
326
348
|
}
|
|
@@ -361,6 +383,62 @@ async function handleInstall(packageSpec, options) {
|
|
|
361
383
|
mainFile = (0, claude_config_1.applyClaudeConfig)(mainFile, config.claude);
|
|
362
384
|
}
|
|
363
385
|
}
|
|
386
|
+
// Special handling for Claude hooks - merge into settings.json
|
|
387
|
+
if (effectiveFormat === 'claude' && effectiveSubtype === 'hook') {
|
|
388
|
+
const { readFile } = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
389
|
+
const { fileExists } = await Promise.resolve().then(() => __importStar(require('../core/filesystem')));
|
|
390
|
+
// Parse the hook configuration from the downloaded file
|
|
391
|
+
let hookConfig;
|
|
392
|
+
try {
|
|
393
|
+
hookConfig = JSON.parse(mainFile);
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
throw new Error(`Invalid hook configuration: ${err}. Hook file must be valid JSON.`);
|
|
397
|
+
}
|
|
398
|
+
// Generate unique hook ID for this installation
|
|
399
|
+
const hookId = `${packageId}@${actualVersion || version}`;
|
|
400
|
+
// Read existing settings.json if it exists
|
|
401
|
+
let existingSettings = { hooks: {} };
|
|
402
|
+
if (await fileExists(destPath)) {
|
|
403
|
+
try {
|
|
404
|
+
const existingContent = await readFile(destPath, 'utf-8');
|
|
405
|
+
existingSettings = JSON.parse(existingContent);
|
|
406
|
+
if (!existingSettings.hooks) {
|
|
407
|
+
existingSettings.hooks = {};
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch (err) {
|
|
411
|
+
console.log(` ā ļø Warning: Could not parse existing settings.json, creating new one.`);
|
|
412
|
+
existingSettings = { hooks: {} };
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// Track which events this hook adds to
|
|
416
|
+
const events = [];
|
|
417
|
+
// Merge the new hook configuration
|
|
418
|
+
// Assume the downloaded file contains a hooks object
|
|
419
|
+
if (hookConfig.hooks) {
|
|
420
|
+
for (const [event, eventHooks] of Object.entries(hookConfig.hooks)) {
|
|
421
|
+
if (!existingSettings.hooks[event]) {
|
|
422
|
+
existingSettings.hooks[event] = [];
|
|
423
|
+
}
|
|
424
|
+
// Add hook ID to each hook config for tracking
|
|
425
|
+
const hooksWithId = eventHooks.map(hook => ({
|
|
426
|
+
...hook,
|
|
427
|
+
__prpm_hook_id: hookId, // Internal tracking ID
|
|
428
|
+
}));
|
|
429
|
+
// Add new hooks to the event
|
|
430
|
+
existingSettings.hooks[event] = [
|
|
431
|
+
...existingSettings.hooks[event],
|
|
432
|
+
...hooksWithId
|
|
433
|
+
];
|
|
434
|
+
events.push(event);
|
|
435
|
+
}
|
|
436
|
+
console.log(` ā Merged hook configuration into settings.json`);
|
|
437
|
+
// Store metadata for lockfile
|
|
438
|
+
hookMetadata = { events, hookId };
|
|
439
|
+
}
|
|
440
|
+
mainFile = JSON.stringify(existingSettings, null, 2);
|
|
441
|
+
}
|
|
364
442
|
await (0, filesystem_1.saveFile)(destPath, mainFile);
|
|
365
443
|
fileCount = 1;
|
|
366
444
|
}
|
|
@@ -477,7 +555,6 @@ async function handleInstall(packageSpec, options) {
|
|
|
477
555
|
}
|
|
478
556
|
// Update or create lock file
|
|
479
557
|
const updatedLockfile = lockfile || (0, lockfile_1.createLockfile)();
|
|
480
|
-
const actualVersion = version === 'latest' ? pkg.latest_version?.version : version;
|
|
481
558
|
(0, lockfile_1.addToLockfile)(updatedLockfile, packageId, {
|
|
482
559
|
version: actualVersion || version,
|
|
483
560
|
tarballUrl,
|
|
@@ -485,6 +562,7 @@ async function handleInstall(packageSpec, options) {
|
|
|
485
562
|
subtype: pkg.subtype, // Preserve original package subtype
|
|
486
563
|
installedPath: destPath,
|
|
487
564
|
fromCollection: options.fromCollection,
|
|
565
|
+
hookMetadata, // Track hook installation metadata for uninstall
|
|
488
566
|
});
|
|
489
567
|
(0, lockfile_1.setPackageIntegrity)(updatedLockfile, packageId, tarball);
|
|
490
568
|
await (0, lockfile_1.writeLockfile)(updatedLockfile);
|
package/dist/commands/publish.js
CHANGED
|
@@ -52,6 +52,7 @@ const marketplace_converter_1 = require("../core/marketplace-converter");
|
|
|
52
52
|
const schema_validator_1 = require("../core/schema-validator");
|
|
53
53
|
const license_extractor_1 = require("../utils/license-extractor");
|
|
54
54
|
const snippet_extractor_1 = require("../utils/snippet-extractor");
|
|
55
|
+
const script_executor_1 = require("../utils/script-executor");
|
|
55
56
|
/**
|
|
56
57
|
* Try to find and load manifest files
|
|
57
58
|
* Checks for:
|
|
@@ -357,6 +358,26 @@ async function handlePublish(options) {
|
|
|
357
358
|
// Read and validate manifests
|
|
358
359
|
console.log('š Validating package manifest(s)...');
|
|
359
360
|
const { manifests, collections, source } = await findAndLoadManifests();
|
|
361
|
+
// Execute prepublishOnly script if defined (for multi-package manifests)
|
|
362
|
+
// This runs before any packages are published
|
|
363
|
+
if (source === 'prpm.json (multi-package)' || source === 'prpm.json') {
|
|
364
|
+
try {
|
|
365
|
+
// Re-read the raw prpm.json to check for scripts
|
|
366
|
+
const prpmJsonPath = (0, path_1.join)(process.cwd(), 'prpm.json');
|
|
367
|
+
const prpmContent = await (0, promises_1.readFile)(prpmJsonPath, 'utf-8');
|
|
368
|
+
const prpmManifest = JSON.parse(prpmContent);
|
|
369
|
+
if (prpmManifest.scripts) {
|
|
370
|
+
await (0, script_executor_1.executePrepublishOnly)(prpmManifest.scripts);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
// If script execution fails, abort publish
|
|
375
|
+
if (error instanceof Error && error.message.includes('script')) {
|
|
376
|
+
throw error;
|
|
377
|
+
}
|
|
378
|
+
// Ignore other errors (e.g., file not found - shouldn't happen at this point)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
360
381
|
if (manifests.length > 1 || collections.length > 0) {
|
|
361
382
|
if (manifests.length > 0) {
|
|
362
383
|
console.log(` Found ${manifests.length} package(s) in ${source}`);
|
package/dist/commands/search.js
CHANGED
|
@@ -422,7 +422,7 @@ function createSearchCommand() {
|
|
|
422
422
|
const limit = options.limit ? parseInt(options.limit, 10) : 20;
|
|
423
423
|
const page = options.page ? parseInt(options.page, 10) : 1;
|
|
424
424
|
const validFormats = ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'agents.md', 'generic', 'mcp'];
|
|
425
|
-
const validSubtypes = ['rule', 'agent', 'skill', 'slash-command', 'prompt', 'collection', 'chatmode'];
|
|
425
|
+
const validSubtypes = ['rule', 'agent', 'skill', 'slash-command', 'prompt', 'collection', 'chatmode', 'tool', 'hook'];
|
|
426
426
|
if (options.format && !validFormats.includes(format)) {
|
|
427
427
|
console.error(`ā Format must be one of: ${validFormats.join(', ')}`);
|
|
428
428
|
throw new errors_1.CLIError(`ā Format must be one of: ${validFormats.join(', ')}`, 1);
|
|
@@ -21,7 +21,46 @@ async function handleUninstall(name) {
|
|
|
21
21
|
if (!pkg) {
|
|
22
22
|
throw new errors_1.CLIError(`ā Package "${name}" not found`, 1);
|
|
23
23
|
}
|
|
24
|
-
//
|
|
24
|
+
// Special handling for Claude hooks
|
|
25
|
+
if (pkg.format === 'claude' && pkg.subtype === 'hook' && pkg.hookMetadata) {
|
|
26
|
+
const settingsPath = pkg.installedPath || '.claude/settings.json';
|
|
27
|
+
try {
|
|
28
|
+
// Read settings.json
|
|
29
|
+
const settingsContent = await fs_1.promises.readFile(settingsPath, 'utf-8');
|
|
30
|
+
const settings = JSON.parse(settingsContent);
|
|
31
|
+
if (settings.hooks) {
|
|
32
|
+
let removedCount = 0;
|
|
33
|
+
// Remove hooks with matching hook ID from each event
|
|
34
|
+
for (const event of pkg.hookMetadata.events) {
|
|
35
|
+
if (settings.hooks[event]) {
|
|
36
|
+
const originalLength = settings.hooks[event].length;
|
|
37
|
+
settings.hooks[event] = settings.hooks[event].filter((hook) => hook.__prpm_hook_id !== pkg.hookMetadata.hookId);
|
|
38
|
+
const newLength = settings.hooks[event].length;
|
|
39
|
+
removedCount += originalLength - newLength;
|
|
40
|
+
// Clean up empty event arrays
|
|
41
|
+
if (settings.hooks[event].length === 0) {
|
|
42
|
+
delete settings.hooks[event];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Write updated settings back
|
|
47
|
+
await fs_1.promises.writeFile(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
48
|
+
console.log(` šŖ Removed ${removedCount} hook(s) from ${settingsPath}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const err = error;
|
|
53
|
+
if (err.code === 'ENOENT') {
|
|
54
|
+
console.warn(` ā ļø Settings file not found: ${settingsPath}`);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw new Error(`Failed to remove hooks from settings: ${error}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
console.log(`ā
Successfully uninstalled ${name}`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Standard file/directory uninstall for non-hook packages
|
|
25
64
|
const packageName = (0, filesystem_1.stripAuthorNamespace)(name);
|
|
26
65
|
let targetPath;
|
|
27
66
|
if (pkg.installedPath) {
|
package/dist/core/filesystem.js
CHANGED
|
@@ -45,6 +45,9 @@ function getDestinationDir(format, subtype, name) {
|
|
|
45
45
|
return '.claude/commands';
|
|
46
46
|
if (subtype === 'agent')
|
|
47
47
|
return '.claude/agents';
|
|
48
|
+
// Hooks are configured in settings.json, return .claude directory
|
|
49
|
+
if (subtype === 'hook')
|
|
50
|
+
return '.claude';
|
|
48
51
|
return '.claude/agents'; // Default for claude
|
|
49
52
|
case 'continue':
|
|
50
53
|
// Continue has separate directories for prompts (slash commands) and rules
|
package/dist/core/lockfile.js
CHANGED
|
@@ -80,6 +80,7 @@ function addToLockfile(lockfile, packageId, packageInfo) {
|
|
|
80
80
|
subtype: packageInfo.subtype,
|
|
81
81
|
installedPath: packageInfo.installedPath,
|
|
82
82
|
fromCollection: packageInfo.fromCollection,
|
|
83
|
+
hookMetadata: packageInfo.hookMetadata,
|
|
83
84
|
};
|
|
84
85
|
lockfile.generated = new Date().toISOString();
|
|
85
86
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility for executing package lifecycle scripts
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.executeScript = executeScript;
|
|
7
|
+
exports.executePrepublishOnly = executePrepublishOnly;
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const util_1 = require("util");
|
|
10
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
11
|
+
/**
|
|
12
|
+
* Execute a package lifecycle script
|
|
13
|
+
* @param script - The script command to execute
|
|
14
|
+
* @param scriptName - Name of the script (for logging)
|
|
15
|
+
* @param options - Execution options
|
|
16
|
+
* @throws Error if script fails
|
|
17
|
+
*/
|
|
18
|
+
async function executeScript(script, scriptName, options = {}) {
|
|
19
|
+
const { cwd = process.cwd(), timeout = 5 * 60 * 1000, // 5 minutes default
|
|
20
|
+
maxBuffer = 10 * 1024 * 1024, // 10MB default
|
|
21
|
+
} = options;
|
|
22
|
+
console.log(`š§ Running ${scriptName} script...`);
|
|
23
|
+
console.log(` $ ${script}\n`);
|
|
24
|
+
try {
|
|
25
|
+
const { stdout, stderr } = await execAsync(script, {
|
|
26
|
+
cwd,
|
|
27
|
+
timeout,
|
|
28
|
+
maxBuffer,
|
|
29
|
+
// Inherit environment variables
|
|
30
|
+
env: process.env,
|
|
31
|
+
});
|
|
32
|
+
// Show output in real-time
|
|
33
|
+
if (stdout) {
|
|
34
|
+
process.stdout.write(stdout);
|
|
35
|
+
}
|
|
36
|
+
if (stderr) {
|
|
37
|
+
process.stderr.write(stderr);
|
|
38
|
+
}
|
|
39
|
+
console.log(`\nā ${scriptName} script completed successfully\n`);
|
|
40
|
+
return {
|
|
41
|
+
stdout,
|
|
42
|
+
stderr,
|
|
43
|
+
exitCode: 0,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
// Show error output
|
|
48
|
+
if (error.stdout) {
|
|
49
|
+
process.stdout.write(error.stdout);
|
|
50
|
+
}
|
|
51
|
+
if (error.stderr) {
|
|
52
|
+
process.stderr.write(error.stderr);
|
|
53
|
+
}
|
|
54
|
+
const errorMessage = error.code === 'ETIMEDOUT'
|
|
55
|
+
? `${scriptName} script timed out after ${timeout}ms`
|
|
56
|
+
: error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER'
|
|
57
|
+
? `${scriptName} script output exceeded maximum buffer size`
|
|
58
|
+
: `${scriptName} script failed with exit code ${error.code || 1}`;
|
|
59
|
+
throw new Error(errorMessage);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Execute prepublishOnly script if defined
|
|
64
|
+
* @param scripts - Package scripts object
|
|
65
|
+
* @param options - Execution options
|
|
66
|
+
*/
|
|
67
|
+
async function executePrepublishOnly(scripts, options = {}) {
|
|
68
|
+
if (!scripts?.prepublishOnly) {
|
|
69
|
+
return; // No script defined, nothing to do
|
|
70
|
+
}
|
|
71
|
+
await executeScript(scripts.prepublishOnly, 'prepublishOnly', options);
|
|
72
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prpm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
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.14",
|
|
49
|
+
"@pr-pm/types": "^0.2.15",
|
|
50
50
|
"ajv": "^8.17.1",
|
|
51
51
|
"ajv-formats": "^3.0.1",
|
|
52
52
|
"commander": "^11.1.0",
|
|
@@ -153,6 +153,29 @@
|
|
|
153
153
|
"description": "Whether the package is private. Private packages are only accessible to the owner/organization members. Defaults to false (public).",
|
|
154
154
|
"default": false
|
|
155
155
|
},
|
|
156
|
+
"scripts": {
|
|
157
|
+
"type": "object",
|
|
158
|
+
"description": "Lifecycle scripts that run during package operations. Only applies to multi-package manifests (prpm.json with packages array).",
|
|
159
|
+
"properties": {
|
|
160
|
+
"prepublishOnly": {
|
|
161
|
+
"type": "string",
|
|
162
|
+
"description": "Script to run before publishing (recommended - only runs on 'prpm publish')",
|
|
163
|
+
"examples": [
|
|
164
|
+
"npm run build",
|
|
165
|
+
"cd packages/hooks && npm run build",
|
|
166
|
+
"npm test && npm run build"
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
"prepublish": {
|
|
170
|
+
"type": "string",
|
|
171
|
+
"description": "Script to run before publishing and on npm install (not recommended - use prepublishOnly instead)",
|
|
172
|
+
"examples": [
|
|
173
|
+
"npm run build"
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"additionalProperties": false
|
|
178
|
+
},
|
|
156
179
|
"tags": {
|
|
157
180
|
"type": "array",
|
|
158
181
|
"description": "Package tags for categorization",
|