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

core-commands.mddocs/

Core Commands

Foundation command classes and execution framework for building CLI applications. The CliktCommand class provides the base functionality for parameter registration, parsing, and execution.

Capabilities

CliktCommand

Abstract base class for all CLI commands. Provides parameter registration, parsing lifecycle, and execution framework.

/**
 * Abstract base class for all CLI commands
 * @param help Help text for the command
 * @param epilog Text displayed at end of help output
 * @param name Command name (inferred from class name if null)
 * @param invokeWithoutSubcommand Allow execution without subcommands
 * @param printHelpOnEmptyArgs Print help when no arguments given
 * @param helpTags Extra help formatter information
 */
abstract class CliktCommand(
    help: String = "",
    epilog: String = "",
    name: String? = null,
    invokeWithoutSubcommand: Boolean = false,
    printHelpOnEmptyArgs: Boolean = false,
    helpTags: Map<String, String> = emptyMap()
) {
    /** Main execution method - must be implemented by subclasses */
    abstract fun run()
    
    /** Parse and execute command with error handling */
    fun main(argv: List<String>)
    
    /** Parse command without error handling */
    fun parse(argv: List<String>, parentContext: Context? = null)
    
    /** Print output to terminal */
    fun echo(message: Any?, trailingNewline: Boolean = true, err: Boolean = false)
    
    /** Add message for later printing */
    fun issueMessage(message: String)
    
    /** Get formatted help text */
    fun getFormattedHelp(error: CliktError? = null): String?
    
    /** Print formatted help text */
    fun echoFormattedHelp(error: CliktError? = null)
    
    /** Get command aliases map */
    fun aliases(): Map<String, List<String>>
    
    /** Get all help parameters */
    fun allHelpParams(): List<ParameterHelp>
    
    /** Get registered subcommands */
    fun registeredSubcommands(): List<CliktCommand>
    
    /** Get registered options */
    fun registeredOptions(): List<Option>
    
    /** Get registered arguments */
    fun registeredArguments(): List<Argument>
    
    /** Get registered parameter groups */
    fun registeredParameterGroups(): List<ParameterGroup>
    
    /** Register an option */
    fun registerOption(option: Option)
    
    /** Register an argument */
    fun registerArgument(argument: Argument)
    
    /** Register a parameter group */
    fun registerOptionGroup(group: ParameterGroup)
    
    /** Get command help text */
    fun commandHelp(context: Context): String
    
    /** Get epilog text */
    fun commandHelpEpilog(context: Context): String
    
    /** The name of this command, used in help output */
    val commandName: String
    
    /** All messages issued during parsing */
    val messages: List<String>
    
    /** The names of all direct children of this command */
    fun registeredSubcommandNames(): List<String>
    
    /** Parse command line and throw exception if parsing fails */
    fun parse(argv: Array<String>, parentContext: Context? = null)
    
    /** Parse command line and print helpful output on errors */
    fun main(argv: Array<out String>)
    
    /** The help displayed in commands list when used as subcommand */
    protected fun shortHelp(context: Context): String
}

Usage Examples:

import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.arguments.*

// Basic command
class GreetCommand : CliktCommand(name = "greet", help = "Greet a person") {
    private val name by argument(help = "Name of person to greet")
    private val greeting by option("-g", "--greeting", help = "Greeting to use").default("Hello")
    
    override fun run() {
        echo("$greeting $name!")
    }
}

// Command with subcommands
class GitCommand : CliktCommand(name = "git") {
    override fun run() = Unit
}

class CommitCommand : CliktCommand(name = "commit", help = "Record changes") {
    private val message by option("-m", "--message", help = "Commit message").required()
    
    override fun run() {
        echo("Committing with message: $message")
    }
}

fun main() {
    GitCommand().subcommands(CommitCommand()).main()
}

NoOpCliktCommand

Convenience base class with empty run() implementation for commands that only contain subcommands.

/**
 * Convenience base class with empty run() implementation
 * Useful for commands that only contain subcommands
 */
open class NoOpCliktCommand(
    help: String = "",
    epilog: String = "",
    name: String? = null,
    invokeWithoutSubcommand: Boolean = false,
    printHelpOnEmptyArgs: Boolean = false,
    helpTags: Map<String, String> = emptyMap()
) : CliktCommand(
    help, epilog, name, invokeWithoutSubcommand, printHelpOnEmptyArgs, helpTags
) {
    override fun run() = Unit
}

Context

Manages parsing configuration and execution state. Provides context-aware error handling and configuration management.

/**
 * Manages parsing configuration and execution state
 * Context instances are created internally and configured via Context.Builder
 */
