Right now, if there's a requested plugin or a plugin that's a dependency that is lower than a required version of the same plugin, that lower version of the plugin is "replaced" by the higher version. However, one issue with this is that the lower version might have required dependencies that the higher one doesn't require, and these unneeded plugins are never cleaned up and will be downloaded even though they're not needed.
Example: Suppose the user wants to download DotCi (version 2.3) and the latest version of DotCi-InstallPackages.
DotCi:2.3 depends on matrix project and github-oauth, github-oauth has an entire tree of dependencies
DotCi-InstallPackages depends on DotCi:2.32.1, which no longer depends on github-oauth
DotCi:2.3 will be replaced by DotCi:2.32.1, but all of its dependencies that are no longer needed will still be marked as being needed to be installed
If a plugin is replaced by a higher version, the original plugin's children should probably be removed from the list of installed plugins (and also replaced with the higher version's dependencies) . But what if one of those children was replaces a lower version of another plugin? You would have to restore the original version of that other plugin.
This seems like it could get complicated somewhat quickly, so another way to do this could be to build the full dependency tree without any replacements first, then "optimize" the tree after.