run.go

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