Multiplatform command line interface parsing for Kotlin
—
Positional argument parsing with type conversion and validation. Arguments are processed in order and support optional, multiple, and paired value patterns.
Create positional arguments using the argument() function with support for help text and completion.
/**
* Create a positional argument
* @param name Argument name for help display
* @param help Help text for the argument
* @param helpTags Extra help formatter information
* @param completionCandidates Completion candidates
*/
fun CliktCommand.argument(
name: String = "",
help: String = "",
helpTags: Map<String, String> = emptyMap(),
completionCandidates: CompletionCandidates? = null
): RawArgumentUsage Examples:
class MyCommand : CliktCommand() {
// Basic argument
private val filename by argument(name = "FILE", help = "Input file to process")
// Argument with completion
private val hostname by argument(
name = "HOST",
help = "Target hostname",
completionCandidates = CompletionCandidates.Hostname
)
}Convert argument values to specific types with built-in converters.
/**
* Convert argument value using custom conversion function
*/
fun <T : Any> RawArgument.convert(
conversion: ArgValueConverter<String, T>
): ProcessedArgument<T, T>
/** Convert to Int */
fun RawArgument.int(): ProcessedArgument<Int, Int>
/** Convert to Long */
fun RawArgument.long(): ProcessedArgument<Long, Long>
/** Convert to Float */
fun RawArgument.float(): ProcessedArgument<Float, Float>
/** Convert to Double */
fun RawArgument.double(): ProcessedArgument<Double, Double>
/** Convert to Boolean */
fun RawArgument.boolean(): ProcessedArgument<Boolean, Boolean>
/** Convert to Enum */
fun <T : Enum<T>> RawArgument.enum(): ProcessedArgument<T, T>
/** Convert using choice map */
fun <T : Any> RawArgument.choice(choices: Map<String, T>): ProcessedArgument<T, T>
/** Convert using string choices */
fun RawArgument.choice(vararg choices: String): ProcessedArgument<String, String>Usage Examples:
class MyCommand : CliktCommand() {
// Numeric arguments
private val port by argument(name = "PORT", help = "Port number").int()
private val ratio by argument(name = "RATIO", help = "Ratio value").double()
// Enum argument
enum class Operation { ADD, SUBTRACT, MULTIPLY, DIVIDE }
private val operation by argument(name = "OP", help = "Operation to perform").enum<Operation>()
// Choice argument
private val format by argument(name = "FORMAT", help = "Output format")
.choice("json", "xml", "yaml")
// Custom conversion
private val date by argument(name = "DATE", help = "Date in ISO format")
.convert { LocalDate.parse(it) }
override fun run() {
echo("Port: $port")
echo("Operation: $operation")
echo("Format: $format")
echo("Date: $date")
}
}Process argument values with optional, multiple, and paired value patterns.
/** Make argument optional */
fun <T : Any> ProcessedArgument<T, T>.optional(): ProcessedArgument<T?, T>
/** Accept multiple values */
fun <T : Any> ProcessedArgument<T, T>.multiple(): ProcessedArgument<List<T>, T>
/** Convert multiple values to Set */
fun <T : Any> ProcessedArgument<List<T>, T>.unique(): ProcessedArgument<Set<T>, T>
/** Accept exactly 2 values */
fun <T : Any> ProcessedArgument<T, T>.pair(): ProcessedArgument<Pair<T, T>, T>
/** Accept exactly 3 values */
fun <T : Any> ProcessedArgument<T, T>.triple(): ProcessedArgument<Triple<T, T, T>, T>
/** Provide default value */
fun <T : Any> ProcessedArgument<T, T>.default(value: T): ArgumentDelegate<T>
/** Provide lazy default value */
fun <T : Any> ProcessedArgument<T, T>.defaultLazy(value: () -> T): ArgumentDelegate<T>Usage Examples:
class MyCommand : CliktCommand() {
// Optional argument
private val inputFile by argument(name = "INPUT", help = "Input file").optional()
// Multiple arguments
private val files by argument(name = "FILES", help = "Files to process").multiple()
private val uniqueNames by argument(name = "NAMES", help = "Unique names").multiple().unique()
// Paired arguments
private val coordinates by argument(name = "X Y", help = "X Y coordinates").int().pair()
// Triple arguments
private val rgb by argument(name = "R G B", help = "RGB color values").int().triple()
// Default value
private val outputFile by argument(name = "OUTPUT", help = "Output file")
.default("output.txt")
override fun run() {
echo("Input file: $inputFile")
echo("Files: $files")
echo("Coordinates: $coordinates")
echo("RGB: $rgb")
echo("Output file: $outputFile")
}
}Validate argument values with custom validators and built-in checks.
/**
* Validate argument value
* @param validator Custom validation function
*/
fun <AllT, ValueT> ProcessedArgument<AllT, ValueT>.validate(
validator: ArgValidator<AllT>
): ArgumentDelegate<AllT>
/**
* Check argument value with boolean condition
* @param message Error message if check fails
* @param validator Boolean check function
*/
fun <AllT, ValueT> ProcessedArgument<AllT, ValueT>.check(
message: String,
validator: (AllT) -> Boolean
): ArgumentDelegate<AllT>Usage Examples:
class MyCommand : CliktCommand() {
// Custom validation
private val port by argument(name = "PORT", help = "Port number").int().validate {
require(it in 1..65535) { "Port must be between 1 and 65535" }
}
// Boolean check
private val percentage by argument(name = "PERCENT", help = "Percentage value")
.int().check("Must be 0-100") { it in 0..100 }
// Validate multiple values
private val files by argument(name = "FILES", help = "Input files").multiple()
.validate { fileList ->
require(fileList.isNotEmpty()) { "At least one file must be specified" }
}
override fun run() {
echo("Port: $port")
echo("Percentage: $percentage")
echo("Files: $files")
}
}Add or modify help text for arguments.
/**
* Add help text to processed argument
* @param help Help text for the argument
*/
fun <AllT, ValueT> ProcessedArgument<AllT, ValueT>.help(help: String): ProcessedArgument<AllT, ValueT>Usage Examples:
class MyCommand : CliktCommand() {
private val count by argument()
.int()
.help("Number of items to process (must be positive)")
.check("Must be positive") { it > 0 }
private val files by argument()
.multiple()
.help("List of files to process (at least one required)")
.validate { require(it.isNotEmpty()) { "At least one file required" } }
}class MyCommand : CliktCommand() {
private val filename by argument(name = "FILE", help = "Input file")
override fun run() {
echo("Processing file: $filename")
}
}class MyCommand : CliktCommand() {
private val outputFile by argument(name = "OUTPUT", help = "Output file").optional()
override fun run() {
val output = outputFile ?: "default.txt"
echo("Output file: $output")
}
}class MyCommand : CliktCommand() {
private val inputFiles by argument(name = "FILES", help = "Input files").multiple()
override fun run() {
if (inputFiles.isEmpty()) {
echo("No files specified")
} else {
echo("Processing ${inputFiles.size} files:")
inputFiles.forEach { echo(" - $it") }
}
}
}class MyCommand : CliktCommand() {
private val operation by argument(name = "OPERATION", help = "Operation to perform")
.choice("copy", "move", "delete")
private val source by argument(name = "SOURCE", help = "Source file")
private val destination by argument(name = "DEST", help = "Destination file").optional()
override fun run() {
when (operation) {
"copy", "move" -> {
requireNotNull(destination) { "Destination required for $operation" }
echo("$operation $source to $destination")
}
"delete" -> {
echo("Deleting $source")
}
}
}
}/**
* Base argument interface
*/
interface Argument {
/** The metavar for this argument */
val name: String
/** The number of values that this argument takes (negative indicates variable number) */
val nvalues: Int
/** If true, an error will be thrown if this argument is not given */
val required: Boolean
/** The description of this argument */
fun getArgumentHelp(context: Context): String
/** Extra information about this argument 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
/** Information about this argument for the help output */
fun parameterHelp(context: Context): ParameterHelp.Argument?
/** Called after this command's argv is parsed to transform and store the argument's value */
fun finalize(context: Context, values: List<String>)
/** Called after all parameters have been finalized to perform validation */
fun postValidate(context: Context)
}
/**
* Argument property delegate interface
*/
interface ArgumentDelegate<out T> : Argument, ReadOnlyProperty<CliktCommand, T>, PropertyDelegateProvider<CliktCommand, ReadOnlyProperty<CliktCommand, T>> {
/** The value for this argument */
val value: T
override fun getValue(thisRef: CliktCommand, property: KProperty<*>): T = value
}
/**
* Raw unprocessed argument
*/
interface RawArgument : Argument
/**
* Argument with processed values
*/
interface ProcessedArgument<out AllT, out ValueT> : Argumenttypealias ArgValueConverter<InT, ValueT> = ArgumentTransformContext.(InT) -> ValueT
typealias ArgValueTransformer<T> = ArgValueConverter<String, T>
typealias ArgValidator<T> = ArgumentTransformContext.(T) -> UnitInstall with Tessl CLI
npx tessl i tessl/maven-com-github-ajalt--clikt-jvm