( pkgName: string, candidatePaths: string[], )
| 151 | } |
| 152 | |
| 153 | export function detectInstallMethodFromPaths( |
| 154 | pkgName: string, |
| 155 | candidatePaths: string[], |
| 156 | ): InstallMethod { |
| 157 | const normalized = candidatePaths.map((p) => p.toLowerCase()); |
| 158 | |
| 159 | const isNpx = normalized.some( |
| 160 | (p) => p.includes('/_npx/') && p.includes(`/node_modules/${pkgName}/`), |
| 161 | ); |
| 162 | if (isNpx) { |
| 163 | return { |
| 164 | kind: 'npx', |
| 165 | manualInstructions: [ |
| 166 | 'npx always fetches the latest version by default when using @latest.', |
| 167 | 'If you pinned a specific version, update the version in your MCP client config.', |
| 168 | ], |
| 169 | }; |
| 170 | } |
| 171 | |
| 172 | const isHomebrew = normalized.some( |
| 173 | (p) => p.includes(`/cellar/${pkgName}/`) || p.includes(`/homebrew/cellar/${pkgName}/`), |
| 174 | ); |
| 175 | if (isHomebrew) { |
| 176 | return { |
| 177 | kind: 'homebrew', |
| 178 | manualCommand: `brew update && brew upgrade ${pkgName}`, |
| 179 | autoCommands: [ |
| 180 | ['brew', 'update'], |
| 181 | ['brew', 'upgrade', pkgName], |
| 182 | ], |
| 183 | }; |
| 184 | } |
| 185 | |
| 186 | const isNpmGlobal = normalized.some((p) => p.includes(`/node_modules/${pkgName}/`)); |
| 187 | if (isNpmGlobal) { |
| 188 | return { |
| 189 | kind: 'npm-global', |
| 190 | manualCommand: `npm install -g ${pkgName}@latest`, |
| 191 | autoCommands: [['npm', 'install', '-g', `${pkgName}@latest`]], |
| 192 | }; |
| 193 | } |
| 194 | |
| 195 | return { |
| 196 | kind: 'unknown', |
| 197 | manualInstructions: [ |
| 198 | `Homebrew: brew update && brew upgrade ${pkgName}`, |
| 199 | `npm: npm install -g ${pkgName}@latest`, |
| 200 | `npx: npx always fetches the latest when using @latest`, |
| 201 | ], |
| 202 | }; |
| 203 | } |
| 204 | |
| 205 | // --- Latest version lookup --- |
| 206 |
no outgoing calls
no test coverage detected