run.go

 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 "Guess my 5 favorite Pokรฉmon"
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# Redirect output to a file
32crush run "Generate a hot README for this project" > MY_HOT_README.md
33
34# Run in quiet mode (hide the spinner)
35crush run --quiet "Generate a README for this project"
36
37# Run in verbose mode (show logs)
38crush run --verbose "Generate a README for this project"
39  `,
40	RunE: func(cmd *cobra.Command, args []string) error {
41		quiet, _ := cmd.Flags().GetBool("quiet")
42		verbose, _ := cmd.Flags().GetBool("verbose")
43		largeModel, _ := cmd.Flags().GetString("model")
44		smallModel, _ := cmd.Flags().GetString("small-model")
45
46		// Cancel on SIGINT or SIGTERM.
47		ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
48		defer cancel()
49
50		app, err := setupApp(cmd)
51		if err != nil {
52			return err
53		}
54		defer app.Shutdown()
55
56		if !app.Config().IsConfigured() {
57			return fmt.Errorf("no providers configured - please run 'crush' to set up a provider interactively")
58		}
59
60		if verbose {
61			slog.SetDefault(slog.New(log.New(os.Stderr)))
62		}
63
64		prompt := strings.Join(args, " ")
65
66		prompt, err = MaybePrependStdin(prompt)
67		if err != nil {
68			slog.Error("Failed to read from stdin", "error", err)
69			return err
70		}
71
72		if prompt == "" {
73			return fmt.Errorf("no prompt provided")
74		}
75
76		event.SetNonInteractive(true)
77		event.AppInitialized()
78
79		return app.RunNonInteractive(ctx, os.Stdout, prompt, largeModel, smallModel, quiet || verbose)
80	},
81	PostRun: func(cmd *cobra.Command, args []string) {
82		event.AppExited()
83	},
84}
85
86func init() {
87	runCmd.Flags().BoolP("quiet", "q", false, "Hide spinner")
88	runCmd.Flags().BoolP("verbose", "v", false, "Show logs")
89	runCmd.Flags().StringP("model", "m", "", "Model to use. Accepts 'model' or 'provider/model' to disambiguate models with the same name across providers")
90	runCmd.Flags().String("small-model", "", "Small model to use. If not provided, uses the default small model for the provider")
91}