Creating a New Command in Askimo
This guide explains how to implement a new command in the Askimo CLI. By following these steps, you can add custom functionality to the command-line interface.
Architecture Overview
Askimo uses a simple command handling architecture with the following key components:
- CommandHandler: Interface that defines the contract for all command handlers
- Command implementations: Classes that implement the CommandHandler interface
- Command registration: Process of adding commands to the CLI application
Each command is identified by a keyword (starting with a colon) and has a description and handling logic.
Implementation Steps
1. Create a New Command Handler
First, create a new class that implements the CommandHandler
interface. This class will handle your specific command:
// File: io.askimo.cli.commands.YourCommandHandler.kt
/**
* Handles the command to [describe what your command does].
*
* This class provides functionality to [explain the purpose and functionality of your command].
*/
class YourCommandHandler(
private val session: Session, // Include any dependencies your command needs
) : CommandHandler {
override val keyword: String = ":yourcommand" // Command keyword (starts with colon)
override val description: String = "Description of what your command does."
override fun handle(line: ParsedLine) {
// Implement your command logic here
// Example: Access command arguments
val args = line.words().drop(1) // Skip the command itself
// Example: Access the current session
val provider = session.getActiveProvider()
val modelName = session.params.getModel(provider)
// Example: Print output to the console
println("✅ Your command executed successfully!")
}
}
2. Register Your Command
To make your command available in the CLI, add it to the list of command handlers in the main
function in ChatCli.kt
:
// In ChatCli.kt
val commandHandlers: List<CommandHandler> =
listOf(
HelpCommandHandler(),
ConfigCommand(session),
ParamsCommandHandler(session),
SetParamCommandHandler(session),
ListProvidersCommandHandler(),
SetProviderCommandHandler(session),
ModelsCommandHandler(session),
CopyCommandHandler(session),
ClearMemoryCommandHandler(session),
YourCommandHandler(session), // Add your command here
)
Don’t forget to add the import for your new command handler at the top of the file:
// Add this to the imports section at the top of ChatCli.kt
import io.askimo.cli.commands.YourCommandHandler
3. Command Implementation Example
Let’s look at a real example: the ClearMemoryCommandHandler
which clears the chat memory for the current provider and model:
// File: io.askimo.cli.commands.ClearMemoryCommandHandler.kt
/**
* Handles the command to clear the chat memory.
*
* This class provides functionality to reset the conversation history for the current
* provider and model combination. It allows users to start fresh conversations without
* changing their model configuration.
*/
class ClearMemoryCommandHandler(
private val session: Session,
) : CommandHandler {
override val keyword: String = ":clear"
override val description: String = "Clear the current chat memory for the active provider/model."
override fun handle(line: ParsedLine) {
val provider = session.getActiveProvider()
val modelName = session.params.getModel(provider)
session.removeMemory(provider, modelName)
println("🧹 Chat memory cleared for $provider / $modelName")
}
}
This command:
- Gets the current provider and model from the session
- Calls the session’s
removeMemory
method to clear the chat history - Prints a confirmation message to the user
4. Working with Command Arguments
If your command needs to handle arguments, you can access them from the ParsedLine
object:
override fun handle(line: ParsedLine) {
val args = line.words().drop(1) // Skip the command itself
if (args.isEmpty()) {
println("❌ This command requires arguments.")
println("Usage: ${keyword} <argument1> <argument2>")
return
}
val arg1 = args[0]
val arg2 = args.getOrNull(1) // Safely get optional arguments
// Process arguments...
}
5. Accessing Session Data
The Session
object provides access to various aspects of the application state:
// Get the active provider
val provider = session.getActiveProvider()
// Get the current model for a provider
val modelName = session.params.getModel(provider)
// Get a parameter value
val paramValue = session.params.get(ParamKey.SYSTEM_PROMPT)
// Set a parameter value
session.params.set(ParamKey.SYSTEM_PROMPT, "New value")
// Get the current chat model
val model = session.getChatModel()
// Store the last response
session.lastResponse = "Response text"
Best Practices
Command Naming Conventions
- Command keywords should start with a colon (
:
) - Use lowercase for command names
- Use simple, descriptive names that clearly indicate the command’s purpose
Command Implementation
- Keep commands focused on a single responsibility
- Provide clear feedback to the user about what happened
- Handle errors gracefully with helpful error messages
- Use emojis for visual feedback (e.g., ✅, ❌, 🧹)
- Include detailed documentation in the class comment
Command Description
- Keep descriptions concise but informative
- Start with a verb (e.g., “Clear”, “Set”, “Show”)
- Make sure the description clearly explains what the command does
Testing Your Command
After implementing your command, you can test it by:
- Building and running the Askimo CLI
- Using your command with the appropriate syntax:
askimo> :yourcommand [arguments]
- Verifying that the command produces the expected output and behavior
Conclusion
By following these steps, you can extend the Askimo CLI with custom commands. The command handler architecture makes it easy to add new functionality while maintaining a consistent interface for users.
Remember to handle errors gracefully and provide clear feedback to users when something goes wrong with your command.