diff --git a/src/Fallout.Cli/Commands/AddPackageCommand.cs b/src/Fallout.Cli/Commands/AddPackageCommand.cs
new file mode 100644
index 00000000..2e4879ad
--- /dev/null
+++ b/src/Fallout.Cli/Commands/AddPackageCommand.cs
@@ -0,0 +1,50 @@
+using System.Linq;
+using Fallout.Common;
+using Fallout.Common.Execution;
+using Fallout.Common.IO;
+using Fallout.Solutions;
+using Fallout.Common.Tooling;
+using Fallout.Common.Tools.DotNet;
+
+namespace Fallout.Cli.Commands;
+
+///
+/// fallout :add-package: adds (or upgrades) a NuGet package reference in the build project.
+///
+public sealed class AddPackageCommand : IFalloutCommand
+{
+ public string Name => "add-package";
+
+ public int Execute(string[] args, AbsolutePath rootDirectory, AbsolutePath buildScript)
+ {
+ Program.PrintInfo();
+ Logging.Configure();
+ Telemetry.AddPackage();
+ ProjectModelTasks.Initialize();
+
+ var packageId = args.ElementAt(0);
+ var packageVersion =
+ (EnvironmentInfo.GetNamedArgument("version") ??
+ args.ElementAtOrDefault(1) ??
+ NuGetVersionResolver.GetLatestVersion(packageId, includePrereleases: false).GetAwaiter().GetResult() ??
+ NuGetPackageResolver.GetGlobalInstalledPackage(packageId, version: null, packagesConfigFile: null)?.Version.ToString())
+ .NotNull("packageVersion != null");
+
+ // GetConfiguration / AddOrReplacePackage / BUILD_PROJECT_FILE / PACKAGE_TYPE_* are shared
+ // helpers still on Program; they move into services in the final #392 collapse PR.
+ var configuration = Program.GetConfiguration(buildScript, evaluate: true);
+ var buildProjectFile = configuration[Program.BUILD_PROJECT_FILE];
+ Host.Information($"Installing {packageId}/{packageVersion} to {buildProjectFile} ...");
+ Program.AddOrReplacePackage(packageId, packageVersion, Program.PACKAGE_TYPE_DOWNLOAD, buildProjectFile);
+ DotNetTasks.DotNet($"restore {buildProjectFile}");
+
+ var installedPackage = NuGetPackageResolver.GetGlobalInstalledPackage(packageId, packageVersion, packagesConfigFile: null)
+ .NotNull("installedPackage != null");
+ var hasToolsDirectory = installedPackage.Directory.GlobDirectories("tools").Any();
+ if (!hasToolsDirectory)
+ Program.AddOrReplacePackage(packageId, packageVersion, Program.PACKAGE_TYPE_REFERENCE, buildProjectFile);
+
+ Host.Information($"Done installing {packageId}/{packageVersion} to {buildProjectFile}");
+ return 0;
+ }
+}
diff --git a/src/Fallout.Cli/Program.AddPackage.cs b/src/Fallout.Cli/Program.AddPackage.cs
index 29efb84b..87852ae4 100644
--- a/src/Fallout.Cli/Program.AddPackage.cs
+++ b/src/Fallout.Cli/Program.AddPackage.cs
@@ -14,37 +14,8 @@ partial class Program
public const string PACKAGE_TYPE_DOWNLOAD = "PackageDownload";
public const string PACKAGE_TYPE_REFERENCE = "PackageReference";
- public static int AddPackage(string[] args, AbsolutePath rootDirectory, AbsolutePath buildScript)
- {
- PrintInfo();
- Logging.Configure();
- Telemetry.AddPackage();
- ProjectModelTasks.Initialize();
-
- var packageId = args.ElementAt(0);
- var packageVersion =
- (EnvironmentInfo.GetNamedArgument("version") ??
- args.ElementAtOrDefault(1) ??
- NuGetVersionResolver.GetLatestVersion(packageId, includePrereleases: false).GetAwaiter().GetResult() ??
- NuGetPackageResolver.GetGlobalInstalledPackage(packageId, version: null, packagesConfigFile: null)?.Version.ToString())
- .NotNull("packageVersion != null");
-
- var configuration = GetConfiguration(buildScript, evaluate: true);
- var buildProjectFile = configuration[BUILD_PROJECT_FILE];
- Host.Information($"Installing {packageId}/{packageVersion} to {buildProjectFile} ...");
- AddOrReplacePackage(packageId, packageVersion, PACKAGE_TYPE_DOWNLOAD, buildProjectFile);
- DotNetTasks.DotNet($"restore {buildProjectFile}");
-
- var installedPackage = NuGetPackageResolver.GetGlobalInstalledPackage(packageId, packageVersion, packagesConfigFile: null)
- .NotNull("installedPackage != null");
- var hasToolsDirectory = installedPackage.Directory.GlobDirectories("tools").Any();
- if (!hasToolsDirectory)
- AddOrReplacePackage(packageId, packageVersion, PACKAGE_TYPE_REFERENCE, buildProjectFile);
-
- Host.Information($"Done installing {packageId}/{packageVersion} to {buildProjectFile}");
- return 0;
- }
-
+ // Residual after the :add-package command moved to AddPackageCommand: this helper is shared with
+ // cake and moves into a package service in the #392 collapse PR.
internal static void AddOrReplacePackage(string packageId, string packageVersion, string packageType, string buildProjectFile)
{
var buildProject = ProjectModelTasks.ParseProject(buildProjectFile).NotNull();
diff --git a/src/Fallout.Cli/Program.cs b/src/Fallout.Cli/Program.cs
index 43ac5823..cbd1dcab 100644
--- a/src/Fallout.Cli/Program.cs
+++ b/src/Fallout.Cli/Program.cs
@@ -56,12 +56,12 @@ private static void RegisterCommands(IServiceCollection services)
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
// Legacy handlers still living on Program, adapted until they are extracted into command
// types. Each conversion deletes one line here plus its Program.X.cs partial.
services.AddSingleton(new DelegateCommand("setup", Setup));
services.AddSingleton(new DelegateCommand("update", Update));
- services.AddSingleton(new DelegateCommand("add-package", AddPackage));
services.AddSingleton(new DelegateCommand("cake-convert", CakeConvert));
services.AddSingleton(new DelegateCommand("cake-clean", CakeClean));
services.AddSingleton(new DelegateCommand("secrets", Secrets));