diff --git a/commands/core/download.go b/commands/core/download.go
index a64d35e4ae1..7c686f4aee9 100644
--- a/commands/core/download.go
+++ b/commands/core/download.go
@@ -36,7 +36,7 @@ func PlatformDownload(ctx context.Context, req *rpc.PlatformDownloadRequest, dow
 	}
 	defer release()
 
-	version, err := commands.ParseVersion(req)
+	version, err := commands.ParseVersion(req.GetVersion())
 	if err != nil {
 		return nil, &cmderrors.InvalidVersionError{Cause: err}
 	}
diff --git a/commands/core/install.go b/commands/core/install.go
index ce012081526..a568a9dcfba 100644
--- a/commands/core/install.go
+++ b/commands/core/install.go
@@ -35,7 +35,7 @@ func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallRequest, downl
 		}
 		defer release()
 
-		version, err := commands.ParseVersion(req)
+		version, err := commands.ParseVersion(req.GetVersion())
 		if err != nil {
 			return &cmderrors.InvalidVersionError{Cause: err}
 		}
diff --git a/commands/instances.go b/commands/instances.go
index c06d81e93eb..e6d9a2ebd68 100644
--- a/commands/instances.go
+++ b/commands/instances.go
@@ -301,17 +301,13 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
 	}
 
 	// Create library manager and add libraries directories
