CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-github-ajalt--clikt-jvm

Multiplatform command line interface parsing for Kotlin

Pending
Overview
Eval results
Files

options.mddocs/

Options

Declarative option parsing with type conversion, validation, and default values. Options are declared as delegated properties with a fluent API for configuration.

Capabilities

Option Declaration

Create options using the option() function with support for multiple names, help text, environment variables, and completion.

/**
 * Create an option
 * @param names Option names (e.g., "-v", "--verbose")
 * @param help Help text for the option
 * @param metavar Placeholder for option value in help
 * @param hidden Hide option from help
 * @param helpTags Extra help formatter information
 * @param envvar Environment variable name
 * @param valueSourceKey Key for value source lookup
 * @param completionCandidates Completion candidates
 */
fun CliktCommand.option(
    vararg names: String,
    help: String = "",
    metavar: String? = null,
    hidden: Boolean = false,
    helpTags: Map<String, String> = emptyMap(),
    envvar: String? = null,
    valueSourceKey: String? = null,
    completionCandidates: CompletionCandidates? = null
): RawOption

Usage Examples:

class MyCommand : CliktCommand() {
    // Basic option
    private val verbose by option("-v", "--verbose", help = "Enable verbose output")
    
    // Option with environment variable
    private val apiKey by option("--api-key", envvar = "API_KEY", help = "API key")
    
    // Hidden option
    private val debug by option("--debug", hidden = true, help = "Debug mode")
}

Type Conversion

Convert option values to specific types with built-in converters.

/**
 * Convert option value using custom conversion function
 */
inline fun <InT : Any, ValueT : Any> NullableOption<InT, InT>.convert(
    metavar: String,
    completionCandidates: CompletionCandidates? = null,
    crossinline conversion: ValueConverter<InT, ValueT>
): NullableOption<ValueT, ValueT>

inline fun <InT : Any, ValueT : Any> NullableOption<InT, InT>.convert(
    crossinline metavar: (Context) -> String,
    completionCandidates: CompletionCandidates? = null,
    crossinline conversion: ValueConverter<InT, ValueT>
): NullableOption<ValueT, ValueT>

/** Convert to Int */
fun RawOption.int(): NullableOption<Int, Int>

/** Convert to Long */
fun RawOption.long(): NullableOption<Long, Long>

/** Convert to Float */
fun RawOption.float(): NullableOption<Float, Float>

/** Convert to Double */
fun RawOption.double(): NullableOption<Double, Double>

/** Convert to Boolean */
fun RawOption.boolean(): NullableOption<Boolean, Boolean>

/** Convert to Enum */
fun <T : Enum<T>> RawOption.enum(): NullableOption<T, T>

/** Convert using choice map */
fun <T : Any> RawOption.choice(choices: Map<String, T>): NullableOption<T, T>

/** Convert using string choices */
fun RawOption.choice(vararg choices: String): NullableOption<String, String>

Usage Examples:

class MyCommand : CliktCommand() {
    // Numeric options
    private val port by option("--port", help = "Port number").int().default(8080)
    private val timeout by option("--timeout", help = "Timeout in seconds").double()
    
    // Enum option
    enum class LogLevel { DEBUG, INFO, WARN, ERROR }
    private val logLevel by option("--log-level").enum<LogLevel>().default(LogLevel.INFO)
    
    // Choice option
    private val format by option("--format").choice("json", "xml", "yaml").default("json")
    
    // Custom conversion
    private val date by option("--date").convert("DATE") { LocalDate.parse(it) }
}

Value Processing

Process option values with defaults, validation, and multiple value handling.

/** Provide default value */
fun <T : Any> NullableOption<T, T>.default(value: T): OptionDelegate<T>

/** Provide lazy default value */
fun <T : Any> NullableOption<T, T>.defaultLazy(value: () -> T): OptionDelegate<T>

/** Mark option as required */
fun <T : Any> NullableOption<T, T>.required(): OptionDelegate<T>

/** Accept multiple values */
fun <T : Any> NullableOption<T, T>.multiple(): OptionDelegate<List<T>>

/** Convert multiple values to Set */
fun <T : Any> OptionDelegate<List<T>>.unique(): OptionDelegate<Set<T>>

/** Accept exactly 2 values */
fun <T : Any> NullableOption<T, T>.pair(): NullableOption<Pair<T, T>, T>

/** Accept exactly 3 values */
fun <T : Any> NullableOption<T, T>.triple(): NullableOption<Triple<T, T, T>, T>

Usage Examples:

class MyCommand : CliktCommand() {
    // Default values
    private val host by option("--host").default("localhost")
    private val port by option("--port").int().default(8080)
    
    // Lazy default
    private val timestamp by option("--timestamp").defaultLazy { Instant.now().toString() }
    
    // Required option
    private val apiKey by option("--api-key").required()
    
    // Multiple values
    private val includes by option("--include").multiple()
    private val tags by option("--tag").multiple().unique()
    
    // Paired values
    private val coordinates by option("--coord").int().pair()
    
    override fun run() {
        echo("Host: $host:$port")
        echo("Includes: $includes")
        echo("Coordinates: $coordinates")
    }
}

Validation

Validate option values with custom validators and built-in checks.

/**
 * Validate option value
 * @param validator Custom validation function
 */
fun <T> NullableOption<T, T>.validate(validator: OptionValidator<T>): NullableOption<T, T>

/**
 * Check option value with boolean condition
 * @param message Error message if check fails
 * @param validator Boolean check function
 */
