prpm 0.1.9 → 0.1.11

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.
@@ -141,6 +141,7 @@ function setupGlobalMocks() {
141
141
  }
142
142
  /**
143
143
  * Mock process.exit to throw instead of exiting
144
+ * @deprecated No longer needed - commands now throw CLIError instead of calling process.exit
144
145
  */
145
146
  function mockProcessExit() {
146
147
  const mockExit = jest.spyOn(process, 'exit').mockImplementation((code) => {
@@ -11,6 +11,7 @@ const telemetry_1 = require("../core/telemetry");
11
11
  const child_process_1 = require("child_process");
12
12
  const util_1 = require("util");
13
13
  const webapp_url_1 = require("../utils/webapp-url");
14
+ const errors_1 = require("../core/errors");
14
15
  const execAsync = (0, util_1.promisify)(child_process_1.exec);
15
16
  /**
16
17
  * Make authenticated API call
@@ -102,7 +103,7 @@ async function handleBuyCredits(options) {
102
103
  console.error('āŒ Authentication required');
103
104
  console.log('\nšŸ’” Please login first:');
104
105
  console.log(' prpm login');
105
- process.exit(1);
106
+ throw new errors_1.CLIError('āŒ Authentication required', 1);
106
107
  }
107
108
  // Get current balance
108
109
  console.log('šŸ” Checking current credits balance...');
@@ -122,7 +123,7 @@ async function handleBuyCredits(options) {
122
123
  if (!validPackages.includes(options.package)) {
123
124
  console.error(`\nāŒ Invalid package: ${options.package}`);
124
125
  console.log(' Valid options: small, medium, large');
125
- process.exit(1);
126
+ throw new errors_1.CLIError(`\nāŒ Invalid package: ${options.package}`, 1);
126
127
  }
127
128
  purchaseUrl += `?package=${options.package}`;
128
129
  }
@@ -154,7 +155,7 @@ async function handleBuyCredits(options) {
154
155
  catch (err) {
155
156
  error = err instanceof Error ? err.message : String(err);
156
157
  console.error(`\nāŒ Purchase failed: ${error}`);
157
- process.exit(1);
158
+ throw new errors_1.CLIError(`\nāŒ Purchase failed: ${error}`, 1);
158
159
  }
159
160
  finally {
160
161
  await telemetry_1.telemetry.track({
@@ -218,7 +219,6 @@ Note: Purchased credits are one-time and never expire, unlike monthly credits.
218
219
  `)
219
220
  .action(async (options) => {
220
221
  await handleBuyCredits(options);
221
- process.exit(0);
222
222
  });
223
223
  return command;
224
224
  }
@@ -10,6 +10,7 @@ const promises_1 = require("fs/promises");
10
10
  const path_1 = require("path");
11
11
  const telemetry_1 = require("../core/telemetry");
12
12
  const lockfile_1 = require("../core/lockfile");
13
+ const errors_1 = require("../core/errors");
13
14
  /**
14
15
  * Detect format and subtype from file path and content
15
16
  */
@@ -333,7 +334,7 @@ async function handleCatalog(directories, options) {
333
334
  catch (err) {
334
335
  error = err instanceof Error ? err.message : String(err);
335
336
  console.error(`\nāŒ Failed to catalog packages: ${error}\n`);
336
- process.exit(1);
337
+ throw new errors_1.CLIError(`\nāŒ Failed to catalog packages: ${error}`, 1);
337
338
  }
338
339
  finally {
339
340
  await telemetry_1.telemetry.track({
@@ -360,6 +361,5 @@ function createCatalogCommand() {
360
361
  .option('--dry-run', 'Show what would be cataloged without making changes')
361
362
  .action(async (directories, options) => {
362
363
  await handleCatalog(directories, options);
363
- process.exit(0);
364
364
  });
365
365
  }
@@ -48,6 +48,7 @@ const user_config_1 = require("../core/user-config");
48
48
  const install_1 = require("./install");
49
49
  const telemetry_1 = require("../core/telemetry");
50
50
  const lockfile_1 = require("../core/lockfile");
51
+ const errors_1 = require("../core/errors");
51
52
  /**
52
53
  * Search collections by query
53
54
  */
@@ -130,7 +131,7 @@ async function handleCollectionsSearch(query, options) {
130
131
  error: errorMessage,
131
132
  duration: Date.now() - startTime,
132
133
  });
133
- process.exit(1);
134
+ throw new errors_1.CLIError(`\nāŒ Failed to search collections: ${errorMessage}`, 1);
134
135
  }
135
136
  finally {
136
137
  await telemetry_1.telemetry.shutdown();
@@ -218,7 +219,7 @@ async function handleCollectionsList(options) {
218
219
  error: errorMessage,
219
220
  duration: Date.now() - startTime,
220
221
  });
221
- process.exit(1);
222
+ throw new errors_1.CLIError(`\nāŒ Failed to list collections: ${errorMessage}`, 1);
222
223
  }
223
224
  finally {
224
225
  await telemetry_1.telemetry.shutdown();
@@ -338,7 +339,7 @@ async function handleCollectionInfo(collectionSpec) {
338
339
  error: errorMessage,
339
340
  duration: Date.now() - startTime,
340
341
  });
341
- process.exit(1);
342
+ throw new errors_1.CLIError(`\nāŒ Failed to get collection info: ${errorMessage}`, 1);
342
343
  }
343
344
  finally {
344
345
  await telemetry_1.telemetry.shutdown();
@@ -355,7 +356,7 @@ async function handleCollectionPublish(manifestPath = './collection.json') {
355
356
  // Check authentication
356
357
  if (!config.token) {
357
358
  console.error('\nāŒ Authentication required. Run `prpm login` first.\n');
358
- process.exit(1);
359
+ throw new errors_1.CLIError('\nāŒ Authentication required. Run `prpm login` first.', 1);
359
360
  }
360
361
  console.log('šŸ“¦ Publishing collection...\n');
361
362
  // Read collection manifest
@@ -438,7 +439,7 @@ async function handleCollectionPublish(manifestPath = './collection.json') {
438
439
  error: errorMessage,
439
440
  duration: Date.now() - startTime,
440
441
  });
441
- process.exit(1);
442
+ throw new errors_1.CLIError(`\nāŒ Failed to publish collection: ${errorMessage}`, 1);
442
443
  }
443
444
  finally {
444
445
  await telemetry_1.telemetry.shutdown();
@@ -501,14 +502,21 @@ async function handleCollectionInstall(collectionSpec, options) {
501
502
  const progress = `${i + 1}/${packages.length}`;
502
503
  try {
503
504
  console.log(`\n ${progress} Installing ${pkg.packageId}@${pkg.version}...`);
504
- await (0, install_1.handleInstall)(`${pkg.packageId}@${pkg.version}`, {
505
- as: pkg.format,
505
+ // Only pass 'as' format if user explicitly requested it via --as flag
506
+ // Otherwise, let handleInstall auto-detect the format based on existing directories
507
+ // This ensures collections respect existing .claude or .cursor directories
508
+ const installOptions = {
506
509
  fromCollection: {
507
510
  scope,
508
511
  name_slug,
509
512
  version: collection.version || version || '1.0.0',
510
513
  },
511
- });
514
+ };
515
+ // Only set 'as' if user explicitly provided a format
516
+ if (options.format) {
517
+ installOptions.as = options.format;
518
+ }
519
+ await (0, install_1.handleInstall)(`${pkg.packageId}@${pkg.version}`, installOptions);
512
520
  console.log(` ${progress} āœ“ ${pkg.packageId}`);
513
521
  installedPackageIds.push(pkg.packageId);
514
522
  packagesInstalled++;
@@ -566,7 +574,7 @@ async function handleCollectionInstall(collectionSpec, options) {
566
574
  failed: packagesFailed,
567
575
  },
568
576
  });
569
- process.exit(1);
577
+ throw new errors_1.CLIError(`\nāŒ Failed to install collection: ${errorMessage}`, 1);
570
578
  }
571
579
  finally {
572
580
  await telemetry_1.telemetry.shutdown();
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createConfigCommand = createConfigCommand;
7
7
  const commander_1 = require("commander");
8
8
  const user_config_1 = require("../core/user-config");
9
+ const errors_1 = require("../core/errors");
9
10
  /**
10
11
  * Get a config value
11
12
  */
@@ -14,15 +15,15 @@ async function handleConfigGet(key) {
14
15
  const config = await (0, user_config_1.getConfig)();
15
16
  const value = config[key];
16
17
  if (value === undefined) {
17
- console.error(`āŒ Config key "${key}" not found`);
18
- console.log('\nAvailable keys: registryUrl, telemetryEnabled, token, username');
19
- process.exit(1);
18
+ throw new errors_1.CLIError(`āŒ Config key "${key}" not found\n\nAvailable keys: registryUrl, telemetryEnabled, token, username`, 1);
20
19
  }
21
20
  console.log(value);
22
21
  }
23
22
  catch (error) {
24
- console.error(`āŒ Failed to get config: ${error instanceof Error ? error.message : String(error)}`);
25
- process.exit(1);
23
+ if (error instanceof errors_1.CLIError) {
24
+ throw error;
25
+ }
26
+ throw new errors_1.CLIError(`āŒ Failed to get config: ${error instanceof Error ? error.message : String(error)}`, 1);
26
27
  }
27
28
  }
28
29
  /**
@@ -34,10 +35,7 @@ async function handleConfigSet(key, value) {
34
35
  // Validate key
35
36
  const validKeys = ['registryUrl', 'telemetryEnabled'];
36
37
  if (!validKeys.includes(key)) {
37
- console.error(`āŒ Cannot set config key "${key}"`);
38
- console.log('\nSettable keys: registryUrl, telemetryEnabled');
39
- console.log('Note: token and username are set via "prpm login"');
40
- process.exit(1);
38
+ throw new errors_1.CLIError(`āŒ Cannot set config key "${key}"\n\nSettable keys: registryUrl, telemetryEnabled\nNote: token and username are set via "prpm login"`, 1);
41
39
  }
42
40
  // Parse boolean values
43
41
  let parsedValue = value;
@@ -49,8 +47,7 @@ async function handleConfigSet(key, value) {
49
47
  parsedValue = false;
50
48
  }
51
49
  else {
52
- console.error(`āŒ Invalid boolean value "${value}". Use: true, false, yes, no, 1, or 0`);
53
- process.exit(1);
50
+ throw new errors_1.CLIError(`āŒ Invalid boolean value "${value}". Use: true, false, yes, no, 1, or 0`, 1);
54
51
  }
55
52
  }
56
53
  // Update config
@@ -59,8 +56,10 @@ async function handleConfigSet(key, value) {
59
56
  console.log(`āœ… Set ${key} = ${parsedValue}`);
60
57
  }
61
58
  catch (error) {
62
- console.error(`āŒ Failed to set config: ${error instanceof Error ? error.message : String(error)}`);
63
- process.exit(1);
59
+ if (error instanceof errors_1.CLIError) {
60
+ throw error;
61
+ }
62
+ throw new errors_1.CLIError(`āŒ Failed to set config: ${error instanceof Error ? error.message : String(error)}`, 1);
64
63
  }
65
64
  }
66
65
  /**
@@ -81,8 +80,7 @@ async function handleConfigList() {
81
80
  console.log(`Config file: ${configPath}`);
82
81
  }
83
82
  catch (error) {
84
- console.error(`āŒ Failed to list config: ${error instanceof Error ? error.message : String(error)}`);
85
- process.exit(1);
83
+ throw new errors_1.CLIError(`āŒ Failed to list config: ${error instanceof Error ? error.message : String(error)}`, 1);
86
84
  }
87
85
  }
88
86
  /**
@@ -94,8 +92,7 @@ async function handleConfigDelete(key) {
94
92
  // Validate key
95
93
  const deletableKeys = ['registryUrl', 'telemetryEnabled', 'token', 'username'];
96
94
  if (!deletableKeys.includes(key)) {
97
- console.error(`āŒ Cannot delete config key "${key}"`);
98
- process.exit(1);
95
+ throw new errors_1.CLIError(`āŒ Cannot delete config key "${key}"`, 1);
99
96
  }
100
97
  // Reset to defaults
101
98
  if (key === 'registryUrl') {
@@ -114,8 +111,10 @@ async function handleConfigDelete(key) {
114
111
  console.log(`āœ… Reset ${key} to default value`);
115
112
  }
116
113
  catch (error) {
117
- console.error(`āŒ Failed to delete config: ${error instanceof Error ? error.message : String(error)}`);
118
- process.exit(1);
114
+ if (error instanceof errors_1.CLIError) {
115
+ throw error;
116
+ }
117
+ throw new errors_1.CLIError(`āŒ Failed to delete config: ${error instanceof Error ? error.message : String(error)}`, 1);
119
118
  }
120
119
  }
121
120
  /**
@@ -131,7 +130,6 @@ function createConfigCommand() {
131
130
  .description('List all configuration values')
132
131
  .action(async () => {
133
132
  await handleConfigList();
134
- process.exit(0);
135
133
  });
136
134
  // config get <key>
137
135
  command
@@ -139,7 +137,6 @@ function createConfigCommand() {
139
137
  .description('Get a configuration value')
140
138
  .action(async (key) => {
141
139
  await handleConfigGet(key);
142
- process.exit(0);
143
140
  });
144
141
  // config set <key> <value>
145
142
  command
@@ -147,7 +144,6 @@ function createConfigCommand() {
147
144
  .description('Set a configuration value')
148
145
  .action(async (key, value) => {
149
146
  await handleConfigSet(key, value);
150
- process.exit(0);
151
147
  });
152
148
  // config delete <key>
153
149
  command
@@ -156,12 +152,10 @@ function createConfigCommand() {
156
152
  .description('Reset a configuration value to default')
157
153
  .action(async (key) => {
158
154
  await handleConfigDelete(key);
159
- process.exit(0);
160
155
  });
161
156
  // Default action (show list if no subcommand)
162
157
  command.action(async () => {
163
158
  await handleConfigList();
164
- process.exit(0);
165
159
  });
166
160
  return command;
167
161
  }
@@ -8,6 +8,7 @@ exports.createCreditsCommand = createCreditsCommand;
8
8
  const commander_1 = require("commander");
9
9
  const user_config_1 = require("../core/user-config");
10
10
  const telemetry_1 = require("../core/telemetry");
11
+ const errors_1 = require("../core/errors");
11
12
  /**
12
13
  * Make authenticated API call
13
14
  */
@@ -112,7 +113,7 @@ async function handleCredits(options) {
112
113
  console.error('āŒ Authentication required');
113
114
  console.log('\nšŸ’” Please login first:');
114
115
  console.log(' prpm login');
115
- process.exit(1);
116
+ throw new errors_1.CLIError('āŒ Authentication required', 1);
116
117
  }
117
118
  if (options.history) {
118
119
  await showHistory(options.limit || 10);
@@ -130,7 +131,7 @@ async function handleCredits(options) {
130
131
  catch (err) {
131
132
  error = err instanceof Error ? err.message : String(err);
132
133
  console.error(`\nāŒ Failed to fetch credits: ${error}`);
133
- process.exit(1);
134
+ throw new errors_1.CLIError(`\nāŒ Failed to fetch credits: ${error}`, 1);
134
135
  }
135
136
  finally {
136
137
  await telemetry_1.telemetry.track({
@@ -180,7 +181,6 @@ Get more credits:
180
181
  history: options.history,
181
182
  limit: options.limit ? parseInt(options.limit, 10) : undefined,
182
183
  });
183
- process.exit(0);
184
184
  });
185
185
  return command;
186
186
  }
@@ -13,6 +13,7 @@ const fs_1 = require("fs");
13
13
  const path_1 = __importDefault(require("path"));
14
14
  const lockfile_1 = require("../core/lockfile");
15
15
  const filesystem_1 = require("../core/filesystem");
16
+ const errors_1 = require("../core/errors");
16
17
  /**
17
18
  * Scan directory for files and return file information
18
19
  * Recursively scans subdirectories for Claude skills/agents
@@ -167,10 +168,7 @@ async function handleIndex(options = {}) {
167
168
  }
168
169
  catch (error) {
169
170
  console.error(`āŒ Failed to index packages: ${error}`);
170
- process.exit(1);
171
- }
172
- finally {
173
- process.exit(0);
171
+ throw new errors_1.CLIError(`āŒ Failed to index packages: ${error}`, 1);
174
172
  }
175
173
  }
176
174
  /**
@@ -9,6 +9,7 @@ const commander_1 = require("commander");
9
9
  const registry_client_1 = require("@pr-pm/registry-client");
10
10
  const user_config_1 = require("../core/user-config");
11
11
  const telemetry_1 = require("../core/telemetry");
12
+ const errors_1 = require("../core/errors");
12
13
  async function handleInfo(packageName) {
13
14
  const startTime = Date.now();
14
15
  let success = false;
@@ -50,12 +51,7 @@ async function handleInfo(packageName) {
50
51
  }
51
52
  catch (err) {
52
53
  error = err instanceof Error ? err.message : String(err);
53
- console.error(`\nāŒ Failed to fetch package info: ${error}`);
54
- console.log(`\nšŸ’” Tips:`);
55
- console.log(` - Check the package ID spelling`);
56
- console.log(` - Search for packages: prpm search <query>`);
57
- console.log(` - View trending: prpm trending`);
58
- process.exit(1);
54
+ throw new errors_1.CLIError(`\nāŒ Failed to fetch package info: ${error}\n\nšŸ’” Tips:\n - Check the package ID spelling\n - Search for packages: prpm search <query>\n - View trending: prpm trending`, 1);
59
55
  }
60
56
  finally {
61
57
  await telemetry_1.telemetry.track({
@@ -77,7 +73,6 @@ function createInfoCommand() {
77
73
  .argument('<package>', 'Package ID to get information about')
78
74
  .action(async (packageId) => {
79
75
  await handleInfo(packageId);
80
- process.exit(0);
81
76
  });
82
77
  return command;
83
78
  }
@@ -45,6 +45,7 @@ const fs_1 = require("fs");
45
45
  const readline = __importStar(require("readline/promises"));
46
46
  const process_1 = require("process");
47
47
  const types_1 = require("../types");
48
+ const errors_1 = require("../core/errors");
48
49
  const FORMAT_EXAMPLES = {
49
50
  cursor: {
50
51
  description: 'Cursor AI coding rules',
@@ -648,7 +649,9 @@ async function initPackage(options) {
648
649
  // Create example files
649
650
  if (config.files && config.format && config.name) {
650
651
  console.log('Creating example files...\n');
651
- await createExampleFiles(config.format, config.files, config.name);
652
+ // Filter out README.md since createReadme will handle it with proper content
653
+ const filesToCreate = config.files.filter(f => f !== 'README.md');
654
+ await createExampleFiles(config.format, filesToCreate, config.name);
652
655
  // Create README
653
656
  await createReadme(config);
654
657
  }
@@ -671,11 +674,10 @@ function createInitCommand() {
671
674
  .action(async (options) => {
672
675
  try {
673
676
  await initPackage(options);
674
- process.exit(0);
675
677
  }
676
678
  catch (error) {
677
679
  console.error('\nāŒ Error:', error instanceof Error ? error.message : error);
678
- process.exit(1);
680
+ throw new errors_1.CLIError('\nāŒ Error: ' + (error instanceof Error ? error.message : error), 1);
679
681
  }
680
682
  });
681
683
  return command;
@@ -45,6 +45,7 @@ const user_config_1 = require("../core/user-config");
45
45
  const filesystem_1 = require("../core/filesystem");
46
46
  const telemetry_1 = require("../core/telemetry");
47
47
  const tar = __importStar(require("tar"));
48
+ const errors_1 = require("../core/errors");
48
49
  const lockfile_1 = require("../core/lockfile");
49
50
  const cursor_config_1 = require("../core/cursor-config");
50
51
  const claude_config_1 = require("../core/claude-config");
@@ -356,10 +357,14 @@ async function handleInstall(packageSpec, options) {
356
357
  const destDir = (0, filesystem_1.getDestinationDir)(effectiveFormat, effectiveSubtype, pkg.name);
357
358
  // Multi-file package - create directory for package
358
359
  // For Claude skills, destDir already includes package name, so use it directly
360
+ // For Cursor rules converted from Claude skills, use flat structure
359
361
  const packageName = (0, filesystem_1.stripAuthorNamespace)(packageId);
362
+ const isCursorConversion = (effectiveFormat === 'cursor' && pkg.format === 'claude' && pkg.subtype === 'skill');
360
363
  const packageDir = (effectiveFormat === 'claude' && effectiveSubtype === 'skill')
361
364
  ? destDir
362
- : `${destDir}/${packageName}`;
365
+ : isCursorConversion
366
+ ? destDir // Cursor uses flat structure
367
+ : `${destDir}/${packageName}`;
363
368
  destPath = packageDir;
364
369
  console.log(` šŸ“ Multi-file package - creating directory: ${packageDir}`);
365
370
  // For Claude skills, verify SKILL.md exists
@@ -385,6 +390,8 @@ async function handleInstall(packageSpec, options) {
385
390
  }
386
391
  }
387
392
  }
393
+ // Track JSON files for @reference insertion in Cursor conversion
394
+ const jsonFiles = [];
388
395
  for (const file of extractedFiles) {
389
396
  // Strip the tarball's root directory prefix to preserve subdirectories
390
397
  // Example: ".claude/skills/agent-builder/docs/examples.md" → "docs/examples.md"
@@ -404,10 +411,58 @@ async function handleInstall(packageSpec, options) {
404
411
  // Fallback: just take the filename (last part)
405
412
  relativeFileName = pathParts[pathParts.length - 1];
406
413
  }
407
- const filePath = `${packageDir}/${relativeFileName}`;
408
- await (0, filesystem_1.saveFile)(filePath, file.content);
414
+ let fileContent = file.content;
415
+ let fileName = relativeFileName;
416
+ // Handle Cursor conversion from Claude skill
417
+ if (isCursorConversion) {
418
+ // Convert SKILL.md to .mdc
419
+ if (fileName === 'SKILL.md' || fileName.endsWith('/SKILL.md')) {
420
+ fileName = `${packageName}.mdc`;
421
+ // Add MDC header if missing
422
+ if (!(0, cursor_config_1.hasMDCHeader)(fileContent)) {
423
+ console.log(` āš ļø Adding MDC header to converted skill...`);
424
+ fileContent = (0, cursor_config_1.addMDCHeader)(fileContent, pkg.description);
425
+ }
426
+ // Apply cursor config if available
427
+ if (config.cursor) {
428
+ console.log(` āš™ļø Applying cursor config...`);
429
+ fileContent = (0, cursor_config_1.applyCursorConfig)(fileContent, config.cursor);
430
+ }
431
+ }
432
+ // Track JSON files for @reference
433
+ else if (fileName.endsWith('.json')) {
434
+ // Flatten structure - remove subdirectories
435
+ const jsonFileName = fileName.split('/').pop() || fileName;
436
+ fileName = jsonFileName;
437
+ jsonFiles.push(jsonFileName);
438
+ }
439
+ // For other files (docs, etc), flatten the structure
440
+ else {
441
+ fileName = fileName.split('/').pop() || fileName;
442
+ }
443
+ }
444
+ const filePath = `${packageDir}/${fileName}`;
445
+ await (0, filesystem_1.saveFile)(filePath, fileContent);
409
446
  fileCount++;
410
447
  }
448
+ // Add @references to .mdc file for JSON files
449
+ if (isCursorConversion && jsonFiles.length > 0) {
450
+ const mdcFile = `${packageDir}/${packageName}.mdc`;
451
+ const { readFile } = await Promise.resolve().then(() => __importStar(require('fs/promises')));
452
+ let mdcContent = await readFile(mdcFile, 'utf-8');
453
+ // Find the end of frontmatter (if exists)
454
+ const frontmatterMatch = mdcContent.match(/^---\n[\s\S]*?\n---\n/);
455
+ if (frontmatterMatch) {
456
+ const frontmatterEnd = frontmatterMatch[0].length;
457
+ const beforeFrontmatter = mdcContent.slice(0, frontmatterEnd);
458
+ const afterFrontmatter = mdcContent.slice(frontmatterEnd);
459
+ // Add @references right after frontmatter
460
+ const references = jsonFiles.map(f => `@${f}`).join('\n');
461
+ mdcContent = `${beforeFrontmatter}\n${references}\n${afterFrontmatter}`;
462
+ await (0, filesystem_1.saveFile)(mdcFile, mdcContent);
463
+ console.log(` āœ“ Added ${jsonFiles.length} @reference(s) to ${packageName}.mdc`);
464
+ }
465
+ }
411
466
  }
412
467
  // Update or create lock file
413
468
  const updatedLockfile = lockfile || (0, lockfile_1.createLockfile)();
@@ -440,11 +495,7 @@ async function handleInstall(packageSpec, options) {
440
495
  }
441
496
  catch (err) {
442
497
  error = err instanceof Error ? err.message : String(err);
443
- console.error(`\nāŒ Installation failed: ${error}`);
444
- console.log(`\nšŸ’” Tips:`);
445
- console.log(` - Check package name: prpm search <query>`);
446
- console.log(` - Get package info: prpm info <package>`);
447
- process.exit(1);
498
+ throw new errors_1.CLIError(`\nāŒ Installation failed: ${error}\n\nšŸ’” Tips:\n - Check package name: prpm search <query>\n - Get package info: prpm info <package>`, 1);
448
499
  }
449
500
  finally {
450
501
  await telemetry_1.telemetry.track({
@@ -557,9 +608,7 @@ async function installFromLockfile(options) {
557
608
  // Read lockfile
558
609
  const lockfile = await (0, lockfile_1.readLockfile)();
559
610
  if (!lockfile) {
560
- console.error('āŒ No prpm.lock file found');
561
- console.log('\nšŸ’” Run "prpm install <package>" first to create a lockfile, or initialize a new project with "prpm init"');
562
- process.exit(1);
611
+ throw new errors_1.CLIError('āŒ No prpm.lock file found\n\nšŸ’” Run "prpm install <package>" first to create a lockfile, or initialize a new project with "prpm init"', 1);
563
612
  }
564
613
  const packageIds = Object.keys(lockfile.packages);
565
614
  if (packageIds.length === 0) {
@@ -588,19 +637,31 @@ async function installFromLockfile(options) {
588
637
  successCount++;
589
638
  }
590
639
  catch (error) {
591
- failCount++;
592
- console.error(` āŒ Failed to install ${packageId}: ${error}`);
640
+ // Check if this is a success exit (CLIError with exitCode 0)
641
+ if (error instanceof errors_1.CLIError && error.exitCode === 0) {
642
+ successCount++;
643
+ }
644
+ else {
645
+ failCount++;
646
+ console.error(` āŒ Failed to install ${packageId}:`);
647
+ console.error(` Type: ${error?.constructor?.name}`);
648
+ console.error(` Message: ${error instanceof Error ? error.message : String(error)}`);
649
+ if (error instanceof errors_1.CLIError) {
650
+ console.error(` ExitCode: ${error.exitCode}`);
651
+ }
652
+ }
593
653
  }
594
654
  }
595
655
  console.log(`\nāœ… Installed ${successCount}/${packageIds.length} packages`);
596
656
  if (failCount > 0) {
597
- console.error(`āŒ ${failCount} package${failCount === 1 ? '' : 's'} failed to install`);
598
- process.exit(1);
657
+ throw new errors_1.CLIError(`āŒ ${failCount} package${failCount === 1 ? '' : 's'} failed to install`, 1);
599
658
  }
600
659
  }
601
660
  catch (error) {
602
- console.error(`āŒ Failed to install from lockfile: ${error}`);
603
- process.exit(1);
661
+ if (error instanceof errors_1.CLIError) {
662
+ throw error;
663
+ }
664
+ throw new errors_1.CLIError(`āŒ Failed to install from lockfile: ${error}`, 1);
604
665
  }
605
666
  }
606
667
  function createInstallCommand() {
@@ -617,14 +678,7 @@ function createInstallCommand() {
617
678
  // Support both --as and --format (format is alias for as)
618
679
  const convertTo = options.format || options.as;
619
680
  if (convertTo && !['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'agents.md', 'canonical'].includes(convertTo)) {
620
- console.error('āŒ Format must be one of: cursor, claude, continue, windsurf, copilot, kiro, agents.md, canonical');
621
- console.log('\nšŸ’” Examples:');
622
- console.log(' prpm install my-package --as cursor # Convert to Cursor format');
623
- console.log(' prpm install my-package --format claude # Convert to Claude format');
624
- console.log(' prpm install my-package --format kiro # Convert to Kiro format');
625
- console.log(' prpm install my-package --format agents.md # Convert to Agents.md format');
626
- console.log(' prpm install my-package # Install in native format');
627
- process.exit(1);
681
+ throw new errors_1.CLIError('āŒ Format must be one of: cursor, claude, continue, windsurf, copilot, kiro, agents.md, canonical\n\nšŸ’” Examples:\n prpm install my-package --as cursor # Convert to Cursor format\n prpm install my-package --format claude # Convert to Claude format\n prpm install my-package --format kiro # Convert to Kiro format\n prpm install my-package --format agents.md # Convert to Agents.md format\n prpm install my-package # Install in native format', 1);
628
682
  }
629
683
  // If no package specified, install from lockfile
630
684
  if (!packageSpec) {
@@ -633,7 +687,6 @@ function createInstallCommand() {
633
687
  subtype: options.subtype,
634
688
  frozenLockfile: options.frozenLockfile
635
689
  });
636
- process.exit(0);
637
690
  return;
638
691
  }
639
692
  await handleInstall(packageSpec, {
@@ -642,7 +695,6 @@ function createInstallCommand() {
642
695
  subtype: options.subtype,
643
696
  frozenLockfile: options.frozenLockfile
644
697
  });
645
- process.exit(0);
646
698
  });
647
699
  return command;
648
700
  }
@@ -13,6 +13,7 @@ const lockfile_1 = require("../core/lockfile");
13
13
  const telemetry_1 = require("../core/telemetry");
14
14
  const fs_1 = require("fs");
15
15
  const path_1 = __importDefault(require("path"));
16
+ const errors_1 = require("../core/errors");
16
17
  /**
17
18
  * Get destination directory based on package type
18
19
  */
@@ -158,8 +159,7 @@ async function handleList() {
158
159
  }
159
160
  catch (err) {
160
161
  error = err instanceof Error ? err.message : String(err);
161
- console.error(`āŒ Failed to list packages: ${error}`);
162
- process.exit(1);
162
+ throw new errors_1.CLIError(`āŒ Failed to list packages: ${error}`, 1);
163
163
  }
164
164
  finally {
165
165
  // Track telemetry
@@ -184,7 +184,6 @@ function createListCommand() {
184
184
  .description('List all installed prompt packages')
185
185
  .action(async () => {
186
186
  await handleList();
187
- process.exit(0);
188
187
  });
189
188
  return command;
190
189
  }
@@ -42,6 +42,7 @@ const commander_1 = require("commander");
42
42
  const http_1 = require("http");
43
43
  const telemetry_1 = require("../core/telemetry");
44
44
  const user_config_1 = require("../core/user-config");
45
+ const errors_1 = require("../core/errors");
45
46
  /**
46
47
  * Start OAuth callback server
47
48
  */
@@ -270,9 +271,7 @@ async function handleLogin(options) {
270
271
  }
271
272
  catch (err) {
272
273
  error = err instanceof Error ? err.message : String(err);
273
- console.error(`\nāŒ Login failed: ${error}\n`);
274
- console.error('šŸ’” Try again or use "prpm login --token YOUR_TOKEN"\n');
275
- process.exit(1);
274
+ throw new errors_1.CLIError(`\nāŒ Login failed: ${error}\n\nšŸ’” Try again or use "prpm login --token YOUR_TOKEN"\n`, 1);
276
275
  }
277
276
  finally {
278
277
  // Track telemetry
@@ -297,6 +296,5 @@ function createLoginCommand() {
297
296
  .option('--token <token>', 'Login with a personal access token')
298
297
  .action(async (options) => {
299
298
  await handleLogin(options);
300
- process.exit(0);
301
299
  });
302
300
  }