@lpm-registry/cli 0.2.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 (54) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +15 -0
  3. package/README.md +406 -0
  4. package/bin/lpm.js +334 -0
  5. package/index.d.ts +131 -0
  6. package/index.js +31 -0
  7. package/lib/api.js +324 -0
  8. package/lib/commands/add.js +1217 -0
  9. package/lib/commands/audit.js +283 -0
  10. package/lib/commands/cache.js +209 -0
  11. package/lib/commands/check-name.js +112 -0
  12. package/lib/commands/config.js +174 -0
  13. package/lib/commands/doctor.js +142 -0
  14. package/lib/commands/info.js +215 -0
  15. package/lib/commands/init.js +146 -0
  16. package/lib/commands/install.js +217 -0
  17. package/lib/commands/login.js +547 -0
  18. package/lib/commands/logout.js +94 -0
  19. package/lib/commands/marketplace-compare.js +164 -0
  20. package/lib/commands/marketplace-earnings.js +89 -0
  21. package/lib/commands/mcp-setup.js +363 -0
  22. package/lib/commands/open.js +82 -0
  23. package/lib/commands/outdated.js +291 -0
  24. package/lib/commands/pool-stats.js +100 -0
  25. package/lib/commands/publish.js +707 -0
  26. package/lib/commands/quality.js +211 -0
  27. package/lib/commands/remove.js +82 -0
  28. package/lib/commands/run.js +14 -0
  29. package/lib/commands/search.js +143 -0
  30. package/lib/commands/setup.js +92 -0
  31. package/lib/commands/skills.js +863 -0
  32. package/lib/commands/token-rotate.js +25 -0
  33. package/lib/commands/whoami.js +129 -0
  34. package/lib/config.js +240 -0
  35. package/lib/constants.js +190 -0
  36. package/lib/ecosystem.js +501 -0
  37. package/lib/editors.js +215 -0
  38. package/lib/import-rewriter.js +364 -0
  39. package/lib/install-targets/mcp-server.js +245 -0
  40. package/lib/install-targets/vscode-extension.js +178 -0
  41. package/lib/install-targets.js +82 -0
  42. package/lib/integrity.js +179 -0
  43. package/lib/lpm-config-prompts.js +102 -0
  44. package/lib/lpm-config.js +408 -0
  45. package/lib/project-utils.js +152 -0
  46. package/lib/quality/checks.js +654 -0
  47. package/lib/quality/display.js +139 -0
  48. package/lib/quality/score.js +115 -0
  49. package/lib/quality/swift-checks.js +447 -0
  50. package/lib/safe-path.js +180 -0
  51. package/lib/secure-store.js +288 -0
  52. package/lib/swift-project.js +637 -0
  53. package/lib/ui.js +40 -0
  54. package/package.json +74 -0
