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.
@@ -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
- const [packageId, specVersion] = packageSpec.split('@');
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
- // Determine format preference
109
- const format = options.as || config.defaultFormat || detectProjectFormat() || 'cursor';
110
- if (format !== 'canonical') {
111
- console.log(` 🔄 Converting to ${format} format...`);
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.display_name} ${pkg.official ? '🏅' : ''}`);
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
- const type = options.type || pkg.type;
140
- const destDir = (0, filesystem_1.getDestinationDir)(type);
141
- // For MVP, assume single file in tarball
142
- // TODO: Implement proper tar extraction
143
- const mainFile = await extractMainFile(tarball, packageId);
144
- const destPath = `${destDir}/${packageId}.md`;
145
- await (0, filesystem_1.saveFile)(destPath, mainFile);
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 configuration
158
- const packageRecord = {
159
- id: packageId,
160
- type,
161
- url: tarballUrl,
162
- dest: destPath,
163
- version: actualVersion,
164
- };
165
- await (0, config_1.addPackage)(packageRecord);
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 ${pkg.total_downloads.toLocaleString()} times`);
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
- * Extract main file from tarball
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
- zlib.gunzip(tarball, (err, result) => {
209
- if (err)
373
+ // Decompress gzip first
374
+ zlib.gunzip(tarball, async (err, result) => {
375
+ if (err) {
210
376
  reject(err);
211
- else
212
- resolve(result.toString('utf-8'));
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, options);
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
  }
@@ -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 config_1 = require("../core/config");
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, ...packages.map(p => p.id.length));
23
- const typeWidth = Math.max(6, ...packages.map(p => p.type.length));
24
- const urlWidth = Math.max(20, ...packages.map(p => p.url.length));
25
- const destWidth = Math.max(15, ...packages.map(p => p.dest.length));
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
- 'URL'.padEnd(urlWidth),
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
- packages.forEach(pkg => {
112
+ packagesWithLocations.forEach(pkg => {
37
113
  const row = [
38
114
  pkg.id.padEnd(idWidth),
39
- pkg.type.padEnd(typeWidth),
40
- pkg.url.padEnd(urlWidth),
41
- pkg.dest.padEnd(destWidth)
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, config_1.listPackages)();
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
  /**