@lpm-registry/cli 0.58.0 → 0.59.1

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.
Files changed (62) hide show
  1. package/README.md +25 -44
  2. package/bin/lpm.js +2 -354
  3. package/bin/lpx.js +4 -0
  4. package/bin/native.js +154 -0
  5. package/package.json +42 -82
  6. package/scripts/install-binary.js +219 -550
  7. package/CHANGELOG.md +0 -197
  8. package/LICENSE +0 -15
  9. package/index.d.ts +0 -131
  10. package/index.js +0 -31
  11. package/lib/api.js +0 -398
  12. package/lib/commands/add.js +0 -1217
  13. package/lib/commands/audit.js +0 -283
  14. package/lib/commands/cache.js +0 -209
  15. package/lib/commands/check-name.js +0 -112
  16. package/lib/commands/config.js +0 -186
  17. package/lib/commands/doctor.js +0 -140
  18. package/lib/commands/info.js +0 -215
  19. package/lib/commands/init.js +0 -146
  20. package/lib/commands/install.js +0 -264
  21. package/lib/commands/login.js +0 -595
  22. package/lib/commands/logout.js +0 -94
  23. package/lib/commands/marketplace-compare.js +0 -164
  24. package/lib/commands/marketplace-earnings.js +0 -89
  25. package/lib/commands/mcp-setup.js +0 -363
  26. package/lib/commands/npmrc.js +0 -178
  27. package/lib/commands/open.js +0 -82
  28. package/lib/commands/outdated.js +0 -291
  29. package/lib/commands/pool-stats.js +0 -100
  30. package/lib/commands/publish.js +0 -949
  31. package/lib/commands/quality.js +0 -211
  32. package/lib/commands/remove.js +0 -82
  33. package/lib/commands/run.js +0 -14
  34. package/lib/commands/search.js +0 -143
  35. package/lib/commands/setup.js +0 -221
  36. package/lib/commands/skills.js +0 -898
  37. package/lib/commands/token-rotate.js +0 -25
  38. package/lib/commands/uninstall.js +0 -107
  39. package/lib/commands/whoami.js +0 -129
  40. package/lib/config.js +0 -328
  41. package/lib/constants.js +0 -190
  42. package/lib/ecosystem.js +0 -501
  43. package/lib/editor-skills.js +0 -321
  44. package/lib/editors.js +0 -215
  45. package/lib/import-rewriter.js +0 -364
  46. package/lib/install-targets/mcp-server.js +0 -245
  47. package/lib/install-targets/vscode-extension.js +0 -178
  48. package/lib/install-targets.js +0 -82
  49. package/lib/integrity.js +0 -179
  50. package/lib/lpm-config-prompts.js +0 -102
  51. package/lib/lpm-config.js +0 -408
  52. package/lib/oidc.js +0 -166
  53. package/lib/project-utils.js +0 -152
  54. package/lib/quality/checks.js +0 -687
  55. package/lib/quality/display.js +0 -155
  56. package/lib/quality/score.js +0 -118
  57. package/lib/quality/swift-checks.js +0 -447
  58. package/lib/safe-path.js +0 -180
  59. package/lib/secure-store.js +0 -321
  60. package/lib/swift-project.js +0 -637
  61. package/lib/ui.js +0 -40
  62. package/scripts/__tests__/install-binary.test.js +0 -346