class Context private constructor(
    val parent: Context?,
    val command: CliktCommand,
    val allowInterspersedArgs: Boolean,
    val allowGroupedShortOptions: Boolean,
    val autoEnvvarPrefix: String?,
    val printExtraMessages: Boolean,
    val helpOptionNames: Set<String>,
    val helpFormatter: (Context) -> HelpFormatter,
    val tokenTransformer: Context.(String) -> String,
    val terminal: Terminal,
    val argumentFileReader: ((filename: String) -> String)?,
    val readEnvvarBeforeValueSource: Boolean,
    val valueSource: ValueSource?,
    val correctionSuggestor: TypoSuggestor,
    val localization: Localization,
    val readEnvvar: (String) -> String?,
    var obj: Any?,
    val originalArgv: List<String>
) {
    /** If this command has subcommands and one was invoked, this is the subcommand */
    var invokedSubcommand: CliktCommand?
    
    /** If true, an error was encountered while parsing but parsing continues */
    var errorEncountered: Boolean
    
    /** Find context object by type */
    inline fun <reified T : Any> findObject(): T?
    
    /** Find or set context object */
    inline fun <reified T : Any> findOrSetObject(defaultValue: () -> T): T
    
    /** Get root context */
    fun findRoot(): Context
    
    /** Get parent command names */
    fun parentNames(): List<String>
    
    /** Get full command path */
    fun commandNameWithParents(): List<String>
    
    /** Throw UsageError */
    fun fail(message: String = ""): Nothing
    
    /** Register cleanup callback */
    fun callOnClose(closeable: () -> Unit)
    
    /** Execute cleanup callbacks */
    fun close()
    
    /** If true, arguments starting with @ will be expanded as argument files */
    val expandArgumentFiles: Boolean
    
    class Builder(command: CliktCommand, val parent: Context? = null) {
        var allowInterspersedArgs: Boolean
        var allowGroupedShortOptions: Boolean
        var printExtraMessages: Boolean
        var helpOptionNames: Iterable<String>
        var helpFormatter: ((Context) -> HelpFormatter)?
        var tokenTransformer: Context.(String) -> String
        var autoEnvvarPrefix: String?
        var terminal: Terminal
        var expandArgumentFiles: Boolean
        var argumentFileReader: ((filename: String) -> String)?
        var readEnvvarBeforeValueSource: Boolean
        var valueSource: ValueSource?
        fun valueSources(vararg sources: ValueSource)
        var correctionSuggestor: TypoSuggestor
        var localization: Localization
        var envvarReader: (key: String) -> String?
        var obj: Any?
    }
}

Extension Functions

/**
 * Add subcommands to a command
 */
fun <T : CliktCommand> T.subcommands(commands: Iterable<CliktCommand>): T

fun <T : CliktCommand> T.subcommands(vararg commands: CliktCommand): T

/**
 * Configure command context
 */
fun <T : CliktCommand> T.context(block: Context.Builder.() -> Unit): T

/**
 * Register an AutoCloseable to be closed when command finishes
 */
fun <T: AutoCloseable> Context.registerCloseable(closeable: T): T

/**
 * Find the closest object of type T, or throw NullPointerException
 */
inline fun <reified T : Any> CliktCommand.requireObject(): ReadOnlyProperty<CliktCommand, T>

/**
 * Find the closest object of type T, or null
 */
inline fun <reified T : Any> CliktCommand.findObject(): ReadOnlyProperty<CliktCommand, T?>

/**
 * Find the closest object of type T, setting context.obj if not found
 */
inline fun <reified T : Any> CliktCommand.findOrSetObject(crossinline default: () -> T): ReadOnlyProperty<CliktCommand, T>

/**
 * The current terminal's theme
 */
val Context.theme : Theme

/**
 * Short for accessing the terminal from the currentContext
 */
val CliktCommand.terminal: Terminal

Usage Examples:

// Configure context
class MyCommand : CliktCommand() {
    init {
        context {
            allowInterspersedArgs = false
            helpOptionNames = setOf("--help")
        }
    }
    
    override fun run() {
        // Access context
        val ctx = currentContext
        echo("Command name: ${ctx.command.commandName}")
        
        // Store data in context
        ctx.obj = "shared data"
    }
}

// Add subcommands
class MainCommand : NoOpCliktCommand() {
    override fun run() = Unit
}

fun main() {
    MainCommand()
        .subcommands(
            SubCommand1(),
            SubCommand2(),
            SubCommand3()
        )
        .main()
}

Core Interfaces

/**
 * DSL marker annotation for parameter holder interfaces
 */
@DslMarker
annotation class ParameterHolderDsl

/**
 * Base interface for classes that hold parameters
 */
@ParameterHolderDsl
interface ParameterHolder {
    /** Register an option with this command or group */
    fun registerOption(option: GroupableOption)
}

/**
 * Base interface for options with static group names
 */
interface StaticallyGroupedOption : Option {
    /** The name of the group, or null if this option should not be grouped */
    val groupName: String?
}

/**
 * An option that can be added to a ParameterGroup
 */
interface GroupableOption : StaticallyGroupedOption {
    /** The group that this option belongs to, or null. Set by the group */
    var parameterGroup: ParameterGroup?
    
    /** The name of the group, or null if this option should not be grouped */
    override var groupName: String?
}

/**
 * Marker interface for context-aware errors
 */
interface ContextCliktError {
    /** The context of the command that raised this error */
    var context: Context?
}

/**
 * Type alias for typo correction functions
 */
typealias TypoSuggestor = (enteredValue: String, possibleValues: List<String>) -> List<String>

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