@@ -12,6 +12,7 @@ import (
12 "github.com/opencode-ai/opencode/internal/app"
12 "github.com/opencode-ai/opencode/internal/app"
13 "github.com/opencode-ai/opencode/internal/config"
13 "github.com/opencode-ai/opencode/internal/config"
14 "github.com/opencode-ai/opencode/internal/db"
14 "github.com/opencode-ai/opencode/internal/db"
15+ "github.com/opencode-ai/opencode/internal/format"
15 "github.com/opencode-ai/opencode/internal/llm/agent"
16 "github.com/opencode-ai/opencode/internal/llm/agent"
16 "github.com/opencode-ai/opencode/internal/logging"
17 "github.com/opencode-ai/opencode/internal/logging"
17 "github.com/opencode-ai/opencode/internal/pubsub"
18 "github.com/opencode-ai/opencode/internal/pubsub"
@@ -21,11 +22,30 @@ import (
23var rootCmd = &cobra.Command{
24var rootCmd = &cobra.Command{
24− Use: "OpenCode",
25+ Use: "opencode",
25− Short: "A terminal AI assistant for software development",
26+ Short: "Terminal-based AI assistant for software development",
26 Long: `OpenCode is a powerful terminal-based AI assistant that helps with software development tasks.
27 Long: `OpenCode is a powerful terminal-based AI assistant that helps with software development tasks.
27It provides an interactive chat interface with AI capabilities, code analysis, and LSP integration
28It provides an interactive chat interface with AI capabilities, code analysis, and LSP integration
28to assist developers in writing, debugging, and understanding code directly from the terminal.`,
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 logging in a specific directory
38+ opencode -d -c /path/to/project
43+ # Run a single non-interactive prompt
44+ opencode -p "Explain the use of context in Go"
46+ # Run a single non-interactive prompt with JSON output format
47+ opencode -p "Explain the use of context in Go" -f json
29 RunE: func(cmd *cobra.Command, args []string) error {
49 RunE: func(cmd *cobra.Command, args []string) error {
30 // If the help flag is set, show the help message
50 // If the help flag is set, show the help message
31 if cmd.Flag("help").Changed {
51 if cmd.Flag("help").Changed {
@@ -40,6 +60,15 @@ to assist developers in writing, debugging, and understanding code directly from
40 // Load the config
60 // Load the config
41 debug, _ := cmd.Flags().GetBool("debug")
61 debug, _ := cmd.Flags().GetBool("debug")
42 cwd, _ := cmd.Flags().GetString("cwd")
62 cwd, _ := cmd.Flags().GetString("cwd")
63+ prompt, _ := cmd.Flags().GetString("prompt")
64+ outputFormat, _ := cmd.Flags().GetString("output-format")
65+ quiet, _ := cmd.Flags().GetBool("quiet")
67+ // Validate format option
68+ if !format.IsValid(outputFormat) {
69+ return fmt.Errorf("invalid format option: %s\n%s", outputFormat, format.GetHelpText())
43 if cwd != "" {
72 if cwd != "" {
44 err := os.Chdir(cwd)
73 err := os.Chdir(cwd)
45 if err != nil {
74 if err != nil {
@@ -73,7 +102,19 @@ to assist developers in writing, debugging, and understanding code directly from
73 logging.Error("Failed to create app: %v", err)
102 logging.Error("Failed to create app: %v", err)
74 return err
103 return err
105+ // Defer shutdown here so it runs for both interactive and non-interactive modes
106+ defer app.Shutdown()
108+ // Initialize MCP tools early for both modes
109+ initMCPTools(ctx, app)
111+ // Non-interactive mode
113+ // Run non-interactive flow using the App method
114+ return app.RunNonInteractive(ctx, prompt, outputFormat, quiet)
77 // Set up the TUI
118 // Set up the TUI
78 zone.NewGlobal()
119 zone.NewGlobal()
79 program := tea.NewProgram(
120 program := tea.NewProgram(
@@ -81,9 +122,6 @@ to assist developers in writing, debugging, and understanding code directly from
81 tea.WithAltScreen(),
122 tea.WithAltScreen(),
84− // Initialize MCP tools in the background
85− initMCPTools(ctx, app)
87 // Setup the subscriptions, this will send services events to the TUI
125 // Setup the subscriptions, this will send services events to the TUI
88 ch, cancelSubs := setupSubscriptions(app, ctx)
126 ch, cancelSubs := setupSubscriptions(app, ctx)
@@ -255,4 +293,17 @@ func init() {
255 rootCmd.Flags().BoolP("version", "v", false, "Version")
293 rootCmd.Flags().BoolP("version", "v", false, "Version")
256 rootCmd.Flags().BoolP("debug", "d", false, "Debug")
294 rootCmd.Flags().BoolP("debug", "d", false, "Debug")
257 rootCmd.Flags().StringP("cwd", "c", "", "Current working directory")
295 rootCmd.Flags().StringP("cwd", "c", "", "Current working directory")
296+ rootCmd.Flags().StringP("prompt", "p", "", "Prompt to run in non-interactive mode")
298+ // Add format flag with validation logic
299+ rootCmd.Flags().StringP("output-format", "f", format.Text.String(),
300+ "Output format for non-interactive mode (text, json)")
302+ // Add quiet flag to hide spinner in non-interactive mode
303+ rootCmd.Flags().BoolP("quiet", "q", false, "Hide spinner in non-interactive mode")
305+ // Register custom validation for the format flag
306+ rootCmd.RegisterFlagCompletionFunc("output-format", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
307+ return format.SupportedFormats, cobra.ShellCompDirectiveNoFileComp