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/llm/agent"
16 "github.com/charmbracelet/crush/internal/log"
17 "github.com/charmbracelet/crush/internal/tui"
18 "github.com/charmbracelet/crush/internal/version"
19 "github.com/charmbracelet/fang"
20 "github.com/charmbracelet/x/term"
21 "github.com/spf13/cobra"
24var rootCmd = &cobra.Command{
26 Short: "Terminal-based AI assistant for software development",
27 Long: `Crush is a powerful terminal-based AI assistant that helps with software development tasks.
28It provides an interactive chat interface with AI capabilities, code analysis, and LSP integration
29to assist developers in writing, debugging, and understanding code directly from the terminal.`,
31 # Run in interactive mode
34 # Run with debug logging
37 # Run with debug slog.in a specific directory
38 crush -d -c /path/to/project
43 # Run a single non-interactive prompt
44 crush -p "Explain the use of context in Go"
46 # Run a single non-interactive prompt with JSON output format
47 crush -p "Explain the use of context in Go" -f json
49 RunE: func(cmd *cobra.Command, args []string) error {
51 debug, _ := cmd.Flags().GetBool("debug")
52 cwd, _ := cmd.Flags().GetString("cwd")
53 prompt, _ := cmd.Flags().GetString("prompt")
54 quiet, _ := cmd.Flags().GetBool("quiet")
59 return fmt.Errorf("failed to change directory: %v", err)
65 return fmt.Errorf("failed to get current working directory: %v", err)
70 cfg, err := config.Init(cwd, debug)
77 // Connect DB, this will also run migrations
78 conn, err := db.Connect(ctx, cfg.Options.DataDirectory)
83 app, err := app.New(ctx, conn, cfg)
85 slog.Error(fmt.Sprintf("Failed to create app instance: %v", err))
90 // Initialize MCP tools early for both modes
91 initMCPTools(ctx, app, cfg)
93 prompt, err = maybePrependStdin(prompt)
95 slog.Error(fmt.Sprintf("Failed to read from stdin: %v", err))
99 // Non-interactive mode
101 // Run non-interactive flow using the App method
102 return app.RunNonInteractive(ctx, prompt, quiet)
106 program := tea.NewProgram(
109 tea.WithContext(ctx),
110 tea.WithMouseCellMotion(), // Use cell motion instead of all motion to reduce event flooding
111 tea.WithFilter(tui.MouseEventFilter), // Filter mouse events based on focus state
114 go app.Subscribe(program)
116 if _, err := program.Run(); err != nil {
117 slog.Error(fmt.Sprintf("TUI run error: %v", err))
118 return fmt.Errorf("TUI error: %v", err)
124func initMCPTools(ctx context.Context, app *app.App, cfg *config.Config) {
126 defer log.RecoverPanic("MCP-goroutine", nil)
128 // Create a context with timeout for the initial MCP tools fetch
129 ctxWithTimeout, cancel := context.WithTimeout(ctx, 30*time.Second)
132 // Set this up once with proper error handling
133 agent.GetMcpTools(ctxWithTimeout, app.Permissions, cfg)
134 slog.Info("MCP message handling goroutine exiting")
139 if err := fang.Execute(
140 context.Background(),
142 fang.WithVersion(version.Version),
143 fang.WithNotifySignal(os.Interrupt),
150 rootCmd.PersistentFlags().StringP("cwd", "c", "", "Current working directory")
152 rootCmd.Flags().BoolP("help", "h", false, "Help")
153 rootCmd.Flags().BoolP("debug", "d", false, "Debug")
154 rootCmd.Flags().StringP("prompt", "p", "", "Prompt to run in non-interactive mode")
156 // Add quiet flag to hide spinner in non-interactive mode
157 rootCmd.Flags().BoolP("quiet", "q", false, "Hide spinner in non-interactive mode")
160func maybePrependStdin(prompt string) (string, error) {
161 if term.IsTerminal(os.Stdin.Fd()) {
164 fi, err := os.Stdin.Stat()
168 if fi.Mode()&os.ModeNamedPipe == 0 {
171 bts, err := io.ReadAll(os.Stdin)
175 return string(bts) + "\n\n" + prompt, nil
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