package ai import ( "context" "fmt" "os/exec" "strings" ) // safeActions lists the actions whose suggested commands may be executed // with explicit user confirmation. Anything else is treated as advisory // only, because the LLM output must never be able to run arbitrary // commands on the user's machine. var safeActions = map[string]bool{ "commit": true, "push": true, "pull": true, "stash": true, "checkout": true, } // ConfirmFunc asks the user whether to run a suggestion's command. type ConfirmFunc func(Suggestion) (bool, error) // RunConfirmed runs the commands of confirmed suggestions in their // repository working directories. Suggestions whose action is not in the // allowlist are never executed. Cancellation stops the remaining // suggestions. func RunConfirmed(ctx context.Context, suggestions []Suggestion, confirm ConfirmFunc) error { for _, s := range suggestions { if s.Command == "" || !safeActions[s.Action] { continue } ok, err := confirm(s) if err != nil || !ok { continue } if err := runCommand(ctx, s); err != nil { return fmt.Errorf("ai: %s: %w", s.RepoPath, err) } } return nil } // runCommand executes the suggestion's command inside its repository and // prints its output. func runCommand(ctx context.Context, s Suggestion) error { cmd := exec.CommandContext(ctx, "sh", "-c", s.Command) cmd.Dir = s.RepoPath out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("run %q: %w: %s", s.Command, err, strings.TrimSpace(string(out))) } if text := strings.TrimSpace(string(out)); text != "" { fmt.Println(text) } return nil }