{"id":13465457,"url":"https://github.com/jakeheis/SwiftCLI","last_synced_at":"2025-03-25T16:31:52.206Z","repository":{"id":19151100,"uuid":"22381591","full_name":"jakeheis/SwiftCLI","owner":"jakeheis","description":"A powerful framework for developing CLIs in Swift","archived":false,"fork":false,"pushed_at":"2021-09-17T16:12:00.000Z","size":2340,"stargazers_count":869,"open_issues_count":9,"forks_count":72,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-03-02T14:37:51.200Z","etag":null,"topics":["cli","command-line","framework","option-parser","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jakeheis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-07-29T13:54:59.000Z","updated_at":"2025-02-28T02:47:55.000Z","dependencies_parsed_at":"2022-07-27T00:02:02.933Z","dependency_job_id":null,"html_url":"https://github.com/jakeheis/SwiftCLI","commit_stats":null,"previous_names":[],"tags_count":54,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakeheis%2FSwiftCLI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakeheis%2FSwiftCLI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakeheis%2FSwiftCLI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakeheis%2FSwiftCLI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jakeheis","download_url":"https://codeload.github.com/jakeheis/SwiftCLI/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245500279,"owners_count":20625541,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","command-line","framework","option-parser","swift"],"created_at":"2024-07-31T15:00:30.305Z","updated_at":"2025-03-25T16:31:51.860Z","avatar_url":"https://github.com/jakeheis.png","language":"Swift","readme":"SwiftCLI\n========\n\n[![Build Status](https://github.com/jakeheis/SwiftCLI/workflows/Test/badge.svg)](https://github.com/jakeheis/SwiftCLI/actions)\n\nA powerful framework for developing CLIs, from the simplest to the most complex, in Swift.\n\n```swift\nimport SwiftCLI\n\nclass GreetCommand: Command {\n    let name = \"greet\"\n    \n    @Param var person: String\n\n    func execute() throws {\n        stdout \u003c\u003c\u003c \"Hello \\(person)!\"\n    }\n}\n\nlet greeter = CLI(name: \"greeter\")\ngreeter.commands = [GreetCommand()]\ngreeter.go()\n```\n\n```bash\n~ \u003e greeter greet world\nHello world!\n```\n\nWith SwiftCLI, you automatically get:\n- Command routing\n- Option parsing\n- Help messages\n- Usage statements\n- Error messages when commands are used incorrectly\n- Zsh completions\n\nTable of Contents\n=================\n  \n  * [Installation](#installation)\n  * [Creating a CLI](#creating-a-cli)\n  * [Commands](#commands)\n    * [Parameters](#parameters)\n      * [Required parameters](#required-parameters)\n      * [Optional parameters](#optional-parameters)\n      * [Collected parameters](#collected-parameters)\n    * [Options](#options)\n      * [Flag options](#flags)\n      * [Keyed options](#keys)\n      * [Option groups](#option-groups)\n      * [Global options](#global-options)\n  * [Command groups](#command-groups)\n  * [Shell completions](#shell-completions)\n  * [Built-in commands](#built-in-commands)\n  * [Input](#input)\n  * [External tasks](#external-tasks)\n  * [Single command CLIs](#single-command-clis)\n  * [Customization](#customization)\n    * [Aliases](#aliases)\n  * [Running your CLI](#running-your-cli)\n  * [Example](#example)\n\n## Installation\n### [Ice Package Manager](https://github.com/jakeheis/Ice)\n```shell\n\u003e ice add jakeheis/SwiftCLI\n```\n### Swift Package Manager\nAdd SwiftCLI as a dependency to your project:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/jakeheis/SwiftCLI\", from: \"6.0.0\")\n]\n```\n\n### Carthage\n\n```\ngithub \"jakeheis/SwiftCLI\" ~\u003e 5.2.2\n```\n\n### CocoaPods\n\n```ruby\npod 'SwiftCLI', '~\u003e 6.0.0'\n```\n\n## Creating a CLI\n\nWhen creating a `CLI`, a `name` is required, and a `version` and `description` are both optional.\n\n```swift\nlet myCli = CLI(name: \"greeter\", version: \"1.0.0\", description: \"Greeter - a friendly greeter\")\n```\n\nYou set commands through the `.commands` property:\n\n```swift\nmyCli.commands = [myCommand, myOtherCommand]\n```\n\nFinally, to run the CLI, you call one of the `go` methods.\n\n```swift\n// Use go if you want program execution to continue afterwards\nmyCli.go() \n\n// Use goAndExit if you want your program to terminate after the CLI has finished\nmyCli.goAndExit()\n\n// Use go(with:) if you want to control the arguments which the CLI runs with\nmyCli.go(with: [\"arg1\", \"arg2\"])\n```\n\n## Commands\n\nIn order to create a command, you must implement the `Command` protocol. All that's required is to implement a `name` property and an `execute` function; the other properties of `Command` are optional (though a `shortDescription` is highly recommended). A simple hello world command could be created as such:\n\n```swift\nclass GreetCommand: Command {\n\n    let name = \"greet\"\n    let shortDescription = \"Says hello to the world\"\n\n    func execute() throws  {\n        stdout \u003c\u003c\u003c \"Hello world!\"\n    }\n\n}\n```\n\n### Parameters\n\nA command can specify what parameters it accepts through certain instance variables. Using reflection, SwiftCLI will identify property wrappers of type `@Param` and `@CollectedParam`. These properties should appear in the order that the command expects the user to pass the arguments. All required parameters must come first, followed by any optional parameters, followed by at most one collected parameter.\n\n```swift\nclass GreetCommand: Command {\n    let name = \"greet\"\n\n    @Param var first: String\n    @Param var second: String?\n    @CollectedParam var remaining: [String]\n}\n```\n\nIn this example, if the user runs `greeter greet Jack Jill up the hill`, `first` will contain the value `Jack`, `second` will contain the value `Jill`, and `remaining` will contain the value `[\"up\", \"the\", \"hill\"]`.\n\n#### @Param\n\nIndividual parameters take the form of the property wrapper `@Param`. Properties wrapped by `@Param` can be required or optional. If the command is not passed enough arguments to satisfy all required parameters, the command will fail.\n\n```swift\nclass GreetCommand: Command {\n    let name = \"greet\"\n\n    @Param var person: String\n    @Param var followUp: String\n\n    func execute() throws {\n        stdout \u003c\u003c\u003c \"Hey there, \\(person)!\"\n        stdout \u003c\u003c\u003c followUp\n    }\n}\n```\n\n```bash\n~ \u003e greeter greet Jack\n\nUsage: greeter greet \u003cperson\u003e \u003cfollowUp\u003e [options]\n\nOptions:\n  -h, --help      Show help information\n\nError: command requires exactly 2 arguments\n\n~ \u003e greeter greet Jack \"What's up?\"\nHey there, Jack!\nWhat's up?\n```\nIf the user does not pass enough arguments to satisfy all optional parameters, the value of these unsatisfied parameters will be `nil`.\n\n```swift\nclass GreetCommand: Command {\n    let name = \"greet\"\n\n    @Param var person: String\n    @Param var followUp: String? // Note: String? in this example, not String\n\n    func execute() throws {\n        stdout \u003c\u003c\u003c \"Hey there, \\(person)!\"\n        if let followUpText = followUp {\n            stdout \u003c\u003c\u003c followUpText\n        }\n    }\n}\n```\n\n```bash\n~ \u003e greeter greet Jack\nHey there, Jack!\n~ \u003e greeter greet Jack \"What's up?\"\nHello, Jack!\nWhat's up?\n```\n\n#### @CollectedParam\n\nCommands may have a single collected parameter after all the other parameters called a `@CollectedParam`. This parameter allows the user to pass any number of arguments, and these arguments will be collected into the array wrapped by the collected parameter. The property wrapped by `@CollectedParam` **must** be an array. By default, `@CollectedParam` does not require the user to pass any arguments. The parameter can require a certain number of values by using the `@CollectedParam(minCount:)` initializer.\n\n```swift\nclass GreetCommand: Command {\n    let name = \"greet\"\n\n    @CollectedParam(minCount: 1) var people: [String]\n\n    func execute() throws {\n        for person in people {\n            stdout \u003c\u003c\u003c \"Hey there, \\(person)!\"\n        }        \n    }\n}\n```\n\n```bash\n~ \u003e greeter greet Jack\nHey there, Jack!\n~ \u003e greeter greet Jack Jill Water\nHey there, Jack!\nHey there, Jill!\nHey there, Water!\n```\n\n#### Value type of parameter\n\nWith all of these parameter property wrappers, any type can be used so long as it conforms to `ConvertibleFromString`. Most primitive types (e.g. `Int`) conform to `ConvertibleFromString` already, as do enums with raw values that are primitive types. \n\n```swift\nclass GreetCommand: Command {\n    let name = \"greet\"\n\n    @Param var number: Int\n\n    func execute() throws {\n        stdout \u003c\u003c\u003c \"Hey there, number \\(number)!\"     \n    }\n}\n```\n\n```bash\n~ \u003e greeter greet Jack\n\nUsage: greeter greet \u003cnumber\u003e [options]\n\nOptions:\n  -h, --help      Show help information\n\nError: invalid value passed to 'number'; expected Int\n\n~ \u003e greeter greet 4\nHey there, number 4!\n```\n\nParameters with enum types which conform to `CaseIterable` have additional specialized behavior. In an error message, the allowed values for that parameter will be spelled out.\n\n```swift\nclass GreetCommand: Command {\n    \n    let name = \"greet\"\n    \n    enum Volume: String, ConvertibleFromString, CaseIterable {\n        case loud\n        case quiet\n    }\n    \n    @Param var volume: Volume\n    \n    func execute() throws {\n        let greeting = \"Hello world!\"\n        \n        switch volume {\n        case .loud: stdout \u003c\u003c\u003c greeting.uppercased()\n        case .quiet: stdout \u003c\u003c\u003c greeting.lowercased()\n        }\n        \n    }\n}\n```\n\n```bash\n~ \u003e greeter greet Jack\n\nUsage: greeter greet \u003cvolume\u003e [options]\n\nOptions:\n  -h, --help      Show help information\n\nError: invalid value passed to 'volume'; expected one of: loud, quiet\n\n~ \u003e greet greet loud\nHELLO WORLD!\n```\n\nTo conform a custom type to `ConvertibleFromString`, simply implement one function:\n\n```swift\nextension MyType: ConvertibleFromString {\n    init?(input: String) {\n        // Construct an instance of MyType from the String, or return nil if not possible\n        ...\n    }\n}\n```\n\n### Options\n\nCommands have support for two types of options: flag options and keyed options. Both types of options can be denoted by either a dash followed by a single letter (e.g. `git commit -a`) or two dashes followed by the option name (e.g. `git commit --all`). Single letter options can be cascaded into a single dash followed by all the desired options: `git commit -am \"message\"` == `git commit -a -m \"message\"`.\n\nOptions are specified with property wrappers on the command class, just like parameters:\n\n```swift\nclass ExampleCommand: Command {\n    ...\n    @Flag(\"-a\", \"--all\")\n    var flag: Bool\n\n    @Key(\"-t\", \"--times\")\n    var key: Int?\n    ...\n}\n```\n\n#### Flags\n\nFlags are simple options that act as boolean switches. For example, if you were to implement `git commit`, `-a` would be a flag option. They take the form of booleans wrapped by `@Flag`.\n\nThe `GreetCommand` could take a \"loudly\" flag:\n\n```swift\nclass GreetCommand: Command {\n\n    ...\n\n    @Flag(\"-l\", \"--loudly\", description: \"Say the greeting loudly\")\n    var loudly: Bool\n\n    func execute() throws {\n        if loudly {\n             ...\n        } else {\n            ...\n        }\n    }\n\n}\n```\n\nA related option type is `@CounterFlag`, which counts the nubmer of times the user passes the same flag. `@CounterFlag` can only wrap properties of type `Int`. For example, with a flag declaration like:\n\n```swift\nclass GreetCommand: Command {\n    ...\n    @CounterFlag(\"-s\", \"--softly\", description: \"Say the greeting softly\")\n    var softly: Int\n    ...\n}\n```\n\nthe user can write `greeter greet -s -s`, and `softly.value` will be `2`.\n\n#### Keys\n\nKeys are options that have an associated value. Using \"git commit\" as an example, \"-m\" would be a keyed option, as it has an associated value - the commit message. They take the form of variables wrapped by '@Key`.\n\nThe `GreetCommand` could take a \"number of times\" option:\n\n```swift\nclass GreetCommand: Command {\n\n    ...\n\n    @Key(\"-n\", \"--number-of-times\", description: \"Say the greeting a certain number of times\")\n    var numberOfTimes: Int?\n\n    func execute() throws {\n        for i in 0..\u003c(numberOfTimes ?? 1) {\n            ...\n        }\n    }\n\n}\n```\n\nThe variable wrapped by `@Key` can be any type conforming to `ConvertibleFromString` as described above. It **must** be optional, or the Swift compiler will crash.\n\nA related option type is `VariadicKey`, which allows the user to pass the same key multiples times with different values. For example, with a key declaration like:\n\n```swift\nclass GreetCommand: Command {\n    ...\n    @VariadicKey(\"-l\", \"--location\", description: \"Say the greeting in a certain location\")\n    var locations: [String]\n    ...\n}\n```\n\nthe user can write `greeter greet -l Chicago -l NYC`, and `locations.value` will be `[\"Chicago\", \"NYC\"]`. The variable wrapped by `@VariadicKey` must be an array of a type conforming to `ConvertibleFromString`.\n\n#### Option groups\n\nThe relationship between multiple options can be specified through option groups. Option groups allow a command to specify that the user must pass at most one option of a group (passing more than one is an error), must pass exactly one option of a group (passing zero or more than one is an error), or must pass one or more options of a group (passing zero is an error). \n\nTo add option groups, a `Command` should implement the property `optionGroups`. Option groups refer to options through the `$` syntax. For example, if the `GreetCommand` had a `loudly` flag and a `whisper` flag but didn't want the user to be able to pass both, an `OptionGroup` could be used:\n\n```swift\nclass GreetCommand: Command {\n\n    ...\n\n    @Flag(\"-l\", \"--loudly\", description: \"Say the greeting loudly\")\n    var loudly: Bool\n\n    @Flag(\"-w\", \"--whisper\", description: \"Whisper the greeting\")\n    var whisper: Bool\n    \n    var optionGroups: [OptionGroup] {\n        return [.atMostOne($loudly, $whipser)] // Note: $loudly and $whisper, not loudly and whisper\n    }\n\n    func execute() throws {\n        if loudly {\n            ...\n        } else if whisper {\n            ...\n        } else {\n            ...\n        }\n    }\n\n}\n```\n\n#### Global options\n\nGlobal options can be used to specify that every command should have a certain option. This is how the `-h` flag is implemented for all commands. Simply add an option to CLI's `.globalOptions` array (and optionally extend `Command` to make the option easy to access in your commands):\n\n```swift\nprivate let verboseFlag = Flag(\"-v\")\nextension Command {\n    var verbose: Bool {\n        return verboseFlag.value\n    }\n}\n\nmyCli.globalOptions.append(verboseFlag)\n```\n\nBy default, every command has a `-h` flag which prints help information. You can turn this off by setting the CLI `helpFlag` to nil:\n\n```swift\nmyCli.helpFlag = nil\n```\n\n#### Usage of options\n\nAs seen in the above examples, `@Flag` and `@Key` both take an optional `description` parameter. A concise description of what the option does should be included here. This allows the `HelpMessageGenerator` to generate a fully informative usage statement for the command.\n\nA command's usage statement is shown in three situations:\n- The user passed an option that the command does not support -- ```greeter greet -z```\n- The user passed the wrong number of arguments\n- The command's help was invoked -- `greeter greet -h`\n\n```bash\n~ \u003e greeter greet -h\n\nUsage: greeter greet \u003cperson\u003e [options]\n\nOptions:\n  -l, --loudly                          Say the greeting loudly\n  -n, --number-of-times \u003cvalue\u003e         Say the greeting a certain number of times\n  -h, --help                            Show help information for this command\n\n```\n\n## Command groups\n\nCommand groups provide a way for related commands to be nested under a certain namespace. Groups can themselves contain other groups.\n\n```swift\nclass ConfigGroup: CommandGroup {\n    let name = \"config\"\n    let children = [GetCommand(), SetCommand()]\n}\nclass GetCommand: Command {\n    let name = \"get\"\n    func execute() throws {}\n}\nclass SetCommand: Command {\n    let name = \"set\"\n    func execute() throws {}\n}\n```\n\nYou can add a command group to your CLI's `.commands` array just as add a normal command:\n\n```swift\ngreeter.commands = [ConfigGroup()]\n```\n\n```shell\n\u003e greeter config\n\nUsage: greeter config \u003ccommand\u003e [options]\n\nCommands:\n  get\n  set\n\n\u003e greeter config set\n\u003e greeter config get\n```\n\n## Shell completions\n\nZsh completions can be automatically generated for your CLI.\n\n```swift\nlet myCli = CLI(...)\n\nlet generator = ZshCompletionGenerator(cli: myCli)\ngenerator.writeCompletions()\n```\n\nCompletions will be automatically generated for command names and options. Parameter completion mode can be specified:\n\n```swift\n@Param(completion: .none)\nvar noCompletions: String\n\n@Param(completion: .filename)\nvar aFile: String\n\n@Param(completion: .values([\n    (\"optionA\", \"the first available option\"),\n    (\"optionB\", \"the second available option\")\n]))\nvar aValue: String\n\n@Param(completion: .function(\"_my_custom_func\"))\nvar aFunction: String\n```\n\nThe default parameter completion mode is `.filename`. If you specify a custom function with `.function`, that function must be supplied when creating the completion generator:\n\n```swift\nclass MyCommand {\n    ...\n    @Param(completion: .function(\"_list_processes\"))\n    var pid: String\n    ...\n}\n\nlet myCLI = CLI(...)\nmyCLI.commands [MyCommand()]\nlet generator = ZshCompletionGenerator(cli: myCli, functions: [\n    \"_list_processes\": \"\"\"\n        local pids\n        pids=( $(ps -o pid=) )\n        _describe '' pids\n        \"\"\"\n])\n```\n\n## Built-in commands\n\n`CLI` has two built-in commands: `HelpCommand` and `VersionCommand`.\n\n### Help Command\n\nThe `HelpCommand` can be invoked with `myapp help`. The `HelpCommand` first prints the app description (if any was given during `CLI.init`). It then iterates through all available commands, printing their name and their short description.\n\n```bash\n~ \u003e greeter help\n\nUsage: greeter \u003ccommand\u003e [options]\n\nGreeter - your own personal greeter\n\nCommands:\n  greet        Greets the given person\n  help         Prints this help information\n\n```\n\nIf you don't want this command to be automatically included, set the `helpCommand` property to nil:\n\n```swift\nmyCLI.helpCommand = nil\n```\n\n### Version Command\nThe `VersionCommand` can be invoked with `myapp version` or `myapp --version`. The VersionCommand prints the version of the app given during init `CLI(name:version:)`. If no version is given, the command is not available.\n\n```bash\n~ \u003e greeter --version\nVersion: 1.0\n```\n\nIf you don't want this command to be automatically included, set the `versionCommand` property to nil:\n\n```swift\nmyCLI.versionCommand = nil\n```\n\n## Input\n\nThe `Input` class makes it easy to read input from stdin. Several methods are available:\n\n```swift\nlet str = Input.readLine()\nlet int = Input.readInt()\nlet double = Input.readDouble()\nlet bool = Input.readBool()\n```\n\nAll `read` methods have four optional parameters:\n- `prompt`: the message to print before accepting input (e.g. \"Input: \")\n- `secure`: if true, the input is hidden as the user types\n- `validation`: a closure which defines whether the input is valid, or if the user should be reprompted\n- `errorResponse`: a closure which is executed when the user enters input which is not valid\n\nFor example, you could write:\n\n```swift\nlet percentage = Input.readDouble(\n    prompt: \"Percentage:\",\n    validation: [.within(0...100)],\n    errorResponse: { (input, reason) in\n        Term.stderr \u003c\u003c\u003c \"'\\(input)' is invalid; must be a number between 0 and 100\"\n    }\n)\n```\n\nwhich would result in an interaction such as:\n\n```shell\nPercentage: asdf\n'asdf' is invalid; must be a number between 0 and 100\nPercentage: 104\n'104' is invalid; must be a number between 0 and 100\nPercentage: 43.6\n```\n\n## External tasks\n\nSwiftCLI makes it easy to execute external tasks:\n\n```swift\n// Execute a command and print output:\ntry Task.run(\"echo\", \"hello\")\ntry Task.run(bash: \"while true; do echo hi \u0026\u0026 sleep 1; done\")\n\n// Execute a command and capture the output:\nlet currentDirectory = try Task.capture(\"pwd\").stdout\nlet sorted = try Task.capture(bash: \"cat Package.swift | sort\").stdout\n```\n\nYou can also use the `Task` class for more custom behavior:\n\n```swift\nlet input = PipeStream()\nlet output = PipeStream()\nlet task = Task(executable: \"sort\", currentDirectory: \"~/Ice\", stdout: output, stdin: input)\ntask.runAsync()\n\ninput \u003c\u003c\u003c \"beta\"\ninput \u003c\u003c\u003c \"alpha\"\ninput.closeWrite()\n\noutput.readAll() // will be alpha\\nbeta\\n\n```\n\nSee `Sources/SwiftCLI/Task.swift` for full documentation on `Task`.\n\n## Single command CLIs\n\nIf your CLI only contains a single command, you may want to execute the command simply by calling `cli`, rather than `cli command`. In this case, you can create your CLI as such:\n\n```swift\nclass Ln: Command {\n    let name = \"ln\"\n    func execute() throws { ... }\n}\n\nlet ln = CLI(singleCommand: Ln())\nln.go()\n```\n\nIn this case, if the user writes `ln myFile newLocation`, rather than searching for a command with the name \"myFile\", `SwiftCLI` will execute the `Ln` command and pass on \"myFile\" as the first argument to that command.\n\nKeep in mind that when creating a single command CLI, you lose the default `VersionCommand`. This means that `cli -v` will not work automatically, and that if you want to print your CLI version you will need to manually implement a `Flag(\"-v\")` on your single command.\n\n## Customization\n\nSwiftCLI was designed with sensible defaults but also the ability to be customized at every level. `CLI` has three properties that can be changed from the default implementations to customized implementations.\n\n### `parser`\n\nThe `Parser` steps through arguments to find the corresponding command, update its parameter values, and recognizes options. Each CLI has a `parser` property which has three mutable properties: `routeBehavior`, `parseOptionsAfterCollectedParameter`, and `responders`.\n\n`routeBehavior` has three possible values. The default, `.search`, steps through the arguments the user passed and tries to find a command with a matching name. If it fails, a help message is printed. `git` is an example of a program which operates this way. The second option, `.searchWithFallback(Command)`, also initially tries to find a command with a name matching the arguments passed by the user, but if it fails, rather than printing a help message it falls back to a certain command. 'bundle' is an example of a program which operates this way. The last option is `.automatically(Command)`. In this route behavior, the CLI automatically routes to the given command without considering arguments. 'ln' is an example of a program which operates this way.\n\n`parseOptionsAfterCollectedParameter` controls whether or not options are recognized once a collected parameter is encountered. It defaults to `false`. Given the following command:\n\n```swift\nclass Run: Command {\n    let name = \"run\"\n\n    @Param var executable: String\n    @CollectedParam var arguments: [String]\n\n    @Flag(\"-a\") var all: Bool\n    ...\n}\n```\n\nif the user calls `swift run myExec -a` and `parseOptionsAfterCollectedParameter` is false, the value of `executable` will be \"myExec\", the value of `arguments` will be `[\"-a\"]`, and the value of `all` will be false. If `parseOptionsAfterCollectedParameter` were true in this situation, the value of `executable` would be \"myExec\", the value of `arguments` would be `[]`, and the value of `all` would be true.\n\n`responders` allows the parser to be completely customized. Check out `Parser.swift` for more information on how this functions by default and how it can be customized in any way.\n\n### `aliases`\n\nAliases can be made through the the `aliases` property on CLI. `Parser` will take these aliases into account while routing to the matching command. For example, if you write:\n\n```swift\nmyCLI.aliases[\"-c\"] = \"command\"\n```\n\nAnd the user makes the call `myapp -c`, the parser will search for a command with the name \"command\" because of the alias, not a command with the name \"-c\".\n\nBy default, \"--version\" is an alias for \"version\", but you can remove this if desired:\n\n```swift\nmyCLI.aliases[\"--version\"] = nil\n```\n\n### `argumentListManipulators`\n\n`ArgumentListManipulator`s act before the `Parser` begins. They take in the arguments as given by the user and can change them slightly. By default, the only argument list manipulator used is `OptionSplitter` which splits options like `-am` into `-a -m`.\n\nYou can implement `ArgumentListManipulator` on your own type and update CLI's property:\n\n```swift\ncli.argumentListManipulators.append(MyManipulator())\n```\n\n### `helpMessageGenerator`\n\nThe messages formed by SwiftCLI can also be customized:\n\n```swift\ncli.helpMessageGenerator = MyHelpMessageGenerator()\n```\n\n## Running your CLI\n\nSimply call `swift run`. In order to ensure your `CLI` gets the arguments passed on the command line, make sure to call `CLI.go()`, **not** `CLI.go(with: [])`.\n\n## CLIs build with SwiftCLI\n\n- [XcodeGen](https://github.com/yonaskolb/XcodeGen)\n- [Mint](https://github.com/yonaskolb/Mint)\n- [SwiftKit](https://github.com/SvenTiigi/SwiftKit)\n- [BartyCrouch](https://github.com/Flinesoft/BartyCrouch)\n- [Ice](https://github.com/jakeheis/Ice)\n\n","funding_links":[],"categories":["Libs","Command Line","Command Line [🔝](#readme)","Swift","Recently Updated","The Index"],"sub_categories":["Command Line","Linter","[Feb 05, 2025](/content/2025/02/05/README.md)","Command Line UI tools"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakeheis%2FSwiftCLI","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjakeheis%2FSwiftCLI","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakeheis%2FSwiftCLI/lists"}