@@ -1,687 +0,0 @@
1
- /**
2
- * Quality check definitions for LPM packages.
3
- * Each check is a pure function that returns { passed, detail }.
4
- * Checks are grouped by category and scored out of 100 total points.
5
- *
6
- * Categories:
7
- * - documentation: 22 points (6 checks)
8
- * - code: 31 points (9 checks)
9
- * - testing: 11 points (2 checks)
10
- * - health: 36 points (11 checks)
11
- */
12
-
13
- const DEFAULT_TEST_SCRIPT = 'echo "Error: no test" && exit 1'
14
-
15
- // --- Documentation checks (22 points) ---
16
-
17
- const hasReadme = {
18
- id: "has-readme",
19
- category: "documentation",
20
- label: "Has README",
21
- max_points: 8,
22
- run: ({ readme }) => ({
23
- passed: !!readme && readme.length > 100,
24
- detail: readme
25
- ? `README found (${readme.length.toLocaleString()} chars)`
26
- : "No README found",
27
- }),
28
- }
29
-
30
- const readmeHasInstall = {
31
- id: "readme-install",
32
- category: "documentation",
33
- label: "README has install section",
34
- max_points: 3,
35
- run: ({ readme }) => {
36
- if (!readme) return { passed: false, detail: "No README" }
37
- const lower = readme.toLowerCase()
38
- const hasSection =
39
- lower.includes("## install") ||
40
- lower.includes("## getting started") ||
41
- lower.includes("## setup") ||
42
- lower.includes("npm install") ||
43
- lower.includes("lpm add") ||
44
- lower.includes("lpm install")
45
- return {
46
- passed: hasSection,
47
- detail: hasSection
48
- ? "Install instructions found"
49
- : "No install section found in README",
50
- }
51
- },
52
- }
53
-
54
- const readmeHasUsage = {
55
- id: "readme-usage",
56
- category: "documentation",
57
- label: "README has usage examples",
58
- max_points: 3,
59
- run: ({ readme }) => {
60
- if (!readme) return { passed: false, detail: "No README" }
61
- const lower = readme.toLowerCase()
62
- const hasUsageSection =
63
- lower.includes("## usage") || lower.includes("## example")
64
- const codeBlockCount = (readme.match(/```/g) || []).length / 2
65
- const hasCodeBlocks = codeBlockCount >= 2
66
- const passed = hasUsageSection || hasCodeBlocks
67
- return {
68
- passed,
69
- detail: passed
70
- ? `Usage examples found (${Math.floor(codeBlockCount)} code blocks)`
71
- : "No usage examples found in README",
72
- }
73
- },
74
- }
75
-
76
- const readmeHasApi = {
77
- id: "readme-api",
78
- category: "documentation",
79
- label: "README has API docs",
80
- max_points: 2,
81
- run: ({ readme }) => {
82
- if (!readme) return { passed: false, detail: "No README" }
83
- const lower = readme.toLowerCase()
84
- const hasApiSection =
85
- lower.includes("## api") ||
86
- lower.includes("## reference") ||
87
- lower.includes("## props") ||
88
- lower.includes("## parameters") ||
89
- lower.includes("## options")
90
- return {
91
- passed: hasApiSection,
92
- detail: hasApiSection
93
- ? "API documentation found"
94
- : "No API/reference section found in README",
95
- }
96
- },
97
- }
98
-
99
- const hasChangelog = {
100
- id: "has-changelog",
101
- category: "documentation",
102
- label: "Has CHANGELOG",
103
- max_points: 3,
104
- run: ({ files }) => {
105
- const changelogFiles = files.filter(f => {
106
- const name = (f.path || f).toLowerCase()
107
- return name.includes("changelog")
108
- })
109
- return {
110
- passed: changelogFiles.length > 0,
111
- detail:
112
- changelogFiles.length > 0
113
- ? "CHANGELOG found"
114
- : "No CHANGELOG file found",
115
- }
116
- },
117
- }
118
-
119
- const hasLicense = {
120
- id: "has-license",
121
- category: "documentation",
122
- label: "Has LICENSE file",
123
- max_points: 3,
124
- run: ({ files, packageJson }) => {
125
- const licenseFiles = files.filter(f => {
126
- const name = (f.path || f).toLowerCase()
127
- return name.includes("license") || name.includes("licence")
128
- })
129
- const hasLicenseField = !!packageJson.license
130
- const passed = licenseFiles.length > 0 || hasLicenseField
131
- return {
132
- passed,
133
- detail:
134
- licenseFiles.length > 0
135
- ? "LICENSE file found"
136
- : hasLicenseField
137
- ? `License field: ${packageJson.license}`
138
- : "No LICENSE file or license field",
139
- }
140
- },
141
- }
142
-
143
- // --- Code Quality checks (31 points) ---
144
-
145
- const hasTypes = {
146
- id: "has-types",
147
- category: "code",
148
- label: "Has TypeScript types",
149
- max_points: 8,
150
- run: ({ packageJson, files }) => {
151
- const hasTypesField = !!(packageJson.types || packageJson.typings)
152
- const hasDtsFiles = files.some(f => {
153
- const name = f.path || f
154
- return name.endsWith(".d.ts") || name.endsWith(".d.mts")
155
- })
156
- const passed = hasTypesField || hasDtsFiles
157
- return {
158
- passed,
159
- detail: hasTypesField
160
- ? `Types field: ${packageJson.types || packageJson.typings}`
161
- : hasDtsFiles
162
- ? ".d.ts files found"
163
- : "No TypeScript type definitions found",
164
- }
165
- },
166
- }
167
-
168
- const intellisenseCoverage = {
169
- id: "intellisense-coverage",
170
- category: "code",
171
- label: "Intellisense coverage",
172
- max_points: 4,
173
- run: ({ packageJson, files }) => {
174
- // Full points if .d.ts files exist (complete intellisense)
175
- const hasTypesField = !!(packageJson.types || packageJson.typings)
176
- const hasDtsFiles = files.some(f => {
177
- const name = f.path || f
178
- return name.endsWith(".d.ts") || name.endsWith(".d.mts")
179
- })
180
- if (hasTypesField || hasDtsFiles) {
181
- return {
182
- passed: true,
183
- detail: "TypeScript definitions provide full intellisense",
184
- }
185
- }
186
- // Partial check: JSDoc detection runs server-side from tarball
187
- // CLI assumes fail; server can upgrade if JSDoc found
188
- return {
189
- passed: false,
190
- detail: "No .d.ts files or JSDoc detected for intellisense",
191
- server_only: true,
192
- }
193
- },
194
- }
195
-
196
- const hasEsm = {
197
- id: "esm-exports",
198
- category: "code",
199
- label: "ESM exports",
200
- max_points: 3,
201
- run: ({ packageJson }) => {
202
- const isModule = packageJson.type === "module"
203
- const hasModuleField = !!packageJson.module
204
- const hasExportsField = !!packageJson.exports
205
- const passed = isModule || hasModuleField || hasExportsField
206
- return {
207
- passed,
208
- detail: isModule
209
- ? '"type": "module" detected'
210
- : hasExportsField
211
- ? '"exports" field detected'
212
- : hasModuleField
213
- ? '"module" field detected'
214
- : "No ESM support detected",
215
- }
216
- },
217
- }
218
-
219
- const treeShakable = {
220
- id: "tree-shakable",
221
- category: "code",
222
- label: "Tree-shakable",
223
- max_points: 3,
224
- run: ({ packageJson }) => {
225
- const hasSideEffects = packageJson.sideEffects === false
226
- const hasExportsField = !!packageJson.exports
227
- const isModule = packageJson.type === "module"
228
- const passed = hasSideEffects || (hasExportsField && isModule)
229
- let detail
230
- if (hasSideEffects) {
231
- detail = '"sideEffects": false enables tree-shaking'
232
- } else if (hasExportsField && isModule) {
233
- detail = "ESM + exports map enables tree-shaking"
234
- } else {
235
- detail = 'No "sideEffects": false in package.json'
236
- }
237
- return { passed, detail }
238
- },
239
- }
240
-
241
- const noEval = {
242
- id: "no-eval",
243
- category: "code",
244
- label: "No eval/Function() patterns",
245
- max_points: 3,
246
- run: () => {
247
- // This check runs server-side from tarball contents.
248
- // CLI assumes pass; server overrides if eval detected.
249
- return {
250
- passed: true,
251
- detail: "Full check runs server-side",
252
- server_only: true,
253
- }
254
- },
255
- }
256
-
257
- const noLifecycleScripts = {
258
- id: "no-lifecycle-scripts",
259
- category: "health",
260
- label: "No lifecycle scripts",
261
- max_points: 2,
262
- run: ({ packageJson }) => {
263
- const lifecycleNames = [
264
- "preinstall",
265
- "install",
266
- "postinstall",
267
- "preuninstall",
268
- "uninstall",
269
- "postuninstall",
270
- ]
271
- const scripts = packageJson.scripts || {}
272
- const found = lifecycleNames.filter(name => scripts[name])
273
- if (found.length === 0) {
274
- return {
275
- passed: true,
276
- detail: "No lifecycle scripts detected",
277
- }
278
- }
279
- return {
280
- passed: false,
281
- detail: `Lifecycle scripts found: ${found.join(", ")}`,
282
- }
283
- },
284
- }
285
-
286
- const hasEngines = {
287
- id: "has-engines",
288
- category: "code",
289
- label: 'Has "engines" field',
290
- max_points: 1,
291
- run: ({ packageJson }) => {
292
- const hasField = !!packageJson.engines?.node
293
- return {
294
- passed: hasField,
295
- detail: hasField
296
- ? `engines.node: ${packageJson.engines.node}`
297
- : 'No "engines" field in package.json',
298
- }
299
- },
300
- }
301
-
302
- const hasExportsMap = {
303
- id: "has-exports-map",
304
- category: "code",
305
- label: 'Has "exports" map',
306
- max_points: 3,
307
- run: ({ packageJson }) => {
308
- const hasField = !!packageJson.exports
309
- return {
310
- passed: hasField,
311
- detail: hasField
312
- ? '"exports" map defined'
313
- : 'No "exports" map in package.json',
314
- }
315
- },
316
- }
317
-
318
- const smallDeps = {
319
- id: "small-deps",
320
- category: "code",
321
- label: "Small dependency footprint",
322
- max_points: 3,
323
- run: ({ packageJson }) => {
324
- const deps = packageJson.dependencies
325
- ? Object.keys(packageJson.dependencies).length
326
- : 0
327
- let points
328
- if (deps === 0) points = 3
329
- else if (deps <= 3) points = 2
330
- else if (deps <= 7) points = 1
331
- else points = 0
332
- return {
333
- passed: points > 0,
334
- points,
335
- detail: `${deps} production ${deps === 1 ? "dependency" : "dependencies"}`,
336
- }
337
- },
338
- }
339
-
340
- const sourceMaps = {
341
- id: "source-maps",
342
- category: "code",
343
- label: "Source maps included",
344
- max_points: 1,
345
- run: ({ files }) => {
346
- const mapFiles = files.filter(f => {
347
- const name = f.path || f
348
- return name.endsWith(".js.map") || name.endsWith(".mjs.map")
349
- })
350
- return {
351
- passed: mapFiles.length > 0,
352
- detail:
353
- mapFiles.length > 0
354
- ? `${mapFiles.length} source map${mapFiles.length !== 1 ? "s" : ""} found`
355
- : "No source map files found",
356
- }
357
- },
358
- }
359
-
360
- // --- Testing checks (11 points) ---
361
-
362
- const hasTestFiles = {
363
- id: "has-test-files",
364
- category: "testing",
365
- label: "Has test files",
366
- max_points: 7,
367
- run: ({ projectFiles, files }) => {
368
- // Prefer projectFiles (local project scan) over files (tarball)
369
- // so authors don't need to ship test files in the package
370
- const fileList = projectFiles || files
371
- const testFiles = fileList.filter(f => {
372
- const name = (f.path || f).toLowerCase()
373
- return (
374
- name.includes(".test.") ||
375
- name.includes(".spec.") ||
376
- name.includes("__tests__/") ||
377
- name.startsWith("test/") ||
378
- name.startsWith("tests/")
379
- )
380
- })
381
- return {
382
- passed: testFiles.length > 0,
383
- detail:
384
- testFiles.length > 0
385
- ? `${testFiles.length} test file${testFiles.length !== 1 ? "s" : ""} found`
386
- : "No test files found",
387
- }
388
- },
389
- }
390
-
391
- const hasTestScript = {
392
- id: "has-test-script",
393
- category: "testing",
394
- label: "Has test script",
395
- max_points: 4,
396
- run: ({ packageJson }) => {
397
- const testScript = packageJson.scripts?.test
398
- const hasScript = !!testScript && !testScript.includes(DEFAULT_TEST_SCRIPT)
399
- return {
400
- passed: hasScript,
401
- detail: hasScript
402
- ? `test script: ${testScript}`
403
- : "No test script in package.json",
404
- }
405
- },
406
- }
407
-
408
- // --- Package Health checks (36 points) ---
409
-
410
- const hasDescription = {
411
- id: "has-description",
412
- category: "health",
413
- label: "Has description",
414
- max_points: 3,
415
- run: ({ packageJson }) => {
416
- const desc = packageJson.description
417
- const passed = !!desc && desc.length > 10
418
- return {
419
- passed,
420
- detail: passed
421
- ? `Description: "${desc.substring(0, 60)}${desc.length > 60 ? "..." : ""}"`
422
- : "No meaningful description in package.json",
423
- }
424
- },
425
- }
426
-
427
- const hasKeywords = {
428
- id: "has-keywords",
429
- category: "health",
430
- label: "Has keywords",
431
- max_points: 1,
432
- run: ({ packageJson }) => {
433
- const keywords = packageJson.keywords
434
- const passed = Array.isArray(keywords) && keywords.length > 0
435
- return {
436
- passed,
437
- detail: passed
438
- ? `${keywords.length} keyword${keywords.length !== 1 ? "s" : ""}`
439
- : "No keywords in package.json",
440
- }
441
- },
442
- }
443
-
444
- const hasRepository = {
445
- id: "has-repository",
446
- category: "health",
447
- label: "Has repository URL",
448
- max_points: 2,
449
- run: ({ packageJson }) => {
450
- const repo = packageJson.repository
451
- const hasRepo = !!(
452
- repo &&
453
- (typeof repo === "string" || typeof repo?.url === "string")
454
- )
455
- return {
456
- passed: hasRepo,
457
- detail: hasRepo
458
- ? `Repository: ${typeof repo === "string" ? repo : repo.url}`
459
- : "No repository field in package.json",
460
- }
461
- },
462
- }
463
-
464
- const hasHomepage = {
465
- id: "has-homepage",
466
- category: "health",
467
- label: "Has homepage",
468
- max_points: 1,
469
- run: ({ packageJson }) => {
470
- const passed = !!packageJson.homepage
471
- return {
472
- passed,
473
- detail: passed
474
- ? `Homepage: ${packageJson.homepage}`
475
- : "No homepage in package.json",
476
- }
477
- },
478
- }
479
-
480
- const reasonableSize = {
481
- id: "reasonable-size",
482
- category: "health",
483
- label: "Reasonable bundle size",
484
- max_points: 3,
485
- run: ({ unpackedSize }) => {
486
- if (!unpackedSize)
487
- return { passed: true, points: 3, detail: "Size unknown" }
488
- const kb = unpackedSize / 1024
489
- const mb = kb / 1024
490
- let points
491
- if (mb < 0.1) points = 3
492
- else if (mb < 0.5) points = 2
493
- else if (mb < 1) points = 1
494
- else points = 0
495
-
496
- const sizeStr = mb >= 1 ? `${mb.toFixed(1)}MB` : `${Math.round(kb)}KB`
497
- return {
498
- passed: points > 0,
499
- points,
500
- detail: `Unpacked size: ${sizeStr}`,
501
- }
502
- },
503
- }
504
-
505
- const noVulnerabilities = {
506
- id: "no-vulnerabilities",
507
- category: "health",
508
- label: "No known vulnerabilities",
509
- max_points: 5,
510
- run: () => {
511
- // This check runs server-side with OSV scan results.
512
- // CLI assumes pass; server overrides with actual results.
513
- return {
514
- passed: true,
515
- detail: "Full check runs server-side",
516
- server_only: true,
517
- }
518
- },
519
- }
520
-
521
- const maintenanceHealth = {
522
- id: "maintenance-health",
523
- category: "health",
524
- label: "Active maintenance",
525
- max_points: 4,
526
- run: () => {
527
- // Server-only: checks if last version was published within 90 days.
528
- // CLI cannot know this — assumes pass.
529
- return {
530
- passed: true,
531
- detail: "Full check runs server-side",
532
- server_only: true,
533
- }
534
- },
535
- }
536
-
537
- const semverConsistency = {
538
- id: "semver-consistency",
539
- category: "health",
540
- label: "SemVer consistency",
541
- max_points: 4,
542
- run: ({ packageJson }) => {
543
- // CLI can check if the current version is valid semver
544
- const version = packageJson.version
545
- if (!version) return { passed: false, detail: "No version in package.json" }
546
- // Basic semver regex (major.minor.patch with optional pre-release)
547
- const semverRegex = /^\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$/
548
- const isValid = semverRegex.test(version)
549
- // Server can also check version history for wild jumps
550
- return {
551
- passed: isValid,
552
- detail: isValid
553
- ? `Valid semver: ${version}`
554
- : `Invalid semver format: ${version}`,
555
- server_only: !isValid ? undefined : true,
556
- }
557
- },
558
- }
559
-
560
- const authorVerified = {
561
- id: "author-verified",
562
- category: "health",
563
- label: "Verified author",
564
- max_points: 3,
565
- run: () => {
566
- // Server-only: checks if the author has linked social accounts.
567
- // CLI cannot know this — assumes pass.
568
- return {
569
- passed: true,
570
- detail: "Full check runs server-side",
571
- server_only: true,
572
- }
573
- },
574
- }
575
-
576
- // --- Agent Skills checks (10 points) ---
577
-
578
- const hasSkills = {
579
- id: "has-skills",
580
- category: "health",
581
- label: "Has Agent Skills",
582
- max_points: 7,
583
- run: () => {
584
- // Server-only: checks if package has approved skills.
585
- // CLI cannot determine this — assumes fail.
586
- return {
587
- passed: false,
588
- detail: "Full check runs server-side",
589
- server_only: true,
590
- }
591
- },
592
- }
593
-
594
- const skillsComprehensive = {
595
- id: "skills-comprehensive",
596
- category: "health",
597
- label: "Comprehensive Agent Skills",
598
- max_points: 3,
599
- run: () => {
600
- // Server-only: checks if package has 3+ approved skills.
601
- // CLI cannot determine this — assumes fail.
602
- return {
603
- passed: false,
604
- detail: "Full check runs server-side",
605
- server_only: true,
606
- }
607
- },
608
- }
609
-
610
- // --- Export all checks ---
611
-
612
- /** JS checks (default) */
613
- export const checks = [
614
- // Documentation (22)
615
- hasReadme,
616
- readmeHasInstall,
617
- readmeHasUsage,
618
- readmeHasApi,
619
- hasChangelog,
620
- hasLicense,
621
- // Code Quality (31)
622
- hasTypes,
623
- intellisenseCoverage,
624
- hasEsm,
625
- treeShakable,
626
- noEval,
627
- hasEngines,
628
- hasExportsMap,
629
- smallDeps,
630
- sourceMaps,
631
- // Testing (11)
632
- hasTestFiles,
633
- hasTestScript,
634
- // Health (36)
635
- hasDescription,
636
- hasKeywords,
637
- hasRepository,
638
- hasHomepage,
639
- reasonableSize,
640
- noVulnerabilities,
641
- noLifecycleScripts,
642
- maintenanceHealth,
643
- semverConsistency,
644
- authorVerified,
645
- hasSkills,
646
- skillsComprehensive,
647
- ]
648
-
649
- // Export individual universal checks for reuse by ecosystem-specific check sets
650
- export {
651
- hasReadme,
652
- readmeHasUsage,
653
- readmeHasApi,
654
- hasChangelog,
655
- hasLicense,
656
- hasDescription,
657
- hasKeywords,
658
- hasRepository,
659
- hasHomepage,
660
- reasonableSize,
661
- noVulnerabilities,
662
- noLifecycleScripts,
663
- maintenanceHealth,
664
- semverConsistency,
665
- authorVerified,
666
- hasSkills,
667
- skillsComprehensive,
668
- }
669
-
670
- /**
671
- * Get source package informational badges (not scored)
672
- * @param {object} lpmConfig
673
- * @returns {object|null}
674
- */
675
- export function getSourcePackageInfo(lpmConfig) {
676
- if (!lpmConfig) return null
677
- return {
678
- hasConfig: true,
679
- hasDefaults: !!lpmConfig.defaultConfig,
680
- optionCount: lpmConfig.configSchema
681
- ? Object.keys(lpmConfig.configSchema).length
682
- : 0,
683
- usesConditionalIncludes: (lpmConfig.files || []).some(
684
- f => f.include === "when" && f.condition,
685
- ),
686
- }
687
- }