> ## Documentation Index
> Fetch the complete documentation index at: https://chatcli.edilsonfreitas.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sistema de Plugins

> Estenda o ChatCLI com plugins customizados. Crie, instale e gerencie executáveis que adicionam novos comandos.

O ChatCLI suporta um sistema de plugins para estender suas funcionalidades. Um plugin é um **executável** que segue um contrato específico, permitindo que o ChatCLI o descubra, execute e interaja com ele de forma segura.

Isso permite criar comandos customizados (como `@kind`, `@deploy`) que podem orquestrar ferramentas, interagir com APIs ou realizar qualquer lógica que você possa programar.

***

## Para Usuários: Gerenciando Plugins

### Listar plugins instalados

```bash theme={"system"}
/plugin list
```

Mostra todos os comandos de plugin disponíveis, incluindo plugins `[builtin]` (como `@coder`) e `[remote]` (de servidores conectados).

### Instalar um plugin

Instale diretamente de um repositório Git:

```bash theme={"system"}
/plugin install https://github.com/usuário/meu-plugin-chatcli.git
```

O ChatCLI irá clonar, compilar (se for Go) e instalar o executável em `~/.chatcli/plugins/`.

<Warning>
  **Segurança:** A instalação de um plugin envolve baixar e executar código de terceiros. Instale plugins apenas de fontes que você confia.
</Warning>

### Ver detalhes de um plugin

```bash theme={"system"}
/plugin show <nome-do-plugin>
```

### Desinstalar um plugin

```bash theme={"system"}
/plugin uninstall <nome-do-plugin>
```

### Recarregar plugins

O ChatCLI monitora automaticamente `~/.chatcli/plugins/` e **recarrega quando detecta mudanças** (criação, remoção, modificação). Um debounce de 500ms evita recarregamentos múltiplos.

Para forçar um recarregamento manual:

```bash theme={"system"}
/plugin reload
```

<Tip>
  Desenvolva plugins iterativamente: edite o código, recompile, envie ao diretório de plugins — o ChatCLI detectará a mudança automaticamente.
</Tip>

***

## Para Desenvolvedores: Criando um Plugin

### O contrato do plugin

1. **Executável** — O plugin deve ser um arquivo executável (qualquer linguagem)
2. **Localização** — Colocado em `~/.chatcli/plugins/`
3. **Nome do comando** — O nome do arquivo vira o comando. Ex: arquivo `kind` = comando `@kind`
4. **Metadados (`--metadata`)** — Obrigatório. O executável deve responder a está flag com JSON:

```json theme={"system"}
{
  "name": "@meu-comando",
  "description": "Uma breve descrição do que o plugin faz.",
  "usage": "@meu-comando <subcomando> [--flag value]",
  "version": "1.0.0"
}
```

5. **Schema (`--schema`)** — Opcional. Descreve os parâmetros aceitos:

```json theme={"system"}
{
  "parameters": [
    {
      "name": "cluster-name",
      "type": "string",
      "required": true,
      "description": "Nome do cluster Kubernetes"
    }
  ]
}
```

6. **Comunicação (stdout vs stderr)**:
   * **stdout** — Apenas o resultado final (retornado ao ChatCLI/IA)
   * **stderr** — Logs de progresso, status e avisos (exibidos em tempo real ao usuário)

***

### Exemplo: Plugin "Hello World" em Go

```go theme={"system"}
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 envia mensagens de progresso para o usuário (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))
}
```

### Compilação e instalação

```bash theme={"system"}
# 1. Compilar
go build -o hello ./hello/main.go

# 2. Dar permissão de execução
chmod +x hello

# 3. Criar diretório de plugins (se não existir)
mkdir -p ~/.chatcli/plugins/

# 4. Mover o executável
mv hello ~/.chatcli/plugins/

# 5. Usar no ChatCLI
/agent Olá meu nome é Fulano
```

***

## Capability Interfaces (Opt-in)

Plugins podem expor capabilities através de interfaces Go opcionais. Plugins legados que não implementam continuam funcionando — todas as interfaces são fail-closed (default conservador). Implementando, o plugin participa de otimizações do orquestrador:

