11 tea "github.com/charmbracelet/bubbletea/v2"
12 "github.com/charmbracelet/crush/internal/app"
13 "github.com/charmbracelet/crush/internal/config"
14 "github.com/charmbracelet/crush/internal/db"
15 "github.com/charmbracelet/crush/internal/tui"
16 "github.com/charmbracelet/crush/internal/version"
17 "github.com/charmbracelet/fang"
18 "github.com/charmbracelet/x/term"
19 "github.com/spf13/cobra"
23 rootCmd.PersistentFlags().StringP("cwd", "c", "", "Current working directory")
24 rootCmd.PersistentFlags().BoolP("debug", "d", false, "Debug")
26 rootCmd.Flags().BoolP("help", "h", false, "Help")
27 rootCmd.Flags().BoolP("yolo", "y", false, "Automatically accept all permissions (dangerous mode)")
29 runCmd.Flags().BoolP("quiet", "q", false, "Hide spinner")
30 rootCmd.AddCommand(runCmd)
33var rootCmd = &cobra.Command{
35 Short: "Terminal-based AI assistant for software development",
36 Long: `Crush is a powerful terminal-based AI assistant that helps with software development tasks.
37It provides an interactive chat interface with AI capabilities, code analysis, and LSP integration
38to assist developers in writing, debugging, and understanding code directly from the terminal.`,
40# Run in interactive mode
43# Run with debug logging
46# Run with debug logging in a specific directory
47crush -d -c /path/to/project
52# Run a single non-interactive prompt
53crush run "Explain the use of context in Go"
55# Run in dangerous mode (auto-accept all permissions)
58 RunE: func(cmd *cobra.Command, args []string) error {
59 app, err := setupApp(cmd)
66 program := tea.NewProgram(
69 tea.WithContext(cmd.Context()),
70 tea.WithMouseCellMotion(), // Use cell motion instead of all motion to reduce event flooding
71 tea.WithFilter(tui.MouseEventFilter), // Filter mouse events based on focus state
74 go app.Subscribe(program)
76 if _, err := program.Run(); err != nil {
77 slog.Error("TUI run error", "error", err)
78 return fmt.Errorf("TUI error: %v", err)
84var runCmd = &cobra.Command{
85 Use: "run [prompt...]",
86 Short: "Run a single non-interactive prompt",
87 Long: `Run a single prompt in non-interactive mode and exit.
88The prompt can be provided as arguments or piped from stdin.`,
91crush run Explain the use of context in Go
93# Pipe input from stdin
94echo "What is this code doing?" | crush run
96# Run with quiet mode (no spinner)
97crush run -q "Generate a README for this project"
99 RunE: func(cmd *cobra.Command, args []string) error {
100 quiet, _ := cmd.Flags().GetBool("quiet")
102 app, err := setupApp(cmd)
108 prompt := strings.Join(args, " ")
110 prompt, err = maybePrependStdin(prompt)
112 slog.Error("Failed to read from stdin", "error", err)
117 return fmt.Errorf("no prompt provided")
120 // Run non-interactive flow using the App method
121 return app.RunNonInteractive(cmd.Context(), prompt, quiet)
126 if err := fang.Execute(
127 context.Background(),
129 fang.WithVersion(version.Version),
130 fang.WithNotifySignal(os.Interrupt),
136// setupApp handles the common setup logic for both interactive and non-interactive modes.
137// It returns the app instance, config, cleanup function, and any error.
138func setupApp(cmd *cobra.Command) (*app.App, error) {
139 debug, _ := cmd.Flags().GetBool("debug")
140 yolo, _ := cmd.Flags().GetBool("yolo")
143 cwd, err := resolveCwd(cmd)
148 cfg, err := config.Init(cwd, debug)
153 if cfg.Permissions == nil {
154 cfg.Permissions = &config.Permissions{}
156 cfg.Permissions.SkipRequests = yolo
158 // Connect to DB; this will also run migrations.
159 conn, err := db.Connect(ctx, cfg.Options.DataDirectory)
164 appInstance, err := app.New(ctx, conn, cfg)
166 slog.Error("Failed to create app instance", "error", err)
170 return appInstance, nil
173func maybePrependStdin(prompt string) (string, error) {
174 if term.IsTerminal(os.Stdin.Fd()) {
177 fi, err := os.Stdin.Stat()
181 if fi.Mode()&os.ModeNamedPipe == 0 {
184 bts, err := io.ReadAll(os.Stdin)
188 return string(bts) + "\n\n" + prompt, nil
191func resolveCwd(cmd *cobra.Command) (string, error) {
192 cwd, _ := cmd.Flags().GetString("cwd")
196 return "", fmt.Errorf("failed to change directory: %v", err)
200 cwd, err := os.Getwd()
202 return "", fmt.Errorf("failed to get current working directory: %v", err)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list