From 5764007994a147b4afb317237563fe3462c24c49 Mon Sep 17 00:00:00 2001 From: Diogo Autilio Date: Sun, 16 Nov 2025 12:55:31 -0300 Subject: [PATCH 1/8] feat: add support to uninstall a specific version when multiples versions of the same tool are installed --- .../MintCLI/Commands/UninstallCommand.swift | 1 + Sources/MintKit/Mint.swift | 70 +++++++++++++++---- Tests/MintTests/MintTests.swift | 38 ++++++++++ 3 files changed, 96 insertions(+), 13 deletions(-) diff --git a/Sources/MintCLI/Commands/UninstallCommand.swift b/Sources/MintCLI/Commands/UninstallCommand.swift index 3d7cf7a..78e1629 100644 --- a/Sources/MintCLI/Commands/UninstallCommand.swift +++ b/Sources/MintCLI/Commands/UninstallCommand.swift @@ -5,6 +5,7 @@ import SwiftCLI class UninstallCommand: MintCommand { @Param var package: String + @Key("-v", "--version", description: "Specify the version to uninstall") var version: String? init(mint: Mint) { super.init(mint: mint, name: "uninstall", description: "Uninstall a package by name") diff --git a/Sources/MintKit/Mint.swift b/Sources/MintKit/Mint.swift index 7e5c791..ec8e7ab 100644 --- a/Sources/MintKit/Mint.swift +++ b/Sources/MintKit/Mint.swift @@ -584,7 +584,11 @@ public class Mint { } } - public func uninstall(name: String) throws { + /// Uninstall a package entirely or a specific installed version. + /// - Parameters: + /// - name: The package name (or a case-insensitive substring of the git repo) + /// - version: Optional specific version to uninstall. If nil, all installed versions are removed. + public func uninstall(name: String, version: String? = nil) throws { // find packages var metadata = try readMetadata() @@ -592,7 +596,7 @@ public class Mint { let cache = try Cache(path: packagesPath, metadata: metadata, linkedExecutables: linkedExecutables) let packages = cache.packages.filter { $0.gitRepo.lowercased().contains(name.lowercased()) } - // remove package + // select package to operate on let package: Cache.PackageInfo switch packages.count { case 0: @@ -605,27 +609,67 @@ public class Mint { package = packages.first { $0.gitRepo == option }! } - // get resources across all installed versions + // determine which version dirs to delete + let versionDirsToDelete: [Cache.VersionDir] + if let version = version { + // try exact match first + let matches = package.versionDirs.filter { $0.version == version } + if matches.isEmpty { + // fallback: contains (helps when user passes just a short sha / partial) + let fuzzy = package.versionDirs.filter { $0.version.contains(version) } + if fuzzy.isEmpty { + errorOutput("Version '\(version)' for package \(package.name) was not found".red) + return + } else { + versionDirsToDelete = fuzzy + } + } else { + versionDirsToDelete = matches + } + } else { + versionDirsToDelete = package.versionDirs + } + + // get resources for the versions we will remove let resources = Set( - try package.versionDirs + try versionDirsToDelete .map { try getResources(from: $0.path) } .flatMap { $0 } ) - try package.path.delete() - output("\(package.name) was uninstalled") - - // remove metadata - metadata.packages[package.gitRepo] = nil - try writeMetadata(metadata) + // delete the selected version directories + for vd in versionDirsToDelete { + try vd.path.delete() + } + + // check if any version directories remain under build path + let buildPath = package.path + "build" + let remainingVersionDirs = (try? buildPath.children() + .filter { $0.isDirectory && !$0.lastComponent.hasPrefix(".") } + .map { $0.lastComponent }) ?? [] + let removedAllVersions = remainingVersionDirs.isEmpty + + if removedAllVersions { + // fully removed package; ensure package path cleanup and metadata update + try package.path.delete() + output("\(package.name) was uninstalled") + // remove metadata entry + metadata.packages[package.gitRepo] = nil + try writeMetadata(metadata) + } else { + // only specific version(s) removed + let removedVersionsList = versionDirsToDelete.map { $0.version }.joined(separator: ", ") + output("\(package.name) (\(removedVersionsList)) was uninstalled") + // metadata remains unchanged because package still has installed versions + } - // remove link - for executable in Set(package.versionDirs.flatMap { $0.executables }) where executable.linked { + // remove links for executables belonging to removed versions + for executable in Set(versionDirsToDelete.flatMap { $0.executables }) where executable.linked { let installPath = linkPath + executable.name try installPath.delete() } - // remove all resource artifact links + // remove resource artifact links related only to removed versions for resource in resources { let installPath = linkPath + resource.lastComponent try installPath.delete() diff --git a/Tests/MintTests/MintTests.swift b/Tests/MintTests/MintTests.swift index 4eee085..7ff9c9f 100644 --- a/Tests/MintTests/MintTests.swift +++ b/Tests/MintTests/MintTests.swift @@ -308,4 +308,42 @@ class MintTests: XCTestCase { XCTAssertEqual(try mint.listPackages(), [:]) XCTAssertEqual(try mint.readMetadata().packages, [:]) } + + func testUninstallSpecificVersionRemovesOnlyThatVersionAndLinks() throws { + let globalPath = mint.linkPath + testCommand + // Use versions that exist in the test fixtures/repo + let packageOne = PackageReference(repo: testRepo, version: testVersion) + let packageTwo = PackageReference(repo: testRepo, version: latestVersion) + + // install two versions + try mint.install(package: packageOne, link: true) + try mint.install(package: packageTwo, link: true) + + // check everything expected is there + // installing and linking the newer version should update the symlink + XCTAssertTrue(globalPath.exists) + XCTAssertEqual(mint.getLinkedExecutables(), [expectedExecutablePath(latestVersion)]) + XCTAssertEqual(try mint.listPackages(), [fullTestRepo: [testVersion, latestVersion]]) + XCTAssertEqual(try mint.readMetadata().packages, [fullTestRepo: testPackageDir]) + + // Perform uninstall for specific version + try mint.uninstall(name: testRepo, version: "4.0.0") + + // Assert: older version removed, newer version still present + XCTAssertFalse((mintPath + "packages" + testPackageDir + "build" + testVersion).exists, "Requested version should be removed") + XCTAssertTrue((mintPath + "packages" + testPackageDir + "build" + latestVersion).exists, "Other versions should remain") + + // Symlink should still exist and point to the remaining (newer) version + XCTAssertTrue(globalPath.exists) + XCTAssertEqual(mint.getLinkedExecutables(), [expectedExecutablePath(latestVersion)]) + + // Metadata should still contain the package because a version remains + let metadataData = try (mintPath + "metadata.json").read() + if let meta = try JSONSerialization.jsonObject(with: metadataData, options: []) as? [String: Any], + let packages = meta["packages"] as? [String: String] { + XCTAssertEqual(packages[fullTestRepo], testPackageDir, "Metadata mapping should still exist when package has remaining versions") + } else { + XCTFail("metadata.json could not be parsed") + } + } } From 5623bcfc0f0fec1d47edad26a20d41e9bec9b316 Mon Sep 17 00:00:00 2001 From: Diogo Autilio Date: Thu, 11 Dec 2025 05:26:03 -0300 Subject: [PATCH 2/8] fix(cli): wire version flag to uninstall method --- Sources/MintCLI/Commands/UninstallCommand.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/MintCLI/Commands/UninstallCommand.swift b/Sources/MintCLI/Commands/UninstallCommand.swift index 78e1629..d5ec36c 100644 --- a/Sources/MintCLI/Commands/UninstallCommand.swift +++ b/Sources/MintCLI/Commands/UninstallCommand.swift @@ -13,6 +13,6 @@ class UninstallCommand: MintCommand { override func execute() throws { try super.execute() - try mint.uninstall(name: package) + try mint.uninstall(name: package, version: version) } } From e17a5c9a93409817b482a00d4cebfad301732d26 Mon Sep 17 00:00:00 2001 From: Diogo Autilio Date: Sat, 13 Dec 2025 11:47:12 -0300 Subject: [PATCH 3/8] handle silent error Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Sources/MintKit/Mint.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Sources/MintKit/Mint.swift b/Sources/MintKit/Mint.swift index ec8e7ab..ebffb4a 100644 --- a/Sources/MintKit/Mint.swift +++ b/Sources/MintKit/Mint.swift @@ -644,9 +644,17 @@ public class Mint { // check if any version directories remain under build path let buildPath = package.path + "build" - let remainingVersionDirs = (try? buildPath.children() - .filter { $0.isDirectory && !$0.lastComponent.hasPrefix(".") } - .map { $0.lastComponent }) ?? [] + var remainingVersionDirs: [String] = [] + if buildPath.exists { + do { + remainingVersionDirs = try buildPath.children() + .filter { $0.isDirectory && !$0.lastComponent.hasPrefix(".") } + .map { $0.lastComponent } + } catch { + errorOutput("Failed to read build path '\(buildPath)': \(error)".red) + return + } + } let removedAllVersions = remainingVersionDirs.isEmpty if removedAllVersions { From 0bdac91fa948818a8f56d2e538d48fe768100aad Mon Sep 17 00:00:00 2001 From: Diogo Autilio Date: Sat, 13 Dec 2025 14:16:31 -0300 Subject: [PATCH 4/8] fix(cli): updated the uninstall logic to only remove resource symlinks when no remaining installed version still references the same artifact --- Sources/MintKit/Mint.swift | 13 +++++++++++-- Tests/MintTests/MintTests.swift | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Sources/MintKit/Mint.swift b/Sources/MintKit/Mint.swift index ebffb4a..38fd880 100644 --- a/Sources/MintKit/Mint.swift +++ b/Sources/MintKit/Mint.swift @@ -679,8 +679,17 @@ public class Mint { // remove resource artifact links related only to removed versions for resource in resources { - let installPath = linkPath + resource.lastComponent - try installPath.delete() + let resourceName = resource.lastComponent + + let stillUsed = remainingVersionDirs.contains { versionDir in + let candidatePath = buildPath + versionDir + resourceName + return candidatePath.exists + } + + if !stillUsed { + let installPath = linkPath + resourceName + try installPath.delete() + } } } } diff --git a/Tests/MintTests/MintTests.swift b/Tests/MintTests/MintTests.swift index 7ff9c9f..673ac54 100644 --- a/Tests/MintTests/MintTests.swift +++ b/Tests/MintTests/MintTests.swift @@ -327,7 +327,7 @@ class MintTests: XCTestCase { XCTAssertEqual(try mint.readMetadata().packages, [fullTestRepo: testPackageDir]) // Perform uninstall for specific version - try mint.uninstall(name: testRepo, version: "4.0.0") + try mint.uninstall(name: testRepo, version: testVersion) // Assert: older version removed, newer version still present XCTAssertFalse((mintPath + "packages" + testPackageDir + "build" + testVersion).exists, "Requested version should be removed") From 55fd69e96328a6abf6b95a4d54c35ea3de23fe83 Mon Sep 17 00:00:00 2001 From: Diogo Autilio Date: Sat, 13 Dec 2025 14:41:10 -0300 Subject: [PATCH 5/8] fix(cli): limit uninstall version matching to exact versions or SHA prefixes --- .../MintCLI/Commands/UninstallCommand.swift | 2 +- Sources/MintKit/Mint.swift | 29 +++++++++++++------ Tests/MintTests/MintTests.swift | 8 +---- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Sources/MintCLI/Commands/UninstallCommand.swift b/Sources/MintCLI/Commands/UninstallCommand.swift index d5ec36c..6a9330d 100644 --- a/Sources/MintCLI/Commands/UninstallCommand.swift +++ b/Sources/MintCLI/Commands/UninstallCommand.swift @@ -8,7 +8,7 @@ class UninstallCommand: MintCommand { @Key("-v", "--version", description: "Specify the version to uninstall") var version: String? init(mint: Mint) { - super.init(mint: mint, name: "uninstall", description: "Uninstall a package by name") + super.init(mint: mint, name: "uninstall", description: "Uninstall a package or a specific version by name") } override func execute() throws { diff --git a/Sources/MintKit/Mint.swift b/Sources/MintKit/Mint.swift index 38fd880..52c2190 100644 --- a/Sources/MintKit/Mint.swift +++ b/Sources/MintKit/Mint.swift @@ -515,6 +515,13 @@ public class Mint { } } } + + private func isLikelySHA(_ value: String) -> Bool { + let minSHALength = 7 + let hexSet = CharacterSet(charactersIn: "0123456789abcdefABCDEF") + return value.count >= minSHALength && + value.unicodeScalars.allSatisfy { hexSet.contains($0) } + } public func bootstrap(link: Bool = false, overwrite: Bool? = nil) throws { @@ -612,21 +619,25 @@ public class Mint { // determine which version dirs to delete let versionDirsToDelete: [Cache.VersionDir] if let version = version { - // try exact match first - let matches = package.versionDirs.filter { $0.version == version } - if matches.isEmpty { - // fallback: contains (helps when user passes just a short sha / partial) - let fuzzy = package.versionDirs.filter { $0.version.contains(version) } - if fuzzy.isEmpty { + let exactMatches = package.versionDirs.filter { $0.version == version } + + if !exactMatches.isEmpty { + versionDirsToDelete = exactMatches + } else if isLikelySHA(version) { + let shaMatches = package.versionDirs.filter { $0.version.hasPrefix(version) } + + if shaMatches.isEmpty { errorOutput("Version '\(version)' for package \(package.name) was not found".red) return - } else { - versionDirsToDelete = fuzzy } + + versionDirsToDelete = shaMatches } else { - versionDirsToDelete = matches + errorOutput("Version '\(version)' for package \(package.name) was not found".red) + return } } else { + // no version specified → uninstall all versions versionDirsToDelete = package.versionDirs } diff --git a/Tests/MintTests/MintTests.swift b/Tests/MintTests/MintTests.swift index 673ac54..a67a0a1 100644 --- a/Tests/MintTests/MintTests.swift +++ b/Tests/MintTests/MintTests.swift @@ -338,12 +338,6 @@ class MintTests: XCTestCase { XCTAssertEqual(mint.getLinkedExecutables(), [expectedExecutablePath(latestVersion)]) // Metadata should still contain the package because a version remains - let metadataData = try (mintPath + "metadata.json").read() - if let meta = try JSONSerialization.jsonObject(with: metadataData, options: []) as? [String: Any], - let packages = meta["packages"] as? [String: String] { - XCTAssertEqual(packages[fullTestRepo], testPackageDir, "Metadata mapping should still exist when package has remaining versions") - } else { - XCTFail("metadata.json could not be parsed") - } + XCTAssertEqual(try mint.readMetadata().packages, [fullTestRepo: testPackageDir]) } } From 6231c1ab273f22b20cf941c2ea1656df393c7174 Mon Sep 17 00:00:00 2001 From: Diogo Autilio Date: Mon, 15 Dec 2025 08:46:18 -0300 Subject: [PATCH 6/8] feat(cli): improve uninstall: SHA validation, error handling, and tests --- Sources/MintKit/Cache.swift | 4 ++ Sources/MintKit/Mint.swift | 29 ++++++++--- Tests/MintTests/MintTests.swift | 92 +++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 8 deletions(-) diff --git a/Sources/MintKit/Cache.swift b/Sources/MintKit/Cache.swift index e9b0359..485f6de 100644 --- a/Sources/MintKit/Cache.swift +++ b/Sources/MintKit/Cache.swift @@ -28,6 +28,10 @@ struct Cache: Hashable { let packages: [PackageInfo] init(path: Path, metadata: Mint.Metadata, linkedExecutables: [Path]) throws { + guard path.exists else { + packages = [] + return + } packages = try path.children() .filter { $0.isDirectory && !$0.lastComponent.hasPrefix(".") } .map { originPath in diff --git a/Sources/MintKit/Mint.swift b/Sources/MintKit/Mint.swift index 52c2190..30bb129 100644 --- a/Sources/MintKit/Mint.swift +++ b/Sources/MintKit/Mint.swift @@ -516,6 +516,9 @@ public class Mint { } } + /// Checks if a string is likely a Git SHA commit hash (heuristic: at least 7 hex characters). + /// - Parameter value: The string to check + /// - Returns: `true` if the string appears to be a SHA, `false` otherwise private func isLikelySHA(_ value: String) -> Bool { let minSHALength = 7 let hexSet = CharacterSet(charactersIn: "0123456789abcdefABCDEF") @@ -627,7 +630,15 @@ public class Mint { let shaMatches = package.versionDirs.filter { $0.version.hasPrefix(version) } if shaMatches.isEmpty { - errorOutput("Version '\(version)' for package \(package.name) was not found".red) + errorOutput("SHA version '\(version)' for package \(package.name) was not found".red) + return + } + + // Warn if multiple versions match the SHA prefix to prevent accidental deletion + if shaMatches.count > 1 { + let matchingVersions = shaMatches.map { $0.version }.joined(separator: ", ") + errorOutput("SHA prefix '\(version)' matches multiple versions: \(matchingVersions)".yellow) + errorOutput("Please provide a longer SHA prefix or the full SHA to uniquely identify the version.".yellow) return } @@ -648,19 +659,16 @@ public class Mint { .flatMap { $0 } ) - // delete the selected version directories - for vd in versionDirsToDelete { - try vd.path.delete() - } - - // check if any version directories remain under build path + // check if any version directories will remain after deletion (before deleting) let buildPath = package.path + "build" var remainingVersionDirs: [String] = [] + let versionsToDelete = Set(versionDirsToDelete.map { $0.version }) if buildPath.exists { do { remainingVersionDirs = try buildPath.children() .filter { $0.isDirectory && !$0.lastComponent.hasPrefix(".") } .map { $0.lastComponent } + .filter { !versionsToDelete.contains($0) } } catch { errorOutput("Failed to read build path '\(buildPath)': \(error)".red) return @@ -668,6 +676,11 @@ public class Mint { } let removedAllVersions = remainingVersionDirs.isEmpty + // delete the selected version directories + for vd in versionDirsToDelete { + try vd.path.delete() + } + if removedAllVersions { // fully removed package; ensure package path cleanup and metadata update try package.path.delete() @@ -677,9 +690,9 @@ public class Mint { try writeMetadata(metadata) } else { // only specific version(s) removed + // metadata remains unchanged because package still has installed versions let removedVersionsList = versionDirsToDelete.map { $0.version }.joined(separator: ", ") output("\(package.name) (\(removedVersionsList)) was uninstalled") - // metadata remains unchanged because package still has installed versions } // remove links for executables belonging to removed versions diff --git a/Tests/MintTests/MintTests.swift b/Tests/MintTests/MintTests.swift index a67a0a1..0579398 100644 --- a/Tests/MintTests/MintTests.swift +++ b/Tests/MintTests/MintTests.swift @@ -339,5 +339,97 @@ class MintTests: XCTestCase { // Metadata should still contain the package because a version remains XCTAssertEqual(try mint.readMetadata().packages, [fullTestRepo: testPackageDir]) + + // Verify package list is updated correctly after uninstalling specific version + XCTAssertEqual(try mint.listPackages(), [fullTestRepo: [latestVersion]]) + } + + func testUninstallSpecificVersionRemovesLastVersion() throws { + let globalPath = mint.linkPath + testCommand + let package = PackageReference(repo: testRepo, version: testVersion) + + // install version + try mint.install(package: package, link: true) + + // verify installed + XCTAssertTrue(globalPath.exists) + XCTAssertEqual(try mint.listPackages(), [fullTestRepo: [testVersion]]) + XCTAssertEqual(try mint.readMetadata().packages, [fullTestRepo: testPackageDir]) + + // uninstall the only version + try mint.uninstall(name: testRepo, version: testVersion) + + // verify package is completely removed + XCTAssertFalse(globalPath.exists) + XCTAssertEqual(mint.getLinkedExecutables(), []) + XCTAssertEqual(try mint.listPackages(), [:]) + XCTAssertEqual(try mint.readMetadata().packages, [:]) + } + + func testUninstallShaVersion() throws { + let shaVersion = "c3cf95c" + let package = PackageReference(repo: testRepo, version: shaVersion) + + // install SHA version (only this version) + try mint.install(package: package) + + // verify installed + XCTAssertTrue((mintPath + "packages" + testPackageDir + "build" + shaVersion).exists) + let installedPackages = try mint.listPackages() + XCTAssertEqual(installedPackages[fullTestRepo], [shaVersion]) + + // uninstall using SHA prefix (removes the only version, so package should be completely removed) + try mint.uninstall(name: testRepo, version: shaVersion) + + // verify package is completely removed + XCTAssertFalse((mintPath + "packages" + testPackageDir + "build" + shaVersion).exists) + let remainingPackages = try mint.listPackages() + XCTAssertEqual(remainingPackages, [:]) + } + + func testUninstallInvalidVersion() throws { + let package = PackageReference(repo: testRepo, version: testVersion) + + // install a version + try mint.install(package: package) + + // verify installed + let packagesBefore = try mint.listPackages() + XCTAssertEqual(packagesBefore[fullTestRepo], [testVersion]) + + // try to uninstall non-existent version + try mint.uninstall(name: testRepo, version: "99.99.99") + + // verify nothing was removed (error should have prevented deletion) + let packagesAfter = try mint.listPackages() + XCTAssertEqual(packagesAfter[fullTestRepo], [testVersion]) + XCTAssertTrue((mintPath + "packages" + testPackageDir + "build" + testVersion).exists) + } + + func testUninstallInvalidSha() throws { + let package = PackageReference(repo: testRepo, version: testVersion) + + // install a version + try mint.install(package: package) + + // verify installed + let packagesBefore = try mint.listPackages() + XCTAssertEqual(packagesBefore[fullTestRepo], [testVersion]) + + // try to uninstall non-existent SHA + try mint.uninstall(name: testRepo, version: "abc1234") + + // verify nothing was removed (error should have prevented deletion) + let packagesAfter = try mint.listPackages() + XCTAssertEqual(packagesAfter[fullTestRepo], [testVersion]) + XCTAssertTrue((mintPath + "packages" + testPackageDir + "build" + testVersion).exists) + } + + func testUninstallWhenNoVersionsInstalled() throws { + // try to uninstall a version when package is not installed at all + try mint.uninstall(name: testRepo, version: testVersion) + + // verify no error state (function should return gracefully) + XCTAssertEqual(try mint.listPackages(), [:]) } } From 901f4f16c2cef567ade2f535d81c409ae4a4bb8b Mon Sep 17 00:00:00 2001 From: Diogo Autilio Date: Mon, 15 Dec 2025 09:33:23 -0300 Subject: [PATCH 7/8] unit test: add test for ambiguous SHA prefix uninstall prevention --- Tests/MintTests/MintTests.swift | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Tests/MintTests/MintTests.swift b/Tests/MintTests/MintTests.swift index 0579398..beaef7c 100644 --- a/Tests/MintTests/MintTests.swift +++ b/Tests/MintTests/MintTests.swift @@ -432,4 +432,38 @@ class MintTests: XCTestCase { // verify no error state (function should return gracefully) XCTAssertEqual(try mint.listPackages(), [:]) } + + func testUninstallAmbiguousShaPrefix() throws { + // Install a SHA version + let shaVersion1 = "c3cf95c" + try mint.install(package: PackageReference(repo: testRepo, version: shaVersion1)) + + // Create setup: two versions with same SHA prefix (neither exact match for the prefix) + let buildPath = mintPath + "packages" + testPackageDir + "build" + let firstVersionPath = buildPath + shaVersion1 + let longerShaVersion1 = "c3cf95c0" + let longerFirstVersionPath = buildPath + longerShaVersion1 + let shaVersion2 = "c3cf95c1234567890abcdef" + let secondVersionPath = buildPath + shaVersion2 + + // Rename first version to be longer (so prefix isn't exact match) + try firstVersionPath.move(longerFirstVersionPath) + + // Create second version with same prefix + try secondVersionPath.mkpath() + let executable = try longerFirstVersionPath.children().first(where: { $0.isFile && $0.extension == nil }) + if let executable = executable { + try executable.copy(secondVersionPath + executable.lastComponent) + } + + // Try to uninstall with prefix that matches both versions + try mint.uninstall(name: testRepo, version: "c3cf95c") + + // Verify no versions were deleted (ambiguous prefix should prevent deletion) + XCTAssertTrue(longerFirstVersionPath.exists) + XCTAssertTrue(secondVersionPath.exists) + + // Cleanup + try? secondVersionPath.delete() + } } From 5a12bb8e5b2b85f4913aff056ee248d08799ab05 Mon Sep 17 00:00:00 2001 From: Diogo Autilio Date: Mon, 15 Dec 2025 10:09:04 -0300 Subject: [PATCH 8/8] refactor(cli): change uninstall version flag from -v to -V Use uppercase -V for --version to avoid confusion with -v (--verbose) used in other Mint commands like install, run, and bootstrap. --- Sources/MintCLI/Commands/UninstallCommand.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/MintCLI/Commands/UninstallCommand.swift b/Sources/MintCLI/Commands/UninstallCommand.swift index 6a9330d..1151006 100644 --- a/Sources/MintCLI/Commands/UninstallCommand.swift +++ b/Sources/MintCLI/Commands/UninstallCommand.swift @@ -5,7 +5,7 @@ import SwiftCLI class UninstallCommand: MintCommand { @Param var package: String - @Key("-v", "--version", description: "Specify the version to uninstall") var version: String? + @Key("-V", "--version", description: "Specify the version to uninstall") var version: String? init(mint: Mint) { super.init(mint: mint, name: "uninstall", description: "Uninstall a package or a specific version by name")