@lpm-registry/cli 0.57.0 → 0.59.0

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 +21 -45
  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 +40 -82
  6. package/CHANGELOG.md +0 -197
  7. package/LICENSE +0 -15
  8. package/index.d.ts +0 -131
  9. package/index.js +0 -31
  10. package/lib/api.js +0 -398
  11. package/lib/commands/add.js +0 -1217
  12. package/lib/commands/audit.js +0 -283
  13. package/lib/commands/cache.js +0 -209
  14. package/lib/commands/check-name.js +0 -112
  15. package/lib/commands/config.js +0 -186
  16. package/lib/commands/doctor.js +0 -140
  17. package/lib/commands/info.js +0 -215
  18. package/lib/commands/init.js +0 -146
  19. package/lib/commands/install.js +0 -264
  20. package/lib/commands/login.js +0 -595
  21. package/lib/commands/logout.js +0 -94
  22. package/lib/commands/marketplace-compare.js +0 -164
  23. package/lib/commands/marketplace-earnings.js +0 -89
  24. package/lib/commands/mcp-setup.js +0 -363
  25. package/lib/commands/npmrc.js +0 -178
  26. package/lib/commands/open.js +0 -82
  27. package/lib/commands/outdated.js +0 -291
  28. package/lib/commands/pool-stats.js +0 -100
  29. package/lib/commands/publish.js +0 -949
  30. package/lib/commands/quality.js +0 -211
  31. package/lib/commands/remove.js +0 -82
  32. package/lib/commands/run.js +0 -14
  33. package/lib/commands/search.js +0 -143
  34. package/lib/commands/setup.js +0 -221
  35. package/lib/commands/skills.js +0 -898
  36. package/lib/commands/token-rotate.js +0 -25
  37. package/lib/commands/uninstall.js +0 -107
  38. package/lib/commands/whoami.js +0 -129
  39. package/lib/config.js +0 -328
  40. package/lib/constants.js +0 -190
  41. package/lib/ecosystem.js +0 -501
  42. package/lib/editor-skills.js +0 -321
  43. package/lib/editors.js +0 -215
  44. package/lib/import-rewriter.js +0 -364
  45. package/lib/install-targets/mcp-server.js +0 -245
  46. package/lib/install-targets/vscode-extension.js +0 -178
  47. package/lib/install-targets.js +0 -82
  48. package/lib/integrity.js +0 -179
  49. package/lib/lpm-config-prompts.js +0 -102
  50. package/lib/lpm-config.js +0 -408
  51. package/lib/oidc.js +0 -166
  52. package/lib/project-utils.js +0 -152
  53. package/lib/quality/checks.js +0 -687
  54. package/lib/quality/display.js +0 -155
  55. package/lib/quality/score.js +0 -118
  56. package/lib/quality/swift-checks.js +0 -447
  57. package/lib/safe-path.js +0 -180
  58. package/lib/secure-store.js +0 -321
  59. package/lib/swift-project.js +0 -637
  60. package/lib/ui.js +0 -40
  61. package/scripts/__tests__/install-binary.test.js +0 -346
  62. package/scripts/install-binary.js +0 -571
@@ -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
- }