package secutools import ( "fmt" "strings" ) // DecideProvider inspects a frontmatter map (as decoded from YAML) and // returns the provider string that should handle the task. Valid return // values: // // - "local" — no delegation, dispatch on a Claude Code session (Phase 1) // - "claude-code" — explicit local dispatch (alias of "local") // - "gpu" — delegate to secutools with preferred_ai=gpu // - "gemini" — delegate to secutools with preferred_ai=gemini // - "claude-api" — delegate to secutools with preferred_ai=claude-api // - "auto" — delegate to secutools, let smart_triage choose // // Precedence: // 1. needs_claude_code: true → "local" // 2. preferred_ai in {claude-code} → "local" // 3. allow_delegation == false / missing → "local" // 4. preferred_ai in {gpu,gemini,claude-api} → that provider // 5. preferred_ai in {"", auto} → "auto" // 6. unknown preferred_ai → "local" (fail-safe) // // This function is intentionally permissive on input types: YAML booleans // may decode as bool, strings as string. It coerces common forms and // returns "local" on malformed input rather than panicking. func DecideProvider(fm map[string]any) string { if fm == nil { return "local" } if asBool(fm["needs_claude_code"]) { return "local" } pref := strings.ToLower(strings.TrimSpace(asString(fm["preferred_ai"]))) if pref == "claude-code" || pref == "local" { return "local" } if !asBool(fm["allow_delegation"]) { return "local" } switch pref { case "gpu", "gemini", "claude-api": return pref case "", "auto": return "auto" default: // Unknown provider — fail safe to local. return "local" } } // asBool accepts bool, "true"/"false", "1"/"0". Defaults to false. func asBool(v any) bool { switch t := v.(type) { case bool: return t case string: s := strings.ToLower(strings.TrimSpace(t)) return s == "true" || s == "1" || s == "yes" case int: return t != 0 default: return false } } // asString coerces v to a trimmed string. Returns "" for nil/unknown types. func asString(v any) string { switch t := v.(type) { case string: return t case fmt.Stringer: return t.String() case nil: return "" default: return fmt.Sprintf("%v", t) } }