| Interface              | Método                                             | Para que serve                                                                                                            |
| ---------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `ReadOnlyAware`        | `IsReadOnly(args []string) bool`                   | Habilita capability gate (security auto-allow) e participa de batches paralelos                                           |
| `ConcurrencySafeAware` | `IsConcurrencySafe(args []string) bool`            | Sinaliza que múltiplas chamadas podem rodar em paralelo                                                                   |
| `DescriberWithInput`   | `DescribeCall(args []string) string`               | Label dinâmico no spinner ("Reading: main.go" em vez de "EXECUTANDO: @read")                                              |
| `JSONSchemaAware`      | `JSONSchema() string`                              | Schema draft-2020-12 validado antes do dispatch — input inválido vira `ToolResult{IsError:true, ErrorCode:"InvalidArgs"}` |
| `TruncationAware`      | `MaxResultChars() int`                             | Per-tool cap de output (versus o global de 30 000)                                                                        |
| `Prompter`             | `Prompt(opts PromptOpts) (string, error)`          | Slice do system prompt contextual                                                                                         |
| `StreamingInputAware`  | `UpdateStreamingInput(field, value string)`        | Eventos de input parcial enquanto o LLM streama args                                                                      |
| `StructuredExecutor`   | `ExecuteStructured(...) (StructuredResult, error)` | Retorno tipado em vez do `(string, error)` legado                                                                         |

Exemplo — um plugin que quer auto-allow + paralelização + label customizado:

```go theme={"system"}
func (p *MyReadOnlyPlugin) IsReadOnly(_ []string) bool        { return true }
func (p *MyReadOnlyPlugin) IsConcurrencySafe(_ []string) bool { return true }
func (p *MyReadOnlyPlugin) DescribeCall(args []string) string {
    return i18n.T("plugins.myplugin.describe", extractTarget(args))
}
func (p *MyReadOnlyPlugin) JSONSchema() string {
    return `{"type":"object","properties":{"file":{"type":"string"}},"required":["file"]}`
}
```

Os 4 plugins atômicos (`@read`, `@search`, `@tree`, `@todo`) — veja [Tools Atômicos](/pt/features/atomic-tools) — usam todas as capabilities relevantes como exemplo de referência.

***

## Verificação de Assinatura

A partir desta versão, plugins requerem **assinatura digital Ed25519** por padrão. Isso garante que apenas plugins de fontes confiáveis sejam carregados e executados.

### Como Funciona

Cada plugin deve ter um arquivo `.sig` correspondente no mesmo diretório:

```text theme={"system"}
~/.chatcli/plugins/
  meu-plugin          # executável do plugin
  meu-plugin.sig      # assinatura Ed25519 do executável
```

O ChatCLI verifica a assinatura contra as chaves públicas registradas antes de carregar o plugin. Se a verificação falhar, o plugin é rejeitado.

### Gerenciando Chaves Confiáveis

As chaves públicas Ed25519 ficam no diretório `~/.chatcli/trusted-keys/`:

<Steps>
  <Step title="Gerar um par de chaves">
    ```bash theme={"system"}
    # Gera chave privada e pública
    chatcli plugin keygen --name minha-org
    # Cria: ~/.chatcli/trusted-keys/minha-org.pub
    # Cria: ~/.chatcli/signing-keys/minha-org.key (privada - proteja!)
    ```
  </Step>

  <Step title="Assinar um plugin">
    ```bash theme={"system"}
    chatcli plugin sign --key ~/.chatcli/signing-keys/minha-org.key meu-plugin
    # Cria: meu-plugin.sig
    ```
  </Step>

  <Step title="Distribuir a chave pública">
    Compartilhe o arquivo `.pub` com os usuários que devem confiar nos seus plugins. Eles devem colocá-lo em `~/.chatcli/trusted-keys/`.
  </Step>
</Steps>

### Permissões de Arquivo

O diretório de plugins usa permissões **0o700** (somente o dono pode ler, escrever e executar). O ChatCLI verifica as permissões na inicialização e emite um aviso se estiverem mais permissivas.

### Modo de Desenvolvimento

Para desenvolvimento local, é possível desabilitar a verificação de assinatura:

```bash theme={"system"}
export CHATCLI_ALLOW_UNSIGNED_PLUGINS=true
chatcli
```

<Warning>
  **Nunca** habilite `CHATCLI_ALLOW_UNSIGNED_PLUGINS` em produção. Plugins não assinados podem executar código arbitrário com as permissões do processo ChatCLI.
</Warning>

***

## Plugins Remotos

Ao conectar a um servidor via `chatcli connect`, plugins do servidor são descobertos automaticamente:

* Aparecem em `/plugin list` com a tag `[remote]`
* São executados no servidor (não baixados localmente por padrão)
* Plugins locais e remotos coexistem sem conflito
