ChatCLI supports a plugin system to extend its functionality. A plugin is an executable that follows a specific contract, allowing ChatCLI to discover, execute, and interact with it securely.
This lets you create custom commands (such as @kind, @deploy) that can orchestrate tools, interact with APIs, or perform any logic you can program.
For Users: Managing Plugins
List installed plugins
Shows all available plugin commands, including [builtin] plugins (such as @coder) and [remote] plugins (from connected servers).
Install a plugin
Install directly from a Git repository:
/plugin install https://github.com/usuario/meu-plugin-chatcli.git
ChatCLI will clone, compile (if it’s Go), and install the executable in ~/.chatcli/plugins/.
Security: Installing a plugin involves downloading and executing third-party code. Only install plugins from sources you trust.
View plugin details
/plugin show <plugin-name>
Uninstall a plugin
/plugin uninstall <plugin-name>
Reload plugins
ChatCLI automatically monitors ~/.chatcli/plugins/ and reloads when it detects changes (creation, removal, modification). A 500ms debounce prevents multiple reloads.
To force a manual reload:
Develop plugins iteratively: edit the code, recompile, send it to the plugins directory — ChatCLI will detect the change automatically.
For Developers: Creating a Plugin
The plugin contract
- Executable — The plugin must be an executable file (any language)
- Location — Placed in
~/.chatcli/plugins/
- Command name — The file name becomes the command. E.g., file
kind = command @kind
- Metadata (
--metadata) — Required. The executable must respond to this flag with JSON:
{
"name": "@meu-comando",
"description": "A brief description of what the plugin does.",
"usage": "@meu-comando <subcommand> [--flag value]",
"version": "1.0.0"
}
- Schema (
--schema) — Optional. Describes the accepted parameters:
{
"parameters": [
{
"name": "cluster-name",
"type": "string",
"required": true,
"description": "Kubernetes cluster name"
}
]
}
- Communication (stdout vs stderr):
- stdout — Only the final result (returned to ChatCLI/AI)
- stderr — Progress logs, status, and warnings (displayed in real time to the user)
Example: “Hello World” Plugin in Go
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"time"
)
type Metadata struct {
Name string `json:"name"`
Description string `json:"description"`
Usage string `json:"usage"`
Version string `json:"version"`
}
// logf sends progress messages to the user (via stderr).
func logf(format string, v ...interface{}) {
fmt.Fprintf(os.Stderr, format, v...)
}
func main() {
metadataFlag := flag.Bool("metadata", false, "Exibe os metadados do plugin")
schemaFlag := flag.Bool("schema", false, "Exibe o schema de parâmetros")
flag.Parse()
if *metadataFlag {
meta := Metadata{
Name: "@hello",
Description: "Plugin de exemplo que demonstra o fluxo stdout/stderr.",
Usage: "@hello [seu-nome]",
Version: "1.0.0",
}
jsonMeta, _ := json.Marshal(meta)
fmt.Println(string(jsonMeta))
return
}
if *schemaFlag {
schema := map[string]interface{}{
"parameters": []map[string]interface{}{
{
"name": "nome",
"type": "string",
"required": false,
"description": "Nome da pessoa a ser cumprimentada",
"default": "Mundo",
},
},
}
jsonSchema, _ := json.Marshal(schema)
fmt.Println(string(jsonSchema))
return
}
// Lógica principal
logf("Plugin 'hello' iniciado!\n")
time.Sleep(2 * time.Second)
logf(" - Realizando uma tarefa demorada...\n")
time.Sleep(2 * time.Second)
name := "Mundo"
if len(flag.Args()) > 0 {
name = flag.Args()[0]
}
logf("Tarefa concluída!\n")
// Resultado final vai para stdout
fmt.Printf("Olá, %s! A hora agora é %s.", name, time.Now().Format(time.RFC1123))
}
Compilation and installation
# 1. Compile
go build -o hello ./hello/main.go
# 2. Make it executable
chmod +x hello
# 3. Create the plugins directory (if it doesn't exist)
mkdir -p ~/.chatcli/plugins/
# 4. Move the executable
mv hello ~/.chatcli/plugins/
# 5. Use in ChatCLI
/agent Hello, my name is John
Remote Plugins
When connecting to a server via chatcli connect, server plugins are discovered automatically:
- They appear in
/plugin list with the [remote] tag
- They are executed on the server (not downloaded locally by default)
- Local and remote plugins coexist without conflict