fun <T> NullableOption<T, T>.check(
    message: String,
    validator: (T) -> Boolean
): NullableOption<T, T>

Usage Examples:

class MyCommand : CliktCommand() {
    // Custom validation
    private val port by option("--port").int().validate {
        require(it in 1..65535) { "Port must be between 1 and 65535" }
    }
    
    // Boolean check
    private val percentage by option("--percentage").int().check("Must be 0-100") {
        it in 0..100
    }
    
    // Multiple validations
    private val email by option("--email")
        .check("Must contain @") { "@" in it }
        .check("Must end with .com") { it.endsWith(".com") }
}

Special Option Types

Special option types for common CLI patterns.

/** Boolean flag option (no value) */
fun RawOption.flag(default: Boolean = false): OptionDelegate<Boolean>

/** Count option occurrences */
fun RawOption.counted(): OptionDelegate<Int>

/** Switch option with choices */
fun <T> RawOption.switch(choices: Map<String, T>): OptionDelegate<T?>

/** Version option that prints version and exits */
fun CliktCommand.versionOption(
    version: String,
    names: Set<String> = setOf("-V", "--version"),
    help: String = "Show the version and exit",
    message: String = version
): Unit

Usage Examples:

class MyCommand : CliktCommand() {
    // Flag option
    private val verbose by option("-v", "--verbose", help = "Verbose output").flag()
    
    // Counted option
    private val verbosity by option("-v", "--verbose", help = "Increase verbosity").counted()
    
    // Switch option
    private val mode by option("--mode").switch(
        mapOf(
            "--dev" to "development",
            "--prod" to "production",
            "--test" to "testing"
        )
    )
    
    // Version option
    init {
        versionOption("1.0.0")
    }
    
    override fun run() {
        if (verbose) echo("Verbose mode enabled")
        echo("Verbosity level: $verbosity")
        echo("Mode: $mode")
    }
}

Eager Options

Options that are processed before normal parameter parsing.

/**
 * Create eager option that processes before normal parsing
 */
fun CliktCommand.eagerOption(
    vararg names: String,
    help: String = "",
    hidden: Boolean = false,
    helpTags: Map<String, String> = emptyMap()
): EagerOption

abstract class EagerOption : Option {
    /** Process the option value */
    abstract fun process(context: Context, value: String)
}

Option Interfaces

/**
 * Base option interface
 */
interface Option {
    /** A name representing the values for this option that can be displayed to the user */
    fun metavar(context: Context): String?
    
    /** The description of this option, usually a single line */
    fun optionHelp(context: Context): String
    
    /** The names that can be used to invoke this option */
    val names: Set<String>
    
    /** Names that can be used for a secondary purpose, like disabling flag options */
    val secondaryNames: Set<String>
    
    /** The min and max number of values that must be given to this option */
    val nvalues: IntRange
    
    /** If true, this option should not appear in help output */
    val hidden: Boolean
    
    /** Extra information about this option to pass to the help formatter */
    val helpTags: Map<String, String>
    
    /** Optional set of strings to use when the user invokes shell autocomplete */
    val completionCandidates: CompletionCandidates
    
    /** Optional explicit key to use when looking this option up from a ValueSource */
    val valueSourceKey: String?
    
    /** If true, this option can be specified without a name e.g. `-2` instead of `-o2` */
    val acceptsNumberValueWithoutName: Boolean
    
    /** If true, the presence of this option will halt parsing immediately */
    val eager: Boolean
    
    /** If false, invocations must be of the form `--foo=1` or `-f1` */
    val acceptsUnattachedValue: Boolean
    
    /** Information about this option for the help output */
    fun parameterHelp(context: Context): HelpFormatter.ParameterHelp.Option?
    
    /** Called after this command's argv is parsed to transform and store the option's value */
    fun finalize(context: Context, invocations: List<Invocation>)
    
    /** Called after all parameters have been finalized to perform validation */
    fun postValidate(context: Context)
}

/**
 * Option property delegate interface
 */
interface OptionDelegate<T> : GroupableOption, ReadOnlyProperty<ParameterHolder, T>, PropertyDelegateProvider<ParameterHolder, ReadOnlyProperty<ParameterHolder, T>> {
    /** The value for this option */
    val value: T
    
    /** Implementations must call ParameterHolder.registerOption */
    override operator fun provideDelegate(
        thisRef: ParameterHolder,
        property: KProperty<*>
    ): ReadOnlyProperty<ParameterHolder, T>
    
    override fun getValue(thisRef: ParameterHolder, property: KProperty<*>): T = value
}

/**
 * Raw unprocessed option
 */
interface RawOption : Option

/**
 * Option that may have null value
 */
interface NullableOption<out T, ValueT> : Option

/**
 * Option with processed values
 */
interface OptionWithValues<ValueT, EachT, AllT> : Option

/**
 * Flag option for boolean values
 */
interface FlagOption : Option

/**
 * Eager option processed before normal parsing
 */
abstract class EagerOption : Option {
    abstract fun process(context: Context, value: String)
}

Type Aliases

typealias ValueConverter<InT, ValueT> = OptionTransformContext.(InT) -> ValueT
typealias OptionValidator<T> = OptionTransformContext.(T) -> Unit

Install with Tessl CLI

npx tessl i tessl/maven-com-github-ajalt--clikt-jvm

docs

arguments.md

configuration-sources.md

core-commands.md

exceptions.md

index.md

options.md

parameter-groups.md

parameter-types.md

shell-completion.md

testing-utilities.md

tile.json