-	lm := librariesmanager.NewLibraryManager(
-		pme.IndexDir,
-		pme.DownloadDir,
-	)
-	_ = instances.SetLibraryManager(instance, lm) // should never fail
+	lmb := librariesmanager.NewBuilder()
 
 	// Load libraries
 	for _, pack := range pme.GetPackages() {
 		for _, platform := range pack.Platforms {
 			if platformRelease := pme.GetInstalledPlatformRelease(platform); platformRelease != nil {
-				lm.AddLibrariesDir(&librariesmanager.LibrariesDir{
+				lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
 					PlatformRelease: platformRelease,
 					Path:            platformRelease.GetLibrariesDir(),
 					Location:        libraries.PlatformBuiltIn,
@@ -320,22 +316,33 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
 		}
 	}
 
-	if err := lm.LoadIndex(); err != nil {
+	indexFileName, err := globals.LibrariesIndexResource.IndexFileName()
+	if err != nil {
+		// should never happen
+		panic("failed getting libraries index file name: " + err.Error())
+	}
+	indexFile := pme.IndexDir.Join(indexFileName)
+
+	logrus.WithField("index", indexFile).Info("Loading libraries index file")
+	li, err := librariesindex.LoadIndex(indexFile)
+	if err != nil {
 		s := status.Newf(codes.FailedPrecondition, tr("Loading index file: %v"), err)
 		responseError(s)
+		li = librariesindex.EmptyIndex
 	}
+	instances.SetLibrariesIndex(instance, li)
 
 	if profile == nil {
 		// Add directories of libraries bundled with IDE
 		if bundledLibsDir := configuration.IDEBuiltinLibrariesDir(configuration.Settings); bundledLibsDir != nil {
-			lm.AddLibrariesDir(&librariesmanager.LibrariesDir{
+			lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
 				Path:     bundledLibsDir,
 				Location: libraries.IDEBuiltIn,
 			})
 		}
 
 		// Add libraries directory from config file
-		lm.AddLibrariesDir(&librariesmanager.LibrariesDir{
+		lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
 			Path:     configuration.LibrariesDir(configuration.Settings),
 			Location: libraries.User,
 		})
@@ -349,17 +356,14 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
 			if !libDir.IsDir() {
 				// Download library
 				taskCallback(&rpc.TaskProgress{Name: tr("Downloading library %s", libraryRef)})
-				libRelease := lm.Index.FindRelease(&librariesindex.Reference{
-					Name:    libraryRef.Library,
-					Version: libraryRef.Version,
-				})
-				if libRelease == nil {
+				libRelease, err := li.FindRelease(libraryRef.Library, libraryRef.Version)
+				if err != nil {
 					taskCallback(&rpc.TaskProgress{Name: tr("Library %s not found", libraryRef)})
 					err := &cmderrors.LibraryNotFoundError{Library: libraryRef.Library}
 					responseError(err.ToRPCStatus())
 					continue
 				}
-				if err := libRelease.Resource.Download(lm.DownloadsDir, nil, libRelease.String(), downloadCallback, ""); err != nil {
+				if err := libRelease.Resource.Download(pme.DownloadDir, nil, libRelease.String(), downloadCallback, ""); err != nil {
 					taskCallback(&rpc.TaskProgress{Name: tr("Error downloading library %s", libraryRef)})
 					e := &cmderrors.FailedLibraryInstallError{Cause: err}
 					responseError(e.ToRPCStatus())
@@ -369,7 +373,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
 
 				// Install library
 				taskCallback(&rpc.TaskProgress{Name: tr("Installing library %s", libraryRef)})
-				if err := libRelease.Resource.Install(lm.DownloadsDir, libRoot, libDir); err != nil {
+				if err := libRelease.Resource.Install(pme.DownloadDir, libRoot, libDir); err != nil {
 					taskCallback(&rpc.TaskProgress{Name: tr("Error installing library %s", libraryRef)})
 					e := &cmderrors.FailedLibraryInstallError{Cause: err}
 					responseError(e.ToRPCStatus())
@@ -378,16 +382,23 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
 				taskCallback(&rpc.TaskProgress{Completed: true})
 			}
 
-			lm.AddLibrariesDir(&librariesmanager.LibrariesDir{
+			lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
 				Path:     libRoot,
 				Location: libraries.User,
 			})
 		}
 	}
 
-	for _, status := range lm.RescanLibraries() {
-		logrus.WithError(status.Err()).Warnf("Error loading library")
-		// TODO: report as warning: responseError(err)
+	lm := lmb.Build()
+	_ = instances.SetLibraryManager(instance, lm) // should never fail
+
+	{
+		lmi, release := lm.NewInstaller()
+		for _, status := range lmi.RescanLibraries() {
+			logrus.WithError(status.Err()).Warnf("Error loading library")
+			// TODO: report as warning: responseError(err)
+		}
+		release()
 	}
 
 	// Refreshes the locale used, this will change the
@@ -409,23 +420,18 @@ func Destroy(ctx context.Context, req *rpc.DestroyRequest) (*rpc.DestroyResponse
 // UpdateLibrariesIndex updates the library_index.json
 func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexRequest, downloadCB rpc.DownloadProgressCB) error {
 	logrus.Info("Updating libraries index")
-	lm, err := instances.GetLibraryManager(req.GetInstance())
+	pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance())
 	if err != nil {
 		return err
 	}
+	indexDir := pme.IndexDir
+	release()
 
-	if err := lm.IndexFile.Parent().MkdirAll(); err != nil {
+	if err := indexDir.MkdirAll(); err != nil {
 		return &cmderrors.PermissionDeniedError{Message: tr("Could not create index directory"), Cause: err}
 	}
 
-	// Create a temp dir to stage all downloads
-	tmp, err := paths.MkTempDir("", "library_index_download")
-	if err != nil {
-		return &cmderrors.TempDirCreationFailedError{Cause: err}
-	}
-	defer tmp.RemoveAll()
-
-	if err := globals.LibrariesIndexResource.Download(lm.IndexFile.Parent(), downloadCB); err != nil {
+	if err := globals.LibrariesIndexResource.Download(indexDir, downloadCB); err != nil {
 		return err
 	}
 
diff --git a/commands/internal/instances/instances.go b/commands/internal/instances/instances.go
index 0621047f4bd..a26a2dee1f8 100644
--- a/commands/internal/instances/instances.go
+++ b/commands/internal/instances/instances.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/arduino/arduino-cli/commands/cmderrors"
 	"github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager"
+	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex"
 	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesmanager"
 	rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
 	"github.com/arduino/arduino-cli/version"
@@ -17,6 +18,7 @@ import (
 type coreInstance struct {
 	pm *packagemanager.PackageManager
 	lm *librariesmanager.LibrariesManager
+	li *librariesindex.Index
 }
 
 // instances contains all the running Arduino Core Services instances
@@ -60,6 +62,49 @@ func GetLibraryManager(inst *rpc.Instance) (*librariesmanager.LibrariesManager,
 	return i.lm, nil
 }
 
+// GetLibraryManagerExplorer returns the library manager Explorer for the given instance.
+func GetLibraryManagerExplorer(inst *rpc.Instance) (*librariesmanager.Explorer, func(), error) {
+	lm, err := GetLibraryManager(inst)
+	if err != nil {
+		return nil, nil, err
+	}
+	lmi, release := lm.NewExplorer()
+	return lmi, release, nil
+}
+
+// GetLibraryManagerInstaller returns the library manager Installer for the given instance.
+func GetLibraryManagerInstaller(inst *rpc.Instance) (*librariesmanager.Installer, func(), error) {
+	lm, err := GetLibraryManager(inst)
+	if err != nil {
+		return nil, nil, err
+	}
+	lmi, release := lm.NewInstaller()
+	return lmi, release, nil
+}
+
+// GetLibrariesIndex returns the library index for the given instance.
+func GetLibrariesIndex(inst *rpc.Instance) (*librariesindex.Index, error) {
+	instancesMux.Lock()
+	defer instancesMux.Unlock()
+	i := instances[inst.GetId()]
+	if i == nil {
+		return nil, &cmderrors.InvalidInstanceError{}
+	}
+	return i.li, nil
+}
+
+// SetLibrariesIndex sets the library index for the given instance.
+func SetLibrariesIndex(inst *rpc.Instance, li *librariesindex.Index) error {
+	instancesMux.Lock()
+	defer instancesMux.Unlock()
+	i := instances[inst.GetId()]
+	if i == nil {
+		return &cmderrors.InvalidInstanceError{}
+	}
+	i.li = li
+	return nil
+}
+
 // SetLibraryManager sets the library manager for the given instance.
 func SetLibraryManager(inst *rpc.Instance, lm *librariesmanager.LibrariesManager) bool {
 	instancesMux.Lock()
@@ -74,16 +119,18 @@ func SetLibraryManager(inst *rpc.Instance, lm *librariesmanager.LibrariesManager
 
 // Create a new *rpc.Instance ready to be initialized
 func Create(dataDir, packagesDir, downloadsDir *paths.Path, extraUserAgent ...string) (*rpc.Instance, error) {
-	instance := &coreInstance{}
-
 	// Create package manager
 	userAgent := "arduino-cli/" + version.VersionInfo.VersionString
 	for _, ua := range extraUserAgent {
 		userAgent += " " + ua
 	}
 	tempDir := dataDir.Join("tmp")
-	instance.pm = packagemanager.NewBuilder(dataDir, packagesDir, downloadsDir, tempDir, userAgent).Build()
-	instance.lm = librariesmanager.NewLibraryManager(dataDir, downloadsDir)
+
+	instance := &coreInstance{
+		pm: packagemanager.NewBuilder(dataDir, packagesDir, downloadsDir, tempDir, userAgent).Build(),
+		lm: librariesmanager.NewBuilder().Build(),
+		li: librariesindex.EmptyIndex,
+	}
 
 	// Save instance
 	instancesMux.Lock()
diff --git a/commands/lib/download.go b/commands/lib/download.go
index b8019aff488..9d174ae1927 100644
--- a/commands/lib/download.go
+++ b/commands/lib/download.go
@@ -18,13 +18,14 @@ package lib
 import (
 	"context"
 
+	"github.com/arduino/arduino-cli/commands"
 	"github.com/arduino/arduino-cli/commands/cmderrors"
 	"github.com/arduino/arduino-cli/commands/internal/instances"
 	"github.com/arduino/arduino-cli/internal/arduino/httpclient"
 	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex"
-	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesmanager"
 	"github.com/arduino/arduino-cli/internal/i18n"
 	rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
+	"github.com/arduino/go-paths-helper"
 	"github.com/sirupsen/logrus"
 )
 
@@ -35,26 +36,39 @@ var tr = i18n.Tr
 func LibraryDownload(ctx context.Context, req *rpc.LibraryDownloadRequest, downloadCB rpc.DownloadProgressCB) (*rpc.LibraryDownloadResponse, error) {
 	logrus.Info("Executing `arduino-cli lib download`")
 
-	lm, err := instances.GetLibraryManager(req.GetInstance())
+	var downloadsDir *paths.Path
+	if pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()); err != nil {
+		return nil, err
+	} else {
+		downloadsDir = pme.DownloadDir
+		release()
+	}
+
+	li, err := instances.GetLibrariesIndex(req.GetInstance())
 	if err != nil {
 		return nil, err
 	}
 
 	logrus.Info("Preparing download")
 
-	lib, err := findLibraryIndexRelease(lm.Index, req)
+	version, err := commands.ParseVersion(req.GetVersion())
+	if err != nil {
+		return nil, err
+	}
+
+	lib, err := li.FindRelease(req.GetName(), version)
 	if err != nil {
 		return nil, err
 	}
 
-	if err := downloadLibrary(lm, lib, downloadCB, func(*rpc.TaskProgress) {}, "download"); err != nil {
+	if err := downloadLibrary(downloadsDir, lib, downloadCB, func(*rpc.TaskProgress) {}, "download"); err != nil {
 		return nil, err
 	}
 
 	return &rpc.LibraryDownloadResponse{}, nil
 }
 
-func downloadLibrary(lm *librariesmanager.LibrariesManager, libRelease *librariesindex.Release,
+func downloadLibrary(downloadsDir *paths.Path, libRelease *librariesindex.Release,
 	downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, queryParameter string) error {
 
 	taskCB(&rpc.TaskProgress{Name: tr("Downloading %s", libRelease)})
@@ -62,7 +76,7 @@ func downloadLibrary(lm *librariesmanager.LibrariesManager, libRelease *librarie
 	if err != nil {
 		return &cmderrors.FailedDownloadError{Message: tr("Can't download library"), Cause: err}
 	}
-	if err := libRelease.Resource.Download(lm.DownloadsDir, config, libRelease.String(), downloadCB, queryParameter); err != nil {
+	if err := libRelease.Resource.Download(downloadsDir, config, libRelease.String(), downloadCB, queryParameter); err != nil {
 		return &cmderrors.FailedDownloadError{Message: tr("Can't download library"), Cause: err}
 	}
 	taskCB(&rpc.TaskProgress{Completed: true})
diff --git a/commands/lib/install.go b/commands/lib/install.go
index 4f57741456d..873e442e4a0 100644
--- a/commands/lib/install.go
+++ b/commands/lib/install.go
@@ -33,25 +33,27 @@ import (
 
 // LibraryInstall resolves the library dependencies, then downloads and installs the libraries into the install location.
 func LibraryInstall(ctx context.Context, req *rpc.LibraryInstallRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
-	lm, err := instances.GetLibraryManager(req.GetInstance())
+	// Obtain the library index from the manager
+	li, err := instances.GetLibrariesIndex(req.GetInstance())
 	if err != nil {
 		return err
 	}
 
 	toInstall := map[string]*rpc.LibraryDependencyStatus{}
-	installLocation := libraries.FromRPCLibraryInstallLocation(req.GetInstallLocation())
 	if req.GetNoDeps() {
 		toInstall[req.GetName()] = &rpc.LibraryDependencyStatus{
 			Name:            req.GetName(),
 			VersionRequired: req.GetVersion(),
 		}
 	} else {
-		res, err := LibraryResolveDependencies(ctx, &rpc.LibraryResolveDependenciesRequest{
-			Instance:                      req.GetInstance(),
-			Name:                          req.GetName(),
-			Version:                       req.GetVersion(),
-			DoNotUpdateInstalledLibraries: req.GetNoOverwrite(),
-		})
+		// Obtain the library explorer from the instance
+		lme, releaseLme, err := instances.GetLibraryManagerExplorer(req.GetInstance())
+		if err != nil {
+			return err
+		}
+
+		res, err := libraryResolveDependencies(ctx, lme, li, req.GetName(), req.GetVersion(), req.GetNoOverwrite())
+		releaseLme()
 		if err != nil {
 			return err
 		}
@@ -69,18 +71,36 @@ func LibraryInstall(ctx context.Context, req *rpc.LibraryInstallRequest, downloa
 		}
 	}
 
+	// Obtain the download directory
+	var downloadsDir *paths.Path
+	if pme, releasePme, err := instances.GetPackageManagerExplorer(req.GetInstance()); err != nil {
+		return err
+	} else {
+		downloadsDir = pme.DownloadDir
+		releasePme()
+	}
+
+	// Obtain the library installer from the manager
+	lmi, releaseLmi, err := instances.GetLibraryManagerInstaller(req.GetInstance())
+	if err != nil {
+		return err
+	}
+	defer releaseLmi()
+
 	// Find the libReleasesToInstall to install
 	libReleasesToInstall := map[*librariesindex.Release]*librariesmanager.LibraryInstallPlan{}
+	installLocation := libraries.FromRPCLibraryInstallLocation(req.GetInstallLocation())
 	for _, lib := range toInstall {
-		libRelease, err := findLibraryIndexRelease(lm.Index, &rpc.LibraryInstallRequest{
-			Name:    lib.GetName(),
-			Version: lib.GetVersionRequired(),
-		})
+		version, err := commands.ParseVersion(lib.GetVersionRequired())
+		if err != nil {
+			return err
+		}
+		libRelease, err := li.FindRelease(lib.GetName(), version)
 		if err != nil {
 			return err
 		}
 
-		installTask, err := lm.InstallPrerequisiteCheck(libRelease.Library.Name, libRelease.Version, installLocation)
+		installTask, err := lmi.InstallPrerequisiteCheck(libRelease.Library.Name, libRelease.Version, installLocation)
 		if err != nil {
 			return err
 		}
@@ -109,10 +129,10 @@ func LibraryInstall(ctx context.Context, req *rpc.LibraryInstallRequest, downloa
 				downloadReason += "-builtin"
 			}
 		}
-		if err := downloadLibrary(lm, libRelease, downloadCB, taskCB, downloadReason); err != nil {
+		if err := downloadLibrary(downloadsDir, libRelease, downloadCB, taskCB, downloadReason); err != nil {
 			return err
 		}
-		if err := installLibrary(lm, libRelease, installTask, taskCB); err != nil {
+		if err := installLibrary(lmi, downloadsDir, libRelease, installTask, taskCB); err != nil {
 			return err
 		}
 	}
@@ -124,18 +144,21 @@ func LibraryInstall(ctx context.Context, req *rpc.LibraryInstallRequest, downloa
 	return nil
 }
 
-func installLibrary(lm *librariesmanager.LibrariesManager, libRelease *librariesindex.Release, installTask *librariesmanager.LibraryInstallPlan, taskCB rpc.TaskProgressCB) error {
+func installLibrary(lmi *librariesmanager.Installer, downloadsDir *paths.Path, libRelease *librariesindex.Release, installTask *librariesmanager.LibraryInstallPlan, taskCB rpc.TaskProgressCB) error {
 	taskCB(&rpc.TaskProgress{Name: tr("Installing %s", libRelease)})
 	logrus.WithField("library", libRelease).Info("Installing library")
 
 	if libReplaced := installTask.ReplacedLib; libReplaced != nil {
 		taskCB(&rpc.TaskProgress{Message: tr("Replacing %[1]s with %[2]s", libReplaced, libRelease)})
-		if err := lm.Uninstall(libReplaced); err != nil {
+		if err := lmi.Uninstall(libReplaced); err != nil {
 			return &cmderrors.FailedLibraryInstallError{
 				Cause: fmt.Errorf("%s: %s", tr("could not remove old library"), err)}
 		}
 	}
-	if err := lm.Install(libRelease, installTask.TargetPath); err != nil {
+
+	installPath := installTask.TargetPath
+	tmpDirPath := installPath.Parent()
+	if err := libRelease.Resource.Install(downloadsDir, tmpDirPath, installPath); err != nil {
 		return &cmderrors.FailedLibraryInstallError{Cause: err}
 	}
 
@@ -149,7 +172,9 @@ func ZipLibraryInstall(ctx context.Context, req *rpc.ZipLibraryInstallRequest, t
 	if err != nil {
 		return err
 	}
-	if err := lm.InstallZipLib(ctx, paths.New(req.GetPath()), req.GetOverwrite()); err != nil {
+	lmi, release := lm.NewInstaller()
+	defer release()
+	if err := lmi.InstallZipLib(ctx, paths.New(req.GetPath()), req.GetOverwrite()); err != nil {
 		return &cmderrors.FailedLibraryInstallError{Cause: err}
 	}
 	taskCB(&rpc.TaskProgress{Message: tr("Library installed"), Completed: true})
@@ -162,7 +187,9 @@ func GitLibraryInstall(ctx context.Context, req *rpc.GitLibraryInstallRequest, t
 	if err != nil {
 		return err
 	}
-	if err := lm.InstallGitLib(req.GetUrl(), req.GetOverwrite()); err != nil {
+	lmi, release := lm.NewInstaller()
+	defer release()
+	if err := lmi.InstallGitLib(req.GetUrl(), req.GetOverwrite()); err != nil {
 		return &cmderrors.FailedLibraryInstallError{Cause: err}
 	}
 	taskCB(&rpc.TaskProgress{Message: tr("Library installed"), Completed: true})
diff --git a/commands/lib/list.go b/commands/lib/list.go
index 0f2f7ec1339..f5b28bdc426 100644
--- a/commands/lib/list.go
+++ b/commands/lib/list.go
@@ -42,16 +42,22 @@ func LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.Library
 	}
 	defer release()
 
-	lm, err := instances.GetLibraryManager(req.GetInstance())
+	li, err := instances.GetLibrariesIndex(req.GetInstance())
 	if err != nil {
 		return nil, err
 	}
 
+	lme, release, err := instances.GetLibraryManagerExplorer(req.GetInstance())
+	if err != nil {
+		return nil, err
+	}
+	defer release()
+
 	nameFilter := strings.ToLower(req.GetName())
 
 	var allLibs []*installedLib
 	if fqbnString := req.GetFqbn(); fqbnString != "" {
-		allLibs = listLibraries(lm, req.GetUpdatable(), true)
+		allLibs = listLibraries(lme, li, req.GetUpdatable(), true)
 		fqbn, err := cores.ParseFQBN(req.GetFqbn())
 		if err != nil {
 			return nil, &cmderrors.InvalidFQBNError{Cause: err}
@@ -91,7 +97,7 @@ func LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.Library
 			allLibs = append(allLibs, lib)
 		}
 	} else {
-		allLibs = listLibraries(lm, req.GetUpdatable(), req.GetAll())
+		allLibs = listLibraries(lme, li, req.GetUpdatable(), req.GetAll())
 	}
 
 	installedLibs := []*rpc.InstalledLibrary{}
@@ -117,25 +123,25 @@ func LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.Library
 }
 
 // listLibraries returns the list of installed libraries. If updatable is true it
-// returns only the libraries that may be updated.
-func listLibraries(lm *librariesmanager.LibrariesManager, updatable bool, all bool) []*installedLib {
+// returns only the libraries that may be updated by looking at the index for updates.
+// If all is true, it returns all the libraries (including the libraries builtin in the
+// platforms), otherwise only the user installed libraries.
+func listLibraries(lme *librariesmanager.Explorer, li *librariesindex.Index, updatable bool, all bool) []*installedLib {
 	res := []*installedLib{}
-	for _, libAlternatives := range lm.Libraries {
-		for _, lib := range libAlternatives {
-			if !all {
-				if lib.Location != libraries.User {
-					continue
-				}
-			}
-			available := lm.Index.FindLibraryUpdate(lib)
-			if updatable && available == nil {
+	for _, lib := range lme.FindAllInstalled() {
+		if !all {
+			if lib.Location != libraries.User {
 				continue
 			}
-			res = append(res, &installedLib{
-				Library:   lib,
-				Available: available,
-			})
 		}
+		available := li.FindLibraryUpdate(lib)
+		if updatable && available == nil {
+			continue
+		}
+		res = append(res, &installedLib{
+			Library:   lib,
+			Available: available,
+		})
 	}
 	return res
 }
diff --git a/commands/lib/resolve_deps.go b/commands/lib/resolve_deps.go
index b25073424d2..b734e3d4ec7 100644
--- a/commands/lib/resolve_deps.go
+++ b/commands/lib/resolve_deps.go
@@ -20,55 +20,69 @@ import (
 	"errors"
 	"sort"
 
+	"github.com/arduino/arduino-cli/commands"
 	"github.com/arduino/arduino-cli/commands/cmderrors"
 	"github.com/arduino/arduino-cli/commands/internal/instances"
 	"github.com/arduino/arduino-cli/internal/arduino/libraries"
 	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex"
+	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesmanager"
 	rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
 	semver "go.bug.st/relaxed-semver"
 )
 
 // LibraryResolveDependencies FIXMEDOC
 func LibraryResolveDependencies(ctx context.Context, req *rpc.LibraryResolveDependenciesRequest) (*rpc.LibraryResolveDependenciesResponse, error) {
-	lm, err := instances.GetLibraryManager(req.GetInstance())
+	lme, release, err := instances.GetLibraryManagerExplorer(req.GetInstance())
+	if err != nil {
+		return nil, err
+	}
+	defer release()
+
+	li, err := instances.GetLibrariesIndex(req.GetInstance())
+	if err != nil {
+		return nil, err
+	}
+
+	return libraryResolveDependencies(ctx, lme, li, req.GetName(), req.GetVersion(), req.GetDoNotUpdateInstalledLibraries())
+}
+
+func libraryResolveDependencies(ctx context.Context, lme *librariesmanager.Explorer, li *librariesindex.Index,
+	reqName, reqVersion string, noOverwrite bool) (*rpc.LibraryResolveDependenciesResponse, error) {
+	version, err := commands.ParseVersion(reqVersion)
 	if err != nil {
 		return nil, err
 	}
 
 	// Search the requested lib
-	reqLibRelease, err := findLibraryIndexRelease(lm.Index, req)
+	reqLibRelease, err := li.FindRelease(reqName, version)
 	if err != nil {
 		return nil, err
 	}
 
 	// Extract all installed libraries
 	installedLibs := map[string]*libraries.Library{}
-	for _, lib := range listLibraries(lm, false, false) {
+	for _, lib := range listLibraries(lme, li, false, false) {
 		installedLibs[lib.Library.Name] = lib.Library
 	}
 
 	// Resolve all dependencies...
 	var overrides []*librariesindex.Release
-	if req.GetDoNotUpdateInstalledLibraries() {
-		libs := lm.FindAllInstalled()
+	if noOverwrite {
+		libs := lme.FindAllInstalled()
 		libs = libs.FilterByVersionAndInstallLocation(nil, libraries.User)
 		for _, lib := range libs {
-			release := lm.Index.FindRelease(&librariesindex.Reference{
-				Name:    lib.Name,
-				Version: lib.Version,
-			})
-			if release != nil {
+			if release, err := li.FindRelease(lib.Name, lib.Version); err == nil {
 				overrides = append(overrides, release)
 			}
 		}
 	}
-	deps := lm.Index.ResolveDependencies(reqLibRelease, overrides)
+	deps := li.ResolveDependencies(reqLibRelease, overrides)
 
 	// If no solution has been found
 	if len(deps) == 0 {
 		// Check if there is a problem with the first level deps
 		for _, directDep := range reqLibRelease.GetDependencies() {
-			if _, ok := lm.Index.Libraries[directDep.GetName()]; !ok {
+			if _, ok := li.Libraries[directDep.GetName()]; !ok {
 				err := errors.New(tr("dependency '%s' is not available", directDep.GetName()))
 				return nil, &cmderrors.LibraryDependenciesResolutionFailedError{Cause: err}
 			}
diff --git a/commands/lib/search.go b/commands/lib/search.go
index cbd9bf4901a..7c2d21cdd70 100644
--- a/commands/lib/search.go
+++ b/commands/lib/search.go
@@ -22,26 +22,25 @@ import (
 
 	"github.com/arduino/arduino-cli/commands/internal/instances"
 	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex"
-	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesmanager"
 	rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
 	semver "go.bug.st/relaxed-semver"
 )
 
 // LibrarySearch FIXMEDOC
 func LibrarySearch(ctx context.Context, req *rpc.LibrarySearchRequest) (*rpc.LibrarySearchResponse, error) {
-	lm, err := instances.GetLibraryManager(req.GetInstance())
+	li, err := instances.GetLibrariesIndex(req.GetInstance())
 	if err != nil {
 		return nil, err
 	}
-	return searchLibrary(req, lm), nil
+	return searchLibrary(req, li), nil
 }
 
-func searchLibrary(req *rpc.LibrarySearchRequest, lm *librariesmanager.LibrariesManager) *rpc.LibrarySearchResponse {
+func searchLibrary(req *rpc.LibrarySearchRequest, li *librariesindex.Index) *rpc.LibrarySearchResponse {
 	res := []*rpc.SearchedLibrary{}
 	query := req.GetSearchArgs()
 	matcher := MatcherFromQueryString(query)
 
-	for _, lib := range lm.Index.Libraries {
+	for _, lib := range li.Libraries {
 		if matcher(lib) {
 			res = append(res, indexLibraryToRPCSearchLibrary(lib, req.GetOmitReleasesDetails()))
 		}
diff --git a/commands/lib/search_test.go b/commands/lib/search_test.go
index e1e0b3157f2..faae69ef1a9 100644
--- a/commands/lib/search_test.go
+++ b/commands/lib/search_test.go
@@ -19,22 +19,24 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesmanager"
+	"github.com/arduino/arduino-cli/internal/arduino/globals"
+	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex"
 	rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
 	paths "github.com/arduino/go-paths-helper"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
-var customIndexPath = paths.New("testdata", "test1")
-var fullIndexPath = paths.New("testdata", "full")
-var qualifiedSearchIndexPath = paths.New("testdata", "qualified_search")
+var indexFilename, _ = globals.LibrariesIndexResource.IndexFileName()
+var customIndexPath = paths.New("testdata", "test1", indexFilename)
+var fullIndexPath = paths.New("testdata", "full", indexFilename)
+var qualifiedSearchIndexPath = paths.New("testdata", "qualified_search", indexFilename)
 
 func TestSearchLibrary(t *testing.T) {
-	lm := librariesmanager.NewLibraryManager(customIndexPath, nil)
-	lm.LoadIndex()
+	li, err := librariesindex.LoadIndex(customIndexPath)
+	require.NoError(t, err)
 
-	resp := searchLibrary(&rpc.LibrarySearchRequest{SearchArgs: "test"}, lm)
+	resp := searchLibrary(&rpc.LibrarySearchRequest{SearchArgs: "test"}, li)
 	assert := assert.New(t)
 	assert.Equal(resp.GetStatus(), rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_SUCCESS)
 	assert.Equal(len(resp.GetLibraries()), 2)
@@ -43,10 +45,10 @@ func TestSearchLibrary(t *testing.T) {
 }
 
 func TestSearchLibrarySimilar(t *testing.T) {
-	lm := librariesmanager.NewLibraryManager(customIndexPath, nil)
-	lm.LoadIndex()
+	li, err := librariesindex.LoadIndex(customIndexPath)
+	require.NoError(t, err)
 
-	resp := searchLibrary(&rpc.LibrarySearchRequest{SearchArgs: "arduino"}, lm)
+	resp := searchLibrary(&rpc.LibrarySearchRequest{SearchArgs: "arduino"}, li)
 	assert := assert.New(t)
 	assert.Equal(resp.GetStatus(), rpc.LibrarySearchStatus_LIBRARY_SEARCH_STATUS_SUCCESS)
 	assert.Equal(len(resp.GetLibraries()), 2)
@@ -59,12 +61,12 @@ func TestSearchLibrarySimilar(t *testing.T) {
 }
 
 func TestSearchLibraryFields(t *testing.T) {
-	lm := librariesmanager.NewLibraryManager(fullIndexPath, nil)
-	lm.LoadIndex()
+	li, err := librariesindex.LoadIndex(fullIndexPath)
+	require.NoError(t, err)
 
 	query := func(q string) []string {
 		libs := []string{}
-		for _, lib := range searchLibrary(&rpc.LibrarySearchRequest{SearchArgs: q}, lm).GetLibraries() {
+		for _, lib := range searchLibrary(&rpc.LibrarySearchRequest{SearchArgs: q}, li).GetLibraries() {
 			libs = append(libs, lib.GetName())
 		}
 		return libs
@@ -97,12 +99,12 @@ func TestSearchLibraryFields(t *testing.T) {
 }
 
 func TestSearchLibraryWithQualifiers(t *testing.T) {
-	lm := librariesmanager.NewLibraryManager(qualifiedSearchIndexPath, nil)
-	lm.LoadIndex()
+	li, err := librariesindex.LoadIndex(qualifiedSearchIndexPath)
+	require.NoError(t, err)
 
 	query := func(q string) []string {
 		libs := []string{}
-		for _, lib := range searchLibrary(&rpc.LibrarySearchRequest{SearchArgs: q}, lm).GetLibraries() {
+		for _, lib := range searchLibrary(&rpc.LibrarySearchRequest{SearchArgs: q}, li).GetLibraries() {
 			libs = append(libs, lib.GetName())
 		}
 		return libs
diff --git a/commands/lib/uninstall.go b/commands/lib/uninstall.go
index 28f10153459..aee4ea68143 100644
--- a/commands/lib/uninstall.go
+++ b/commands/lib/uninstall.go
@@ -18,6 +18,7 @@ package lib
 import (
 	"context"
 
+	"github.com/arduino/arduino-cli/commands"
 	"github.com/arduino/arduino-cli/commands/cmderrors"
 	"github.com/arduino/arduino-cli/commands/internal/instances"
 	"github.com/arduino/arduino-cli/internal/arduino/libraries"
@@ -32,13 +33,14 @@ func LibraryUninstall(ctx context.Context, req *rpc.LibraryUninstallRequest, tas
 		return err
 	}
 
-	ref, err := createLibIndexReference(req)
+	version, err := commands.ParseVersion(req.GetVersion())
 	if err != nil {
-		return &cmderrors.InvalidLibraryError{Cause: err}
+		return err
 	}
+	lmi, release := lm.NewInstaller()
+	defer release()
 
-	libs := lm.FindByReference(ref, libraries.User)
-
+	libs := lmi.FindByReference(req.GetName(), version, libraries.User)
 	if len(libs) == 0 {
 		taskCB(&rpc.TaskProgress{Message: tr("Library %s is not installed", req.GetName()), Completed: true})
 		return nil
@@ -46,7 +48,7 @@ func LibraryUninstall(ctx context.Context, req *rpc.LibraryUninstallRequest, tas
 
 	if len(libs) == 1 {
 		taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s", libs)})
-		lm.Uninstall(libs[0])
+		lmi.Uninstall(libs[0])
 		taskCB(&rpc.TaskProgress{Completed: true})
 		return nil
 	}
diff --git a/commands/lib/upgrade.go b/commands/lib/upgrade.go
index 824d645d648..0377744b972 100644
--- a/commands/lib/upgrade.go
+++ b/commands/lib/upgrade.go
@@ -26,12 +26,19 @@ import (
 
 // LibraryUpgradeAll upgrades all the available libraries
 func LibraryUpgradeAll(req *rpc.LibraryUpgradeAllRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
-	lm, err := instances.GetLibraryManager(req.GetInstance())
+	li, err := instances.GetLibrariesIndex(req.GetInstance())
 	if err != nil {
 		return err
 	}
 
-	if err := upgrade(req.GetInstance(), listLibraries(lm, true, false), downloadCB, taskCB); err != nil {
+	lme, release, err := instances.GetLibraryManagerExplorer(req.GetInstance())
+	if err != nil {
+		return err
+	}
+	libsToUpgrade := listLibraries(lme, li, true, false)
+	release()
+
+	if err := upgrade(req.GetInstance(), libsToUpgrade, downloadCB, taskCB); err != nil {
 		return err
 	}
 
@@ -44,14 +51,21 @@ func LibraryUpgradeAll(req *rpc.LibraryUpgradeAllRequest, downloadCB rpc.Downloa
 
 // LibraryUpgrade upgrades a library
 func LibraryUpgrade(ctx context.Context, req *rpc.LibraryUpgradeRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
-	lm, err := instances.GetLibraryManager(req.GetInstance())
+	li, err := instances.GetLibrariesIndex(req.GetInstance())
+	if err != nil {
+		return err
+	}
+
+	lme, release, err := instances.GetLibraryManagerExplorer(req.GetInstance())
 	if err != nil {
 		return err
 	}
+	libs := listLibraries(lme, li, false, false)
+	release()
 
 	// Get the library to upgrade
 	name := req.GetName()
-	lib := filterByName(listLibraries(lm, false, false), name)
+	lib := filterByName(libs, name)
 	if lib == nil {
 		// library not installed...
 		return &cmderrors.LibraryNotFoundError{Library: name}
diff --git a/commands/lib/utils.go b/commands/lib/utils.go
deleted file mode 100644
index 25e3364cdb1..00000000000
--- a/commands/lib/utils.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// This file is part of arduino-cli.
-//
-// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
-//
-// This software is released under the GNU General Public License version 3,
-// which covers the main part of arduino-cli.
-// The terms of this license can be found at:
-// https://www.gnu.org/licenses/gpl-3.0.en.html
-//
-// You can be released from the requirements of the above licenses by purchasing
-// a commercial license. Buying such a license is mandatory if you want to
-// modify or otherwise use the software for commercial activities involving the
-// Arduino software without disclosing the source code of your own applications.
-// To purchase a commercial license, send an email to license@arduino.cc.
-
-package lib
-
-import (
-	"github.com/arduino/arduino-cli/commands"
-	"github.com/arduino/arduino-cli/commands/cmderrors"
-	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex"
-)
-
-type libraryReferencer interface {
-	commands.Versioned
-	GetName() string
-}
-
-func createLibIndexReference(req libraryReferencer) (*librariesindex.Reference, error) {
-	version, err := commands.ParseVersion(req)
-	if err != nil {
-		return nil, &cmderrors.InvalidVersionError{Cause: err}
-	}
-
-	return &librariesindex.Reference{Name: req.GetName(), Version: version}, nil
-}
-
-func findLibraryIndexRelease(li *librariesindex.Index, req libraryReferencer) (*librariesindex.Release, error) {
-	ref, err := createLibIndexReference(req)
-	if err != nil {
-		return nil, err
-	}
-	lib := li.FindRelease(ref)
-	if lib == nil {
-		return nil, &cmderrors.LibraryNotFoundError{Library: ref.String()}
-	}
-	return lib, nil
-}
diff --git a/commands/version.go b/commands/version.go
index fe9d36ca727..2fd87215a2f 100644
--- a/commands/version.go
+++ b/commands/version.go
@@ -16,19 +16,20 @@
 package commands
 
 import (
+	"github.com/arduino/arduino-cli/commands/cmderrors"
 	semver "go.bug.st/relaxed-semver"
 )
 
-// Versioned is an object that provides a GetVersion() method
-type Versioned interface {
-	GetVersion() string
-}
-
-// ParseVersion returns the version parsed from an interface that provides
-// the GetVersion() method (interface Versioned)
-func ParseVersion(req Versioned) (*semver.Version, error) {
-	if req.GetVersion() != "" {
-		return semver.Parse(req.GetVersion())
+// ParseVersion returns the parsed version or nil if the version is
+// the empty string. An error is returned if the version is not valid
+// semver.
+func ParseVersion(version string) (*semver.Version, error) {
+	if version == "" {
+		return nil, nil
+	}
+	res, err := semver.Parse(version)
+	if err != nil {
+		return nil, &cmderrors.InvalidVersionError{Cause: err}
 	}
-	return nil, nil
+	return res, nil
 }
diff --git a/internal/arduino/builder/internal/detector/detector.go b/internal/arduino/builder/internal/detector/detector.go
index efc0bd9b970..7d0a67f7cdd 100644
--- a/internal/arduino/builder/internal/detector/detector.go
+++ b/internal/arduino/builder/internal/detector/detector.go
@@ -598,30 +598,30 @@ func LibrariesLoader(
 	if useCachedLibrariesResolution {
 		// Since we are using the cached libraries resolution
 		// the library manager is not needed.
-		lm = librariesmanager.NewLibraryManager(nil, nil)
+		lm = librariesmanager.NewBuilder().Build()
 	}
 	if librariesManager == nil {
-		lm = librariesmanager.NewLibraryManager(nil, nil)
+		lmb := librariesmanager.NewBuilder()
 
 		builtInLibrariesFolders := builtInLibrariesDirs
 		if builtInLibrariesFolders != nil {
 			if err := builtInLibrariesFolders.ToAbs(); err != nil {
 				return nil, nil, nil, err
 			}
-			lm.AddLibrariesDir(&librariesmanager.LibrariesDir{
+			lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
 				Path:     builtInLibrariesFolders,
 				Location: libraries.IDEBuiltIn,
 			})
 		}
 
 		if actualPlatform != targetPlatform {
-			lm.AddLibrariesDir(&librariesmanager.LibrariesDir{
+			lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
 				PlatformRelease: actualPlatform,
 				Path:            actualPlatform.GetLibrariesDir(),
 				Location:        libraries.ReferencedPlatformBuiltIn,
 			})
 		}
-		lm.AddLibrariesDir(&librariesmanager.LibrariesDir{
+		lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
 			PlatformRelease: targetPlatform,
 			Path:            targetPlatform.GetLibrariesDir(),
 			Location:        libraries.PlatformBuiltIn,
@@ -632,46 +632,39 @@ func LibrariesLoader(
 			return nil, nil, nil, err
 		}
 		for _, folder := range librariesFolders {
-			lm.AddLibrariesDir(&librariesmanager.LibrariesDir{
+			lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
 				Path:     folder,
 				Location: libraries.User, // XXX: Should be libraries.Unmanaged?
 			})
 		}
 
 		for _, dir := range libraryDirs {
-			lm.AddLibrariesDir(&librariesmanager.LibrariesDir{
+			lmb.AddLibrariesDir(&librariesmanager.LibrariesDir{
 				Path:            dir,
 				Location:        libraries.Unmanaged,
 				IsSingleLibrary: true,
 			})
 		}
 
-		for _, status := range lm.RescanLibraries() {
-			// With the refactoring of the initialization step of the CLI we changed how
-			// errors are returned when loading platforms and libraries, that meant returning a list of
-			// errors instead of a single one to enhance the experience for the user.
-			// I have no intention right now to start a refactoring of the legacy package too, so
-			// here's this shitty solution for now.
-			// When we're gonna refactor the legacy package this will be gone.
-			verboseOut.Write([]byte(status.Message()))
+		lm = lmb.Build()
+
+		{
+			lmi, release := lm.NewInstaller()
+			for _, status := range lmi.RescanLibraries() {
+				// With the refactoring of the initialization step of the CLI we changed how
+				// errors are returned when loading platforms and libraries, that meant returning a list of
+				// errors instead of a single one to enhance the experience for the user.
+				// I have no intention right now to start a refactoring of the legacy package too, so
+				// here's this shitty solution for now.
+				// When we're gonna refactor the legacy package this will be gone.
+				verboseOut.Write([]byte(status.Message()))
+			}
+			release()
 		}
 	}
 
-	resolver := librariesresolver.NewCppResolver()
-	if err := resolver.ScanIDEBuiltinLibraries(lm); err != nil {
-		return nil, nil, nil, err
-	}
-	if err := resolver.ScanUserAndUnmanagedLibraries(lm); err != nil {
-		return nil, nil, nil, err
-	}
-	if err := resolver.ScanPlatformLibraries(lm, targetPlatform); err != nil {
-		return nil, nil, nil, err
-	}
-	if actualPlatform != targetPlatform {
-		if err := resolver.ScanPlatformLibraries(lm, actualPlatform); err != nil {
-			return nil, nil, nil, err
-		}
-	}
+	allLibs := lm.FindAllInstalled()
+	resolver := librariesresolver.NewCppResolver(allLibs, targetPlatform, actualPlatform)
 	return lm, resolver, verboseOut.Bytes(), nil
 }
 
diff --git a/internal/arduino/libraries/librariesindex/index.go b/internal/arduino/libraries/librariesindex/index.go
index 92226146434..033630f910d 100644
--- a/internal/arduino/libraries/librariesindex/index.go
+++ b/internal/arduino/libraries/librariesindex/index.go
@@ -18,6 +18,7 @@ package librariesindex
 import (
 	"sort"
 
+	"github.com/arduino/arduino-cli/commands/cmderrors"
 	"github.com/arduino/arduino-cli/internal/arduino/libraries"
 	"github.com/arduino/arduino-cli/internal/arduino/resources"
 	rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
@@ -112,14 +113,19 @@ func (r *Release) String() string {
 // FindRelease search a library Release in the index. Returns nil if the
 // release is not found. If the version is not specified returns the latest
 // version available.
-func (idx *Index) FindRelease(ref *Reference) *Release {
-	if library, exists := idx.Libraries[ref.Name]; exists {
-		if ref.Version == nil {
-			return library.Latest
+func (idx *Index) FindRelease(name string, version *semver.Version) (*Release, error) {
+	if library, exists := idx.Libraries[name]; exists {
+		if version == nil {
+			return library.Latest, nil
+		}
+		if release, exists := library.Releases[version.NormalizedString()]; exists {
+			return release, nil
 		}
-		return library.Releases[ref.Version.NormalizedString()]
 	}
-	return nil
+	if version == nil {
+		return nil, &cmderrors.LibraryNotFoundError{Library: name + "@latest"}
+	}
+	return nil, &cmderrors.LibraryNotFoundError{Library: name + "@" + version.String()}
 }
 
 // FindIndexedLibrary search an indexed library that matches the provided
diff --git a/internal/arduino/libraries/librariesindex/index_test.go b/internal/arduino/libraries/librariesindex/index_test.go
index b347d2bc664..2340d550b85 100644
--- a/internal/arduino/libraries/librariesindex/index_test.go
+++ b/internal/arduino/libraries/librariesindex/index_test.go
@@ -49,27 +49,22 @@ func TestIndexer(t *testing.T) {
 	require.Equal(t, "", alp.Latest.Dependencies[0].GetConstraint().String())
 	require.Equal(t, "[1.0.0 1.1.0 1.2.0 1.2.1 1.2.2]", fmt.Sprintf("%v", alp.Versions()))
 
-	rtc100ref := &Reference{Name: "RTCZero", Version: semver.MustParse("1.0.0")}
-	require.Equal(t, "RTCZero@1.0.0", rtc100ref.String())
-	rtc100 := index.FindRelease(rtc100ref)
+	rtc100, err := index.FindRelease("RTCZero", semver.MustParse("1.0.0"))
+	require.NoError(t, err)
 	require.NotNil(t, rtc100)
 	require.Equal(t, "RTCZero@1.0.0", rtc100.String())
 
-	rtcLatestRef := &Reference{Name: "RTCZero"}
-	require.Equal(t, "RTCZero", rtcLatestRef.String())
-	rtcLatest := index.FindRelease(rtcLatestRef)
+	rtcLatest, err := index.FindRelease("RTCZero", nil)
+	require.NoError(t, err)
 	require.NotNil(t, rtcLatest)
 	require.Equal(t, "RTCZero@1.6.0", rtcLatest.String())
 
-	rtcInexistent := index.FindRelease(&Reference{
-		Name:    "RTCZero",
-		Version: semver.MustParse("0.0.0-blah"),
-	})
+	rtcInexistent, err := index.FindRelease("RTCZero", semver.MustParse("0.0.0-blah"))
+	require.Error(t, err)
 	require.Nil(t, rtcInexistent)
 
-	rtcInexistent = index.FindRelease(&Reference{
-		Name: "RTCZero-blah",
-	})
+	rtcInexistent, err = index.FindRelease("RTCZero-blah", nil)
+	require.Error(t, err)
 	require.Nil(t, rtcInexistent)
 
 	rtc := index.FindIndexedLibrary(&libraries.Library{Name: "RTCZero"})
@@ -95,16 +90,20 @@ func TestIndexer(t *testing.T) {
 	require.Contains(t, resolve1, alp.Releases["1.2.1"])
 	require.Contains(t, resolve1, rtc.Releases["1.6.0"])
 
-	oauth010 := index.FindRelease(&Reference{Name: "Arduino_OAuth", Version: semver.MustParse("0.1.0")})
+	oauth010, err := index.FindRelease("Arduino_OAuth", semver.MustParse("0.1.0"))
+	require.NoError(t, err)
 	require.NotNil(t, oauth010)
 	require.Equal(t, "Arduino_OAuth@0.1.0", oauth010.String())
-	eccx135 := index.FindRelease(&Reference{Name: "ArduinoECCX08", Version: semver.MustParse("1.3.5")})
+	eccx135, err := index.FindRelease("ArduinoECCX08", semver.MustParse("1.3.5"))
+	require.NoError(t, err)
 	require.NotNil(t, eccx135)
 	require.Equal(t, "ArduinoECCX08@1.3.5", eccx135.String())
-	bear172 := index.FindRelease(&Reference{Name: "ArduinoBearSSL", Version: semver.MustParse("1.7.2")})
+	bear172, err := index.FindRelease("ArduinoBearSSL", semver.MustParse("1.7.2"))
+	require.NoError(t, err)
 	require.NotNil(t, bear172)
 	require.Equal(t, "ArduinoBearSSL@1.7.2", bear172.String())
-	http040 := index.FindRelease(&Reference{Name: "ArduinoHttpClient", Version: semver.MustParse("0.4.0")})
+	http040, err := index.FindRelease("ArduinoHttpClient", semver.MustParse("0.4.0"))
+	require.NoError(t, err)
 	require.NotNil(t, http040)
 	require.Equal(t, "ArduinoHttpClient@0.4.0", http040.String())
 
diff --git a/internal/arduino/libraries/librariesindex/reference.go b/internal/arduino/libraries/librariesindex/reference.go
deleted file mode 100644
index a69663ee8f5..00000000000
--- a/internal/arduino/libraries/librariesindex/reference.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// This file is part of arduino-cli.
-//
-// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
-//
-// This software is released under the GNU General Public License version 3,
-// which covers the main part of arduino-cli.
-// The terms of this license can be found at:
-// https://www.gnu.org/licenses/gpl-3.0.en.html
-//
-// You can be released from the requirements of the above licenses by purchasing
-// a commercial license. Buying such a license is mandatory if you want to
-// modify or otherwise use the software for commercial activities involving the
-// Arduino software without disclosing the source code of your own applications.
-// To purchase a commercial license, send an email to license@arduino.cc.
-
-package librariesindex
-
-import (
-	semver "go.bug.st/relaxed-semver"
-)
-
-// Reference uniquely identify a Library in the library index
-type Reference struct {
-	Name    string          // The name of the parsed item.
-	Version *semver.Version // The Version of the parsed item.
-}
-
-func (r *Reference) String() string {
-	if r.Version == nil {
-		return r.Name
-	}
-	return r.Name + "@" + r.Version.String()
-}
diff --git a/internal/arduino/libraries/librariesmanager/install.go b/internal/arduino/libraries/librariesmanager/install.go
index 39af1f4b704..4517756e3e7 100644
--- a/internal/arduino/libraries/librariesmanager/install.go
+++ b/internal/arduino/libraries/librariesmanager/install.go
@@ -25,7 +25,6 @@ import (
 	"github.com/arduino/arduino-cli/commands/cmderrors"
 	"github.com/arduino/arduino-cli/internal/arduino/globals"
 	"github.com/arduino/arduino-cli/internal/arduino/libraries"
-	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex"
 	"github.com/arduino/arduino-cli/internal/arduino/utils"
 	paths "github.com/arduino/go-paths-helper"
 	"github.com/codeclysm/extract/v3"
@@ -58,15 +57,15 @@ type LibraryInstallPlan struct {
 // InstallPrerequisiteCheck performs prequisite checks to install a library. It returns the
 // install path, where the library should be installed and the possible library that is already
 // installed on the same folder and it's going to be replaced by the new one.
-func (lm *LibrariesManager) InstallPrerequisiteCheck(name string, version *semver.Version, installLocation libraries.LibraryLocation) (*LibraryInstallPlan, error) {
-	installDir, err := lm.getLibrariesDir(installLocation)
+func (lmi *Installer) InstallPrerequisiteCheck(name string, version *semver.Version, installLocation libraries.LibraryLocation) (*LibraryInstallPlan, error) {
+	installDir, err := lmi.getLibrariesDir(installLocation)
 	if err != nil {
 		return nil, err
 	}
 
-	lm.RescanLibraries()
-	libs := lm.FindByReference(&librariesindex.Reference{Name: name}, installLocation)
+	lmi.RescanLibraries()
 
+	libs := lmi.FindByReference(name, nil, installLocation)
 	if len(libs) > 1 {
 		libsDir := paths.NewPathList()
 		for _, lib := range libs {
@@ -103,13 +102,8 @@ func (lm *LibrariesManager) InstallPrerequisiteCheck(name string, version *semve
 	}, nil
 }
 
-// Install installs a library on the specified path.
-func (lm *LibrariesManager) Install(indexLibrary *librariesindex.Release, installPath *paths.Path) error {
-	return indexLibrary.Resource.Install(lm.DownloadsDir, installPath.Parent(), installPath)
-}
-
 // importLibraryFromDirectory installs a library by copying it from the given directory.
-func (lm *LibrariesManager) importLibraryFromDirectory(libPath *paths.Path, overwrite bool) error {
+func (lmi *Installer) importLibraryFromDirectory(libPath *paths.Path, overwrite bool) error {
 	// Check if the library is valid and load metatada
 	if err := validateLibrary(libPath); err != nil {
 		return err
@@ -120,7 +114,7 @@ func (lm *LibrariesManager) importLibraryFromDirectory(libPath *paths.Path, over
 	}
 
 	// Check if the library is already installed and determine install path
-	installPlan, err := lm.InstallPrerequisiteCheck(library.Name, library.Version, libraries.User)
+	installPlan, err := lmi.InstallPrerequisiteCheck(library.Name, library.Version, libraries.User)
 	if err != nil {
 		return err
 	}
@@ -134,7 +128,7 @@ func (lm *LibrariesManager) importLibraryFromDirectory(libPath *paths.Path, over
 		if !overwrite {
 			return fmt.Errorf(tr("Library %[1]s is already installed, but with a different version: %[2]s", installPlan.Name, installPlan.ReplacedLib))
 		}
-		if err := lm.Uninstall(installPlan.ReplacedLib); err != nil {
+		if err := lmi.Uninstall(installPlan.ReplacedLib); err != nil {
 			return err
 		}
 	}
@@ -148,7 +142,7 @@ func (lm *LibrariesManager) importLibraryFromDirectory(libPath *paths.Path, over
 }
 
 // Uninstall removes a Library
-func (lm *LibrariesManager) Uninstall(lib *libraries.Library) error {
+func (lmi *Installer) Uninstall(lib *libraries.Library) error {
 	if lib == nil || lib.InstallDir == nil {
 		return fmt.Errorf(tr("install directory not set"))
 	}
@@ -156,14 +150,14 @@ func (lm *LibrariesManager) Uninstall(lib *libraries.Library) error {
 		return fmt.Errorf(tr("removing library directory: %s"), err)
 	}
 
-	alternatives := lm.Libraries[lib.Name]
+	alternatives := lmi.libraries[lib.Name]
 	alternatives.Remove(lib)
-	lm.Libraries[lib.Name] = alternatives
+	lmi.libraries[lib.Name] = alternatives
 	return nil
 }
 
 // InstallZipLib installs a Zip library on the specified path.
-func (lm *LibrariesManager) InstallZipLib(ctx context.Context, archivePath *paths.Path, overwrite bool) error {
+func (lmi *Installer) InstallZipLib(ctx context.Context, archivePath *paths.Path, overwrite bool) error {
 	// Clone library in a temporary directory
 	tmpDir, err := paths.MkTempDir("", "")
 	if err != nil {
@@ -197,7 +191,7 @@ func (lm *LibrariesManager) InstallZipLib(ctx context.Context, archivePath *path
 	tmpInstallPath := libRootFiles[0]
 
 	// Install extracted library in the destination directory
-	if err := lm.importLibraryFromDirectory(tmpInstallPath, overwrite); err != nil {
+	if err := lmi.importLibraryFromDirectory(tmpInstallPath, overwrite); err != nil {
 		return fmt.Errorf(tr("moving extracted archive to destination dir: %s"), err)
 	}
 
@@ -205,7 +199,7 @@ func (lm *LibrariesManager) InstallZipLib(ctx context.Context, archivePath *path
 }
 
 // InstallGitLib installs a library hosted on a git repository on the specified path.
-func (lm *LibrariesManager) InstallGitLib(gitURL string, overwrite bool) error {
+func (lmi *Installer) InstallGitLib(gitURL string, overwrite bool) error {
 	gitLibraryName, ref, err := parseGitURL(gitURL)
 	if err != nil {
 		return err
@@ -246,7 +240,7 @@ func (lm *LibrariesManager) InstallGitLib(gitURL string, overwrite bool) error {
 	tmpInstallPath.Join(".git").RemoveAll()
 
 	// Install extracted library in the destination directory
-	if err := lm.importLibraryFromDirectory(tmpInstallPath, overwrite); err != nil {
+	if err := lmi.importLibraryFromDirectory(tmpInstallPath, overwrite); err != nil {
 		return fmt.Errorf(tr("moving extracted archive to destination dir: %s"), err)
 	}
 
diff --git a/internal/arduino/libraries/librariesmanager/librariesmanager.go b/internal/arduino/libraries/librariesmanager/librariesmanager.go
index db7f3b2f586..64102cf46f9 100644
--- a/internal/arduino/libraries/librariesmanager/librariesmanager.go
+++ b/internal/arduino/libraries/librariesmanager/librariesmanager.go
@@ -21,27 +21,42 @@ import (
 	"os"
 	"slices"
 	"strings"
+	"sync"
 
 	"github.com/arduino/arduino-cli/internal/arduino/cores"
 	"github.com/arduino/arduino-cli/internal/arduino/libraries"
-	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex"
 	"github.com/arduino/arduino-cli/internal/i18n"
 	paths "github.com/arduino/go-paths-helper"
 	"github.com/sirupsen/logrus"
+	semver "go.bug.st/relaxed-semver"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 )
 
+// Builder is used to create a new LibrariesManager. The builder has
+// methods to load and parse libraries and to actually build the
+// LibraryManager. Once the LibrariesManager is built, it cannot be
+// altered anymore.
+type Builder struct {
+	*LibrariesManager
+}
+
+// Explorer is used to query the library manager about the installed libraries.
+type Explorer struct {
+	*LibrariesManager
+}
+
+// Installer is used to rescan installed libraries after install/uninstall.
+type Installer struct {
+	*LibrariesManager
+}
+
 // LibrariesManager keeps the current status of the libraries in the system
 // (the list of libraries, revisions, installed paths, etc.)
 type LibrariesManager struct {
-	LibrariesDir []*LibrariesDir
-	Libraries    map[string]libraries.List `json:"libraries"`
-
-	Index              *librariesindex.Index
-	IndexFile          *paths.Path
-	IndexFileSignature *paths.Path
-	DownloadsDir       *paths.Path
+	librariesLock sync.RWMutex
+	librariesDir  []*LibrariesDir
+	libraries     map[string]libraries.List
 }
 
 // LibrariesDir is a directory containing libraries
@@ -55,10 +70,10 @@ type LibrariesDir struct {
 var tr = i18n.Tr
 
 // Names returns an array with all the names of the installed libraries.
-func (lm LibrariesManager) Names() []string {
-	res := make([]string, len(lm.Libraries))
+func (lm *Explorer) Names() []string {
+	res := make([]string, len(lm.libraries))
 	i := 0
-	for n := range lm.Libraries {
+	for n := range lm.libraries {
 		res[i] = n
 		i++
 	}
@@ -71,43 +86,63 @@ func (lm LibrariesManager) Names() []string {
 	return res
 }
 
-// NewLibraryManager creates a new library manager
-func NewLibraryManager(indexDir *paths.Path, downloadsDir *paths.Path) *LibrariesManager {
-	var indexFile, indexFileSignature *paths.Path
-	if indexDir != nil {
-		indexFile = indexDir.Join("library_index.json")
-		indexFileSignature = indexDir.Join("library_index.json.sig")
-	}
-	return &LibrariesManager{
-		Libraries:          map[string]libraries.List{},
-		IndexFile:          indexFile,
-		IndexFileSignature: indexFileSignature,
-		DownloadsDir:       downloadsDir,
-		Index:              librariesindex.EmptyIndex,
+// NewBuilder creates a new library manager builder.
+func NewBuilder() *Builder {
+	return &Builder{
+		&LibrariesManager{
+			libraries: map[string]libraries.List{},
+		},
 	}
 }
 
-// LoadIndex reads a library_index.json from a file and returns
-// the corresponding Index structure.
-func (lm *LibrariesManager) LoadIndex() error {
-	logrus.WithField("index", lm.IndexFile).Info("Loading libraries index file")
-	index, err := librariesindex.LoadIndex(lm.IndexFile)
-	if err != nil {
-		lm.Index = librariesindex.EmptyIndex
-		return err
+// NewBuilder creates a Builder with the same configuration of this
+// LibrariesManager. A "commit" function callback is returned: calling
+// this function will write the new configuration into this LibrariesManager.
+func (lm *LibrariesManager) NewBuilder() (*Builder, func()) {
+	lmb := NewBuilder()
+	return lmb, func() {
+		lmb.BuildIntoExistingLibrariesManager(lm)
 	}
-	lm.Index = index
-	return nil
+}
+
+// NewExplorer returns a new Explorer. The returned function must be called
+// to release the lock on the LibrariesManager.
+func (lm *LibrariesManager) NewExplorer() (*Explorer, func()) {
+	lm.librariesLock.RLock()
+	return &Explorer{lm}, lm.librariesLock.RUnlock
+}
+
+// NewInstaller returns a new Installer. The returned function must be called
+// to release the lock on the LibrariesManager.
+func (lm *LibrariesManager) NewInstaller() (*Installer, func()) {
+	lm.librariesLock.Lock()
+	return &Installer{lm}, lm.librariesLock.Unlock
+}
+
+// Build builds a new LibrariesManager.
+func (lmb *Builder) Build() *LibrariesManager {
+	res := &LibrariesManager{}
+	lmb.BuildIntoExistingLibrariesManager(res)
+	return res
+}
+
+// BuildIntoExistingLibrariesManager will overwrite the given LibrariesManager instead
+// of building a new one.
+func (lmb *Builder) BuildIntoExistingLibrariesManager(old *LibrariesManager) {
+	old.librariesLock.Lock()
+	old.librariesDir = lmb.librariesDir
+	old.libraries = lmb.libraries
+	old.librariesLock.Unlock()
 }
 
 // AddLibrariesDir adds path to the list of directories
 // to scan when searching for libraries. If a path is already
 // in the list it is ignored.
-func (lm *LibrariesManager) AddLibrariesDir(libDir *LibrariesDir) {
+func (lmb *Builder) AddLibrariesDir(libDir *LibrariesDir) {
 	if libDir.Path == nil {
 		return
 	}
-	for _, dir := range lm.LibrariesDir {
+	for _, dir := range lmb.librariesDir {
 		if dir.Path.EquivalentTo(libDir.Path) {
 			return
 		}
@@ -116,15 +151,15 @@ func (lm *LibrariesManager) AddLibrariesDir(libDir *LibrariesDir) {
 		WithField("location", libDir.Location.String()).
 		WithField("isSingleLibrary", libDir.IsSingleLibrary).
 		Info("Adding libraries dir")
-	lm.LibrariesDir = append(lm.LibrariesDir, libDir)
+	lmb.librariesDir = append(lmb.librariesDir, libDir)
 }
 
 // RescanLibraries reload all installed libraries in the system.
-func (lm *LibrariesManager) RescanLibraries() []*status.Status {
-	lm.clearLibraries()
+func (lmi *Installer) RescanLibraries() []*status.Status {
+	lmi.libraries = map[string]libraries.List{}
 	statuses := []*status.Status{}
-	for _, dir := range lm.LibrariesDir {
-		if errs := lm.loadLibrariesFromDir(dir); len(errs) > 0 {
+	for _, dir := range lmi.librariesDir {
+		if errs := lmi.loadLibrariesFromDir(dir); len(errs) > 0 {
 			statuses = append(statuses, errs...)
 		}
 	}
@@ -132,7 +167,7 @@ func (lm *LibrariesManager) RescanLibraries() []*status.Status {
 }
 
 func (lm *LibrariesManager) getLibrariesDir(installLocation libraries.LibraryLocation) (*paths.Path, error) {
-	for _, dir := range lm.LibrariesDir {
+	for _, dir := range lm.librariesDir {
 		if dir.Location == installLocation {
 			return dir.Path, nil
 		}
@@ -149,7 +184,7 @@ func (lm *LibrariesManager) getLibrariesDir(installLocation libraries.LibraryLoc
 
 // loadLibrariesFromDir loads all libraries in the given directory. Returns
 // nil if the directory doesn't exists.
-func (lm *LibrariesManager) loadLibrariesFromDir(librariesDir *LibrariesDir) []*status.Status {
+func (lmi *Installer) loadLibrariesFromDir(librariesDir *LibrariesDir) []*status.Status {
 	statuses := []*status.Status{}
 
 	var libDirs paths.PathList
@@ -169,17 +204,17 @@ func (lm *LibrariesManager) loadLibrariesFromDir(librariesDir *LibrariesDir) []*
 		libDirs = d
 	}
 
-	for _, subDir := range libDirs {
-		library, err := libraries.Load(subDir, librariesDir.Location)
+	for _, libDir := range libDirs {
+		library, err := libraries.Load(libDir, librariesDir.Location)
 		if err != nil {
-			s := status.Newf(codes.Internal, tr("loading library from %[1]s: %[2]s"), subDir, err)
+			s := status.Newf(codes.Internal, tr("loading library from %[1]s: %[2]s"), libDir, err)
 			statuses = append(statuses, s)
 			continue
 		}
 		library.ContainerPlatform = librariesDir.PlatformRelease
-		alternatives := lm.Libraries[library.Name]
+		alternatives := lmi.libraries[library.Name]
 		alternatives.Add(library)
-		lm.Libraries[library.Name] = alternatives
+		lmi.libraries[library.Name] = alternatives
 	}
 
 	return statuses
@@ -188,19 +223,20 @@ func (lm *LibrariesManager) loadLibrariesFromDir(librariesDir *LibrariesDir) []*
 // FindByReference return the installed libraries matching the Reference
 // name and version or, if the version is nil, the libraries installed
 // in the installLocation.
-func (lm *LibrariesManager) FindByReference(libRef *librariesindex.Reference, installLocation libraries.LibraryLocation) libraries.List {
-	alternatives := lm.Libraries[libRef.Name]
+func (lmi *Installer) FindByReference(name string, version *semver.Version, installLocation libraries.LibraryLocation) libraries.List {
+	alternatives := lmi.libraries[name]
 	if alternatives == nil {
 		return nil
 	}
-	return alternatives.FilterByVersionAndInstallLocation(libRef.Version, installLocation)
+	return alternatives.FilterByVersionAndInstallLocation(version, installLocation)
 }
 
 // FindAllInstalled returns all the installed libraries
 func (lm *LibrariesManager) FindAllInstalled() libraries.List {
 	var res libraries.List
-	for _, libAlternatives := range lm.Libraries {
+	for _, libAlternatives := range lm.libraries {
 		for _, libRelease := range libAlternatives {
+			// TODO: is this check redundant?
 			if libRelease.InstallDir == nil {
 				continue
 			}
@@ -209,9 +245,3 @@ func (lm *LibrariesManager) FindAllInstalled() libraries.List {
 	}
 	return res
 }
-
-func (lm *LibrariesManager) clearLibraries() {
-	for k := range lm.Libraries {
-		delete(lm.Libraries, k)
-	}
-}
diff --git a/internal/arduino/libraries/librariesmanager/librariesmanager_test.go b/internal/arduino/libraries/librariesmanager/librariesmanager_test.go
index 54475cd1fd3..2393868999e 100644
--- a/internal/arduino/libraries/librariesmanager/librariesmanager_test.go
+++ b/internal/arduino/libraries/librariesmanager/librariesmanager_test.go
@@ -18,15 +18,20 @@ import (
 	"testing"
 
 	"github.com/arduino/arduino-cli/internal/arduino/libraries"
-	"github.com/arduino/go-paths-helper"
 	"github.com/stretchr/testify/require"
 )
 
 func Test_RescanLibrariesCallClear(t *testing.T) {
-	baseDir := paths.New(t.TempDir())
-	lm := NewLibraryManager(baseDir.Join("index_dir"), baseDir.Join("downloads_dir"))
-	lm.Libraries["testLibA"] = libraries.List{}
-	lm.Libraries["testLibB"] = libraries.List{}
-	lm.RescanLibraries()
-	require.Len(t, lm.Libraries, 0)
+	lmb := NewBuilder()
+	lmb.libraries["testLibA"] = libraries.List{}
+	lmb.libraries["testLibB"] = libraries.List{}
+	lm := lmb.Build()
+
+	{
+		lmi, release := lm.NewInstaller()
+		lmi.RescanLibraries()
+		release()
+	}
+
+	require.Len(t, lm.libraries, 0)
 }
diff --git a/internal/arduino/libraries/librariesresolver/cpp.go b/internal/arduino/libraries/librariesresolver/cpp.go
index deee71545d3..7f11f8a66c8 100644
--- a/internal/arduino/libraries/librariesresolver/cpp.go
+++ b/internal/arduino/libraries/librariesresolver/cpp.go
@@ -22,7 +22,6 @@ import (
 
 	"github.com/arduino/arduino-cli/internal/arduino/cores"
 	"github.com/arduino/arduino-cli/internal/arduino/libraries"
-	"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesmanager"
 	"github.com/arduino/arduino-cli/internal/arduino/utils"
 	"github.com/arduino/arduino-cli/internal/i18n"
 	"github.com/schollz/closestmatch"
@@ -37,64 +36,52 @@ type Cpp struct {
 var tr = i18n.Tr
 
 // NewCppResolver creates a new Cpp resolver
-func NewCppResolver() *Cpp {
-	return &Cpp{
+func NewCppResolver(allLibs []*libraries.Library, targetPlatform, actualPlatform *cores.PlatformRelease) *Cpp {
+	resolver := &Cpp{
 		headers: map[string]libraries.List{},
 	}
-}
-
-// ScanFromLibrariesManager reads all librariers loaded in the LibrariesManager to find
-// and cache all C++ headers for later retrieval
-func (resolver *Cpp) ScanFromLibrariesManager(lm *librariesmanager.LibrariesManager) error {
-	for _, libAlternatives := range lm.Libraries {
-		for _, lib := range libAlternatives {
-			resolver.ScanLibrary(lib)
-		}
+	resolver.ScanIDEBuiltinLibraries(allLibs)
+	resolver.ScanUserAndUnmanagedLibraries(allLibs)
+	resolver.ScanPlatformLibraries(allLibs, targetPlatform)
+	if actualPlatform != targetPlatform {
+		resolver.ScanPlatformLibraries(allLibs, actualPlatform)
 	}
-	return nil
+
+	return resolver
 }
 
 // ScanIDEBuiltinLibraries reads ide-builtin librariers loaded in the LibrariesManager to find
 // and cache all C++ headers for later retrieval.
-func (resolver *Cpp) ScanIDEBuiltinLibraries(lm *librariesmanager.LibrariesManager) error {
-	for _, libAlternatives := range lm.Libraries {
-		for _, lib := range libAlternatives {
-			if lib.Location == libraries.IDEBuiltIn {
-				resolver.ScanLibrary(lib)
-			}
+func (resolver *Cpp) ScanIDEBuiltinLibraries(allLibs []*libraries.Library) {
+	for _, lib := range allLibs {
+		if lib.Location == libraries.IDEBuiltIn {
+			_ = resolver.ScanLibrary(lib)
 		}
 	}
-	return nil
 }
 
 // ScanUserAndUnmanagedLibraries reads user/unmanaged librariers loaded in the LibrariesManager to find
 // and cache all C++ headers for later retrieval.
-func (resolver *Cpp) ScanUserAndUnmanagedLibraries(lm *librariesmanager.LibrariesManager) error {
-	for _, libAlternatives := range lm.Libraries {
-		for _, lib := range libAlternatives {
-			if lib.Location == libraries.User || lib.Location == libraries.Unmanaged {
-				resolver.ScanLibrary(lib)
-			}
+func (resolver *Cpp) ScanUserAndUnmanagedLibraries(allLibs []*libraries.Library) {
+	for _, lib := range allLibs {
+		if lib.Location == libraries.User || lib.Location == libraries.Unmanaged {
+			_ = resolver.ScanLibrary(lib)
 		}
 	}
-	return nil
 }
 
 // ScanPlatformLibraries reads platform-bundled libraries for a specific platform loaded in the LibrariesManager
 // to find and cache all C++ headers for later retrieval.
-func (resolver *Cpp) ScanPlatformLibraries(lm *librariesmanager.LibrariesManager, platform *cores.PlatformRelease) error {
-	for _, libAlternatives := range lm.Libraries {
-		for _, lib := range libAlternatives {
-			if lib.Location != libraries.PlatformBuiltIn && lib.Location != libraries.ReferencedPlatformBuiltIn {
-				continue
-			}
-			if lib.ContainerPlatform != platform {
-				continue
-			}
-			resolver.ScanLibrary(lib)
+func (resolver *Cpp) ScanPlatformLibraries(allLibs []*libraries.Library, platform *cores.PlatformRelease) {
+	for _, lib := range allLibs {
+		if lib.Location != libraries.PlatformBuiltIn && lib.Location != libraries.ReferencedPlatformBuiltIn {
+			continue
 		}
+		if lib.ContainerPlatform != platform {
+			continue
+		}
+		_ = resolver.ScanLibrary(lib)
 	}
-	return nil
 }
 
 // ScanLibrary reads a library to find and cache C++ headers for later retrieval
diff --git a/internal/arduino/libraries/librariesresolver/cpp_test.go b/internal/arduino/libraries/librariesresolver/cpp_test.go
index f65bc195769..a82da18f465 100644
--- a/internal/arduino/libraries/librariesresolver/cpp_test.go
+++ b/internal/arduino/libraries/librariesresolver/cpp_test.go
@@ -35,7 +35,7 @@ var bundleServo = &libraries.Library{Name: "Servo", Location: libraries.IDEBuilt
 func runResolver(include string, arch string, libs ...*libraries.Library) *libraries.Library {
 	libraryList := libraries.List{}
 	libraryList.Add(libs...)
-	resolver := NewCppResolver()
+	resolver := &Cpp{headers: make(map[string]libraries.List)}
 	resolver.headers[include] = libraryList
 	return resolver.ResolveFor(include, arch)
 }
@@ -95,7 +95,7 @@ func TestClosestMatchWithTotallyDifferentNames(t *testing.T) {
 	libraryList.Add(l6)
 	libraryList.Add(l7)
 	libraryList.Add(l8)
-	resolver := NewCppResolver()
+	resolver := &Cpp{headers: make(map[string]libraries.List)}
 	resolver.headers["XYZ.h"] = libraryList
 	res := resolver.ResolveFor("XYZ.h", "xyz")
 	require.NotNil(t, res)
@@ -121,7 +121,7 @@ func TestCppHeaderPriority(t *testing.T) {
 }
 
 func TestCppHeaderResolverWithNilResult(t *testing.T) {
-	resolver := NewCppResolver()
+	resolver := &Cpp{headers: make(map[string]libraries.List)}
 	libraryList := libraries.List{}
 	libraryList.Add(l1)
 	resolver.headers["aaa.h"] = libraryList
@@ -130,7 +130,7 @@ func TestCppHeaderResolverWithNilResult(t *testing.T) {
 
 func TestCppHeaderResolver(t *testing.T) {
 	resolve := func(header string, libs ...*libraries.Library) string {
-		resolver := NewCppResolver()
+		resolver := &Cpp{headers: make(map[string]libraries.List)}
 		librarylist := libraries.List{}
 		for _, lib := range libs {
 			librarylist.Add(lib)
@@ -149,7 +149,7 @@ func TestCppHeaderResolver(t *testing.T) {
 }
 
 func TestCppHeaderResolverWithLibrariesInStrangeDirectoryNames(t *testing.T) {
-	resolver := NewCppResolver()
+	resolver := &Cpp{headers: make(map[string]libraries.List)}
 	librarylist := libraries.List{}
 	librarylist.Add(&libraries.Library{DirName: "onewire_2_3_4", Name: "OneWire", Architectures: []string{"*"}})
 	librarylist.Add(&libraries.Library{DirName: "onewireng_2_3_4", Name: "OneWireNg", Architectures: []string{"avr"}})
diff --git a/internal/integrationtest/completion/completion_test.go b/internal/integrationtest/completion/completion_test.go
index a216eb0f6ff..678f2f0139e 100644
--- a/internal/integrationtest/completion/completion_test.go
+++ b/internal/integrationtest/completion/completion_test.go
@@ -247,7 +247,7 @@ func TestProfileCompletion(t *testing.T) {
 	stdout, _, _ = cli.Run("__complete", "upload", sketchWithProfilesPath.String(), "--profile", "")
 	require.Contains(t, string(stdout), "profile1")
 
-	// The cli is running in the sketch folder, so need the explictly specify the path in the cli
+	// The cli is running in the sketch folder, so need the explicitly specify the path in the cli
 	cli.SetWorkingDir(sketchWithProfilesPath)
 	stdout, _, _ = cli.Run("__complete", "compile", "--profile", "")
 	require.Contains(t, string(stdout), "profile1")