@@ -0,0 +1,217 @@
1
+ import { spawn } from "node:child_process"
2
+ import fs from "node:fs"
3
+ import path from "node:path"
4
+ import { getRegistryUrl, getToken } from "../config.js"
5
+ import { createSpinner, log, printHeader } from "../ui.js"
6
+ import { skillsInstall } from "./skills.js"
7
+
8
+ /**
9
+ * Check if a package name is an LPM package
10
+ * LPM packages use the @lpm.dev scope
11
+ */
12
+ function isLpmPackage(pkgName) {
13
+ return pkgName.startsWith("@lpm.dev/")
14
+ }
15
+
16
+ export async function install(packages, options) {
17
+ const isJson = options?.json
18
+
19
+ if (!isJson) printHeader()
20
+
21
+ const token = await getToken()
22
+ if (!token) {
23
+ if (isJson) {
24
+ process.stdout.write(
25
+ `${JSON.stringify(
26
+ {
27
+ success: false,
28
+ packages: [],
29
+ npmOutput: "",
30
+ warnings: [],
31
+ errors: [
32
+ 'You must be logged in to install packages. Run "lpm login" first.',
33
+ ],
34
+ },
35
+ null,
36
+ 2,
37
+ )}\n`,
38
+ )
39
+ } else {
40
+ log.error(
41
+ 'You must be logged in to install packages. Run "lpm login" first.',
42
+ )
43
+ }
44
+ process.exit(1)
45
+ }
46
+
47
+ if (!packages || packages.length === 0) {
48
+ // No packages specified - read from package.json
49
+ const packageJsonPath = path.resolve(process.cwd(), "package.json")
50
+ if (!fs.existsSync(packageJsonPath)) {
51
+ if (isJson) {
52
+ process.stdout.write(
53
+ `${JSON.stringify(
54
+ {
55
+ success: false,
56
+ packages: [],
57
+ npmOutput: "",
58
+ warnings: [],
59
+ errors: ["No packages specified and no package.json found."],
60
+ },
61
+ null,
62
+ 2,
63
+ )}\n`,
64
+ )
65
+ } else {
66
+ log.error("No packages specified and no package.json found.")
67
+ }
68
+ process.exit(1)
69
+ }
70
+
71
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"))
72
+ const allDeps = {
73
+ ...pkg.dependencies,
74
+ ...pkg.devDependencies,
75
+ }
76
+
77
+ // Filter to LPM packages only (@lpm.dev scope)
78
+ packages = Object.keys(allDeps).filter(isLpmPackage)
79
+
80
+ if (packages.length === 0) {
81
+ if (isJson) {
82
+ process.stdout.write(
83
+ `${JSON.stringify(
84
+ {
85
+ success: true,
86
+ packages: [],
87
+ npmOutput: "",
88
+ warnings: ["No LPM packages (@lpm.dev/*) found in package.json."],
89
+ errors: [],
90
+ },
91
+ null,
92
+ 2,
93
+ )}\n`,
94
+ )
95
+ } else {
96
+ log.info("No LPM packages (@lpm.dev/*) found in package.json.")
97
+ }
98
+ process.exit(0)
99
+ }
100
+
101
+ if (!isJson) {
102
+ log.info(
103
+ `Installing ${packages.length} LPM packages from package.json...`,
104
+ )
105
+ }
106
+ }
107
+
108
+ let spinner
109
+ if (!isJson) {
110
+ spinner = createSpinner(
111
+ `Preparing to install ${packages.join(", ")}...`,
112
+ ).start()
113
+ }
114
+
115
+ const baseRegistryUrl = getRegistryUrl()
116
+ // Ensure we have the full registry path for npm
117
+ const registryUrl = baseRegistryUrl.endsWith("/api/registry")
118
+ ? baseRegistryUrl
119
+ : `${baseRegistryUrl}/api/registry`
120
+ // Remove protocol for auth token config (e.g. https://registry.com/ -> //registry.com/)
121
+ const registryHost = registryUrl.replace(/^https?:/, "")
122
+
123
+ // Create temporary .npmrc content
124
+ // Simple configuration - all LPM packages use @lpm.dev scope
125
+ const npmrcContent = `${registryHost}/:_authToken=${token}
126
+ @lpm.dev:registry=${registryUrl}
127
+ `
128
+
129
+ // Write to temp file
130
+ const tempNpmrcPath = path.resolve(process.cwd(), `.npmrc.lpm-${Date.now()}`)
131
+ fs.writeFileSync(tempNpmrcPath, npmrcContent)
132
+
133
+ if (!isJson) {
134
+ spinner.succeed("Configuration generated. Running npm install...")
135
+ }
136
+
137
+ // Run npm install
138
+ const npmArgs = ["install", ...packages, "--userconfig", tempNpmrcPath]
139
+
140
+ // In JSON mode, capture stdout/stderr instead of inheriting
141
+ const child = spawn("npm", npmArgs, {
142
+ stdio: isJson ? "pipe" : "inherit",
143
+ env: { ...process.env, LPM_TOKEN: token },
144
+ })
145
+
146
+ const cleanup = () => {
147
+ if (fs.existsSync(tempNpmrcPath)) {
148
+ fs.unlinkSync(tempNpmrcPath)
149
+ }
150
+ }
151
+
152
+ const fetchSkills = options?.skills !== false
153
+
154
+ if (isJson) {
155
+ let stdout = ""
156
+ let stderr = ""
157
+ child.stdout.on("data", data => {
158
+ stdout += data.toString()
159
+ })
160
+ child.stderr.on("data", data => {
161
+ stderr += data.toString()
162
+ })
163
+
164
+ child.on("close", async code => {
165
+ cleanup()
166
+ const output = {
167
+ success: code === 0,
168
+ packages: packages.map(p => ({ name: p })),
169
+ npmOutput: (stdout + stderr).trim(),
170
+ warnings: [],
171
+ errors: code !== 0 ? [`npm install failed with code ${code}`] : [],
172
+ }
173
+ process.stdout.write(`${JSON.stringify(output, null, 2)}\n`)
174
+ if (code !== 0) process.exit(code)
175
+ if (fetchSkills) await skillsInstall(null, { json: true })
176
+ })
177
+
178
+ child.on("error", err => {
179
+ cleanup()
180
+ const output = {
181
+ success: false,
182
+ packages: packages.map(p => ({ name: p })),
183
+ npmOutput: "",
184
+ warnings: [],
185
+ errors: [`Failed to start npm: ${err.message}`],
186
+ }
187
+ process.stdout.write(`${JSON.stringify(output, null, 2)}\n`)
188
+ process.exit(1)
189
+ })
190
+ } else {
191
+ child.on("close", async code => {
192
+ cleanup()
193
+ if (code !== 0) {
194
+ log.error(`npm install failed with code ${code}`)
195
+ process.exit(code)
196
+ } else {
197
+ log.success("Packages installed successfully.")
198
+ if (fetchSkills) {
199
+ console.log("")
200
+ await skillsInstall(null)
201
+ }
202
+ }
203
+ })
204
+
205
+ child.on("error", err => {
206
+ log.error(`Failed to start npm: ${err.message}`)
207
+ cleanup()
208
+ process.exit(1)
209
+ })
210
+ }
211
+
212
+ // Handle interrupt to cleanup
213
+ process.on("SIGINT", () => {
214
+ cleanup()
215
+ process.exit()
216
+ })
217
+ }