run.go

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