Clikt Documentation

repository·master·Indexed 25 days ago

https://github.com/ajalt/clikt

A multiplatform Kotlin library for creating simple, intuitive, and type-safe command line interfaces. It includes modules like clikt-core for basic functionality, clikt-mordant for enhanced terminal styling, and clikt-markdown for rendering help text as Markdown. Key features include support for subcommands, custom validation, type conversion, shell completion for bash, zsh, and fish, and specialized command types such as SuspendingCliktCommand for coroutines and ChainedCliktCommand for passing values between commands.

Tokens
30.1K
Snippets
97
Records
160
Agent score
84%

What's inside Clikt

  1. Clikt vs Java-based CLI libraries (JCommander/Picocli)

    master

    While Java libraries like JCommander and Picocli are functional, they rely on annotations and reflection, which can lead to several limitations in Kotlin:

    • Runtime Type Errors: Type errors are often caught at runtime rather than compile time.
    • Complexity in Custom Types: Defining custom types requires registering type adapters.
    • Arity Limitations: Some libraries (like JCommander) have restrictions on converting multiple values to specific types (e.g., Integer) due to Java type erasure.
    • Lack of Composability: Java libraries often struggle with nesting subcommands or combining multiple values with multiple occurrences.
  2. Distinguish between Options and Arguments

    master

    Clikt provides two types of parameters:

    • Options: Typically optional. They can act as flags (no value required), prompt for missing input, or load values from environment variables. They are best for configuration and settings.
    • Arguments: Typically required. They can accept a variable number of values. They are best for values like file paths, URLs, or other mandatory inputs.

    If following Unix conventions, use options for most parameters and arguments for required values or file paths.

  3. Compare Clikt with other Kotlin CLI libraries

    master

    Clikt is designed for robust, POSIX-compliant command line interfaces with a focus on composability and type safety. Unlike kotlin-argparser or kotlinx.cli, Clikt provides:

    • Unrestricted command composability: Easily nest subcommands.
    • Full static type safety: Parameters are type-safe at compile time.
    • Composable parameter customization: Transform values without registering external converter objects.
    • Unix convention support: Built-in support for environment variables, keyboard interactivity, and line ending normalization.
    • Consistent API: All options are created via option() and all arguments via argument(), making the distinction clear.
  4. Understand Clikt Multiplatform Support

    master

    Clikt is designed for multiplatform use, but support varies by target:

    • JVM: Full support, including JVM-specific extensions like file and path parameter types.
    • Desktop native (Linux, Windows, macOS): Full support.
    • JavaScript and WasmJS:
      • Node.js: Full functionality supported.
      • Browser: The default TerminalInterface outputs to the browser's developer console. To change this, you can define a custom TerminalInterface or call parse() instead of main() to handle output manually.
    • iOS: Full support.
    • watchOS, tvOS, and WasmWasi: All functionality is available, but the markdown module is not supported.
  5. Upgrade Clikt to 5.0: CliktCommand Constructor Changes

    master

    In version 5.0, the CliktCommand constructor no longer accepts most configuration parameters. Instead, these must be provided by overriding open properties or functions in your command class.

    Mapping of removed parameters to new properties:

    Removed ParameterNew Replacement
    helpoverride fun help(context: Context)
    epilogoverride fun helpEpilog(context: Context)
    invokeWithoutSubcommandoverride val invokeWithoutSubcommand
    printHelpOnEmptyArgsoverride val printHelpOnEmptyArgs
    helpTagsoverride val helpTags
    autoCompleteEnvvaroverride val autoCompleteEnvvar
    allowMultipleSubcommandsoverride val allowMultipleSubcommands
    treatUnknownOptionsAsArgsoverride val treatUnknownOptionsAsArgs
    hiddenoverride val hiddenFromHelp
    class MyCommand : CliktCommand(name="mycommand") {
        override fun help(context: Context) = "command help"
        override fun helpEpilog(context: Context) = "command epilog"
        override val invokeWithoutSubcommand = true
        override val printHelpOnEmptyArgs = true
        override val helpTags = mapOf("tag" to "value")
        override val autoCompleteEnvvar = "MYCOMMAND_COMPLETE"
        override val allowMultipleSubcommands = true
        override val treatUnknownOptionsAsArgs = true
        override val hiddenFromHelp = true
    }
  6. Install Clikt via Gradle

    master

    Clikt is distributed through Maven Central. You can add it to your Kotlin project using the following Gradle dependencies. You can also optionally include clikt-markdown for rendering markdown in help messages.

    If you are using Maven instead of Gradle, use the artifact ID clikt-jvm.

    dependencies {
       implementation("com.github.ajalt.clikt:clikt:5.1.0")
    
       // optional support for rendering markdown in help messages
       implementation("com.github.ajalt.clikt:clikt-markdown:5.1.0")
    }
  7. Print to Stdout and Stderr using echo

    master

    Use the echo function instead of println for better multi-platform support and terminal features. echo uses Mordant, which supports colors and automatic terminal detection.

    • To print to stderr, pass err = true to the echo function.
    • Using echo allows Clikt's testing utilities to capture output, whereas println output will not be captured.
  8. Read option values from configuration files

    master

    Use Context.valueSource to define sources for option values when they are not provided via CLI or environment variables.

    • Properties Files: Use PropertiesValueSource.from("path/to/file") (JVM only).
    • Maps: Use MapValueSource.
    • Custom Sources: Implement the ValueSource interface (e.g., for JSON).

    To support multiple sources, use Context.valueSources. Clikt searches them in order.

    Precedence: By default, environment variables are checked before configuration files. To reverse this, set Context.readEnvvarBeforeValueSource = false.

    class Hello : CliktCommand() {
        init {
            context {
                valueSource = PropertiesValueSource.from("myconfig.properties")
            }
        }
        val name by option()
        override fun run() = echo("Hello $name")
    }
  9. Customize option types and behaviors

    master

    You can customize options using extension functions. Customizations are orthogonal and can be combined:

    1. Value Type: Change the type from String using functions like .int(), .float(), .double(), .choice(), or manually with .convert().
    2. Number of Values: Change how many values an option requires using .pair(), .triple(), or .transformValues(n) { ... }.
    3. Handling Occurrences: Change how missing or multiple values are handled using .default(), .defaultLazy(), or .multiple().
    val a: String? by option()
    val b: Int? by option().int()
    val c: Pair<Int, Int>? by option().int().pair()
    val d: Pair<Int, Int> by option().int().pair().default(0 to 0)
    val e: Pair<Float, Float> by option().float().pair().default(0f to 0f)
  10. Upgrade Clikt to 5.0: Extension Functions

    master

    In version 5.0, several key methods and properties have been moved to extension functions. You must explicitly import them to use them.

    Required Imports:

    • CliktCommand.main and CliktCommand.parse require import com.github.ajalt.clikt.core.main.
    • Context.obj requires import com.github.ajalt.clikt.core.obj.
    • Context.terminal and OptionTransformContext.terminal require import com.github.ajalt.clikt.core.terminal.
    import com.github.ajalt.clikt.core.main
    fun main(args: Array<String>) = MyCommand().main(args)
    
    import com.github.ajalt.clikt.core.obj
    import com.github.ajalt.clikt.core.terminal
    
    fun main() {
        val ctx = MyCommand().currentContext
        ctx.terminal.info(ctx.obj)
    }
  11. Share common options with subcommands using OptionGroup

    master

    You can group common options into an OptionGroup. By including this group in each subcommand, you allow users to specify the common options after the subcommand name. This is useful for keeping the command line structure intuitive.

    class CommonOptions: OptionGroup("Standard Options:") {
        val token by option(help="api token to use for requests").default("...")
        val hostname by option(help="base url for requests").default("example.com")
    }
    
    class MyApi : NoOpCliktCommand()
    
    class Store : CliktCommand() {
        private val commonOptions by CommonOptions()
        private val file by option(help="file to store").file(canBeDir = false)
        override fun run() {
            myApiStoreFile(commonOptions.token, commonOptions.hostname, file)
        }
    }
    
    class Fetch : CliktCommand() {
        private val commonOptions by CommonOptions()
        private val outdir by option(help="directory to store file in").file(canBeFile = false)
        override fun run() {
            myApiFetchFile(commonOptions.token, commonOptions.hostname, outdir)
        }
    }
    
    fun main(args: Array<String>) = MyApi().subcommands(Store(), Fetch()).main(args)