package commands import ( "errors" "strings" "unicode" "gitea-codex-bot/internal/domain" ) var helpAliases = map[string]bool{"-h": true, "--help": true, "help": true} var supported = map[string]bool{"review": true, "rerun": true, "explain": true, "ignore": true} func DetectPrefixedCommand(body string, aliases map[string]bool) string { parts, ok := prefixParts(body, aliases) if !ok || len(parts) == 0 { return "" } return strings.ToLower(parts[0]) } func Parse(body string, aliases map[string]bool) (domain.ParsedCommand, bool) { stripped := strings.TrimSpace(body) parts, ok := prefixParts(stripped, aliases) if !ok || len(parts) == 0 { return domain.ParsedCommand{}, false } name := strings.ToLower(parts[0]) rawArgs := append([]string(nil), parts[1:]...) if helpAliases[name] { return domain.ParsedCommand{Name: "help", Alias: aliasOf(stripped), Raw: stripped, Arguments: rawArgs}, true } if !supported[name] { return domain.ParsedCommand{}, false } cmd := domain.ParsedCommand{Name: name, Alias: aliasOf(stripped), Raw: stripped, Mode: "summary", Arguments: rawArgs} if name == "review" { for _, token := range rawArgs { switch strings.ToLower(token) { case "--full": cmd.Full = true cmd.Mode = "full" cmd.ModeExplicit = true case "security", "performance", "tests": if !cmd.ModeExplicit || cmd.Mode == "full" { cmd.Mode = strings.ToLower(token) cmd.ModeExplicit = true } } } } return cmd, true } func prefixParts(body string, aliases map[string]bool) ([]string, bool) { trimmed := strings.TrimSpace(body) if !strings.HasPrefix(trimmed, "@") { return nil, false } space := strings.IndexFunc(trimmed, unicode.IsSpace) if space <= 1 { return nil, false } alias := strings.ToLower(strings.TrimPrefix(trimmed[1:space], "@")) if !aliases[alias] { return nil, false } remainder := strings.TrimSpace(trimmed[space:]) tokens, err := lex(remainder) if err != nil { return nil, false } return tokens, true } func aliasOf(body string) string { end := strings.IndexFunc(body[1:], unicode.IsSpace) if end < 0 { return body } return body[:end+1] } func lex(input string) ([]string, error) { var out []string var b strings.Builder quoted := rune(0) escaped := false started := false flush := func() { if started { out = append(out, b.String()) b.Reset() started = false } } for _, r := range input { if escaped { b.WriteRune(r) started = true escaped = false continue } if r == '\\' { escaped = true started = true continue } if quoted != 0 { if r == quoted { quoted = 0 } else { b.WriteRune(r) } started = true continue } if r == '\'' || r == '"' { quoted = r started = true continue } if unicode.IsSpace(r) { flush() continue } b.WriteRune(r) started = true } if escaped || quoted != 0 { return nil, errors.New("unterminated command quote") } flush() return out, nil }