SwiftPM Command - Process gets stuck

Hi everyone. I'm trying to become more familiar with SPM and have started exploring the idea of building a small "SMP modularized project" command-line tool. I got stuck on a step where I am trying to use Process to basically execute swift package tools-version --set and swift package add-target.

The add-target command hangs when I run it from the command line, but it works as expected when I include the add-target launch argument and run the same command from Xcode.

The tools version is working well in both Xcode and the command line. I've created a sample repository GitHub - JozefLipovsky/CmdToolExample that hopefully better demonstrates what I'm trying to explain here. I would appreciate any advice.

I have a simple extension on ParsableCommand:

import ArgumentParser
import Foundation

extension ParsableCommand {
    func execute(arguments: [String]) throws {
        let process = Process()
        process.executableURL = URL(fileURLWithPath: "/usr/bin/swift")
        process.arguments = arguments

        print("\(String(describing: type(of: self))) executing arguments: \(arguments.joined(separator: " "))")

        try process.run()
        process.waitUntilExit()

        print("\(String(describing: type(of: self))) finished executing")
    }
}

And then these 2 sub-commands as an example:

import ArgumentParser
import Foundation

struct UpdateToolsVersion: ParsableCommand {
    static let configuration = CommandConfiguration(
        commandName: "update-tools-version",
        abstract: "Updates Swift.package swift-tools-version"
    )

    mutating func run() throws {
        try execute(arguments: ["package", "tools-version", "--set", "5.9"])
    }
}
import ArgumentParser
import Foundation

struct AddTarget: ParsableCommand {
    static let configuration = CommandConfiguration(
        commandName: "add-target",
        abstract: "Adds a new target in Swift.package"
    )

    mutating func run() throws {
        try execute(arguments: ["package", "add-target", "NewTarget"])
    }
}

With the main command:

import ArgumentParser

public struct Commands: ParsableCommand {
    public static let configuration = CommandConfiguration(
        commandName: "cmd-tool",
        abstract: "A Swift command-line tool example.",
        shouldDisplay: true,
        subcommands: [
            UpdateToolsVersion.self,
            AddTarget.self
        ]
    )

    public init() {}
}

Package manifest:

// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "CmdTool",
    platforms: [
        .macOS(.v13)
    ],
    products: [
        .plugin(
            name: "CmdToolPlugin",
            targets: [
                "CmdToolPlugin"
            ]
        ),
        .executable(
            name: "CmdToolExecutable",
            targets: [
                "CmdToolExecutable"
            ]
        ),
    ],
    dependencies: [
        .package(
            url: "https://github.com/apple/swift-argument-parser.git",
            from: "1.2.0"
        )
    ],
    targets: [
        .plugin(
            name: "CmdToolPlugin",
            capability: .command(
                intent: .custom(
                    verb: "cmd-tool",
                    description: "A CMD tool example."
                ),
                permissions: [
                    .writeToPackageDirectory(reason: "Update SMP project config.")
                ]
            ),
            dependencies: [
                "CmdToolExecutable"
            ]
        ),

        .executableTarget(
            name: "CmdToolExecutable",
            dependencies: [
                "Commands"
            ]
        ),

        .target(
            name: "Commands",
            dependencies: [
                .product(
                    name: "ArgumentParser",
                    package: "swift-argument-parser"
                ),
            ]
        ),
    ]
)

I am assuming the issue is how the commands are executed directly when I build from Xcode with the scheme launch argument vs how they are executed via CmdToolPlugin from the command line

import PackagePlugin
import Foundation

@main
struct CmdToolPlugin: CommandPlugin {
    func performCommand(context: PluginContext, arguments: [String]) async throws {
        let tool = try context.tool(named: "CmdToolExecutable")

        let process = Process()
        process.executableURL = tool.url
        process.arguments = arguments

        print("\(String(describing: type(of: self))) performCommand arguments: \(arguments.joined(separator: " "))")

        try process.run()
        process.waitUntilExit()

        let gracefulExit = process.terminationReason == .exit && process.terminationStatus == 0
        if !gracefulExit {
            let reason = process.terminationReason.rawValue
            let status = process.terminationStatus
            throw "\(String(describing: type(of: self))) failed - reason: \(reason) - status: \(status)"
        }

        print("\(String(describing: type(of: self))) finished performCommand")
    }
}

I'm still confused about how UpdateToolsVersion works in both cases, and AddTarget always gets stuck in the command line. Do you have any idea what I might be doing wrong?