1package cmd
2
3import (
4 "context"
5 "fmt"
6 "log/slog"
7 "os"
8 "os/signal"
9 "strings"
10
11 "charm.land/log/v2"
12 "github.com/charmbracelet/crush/internal/event"
13 "github.com/spf13/cobra"
14)
15
16var runCmd = &cobra.Command{
17 Use: "run [prompt...]",
18 Short: "Run a single non-interactive prompt",
19 Long: `Run a single prompt in non-interactive mode and exit.
20The prompt can be provided as arguments or piped from stdin.`,
21 Example: `
22# Run a simple prompt
23crush run Explain the use of context in Go
24
25# Pipe input from stdin
26curl https://charm.land | crush run "Summarize this website"
27
28# Read from a file
29crush run "What is this code doing?" <<< prrr.go
30
31# Run in quiet mode (hide the spinner)
32crush run --quiet "Generate a README for this project"
33
34# Run in verbose mode
35crush run --verbose "Generate a README for this project"
36 `,
37 RunE: func(cmd *cobra.Command, args []string) error {
38 quiet, _ := cmd.Flags().GetBool("quiet")
39 verbose, _ := cmd.Flags().GetBool("verbose")
40 largeModel, _ := cmd.Flags().GetString("model")
41 smallModel, _ := cmd.Flags().GetString("small-model")
42
43 // Cancel on SIGINT or SIGTERM.
44 ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
45 defer cancel()
46
47 app, err := setupApp(cmd)
48 if err != nil {
49 return err
50 }
51 defer app.Shutdown()
52
53 if !app.Config().IsConfigured() {
54 return fmt.Errorf("no providers configured - please run 'crush' to set up a provider interactively")
55 }
56
57 if verbose {
58 slog.SetDefault(slog.New(log.New(os.Stderr)))
59 }
60
61 prompt := strings.Join(args, " ")
62
63 prompt, err = MaybePrependStdin(prompt)
64 if err != nil {
65 slog.Error("Failed to read from stdin", "error", err)
66 return err
67 }
68
69 if prompt == "" {
70 return fmt.Errorf("no prompt provided")
71 }
72
73 event.SetNonInteractive(true)
74 event.AppInitialized()
75
76 return app.RunNonInteractive(ctx, os.Stdout, prompt, largeModel, smallModel, quiet || verbose)
77 },
78 PostRun: func(cmd *cobra.Command, args []string) {
79 event.AppExited()
80 },
81}
82
83func init() {
84 runCmd.Flags().BoolP("quiet", "q", false, "Hide spinner")
85 runCmd.Flags().BoolP("verbose", "v", false, "Show logs")
86 runCmd.Flags().StringP("model", "m", "", "Model to use. Accepts 'model' or 'provider/model' to disambiguate models with the same name across providers")
87 runCmd.Flags().String("small-model", "", "Small model to use. If not provided, uses the default small model for the provider")
88}