Files
egutierrez 33b11a63c8 feat: añadir sistema de comandos directos (!command)
Implementa pkg/command/ como core puro: tipos Spec/ParsedArgs, parser de
args key=value con soporte de comillas, specs de 8 comandos built-in
(help, tools, tool, ping, status, info, clear, version) y BuiltinNames()
para aliases.

En agents/runtime.go: nuevo flujo handleEvent que prioriza comandos sobre
LLM — custom rules del agente → built-in handlers → comando desconocido →
LLM fallback. Handlers en agents/commands.go. El comando !tool ejecuta
tools directamente via Registry con args key=value parseados.

LLM ahora es opcional: si no hay provider configurado, el agente corre
como simple_bot respondiendo solo a comandos.

Se extrae executeActions() como helper reutilizable para ambos flujos
(comando y no-comando).
2026-03-07 01:11:26 +00:00

92 lines
2.1 KiB
Go

package command
import (
"encoding/json"
"strings"
)
// ParseArgs converts a slice of raw arguments into structured ParsedArgs.
// Supports: positional args, key=value pairs, and quoted values like key="hello world".
// Pure function — no side effects.
func ParseArgs(args []string) ParsedArgs {
p := ParsedArgs{
Named: make(map[string]string),
Raw: args,
}
// First, rejoin args to handle quoted values that were split by Fields().
joined := strings.Join(args, " ")
tokens := tokenize(joined)
for _, tok := range tokens {
if idx := strings.IndexByte(tok, '='); idx > 0 {
key := tok[:idx]
val := tok[idx+1:]
// Strip surrounding quotes from value
val = stripQuotes(val)
p.Named[key] = val
} else {
p.Positional = append(p.Positional, tok)
}
}
return p
}
// ArgsToJSON converts a named args map to a JSON string for tools.Registry.Execute.
// Pure function.
func ArgsToJSON(named map[string]string) string {
if len(named) == 0 {
return ""
}
m := make(map[string]any, len(named))
for k, v := range named {
m[k] = v
}
b, _ := json.Marshal(m)
return string(b)
}
// tokenize splits a string respecting quoted values.
// e.g. `host=server1 command="uptime -a"` → ["host=server1", `command="uptime -a"`]
func tokenize(s string) []string {
var tokens []string
var current strings.Builder
inQuote := false
quoteChar := byte(0)
for i := 0; i < len(s); i++ {
ch := s[i]
switch {
case !inQuote && (ch == '"' || ch == '\''):
inQuote = true
quoteChar = ch
current.WriteByte(ch)
case inQuote && ch == quoteChar:
inQuote = false
current.WriteByte(ch)
case !inQuote && ch == ' ':
if current.Len() > 0 {
tokens = append(tokens, current.String())
current.Reset()
}
default:
current.WriteByte(ch)
}
}
if current.Len() > 0 {
tokens = append(tokens, current.String())
}
return tokens
}
// stripQuotes removes surrounding double or single quotes from a string.
func stripQuotes(s string) string {
if len(s) >= 2 {
if (s[0] == '"' && s[len(s)-1] == '"') || (s[0] == '\'' && s[len(s)-1] == '\'') {
return s[1 : len(s)-1]
}
}
return s
}